X-Git-Url: https://gapil.gnulinux.it/gitweb/?p=gapil.git;a=blobdiff_plain;f=sockctrl.tex;h=4bc3bc4d90e2ef92526840e4501814b8b0dc208e;hp=a6eabcd8cbecd2a044d681b8967645505480269d;hb=66e83c068629844f84fe4a0d44b382f756c9ef32;hpb=a3e1645556c933f9c430c507db629e31027c4dd9 diff --git a/sockctrl.tex b/sockctrl.tex index a6eabcd..4bc3bc4 100644 --- a/sockctrl.tex +++ b/sockctrl.tex @@ -1,6 +1,6 @@ %% sockctrl.tex %% -%% Copyright (C) 2004 Simone Piccardi. Permission is granted to +%% Copyright (C) 2004-2005 Simone Piccardi. Permission is granted to %% copy, distribute and/or modify this document under the terms of the GNU Free %% Documentation License, Version 1.1 or any later version published by the %% Free Software Foundation; with the Invariant Sections being "Prefazione", @@ -34,6 +34,7 @@ porte o altre propriet \subsection{La struttura del \textit{resolver}} \label{sec:sock_resolver} +\index{\textit{resolver}|(} La risoluzione dei nomi è associata tradizionalmente al servizio del \textit{Domain Name Service} che permette di identificare le macchine su internet invece che per numero IP attraverso il relativo \textsl{nome a @@ -428,7 +429,7 @@ tab.~\ref{tab:DNS_address_class}.\footnote{esisteva in realt \const{C\_IN} & indirizzi internet, in pratica i soli utilizzati oggi.\\ \const{C\_HS} & indirizzi \textit{Hesiod}, utilizzati solo al MIT, oggi completamente estinti. \\ - \const{C\_CHAOS}& indizzi per la rete \textit{Chaosnet}, un'altra rete + \const{C\_CHAOS}& indirizzi per la rete \textit{Chaosnet}, un'altra rete sperimentale nata al MIT. \\ \const{C\_ANY} & indica un indirizzo di classe qualunque.\\ \hline @@ -607,6 +608,7 @@ Restituisce una stringa corrispondente ad un errore di risoluzione. \noindent che, come l'analoga \func{strerror}, restituisce una stringa con un messaggio di errore già formattato, corrispondente al codice passato come argomento (che si presume sia dato da \var{h\_errno}). +\index{\textit{resolver}|)} @@ -614,11 +616,12 @@ argomento (che si presume sia dato da \var{h\_errno}). \subsection{La risoluzione dei nomi a dominio} \label{sec:sock_name_services} -La principale funzionalità del \textit{resolver} resta quella di risolvere i -nomi a dominio in indirizzi IP, per cui non ci dedicheremo oltre alle funzioni -di richiesta generica ed esamineremo invece le funzioni a questo dedicate. La -prima funzione è \funcd{gethostbyname} il cui scopo è ottenere l'indirizzo di -una stazione noto il suo nome a dominio, il suo prototipo è: +La principale funzionalità del \index{\textit{resolver}}\textit{resolver} +resta quella di risolvere i nomi a dominio in indirizzi IP, per cui non ci +dedicheremo oltre alle funzioni di richiesta generica ed esamineremo invece le +funzioni a questo dedicate. La prima funzione è \funcd{gethostbyname} il cui +scopo è ottenere l'indirizzo di una stazione noto il suo nome a dominio, il +suo prototipo è: \begin{prototype}{netdb.h} {struct hostent *gethostbyname(const char *name)} @@ -678,10 +681,10 @@ Con l'uso di \func{gethostbyname} normalmente si ottengono solo gli indirizzi IPv4, se si vogliono ottenere degli indirizzi IPv6 occorrerà prima impostare l'opzione \const{RES\_USE\_INET6} nel campo \texttt{\_res.options} e poi chiamare \func{res\_init} (vedi sez.~\ref{sec:sock_resolver_functions}) per -modificare le opzioni del resolver; dato che questo non è molto comodo è stata -definita\footnote{questa è una estensione fornita dalle \acr{glibc}, - disponibile anche in altri sistemi unix-like.} un'altra funzione, -\funcd{gethostbyname2}, il cui prototipo è: +modificare le opzioni del \index{\textit{resolver}}\textit{resolver}; dato che +questo non è molto comodo è stata definita\footnote{questa è una estensione + fornita dalle \acr{glibc}, disponibile anche in altri sistemi unix-like.} +un'altra funzione, \funcd{gethostbyname2}, il cui prototipo è: \begin{functions} \headdecl{netdb.h} \headdecl{sys/socket.h} @@ -705,20 +708,20 @@ suoi risultati. \begin{figure}[!htb] \footnotesize \centering \begin{minipage}[c]{15cm} - \includecodesample{listati/myhost.c} + \includecodesample{listati/mygethost.c} \end{minipage} \normalsize \caption{Esempio di codice per la risoluzione di un indirizzo.} - \label{fig:myhost_example} + \label{fig:mygethost_example} \end{figure} Vediamo allora un primo esempio dell'uso delle funzioni di risoluzione, in -fig.~\ref{fig:myhost_example} è riportato un estratto del codice di un -programma che esegue una semplice interrogazione al \textit{resolver} usando -\func{gethostbyname} e poi ne stampa a video i risultati. Al solito il -sorgente completo, che comprende il trattamento delle opzioni ed una funzione -per stampare un messaggio di aiuto, è nel file \texttt{myhost.c} dei sorgenti -allegati alla guida. +fig.~\ref{fig:mygethost_example} è riportato un estratto del codice di un +programma che esegue una semplice interrogazione al +\index{\textit{resolver}}\textit{resolver} usando \func{gethostbyname} e poi +ne stampa a video i risultati. Al solito il sorgente completo, che comprende +il trattamento delle opzioni ed una funzione per stampare un messaggio di +aiuto, è nel file \texttt{mygethost.c} dei sorgenti allegati alla guida. Il programma richiede un solo argomento che specifichi il nome da cercare, senza il quale (\texttt{\small 12--15}) esce con un errore. Dopo di che @@ -1146,7 +1149,7 @@ aperto riporta la posizione di lettura alla prima voce del file, in questo modo si può far ricominciare da capo una lettura sequenziale. L'argomento \param{stayopen}, se diverso da zero, fa sì che il file resti aperto anche fra diverse chiamate a \func{getservbyname} e \func{getservbyaddr}.\footnote{di - default dopo una chiamata a queste funzioni il file viene chiuso, cosicchè + default dopo una chiamata a queste funzioni il file viene chiuso, cosicché una successiva chiamata a \func{getservent} riparte dall'inizio.} La terza funzione, \funcd{endservent}, provvede semplicemente a chiudere il file. @@ -1199,16 +1202,18 @@ forniscono una interfaccia sufficientemente generica. 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} @@ -1230,16 +1235,16 @@ forma \textit{dotted-decimal} per IPv4 o in formato esadecimale per IPv6. Si 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 @@ -1257,7 +1262,7 @@ La struttura \struct{addrinfo}, la cui definizione\footnote{la definizione 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, @@ -1272,10 +1277,10 @@ Tutti i campi seguenti vengono usati soltanto in uscita; il campo \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 @@ -1288,10 +1293,10 @@ I due campi \var{ai\_family} e \var{ai\_socktype} prendono gli stessi valori 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. @@ -1328,7 +1333,7 @@ bit della maschera. \const{IN6ADDR\_ANY\_INIT} per IPv6), altrimenti verrà usato l'indirizzo dell'interfaccia di \textit{loopback}. Se invece non è impostato gli - indirizzi verrano restituiti in formato adatto ad + indirizzi verranno restituiti in formato adatto ad una chiamata a \func{connect} o \func{sendto}.\\ \const{AI\_CANONNAME} & richiede la restituzione del nome canonico della macchina, che verrà salvato in una stringa il cui @@ -1359,12 +1364,12 @@ bit della maschera. 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 @@ -1396,7 +1401,8 @@ corrispondente 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 @@ -1419,14 +1425,36 @@ corrispondente \label{tab:addrinfo_error_code} \end{table} +Come per i codici di errore di \func{gethostbyname} anche in questo caso è +fornita una apposita funzione, analoga di \func{strerror}, che consente di +utilizzarli direttamente per stampare a video un messaggio esplicativo; la +funzione è \func{gai\_strerror} ed il suo prototipo è: +\begin{functions} + \headdecl{netdb.h} + + \funcdecl{const char *gai\_strerror(int errcode)} + + Fornisce il messaggio corrispondente ad un errore di \func{getaddrinfo}. -Dato che più di un indirizzo possono corrispondere ad un certo nome a dominio, -e più di un servizio possono essere associati ad un certo nome (in genere su -due protocolli diversi) è assolutamente normale ricevere come risposta una -lista di più strutture \struct{addrinfo}, a meno di non avere usato una -selezione specifica. Ad esempio se si richiede la risoluzione del servizio -\textit{echo} si avrà come risposta la lista illustrata in -fig.~\ref{fig:sock_addrinfo_list}. + \bodydesc{La funzione restituisce il puntatore alla stringa contenente il + messaggio di errore.} +\end{functions} + +La funzione restituisce un puntatore alla stringa contenente il messaggio +corrispondente dal codice di errore \param{errcode} ottenuto come valore di +ritorno di \func{getaddrinfo}. La stringa è allocata staticamente, ma essendo +costante, ed accessibile in sola lettura, questo non comporta nessun problema +di rientranza della funzione. + +Dato che ad un certo nome a dominio possono corrispondere più indirizzi IP +(sia IPv4 che IPv6), e che un certo servizio può essere fornito su protocolli +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} 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] \centering @@ -1436,11 +1464,102 @@ fig.~\ref{fig:sock_addrinfo_list}. \label{fig:sock_addrinfo_list} \end{figure} +Come primo esempio di uso di \func{getaddrinfo} vediamo un programma +elementare di interrogazione del \index{\textit{resolver}}\textit{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 + \begin{minipage}[c]{15cm} + \includecodesample{listati/mygetaddr.c} + \end{minipage} + \normalsize + \caption{Esempio di codice per la risoluzione di un indirizzo.} + \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 +successivo 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 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 + 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}) è 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. + +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} @@ -1457,23 +1576,807 @@ strutture per liberare tutta la memoria allocata. Dato che la funzione non ha 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. + +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{in Linux 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 della nuova +interfaccia, adottandola per le precedenti implementazioni del client e del +server per il servizio \textit{echo}; dato che l'uso delle funzioni appena +illustrate (in particolare di \func{getaddrinfo}) è piuttosto complesso, +essendo necessaria anche una impostazione diretta dei campi dell'argomento +\param{hints}, provvederemo una interfaccia semplificata per i due casi visti +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 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 + \begin{minipage}[c]{15cm} + \includecodesample{listati/sockconn.c} + \end{minipage} + \normalsize + \caption{Il codice della funzione \func{sockconn}.} + \label{fig:sockconn_code} +\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 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 +associato al socket (un numero positivo) 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} contenenti i relativi 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 sono stampati i +messaggi d'errore direttamente nella funzione. + +Una volta definite le variabili necessarie (\texttt{\small 3--5}) la funzione +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), mantre 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 (\texttt{\small 18}) 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}) e si +riprende (\texttt{\small 22}) il ciclo da capo; se non ve ne sono si stampa +l'errore ritornando immediatamente (\texttt{\small 24-27}). Quando la +creazione del socket ha avuto successo si procede (\texttt{\small 29}) +direttamente con la connessione, di nuovo in caso di fallimento viene ripetuto +(\texttt{\small 30--38}) il controllo se vi sono o no altri indirizzi da +provare nella stessa modalità fatta in precedenza, aggiungendovi però in +entrambi i casi (\texttt{\small 32} e (\texttt{\small 36}) la chiusura del +socket precedentemente aperto, che non è più utilizzabile. + +Se la connessione ha avuto successo invece si termina (\texttt{\small 39}) +direttamente il ciclo, e 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} utilizzando il +valore del relativo puntatore precedentemente (\texttt{\small 17}) salvato. +Si noti come per la funzione sia del tutto irrilevante se la struttura +ritornata contiene indirizzi IPv6 o IPv4, in quanto si fa uso direttamente dei +dati relativi alle strutture degli indirizzi di \struct{addrinfo} che sono +\textsl{opachi} rispetto all'uso della funzione \func{connect}. + +\begin{figure}[!htb] + \footnotesize \centering + \begin{minipage}[c]{15cm} + \includecodesample{listati/TCP_echo_fifth.c} + \end{minipage} + \normalsize + \caption{Il nuovo codice per la connessione del client \textit{echo}.} + \label{fig:TCP_echo_fifth} +\end{figure} + +Per usare questa funzione possiamo allora modificare ulteriormente il nostro +programma client per il servizio \textit{echo}; in questo caso rispetto al +codice usato finora per collegarsi (vedi fig.~\ref{fig:TCP_echo_client_1}) +avremo una semplificazione per cui il corpo principale del nostro client +diventerà quello illustrato in fig.~\ref{fig:TCP_echo_fifth}, in cui le +chiamate a \func{socket}, \func{inet\_pton} e \func{connect} sono sostituite +da una singola chiamata a \func{sockconn}. Inoltre il nuovo client (il cui +codice completo è nel file \file{TCP\_echo\_fifth.c} dei sorgenti allegati) +consente di utilizzare come argomento del programma un nome a dominio al posto +dell'indirizzo numerico, e può utilizzare sia indirizzi IPv4 che IPv6. + +\begin{figure}[!htb] + \footnotesize \centering + \begin{minipage}[c]{15cm} + \includecodesample{listati/sockbind.c} + \end{minipage} + \normalsize + \caption{Il codice della funzione \func{sockbind}.} + \label{fig:sockbind_code} +\end{figure} + +La seconda funzione di ausilio è \func{sockbind}, il cui corpo principale è +riportato in fig.~\ref{fig:sockbind_code} (al solito il sorgente completo è +nel file \file{sockbind.c} dei sorgenti allegati alla guida). Come si può +notare la funzione è del tutto analoga alla precedente \func{sockconn}, e +prende gli stessi argomenti, però invece di eseguire una connessione con +\func{connect} si limita a chiamare \func{bind} per collegare il socket ad una +porta. + +Dato che la funzione è pensata per essere utilizzata da un server ci si può +chiedere a quale scopo mantenere l'argomento \param{host} quando l'indirizzo +di questo è usualmente noto. Si ricordi però quanto detto in +sez.~\ref{sec:TCP_func_bind}, relativamente al significato della scelta di un +indirizzo specifico come argomento di \func{bind}, che consente di porre il +server in ascolto su uno solo dei possibili diversi indirizzi presenti su di +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{AI\_PASSIVE} serve ad ottenere il valore generico nella +rispettiva struttura degli indirizzi. + +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 +(\texttt{\small 8}) una impostazione specifica dei flag di \var{hint} usando +\const{AI\_PASSIVE} per indicare che il socket sarà usato per una apertura +passiva. Per il resto la chiamata (\texttt{\small 12-18}) a \func{getaddrinfo} +e ed il ciclo principale (\texttt{\small 20--42}) sono identici, solo che si è +sostituita (\texttt{\small 31}) la chiamata a \func{connect} con una chiamata +a \func{bind}. Anche la conclusione (\texttt{\small 43--44}) della funzione è +identica. + +Si noti come anche in questo caso si siano inserite le stampe degli errori +sullo standard error, nonostante la funzione possa essere 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 +error. + +\begin{figure}[!htb] + \footnotesize \centering + \begin{minipage}[c]{15cm} + \includecodesample{listati/TCP_echod_third.c} + \end{minipage} + \normalsize + \caption{Nuovo codice per l'apertura passiva del server \textit{echo}.} + \label{fig:TCP_echod_third} +\end{figure} + +Con l'uso di questa funzione si può modificare anche il codice del nostro +server \textit{echo}, che rispetto a quanto illustrato nella versione iniziale +di fig.~\ref{fig:TCP_echo_server_first_code} viene modificato nella forma +riportata in fig.~\ref{fig:TCP_echod_third}. In questo caso il socket su cui +porsi in ascolto viene ottenuto (\texttt{\small 15--18}) da \func{sockbind} +che si cura anche della eventuale risoluzione di un indirizzo specifico sul +quale si voglia far ascoltare il server. \section{Le opzioni dei socket} -\label{sec:TCP_sock_options} +\label{sec:sock_options} + +Benché dal punto di vista del loro uso come canali di trasmissione di dati i +socket siano trattati allo stesso modo dei file, ed acceduti tramite i file +descriptor, la normale interfaccia usata per la gestione dei file non è +sufficiente a poterne controllare tutte le caratteristiche, che variano tra +l'altro a seconda del loro tipo (e della relativa forma di comunicazione +sottostante). In questa sezione vedremo allora quali sono le funzioni dedicate +alla gestione delle caratteristiche specifiche dei vari tipi di socket, le +cosiddette \textit{socket options}. + + +\subsection{Le funzioni \func{setsockopt} e \func{getsockopt}} +\label{sec:sock_setsockopt} + +Le varie caratteristiche dei socket possono essere gestite attraverso l'uso di +due funzioni generiche che permettono rispettivamente di impostarle e di +recuperarne il valore corrente. La prima di queste due funzioni, quella usata +per impostare le \textit{socket options}, è \funcd{setsockopt}, ed il suo +prototipo è: +\begin{functions} + \headdecl{sys/socket.h} + \headdecl{sys/types.h} + + \funcdecl{int setsockopt(int sock, int level, int optname, const void + *optval, socklen\_t optlen)} + Imposta le opzioni di un socket. + + \bodydesc{La funzione restituisce 0 in caso di successo e -1 in caso di + errore, nel qual caso \var{errno} assumerà i valori: + \begin{errlist} + \item[\errcode{EBADF}] il file descriptor \param{sock} non è valido. + \item[\errcode{EFAULT}] l'indirizzo \param{optval} non è valido. + \item[\errcode{EINVAL}] il valore di \param{optlen} non è valido. + \item[\errcode{ENOPROTOOPT}] l'opzione scelta non esiste per il livello + indicato. + \item[\errcode{ENOTSOCK}] il file descriptor \param{sock} non corrisponde ad + un socket. + \end{errlist} +} +\end{functions} + + +Il primo argomento della funzione, \param{sock}, indica il socket su cui si +intende operare; per indicare l'opzione da impostare si devono usare i due +argomenti successivi, \param{level} e \param{optname}. Come abbiamo visto in +sez.~\ref{sec:net_protocols} i protocolli di rete sono strutturati su vari +livelli, ed l'interfaccia dei socket può usarne più di uno. Si avranno allora +funzionalità e caratteristiche diverse per ciascun protocollo usato da un +socket, e quindi saranno anche diverse le opzioni che si potranno impostare +per ciascun socket, a seconda del \textsl{livello} (trasporto, rete, ecc.) su +cui si vuole andare ad operare. + +Il valore di \param{level} seleziona allora il protocollo su cui vuole +intervenire, mentre \param{optname} permette di scegliere su quale delle +opzioni che sono definite per quel protocollo si vuole operare. In sostanza la +selezione di una specifica opzione viene fatta attraverso una coppia di valori +\param{level} e \param{optname} e chiaramente la funzione avrà successo +soltanto se il protocollo in questione prevede quella opzione ed è utilizzato +dal socket. Infine \param{level} prevede anche il valore speciale +\const{SOL\_SOCKET} usato per le opzioni generiche che sono disponibili per +qualunque tipo di socket. + +I valori usati per \param{level}, corrispondenti ad un dato protocollo usato +da un socket, sono quelli corrispondenti al valore numerico che identifica il +suddetto protocollo in \file{/etc/protocols}; dato che la leggibilità di un +programma non trarrebbe certo beneficio dall'uso diretto dei valori numerici, +più comunemente si indica il protocollo tramite le apposite costanti +\texttt{SOL\_*} riportate in tab.~\ref{tab:sock_option_levels}, dove si sono +riassunti i valori che possono essere usati per l'argomento +\param{level}.\footnote{la notazione in questo caso è, purtroppo, abbastanza + confusa: infatti in Linux il valore si può impostare sia usando le costanti + \texttt{SOL\_*}, che le analoghe \texttt{IPPROTO\_*} (citate anche da + Stevens in \cite{UNP1}); entrambe hanno gli stessi valori che sono + equivalenti ai numeri di protocollo di \file{/etc/protocols}, con una + eccesione specifica, che è quella del protocollo ICMP, per la quale non + esista una costante, il che è comprensibile dato che il suo valore, 1, è + quello che viene assegnato a \const{SOL\_SOCKET}.} + +\begin{table}[!htb] + \centering + \footnotesize + \begin{tabular}[c]{|l|l|} + \hline + \textbf{Livello} & \textbf{Significato} \\ + \hline + \hline + \const{SOL\_SOCKET}& opzioni generiche dei socket.\\ + \const{SOL\_IP} & opzioni specifiche per i socket che usano IPv4.\\ + \const{SOL\_TCP} & opzioni per i socket che usano TCP.\\ + \const{SOL\_IPV6} & opzioni specifiche per i socket che usano IPv6.\\ + \const{SOL\_ICMPV6}& opzioni specifiche per i socket che usano ICMPv6.\\ + \hline + \end{tabular} + \caption{Possibili valori dell'argomento \param{level} delle + funzioni \func{setsockopt} e \func{getsockopt}.} + \label{tab:sock_option_levels} +\end{table} + +Il quarto argomento, \param{optval} è un puntatore ad una zona di memoria che +contiene i dati che specificano il valore dell'opzione che si vuole passare al +socket, mentre l'ultimo argomento \param{optlen},\footnote{questo argomento è + in realtà sempre di tipo \ctyp{int}, come era nelle \acr{libc4} e + \acr{libc5}; l'uso di \ctyp{socklen\_t} è stato introdotto da POSIX (valgono + le stesse considerazioni per l'uso di questo tipo di dato fatte in + sez.~\ref{sec:TCP_func_accept}) ed adottato dalle \acr{glibc}.} è la +dimensione in byte dei dati presenti all'indirizzo indicato da \param{optval}. +Dato che il tipo di dati varia a seconda dell'opzione scelta, occorrerà +individuare qual'è quello che deve essere usato, ed utilizzare le opportune +variabili. + +La gran parte delle opzioni utilizzano per \param{optval} un valore intero, se +poi l'opzione esprime una condizione logica, il valore è sempre un intero, am +si dovrà usare un valore non nullo per abilitarla ed un valore nullo per +disabilitarla. Se invece l'opzione non prevede di dover ricevere nessun tipo +di valore si deve impostare \param{optval} a \const{NULL}. Un piccolo numero +di opzioni però usano dei tipi di dati peculiari, è questo il motivo per cui +\param{optval} è stato definito come puntatore generico. + +La seconda funzione usata per controllare le proprietà dei socket è +\funcd{getsockopt}, che serve a leggere i valori delle opzioni dei socket ed a +farsi restituire i dati relativi al loro funzionamento; il suo prototipo è: +\begin{functions} + \headdecl{sys/socket.h} + \headdecl{sys/types.h} + + \funcdecl{int getsockopt(int s, int level, int optname, void *optval, + socklen\_t *optlen)} Legge le opzioni di un socket. + + \bodydesc{La funzione restituisce 0 in caso di successo e -1 in caso di + errore, nel qual caso \var{errno} assumerà i valori: + \begin{errlist} + \item[\errcode{EBADF}] il file descriptor \param{sock} non è valido. + \item[\errcode{EFAULT}] l'indirizzo \param{optval} o quello di + \param{optlen} non è valido. + \item[\errcode{ENOPROTOOPT}] l'opzione scelta non esiste per il livello + indicato. + \item[\errcode{ENOTSOCK}] il file descriptor \param{sock} non corrisponde ad + un socket. + \end{errlist} +} +\end{functions} + +I primi tre argomenti sono identici ed hanno lo stesso significato di quelli +di \func{setsockopt}, anche se non è detto che tutte le opzioni siano definite +per entrambe le funzioni. In questo caso \param{optval} viene usato per +ricevere le informazioni ed indica l'indirizzo a cui andranno scritti i dati +letti dal socket, infine \param{optlen} diventa un puntatore ad una variabile +che viene usata come \textit{value result argument} per indicare, prima della +chiamata della funzione, la lunghezza del buffer allocato per \param{optval} e +per ricevere indietro, dopo la chiamata della funzione, la dimensione +effettiva dei dati scritti su di esso. Se la dimenzione del buffer allocato +per \param{optval} non è sufficiente si avrà un errore. + + + +\subsection{Le opzioni generiche} +\label{sec:sock_generic_options} + +Come accennato esiste un insieme generico di opzioni dei socket che possono +applicarsi a qualunque tipo di socket,\footnote{una descrizione di queste + opzioni è generalmente disponibile nella settima sezione delle pagine di + manuale, nel caso specifico la si può consultare con \texttt{man 7 socket}.} +indipendentemente da quale protocollo venga poi utilizzato. Se si vuole +operare su queste opzioni generiche il livello da utilizzare è +\const{SOL\_SOCKET}; si è riportato un elenco di queste opzioni in +tab.~\ref{tab:sock_opt_socklevel}. + + +\begin{table}[!htb] + \centering + \footnotesize + \begin{tabular}[c]{|l|c|c|c|l|l|} + \hline + \textbf{Opzione}&\texttt{get}&\texttt{set}&\textbf{flag}&\textbf{Tipo}& + \textbf{Descrizione}\\ + \hline + \hline + \const{SO\_KEEPALIVE}&$\bullet$&$\bullet$&$\bullet$&\texttt{int}& + controlla l'attività della connessione.\\ + \const{SO\_OOBINLINE}&$\bullet$&$\bullet$&$\bullet$&\texttt{int}& + lascia in linea i dati \textit{out-of-band}.\\ + \const{SO\_RCVLOWAT} &$\bullet$&$\bullet$&$\bullet$&\texttt{int}& + basso livello sul buffer di ricezione.\\ + \const{SO\_SNDLOWAT} &$\bullet$&$\bullet$& &\texttt{int}& + basso livello sul buffer di trasmissione.\\ + \const{SO\_RCVTIMEO} &$\bullet$&$\bullet$& &\texttt{timeval}& + timeout in ricezione.\\ + \const{SO\_SNDTIMEO} &$\bullet$&$\bullet$& &\texttt{timeval}& + timeout in trasmissione.\\ + \const{SO\_BSDCOMPAT}&$\bullet$&$\bullet$&$\bullet$&\texttt{int}& + abilita la compatibilità con BSD.\\ + \const{SO\_PASSCRED} &$\bullet$&$\bullet$&$\bullet$&\texttt{int}& + abilita la ricezione di credenziali.\\ + \const{SO\_PEERCRED} &$\bullet$& & &\texttt{ucred}& + restituisce le credenziali del processo remoto.\\ + \const{SO\_BINDTODEVICE}&$\bullet$&$\bullet$& &\texttt{char *}& + lega il socket ad un dispositivo.\\ + \const{SO\_DEBUG} &$\bullet$&$\bullet$&$\bullet$&\texttt{int}& + abilita il debugging sul socket.\\ + \const{SO\_REUSEADDR}&$\bullet$&$\bullet$&$\bullet$&\texttt{int}& + consente il riutilizzo di un indirizzo locale.\\ + \const{SO\_TYPE} &$\bullet$& & &\texttt{int}& + restituisce il tipo di socket.\\ + \const{SO\_ACCEPTCONN}&$\bullet$& & &\texttt{int}& + indica se il socket è in ascolto.\\ + \const{SO\_DONTROUTE}&$\bullet$&$\bullet$&$\bullet$&\texttt{int}& + non invia attraverso un gateway.\\ + \const{SO\_BROADCAST}&$\bullet$&$\bullet$&$\bullet$&\texttt{int}& + attiva o disattiva il \textit{broadcast}.\\ + \const{SO\_SNDBUF} &$\bullet$&$\bullet$& &\texttt{int}& + imposta dimensione del buffer di trasmissione.\\ + \const{SO\_RCVBUF} &$\bullet$&$\bullet$& &\texttt{int}& + imposta dimensione del buffer di ricezione.\\ + \const{SO\_LINGER} &$\bullet$&$\bullet$& &\texttt{linger}& + indugia nella chiusura con dati da spedire.\\ + \const{SO\_PRIORITY} &$\bullet$&$\bullet$& &\texttt{int}& + imposta la priorità del socket.\\ + \const{SO\_ERROR} &$\bullet$& & &\texttt{int}& + riceve e cancella gli errori pendenti.\\ + \hline + \end{tabular} + \caption{Le opzioni disponibili al livello \const{SOL\_SOCKET}.} + \label{tab:sock_opt_socklevel} +\end{table} + +La tabella elenca le costanti che identificano le singole opzioni da usare +come valore per \param{optname}; le due colonne seguenti indicano per quali +delle due funzioni (\func{getsockopt} o \func{setsockopt}) l'opzione è +disponibile, mentre la colonna successiva indica, quando di ha a che fare con +un valore di \param{optval} intero, se l'opzione è da considerare un numero o +un valore logico. Si è inoltre riportato sulla quinta colonna il tipo di dato +usato per \param{optval} ed una breve descrizione del significato delle +singole opzioni sulla sesta. + + +Dato che le descrizioni di tab.~\ref{tab:sock_opt_socklevel} sono estremamente +sommarie, vale la pena entrare in dettagli maggiori; questo ci consentirà +anche di trattare i vari casi particolari, dato che nonostante queste opzioni +siano indicate al livello generico, alcune di esse han senso solo per alcuni +tipi di socket. L'elenco dettagliato del significato di ciascuna di esse è +allora il seguente: +\begin{basedescript}{\desclabelwidth{3cm}\desclabelstyle{\nextlinelabel}} +\item[\const{SO\_KEEPALIVE}] una connessione può restare attiva se non viene + effettuato alcun traffico su di essa; in certi casi però può essere utile + controllarne lo stato per accorgersi di eventuali problemi. Per questo, se + si imposta questa opzione, è cura del kernel inviare degli appositi messaggi + sulla rete (detti appunto \textit{keep-alive}) per verificare se la + connessione è attiva. L'opzione funziona soltanto con socket che supportino + le connessioni (non ha senso per socket UDP ad esempio), ed utilizza per + \param{optval} un intero usato come valore logico. + + L'opzione si applica principalmente ai socket TCP. Con le impostazioni di + default (che sono riprese da BSD) Linux emette un messaggio di + \textit{keep-alive} verso l'altro capo della connessione se questa è rimasta + senza traffico per più di due ore. Se è tutto a posto il messaggio viene + ricevuto e verrà emesso un segmento ACK di risposta, alla cui ricezione + ripartirà un'altro ciclo di attesa per altre due ore di inattività; tutto + ciò viene effettuato dal kernel e le applicazioni non riceveranno nessun + dato. + + In caso di problemi invece si possono avere i due casi già illustrati in + sez.~\ref{sec:TCP_conn_crash} per il caso di terminazione prococe del + server: il primo è quello in cui la macchina remota è caduta ed è stata + riavviata, per cui dopo il riavvio la connessione non viene più + riconosciuta,\footnote{si ricordi che un normale riavvio non ha questo + effetto, in quanto si passa per la chiusura del processo, che chiude anche + il socket inviando un segmento FIN all'altro capo della connessione.} e si + otterrà come risposta un RST. In tal caso il socket viene chiuso dopo aver + impostato un errore \errcode{ECONNRESET}. + + Se invece non viene ricevuta nessuna risposta (indice che la macchina non è + più raggiungibile) l'emissione dei messaggi viene ripetuta ad intervalli di + 75 secondi ad un massimo di 9 volte\footnote{entrambi questi valori possono + essere opportunamente modificati con gli opportuni parametri illustrati in + sez.~\ref{sec:sock_sysctl}, si tenga presente che però questo vale a + livello di kernel ed i valori saranno applicati a \textsl{tutti} i + socket.} (per un totale di 11 minuti e 15 secondi) dopo di che, se non si + è ricevuta nessuna risposta, il socket viene chiuso dopo aver impostato un + errore di \errcode{ETIMEDOUT}. Se invece si riceve in risposta ad uno di + questi messaggi un pacchetto ICMP di destinazione irraggiungibile, verrà + restituito l'errore corrispondente. + + In generale questa opzione serve per individuare una caduta della + connessione,\footnote{il crash di un processo di nuovo comporta la chiusura + di tutti i file che aveva aperti e la relativa emissione degli opportuni + segmenti FIN nel caso dei socket.} e viene usata sui server per evitare di + mantenere impegnate le risorse dedicate a trattare delle connessioni in + realtà terminate. Abilitandola le connessioni effettivamente terminate + vengono chiuse ed una \func{select} potrà rilevare la conclusione delle + stesse e ricevere il relativo errore. Si tenga però presente che non si ha + la certezza assoluta che un errore di \errcode{ETIMEDOUT} corrisponda ad una + reale conclusione della connessione, il problema potrebbe essere dovuto ad + un problema di routing che perduri per un tempo maggiore di quello impiegato + nei vari tentativi di ritrasmissione del \textit{keep-alive}. + + +\item[\const{SO\_OOBINLINE}] se questa opzione viene abilitata i dati + \textit{out-of-band} vengono inviati direttamente nel flusso di dati del + socket (e sono quindi letti con una normale \func{read}) invece che restare + disponibili solo per l'accesso con l'uso del flag \const{MSG\_OOB} di + \func{recvmsg}. L'argomento è trattato in dettaglio in + sez.~\ref{sec:TCP_urgent_data}. L'opzione funziona soltanto con socket che + supportino i dati \textit{out-of-band} (non ha senso per socket UDP ad + esempio), ed utilizza per \param{optval} un intero usato come valore logico. + + +\item[\const{SO\_RCVLOWAT}] questa opzione imposta il valore che indica il + numero minimo di byte che devono essere presenti nel buffer di ricezione + perché il kernel passi i dati all'utente, restituendoli ad una \func{read} o + segnalando ad una \func{select} (vedi sez.~\ref{sec:TCP_sock_select}) che ci + sono dati in ingresso. L'opzione utilizza per \param{optval} un intero che + specifica il numero di byte, ma con Linux questo valore è sempre 1 e non può + essere cambiato; \func{getsockopt} leggerà questo valore mentre + \func{setsockopt} darà un errore di \errcode{ENOPROTOOPT}. + + +\item[\const{SO\_SNDLOWAT}] questa opzione imposta il valore che indica il + numero minimo di byte che devono essere presenti nel buffer di scrittura + perché il kernel li invii al protocollo successivo, consentendo ad una + \func{write} di ritornare o segnalando ad una \func{select} (vedi + sez.~\ref{sec:TCP_sock_select}) che è possibile eseguire una scrittura. + L'opzione utilizza per \param{optval} un intero che specifica il numero di + byte, come per la precedente \const{SO\_RCVLOWAT} con Linux questo valore è + sempre 1 e non può essere cambiato; \func{getsockopt} leggerà questo valore + mentre \func{setsockopt} darà un errore di \errcode{ENOPROTOOPT}. + + +\item[\const{SO\_RCVTIMEO}] l'opzione permette di impostare un tempo massimo + sulle operazioni di lettura da un socket, e prende per \param{optval} una + struttura di tipo \struct{timeval} (vedi fig.~\ref{fig:sys_timeval_struct}) + identica a quella usata con \func{select}. Con \func{getsockopt} si può + leggere il valore attuale, mentre con \func{setsockopt} si imposta il tempo + voluto, usando un valore nullo per \struct{timeval} il timeout viene + rimosso. + + Se l'opzione viene attivata tutte le volte che una delle funzioni di lettura + (\func{read}, \func{readv}, \func{recv}, \func{recvfrom} e \func{recvmsg}) + si blocca in attesa di dati per un tempo maggiore di quello impostato, essa + ritornerà un valore -1 e la variabile \var{errno} sarà impostata con un + errore di \errcode{EAGAIN} e \errcode{EWOULDBLOCK}, così come sarebbe + avvenuto se si fosse aperto il socket in modalità non bloccante.\footnote{in + teoria, se il numero di byte presenti nel buffer di ricezione fosse + inferiore a quello specificato da \const{SO\_RCVLOWAT}, l'effetto potrebbe + essere semplicemente quello di provocare l'uscita delle funzioni di + lettura restituendo il numero di byte fino ad allora ricevuti; dato che + con Linux questo valore è sempre 1 questo caso non esiste.} + + In genere questa opzione non è molto utilizzata se si ha a che fare con la + lettura dei dati, in quanto è sempre possibile usare una \func{select} che + consente di specificare un \textit{timeout}; l'uso di \func{select} non + consente però di impostare il timout per l'uso di \func{connect}, per avere + il quale si può ricorrere a questa opzione. + +% verificare con un programma di test + +\item[\const{SO\_SNDTIMEO}] l'opzione permette di impostare un tempo massimo + sulle operazioni di scrittura su un socket, ed usa gli stessi valori di + \const{SO\_RCVTIMEO}. In questo caso però si avrà un errore di + \errcode{EAGAIN} o \errcode{EWOULDBLOCK} per le funzioni di scrittura + \func{write}, \func{writev}, \func{send}, \func{sendfrom} e \func{sendmsg} + qualora queste restino bloccate per un tempo maggiore di quello specificato. + +\item[\const{SO\_BSDCOMPAT}] questa opzione abilita la compatibilità con il + comportamento di BSD (in particolare ne riproduce i bug). Attualmente è una + opzione usata solo per il protocollo UDP e ne è prevista la rimozione in + futuro. L'opzione utilizza per \param{optval} un intero usato come valore + logico. + + Quando viene abilitata gli errori riportati da messaggi ICMP per un socket + UDP non vengono passati al programma in user space. Con le versioni 2.0.x + del kernel erano anche abilitate altre opzioni per i socket raw, che sono + state rimosse con il passaggio al 2.2; è consigliato correggere i programmi + piuttosto che usare questa funzione. + +\item[\const{SO\_PASSCRED}] questa opzione abilita sui socket unix-domain la + ricezione dei messaggi di controllo di tipo \const{SCM\_CREDENTIALS}. Prende + come \param{optval} un intero usato come valore logico. + +\item[\const{SO\_PEERCRED}] questa opzione restituisce le credenziali del + processo remoto connesso al socket; l'opzione è disponibile solo per socket + unix-domain e può essere usata solo con \func{getsockopt}. Utilizza per + \param{optval} una apposita struttura \struct{ucred} (vedi + sez.~\ref{sec:unix_socket_xxx}). + +\item[\const{SO\_BINDTODEVICE}] questa opzione permette di \textsl{legare} il + socket ad una particolare interfaccia interfaccia, in modo che esso possa + ricevere ed inviare pacchetti solo su quella. L'opzione richiede per + \param{optval} il puntatore ad una stringa contenente il nome + dell'interfaccia (ad esempio \texttt{eth0}); se si utilizza una stringa + nulla o un valore nullo per \param{optlen} si rimuove un precedente + collegamento. + + Il nome della interfaccia deve essere specificato con una stringa terminata + da uno zero e di lunghezza massima pari a \const{IFNAMSIZ}; l'opzione è + effettiva solo per alcuni tipi di socket, ed in particolare per quelli della + famiglia \const{AF\_INET}; non è invece supportata per i \textit{packet + socket} (vedi sez.~\ref{cha:advanced_socket_xxx}). + + +\item[\const{SO\_DEBUG}] questa opzione abilita il debugging delle operazioni + dei socket; l'opzione utilizza per \param{optval} un intero usato come + valore logico, e può essere utilizzata solo da un processo con i privilegi + di amministratore (in particolare con la \textit{capability} + \const{CAP\_NET\_ADMIN}). L'opzione necessita inoltre dell'opportuno + supporto nel kernel;\footnote{deve cioè essere definita la macro di + preprocessore \texttt{SOCK\_DEBUGGING} nel file \file{include/net/sock.h} + dei sorgenti del kernel, questo è sempre vero nei kernel delle serie + superiori alla 2.3, per i kernel delle serie precedenti invece è + necessario aggiungere a mano detta definizione; è inoltre possibile + abilitare anche il tracciamento degli stati del TCP definendo la macro + \texttt{STATE\_TRACE} in \file{include/net/tcp.h}.} quando viene + abilitata una serie di messaggi con le informazioni di debug vengono inviati + direttamente al sistema del kernel log.\footnote{si tenga presente che il + comportamento è diverso da quanto avviene con BSD, dove l'opzione opera + solo sui socket TCP, causando la scrittura di tutti i pacchetti inviati + sulla rete su un buffer circolare che viene letto da un apposito + programma, \cmd{trpt}.} + +\item[\const{SO\_REUSEADDR}] questa opzione permette di eseguire la funzione + \func{bind} su indirizzi locali che siano già in uso; l'opzione utilizza per + \param{optval} un intero usato come valore logico. Questa opzione modifica + il comportamento normale dell'interfaccia dei socket che fa fallire + l'esecuzione della funzione \func{bind} con un errore di + \errcode{EADDRINUSE} quando l'indirizzo locale\footnote{più propriamente il + controllo viene eseguito sulla porta.} è già in uso da parte di un altro + socket. + + Come Stevens sottolinea in \cite{UNP1} si distinguono quattro casi per + l'utilizzo di questa opzione; il primo è quello in cui un server è terminato + ma esistono ancora dei processi figli che mantengono attiva almeno una + connessione remota che utilizza l'indirizzo locale; quando si riavvia il + server questo viene bloccato sulla chiamata a \func{bind} dato che la porta + è ancora utilizzata in una connessione esistente.\footnote{questa è una + delle domande più frequenti sui newsgroup dedicati allo sviluppo, in + quanto è piuttosto comune in questa situazione quando si sta sviluppando + un server che si ferma e si riavvia in continuazione.} Inoltre se si usa + il protocollo TCP questo può avvenire anche dopo che l'ultimo processo + figlio è terminato, dato che la connessione può restare attiva anche dopo la + chiusura del socket mantenendosi nello stato \texttt{TIME\_WAIT}. + + Usando \const{SO\_REUSEADDR} fra la chiamata a \func{socket} e quella a + \func{bind} si consente a quest'ultima di avere comunque successo anche se + la connessione è attiva (o nello stato texttt{TIME\_WAIT}). È bene però + ricordare (si riveda quanto detto in sez.~\ref{sec:TCP_time_wait}) che la + presenza dello stato \texttt{TIME\_WAIT} ha una ragione, ed infatti se si + usa questa opzione esiste sempre una probabilità, anche se estremamente + remota,\footnote{perché ciò avvenga infatti non solo devono coincidere gli + indirizzi IP e le porte degli estremi della nuova connessione, ma anche i + numeri di sequenza dei pacchetti, e questo è estremamente improbabile.} + che eventuali pacchetti rimasti intrappolati in una precedente connessione + possano finire fra quelli di una nuova. + + Il secondo caso in cui viene usata questa opzione è quando si ha una + macchina cui sono assegnati diversi numeri IP (o come suol dirsi + \textit{multi-homed}) e si vuole porre in ascolto sulla stessa porta un + programma diverso (o una istanza diversa dello stesso programma) per + indirizzi IP diversi. Si ricordi infatti che è sempre possibile indicare a + \func{bind} di collegarsi solo su di un indirizzo specifico; in tal caso se + un altro programma cerca di riutilizzare la stessa porta (anche specificando + un indirizzo diverso) otterrà un errore a meno di non aver preventivamente + impostato \const{SO\_REUSEADDR}. Usando questa opzione diventa anche + possibile eseguire \func{bind} sull'indirizzo generico, e questo permetterà + il collegamento per tutti gli indirizzi (di quelli presenti) per i quali la + porta risulti libera. Infine si tenga presente che con il protocollo TCP non + è mai possibile far partire server che eseguano \func{bind} sullo stesso + indirizzo e la stessa porta, cioè ottenere quello che viene chiamato un + \textit{completely duplicate binding}. + + Il terzo impiego è simile al precedente e prevede l'uso di \func{bind} + all'interno dello stesso programma per associare indirizzi diversi a socket + diversi. Vale in questo caso quanto detto in precedenza, l'unica differenza + è che in questo caso le diverse chiamate a \func{bind} sono eseguite + all'interno dello stesso programma. + + +\item[\const{SO\_TYPE}] questa opzione permette di leggere il tipo di socket + su cui si opera; funziona solo con \func{getsockopt}, ed utilizza per + \param{optval} un intero in cui verrà restituto il valore numerico che lo + identifica (ad esempio \const{SOCK\_STREAM}). + +\item[\const{SO\_ACCEPTCONN}] questa opzione permette di rilevare se il socket + su cui opera è stato posto in modalità di ricezione di eventuali connessioni + con una chiamata a \func{listen}. L'opzione può essere usata soltanto con + \func{getsockopt} e utilizza per \param{optval} un intero in cui viene + restituito 1 se il socket è in ascolto e 0 altrimenti. + +\item[\const{SO\_DONTROUTE}] questa opzione forza l'invio diretto dei + pacchetti del socket, saltando ogni processo relativo all'uso della tabella + di routing del kernel. Prende come \param{optval} un intero usato come + valore logico. + +\item[\const{SO\_BROADCAST}] + + +\item[\const{SO\_SNDBUF}] + + +\item[\const{SO\_RCVBUF}] + + +\item[\const{SO\_LINGER}] +\item[\const{SO\_PRIORITY}] + +\item[\const{SO\_ERROR}] + +\end{basedescript} + -Finora abbiamo trattato i socket nel loro comportamento più comune, è però -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 -preferito trattare l'argomento in generale in questa sezione piuttosto che nel -capitolo dedicato alla trattazione generica dei socket. \section{Altre funzioni di controllo} -\label{sec:TCP_sock_ctrl} +\label{sec:sock_ctrl_func} + +Benché la maggior parte delle caratteristiche dei socket sia gestita +attraverso le due funzioni \func{setsockopt} e \func{getsockopt}, alcune +funzionalità possono essere impostate attraverso quelle che sono le funzioni +classiche per il controllo delle proprietà dei file, cioè \func{fcntl} e +\func{ioctl}. + + +\subsection{L'uso di \func{fcntl} per i socket} +\label{sec:sock_fcntl} + +Abbiamo già trattato l'uso di \func{fcntl} in sez.~\ref{sec:file_fcntl}, dove +però ne abbiamo descritto le funzionalità nell'ambito della sua applicazione a +file descriptor associati a file normali; tratteremo qui invece il suo uso +specifico quando la si impiega su file descriptor associati a dei socket. + + +\subsection{L'uso di \func{ioctl} per i socket} +\label{sec:sock_ioctl} + +Come per \func{fcntl} abbiamo trattato l'uso di \func{ioctl} in +sez.~\ref{sec:file_ioctl}, dove ne abbiamo descritto le funzionalità +nell'ambito dell'applicazione su file normali; tratteremo qui il suo uso +specifico quando la si impiega su file descriptor associati a dei socket. + + +\subsection{L'uso di \func{sysctl} per le proprietà della rete} +\label{sec:sock_sysctl} +Come ultimo argomento di questa sezione tratteremo l'uso della funzione +\func{sysctl} (che è stata introdotta nelle sue funzionalità generiche in +sez.~\ref{sec:sys_sysctl}) per quanto riguarda le sue capacità di effettuare +impostazioni relative a proprietà generali dei socket (di tutti quelli di un +certo tipo o di tutti quelli che usano un certo protocollo) rispetto alle +funzioni viste finora che consentono di controllare quelle di un singolo +socket.