X-Git-Url: https://gapil.gnulinux.it/gitweb/?p=gapil.git;a=blobdiff_plain;f=sockctrl.tex;h=4b07f89b92e601cd8103f02a2ab5b7a7ec3a6d49;hp=62fd3d7037692a521a027423fa3d9f40f293cf9a;hb=f209f0494e4dd9065f3bf0cda54612297b3e079c;hpb=eefeeaee57a56cfe1ff8c02a26c44d6f87f1368c diff --git a/sockctrl.tex b/sockctrl.tex index 62fd3d7..4b07f89 100644 --- a/sockctrl.tex +++ b/sockctrl.tex @@ -1538,7 +1538,7 @@ 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} +\begin{Verbatim} [piccardi@gont sources]$ ./mygetaddr -c gapil.truelite.it echo Canonical name sources2.truelite.it IPv4 address: @@ -1549,7 +1549,7 @@ IPv4 address: Indirizzo 62.48.34.25 Protocollo 17 Porta 7 -\end{verbatim} +\end{Verbatim} %$ Una volta estratti i risultati dalla \textit{linked list} puntata da @@ -1650,30 +1650,318 @@ 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 +\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 diretto della nuova -interfaccia, adottandola per rendere i nostri client +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} + -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. +\subsection{Le opzioni generiche} +\label{sec:sock_generic_options} + +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. + \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.