====== Zmiana adresu domenowego na IP ======
===== Tradycyjna funkcja gethostbyname =====
Najstarsze funkcje do zmiany to ''gethostbyname'' i ''gethostbyaddr''.
Funkcje są przestarzałe i powszechnie używane. Problemy z ich działaniem obrazuje poniższy kod:
#include
#include
#include
#include
#include
int main() {
std::thread t1([]{
sleep(1);
gethostbyname("spam.org");
});
std::thread t2([]{
hostent* ret = gethostbyname("cat.put.poznan.pl");
sleep(2);
printf("%s: %s\n", ret->h_name, inet_ntoa(**(in_addr**)ret->h_addr_list));
});
t1.join();
t2.join();
return 0;
}
//Zadanie 1.// Przetestuj powyższy kod. Zastanów się jak uniknąć problemu, który pokazuje powyższy kod.
===== Rozszerzenia GNU =====
Kompilator GCC udostępnia wiele rozszerzeń "łatających" braki standardów [[https://gcc.gnu.org/onlinedocs/gcc/C-Extensions.html]].\\
Towarzysząca mu biblioteka [[https://www.gnu.org/software/libc/|glibc]] wprowadza między innymi funkcję ''[[https://www.gnu.org/software/libc/manual/html_node/Host-Names.html#index-gethostbyname_005fr|gethostbyname_r]]'' (funkcje ''*_r'' są reentrant, tj. można je łatwiej stosować w aplikacjach wielowątkowych [[https://en.wikipedia.org/wiki/Reentrancy_%28computing%29]]). ''gethostbyname_r'' może być używana współbieżnie.
hostent he, *resptr;
int status, retval;
char buffer[4096];
retval = gethostbyname_r("cat.put.poznan.pl", &he, buffer, sizeof(buffer), &resptr, &status);
if(retval)
error(1,0,"gethostbyname_r error: %s", hstrerror(status));
if(!resptr)
error(1,0,"empty result");
print(resptr);
===== getaddrinfo =====
Standard POSIX wprowadza funkcje ''getaddrinfo'', która pozwala na bezpieczne tłumaczenie nazwy domenowej na adresy IP (zarówno IPv4 i IPv6). Funkcja od razu tworzy gotową strukturę ''sockaddr''. Wynik musi być zwolniony przez programistę funkcją ''freeaddrinfo''. Poza ''getaddrinfo'' standard wprowadza też m. inn. funkcję do tłumaczenia ''sockaddr*'' na tekst – ''getnameinfo''.
Składnia:
int getaddrinfo( // zwraca 0 (ok) lub niezerowy kod błędu (można go przerobić na tekst używając gai_strerror)
const char * ip_or_hostname, // nazwa domenowa lub adres IP
const char * port_or_service, // numer portu (np. "80") lub nazwa usługi (np. "http", ustawi port na 80)
const struct addrinfo *hints, // pozwala wybrać typ adresu, wyłączyć używanie DNS, etc.
struct addrinfo **res); // pole na wynik (jeśli puste - nic nie znaleziono)
Definicja struktury ''addrinfo'':
struct addrinfo {
int ai_flags;
int ai_family; // ai_family, ai_socktype i ai_protocol mają znaczenie identyczne jak
int ai_socktype; // w funkcji socket(). Mogą być ustawione w podpowiedziach na wybraną
int ai_protocol; // wartość lub na 0 (wtedy oznaczają wszystkie pasujące wartości)
socklen_t ai_addrlen; // ai_addr to adres o długości ai_addrlen który może być
struct sockaddr *ai_addr; // użyty w funkcji connect lub bind
char *ai_canonname;
struct addrinfo *ai_next; // struktura addrinfo tworzy jednokierunkową linked listę, ai_next wskazuje na jej następny element
};
Pełen opis funkcji ''getaddrinfo'' i struktury ''addrinfo'' znajdziesz na stronie manuala do getaddrinfo (''man getaddrinfo'') lub w standardzie POSIX ([[https://pubs.opengroup.org/onlinepubs/9699919799/functions/freeaddrinfo.html|[1]]], [[https://pubs.opengroup.org/onlinepubs/9699919799/functions/getnameinfo.html|[2]]], [[https://pubs.opengroup.org/onlinepubs/9699919799/functions/gai_strerror.html|[3]]]).
\\
Uwaga: ''getaddrinfo'' w razie sukcesu zwraca ''0'', a w przypadku błędu zwraca jego kod.
Przykłady użycia:
#include
#include
#include // niestandardowa funkcja ułatwiająca obsługę błędów (dostępna w glibc)
#include
int main(){
// Ustawienie "podpowiedzi" - sterowanie jakie wyniki chcemy otrzymać:
addrinfo hints {}; // uwaga: puste 'list initialization' zeruje całą strukturę
hints.ai_family = AF_INET; // tylko IPv4 (AF_INET)
hints.ai_protocol = IPPROTO_UDP; // protokół UDP
// Zmienna w której będzie umieszczona lokalizacja wyniku w pamięci
addrinfo * resolved;
// Z sieci bezprzewodowej PP (lub innych używających serwerów nazw z podsieci 150.254.5.0/24)
// proszę zmienić "pool.ntp.org" na "onet.pl" - Dział Obsługi i Eksploatacji z niezrozumiałych
// przyczyn cenzuruje odpowiedzi z DNS
int res = getaddrinfo("pool.ntp.org", "ntp", &hints, &resolved);
if(res) error(1,0,"Getaddrinfo failed: %s\n", gai_strerror(res));
if(!resolved) error(1,0,"Empty result\n");
for(addrinfo * it = resolved; it; it=it->ai_next){
sockaddr_in* addr = (sockaddr_in*) it->ai_addr; // <- rzutowanie bezpieczne,
printf(" %s\n",inet_ntoa(addr->sin_addr)); // bo w podpowiedziach
} // zażądaliśmy adresów IPv4
freeaddrinfo(resolved);
return 0;
}
#include
#include
#include
int main(){
addrinfo * resolved;
// Podpowiedzi, port i nazwa hosta są opcjonalne.
int res = getaddrinfo("ietf.org", nullptr, nullptr, &resolved);
if(res) error(1,0,"Getaddrinfo failed: %s\n", gai_strerror(res));
if(!resolved) error(1,0,"Empty result\n");
char ip[40]; // maks. długość IP(v6) jako tekst: 8 bloków po 4 znaki oddzielone ':'
// można użyć stałą NI_MAXHOST określającą maksymalną długość nazwy domenowej
for(addrinfo * it = resolved; it; it=it->ai_next){
// funkcja getnameinfo tłumaczy dowolny sockaddr na tekst
res = getnameinfo(it->ai_addr, it->ai_addrlen, ip, 40, nullptr, 0, NI_NUMERICHOST);
if(res) error(1,0,"Getnameinfo failed: %s\n", gai_strerror(res));
else printf("%40s (socktype: %d, proto: %d)\n", ip, it->ai_socktype, it->ai_protocol);
}
freeaddrinfo(resolved);
return 0;
}
//Zadanie 2.// Stwórz klienta TCP, który:
* odczyta z listy argumentów adres i numer portu
* użyje funkcji ''getaddrinfo'' z podpowiedzią żądającą protokołu TCP
* utworzy socket korzystając z rodziny adresów zwróconej przez ''getaddrinfo''
* połączy się funkcją connect korzystając ze struktury sockaddr i informacji o jej długości zwróconej przez ''getaddrinfo''