3. Utilisation de getaddrinfo()

L'objectif de cette section est de fournir un programme minimaliste qui affiche les adresses IP associées à un hôte ou à une interface réseau. Le code source de ce programme fait donc appel à la fonction getaddrinfo(). Cette fonction offre de nombreuses options mais son utilisation reste simple. Elle s'appuie sur l'enregistrement de type addrinfo. Ce type permet de constituer une liste chaînée des différentes adresses disponibles.

[Note] Note

Cette section suit la démarche proposée dans le livre Beej's Guide to Network Programming. Relativement au code proposé dans cet excellent guide, les modifications apportées ici sont marginales. Elles ont cependant une incidence sur l'organisation du code des sections suivantes.

Le programme showip est constitué d'un appel à la fonction getaddrinfo() suivi d'une boucle de parcours des enregistrements renseignés lors de l'appel. Cette boucle de parcours est reprise ensuite dans tous les autres programmes de ce document.

Appel à getaddrinfo()

  struct addrinfo hints, *res, *p;1

<snipped/>
  memset(&hints, 0, sizeof hints);2
  hints.ai_family = AF_UNSPEC; // IPv4 ou IPv6
  hints.ai_socktype = SOCK_STREAM; // Une seule famille de socket

  if ((status = getaddrinfo(argv[1]3, NULL, &hints, &res4)) != 0) {
    fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(status)5);
    return 2;
  }

1

On utilise 3 variables de type addrinfo. L'enregistrement hints sert à positionner des options avant l'appel à getaddrinfo() et les deux pointeurs servent respectivement à adresser le premier résultat et à parcourir la liste des enregistrements contenant les informations sur les adresses IP.

2

Les options choisies pour l'appel à getaddrinfo() permettent d'orienter les résultats. Ici, on souhaite obtenir les informations sur les adresses IPv4 (et|ou) IPv6. De plus, on est obligé de choisir une famille de socket même si nous n'avons pas l'intention de poursuivre avec l'ouverture d'un socket.

3

Les paramètres du programme showip sont passés directement en ligne de commande de façon classique ; argv[1] correspond à la chaîne de caractères décrivant l'hôte dont on souhaite obtenir la liste des adresses réseau.

4

Le pointeur *res indique l'adresse du premier enregistrement réponse au retour de l'appel à getaddrinfo().

5

En cas d'erreur sur l'interprétation de la chaîne fournie dans argv[1], la variable status reçoit une valeur interprétée par la fonction gai_strerror().

Boucle de parcours des enregistrements addrinfo

<snipped/>

  printf("IP addresses for %s:\n\n", argv[1]);

  p = res;1
  while (p != NULL) {2

    // Identification de l'adresse courante

<snipped/>

    // Adresse suivante
    p = p->ai_next;3
  }

1

Le pointeur p reçoit l'adresse du premier enregistrement suite à l'appel à la fonction getaddrinfo().

2

Si cette adresse vaut NULL, il n'y a plus d'enregistrement d'adresse dans la liste et on sort de la boucle.

3

On fait pointer p sur l'enregistrement d'adresse suivant.

Cette technique de parcours des enregistrements de type addrinfo est reprise dans les sections suivantes pour définir les conditions d'ouverture des «prises» réseau ou sockets.

Exemple d'utilisation du programme showip

L'exemple donné ci-dessous montre que deux adresses IP sont assoicées au nom d'hôte vm1.fake.domain.

$ ./showip.o vm1.fake.domain
IP addresses for vm1.fake.domain:

 IPv6: 2001:db8:feb2:10::11
 IPv4: 192.0.2.11

Code source complet du programme showip.c

/*
*  showip.c -- Affiche les adresses IP correspondant à un nom d'hôte donné en
*  ligne de commande
*
*  This code was first published in the
*  Beej's Guide to Network Programming
*  Credit has to be given back to beej(at)beej.us
*/
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <arpa/inet.h>

int main (int argc, char *argv[]) {

  struct addrinfo hints, *res, *p;
  void *addr;
  int status;
  char ipstr[INET6_ADDRSTRLEN], ipver;

  if (argc != 2) {
    fprintf(stderr, "usage: showip hostname\n");
    return 1;
  }

  memset(&hints, 0, sizeof hints);
  hints.ai_family = AF_UNSPEC; // IPv4 ou IPv6
  hints.ai_socktype = SOCK_STREAM; // Une seule famille de socket

  if ((status = getaddrinfo(argv[1], NULL, &hints, &res)) != 0) {
    fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(status));
    return 2;
  }

  printf("IP addresses for %s:\n\n", argv[1]);

  p = res;
  while (p != NULL) {

    // Identification de l'adresse courante
    if (p->ai_family == AF_INET) { // IPv4
      struct sockaddr_in *ipv4 = (struct sockaddr_in *)p->ai_addr;
      addr = &(ipv4->sin_addr);
      ipver = '4';
    }
    else { // IPv6
      struct sockaddr_in6 *ipv6 = (struct sockaddr_in6 *)p->ai_addr;
      addr = &(ipv6->sin6_addr);
      ipver = '6';
    }

    // Conversion de l'adresse IP en une chaîne de caractères
    inet_ntop(p->ai_family, addr, ipstr, sizeof ipstr);
    printf(" IPv%c: %s\n", ipver, ipstr);

    // Adresse suivante
    p = p->ai_next;
  }

  // Libération de la mémoire occupée par les enregistrements
  freeaddrinfo(res);

  return 0;
}