GETADDRINFO(3)

NOM
getaddrinfo, freeaddrinfo, gai_strerror - Traduction d'adresses et de services réseau

SYNOPSIS
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>

int getaddrinfo(const char * node, const char * service,
                   const struct addrinfo *hints,
                   struct addrinfo ** res);

void freeaddrinfo(struct addrinfo * res);

char * gai_strerror(int errcode);

DESCRIPTION
La fonction getaddrinfo(3) combine les possibilités offertes par les fonctions getservbyname(3) et getservbyport(3) en une unique interface. La fonction getaddrinfo(3), adaptée aux contextes multithreads, crée une ou plusieurs structures d'adresses de socket, utilisables par bind(2) et connect(2) pour créer une socket de client ou de serveur.

La fonction getaddrinfo(3) n'est pas limitée aux structures d'adresses IPv4, elle permet également la création de structures IPv6 si le support en est disponible. Ces structures peuvent être utilisées directement par bind(2) ou connect(2), pour préparer une socket de client ou de serveur.

La structure addrinfo utilisée par cette fonction contient les membres suivants :

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

La fonction getaddrinfo(3) fait pointer res vers une liste de structures addrinfo nouvellement allouées, chaînées par leurs membres ai_next. La liste peut contenir plusieurs structures addrinfo pour plusieurs raisons, par exemple : l'hôte fonctionne en multi-home, ou le même service est disponible sur plusieurs types de sockets (une socket SOCK_STREAM et une socket SOCK_DGRAM par exemple).

Les membres ai_family, ai_socktype, et ai_protocol ont la même signification que leurs homologues de l'appel système socket(2). La fonction getaddrinfo(3) renvoie les adresses de sockets autant en IPv4 qu'en IPv6 (ai_family contiendra AF_INET ou AF_INET6).

L'argument hints permer de préciser le type préféré de socket ou de protocole. Un argument hints NULL indique que tout type d'adresse ou de protocole est acceptable. Si ce paramètre n'est pas NULL, il doit pointer sur une structure addrinfo dont les membres ai_family, ai_socktype, et ai_protocol indiquent les types de socket préférés. AF_UNSPEC dans le membre ai_family indique que toute famille de d'adresse (autant IPv4 que IPv6, par exemple) est acceptable. De même, un 0 dans les membres ai_socktype ou ai_protocol indique que tout type de socket ou de protocole est admis. Le membre ai_flags indique des options supplémentaires décrites plus bas. Divers attributs sont regroupés par un OU binaire. Tous les autres membres de l'argument hints doivent contenir 0 ou être des pointeurs NULL.

L'argument node ou l'argument service peuvent être NULL, mais pas les deux en même temps. node indique soit une adresse réseau en format numérique (décimal pointé pour l'IPv4, hexadécimal pour l'IPv6), soit un nom d'hôte, dont l'adresse réseau est alors résolue. Si hints.ai_flags contient l'attribut AI_NUMERICHOST, alors le paramètre node devra être une adresse réseau numérique. L'attribut AI_NUMERICHOST empêche toute tentative - éventuellement longue - de résolution de nom d'hôte.

La fonction getaddrinfo(3) crée une liste chaînée de structures addrinfo, une pour chaque adresse réseau soumise aux restrictions imposées par l'argument hints. Le membre ai_canonname de la première de ces structures addrinfo pointera vers le nom officiel de l'hôte si hints.ai_flags contient l'attribut AI_CANONNAME. Les membres ai_family, ai_socktype, et ai_protocol indiquent les paramètres de création de socket. Un pointeur vers l'adresse de la socket est placé dans le membre ai_addr, et la longueur de l'adresse de la socket, en octets, est inscrite dans le membre ai_addrlen de la structure.

Si l'argument node est NULL, l'adresse réseau dans chaque structure d'adresse est initialisée en fonction de l'attribut AI_PASSIVE que l'on inscrit dans hints.ai_flags. L'adresse réseau de chaque structure sera laissée vide si l'attribut AI_PASSIVE est présent. Ceci est utilisé par les serveurs qui désirent accepter les connexions des clients sur n'importe quelle interface réseau. L'adresse réseau sera remplie avec l'adresse de boucle loopback si l'attribut AI_PASSIVE n'est pas utilisé. Ceci est utilisé par les clients qui désirent se connecter sur un serveur fonctionnant sur le même hôte.

Si hints.ai_flags inclut l'attribut AI_ADDRCONFIG, les adresses IPv4 sont renvoyées dans la liste pointée par result seulement si le système local a au moins une adresse IPv4 configurée, et les adresses IPv6 ne sont retournées que si le système local a au moins une adresse IPv6 configurée.

Si hint.ai_flags spécifie l'attribut AI_V4MAPPED, et hints.ai_family été spécifié avec AF_INET6, et qu'aucune adresse IPv6 correspondante ne puisse être trouvée, les adresses IPv6 transformées en IPv4 sont renvoyées dans la liste pointée par result. Si les deux attributs AI_V4MAPPED et AI_ALL sont spécifiés dans hints.ai_family, les adresses IPv6 et les adresses IPv6 transformées en IPv4 sont renvoyées dans la liste pointée par result. AI_ALL est ignoré si AI_V4MAPPED n'est pas également spécifié.

L'argument service indique le numéro de port au sein de l'adresse réseau de la socket. Si service est NULL, le numéro de port restera non initialisé. Si l'attribut AI_NUMERICSERV est spécifié dans hints.ai_flags et si service n'est pas NULL, service doit pointer sur une chaîne contenant un numéro de port. Cet attribut est utilisé pour inhiber l'invocation du service de résolution de nom dans les cas où l'on sait qu'il n'est pas nécessaire.

La fonction freeaddrinfo(3) libère la mémoire qui a été allouée dynamiquement pour la liste chaînée res.

  Extensions de getaddrinfo() pour les noms de domaines internationalisés

À partir de la glibc 2.3.4, getaddrinfo() a été étendue pour sélectivement permettre que les noms d'hôtes entrant et sortant soient convertis de manière transparente vers ou depuis le format des noms de domaines internationalisés (« Internationalized Domain Name », IDN). (Voir la RFC 3490, Internationalizing Domain Names in Applications (IDNA)). Quatre nouveaux attributs ont été définis :

AI_IDN
    Si cet attribut est spécifié, le nom du noeud passé dans node est convertit, si nécessaire, au format IDN. L'encodage de la source est celui de la locale du système. Si le nom en entrée contient des caractères non ASCII, l'encodage IDN est utilisé. Les parties du nom du noeud (délimités par des points) qui contiennent des caractères non ASCII sont encodées en utilisant l'encodage compatible ASCII (« ASCII Compatible Encoding », ACE) avant d'être passées aux fonctions de résolution de noms.

AI_CANONIDN
    Après une résolution de nom réussie et si l'attribut AI_CANONNAME a été spécifié, getaddrinfo() retournera le nom canonique du noeud correspondant à la valeur de la structure addrinfo passée. La valeur de retour ezst une copie exacte de la valeur renvoyée par la fonction de résolution de nom. Si le nom est encodé en utilisant l'ACE, il contiendra le préfixe xn-- pour une ou plusieurs composantes du nom. Pour convertir ces composantes dans une forme lisible, l'attribut AI_CANONIDN peut être utilisé en plus de AI_CANONNAME. La chaîne résultante est encodée en utilisant l'encodage de la locale du système.

AI_IDN_ALLOW_UNASSIGNED, AI_IDN_USE_STD3_ASCII_RULES
    Positionner ces attributs permet d'activer respectivement les attributs IDNA_ALLOW_UNASSIGNED (permettre des caractères Unicode non assignés) et IDNA_USE_STD3_ASCII_RULES (vérifier la sortie pour être sûr que le nom d'hôte est conforme à STD3) utilisés dans la gestion de l'IDNA.

VALEUR RENVOYÉE
getaddrinfo(3) renvoie 0 si elle réussit, ou l'un des codes d'erreur non-nuls suivants :

EAI_ADDRFAMILY
    L'hôte indiqué n'a pas d'adresse dans la famille réseau demandée.

EAI_AGAIN
    Le serveur de noms a renvoyé une erreur temporaire. Réessayez plus tard.

EAI_BADFLAGS
    ai_flags contient des attributs invalides.

EAI_FAIL
    Le serveur de noms a renvoyé une erreur définitive.

EAI_FAMILY
    La famille d'adresse réclamée n'est pas supportée du tout.

EAI_MEMORY
    Pas assez de mémoire.

EAI_NODATA
    L'hôte existe mais n'a pas d'adresse réseau définie.

EAI_NONAME
    Le contenu du champ node ou de service est inconnu ; ou node et service sont simultanément NULL ; ou AI_NUMERICSERV était spécifié dans hints.ai_flags et service n'était pas une chaîne numéro de port numérique.

EAI_SERVICE
    Le service demandé n'est pas disponible pour le type de socket réclamé. Il peut être disponible pour un autre type de socket.

EAI_SOCKTYPE
    Le type de socket demandé n'est pas supporté.

EAI_SYSTEM
    Autre erreur système, voir errno pour plus de détail.

La fonction gai_strerror(3) traduit ces codes d'erreur en une chaîne de caractères compréhensible, utilisable pour rendre compte du problème.

CONFORMITÉ
POSIX.1-2001. La fonction getaddrinfo() est documentée dans la RFC 2553.

NOTES
AI_ADDRCONFIG, AI_ALL et AI_V4MAPPED sont disponibles depuis la glibc 2.3.3. AI_NUMERICSERV est disponible depuis la glibc 2.3.4.

EXEMPLE
Les programmes suivants montrent l'utilisation de getaddrinfo(), gai_strerror(), freeaddrinfo() et getnameinfo(3). Les programmes sont un serveur et un client qui renvoient en écho des datagrammes UDP. Voici le serveur :
#include <sys/types.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/socket.h>
#include <netdb.h>
#define BUF_SIZE 500
int
main(int argc, char *argv[])
{
    struct addrinfo hints;
    struct addrinfo *result, *rp;
    int sfd, s;
    struct sockaddr_storage peer_addr;
    socklen_t peer_addr_len;
    ssize_t nread;
    char buf[BUF_SIZE];
    if (argc != 2) {
        fprintf(stderr, "Usage: %s port\n", argv[0]);
        exit(EXIT_FAILURE);
    }
    memset(&hints, 0, sizeof(struct addrinfo));
    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() (or bind()) 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);
    }
    if (rp == NULL) {               /* No address succeeded */
        fprintf(stderr, "Could not bind\n");
        exit(EXIT_FAILURE);
    }
    freeaddrinfo(result);           /* No longer needed */
    /* Read datagrams and echo them back to sender */
    for (;;) {
        peer_addr_len = sizeof(struct sockaddr_storage);
        nread = recvfrom(sfd, buf, BUF_SIZE, 0,
                (struct sockaddr *) &peer_addr, &peer_addr_len);
        if (nread == -1)
            continue;               /* Ignore failed request */
        char host[NI_MAXHOST], service[NI_MAXSERV];
        s = getnameinfo((struct sockaddr *) &peer_addr,
                        peer_addr_len, host, NI_MAXHOST,
                        service, NI_MAXSERV, NI_NUMERICSERV);
       if (s == 0)
            printf("Received %ld bytes from %s:%s\n",
                    (long) nread, host, service);
        else
            fprintf(stderr, "getnameinfo: %s\n", gai_strerror(s));
        if (sendto(sfd, buf, nread, 0,
                    (struct sockaddr *) &peer_addr,
                    peer_addr_len) != nread)
            fprintf(stderr, "Error sending response\n");
    }
}
Voici le client :
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#define BUF_SIZE 500
int
main(int argc, char *argv[])
{
    struct addrinfo hints;
    struct addrinfo *result, *rp;
    int sfd, s, j;
    size_t len;
    ssize_t nread;
    char buf[BUF_SIZE];
    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(struct addrinfo));
    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);
    }
    if (rp == NULL) {               /* No address succeeded */
        fprintf(stderr, "Could not connect\n");
        exit(EXIT_FAILURE);
    }
    freeaddrinfo(result);           /* No longer needed */
    /* Send remaining command-line arguments as separate
       datagrams, and read responses from server */
    for (j = 3; j < argc; j++) {
        len = strlen(argv[j]) + 1;
                /* +1 for terminating null byte */
        if (len + 1 > BUF_SIZE) {
            fprintf(stderr,
                    "Ignoring long message in argument %d\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 %ld bytes: %s\n", (long) nread, buf);
    }
    exit(EXIT_SUCCESS);
}

VOIR AUSSI
getnameinfo(3)

TRADUCTION

Ce document est une traduction réalisée par Christophe Blaess <http://www.blaess.fr/christophe/> le 31 août 2000 et révisée le 23 décembre 2007.

L'équipe de traduction a fait le maximum pour réaliser une adaptation française de qualité. La version anglaise la plus à jour de ce document est toujours consultable via la commande : « LANG=C man 3 getaddrinfo ». N'hésitez pas à signaler à l'auteur ou au traducteur, selon le cas, toute erreur dans cette page de manuel.