From: Simone Piccardi Date: Wed, 8 Dec 2004 00:52:39 +0000 (+0000) Subject: Altre correzioni alle funzioni, con riscrittura di ip_ntop per usare switch, X-Git-Url: https://gapil.gnulinux.it/gitweb/?p=gapil.git;a=commitdiff_plain;h=866f3cf8f2fe10ea9c7b5c4b10faf37fce74b1aa Altre correzioni alle funzioni, con riscrittura di ip_ntop per usare switch, e aggiunta di commenti vari, reiniziato a descrivere le nuove versioni. --- diff --git a/listati/TCP_echo_fifth.c b/listati/TCP_echo_fifth.c index 2f21902..d3e383c 100644 --- a/listati/TCP_echo_fifth.c +++ b/listati/TCP_echo_fifth.c @@ -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 */ diff --git a/listati/mygetaddr.c b/listati/mygetaddr.c index 2332b13..ecb81df 100644 --- a/listati/mygetaddr.c +++ b/listati/mygetaddr.c @@ -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)); diff --git a/listati/sockbind.c b/listati/sockbind.c index ddf48d8..a131e80 100644 --- a/listati/sockbind.c +++ b/listati/sockbind.c @@ -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; } diff --git a/sockctrl.tex b/sockctrl.tex index 872f7b7..594e608 100644 --- a/sockctrl.tex +++ b/sockctrl.tex @@ -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. diff --git a/sources/SockUtil.c b/sources/SockUtil.c index c861144..0bbaa79 100644 --- a/sources/SockUtil.c +++ b/sources/SockUtil.c @@ -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; } diff --git a/sources/TCP_echod.c b/sources/TCP_echod.c index f0eab95..6890492 100644 --- a/sources/TCP_echod.c +++ b/sources/TCP_echod.c @@ -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 */