e aggiunta di commenti vari, reiniziato a descrivere le nuove versioni.
...
/* 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 */
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));
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;
}
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
\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
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.
\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
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.
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;
}
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;
}
/****************************************************************
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;
}
}
/* 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 */