X-Git-Url: https://gapil.gnulinux.it/gitweb/?p=gapil.git;a=blobdiff_plain;f=sockctrl.tex;h=88804386e2fa9daddd6a4d80a249308011330076;hp=62989d4a2deaa2922461f33ff4c9e966f0a82500;hb=9a6d19e384fe9b1afbe4d9124ac34eaf7aa57562;hpb=9f5fac5abf66c3a2fe782ecc17d63b62af2485ef diff --git a/sockctrl.tex b/sockctrl.tex index 62989d4..8880438 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", @@ -24,22 +24,23 @@ comportamento. Negli esempi dei capitoli precedenti abbiamo sempre identificato le singole macchine attraverso indirizzi numerici, sfruttando al più le funzioni di conversione elementare illustrate in sez.~\ref{sec:sock_addr_func} che -permettono di passare da un indirizzo espresso in forma dotted decimal ad un -numero. Vedremo in questa sezione le funzioni utilizzate per poter utilizzare -dei nomi simbolici al posto dei valori numerici, e viceversa quelle che -permettono di ottenere i nomi simbolici associati ad indirizzi, porte o altre -proprietà del sistema. +permettono di passare da un indirizzo espresso in forma \textit{dotted + decimal} ad un numero. Vedremo in questa sezione le funzioni utilizzate per +poter utilizzare dei nomi simbolici al posto dei valori numerici, e viceversa +quelle che permettono di ottenere i nomi simbolici associati ad indirizzi, +porte o altre proprietà del sistema. \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 dominio}.\footnote{non staremo ad entrare nei dettagli della definizione di cosa è un nome a dominio, dandolo per noto, una introduzione alla - problematica si trova in \cite{AGL} (cap. 9) mentre per una trattazione + problematica si trova in \cite{AGL} (cap.~9) mentre per una trattazione approfondita di tutte le problematiche relative al DNS si può fare riferimento a \cite{DNSbind}.} In realtà per DNS si intendono spesso i server che forniscono su internet questo servizio, mentre nel nostro caso @@ -48,7 +49,7 @@ necessita di compiere questa operazione. \begin{figure}[htb] \centering - \includegraphics[width=10cm]{img/resolver} + \includegraphics[width=9cm]{img/resolver} \caption{Schema di funzionamento delle routine del \textit{resolver}.} \label{fig:sock_resolver_schema} \end{figure} @@ -64,17 +65,17 @@ con le librerie del C, detto appunto \textit{resolver}. Lo schema di funzionamento del \textit{resolver} è illustrato in fig.~\ref{fig:sock_resolver_schema}; in sostanza i programmi hanno a disposizione un insieme di funzioni di libreria con cui chiamano il -\textit{resolver}, indicate con le freccie nere. Ricevuta la richiesta è +\textit{resolver}, indicate con le frecce nere. Ricevuta la richiesta è quest'ultimo che, sulla base della sua configurazione, esegue le operazioni necessarie a fornire la risposta, che possono essere la lettura delle informazioni mantenute nei relativi dei file statici presenti sulla macchina, una interrogazione ad un DNS (che a sua volta, per il funzionamento del -protocollo può interrogarene altri) o la richiesta ad altri server per i quali +protocollo, può interrogarne altri) o la richiesta ad altri server per i quali sia fornito il supporto, come LDAP.\footnote{la sigla LDAP fa riferimento ad un protocollo, il \textit{Lightweight Directory Access Protocol}, che prevede un meccanismo per la gestione di \textsl{elenchi} di informazioni via rete; il contenuto di un elenco può essere assolutamente generico, e - questo permette il manenimento dei più vari tipi di informazioni su una + questo permette il mantenimento dei più vari tipi di informazioni su una infrastruttura di questo tipo.} La configurazione del \textit{resolver} attiene più alla amministrazione di @@ -86,12 +87,13 @@ dominio, e prevedeva solo l'utilizzo del DNS e del file statico \file{/etc/hosts}. Per questo aspetto il file di configurazione principale del sistema è -\file{/etc/resolv.conf} che contiene in sostanza l'elenco dei server DNS da -contattare; a questo si affianca il file \file{/etc/host.conf} il cui scopo -principale è indicare l'ordine in cui eseguire la risoluzione dei nomi (se -usare prima i valori di \file{/etc/hosts} o quelli del DNS). Tralasciamo i -dettagli relativi alle varie direttive che possono essere usate in questi -file, che si trovano nelle rispettive pagine di manuale. +\file{/etc/resolv.conf} che contiene in sostanza l'elenco degli indirizzi IP +dei server DNS da contattare; a questo si affianca il file +\file{/etc/host.conf} il cui scopo principale è indicare l'ordine in cui +eseguire la risoluzione dei nomi (se usare prima i valori di \file{/etc/hosts} +o quelli del DNS). Tralasciamo i dettagli relativi alle varie direttive che +possono essere usate in questi file, che si trovano nelle rispettive pagine di +manuale. Con il tempo però è divenuto possibile fornire diversi sostituti per l'utilizzo delle associazione statiche in \file{/etc/hosts}, inoltre oltre @@ -105,19 +107,20 @@ NIS,\footnote{il \textit{Network Information Service} di utenti e gruppi e di autenticazione, oggi è sempre più spesso sostituito da LDAP.} o come quelli dei protocolli e dei servizi che sono mantenuti nei file statici \file{/etc/protocols} e \file{/etc/services}. Molte di queste -informazioni non si trovano su un DNS, e poi in un ambiente distribuito può -essere molto utile centralizzare il mentenimento di alcune di esse su -opportuni server. Inoltre l'uso di diversi supporti possibili per le stesse -informazioni (ad esempio il nome delle macchine può essere mantenuto sia -tramite \file{/etc/hosts}, che con il DNS, che con NIS) comporta il problema +informazioni non si trovano su un DNS, ma in una rete locale può essere molto +utile centralizzare il mantenimento di alcune di esse su opportuni server. +Inoltre l'uso di diversi supporti possibili per le stesse informazioni (ad +esempio il nome delle macchine può essere mantenuto sia tramite +\file{/etc/hosts}, che con il DNS, che con NIS) comporta il problema dell'ordine in cui questi vengono interrogati.\footnote{con le implementazioni classiche i vari supporti erano introdotti modificando direttamente le - funzioni di liberia, prevedendo un ordine di interrogazione predefinito e + funzioni di libreria, prevedendo un ordine di interrogazione predefinito e non modificabile (a meno di una ricompilazione delle librerie stesse).} +\index{\textit{Name~Service~Switch}|(} Per risolvere questa serie di problemi la risoluzione dei nomi a dominio -eseguira dal \textit{resolver} è stata inclusa all'interno di un meccanismo -generico per la risoluzione di corripondenze fra nomi ed informazioni ad essi +eseguirà dal \textit{resolver} è stata inclusa all'interno di un meccanismo +generico per la risoluzione di corrispondenze fra nomi ed informazioni ad essi associate chiamato \textit{Name Service Switch}\footnote{il sistema è stato introdotto la prima volta nelle librerie standard di Solaris, le \acr{glibc} hanno ripreso lo stesso schema, si tenga presente che questo sistema non @@ -144,7 +147,7 @@ tab.~\ref{tab:sys_NSS_classes}. (\acr{uid}, ecc.).\\ \texttt{group} & corrispondenze fra nome del gruppo e proprietà dello stesso.\\ - \texttt{aliases} & alias per la posta elettronica\\ + \texttt{aliases} & alias per la posta elettronica.\\ \texttt{ethers} & corrispondenze fra numero IP e MAC address della scheda di rete.\\ \texttt{hosts} & corrispondenze fra nome a dominio e numero IP.\\ @@ -190,6 +193,7 @@ quello che conta sono le funzioni classiche che il \textit{resolver} mette a disposizione,\footnote{è cura della implementazione fattane nelle \acr{glibc} tenere conto della presenza del \textit{Name Service Switch}.} e sono queste quelle che tratteremo nelle sezioni successive. +\index{\textit{Name~Service~Switch}|)} \subsection{Le funzioni di interrogazione del \textit{resolver}} @@ -201,43 +205,50 @@ da esse utilizzato e cio benché in teoria sia solo uno dei possibili supporti su cui mantenere le informazioni, in pratica costituisce il meccanismo principale con cui vengono risolti i nomi a dominio. Per questo motivo esistono una serie di funzioni di -libreria che servono specificamente ad esseguire delle interrogazioni verso un +libreria che servono specificamente ad eseguire delle interrogazioni verso un server DNS, funzioni che poi vengono utilizzate per realizzare le funzioni generiche di libreria usate anche dal sistema del \textit{resolver}. Il sistema del DNS è in sostanza di un database distribuito organizzato in -maniera gerarchica, la manutenzione dei dati è mantenuta in tanti server -distinti ciascuno dei quali si occupa della risoluzione del proprio -\textsl{dominio}; i nomi a dominio sono poi organizzati in una struttura ad -albero analoga a quella dell'albero dei file in un sistema unix-like, con -domini di primo livello (come i \texttt{.org}), secondo livello (come -\texttt{.truelite.it}), ecc. In questo caso le separazioni sono fra i vari -livelli sono definite dal carattere ``\texttt{.}'' ed i nomi devono essere -risolti da destra verso sinistra. Il meccanismo funziona con il criterio della -\textsl{delegazione}, un server responsabile per un dominio di primo livello -può delegare la risoluzione degli indirizzi per un suo dominio di secondo -livello ad un altro server, il quale a sua volta potrà delegare la risoluzione -di un eventuale sottodominio di terzo livello ad un altro server ancora. - -In realtà un server DNS contiene comunque una serie di altre informazioni; -ciascuna voce nel database viene chiamata \textit{resource record}, e può -contenere diverse informazioni; in genere i \textit{resource record} vengono -classificati per la \textsl{classe di indirizzi} cui i dati contenuti fanno -riferimento, e per il \textsl{tipo} di questi ultimi. Oggigiorno i dati -mantenuti nei server DNS sono sostanzialmente relativi soltanto ad indirizzi -internet, per cui in pratica c'è soltanto una classe di indirizzi utilizzata, -i dati invece possono essere di vario tipo, uno dei quali è appunto la -corrispondenza fra nome a dominio e numero IP. - -L'esistenza di vari tipi di informazioni ottenibili da un server DNS è -un'altro dei motivi per cui il \textit{resolver} prevede un insieme di -funzioni dedicato rispetto a quelle della semplice risoluzione dei nomi; la -prima di queste è \funcd{res\_init}, il cui prototipo è: +maniera gerarchica, i dati vengono mantenuti in tanti server distinti ciascuno +dei quali si occupa della risoluzione del proprio \textsl{dominio}; i nomi a +dominio sono organizzati in una struttura ad albero analoga a quella +dell'albero dei file, con domini di primo livello (come i \texttt{.org}), +secondo livello (come \texttt{.truelite.it}), ecc. In questo caso le +separazioni sono fra i vari livelli sono definite dal carattere ``\texttt{.}'' +ed i nomi devono essere risolti da destra verso sinistra.\footnote{per chi si + stia chiedendo quale sia la radice di questo albero, cioè l'equivalente di + ``\texttt{/}'', la risposta è il dominio speciale ``\texttt{.}'', che in + genere non viene mai scritto esplicitamente, ma che, come chiunque abbia + configurato un server DNS sa bene, esiste ed è gestito dai cosiddetti + \textit{root DNS} che risolvono i domini di primo livello.} Il meccanismo +funziona con il criterio della \textsl{delegazione}, un server responsabile +per un dominio di primo livello può delegare la risoluzione degli indirizzi +per un suo dominio di secondo livello ad un altro server, il quale a sua volta +potrà delegare la risoluzione di un eventuale sottodominio di terzo livello ad +un altro server ancora. + +In realtà un server DNS è in grado di fare altro rispetto alla risoluzione di +un nome a dominio in un indirizzo IP; ciascuna voce nel database viene +chiamata \textit{resource record}, e può contenere diverse informazioni. In +genere i \textit{resource record} vengono classificati per la \textsl{classe + di indirizzi} cui i dati contenuti fanno riferimento, e per il \textsl{tipo} +di questi ultimi.\footnote{ritroveremo classi di indirizzi e tipi di record + più avanti in tab.~\ref{tab:DNS_address_class} e + tab.~\ref{tab:DNS_record_type}.} Oggigiorno i dati mantenuti nei server DNS +sono quasi esclusivamente relativi ad indirizzi internet, per cui in pratica +viene utilizzata soltanto una classe di indirizzi; invece le corrispondenze +fra un nome a dominio ed un indirizzo IP sono solo uno fra i vari tipi di +informazione che un server DNS fornisce normalmente. + +L'esistenza di vari tipi di informazioni è un altro dei motivi per cui il +\textit{resolver} prevede, rispetto a quelle relative alla semplice +risoluzione dei nomi, un insieme di funzioni specifiche dedicate +all'interrogazione di un server DNS; la prima di queste funzioni è +\funcd{res\_init}, il cui prototipo è: \begin{functions} -\headdecl{netinet/in.h} -\headdecl{arpa/nameser.h} -\headdecl{resolv.h} -\funcdecl{int res\_init(void)} + \headdecl{netinet/in.h} \headdecl{arpa/nameser.h} \headdecl{resolv.h} + \funcdecl{int res\_init(void)} Inizializza il sistema del \textit{resolver}. @@ -245,29 +256,30 @@ Inizializza il sistema del \textit{resolver}. errore.} \end{functions} -La funzione legge il contenuto dei file di configurazione per impostare il -dominio di default, gli indirizzi dei server DNS da contattare e l'ordine -delle ricerche; se non sono specificati server verrà utilizzato l'indirizzo -locale, e se non è definito un dominio di default sarà usato quello associato -con l'indirizzo locale. In genere si deve eseguire questa funzione prima di -chiamare tutte le altre. - -Le impostazioni del resolver e lo stato del sistema vengono mantenute in una -serie di variabili globali contenuti in una apposita struttura interna, -definita in \file{resolv.h}, che può essere acceduta una volta che la si -dichiara con: +La funzione legge il contenuto dei file di configurazione (i già citati +\file{resolv.conf} e \file{host.conf}) per impostare il dominio di default, +gli indirizzi dei server DNS da contattare e l'ordine delle ricerche; se non +sono specificati server verrà utilizzato l'indirizzo locale, e se non è +definito un dominio di default sarà usato quello associato con l'indirizzo +locale (ma questo può essere sovrascritto con l'uso della variabile di +ambiente \texttt{LOCALDOMAIN}). In genere non è necessario eseguire questa +funzione direttamente in quanto viene automaticamente chiamata la prima volta +che si esegue una delle altre. + +Le impostazioni e lo stato del \textit{resolver} vengono mantenuti in una +serie di variabili raggruppate nei campi di una apposita struttura \var{\_res} +usata da tutte queste funzioni. Essa viene definita in \file{resolv.h} ed è +utilizzata internamente alle funzioni essendo definita come variabile globale; +questo consente anche di accedervi direttamente all'interno di un qualunque +programma, una volta che la sia opportunamente dichiarata come: \includecodesnip{listati/resolv_option.c} -Tutti i campi della struttura sono ad uso interno, l'unico che può essere -utile modificare è il campo \var{\_res.options}, che è una maschera binaria -che permette di controllare il comportamento del resolver, modificandone -alcune impostazioni direttamente da programma prima di invocare -\func{res\_init}. Le costanti che definiscono i vari bit di questo campo, con -il relativo significato sono illustrate in tab.~\ref{tab:resolver_option}, un -valore deve essere espresso con un opportuno OR aritmetico di dette costanti; -ad esempio il valore di default dato dalla costante \const{RES\_DEFAULT} è -definito come: -\includecodesnip{listati/resolv_option_def.c} +Tutti i campi della struttura sono ad uso interno, e vengono usualmente +inizializzati da \func{res\_init} in base al contenuto dei file di +configurazione e ad una serie di valori di default. L'unico campo che può +essere utile modificare è \var{\_res.options}, una maschera binaria che +contiene una serie di bit di opzione che permettono di controllare il +comportamento del \textit{resolver}. \begin{table}[htb] \centering @@ -293,7 +305,7 @@ definito come: del dominio di default ai nomi singoli (che non contengono cioè un ``\texttt{.}'').\\ \const{RES\_STAYOPEN} & usato con \const{RES\_USEVC} per mantenere - aperte le connesioni TCP fra interrogazioni + aperte le connessioni TCP fra interrogazioni diverse. \\ \const{RES\_DNSRCH} & se attivo \func{res\_search} esegue le ricerche di nomi di macchine nel dominio corrente o nei @@ -318,10 +330,37 @@ definito come: \label{tab:resolver_option} \end{table} -La funzione di richiesta principale è \funcd{res\_query}, che serve ad -eseguire una richiesta ad un server DNS per un nome a dominio completamente -specificato (quello che si chiama FQDN, \textit{Fully Qualified Domain Name}); -il suo prototipo è: +Per utilizzare questa funzionalità per modificare le impostazioni direttamente +da programma occorrerà impostare un opportuno valore per questo campo ed +invocare esplicitamente \func{res\_init}, dopo di che le altre funzioni +prenderanno le nuove impostazioni. Le costanti che definiscono i vari bit di +questo campo, ed il relativo significato sono illustrate in +tab.~\ref{tab:resolver_option}; trattandosi di una maschera binaria un valore +deve essere espresso con un opportuno OR aritmetico di dette costanti; ad +esempio il valore di default delle opzioni, espresso dalla costante +\const{RES\_DEFAULT}, è definito come: +\includecodesnip{listati/resolv_option_def.c} + +Non tratteremo il significato degli altri campi non essendovi necessità di +modificarli direttamente; gran parte di essi sono infatti impostati dal +contenuto dei file di configurazione, mentre le funzionalità controllate da +alcuni di esse possono essere modificate con l'uso delle opportune variabili +di ambiente come abbiamo visto per \texttt{LOCALDOMAIN}. In particolare con +\texttt{RES\_RETRY} si soprassiede il valore del campo \var{retry} che +controlla quante volte viene ripetuto il tentativo di connettersi ad un server +DNS prima di dichiarare fallimento; il valore di default è 4, un valore nullo +significa bloccare l'uso del DNS. Infine con \texttt{RES\_TIMEOUT} si +soprassiede il valore del campo \var{retrans},\footnote{preimpostato al valore + della omonima costante \const{RES\_TIMEOUT} di \file{resolv.h}.} che è il +valore preso come base (in numero di secondi) per definire la scadenza di una +richiesta, ciascun tentativo di richiesta fallito viene ripetuto raddoppiando +il tempo di scadenza per il numero massimo di volte stabilito da +\texttt{RES\_RETRY}. + +La funzione di interrogazione principale è \funcd{res\_query}, che serve ad +eseguire una richiesta ad un server DNS per un nome a dominio +\textsl{completamente specificato} (quello che si chiama FQDN, \textit{Fully + Qualified Domain Name}); il suo prototipo è: \begin{functions} \headdecl{netinet/in.h} @@ -332,106 +371,288 @@ il suo prototipo Esegue una interrogazione al DNS. -\bodydesc{La funzione restituisce 0 in caso di successo e -1 in caso di - errore.} +\bodydesc{La funzione restituisce un valore positivo pari alla lunghezza dei + dati scritti nel buffer \param{answer} in caso di successo e -1 in caso di + errore.} \end{functions} +La funzione esegue una interrogazione ad un server DNS relativa al nome da +risolvere passato nella stringa indirizzata da \param{dname}, inoltre deve +essere specificata la classe di indirizzi in cui eseguire la ricerca con +\param{class}, ed il tipo di \textit{resource record} che si vuole ottenere +con \param{type}. Il risultato della ricerca verrà scritto nel buffer di +lunghezza \param{anslen} puntato da \param{answer} che si sarà opportunamente +allocato in precedenza. + + +Una seconda funzione di ricerca, analoga a \func{res\_query}, che prende gli +stessi argomenti, ma che esegue l'interrogazione con le funzionalità +addizionali previste dalle due opzioni \const{RES\_DEFNAMES} e +\const{RES\_DNSRCH}, è \funcd{res\_search}, il cui prototipo è: +\begin{functions} +\headdecl{netinet/in.h} +\headdecl{arpa/nameser.h} +\headdecl{resolv.h} +\funcdecl{int res\_search(const char *dname, int class, int type, + unsigned char *answer, int anslen)} + + Esegue una interrogazione al DNS. + + \bodydesc{La funzione restituisce un valore positivo pari alla lunghezza dei + dati scritti nel buffer \param{answer} in caso di successo e -1 in caso di + errore.} +\end{functions} + +In sostanza la funzione ripete una serie di chiamate a \func{res\_query} +aggiungendo al nome contenuto nella stringa \param{dname} il dominio di +default da cercare, fermandosi non appena trova un risultato. Il risultato di +entrambe le funzioni viene scritto nel formato opportuno (che sarà diverso a +seconda del tipo di record richiesto) nel buffer di ritorno; sarà compito del +programma (o di altre funzioni) estrarre i relativi dati, esistono una serie +di funzioni interne usate per la scansione di questi dati, per chi fosse +interessato una trattazione dettagliata è riportata nel quattordicesimo +capitolo di \cite{DNSbind}. + +Le classi di indirizzi supportate da un server DNS sono tre, ma di queste in +pratica oggi viene utilizzata soltanto quella degli indirizzi internet; le +costanti che identificano dette classi, da usare come valore per l'argomento +\param{class} delle precedenti funzioni, sono riportate in +tab.~\ref{tab:DNS_address_class}.\footnote{esisteva in realtà anche una classe + \const{C\_CSNET} per la omonima rete, ma è stata dichiarata obsoleta.} + +\begin{table}[htb] + \centering + \footnotesize + \begin{tabular}[c]{|l|p{8cm}|} + \hline + \textbf{Costante} & \textbf{Significato} \\ + \hline + \hline + \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}& indirizzi per la rete \textit{Chaosnet}, un'altra rete + sperimentale nata al MIT. \\ + \const{C\_ANY} & indica un indirizzo di classe qualunque.\\ + \hline + \end{tabular} + \caption{Costanti identificative delle classi di indirizzi per l'argomento + \param{class} di \func{res\_query}.} + \label{tab:DNS_address_class} +\end{table} + +Come accennato le tipologie di dati che sono mantenibili su un server DNS sono +diverse, ed a ciascuna di essa corrisponde un diverso tipo di \textit{resource + record}. L'elenco delle costanti\footnote{ripreso dai file di dichiarazione + \file{arpa/nameser.h} e \file{arpa/nameser\_compat.h}.} che definiscono i +valori che si possono usare per l'argomento \param{type} per specificare il +tipo di \textit{resource record} da richiedere è riportato in +tab.~\ref{tab:DNS_record_type}; le costanti (tolto il \texttt{T\_} iniziale) +hanno gli stessi nomi usati per identificare i record nei file di zona di +BIND,\footnote{BIND, acronimo di \textit{Berkley Internet Name Domain}, è una + implementazione di un server DNS, ed, essendo utilizzata nella stragrande + maggioranza dei casi, fa da riferimento; i dati relativi ad un certo + dominio (cioè i suoi \textit{resource record} vengono mantenuti in quelli + che sono usualmente chiamati \textsl{file di zona}, e in essi ciascun tipo + di dominio è identificato da un nome che è appunto identico a quello delle + costanti di tab.~\ref{tab:DNS_record_type} senza il \texttt{T\_} iniziale.} +e che normalmente sono anche usati come nomi per indicare i record. + +\begin{table}[!htb] + \centering + \footnotesize + \begin{tabular}[c]{|l|l|} + \hline + \textbf{Costante} & \textbf{Significato} \\ + \hline + \hline + \const{T\_A} & indirizzo di una stazione.\\ + \const{T\_NS} & server DNS autoritativo per il dominio richiesto.\\ + \const{T\_MD} & destinazione per la posta elettronica.\\ + \const{T\_MF} & redistributore per la posta elettronica.\\ + \const{T\_CNAME} & nome canonico.\\ + \const{T\_SOA} & inizio di una zona di autorità.\\ + \const{T\_MB} & nome a dominio di una casella di posta.\\ + \const{T\_MG} & nome di un membro di un gruppo di posta.\\ + \const{T\_MR} & nome di un cambiamento di nome per la posta.\\ + \const{T\_NULL} & record nullo.\\ + \const{T\_WKS} & servizio noto.\\ + \const{T\_PTR} & risoluzione inversa di un indirizzo numerico.\\ + \const{T\_HINFO} & informazione sulla stazione.\\ + \const{T\_MINFO} & informazione sulla casella di posta.\\ + \const{T\_MX} & server cui instradare la posta per il dominio.\\ + \const{T\_TXT} & stringhe di testo (libere).\\ + \const{T\_RP} & nome di un responsabile (\textit{responsible person}).\\ + \const{T\_AFSDB} & database per una cella AFS.\\ + \const{T\_X25} & indirizzo di chiamata per X.25.\\ + \const{T\_ISDN} & indirizzo di chiamata per ISDN.\\ + \const{T\_RT} & router.\\ + \const{T\_NSAP} & indirizzo NSAP.\\ + \const{T\_NSAP\_PTR}& risoluzione inversa per NSAP (deprecato).\\ + \const{T\_SIG} & firma digitale di sicurezza.\\ + \const{T\_KEY} & chiave per firma.\\ + \const{T\_PX} & corrispondenza per la posta X.400.\\ + \const{T\_GPOS} & posizione geografica.\\ + \const{T\_AAAA} & indirizzo IPv6.\\ + \const{T\_LOC} & informazione di collocazione.\\ + \const{T\_NXT} & dominio successivo.\\ + \const{T\_EID} & identificatore di punto conclusivo.\\ + \const{T\_NIMLOC}& posizionatore \textit{nimrod}.\\ + \const{T\_SRV} & servizio.\\ + \const{T\_ATMA} & indirizzo ATM.\\ + \const{T\_NAPTR} & puntatore ad una \textit{naming authority} .\\ + \const{T\_TSIG} & firma di transazione.\\ + \const{T\_IXFR} & trasferimento di zona incrementale.\\ + \const{T\_AXFR} & trasferimento di zona di autorità.\\ + \const{T\_MAILB} & trasferimento di record di caselle di posta.\\ + \const{T\_MAILA} & trasferimento di record di server di posta.\\ + \const{T\_ANY} & valore generico.\\ + \hline + \end{tabular} + \caption{Costanti identificative del tipo di record per l'argomento + \param{type} di \func{res\_query}.} + \label{tab:DNS_record_type} +\end{table} -Per quanto ci interessa i tipi di \textit{resource record} che vengono -utilizzati dal \textit{resolver} sono sostanzialmente i seguenti: + +L'elenco di tab.~\ref{tab:DNS_record_type} è quello di \textsl{tutti} i +\textit{resource record} definiti, con una breve descrizione del relativo +significato. Di tutti questi però viene impiegato correntemente solo un +piccolo sottoinsieme, alcuni sono obsoleti ed altri fanno riferimento a dati +applicativi che non ci interessano non avendo nulla a che fare con la +risoluzione degli indirizzi IP, pertanto non entreremo nei dettagli del +significato di tutti i \textit{resource record}, ma solo di quelli usati dalle +funzioni del \textit{resolver}. Questi sono sostanzialmente i seguenti (per +indicarli si è usata la notazione dei file di zona di BIND): \begin{basedescript}{\desclabelwidth{1.2cm}\desclabelstyle{\nextlinelabel}} -\item[\texttt{A}] indica la corripondenza fra un nome a dominio ed un - indirizzo IPv4, ad esempio la corrispondenza fra \texttt{dodds.truelite.it} - e l'indirizzo IP \texttt{62.48.34.25}. -\item[\texttt{AAAA}] chiamato in questo modo dato che la dimensione è quattro - volte quella di un indirizzo IPv4, questo record contiene la corrispondenza - fra un nome a dominio ed un indirizzo IPv6. -\item[\texttt{PTR}] per provvedere la mappatura inversa fra un indirizzo IP ed - un nome a dominio si utilizza invece questo tipo di record (il cui nome sta - per \textit{pointer}). -\item[\texttt{CNAME}] qualora si abbiamo più nomi con i quali si voglia - indicare lo stesso indirizzo (ad esempio \texttt{www.truelite.it}, o - \texttt{sources.truelite.it}, che comunque fanno sempre riferimento alla - macchina \texttt{dodds.truelite.it}) si può usare questo tipo di record per - creare degli \textit{alias} in modo da associare un qualunque altro nome al - \textsl{nome canonico} della macchina (quello associato al record - \texttt{A}). +\item[\texttt{A}] viene usato per indicare la corrispondenza fra un nome a + dominio ed un indirizzo IPv4; ad esempio la corrispondenza fra + \texttt{dodds.truelite.it} e l'indirizzo IP \texttt{62.48.34.25}. +\item[\texttt{AAAA}] viene usato per indicare la corrispondenza fra un nome a + dominio ed un indirizzo IPv6; è chiamato in questo modo dato che la + dimensione di un indirizzo IPv6 è quattro volte quella di un indirizzo IPv4. +\item[\texttt{PTR}] per fornire la corrispondenza inversa fra un indirizzo IP + ed un nome a dominio ad esso associato si utilizza questo tipo di record (il + cui nome sta per \textit{pointer}). +\item[\texttt{CNAME}] qualora si abbiamo più nomi che corrispondono allo + stesso indirizzo (come ad esempio \texttt{www.truelite.it}, o + \texttt{sources.truelite.it}, che fanno sempre riferimento a + \texttt{dodds.truelite.it}) si può usare questo tipo di record per creare + degli \textit{alias} in modo da associare un qualunque altro nome al + \textsl{nome canonico} della macchina (si chiama così quello associato al + record \texttt{A}). \end{basedescript} +Come accennato in caso di successo le due funzioni di richiesta restituiscono +il risultato della interrogazione al server, in caso di insuccesso l'errore +invece viene segnalato da un valore di ritorno pari a -1, ma in questo caso, +non può essere utilizzata la variabile \var{errno} per riportare un codice di +errore, in quanto questo viene impostato per ciascuna delle chiamate al +sistema utilizzate dalle funzioni del \textit{resolver}, non avrà alcun +significato nell'indicare quale parte del procedimento di risoluzione è +fallita. +Per questo motivo è stata definita una variabile di errore separata, +\var{h\_errno}, che viene utilizzata dalle funzioni del \textit{resolver} per +indicare quale problema ha causato il fallimento della risoluzione del nome. +Ad essa si può accedere una volta che la si dichiara con: +\includecodesnip{listati/herrno.c} +ed i valori che può assumere, con il relativo significato, sono riportati in +tab.~\ref{tab:h_errno_values}. +\begin{table}[!htb] + \centering + \footnotesize + \begin{tabular}[c]{|l|p{10cm}|} + \hline + \textbf{Costante} & \textbf{Significato} \\ + \hline + \hline + \const{HOST\_NOT\_FOUND} & l'indirizzo richiesto non è valido e la + macchina indicata è sconosciuta. \\ + \const{NO\_ADDRESS} & il nome a dominio richiesto è valido, ma non ha + un indirizzo associato ad esso + (alternativamente può essere indicato come + \const{NO\_DATA}). \\ + \const{NO\_RECOVERY} & si è avuto un errore non recuperabile + nell'interrogazione di un server DNS. \\ + \const{TRY\_AGAIN} & si è avuto un errore temporaneo + nell'interrogazione di un server DNS, si può + ritentare l'interrogazione in un secondo + tempo. \\ + \hline + \end{tabular} + \caption{Valori possibili della variabile \var{h\_errno}.} + \label{tab:h_errno_values} +\end{table} +Insieme alla nuova variabile vengono definite anche due nuove funzioni per +stampare l'errore a video, analoghe a quelle di sez.~\ref{sec:sys_strerror} +per \var{errno}, ma che usano il valore di \var{h\_errno}; la prima è +\funcd{herror} ed il suo prototipo è: +\begin{functions} +\headdecl{netdb.h} +\funcdecl{void herror(const char *string)} +Stampa un errore di risoluzione. +\end{functions} -Per questo motivo il \textit{resolver} prevede delle funzioni che permettono -sia di eseguire direttamente delle interrogazione ad un server DNS, che di -controllare le modalità con cui queste vengono eseguite; diventa così -possibile modificare da programma buona parte dei parametri controllati da -\file{/etc/resolv.conf}. +La funzione è l'analoga di \func{perror} e stampa sullo standard error un +messaggio di errore corrispondente al valore corrente di \var{h\_errno}, a cui +viene anteposta la stringa \param{string} passata come argomento. La seconda +funzione è \funcd{hstrerror} ed il suo prototipo è: +\begin{functions} +\headdecl{netdb.h} +\funcdecl{const char *hstrerror(int err)} +Restituisce una stringa corrispondente ad un errore di risoluzione. +\end{functions} +\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}|)} -\subsection{La risoluzione dei nomi a dominio} -\label{sec:sock_gethostbyname} -Dato che la principale funzionalità del \textit{resolver} resta quella di -risolvere i nomi a dominio in indirizzi IP, vedremo per prime 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 è: + +\subsection{La risoluzione dei nomi a dominio} +\label{sec:sock_name_services} + +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)} Determina l'indirizzo associato al nome a dominio \param{name}. \bodydesc{La funzione restituisce in caso di successo il puntatore ad una - struttura di tipo \struct{hostent} contente i dati associati al nome a - dominio o un puntatore nullo in caso di errore.} + struttura di tipo \struct{hostent} contenente i dati associati al nome a + dominio, o un puntatore nullo in caso di errore.} \end{prototype} La funzione prende come argomento una stringa \param{name} contenente il nome a dominio che si vuole risolvere, in caso di successo i dati ad esso relativi vengono memorizzati in una opportuna struttura \struct{hostent} la cui -definizione è riportata in fig.~\ref{fig:sock_hostent_struct}. In caso di -insuccesso l'errore viene segnalato da un valore nullo del puntatore, ma in -questo caso, a differenza delle funzioni viste finora, non viene utilizzata la -variabile \var{errno} per riportare un codice di errore, in quanto questo -dipende solo dalle sottostanti chiamate al sistema e può non avere nessun -significato nell'indicare quale parte del procedimento di risoluzione è -fallita. +definizione è riportata in fig.~\ref{fig:sock_hostent_struct}. \begin{figure}[!htb] \footnotesize \centering \begin{minipage}[c]{15cm} \includestruct{listati/hostent.h} \end{minipage} - \caption{La struttura \structd{hostent}.} + \caption{La struttura \structd{hostent} per la risoluzione dei nomi a + dominio e degli indirizzi IP.} \label{fig:sock_hostent_struct} \end{figure} -Per questo motivo all'interno del resolver è stata definita una apposita -variabile di errore, \var{h\_errno} che viene utilizzata dalle funzioni di -libreria per indicare quale problema ha causato il fallimento della -risoluzione del nome. Ad essa si può accedere una volta che la si dichiara -con: -\includecodesnip{listati/herrno.c} -ed i valori che può assumere sono i seguenti: -\begin{basedescript}{\desclabelwidth{3cm}\desclabelstyle{\nextlinelabel}} -\item[\const{HOST\_NOT\_FOUND}] l'indirizzo richiesto non è valido e la - macchina indicata è sconosciuta. -\item[\const{NO\_ADDRESS}] il nome a dominio richiesto è valido, ma non ha un - indirizzo associato ad esso (alternativamente può essere indicato come - \const{NO\_DATA}). -\item[\const{NO\_RECOVERY}] si è avuto un errore non recuperabile - nell'interrogazione di un server DNS. -\item[\const{TRY\_AGAIN}] si è avuto un errore temporaneo nell'interrogazione - di un server DNS, si può ritentare l'interrogazione in un secondo tempo. -\end{basedescript} - Quando un programma chiama \func{gethostbyname} e questa usa il DNS per -effettuare la risoluzione del nome, è con i valori di questi record che -vengono riempite le varie parti della struttura \struct{hostent}. Il primo -campo della struttura, \var{h\_name} contiene sempre il \textsl{nome +effettuare la risoluzione del nome, è con i valori contenuti nei relativi +record che vengono riempite le varie parti della struttura \struct{hostent}. +Il primo campo della struttura, \var{h\_name} contiene sempre il \textsl{nome canonico}, che nel caso del DNS è appunto il nome associato ad un record \texttt{A}. Il secondo campo della struttura, \var{h\_aliases}, invece è un puntatore ad vettore di puntatori, terminato da un puntatore nullo. Ciascun @@ -442,8 +663,7 @@ DNS vengono inseriti come record di tipo \texttt{CNAME}). Il terzo campo della struttura, \var{h\_addrtype}, indica il tipo di indirizzo che è stato restituito, e può assumere soltanto i valori \const{AF\_INET} o \const{AF\_INET6}, mentre il quarto campo, \var{h\_length}, indica la -lunghezza dell'indirizzo stesso in byte. La funzione ritorna sempre una -struttura +lunghezza dell'indirizzo stesso in byte. Infine il campo \var{h\_addr\_list} è il puntatore ad un vettore di puntatori ai singoli indirizzi; il vettore è terminato da un puntatore nullo. Inoltre, @@ -453,33 +673,2307 @@ diretto al primo indirizzo della lista. Oltre ai normali nomi a dominio la funzione accetta come argomento \param{name} anche indirizzi numerici, in formato dotted decimal per IPv4 o -con la notazione illustrata in sez.~\ref{sec:IP_ipv6_notation}. In tal caso -\func{gethostbyname} non eseguirà nessuna interrogazione remota, ma si -limiterà a copiare la stringa nel campo \var{h\_name} ed a creare la -corrispondente struttura \var{in\_addr} da indirizzara con +con la notazione illustrata in sez.~\ref{sec:IP_ipv6_notation} per IPv6. In +tal caso \func{gethostbyname} non eseguirà nessuna interrogazione remota, ma +si limiterà a copiare la stringa nel campo \var{h\_name} ed a creare la +corrispondente struttura \var{in\_addr} da indirizzare con \code{h\_addr\_list[0]}. +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 \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} + \funcdecl{struct hostent *gethostbyname2(const char *name, int af)} + +Determina l'indirizzo di tipo \param{af} associato al nome a dominio +\param{name}. + +\bodydesc{La funzione restituisce in caso di successo il puntatore ad una + struttura di tipo \struct{hostent} contenente i dati associati al nome a + dominio, o un puntatore nullo in caso di errore.} +\end{functions} + +In questo caso la funzione prende un secondo argomento \param{af} che indica +(i soli valori consentiti sono \const{AF\_INET} o \const{AF\_INET6}, per +questo è necessario l'uso di \texttt{sys/socket.h}) la famiglia di indirizzi +che dovrà essere utilizzata nei risultati restituiti dalla funzione. Per tutto +il resto la funzione è identica a \func{gethostbyname}, ed identici sono i +suoi risultati. + +\begin{figure}[!htb] + \footnotesize \centering + \begin{minipage}[c]{15cm} + \includecodesample{listati/mygethost.c} + \end{minipage} + \normalsize + \caption{Esempio di codice per la risoluzione di un indirizzo.} + \label{fig:mygethost_example} +\end{figure} + +Vediamo allora un primo esempio dell'uso delle funzioni di risoluzione, in +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 +(\texttt{\small 16}) si limita a chiamare \func{gethostbyname}, ricevendo il +risultato nel puntatore \var{data}. Questo (\texttt{\small 17--20}) viene +controllato per rilevare eventuali errori, nel qual caso il programma esce +dopo aver stampato un messaggio con \func{herror}. + +Se invece la risoluzione è andata a buon fine si inizia (\texttt{\small 21}) +con lo stampare il nome canonico, dopo di che (\texttt{\small 22--26}) si +stampano eventuali altri nomi. Per questo prima (\texttt{\small 22}) si prende +il puntatore alla cima della lista che contiene i nomi e poi (\texttt{\small + 23--26}) si esegue un ciclo che sarà ripetuto fin tanto che nella lista si +troveranno dei puntatori validi\footnote{si ricordi che la lista viene + terminata da un puntatore nullo.} per le stringhe dei nomi; prima +(\texttt{\small 24}) si stamperà la stringa e poi (\texttt{\small 25}) si +provvederà ad incrementare il puntatore per passare al successivo elemento +della lista. + +Una volta stampati i nomi si passerà a stampare gli indirizzi, il primo passo +(\texttt{\small 27--34}) è allora quello di riconoscere il tipo di indirizzo +sulla base del valore del campo \var{h\_addrtype}, stampandolo a video. Si è +anche previsto di stampare un errore nel caso (che non dovrebbe mai accadere) +di un indirizzo non valido. + +Infine (\texttt{\small 35--40}) si stamperanno i valori degli indirizzi, di +nuovo (\texttt{\small 35}) si inizializzerà un puntatore alla cima della lista +e si eseguirà un ciclo fintanto che questo punterà ad indirizzi validi in +maniera analoga a quanto fatto in precedenza per i nomi a dominio. Si noti +come, essendo il campo \var{h\_addr\_list} un puntatore ad strutture di +indirizzi generiche, questo sia ancora di tipo \texttt{char **} e si possa +riutilizzare lo stesso puntatore usato per i nomi. + +Per ciascun indirizzo valido si provvederà (\texttt{\small 37}) ad una +conversione con la funzione \func{inet\_ntop} (vedi +sez.~\ref{sec:sock_addr_func}) passandole gli opportuni argomenti, questa +restituirà la stringa da stampare (\texttt{\small 38}) con il valore +dell'indirizzo in \var{buffer}, che si è avuto la cura di dichiarare +inizialmente (\texttt{\small 10}) con dimensioni adeguate; dato che la +funzione è in grado di tenere conto automaticamente del tipo di indirizzo non +ci sono precauzioni particolari da prendere.\footnote{volendo essere pignoli + si dovrebbe controllarne lo stato di uscita, lo si è tralasciato per non + appesantire il codice, dato che in caso di indirizzi non validi si sarebbe + avuto un errore con \func{gethostbyname}, ma si ricordi che la sicurezza non + è mai troppa.} + +Le funzioni illustrate finora hanno un difetto: utilizzando una area di +memoria interna per allocare i contenuti della struttura \struct{hostent} non +possono essere rientranti. Questo comporta anche che in due successive +chiamate i dati potranno essere sovrascritti. Si tenga presente poi che +copiare il contenuto della sola struttura non è sufficiente per salvare tutti +i dati, in quanto questa contiene puntatori ad altri dati, che pure possono +essere sovrascritti; per questo motivo, se si vuole salvare il risultato di +una chiamata, occorrerà eseguire quella che si chiama una +\index{\textit{deep~copy}}\textit{deep copy}.\footnote{si chiama così quella + tecnica per cui, quando si deve copiare il contenuto di una struttura + complessa (con puntatori che puntano ad altri dati, che a loro volta possono + essere puntatori ad altri dati) si deve copiare non solo il contenuto della + struttura, ma eseguire una scansione per risolvere anche tutti i puntatori + contenuti in essa (e così via se vi sono altre sottostrutture con altri + puntatori) e copiare anche i dati da questi referenziati.} + +Per ovviare a questi problemi nelle \acr{glibc} sono definite anche delle +versioni rientranti delle precedenti funzioni, al solito queste sono +caratterizzate dall'avere un suffisso \texttt{\_r}, pertanto avremo le due +funzioni \funcd{gethostbyname\_r} e \funcd{gethostbyname2\_r} i cui prototipi +sono: +\begin{functions} + \headdecl{netdb.h} + \headdecl{sys/socket.h} + \funcdecl{int gethostbyname\_r(const char *name, struct hostent *ret, + char *buf, size\_t buflen, struct hostent **result, int *h\_errnop)} + \funcdecl{int gethostbyname2\_r(const char *name, int af, + struct hostent *ret, char *buf, size\_t buflen, + struct hostent **result, int *h\_errnop)} + + Versioni rientranti delle funzioni \func{gethostbyname} e + \func{gethostbyname2}. + + \bodydesc{Le funzioni restituiscono 0 in caso di successo ed un valore + negativo in caso di errore.} +\end{functions} + +Gli argomenti \param{name} (e \param{af} per \func{gethostbyname2\_r}) hanno +lo stesso significato visto in precedenza. Tutti gli altri argomenti hanno lo +stesso significato per entrambe le funzioni. Per evitare l'uso di variabili +globali si dovrà allocare preventivamente una struttura \struct{hostent} in +cui ricevere il risultato, passandone l'indirizzo alla funzione nell'argomento +\param{ret}. Inoltre, dato che \struct{hostent} contiene dei puntatori, dovrà +essere allocato anche un buffer in cui le funzioni possano scrivere tutti i +dati del risultato dell'interrogazione da questi puntati; l'indirizzo e la +lunghezza di questo buffer devono essere indicati con gli argomenti +\param{buf} e \param{buflen}. + +Gli ultimi due argomenti vengono utilizzati per avere indietro i risultati +come \index{\textit{value~result~argument}}\textit{value result argument}, si +deve specificare l'indirizzo della variabile su cui la funzione dovrà salvare +il codice di errore con \param{h\_errnop} e quello su cui dovrà salvare il +puntatore che si userà per accedere i dati con \param{result}. + +In caso di successo entrambe le funzioni restituiscono un valore nullo, +altrimenti restituiscono un codice di errore negativo e all'indirizzo puntato +da \param{result} sarà salvato un puntatore nullo, mentre a quello puntato da +\param{h\_errnop} sarà salvato il valore del codice di errore, dato che per +essere rientrante la funzione non può la variabile globale \var{h\_errno}. In +questo caso il codice di errore, oltre ai valori di +tab.~\ref{tab:h_errno_values}, può avere anche quello di \errcode{ERANGE} +qualora il buffer allocato su \param{buf} non sia sufficiente a contenere i +dati, in tal caso si dovrà semplicemente ripetere l'esecuzione della funzione +con un buffer di dimensione maggiore. + +Una delle caratteristiche delle interrogazioni al servizio DNS è che queste +sono normalmente eseguite con il protocollo UDP, ci sono casi in cui si +preferisce che vengano usate connessioni permanenti con il protocollo TCP. Per +ottenere questo\footnote{si potrebbero impostare direttamente le opzioni di + \var{\_\_res.options}, ma queste funzioni permettono di semplificare la + procedura.} sono previste delle funzioni apposite; la prima è +\funcd{sethostent}, il cui prototipo è: +\begin{prototype}{netdb.h} +{void sethostent(int stayopen)} + +Richiede l'uso di connessioni per le interrogazioni ad un server DNS. + +\bodydesc{La funzione non restituisce nulla.} +\end{prototype} + +La funzione permette di richiedere l'uso di connessioni TCP per la richiesta +dei dati, e che queste restino aperte per successive richieste. Il valore +dell'argomento \param{stayopen} indica se attivare questa funzionalità, un +valore pari a 1 (o diverso da zero), che indica una condizione vera in C, +attiva la funzionalità. Come si attiva l'uso delle connessioni TCP lo si può +disattivare con la funzione \funcd{endhostent}; il suo prototipo è: +\begin{prototype}{netdb.h} +{void endhostent(void)} + +Disattiva l'uso di connessioni per le interrogazioni ad un server DNS. + +\bodydesc{La funzione non restituisce nulla.} +\end{prototype} +\noindent e come si può vedere la funzione è estremamente semplice, non +richiedendo nessun argomento. + + +Infine si può richiedere la risoluzione inversa di un indirizzo IP od IPv6, +per ottenerne il nome a dominio ad esso associato, per fare questo si può +usare la funzione \funcd{gethostbyaddr}, il cui prototipo è: +\begin{functions} + \headdecl{netdb.h} + \headdecl{sys/socket.h} + \funcdecl{struct hostent *gethostbyaddr(const char *addr, int len, int type)} + + Richiede la risoluzione inversa di un indirizzo IP. + + \bodydesc{La funzione restituisce l'indirizzo ad una struttura + \struct{hostent} in caso di successo ed \const{NULL} in caso di errore.} +\end{functions} + +In questo caso l'argomento \param{addr} dovrà essere il puntatore ad una +appropriata struttura contenente il valore dell'indirizzo IP (o IPv6) che si +vuole risolvere. L'uso del tipo \type{char *} per questo argomento è storico, +il dato dovrà essere fornito in una struttura \struct{in\_addr}\footnote{si + ricordi che, come illustrato in fig.~\ref{fig:sock_sa_ipv4_struct}, questo + in realtà corrisponde ad un numero intero, da esprimere comunque in + \textit{network order}, non altrettanto avviene però per \var{in6\_addr}, + pertanto è sempre opportuno inizializzare questi indirizzi con + \func{inet\_pton} (vedi sez.~\ref{sec:sock_conv_func_gen}).} per un +indirizzo IPv4 ed una struttura \struct{in6\_addr} per un indirizzo IPv6, +mentre in \param{len} se ne dovrà specificare la dimensione (rispettivamente 4 +o 16), infine l'argomento \param{type} indica il tipo di indirizzo e dovrà +essere o \const{AF\_INET} o \const{AF\_INET6}. + +La funzione restituisce, in caso di successo, un puntatore ad una struttura +\struct{hostent}, solo che in questo caso la ricerca viene eseguita +richiedendo al DNS un record di tipo \texttt{PTR} corrispondente all'indirizzo +specificato. In caso di errore al solito viene usata la variabile +\var{h\_errno} per restituire un opportuno codice. In questo caso l'unico +campo del risultato che interessa è \var{h\_name} che conterrà il nome a +dominio, la funziona comunque inizializza anche il primo campo della lista +\var{h\_addr\_list} col valore dell'indirizzo passato come argomento. + +Per risolvere il problema dell'uso da parte delle due funzioni +\func{gethostbyname} e \func{gethostbyaddr} di memoria statica che può essere +sovrascritta fra due chiamate successive, e per avere sempre la possibilità di +indicare esplicitamente il tipo di indirizzi voluto (cosa che non è possibile +con \func{gethostbyname}), vennero introdotte due nuove funzioni di +risoluzione,\footnote{le funzioni sono presenti nelle \acr{glibc} versione + 2.1.96, ma essendo considerate deprecate (vedi + sez.~\ref{sec:sock_advanced_name_services}) sono state rimosse nelle + versioni successive.} \funcd{getipnodebyname} e \funcd{getipnodebyaddr}, i +cui prototipi sono: +\begin{functions} + \headdecl{netdb.h} + \headdecl{sys/types.h} + \headdecl{sys/socket.h} + + \funcdecl{struct hostent *getipnodebyname(const char *name, int af, int + flags, int *error\_num)} + + \funcdecl{struct hostent *getipnodebyaddr(const void *addr, size\_t len, + int af, int *error\_num)} + + Richiedono rispettivamente la risoluzione e la risoluzione inversa di un + indirizzo IP. + + \bodydesc{Entrambe le funzioni restituiscono l'indirizzo ad una struttura + \struct{hostent} in caso di successo ed \const{NULL} in caso di errore.} +\end{functions} + +Entrambe le funzioni supportano esplicitamente la scelta di una famiglia di +indirizzi con l'argomento \param{af} (che può assumere i valori +\const{AF\_INET} o \const{AF\_INET6}), e restituiscono un codice di errore +(con valori identici a quelli precedentemente illustrati in +tab.~\ref{tab:h_errno_values}) nella variabile puntata da \param{error\_num}. +La funzione \func{getipnodebyaddr} richiede poi che si specifichi l'indirizzo +come per \func{gethostbyaddr} passando anche la lunghezza dello stesso +nell'argomento \param{len}. + +La funzione \func{getipnodebyname} prende come primo argomento il nome da +risolvere, inoltre prevede un apposito argomento \param{flags}, da usare come +maschera binaria, che permette di specificarne il comportamento nella +risoluzione dei diversi tipi di indirizzi (IPv4 e IPv6); ciascun bit +dell'argomento esprime una diversa opzione, e queste possono essere specificate +con un OR aritmetico delle costanti riportate in +tab.~\ref{tab:sock_getipnodebyname_flags}. + +\begin{table}[!htb] + \centering + \footnotesize + \begin{tabular}[c]{|l|p{10cm}|} + \hline + \textbf{Costante} & \textbf{Significato} \\ + \hline + \hline + \const{AI\_V4MAPPED} & usato con \const{AF\_INET6} per richiedere una + ricerca su un indirizzo IPv4 invece che IPv6; gli + eventuali risultati saranno rimappati su indirizzi + IPv6.\\ + \const{AI\_ALL} & usato con \const{AI\_V4MAPPED}; richiede sia + indirizzi IPv4 che IPv6, e gli indirizzi IPv4 + saranno rimappati in IPv6.\\ + \const{AI\_ADDRCONFIG}& richiede che una richiesta IPv4 o IPv6 venga + eseguita solo se almeno una interfaccia del + sistema è associata ad un indirizzo di tale tipo.\\ + \const{AI\_DEFAULT} & il valore di default, è equivalente alla + combinazione di \const{AI\_ADDRCONFIG} e di + \const{AI\_V4MAPPED)}.\\ + \hline + \end{tabular} + \caption{Valori possibili per i bit dell'argomento \param{flags} della + funzione \func{getipnodebyname}.} + \label{tab:sock_getipnodebyname_flags} +\end{table} + +Entrambe le funzioni restituiscono un puntatore ad una struttura \var{hostent} +che contiene i risultati della ricerca, che viene allocata dinamicamente +insieme a tutto lo spazio necessario a contenere i dati in essa referenziati; +per questo motivo queste funzioni non soffrono dei problemi dovuti all'uso di +una sezione statica di memoria presenti con le precedenti \func{gethostbyname} +e \func{gethostbyaddr}. L'uso di una allocazione dinamica però comporta anche +la necessità di deallocare esplicitamente la memoria occupata dai risultati +una volta che questi non siano più necessari; a tale scopo viene fornita la +funzione \funcd{freehostent}, il cui prototipo è: +\begin{functions} + \headdecl{netdb.h} + \headdecl{sys/types.h} + \headdecl{sys/socket.h} + + \funcdecl{void freehostent(struct hostent *ip)} + + Disalloca una struttura \var{hostent}. + + \bodydesc{La funzione non ritorna nulla.} +\end{functions} + +La funzione permette di disallocare una struttura \var{hostent} +precedentemente allocata in una chiamata di \func{getipnodebyname} o +\func{getipnodebyaddr}, e prende come argomento l'indirizzo restituito da una +di queste funzioni. + +Infine per concludere la nostra panoramica sulle funzioni di risoluzione dei +nomi dobbiamo citare le funzioni che permettono di interrogare gli altri +servizi di risoluzione dei nomi illustrati in sez.~\ref{sec:sock_resolver}; in +generale infatti ci sono una serie di funzioni nella forma +\texttt{getXXXbyname} e \texttt{getXXXbyaddr} per ciascuna delle informazioni +di rete mantenute dal \textit{Name Service Switch} che permettono +rispettivamente di trovare una corrispondenza cercando per nome o per numero. + +L'elenco di queste funzioni è riportato nelle colonne finali di +tab.~\ref{tab:name_resolution_functions}, dove le si sono suddivise rispetto +al tipo di informazione che forniscono (riportato in prima colonna). Nella +tabella si è anche riportato il file su cui vengono ordinariamente mantenute +queste informazioni, che però può essere sostituito da un qualunque supporto +interno al \textit{Name Service Switch} (anche se usualmente questo avviene +solo per la risoluzione degli indirizzi). Ciascuna funzione fa riferimento ad +una sua apposita struttura che contiene i relativi dati, riportata in terza +colonna. + +\begin{table}[!htb] + \centering + \footnotesize + \begin{tabular}[c]{|l|l|l|l|l|} + \hline + \textbf{Informazione}&\textbf{File}&\textbf{Struttura}& + \multicolumn{2}{|c|}{\textbf{Funzioni}}\\ + \hline + \hline + indirizzo&\file{/etc/hosts}&\struct{hostent}&\func{gethostbyname}& + \func{gethostbyaddr}\\ + servizio &\file{/etc/services}&\struct{servent}&\func{getservbyname}& + \func{getservbyaddr}\\ + rete &\file{/etc/networks}&\struct{netent}&\func{getnetbyname}& + \func{getnetbyaddr}\\ + protocollo&\file{/etc/protocols}&\struct{protoent}&\func{getprotobyname}& + \func{getprotobyaddr}\\ + \hline + \end{tabular} + \caption{Funzioni di risoluzione dei nomi per i vari servizi del + \textit{Name Service Switch}.} + \label{tab:name_resolution_functions} +\end{table} + +Delle funzioni di tab.~\ref{tab:name_resolution_functions} abbiamo trattato +finora soltanto quelle relative alla risoluzione dei nomi, dato che sono le +più usate, e prevedono praticamente da sempre la necessità di rivolgersi ad +una entità esterna; per le altre invece, estensioni fornite dal NSS a parte, +si fa sempre riferimento ai dati mantenuti nei rispettivi file. + +Dopo la risoluzione dei nomi a dominio una delle ricerche più comuni è quella +sui nomi dei servizi noti (cioè \texttt{http}, \texttt{smtp}, ecc.) da +associare alle rispettive porte, le due funzioni da utilizzare per questo sono +\funcd{getservbyname} e \funcd{getservbyaddr}, che permettono rispettivamente +di ottenere il numero di porta associato ad un servizio dato il nome e +viceversa; i loro prototipi sono: +\begin{functions} + \headdecl{netdb.h} + \funcdecl{struct servent *getservbyname(const char *name, const char *proto)} + \funcdecl{struct servent *getservbyport(int port, const char *proto)} + + Risolvono il nome di un servizio nel rispettivo numero di porta e viceversa. + + \bodydesc{Ritornano il puntatore ad una struttura \struct{servent} con i + risultati in caso di successo, o \const{NULL} in caso di errore.} +\end{functions} + +Entrambe le funzioni prendono come ultimo argomento una stringa \param{proto} +che indica il protocollo per il quale si intende effettuare la +ricerca,\footnote{le informazioni mantenute in \file{/etc/services} infatti + sono relative sia alle porte usate su UDP che su TCP, occorre quindi + specificare a quale dei due protocolli si fa riferimento.} che nel caso si +IP può avere come valori possibili solo \texttt{udp} o +\texttt{tcp};\footnote{in teoria si potrebbe avere un qualunque protocollo fra + quelli citati in \file{/etc/protocols}, posto che lo stesso supporti il + concetto di \textsl{porta}, in pratica questi due sono gli unici presenti.} +se si specifica un puntatore nullo la ricerca sarà eseguita su un protocollo +qualsiasi. + +Il primo argomento è il nome del servizio per \func{getservbyname}, +specificato tramite la stringa \param{name}, mentre \func{getservbyport} +richiede il numero di porta in \param{port}. Entrambe le funzioni eseguono una +ricerca sul file \file{/etc/services}\footnote{il \textit{Name Service Switch} + astrae il concetto a qualunque supporto su cui si possano mantenere i + suddetti dati. } ed estraggono i dati dalla prima riga che corrisponde agli +argomenti specificati; se la risoluzione ha successo viene restituito un +puntatore ad una apposita struttura \struct{servent} contenente tutti i +risultati), altrimenti viene restituito un puntatore nullo. Si tenga presente +che anche in questo caso i dati vengono mantenuti in una area di memoria +statica e che quindi la funzione non è rientrante. + +\begin{figure}[!htb] + \footnotesize \centering + \begin{minipage}[c]{15cm} + \includestruct{listati/servent.h} + \end{minipage} + \caption{La struttura \structd{servent} per la risoluzione dei nomi dei + servizi e dei numeri di porta.} + \label{fig:sock_servent_struct} +\end{figure} + +La definizione della struttura \struct{servent} è riportata in +fig.~\ref{fig:sock_servent_struct}, il primo campo, \var{s\_name} contiene +sempre il nome canonico del servizio, mentre \var{s\_aliases} è un puntatore +ad un vettore di stringhe contenenti gli eventuali nomi alternativi +utilizzabili per identificare lo stesso servizio. Infine \var{s\_port} +contiene il numero di porta e \var{s\_proto} il nome del protocollo. + +Come riportato in tab.~\ref{tab:name_resolution_functions} ci sono analoghe +funzioni per la risoluzione del nome dei protocolli e delle reti; non staremo +a descriverle nei dettagli, in quanto il loro uso è molto limitato, esse +comunque hanno una struttura del tutto analoga alle precedenti, e tutti i +dettagli relativi al loro funzionamento possono essere trovati nelle +rispettive pagine di manuale. + +Oltre alle funzioni di ricerca esistono delle ulteriori funzioni che prevedono +una lettura sequenziale delle informazioni mantenute nel \textit{Name Service + Switch} (in sostanza permettono di leggere i file contenenti le informazioni +riga per riga), che sono analoghe a quelle elencate in +tab.~\ref{tab:sys_passwd_func} per le informazioni relative ai dati degli +utenti e dei gruppi. Nel caso specifico dei servizi avremo allora le tre +funzioni \funcd{setservent}, \funcd{getservent} e \funcd{endservent} i cui +prototipi sono: +\begin{functions} + \headdecl{netdb.h} + \funcdecl{void setservent(int stayopen)} + Apre il file \file{/etc/services} e si posiziona al suo inizio. + + \funcdecl{struct servent *getservent(void)} + Legge la voce successiva nel file \file{/etc/services}. + + \funcdecl{void endservent(void)} + Chiude il file \file{/etc/services}. + + \bodydesc{Le due funzioni \func{setservent} e \func{endservent} non + restituiscono nulla, \func{getservent} restituisce il puntatore ad una + struttura \struct{servent} in caso di successo e \const{NULL} in caso di + errore o fine del file.} +\end{functions} + +La prima funzione, \func{getservent}, legge una singola voce a partire dalla +posizione corrente in \file{/etc/services}, pertanto si può eseguire una +lettura sequenziale dello stesso invocandola più volte. Se il file non è +aperto provvede automaticamente ad aprirlo, nel qual caso leggerà la prima +voce. La seconda funzione, \func{setservent}, permette di aprire il file +\file{/etc/services} per una successiva lettura, ma se il file è già stato +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é + una successiva chiamata a \func{getservent} riparte dall'inizio.} La terza +funzione, \funcd{endservent}, provvede semplicemente a chiudere il file. + +Queste tre funzioni per la lettura sequenziale di nuovo sono presenti per +ciascuno dei vari tipi di informazione relative alle reti di +tab.~\ref{tab:name_resolution_functions}; questo significa che esistono +altrettante funzioni nella forma \texttt{setXXXent}, \texttt{getXXXent} e +\texttt{endXXXent}, analoghe alle precedenti per la risoluzione dei servizi, +che abbiamo riportato in tab.~\ref{tab:name_sequential_read}. Essendo, a +parte il tipo di informazione che viene trattato, sostanzialmente identiche +nel funzionamento e di scarso utilizzo, non staremo a trattarle una per una, +rimandando alle rispettive pagine di manuale. + +\begin{table}[!htb] + \centering + \footnotesize + \begin{tabular}[c]{|l|l|l|l|} + \hline + \textbf{Informazione}&\multicolumn{3}{|c|}{\textbf{Funzioni}}\\ + \hline + \hline + indirizzo&\func{sethostent}&\func{gethostent}&\func{endhostent} \\ + servizio &cd te\func{setservent}&\func{getservent}&\func{endservent}\\ + rete &\func{setnetent}&\func{getnetent}&\func{endnetent}\\ + protocollo&\func{setprotoent}&\func{getprotoent}&\func{endprotoent}\\ + \hline + \end{tabular} + \caption{Funzioni lettura sequenziale dei dati del \textit{Name Service + Switch}.} + \label{tab:name_sequential_read} +\end{table} + + + + + +\subsection{Le funzioni avanzate per la risoluzione dei nomi} +\label{sec:sock_advanced_name_services} + +Quelle illustrate nella sezione precedente sono le funzioni classiche per la +risoluzione di nomi ed indirizzi IP, ma abbiamo già visto come esse soffrano +di vari inconvenienti come il fatto che usano informazioni statiche, e non +prevedono la possibilità di avere diverse classi di indirizzi. Anche se sono +state create delle estensioni o metodi diversi che permettono di risolvere +alcuni di questi inconvenienti,\footnote{rimane ad esempio il problema + generico che si deve sapere in anticipo quale tipo di indirizzi IP (IPv4 o + IPv6) corrispondono ad un certo nome a dominio.} comunque esse non +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 +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},\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 +di un servizio; il suo prototipo è: +\begin{functions} + \headdecl{netdb.h} + \headdecl{sys/socket.h} + \headdecl{netdb.h} + + \funcdecl{int getaddrinfo(const char *node, const char *service, const + struct addrinfo *hints, struct addrinfo **res)} + + Esegue una risoluzione di un nome a dominio e di un nome di servizio. + + \bodydesc{La funzione restituisce 0 in caso di successo o un codice di + errore diverso da zero in caso di fallimento.} +\end{functions} + +La funzione prende come primo argomento il nome della macchina che si vuole +risolvere, specificato tramite la stringa \param{node}. Questo argomento, +oltre ad un comune nome a dominio, può indicare anche un indirizzo numerico in +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 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; \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 + \begin{minipage}[c]{15cm} + \includestruct{listati/addrinfo.h} + \end{minipage} + \caption{La struttura \structd{addrinfo} usata nella nuova interfaccia POSIX + per la risoluzione di nomi a dominio e servizi.} + \label{fig:sock_addrinfo_struct} +\end{figure} + +La struttura \struct{addrinfo}, la cui definizione\footnote{la definizione è + ripresa direttamente dal file \texttt{netdb.h} in questa struttura viene + dichiarata, la pagina di manuale riporta \type{size\_t} come tipo di dato + 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 +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, +che viene usato soltanto in ingresso. I tre campi successivi \var{ai\_family}, +\var{ai\_socktype}, e \var{ai\_protocol} contengono rispettivamente la +famiglia di indirizzi, il tipo di socket e il protocollo, in ingresso vengono +usati per impostare una selezione (impostandone il valore nella struttura +puntata da \param{hints}), mentre in uscita indicano il tipo di risultato +contenuto nella struttura. + +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} è un puntatore alla stringa contenente il nome +canonico della macchina, ed infine, quando la funzione restituisce più di un +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 +ricerche generiche. Se però si specifica un valore non nullo questo deve +puntare ad una struttura \struct{addrinfo} precedentemente allocata nella +quale siano stati opportunamente impostati i valori dei campi +\var{ai\_family}, \var{ai\_socktype}, \var{ai\_protocol} ed \var{ai\_flags}. + +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 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. + +Il campo \var{ai\_protocol} permette invece di effettuare la selezione dei +risultati per il nome del servizio usando il numero identificativo del +rispettivo protocollo di trasporto (i cui valori possibili sono riportati in +\file{/etc/protocols}); di nuovo i due soli valori utilizzabili sono quelli +relativi a UDP e TCP, o il valore nullo che indica di ignorare questo campo +nella selezione. + +Infine l'ultimo campo è \var{ai\_flags}; che deve essere impostato come una +maschera binaria; i bit di questa variabile infatti vengono usati per dare +delle indicazioni sul tipo di risoluzione voluta, ed hanno valori analoghi a +quelli visti in sez.~\ref{sec:sock_name_services} per \func{getipnodebyname}; +il valore di \var{ai\_flags} può essere impostata con un OR aritmetico delle +costanti di tab.~\ref{tab:ai_flags_values}, ciascuna delle quali identifica un +bit della maschera. + +\begin{table}[!htb] + \centering + \footnotesize + \begin{tabular}[c]{|l|p{10cm}|} + \hline + \textbf{Costante} & \textbf{Significato} \\ + \hline + \hline + \const{AI\_PASSIVE} & viene utilizzato per ottenere un indirizzo in + formato adatto per una successiva chiamata a + \func{bind}. Se specificato quando si è usato + \const{NULL} come valore per \param{node} gli + indirizzi restituiti saranno inizializzati al + valore generico (\const{INADDR\_ANY} per IPv4 e + \const{IN6ADDR\_ANY\_INIT} per IPv6), altrimenti + verrà usato l'indirizzo dell'interfaccia di + \textit{loopback}. Se invece non è impostato gli + 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 + indirizzo sarà restituito nel campo + \var{ai\_canonname} della prima struttura + \struct{addrinfo} dei risultati. Se il nome + canonico non è disponibile al suo posto + viene restituita una copia di \param{node}. \\ + \const{AI\_NUMERICHOST}& se impostato il nome della macchina specificato + con \param{node} deve essere espresso in forma + numerica, altrimenti sarà restituito un errore + \const{EAI\_NONAME} (vedi + tab.~\ref{tab:addrinfo_error_code}), in questo + modo si evita ogni chiamata alle funzioni di + risoluzione.\\ + \const{AI\_V4MAPPED} & stesso significato dell'analoga di + tab.~\ref{tab:sock_getipnodebyname_flags}.\\ + \const{AI\_ALL} & stesso significato dell'analoga di + tab.~\ref{tab:sock_getipnodebyname_flags}.\\ + \const{AI\_ADDRCONFIG} & stesso significato dell'analoga di + tab.~\ref{tab:sock_getipnodebyname_flags}.\\ + \hline + \end{tabular} + \caption{Costanti associate ai bit del campo \var{ai\_flags} della struttura + \struct{addrinfo}.} + \label{tab:ai_flags_values} +\end{table} + +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 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 scriverà in \param{res} il puntatore +iniziale ad una \index{\textit{linked~list}}\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 +tab.~\ref{tab:addrinfo_error_code}; dato che la funzione utilizza altre +funzioni e chiamate al sistema per ottenere il suo risultato in generale il +valore di \var{errno} non è significativo, eccetto il caso in cui si sia +ricevuto un errore di \const{EAI\_SYSTEM}, nel qual caso l'errore +corrispondente è riportato tramite \var{errno}. + +\begin{table}[!htb] + \centering + \footnotesize + \begin{tabular}[c]{|l|p{10cm}|} + \hline + \textbf{Costante} & \textbf{Significato} \\ + \hline + \hline + \const{EAI\_FAMILY} & la famiglia di indirizzi richiesta non è + supportata. \\ + \const{EAI\_SOCKTYPE}& il tipo di socket richiesto non è supportato. \\ + \const{EAI\_BADFLAGS}& il campo \var{ai\_flags} contiene dei valori non + validi. \\ + \const{EAI\_NONAME} & il nome a dominio o il servizio non sono noti, + viene usato questo errore anche quando si specifica + il valore \const{NULL} per entrambi gli argomenti + \param{node} e \param{service}. \\ + \const{EAI\_SERVICE} & il servizio richiesto non è disponibile per il tipo + di socket richiesto, anche se può esistere per + 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 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 + permanente. \\ + \const{EAI\_AGAIN} & il DNS ha restituito un errore di risoluzione + temporaneo, si può ritentare in seguito. \\ + \const{EAI\_SYSTEM} & c'è stato un errore di sistema, si può controllare + \var{errno} per i dettagli. \\ +% \hline +% estensioni GNU, trovarne la documentazione +% \const{EAI\_INPROGRESS}& richiesta in corso. \\ +% \const{EAI\_CANCELED}& la richiesta è stata cancellata.\\ +% \const{EAI\_NOTCANCELED}& la richiesta non è stata cancellata. \\ +% \const{EAI\_ALLDONE} & tutte le richieste sono complete. \\ +% \const{EAI\_INTR} & richiesta interrotta. \\ + \hline + \end{tabular} + \caption{Costanti associate ai valori dei codici di errore della funzione + \func{getaddrinfo}.} + \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}. + + \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 + \includegraphics[width=10cm]{img/addrinfo_list} + \caption{La \index{\textit{linked~list}}\textit{linked list} delle strutture + \struct{addrinfo} restituite da \func{getaddrinfo}.} + \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 +\index{\textit{linked~list}}\textit{linked list} puntata da \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} + + \funcdecl{void freeaddrinfo(struct addrinfo *res)} + + Libera la memoria allocata da una precedente chiamata a \func{getaddrinfo}. + \bodydesc{La funzione non restituisce nessun codice di errore.} +\end{functions} + +La funzione prende come unico argomento il puntatore \param{res}, ottenuto da +una precedente chiamata a \func{getaddrinfo}, e scandisce la lista delle +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. + +Le descrizioni delle opzioni presenti in tab.~\ref{tab:sock_opt_socklevel} +sono estremamente sommarie, è perciò necessario fornire un po' più di +informazioni. Alcune opzioni inoltre hanno una notevole rilevanza nella +gestione dei socket, e pertanto il loro utilizzo sarà approfondito +separatamente in sez.~\ref{sec:sock_options_main}. Quello che segue è quindi +soltanto un elenco più dettagliato della breve descrizione di +tab.~\ref{tab:sock_opt_socklevel} sul significato delle varie opzioni: +\begin{basedescript}{\desclabelwidth{2.5cm}\desclabelstyle{\nextlinelabel}} + +\item[\const{SO\_KEEPALIVE}] questa opzione abilita un meccanismo di verifica + della persistenza di una connessione associata al socket (ed è pertanto + effettiva solo sui socket che supportano le connessioni, ed è usata + principalmente con il TCP). L'opzione utilizza per \param{optval} un intero + usato come valore logico. Maggiori dettagli sul suo funzionamento sono + forniti in sez.~\ref{sec:sock_options_main}. + +\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{sendto} 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 + (vedi sez.~\ref{sec:unix_socket}) 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, 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}); utilizzando una stringa nulla o un valore nullo per + \param{optlen} si può rimuovere 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 \macro{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 + \macro{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 da altri socket; + 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. Maggiori dettagli sul suo funzionamento sono forniti in + sez.~\ref{sec:sock_options_main}. + +\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 per \param{optval} un intero usato come valore + logico. + +\item[\const{SO\_BROADCAST}] questa opzione abilita il \textit{broadcast}; + quanto abilitata i socket di tipo \const{SOCK\_DGRAM} riceveranno i + pacchetti inviati all'indirizzo di broadcast, e potranno scrivere pacchetti + su tale indirizzo. Prende per \param{optval} un intero usato come valore + logico. L'opzione non ha effetti su un socket di tipo \const{SOCK\_STREAM}. + +\item[\const{SO\_SNDBUF}] questa opzione imposta la dimenzione del buffer di + uscita del socket. Prende per \param{optval} un intero indicante il numero + di byte. Il valore di default ed il valore massimo che si può specificare + come argomento per questa opzione sono impostabili tramiti gli opportuni + valori di \func{sysctl} (vedi sez.~\ref{sec:sock_sysctl}). + +\item[\const{SO\_RCVBUF}] questa opzione imposta la dimenzione del buffer di + ingresso del socket. Prende per \param{optval} un intero indicante il numero + di byte. Il valore di default ed il valore massimo che si può specificare + come argomento per questa opzione sono impostabili tramiti gli opportuni + valori di \func{sysctl} (vedi sez.~\ref{sec:sock_sysctl}). + +\item[\const{SO\_LINGER}] questa opzione controlla le modalità con cui viene + chiuso un socket quando si utilizza un protocollo che supporta le + connessioni (è pertanto usata con i socket TCP ed ignorata per UDP) e + modifica il comportamento delle funzioni \func{close} e \func{shutdown}. + L'opzione richiede che l'argomento \param{optval} sia una struttura di tipo + \struct{linger}, definita in \texttt{sys/socket.h} ed illustrata in + fig.~\ref{fig:sock_linger_struct}. Maggiori dettagli sul suo funzionamento + sono forniti in sez.~\ref{sec:sock_options_main}. + +\item[\const{SO\_PRIORITY}] questa opzione permette di impostare le priorità + per tutti i pacchetti che sono inviati sul socket, prende per \param{optval} + un valore intero. Con questa opzione il kernel usa il valore per ordinare le + priorità sulle code di rete,\footnote{questo richiede che sia abilitato il + sistema di \textit{Quality of Service} disponibile con le opzioni di + routing avanzato.} i pacchetti con priorità più alta vengono processati + per primi, in modalità che dipendono dalla disciplina di gestione della + coda. Nel caso di protocollo IP questa opzione permette anche di impostare i + valori del campo \textit{type of service} (noto come TOS, vedi + sez.~\ref{sec:IP_header}) per i pacchetti uscenti. Per impostare una + priorità al di fuori dell'intervallo di valori fra 0 e 6 sono richiesti i + privilegi di amministratore con la capability \const{CAP\_NET\_ADMIN}. + +\item[\const{SO\_ERROR}] questa opzione riceve un errore presente sul socket; + può essere utilizzata soltanto con \func{getsockopt} e prende per + \param{optval} un valore intero. +\end{basedescript} + + +\subsection{L'uso delle principali opzioni dei socket} +\label{sec:sock_options_main} + +La descrizione sintetica del significato delle opzioni generiche dei socket, +riportata nell'elenco in sez.~\ref{sec:sock_generic_options}, è +necessariamente sintentica, alcune di queste però possono essere utilizzate +per controllare delle funzionalità che hanno una notevole rilevanza nella +programmazione dei socket. Per questo motivo faremo in questa sezione un +approfondimento sul significato delle opzioni generiche più importanti. + + +\index{\texttt{SO\_KEEPALIVE} (costante)|(} +\subsubsection{L'opzione \const{SO\_KEEPALIVE}} + +La prima opzione da approfondire è \const{SO\_KEEPALIVE} che permette di +tenere sotto controllo lo stato di una connessione. Una connessione infatti +resta attiva anche quando non viene effettuato alcun traffico su di essa, +questo può comportare che un crollo della connessione, qualora avvenisse ad +esempio in conseguenza di una interruzione completa della rete, potrebbe +passare inosservato. + +Se si imposta questa opzione, è invece 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) e si applica +principalmente ai socket TCP. + +Con le impostazioni di default (che sono riprese da BSD) Linux emette un +messaggio di \textit{keep-alive}\footnote{in sostanza un segmento ACK vuoto, + cui sarà risposto con un altro segmento ACK vuoto.} 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à; il tutto avviene all'interno del 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 in tal caso + si passa per la chiusura del processo, e questo, come illustrato in + sez.~\ref{sec:file_close}, comporta la chiusura del socket col'invio di un + segmento FIN all'altro capo della connessione, che verrà regolarmente + chiusa.} in questo caso all'invio del messaggio di \textit{keep-alive} si +otterrà come risposta un segmento RST che indica che l'altro capo non +riconosce più l'esistenza della connessione. 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 per 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 suddetti 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}. Qualora la connessione si sia ristabilita e si riceva un +successivo messaggio di risposta il ciclo riparte come se niente fosse +avvenuto. Infine se invece si riceve come risposta un pacchetto ICMP di +destinazione irraggiungibile (vedi sez.~\ref{sec:icmp_protocol_xxx}), verrà +restituito l'errore corrispondente. + +In generale questa opzione serve per individuare una caduta della connessione +anche quando non si sta facendo traffico su di essa. Viene usata +principalmente sui server per evitare di mantenere impegnate le risorse che +verrbbero dedicate a trattare delle connessioni che in realtà sono già +terminate (quelle che vengono anche chiamate connessioni +\textsl{semi-aperte}); in tutti quei casi cioè in cui il server si trova in +attesa di dati in ingresso su una connessione che non arriveranno mai perché o +il client sull'altro capo non è più attivo o non è più in grado di comunicare +con il server via rete. + +\begin{figure}[!htb] + \footnotesize \centering + \begin{minipage}[c]{15cm} + \includecodesample{listati/TCP_echod_fourth.c} + \end{minipage} + \normalsize + \caption{La sezione della nuova versione del server del servizio + \textit{echo} che prevede l'attivazione del \textit{keepalive} sui + socket.} + \label{fig:echod_keepalive_code} +\end{figure} + +Abilitandola dopo un certo tempo le connessioni effettivamente terminate +verrano comunque chiuse per cui, utilizzando ad esempio una \func{select}, se +be potrà rilevare la conclusione e ricevere il relativo errore. Si tenga +presente però che non può avere la certezza assoluta che un errore di +\errcode{ETIMEDOUT} ottenuto dopo aver abilitato questa opzione corrisponda +necessariamente ad una reale conclusione della connessione, il problema +potrebbe anche 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} (anche se questa non è una una condizione molto +probabile). + +Come esempio dell'utilizzo di questa opzione introduciamo all'interno del +nostro server per il servizio \textit{echo} la nuova opzione \texttt{-k} che +permette di attivare il \textit{keep-alive} sui socket; tralasciando la parte +relativa alla gestione di detta opzione (che si limita ad assegnare ad 1 la +variabile \var{keepalive}) tutte le modifiche al server sono riportate in +fig.~\ref{fig:echod_keepalive_code}. Al solito il codice completo è contenuto +nel file \texttt{TCP\_echod\_fourth.c} dei sorgenti allegati alla guida. + +Come si può notare la variabile \var{keepalive} è preimpostata (\texttt{\small + 8}) ad un valore nullo; essa viene utilizzata sia come variabile logica per +la condizione (\texttt{\small 14}) che controlla l'attivazione del +\textit{keep-alive} che come valore dell'argomento \param{optval} della +chiamata a \func{setsockopt} (\texttt{\small 16}). A seconda del suo valore +tutte le volte che un processo figlio viene eseguito in risposta ad una +connessione verrà pertanto eseguita o meno la sezione (\texttt{\small 14--17}) +che esegue l'impostazione di \const{SO\_KEEPALIVE} sul socket connesso, +attivando il relativo comportamento. +\index{\texttt{SO\_KEEPALIVE} (costante)|)} + + +\index{\texttt{SO\_REUSEADDR} (costante)|(} +\subsubsection{L'opzione \const{SO\_REUSEADDR}} + +La seconda opzione da approfondire è \const{SO\_REUSEADDR}, che consente di +eseguire \func{bind} su un socket anche quando la porta specificata è già in +uso da parte di un altro socket. Si ricordi infatti che, come accennato in +sez.~\ref{sec:TCP_func_bind}, normalmente la funzione \func{bind} fallisce con +un errore di \errcode{EADDRINUSE} se la porta scelta è già utilizzata da un +altro socket, proprio per evitare che possano essere lanciati due server sullo +stesso indirizzo e la stessa porta, che verrebbero a contendersi i pacchetti +aventi quella destinazione. + +Esistono però situazioni ed esigenze particolari in cui non si vuole che +questo comportamento di salvaguardia accada, ed allora si può fare ricorso a +questa opzione. La questione è comunque abbastanza complessa in quanto, come +sottolinea Stevens in \cite{UNP1}, si distinguono ben quattro casi diversi in +cui è prevista la possibilità di un utilizzo di questa opzione, il che la +rende una delle più difficili da capire. + +Il primo caso, che è anche il più comune, in cui si fa ricorso a +\const{SO\_REUSEADDR} è quello in cui un server è terminato ma esistono ancora +dei processi figli che mantengono attiva almeno una connessione remota che +utilizza l'indirizzo locale, mantenendo occupata la porta. Quando si riesegue +il server allora questo riceve un errore 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 trovarsi in questa situazione quando si sta + sviluppando un server che si ferma e si riavvia in continuazione dopo aver + fatto modifiche.} Inoltre se si usa il protocollo TCP questo può avvenire +anche dopo tutti i processi figli sono terminati, dato che una connessione può +restare attiva anche dopo la chiusura del socket, mantenendosi nello stato +\texttt{TIME\_WAIT} (vedi sez.~\ref{sec:TCP_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. + +Come esempio di uso di questa connessione abbiamo predisposto una nuova +versione della funzione \func{sockbind} (vedi fig.~\ref{fig:sockbind_code}) +che consenta l'impostazione di questa opzione. La nuova funzione è +\func{sockbindopt}, e le principali differenze rispetto alla precedente sono +illustrate in fig.~\ref{fig:sockbindopt_code}, dove si sono riportate le +sezioni di codice modificate rispetto alla versione precedente. Il codice +completo della funzione si trova, insieme alle altre funzioni di servizio dei +socket, all'interno del file \texttt{SockUtils.c} dei sorgenti allegati alla +guida. + +\begin{figure}[!htb] + \footnotesize \centering + \begin{minipage}[c]{15cm} + \includecodesample{listati/sockbindopt.c} + \end{minipage} + \normalsize + \caption{Le sezioni della funzione \func{sockbindopt} modificate rispetto al + codice della precedente \func{sockbind}.} + \label{fig:sockbindopt_code} +\end{figure} + +In realtà tutto quello che si è fatto è stato introdurre nella nuova funzione +(\texttt{\small 1}) un nuovo argomento intero, \param{reuse}, che conterrà il +valore logico da usare nella successiva chiamata (\texttt{\small 14}) a +\func{setsockopt}. Si è poi aggiunta una sezione (\texttt{\small 13-17}) che +esegue l'impostazione dell'opzione fra la chiamata a \func{socket} e quella a +\func{bind}. + + +A questo punto basterà modificare il server per utilizzare la nuova +funzione; in fig.~\ref{fig:TCP_echod_fifth} abbiamo riportato le sezioni +modificate rispetto alla precedente versione di +fig.~\ref{fig:TCP_echod_third}. Al solito il codice completo è coi sorgenti +allegati alla guida, nel file \texttt{TCP\_echod\_fifth.c}. + +Anche in questo caso si è introdotta (\texttt{\small 8}) una nuova variabile +\var{reuse} che consente di controllare l'uso dell'opzione e che poi sarà +usata (\texttt{\small 14}) come ultimo argomento di \func{setsockopt}. Il +valore di default di questa variabile è nullo, ma usando l'opzione \texttt{-r} +nell'invocazione del server (al solito la gestione delle opzioni non è +riportata in fig.~\ref{fig:TCP_echod_fifth}) se ne potrà impostare ad 1 il +valore, per cui in tal caso la successiva chiamata (\texttt{\small 13-17}) a +\func{setsockopt} attiverà l'opzione \const{SO\_REUSEADDR}. + +\begin{figure}[!htb] + \footnotesize \centering + \begin{minipage}[c]{15cm} + \includecodesample{listati/TCP_echod_fifth.c} + \end{minipage} + \normalsize + \caption{Il nuovo codice per l'apertura passiva del server \textit{echo} che + usa la nuova funzione \func{sockbindopt}.} + \label{fig:TCP_echod_fifth} +\end{figure} + +Il secondo caso in cui viene usata \const{SO\_REUSEADDR} è 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 non risulti occupata da +una precedente chiamata più specifica. 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 locali diversi a +socket diversi. In genere questo viene fatto per i socket UDP quando è +necessario ottenere l'indirizzo a cui sono rivolte le richieste del client ed +il sistema non supporta l'opzione \const{IP\_RECVDSTADDR};\footnote{nel caso + di Linux questa opzione è stata supportata per in certo periodo nello + sviluppo del kernel 2.1.x, ma è in seguito stata soppiantata dall'uso di + \const{IP\_PKTINFO} (vedi sez.~\ref{sec:sock_ipv4_options}).} in tale modo +si può sapere a quale socket corrisponde un certo indirizzo. Non ha senso +fare questa operazione per un socket TCP dato che su di essi si può sempre +invocare \func{getsockname} una volta che si è completata la connessione. + +Infine il quarto caso è quello in cui si vuole effettivamente ottenere un +\textit{completely duplicate binding}, quando cioè si vuole eseguire +\func{bind} su un indirizzo ed una porta che sono già \textsl{legati} ad un +altro socket. Questo ovviamente non ha senso per il normale traffico di rete, +in cui i pacchetti vengono scambiati direttamente fra due applicazioni; ma +quando un sistema supporta il traffico in multicast, in cui una applicazione +invia i pacchetti a molte altre (vedi sez.~\ref{sec:multicast_xxx}), allora ha +senso che su una macchina i pacchetti provenienti dal traffico in multicast +possano essere ricevuti da più applicazioni\footnote{l'esempio classico di + traffico in multicast è quello di uno streaming di dati (audio, video, + ecc.), l'uso del multicast consente in tal caso di trasmettere un solo + pacchetto, che potrà essere ricevuto da tutti i possibili destinatari + (invece di inviarne un duplicato a ciascuno); in questo caso è perfettamente + logico aspettarsi che sulla stessa macchina più utenti possano lanciare un + programma che permetta loro di ricevere gli stessi dati.} o da diverse +istanze della stessa applicazione. + +In questo caso utilizzando \const{SO\_REUSEADDR} si consente ad una +applicazione eseguire \func{bind} sulla stessa porta ed indirizzo usata da +un'altra, così che anche essa possa ricevere gli stessi pacchetti (chiaramente +la cosa non ha alcun senso per i socket TCP, ed infatti in questo tipo di +applicazione è normale l'uso del protovollo UDP). La regola è che quando si +hanno più applicazioni che hanno eseguito \func{bind} sulla stessa porta, di +tutti pacchetti destinati ad un indirizzo di broadcast o di multicast viene +inviata una copia a ciascuna applicazione. Non è definito invece cosa accade +qualora il pacchetto sia destinato ad un indirizzo normale (unicast). + +Essendo questo un caso particolare in alcuni sistemi (come BSD) è stata +introdotta una opzione ulteriore, \const{SO\_REUSEPORT} che richiede che detta +opzione sia specificata per tutti i socket per i quali si vuole eseguire il +\textit{completely duplicate binding}. Nel caso di Linux questa opzione non +esiste, ma il comportamento di \const{SO\_REUSEADDR} è analogo, sarà cioè +possibile effettuare un \textit{completely duplicate binding} ed ottenere il +successo di \func{bind} su un socket legato allo stesso indirizzo e porta solo +se il programma che ha eseguito per primo \func{bind} su di essi ha impostato +questa opzione.\footnote{Questa restrizione permette di evitare il cosiddetto + \textit{port stealing}, in cui un programma, usando \const{SO\_REUSEADDR}, + può collegarsi ad una porta già in uso e ricevere i pacchetti destinati ad + un altro programma; con questa caratteristica ciò è possibile soltanto se il + primo programma a consentirlo, avendo usato fin dall'inizio + \const{SO\_REUSEADDR}.} + +\index{\texttt{SO\_REUSEADDR} (costante)|)} + + +\index{\texttt{SO\_LINGER} (costante)|(} +\subsubsection{L'opzione \const{SO\_LINGER}} + +La terza opzione da approfondire è \const{SO\_LINGER}; essa, come il nome +suggerisce, consente di ``\textsl{indugiare}'' nella chiusura di un socket. Il +comportamento standard sia di \func{close} che \func{shutdown} è infatti +quello di terminare immediatamente dopo la chiamata, mentre il procedimento di +chiusura della connessione (o di un lato di essa) ed il rispettivo invio sulla +rete di tutti i dati ancora presenti nei buffer, viene gestito in sottofondo +dal kernel. + +\begin{figure}[!htb] + \footnotesize \centering + \begin{minipage}[c]{15cm} + \includestruct{listati/linger.h} + \end{minipage} + \caption{La struttura \structd{linger} richiesta come valore dell'argomento + \param{optval} per l'impostazione dell'opzione dei socket + \const{SO\_LINGER}.} + \label{fig:sock_linger_struct} +\end{figure} + +L'uso di \const{SO\_LINGER} con \func{setsockopt} permette di modificare (ed +eventualmente ripristinare) questo comportamento in base ai valori passati nei +campi della stuttura \struct{linger}, illustrata in +fig.~\ref{fig:sock_linger_struct}. Fintanto che il valore del campo +\var{l\_onoff} di \struct{linger} è nullo la modalità che viene impostata +(qualunque sia il valore di \var{l\_linger}) è quella standard appena +illustrata; questa combinazione viene utilizzata per riportarsi al +comportamento normale qualora esso sia stato cambiato da una precedente +chiamata. + +Se si utilizza un valore di \var{l\_onoff} diverso da zero, il comportamento +alla chiusura viene a dipendere dal valore specificato per il campo +\var{l\_linger}; se quest'ultimo è nullo l'uso delle funzioni \func{close} e +\func{shutdown} provoca la terminazione immediata della connessione: nel caso +di TCP cioè non viene eseguito il procedimento di chiusura illustrato in +sez.~\ref{sec:TCP_conn_term}, ma tutti i dati ancora presenti nel buffer +vengono immediatamente scartati e sulla rete viene inviato un segmento di RST +che termina immediatamente la connessione. + +Un esempio di questo comportamento si può abilitare nel nostro client del +servizio \textit{echo} utilizzando l'opzione \texttt{-r}; riportiamo in +fig.~\ref{fig:TCP_echo_sixth} la sezione di codice che permette di introdurre +questa funzionalità,; al solito il codice completo è disponibile nei sorgenti +allegati. + +\begin{figure}[!htb] + \footnotesize \centering + \begin{minipage}[c]{15cm} + \includecodesample{listati/TCP_echo_sixth.c} + \end{minipage} + \normalsize + \caption{La sezione del codice del client \textit{echo} che imposta la + terminazione immediata della connessione in caso di chiusura.} + \label{fig:TCP_echo_sixth} +\end{figure} + +La sezione indicata viene eseguita dopo aver effettuato la connessione e prima +di chiamare la funzione di gestione, cioè fra le righe (\texttt{\small 12}) e +(\texttt{\small 13}) del precedente esempio di fig.~\ref{fig:TCP_echo_fifth}. +Il codice si limita semplicememente a controllare (\texttt{\small 3}) il +valore della variabile \var{reset} che assegnata nella gestione delle opzioni +in corrispondenza all'uso di \texttt{-r} nella chiamata del client. Nel caso +questa sia diversa da zero vengono impostati (\texttt{\small 5--6}) i valori +della struttura \var{ling} che permettono una terminazione immediata della +connessine. Questa viene poi usata nella successiva (\texttt{\small 7}) +chiamata a \func{setsockopt}. Al solito si controlla (\texttt{\small 7--10}) +il valore di ritorno e si termina il programma in caso di errore, stampadone +il valore. + +Infine l'ultima possibilità, quella in cui si utilizza effettivamente +\const{SO\_LINGER} per \textsl{indugiare} nella chiusura, è quella in cui sia +\var{l\_onoff} che \var{l\_linger} hanno un valore diverso da zero. Se si +esegue l'impostazione con questi valori sia \func{close} che \func{shutdown} +si bloccano, nel frattempo viene eseguita la normale procedura di conclusione +della connessione (quella di sez.~\ref{sec:TCP_conn_term}) ma entrambe le +funzioni non ritornano fintanto che non si sia concluso il procedimento di +chiusura della connessione, o non sia passato un numero di +secondi\footnote{questa è l'unità di misura indicata da POSIX ed adottata da + Linux, altri kernel possono usare unità di misura diverse, oppure usare il + campo \var{l\_linger} come valore logico (ignorandone il valore) per rendere + (quando diverso da zero) \func{close} e \func{shutdown} bloccanti fino al + completamento della trasmissione dei dati sul buffer.} pari al valore +specificato in \var{l\_linger}. + + + +\index{\texttt{SO\_LINGER} (costante)|)} + + + + + +\subsection{Le opzioni per il protocollo IPv4} +\label{sec:sock_ipv4_options} + +Il secondo insieme di opzioni dei socket che tratteremo è quello relativo ai +socket che usano il protocollo IPv4.\footnote{come per le precedenti opzioni + generiche una descrizione di esse è disponibile nella settima sezione delle + pagine di manuale, nel caso specifico la documentazione si può consultare + con \texttt{man 7 ip}.} Se si vuole operare su queste opzioni generiche il +livello da utilizzare è \const{SOL\_IP}; si è riportato un elenco di queste +opzioni in tab.~\ref{tab:sock_opt_iplevel}. Le costanti indicanti le opzioni e +tutte le altre costanti ad esse collegate sono definite in +\file{netinet/ip.h}, ed accessibili includendo detto file. + + +\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{IP\_OPTIONS} &$\bullet$&$\bullet$&$\bullet$&\texttt{int}& + Imposta o riceve le opzioni di IP.\\ + \const{IP\_PKTINFO} &$\bullet$&$\bullet$&$\bullet$&\texttt{int}& + Passa un messaggio di informazione.\\ + \const{IP\_RECVTOS} &$\bullet$&$\bullet$&$\bullet$&\texttt{int}& + Passa un messaggio col campo TOS.\\ + \const{IP\_RECVTTL} &$\bullet$&$\bullet$&$\bullet$&\texttt{int}& + Passa un messaggio col campo TTL.\\ + \const{IP\_RECVOPTS} &$\bullet$&$\bullet$&$\bullet$&\texttt{int}& + Passa un messaggio con le opzioni IP.\\ + \const{IP\_RETOPTS} &$\bullet$&$\bullet$&$\bullet$&\texttt{int}& + Passa un messaggio con le opzioni IP non trattate.\\ + \const{IP\_TOS} &$\bullet$&$\bullet$&$\bullet$&\texttt{int}& + Imposta il valore del campo TOS.\\ + \const{IP\_TTL} &$\bullet$&$\bullet$&$\bullet$&\texttt{int}& + Imposta il valore del campo TTL.\\ + \const{IP\_HDRINCL} &$\bullet$&$\bullet$&$\bullet$&\texttt{int}& + Passa l'intestazione di IP nei dati.\\ + \const{IP\_RECVERR} &$\bullet$&$\bullet$&$\bullet$&\texttt{int}& + Abilita la gestione degli errori.\\ + \const{IP\_MTU\_DISCOVER} &$\bullet$&$\bullet$&$\bullet$&\texttt{int}& + Imposta il Path MTU Discovery.\\ + \const{IP\_MTU} &$\bullet$& &$\bullet$&\texttt{int}& + Legge il valore attuale della MTU.\\ + \const{IP\_ROUTER\_ALERT} &$\bullet$&$\bullet$&$\bullet$&\texttt{int}& + Imposta l'opzione \textit{IP router alert} sui pacchetti.\\ + \const{IP\_MULTICAST\_TTL} &$\bullet$&$\bullet$&$\bullet$&\texttt{int}& + Imposta il TTL per i pacchetti multicast.\\ + \const{IP\_MULTICAST\_LOOP} &$\bullet$&$\bullet$&$\bullet$&\texttt{int}& + Controlla il reinvio a se stessi dei dati di multicast.\\ + \const{IP\_ADD\_MEMBERSHIP} & &$\bullet$&$\bullet$&\texttt{int}& + Si unisce a un gruppo di multicast.\\ + \const{IP\_DROP\_MEMBERSHIP}& &$\bullet$&$\bullet$&\texttt{int}& + Si sgancia da un gruppo di multicast.\\ + \const{IP\_MULTICAST\_IF} &$\bullet$&$\bullet$&$\bullet$&\texttt{int}& + Imposta l'interfaccia locale di un socket multicast.\\ + \hline + \end{tabular} + \caption{Le opzioni disponibili al livello \const{SOL\_IP}.} + \label{tab:sock_opt_iplevel} +\end{table} + +Le descrizioni di tab.~\ref{tab:sock_opt_iplevel} sono estremamente succinte, +una maggiore quantità di dettagli su queste opzioni è fornito nel seguente +elenco: +\begin{basedescript}{\desclabelwidth{2.5cm}\desclabelstyle{\nextlinelabel}} + + +\item[\const{IP\_OPTIONS}] l'opzione permette di impostare o leggere le + opzioni del protocollo IP (si veda sez.~\ref{sec:IP_options}). L'opzione + prende come valore dell'argomento \param{optval} un puntatore ad un buffer + dove sono mantenute le opzioni, mentre \param{optlen} indica la dimensione + di quest'ultimo. Quando la si usa con \func{getsockopt} vengono lette le + opzioni IP utilizzate per la spedizione, quando la si usa con + \func{setsockopt} vengono impostate le opzioni specificate. L'uso di questa + opzione richiede una profonda conoscenza del funzionamento del protocollo, + torneremo in parte sull'argomento in sez.~\ref{sec:sock_advanced_xxx}. + + +\item[\const{IP\_PKTINFO}] Quando abilitata l'opzione permette di ricevere + insieme ai pacchetti un messaggio ancillare (vedi + sez.~\ref{sec:TCP_ancillary_data}) di tipo \const{IP\_PKTINFO} contenente + una struttura \struct{pktinfo} (vedi fig.~\ref{fig:sock_pktinfo_struct}) che + mantiene una serie di informazioni riguardo i pacchetti in arrivo. In + particolare è possibile conoscere l'interfaccia su cui è stato ricevuto un + pacchetto (nel campo \var{ipi\_ifindex}), l'indirizzo locale da esso + utilizzato (nel campo \var{ipi\_spec\_dst}) e l'indirizzo remoto dello + stesso (nel campo \var{ipi\_addr}). + +\begin{figure}[!htb] + \footnotesize \centering + \begin{minipage}[c]{15cm} + \includestruct{listati/pktinfo.h} + \end{minipage} + \caption{La struttura \structd{pktinfo} usata dall'opzione + \const{IP\_PKTINFO} per ricavare informazioni sui pacchetti di un socket + di tipo \const{SOCK\_DGRAM}.} + \label{fig:sock_pktinfo_struct} +\end{figure} + + +L'opzione è utilizzabile solo per socket di tipo \const{SOCK\_DGRAM}. Questa è +una opzione introdotta con i kernel della serie 2.2.x, ed è specifica di +Linux;\footnote{non dovrebbe pertanto essere utilizzata se si ha a cuore la + portabilità.} essa permette di sostituire le opzioni \const{IP\_RECVDSTADDR} +e \const{IP\_RECVIF} presenti in altri Unix (la relativa informazione è quella +ottenibile rispettivamente dai campi \var{ipi\_addr} e \var{ipi\_ifindex} di +\struct{pktinfo}). + + +\item[\const{IP\_RECVTOS}] Quando abilitata l'opzione permette di ricevere + insieme ai pacchetti un messaggio ancillare di tipo \const{IP\_TOS}, che + contiene un byte con il valore del campo \textit{Type of Service} + dell'intestazione IP del pacchetto stesso (vedi sez.~\ref{sec:IP_header}). + Prende per \param{optval} un intero usato come valore logico. + +\item[\const{IP\_RECVTTL}] Quando abilitata l'opzione permette di ricevere + insieme ai pacchetti un messaggio ancillare di tipo \const{IP\_RECVTTL}, + contenente un byte con il valore del campo \textit{Time to Live} + dell'intestazione IP (vedi sez.~\ref{sec:IP_header}). L'opzione richiede + per \param{optval} un intero usato come valore logico. L'opzione non è + supportata per socket di tipo \const{SOCK\_STREAM}. + +\item[\const{IP\_RECVOPTS}] Quando abilitata l'opzione permette di ricevere + insieme ai pacchetti un messaggio ancillare di tipo \const{IP\_OPTIONS}, + contenente le opzioni IP del protocollo (vedi sez.~\ref{sec:IP_options}). Le + intestazioni di instradamento e le altre opzioni sono già riempite con i + dati locali. L'opzione richiede per \param{optval} un intero usato come + valore logico. L'opzione non è supportata per socket di tipo + \const{SOCK\_STREAM}. + +\item[\const{IP\_RETOPTS}] Identica alla precedente \const{IP\_RECVOPTS}, ma + in questo caso restituisce i dati grezzi delle opzioni, senza che siano + riempiti i capi di instradamento e le marche temporali. L'opzione richiede + per \param{optval} un intero usato come valore logico. L'opzione non è + supportata per socket di tipo \const{SOCK\_STREAM}. + + +\item[\const{IP\_TOS}] L'opzione consente di leggere o impostare il campo + \textit{Type of Service} dell'intestazione IP (vedi + sez.~\ref{sec:IP_header}) che permette di indicare le priorità dei + pacchetti. Il campo TOS è di 8 bit e l'opzione richiede per \param{optval} + un intero che ne contiene il valore. Sono definite anche alcune costanti che + definiscono alcuni valori standardizzati per il \textit{Type of Service}, + riportate in tab.~\ref{tab:IP_TOS_values}, il valore di default usato da + Linux è \const{IPTOS\_LOWDELAY}, ma esso può essere modificato con le + funzionalità del cosiddetto \textit{Advanced Routing}. Si ricordi che la + priorità dei pacchetti può essere impostata anche in maniera indipendente + dal protocollo utilizzando l'opzione \const{SO\_PRIORITY} illustrata in + sez.~\ref{sec:sock_generic_options}. + + +\item[\const{IP\_TTL}] L'opzione consente di leggere o impostare il campo + \textit{Time to Live} dell'intestazione IP (vedi sez.~\ref{sec:IP_header}). + Il campo TTL è di 8 bit e l'opzione richiede che \param{optval} sia un + intero, che ne conterrà il valore. + + +\item[\const{IP\_HDRINCL}] Se abilitata l'utente deve fornire lui stesso + l'intestazione IP in cima ai propri dati. L'opzione è valida soltanto per + socket di tipo \const{SOCK\_RAW}, e quando utilizzata eventuali valori + impostati con \const{IP\_OPTIONS}, \const{IP\_TOS} o \const{IP\_TTL} sono + ignorati. In ogni caso prima della spedizione alcuni campi + dell'instestazione vengono comunque modificati dal kernel, torneremo + sull'argomento in sez.~\ref{sec:socket_raw_xxx} + + +\item[\const{IP\_RECVERR}] Questa è una opzione introdotta con i kernel della + serie 2.2.x, ed è specifica di Linux. Essa permette di usufruire di un + meccanismo affidabile per ottenere un maggior numero di informazioni in caso + di errori. Se l'opzione è abilitata tutti gli errori generati su un socket + vengono memorizzati su una coda, dalla quale poi possono essere letti con + \func{recvmsg} (torneremo su questo in sez.~\ref{sec:TCP_ancillary_data}). + L'opzione richiede per \param{optval} un intero usato come valore logico; + l'opzione non è applicabile a socket di tipo \const{SOCK\_STREAM}. + +\item[\const{IP\_MTU\_DISCOVER}] Questa è una opzione introdotta con i kernel + della serie 2.2.x, ed è specifica di Linux. L'opzione permette di scrivere + o leggere le impostazioni usante nella determinazione della \textit{Maximum + Tranfer Unit} (vedi sez.~\ref{sec:net_lim_dim}) per il socket. + +\item[\const{IP\_MTU}] Permette di leggere il valore della \textit{Maximum + Tranfer Unit} di percorso del socket. L'opzione richiede per + \param{optval} un intero che conterrà il valore della MTU in byte. Questa è + una opzione introdotta con i kernel della serie 2.2.x, ed è specifica di + Linux. + +\item[\const{IP\_ROUTER\_ALERT}] Questa è una opzione introdotta con i kernel + della serie 2.2.x, ed è specifica di Linux. + +\item[\const{IP\_MULTICAST\_TTL}] L'opzione permette di impostare o leggere il + valore del campo TTL per i pacchetti in uscita associati al socket. È + importante che questo valore sia il più basso possibile, ed il default è 1, + che significa che i pacchetti non potranno uscire dalla rete locale. Questa + opzione consente ai programmi che lo richiedono di superare questo limite. + L'opzione richiede per \param{optval} un intero che conterrà il valore del + TTL. + +\item[\const{IP\_MULTICAST\_LOOP}] L'opzione consente di decidere se i dati + che si inviano su un socket usato con il multicast vengano ricevuti anche + sulla stessa macchina da cui li si stanno inviando. Prende per + \param{optval} un intero usato come valore logico. + + In generale se si vuole che eventuali client possano ricevere i dati che si + inviano occorre che questa funzionalità sia abilitata (come avviene di + default). Qualora però non si voglia generare traffico per dati che già sono + disponibili in locale l'uso di questa opzione permette di disabilitare + questo tipo di traffico. + +\item[\const{IP\_ADD\_MEMBERSHIP}] + +\item[\const{IP\_DROP\_MEMBERSHIP}] + +\item[\const{IP\_MULTICAST\_IF}] + + +\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 alle proprietà dei socket. La differenza nell'uso di +\func{sysctl} rispetto alle funzioni viste finora è che esse consentono di +controllare le proprietà di un singolo socket, mentre con \func{sysctl} si +impostano proprietà (o valori di default) validi a livello dell'intero +sistema. + +Le opzioni disponibili per le proprietà della rete sono riportate nella +gerarchia dei valori impostabili con \func{sysctl}, sotto il nodo +\texttt{net}, o, se acceduti tramite l'interfaccia del filesystem +\texttt{/proc}, sotto \texttt{/proc/sys/net}. In genere sotto questa directory +compaiono le sottodirectory (corrispondenti ad altrettanti sottonodi per +\func{sysctl}) relative ai vari protocolli e tipi di interfacce su cui è +possibile intervenire per effettuare impostazioni; un contenuto tipico di +questa directory è il seguente: +\begin{verbatim} +/proc/sys/net/ +|-- core +|-- ethernet +|-- ipv4 +|-- ipv6 +|-- irda +|-- token-ring +`-- unix +\end{verbatim} +e sono presenti varie centinaia di diversi parametri; nel nostro caso ci +limiteremo a vedere quelli più significativi. + +Nella directory \texttt{/proc/sys/net/core} sono disponibili i parametri +generici validi per tutti i socket, quelli descritti anche nella rispettiva +pagina di manuale.\footnote{quella accessibile con \texttt{man 7 socket}.} +I principali sono: +\begin{basedescript}{\desclabelwidth{3cm}\desclabelstyle{\nextlinelabel}} +\item[\texttt{rmem\_default}] imposta la dimensione di default del buffer di + lettura (cioè per i dati in ingresso) dei socket. +\item[\texttt{rmem\_max}] imposta la dimensione massima che si può assegnare al + buffer di ingresso dei socket attraverso l'uso dell'opzione + \const{SO\_RCVBUF}. +\item[\texttt{wmem\_default}] imposta la dimensione di default del buffer di + scrittura (cioè per i dati in uscita) dei socket. +\item[\texttt{wmem\_max}] imposta la dimensione massima che si può assegnare al + buffer di uscita dei socket attraverso l'uso dell'opzione + \const{SO\_SNDBUF}. +\item[\texttt{message\_cost}] +\item[\texttt{message\_burst}] +\item[\texttt{netdev\_max\_backlog}] +\item[\texttt{optmem\_max}] +\end{basedescript} +Nella directory \texttt{/proc/sys/net/ipv4} sono disponibili i parametri per i +socket IPv4, descritti anche nella rispettiva pagina di +manuale.\footnote{quella accessibile con \texttt{man 7 ip}.} I principali +sono: +\begin{basedescript}{\desclabelwidth{3cm}\desclabelstyle{\nextlinelabel}} +\item[\texttt{ip\_no\_pmtu\_disc}] imposta la discliplina di ricerca della + \textit{Path MTU} (vedi sez.~\ref{sec:net_lim_dim} e + sez.~\ref{sec:sock_ipv4_options}). +\end{basedescript}