X-Git-Url: https://gapil.gnulinux.it/gitweb/?p=gapil.git;a=blobdiff_plain;f=sockctrl.tex;h=4b07f89b92e601cd8103f02a2ab5b7a7ec3a6d49;hp=dbe5af3b1c056293f8ab5b8589cb36c67466bb11;hb=f209f0494e4dd9065f3bf0cda54612297b3e079c;hpb=3328cad7520ab00c506771acf63b2abecc0b8fc7 diff --git a/sockctrl.tex b/sockctrl.tex index dbe5af3..4b07f89 100644 --- a/sockctrl.tex +++ b/sockctrl.tex @@ -705,20 +705,20 @@ suoi risultati. \begin{figure}[!htb] \footnotesize \centering \begin{minipage}[c]{15cm} - \includecodesample{listati/myhost.c} + \includecodesample{listati/mygethost.c} \end{minipage} \normalsize \caption{Esempio di codice per la risoluzione di un indirizzo.} - \label{fig:myhost_example} + \label{fig:mygethost_example} \end{figure} Vediamo allora un primo esempio dell'uso delle funzioni di risoluzione, in -fig.~\ref{fig:myhost_example} è riportato un estratto del codice di un +fig.~\ref{fig:mygethost_example} è riportato un estratto del codice di un programma che esegue una semplice interrogazione al \textit{resolver} usando \func{gethostbyname} e poi ne stampa a video i risultati. Al solito il sorgente completo, che comprende il trattamento delle opzioni ed una funzione -per stampare un messaggio di aiuto, è nel file \texttt{myhost.c} dei sorgenti -allegati alla guida. +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 @@ -1199,16 +1199,18 @@ forniscono una interfaccia sufficientemente generica. Inoltre in genere quando si ha a che fare con i socket non esiste soltanto il problema della risoluzione del nome che identifica la macchina, ma anche quello del servizio a cui ci si vuole rivolgere. Per questo motivo con lo -standard Posix 1003.1-2001 sono state indicate come deprecate le varie +standard POSIX 1003.1-2001 sono state indicate come deprecate le varie funzioni \func{gethostbyaddr}, \func{gethostbyname}, \var{getipnodebyname} e \var{getipnodebyaddr} ed è stata introdotta una interfaccia completamente nuova. -La prima funzione di questa interfaccia, che combina le funzionalità di -\func{getipnodebyname}, \func{getipnodebyaddr}, \func{getservbyname} e -\func{getservbyport}, consentendo di ottenere contemporaneamente la -risoluzione di un indirizzo e di un servizio, è \funcd{getaddrinfo}, il cui -prototipo è: +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} @@ -1224,29 +1226,742 @@ prototipo \end{functions} La funzione prende come primo argomento il nome della macchina che si vuole -risolvere. Questo, oltre ad un comune nome a dominio, può essere 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 specifica invece il nome del -servizio che si intende risolvere. Uno di questi due argomenti può essere -anche inizializzato a \const{NULL} +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 verrano 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 \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 \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 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 +sucessivo ciclo (\texttt{\small 14--35}) di scansione della lista delle +strutture \struct{addrinfo} restituite dalla funzione. Prima di eseguire +questa scansione (\texttt{\small 12}) viene stampato il valore del nome +canonico che è presente solo nella prima struttura. + +La scansione viene ripetuta (\texttt{\small 14}) fintanto che si ha un +puntatore valido. La selezione principale è fatta sul campo \var{ai\_family}, +che stabilisce a quale famiglia di indirizzi fa riferimento la struttura in +esame. Le possibilità sono due, un indirizzo IPv4 o IPv6, se nessuna delle due +si verifica si provvede (\texttt{\small 27--30}) a stampare un messaggio di +errore ed uscire.\footnote{questa eventualità non dovrebbe mai verificarsi, + almeno fintanto che la funzione \func{getaddrinfo} lavora correttamente.} + +Per ciascuno delle due possibili famiglie di indirizzi si estraggono le +informazioni che poi verranno stampate alla fine del ciclo (\texttt{\small + 31--34}). Il primo caso esaminato (\texttt{\small 15--21}) è quello degli +indirizzi IPv4, nel qual caso prima se ne stampa l'indentificazione +(\texttt{\small 16}) poi si provvede a ricavare la struttura degli indirizzi +(\texttt{\small 17}) indirizzata dal campo \var{ai\_addr}, eseguendo un +opportuno casting del puntatore per poter estrarre da questa la porta +(\texttt{\small 18}) e poi l'indirizzo (\texttt{\small 19}) che verrà +convertito con una chiamata ad \func{inet\_ntop}. + +La stessa operazione (\texttt{\small 21--27}) viene ripetuta per gli indirizzi +IPv6, usando la rispettiva struttura degli indirizzi. Si noti anche come in +entrambi i casi per la chiamata a \func{inet\_ntop} si sia dovuto passare il +puntatore al campo contenente l'indirizzo IP nella struttura puntata dal campo +\var{ai\_addr}.\footnote{il meccanismo è complesso a causa del fatto che al + contrario di IPv4, in cui l'indirizzo IP può essere espresso con un semplice + numero intero, in IPv6 questo deve essere necessariamente fornito come + struttura, e pertanto anche se nella struttura puntata da \var{ai\_addr} + sono presenti direttamente i valori finali, per l'uso con \func{inet\_ntop} + occorre comunque passare un puntatore agli stessi (ed il costrutto + \code{\&addr6->sin6\_addr} è corretto in quanto l'operatore \texttt{->} ha + on questo caso precedenza su \texttt{\&}).} + +Una volta estratte dalla struttura \struct{addrinfo} tutte le informazioni +relative alla risoluzione richiesta e stampati i relativi valori, l'ultimo +passo (\texttt{\small 34}) è di estrarre da \var{ai\_next} l'indirizzo della +eventuale successiva struttura presente nella lista e ripetere il ciclo, fin +tanto che, completata la scansione, questo avrà un valore nullo e si potrà +terminare (\texttt{\small 36}) il programma. + +Si tenga presente che \func{getaddrinfo} non garantisce nessun particolare +ordinamento della lista delle strutture \struct{addrinfo} restituite, anche se +usualmente i vari indirizzi IP (se ne è presente più di uno) sono forniti +nello stesso ordine in cui vengono inviati dal server DNS. In particolare +nulla garantisce che vengano forniti prima i dati relativi ai servizi di un +determinato protocollo o tipo di socket, se ne sono presenti di diversi. Se +allora utilizziamo il nostro programma potremo verificare il risultato: +\begin{Verbatim} +[piccardi@gont sources]$ ./mygetaddr -c gapil.truelite.it echo +Canonical name sources2.truelite.it +IPv4 address: + Indirizzo 62.48.34.25 + Protocollo 6 + Porta 7 +IPv4 address: + Indirizzo 62.48.34.25 + Protocollo 17 + Porta 7 +\end{Verbatim} +%$ + +Una volta estratti i risultati dalla \textit{linked list} puntata da +\param{res} 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)} + + + \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; il secondo argomento, \param{level} indica invece il livello +a cui si intende impostare l'opzione. Come abbiamo visto in +sez.~\ref{sec:net_protocols} infatti i protocolli di rete sono strutturati su +più livelli; pertanto anche le proprietà e le opzioni disponibili dipendono +dai protocolli usati dal socket sul quale si va ad agire, e saranno anche esse +differenziate a seconda del protocollo cui fanno riferimento. + + + + +Il valore di \param{level} seleziona allora il livello sul quale si va ad +intervenire e permette di usare le opzioni definite su quel livello. Esiste +poi il valore \const{SOL\_SOCKET} che indica un livello generico e cioè le +opzioni disponibili per qualunque tipo di socket. Per impostare le opzioni +relative alle funzionalità disponibili per socket che usano particolari +protocolli può utilizzare il valore numerico che identifica questi ultimi in +\file{/etc/protocols}, ma più comunemente si suano le apposite costanti +\texttt{SOL\_*} riportate in tab.~\ref{tab:sock_option_levels} dove si sono +riassunti i possibili valori 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 delle + analoghe \texttt{IPPROTO\_*} (citate anche da Stevens in \cite{UNP1}) che di + nuovo 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, dato poi che il suo valore, 1, è anche 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} + + + +\subsection{Le opzioni generiche} +\label{sec:sock_generic_options} -Finora abbiamo trattato i socket nel loro comportamento più comune, è però -possibile attivare alcune modalità diverse di funzionamento degli stessi +Anche se ciascun tipo di socket presenta una serie di caratteristiche +particolari, gestite attraverso delle opzioni specifiche, ma esiste un insieme +generico di opzioni che possono applicarsi a qualunque tipo di socket. -Dato che la maggior parte delle opzioni dei socket sono relative ai socket -TCP, ed hanno poi significato analogo quando usate con altri socket, abbiamo -preferito trattare l'argomento in generale in questa sezione piuttosto che nel -capitolo dedicato alla trattazione generica dei socket. \section{Altre funzioni di controllo} -\label{sec:TCP_sock_ctrl} +\label{sec:sock_ctrl_func} + +Benché la maggior parte delle caratteristiche dei socket sia gestita +attraverso le due funzioni \func{setsockopt} e \func{getsockopt}, alcune +funzionalità possono essere impostate attraverso quelle che sono le funzioni +classiche per il controllo delle proprietà dei file, cioè \func{fcntl} e +\func{ioctl}. + + +\subsection{L'uso di \func{fcntl} per i socket} +\label{sec:sock_fcntl} + +Abbiamo già trattato l'uso di \func{fcntl} in sez.~\ref{sec:file_fcntl}, dove +però ne abbiamo descritto le funzionalità nell'ambito della sua applicazione a +file descriptor associati a file normali; tratteremo qui invece il suo uso +specifico quando la si impiega su file descriptor associati a dei socket. + + +\subsection{L'uso di \func{ioctl} per i socket} +\label{sec:sock_ioctl} + +Come per \func{fcntl} abbiamo trattato l'uso di \func{ioctl} in +sez.~\ref{sec:file_ioctl}, dove ne abbiamo descritto le funzionalità +nell'ambito dell'applicazione su file normali; tratteremo qui il suo uso +specifico quando la si impiega su file descriptor associati a dei socket. + + +\subsection{L'uso di \func{sysctl} per le proprietà della rete} +\label{sec:sock_sysctl} +Come ultimo argomento di questa sezione tratteremo l'uso della funzione +\func{sysctl} (che è stata introdotta nelle sue funzionalità generiche in +sez.~\ref{sec:sys_sysctl}) per quanto riguarda le sue capacità di effettuare +impostazioni relative a proprietà generali dei socket (di tutti quelli di un +certo tipo o di tutti quelli che usano un certo protocollo) rispetto alle +funzioni viste finora che consentono di controllare quelle di un singolo +socket.