X-Git-Url: https://gapil.gnulinux.it/gitweb/?p=gapil.git;a=blobdiff_plain;f=sockctrl.tex;h=62fd3d7037692a521a027423fa3d9f40f293cf9a;hp=8f6e63426e0d2b8315e55254a127d735e033e96b;hb=eefeeaee57a56cfe1ff8c02a26c44d6f87f1368c;hpb=4c6bdffb5a1a4746b241d8323faaf3a49a4633a9 diff --git a/sockctrl.tex b/sockctrl.tex index 8f6e634..62fd3d7 100644 --- a/sockctrl.tex +++ b/sockctrl.tex @@ -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 è \funcd{getaddrinfo}, che combina le +La prima funzione di questa interfaccia è \funcd{getaddrinfo},\footnote{la + funzione è definita, insieme a \func{getnameinfo} che vedremo più avanti, + nell'\href{http://www.ietf.org/rfc/rfc2553.txt} {RFC~2553}.} che combina le funzionalità delle precedenti \func{getipnodebyname}, \func{getipnodebyaddr}, \func{getservbyname} e \func{getservbyport}, consentendo di ottenere contemporaneamente sia la risoluzione di un indirizzo simbolico che del nome -un servizio; il suo prototipo è: +di un servizio; il suo prototipo è: \begin{functions} \headdecl{netdb.h} \headdecl{sys/socket.h} @@ -1230,16 +1232,16 @@ forma \textit{dotted-decimal} per IPv4 o in formato esadecimale per IPv6. Si può anche specificare il nome di una rete invece che di una singola macchina. Il secondo argomento, \param{service}, specifica invece il nome del servizio che si intende risolvere. Per uno dei due argomenti si può anche usare il -valore \const{NULL}, nel qual caso la risoluzione verrà effettuata utilizzando -soltantoo sulla base del valore dell'altro. +valore \const{NULL}, nel qual caso la risoluzione verrà effettuata soltanto +sulla base del valore dell'altro. Il terzo argomento, \param{hints}, deve essere invece un puntatore ad una struttura \struct{addrinfo} usata per dare dei \textsl{suggerimenti} al procedimento di risoluzione riguardo al protocollo o del tipo di socket che si -intenderà utilizzare; la funzione infatti permette di effettuare ricerche -generiche sugli indirizzi, usando sia IPv4 che IPv6, e richiedere risoluzioni -sui nomi dei servizi indipendentemente dal protocollo (ad esempio TCP o UDP) -che questi possono utilizzare. +intenderà utilizzare; \func{getaddrinfo} infatti permette di effettuare +ricerche generiche sugli indirizzi, usando sia IPv4 che IPv6, e richiedere +risoluzioni sui nomi dei servizi indipendentemente dal protocollo (ad esempio +TCP o UDP) che questi possono utilizzare. \begin{figure}[!htb] \footnotesize \centering @@ -1257,7 +1259,7 @@ La struttura \struct{addrinfo}, la cui definizione\footnote{la definizione per il campo \var{ai\_addrlen}, qui viene usata quanto previsto dallo standard POSIX, in cui viene utilizzato \type{socklen\_t}; i due tipi di dati sono comunque equivalenti.} è riportata in -fig.~\ref{fig:sock_addrinfo_struct} viene usata sia in ingresso, per passare +fig.~\ref{fig:sock_addrinfo_struct}, viene usata sia in ingresso, per passare dei valori di controllo alla funzione, che in uscita, per ricevere i risultati. Il primo campo, \var{ai\_flags}, è una maschera binaria di bit che permettono di controllare le varie modalità di risoluzione degli indirizzi, @@ -1272,10 +1274,10 @@ Tutti i campi seguenti vengono usati soltanto in uscita; il campo \var{ai\_addrlen} indica la dimensione della struttura degli indirizzi ottenuta come risultato, il cui contenuto sarà memorizzato nella struttura \struct{sockaddr} posta all'indirizzo puntato dal campo \var{ai\_addr}. Il -campo \var{ai\_canonname} è il puntatore alla stringa contenente il nome +campo \var{ai\_canonname} è un puntatore alla stringa contenente il nome canonico della macchina, ed infine, quando la funzione restituisce più di un -risultato, \var{ai\_next} è il puntatore alla struttura \struct{addrinfo} -successiva della lista. +risultato, \var{ai\_next} è un puntatore alla successiva struttura +\struct{addrinfo} della lista. Ovviamente non è necessario dare dei suggerimenti in ingresso, ed usando \const{NULL} come valore per l'argomento \param{hints} si possono compiere @@ -1288,10 +1290,10 @@ I due campi \var{ai\_family} e \var{ai\_socktype} prendono gli stessi valori degli analoghi argomenti della funzione \func{socket}; in particolare per \var{ai\_family} si possono usare i valori di tab.~\ref{tab:net_pf_names} ma sono presi in considerazione solo \const{PF\_INET} e \const{PF\_INET6}, mentre -se non si vuole specificare questo nessuna famiglia di indirizzi si può usare -il valore \const{PF\_UNSPEC}. Allo stesso modo per \var{ai\_socktype} si -possono usare i valori illustrati sez.~\ref{sec:sock_type} per indicare per -quale tipo di socket si vuole risolvere il servizio indicato, anche se i soli +se non si vuole specificare nessuna famiglia di indirizzi si può usare il +valore \const{PF\_UNSPEC}. Allo stesso modo per \var{ai\_socktype} si possono +usare i valori illustrati in sez.~\ref{sec:sock_type} per indicare per quale +tipo di socket si vuole risolvere il servizio indicato, anche se i soli significativi sono \const{SOCK\_STREAM} e \const{SOCK\_DGRAM}; in questo caso, se non si vuole effettuare nessuna risoluzione specifica, si potrà usare un valore nullo. @@ -1359,12 +1361,12 @@ bit della maschera. Come ultimo argomento di \func{getaddrinfo} deve essere passato un puntatore ad una variabile (di tipo puntatore ad una struttura \struct{addrinfo}) che -verrà utilizzata dalla funzione per restituire (come \textit{value result +verrà utilizzata dalla funzione per riportare (come \textit{value result argument}) i propri risultati. La funzione infatti è rientrante, ed alloca autonomamente tutta la memoria necessaria in cui verranno riportati i -risultati della risoluzione. La funzione restituisce in \param{res} il -puntatore alla prima di una \textit{linked list} di strutture di tipo -\struct{addrinfo} contenenti tutte le informazioni ottenute. +risultati della risoluzione. La funzione scriverà in \param{res} il puntatore +iniziale ad una \textit{linked list} di strutture di tipo \struct{addrinfo} +contenenti tutte le informazioni ottenute. La funzione restituisce un valore nullo in caso di successo, o un codice in caso di errore. I valori usati come codice di errore sono riportati in @@ -1396,7 +1398,8 @@ corrispondente altri tipi di socket. \\ \const{EAI\_ADDRFAMILY}& la rete richiesta non ha nessun indirizzo di rete per la famiglia di indirizzi specificata. \\ - \const{EAI\_NODATA} & la . \\ + \const{EAI\_NODATA} & la macchina specificata esiste, ma non ha nessun + indirizzo di rete definito. \\ \const{EAI\_MEMORY} & è stato impossibile allocare la memoria necessaria alle operazioni. \\ \const{EAI\_FAIL} & il DNS ha restituito un errore di risoluzione @@ -1445,7 +1448,9 @@ Dato che ad un certo nome a dominio possono corrispondere pi 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} si avrà come risposta la +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] @@ -1457,10 +1462,12 @@ lista illustrata in fig.~\ref{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.. Il codice -del programma è nel file \texttt{mygetaddr.c}, dei sorgenti allegati alla -guida. +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 @@ -1469,15 +1476,86 @@ guida. \end{minipage} \normalsize \caption{Esempio di codice per la risoluzione di un indirizzo.} - \label{fig:mygethost_example} + \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} si dovrà avere cura di disallocare opportunamente tutta la -memoria, per questo viene fornita l'apposita funzione \funcd{freeaddrinfo}, il -cui prototipo è: +\param{res} se questa non viene più utilizzata si dovrà avere cura di +disallocare opportunamente tutta la memoria, per questo viene fornita +l'apposita funzione \funcd{freeaddrinfo}, il cui prototipo è: \begin{functions} \headdecl{netdb.h} @@ -1494,7 +1572,92 @@ 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{le due costanti sono + definite in \file{netdb.h} ed hanno rispettivamente il valore 1024 e 12.} +che possono essere utilizzate come limiti massimi. In caso di errore viene +restituito invece un codice che assume gli stessi valori illustrati in +tab.~\ref{tab:addrinfo_error_code}. +A questo punto possiamo fornire degli esempi di utilizzo diretto della nuova +interfaccia, adottandola per rendere i nostri client \section{Le opzioni dei socket} @@ -1504,7 +1667,7 @@ Finora abbiamo trattato i socket nel loro comportamento pi possibile attivare alcune modalità diverse di funzionamento degli stessi Dato che la maggior parte delle opzioni dei socket sono relative ai socket -TCP, ed hanno poi significato analogo quando usate con altri socket, abbiamo + TCP, ed hanno poi significato analogo quando usate con altri socket, abbiamo preferito trattare l'argomento in generale in questa sezione piuttosto che nel capitolo dedicato alla trattazione generica dei socket.