- 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);
-}
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]
\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
\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}
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}
*
*****************************************************************************/
#include <netdb.h>
+#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <stdlib.h> /* C standard library */
/*
* 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);
* 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);
}
/*
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);
}