Completata la parte su getaddrinfo e rifatto il programma di esempio per
authorSimone Piccardi <piccardi@gnulinux.it>
Sat, 13 Nov 2004 15:16:13 +0000 (15:16 +0000)
committerSimone Piccardi <piccardi@gnulinux.it>
Sat, 13 Nov 2004 15:16:13 +0000 (15:16 +0000)
consentire l'impostazione della ricerca per protocolli, tipi di socket e
famiglie di indirizzi

img/addrinfo_list.dia
listati/mygetaddr.c
sockctrl.tex
sources/mygetaddr.c

index fca8a26..fa5937f 100644 (file)
Binary files a/img/addrinfo_list.dia and b/img/addrinfo_list.dia differ
index 1499e2b..2332b13 100644 (file)
@@ -1,56 +1,37 @@
-    struct addrinfo hint;
-    struct addrinfo *res, *ptr;
-    int ret, port;
-    struct sockaddr_in *addr;
-    struct sockaddr_in6 *addr6;
-    char buffer[INET6_ADDRSTRLEN];
-    char *string;
-    ...
+    /* remaining argument check */
     if ((argc - optind) != 2) {
        printf("Wrong number of arguments %d\n", argc - optind);
         usage();
     }
-/* main body */
-    ret = getaddrinfo(argv[1], argv[2], NULL, &res);
+    /* main body */    
+    ret = getaddrinfo(argv[optind], argv[optind+1], &hint, &res); 
     if (ret != 0) {
        printf("Resolution error %s\n", gai_strerror(ret));
        exit(1);
     }
-    ptr = res;
-    printf("Canonical name %s\n", ptr->ai_canonname);
-    do {
-       if (ptr->ai_family == PF_INET) {
+    ptr = res;                                        /* init list pointer */
+    printf("Canonical name %s\n", ptr->ai_canonname); /* print cname */
+    while (ptr != NULL) {                             /* loop on list */
+       if (ptr->ai_family == PF_INET) {              /* if IPv4 */
            printf("IPv4 address: \n");
-           addr = (struct sockaddr_in *) ptr->ai_addr;
-           port = ntohs(addr->sin_port);
+           addr = (struct sockaddr_in *) ptr->ai_addr;     /* address */
+           port = ntohs(addr->sin_port);                   /* port */
            string = inet_ntop(addr->sin_family, &addr->sin_addr, 
                               buffer, sizeof(buffer));
-       } else if (ptr->ai_family == PF_INET6) {
+       } else if (ptr->ai_family == PF_INET6) {      /* if IPv6 */
            printf("IPv6 address: \n");
-           addr6 = (struct sockaddr_in *) ptr->ai_addr;
-           port = ntohs(addr6->sin6_port);
+           addr6 = (struct sockaddr_in *) ptr->ai_addr;    /* address */
+           port = ntohs(addr6->sin6_port);                 /* port */
            string = inet_ntop(addr6->sin6_family, &addr6->sin6_addr, 
                               buffer, sizeof(buffer));
-       } else {
+       } else {                                      /* else is an error */
            printf("Address family error\n");
            exit(1);
        }       
        printf("\tIndirizzo %s\n", string);
        printf("\tProtocollo %i\n", ptr->ai_protocol);
        printf("\tPorta %i\n", port);
-       
-
        ptr = ptr->ai_next;
-    } while (ptr != NULL);
+    }
     exit(0);
 }
-/*
- * routine to print usage info and exit
- */
-void usage(void) {
-    printf("Program mygethost: do an hostname resolution \n");
-    printf("Usage:\n");
-    printf("  mygethost [-h] hostname service\n");
-    printf("  -h   print this help\n");
-    exit(1);
-}
index 8f6e634..b866442 100644 (file)
@@ -1445,7 +1445,9 @@ Dato che ad un certo nome a dominio possono corrispondere pi
 e tipi di socket diversi, in generale, a meno di non aver eseguito una
 selezione specifica attraverso l'uso di \param{hints}, si otterrà una diversa
 struttura \struct{addrinfo} per ciascuna possibilità.  Ad esempio se si
-richiede la risoluzione del servizio \textit{echo} si avrà come risposta la
+richiede la risoluzione del servizio \textit{echo} per l'indirizzo
+\texttt{www.truelite.it}, e si imposta \const{AI\_CANONNAME} per avere anche
+la risoluzione del nome canonico, si avrà come risposta della funzione la
 lista illustrata in fig.~\ref{fig:sock_addrinfo_list}.
 
 \begin{figure}[!htb]
@@ -1457,10 +1459,12 @@ lista illustrata in fig.~\ref{fig:sock_addrinfo_list}.
 \end{figure}
 
 Come primo esempio di uso di \func{getaddrinfo} vediamo un programma
-elementare di interrogazione del resolver, basato questa funzione, il cui
-corpo principale è riportato in fig.. Il codice
-del programma è nel file \texttt{mygetaddr.c}, dei sorgenti allegati alla
-guida.
+elementare di interrogazione del resolver basato questa funzione, il cui corpo
+principale è riportato in fig.~\ref{fig:mygetaddr_example}. Il codice completo
+del programma, compresa la gestione delle opzioni in cui è gestita l'eventuale
+inizializzazione dell'argomento \var{hints} per restringere le ricerche su
+protocolli, tipi di socket o famiglie di indirizzi, è disponibile nel file
+\texttt{mygetaddr.c} dei sorgenti allegati alla guida.
 
 \begin{figure}[!htb]
   \footnotesize \centering
@@ -1469,15 +1473,87 @@ guida.
   \end{minipage}
   \normalsize
   \caption{Esempio di codice per la risoluzione di un indirizzo.}
-  \label{fig:mygethost_example}
+  \label{fig:mygetaddr_example}
 \end{figure}
 
-
+Il corpo principale inizia controllando (\texttt{\small 1--5}) il numero di
+argomenti passati, che devono essere sempre due, e corrispondere
+rispettivamente all'indirizzo ed al nome del servizio da risolvere. A questo
+segue la chiamata (\texttt{\small 7}) alla funzione \func{getaddrinfo}, ed il
+successivo controllo (\texttt{\small 8--11}) del suo corretto funzionamento,
+senza il quale si esce immediatamente stampando il relativo codice di errore.
+
+Se la funzione ha restituito un valore nullo il programma prosegue
+inizializzando (\texttt{\small 12}) il puntatore \var{ptr} che sarà usato nel
+sucessivo ciclo (\texttt{\small 14--35}) di scansione della lista delle
+strutture \struct{addrinfo} restituite dalla funzione. Prima di eseguire
+questa scansione (\texttt{\small 12}) viene stampato il valore del nome
+canonico che è presente solo nella prima struttura.
+
+La scansione viene ripetuta (\texttt{\small 14}) fintanto che si ha un
+puntatore valido. La selezione principale è fatta sul campo \var{ai\_family},
+che stabilisce a quale famiglia di indirizzi fa riferimento la struttura in
+esame. Le possibilità sono due, un indirizzo IPv4 (\texttt{\small 15}) o IPv6
+(\texttt{\small 21}), se nessuna delle due si verifica si provvede
+(\texttt{\small 27--30}) a stampare un messaggio di errore ed
+uscire.\footnote{questa eventualità non dovrebbe mai verificarsi, almeno
+  fintanto che la funzione \func{getaddrinfo} lavora correttamente.}
+
+Per ciascuno delle due possibili famiglie di indirizzi si estraggono le
+informazioni che poi verranno stampate alla fine del ciclo (\texttt{\small
+  31--34}). Il primo caso esaminato (\texttt{\small 15--21}) è quello degli
+indirizzi IPv4, nel qual caso prima se ne stampa l'indentificazione
+(\texttt{\small 16}) poi si provvede a ricavare la struttura degli indirizzi
+(\texttt{\small 17}) indirizzata dal campo \var{ai\_addr}, eseguendo un
+opportuno casting del puntatore per poter estrarre da questa la porta
+(\texttt{\small 18}) e poi l'indirizzo (\texttt{\small 19}) che verrà
+convertito con una chiamata ad \func{inet\_ntop}.
+
+La stessa operazione (\texttt{\small 21--27}) viene ripetuta per gli indirizzi
+IPv6, usando la rispettiva struttura degli indirizzi. Si noti anche come in
+entrambi i casi per la chiamata a \func{inet\_ntop} si sia dovuto passare il
+puntatore al campo contenente l'indirizzo IP nella struttura puntata dal campo
+\var{ai\_addr}.\footnote{il meccanismo è complesso a causa del fatto che al
+  contrario di IPv4, in cui l'indirizzo IP può essere espresso con un semplice
+  numero intero, in IPv6 questo deve essere necessariamente fornito come
+  struttura, e pertanto anche se nella struttura puntata da \var{ai\_addr}
+  sono presenti direttamente i valori finali, per l'uso con \func{inet\_ntop}
+  occorre comunque passare un puntatore agli stessi (ed il costrutto
+  \code{\&addr6->sin6\_addr} è corretto in quanto l'operatore \texttt{->} ha
+  on questo caso precedenza su \texttt{\&}).}
+
+Una volta estratte dalla struttura \struct{addrinfo} tutte le informazioni
+relative alla risoluzione richiesta e stampati i relativi valori, l'ultimo
+passo è (\texttt{\small 34}) estrarre da \var{ai\_next} l'indirizzo della
+eventuale successiva struttura presente nella lista e ripetere il ciclo, fin
+tanto che, completata la scansione, questo avrà un valore nullo e si potrà
+terminare (\texttt{\small 36}) il programma.
+
+Si tenga presente che \func{getaddrinfo} non garantisce nessun particolare
+ordinamento della lista delle strutture \struct{addrinfo} restituite, anche se
+usualmente i vari indirizzi IP (se ne è presente più di uno) sono forniti
+nello stesso ordine in cui vengono inviati dal server DNS. In particolare
+nulla garantisce che vengano forniti prima i dati relativi ai servizi di un
+determinato protocollo o tipo di socket, se ne sono presenti di diversi.  Se
+allora utilizziamo il nostro programma potremo verificare il risultato:
+\begin{verbatim}
+[piccardi@gont sources]$ ./mygetaddr -c  gapil.truelite.it echo
+Canonical name sources2.truelite.it
+IPv4 address:
+        Indirizzo 62.48.34.25
+        Protocollo 6
+        Porta 7
+IPv4 address:
+        Indirizzo 62.48.34.25
+        Protocollo 17
+        Porta 7
+\end{verbatim}
+%$
 
 Una volta estratti i risultati dalla \textit{linked list} puntata da
-\param{res} si dovrà avere cura di disallocare opportunamente tutta la
-memoria, per questo viene fornita l'apposita funzione \funcd{freeaddrinfo}, il
-cui prototipo è:
+\param{res} se questa non viene più utilizzata si dovrà avere cura di
+disallocare opportunamente tutta la memoria, per questo viene fornita
+l'apposita funzione \funcd{freeaddrinfo}, il cui prototipo è:
 \begin{functions}
   \headdecl{netdb.h} 
 
@@ -1494,7 +1570,12 @@ strutture per liberare tutta la memoria allocata. Dato che la funzione non ha
 valori di ritorno deve essere posta molta cura nel passare un valore valido
 per \param{res}.
 
-
+Si tenga presente infine che se si copiano i risultati da una delle strutture
+\struct{addrinfo} restituite nella lista indicizzata da \param{res}, occorre
+avere cura di eseguire una \index{\textit{deep~copy}}\textit{deep copy} in cui
+si copiano anche tutti i dati presenti agli indirizzi contenuti nella
+struttura \struct{addrinfo}, perché una volta disallocati i dati con
+\func{freeaddrinfo} questi non sarebbero più disponibili. 
 
 
 \section{Le opzioni dei socket}
index aa83006..ca01d74 100644 (file)
@@ -26,6 +26,7 @@
  *
  *****************************************************************************/
 #include <netdb.h>
+#include <string.h>
 #include <sys/types.h>
 #include <sys/stat.h>
 #include <stdlib.h>        /* C standard library */
@@ -49,28 +50,84 @@ int main(int argc, char *argv[])
 /* 
  * Variables definition
  */
-    int i;
+    int i,j;
     struct addrinfo hint;
     struct addrinfo *res, *ptr;
     int ret, port;
     struct sockaddr_in *addr;
     struct sockaddr_in6 *addr6;
-    char buffer[INET6_ADDRSTRLEN];
     char *string;
+    char buffer[INET6_ADDRSTRLEN];
+    char *protocols[] = { "tcp","udp", NULL };
+    int protval[] = { 6, 17 };
+    char *socktype[] = { "dgram","stream", NULL };
+    int sockval[] = { SOCK_DGRAM, SOCK_STREAM };
+    int debug = 0;
+    /*
+     * Init variables
+     */
+    memset(&hint, 0, sizeof(hint));
+    hint.ai_family = PF_UNSPEC;
     /*
      * Input section: decode command line parameters 
      * Use getopt function
      */
     opterr = 0;         /* don't want writing to stderr */
-    while ( (i = getopt(argc, argv, "h")) != -1) {
+    while ( (i = getopt(argc, argv, "hdcp:t:v:")) != -1) {
        switch (i) {
        /* 
         * Handling options 
         */ 
-       case 'h':                                            /* help option */
+       case 'h':                                             /* help option */
            printf("Wrong -h option use\n");
            usage();
-           return -1;
+           break;
+       case 'c':                      /* set canonical host name resolution */
+           hint.ai_flags = hint.ai_flags | AI_CANONNAME;
+           break;
+       case 'd':                      /* set canonical host name resolution */
+           debug = 1;
+           break;
+       case 'p':                                      /* set protocol value */
+           j = 0;
+           while ( (string = protocols[j]) != NULL ) {
+               if ( (strncmp(string, optarg, strlen(string)) == 0) ) {
+                   hint.ai_protocol = protval[j];
+                   break;
+               }
+               j++;
+           }
+           if (j>=2) {
+               printf("Wrong protocol, use 'tcp' or 'udp'\n\n");
+               usage();
+           }
+           break;
+       case 't':                                   /* set socket type value */
+           j = 0;
+           while ( (string = socktype[j]) != NULL ) {
+               if ( (strncmp(string, optarg, strlen(string)) == 0) ) {
+                   hint.ai_socktype = sockval[j];
+                   break;
+               }
+               j++;
+           }           
+           if (j>=2) {
+               printf("Wrong socket type, use 'dgram' or 'stream'\n\n");
+               usage();
+           }
+           break;
+       case 'v':                                        /* set address type */
+           j = strtol(optarg, NULL, 10);
+           if (j == 4) {
+               hint.ai_family = PF_INET;
+               break;
+           } 
+           if (j == 6) {
+               hint.ai_family = PF_INET6;
+               break;
+           }
+           printf("Wrong IP protocol version, use 4 o 6\n\n");
+           usage();
            break;
        case '?':                                    /* unrecognized options */
            printf("Unrecognized options -%c\n",optopt);
@@ -86,42 +143,50 @@ int main(int argc, char *argv[])
      *               Main code beginning
      * 
      * ***********************************************************/
+    /* if debug actived printout hint values*/
+    if (debug) {
+       printf("hint.ai_flag     = %d\n", hint.ai_flags);
+       printf("hint.ai_family   = %d\n", hint.ai_family);
+       printf("hint.ai_socktype = %d\n", hint.ai_socktype);
+       printf("hint.ai_protocol = %d\n", hint.ai_protocol);
+       printf("address = %s\n", argv[optind]);
+       printf("port    = %s\n", argv[optind+1]);
+    }
+    /* remaining argument check */
     if ((argc - optind) != 2) {
        printf("Wrong number of arguments %d\n", argc - optind);
         usage();
     }
-    
-    ret = getaddrinfo(argv[1], argv[2], NULL, &res);  /* main call */
-    if (ret != 0) {                                   /* on error exit */
+    /* main body */    
+    ret = getaddrinfo(argv[optind], argv[optind+1], &hint, &res); 
+    if (ret != 0) {
        printf("Resolution error %s\n", gai_strerror(ret));
        exit(1);
     }
-    ptr = res;                                        /* store pointer */
+    ptr = res;                                        /* init list pointer */
     printf("Canonical name %s\n", ptr->ai_canonname); /* print cname */
-    do {
-       if (ptr->ai_family == PF_INET) {
+    while (ptr != NULL) {                             /* loop on list */
+       if (ptr->ai_family == PF_INET) {              /* if IPv4 */
            printf("IPv4 address: \n");
-           addr = (struct sockaddr_in *) ptr->ai_addr;
-           port = ntohs(addr->sin_port);
+           addr = (struct sockaddr_in *) ptr->ai_addr;          /* address */
+           port = ntohs(addr->sin_port);                        /* port */
            string = inet_ntop(addr->sin_family, &addr->sin_addr, 
                               buffer, sizeof(buffer));
-       } else if (ptr->ai_family == PF_INET6) {
+       } else if (ptr->ai_family == PF_INET6) {      /* if IPv6 */
            printf("IPv6 address: \n");
-           addr6 = (struct sockaddr_in *) ptr->ai_addr;
-           port = ntohs(addr6->sin6_port);
+           addr6 = (struct sockaddr_in *) ptr->ai_addr;         /* address */
+           port = ntohs(addr6->sin6_port);                      /* port */
            string = inet_ntop(addr6->sin6_family, &addr6->sin6_addr, 
                               buffer, sizeof(buffer));
-       } else {
+       } else {                                      /* else is an error */
            printf("Address family error\n");
            exit(1);
        }       
        printf("\tIndirizzo %s\n", string);
        printf("\tProtocollo %i\n", ptr->ai_protocol);
        printf("\tPorta %i\n", port);
-       
-
        ptr = ptr->ai_next;
-    } while (ptr != NULL);
+    }
     exit(0);
 }
 /*
@@ -130,7 +195,11 @@ int main(int argc, char *argv[])
 void usage(void) {
     printf("Program mygethost: do an hostname resolution \n");
     printf("Usage:\n");
-    printf("  mygethost [-h] hostname service\n");
-    printf("  -h   print this help\n");
+    printf("mygethost [-h] [-p protocol] [-t socktype] hostname service\n");
+    printf("  -h              print this help\n");
+    printf("  -p udp,tcp      select a protocol\n");
+    printf("  -t dgram,stream select a socket type\n");
+    printf("  -v 4,6          select IPv4 or IPv6 \n");
+    printf("  -c              require canonical name resolution\n");
     exit(1);
 }