getaddrinfo(3) Library Functions Manual getaddrinfo(3)

getaddrinfo, freeaddrinfo, gai_strerror - tłumaczy adresy i usługi sieciowe

Standardowa biblioteka C (libc, -lc)

#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
int getaddrinfo(const char *restrict node,
                const char *restrict service,
                const struct addrinfo *restrict hints,
                struct addrinfo **restrict res);
void freeaddrinfo(struct addrinfo *res);
const char *gai_strerror(int errcode);
Wymagane ustawienia makr biblioteki glibc (patrz feature_test_macros(7)):

getaddrinfo(), freeaddrinfo(), gai_strerror():

    Od glibc 2.22:
        _POSIX_C_SOURCE >= 200112L
    glibc 2.21 i wcześniejsze:
        _POSIX_C_SOURCE

Dla danego węzła node i usługi service, które identyfikują stację i usługę internetową, getaddrinfo() zwraca jedną lub więcej struktur addrinfo, każda z których zawiera adres internetowy, który można podać w wywołaniu do bind(2) lub connect(2). Funkcja getaddrinfo() łączy funkcjonalność udostępnianą przez funkcje gethostbyname(3) i getservbyname(3) w jeden interfejs, lecz w przeciwieństwie do nich, getaddrinfo() jest wielobieżna i umożliwia programom wyeliminowanie zależności od IPv4 lub IPv6.

Struktura addrinfo używana przez getaddrinfo() zawiera następujące pola:


struct addrinfo {
    int              ai_flags;
    int              ai_family;
    int              ai_socktype;
    int              ai_protocol;
    socklen_t        ai_addrlen;
    struct sockaddr *ai_addr;
    char            *ai_canonname;
    struct addrinfo *ai_next;
};

Argument hints wskazuje na strukturę addrinfo, która określa kryteria wyboru struktur adresów gniazd zwracanych w liście wskazywanej przez res. Jeśli hints nie wynosi NULL, wskazuje na strukturę addrinfo, której pola ai_family, ai_socktype i ai_protocol określają kryteria ograniczające zbiory adresów gniazd zwracane przez getaddrinfo(), zgodnie z poniższym opisem:

Pole określa żądaną rodzinę adresów wobec zwracanych adresów. Prawidłowe wartości tego pola obejmują AF_INET i AF_INET6. Wartość AF_UNSPEC wskazuje, że getaddrinfo() powinno zwrócić adresy gniazd dla dowolnej rodziny adresów (np. IPv4 lub IPv6), których można użyć z node i service.
Pole określa preferowany typ gniazda np. SOCK_STREAM lub SOCK_DGRAM. Podanie w tym polu wartości 0 wskazuje, że getaddrinfo() może zwrócić adresy gniazd dowolnego typu.
Pole określa protokół zwracanych adresów gniazd. Podanie w tym polu wartości 0 wskazuje, że getaddrinfo() może zwrócić adresy gniazd dowolnego protokołu.
Pole określa dodatkowe opcje, opisane poniżej. Można podać wiele flag, sumując je bitowo (OR).

Wszystkie pozostałe pola w strukturze na którą wskazuje hints, muszą zawierać albo 0, albo pusty wskaźnik (w zależności od pola).

Podanie hints jako NULL jest równoważne ustawieniu ai_socktype i ai_protocol na 0; ai_family na AF_UNSPEC; a ai_flags na (AI_V4MAPPED | AI_ADDRCONFIG) (POSIX określa inną wartość domyślną dla ai_flags; zob. UWAGI). node zawiera albo adres sieciowy w postaci numerycznej (dla IPv4: w formacie liczb i kropek takim, jak obsługiwany przez inet_aton(3); dla IPv6: w formacie szesnastkowego łańcucha takim, jak obsługiwany przez init_pton(3)), albo nazwę stacji, dla której adresy sieciowe będą poszukiwane i rozwiązane. Jeśli hints.ai_flags zawiera znacznik AI_NUMERICHOST, to node musi być adresem sieciowym w postaci numerycznej. Znacznik AI_NUMERICHOST eliminuje jakiekolwiek, potencjalnie długotrwałe, poszukiwania adresu sieciowego stacji.

Jeśli w hints.ai_flags podano znacznik AI_PASSIVE, a node wynosi NULL, to zwracane adresy gniazd będą odpowiednie do przypisywania (bind(2)) gniazd, które będą akceptowały (accept(2)) połączenia. Zwracany adres gniazda będzie zawierał „adres wieloznaczny” („wildcard address”; INADDR_ANY w przypadku adresów IPv4, IN6ADDR_ANY_INIT w przypadku adresów IPv6). Adres wieloznaczny jest używany przez aplikacje (zwykle serwery), które mają zamiar akceptować połączenia na dowolnym z adresów sieciowych stacji. Jeśli node nie wynosi NULL, to znacznik AI_PASSIVE jest ignorowany.

Jeśli w hints.ai_flags nie podano znacznika AI_PASSIVE, to zwracane adresy gniazd będą odpowiednie do korzystania z connect(2), sendto(2) lub sendmsg(2). Jeśli node wynosi NULL, to adres sieciowy będzie ustawiony na adres interfejsu pętli zwrotnej (INADDR_LOOPBACK w przypadku adresów IPv4, IN6ADDR_LOOPBACK_INIT w przypadku adresów IPv6); jest to używane przez aplikacje, które mają zamiar komunikować się z innymi aplikacjami działającymi na tej samej stacji.

service ustawia port w każdej zwracanej strukturze adresu. Jeśli argument ten jest nazwą usługi (zob. services(5)), to jest tłumaczona na odpowiedni numer portu. Argument ten może być też liczbą dziesiętną, wówczas jest jedynie przekształcana na liczbę binarną. Jeśli service wynosi NULL, to numer portu zwracanych adresów gniazd będzie pozostawiony niezainicjowany. Jeśli w hints.ai_flags podano AI_NUMERICSERV, a service nie wynosi NULL, to service musi wskazywać na łańcuch zawierający numeryczny numer portu. Znacznik ten jest używany do powstrzymania rozwiązywania nazw w przypadkach, w których wiadomo, że nie będzie to konieczne.

Parametry node i service mogą być równe NULL, ale nie oba naraz.

Funkcja getaddrinfo() alokuje i inicjuje podlinkowaną listę struktur addrinfo, po jednej dla każdego adresu sieciowego, który pasuje do node i service, jest przedmiotem wszelkich ograniczeń nałożonych przez hints, a zwraca wskaźnik do początku listy w res. Pozycje na podlinkownej liście są linkowane za pomocą pola ai_next.

Istnieje wiele powodów, dla których podlinkowana lista może mieć więcej niż jedną strukturę addrinfo m.in.: stacja sieciowa może być wieloadresowa, dostępna za pomocą różnych protokołów (np. AF_INET oraz AF_INET6); albo ta sama usługa jest dostępna za pomocą różnych typów gniazd (np. jeden adres SOCK_STREAM i inny adres SOCK_DGRAM). Aplikacja zwykle spróbuje użyć adresy w zwracanej kolejności. Funkcja sortowania używana w getaddrinfo() jest zdefiniowana w RFC 3484; kolejność można dostosować dla danego systemu, edytując plik /etc/gai.conf (dostępny od glibc 2.5).

Jeśli hints.ai_flags zawiera znacznik AI_CANONNAME, to pole ai_canonname w pierwszej ze struktur addrinfo w zwracanej liście, jest ustawiane na oficjalną nazwę stacji.

Pozostałe pola w każdej ze zwracanych struktur addrinfo są inicjowane w następujący sposób:

Pola ai_family, ai_socktype i ai_protocol zwracają parametry tworzenia gniazd (tzn. pola te mają takie samo znaczenie, jak odpowiadające im argumenty socket(2)). Przykładowo ai_family może zwrócić AF_INET lub AF_INET6; ai_socktype może zwrócić SOCK_DGRAM lub SOCK_STREAM; a ai_protocol zwróci protokół gniazda.
Wskaźnik do adresu gniazda jest umieszczany w polu ai_addr, a długość adresu gniazda w bajtach, jest umieszczana w polu ai_addrlen.

Jeśli hints.ai_flags zawiera znacznik AI_ADDRCONFIG, to adresy IPv4 są zwracane w liście, na którą wskazuje res tylko, gdy lokalny system ma skonfigurowany przynajmniej jeden adres IPv4, a adresy IPv6 są zwracane tylko, gdy lokalny system ma skonfigurowany przynajmniej jeden adres IPv6. Adres pętli zwrotnej nie jest w tym przypadku uważany za prawidłowy skonfigurowany adres. Znacznik ten jest przydatny np. w systemach korzystających wyłącznie z IPv4 aby zapewnić, że getaddrinfo() nie zwróci adresów gniazd IPv6, które zawsze zawiodłyby w connect(2) lub bind(2).

Jeśli hints.ai_flags określa znacznik AI_V4MAPPED, a hints.ai_family podano jako AF_INET6 oraz nie da się znaleźć pasującego adresu IPv6, to w liście, na którą wskazuje res, zwracane są adresy IPv4 zmapowane jako IPv6. Jeśli w hints.ai_flags podano AI_V4MAPPED oraz AI_ALL, to zwracane są adresy IPv6 oraz adresy IPv4 zmapowane jako IPv6. AI_ALL jest ignorowane, jeśli nie podano również AI_V4MAPPED.

Funkcja freeaddrinfo() zwalnia pamięć przydzieloną dla dynamicznie zaalokowanej listy res.

Od glibc 2.3.4, getaddrinfo() rozszerzono w celu umożliwiania wybiórczego i przezroczystego konwertowania przychodzących i wychodzących nazw stacji z i na format Internationalized Domain Name (IDN; zob. RFC 3490, Internationalizing Domain Names in Applications (IDNA)). Zdefiniowano cztery nowe znaczniki:

Jeśli znacznik jest podany, nazwa węzła podana w node jest konwertowana na format IDN, jeśli to konieczne. Kodowanie źródłowe jest takie, jak w bieżących ustawieniach regionalnych (locale).
Jeśli nazwa wejściowa zawiera znaki spoza ASCII, to używane jest kodowanie IDN. Te fragmenty nazwy węzła (oddzielone kropką), które zawierają znaki spoza ASCII są kodowane za pomocą ASCII Compatible Encoding, przed ich przekazaniem do funkcji rozwiązywania nazw.
Po pomyślnym wyszukaniu nazwy, jeśli podano AI_CANONNAME, getaddrinfo() zwróci kanoniczną nazwę węzła odnoszącą się do wartości struktury addrinfo przekazywanej zwrotnie. Zwracana wartość jest dokładną kopią wartości zwracanej przez funkcję rozwiązywania nazw.
Jeśli nazwa jest zakodowana za pomocą ACE, to będzie zawierać przedrostek xn-- w jednej lub więcej składowych nazw. Aby przekształcić te składowe w czytelną postać, oprócz znacznika AI_CANONNAME można podać też AI_CANONIDN. Wynikowy łańcuch będzie kodowany przy użyciu kodowania z bieżących ustawieniach regionalnych (locale).
Ustawienie tych znaczników włączy znaczniki, odpowiednio, IDNA_ALLOW_UNASSIGNED (zezwala na nieprzypisane kody Unikodu) i IDNA_USE_STD3_ASCII_RULES (sprawdza wyjście, aby upewnić się że jest to nazwa stacji zgodna z STD3) do użycia w obsłudze IDNA.

getaddrinfo() zwraca 0, gdy zakończy się pomyślnie, a w przeciwnym razie jeden z następujących niezerowych kodów błędów:

Podana stacja nie posiada żadnego adresu sieciowego dla zadanej rodziny adresów.
Serwer nazw zwrócił błąd tymczasowy. Należy spróbować później.
hints.ai_flags zawiera nieprawidłowe znaczniki; lub hints.ai_flags zawierało AI_CANONNAME i node wynosiło NULL.
Serwer nazw zwrócił błąd trwały.
Żądana rodzina adresów nie jest obsługiwana.
Brak pamięci.
Podana stacja sieciowa istnieje, ale nie zdefiniowano dla niej żadnego adresu sieciowego.
node lub service nie jest znane; albo zarówno node jak i service wynoszą NULL; albo w hints.ai_flags podano AI_NUMERICSERV, a service nie było numerycznym łańcuchem numeru portu.
Żądana usługa nie jest dostępna dla żądanego typu portu. Może być dostępna za pomocą innego typu portu. Ten błąd może wystąpić na przykład, gdy jako service podano „shell” (usługa dostępna tylko na gniazdach strumieniowych), a hints.ai_protocol wynosiło IPPROTO_UDP albo hints.ai_socktype wynosiło SOCK_DGRAM; albo błąd może wystąpić gdy service nie wynosiło NULL, a hints.ai_socktype wynosiło SOCK_RAW (typ gniazda, w ogóle nie obsługujący koncepcji usług).
Żądany typ gniazda nie jest obsługiwany. Może się to zdarzyć na przykład, gdy hints.ai_socktype i hints.ai_protocol są niespójne (np. wynoszą, odpowiednio, SOCK_DGRAM i IPPROTO_TCP).
Inny błąd systemowy; ustawiane jest errno aby wskazać błąd.

Funkcja gai_strerror() tłumaczy te kody błędów na czytelny dla człowieka łańcuch, odpowiedni do zgłaszania błędów.

/etc/gai.conf

Informacje o pojęciach używanych w tym rozdziale można znaleźć w podręczniku attributes(7).

Interfejs Atrybut Wartość
getaddrinfo() Bezpieczeństwo wątkowe MT-bezpieczne env locale
freeaddrinfo(), gai_strerror() Bezpieczeństwo wątkowe MT-bezpieczne

Zgodnie z POSIX.1, określenie hints jako NULL, powinno powodować przyjęcie ai_flags jako 0. Biblioteka GNU C zamiast tego przyjmuje w tym przypadku wartość (AI_V4MAPPED | AI_ADDRCONFIG), ponieważ wartość ta jest uważana za lepszą od przewidzianej normą.

POSIX.1-2008.

RFC 2553.

POSIX.1-2001.

glibc 2.3.3.
glibc 2.3.4.

getaddrinfo() obsługuje notację adres%identyfikator-zasięgu do określenia identyfikatora zasięgu IPv6.

Poniższe programy demonstrują użycie getaddrinfo(), gai_strerror(), freeaddrinfo() i getnameinfo(3). Programy są serwerem i klientem typu echo dla datagramów UDP.

#include <netdb.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <unistd.h>
#define BUF_SIZE 500
int
main(int argc, char *argv[])
{
    int                      sfd, s;
    char                     buf[BUF_SIZE];
    ssize_t                  nread;
    socklen_t                peer_addrlen;
    struct addrinfo          hints;
    struct addrinfo          *result, *rp;
    struct sockaddr_storage  peer_addr;
    if (argc != 2) {
        fprintf(stderr, "Usage: %s port\n", argv[0]);
        exit(EXIT_FAILURE);
    }
    memset(&hints, 0, sizeof(hints));
    hints.ai_family = AF_UNSPEC;    /* Allow IPv4 or IPv6 */
    hints.ai_socktype = SOCK_DGRAM; /* Datagram socket */
    hints.ai_flags = AI_PASSIVE;    /* For wildcard IP address */
    hints.ai_protocol = 0;          /* Any protocol */
    hints.ai_canonname = NULL;
    hints.ai_addr = NULL;
    hints.ai_next = NULL;
    s = getaddrinfo(NULL, argv[1], &hints, &result);
    if (s != 0) {
        fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(s));
        exit(EXIT_FAILURE);
    }
    /* getaddrinfo() returns a list of address structures.
       Try each address until we successfully bind(2).
       If socket(2) (or bind(2)) fails, we (close the socket
       and) try the next address. */
    for (rp = result; rp != NULL; rp = rp->ai_next) {
        sfd = socket(rp->ai_family, rp->ai_socktype,
                     rp->ai_protocol);
        if (sfd == -1)
            continue;
        if (bind(sfd, rp->ai_addr, rp->ai_addrlen) == 0)
            break;                  /* Success */
        close(sfd);
    }
    freeaddrinfo(result);           /* No longer needed */
    if (rp == NULL) {               /* No address succeeded */
        fprintf(stderr, "Could not bind\n");
        exit(EXIT_FAILURE);
    }
    /* Read datagrams and echo them back to sender. */
    for (;;) {
        char host[NI_MAXHOST], service[NI_MAXSERV];
        peer_addrlen = sizeof(peer_addr);
        nread = recvfrom(sfd, buf, BUF_SIZE, 0,
                         (struct sockaddr *) &peer_addr, &peer_addrlen);
        if (nread == -1)
            continue;               /* Ignore failed request */
        s = getnameinfo((struct sockaddr *) &peer_addr,
                        peer_addrlen, host, NI_MAXHOST,
                        service, NI_MAXSERV, NI_NUMERICSERV);
        if (s == 0)
            printf("Received %zd bytes from %s:%s\n",
                   nread, host, service);
        else
            fprintf(stderr, "getnameinfo: %s\n", gai_strerror(s));
        if (sendto(sfd, buf, nread, 0, (struct sockaddr *) &peer_addr,
                   peer_addrlen) != nread)
        {
            fprintf(stderr, "Error sending response\n");
        }
    }
}

#include <netdb.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <unistd.h>
#define BUF_SIZE 500
int
main(int argc, char *argv[])
{
    int              sfd, s;
    char             buf[BUF_SIZE];
    size_t           len;
    ssize_t          nread;
    struct addrinfo  hints;
    struct addrinfo  *result, *rp;
    if (argc < 3) {
        fprintf(stderr, "Usage: %s host port msg...\n", argv[0]);
        exit(EXIT_FAILURE);
    }
    /* Obtain address(es) matching host/port. */
    memset(&hints, 0, sizeof(hints));
    hints.ai_family = AF_UNSPEC;    /* Allow IPv4 or IPv6 */
    hints.ai_socktype = SOCK_DGRAM; /* Datagram socket */
    hints.ai_flags = 0;
    hints.ai_protocol = 0;          /* Any protocol */
    s = getaddrinfo(argv[1], argv[2], &hints, &result);
    if (s != 0) {
        fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(s));
        exit(EXIT_FAILURE);
    }
    /* getaddrinfo() returns a list of address structures.
       Try each address until we successfully connect(2).
       If socket(2) (or connect(2)) fails, we (close the socket
       and) try the next address. */
    for (rp = result; rp != NULL; rp = rp->ai_next) {
        sfd = socket(rp->ai_family, rp->ai_socktype,
                     rp->ai_protocol);
        if (sfd == -1)
            continue;
        if (connect(sfd, rp->ai_addr, rp->ai_addrlen) != -1)
            break;                  /* Success */
        close(sfd);
    }
    freeaddrinfo(result);           /* No longer needed */
    if (rp == NULL) {               /* No address succeeded */
        fprintf(stderr, "Could not connect\n");
        exit(EXIT_FAILURE);
    }
    /* Send remaining command-line arguments as separate
       datagrams, and read responses from server. */
    for (size_t j = 3; j < argc; j++) {
        len = strlen(argv[j]) + 1;
                /* +1 for terminating null byte */
        if (len > BUF_SIZE) {
            fprintf(stderr,
                    "Ignoring long message in argument %zu\n", j);
            continue;
        }
        if (write(sfd, argv[j], len) != len) {
            fprintf(stderr, "partial/failed write\n");
            exit(EXIT_FAILURE);
        }
        nread = read(sfd, buf, BUF_SIZE);
        if (nread == -1) {
            perror("read");
            exit(EXIT_FAILURE);
        }
        printf("Received %zd bytes: %s\n", nread, buf);
    }
    exit(EXIT_SUCCESS);
}

getaddrinfo_a(3), gethostbyname(3), getnameinfo(3), inet(3), gai.conf(5), hostname(7), ip(7)

Autorami polskiego tłumaczenia niniejszej strony podręcznika są: Andrzej Krzysztofowicz <ankry@green.mf.pg.gda.pl> i Michał Kułach <michal.kulach@gmail.com>

Niniejsze tłumaczenie jest wolną dokumentacją. Bliższe informacje o warunkach licencji można uzyskać zapoznając się z GNU General Public License w wersji 3 lub nowszej. Nie przyjmuje się ŻADNEJ ODPOWIEDZIALNOŚCI.

Błędy w tłumaczeniu strony podręcznika prosimy zgłaszać na adres listy dyskusyjnej manpages-pl-list@lists.sourceforge.net.

15 czerwca 2024 r. Linux man-pages 6.9.1