X-Git-Url: https://gapil.gnulinux.it/gitweb/?p=gapil.git;a=blobdiff_plain;f=tcpsockadv.tex;h=91a5ada2f0cf7036f8a7222d4b3f3f46dc9b7e12;hp=7351738281d804bdd6762a143c2b2c93ced5cb26;hb=0605ed025fd00ee41a7515b778bc686a6044f059;hpb=38b9e1e53adc5b440459a6044f3a8ff133f34824 diff --git a/tcpsockadv.tex b/tcpsockadv.tex index 7351738..91a5ada 100644 --- a/tcpsockadv.tex +++ b/tcpsockadv.tex @@ -1,4 +1,4 @@ - %% tcpsockadv.tex +%% tcpsockadv.tex %% %% Copyright (C) 2003 Simone Piccardi. Permission is granted to %% copy, distribute and/or modify this document under the terms of the GNU Free @@ -658,22 +658,177 @@ presenta un file descriptor aperto, e lo imposta (\texttt{\small 44}) come nuovo massimo, per poi tornare (\texttt{\small 44}) al ciclo principale con un \code{break}, e rieseguire \func{select}. -Se infine si sono letti dei dati (ultimo caso rimasto) si potrà invocare -(\texttt{\small 49}) \func{FullWrite} per riscriverli indietro sul socket in -questione, avendo cura di uscire con un messaggio in caso di errore -(\texttt{\small 50--53}). +Se infine si sono effettivamente letti dei dati dal socket (ultimo caso +rimasto) si potrà invocare immediatamente (\texttt{\small 49}) +\func{FullWrite} per riscriverli indietro sul socket stesso, avendo cura di +uscire con un messaggio in caso di errore (\texttt{\small 50--53}). Si noti +che nel ciclo si esegue una sola lettura, contrariamente a quanto fatto con la +precedente versione (si riveda il codice di \secref{fig:TCP_ServEcho_second}) +in cui si continuava a leggere fintanto che non si riceveva un +\textit{end-of-file}, questo perché usando l'\textit{I/O multiplexing} non si +vuole essere bloccati in lettura. L'uso di \func{select} ci permette di +trattare automaticamente anche il caso in cui la \func{read} non è stata in +grado di leggere tutti i dati presenti sul socket, dato che alla iterazione +successiva \func{select} ritornerà immediatamente segnalando l'ulteriore +disponibilità. + +Il nostro server comunque soffre di una vulnerabilità per un attacco di tipo +\textit{Denial of Service}. Il problema è che in caso di blocco di una +qualunque delle funzioni di I/O, non avendo usato processi separati, tutto il +server si ferma e non risponde più a nessuna richiesta. Abbiamo scongiurato +questa evenienza per l'I/O in ingresso con l'uso di \func{select}, ma non vale +altrettanto per l'I/O in uscita. Il problema pertanto può sorgere qualora una +delle chiamate a \func{write} effettuate da \func{FullWrite} si blocchi. Con +il funzionamento normale questo non accade in quanto il server si limita a +scrivere quanto riceve in ingresso, ma qualora venga utilizzato un client +malevolo che esegua solo scritture e non legga mai indietro l'\textsl{eco} del +server, si potrebbe giungere alla saturazione del buffer di scrittura, ed al +conseguente blocco del server su di una \func{write}. + +Le possibili soluzioni in questo caso sono quelle di ritornare ad eseguire il +ciclo di risposta alle richieste all'interno di processi separati, utilizzare +un timeout per le operazioni di scrittura, o eseguire queste ultime in +modalità non bloccante, concludendo le operazioni qualora non vadano a buon +fine. + + + +\subsection{I/O multiplexing con \func{poll}} +\label{sec:TCP_serv_poll} +Finora abbiamo trattato le problematiche risolubili con l'I/O multiplexing +impiegando la funzione \func{select}; questo è quello che avviene nella +maggior parte dei casi, in quanto essa è nata sotto BSD proprio per affrontare +queste problematiche con i socket. Abbiamo però visto in +\secref{sec:file_multiplexing} come la funzione \func{poll} possa costituire +una alternativa a \func{select}, con alcuni vantaggi.\footnote{non soffrendo + delle limitazioni dovute all'uso dei \textit{file descriptor set}.} + +Ancora una volta in \secref{sec:file_poll} abbiamo trattato la funzione in +maniera generica, parlando di file descriptor, ma come per \func{select} +quando si ha a che fare con dei socket il concetto di essere \textsl{pronti} +per l'I/O deve essere specificato nei dettagli, per tener conto delle +condizioni della rete. Inoltre deve essere specificato come viene classificato +il traffico nella suddivisione fra dati normali e prioritari. In generale +pertanto: +\begin{itemize} +\item i dati trasmessi su un socket vengono considerati traffico normale, + pertanto vengono rilevati da una selezione con \const{POLLIN} o + \const{POLLRDNORM}. +\item i dati \textit{out-of-band} su un socket TCP vengono considerati + traffico prioritario e vengono rilevati da una condizione \const{POLLIN}, + \const{POLLPRI} o \const{POLLRDBAND}. +\item la chiusura di una connessione (cioè la ricezione di un segmento FIN) + viene considerato traffico normale, pertanto viene rilevato da una + condizione \const{POLLIN} o \const{POLLRDNORM}, ma una conseguente chiamata + a \func{read} restituirà 0. +\item la presenza di un errore sul socket (sia dovuta ad un segmento RST che a + timeout) viene considerata traffico normale, ma viene segnalata anche dalla + condizione \const{POLLERR}. +\item la presenza di una nuova connessione su un socket in ascolto può essere + considerata sia traffico normale che prioritario, nel caso di Linux + l'implementazione la classifica come normale. +\end{itemize} + +Come esempio dell'uso di \func{poll} proviamo allora a reimplementare il +server \textit{echo} secondo lo schema di \figref{fig:TCP_echo_multiplex} +usando \func{poll} al posto di \func{select}. In questo caso dovremo fare +qualche modifica, per tenere conto della diversa sintassi delle due funzioni, +ma la struttura del programma resta sostanzialmente la stessa. -\subsection{Un esempio di I/O multiplexing con \func{poll}} -\label{sec:TCP_serv_poll} +\begin{figure}[!htbp] + \footnotesize \centering + \begin{minipage}[c]{15.6cm} + \includecodesample{listati/poll_echod.c} + \end{minipage} + \normalsize + \caption{La sezione principale del codice della nuova versione di server + \textit{echo} basati sull'uso della funzione \func{poll}.} + \label{fig:TCP_PollEchod} +\end{figure} + +In \figref{fig:TCP_PollEchod} è riportata la sezione principale della nuova +versione del server, la versione completa del codice è riportata nel file +\file{poll\_echod.c} dei sorgenti allegati alla guida. Al solito nella figura +si sono tralasciate la gestione delle opzioni, la creazione del socket in +ascolto, la cessione dei privilegi e le operazioni necessarie a far funzionare +il programma come demone, privilegiando la sezione principale del programma. + +Come per il precedente server basato su \func{select} il primo passo +(\texttt{\small 2--8}) è quello di inizializzare le variabili necessarie. Dato +che in questo caso dovremo usare un vettore di strutture occorre anzitutto +(\texttt{\small 2}) allocare la memoria necessaria utilizzando il numero +massimo \var{n} di socket osservabili, che viene impostato attraverso +l'opzione \texttt{-n} ed ha un valore di default di 256. + +Dopo di che si preimposta (\texttt{\small 3}) il valore \var{max\_fd} del file +descriptor aperto con valore più alto a quello del socket in ascolto (al +momento l'unico), e si provvede (\texttt{\small 4--7}) ad inizializzare le +strutture, disabilitando (\texttt{\small 5}) l'osservazione con un valore +negativo del campo \var{fd} ma predisponendo (\texttt{\small 6}) il campo +\var{events} per l'osservazione dei dati normali con \const{POLLRDNORM}. +Infine (\texttt{\small 8}) si attiva l'osservazione del socket in ascolto +inizializzando la corrispondente struttura. Questo metodo comporta, in +modalità interattiva, lo spreco di tre strutture (quelle relative a standard +input, output ed error) che non vengono mai utilizzate in quanto la prima è +sempre quella relativa al socket in ascolto. + +Una volta completata l'inizializzazione tutto il lavoro viene svolto +all'interno del ciclo principale \texttt{\small 10--55}) che ha una struttura +sostanzialmente identica a quello usato per il precedente esempio basato su +\func{select}. La prima istruzione (\texttt{\small 11--12}) è quella di +eseguire \func{poll} all'interno di un ciclo che la ripete qualora venisse +interrotta da un segnale, da cui si esce soltanto quando la funzione ritorna, +restituendo nella variabile \var{n} il numero di file descriptor trovati +attivi. Qualora invece si sia ottenuto un errore si procede (\texttt{\small + 13--16}) alla terminazione immediata del processo provvedendo a stampare una +descrizione dello stesso. + +Una volta ottenuta dell'attività su un file descriptor si hanno di nuovo due +possibilità. La prima possibilità è che ci sia attività sul socket in ascolto, +indice di una nuova connessione, nel qual caso si controlla (\texttt{\small + 17}) se il campo \var{revents} della relativa struttura è attivo; se è così +si provvede (\texttt{\small 18}) a decrementare la variabile \var{n} (che +assume il significato di numero di file descriptor attivi rimasti da +controllare) per poi (\texttt{\small 19--23}) effettuare la chiamata ad +\func{accept}, terminando il processo in caso di errore. Se la chiamata ad +\func{accept} ha successo si procede attivando (\texttt{\small 24}) la +struttura relativa al nuovo file descriptor da essa ottenuto, modificando +(\texttt{\small 24}) infine quando necessario il valore massimo dei file +descriptor aperti mantenuto in \var{max\_fd}. + +La seconda possibilità è che vi sia dell'attività su uno dei socket aperti in +precedenza, nel qual caso si inizializza (\texttt{\small 27}) l'indice \var{i} +del vettore delle strutture \struct{pollfd} al valore del socket in ascolto, +dato che gli ulteriori socket aperti avranno comunque un valore superiore. Il +ciclo (\texttt{\small 28--54}) prosegue fintanto che il numero di file +descriptor attivi, mantenuto nella variabile \var{n}, è diverso da zero. Se +pertanto ci sono ancora socket attivi da individuare si comincia con +l'incrementare (\texttt{\small 30}) l'indice e controllare (\texttt{\small + 31}) se corrisponde ad un file descriptor in uso, analizzando il valore del +campo \var{fd} della relativa struttura, e chiudendo immediatamente il ciclo +qualora non lo sia. Se invece il file descriptor è in uso si verifica +(\texttt{\small 31}) se c'è stata attività controllando il campo +\var{revents}. + +Di nuovo se non si verifica la presenza di attività il ciclo si chiude subito, +altrimenti si provvederà (\texttt{\small 32}) a decrementare il numero \var{n} +di file descriptor attivi da controllare e ad eseguire (\texttt{\small 33}) la +lettura, ed in caso di errore (\texttt{\small 34--37}) al solito lo si +notificherà uscendo immediatamente. Qualora invece si ottenga una condizione +di end-of-file (\texttt{\small 38--47}) si provvederà a chiudere +(\texttt{\small 39}) anche il nostro capo del socket e a marcarlo +(\texttt{\small 40}) nella struttura ad esso associata come inutilizzato. +Infine dovrà essere ricalcolato (\texttt{\small 41--45}) un eventiale nuovo +valore di \var{max\_fd}. L'ultimo passo è (\texttt{\small 46}) chiudere il +ciclo in quanto in questo caso non c'è più niente da riscrivere all'indietro +sul socket. + +Se invece si sono letti dei dati si provvede (\texttt{\small 48}) ad +effettuarne la riscrittura all'indietro, con il solito controllo ed eventuale +uscita e notifica in caso si errore (\texttt{\small 49--52}). -Abbiamo visto in \secref{sec:TCP_serv_select} come creare un server che -utilizzi l'I/O multiplexing attraverso l'impiego della funzione \func{select}, -ma in \secref{sec:file_multiplexing} abbiamo visto come la funzione -\func{poll} costituisca una alternativa a \func{select} con delle funzionalità -migliori, vediamo allora come reimplementare il nostro servizio usando questa -funzione. \section{Le opzioni dei socket}