Narzędzia użytkownika

Narzędzia witryny


Pasek boczny

sk2:sockets_netdbs

To jest stara wersja strony!


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:

ghbn.cpp
#include <unistd.h>
#include <thread>
#include <arpa/inet.h>
#include <netdb.h>
 
int main() {
 
    std::thread t1([&]{
        sleep(1);
        gethostbyname("spam.org");
    });
 
    std::thread t2([&]{
        hostent* ret = gethostbyname("fc.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.
Między innymi wprowadza funkcje gethostbyname_r (funkcje *_r są reentrant, tj. można je bezpiecznie stosować w aplikacjach wielowątkowych https://en.wikipedia.org/wiki/Reentrancy_%28computing%29)

hostent he, *resptr;
	int status, retval;
	char buffer[4096];
	retval = gethostbyname_r("fc.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.

Składnia:

int getaddrinfo(                  // zwraca 0 (ok) lub -1 (błąd) 
    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)

Opis struktury addrinfo znajdziesz na stronie manuala do getaddringo (man getaddrinfo).

Przykłady użycia:

gai1.cpp
#include <netdb.h>
#include <cstdio>
#include <error.h>
#include <arpa/inet.h>
 
int main(){
    // Ustawienie "podpowiedzi" - sterowanie jakie wyniki chcemy otrzymać:
    addrinfo hints {};
    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;
 
    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;
}
gai2.cpp
#include <netdb.h>
#include <cstdio>
#include <error.h>
 
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 ':'
    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
sk2/sockets_netdbs.1571661385.txt.gz · ostatnio zmienione: 2019/10/21 14:36 przez jkonczak