| 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);
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:
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:
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:
getaddrinfo() zwraca 0, gdy zakończy się pomyślnie, a w przeciwnym razie jeden z następujących niezerowych kodów błędów:
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.
POSIX.1-2001.
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, "Użycie: %s port\n", argv[0]);
exit(EXIT_FAILURE);
}
memset(&hints, 0, sizeof(hints));
hints.ai_family = AF_UNSPEC; /* Zezwala na IPv4 lub IPv6 */
hints.ai_socktype = SOCK_DGRAM; /* Gniazdo datagramowe */
hints.ai_flags = AI_PASSIVE; /* Dla wieloznacznego adresu IP */
hints.ai_protocol = 0; /* Dowolny protokół */
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() zwraca listę struktur adresów. Próbowany
jest każdy adres do momentu pomyślnego bind(2).
Jeśli socket(2) (lub bind(2)) zawiedzie, (gniazdo jest
jest zamykane i) próbowany jest następny adres. */
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; /* Powodzenie */
close(sfd);
}
freeaddrinfo(result); /* Nie jest już potrzebne */
if (rp == NULL) { /* Nie udało się z żadnym adresem */
fprintf(stderr, "Przypisanie nie powiodło się\n");
exit(EXIT_FAILURE);
}
/* Odczytuje datagramy i odsyła je wysyłającemu. */
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; /* Ignoruje niepomyślne żądanie */
s = getnameinfo((struct sockaddr *) &peer_addr,
peer_addrlen, host, NI_MAXHOST,
service, NI_MAXSERV, NI_NUMERICSERV);
if (s == 0)
printf("Otrzymano %zd bajtów z %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, "Błąd przy wysyłaniu odpowiedzi\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, "Użycie: %s stacja port komunik...\n", argv[0]);
exit(EXIT_FAILURE);
}
/* Pozyskuje adres(y) pasującej stacji/portu. */
memset(&hints, 0, sizeof(hints));
hints.ai_family = AF_UNSPEC; /* Zezwala na IPv4 lub IPv6 */
hints.ai_socktype = SOCK_DGRAM; /* Gniazdo datagramowe */
hints.ai_flags = 0;
hints.ai_protocol = 0; /* Dowolny protokół */
s = getaddrinfo(argv[1], argv[2], &hints, &result);
if (s != 0) {
fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(s));
exit(EXIT_FAILURE);
}
/* getaddrinfo() zwraca listę struktur adresów. Próbuje
każdego adresu, do pomyślnego połączenia (connect(2)).
Jeśli socket(2) (lub connect(2)) zawiedzie, (zamykamy
gniazdo i) próbujemy następny adres. */
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; /* Powodzenie */
close(sfd);
}
freeaddrinfo(result); /* Nie jest już potrzebne */
if (rp == NULL) { /* Nie udało się z żadnym adresem */
fprintf(stderr, "Nie udało się połączyć\n");
exit(EXIT_FAILURE);
}
/* Wysyła pozostałe argumenty wiersza poleceń jako oddzielne
datagramy i odczytuje odpowiedzi z serwera. */
for (size_t j = 3; j < argc; j++) {
len = strlen(argv[j]) + 1;
/* +1 dla końcowego bajtu null */
if (len > BUF_SIZE) {
fprintf(stderr,
"Ignorowanie długiego komunikatu w argumencie %zu\n", j);
continue;
}
if (write(sfd, argv[j], len) != len) {
fprintf(stderr, "częściowy/nieudany zapis\n");
exit(EXIT_FAILURE);
}
nread = read(sfd, buf, BUF_SIZE);
if (nread == -1) {
perror("read");
exit(EXIT_FAILURE);
}
printf("Otrzymano %zd bajtów: %s\n", nread, buf);
}
exit(EXIT_SUCCESS);
}
getaddrinfo_a(3), gethostbyname(3), getnameinfo(3), inet(3), gai.conf(5), hostname(7), ip(7)
Tłumaczenie niniejszej strony podręcznika: 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 |