Inoltre in genere quando si ha a che fare con i socket non esiste soltanto il
problema della risoluzione del nome che identifica la macchina, ma anche
quello del servizio a cui ci si vuole rivolgere. Per questo motivo con lo
-standard Posix 1003.1-2001 sono state indicate come deprecate le varie
+standard POSIX 1003.1-2001 sono state indicate come deprecate le varie
funzioni \func{gethostbyaddr}, \func{gethostbyname}, \var{getipnodebyname} e
\var{getipnodebyaddr} ed è stata introdotta una interfaccia completamente
nuova.
-La prima funzione di questa interfaccia è \funcd{getaddrinfo}, che combina le
+La prima funzione di questa interfaccia è \funcd{getaddrinfo},\footnote{la
+ funzione è definita, insieme a \func{getnameinfo} che vedremo più avanti,
+ nell'\href{http://www.ietf.org/rfc/rfc2553.txt} {RFC~2553}.} che combina le
funzionalità delle precedenti \func{getipnodebyname}, \func{getipnodebyaddr},
\func{getservbyname} e \func{getservbyport}, consentendo di ottenere
contemporaneamente sia la risoluzione di un indirizzo simbolico che del nome
-un servizio; il suo prototipo è:
+di un servizio; il suo prototipo è:
\begin{functions}
\headdecl{netdb.h}
\headdecl{sys/socket.h}
può anche specificare il nome di una rete invece che di una singola macchina.
Il secondo argomento, \param{service}, specifica invece il nome del servizio
che si intende risolvere. Per uno dei due argomenti si può anche usare il
-valore \const{NULL}, nel qual caso la risoluzione verrà effettuata utilizzando
-soltantoo sulla base del valore dell'altro.
+valore \const{NULL}, nel qual caso la risoluzione verrà effettuata soltanto
+sulla base del valore dell'altro.
Il terzo argomento, \param{hints}, deve essere invece un puntatore ad una
struttura \struct{addrinfo} usata per dare dei \textsl{suggerimenti} al
procedimento di risoluzione riguardo al protocollo o del tipo di socket che si
-intenderà utilizzare; la funzione infatti permette di effettuare ricerche
-generiche sugli indirizzi, usando sia IPv4 che IPv6, e richiedere risoluzioni
-sui nomi dei servizi indipendentemente dal protocollo (ad esempio TCP o UDP)
-che questi possono utilizzare.
+intenderà utilizzare; \func{getaddrinfo} infatti permette di effettuare
+ricerche generiche sugli indirizzi, usando sia IPv4 che IPv6, e richiedere
+risoluzioni sui nomi dei servizi indipendentemente dal protocollo (ad esempio
+TCP o UDP) che questi possono utilizzare.
\begin{figure}[!htb]
\footnotesize \centering
per il campo \var{ai\_addrlen}, qui viene usata quanto previsto dallo
standard POSIX, in cui viene utilizzato \type{socklen\_t}; i due tipi di
dati sono comunque equivalenti.} è riportata in
-fig.~\ref{fig:sock_addrinfo_struct} viene usata sia in ingresso, per passare
+fig.~\ref{fig:sock_addrinfo_struct}, viene usata sia in ingresso, per passare
dei valori di controllo alla funzione, che in uscita, per ricevere i
risultati. Il primo campo, \var{ai\_flags}, è una maschera binaria di bit che
permettono di controllare le varie modalità di risoluzione degli indirizzi,
\var{ai\_addrlen} indica la dimensione della struttura degli indirizzi
ottenuta come risultato, il cui contenuto sarà memorizzato nella struttura
\struct{sockaddr} posta all'indirizzo puntato dal campo \var{ai\_addr}. Il
-campo \var{ai\_canonname} è il puntatore alla stringa contenente il nome
+campo \var{ai\_canonname} è un puntatore alla stringa contenente il nome
canonico della macchina, ed infine, quando la funzione restituisce più di un
-risultato, \var{ai\_next} è il puntatore alla struttura \struct{addrinfo}
-successiva della lista.
+risultato, \var{ai\_next} è un puntatore alla successiva struttura
+\struct{addrinfo} della lista.
Ovviamente non è necessario dare dei suggerimenti in ingresso, ed usando
\const{NULL} come valore per l'argomento \param{hints} si possono compiere
degli analoghi argomenti della funzione \func{socket}; in particolare per
\var{ai\_family} si possono usare i valori di tab.~\ref{tab:net_pf_names} ma
sono presi in considerazione solo \const{PF\_INET} e \const{PF\_INET6}, mentre
-se non si vuole specificare questo nessuna famiglia di indirizzi si può usare
-il valore \const{PF\_UNSPEC}. Allo stesso modo per \var{ai\_socktype} si
-possono usare i valori illustrati sez.~\ref{sec:sock_type} per indicare per
-quale tipo di socket si vuole risolvere il servizio indicato, anche se i soli
+se non si vuole specificare nessuna famiglia di indirizzi si può usare il
+valore \const{PF\_UNSPEC}. Allo stesso modo per \var{ai\_socktype} si possono
+usare i valori illustrati in sez.~\ref{sec:sock_type} per indicare per quale
+tipo di socket si vuole risolvere il servizio indicato, anche se i soli
significativi sono \const{SOCK\_STREAM} e \const{SOCK\_DGRAM}; in questo caso,
se non si vuole effettuare nessuna risoluzione specifica, si potrà usare un
valore nullo.
Come ultimo argomento di \func{getaddrinfo} deve essere passato un puntatore
ad una variabile (di tipo puntatore ad una struttura \struct{addrinfo}) che
-verrà utilizzata dalla funzione per restituire (come \textit{value result
+verrà utilizzata dalla funzione per riportare (come \textit{value result
argument}) i propri risultati. La funzione infatti è rientrante, ed alloca
autonomamente tutta la memoria necessaria in cui verranno riportati i
-risultati della risoluzione. La funzione restituisce in \param{res} il
-puntatore alla prima di una \textit{linked list} di strutture di tipo
-\struct{addrinfo} contenenti tutte le informazioni ottenute.
+risultati della risoluzione. La funzione scriverà in \param{res} il puntatore
+iniziale ad una \textit{linked list} di strutture di tipo \struct{addrinfo}
+contenenti tutte le informazioni ottenute.
La funzione restituisce un valore nullo in caso di successo, o un codice in
caso di errore. I valori usati come codice di errore sono riportati in
altri tipi di socket. \\
\const{EAI\_ADDRFAMILY}& la rete richiesta non ha nessun indirizzo di rete
per la famiglia di indirizzi specificata. \\
- \const{EAI\_NODATA} & la . \\
+ \const{EAI\_NODATA} & la macchina specificata esiste, ma non ha nessun
+ indirizzo di rete definito. \\
\const{EAI\_MEMORY} & è stato impossibile allocare la memoria necessaria
alle operazioni. \\
\const{EAI\_FAIL} & il DNS ha restituito un errore di risoluzione
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.}
+esame. Le possibilità sono due, un indirizzo IPv4 o IPv6, 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
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
+passo (\texttt{\small 34}) è di 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.
struttura \struct{addrinfo}, perché una volta disallocati i dati con
\func{freeaddrinfo} questi non sarebbero più disponibili.
+Anche la nuova intefaccia definita da POSIX prevede una nuova funzione per
+eseguire la risoluzione inversa e determinare nomi di servizi e di dominio
+dati i rispettivi valori numerici. La funzione che sostituisce le varie
+\func{gethostbyname}, \func{geipnodebyname} e \func{getservname} è
+\funcd{getnameinfo}, ed il suo prototipo è:
+\begin{functions}
+ \headdecl{sys/socket.h}
+ \headdecl{netdb.h}
+
+ \funcdecl{int getnameinfo(const struct sockaddr *sa, socklen\_t salen, char
+ *host, size\_t hostlen, char *serv, size\_t servlen, int flags)}
+
+ Risolve il contenuto di una struttura degli indirizzi in maniera
+ indipendente dal protocollo.
+
+ \bodydesc{La funzione restituisce 0 in caso di successo e un codice di
+ errore diverso da zero altrimenti.}
+\end{functions}
+
+La principale caratteristica di \func{getnameinfo} è che la funzione è in
+grado di eseguire una risoluzione inversa in maniera indipendente dal
+protocollo; il suo primo argomento \param{sa} infatti è il puntatore ad una
+struttura degli indirizzi generica, che può contenere sia indirizzi IPv4 che
+IPv6, la cui dimensione deve comunque essere specificata con l'argomento
+\param{salen}.
+
+I risultati della funzione saranno restituiti nelle due stringhe puntate da
+\param{host} e \param{serv}, che dovranno essere state precedentemente
+allocate per una lunghezza massima che deve essere specificata con gli altri
+due argomenti \param{hostlen} e \param{servlen}. Si può, quando non si è
+interessati ad uno dei due, passare il valore \const{NULL} come argomento,
+così che la corrispondente informazione non verrà richiesta. Infine l'ultimo
+argomento \param{flags} è una maschera binaria i cui bit consentono di
+impostare le modalità con cui viene eseguita la ricerca, e deve essere
+specificato attraverso l'OR aritmetico dei valori illustrati in
+tab.~\ref{tab:getnameinfo_flags}.
+
+\begin{table}[!htb]
+ \centering
+ \footnotesize
+ \begin{tabular}[c]{|l|p{10cm}|}
+ \hline
+ \textbf{Costante} & \textbf{Significato} \\
+ \hline
+ \hline
+ \const{NI\_NOFQDN} & richiede che venga restituita solo il nome della
+ macchina all'interno del dominio al posto del
+ nome completo (FQDN).\\
+ \const{NI\_NUMERICHOST}& richiede che venga restituita la forma numerica
+ dell'indirizzo (questo succede sempre se il nome
+ non può essere ottenuto).\\
+ \const{NI\_NAMEREQD} & richiede la restituzione di un errore se il nome
+ non può essere risolto.\\
+ \const{NI\_NUMERICSERV}& richiede che il servizio venga restituito in
+ forma numerica (attraverso il numero di porta).\\
+ \const{NI\_DGRAM} & richiede che venga restituito il nome del
+ servizio su UDP invece che quello su TCP per quei
+ pichi servizi (porte 512-214) che soni diversi
+ nei due protocolli.\\
+ \hline
+ \end{tabular}
+ \caption{Costanti associate ai bit dell'argomento \param{flags} della
+ funzione \func{getnameinfo}.}
+ \label{tab:getnameinfo_flags}
+\end{table}
+
+La funzione ritorna zero in caso di successo, e scrive i propri risultati agli
+indirizzi indicati dagli argomenti \param{host} e \param{serv} come stringhe
+terminate dal carattere NUL, a meno che queste non debbano essere troncate
+qualora la loro dimensione ecceda quelle specificate dagli argomenti
+\param{hostlen} e \param{servlen}. Sono comunque definite le due costanti
+\const{NI\_MAXHOST} e \const{NI\_MAXSERV}\footnote{le due costanti sono
+ definite in \file{netdb.h} ed hanno rispettivamente il valore 1024 e 12.}
+che possono essere utilizzate come limiti massimi. In caso di errore viene
+restituito invece un codice che assume gli stessi valori illustrati in
+tab.~\ref{tab:addrinfo_error_code}.
+
+A questo punto possiamo fornire degli esempi di utilizzo diretto della nuova
+interfaccia, adottandola per rendere i nostri client
+
\section{Le opzioni dei socket}
\label{sec:TCP_sock_options}
possibile attivare alcune modalità diverse di funzionamento degli stessi
Dato che la maggior parte delle opzioni dei socket sono relative ai socket
-TCP, ed hanno poi significato analogo quando usate con altri socket, abbiamo
+ TCP, ed hanno poi significato analogo quando usate con altri socket, abbiamo
preferito trattare l'argomento in generale in questa sezione piuttosto che nel
capitolo dedicato alla trattazione generica dei socket.
--- /dev/null
+/* TCP_echo.c
+ *
+ * Copyright (C) 2001-2003 Simone Piccardi
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or (at
+ * your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+/****************************************************************
+ *
+ * Program TCP_echo.c
+ * Simple TCP client for echo service (port 7)
+ *
+ * Author: Simone Piccardi
+ * Jun. 2001
+ *
+ * Usage: echo -h give all info's
+ *
+ * $Id: TCP_echo.c,v 1.11 2003/10/20 22:44:16 piccardi Exp $
+ *
+ ****************************************************************/
+/*
+ * Include needed headers
+ */
+#include <sys/types.h> /* predefined types */
+#include <unistd.h> /* include unix standard library */
+#include <arpa/inet.h> /* IP addresses conversion utiliites */
+#include <sys/socket.h> /* socket library */
+#include <stdio.h> /* include standard I/O library */
+#include <errno.h> /* include error codes */
+#include <string.h> /* include erroro strings definitions */
+
+#include "macros.h"
+
+#define MAXLINE 256
+void usage(void);
+void ClientEcho(FILE * filein, int socket);
+void SigTERM_hand(int sig);
+
+/* Program begin */
+int main(int argc, char *argv[])
+{
+/*
+ * Variables definition
+ */
+ int sock, i;
+ int reset = 0;
+ struct sockaddr_in serv_add;
+ struct linger ling;
+ /*
+ * Input section: decode parameters passed in the calling
+ * Use getopt function
+ */
+ opterr = 0; /* don't want writing to stderr */
+ while ( (i = getopt(argc, argv, "hr")) != -1) {
+ switch (i) {
+ /*
+ * Handling options
+ */
+ case 'h':
+ printf("Wrong -h option use\n");
+ usage();
+ return(1);
+ break;
+ case 'r':
+ reset = 1;
+ break;
+ case '?': /* unrecognized options */
+ printf("Unrecognized options -%c\n",optopt);
+ usage();
+ default: /* should not reached */
+ usage();
+ }
+ }
+ /* ***********************************************************
+ *
+ * Options processing completed
+ *
+ * Main code beginning
+ *
+ * ***********************************************************/
+ /* create socket */
+ if ( (sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
+ perror("Socket creation error");
+ return 1;
+ }
+ /* initialize address */
+ memset((void *) &serv_add, 0, sizeof(serv_add)); /* clear server address */
+ serv_add.sin_family = AF_INET; /* address type is INET */
+ serv_add.sin_port = htons(7); /* echo port is 7 */
+ /* build address using inet_pton */
+ if ( (inet_pton(AF_INET, argv[optind], &serv_add.sin_addr)) <= 0) {
+ perror("Address creation error");
+ return 1;
+ }
+ /* extablish connection */
+ if (connect(sock, (struct sockaddr *)&serv_add, sizeof(serv_add)) < 0) {
+ perror("Connection error");
+ return 1;
+ }
+ /* check if resetting on close is required */
+ if (reset) {
+ printf("Setting reset on close \n");
+ ling.l_onoff = 1;
+ ling.l_linger = 0;
+ if (setsockopt(sock, SOL_SOCKET, SO_LINGER, &ling, sizeof(ling))) {
+ perror("Cannot set linger");
+ exit(1);
+ }
+ }
+ /* do read/write operations */
+ ClientEcho(stdin, sock);
+ /* normal exit */
+ return 0;
+}
+/*
+ * routine to print usage info and exit
+ */
+void usage(void) {
+ printf("Take daytime from a remote host \n");
+ printf("Usage:\n");
+ printf(" daytime [-h] [-v] [host in dotted decimal form] \n");
+// printf(" -v set verbosity on\n");
+ printf(" -r require reset on closing\n");
+ printf(" -h print this help\n");
+ exit(1);
+}
+
+void ClientEcho(FILE * filein, int socket)
+{
+ char sendbuff[MAXLINE+1], recvbuff[MAXLINE+1];
+ int nread, nwrite;
+ int maxfd;
+ fd_set fset;
+ int eof = 0;
+ /* initialize file descriptor set */
+ FD_ZERO(&fset);
+ maxfd = max(fileno(filein), socket) + 1;
+ while (1) {
+ FD_SET(socket, &fset); /* set for the socket */
+ if (eof == 0) {
+ FD_SET(fileno(filein), &fset); /* set for the standard input */
+ }
+ select(maxfd, &fset, NULL, NULL, NULL); /* wait for read ready */
+ if (FD_ISSET(fileno(filein), &fset)) { /* if ready on stdin */
+ if (fgets(sendbuff, MAXLINE, filein) == NULL) { /* if no input */
+ eof = 1; /* EOF on input */
+ shutdown(socket, SHUT_WR); /* close write half */
+ FD_CLR(fileno(filein), &fset); /* no more interest on stdin */
+ } else { /* else we have to write to socket */
+ nwrite = FullWrite(socket, sendbuff, strlen(sendbuff));
+ if (nwrite < 0) { /* on error stop */
+ printf("Errore in scrittura: %s", strerror(errno));
+ return;
+ }
+ }
+ }
+ if (FD_ISSET(socket, &fset)) { /* if ready on socket */
+ nread = read(socket, recvbuff, strlen(sendbuff)); /* do read */
+ if (nread < 0) { /* error condition, stop client */
+ printf("Errore in lettura: %s\n", strerror(errno));
+ return;
+ }
+ if (nread == 0) { /* server closed connection, stop */
+ if (eof == 1) {
+ return;
+ } else {
+ printf("EOF prematuro sul socket\n");
+ return;
+ }
+ }
+ recvbuff[nread] = 0; /* else read is ok, write on stdout */
+ if (fputs(recvbuff, stdout) == EOF) {
+ perror("Errore in scrittura su terminale");
+ return;
+ }
+ }
+ }
+}