Altre correzioni alle funzioni, con riscrittura di ip_ntop per usare switch,
authorSimone Piccardi <piccardi@gnulinux.it>
Wed, 8 Dec 2004 00:52:39 +0000 (00:52 +0000)
committerSimone Piccardi <piccardi@gnulinux.it>
Wed, 8 Dec 2004 00:52:39 +0000 (00:52 +0000)
e aggiunta di commenti vari, reiniziato a descrivere le nuove versioni.

listati/TCP_echo_fifth.c
listati/mygetaddr.c
listati/sockbind.c
sockctrl.tex
sources/SockUtil.c
sources/TCP_echod.c

index 2f21902..d3e383c 100644 (file)
@@ -8,7 +8,6 @@ int main(int argc, char *argv[])
     ...
     /* call sockaddr to get a connected socket */
     if ( (sock = sockconn(argv[optind], "echo", 6, SOCK_STREAM)) < 0) {
-       if (errno) perror("Socket creation error");
        return 1;
     }
     /* do read/write operations */
index 2332b13..ecb81df 100644 (file)
@@ -20,7 +20,7 @@
                               buffer, sizeof(buffer));
        } else if (ptr->ai_family == PF_INET6) {      /* if IPv6 */
            printf("IPv6 address: \n");
-           addr6 = (struct sockaddr_in *) ptr->ai_addr;    /* address */
+           addr6 = (struct sockaddr_in6 *) ptr->ai_addr;   /* address */
            port = ntohs(addr6->sin6_port);                 /* port */
            string = inet_ntop(addr6->sin6_family, &addr6->sin6_addr, 
                               buffer, sizeof(buffer));
index ddf48d8..a131e80 100644 (file)
@@ -1,32 +1,45 @@
 int sockbind(char *host, char *serv, int prot, int type) 
 {
-    struct addrinfo hint, *addr;
+    struct addrinfo hint, *addr, *save;
     int res;
     int sock;
-    /* initialize hint structure */
+    char buf[INET6_ADDRSTRLEN];
     memset(&hint, 0, sizeof(struct addrinfo)); 
-    hint.ai_flags = AI_PASSIVE;          /* address for binding */
-    hint.ai_family = PF_UNSPEC;          /* generic address (IPv4 or IPv6) */
-    hint.ai_protocol = prot;             /* protocol */
-    hint.ai_socktype = type;             /* socket type */
+    hint.ai_flags = AI_PASSIVE;            /* address for binding */
+    hint.ai_family = PF_UNSPEC;            /* generic address (IPv4 or IPv6) */
+    hint.ai_protocol = prot;               /* protocol */
+    hint.ai_socktype = type;               /* socket type */
     res = getaddrinfo(host, serv, &hint, &addr);   /* calling getaddrinfo */
     if (res != 0) {                                /* on error exit */
-       printf("sockbind cannot resolve host %s, service %s, ", host, serv);
-       printf("protocol %d: %s\n", prot, gai_strerror(res));
+       fprintf(stderr, "sockbind: resolution failed:");
+       fprintf(stderr, " %s\n", gai_strerror(res));
+       errno = 0;                         /* clear errno */
        return -1;
     }
-    /* get a socket */
-    sock = socket(addr->ai_family, addr->ai_socktype, addr->ai_protocol);
-    if (sock < 0) {
-       printf("sockconn cannot create socket\n");
-       return sock;
+    save = addr;                           /* saving for freeaddrinfo */
+    while (addr != NULL) {                 /* loop on possible addresses */
+       sock = socket(addr->ai_family, addr->ai_socktype, addr->ai_protocol);
+       if (sock < 0) {                    /* on error */
+           if (addr->ai_next != NULL) {   /* if other addresses */
+               addr=addr->ai_next;        /* take next */
+               continue;                  /* restart cycle */
+           } else {                       /* else stop */
+               perror("sockbind: cannot create socket");
+               return sock;
+           }
+       }
+       if ( (res = bind(sock, addr->ai_addr, addr->ai_addrlen)) < 0) {
+           if (addr->ai_next != NULL) {   /* if other addresses */
+               addr=addr->ai_next;        /* take next */
+               close(sock);               /* close socket */
+               continue;                  /* restart cycle */
+           } else {                       /* else stop */
+               perror("sockbind: cannot connect");
+               close(sock);
+               return res;
+           }
+       } else break;                      /* ok, we are binded! */
     }
-    /* connect the socket */
-    res = bind(sock, addr->ai_addr, addr->ai_addrlen);
-    if (res < 0) {
-       printf("sockconn cannot bind socket\n");
-       return res;
-    }
-    freeaddrinfo(addr);         /* done, release memory */
+    freeaddrinfo(save);                    /* done, release memory */
     return sock;
 }
index 872f7b7..594e608 100644 (file)
@@ -1666,12 +1666,12 @@ finora, quello in cui si specifica nel client un indirizzo remoto per la
 connessione al server, e quello in cui si specifica nel server un indirizzo
 locale su cui porsi in ascolto.
 
-La prima funzione è allora \func{sockconn} che restituisce un socket connesso
-all'indirizzo ed al servizio specificati come argomenti; la funzione prende
-poi altri due argomenti aggiuntivi per indicare il protocollo da usare ed il
-tipo di socket. Il corpo della funzione è riportato in
-fig.~\ref{fig:sockconn_code}, ed il codice completo è nel file
-\file{sockconn.c} nei sorgenti allegati alla guida.
+La prima funzione della nostra intefaccia semplificata è \func{sockconn} che
+permette di ottenere un socket, connesso all'indirizzo ed al servizio
+specificati. Il corpo della funzione è riportato in
+fig.~\ref{fig:sockconn_code}, il codice completo è nel file \file{SockUtil.c}
+dei sorgenti allegati alla guida, che contiene varie funzioni di utilità per
+l'uso dei socket.
 
 \begin{figure}[!htb]
   \footnotesize \centering
@@ -1684,26 +1684,54 @@ fig.~\ref{fig:sockconn_code}, ed il codice completo 
 \end{figure}
 
 La funzione prende quattro argomenti, i primi due sono le stringhe che
-indicano il nome della macchina a cui collegarsi ed il relativo servizio;
-seguono il protocollo da usare (in forma numerica) ed il tipo di socket (al
-solito specificato con i valori illustrati in sez.~\ref{sec:sock_type}). La
-funzione ritorna il filde descriptor associato al socket in caso di successo,
-o -1 in caso di errore. Come si vede la funzione prevede anche ad alcune
-stampe in maniera diretta (il che la rende non utilizzabile all'interno di un
-demone).
+indicano il nome della macchina a cui collegarsi ed il relativo servizio su
+cui sarà effettuata la risoluzione; seguono il protocollo da usare (da
+specificare con il valore numerico di \file{/etc/protocols}) ed il tipo di
+socket (al solito specificato con i valori illustrati in
+sez.~\ref{sec:sock_type}).
+
+La funzione ritorna il valore del file descriptor (un numero positivo)
+associato al socket in caso di successo, o -1 in caso di errore. Per risolvere
+il problema di non poter passare indietro i valori di ritorno di
+\func{getaddrinfo} con i codici di errore\footnote{non si può avere nessuna
+  certezza che detti valori siano negativi, è questo è invece nessario per
+  evitare ogni possibile ambiguità nei confronti del valore di ritorno in caso
+  di successo.} si è provvisto a stampare direttamente all'interno della
+funzione i rispettivi errori, si potrà riconoscere questo caso ottenendo -1
+per il valore di ritorno, ma un valore nullo di \var{errno}.
 
 Una volta definite le variabili necessarie (\texttt{\small 3--5}) la funzione
-prima (\texttt{\small 7}) azzera il contenuto della struttura \var{hint} e poi
-provvede (\texttt{\small 8--10}) ad inizializzarne i valori necessari per la
-chiamata (\texttt{\small 11}) a \func{getaddrinfo}. Di quest'ultima si
-controlla (\texttt{\small 12}) il codice di ritorno, in modo da stampare
-(\texttt{\small 13--15}) un avviso di errore ed uscire in caso di errore.  Se
-la risoluzioen del nome ha successo si provvede (\texttt{\small 18--22}) ad
-aprire il socket, ed a connettersi ad esso (\texttt{\small 24--28}); in
-entrambi casi si gestisce il caso di errore. Infine prima di ritornare
-(\texttt{\small 30}) il file descriptor del socket si provvede (\texttt{\small
-  29}) a liberare le strutture \struct{addrinfo} allocate da
-\func{getaddrinfo}.
+prima (\texttt{\small 6}) azzera il contenuto della struttura \var{hint} e poi
+provvede (\texttt{\small 7--9}) ad inizializzarne i valori necessari per la
+chiamata (\texttt{\small 10}) a \func{getaddrinfo}. Di quest'ultima si
+controlla (\texttt{\small 12-16}) il codice di ritorno, in modo da stampare un
+avviso di errore, azzerare \var{errno} ed uscire in caso di errore.  Dato che
+ad una macchina possono corrispondere più indirizzi IP, e di tipo diverso (sia
+IPv4 che IPv6), ed il servizio può essere in ascolto soltanto su uno solo di
+questi, si provvede a tentare la connessione per ciascun indirizzo restituito
+all'interno di un ciclo (\texttt{\small 18-40}) di scansione della lista
+restituita da \func{getaddrinfo}, ma prima (\texttt{\small 17}) si salva il
+valore del puntatore per poterlo riutilizzare alla fine per disallocare la
+lista.
+
+Il ciclo viene ripetuto fintanto che si hanno indirizzi validi, ed inizia
+(\texttt{\small 19}) con l'apertura del socket; se questa fallisce si
+controlla (\texttt{\small 20}) se sono disponibili altri indirizzi, nel qual
+caso si passa al successivo (\texttt{\small 21}) ricominciando il ciclo
+(\texttt{\small 22}), se non ve ne sono si stampa l'errore ritornado
+immediatamente (\texttt{\small 24-27}). Se la creazione del socket ha avuto
+successo si procede (\texttt{\small 29}) con la connessione
+
+
+
+
+
+Se la risoluzione del nome ha successo si provvede (\texttt{\small 19--23}) ad
+aprire il socket, ed a connettersi ad esso (\texttt{\small 25--29}); in
+entrambi casi si gestisce il caso di errore, con una stampa ed il ritorno del
+valore -1. Infine prima di ritornare (\texttt{\small 31}) il valore del file
+descriptor del socket si provvede (\texttt{\small 30}) a liberare le strutture
+\struct{addrinfo} allocate da \func{getaddrinfo}.
 
 Si noti come la funzione si limiti ad usare i valori contenuti nella prima
 struttura \struct{addrinfo} ritornata da \func{getaddrinfo}, non eseguendo
@@ -1752,7 +1780,7 @@ una macchina.
 
 Se non si vuole che la funzione esegua \func{bind} su un indirizzo specifico,
 ma utilizzi l'indirizzo generico, occorrerà avere cura di passare un valore
-\const{NULL} come valore per l'argomento \var{host}, l'uso del valore
+\const{NULL} come valore per l'argomento \var{host}; l'uso del valore
 \const{AI\_PASSIVE} serve ad ottenere il valore generico nella rispettiva
 struttura degli indirizzi.
 
@@ -1766,7 +1794,6 @@ struttura degli indirizzi.
   \label{fig:sockbind_code}
 \end{figure}
 
-
 Come già detto la funzione è analoga a \func{sockconn} ed inizia azzerando ed
 inizializzando (\texttt{\small 6-11}) opportunamente la struttura \var{hint}
 con i valori ricevuti come argomenti, soltanto che in questo caso si è usata
@@ -1778,8 +1805,8 @@ sostituita (\texttt{\small 25--29}) la chiamata a \func{connect} con una
 chiamata a \func{bind}. La conclusione (\texttt{\small 30--31}) della funzione
 è identica. Si noti come anche in questo caso si siano inserite le stampe
 degli errori sullo standard output, nonostante la funzione possa essere
-invocata da un demone. In realtà questo non è un problema in quanto se la
-funzione non ha successo il programma deve uscire immediatamente prima di
+invocata da un demone. Nel nostro caso questo non è un problema in quanto se
+la funzione non ha successo il programma deve uscire immediatamente prima di
 essere posto in background, e può quindi scrivere gli errori direttamente
 sullo standard output.
 
index c861144..0bbaa79 100644 (file)
@@ -59,12 +59,18 @@ char *ip_ntop(struct addrinfo *addr, char *dst, socklen_t cnt)
     char * ret;
     struct sockaddr_in *ip4;
     struct sockaddr_in6 *ip6;
-    if (addr->ai_family == PF_INET) {
+    switch (addr->ai_family) {
+    case PF_INET:
        ip4 = (struct sockaddr_in *) addr->ai_addr;
        ret = inet_ntop(ip4->sin_family, &ip4->sin_addr, dst, cnt);
-    } else {
+       break;
+    case PF_INET6:
        ip6 = (struct sockaddr_in6 *) addr->ai_addr;
        ret = inet_ntop(ip6->sin6_family, &ip6->sin6_addr, dst, cnt);
+       break;
+    default:
+       ret = NULL;
+       errno = EAFNOSUPPORT;
     }
     return ret;
 }
@@ -86,45 +92,44 @@ int sockconn(char *host, char *serv, int prot, int type)
     int sock;
     /* initialize hint structure */
     memset(&hint, 0, sizeof(struct addrinfo)); 
-    hint.ai_family = PF_UNSPEC;          /* generic address (IPv4 or IPv6) */
-    hint.ai_protocol = prot;             /* protocol */
-    hint.ai_socktype = type;             /* socket type */
+    hint.ai_family = PF_UNSPEC;            /* generic address (IPv4 or IPv6) */
+    hint.ai_protocol = prot;               /* protocol */
+    hint.ai_socktype = type;               /* socket type */
     res = getaddrinfo(host, serv, &hint, &addr);    /* calling getaddrinfo */
     if (res != 0) {                                 /* on error exit */
        fprintf(stderr, "sockconn: resolution failed:");
 //     fprintf(stderr, "host %s, service %s, protocol %d", host, serv, prot);
        fprintf(stderr, " %s\n", gai_strerror(res));
-       errno = 0;                                 /* clear errno */
+       errno = 0;                         /* clear errno */
        return -1;
     }
-    /* loop on possible addresses */
     save = addr;
-    while (addr != NULL) {
+    while (addr != NULL) {                 /* loop on possible addresses */
        /* get a socket */
        sock = socket(addr->ai_family, addr->ai_socktype, addr->ai_protocol);
-       if (sock < 0) {
-           if (addr->ai_next != NULL) {
-               addr=addr->ai_next;
-               continue;
-           } else {
+       if (sock < 0) {                    /* on error */
+           if (addr->ai_next != NULL) {   /* if other addresses */
+               addr=addr->ai_next;        /* take next */
+               continue;                  /* restart cycle */
+           } else {                       /* else stop */
                perror("sockconn: cannot create socket");
                return sock;
            }
        }
        /* connect the socket */
        if ( (res = connect(sock, addr->ai_addr, addr->ai_addrlen) < 0)) {
-           if (addr->ai_next != NULL) {
-               addr=addr->ai_next;
-               close(sock);
-               continue;
-           } else {
+           if (addr->ai_next != NULL) {   /* if other addresses */
+               addr=addr->ai_next;        /* take next */
+               close(sock);               /* close socket */
+               continue;                  /* restart cycle */
+           } else {                       /* else stop */
                perror("sockconn: cannot connect");
                close(sock);
                return res;
            }
-       } else break;
+       } else break;                      /* ok, we are connected! */
     }
-    freeaddrinfo(save);         /* done, release memory */
+    freeaddrinfo(save);                    /* done, release memory */
     return sock;
 }
 /****************************************************************
@@ -146,47 +151,45 @@ int sockbind(char *host, char *serv, int prot, int type)
     char buf[INET6_ADDRSTRLEN];
     /* initialize hint structure */
     memset(&hint, 0, sizeof(struct addrinfo)); 
-    hint.ai_flags = AI_PASSIVE;          /* address for binding */
-    hint.ai_family = PF_UNSPEC;          /* generic address (IPv4 or IPv6) */
-    hint.ai_protocol = prot;             /* protocol */
-    hint.ai_socktype = type;             /* socket type */
+    hint.ai_flags = AI_PASSIVE;            /* address for binding */
+    hint.ai_family = PF_UNSPEC;            /* generic address (IPv4 or IPv6) */
+    hint.ai_protocol = prot;               /* protocol */
+    hint.ai_socktype = type;               /* socket type */
     res = getaddrinfo(host, serv, &hint, &addr);   /* calling getaddrinfo */
     if (res != 0) {                                /* on error exit */
        fprintf(stderr, "sockbind: resolution failed:");
 //     fprintf(stderr, "host %s, service %s, protocol %d", host, serv, prot);
        fprintf(stderr, " %s\n", gai_strerror(res));
-       errno = 0;                                 /* clear errno */
+       errno = 0;                         /* clear errno */
        return -1;
     }
-    /* loop on possible addresses */
-    save = addr;
-    while (addr != NULL) {
+    save = addr;                           /* saving for freeaddrinfo */
+    while (addr != NULL) {                 /* loop on possible addresses */
        /* get a socket */
        sock = socket(addr->ai_family, addr->ai_socktype, addr->ai_protocol);
-       if (sock < 0) {
-           if (addr->ai_next != NULL) {
-               addr=addr->ai_next;
-               continue;
-           } else {
-               perror("sockconn: cannot create socket");
+       if (sock < 0) {                    /* on error */
+           if (addr->ai_next != NULL) {   /* if other addresses */
+               addr=addr->ai_next;        /* take next */
+               continue;                  /* restart cycle */
+           } else {                       /* else stop */
+               perror("sockbind: cannot create socket");
                return sock;
            }
        }
        /* connect the socket */
        printf("Indirizzo %s\n", ip_ntop(addr, buf, sizeof(buf)));
-       ;
        if ( (res = bind(sock, addr->ai_addr, addr->ai_addrlen)) < 0) {
-           if (addr->ai_next != NULL) {
-               addr=addr->ai_next;
-               close(sock);
-               continue;
-           } else {
-               perror("sockconn: cannot connect");
+           if (addr->ai_next != NULL) {   /* if other addresses */
+               addr=addr->ai_next;        /* take next */
+               close(sock);               /* close socket */
+               continue;                  /* restart cycle */
+           } else {                       /* else stop */
+               perror("sockbind: cannot connect");
                close(sock);
                return res;
            }
-       } else break;
+       } else break;                      /* ok, we are binded! */
     }
-    freeaddrinfo(save);                            /* done, release memory */
+    freeaddrinfo(save);                    /* done, release memory */
     return sock;
 }
index f0eab95..6890492 100644 (file)
@@ -117,7 +117,6 @@ int main(int argc, char *argv[])
     }
     /* create and bind socket */
     if ( (list_fd = sockbind(NULL, "echo", 6, SOCK_STREAM)) < 0) {
-       if (errno) perror("Socket creation error");
        return 1;
     }   
     /* release privileges and go daemon */