cs:bsd_sockets_-_prevody_mezi_jmeny_a_adresami

Málokterá síťová aplikace pro IPv4 se asi obejde bez použití systému DNS a tím i funkce gethostbyname(). V kontextu IPv6 je ale tato funkce nepoužitelná. Každý uzel Internetu podporující IPv6 totiž bude mít kromě jedné nebo více adres IPv6 obvykle také adresu IPv4. Obě verze adres mohou být v DNS přiřazeny stejnému doménovému jménu - adresu IPv4 najdeme v záznamu typu A a adresu IPv6 v záznamu typu AAAA. Funkce gethostbyname() neumožňuje říci, který z obou typů záznamu chceme, popřípadě zda chceme oba. RFC 2553 proto zavedlo nové funkce getipnodebyname() a getipnodebyaddr() pro převod jména na adresu a obráceně. Bohužel i tyto funkce se ukázaly jako nedostatečné, neboť nepodporují dosahy adres (address scopes). V novějších verzích API byly proto také zavrženy. Pro převod jména na adresu tak zbyla jediná, protokolově nezávislá funkce - getaddrinfo(), jež má svůj původ ve standardu IEEE POSIX 1003.1g. Kromě gethostbyname() plně nahrazuje i funkci getservbyname(). Prototyp vypadá takto:

int *getaddrinfo(const char *nodename, const char *servname,
       const struct addrinfo *hints, struct addrinfo **res);

Funkce vrací buď nulu v případě úspěchu anebo různé chybové kódy. Skutečný výsledek je předán prostřednictvím argumentu res, který je ukazatelem na jednosměrný seznam struktur typu addrinfo definovaného v netdb.h:

struct addrinfo
{
  int ai_flags;              /* Input flags.  */
  int ai_family;             /* Protocol family for socket.  */
  int ai_socktype;           /* Socket type.  */
  int ai_protocol;           /* Protocol for socket.  */
  socklen_t ai_addrlen;      /* Length of socket address.  */
  struct sockaddr *ai_addr;  /* Socket address for socket.  */
  char *ai_canonname;        /* Canonical name.  */
  struct addrinfo *ai_next;  /* Pointer to next in list.  */
};

Položka ai_next ukazuje na následující prvek seznamu, poslední prvek seznamu pak zde má NULL. Paměť pro seznam musí být alokována dynamicky, a proto musíme (alespoň v C) pamatovat na řádné uvolnění této paměti po použití. K tomu slouží funkce freeaddrinfo().

Každý z prvků seznamu obsahuje položky, které můžeme přímo použít jako argumenty pro funkci socket(): ai_family (v našem případě PF_INET či PF_INET6, ai_socktype (obvykle SOCK_STREAM nebo SOCK_DGRAM) a ai_protocol (obvykle IPPROTO_TCP nebo SOCK_UDP). Položka ai_addr pak ukazuje na adresu soketu, jejíž délku specifikuje položka ai_addrlen.

Argument nodename by měl obsahovat doménové jméno, o které nám jde, popřípadě obvyklý textový zápis adresy IPv4 nebo IPv6. Pokud chceme, můžeme vyplnit i argument servname a to buď jménem služby podle /etc/services anebo dekadickým číslem portu ve formě řetězce.

Argument hints umožňuje stanovit, o jaký typ dat máme zájem. Jeho typem je opět struct addrinfo, v níž ovšem smějí být vyplněny jen tyto položky: ai_flags, ai_family, ai_socktype a ai_protocol. Ostatní položky musí být vynulovány. V případě, že v některé kategorii jsme připraveni přijmout cokoli, nastavíme hodnoty podle následující tabulky. Nemáme-li preference v žádné kategorii, stačí předat NULL na místě argumentu hints.

Kritérium Položka Nastavení
Libovolná adresová rodina ai_family AF_UNSPEC
Libovolný typ soketu ai_socktype 0
Libovolný protokol ai_protocol 0

Položka ai_flags v argumentu hints hints obsahuje příznaky (flags), kterými můžeme své požadavky specifikovat ještě detailněji. Jak je obvyklé, jednotlivé příznaky můžeme kombinovat pomocí operace bitového OR (|). K dispozici jsou tyto příznaky:

AI_PASSIVE
Tento příznak hraje roli jen v případě, že je na místě argumentu nodename předána hodnota NULL. Příznak nastavíme, pokud chceme výstup funkce getaddrinfo() použít pro funkci bind(), tj. na lokální straně soketu. Adresa soketu se v tom případě vyplní s INADDR_ANY pro IPv4 nebo IN6ADDR_ANY_INIT pro IPv6 (viz oddíl Základní funkce). Pokud chceme výstup funkce getaddrinfo() použít pro vzdálenou stranu soketu, tedy např. ve funkcích connect() či sendto(), pak tento příznak nenastavíme. Dostaneme pak adresu soketu s INADDR_LOOPBACK pro IPv4 nebo IN6ADDR_LOOPBACK_INIT pro IPv6. AI_CANONNAME
Nastavíme-li tento příznak a argument nodename není NULL, funkce getaddrinfo() se pokusí též vyhledat kanonické doménové jméno uzlu. To se hodí třeba tehdy, když známe DNS alias a chceme zjistit kanonické jméno. AI_NUMERICHOST
Pokud je tento příznak nastaven, musí být argument nodename řetězcem reprezentujícím adresu IPv4 nebo IPv6. Funkce getaddrinfo() pak vůbec nepoužívá DNS. AI_NUMERICSERV
Tento příznak nastavíme, pokud chceme jako argument servname použít řetězec s dekadickým číslem portu. AI_V4MAPPED
Tento příznak má smysl jen pro AF_INET6 v položce ai_family argumentu hints. Pokud je nastaven a funkce getaddrinfo() nenalezne pro zadané nodename v DNS žádné záznamy typu AAAA, poskytne jako výsledek obsah záznamů typu A (pokud takové existují) ve formě IPv4-mapovaných adres IPv6 (IPv4-mapped IPv6 addresses). AI_ALL
Je-li tento příznak nastaven zároveň s AI_V4MAPPED a hints.ai_family je AF_INET6, pak funkce getaddrinfo() vrátí adresové údaje ze všech záznamů DNS typu AAAA i A, přičemž posledně jmenované budou opět ve formě IPv4-mapovaných adres IPv6. V případě, že adresová rodina není specifikována, tj. hints.ai_family==AF_UNSPEC anebo hints==NULL, bere se nastavení příznaků AI_V4MAPPED a AI_ALL v úvahu jen tehdy, je-li na hostitelském počítači podporováno IPv6. AI_ADDRCONFIG
Nastavení tohoto příznaku vede k tomu, že funkce getaddrinfo() vrátí adresy IPv4 jen tehdy, je-li na hostitelském počítači zkonfigurována nějaká lokální adresa IPv4 a podobně vrátí adresy IPv6 jen tehdy, je-li zkonfigurována aspoň jedna lokální adresa IPv6 (v obou případech se nepočítá adresa zpětné smyčky).

V jistém smyslu inverzní funkcí ke getaddrinfo() je tato funkce:

int getnameinfo(const struct sockaddr *sa, socklen_t salen,
                char *node, socklen_t nodelen, char *service,
                socklen_t servicelen, int flags);

Jejím úkolem je převod obsahu adresy soketu na doménové jméno uzlu a/nebo jméno služby. Návratová hodnota je opět buď nula v případě úspěchu nebo příslušný chybový kód.

Struktura typu sockaddr, na niž ukazuje argument sa, může obsahovat adresu IPv6 s vnořenou adresou IPv4, tedy buď IPv4-mapovanou nebo IPv4-kompatibilní adresu. V takovém pádu se vnořená adresa IPv4 extrahuje a nadále se pokračuje tak, jako by byla předložena struktura s adresou IPv4.

Výsledky převodu funkce getnameinfo zapisuje do bufferů, na které ukazují argumenty node a service. Tyto buffery musí mít dopředu přidělenu paměť a argumenty nodelen a servicelen udávají jejich délku. Je-li některý z argumentů node a service předán jako nulový ukazatel, příslušná hodnota se nevrací.

Poslední argument flags specifikuje následující příznaky, které mohou být opět kombinovány pomocí bitového OR:

NI_NOFQDN
Při nastavení tohoto příznaku se pro lokální uzel vrátí jen jeho samotné jméno, tedy bez domény. NI_NUMERICHOST
Při jeho nastavení se do *node zapíše řetězec reprezentující numerickou adresu. Pokud je to adresa IPv6, může v ní být vyznačena i zóna adresy (address scope), například „fe80::1%eth0“. NI_NUMERICSCOPE
Je-li nastaven tento příznak společně s předchozím, pak se zóna v textové reprezentaci adresy IPv6 zapíše jako číslo (např. index rozhraní) a nikoli symbolické jméno (např. eth0). NI_NUMERICSERV
Obdobně při nastavení tohoto příznaku se do *service zapíše řetězec s dekadickým číslem portu. NI_NAMEREQD
Pokud je příznak nastaven a doménové jméno uzlu nemůže být nalezeno, vrátí funkce getnameinfo nenulový chybový kód. NI_DGRAM
Nastavení tohoto příznaku naznačuje, že se jedná o nespojovanou službu (SOCK_DGRAM). Implicitně se předpokládá spojovaná služba (SOCK_STREAM).

Poslední úprava:: 25.07.2019 13:11