Lo strato applicativo di Internet - Dipartimento Infocom
Transcript
Lo strato applicativo di Internet - Dipartimento Infocom
Alessandro Falaschi Lo strato applicativo di Internet Web edition 1.2 - Giugno 2009 Autore Alessandro Falaschi Copertina Marco Sebastiani Editing Composto in HTML con Renderizzato in PDF con Prince Licenza Donazioni Creative Commons Attribuzione - Non commerciale - Condividi allo stesso modo Se hai gradito il testo, puoi manifestare concretamente la tua riconoscenza Liberatoria L'eventuale inclusione non autorizzata di materiale protetto da copyright è da considerasi transitoria, almeno fino a quando non si avrà il tempo di riprodurre delle copie originali dello stesso. Ove possibile, sono forniti i riferimenti all'origine del materiale. L'autore si impegna alla rimozione immediata dei contenuti che saranno ritenuti lesivi dei diritti altrui. 2 Prefazione Questa è la versione PDF del materiale didattico organizzato per l'erogazione di un corso universitario del terzo anno della Laurea in Ingegneria dell'Informazione presso la sede di Cisterna di Latina dell'Università Sapienza di Roma. Sono presi in esame gli aspetti e le tecnologie del software alla base dei sistemi di comunicazione basati su Internet, con particolare riguardo a quanto a disponibile come Software Libero. Da sempre, le telecomunicazioni sono state associate all'idea di telefono; al giorno d'oggi, è sempre più sensato pensarle invece come un qualcosa reso possibile da un computer connesso ad Internet. Partendo dalle basi operative di una rete IP, si illustra l'interfaccia socket, che permette alle applicazioni di comunicare in rete, e si descrive come queste possono essere individuate attraverso l'uso di Nomi a Dominio. Sono quindi illustrati i concetti legati alle comunicazioni email ed ai loro formati, assieme ai meccanismi di autenticazione e confidenzialità, ed approfonditi gli aspetti di sicurezza. Si passa poi a descrivere il mondo del Web, dei linguaggi di descrizione, dell'HTTP, CGI e CMS. Infine, si affronta il tema della Telemedialità, con le applicazioni VoIP, Video, e di collaborazione. Parallelamente agli aspetti descrittivi, il corso prevede un intenso programma di prove pratiche di laboratorio, in cui si potranno toccare con mano i componenti della architettura Internet, e si potrà verificare la loro interazione effettiva. Contenuti 1. 2. 3. 4. 5. 6. 7. 8. 9. Trasmissione Internet Network Programming Risoluzione degli indirizzi applicativi Posta Elettronica Dispositivi e Meccanismi di Sicurezza World Wide Web Applicazioni Web e Overlay Networks Voice over IP Streaming Esercitazioni 1. 2. 3. 4. 5. 6. 7. 8. 9. Mondo Linux Cattura del traffico e uso dei Socket Investigazioni di Rete Domain Name System Posta Elettronica Utilizzo dei Meccanismi di Sicurezza HTML e CSS Server Apache OpenSER e Twinkle Licenza Prefazione Licenza I contenuti di questo testo sono frutto di un lavoro di ricerca e raccolta operata a partire da svariate fonti, sia editoriali che di accesso pubblico. Ovunque si sia ritenuto opportuno lo sforzo di un approfondimento, è stato inserito un rimando a risorse reperibili in rete, in particolare presso Wikipedia, che per la sua vocazione alla neutralità del punto di vista, rappresenta una impagabile fonte di conoscenza. Alcune delle illustrazioni presenti nel presente testo sono state tratte da pubblicazioni editoriali, senza nessuna richiesta di consenso. Per queste, spero di non aver commesso nessuna azione illegale. Quel che invece è il frutto di un contributo assolutamente personale, ossia il lavoro di sintesi e raccordo tra i diversi argomenti, viene rilasciato sotto una licenza Creative Commons. Edizione • • • • I Revisione: Giugno 2009 Web Edition: Maggio 2008 Prima edizione PDF (generata con Prince): Aprile 2008 Inizio del corso: Gennaio 2007 4 Lo strato applicativo di Internet Trasmissione Internet Riassumiamo innanzitutto i meccanismi alla base della comunicazione a pacchetto e del suo instradamento su rete Internet, e di come la stratificazione funzionale dei protocolli si sostanzi in una architettura software realizzata interfacciando strati via via più prossimi al mezzo di comunicazione, rimandando al prossimo capitolo la descrizione di come un programma (applicazione) in esecuzione su di un computer ospite (host) possa in tal modo comunicare con un altro, posto ad una diversa estremità della rete Internet. • Trasmissione Internet ◦ ◦ ◦ ◦ Commutazione di circuito, instradamento, multiplazione Commutazione di pacchetto Modello Internet Stratificazione funzionale ▪ Incapsulamento ◦ Lo stack dei protocolli, dall'applicazione alla trasmissione ◦ Indirizzi Internet • IP: lo strato di rete ◦ ◦ ◦ ◦ ◦ ◦ ◦ ◦ Intestazione IP Instradamento Maschera di sottorete Router e tabelle di routing Sistemi autonomi Indirizzi privati e NAT ICMP Frammentazione di pacchetto e riassemblaggio • Rete locale: strato di collegamento ◦ Intestazione Ethernet ◦ Risoluzione ARP, broadcast ▪ Riassumendo ◦ Bridge e switch • TCP e UDP: strato di trasporto ◦ Servizio con o senza connessione ▪ Datagramma ▪ Canale virtuale e circuito virtuale ▪ Arricchimento del servizio di rete ◦ ◦ ◦ ◦ ◦ ◦ ◦ Controllo di di sequenza, di errore, di flusso, e di congestione Intestazione TCP Entità TCP Apertura connessione Protocollo a finestra Controllo di errore Controllo di flusso Trasmissione Internet Trasmissione Internet ◦ Controllo di congestione ◦ Esempio di capture • Riferimenti Trasmissione Internet Il modello Internet stabilisce che i servizi a cui si accede siano localizzati ai bordi della rete, mentre la rete svolge esclusivamente la funzione di recapitare le unità informative da un estremo all'altro. Ciò costituisce una enorme differenza rispetto alle tradizionali reti telefoniche, in cui si è praticamente vincolati ad usare i servizi offerti dall'operatore che ci consente l'accesso alla rete. Per questo, il modello di trasmissione Internet si traduce in • una netta separazione di ruoli tra gli operatori di telecomunicazioni, ed i fornitori di servizi; • una maggiore concorrenza tra fornitori di connettività da un lato, e fornitori di servizi dall'altro; • possibilità di sviluppo di servizi innovativi, replicabili e indipendenti. Lo studio delle applicazioni Internet, pertanto, potrebbe essere svolto in modo quasi del tutto indipendente dalla comprensione del funzionamento della rete Internet; d'altra parte, una percezione di ciò che avviene dietro le quinte (e come vedremo, anche all'interno del nostro computer di casa) è senz'altro di grande aiuto nelle fasi progettuali e di utilizzo. Lo origini di Internet sono legate all'esigenza del Dipartimento della Difesa (DoD) degli USA di far comunicare tra loro computer di diversa fabbricazione. Alla base del funzionamento di Internet, troviamo le caratteristiche di • • • • poter continuare a funzionare anche in presenza di malfunzionamenti ed anomalie; assenza di un controllo centralizzato; trasmissione numerica di unità autonome denominate pacchetti dati; adozione di specifiche pubbliche e replicabili da costruttori indipendenti. In questo capitolo ci occupiamo appunto di descrivere i meccanismi di funzionamento interno della rete Internet. Ma prima di tutto, vanno definiti alcuni concetti di base. Commutazione di circuito, instradamento, multiplazione Descriviamo innanzitutto cosa non è Internet, illustrando brevissimamente i principi di funzionamento di una rete telefonica pubblica (PSTN), basata sulla commutazione di circuito, e sulla multiplazione a divisione di tempo. Agli albori della telefonia, ossia nell'epoca dei telefoni a manovella, con la cornetta appesa al muro, la comunicazione si basava sulla creazione di un vero proprio circuito elettrico, grazie all'operato di un centralinista umano, che collegava fisicamente tra loro le terminazioni dei diversi utenti. Nel caso in cui intervengano più centralinisti in cascata, la chiamata risulta instradata attraverso più centralini. Da allora, il termine commutazione di circuito individua il caso in cui 6 Lo strato applicativo di Internet Alessandro Falaschi • è necessaria una fase di setup precedente alla comunicazione vera e propria, in cui vengono riservate le risorse; • nella fase di setup si determina anche l'instradamento della chiamata nell'ambito della rete, che rimane lo stesso per tutta la durata della medesima; • le risorse trasmissive restano impegnate in modo esclusivo per l'intera durata della conversazione. Le cose non sono cambiate di molto (da un punto di vista concettuale) con l'avvento della telefonia numerica: in tal caso, più segnali vocali sono campionati e quantizzati in modo sincrono, ed il risultato (numerico) è multiplato in una trama PCM, in cui viene riservato un intervallo temporale per ognuno dei flussi tributari. Il processo di multiplazione è tale per cui il flusso binario risultante da N tributari uguali, deve essere trasmesso ad una velocità binaria pari ad N volte quella del singolo tributario. Ad esempio nel caso del PCM, in cui si estraggono per ogni sorgente 8000 campioni a secondo, e ognuno di questi è quantizzato con 8 bit, si ottiene (per ogni tributario) un contributo di 8 bit/campione * 8.000 campioni/secondo = 64.000 bit/secondo. La trama PCM si ottiene multiplando assieme 32 tributari, e quindi la velocità complessiva risulta essere di 64.000 * 32 = 2.048.000 bit/ secondo, comunemente indicato come flusso a 2 Megabit, ovvero, in accordo alla nomenclatura ITU-T, come circuito E1. In realtà, due dei 32 intervalli temporali possono essere riservati ad informazioni di segnalazione, ma questo... potrà essere oggetto di altri corsi. 7 Trasmissione Internet Trasmissione Internet Per ciò che ci riguarda, possiamo concludere questa brevissima rassegna, specificando che nell'attraversamento di nodi di commutazione, più flussi PCM possono essere ulteriormente raggruppati, producendo quella che viene indicata come gerarchia digitale plesiocrona (PDH), attualmente sostituita dalla gerarchia digitale sincrona (SDH). Commutazione di pacchetto Una grande differenza tra il caso della trasmissione di segnale vocale (sia pur digitalizzato) e quello della trasmissione dati, è che in questo secondo caso non è necessario impegnare il mezzo trasmissivo in modo esclusivo, ma la trasmissione può avvenire in modalità sporadica, ed i dati inviati ad intervalli irregolari. Questo motivo, assieme alla dimensione variabile delle singole comunicazioni, porta a suddividere la comunicazione in unità autonome indicate come pacchetto dati. La trasmissione dei pacchetti può avvenire in forma multiplata (ovvero, condividendo lo stesso mezzo trasmissivo tra più comunicazioni) in modo statistico, ovvero senza riservare con esattezza risorse a questo o quel tributario. Infatti, in questo caso il multiplatore si limita ad inserire i pacchetti ricevuti in apposite code, da cui li preleva per poterli trasmettere in sequenza. La presenza di code, comporta • il determinarsi di un ritardo variabile ed impredicibile • la possibilità che la coda sia piena, ed il pacchetto in ingresso venga scartato a meno di non adottare tecniche di prioritizzazione atte a garantire la Qualità del Servizio (QoS). Per contro, se ogni pacchetto reca con sé le informazioni necessarie al suo recapito, la rete di trasmissione non necessita di una apposita fase di setup dell'instradamento: nel caso della commutazione di pacchetto, ogni pacchetto fa caso a sè. Modello Internet Ora che abbiamo discusso le differenze più evidenti tra reti a circuito e reti a pacchetto, e prima di sviluppare la descrizione delle modalità di sviluppo di una applicazione Internet, ovvero di un programma che operi tramite questa rete, affrontiamo l'analisi dei meccanismi alla base del funzionamento effettivo di Internet, assieme alle tecnologie di collegamento e di trasmissione più comunemente adottate in quest'ambito. L'insieme di questi aspetti, porta alla definizione di un modello concettuale, che si basa sulla stratificazione funzionale, sulla pacchettizzazione e sull'incapsulamento dei dati. Stratificazione funzionale Nel 1978 l'International Organization for Standardization emise uno standard denominato Open Systems Interconnection, che definisce una nomenclatura di riferimento per la 8 Lo strato applicativo di Internet Alessandro Falaschi descrizione di come dei Sistemi Aperti possano comunicare tra loro, allo scopo di favorire l'interoperabilità tra costruttori diversi. Nella figura (tratta da MMC) viene evidenziato come si possa stabilire una relazione gerarchica tra le diverse funzioni, individuando un pila (stack) di strati (layer). Le entità di pari livello N sono da ritenersi virtualmente in colloquio tra loro (tra pari, o peer), mediante lo scambio logico di Protocol Data Unit (PDU), mentre in effetti queste si avvalgono dei servizi offerti dallo strato inferiore N-1, ed a loro volta, offrono dei servizi allo strato superiore N+1. Incapsulamento In base a questo formalismo, le PDU di livello N (o N PDU) sono costruite a partire dalle (N+1) PDU, aggiungendo a queste delle intestazioni (Protocol Control Information, PCI) che permettono il dialogo vituale tra entità di livello N. Il meccanismo si ripete con lo strato immediatamente soggiacente, e così via finchè le unità informative non raggiungono lo strato più inferiore, a diretto contatto con il mezzo trasmissivo, che permette lo scambio fisico tra entità. L'aggiunta delle PCI alle PDU provenienti dallo strato superiore prende il nome di incapsulamento, dato che ricorda molto da vicino il processo di racchiudere un plico ricevuto, in una nuova busta esterna. A volte, la PDU proveniente dallo strato superiore, viene indicata (dal punto di vista dello strato che la riceve) come Service Data Unit o SDU. Il modello ISO/OSI individua sette livelli funzionali, di cui i più rilevanti per ciò che riguarda le applicazioni Internet, sono quelli di trasporto e di rete, indicati rispettivamente come TCP (o UDP) e IP, mentre i tre livelli ISO-OSI superiori vengono raggruppati in un unico strato applicativo, per cui di fatto Internet è basata su di un modello a cinque livelli. 9 Trasmissione Internet Trasmissione Internet Lo stack dei protocolli, dall'applicazione alla trasmissione La figura seguente mostra le relazioni che intercorrono tra i diversi strati funzionali presenti in un computer connesso ad Internet. In particolare, è mostrato come i processi applicativi (AP) affidino i dati generati/richiesti, ad uno strato inferiore (di trasporto, TCP o UDP). L'AP all'altro estremo della comunicazione, non si avvede neanche dell'esistenza di uno strato di trasporto, e la trasmissione delle Application Protocol Data Unit (APDU) sembra avvenire direttamente tra i due AP Client e Server. Ma come fare per distinguere i diversi processi contemporaneamente in esecuzione al computer ricevente? Ciò è possibile in base alle informazioni presenti nell'intestazione (header) aggiunta alla PDU dallo strato di trasporto, che infatti contiene un numero di porta (altrimenti detto SAP, o Service Access Point) che identifica l'AP con cui dialoga. Il processo si ripete, invocando i servizi offerti dallo strato di rete (IP), che ha il compito di recapitare i singoli pacchetti tramite i nodi (router) di Internet. Viene aggiunto un nuovo header (IP), che contiene un protocol field in grado di specificare quale entità dello strato di trasporto viene incapsulata. Infine, viene aggiunto un header ulteriore (di link), in cui un type field identifica il protocollo di rete trasportato. La trama (frame) così composta, può essere effettivamente trasmessa sul mezzo che interconnette i nodi di rete, e recapitata a quello con l'indirizzo di collegamento specificato nel Frame Header. Nella figura sottostante, ovvero in quest'altra, viene posto in evidenza come i router (ossia i 10 Lo strato applicativo di Internet Alessandro Falaschi dispositivi che interconnettono le diverse reti di cui è composta Internet) si limitino a dialogare solo fino allo strato di rete (IP) delle entità a cui sono collegate, senza cioè leggere nulla più interno della intestazione IP. Indirizzi Internet Internet piò essere definita come la rete delle reti, in quanto ha lo scopo di interconnettere reti diverse ed eterogenee, e coinvolge la coesistenza di indirizzi di diversa natura, ognuno legato ad uno strato funzionale: Per ognuno dei quattro tipi di indirizzo, è previsto un meccanismo di traduzione nell'indirizzo necessario allo strato inferiore, in accordo al seguente schema 11 IP: lo strato di rete Trasmissione Internet Indirizzo per lo strato applicativo l'indirizzo prende il nome di URI con la forma proto://fqdn/ risorsa oppure proto:user@fqdn per lo strato di rete, si ha un indirizzo IP lo strato di collegamento spesso è una LAN Ethernet, e gli indirizzi sono detti Formato e Traduzione • ogni protocollo proto (es HTTP, FTP) è associato di default ad un indirizzo di trasporto (ad es porte TCP 80, 21), in accordo alle corrispondenze registrate presso IANA, indicate anche come porte ben note. Viceversa, il numero di porta può anche essere indicato in modo esplicito, con una sintassi del tipo fqdn:port • il Fully Qualified Domain Name fqdn si traduce, mediante interrogazione al DNS, nell'indirizzo IP necessario allo strato di rete per consegnare i pacchetti alla LAN dove risiede l'host di destinazione • risorsa, e user, costituiscono un sottoindirizzamento la cui semantica è definita nel contesto dell'applicazione che gestisce proto. Ad es, se proto è mailto, allora user è il destinatario; se invece proto è http, risorsa è una pagina web. • i 4 bytes x.y.w.z dell'indirizzo IP sono utilizzati, all'interno della rete locale, dal protocollo ARP per conoscere l'indirizzo Ethernet dell'host di destinazione • i 6 byte dell'indirizzo Ethernet vengono usati dallo strato di collegamento per individuare un computer fisicamente connesso alla stessa Local Area network (rete LAN) MAC ovvero Media Access Control Ogni diversa classe di indirizzo ha un significato di localizzazione, relativo al proprio strato funzionale, come ad esempio un indirizzo postale del tipo Persona/via e numero civico/ Città/Nazione è la concatenazione di 4 diversi indirizzi. Quindi, mentre un indirizzo MAC (Media Access Control) individua un computer dentro una LAN, un indirizzo IP individua un computer su Internet, ed un indirizzo di trasporto, una applicazione in esecuzione su quel computer. IP: lo strato di rete IP significa Internet Protocol, ed in effetti rappresenta la quintessenza della filosofia su cui è basata Internet: ogni comunicazione è suddivisa in pacchetti, ognuno dei quali presenta nella intestazione IP, gli indirizzi a 4 byte che identificano il computer di partenza e di destinazione. 12 Lo strato applicativo di Internet Alessandro Falaschi Intestazione IP Si compone di un minimo di 20 byte, in 5 file da 4. Il campo VER indica quale versione si sta utilizzando, quella tuttora in uso è IPv4. HLEN e TLEN indicano rispettivamente la lunghezza dell'header e di tutto il pacchetto, mentre TOS codifica un Type of Service che può essere usato per differenziare il tipo di traffico. L'identificazione riporta lo stesso valore per tutti i frammenti di uno stesso datagramma, e l'Offset di frammento indica la posizione del frammento nel datagramma, espresso come multiplo di 8 byte. Solo 2 dei tre bit di Flag sono usati, DF (Don't Fragment) per richiedere alla rete di non frammentare il datagramma, e MF (More Fragments) per indicare che seguiranno altri frammenti. Il TTL (Time To Live) determina la massima permanenza del pacchetto nella rete, in quanto ogni router attraversato ne decrementa di uno il valore, finché questo non si azzera, ed il pacchetto è scartato. Protocollo indica il tipo di pacchetto incapsulato, ovvero a quale protocollo di trasporto consegnare il datagramma all'arrivo (ad es. TCP o UDP), in accordo ai codici registrati presso IANA, ed infine Checksum serve per verificare l'assenza di errori nell'header. Gli Indirizzi IP di sorgente e destinazione individuano i computer all'interno di Internet, e servono allo strato di rete per recapitare il messaggio e la risposta associata, mentre il campo Opzioni ha una lunghezza variabile, può essere omesso, e consente ad esempio di richiedere il tracciamento della serie di router attraversati. I pacchetti IP non sono di dimensione fissa, ma possono variare tra un minimo ed un massimo: • le dimensioni minime sono dettate sia dalla presenza della intestazioni, sia da considerazioni legate al mezzo fisico che verrà usato per la trasmissione; • quelle massime, pongono un limite alla probabilità che qualche bit del pacchetto inviato, si affetto da errori di trasmissione. Con i 16 bit del campo TLEN, si ha un massimo di 65,535 byte, mai raggiunto nella pratica. Dato che lo strato fisico delle reti attraversate impone spesso una dimensione di pacchetto molto inferiore, lo strato di trasporto tenta di consegnare allo strato IP segmenti di dimensione sufficientemente ridotta, tale da non dover subire frammentazione. Instradamento Ogni computer connesso ad Internet è identificato globalmente mediante un indirizzo di rete (associato appunto allo strato di rete, ossia, l'indirizzo IP) di 4 byte (es 151.100.122.171). Se due computer sono connessi alla medesima rete locale (LAN o Local Area Network), come nel caso dei computer ospitati in uno stesso edificio, questi comunicano in modo diretto, come nel caso (#1) tra LH2 e LH3 della figura seguente, mediante l'indirizzo Ethernet (o MAC) di sei byte, che identifica le rispettive schede di rete. Il pacchetto passa inalterato, attraverso un dispositivo chiamato switch, che si limita a prendere in considerazione la sola 13 IP: lo strato di rete Trasmissione Internet intestazione di strato di collegamento. Quando l'indirizzo IP di destinazione è esterno alla LAN di partenza, come nel caso (#3), allora il datagramma è inviato a destinazione in modo indiretto, per il tramite di un Host speciale presente nella LAN, il Default Gateway (R1 nella figura), che svolge le funzioni di router e interconnette la LAN al resto di Internet. Di li in poi, il pacchetto IP viene rilanciato di router in router, esaminando ogni volta l'indirizzo di destinazione, ed inoltrando il pacchetto sulla interfaccia nella direzione corretta, via via fino a quello che ha accesso alla LAN di destinazione. La direzione giusta viene stabilita in base alla analisi di apposite tabelle di routing, che possono modificarsi nel tempo, così come i collegamenti, possono andare a volte fuori servizio. Per questi motivi, nulla vieta ai pacchetti di una stessa comunicazione di seguire instradamenti alternativi, ed anzi è proprio questo ciò che accade: Flash 14 Lo strato applicativo di Internet Alessandro Falaschi Pertanto, i pacchetti IP possono giungere a destinazione in un ordine diverso da quello di partenza. Maschera di sottorete Ma come fa un computer, a capire che il destinatario è nella sua stessa LAN?? Innanzitutto, va detto che i computer che risiedono fisicamente sulla stessa LAN, devono avere essere stati configurati con degli indirizzi IP tra loro simili, che abbiano in comune tra loro lo stesso prefisso binario, un pò come i telefoni fissi di una stessa città o quartiere, hanno numeri che iniziano con lo stesso prefisso. Dei trentadue bit di indirizzo IP, la parte che costituisce il prefisso, e che è la stessa per tutti i computer della LAN, ha una lunghezza ben determinata, ed è individuata in base alla conoscenza della cosidetta maschera di sottorete, ovvero una sequenza di 4 byte i cui bit sono posti (a partire dal più significativo) tutti ad 1, finché da un certo punto in poi, sono tutti posti a zero. La lunghezza della sequenza di bit pari ad uno rappresenta appunto di quanti bit (a partire da sinistra) è composto il prefisso che identifica la LAN, ed eseguendo l'AND binario tra la maschera ed un indirizzo IP, si ottiene la sua parte prefisso, detta anche indirizzo di sottorete, in quanto si riferisce collettivamente a TUTTI i computer della LAN. Ad esempio, una network mask pari a 255.255.252.0, lunga 22 bit, una volta applicata ad un indirizzo del tipo 192.168.121.10, fornisce un indirizzo di sottorete pari a 192.168.120.0/22, in quanto: Indirizzo IP/maschera 192.168.121.10/22 solo i primi 22 bit identificano la LAN IP in binario 11000000.10101000.01111001.00001010 tutti e 32 i bit sono l'indirizzo completo parte sottorete 11000000.10101000.01111000.00000000 costituita dai primi 22 bit indirizzo di sottorete 192.168.120.0/22 i bit oltre il prefisso sono posti a zero L'indirizzo di sottorete così ottenuto, identifica un sottoinsieme di indirizzi IP contigui, che nell'esempio sono tutti gli indirizzi tali che, ponendo a zero gli ultimi 32-22=10 bit, equivalgono all'indirizzo 192.168.120.0. Questo insieme, può essere partizionato in ulteriori sotto-reti, adottando una maschera più lunga. Ad esempio, i computer con indirizzo IP 192.168.120.X, appartengono alla sottorete 192.168.120.0/24, contenuta dentro a quella con maschera /22. In particolare, la sottorete con maschera di ventidue bit (/22), contiene al suo interno le quattro LAN con maschera a 24 bit, ed indirizzo di sottorete 192.168.120.0/24, 192.168.121.0/24, 192.168.122.0/24, 192.168.123.0/24. D.: Si, ma come fa il mittente a capire che il destinatario è nella sua stessa sottorete ? R.: Mette in AND l'indirizzo IP di destinazione con la Network Mask, e confronta il risultato con l'AND del proprio indirizzo, per la stessa maschera. Se i risultati coincidono, gli indirizzi appartengono alla stessa LAN Esempio: 192.168.121.32 e 192.168.122.45, messi in AND ad una maschera di lunghezza 22 bit, forniscono lo stesso risultato, ovvero 192.168.120.0. Morale, tutti i computer della stessa LAN, devono utilizzare la stessa Network Mask!! Altrimenti, pur potendo comunicare tra loro in modo diretto, necessiterebbero della presenza di un router. 15 IP: lo strato di rete Trasmissione Internet Router e tabelle di routing La funzione di instradamento IP espletata da un router si basa sull'analisi di apposite tabelle, per ogni linea delle quali è indicata l'interfaccia da usare per raggiungere una determinata sottorete. Un router ha almeno due interfacce di rete, appartenenti a due LAN (o sottoreti) diverse, ed opera sui pacchetti scapsulandoli fino al livello di rete, ed esaminando l'indirizzo IP di destinazione. Quindi, per ogni linea contenuta nella tabella di instradamento, l'IP di destinazione viene messo in AND con la relativa maschera di sottorete, e verificato se appartiene o meno alla sottorete associata. Al termine di questo processo, nel caso si sia verificato più di un successo, viene applicata la regola del longest prefix match, selezionando tra i successi quello relativo alla riga con il prefisso più lungo, e quindi più specifico, ed il pacchetto è inviato sull'interfaccia associata a tale riga. Viceversa, nel caso in cui non si verifichi nessuna corrispondenza, il pacchetto viene scartato, ed router genera un pacchetto ICMP Network Unreachable, diretto verso l'IP mittente del pacchetto scartato. Ad esempio, nel caso (#2), l'host mittente LH4 si avvede che il destinatario LS1 ha un IP che appartiene ad una diversa sottorete, e spedisce il pacchetto usando l'indirizzo MAC del proprio Default Gateway R2. Questo, anzichè reindirizzare il pacchetto verso R1 e di lì verso Internet, lo inoltra correttamente sull'interfaccia che collega LS1. Qui sotto, è mostrato un esempio di come potrebbero apparire le tabelle di instradamento IP, per i router che interconnettono quattro diverse LAN. Per i diversi indirizzi di sottorete, è indicato il router da utilizzare per raggiungerla. 16 Lo strato applicativo di Internet Alessandro Falaschi Le tabelle di instradamento dei router sono popolate a seguito di continue comunicazioni con gli altri router di Internet. Nel caso di esempio della figura soprastante, R1 annuncia ad R2, R3 e R4 la raggiungibilità della rete 11.0.0..0/8, provocando in questi l'inserimento dell'informazione che per raggiunere quella rete, occorre consegnare il pacchetto ad R1. Quando R4 annuncia la sua raggiungibilità per la rete 14.0.0.0/8, questo annuncio viene ripropagato da R1, in modo che la rete 14 risulti raggiungibuile per il suo tramite. Quindi, in termini semplificati, ogni router annuncia a quelli a lui collegati, quali sottoreti può raggiungere, che così diventano raggiungibili anche da questi secondi router, che a loro volta, propagano l'annuncio, assumendosi l'incarico dell'inoltro. Il compito dei protocolli di routing è quello di coordinare questo processo, rendendolo efficiente, convergente, e pronto a recepire i cambiamenti di configurazione. Nel caso in cui un router si accorga di conoscere gli istradamenti per tutte le sottoreti contenute sotto una stessa network mask più corta, può leggitimamente aggregare gli instradamenti, ed annunciare la raggiungibilità dell'unica sottorete più grande. Ad esempio, un router che avesse accesso alle reti 192.168.120.0/24, 192.168.121.0/24, 192.168.122.0/24, 192.168.123.0/24, annuncerà la raggiungibilità per la sola rete 192.168.120.0/22, che le racchiude tutte e quattro. In tal modo, si ottiene il risultato di semplificare le tabelle di instradameno per i router più interni di Internet. Sistemi autonomi Un Sistema Autonomo è un insieme di LAN, interconnesse da router, che intendono apparire al resto di Internet come una unica entità, come ad esempio è il caso di un ISP, o di una azienda. Per questo, un sistema autonomo si interconnette con gli altri solo mediante alcuni dei suoi router, che annunciano all'esterno la raggiungibilità delle LAN interne. I protocolli di routing eseguiti all'interno ed all'esterno sono diversi, e prendono rispettivamente il nome di Interior ed Exterior routing protocols; in pratica, il protocollo di comunicazione ufficiale tra router di diversi SA, è il Border Gateway Protocol (BGP). Indirizzi privati e NAT Ad un certo punto dello sviluppo di Internet, sembrava quasi che gli indirizzi IP stessero per esaurirsi di lì a poco. Vennero prese una serie di contromisure, e si iniziò a fare un uso molto diffuso delle classi di indirizzi privati. Si tratta di sottoreti che i router di Internet rifiutano di inoltrare, e che quindi ognuno può usare nel suo privato, per creare una LAN disconnessa da Internet. Le classi di indirizzi privati sono descritte dagli indirizzi di sottorete 10.0.0.0/8, 172.16.0.0/ 12, e 192.168.0.0/16. Tutti gli altri indirizzi (con le dovute eccezioni), sono per contro detti pubblici. 17 IP: lo strato di rete Trasmissione Internet Ma... la possibilità di uscire su Internet, esiste anche per i computer con IP privato, ricorrendo ad un dispositivo NAT (Network Address Translator). Si tratta di un router R potenziato, che sul lato pubblico utilizza un IP (appunto) pubblico, e che oltre ad inoltrare i pacchetti, li modifica. Ad esempio nel caso del Source NAT, sostituisce l'IP privato di un computer sorgente A, con il proprio (pubblico), ed al posto dell'indirizzo di trasporto originario, ossia della porta presso la quale A desidera ricevere risposta, ne mette un altro scelto da lui. A questo punto, il destinatario con IP pubblico B crede di stare parlando con il router R, anzichè con il computer A dotato di IP privato. Quando torna un pacchetto di risposta, il NAT si avvede che la porta di trasporto di destinazione è una di quelle scelte da lui, e ne usa il valore (che ha memorizzato) per ritrovare l'indirizzo IP e la porta originariamente usate dal computer con IP privato, sostituisce questi valori a quelli presenti nel pacchetto di risposta, e finalmente lo consegna al mittente originario. Il principale svantaggio di un NAT, è che i computer con IP privato possono assumere solamente il ruolo di Client, ma non di Server, dato che non possono ospitare servizi raggiungibili dall'esterno, ovvero per i quali i pacchetti devono entrare prima di uscire. A meno di non configurare il Router-NAT, in modo da redirigere le richieste indirizzate ad un particolare indirizzo di trasporto, verso un computer ben preciso. In un capitolo successivo, è riportata una classificazione più approfondita sui diversi tipi di NAT. ICMP Esponiamo ora la funzione di un protocollo (ICMP) già comparso nella figura sul TCP/IP, dove è evidenziato come, sebbene le sue PDU siano incapsulate dentro IP, queste non vengano consegnate allo strato di trasporto, ma siano invece elaborate all'interno dello strato di rete. L'ICMP (Internet Control Message Protocol) ha lo scopo di veicolare informazioni relative al corretto funzionamento della rete, e può seguire sia un protocollo di richiesta-risposta, sia avere una semplice funzione di indicazione non sollecitata, qualora debba segnalare problemi relativi ad un pacchetto IP in transito. In quel caso, l'applicazione che ha generato il paccheto IP originario, può essere notificata dell'evento. 18 Lo strato applicativo di Internet Alessandro Falaschi Come mostrato in figura, l'intestazione minima di ICMP si compone di una sola parola di 32 bit, di cui i primi 8 contengono un codice (type) che qualifica la funzione del pacchetto, ed i secondi otto possono contenere un sottotipo. A seconda del tipo, possono poi essere presenti altre parole di intestazione. Ecco alcuni dei tipi pù frequenti: • • • • • • • • 0 Echo reply 3 Destinazione irraggiungibile 5 Redirect 8 Echo request 11 Time Exceeded 13 Timestamp request 14 Timestamp reply 30 Traceroute Come esempi applicativi, il caso più noto è quello associato all'uso del comando ping (vedi anche le esercitazioni), impiegato per verificare la connettività con un altro computer, inviando un pacchetto ICMP di tipo Echo request. Il computer di destinazione, se operativo ed effettivamente raggiunto, risponde con un pacchetto ICMP di tipo Echo reply, al che il comando ping in esecuzione sul primo computer, scrive a video il tempo intercorso tra andata e ritorno, o Round Trip Time (RTT). Il ciclo si ripete all'infinito, e viene interrotto premendo control-c, al che il ping stampa delle statistiche sui tempi rilevati, e termina. Nel caso in cui il computer di destinazione (od una applicazione che dovrebbe essere in ascolto su di una porta di trasporto) non possa essere raggiunto (dal paccheto ICMP di tipo Echo request, o da qualunque altro pacchetto IP), i router di transito, od il computer di destinazione, possono generare un pacchetto ICMP di tipo Destinazione irraggiungibile, il cui campo code contiene un valore che indica il possibile motivo della irraggiungibiità, come ad esempio Code Description 0 Network unreachable error. 1 Host unreachable error. 3 Port unreachable error (the designated protocol is unable to inform the host of the incoming message). 4 The datagram is too big. Packet fragmentation is required but the 'don't fragment' (DF) flag is on. 19 IP: lo strato di rete 13 Trasmissione Internet Communication administratively prohibited (administrative filtering prevents packet from being forwarded). in cui i codici Network e Host unreachable sono generati dal router che dovrebbe consegnare il pacchetto, Port unreachable è prodotto dall'host di destinazione, quando sul numero di porta di destinazione non è in ascolto nessun processo server, mentre i codici Fragmentation Required e Communication administratively prohibited, sono generati dai router di transito, nei casi in cui rispettvamente sarebbe richiesta la frammentazione del pacchetto, ma è settato il bit "don't fragment", oppure sia presente un firewall che impedisce il transito di un pacchetto per quella destinazione. In tutti i casi, il pacchetto ICMP di ritorno viene assemblato a partire da quello (IP) di andata, come mostrato in figura. In particolare, l'intestazione IP ricevuta, ed i primi 8 byte di ciò che segue (es TCP), viene prefissa dalla intestazione ICMP, che è poi ulteriormente prefissa dalla intestazione IP che serve a recapitare il pacchetto all'indietro. In questo modo, il mittente originario ha tutti gli estremi per ri-associare il messaggio ICMP alla connessione uscente che ha prodotto il pacchetto IP originario. Il tipo Time Exceeded è sfruttato dalla utility traceroute (vedi esercitazione), che invia verso la destinazione un pacchetto con un valore di TTL IP particolarmente basso. Quando questo si azzera, il router lo scarta, ed invia all'indietro, appunto, un pacchetto ICMP Time Exceeded. Quindi, traceroute incrementa il TTL, allo scopo di scoprire il prossimo router che scarterà il pacchetto, finché questo non arriva a destinazione. Infine, il tipo Destinazione irraggiungibile, codice Fragmentation Required, viene usato nell'ambito di una procedura chiamata path MTU discovery, volta a determinare qual'è la dimensione massima di pacchetto che può essere inviata, senza che questo debba essere frammentato. In questo caso, prima di iniziare il collegamento, il computer sorgente invia verso la destinazione dei pacchetti IP con il bit don't fragment settato, e se il pacchetto arriva a destinazione, tutto è filato liscio. Viceversa, se il pacchetto incontra sezioni della rete che devono causare frammentazione, viene generato appunto il messaggio ICMP Destination unreachable, fragmentation required, ed il mittente capisce che deve ridurre la dimensione di pacchetto; ripete quindi la procedura con il pacchetto ridotto, finchè non va. Frammentazione di pacchetto e riassemblaggio Forniamo qui di seguito, un esempio pratico relativo a ciò che accade nel caso di frammentazione IP. L'host source ha pronto un pacchetto IP di 7000 Bytes, completo delle intestazioni di trasporto. Per arrivare all'host destinazione, il pacchetto dovrà attraversare due reti eterogenee, una con architettura Token Ring, ed una seconda Ethernet. Queste due reti, impongono una dimensione massima (MTU) alle trame di strato fisico, rispettivamente di 4000 e di 1500 Bytes. 20 Lo strato applicativo di Internet Alessandro Falaschi Considerando che lo strato IP, di suo, aggiunge una intestazione di 20 Byte, si ottiene una dimensione massima dei frammenti pari a 4000 - 20 = 3980 per la prima tratta, e 1500 - 20 = 1480 per la seconda. Però, come precedentemente illustrato, il campo "fragment offset" presente nella intestazione IP individua la posizione di ogni frammento in modo per così dire quantizzato come multiplo di 8 Byte, e pertanto, la dimensione dei frammenti (eccetto l'ultimo) è vincolata ad essere divisibile per 8. Fortunatamente, 1480 è proprio multiplo di 8 (8 * 185 = 1480), mentre per la sezione Token Ring, occorre ridurre la dimensione del frammento rispetto alla massima, a 3976 (= 496 * 8). E così, i 7000 bytes originari, si suddividono in due pacchetti di 3976 e 3024 byte. Quando questi giungono alla sezione Ethernet, vengono frammentati ulteriormente, ed in particolare, il primo (da 3976) ne genera 3 (2 da 1480 ed uno da 1016 bytes), ed il secondo (da 3024) altri tre (2 da 1480 ed uno da 64 bytes). Nelle tabelle (b) e (c), si evidenziano i contenuti più rilevanti delle intestazioni IP per la prima e la seconda tratta, mostrando come il campo identificazione sia lo stesso per tutti, così come la lunghezza totale, in modo che il ricevente sia in grado di riassemblare il pacchetto. Inoltre, il bit more fragments è sempre settato, tranne che per l'ultimo. Infine, notiamo come il fragment offset (da moltiplicare per 8 per avere la reale posizione) del (iv) pacchetto della tratta Ethernet, sia esattamente uguale a quello del (ii) pacchetto della sezione Token Ring. 21 Rete locale: strato di collegamento Trasmissione Internet Svolgiamo ora la considerazione che, in caso di mancata consegna anche di uno solo dei frammenti a destinazione, il pacchetto non può essere riassemblato per intero, ed allo scadere di un timeout, lo strato di trasporto dell'host sorgente sarà obbligato alla ritrasmissione dell'intero pacchetto originario da 7000 bytes. Pertanto, si preferisce evitare l'insorgenza di frammentazione di pacchetto, ed affidarsi a tecniche come la Path MTU discovery prima illustrata. Rete locale: strato di collegamento Soffermiamoci ora su ciò che accade al di quà del Default Gateway. Intestazione Ethernet Quando l'indirizzo IP di destinazione ricade nella stessa sottorete (LAN) del trasmettitore, i due computer (oppure computer e Default Gateway) possono comunicare direttamente, purché chi vuol trasmettere, riesca a conoscere l'indirizzo fisico (o Ethernet, o MAC) del destinatario. In tal caso, lo strato MAC Ethernet definito dall'IEEE 802.3, incapsula la PDU proveniente dal Logical Link Control (LLC) definito dall'IEEE 802.2 (che a sua volta contiene il pacchetto IP) in accordo ad un formato di trama Ethernet II (detto anche DIX) mostrato appresso. Nel campo Type è presente un codice a 16 bit che indica il protocollo incapsulato (ad es. 0x0800 per IPv4), mentre nei campi indirizzo sorgente e destinazione, trovano posto gli indirizzi Ethernet (di 6 bytes ognuno) delle interfacce di rete agli estremi del collegamento. La parte disegnata in rosa, rappresenta il preambolo necessario al ricevitore per acquisire i sincronismi di trasmissione, seguito da un codice Start Frame Delimiter che segnala l'inizio della intestazione Ethernet. Quando un computer della LAN, osserva transitare sull'interfaccia di rete un pacchetto che riporta il suo indirizzo Ethernet nel campo destinazione, lo "tira su", e lo passa agli strati superiori. D.: Come fa il mittente a conoscere l'indirizzo Ethernet del destinatario, di cui conosce l'indirizzo IP?? R.: per mezzo dell'Address Resolution Protocol (ARP) Risoluzione ARP, broadcast I pacchetti ARP sono anch'essi incapsulati nelle trame ethernet, in cui ora il campo type ha il valore 0x0806. La figura seguente mostra l'ordine temporale con cui opera l'ARP, nel caso in 22 Lo strato applicativo di Internet Alessandro Falaschi cui Host A voglia comunicare con Host B, trovandosi entrambi nella stessa LAN. Ogni computer mantiene una cache (1) delle risoluzioni (gli indirizzi MAC associati agli IP) già ottenute di recente, in modo da evitare il ricorso ad ARP ogni volta. Le corrispondenza della cache sono mantenute per un periodo breve, (es 10 minuti), e possono essere visualizzate con il comando arp -a. La richiesta ARP (2) è inviata in broadcast, un pò come se qualcuno si affacciasse in corridoio, e gridasse: chi ha questo IP ? Ciò si ottiene indirizzando la richiesta ARP verso un indirizzo Ethernet di destinazione, pari ad una sequenza di uni. Le interfacce di rete di tutti i computer della LAN, quando osservano transitare un pacchetto Broadcast, sono obbligate a riceverlo, e passarlo allo strato superiore, che valuta le eventuali azioni da intraprendere. Host B quindi, invia la sua risposta ARP (4) in unicast, ossia usando l'indirizzo Ethernet di Host A come destinazione, comunicando così il proprio indirizzo MAC, usato come mittente. Possiamo verificare ciò che si verifica effettivamente, analizzando il risultato di questocapture prodotto con il comando ping www.libero.it, eseguito sul computer 192.168.120.40, avendo impostato il Default Gateway verso 192.168.120.1, che ospita anche il DNS locale. No Time Source Destination Protocol Info -----------------------------------------------------------------------------------------------1 0.0000 Intel_54:3b:a5 Broadcast ARP Who has 192.168.120.1? Tell 192.168.120.40 2 0.0011 Asiarock_5d:78:5d Intel_54:3b:a5 ARP 192.168.120.1 is at 00:13:8f:5d:78:5d 3 0.0012 192.168.120.40 192.168.120.1 DNS Standard query A www.libero.it 4 0.1048 192.168.120.1 192.168.120.40 DNS response CNAME vs-fe.iol.it A 195.210.91.83 5 0.1052 192.168.120.40 195.210.91.83 ICMP Echo (ping) request 6 0.1316 195.210.91.83 192.168.120.40 ICMP Echo (ping) reply 7 0.1362 192.168.120.40 192.168.120.1 DNS query PTR 83.91.210.195.in-addr.arpa 8 0.3933 192.168.120.1 192.168.120.40 DNS Standard query response PTR vs-fe.iol.it 9 1.1042 192.168.120.40 195.210.91.83 ICMP Echo (ping) request 10 1.1300 195.210.91.83 192.168.120.40 ICMP Echo (ping) reply 11 1.1302 192.168.120.40 192.168.120.1 DNS query PTR 83.91.210.195.in-addr.arpa 12 1.1313 192.168.120.1 192.168.120.40 DNS Standard query response PTR vs-fe.iol.it 13 5.1036 Asiarock_5d:78:5d Intel_54:3b:a5 ARP Who has 192.168.120.40? Tell 192.168.120.1 14 5.1036 Intel_54:3b:a5 Asiarock_5d:78:5d ARP 192.168.120.40 is at 00:16:6f:54:3b:a5 Innanzitutto, osserviamo (p 1-2) la richiesta-risposta ARP necessaria a determinare l'indirizzo Ethernet del DNS, in modo da poter inoltrare verso lo stesso, la richiesta (p 3-4) necessaria a risolvere il nome di dominio www.libero.it in un indirizzo IP. Poi (p 5-6) osserviamo la richiesta-risposta ICMP echo (notiamo come il DNS introduca una latenza di 10 msec, ed il ping solo di 3 msec), fatta passare attraverso il Default Gateway, seguita a sua volta (p. 7-8) da una richiesta di risoluzione inversa al DNS, eseguita allo scopo di poter scrivere a schermo il nome di dominio di chi ha risposto al ping; notiamo che stavolta, la latenza del DNS è di 20 msec. Dopo la seconda richiesta ICMP, osserviamo (p 11-12) una seconda richiesta di risoluzione inversa indirizzata al DNS, che stavolta viene servita in modo praticamente immediato, essendo la risposta già presente nella cache del DNS locale. Per finire, osserviamo (p. 13-14) una richiesta di risoluzione ARP, diretta stavolta dal Default Gateway verso il computer su cui stiamo operando, ed originata probabilmente dallo scadere della 23 Rete locale: strato di collegamento Trasmissione Internet cache ARP del router. Possiamo verificare come i pacchetti ARP siano privi di intestazione IP, essendo la loro circolazione esclusivamente interna alla rete locale. Notiamo infine, che l'assenza di un componente di autenticazione delle informazioni contenute nelle risposte ARP, costituisce una vulnerabilità per una rete LAN Switchata, che prende il nome di ARP poisoning. Riassumendo La figura seguente, illustra le fasi della risoluzione da URI a IP a MAC. 24 Lo strato applicativo di Internet Alessandro Falaschi Bridge e switch Il protocollo Ethernet originario prevedeva la trasmissione a 10 Mbit/sec mediante un cavo coassiale su cui si affacciavano tutti i computer; lo stesso mezzo trasmissivo veniva quindi usato "a turno" dai diversi computer, mediante ad una tecnica di accesso condiviso denominata Carrier Sense Multiple Access, Collision Detect (CSMA/CD), attuata dallo strato MAC 802.3, su cui non ci soffermiamo. Attualmente, oltre a prevedere velocità di trasmissione di 100 Mbit/sec, 1 Gbit/sec e 10 Gbit/sec, adottando due coppie ritorte, o la fibra ottica, i computer di una stessa LAN vengono sempre più spesso interconnessi secondo una topologia a stella, o ad albero, i cui nodi di transito sono denominati switch, ed i computer sono interconnessi su collegamenti full-duplex (es. 100BASE-TX). Gli switch sono dotati di più porte, ognuna delle quali gestisce le comunicazioni con un diverso computer (oppure con un altro switch, od un router). In questo modo, più computer di una stessa LAN possono trasmettere simultaneamente, senza interferenze reciproche, in quanto i domini di collisione restano separati per ognuna delle porte. Lo switch non usa un proprio indirizzo Ethernet, ma instrada sulle porte in uscita le stesse identiche trame che riceve sulle porte in ingresso. Quando un computer connesso allo switch trasmette verso una destinazione per la quale non si è mai osservato traffico, le trame sono ritrasmesse su tutte le porte; d'altra parte, ogni switch attraversato prende nota dell'indirizzo Ethernet del mittente, e riempie una propria tabella di instradamento, mettendo così in corrispondenza l'indirizzo, con la porta di provenienza. 25 TCP e UDP: strato di trasporto Trasmissione Internet Da quel momento in poi, ogni volta che lo switch riceverà un pacchetto indirizzato ad un computer di cui conosce la porta di connessione, ritrasmetterà il pacchetto solo su quella porta, evitando di disturbare gli altri. Allo scopo di accellerare il processo di apprendimento da parte dello switch, è possibile che all'accensione, i computer inviino dei pacchetti ARP di annuncio, detti gratuitous ARP, in cui affermano l'indirizzo MAC di se stessi. In tal modo, comunicano allo switch il proprio indirizzo Ethernet; nel caso il computer abbia cambiato la porta di connessione, lo switch provvede anche ad aggiornare la tabella di instradamento. Uno strumento per stimolare l'emissione di richieste ARP, è l'uso del comando ping, che invia un pacchetto ICMP (Internet Control message Protocol) di tipo Echo Request che, per raggiungere la destinazione, suscita appunto una richiesta ARP. Nel caso in cui quest'ultima non dovesse comparire, può essere benissimo il caso che la risoluzione sia già stata acquista dal proprio computer, e salvata nella cache: questo può essere investigato, invocando il comando arp -a. TCP e UDP: strato di trasporto Come già specificato, lo strato di trasporto esiste solo nei terminali posti ai bordi della rete, sorgente e destinazione delle unità informative generati dai processi applicativi (AP). Il paradigma Internet prevede due modalità di trasporto, indicate come UDP e TCP, e che si differenziano essenziamente per il fatto che • UDP offre un servizio di trasporto a datagramma, non garantisce la corretta consegna dei dati, e i pacchetti che compongono la comunicazione vengono trasmessi uno alla volta in modo indipendente; • TCP offre un servizio di trasporto a circuito virtuale, gestisce le eventuali ritrasmissioni delle unità non ricevute, ed effettua il riordino dei pacchetti fuori sequenza. Nel TCP, esiste una fase precedente alla comunicazione vera e propria, mediante la quale su entrambi gli estremi vengono predisposti dei buffer, e viene inizializzata una informazione di stato presso i due terminali, che tiene traccia dei pacchetti inviati e ricevuti. 26 Lo strato applicativo di Internet Alessandro Falaschi Per meglio illustrare la semantica dei termini datagramma e circuito virtuale, e comprendere come lo strato di trasporto possa arrichire la modalità di trasferimento delle informazioni offerta dallo strato di rete, facciamo di nuovo un passo indietro. Servizio con o senza connessione Quando abbiamo definito la multiplazione statistica in una rete a commutazione di pacchetto, abbiamo fatto un esempio basato su di un solo collegamento tra due nodi. Se invece mittente e destinatario sono collegati per il tramie di una rete, la trasmissione viene detta a comutazione di pacchetto, e sono definite due possibili diverse modalità di instradamento dei pacchetti da uno nodo di rete all'altro. Datagramma E' la modalitaà di instradamento già discussa a proposito della rete IP, in cui ogni pacchetto • • • • contiene l'informazione dell'indirizzo di destinazione, viaggia in modo indipendente dagli altri che fanno parte della stessa comunicazione, ogni nodo deve decidere da che parte reinviarlo, può essere consegnato in un ordine diverso da quello con cui era partito. In questo caso la trasmissione è detta senza connessione, a rimarcare il fatto che non esiste una fase iniziale, in cui si individua un unico instradamento, e si riservano delle risorse. Dato che UDP non aggiunge nulla a questa impostazione, limitandosi in pratica a specificare le porte sorgente e destinazione, il risultato è che il servizio di trasporto offerto è detto anch'esso a datagramma, o senza connessione. Canale virtuale e circuito virtuale Un approccio del tutto diverso, è quello delle reti a commutazione di pacchetto a circuito virtuale, come la rete X.25, una architettura ormai praticamente abbandonata, con origini precedenti ad Internet; lo stesso concetto, è poi stato nuovamente applicato nell'ambito delle reti ATM ed MPLS. In questo caso, l'instradamento viene determinato una volta per tutte all'inizio della trasmissione, durante una fase di richiesta di connessione, la cui presenza determina appunto la denominazione di servizio con connessione. Dopodiché, i pacchetti di uno stesso messaggio seguono tutti lo stesso percorso. La commutazione di pacchetto a circuito virtuale si basa su di una intestazione di pacchetto che, anziché presentare l'indirizzo di rete del nodo di destinazione, contiene invece un identificativo di connessione, che individua un canale virtuale (CV) tra coppie di nodi di rete, determinandone l'appartenenza ad una delle diverse trasmissioni contemporaneamente in transito tra i due nodi. L'identificativo del CV presente nel pacchetto, funge da chiave di accesso alle tabelle di routing presenti nei nodi, tabelle che sono ora generate nella fase di richiesta di connessione iniziale, durante la quale vengono inviati dei pacchetti di controllo che contengono l'indirizzo di rete del nodo di destinazione, e che causano nei nodi attraversati la memorizzazioine della porta di uscita, che sarà la stessa per tutti i successivi pacchetti dati appartenenti ad uno stesso messaggio. 27 TCP e UDP: strato di trasporto Trasmissione Internet Facciamo un esempio: una sorgente, a seguito della fase di instradamento, invia i pacchetti con identificativo CV = 1 al primo nodo individuato dal ruoting. Consultando la propria tabella, il nodo trova che il canale virtuale 1 sulla porta di ingresso (P.I.) A si connette al CV 3 sulla porta di uscita (P.U.) C. Ora i pacchetti escono da C con CV = 3, ed una volta giunti al nodo seguente sulla P.I. A, escono dalla P.U. B con CV = 2, e giungono finalmente a destinazione. Nel collegamento tra due nodi, numeri di CV diversi identificano le diverse comunicazioni in transito, ed uno stesso numero di canale virtuale, può essere riutilizzato su porte differenti dello stesso nodo anche nel contesto di comunicazioni diverse. La concatenazione dei canali virtuali attraversati viene infine indicata con il termine Circuito Virtuale, per similitudine con il caso di commutazione di circuito: tale similitudine trae origine dal fatto che, essendo l'instradamento lo stesso per tutti i pacchetti, questi viaggiano per così dire in fila indiana, e sono consegnati nello stesso ordine con cui sono partiti, un pò come se si fosse tracciato un vero e proprio Circuito. Arricchimento del servizio di rete Anche se il TCP, essendo un protocollo di trasporto, non ha nulla a che fare con l'instradamento, il fatto che garantisca al ricevitore la consegna dei pacchetti nello stesso ordine con cui sono partiti, e che par fare questo, preveda una fase di setup precedente alla trasmissione vera e propria, gli vale il merito di estendere anche a questo caso il termine di protocollo con connessione, risolvendo così a livello di trasporto, il problema del corretto sequenziamento dei pacchetti instradati attraverso una rete (IP) a datagramma. Controllo di sequenza, di errore, di flusso, e di congestione Una serie di problemi legati alla inaffidabilità di una rete a datagramma, basata sulla multiplazione statistica, sulle code, e sulla variabilità dell'instradamento per i paccheti di una stessa comunicazione, riguardano • come riordinare i pacchetti giunti fuori sequenza; • come ritrasmettere i pacchetti persi (controllo di errore); • come permettere al ricevitore di rallentare l'invio da parte della sorgente (controllo di flusso) • come ridurre e/o sospendere le trasmissioni nel caso in cui le prestazioni della rete siano eccessivamente degradate, in modo da non peggiorare la situazione (controllo di congestione) Un elemento comune alla soluzione di questi problemi, è costituito dalla numerazione progressiva dei pacchetti spediti (il numero di sequenza), e da una serie di pacchetti di ritorno, chiamati riscontri (o ACKnoledgment), mediante i quali il ricevitore conferma la corretta ricezione. Questi elementi sono utilizzati nell'ambito dei protocolli riscontrati, detti 28 Lo strato applicativo di Internet Alessandro Falaschi anche protocolli Automatic Repeat reQuest (ARQ), basati sull'uso di una finestra scorrevole, e sviluppatisi per gestire la comunicazione diretta, a livello di collegamento, tra due entità, e poi ri-utilizzati anche da un estremo all'altro della rete, a livello di trasporto, come avviene per il TCP, oppure anche a livello applicativo, come devono essere pronte a fare le applicazioni che si avvalgono del trasporto UDP. Ad ogni buon conto, osserviamo esplicitamente che il TCP non invia riscontri negativi, ma solo positivi. Infatti, nel caso di ricezione di un pacchetto corrotto, il comportamento del ricevitore è lo stesso che per un pacchetto perso, per il quale non viene inviato nessun riscontro. Al contrario, ogni pacchetto (che in questo caso è chiamato segmento) ricevuto correttamente viene inviato un riscontro (ACK) positivo, in mancanza del quale, una volta scaduto il timeout, il mittente provvede a ritrasmettere il segmento. Intestazione TCP L'intestazione TCP ha anch'essa dimensioni minime di 20 byte, in cui troviamo, tra le altre cose • i numeri delle porte sorgente e destinazione, che individuano il processo applicativo che lo ha prodotto, ed a cui è destinato il segmento; • i numeri di sequenza, espressi nei termini di numero di bye trasmessi, che permettono di ◦ ri-ordinare i pacchetti ricevuti in base al Sequence number, depositandoli al punto giusto, all'interno del buffer di ricezione; ◦ riscontrare i pacchetti ricevuti mediante l'Acknowledgement number, in modo che il trasmettitore possa liberare i buffer di trasmissione di tutto ciò che è stato riscontrato, ovvero di accorgersi se manca qualche riscontro, e quindi ritrasmettere il segmento corrispondente. • un gruppo di 8 bit detti flag, che caratterizzano il tipo di pacchetto, come di apertura (SYN) o di chiusura (FIN, RST) della connessione, ovvero contenente un riscontro (ACK), o dei dati urgenti (PSH, URG) • una dimensione di finestra, che indica quanti byte potranno essere accettati, a partire da quello referenziato dall'ACK number, e mediante la quale il ricevitore può modulare la velocità del trasmettitore. 29 TCP e UDP: strato di trasporto Trasmissione Internet • Entità TCP Nella figura che segue viene evidenziato come i processi applicativi che risiedono nello user space, accedano al servizio di trasporto offerto dagli strati inferiori che risiedono all'interno del kernel, per mezzo di primitive dette socket. Al suo interno, l'implementazione della entità di trasporto (in questo caso, il TCP) immagazzina i dati in transito da/verso lo strato applicativo nei send e receive buffer (SB e RB), le cui dimensioni dipendono dal sistema operativo, e dalla sua disponibilità di memoria. L'entità trasmittente, per dialogare con lo strato IP (che gli permette di usufruire del servizio di rete), preleva i dati dal SB in unità chiamate segmenti, per depositarli in ulteriori memorie da cui vengono inoltrati alla strato IP, ma anche conservati in modo da poter ritrasmettere gli eventuali pacchetti persi; l'entità ricevente invece, utilizza memorie simili per conservare i segmenti ricevuti dallo strato IP, in modo da poter sequenziare correttamente quelli ricevuti fuori sequenza, e quando ne completa una serie ordinata, li deposita nel RB, consentendo alla applicazione di prelevarli. La dimensione dei segmenti che vengono passati allo strato IP, e che in figura sono rappresentati dalle linee impilate nelle momorie in basso, è detta Maximum Segmet Size (MSS). Nel caso in cui lo strato fisico si interfacci con una LAN Ethernet, la massima dimensione del Payload della trama (ossia il pacchetto IP) è di 1500 bytes; pertanto in tal caso, tolti i 20 bytes della intestazione IP, ed i 20 bytes della intestazione TCP, restano 1500 - 40 = 1460 bytes per l'MSS. Adottare un MSS più grande, obbligherà lo strato di rete ad operare una frammentazione, con conseguente scadimento prestazionale; considerando poi che la presenza di opzioni negli header IP e TCP, ne può aumentare le dimensioni, può essere 30 Lo strato applicativo di Internet Alessandro Falaschi opportuno adottare un MMS inferiore a 1460 bytes. Apertura connessione Analizziamo più in dettaglio, il comportamento del TCP in corrispondenza della fase di attivazione della connessione. Come illustrato nella figura a fianco, il lato server (applicativo B) a seguito della sequenza di system call socket(), bind(), listen(), accept(), effettua una cosiddetta apertura passiva, mentre il lato client (applicativo A), con la connect(), invia un primo segmento di sincronizzazione, con il flag SYN attivo, in cui comunica che intende iniziare a contare i byte trasmessi da x (NS=x). Il server risponde inviando il riscontro (flag ACK pari ad 1) del primo segmento ricevuto, in cui dichiara di essere in attesa di ricevere il byte x+1 (NR=x+1); nello stesso segmento, sincronizza anche (NS=y) il numero di sequenza con cui conterà i dati inviati nelle risposte. A questo punto sembrerebbe tutto terminato, ma resta invece da riscontrare il server della ricezione del suo SYN, e questo avviene ad opera del terzo segmento. Questa modalità di sincronizzazione, prende il nome di stretta di mano a tre vie (three way handshake). 31 TCP e UDP: strato di trasporto Trasmissione Internet Protocollo a finestra Allo scopo di realizzare un controllo di flusso, il TCP mittente prevede l'uso del Numero di Riscontro (NR) inviato dal ricevente, per dosare il ritmo con cui trasmettere i propri segmenti. La dimensione massima di Finestra è comunicata con il SYN del ricevente, e determina la quantità di memoria riservata dal trasmittente per i buffer dedicati alla connessione, gestita come una memoria a scorrimento o finestra scorrevole, e che contiene i segmenti già trasmessi ed in attesa di riscontro. Man mano che altri segmenti vengono trasmessi, il limite superiore della finestra viene spostato a destra, finché questa non raggiunge la sua ampiezza massima, dopodichè la trasmissione si arresta; la ricezione di riscontri con NR maggiore del limite inferiore; causa lo spostamento a destra del limite inferiore della finestra, restringendola, in modo che nuovi pacchetti possano essere trasmessi. L'ampiezza massima della finestra di trasmissione è determinata come il minimo tra tre diversi criteri: • il primo è quello basato su di una stima del round-trip delay (RTT) del collegamento, in modo da poter trasmettere con continuità, come avviene per i protocolli di tipo Selective Repeat. Man mano che vengono inviati pacchetti verso la medesima destinazione, la stima dell'RTT è aggiornata, e la finestra modificata; • questa ampiezza massima può essere ridotta su intervento del ricevitore, allo scopo di realizzare un controllo di flusso, riducendo la velocità del trasmettitore, fino eventualmente ad azzerarla, oppure • può restringersi su iniziativa del trasmettitore, qualora si avveda di una situazione di congestione. Una finestra simile è adottata anche dal ricevente, per ricostituire l'ordine originario dei pacchetti, che possono essere consegnati disordinatamente dallo strato IP di rete. Non appena il ricevente acquisisce un segmento contiguo al limite inferiore, lo trasferisce nel Receive Buffer, e sposta il lmite inferiore in avanti, di tanti byte quanti ne ha ricevuti in modo contiguo, ed invia un riscontro, con NR pari al più basso numero di byte che ancora non è pervenuto. Ma se il Receive Buffer non è stato ancora svuotato dallo strato applicativo, l'entità ricevente non può liberare la coda dei segmenti provenienti dallo strato di rete, e deve chiedere al trasmittente di azzerare l'ampiezza della sua finestra di trasmissione. Controllo di errore Trascorso un certo tempo (detto Re-transmission Timeout o RTO) nell'attesa di un riscontro, il trasmittente ritiene che il segmento (o il suo riscontro) sia andato perso (e non semplicemente ritardato), e lo re-invia. Il valore del timeout viene impostato dinamicamente dal TCP in base alle sue misure di round-trip time RTT, stimato come media dei ritardi M con cui tornano gli ACK, ed a cui viene aggiunto un margine pari a quattro volte la stima della deviazione dei ritardi rispetto all'RTT, in modo che l'RTO è posto pari a RTO = RTT + 4D in cui RTT = a*RTT + (1-a)*M 32 e D = a*D + (1-a)*|RTT - M| Lo strato applicativo di Internet Alessandro Falaschi dove a = 0.9 rappresenta il coefficente del filtro passa-basso IIR di primo ordine, che esegue lo smoothing delle sequenze di osservazione. Così, il TCP si adatta alle condizioni di carico della rete, ed evita di ri-spedire segmenti troppo presto, o di attendere più del necessario. In particolare, nel caso di rete congestionata, la frequenza dei pacchetti persi aumenta, e valori di timeout troppo ridotti potrebbero peggiorare la situazione. Nel caso in cui la mancata ricezione di un riscontro sia dovuta alla perdita di un segmento in andata (anziché del suo ACK), mentre invece i segmenti successivi sono consegnati correttamente, allora il ricevente continua a citare, nei suoi ACK, sempre il numero di sequenza che indica il byte iniziale del segmento mancante. La ricezione di tre ACK consecutivi fuori sequenza causa nel mittente il convincimento che effettivamente un segmento sia andato perso (e non solo ritardato), ma dato che almeno tre segmenti sucessivi sono invece stati ricevuti corettamente, re-invia fiducioso il segmento perso, anche se l'RTO non è ancora spirato. La figura seguente (tratta ancora da MMC) esemplifica la procedura di controllo di errore, nel caso in cui • il Processo Applicativo AP del mittente scriva 3072 bytes nel Send Buffer SB; • il Maximum Segment Size MMS sia pari a 512 per entrambe le entità, e dunque servano 6 segmenti per tramettere il blocco di dati; • il mittente usi una Retransmission List per mantenere in memoria i segmenti non ancora riscontrati, ed una Variabile di invio (Send) V(S) dove memorizza il numero di sequenza del prossimo segmento da inviare; • il ricevente usi una Receive List per mantenere in memoria i segmenti ricevuti fuori sequenza, ed una Variabile di Ricezione V(R) dove memorizza il prossimo numero di sequenza che si aspetta di ricevere; • i segmenti 2 e 6 vengano persi, così come anche l'ultimo ACK. 33 TCP e UDP: strato di trasporto Trasmissione Internet Analizziamo ora ciò che accade: • il primo segmento è ricevuto correttamente, e dato che il suo numero di sequenza è uguale a V(R), viene trasferito nel RB, la Receive List è svuotata, V(R) si incrementa del MSS, e questo valore è inviato come numero di riscontro nell'ACK. Alla ricezione dell'ACK, il trasmettitore interrompe il Retrasmission Timer del primo segmento; 34 Lo strato applicativo di Internet Alessandro Falaschi • il secondo segmento viene scartato dalla rete, e dato che per questo non perviene nessun ACK, il suo timer continua ad incrementarsi. La ricezione dei segmenti (3), (4), (5), tutti fuori sequenza (infatti Seq>V(R)), determina nel ricevente il loro mantenimento nella Receive List, e l'invio di riscontri contenenti sempre lo stesso valore di V(R) = X + 512, ad indicare che manca ancora il segmento (2); • alla ricezione del terzo ACK duplicato (in quanto già ricevuto), il trasmettitore attua la procedura di fast retransmit, e re-invia il segmento (2) senza attendere lo scadere del timer (che viene resettato, e fatto ripartire). Questa volta il segmento (2) è ricevuto correttamente, ed il ricevente trasferisce tutto nel RB, svuota la receive list, pone V(R) = X + 2560 pari al primo byte del prossimo segmento atteso, ed usa questo valore per riscontrare tutto quanto ricevuto fino a quel momento; • il trasmittente, alla ricezione dell'ACK, rimuove dalla Retrasmissoin List tutti i segmenti riscontrati, ed elimina i rispettivi timer. Nel frattempo, il segmento (6) è stato scartato, e dato che è l'ultimo della comunicazione, il ricevente non invia nessun ACK, cosicché il suo timer giunge a scadenza, ed il segmento viene ritrasmesso; • stavolta è l'ACK di (6) ad andare perso, sicché il trasmittente, dopo un nuovo timeout, ritrasmette (6) per la terza volta. Il ricevente si avvede che si tratta di un duplicato, in quanto Seq < V(R), ma lo riscontra ugualmente, per notificare il mittente della sua corretta ricezione, in modo che possa esere svuotata la Retransmission List, e il timer rimosso. Controllo di flusso Nell'esempio precedente non si è tenuto conto di alcuna finestra, ovvero la stessa è stata supposta di dimensioni sufficientemente grandi, da non modificare il ritmo di invio dei segmenti. Ma come abbiamo detto, il meccanismo a finestra scorrevole determina, istante per istante, il numero massimo di bytes che possono essere trasmessi consecutivamente verso il destinatario senza ricevere riscontri, e consente ad un nodo poco veloce nella gestione dei segmenti ricevuti, di adeguare la velocità di tramissione alle proprie capacità. Infatti, il ricevente può variare (ridurre) la dimensione della finestra di trasmissione nel corso della connessione, impostando il valore presente nel campo Finestra presente nella intestazione TCP dei pacchetti inviati in occasione degli ACK. L'esempio che segue • assume che il mittente (veloce, a sinistra) possieda SB e RB di 4096 bytes, mentre il ricevente (lento, a destra) ha SB e RB di 2048 bytes, e questo determina che la dimensione di finestra impostata durante il three way handshake, nella direzione della comunicazione mostrata, sia di 2048 bytes; • mentre il lato trasmittente imposta una variabile send window Ws che indica il numero di bytes che possono ancora essere trasmessi, il lato ricevente aggiorna una variabile receive window Wr che indica quanti bytes può ancora trasferire nel RB; • l'applicazione di destra può immettere una quantità di dati nel SB fino al valore corrente di Ws, e se Ws > 0, l'entità TCP di trasmissione può copiare fino a Ws bytes nella lista di ritrasmissione, e trametterne i segmenti; se invece Ws = 0, allora la finestra è chiusa e la trasmissione interrotta; • quando l'entità TCP di ricezione (di destra) trasferisce i segmenti ricevuti in sequenza nel RB, decrementa corrispondentemente la Wr, mentre quando la corrispondente applicazione preleva dati dal RB, la Wr viene invece incrementata; in entrambi i casi, il nuovo valore di Wr viene anche comunicato al lato trasmittente, in modo da modificarne il grado di apertura della finestra. 35 TCP e UDP: strato di trasporto Trasmissione Internet La precedente figura può essere così commentata: • l'applicazione dal lato trasmittente deposita 3072 bytes nel SB del TCP, che ne preleva quanti ne permette l'ampiezza di finestra iniziale (2048) in 4 blocchi da 512, che sono 36 Lo strato applicativo di Internet Alessandro Falaschi subito inviati, decrementatando allo stesso tempoW s, finché (Ws=0) la trasmissione si arresta; • quando per l'entità TCP dal lato ricevente arriva il momento di essere eseguita, questa trasferisce nel RB i quattro segmenti ricevuti per i quali il numero di sequenza corrisponde a V(R), che viene a sua volta progressivamente incrementata di una quantità pari alla dimensione dei segmenti trasferiti; quindi, l'entità ricevente invia un unico ACK cumulativo, in cui però afferma di non poter ricevere altro (Wr=0), dato che il proprio RB è pieno. Ciò determina nel lato trasmittente la cancellazione dei segmenti riscontrati, ma non la riapertura della finestra; • quando per il processo applicativo del lato ricevente è il momento di essere eseguito, questo preleva 1024 bytes dal RB, e quindi l'entità TCP invia un nuovo ACK (detto di window update) con lo stesso numero di sequenza precedente, ma un nuovo valore di finestra, pari a 1024; questo permette al TCP trasmittente di inviare anche i segmenti (5) e (6), svuotando completamente il SB, dopodiché la finestra risulta nuovamente chiusa; • quando l'entità TCP d ricezione viene nuovamente eseguita, essa provvede a riscontrare gli ultimi due segmenti, lasciando la finestra chiusa, in quanto il RB è pieno; quest'ultimo viene successivamente letto in due fasi da parte dello strato applicativo, determinando la ri-apertura della finestra di trasmissione, finché all'ultimo le due entità si ritrovano nuovamente nelle condizioni iniziali. Qunto illustrato è solo un esempio, in quanto le diverse implementazioni del TCP possono, ad esempio, inviare un ACK per ogni segmento, o per ogni due, etc; ma fintantoché le dimensioni della lista di ricezione del TCP eguagliano quelle del proprio RB, la procedura basata sull'aggiornamento della finestra del trasmettitore assicura che al ricevitore ci sia sempre uno spazio sufficiente per memorizzare tutti i segmenti ricevuti. Controllo di congestione Durante il transitorio iniziale della comunicazione, in cui la stima di RTT non è ancora consolidata, la trasmissione inizia con una dimensione di finestra ridotta, che viene poi aumentata nel caso in cui non si verifichino errori, la rete sopporti il traffico, ed i nodi abbiano memoria disponibile. La figura che segue illustra l'evoluzione temporale dell'ampiezza della finestra di trasmissione, come conseguenza della strategia di controllo congestione più comue, e che fa uso di una ulteriore variabile al trasmettitore, la finestra di congestione Wc, che concorre assieme a Ws a determinare il massimo numero di bytes che si possono tramettere senza riscontri, ponendo la finestra di trasmissione pari alla più piccola delle due. Il primo pacchetto in assoluto viene inviato con una strategia di tipo Send and Wait, ovvero adottando una finestra di dimensione pari ad un solo segmento, e aspettandone dunque il riscontro, prima di inviarne un secondo. Da qual momento, la connessione entra nella fase di slow start, in cui l'ampiezza della finestra di trasmissione Wc aumenta esponenzialmente nel tempo, in quanto è incrementata di un segmento per ogni ACK ricevuto. Infatti, il primo ACK perviene dopo t0 + RTT, e porta Wc a due segmenti, i cui riscontri pervengono ad un istante t0 + 2RTT, portando l'ampiezza a 3 e poi a 4; i 4 ACK successivi arrivano quindi dopo t0 + 3RTT, e portano Wc a 8. Dopo altri due RTT, Wc raggiunge la dimensione di 32 segmenti, determinando il raggiungimento della soglia di slow start (SST) in un tempo pari a circa cinque volte il valore dell'RTT. 37 TCP e UDP: strato di trasporto Trasmissione Internet Una volta raggiunto il valore di SST, la Wc viene incrementata al ritmo di 1/ Wc segmenti per ogni segmento riscontrato, ossia di un segmento per ogni Wc segmenti riscontrati, e dato che i segmenti partono ad un ritmo di Wc per ogni RTT, questo determina un aumento per Wc pari ad un segmento ogni RTT, ovvero lineare nel tempo: questa fase è indicata con il termine di congestion avoidance. Al raggiungimento di una seconda soglia per Wc (in figura, posta pari a 64) la procedura di controllo congestione entra in una ulteriore fase, in cui il valore di Wc è mantenuto costante, e Wc è detta pienamente aperta. In una rete congestionata, i nodi iniziano a scartare pacchetti. Possono essere scartati i pacchetti di riscontro, o quelli spediti. Nel primo caso, il segmento ritrasmesso, se arriva a destinazione, viene scartato dal ricevitore perché duplicato, ma è riscontrato comunque, e dunque il trasmettitore si vede tornare indietro degli ACK duplicati. Nel caso in cui, invece, vadano persi degli ACK, il trasmettitore inizia a constatare lo scadere dei timer di ritrasmissione. Nel caso di perdita dei segmenti trasmessi, il TCP mittente interpreta la ricezione di tre ACK duplicati per uno stesso segmento, come un indicatore di una situazione di congestione. Anche se di fatto, la ricezione degli ACK (per di più duplicati) indica che la rete è ancor in grado di trasmettere, la perdita di un segmento trasmesso può segnalare l'inizio di un fenomeno di congestione. Peranto, il TCP reagisce dimezzando la dimensione attuale della finestra di congestione, riducendo così il numero di segmenti non riscontrati che è possibile trasmettere in sequenza, e di conseguenza, riducendo il carico della rete. A questo 38 Lo strato applicativo di Internet Alessandro Falaschi punto, Wc riprende a crescere, secondo una modalità detta di fast recovery, in accordo alle regole viste in precedenza, ma ogni volta che vengono ricevuti 3 ACK duplicati, Wc viene nuovamente dimezzata, e via di seguito. A questo meccanismo se ne aggiunge quindi un altro, legato alla perdita dei riscontri, e basato sulla interpretazione dell'evento di scadenza di un timeout, come segnale di un grave peggioramento della congestione, in quanto ciò indica che sono andati persi tutti gli ACK relativi ai segmenti che erano presenti nella stessa finestra di trasmissione, oppure (ma nessuno può saperlo) tutti i segmenti stessi. In questo caso il comportamento del TCP è quello di resettare la procedura di controllo congestione, azzerando la dimensione di Wc, e ri-partendo dalla fase di slow start, che come abbiamo visto, inizia in modalità send and wait. 39 Riferimenti Trasmissione Internet Esempio di capture In questo esempiopossiamo osservare l'apertura della conessione, la variazione della finestra annunciata dal ricevitore, l'andamento di numeri di sequenza, e di riscontro. Riferimenti [AL] - Appunti di informatica libera di Daniele Giacomini [UPF] - Unix Programming Frequently Asked Questions di Andrew Gierth [MMC] - Multimedia Communications di Fred Halsall, ed. Addison-Wesley 2001, ISBN 0-201-39818-4 [BAF] - I procolli TCP/IP di Behrouz A. Forouzan - Ed McGraw-Hill 2001, ISBN 88-386-6005-0 [TST] - Trasmissione dei Segnali e Sistemi di Telecomunicazione di Alessandro Falaschi - Ed Aracne 2003, ISBN 88-7999-477-8 [MCX] - man.cx - Tutte le Man page Computer Networks - di Theo Schouten Realizzato con da Alessandro Falaschi - 40 Lo strato applicativo di Internet Alessandro Falaschi 41 Lo strato applicativo di Internet Network programming Vengono analizzate le chiamate di sistema necessarie alla creazione ed al mantenimento di una canale di comunicazione che consenta a due computer posti ai bordi di Internet, di comunicare tramite essa. Sono discussi esempi di semplici applicazioni client e server, e per quest'ultimo, è discussa e sperimentata la modalità di servizio a thread singolo basata sull'I/O multiplaito sincrono. • Applicazioni di rete ◦ Tipologie di serventi • Comunicazione tra processi tramite Internet ◦ Creazione di un socket ◦ Utilizzo dei socket • Processi e Fork • Un esempio di server parallelo ◦ ◦ ◦ ◦ ◦ Segnali Errori Opzioni del socket Indirizzi speciali Rappresentazione degli interi e reperimento degli indirizzi ▪ ▪ ▪ ▪ ▪ • • • • • • • Big endian Little endian da Host a Network Order e ritorno da Network a Ascii e ritorno da Nome a indirizzo IP e ritorno Un esempio di client Connessioni di tipo DGRAM I/O multiplato sincrono Socket non bloccante Client broadcast Riepilogando Riferimenti Applicazioni di rete A differenza delle applicazioni desktop, che per funzionare non hanno bisogno di una connessione in rete, le applicazioni di rete sono quei programmi che di mestiere interagiscono con altri programmi in esecuzione su computer remoti. I paradigmi più diffusi per le applicazioni di rete sono: • cliente/servente (client/server); • paritetico (p2p, peer to peer); • a tre (o più) livelli (three-tier, multi-thier). Lo strato applicativo di Internet Alessandro Falaschi Il modello a tre livelli, di cui non ci occupiamo ora, prevede di disaccoppiare le tre funzioni di interfaccia utente, logica di calcolo, ed accesso ai dati, che sono svolti da processi indipendenti; un esmpio di questo tipo di applicazioni sono le Applicazioni Web basate su CGI. Il modello paritetico (peer to peer) ha acquisito importanza e notorietà grazie alla diffusione dei servizi di condivisione di file multimediali, e pure non verrà per ora affrontato. Il modello su cui ora ci focalizzeremo, è quello cliente/servente, sia nel caso di protocolli di trasporto orientati alla connessione (TCP) che non (UDP). In una applicazione client-server i computer in comunicazione non hanno ruoli identici, ma sono definite, per così dire, due personalità, in cui uno (il client) svolge la funzione di richiedente, mentre l'altro (il server) risponde. Nel caso in cui anche il computer server si trovi nella necessità di effettuare delle richieste, allora esisterà al suo interno un diverso processo, avente funzione di client. Tipologie di serventi Dal punto di vista dello strato di trasporto, una applicazione server risponde presso un numero di porta ben noto, tipicamente elencato nel file /etc/services, e possedere i privilegi necessari ad aprirla. Il client quindi inserisce nell'intestazione di trasporto, questo numero di porta ben noto come destinazione; viceversa, il numero di porta di origine usato dal client è detto effimero, perché deciso in modo estemporaneo al momento della richiesta, come evidenziato nelle figure che seguono, tratte da BAF. A seconda del modo in cui sono gestite le richieste, possiamo classificare i server come • iterativi, o seriali: rispondono alle richieste e restano occupati fino al loro completamento, dopodiché tornano disponibili. E' questo il caso in cui le richieste e le risposte impegnino un solo pacchetto IP, come nel trasporto UDP, e la generazione delle risposte duri un tempo trascurabile; • concorrenti, o paralleli: creano processi figli (o thread) incaricati di rispondere, e tornano in ascolto di altre richieste. I processi figli, una volta esaurito il loro compito, terminano. Si tratta del caso in cui l'interazione avvenga utilizzando un trasporto con connessione ed affidabile, ovvero il TCP, e l'intervallo tra due richiese sia generalmente superiore a quello necessario a generare una risposta. 43 Comunicazione tra processi tramite Internet Network programming In realtà ci sono almeno altri due modi per realizzare un server parallelo, di cui ci occuperemo più avanti: • multiplazione sincrona dell' I/O: il server riesce allo stesso tempo ad accettare nuove connessioni ed a servire quelle già attive, grazie all'uso di una istruzione un pò particolare, la select(); • polling ciclico: il server si pone in modalità non bloccante, e si occupa periodicamente di controllare se ci sono nuove connessioni e di servire quelle attive. Ma è da evitare. Comunicazione tra processi tramite Internet L'interfaccia software o API (Application Program(ming) Interface) di comunicazione in assoluto più utilizzata fra processi in rete, sono i socket di Berkeley, di Unix BSD, definita nel 1982 in C per Unix BSD 4.1c, è rimasta sostanzialmente invariata da allora. Socket è la parola inglese che indica una presa (elettrica), significando in questo caso l'inserimento metaforico di uno spinotto, da parte di un programma, nella presa che lo collega ad un altro, per il tramite della rete. Ma dato che l'I/O di un programma, non è un segnale elettrico bensì numerico, è più appropriato pensare al socket come ad un identificatore di FILE, che ci permette di leggere/scrivere su/da un altro programma, anziché su/da disco. Dal punto di vista della stratificazione funzionale offerta dal modello ISO-OSI, nella sua implementazione semplificata offerta dal modello TCP/IP, un socket rappresenta il SAP (Service Access Point) dello strato di trasporto, nei confronti del quale il processo applicativo si comporta come il client che utilizza i servizi di trasporto e di rete. Creazione di un socket Nel web esistono valide risorse (BJN, GaPiL, SOG, IIC) relative al network programming. Inoltre, moltissime chiamate di sistema Unix sono documentate con gran dettaglio nelle pagine MAN richiamabili da linea di comando. In particolare, la chiamata socket(2) #include <sys/types.h> #include <sys/socket.h> int socket(int domain, int type, int protocol) 44 Lo strato applicativo di Internet Alessandro Falaschi è quella che ci permette di collegarci allo strato di trasporto. La parametro domain è uno dei define presenti in bits/socket.h, incluso in sys/socket.h, e definisce la Protocol Family da usare, che per Internet è PF_INET. Il campo type specifica il tipo di trasporto da usare nell'ambito della famiglia, e può essere impostato come SOCK_STREAM per il TCP, SOCK_DGRAM (datagram) per UDP, o SOCK_RAW per bypassare lo strato di trasporto ed inviare in rete pacchetti forgiati a mano. Infine il parametro protocol, per PF_INET, può essere posto a zero. In caso di successo, la chiamata socket() restituisce un intero, o handle (maniglia), che identifica il descrittore relativo al socket creato, e che è costituito da una struct C contenente i campi illustrati nella figura che segue, in cui famiglia, tipo e protocollo sono quelli specificati nella chiamata, mentre le due sotto-strutture indirizzo, di tipo sockaddr_in, identificano i due estremi della comunicazione. Le strutture C che rappresentano entrambi i socket locale e remoto, sono costituite dai campi riportati nella figura ancora appresso, in cui il primo e l'ultimo campo non sono in genere usati, sin_family è di nuovo posto a PF_INET, e sin_port, sin_addr identificano l'indirizzo di trasporto (un numero di porta TCP o UDP) e di rete (i 4 byte di indirizzo IP) del socket, tanto che proprio questi due numeri (porta ed IP) sono, in definitiva, l'incarnazione più concreta di un socket. Come si vede dalla definizione, l'indirizzo IP sin_addr è in realtà descritto mediante la struttura in_addr, che come riportiamo sotto a destra, è composta da un solo membro, s_addr, che consiste di un intero senza segno, e che appunto rappresenta i 32 bit associati all'indirizzo IP 45 Comunicazione tra processi tramite Internet Network programming Utilizzo dei socket La creazione di un socket, è solo la prima delle richieste che lo strato applicativo effettua verso la API offerta dall'interfaccia socket, che offe un insieme di possibili chiamate diverse. La figura che segue esemplifica il caso di un client che accede ad un server remoto operante in modalità parallela, e ci serve come linea guida per collocare le diverse chiamate nel rispettivo ruolo funzionale e sequenza temporale. 46 Lo strato applicativo di Internet Alessandro Falaschi Nella figura, le trasmissioni che si svolgono internamente allo stack TCP/IP e non fuoriescono dall'interfaccia socket, sono mostrate con una linea tratteggiata, e rappresentano l'applicazione dei protocolli tra pari strettamente sottostanti lo strato applicativo, come ad esempio, il three way handshake, o la trasmissione degli ACK. Dopo la creazione del socket, la chiamata a bind() #include <sys/types.h> #include <sys/socket.h> int bind(int sockfd, struct sockaddr *my_addr, int addrlen); sul lato server, serve a specificare l'indirizzo locale su cui porsi in ascolto. Nei parametri che vengono passati, sockfd è pari all'handle del socket precedentemente creato, my_addr dovrà essere opportunamente inizializzato con indirizzo IP e porta locale (vedi esempio), mentre addrlen prende il valore di sizeof(struct sockaddr). Se tutto va bene, bind() restituisce 0, altrimenti -1, ad es. quando la porta è già occupata, ovvero un'altra applicazione ha già aperto un socket, su quella stessa porta. 47 Comunicazione tra processi tramite Internet Network programming La chiamata seguente, listen() int listen(int sockfd, int backlog); è quella che effettivamente abilita il socket sockfd ad accettare le richieste in arrivo, che se giungono in questa fase, vengono parcheggiate in una coda (e non è inviata nessuna risposta) di dimensione backlog; al riempimento della coda, il server risponde con dei messaggi di rifiuto. L'istruzione successiva, accept(), int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen); è di tipo bloccante, ovvero se la coda delle richieste è vuota, il server rimane in attesa. Se invece c'é già una richiesta, o quando ne arriva una, accept() ritorna, e scrive nella struttura puntata da addr gli estremi del socket remoto relativo al lato client. Prima della chiamata, *addrlen è preimpostato a sizeof(struct sockaddr_in), allo scopo di impedire ad accept() di sovrascrivere altre parti di memoria, ed a sua volta, accept() modifica *addrlen assegnandogli il numero di byte scritti. L'intero di ritorno, costituisce in realtà un nuovo handle di socket, che dovrà essere usato nelle successive operazioni di lettura/scrittura; nel caso di errori, invece, accept() restituirà -1. Il server si troverà ora a disporre di due socket, come evidenziato nella figura più in basso: uno, è quello che resta in attesa di nuove chiamate, e l'altro è quello connesso con la chiamata accettata, e che verrà chiuso al termine del servizio. Dal lato client, notiamo che dopo la chiamata a socket(), manca la chiamata a bind(). Infatti, la scelta del numero di porta locale da utilizzare viene demandato al kernel, ovvero, viene usata una porta effimera; l'indirizzo IP locale viene invece scelto (nel caso il computer sia multihomed) sempre dal kernel, come quello associato alla interfaccia in grado di raggiungere l'indirizzo di destinazione. Si, ma la destinazione qual'è ? ... E' specificata nella chiamata a connect() int connect(int sockfd, struct sockaddr *serv_addr, int addrlen); anch'essa di tipo bloccante, ovvero non ritorna finchè il server non ha risposto, in modo positivo o negativo. Prima della chiamata, *serv_addr è preimpostato con IP e porta della destinazione, e addrlen è posto a sizeof(struct sockaddr). Al ritorno, connect() restituirà -1 in caso di fallimento. Ora che finalmente le due parti sono collegate, avendo richiesto un socket() di tipo SOCK_STREAM, queste possono inviarsi dati usando le chiamate send() e recv(), indipendentemente da chi sia il server od il client: int send(int sockfd, const void *msg, int len, int flags); in cui sockfd è l'handle del socket locale sui cui inviare i dati (restituito dalla chiamata a socket() per il client, ovvero quello restituito dalla accept() per il server); msg è un puntatore ai dati da inviare, e len è la lunghezza dei dati, in bytes; flags può essere 0, ovvero essere posto ad uno o più dei valori descritti nella manpage di send(). Il valore di ritorno indica il numero di bytes effettivamente inviati, e nel caso sia inferiore a len, è 48 Lo strato applicativo di Internet Alessandro Falaschi compito dell'applicazione reinviare successivamene i dati mancanti. Dall'altro lato della connessione, occorre invocare recv() int recv(int sockfd, void *buf, int len, unsigned int flags); dove sockfd è l'handle da cui leggere, buf punta a dove le informazioni ricevute devono essere scritte, len rappresenta la dimensione dell'area di memoria puntata da buf, e flags può nuovamente essere posto a zero, od a quanto indicato nella manpage di recv. Una volta che lo scambio di dati è terminato, si può chiudere la connessione, invocando close(sockfd); oppure int shutdown(int sockfd, int how); La differenza, è che mentre close() ha un effetto di chiusura totale, con shutdown() la connessione può essere chiusa anche in sola direzione; per liberare del tutto l'handle del socket, occorrà comunque chiamare lo stesso close(). Processi e Fork Ancora con riferimento alla figura precedente, osserviamo che quando la accept() del server ritorna, questo esegue una fork(). pid_t fork(); Ciò significa che il processo che sta eseguendo il codice del server viene clonato, ovvero vengono duplicate le sue aree di memoria programma e memoria dati, che vengono associate ad un nuovo PID (Process IDentifier). Ad esempio, per conoscere il pid di tutti i programmi in esecuzione sul proprio computer Linux, è sufficiente impartire il comando ps ax in cui ps sta per process status, e ax sono due opzioni che permettono di vederli tutti. Il programma clonato è detto figlio (child), ed inizia la sua esecuzione esattamente dallo stesso punto in cui si trovava il padre al momento del fork(): in effetti, è stato clonato anche il program counter, completo del suo valore! E.. come si distinguono padre e figlio, tra loro? semplice ! la fork() restituisce zero al figlio, ed il pid del figlio, al padre, come esemplificato qui sotto: #include <stdio.h> #include <sys/types.h> #include <unistd.h> int main (void) { 49 Processi e Fork Network programming pid_t pid; pid = fork(); if (pid != 0) { printf ("Il processid del padre è %ld e quello del figlio è %ld\n", (long)getpid(), pid); } else { printf ("Il processid del figlio è %ld, e quello del padre %ld\n", (long)getpid(), (long)getppid()); } } Esercizio: compilare (con cc) il codice proposto, ed eseguirlo (./a.out). Modificarlo in modo che sia il padre che il figlio non terminino subito, ma (ad es.) attendano un input da tastiera (ad es, usando una scanf()). Verificare con ps axf la gerarchia dei processi ed i rispettivi pid. Uccidere il figlio con kill, e verificare (con ps) il suo stato di "zombie". Come si vede, lo stesso programma contiene sia il codice del padre che quello del figlio, cosicché a seguito dell'esecuzione della fork(), tutti i descrittori di file aperti, così come i descrittori dei socket, vengono duplicati, come mostrato nella figura che segue. Come già discusso, dopo l'esecuzione della accept() il processo possiede due socket, uno ancora in ascolto e l'altro connesso al client, ed a seguito della fork(), entrambi si duplicano. Quindi, il processo padre provvederà a chiudere la sua copia del socket connesso, ed il processo figlio chiuderà la sua copia del socket in ascolto. 50 Lo strato applicativo di Internet Alessandro Falaschi Un esempio di server parallelo Siamo finalmente pronti a mostrare un diagramma di flusso che descrive in modo chiaro le operazioni fin qui discusse. Osserviamo che nel disegno precedente, vengono utilizzate le istruzioni write() e 51 Un esempio di server parallelo Network programming read() anziché send() e recv(): le prime sono esattamente le stesse che è possibile usare con dei files su disco; le seconde, permettono di specificare dei parametri in più, come ad esempio dei flags, così chiamati perché associati ognuno, ai diversi bit presenti in unica parola. L'uso dei flag permette di variare il comportamento del socket in situazioni particolari. Ad esempio la write(), nel caso in cui la dimensione dei dati da spedire sia eccessiva, rimane bloccata finché i buffer di alimentazione del TCP non siano stati liberati; specificando invece tra i flag di send(), la macro MSG_DONTWAIT, è possibile ottenere un comportamento non bloccante, e permettere alla chiamata di tornare subito, restituendo l'errore EAGAIN. Allo stesso modo, la recv() resta di per sé bloccata se non c'é nulla da leggere, mentre se invocata con il flag MSG_DONTWAIT, assume un comportamento non bloccante, e nella stessa circostanza, ritorna con un errore. Ma per concretizzare le idee, non resta che mostrare il codice di una implementazione reale che, una volta accettata una connessione, si limita ad inviare al client la stringa "Hello, world!" e termina (vedi linea 68): 1 2 3 /* ** server.c -- a stream socket server demo */ 4 5 6 7 8 9 10 11 12 13 14 #include #include #include #include #include #include #include #include #include #include #include <stdio.h> <stdlib.h> <unistd.h> <errno.h> <string.h> <sys/types.h> <sys/socket.h> <netinet/in.h> <arpa/inet.h> <sys/wait.h> <signal.h> 15 #define MYPORT 3490 16 #define BACKLOG 10 // the port users will be connecting to // how many pending connections queue will hold 17 void sigchld_handler(int s) { 18 while(waitpid(-1, NULL, WNOHANG) > 0); 19 } // executed on children exit // read exit state for all the children 20 int main(void) { 21 22 23 24 25 26 int sockfd, new_fd; struct sockaddr_in my_addr; struct sockaddr_in their_addr; socklen_t sin_size; struct sigaction sa; int yes=1; 27 28 29 30 if ((sockfd = socket(PF_INET, SOCK_STREAM, 0)) == -1) { perror("socket"); exit(1); } 31 32 33 34 35 36 37 38 39 if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int)) == -1) { perror("setsockopt"); exit(1); } 40 41 42 43 if (bind(sockfd, (struct sockaddr *)&my_addr, sizeof(struct sockaddr)) == -1) { perror("bind"); exit(1); } 44 if (listen(sockfd, BACKLOG) == -1) { my_addr.sin_family = AF_INET; my_addr.sin_port = htons(MYPORT); my_addr.sin_addr.s_addr = INADDR_ANY; memset(&(my_addr.sin_zero), '\0', 8); // // // // // // // // listen on sock_fd, new connection on new_fd my address information connector's address information will hold sockaddr size host byte order short, network byte order automatically fill with my IP zero the rest of the struct 52 Lo strato applicativo di Internet Alessandro Falaschi 45 46 47 } 48 49 50 51 52 53 54 sa.sa_handler = sigchld_handler; // reap all dead processes sigemptyset(&sa.sa_mask); sa.sa_flags = SA_RESTART; if (sigaction(SIGCHLD, &sa, NULL) == -1) { perror("sigaction"); exit(1); } 55 56 57 58 59 60 61 62 63 64 65 while (1) { // main accept() loop sin_size = sizeof(struct sockaddr_in); if ((new_fd = accept(sockfd, (struct sockaddr *)&their_addr, &sin_size)) == -1) { perror("accept"); continue; } struct hostent *he; he = gethostbyaddr ((struct in_addr *)&their_addr.sin_addr, sizeof (struct in_addr), AF_INET); printf("server: got connection from %s, remote name %s\n", inet_ntoa(their_addr.sin_addr), he->h_name); 66 67 68 69 70 71 72 73 74 75 perror("listen"); exit(1); if (!fork()) { // this is the child process close(sockfd); // child doesn't need the listener if (send(new_fd, "Hello, world!\n", 14, 0) == -1) perror("send"); close(new_fd); exit(0); // child ends here } close(new_fd); // parent doesn't need this } return 0; 76 } Una implementazione alternativa che fa uso di thread anziché di processi figli, può essere trovata presso neworder. Qui sotto, spieghiamo alcuni degli elementi che ancora ci mancano per comprendere le operazioni compiute dal listato del server. Segnali I processi possono comunicare tra loro in modo molto primitivo, inviandosi dei segnali, che non portano altra informazione oltre al loro tipo (il numero di segnale), scelto tra pochi. Sono usati dal kernel per notificare situazioni eccezionali ai processi, e per notificare eventi tra processi, come ad es. la terminazione di un processo figlio, o la richiesta di interruzione espressa con una combinazione di tastiera (es Control-C). Si possono inviare segnali ai processi, mediante il comando kill [ -signal ] pid in cui pid indica il processo a cui inviare il segnale, ed i numeri più usati per signal sono riportati appresso: Name Num ALRM HUP INT KILL PIPE TERM 14 1 2 9 13 15 Action Description exit exit il processo si re-inizializza exit exit terminazione forzata - non può essere bloccato exit exit 53 Un esempio di server parallelo CHLD URG STOP CONT ABRT FPE ILL QUIT SEGV TRAP 6 8 4 3 11 5 Network programming ignore ignore stop non può essere bloccato restart continua se stoppato, altrimenti ignora core genera anche un core dump a fini diagnostici core core core core core Ad esempio, Control-C equivale ad inviare il 15 (TERM), mentre con il 9 (KILL) termina di sicuro. Tornando al nostro listato di server, quando un processo figlio termina, invia al padre il segnale CHLD, ed anche se le risorse da esso impegnate (memoria, buffer) sono liberate, il descrittore del suo processo rimane in memoria (producendo un cosiddetto processo zombie) finchè il processo padre non esegue una istruzione waitpid() per leggere il suo stato di uscita, memorizzato appunto nel suo descrittore. Nella tabella precedente, la colonna Action indica cosa succede al processo che riceve il segnale, ed osserviamo che nel caso di SIGCHLD, non accade nulla. Allora, la funzione sigaction() (linea 51) per così dire arma il segnale, nel senso che specifica quale subroutine eseguire alla ricezione di un dato segnale. Questa viene assegnata dalla istruzione sa.sa_handler = sigchld_handler; in cui sigchld_handler è appunto il puntatore alla routine di gestione del segnale, e sa è una struct sigaction, i cui campi sa_mask e sa_flag vengono pure inizializzati congruentemente. Alla ricezione del segnale SIGCHLD quindi, il processo padre interrompe il normale flusso del codice, e si porta ad eseguire il codice contenuto nell'handler, come fosse una subroutine invocata dal programma stesso. Per quanto riguarda la procedura sigaction(), questa si limita a loopare finché ci sono processi zombie da mietere (traduzioe letterale di to reap dead processes), e poi termina. Errori Osserviamo che spesso viene verificato l'intero restituito dalle system call, che se negativo indica una condizione di errore. In tal caso, viene effettuata una chiamata (vedi linee 28, 32, 41, 45, 52, 58, 69) a #include <stdio.h> void perror(const char *s); che ha l'effetto di stampare su standard error il messaggio associato all'ultima condizione di errore che si è verificata, preceduto dalla stringa posta in argomento, più due punti ed uno spazio, in modo che si capisca in che punto si è verificato l'errore. Quando una system call fallisce, imposta una variabile interna ad un valore (errno) che identifica appunto il tipo di errore; questo valore viene quindi usato dalla funzione perror() come indice nell'array di stringhe sys_errlist[], che contiene le descrizioni testuali dei motivi di errore #include <errno.h> const char *sys_errlist[]; int sys_nerr; int errno; 54 Lo strato applicativo di Internet Alessandro Falaschi Opzioni del socket Alla linea 31 troviamo l'invocazione di setsockopt(), che permette di agire sui parametri che modificano il comportamento del socket, e che possono essere specificati ai diversi livelli della pila protocollare: il secondo argomento posto pari a SOL_SOCKET, specifica che si deve intervenire a livello di socket, ed il terzo posto pari a SO_REUSEADDR, permette a bind() di avere successo, anche se una altro processo occupa già la stessa porta, come potrebbe accadere, ad esempio, se un processo figlio (di una precedente istanza del server) è rimasto in esecuzione, ed in collegamento con un client remoto. Indirizzi speciali Alla linea 38 osserviamo che l'indirizzo my_addr.sin_addr.s_addr viene impostato a INADDR_ANY. Questa costante è un intero a trentadue bit, tutti pari a zero, definita all'interno dell'header netinet/in.h, ed è il modo per indicare questo host su questa rete: in pratica ciò vuol dire che, anche se il computer possiede più di un indirizzo, qualunque questo sia, per il solo fatto che lo strato IP abbia accettato un pacchetto destinato ad uno di questi indirizzi, lo stesso pacchetto deve essere accettato anche dal socket. Viceversa, assegnando a my_addr.sin_addr.s_addr un indirizzo specifico tra quelli dell'host, imponiamo che il server venga raggiunto solo dalle richieste destinate esattamente a quell'indirizzo. In netinet/in.h sono anche definiti altri indirizzi speciali, come INADDR_BROADCAST posto a tutti uno, che quando usato come destinazione, specifica tutti gli host su questa sottorete; INADDR_LOOPBACK che vale 127.0.0.1, e specifica lo stesso host da cui parte il pacchetto (qualunque esso sia), ed impone allo strato IP di non inoltrare il pacchetto verso lo strato di collegamento. Rappresentazione degli interi e reperimento degli indirizzi Alla linea 37, la variabile my_addr.sin_port viene impostata al valore htons(MYPORT). Mentre MYPORT rappresenta l'indirizzo di trasporto presso il quale ascolta il server, la chiamata a htons()serve a convertire tra la rappresentazione degli interi adottata dalla architettura (CPU) utilizzata, ovvero come questi vengono organizzati in memoria detto anche host order, ed il modo in cui gli stessi interi sono invece disposti nelle intestazioni dei pacchetti, il network order. Le due diverse disposizioni in memoria sono le cosiddette Big endian e Little endian: Big endian La disposizione big endian dispone in memoria i bytes iniziando dal più significativo (Most Significant, MS) al meno (less) significativo (LS), ed è quella adottata, oltre che per il network order, dai mainframe IBM e dai processori Motorola, PowerPC, SPARC. 55 Un esempio di server parallelo Network programming Little endian La disposizione little endian al contrario, dispone i byte in memoria dal meno al più significativo, ed è adottata dai processori Intel. Da Host a Network Order e ritorno Per rendere indipendente lo strato di rete, che opera in modalità big endian, da quello dello strato applicativo, che rappresenta gli interi in accordo alla propria dotazione hardware, ora chiamata Host, sono definite due funzioni per convertire dall'Host order verso il Network order e viceversa, chiamate hton=host to network e ntoh=network to host. In effetti, però, le funzioni necessarie sono quattro, perché vanno specializzate ai casi di intero corto (16 bit, short) od intero lungo (32 bit, long), e si ottengono aggiungendo a hton e ntoh i suffissi, rispettivamente, s e l, come mostrato in figura. Nel passaggio di indirizzi tra host e rete, sono usati short per i numeri di porta, e long per gli indirizzi IP. Definiamo ora, due altre funzioni molto utili. Da Network a Ascii e ritorno La chiamata a inet_aton() è il modo per convertire un indirizzo dato nella forma "192.168.151.122" che, anche se rappresenta una sequenza di numeri, è realizzata come una stringa di caratteri ascii, nella sua rappresentazione di intero a 32 bit, espresso in network byte order. Alla linea 65, la chiamata a inet_ntoa() svolge la conversione inversa, passando da network order ad ascii. 56 Lo strato applicativo di Internet Alessandro Falaschi Da nome a indirizzo IP e ritorno La chiamata a gethostbyname() , che sarà usata nel prossimo listato, permette al processo applicativo di risalire all'indirizzo IP, a partire dal campo fqdn che compare nell'indirizzo applicativo, ossia ad esempio www.domino.org. L'operazione coinvolge quasi sempre una interrogazione al DNS, operata dal componente resolver del sistema operativo. Il risultato della chiamata è u puntatore alla struttura hostent, che presenta i campi illustrati di seguito: h_name punta al nome ufficiale dell'host; h_aliases ad un array di stringhe contenenti i nomi alternativi; h_addrtype; vale AF_INET o AF_INET6; h_length è la lunghezza in byte dell'indirizzo; e h_addr_list punta ad un array contenente tutti gli indirizzi IP del computer. La funzione inversa viene invece svolta dalla chiamata a gethostbyaddr(const void *addr, socklen_t len, int type), che restituisce un puntatore ad una struttura hostent, a partire da un puntatore a struttura in_addr, di lunghezza len, e di tipo AF_INET. Un esempio di utilizzo viene mostrato alla linea 62 del listato - server. Un esempio di client Di nuovo sul sito di Brian "Beej" Hall, troviamo il codice di un Client TCP che contatta il server stream illustrato sopra. Senza riportarlo qui, osserviamo innanzitutto che il programma client richiede che l'indirizzo di destinazione sia passato come parametro al momento della chiamata. Quindi • invoca la funzione gethostbyname() da poco illustrata, in modo da ottenere l'IP del server • inserisce gli estremi del server da contattate nella struttura their_addr, ed esegue la connect(); • invoca recv() per ricevere il messaggio "Hello, world!" inviato senza molta fantasia dal server; • stampa la stringa ricevuta a video, e termina. C'é da notare che recv() esce subito dal suo stato di ricezione, anziché attendere l'arrivo di MAXDATASIZE bytes, in quanto il lato server chiude la connessione subito dopo l'invio del messagio. 57 Connessioni di tipo DGRAM Network programming Connessioni di tipo DGRAM Ancora sul sito di Brian "Beej" Hall, possiamo trovare un esempio di server (listener) e di client (talker) che fanno uso di una connessione di tipo SOCK_DGRAM, ossia senza connessione. Stavolta è il client che invia qualcosa al server, che termina subito dopo la ricezione, mentre il client termina subito dopo la trasmissione, che sia andata a buon fine o meno. Dal lato listener, osserviamo che dopo aver creato il socket ed aver effettuato il bind sulla propria porta, si esegue direttamente il recvfrom(), che resta bloccato in attesa. Una volta ricevuto un messaggio, questo viene stampato, e listener esce. Dal lato talker, osserviamo che oltre al nome del computer remoto, è presente un altro parametro di input al programma, e cioé il messaggio da inviare al server. Di nuovo, costruiamo in their_addr l'indirizzo da contattare, dopo averlo ottenuto in network byte order tramite la gethostbyname(), ed aver convertito il numero di porta con htons(). Quindi, si invia il messaggio mediante sendto(), e si chiude il socket, in modo che la recvfrom() del server possa uscire. I/O multiplato sincrono Sebbene il modello di programmazione di rete che fa uso di processi figli o di thread sia tutto sommato semplice ed elegante, e permetta di scrivere del codice ben leggible, in cui l'interazione con la specifica entità connessa all'altro estremo della rete è ben delimitata, spesso si preferisce ricorre ad una soluzione diversa. Nel caso dei processi figli infatti, ognuno di essi determina una nuova occupazione di memoria, ed anche se nel caso dei thread questo avviene in forma molto ridotta, la fase di inizializzazione delle nuove istanze del flusso di controllo può portare ad un aumento del tempo di risposta, e dell'impegno di risorse di calcolo. Inoltre, come abbiamo visto, la chiamata alla accept(), così come alla recv(), sono di tipo bloccante, ovvero non restituiscono il controllo finché non sopraggiunge una connessione, o sono pronti dei dati da leggere, cosicchè si possono verificare problemi di sincronizzazione tra i diversi sotto processi, e lo stato di blocco di alcuni di essi potrebbe causare un blocco generalizzato. Al contrario, nel caso dell'I/O multiplato [SMI] un unico processo si pone contemporaneamente in ascolto di tutti i socket attivi, ad ognuno dei quali è connesso un diverso client, e provvede a gestire il colloquio con ciascuno di essi, non appena sono disponibili nuovi dati. In questo modo, sono virtualmente risolti tutti i problemi di sincronizzazione, ed il server può conseguire una elevata scalabilità (ossia servire un numero molto elevato di connessioni contemporanee). Il lato negativo, è che il codice risulta meno leggibile, dovendo gestire diversi casi nello stesso flusso di controllo. 58 Lo strato applicativo di Internet Alessandro Falaschi Il funzionamento dell'I/O sincrono si basa sull'uso della chiamata select(), il cui prototipo POSIX è #include <sys/select.h> int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout); che mostra come il suo funzionamento dipenda da tre insiemi di descrittori di file (fds), che individuano tutti i canali di cui occorre monitorare lo stato, in attesa di un suo cambiamento. Gli insiemi raccolgono i descrittori da cui vogliamo leggere (readfds), in cui vogliamo scrivere (writefds), e di cui vogliamo monitorare gli errori (exceptfds). Questi insiemi (set) vengono modificati dalle chiamate void void void int FD_SET FD_CLR FD_ZERO FD_ISSET (int fd, fd_set *set); (int fd, fd_set *set); (fd_set *set); (int fd, fd_set *set); /* /* /* /* /* aggiunge il descritore fd all'insieme set */ rimuove il descrittore fd dall'insieme set */ rimuove tutti i descritori dall'insieme set */ verifica se il descrittore fd appartiene */ all'insieme set */ In pratica, occorre prima popolare gli insiemi con FD_SET(), e poi chiamare select(), che non torna finché uno (o più) dei descrittori compresi dagli insiemi non cambia stato. Quando select() ritorna, modifica i fds, lasciandovi dentro solo i fd il cui stato è cambiato. A questo punto, invocando FD_ISSET() per ognuno dei descrittori e degli insiemi, si individua quale(i) di questi è cambiato, in modo che il programma possa svolgere le azioni previste per quel caso particolare. Per ciò che riguarda gli altri due parametri della chiamata a select(), nfds è pari al numero (più uno) del descrittore con il numero più elevato (ad es, se i descrittori settati sono 1, 4 e 7, nfds deve valere 8 = 7+1), mentre timeout codifica per quanto tempo al massimo select() resterà in attesa, ed è espresso mediante la struct timeval: struct timeval { int tv_sec; int tv_usec; }; /* secondi */ /* microsecondi */ Ad esempio, dopo aver posto un socket in ascolto con listen(), ed averlo inserito in readfds, quando questo riceve una richiesta di connessione da parte del client, select() ritorna, e possiamo invocare subito la accept(), senza restare bloccati. Se invece la connessione è già instaurata, dopo aver inserito l'accepting socket in readfds, select() ritornerà non appena il kernel riceve un nuovo pacchetto, e potremo subito invocare recv(), di nuovo senza rimanere bloccati. Negli insiemi, è ovviamente (!) possibile inserire anche i descrittori associati a standard input, standard output e standard error, anzi questo è fortemente raccomandato, se il programma deve anche poter gestire l'I/O da tastiera. Nel caso in cui si voglia monitorare un solo insieme, select() può ricevere NULL al posto di un fd_set *; ponendo tv_sec e tv_usec a zero, select() tornerà immediatamente, mentre passando NULL al posto di struct timeval *, select() userà un timeout infinito. 59 I/O multiplato sincrono Network programming Per riassumere i concetti esposti, e fornire un esempio pratico, mostriamo di seguito un listato ancora tratto da BJN, che realizza un semplice server di chat multiutente. Una volta posto in esecuzione, se da altre finestre-terminale, o da altri computer, si esegue telnet hostname 9034, (telnet apre una connessione TCP collegando lo standard output ed input di un computer ad un sever remoto), tutto ciò che viene scritto da un client, viene mostrato a tutti gli altri. 1 2 3 /* ** selectserver.c -- a cheezy multiperson chat server */ 4 5 6 7 8 9 10 11 #include #include #include #include #include #include #include #include <stdio.h> <stdlib.h> <string.h> <unistd.h> <sys/types.h> <sys/socket.h> <netinet/in.h> <arpa/inet.h> 12 #define PORT 9034 13 14 15 16 17 18 19 20 21 22 23 24 25 26 int main(void) { fd_set master; fd_set read_fds; struct sockaddr_in myaddr; struct sockaddr_in remoteaddr; int fdmax; int listener; int newfd; char buf[256]; int nbytes; int yes=1; socklen_t addrlen; int i, j; // port we're listening on // // // // // // // // master file descriptor list temp file descriptor list for select() server address client address maximum file descriptor number listening socket descriptor newly accept()ed socket descriptor buffer for client data // for setsockopt() SO_REUSEADDR, below 27 28 FD_ZERO(&master); FD_ZERO(&read_fds); // clear the master and temp sets 29 30 31 32 33 // get the listener if ((listener = socket(AF_INET, SOCK_STREAM, 0)) == -1) { perror("socket"); exit(1); } 34 35 36 37 38 // lose the pesky "address already in use" error message if (setsockopt(listener, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int)) == -1) { perror("setsockopt"); exit(1); } 39 40 41 42 43 44 45 46 47 // bind myaddr.sin_family = AF_INET; myaddr.sin_addr.s_addr = INADDR_ANY; myaddr.sin_port = htons(PORT); memset(&(myaddr.sin_zero), '\0', 8); if (bind(listener, (struct sockaddr *)&myaddr, sizeof(myaddr)) == -1) { perror("bind"); exit(1); } 48 49 50 51 52 53 54 // listen if (listen(listener, 10) == -1) { perror("listen"); exit(1); } // add the listener to the master set FD_SET(listener, &master); 55 56 // keep track of the biggest file descriptor fdmax = listener; // so far, it's this one 60 Lo strato applicativo di Internet 57 58 59 60 61 62 63 64 65 66 67 68 69 &addrlen)) 70 71 72 73 74 75 76 77 78 79 80 81 client 82 83 84 85 86 87 88 89 90 91 92 93 sender 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 } Alessandro Falaschi // main loop for (;;) { read_fds = master; // copy it if (select(fdmax+1, &read_fds, NULL, NULL, NULL) == -1) { perror("select"); exit(1); } // run through the existing connections looking for data to read for (i = 0; i <= fdmax; i++) { if (FD_ISSET(i, &read_fds)) { // we got one!! if (i == listener) { // handle new connections addrlen = sizeof(remoteaddr); if ((newfd = accept(listener, (struct sockaddr *)&remoteaddr, == -1) { perror("accept"); } else { FD_SET(newfd, &master); // add to master set if (newfd > fdmax) { // keep track of the maximum fdmax = newfd; } printf("selectserver: new connection from %s on " "socket %d\n", inet_ntoa(remoteaddr.sin_addr), newfd); } } else { // handle data from a client if ((nbytes = recv(i, buf, sizeof(buf), 0)) <= 0) { // got error or connection closed by if (nbytes == 0) { // connection closed printf("selectserver: socket %d hung up\n", i); } else { perror("recv"); } close(i); // bye! FD_CLR(i, &master); // remove from master set } else { // we got some data from a client for(j = 0; j <= fdmax; j++) { // send to everyone! if (FD_ISSET(j, &master)) { // except the listener socket and the if (j != listener && j != i) { if (send(j, buf, nbytes, 0) == -1) { perror("send"); } } } } } } // it's SO UGLY! } } } return 0; Come possiamo osservare, la select() in linea 60 usa solo l'insieme di lettura read_fds, che alla linea 59 viene posto pari a master. Questo a sua volta, definito alla linea 15, è inizializzato a zero alla linea 27, e quindi popolato alla linea 54 con il descrittore del socket di ascolto, ottenuto alla linea 30 dopo l'invocazione di socket(). La select() è posta all'interno di un loop infinito che ha inizio alla linea 58, e la prima volta che viene eseguita, riceve come primo parametro (l'intero superiore del numero di socket più grande negli insiemi) il numero del socket in ascolto + 1, come risulta alla linea 56. Quando select() restituisce il controllo al programma, il for di linea 65 scansiona tutti i descrittori fino al più grande, e quando la condizione di linea 66 ne trova uno contenuto nell'insieme read_fds restituito da select(), ci son due possibilità: o si tratta di una nuova connessione, oppure di un nuovo pacchetto arrivato su di una connessione già attiva. 61 Socket non bloccante Network programming Nel primo caso (che è il primo a verificarsi!) viene eseguita la accept(), il socket così ottenuto è aggiunto all'insieme master (che sarà di nuovo copiato in read_fds alla linea 59), e fdmax viene aggiornato, nel caso in cui il nuovo socket abbia un numero maggiore di quelli già in uso. Nel secondo caso, si tentano di leggere i dati in arrivo, ed in caso di successo (linee 90-100) questi vengono re-inviati a tutti gli altri socket presenti nell'insieme master, ed esclusione di quello relativo al listening socket, ed a quello del client mittente. Se invece la recv() fallisce, viene stampato un diverso messaggio di errore a seconda se sia verificato un errore (linea 85) oppure non venga letto nulla (linea 83), segno che il socket è stato chiuso dall'altro lato. In entrambi i casi, il socket viene chiuso, e rimosso dall'insieme master (linee 87 e 88). Notiamo che non viene neanche tentato di decrementare fdmax, tanto un suo valore eccessivo, non arreca nessun danno. Socket non bloccante Finora il nostro stile di programmazione, è stato completamente vincolato dal fatto che alcune primitive (accept(), recv(), select(), ma anche send(), nel caso in cui il TCP abbia i buffer pieni) bloccano, ovvero non restituiscono il controllo al programma che le ha chiamate finchè non si verifica l'evento di cui sono in attesa. Questo ha il vantaggio che la CPU del computer che esegue l'applicazione non viene impegnata per nulla, ed il processo resta in uno stato di sospensione per la maggior parte del tempo. Una alternativa è quella di modificare il comportamento del socket, andando ad intervenire sui flag associati al suo descrittore, utilizzando la chiamata a fcntl(): #include <unistd.h> #include <fcntl.h> . . sockfd = socket(AF_INET, SOCK_STREAM, 0); fcntl(sockfd, F_SETFL, O_NONBLOCK); . . Questo è possibile perchè in Unix ogni cosa è un file, ed il comportamento di un socket, che è referenziato da una file descriptor alle stregua di una qualunque altro file, dipende dai flags dello stesso. Il secondo argomento di fcntl(), settato a F_SETFL, indica appunto l'intenzione di settare un flag, in particolare il flag O_NONBLOCK, che appunto fa si che le chiamate bloccanti, anziché sospendere il processo, tornino immediatamente, restituendo però -1, e settando errno al valore EWOULDBLOCK. In tal modo la chiamata assume il ruolo di una interrogazione, che il programma può eseguire di continuo, finché il codice di errore non cambia, e l'operazione può avere buon fine. Ma un programma scritto in questo modo, abusa delle risorse del computer, la cui CPU viene impegnata di continuo nell'interrogazione del socket, e adottare questa soluzione, è da sprovveduti. Client broadcast L'esempio di chat server precedentemente svolto, mostra un server centralizzato a cui si connettono tutti client, e che provvede a re-inviare a tutti ciò che ognuno scrive. Evidentemente questo approccio risulta tanto più pesante per il server, quanti più client si collegano. Un approccio alla comunicazione di gruppo del tutto diverso, consiste nel non 62 Lo strato applicativo di Internet Alessandro Falaschi disporre di nessun server, utilizzare uno strato di trasporto senza connessione (udp), e fare invece affidamento ai meccanismi offerti dallo strato di rete: • broadcast: funziona in ambito locale, ovvero tra computer connessi ad una stessa LAN, e consiste nell'inviare i pacchetti ad un indirizzo IP che abbia settati ad uno i bit della parte host dell'indirizzo. Tale indirizzo broadcast viene mostrato dal comando ip addr, e può essere ricavato mediante l'operazione logica network_number OR (NOT netmask). In alternativa, si può usare l'indirizzo IP di broadcast globale, ovvero 255.255.255.255, noto anche come INADDR_BROADCAST; • multicast: funziona (teoricamente) su scala globale, e consiste nell'usare come indirizzo IP di destinazione (detto gruppo), un indirizzo scelto entro un intervallo opportuno, che i router riconoscono come speciale, e quindi tentano di consegnare a tutti gli host che si sono posti in ascolto di quell'indirizzo. Ma non entriamo in dettagli ulteriori. Ma se utilizziamo il programma talkerche appunto invia un messaggio via UDP (vedi sopra), specificando un indirizzo di destinazione broadcast, riceviamo un errore del tipo sendto: Permission denied. Prima, infatti, dobbiamo modificare il comportamento del socket, agendo questa volta su di una sua opzione, mediante il comando setsockopt(): #include <sys/types.h> #include <sys/socket.h> . . sockfd = socket(AF_INET, SOCK_DGRAM, 0); setsockopt(sockfd, SOL_SOCKET, SO_BROADCAST, &broadcast, sizeof(broadcast); . . Il secondo parametro di setsockopt() identifica il livello a cui si intende operare, e indicando SOL_SOCKET, si specifica di operare a livello di socket. L'opzione che abilitiamo è SO_BROADCAST, che appunto permette al socket sia di ricevere che di trasmettere pacchetti da/verso indirizzi broadcast. Un esempio di client che fa uso di questa opzione è broadcaster.c, ancora prelevata dalle guide di Brian "Beej" Hall. Lo possiamo mandare in esecuzione, con il comando ./bradcaster 255.255.255.255 "messaggio di saluto". Se lanciamo listener.csu diversi computer di una stessa LAN, possiamo osservare che tutto ciò che è scritto da broadcaster, compare sugli schermi di tutti i listener. Bello ! :-) Va però osservato che, nel rispetto degli altri, fare uso di comunicazioni broadcast con troppa leggerezza non è una buona pratica, in quanto tutti i computer della stessa LAN si trovano obbligati a leggere tutti i pacchetti broadcast, eventualmente solo per scoprire che non esiste nessun processo in ascolto su quella porta. Riepilogando Questa, è la tabella riassuntiva dei parametri dei programmi proposti programma trasporto Porta modalità trasmissione client TCP - fork unicast server TCP 3490 fork unicast talker UDP - unicast listener UDP 4950 - 63 - - Riferimenti Network programming selectserver TCP 9034 select unicast broadcaster - UDP - broadcast Il codice sorgente dei programmi, è reperibile presso una apposita directory, dove è presente anche lo script di compilazione, ed il Makefile. Una versione modificata dei programmi, come suggerito nel corso delle esercitazioni, è fornita nella directory di test, in cui • server.c è modificato, in modo da inviare un messaggio diverso da Hello, Word! • servermod.c è la versione modificata di server.c, in cui è possibile specificare un ritardo programmabile tra bind, listen e accept, e disabilitare la gestione del segnale SIGCHLD; • listener.c è modificato in modo da non uscire ad ogni pacchetto ricevuto, ma loopare per sempre; inoltre, per ogni pacchetto ricevuto, stampa una sola linea; • selectserver.c stampa anche l'ip mittente del messaggio ricevuto. Riferimenti [AL] - Appunti di informatica libera di Daniele Giacomini [BJN] - Beej's Guide to Network Programming Using Internet Sockets - Brian "Beej Jorgensen" Hall [GaPiL] - Guida alla Programmazione in Linux - Programmazione di rete di Simone Piccardi [SOG] - Internet: Indirizzi e Protocolli di Vittoria Giannuzzi [IIC] - Socket da Imparare il C di Marco Latini e Paolo Lulli [SMI] - Servers Multiprocesso e I/O Multiplexing di Vittorio Ghini, da LABORATORIO di PROGRAMMAZIONE di RETE [UPF] - Unix Programming Frequently Asked Questions di Andrew [MMC] - Multimedia Communications di Fred Halsall, ed. Addison-Wesley 2001, ISBN 0-201-39818-4 [BAF] - I procolli TCP/IP di Behrouz A. Forouzan - Ed McGraw-Hill 2001, ISBN 88-386-6005-0 [TST] - Trasmissione dei Segnali e Sistemi di Telecomunicazione di Alessandro Falaschi - Ed Aracne 2003, ISBN 88-7999-477-8 [MCX] - man.cx - Tutte le Man page Computer Networks - di Theo Schouten Realizzato con da Alessandro Falaschi - 64 Lo strato applicativo di Internet Alessandro Falaschi 65 Lo strato applicativo di Internet Risoluzione degli indirizzi applicativi Come discusso precedentemente, un aspetto chiave del funzionamento di Internet, è la stratificazione degli indirizzi, ed i meccanismi per passare dagli uni agli altri. In questo capitolo, ci occuppiamo di come gli indirizzi di livello applicativo (del tipo www.qualcheserver.it) vengano messi in corrispondenza con gli indirizzi IP. Inoltre, ci occupiamo di alcuni casi applicativi particolari, e che prendono parte a meccanismi più articolati come la Service Discovery, il VoIP, ed il controllo dello Spam. • Domain Name System ◦ Resolver ◦ DNS ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪ Nomi di dominio Le zone: un database distribuito Funzioni del DNS Recursione Durata della Cache Risoluzione Inversa BIND Resource Records File di zona ▪ Glue Records ▪ Strumenti di Indagine ▪ I pacchetti del DNS • Indirizzi variabili ◦ Dynamic Host Configuration Protocul ◦ Dynamic DNS ◦ Zeroconf ▪ Auto-assegnazione degli indirizzi IP ▪ Risoluzione dei nomi ▪ Service discovery ▪ Service Location Protocol ▪ DNS-SD ▪ Simple Service Discovery Protocol • Supporto di DNS al Voice over IP ◦ SRV RR ◦ NAPTR ▪ Selezione di un protocollo di trasporto ▪ Risoluzione di una URI numerica ▪ Adesione ad una federazione VoIP ◦ ENUM • Anti-Spam DNS Blackhole Lists • Appendice Lo strato applicativo di Internet Alessandro Falaschi ◦ IETF Domain Name System Abbiamo già osservato come un programma applicativo possa invocare la chiamata di sistema struct hostent *gethostbyname(const char *name); che, a partire da un nome di dominio, restituisce una struttura dati che contiene tra le altre cose, l'indirizzo IP del proprio computer. Questa chiamata fa il paio con l'altra struct hostent *gethostbyaddr(const void *addr, int len, int type); che permette di popolare la medesima struttura dati, a partire stavolta dall'indirizzo IP a 32 bit x.y.w.z. Nei primi tempi dello sviluppo di Internet, queste funzioni usavano un file presente in ogni computer connesso alla rete, per l'esattezza /etc/hosts, contenente tutte le corrispondenze tra i nomi e gli indirizzi di tutti gli altri, e che doveva essere ri-distribuito a tutti i computer ogni volta che intervenia un cambiamento. Un esempio di /etc/hosts è fornito appresso: notiamo che a fianco di ogni indirizzo, sono presenti due diversi nomi per uno stesso computer, essendo entrambi validi. Inoltre, viene fornita la corrispondenza standard tra 127.0.0.1 e localhost. # # Necessario per il "loopback" IPv4. # 127.0.0.1 localhost.localdomain localhost # # Indirizzi IPv4. # 192.168.1.1 dinkel.brot.dg dinkel 192.168.1.2 roggen.brot.dg roggen # 192.168.2.1 weizen.mehl.dg weizen Ben presto però, con il successo e l'espansione di Arpanet, nel 1983 fu pubblicata la RFC 882 (successivamente modificata dalle RFC 1034 e 1035), in cui si definiva un meccanismo, chiamato DNS, atto a delocalizzare questa tabella in tutta la rete, e tale da permettere di delegare alle singole organizzazioni la manutenzione e l'aggiornamento della propria parte di spazio di indirizzamento. Resolver L'invocazione delle chiamate a gethostbyname() e gethostbyaddr() su riportate, rappresenta essenzialmente una domanda, od un problema, che deve essere risolto in qualche modo: per questo motivo, il sistema operativo invoca a sua volta i sevizi offerti da un componente detto resolver (risolutore), che provvede ad interrogare sia i files locali, che la sua cache, che i DNS esterni: Local Host | | +---------+ user queries +----------+ | | |-------------->| | queries | 67 Foreign +--------+ | | Domain Name System Risoluzione degli indirizzi applicativi | User | gethostby... | |---------|->| Foreign| | Program | | Resolver | | | Name | | |<--------------| |<--------|--| Server | | | user responses| |responses| | | +---------+ +----------+ | +--------+ | A | cache additions | | references | V | | +----------+ | | cache | | +----------+ | La figura che segue, illustra più in dettaglio la sequenza di attività che intercorrono tra quando un programma applicativo effettua la chiamata alla resolver library, e quando effettivamente riesce ad accedere al computer remoto. Il comportamento del resolver è definito da un file di configurazione, che tradizionalmente era /etc/host.conf, e che dalla versione 6 di glibc, è invece /etc/nsswitch.conf, in cui tra le altre cose, troviamo la linea hosts: files dns mdns4 che impone al resolver di provare prima (files) la risoluzione offerta da /etc/ hosts, quindi (dns) di rivolgersi al DNS, ed infine (mdns4) a Zeroconf. Il file locale /etc/ hosts può ad esempio servire per indirizzare i computer interni ad una stessa LAN, ed eventualmente dotati di un IP privato, senza che questi debbano avere un nome "pubblico", oppure per poterli raggiungere anche nel caso in cui il DNS esterno non sia raggiungibile: l'unica condizione, in questo caso, è che i computer conservino sempre il loro stesso indirizzo, cosa che invece non avviene, nel caso in cui vengano configurati mediante un server DHCP. Se /etc/hosts non contiene nessuna corrispondenza, allora occorre accedere ad un DNS esterno, e qundi occorre conoscerne l'indirizzo IP, che è appunto riportato nel file /etc/ resolv.conf, che nel mio caso contiene 68 Lo strato applicativo di Internet Alessandro Falaschi nameserver 193.70.152.15 nameserver 193.70.152.25 che corrispondono alle impostazioni relative al provider Libero. Oltre alla direttiva nameserver, possono anche essere presenti (ma non contemporaneamente) le direttive domain ing.uniroma1.it search ing.uniroma1.it uniroma1.it it che individuano uno o più suffissi da aggiungre al nome di computer, per ri-tentare l'interrogazione al DNS nel caso in cui la prima richiesta abbia determinato un esito negativo. DNS A questo punto, quando una applicazione sul nostro computer deve risolvere un nome di dominio, essa invia un pacchetto UDP verso la porta 53 del computer indicato come nameserver dal file /etc/resolv.conf, contenente una richiesta del tipo "qual'è l'indirizzo IP del computer pippo.topolinia.com ?". Per il nameserver, si può verificare una delle tre possibilità: 1. è lui il DNS autorevole per il dominio topolinia.com e fornisce subito la risposta cercata; 2. non ha la più pallida idea dell'indirizzo richiesto, e si mette in moto per cercare la risposta; 3. pur non essendo autorevole per il dominio topolinia.com, poco tempo prima un'altro client gli aveva fatto la stessa domanda, e la risposta che aveva ottenuto è ancora valida, quindi la recupera dalla cache e restituisce quella; Nel primo caso, l'autorevolezza è stata conferita al DNS in questione mediante una delega emanata dal nameserver autorevole per il dominio di livello superiore .com, e quest'ultimo a sua volta, è stato delegato dal DNS che è autorevole per il root domain. Prima di addentrarci nella spiegazione di come si svolgono i passi 2 e 3, approfondiamo questi concetti. Nomi di dominio Un nome a dominio è costituito da una serie di stringhe separate da punti, ad esempio it.wikipedia.org. A differenza degli indirizzi IP, dove la parte più generica del numero è quella che parte da sinistra, in un nome DNS la parte più generica è la prima partendo da destra, detta dominio di primo livello (o TLD, Top Level Domain), come ad esempio .org o .it. 69 Domain Name System Risoluzione degli indirizzi applicativi Un dominio di secondo livello è meno generico, e consiste di due parti, per esempio wikipedia.org, e così via. Ogni ulteriore elemento specifica un'ulteriore suddivisione. Quando un dominio di secondo livello viene registrato all'assegnatario, questo è autorizzato a usare i nomi di dominio relativi ai successivi livelli come it.wikipedia.org (dominio di terzo livello) e altri come some.other.stuff.wikipedia.org (dominio di quinto livello) e così via. Il nome a dominio completo viene spesso indicato come Fully Qualified Domain Name (FQDN), a per ognuno dei punti che si incontrano (partendo da destra) all'interno di un FQDN, si scende di un livello nella gerarchia dei nomi, che in effetti è organizzata ad albero, ed in cui da ogni livello, possono dipartirsi più sotto livelli, fino ad arrivare alle foglie, costituite dai nomi dei computer veri e propri. Le zone: un database distribuito Un DNS autorevole per il penultimo livello dell'albero, conosce unicamente i nomi dei computer che definiscono l'ultimo livello, mentre i DNS dei livelli superiori, oltre a poter gli risolvere indirizzi di host dotati da un nome più corto, delegano l'autorità delle zone associate ai livelli inferiori, ad altri DNS. I DNS associati al dominio "." (punto e basta) sono detti i root name server, che in effetti tengono assieme internet, sono 13, e sono messi a disposizione da diverse organizzazioni sparse per il mondo (ma principalmente negli USA, sebbene alcune di queste adottino tecniche anycast per distribuire il carico a livello mondiale), e sono coordinati dall'ICANN. 70 Lo strato applicativo di Internet Alessandro Falaschi Il dominio per cui un DNS è autorevole, viene denominato zona (su cui appunto, esercita il dominio), ed a questa zona sono associati uno o più files, le cui righe sono denominate Resource Record (Record della Risorsa, o RR), che descrivono informazioni relative ai nomi di quel dominio. Il meccanismo di delega si basa sulla presenza (nel DNS di livello superiore) di un RR di tipo NS (Name Server), che indica il DNS delegato. 71 Domain Name System Risoluzione degli indirizzi applicativi Tutti i root name server, condividono una medesima tabella di RR, che individua i root name server, autorevoli per i top level domain (.com, .org, .net, .it, .de, .uk...). Per il County Code dell'Italia ad esempio, la delega spetta ad un istituto del CNR di Pisa, presso il quale ci si rivolge (direttamente o per il tramite di un rivenditore di hosting), per registrare il proprio dominio. Funzioni del DNS Un server DNS può essere configurato come: • server autorevole per una o più zone, che è delegato a gestire, sulla base dei record NS inseriti nella zona (livello) superiore. Spesso sono presenti più server autorevoli per una stessa zona: un primario, ed uno o più secondari, che copiano i dati di zona dal primario; • server ricorsivo, che risolve le richieste effettuando una recursione a partire dai root server, mantiene una cache delle risposte ricevute, ed è indicato a volte come caching only DNS; • forwarder, che non risolve le query direttamente, ma interrogando a sua volta un server ricorsivo. Lo schema sottostante è disegnato dal punto di vista di un DNS configurato sia come autorevole, che come recursivo o forwarder. Mostra infatti come, in veste di DNS autorevole, il nameserver legga da un insieme di master files, i Resource Records che definiscono la sua zona, in modo da poter rispondere alle richieste che gli pervengono da un resolver esterno. Allo stesso tempo, il nostro DNS inoltra verso un diverso nameserver le queries a cui non sa 72 Lo strato applicativo di Internet Alessandro Falaschi fornire direttamente risposta. Local Host | Foreign | +---------+ | / /| | +---------+ | +----------+ | +--------+ | | | | |responses| | | | | | | Name |---------|->|Foreign | | Master |-------------->| Server | | |Resolver| | files | | | |<--------|--| | | |/ | | queries | +--------+ +---------+ +----------+ | A |maintenance | +--------+ | +------------|->| | | queries | | Foreign| | | | Name | +------------------|--| Server | maintenance responses | +--------+ Recursione Se un DNS non è autorevole per la richiesta, e non trova la risposta nella sua cache, allora deve effettuare una recursione, andando prima a chiedere ad un root nameserver l'identità dei DNS autorevoli del tld, e quindi a questi, l'identità del DNS autorevole per il primo livello, e così via fino a trovare il DNS autorevole per il FQDN desiderato. Dato che il tempo necessario alla recursione è almeno pari alla somma di RTT relativi ai DNS coinvolti, anche un nameserver chaching-only, non autorevole su nulla, ha comunque un ruolo molto utile nel velocizzare la navigazione (ed è utile configurarlo a casa propria). Durata della cache Associato ad ogni risposta ricevuta durante la recursione, il DNS riceve anche un valore di Time To Live (TTL), che indica il periodo di validità della risposta ricevuta: finchè questa non scade, il DNS non effettua nuovamente la stessa richiesta, ma usa il valore presente in cache, risparmiando il tempo necessario ad inoltrare la richiesta ed attendere risposta. Più tempo il DNS resta in funzione, e più ricca sarà la cache, eliminando via via quasi del tutto, il bisogno di chiedere all'esterno la risoluzione dei suffissi ricorrenti. Risoluzione Inversa Può aver senso (ed in effetti lo ha) di voler effettuare la risoluzione inversa rispetto a quanto fatto finora, e cioè determinare il fqdn di un host, a partire dal suo indirizzo IP. 73 Domain Name System Risoluzione degli indirizzi applicativi Dato che si tratta sempre degli stessi dati, sembra logico voler affidare questo compito ancora una volta al DNS; quindi, come un particolare DNS è autorevole per i nomi degli host localizzati nell'ambito della zona per la quale il DNS è delegato, così è anche autorevole per quanto riguarda il lotto di indirizzi IP a disposizione del provider che ospita il DNS. C'è però da notare un particolare: mentre per i nomi di dominio, il livello più elevato è quello scritto più a destra, e quindi l'albero delle deleghe procede aggiungendo i prefissi di livello inferiore in testa, per gli indirizzi IP la parte più generale è quella scritta a sinistra, e le sottoreti più specifiche si ottengono aggiungendo i byte in coda. Dato che però il DNS, per come è fatto, aggiunge i sotto-livelli (più specifici) in testa, allora si è scelto di scrivere gli indirizzi IP presenti nei RR PTR al contrario, e di pensarli come sotto-dominio di un TLD radice fittizio, chiamato in-addr.arpa. Pertanto, nel chiedere la risoluzione inversa dell'indirizzo IP (ad es.) 151.100.122.144, la query sarà inoltrata per il nome 144.122.100.151.in-addr.arpa. Questo stratagemma, permette di distribuire anche le risoluzioni inverse mediante delle zone organizzate gerarchicamente: il name server autorevole per in-addr.arpa delega l'autorità per il primo byte dell'indirizzo (nell'esempio, 151) ad un DNS gestito dall'organizzazione a cui è assegnato il lotto di indirizzi 151.0.0./8; questo, delega l'autorità a risolvere il secondo byte dell'indirizzo IP, ai DNS a cui questo è intestato (nell'esempio, il lotto 151.100.0.0/16), e così via. BIND L'implementazione più diffusa di un server DNS è BIND, sviluppato fin dal 1988 presso l'Università di Berkeley, ed ora mantenuto da ISC (che gestisce anche uno dei root ns). Il processo che viene lanciato, ha nome named (la d sta per daemon, e ricorre spesso, nel caso di processi che sono eseguiti in background), e determina il tipo di risposte che potrà fornire, in base al contenuto del file /etc/bind/named.conf, di cui forniamo un esempio inspirato da AIL, che fornisce una guida passo-passo alla configurazione di BIND: named.conf file di zona risoluzioni inverse ; /etc/bind/named.conf include "/etc/bind/ named.conf.options"; zone "." { type hint; file "/etc/bind/db.root"; }; zone "localhost" { type master; file "/etc/bind/db.local"; }; zone "127.in-addr.arpa" { type master; file "/etc/bind/db.127"; }; ; /etc/bind/db.local ; /etc/bind/db.127 @ IN SOA localhost. root.localhost. ( 1 ; Serial 604800 ; Refresh 86400 ; Retry 2419200 ; Expire 604800 ) ; Min TTL @ IN SOA localhost. root.localhost. ( 1 ; Serial 604800 ; Refresh 86400 ; Retry 2419200 ; Expire 604800 ) ; Min TTL @ @ @ IN NS 1.0.0 IN PTR IN IN NS A include "/etc/bind/ named.conf.local"; 74 localhost. 127.0.0.1 localhost. localhost. Lo strato applicativo di Internet Alessandro Falaschi ; /etc/bind/dg @ IN SOA dinkel.brot.dg. root.dinkel.brot.dg. ( 1998031800 ; Serial 28800 ; Refresh 7200 ; Retry 604800 ; Expire 86400 ) ; Min TTL ; /etc/bind/named.conf.local zone "168.192.in-addr.arpa" { type master; file "/etc/bind/192.168"; }; zone "dg" { type master; file "/etc/bind/dg"; }; zone "brot.dg" { type master; file "/etc/bind/brot.dg"; }; NS dinkel.brot.dg. ; /etc/bind/brot.dg @ IN SOA dinkel.brot.dg. root.dinkel.brot.dg. ( 1998031800 ; Serial 28800 ; Refresh 7200 ; Retry 604800 ; Expire 86400 ) ; Min TTL NS MX MX dinkel.brot.dg. 10 dinkel 20 roggen www CNAME ftp.brot.dg. dinkel.brot.dg. CNAME dinkel @ dinkel roggen 192.168.1.1 192.168.1.1 192.168.1.2 A A A ; /etc/bind/192.168 @ IN SOA dinkel.brot.dg. root.dinkel.brot.dg. ( 1998031800 ; Serial 28800 ; Refresh 7200 ; Retry 604800 ; Expire 86400 ) ; Min TTL 1.1 2.1 NS dinkel.brot.dg. PTR PTR dinkel.brot.dg. roggen.brot.dg. La sintassi generale dei files di zona, è illustrata qui di seguito. Per ora osserviamo che in named.conf si dichiara che: • in db.root (sotto /etc/bind/) si trova il file di suggerimento (hint) che permette di localizzare i root name server, autorevoli per i TLD; • in db.local e db.127, si trovano le informazioni autorevoli (master) relative agli indirizzi di tipo 127.0.0.1, e che consentono rispettivamente di effettuare la risoluzione diretta ed inversa con il nome localhost. • viene quindi incluso il file /etc/bind/named.conf.local, che a sua volta indica in dg, brot.dg e 192.168, i nomi dei files contenenti le direttive che rendono il DNS autorevole (master) rispettivamente per le zone dg, brot.dg, e per il gruppo di indirizzi locali di tipo 192.168.0.0/16. Prima di analizzare cosa viene descritto in questi files, illustriamo la sintassi e la tipologia dei possibili Resource Records (RR) che vi possono comparire. Resource Records I file di zona sono essenzialmente costituiti da un elenco di registrazioni di risorse (Resource Records, o RR), che rappresentano tutte le informazioni su cui il DNS è autorevole. Questi RR hanno un formato comune, del tipo [nome] [TTL] [classe] tipo dati_della_risorsa in cui i termini tra parentesi, se assenti, assumono lo stesso valore presente nel RR precedente. Prendendo come esempio un file di zona di nome brot.dg, il significato dei campi è il seguente: 75 Domain Name System Risoluzione degli indirizzi applicativi • nome: serve per costruire il FQDN della risorsa descritta dal RR, e può essere espesso nelle forme ◦ @ : si pronuncia at, significa "qui", e compare nel primo record del file (lo Start Of Autority, SOA). E' un meta-simbolo che rappresenta il nome della zona che è stata dichiarata in named.conf, alla riga che referenzia il file stesso. Se è presente nei record successivi al SOA, alla sua occorrenza viene sostituito il suo valore, ossia il nome della zona, ovvero il suffisso di dominio a cui la zona si riferisce. Il valore di @ può essere modificato mediante l'uso della direttiva $ORIGIN, che non è un RR, può occupare da sola una linea con la sintassi $ORIGIN miodominio.org, rimpiazzando così il valore di @; ◦ nomehost.dominio.tld. : il punto finale indica che il nome è un FQDN; ◦ nomehost: l'assenza di un punto finale indica che il record è relativo alla zona corrente, ed il FQDN corrispondente può ottenersi concatenando a nomehost il valore corrente di @, eventualmente modificato da $ORIGIN • TTL: è il campo Time To Live, espresso in secondi, e determina la durata di validità delle informazioni trattenute nelle cache dei server non autorevoli. Non va confuso con il TTL della intestazione IP. Se è assente, il TTL di un RR è posto pari al valore minTTL dichiarato nel RR SOA posto all'inizio del file • classe: è la classe degli indirizzi che vengono risolti dal DNS, e l'unico valore che venga praticamente usato per questo campo, è IN, che significa INternet (vedi un pò!) • tipo: è il campo che caratterizza il RR, ed i valori che più frequentemente possono essere assunti, sono riportati nella tabella che segue • dati: il significato e la sintassi di questo campo dipende dal valore di tipo, come discusso di seguito: Tipo Descrizione Dati si ha qui una sintassi piuttosto strutturata, del tipo nameserver email ( numero_seriale refresh retry expire minTTL ) in cui sono indicati, nell'ordine, il CNAME del nameserver primario e autorevole per la zona, ossia l'host presso cui risiedono i files di zona, l'emaildel suo amministratore (ma al posto della @, si usa un punto, per non creare confusione con il meta-simbolo @ illustrato sopra), un numero seriale che si incrementa ad ogni modifica del file, tre valori in secondi (refresh, retry e expire) che determinano la frequenza degli aggiornamenti da parte dei secondari, e (minTTL) il TTL di default per i RR che non lo dichiarano SOA Start Of Authority - è il primo RR che compare nel file di zona, e definisce un insieme di parametri comuni a tutti i RR della stessa zona NS Name Server - indica il DNS autorevole per il dominio del RR, e rappresenta il meccanismo con cui un dominio di un certo livello, delega l'auterevolezza di un sottodominio, ad un altro DNS. Per uno stesso sottodominio possono essere presenti più record NS, ad indicare che il carico deve essere ripartito su più macchine il FQDN dell'host reso autorevole per il dominio delegato. Generalmente nello stesso file di zona, è presente anche un RR di tipo A, che ne risolve il nome del DNS nelegato nel suo indirizzo IP (vedi anche la definizione di glue records) A Address - effettua la traduzione vera e propria tra il dominio, ed il suo indirizzo. l'indirizzo IP nella sua forma dotted decimal x.y.w.z. Ci possono essere più RR di tipo A, relativi a nomi diversi, e che vengono risolti con un medesimo indirizzo IP. CNAME Canonical Name - qualora ad uno stesso indirizzo IP siano associati più nomi, solo uno di questi è canonico, e restituito dal il FQDN del nome canonico associato al nome relativo a questo RR. Es: weizen.brot.dg. 76 A 192.168.0.1 Lo strato applicativo di Internet processo di risoluzione inversa, mentre gli altri sono detti alias PTR Pointer - è presente nelle zone con estensione in-addr.arpa, e permette la risoluzione inversa da indirizzo IP, a FQDN. Nel suo campo dominio, è presente la parte di indirizzo IP sottostante al nome della zona .in-addr.arpa, scritta da destra a sinistra. MX Mail Exchanger - indica un host incaricato di ricevere la posta elettronica (via SMTP) indirizzata verso gli utenti del dominio del RR. Possono essere presenti più RR MX per lo stesso dominio, ognuno con una associata priorità, per garantire il servizio anche quando un host è in manutenzione Alessandro Falaschi dinkel.brot.dg. weizen.brot.dg. A CNAME 192.168.0.1 dinkel.brot.dg il FQDN del nome canonico dell'host a cui è stato assegnato questo indirizzo. E' infatti possibile inserire un unico RR di tipo PTR, per uno stesso indirizzo IP. compaiono due campi, con il formato priorità FDQN in cui priorità è un numero, ed il RR con il numero inferiore, è quello che viene provato per primo; FQDN invece identifica il server SMTP da utilizzare. Per un elenco più completo dei tipi, si consulti il sito di ISC. Alcuni altri tipi usati sono: HINFO descrive la CPU ed il SO usato da un host AAAA descrive un indirizzo IPv6 LOC memorizza i dati geografici GPS del DNS (RFC 1876) NAPTR name authority pointer (RFC 2915) SRV permette di scoprire chi eroga i servizi di rete (RFC 2782) File di zona Siamo finalmente in grado di commentare i files di zona forniti nell'esempio: • sono presenti due file di zona (db.local e db.127) necessari alla risoluzione diretta ed inversa dell'indirizzo 127.0.0.1 con il nome localhost; • altri tre files di zona (dg, brot.dg, e 192.168) permettono di definire l'esistenza di due computer, di nome dinkel e roggen ◦ appartenenti al dominio brot.dg, ed ◦ a cui sono assegnati gli indirizzi 192.168.1.1 e 192.168.1.2 • dinkel ospita il name server, un server HTTP ed il MX primario; ◦ il RR @ A 192.168.1.1 rende anche il nome a dominio di secondo livello brot.dg risolvibile come un indirizzo IP • roggen ospita un server FTP, e l'MX secondario. Notiamo che i files di zona /etc/bind/dg e /etc/bind/brot.dg contengono sia nomi nella forma di FQDN, che singoli: in questo secondo caso, vengono automaticamente estesi con il valore di @. In particolare, questo processo di estensione può avvenire anche nel campo dati del RR. L'uso di nomi non FQDN, facilita il riuso dei files di zona, qualora lo stesso blocco di indirizzi venga ri-assegnato ad un diverso nome di dominio. Viceversa, nel file di risoluzione inversa (/etc/bind/192.168) non è possibile usare i nomi corti per il campo dati, perché in tal caso il valore di @ non è più pari al suffisso di dominio, ma è invece pari a 168.192.in-addr.arpa. Notiamo infine che, dato che i file di zona indicano in dinkel il DNS autorevole per le zone 77 Domain Name System Risoluzione degli indirizzi applicativi dg e brot.dg, e che questi files dunque, devono risiedere fisicamente proprio su dinkel, allora... il computer che ospita il DNS, non può che essere dinkel! Glue records L'esempio fornito, vede lo stesso DNS autorevole sia per dg, che per brot.dg. Ma cosa sarebbe successo, se le due zone fossero state ospitate su server diversi? Immaginiamo questo scenario: • un resolver chiede al suo DNS, l'IP di www.brot.dg; • la richiesta è inoltrata ai root Name Server, che indicano l'IP del DNS autorevole per .dg: • il DNS autorevole per .dg viene interrogato, ma tutto quel che potrebbe rispondere, è che il nameserver autorevole per brot.dg è dinkel.brot.dg, ma... non ne può conoscere l'indirizzo IP, perché dovrebbe chiederlo a dinkel stesso!! Per ovviare a questa situazione di stallo, è previsto che nel punto di delega sia aggiunto un RR non autorevole di tipo A (nel nostro esempio, ad es. di tipo dinkel.brot.dg IN A 192.168.1.3), che permetta al DNS di livello superiore, di fornire la risoluzione dell'indirizzo IP autorevole per la zona di livello inferiore. Questo caso particolare di risoluzione di un IP per un computer esterno alla propria zona (ma interno ad un sottodominio delegato) prende il nome di Glue Record (o collante) in quanto permette, appunto, di tenere assieme due zone. Pertato si avrebbe: presso il DNS autorevole per .dg: 192.168.1.1 = dg presso il DNS autorevole per brot.dg: 192.168.1.3 = dinkel.brot.dg ; /etc/bind/dg ; /etc/bind/brot.dg @ IN SOA dg. root.dinkel.brot.dg. ( 1998031800 ; Serial 28800 ; Refresh 7200 ; Retry 604800 ; Expire 86400 ) ; Min TTL brot.dg. NS NS dg. A dinkel.brot.dg. A @ IN SOA dinkel.brot.dg. root.dinkel.brot.dg. ( 1998031800 ; Serial 28800 ; Refresh 7200 ; Retry 604800 ; Expire 86400 ) ; Min TTL dg. dinkel.brot.dg. dinkel roggen 192.168.1.1 192.168.1.3 NS dinkel.brot.dg. A A 192.168.1.3 192.168.1.2 Strumenti di Indagine Nella sezione apposita delle esercitazioni, illustriamo il funzionamento di alcuni strumenti di indagine relativi alla configurazione dei DNS, come whois, host, dig, nslookup. 78 Lo strato applicativo di Internet Alessandro Falaschi I pacchetti del DNS Sniffando con Wireshark, possiamo osservare il formato dei pacchetti UDP diretti verso la porta 53 a cui risponde il DNS, e le risposte associate. Il formato di domande e risposte è simile, ed è definito alla sezione 4 della RFC 1035: entrambe contengono il medesimo identificatore; inoltre nelle risposte, sono riportate anche le domande relative, che vengono citate nello spazio delle risposte. Il campo authority riporta gli estremi della fonte autorevole da cui la risposta ha origine, mentre le informazioni addizionali riferiscono, ad esempio, il RR di tipo A di un MX il cui nome canonico è presente nel campo di risposta. Infine, il campo flags è in realtà composto da 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ |QR| Opcode |AA|TC|RD|RA| Z | RCODE | +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ in cui • • • • • • • • QR specifica se è una richiesta od una risposta Opcode specifica il tipo di richiesta (diretta, inversa, o sullo stato del server) AA segnala che chi risponde, è autorevole per il dominio della query TC messaggio troncato RD consente la ricorsione RA ricorsione disponibile Z sono riservati per un uso futuro RCODE contiene un codice che, in caso di errore, ne indica il tipo. Indirizzi variabili L'associazione diretta ed inversa tra nome di computer e suo indirizzo IP, è a rigor di logica possibile, solo nel caso in cui questa non si modifichi nel tempo, ovvero l'host disponga di un cosiddetto indirizzo IP statico. Al contrario, molto spesso l'indirizzo IP viene assegnato dinamicamente poco dopo l'accensione del computer (ad es. via DHCP), ed in quel caso, esistono dei meccanismi (il DDNS) per riconfigurare opportunamente il DNS. Allo stesso tempo, i computer di una stessa rete locale, dovrebbero poter comunicare anche in assenza di un DNS centralizzato ed amministrato da qualcuno: a tale scopo, è nata l'iniziativa di Zeroconf. 79 Indirizzi variabili Risoluzione degli indirizzi applicativi Dynamic Host Configuration Protocol Il DHCP, definito nella RFC 2131 come evoluzione di BOOTP, è un protocollo spesso usato dagli ISP per condividere un certo numero di indirizzi IP tra un numero più elevato di computer, confidando che una richiesta di servizio contemporanea da parte di tutti, non avverrà praticamente mai. Ma al di là di questo, affidare la configurazione di una rete ad un meccanismo dinamico ed automatico è una grande comodità, alla luce della dinamica con cui i computer della rete entrano/escono e sono sostituiti. Il protocollo opera sulla porta 67 di UDP, e mediante i passaggi illustrati in figura, affitta (lease) gli indirizzi di cui dispone, ai client che ne fanno richiesta. • l'host appena acceso, invia in broadcast la sua richiesta (DISCOVERY) di sapere (lui) chi è, od eventualmente, prova a reclamare il rinnovo dell'affitto per l'indirizzo IP ricevuto per ultimo. In quest'ultimo caso, il server può confermare il rinnovo, rifiutarlo, o ignorare la richiesta; • i server DHCP presenti nella LAN inviano allora in unicast un messaggio di OFFER verso il client, che contiene, oltre all'indirizzo IP proposto (ed a lui momentaneamente riservato), anche altri parametri, come la maschera di sottorete, il DNS da usare, il default gateway, e la durata dell'affitto; queste informazioni, possono eventualmente dipendere dall'indirizzo MAC del richiedente; • il client sceglie uno tra gli OFFER ricevuti, e trasmette in broadcast un messaggio di REQUEST, citando sia l'indirizzo che gli è stato offerto, sia il server che lo ha offerto, in modo che gli altri server, ricevendo anch'essi la comunicazione broadcast, possano rendere nuovamente disponibili gli altri indirizzi offerti, ma non adottati; • se non è successo nulla di nuovo e di particolare, il server conferma la richiesta appena ricevuta mediante un messaggio di ACKNOWLEDGE, in cui conferma i parametri già offerti. Il processo continua con il client che periodicamente, prima che termini il periodo di affitto, ne effettua una rinnovo mediante un messaggio di REQUEST, finché il suo funzionamento termina, ed emette un messaggio di RELEASE. Il comportamento del client può essere rappresentato da un diagramma di transizione, che evidenza i passaggi di stato che avvengono: 80 Lo strato applicativo di Internet Alessandro Falaschi Tenendo aperto il capture di Wireshark, e contemporaneamente disabilitando/riabilitando l'interfaccia di rete, si possono osservare i passaggi discussi. Nel casopreso in esame però, manca la fase di DISCOVERY, dato che il computer in questione tenta di riusare l'IP che gli era stato assegnato precedentemente (192.168.0.215), ed il messaggio di REQUEST viene visualizzato come "SSL". La RFC 2132 elenca tutta una serie di ulteriori parametri che anche possono essere specificati tramite i messaggi DHCP, come ad esempio (nelle richieste DHCP) il nome dell'host, e (nelle risposte) il dominio che quest'ultimo deve aggiungere nelle sue richieste al DNS, qualora queste non si riferiscano ad un fqdn. Dynamic DNS Lo standard RFC 2136 definisce un nuovo tipo di messaggio per le richieste al DNS, mediante un Opcode di tipo UPDATE, ed in cui i restanti campi, anziché contenere una richiesta, contengono gli estremi di un RR da aggiungere o modificare. In questo modo, un server DHCP può comunicare al DNS l'indirizzo associato ai lease assegnati, ed associarlo al nome che l'host gli ha comunicato. Inoltre, la RFC 2845 definisce un metodo di autenticazione di questi messaggi, basato su di un segreto condiviso. A conclusione dell'A.A 2006-2007, si è svolta una tesina che ha affrontato l'argomento. Queste tecniche prendono il nome di DDNS (Dynamic DNS appunto), e lo stesso acronimo 81 Indirizzi variabili Risoluzione degli indirizzi applicativi viene utilizzato anche da parte di fornitori di servizio DDNS, ossia soggetti noti come DDNS provider, che permettono anche agli utenti dial-up a cui viene assegnato un IP dinamico, di poter essere raggiunti dal resto di Internet mediante un FQDN. In generale, il TTL dei RR risolti da un provider DDNS sono piuttosto ridotti, dell'ordine della decina di minuti, per permettere alla gerarchia dei DNS di aggiornare frequentemente le proprie cache, e propagare in fretta i RR modificati più di recente. Zeroconf Si tratta di un metodo di configurazione dinamica delle risorse di una rete locale, basato sul protocollo IP, e che non dipende da altri elementi architetturali, nè da pianificazioni ed accordi con altre parti. In altre parole, un computer in area locale deve semplicemente essere in grado di interoperare con altri computer li presenti, senza dipendere da nulla di esterno, un pò come per la corrente elettrica: nessuna lampadina va configurata prima di accenderla! E' un pò ciò che avveniva con la rete Appletalk e che avviene con la rete Windows, con la differenza che usando direttamente IP, uno sviluppatore può sfruttare lo stack TCP/IP anche per le applicazioni di rete che si svolgono tutte in area locale, e che è disponibile nativamente nelle diverse architetture di calcolo. Zeroconf è stato principalmente sviluppato da un impiegato di APPLE, come evoluzione di AppleTalk (video). Per raggiungere gli obiettivi illustrati, occorre che • gli indirizzi IP siamo assegnati senza ricorrere ad un server DHCP • la traduzione tra nomi ed indirizzi IP avvenga senza una server DNS centralizzato • i servizi come stampanti, o directory condivise, siano localizzabili senza ricorrere ad un directory sever centralizzato Sebbene non ancora completamente definito dagli standard, zeroconf viene già utilizzato per accedere, ad esempio, alle stampanti di rete. Illustriamo ora meglio, i tre aspetti sopra evidenziati: Auto-assegnazione degli indirizzi IP I dispositivi che aderiscono a Zeroconf, tentano di auto-assegnarsi un indirizzo link-local di tipo 169.254.*.*, che la RFC 3927 stabilisce possano essere usati nell'ambito di una LAN, e ri-usati su qualunque altra LAN, in quanto non devono essere instradati dai router, e neanche accettati dai dispositivi NAT. La definizione delle modalità con cui avviene questa autoassegnazione è il frutto del lavoro di un WG IETF, ora chiuso. Il risultato, è che ogni host tenta di attribuirsi (e difendere) l'uso (per se) di uno di questi indirizzi. Se però un server DHCP effettivamente risponde, ed assegna all'host un indirizzo instradabile, viene usato quest'ultimo. Risoluzione dei nomi L'idea è di far uso di un DNS che risponda a richieste pubbliche, ossia multicast (detto mDNS). Ogni computer che intenda offrire servizi accessibili mediante indirizzi link-local, ospita un suo mDNS che risponde, presso la porta UDP 5353, alle richieste indirizzate verso l'indirizzo multicast 224.0.0.251 (questo indirizzo, non viene inoltrato dai router). Inoltre, è stato proposto l'uso di uno speciale top level domain, di nome .local, in modo che le richieste di nomi del tipo computer.local vengano dirette, anziché verso il DNS configurato di default, verso l'indirizzo multicast dove ascolta l'mDNS. Una applicazione client che richieda la risoluzione del nome sally.local quindi, riceverà la risposta direttamente dall'mDNS in esecuzione presso la macchina sally. 82 Lo strato applicativo di Internet Alessandro Falaschi Al fine di evitare collisioni di nomi, non appena un mDNS parte, prova a verificare se non ci sia già qualcun altro che rivendica le stesse risorse. Mentre per mDNS esistono già diverse realizzazioni funzionanti, il WG DNSEXT di IETF ha standardizzato una soluzione diversa allo stesso problema, nota come Link-local Multicast Name Resolution (o LLMNR) (RFC 4795), nata in casa Microsoft, e che sebbene funzioni in modo del tutto simile a mDNS, permette di usare anche nomi di tld esistenti, con un possibile effetto di disorientamento. Service discovery Con questo termine si intende una funzione basilare necessaria per accedere ad una qualunque risorsa o servizio disponibile in rete. E' chiaro che noi umani possiamo venire a conoscenza di un particolare indirizzo perché qualcuno ce ne parla, oppure perché questo viene pubblicizzato su giornali, TV, siti web, ed email. Al contrario, un computer in rete utilizza un protocollo di comunicazione, particolarmente progettato per individuare i servizi offerti, descriverne le caratteristiche (o attributi), ed illustrarne le modalità di fruizione, fornendone l'indirizzo (URL). Illustriamo ora tre diverse alternative: SLP, DNS-SD, e SSDP. Service Location Protocol Un caso piuttosto classico, è quello delle stampanti, che possono comparire "magicamente", quando desideriamo stampare qualcosa. Il WG SVRLOC di IETF ha elaborato a tale proposito la RFC 2608 che definisce il Service Location Protocol (SLP), che si basa sulla definizione di tre entità, un client, un server ed un director, sebbene quest'ultimo sia presente solo su reti di grosse dimensioni. Anche se lo standard è definito in modo generico, e non orientato unicamente alle stampanti, è abbastanza comune che queste ultime implementino l'entità SLP server. In linea generale, quando un SPL client ha bisogno di un servizio, invia in multicast una richiesta SLP relativa a quel tipo di servizio, e tutti i server che lo offrono, rispondono. DNS-SD Il DNS Service Discovery (DNS-SD) permette la scoperta dei servizi basandosi sull'uso dei RR SRV del DNS, e per questo funziona particolarmente bene in congiunzione con il mDNS, anche se non ne dipende, nel senso che può essere usato anche con un DNS normale. La sua definizione è frutto dello stesso autore della definizione degli indirizzi Link-Local e del 83 Supporto di DNS al Voice over IP Risoluzione degli indirizzi applicativi mDNS, e quindi si integra perfettamente con la suite definita da Zeroconf, per la quale sono disponibili varie implementazioni, come ad esempio Bonjour e Avahi (video). Nella sezione degli esercizi, sono svolti alcuni esperimenti con Avahi. Simple Service Discovery Protocol SSDP è un protocollo UPnP, usato da Windows XP ma considerato più complesso di DNS-SD, ed il cui processo di standardizzazione si è arrestato nel 2000. Supporto di DNS al Voice over IP Con Voice over IP (VoIP) si intende la possibiità di effettuare conversazioni audio mediante Internet, e l'approfondimento di queste tecniche è oggetto di una sezione del corso specifica. Sebbene la maggior parte degli utenti di Internet identifichi il VoIP con Skype, questo si basa su protocolli proprietari e non pubblici; invece, esiste una valida alternativa, il Session Initiation Protocol (SIP), che è definito da IEFT mediante documenti pubblici. SIP individua gli utenti per mezzo di URI dal formato simile a quello delle email, ossia sip:[email protected] e, quando un computer connesso ad Internet genera una chiamata SIP diretta a questa destinazione, si tenta di individuare il server SIP incaricato di localizzare l'utente alef presso il dominio ing.uniroma1.it, mediante l'interrogazione del DNS a riguardo dei RR di tipo NAPTR e SRV. Se invece di un utente connesso ad Internet, la chiamata deve partire da, o raggiungere, un normale utente telefonico connesso alla rete telefonica PSTN, allora sorge il problema di dover trattare anche semplici numeri, cosidetti E.164. La traduzione da numero E.164 ad indirizzo SIP, avviene ancora una volta per mezzo del DNS, facendo uso dei RR NAPTR, in accordo alle raccomandazioni ENUM. Ma andiamo con ordine. SRV RR Il Resource Record di tipo SRV (SeRVice) è definito nella RFC 2782, e viene effettivamente usato da applicazioni come il VoIP (protocollo SIP) e la messaggistica (protocollo XMPP, RFC 3920). Esso realizza una funzione di Service Location basata sul DNS, e permette di scoprire quali computer offrano, presso un certo dominio, un determinato servizio applicativo, usando il trasporto specificato. Il risultato di questa query indicherà • il FQDN del computer cercato • il numero di porta di trasporto presso la quale è offerto il servizio Queste caratteristiche ne rendono l'utilizzo simile a quello relativo al RR MX (che permette di scoprire, ad esempio, che il server email di alice.it è smtp.aliceposta.it (provare host -t MX alice.it)); in più, il RR SRV permette anche di svincolarsi dall'uso delle porte ben note, potendo infatti offrire il servizio su di una porta qualsiasi, che viene scoperta dal client solo al momento del suo utilizzo effettivo. Il formato dei RR di tipo SRV è _sip._tcp.example.com. 86400 IN --+- --+- ----+------ --+-- + | | | | | serv proto domain TTL class SRV -+| TYP 0 | | pri 5 5060 | | | | wei port 84 sipserver.example.com. ---------+-----------| target Lo strato applicativo di Internet Alessandro Falaschi i cui campi sono descritti al seguente modo: • Nome, in cui il simbolo _ permette di distinguere tre componenti: ◦ Service: il nome simbolico del servizio desiderato, qui sip ◦ Protocol: il trasporto che si intende utilizzare, qui tcp ◦ Domain name: il dominio nel cui ambito si desidera individuare il servizio, qui example.com • • • • • • TTL: il solito Time to Live, qui 86400 secondi, pari a 24 ore Class: il solito campo classe, qui (e sempre) IN Priority: la priorità dell'host target, qui 0 Weight: il peso relativo per i record con la stessa priorità, qui 5 Port: la porta (TCP o UDP) su cui si può accedere al servizio, qui 5060 Target: il nome dell'host che eroga il servizio, qui sipserver.example.com Poniamo che il DNS autorevole per il dominio example.com, ospiti il RR dell'esempio: un resolver che esegua una query chiedendo il RR di tipo SRV associato a _sip._tcp.example.com, vuol conoscere quale sia, e su che porta TCP risponda, il SIP Server del dominio example.com. In analogia con il caso dei RR MX, la query può restituire più RR di tipo SRV, con diversi target, contraddistinti da diverse priorità e pesi, in modo da poter distribuire il carico su più target, ed offrire ridondanza. Nel caso in cui i RR SRV siano erogati da un mDNS, questi possono essere generati dagli stessi applicativi in esecuzione sull'host su cui gira il mDNS, facendo venir meno l'esigenza di immetterli manualmente, in accordo al principio di Zeroconf. NAPTR L'acronimo NAPTR sta per Naming Authority Pointer, come definito nella RFC 2915 e successive, ed ha lo scopo di incorporare nel DNS un insieme di regole tali da permettere alle informazioni di delega, di essere ulteriormente ri-delegate. In altri termini, viene resa possibile la risoluzione di un nome a dominio, in un nuovo nome a dominio, in una URI, o (nello specifico) in un indirizzo VoIP. Un RR di tipo NAPTR aderisce ad una sintassi dalla forma Domain TTL Class Type Order Preference Flags Service Regexp Replacement ed il significato di alcuni dei suoi campi può apparire notevolmente involuto, a causa della estrema genericità che si è voluta dare a questo tipo di RR. In tutti i modi • i campi Domain, TTL e Class hanno i significati già visti, Type vale (appunto) NAPTR, ed Order e Preference hanno lo stesso significato di Priority e Weight del RR SRV, ossia consentono di mantenere un back-up, e di bilanciare il carico su diverse macchine; • il campo Flags è costituito da una singola lettera, in cui ◦ S indica che il prossimo passo, sarà un accesso al DNS, per individuare un RR di tipo SRV; ◦ A indica che il prossimo passo, sarà un accesso al DNS, per individuare un RR di tipo A; ◦ U indica che il prossimo passo non sarà un accesso al DNS, ma che l'esito della applicazione della Regexp a Domain, costituisce il risultato desiderato; ◦ P indica che il prossimo passo è determinato dal protocollo che ha prodotto la query 85 Supporto di DNS al Voice over IP Risoluzione degli indirizzi applicativi • il campo Service è una stringa che deve essere riconosciuta dalla applicazione che ha causato la query, in modo da poter verificare che il risultato è quello atteso. Ad esempio, se la query è effettuata nel contesto di una risoluzione ENUM, il risultato della applicazione della Regexp al dominio, dovrà rappresentare una URI SIP, ed allora Service deve corrispondere al valore "E2U+sip", che sta per E.164 to URI SIP. ◦ in termini più generali, il campo service si scompone in due campi separati da un più (+), e indicati rispettivamente come protocol e resolution service (rs). Per ogni protocollo di strato applicativo (nell'esempio, SIP), deve esistere una RFC che descriva i tipi di rs invocabili da quel protocollo (nell'esempio, la risoluzione da numero E.164 in URI, descritta dalla RFC 3971) • il campo Regexp è una Regular Expression, che costituisce un formalismo (ossia, una sintassi) per descrivere (e riconoscere) un insieme di stringhe, e descrivere allo stesso tempo una trasformazione di tali stringhe, in altre stringhe. Nel caso in cui il Flag sia U, questa Regexp dovrà essere applicata dal client che ha interrogato il DNS, alla chiave utilizzata nella query, ossia al campo Domain, ed il risultato della applicazione della regexp, costituirà la risoluzione desiderata; • Replacement è la stringa che viene restituita come risultato della query, nel caso in cui il Flag valga S oppure A. L'uso del campo Replacement è mutuamente esclusivo con quello di Regexp. Nel caso di una applicazione VoIP, i RR di tipo NAPTR intervengono in almeno tre circostanze: Selezione di un protocollo di trasporto La RFC 3263 specifica che, prima di chiamare una URI SIP, si dovrebbe intraprendere un processo di risoluzione in due passi, che prevede 1. l'interrogazione al DNS, per richiedere i RR di tipo NAPTR relativi al dominio che compare nella URI sip, e da questi, desumere il protocollo di trasporto da usare (UDP, TCP o TLS su TCP), e quindi 2. effettuare una nuova query al DNS, utilizzando come chiave i risultati ottenuti al passo precedente, per richiedere i RR di tipo SRV, in modo da determinare l'host ed il numero di porta verso cui dirigere la chiamata. Ad esempio, volendo chiamare sip:[email protected], il primo passo potrebbe restituire ; example.com example.com example.com IN NAPTR IN NAPTR IN NAPTR order pref flags service regexp 50 50 "s" "SIPS+D2T" "" 90 50 "s" "SIP+D2T" "" 100 50 "s" "SIP+D2U" "" replacement _sips._tcp.example.com. _sip._tcp.example.com. _sip._udp.example.com. indicando che il dominio example.com supporta SIP sia su UDP che su TCP, e SIP su TLS. Nei tre casi, la parte RS del campo service assume il significato di Domain to UDP e Domain to TCP. Come indicato dal flag pari ad s, il risultato delle query è individuato dal campo replacement, e deve essere seguito da una richiesta dei RR di tipo SRV. Nel caso in cui il chiamante (ad es.) non supporti SIPS, tenterà di determinare il computer e la porta da chiamare effettivamente, per il trasporto TCP e UDP, mediante una successiva interrogazione dei RR di tipo SRV: ; _sip._tcp.example.com _sip._udp.example.com IN SRV IN SRV Priority Weight Port 0 1 5060 0 2 5060 86 Target server1.example.com. server2.example.com. Lo strato applicativo di Internet Alessandro Falaschi a cui farà poi seguito una normale interrogazione al DNS, per conoscere i RR di tipo A, ed individuare finalmente l'IP da contattare. Notiamo che nelle due risoluzioni su riportate, i domini di partenza e di arrivo non devono necessariamente coincidere, offrendo una opportunità di URI portability, potendo infatti accasare una URI SIP in cui compare un certo dominio, presso i server di un altro provider VoIP. Infine, nel caso in cui il primo passo fallisca (come conseguenza del fatto che non esistono RR NAPTR per il dominio indicato), si può procedere direttamente al secondo, costruendo le query con il prefisso dei trasporti desiderati. Nel caso in cui fallisca il secondo, si assumerà di chiamare direttamente l'host associato al nome di dominio della URI, ed usare il trasporto UDP sulla porta ben nota 5060. Risoluzione di una URI numerica Come anticipato, quando la destinazione da chiamare non è data nella forma di una URI sip, ma di un normale numero di telefono E.164, allora vengono applicate le regole di riscrittura del numero indicate come ENUM, ed il DNS viene utilizzato prima di tutto, per convertire un numero E.164 in una URI SIP. In questo caso, i RR rilevanti avranno sempre il Flag pari ad U, ed il campo Replacement sarà assente (ma sostituito da un punto). Pertanto, poniamo che la query sia soddisfatta dai RR $ORIGIN 0.0.6.2.3.3.5.2.0.2.1.e164.arpa. IN NAPTR 100 10 "u" "E2U+sip" "!^.*$!sip:[email protected]!" IN NAPTR 100 20 "u" "E2U+mailto" "!^.*$!mailto:[email protected]!" . . in cui il dominio espresso nella $ORIGIN è il risultato delle regole di riscrittura definite da ENUM, per il numero E.164 +12025332600. Le regexp che troviamo sono cosiddette greedy, e sono composte da due campi, delimimitati dai tre punti esclamativi. La prima parte ^.*$ significa (letteralmente) qualsiasi cosa, e quindi la chiave-dominio espressa dalla $ORIGIN, corrisponde. Pertanto, quando il client che ha chiesto la risoluzione riceve questa risposta, applica la sostituzione indicata dal secondo campo della Regexp, che nei due casi di Servizio E2U+sip e E2U+mailto, corrisponde rispettivamente a sip:[email protected] e mailto:[email protected]. In altre parole, la query con chiave E.164, ha fornito come risoluzione a priorità migliore una URI SIP, e se questa chiamata VoIP dovesse fallire, viene proposta come seconda risoluzione, l'invio di una email. Nella figura che segue, un esempio di utilizzo delle alternative di reperibilità. 87 Supporto di DNS al Voice over IP Risoluzione degli indirizzi applicativi L'applicazione di questa metodologia, prevede che il legittimo intestatario di un numero telefonico, richieda al provider VoIP che gestisce il DNS delegato a risolvere il proprio numero in formato ENUM, l'inserimento nel DNS di tanti RR di tipo NAPTR, quanti sono i diversi recapiti che vuole associare a questo numero. In tal modo, anzichè dover comunicare ai suoi conoscenti tutti i diversi indirizzi a cui può essere raggiunto, può comunicare loro un unico numero. In effetti, dopo una risoluzione ENUM, possono ancora essere necessarie le due risoluzioni previste al punto precedente, più ovviamente, l'individuazione dell'indirizzo IP associato al dominio finale. Adesione ad una federazione VoIP in questo caso, i record NAPTR sono usati nel caso della costituzione di una Federazione SIP, ossia di un insieme di provider VoIP, che convengono di basarsi su di una Certification Autority comune, al fine di garantire ai propri utenti l'autenticità delle chiamate in arrivo. La definizione di questa architettura, denominata SIP Peering, è tuttora oggetto di standardizzazione, ma l'orientamento che emerge dai draft, è di usare (mediante RR di tipo NAPTR) il DNS per annunciare le proprie federazioni di appartenenza, in modo che il chiamante e/o il chiamato possano verificare se esistono delle CA in comune, e comportarsi di conseguenza. In questo caso, avremo degli elementi del tipo $ORIGIN vsp-X.example.com @ IN NAPTR 10 50 "U" "D2P+SIP:fed" "!^.*$!http://fed-2.example.org/!" . con cui il quale il provider vsp-X.example.com annuncia la propria adesione alla 88 Lo strato applicativo di Internet Alessandro Falaschi federazione indicata dalla URI http://fed-2.example.org, usando un RR di tipo NAPTR presso il proprio dominio, dove flag pari ad U indica un simbolo terminale, ed il servizio pari a D2P+SIP:fed indica una risoluzione Domain to Peering relativa al protocollo SIP:fed, ossia, l'estensione a SIP per supportare le federazioni. ENUM L'acronimo ENUM sta per Electronic Number Mapping System ENUM, è descritto dalla RFC 3761, è implementato in accordo alle linee guida indicate nella RFC 3824, e definisce il metodo per utilizzare il DNS per la risoluzione dei numeri E.164 in URI. Si basa sulla trasformazione dei numeri telefonici in Domini Internet, attuata ri-scrivendo il numero da destra a sinistra, con la cifra più significativa a destra, intercalando le cifre con dei punti, ed aggiungendo (a destra) il suffisso e164.arpa. Ad esempio, al numero telefonico E.164 +39.081.7507.1 è associato il nome di dominio: 1.7.0.5.7.1.8.0.9.3.e164.arpa. In questo modo, si definisce un meccanismo di delega dei sotto-domini, coerente con la struttura gerarchica della numerazione telefonica, in modo del tutto simile a quanto avviene per la risoluzione inversa degli indirizzi IP. Dato che sembrano esserci ostacoli sia di natura burocratica che politica alla delega delle numerazioni nazionali sotto il suffisso e164.arpa, sono state proposte diverse radici alternative (ossia, suffissi) per la risoluzione ENUM, come la sperimentazione italiana condotta presso NAMEX, che fa riferimento ad una radice 6.9.3.e164.namex.it, l'iniziativa europea nrenum.net, e e164.org, che permette gratuitamente ai singoli individui possessori di un qualunque numero telefonico, di associarvi un indirizzo VoIP. Dato che, noto un numero E.164, non è più ben chiaro sotto che radice debba essere indirizzata l'interrogazione al DNS, i proxy SIP possono interrogare in sequenza più alberi ENUM. Anti-Spam DNS Blackhole Lists Il fenomeno dello spamming della posta elettronica è noto a tutti, e consiste nell'invio di una grande quantità di posta elettronica a destinatari sparsi in tutto il mondo, e che non la vogliono ricevere. Meno nota, è l'origine di questo termine: SPAM è una marca di carne in scatola, la cui invadenza è stata oggetto di uno schetch televisivo dei Monty Pyhton (video). Il fenomeno dello spamming si basa sull'uso di OpenRelay e OpenProxy, ovvero host che rilanciano i messaggi email ricevuti mediante il protocollo SMTP, senza discriminare la loro provenienza. Mentre la configurazione di un server SMTP ufficiale come OpenRelay può, ai giorni nostri, avvenire solo a causa di una grave disattenzione del suo amministratore, spesso gli OpenProxy sono ospitati da host di utenti connessi ad Internet, inconsapevolmente infetti da un virus, che appunto fa da intermediario tra lo spammer, e il server SMTP che l'utente stesso utilizza di diritto. Tra le tecniche di difesa dallo spam, è interessante in questo contesto, citare quella che fa uso del DNS per distribuire in rete una Blackhole List, come ad esempio quelle di SpamCop e SpamHaus. Queste organizzazioni, ricevono segnalazioni a riguardo degli indirizzi IP da cui provengono email di spam, ad esempio perché quel computer ospita un OpenProxy. Quando un server SMTP riceve una connessione, interroga la BHL per sapere se il mittente è tra i cattivi oppure no. L'interrogazione avviene come una richiesta di risoluzione DNS, nel seguente modo: 1. l'indirizzo IP del client viene riscritto con i byte invertiti — ad esempio 192.168.1.254 diventa 254.1.168.192; 89 Appendice Risoluzione degli indirizzi applicativi 2. il dominio della DNSBL viene concatenato all'indirizzo IP invertito, ad es. 254.1.168.192.spammers.example.net; 3. viene effettuata una query DNS (di tipo A) per l'host name così ottenuto. Se la risposta ricevuta è un indirizzo IP (ad es 127.0.0.2), il client è listato, mentre se è NXDOMAIN (No such domain) il client è buono Le risposte a tali interrogazioni, vengono salvate nei DNS intermedi che effettuano la ricorsione, in modo che le email di spam successive, e provenienti dallo stesso OpenProxy, vengono rifiutate senza interrogare di nuovo il DNS autorevole di chi gestisce il sevizio di DNS BL. Appendice IETF L'Internet Engineering Task Force sviluppa e promuove gli standard di Internet, in collaborazione con W3C e ISO/IEC, ed è costituita da volontari, senza necessità di appartenenza formale. E' organizzata in un gran numero di Gruppi di Lavoro (WG), il cui funzionamento è definito dalla RFC 2418, ognuno relativo ad un argomento specifico, e commissionati a portare a termine un lavoro, e quindi ad essere chiusi. Ogni gruppo è diretto da uno o più chairman, e gli obiettivi sono definiti da un charter. Più gruppi sono riuniti all'interno di una stessa area sulla base degli argomenti, ed ogni area supervisionata da un direttore d'area, che nomina i chair dei WG. I direttori d'area assieme al chair dello IETF, compongono l'Internet Engineering Steering Group (IESG), responsabile dell'attività complessiva dello IETF. Ancora, IETF è formalmente una attività svolta sotto l'ombrello della Internet Society (ISOC), mentre è supervisionata dall'Internet Architecture Board (IAB). I gruppi di lavoro si riuniscono fisicamente tre volte l'anno, e negli intervalli, svolgono il lavoro discutendo per via posta elettronica, costituendo così un luminoso esempio di comunicazione telematica. Le decisioni vengo prese senza bisogno di votazioni, ma in base al principio del consenso approssimato. Le mailing list sono ad accesso pubblico, e chiunque vi può intervenire. Quando la discussione raggiunge un livello di maturità sufficiente, oppure per chiarire meglio un punto di vista od un approccio, vengono prodotti dei documenti detti draft, che sono lasciati in visione sui server pubblici per un periodo massimo di sei mesi, dopodiché se non è stata prodotta una nuova versione del draft, contenente gli emendamenti risultanti dalla discussione intercorsanel frattempo, vengono ritirati. Quando il Draft raggiunge uno stato stabile, sempreché abbia ricevuto il consenso, questo evolve allo stadio di Request for Comments (RFC), ma anche in questo caso, ci sono da fare alcuni distinguo, e precisamente: • le specifiche che si prevede possano diventare degli standard internet evolvono secondo tre livelli di maturità, mantenendo il numero progressivo della RFC con cui sono stati emessi, e prendono rispettivamente gli appellativi di Proposed Standard, Draft Standard e Internet Standard: in quest'ultimo caso, gli viene assegnato un secondo numero, nella serie degli STD. • le specifiche che non sono sul binario degli standard (standards track) possono essere etichettate come Experimental, Informational, o Historic, e non sono da considerare standard in nessun senso. 90 Lo strato applicativo di Internet Alessandro Falaschi • La sottoserie delle RFC detto di Best Current Practice (BCP) è un modo per standardizzare le pratiche che risultano da delibere di comunità, con cui lo IETF definisce e ratifica un orientamento comune che si è affermato autonomamente nella comunità della gestione di Internet. Riferimenti • • • • DNS HowTo di Nicolai Langfeldt (in italiano) Introduzione al DNS, Appunti di Informatica libera Pro DNS and BIND Uso di ENUM per le Reti della Ricerca - Marco Sommani (CNR-Pisa), GARR_WS7 Realizzato con da Alessandro Falaschi - 91 Riferimenti Risoluzione degli indirizzi applicativi 92 Lo strato applicativo di Internet Posta elettronica Questa sezione esplora le caratteristiche salienti di una delle più diffuse applicazioni di rete, l'e-mail, il cui funzionamento è completamente poggiato sui servizi offerti dagli strati protocollari inferiori (TCP/IP e DNS), acceduti rispettivamente tramite l'interfaccia socket e la resolver library. L'esposizione si articola nella descrizione di come i messaggi email siano formattati, e come vengano inoltrati; quindi, si illustrano le caratteristiche descrittive offerte dalle estensioni MIME. Alla base della scrittura delle email, ci sono i testi, e quindi vengono discussi gli alfabeti di rappresentazione dei caratteri usati dai vari idiomi della terra. Dopo aver trattato del prelievo dell'email, e degli aspetti di sicurezza legati ad invio e ricezione, sono elencate le possibili alternative di comunicazione testuale. • Elementi architetturali ◦ Normativa e nomenclatura ◦ Configurazione • Protocollo SMTP ◦ ◦ ◦ ◦ ◦ Oggetti email Codici di risposta Extended SMTP Destinatari multipli Intestazioni • MIME ◦ Intestazioni principali ▪ Content-Type ▪ Content-Transfer-Encoding ▪ quoted-printable ▪ base64 ◦ Encoded Word ◦ Multipart Content Type ▪ Multipart Subtypes ▪ Content-Disposition • Insiemi di caratteri ◦ ASCII ◦ Codepage ◦ Unicode ▪ Piani ▪ Basic Multilingual Plane ▪ Unicode Tranformation Format ◦ UTF-8 • Sicurezza ◦ Autenticazione ▪ SASL ▪ SMTP-AUTH Elementi architetturali Posta elettronica ◦ Confidenzialità, integrità e autenticità ▪ StartTLS • Ricezione dell'email ◦ POP3 ◦ IMAP ◦ Webmail • Altre architetture di telecomunicazione testuale ◦ Mailing List ▪ ▪ ▪ ▪ Automazione Gestori Archiviazione Netiquette ◦ Newsgroup ▪ BBS ◦ Internet Relay Chat ◦ Instant Messaging ▪ Topologia e interlavoro ▪ Client multiprotocollo ▪ Standard aperti ▪ Jabber - XMPP ▪ Architettura ▪ Interoperabilità ▪ SIMPLE ▪ Presence ▪ Open Mobile Alliance ▪ Funzioni Telemediali Elementi architetturali La posta elettronica (electronic mail) è un servizio di comunicazione Internet basato sulla consegna asincrona di PDU dati contenenti testo, e/o altri file allegati. I soggetti mittente e destinatario di una email sono individuati da URI applicative dal formato mailto:[email protected] che rappresentano la domiciliazione dell'utente alef presso un dominio internet. Sebbene la consegna delle email sia attuata mediante applicazioni client-server, non accade praticamente mai che il mittente comunichi direttamente con il destinatario. Invece, sono dislocati in rete dei nodi speciali detti server di posta, che dialogano tra loro mediante un protocollo applicativo denominato SMTP, e tramite i quali viene inoltrato il messaggio. Il primo server della catena è concettualmente simile al default gateway dello strato IP, ed è quello a cui il mittente consegna (fiducioso) il messaggio da spedire. Questo primo server, una volta chiusa la conessione TCP su cui avviene la prma transazione SMTP, può a sua volta inviare (trasformandosi in client) il messaggio verso un altro server SMTP di sua scelta, oppure direttamente verso il server che gestise la posta per il dominio presso il quale risiede 94 Lo strato applicativo di Internet Alessandro Falaschi l'utente destinatario. Il messaggio rimarrà li in giacenza, finchè l'intestatario della URI di destinazione non lo preleverà, ricorrendo ad un diverso protocollo. Normativa e nomenclatura Il Simple Mail Transfer Protocol viene definito nel 1982 dalla RFC 821, scritta da John Postel, e da allora aggiornata da altri documenti (RFC 2821 e molti altri, fino a RFC 5321). Il protocollo SMTP è elaborato da una applicazione-demone, in ascolto delle connessioni TCP sulla porta 25. La consegna a destinazione di un messaggio, avviene secondo uno schema di immagazzinamento e rilancio, ad opera dei server di posta indicati anche come agenti, ovvero intermediari, detti Mail Transfer Agent (MTA). Questi, dialogano tra loro in SMTP, e possono rivestire sia il ruolo di server, che quello di client, occupandosi di inoltrare il messaggio in direzione della destinazione. Il caso dell'intervento di due soli agenti è descritto dalla segunete figura, trovata presso Liverani: Il mittente (umano) compone il messaggio da spedire mediante un Mail User Agent (MUA), che si comporta come un client SMTP, ed invia l'email ad un primo MTA di transito: l'SMTP server dell'ISP di partenza. Questo a sua volta, assumendo ora il ruolo di client, inoltra il messaggio verso l'SMTP server situato presso il destinatario, che (attuando la funzione di Mail Delivery Agent, o MDA) salva localmente il messaggio ricevuto, in attesa che il destinario (umano) lo prelevi (con il suo MUA) per mezzo del protocollo POP3 o IMAP, e lo legga. Configurazione Tipicamente il MUA usato dal mittente è lo stesso applicativo usato anche per la ricezione, e deve essere configurato manualmente, inserendo l'indirizzo del server SMTP a cui inoltrare l'email in uscita: nella pratica, questo dovrà essere proprio il server SMTP disponibile presso l'ISP tramite il quale ci si collega ad Internet. Infatti, per limitare il fenomeno dello spamming, ogni ISP configura il proprio server SMTP in modo che questo accetti la posta da recapitare, solo se proveniente da computer collegati tramite la propria stessa rete. Non solo: sempre per lo stesso motivo, i server SMTP che operano dal lato di destinazione del collegamento SMTP, verificano che l'IP del mittente non sia segnalato in una DNSBL, ovvero che non appartenga a lotti di indirizzi che gli ISP assegnano dinamicamente (ad es., via DHCP) ai loro utenti. D'altra parte, esistono casi in cui il Server SMTP usato da un mittente casalingo, non è quello del proprio ISP: ad esempio, si tratta del caso di una email inviata tramite una interfaccia webmail come Gmail, oppure qualora il computer mittente usi un server SMTP co-residente (rischiando però di vedersi l'email bloccata dai meccanismi anti-spam). 95 Protocollo SMTP Posta elettronica Protocollo SMTP Allo scopo di individuare l'MTA di destinazione di una email, il server SMTP ospitato presso l'ISP del mittente interroga il DNS (vedi fig. a lato) per conoscere i record MX relativi al dominio di destinazione; quindi, intrattiene una transazione SMTP con il server di destinazione, del tipo di quella riportata in quest'esempio. Notiamo che come misura antispam, il ricevente può verificare (tramite DNS) che il nome dell'host mittente, si risolva effettivamente nell'indirizzo da cui risulta provenire la connessione, e in caso contrario, chiuderla. Poi, dopo alcune verifiche sull'effettiva esistenza dei destinatari, la transazione procede citando il mittente, i destinatari, e quindi il corpo del messaggio, eventualmente preceduto da altri header testuali, e terminato da una linea isolata, contenente un solo punto. il colloquio SMTP si svolge sia tra il MUA del mittente (che svolge un ruolo di SMTP client) ed il suo server SMTP di uscita, che tra l'SMTP server (che anch'esso, si trasforma per l'occasione in client) dell'ISP del mittente, e quello del destinatario. Riportiamo appresso il risultato di un capture, in cui le linee prefisse con S: e C: indicano rispettivamente le stringhe emesse da server e client. S: C: S: S: S: S: S: S: 220 smtp-out4.libero.it ESMTP Service (7.3.120) ready EHLO [192.168.120.40] 250-smtp-out4.libero.it 250-DSN 250-8BITMIME 250-PIPELINING 250-HELP 250 SIZE 30000000 96 Lo strato applicativo di Internet C: S: C: S: C: S: C: C: C: C: C: C: C: C: C: C: C: C: C: C: S: C: S: Alessandro Falaschi MAIL FROM:<[email protected]> SIZE=358 250 MAIL FROM:<[email protected]> OK RCPT TO:<[email protected]> 250 RCPT TO:<[email protected]> OK DATA 354 Start mail input; end with <CRLF>.<CRLF> Message-ID: <[email protected]> Date: Mon, 05 Mar 2007 17:06:20 +0100 From: alef <[email protected]> User-Agent: Mozilla Thunderbird 1.5.0.9 (X11/20070103) MIME-Version: 1.0 To: [email protected] Subject: test di capture dell'SMTP Content-Type: text/plain; charset=ISO-8859-15 Content-Transfer-Encoding: 7bit salute a tutti Ale . 250 <45D9993D01232796> Mail accepted QUIT 221 smtp-out4.libero.it QUIT Notiamo le seguenti cose. Oggetti email La RFC2821 distingue tra envelope e content. L'envelope è inviato come una serie di unità di protocollo SMTP, e consiste dell'origine (MAIL FROM), di uno o più recipienti (RCPT TO), e di eventuali riferimenti ad estensioni. Il contenuto invece, è inviato mediante l'unità di protocollo DATA, e consiste di due parti, le intestazioni (header) definite da RFC 2822, ed il corpo (body) che se è strutturato, è definito in accordo a MIME (RFC 2045). Sia header che body, sono trasmessi usando caratteri a 7 bit, inseriti in byte con il bit più significativo a zero, ed appartenenti all'alfabeto US-ASCII, tranne per l'eccezione prevista dall'estensione 8BITMIME per il body, e dalla sintassi Encoded Word per gli headers. Il messaggio è composto da linee terminate dalla sequenza CR/LF (0D 0A in esadecimale, 13 10 in decimale), e di lunghezza massima di 1000 bytes. 97 Posta elettronica Codici di risposta Ogni risposta del SMTP server è del tipo reason-code reason-phrase, ovvero un numero seguito da una frase. I codici numerici possono essere usati da un programma per capire la risposta, mentre la frase è a beneficio della leggibilità, per l'operatore umano. Il significato dei codici di risposta può essere classificato nell'ambito di 5 categorie, individuate dalla prima cifra del corrispondente codice numerico, in accordo allo schema reason code categoria significato 1xx Positive Preliminary Reply seguirà una risposta ulteriore, prima che il comando richiesto sia completato 2xx Positive Completion Reply il comando è stato eseguito, e si può iniziare una nuova richiesta 3xx Positive Intermediate Reply ci si aspetta altro input da parte del client 4xx Transient Negative Completion Reply manifestano un errore temporaneo, offrendo la possibilità di ripetere il comando che ha causato l'errore 5xx Permanent Negative Completion Reply il comando non è stato accettato, l'azione non è stata intrapresa, ed il client è scoraggiato a ritentare Extended SMTP L'Extended SMTP è definito dalle RFC 1869 e RFC 2821, ed il server ne annuncia il supporto, mediante la sua risposta iniziale. Se in questa, infatti, compare la stringa ESMTP anziché SMTP, allora vuol dire che il server supporta le estensioni. Il client a sua volta, può manifestare l'intenzione di tentare di sfruttare una delle estensioni disponibili, asserendo a sua volta la stringa EHLO, a cui il server risponde con l'elenco delle estensioni disponibili, ognuna definita in una RFC separata. Altrimenti, nel caso in cui il client si fosse presentato con un semplice HELO, si procede con un comportamento strettamente conforme alla RFC 821. Tra le estensioni presenti nell'esempio sopra riportato, notiamo ◦ SIZE (RFC 1870) che indica la massima dimensione accettata, ed evita al MUA di iniziare a trasmettere qualcosa di più voluminoso; ◦ DSN (RFC 3461) - Delivery Status Notification per delegare all'SMTP il compito di generare una ricevuta di consegna, ma non è utilizzata praticamente mai; ◦ 8BITMIME (RFC 1652) - l'SMTP è definito per consegnare messaggi scritti con caratteri ascii a 7 bit, mentre questa estensione consente l'uso (per il body) di caratteri ad 8 bit, in accordo al formato MIME, qualora il client aggiunga il parametro BODY al comando SMTP MAIL: C: MAIL FROM:<[email protected]> BODY=8BITMIME Dato che invece gli header devono contenere sempre e comunque caratteri US-ASCII, e che a seguito dell'introduzione dello standard MIME, negli header è comunque presente l'informazione a riguardo del Content-Transfer-Encoding, si ottiene che l'ESMTP ricevente ha comunque modo di conoscere l'Encoding del body per questa via, e quindi l'uso di questo parametro è divenuto opzionale. Si veda infatti questo capture, perun esempio di messaggio contenente le lettere accentate àèéìòù offerte dall'alfabeto ISO-8859-15, e trasferito ad 8 bit; ◦ STARTTLS (RFC 3207) permette di invocare i servizi di crittografia offerti dal Transport Layer Security; 98 Lo strato applicativo di Internet Alessandro Falaschi ◦ SMTP-AUTH (RFC 2554) permette di restingere l'uso dell'SMTP di partenza ai soli utenti in grado di autenticarsi, nel tentativo di mitigare il fenomeno dello SPAM. Ma dato che comunque, non risolve il problema dello SPAM in ingresso al server di destinazione, non viene particolarmente usato, se non nel caso dell'utenza roaming, ovvero quando l'SMTP server di partenza è collocato al difuori della sotto rete mediante la quale il MUA del mittente è connesso ad Internet. Destinatari multipli Nel caso in cui uno stesso messaggio, sia destinato a più recipienti (RCPT) questi vengono tutti elencati dall'SMTP mittente, e poi viene inviata una unica copia del messaggio. Un server SMTP intermedio, se ha ricevuto un messaggio con destinari molteplici, alcuni dei quali co-locati presso uno stesso dominio di destinazione, a sua volta invia al server SMTP di quel dominio una unica copia del messaggio, con tutti i RCPT (in quel dominio) raggruppati assieme. Intestazioni Le opzioni dei protocolli analizzati finora (ARP, UDP, TCP, ICMP, DNS...), sono sempre state codificate in formato binario, e poste all'interno delle intestazioni delle PDU prodotte da quei protocolli: queste intestazioni, venivano esaminate direttamente dai dispositivi di rete, o dal kernel, o dalle librerie, dei computer di destinazione. Nel caso attuale invece, l'SMTP è un protocollo di strato applicativo che opera direttamente tra entità pari, non più integrate dentro ad Internet, ma concettualmente disposte ai bordi della rete, ed in esecuzione nello User Space dei computer in comunicazione. Questa profonda differenza, fa sì che l'SMTP adotti una diversa modalità di rappresentazione delle proprie intestazioni di protocollo, così come accadrà anche nel caso dei diversi protocolli applicativi che esamineremo in seguito, e che prevede che le intestazioni di strato applicativo, siano rappresentate in forma umanamente leggibile. Tali intestazioni sono state inizialmente definite in RFC 822, e quindi in RFC 2822: sono disposte una per linea, e composte da un nome, seguito da due punti, e quindi un valore; inoltre, sono separate dal body del messaggio vero e proprio, mediante una linea vuota. Le uniche due intestazioni obbligatorie, sono • Date: Mon, 05 Mar 2007 17:06:20 +0100 - inserite dallo UA di partenza • From: Alessandro Falaschi <[email protected]> - il mittente, a cui inviare l'eventuale risposta Sembra strano che non sia richiesto obbligatoriaente almeno un destinatario? ...il fatto è che questo può essere espresso mediante tre diversi header (To:, Cc:, e Bcc:), nessuno dei quali è di per sé obbligatorio. Analizziamo ora però, alcuni altri header che possono essere presenti: • Sender: segreteria <[email protected]> - chi materialmente invia il messaggio, per conto del mittente vero; • Reply-to: scrivimi <[email protected]> - se presente, le risposte vanno qui, e non al From: • To: [email protected] - il destinatario, possono essere più d'uno; • CC: [email protected] - Carbon Copy, un altro destinatario da mettere in copia, possono essere più d'uno; • BCC: [email protected] - Blind Carbon Copy, ancora altro destinatario da mettere in copia, senza che gli altri destinatari lo sappiano; i BCC possono essere più d'uno 99 MIME Posta elettronica • Message-ID: <[email protected]> - inserito dallo UA come identificativo del particolare messaggio. Questo valore può essere citato come valore degli header In-Reply_To: e References: che, se presenti in una risposta, permettono di associare la stessa al messaggio uscente corrispondente • User-Agent: Mozilla Thunderbird 1.5.0.9 (X11/20070103) - identifica il programa usato per l'invio • Subject: test di capture dell'SMTP - identifica l'oggetto della comunicazione. È considerata buona educazione utilizzare questo campo, per aiutare il destinatario a capire il contenuto del messaggio. le seguenti tre intestazioni hanno a che fare con l'alfabeto usato per scrivere il contenuto della email, e con il modo in cui tale testo viene rappresentato, come descritto di seguito • MIME-Version: 1.0 • Content-Type: text/plain; charset=ISO-8859-15 • Content-Transfer-Encoding: 7bit esistono poi delle ulteriori intestazioni, che possono essere aggiunte dai server SMTP di transito, e che troviamo dal lato di ricezione del messaggio dell'esempio soprastante, come • Return-Path: <[email protected]> • Received: from smtp-out4.libero.it (smtp-out4.libero.it [212.52.84.46]) by infocom.uniroma1.it (8.12.10/8.12.10) with ESMTP id l25GY4aW002416for <[email protected]>; Mon, 5 Mar 2007 17:34:04 +0100 • Received: from localhost (172.31.0.51) by smtp-out4.libero.it (7.3.120) id 45D9992900FDB3E3 for [email protected]; Mon, 5 Mar 2007 17:06:20 +0100 • X-Scanned: with antispam and antivirus automated system at libero.it • Received: from smtp-out4.libero.it ([172.31.0.40]) by localhost (asavout10.libero.it [192.168.32.38]) (amavisd-new, port 10024) with ESMTP id ttPaRI2Q3Cle for <[email protected]>; Mon, 5 Mar 2007 17:06:20 +0100 (CET) • Received: from [192.168.120.40] (151.41.242.192) by smtp-out4.libero.it (7.3.120) id 45D9993D01232796 for [email protected]; Mon, 5 Mar 2007 17:06:20 +0100 in cui • il Return-Path: viene inserito dall'ultimo server SMTP della catena, quello relativo al destinatario, ed indica l'indirizzo a cui inviare eventuali messaggi di errore • le intestazioni Received: sono aggiunte ognuna, dal basso verso l'alto, dai diversi server SMTP di transito, e possono tornare utili, per ricostruire il percorso che ha fatto il messaggio. Indicano l'SMTP mittente (from), il ricevente (to), se si è usato l'ESMTP, l'ID unico con il quale il messaggio è stato registrato nei file di log del ricevente, il destinatario (for) (potrebbe cambiare in transito, se elaborato da una mailing list), e la data e ora locali, più la correzione rispetto all'ora di Greenwitch. Qualora il numero di intestazioni Received: superi un determinato valore (ad es. 30), si presume che si sia verificato un loop anomalo, l'inoltro viene sospeso, e viene inviato un messaggio di errore al Return-Path: • le intestazioni che iniziano per X- (es X-Scanned:) non sono definite da uno standard, ma possono veicolare informazioni speciali per lo UA di ricezione, o semplicemente per chi legge MIME Il Multipurpose Internet Mail Extensions (MIME) estende il formato delle email, per permettere 100 Lo strato applicativo di Internet • • • • Alessandro Falaschi l'uso di caratteri diversi dall'insieme US-ASCII, di allegare i documenti più disparati, anche diversi da file di testo, di spedire messaggi composti da più parti, e di usare caratteri non-ASCII nei valori degli header, come il Subject: Queste specifiche sono usate anche in ambiti diversi dal traffico email, come per i protocolli HTTP e SIP, e sono descritte dai documenti RFC 2045, RFC 2046, RFC 2047, RFC 4288, RFC 4289, RFC 2077, RFC 2231, RFC 3676. Intestazioni principali L'intestazione MIME-Version: 1.0 indica allo User Agent di ricezione, che il messaggio aderisce alle specifiche MIME, e viene semplicemente ignorato da uno UA non aggiornato, e non in grado di interpretarlo correttamente. Content-Type La presenza di Content-Type: text/plain; charset=ISO-8859-15 indica il tipo di contenuto presente, nel formato tipo/sottotipo; parametro=valore, e fornisce allo UA un suggerimento su come visualizzare o riprodurre correttamente il contenuto ricevuto. Il parametro permette di specificare una particolare caratteristica del tipo/sottotipo indicato, ed in questo esempio, stabilisce che il body dell'email contiene caratteri appartenenti all'insieme ISO-8859-15. Nel caso invece in cui si tratti di un contenuto non direttamente visualizzabile da parte del lettore di email, ossia non testuale, occorre eseguire un applicativo idoneo (ad es., un player multimediale per contenuto audio o video), associato al MIME-Type che lo descrive. La lista dei tipi e sottotipi possibili è registrata presso IANA, da cui possiamo evidenziare alcuni casi particolari: • Tipo application: contenuti da aprire con programmi appositi ◦ application/javascript: codice JavaScript; definita in RFC 4329 ◦ application/octet-stream: contenuto binario generico, che può anche essere un programma eseguibile. Se presente nel messaggio ricevuto, il programma ricevente può visualizzare una proposta di salvarlo su disco. La RFC 2046 lo imposta come il tipo di default per sottotipi non riconosciuti, e per questi motivi, presenta rischi di sicurezza. ◦ application/pdf: definita da RFC 3778 ◦ application/vnd.ms-powerpoint: definita da Sukvinder S. Gill. vnd sta per vendor e identifica formati proprietari ◦ application/xml (RFC 3023): i dati sono strutturati in formato XML, e possono codificare informazioni che necessitano di ulteriore elaborazione per poter essere usate • Tipo audio ◦ audio/mpeg: MP3 o altro audio MPEG audio; definito in RFC 3003 101 MIME Posta elettronica ◦ audio/x-wav: WAV audio. La particella x- indica un tipo non registrato presso IANA, ma inventato dal mittente, e che in base ad accordi indipendenti, è può esere riconosciuto dallo UA ricevente. • Tipo image ◦ image/gif: immagine GIF image; definito in RFC 2045 and RFC 2046 ◦ image/jpeg: immagine JPEG; definito in RFC 2045 and RFC 2046 • Tipo multipart: questo tipo permette a MIME di inviare messaggi strutturati ad albero, in cui le singole parti possono essere foglie, con a loro volta un unico Content-Type, oppure essere altri componenti di tipo multipart. In questo modo, si possono ad esempio inviare allegati, come illustrato sotto. Definito in RFC 2045 e RFC 2046. • Tipo text ◦ text/plain: dati testuali visualizzabili direttamente dallo UA, definito in RFC 2046 e RFC 3676, che raccomanda l'uso del set di caratteri US-ASCII se non specificato diversamente, attribuendo al parametro charset un valore opportuno (come as es., ISO-8859-15). In caso di assenza di sottotipo, l'allegato è da intendersi plain ◦ text/html: un file HTML che può essere visualizzato con un browser web o dallo UA stesso; definito da RFC 2854, che suggerisce di specificare il charset mediante l'uso dell'apposito parametro ◦ text/xml (RFC 3023): i dati sono strutturati in formato XML, e possono essere visualizzati da un ricevente generico, come fosse un testo semplice • Tipo video ◦ video/mpeg: video in formato MPEG-1 multiplato assieme con audio, definito in RFC 2045 and RFC 2046 ◦ video/mp4: video MP4, definito in RFC 4337 ◦ video/quicktime: video QuickTime, registrato presso IANA ◦ video/x-ms-wmv: video Windows Media, documentato presso Microsoft KB 288102 Content-Transfer-Encoding A meno che non sia disponible l'estensione ESMTP di tipo 8BITMIME, le email devono contenere solamente caratteri US-ASCII, con il bit più significativo di ogni byte posto a zero. Questo contrasta con l'uso di set di caratteri esteso, come gli alfabeti ISO-8859-x, e con l'invio di allegati contenenti dati binari qualsiasi. Per questo, lo standard MIME prevede l'uso della intestazione Content-Transfer-Encoding, come ad esempio Content-Transfer-Encoding: 7bit che stabilisce un metodo di codifica del body tale da convertire il suo vero contenuto, qualsiasi, in una diversa rappresentazione, tale che nel flusso di byte risultante il bit più significativo sia posto a zero. Il lato trasmittente, una volta noto il formato accettabile dal server SMTP, converte al volo l'email, ed aggiunge questo header, per indicare la trasformazione effettuata, in modo che il lato ricevente, noto il Transfer-Encoding utilizzato, possa poi provvedere ad invertirne la rappresentazione, in modo da ristabilire il 102 Lo strato applicativo di Internet Alessandro Falaschi flusso binario originario. La RFC 2045 definisce i valori che possono essere assunti dalla intestazione Content-Transfer-Encoding:, che sono: adatti all'SMTP normale: • 7bit - è il valore di default, assunto vero se Content-Transfer-Encoding non è presente, e prevede la trasmissone di fino a 998 byte per linea, con codici di carattere nell'intervallo 1..127, ed in cui la sequenza CR e LF (codici 13 e 10) indica la fine di una linea; • quoted-printable - trasforma i caratteri ad 8 bit ed i caratteri non stampabili eventualmente presenti, nella sequenza di 3 caratteri =HH, in cui le due cifre esadecimali HH (0-9 o A-F) corrispondono al codice binario (in base al charset utilizzato) del carattere trasformato. Il risultato à ancora direttamente leggibile per la sua parte stampabile, ed anche se uno UA non supporta la trasformazione inversa, la codifica dei caratteri trasformati può essere visualizzata senza danno. Ad esempio: ◦ il carattere US-ASCII form feed (decimale 12) viene rappresentato come =0C ◦ il carattere é dell'alfabeto ISO-8859-1 (decimale 233, corrispondente ad una e con accento acuto), si rappresenta con la sequenza =E9 ◦ la parola cioè (in ISO-8859-1) è rappresentata come cio=E8 • base64- trasforma tutti i byte presenti, raggruppando i byte a tre a tre (per un totale di 24 bit), e rappresentandoli con 4 caratteri stampabili, ognuno dei quali rappresenta una codifica di solo 6 dei 24 bit complessivi, come in questo esempio in cui si mostrano i passaggi necessari a rappresentare la sequenza esadecimale 0x010203 nella sua rappresentazione base64 AQID: 1 2 3 00000001 00000010 00000011 / \ / \ / \ 000000 01 0000 0010 00 000011 |----| |------| |------| |----| 000000 010000 001000 000011 0 16 8 3 A Q I D In particolare, la trasformazione dell'ultima riga avviene interpretando i valori decimali riportati nella penultima riga, come indici di una tabella di conversione riportata qui sotto, in cui compare un sottoinsieme di 64 caratteri stampabili dell'alfabeto US-ASCII. NUM 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 ASCII A B C D E F G H I J K L M N O P NUM 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 ASCII Q R S T U V W X Y Z a b c d e f NUM 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 103 ASCII g h i j k l m n o p q r s t u v NUM 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 ASCII w x y z 0 1 2 3 4 5 6 7 8 9 + / MIME Posta elettronica Nel caso in cui il testo di partenza non abbia un numero complessivo di byte multiplo di 3, si aggiungono a destra tanti zeri, quanti ne servono per completare l'ultimo gruppo di 6 bit in entrata. Quindi, se il numero di simboli BASE64 ottenuti non è un multiplo di quattro, si aggiungono alla codifica finale uno o due simboli di uguale (=), in numero pari al numero di simboli mancanti, in modo da segnalarlo al ricevitore. Ad esempio, mostriamo la codifica della parola cioé, quando inizialmente rappresentata in ISO-8859-1. In tal caso, la parola si esprime mediante la sequenza esadecimale 0x63 0x69 0x6F 0xE8 (eseguire man ascii e man iso_8859_1 per verificarlo), ossia binaria 01100011 01101001 01101111 11101000, che si suddivide in gruppi di 6 bit come 011000 110110 100101 101111 111010 000000, in cui gli ultimi 4 bit sono stati aggiunti, e posti a zero. Riscrivendo l'ultima sequenza in decimale, otteniamo 24 54 37 47 58 00, che in accordo alla tabella precedente, fornisce una rappresentazione BASE64 pari a Y2lv6A==, dove i due simboli = finali sostituiscono i due gruppi di 6 bit mancanti nella penultima sequenza. Esistono alcuni siti che calcolano la trasformazione on-line, come quelli qui indicati. Adatto ad un server ESMTP che supporta l'estensione 8BITMIME: • 8bit - fino a 998 bytes per linea, con la sequenza CR e LF (codici 13 e 10) che appare solo alla fine di una linea; Adatto ad un server ESMTP che supporta l'estensione BINARYMIME (RFC 3030): • binary - qualunque sequenza di bytes. Encoded Word Le intestazioni MIME Content-Type e Content-Transfer-Encoding stabiliscono alfabeto e codifica del body della email, ma per usare un alfabeto diverso da US-ASCII nel valore di una intestazione, come ad esempio nel Subject, si ricorre ad un diverso espediente, noto come Encoded Word (RFC 2047), che definisce una sintassi tale da indicare, in un sol colpo • il charset originario, • il transfer-encoding usato, • il risultato della trasformazione. La sintassi è =?charset?encoding?encoded text?= • charset può essere qualunque, purché registrato presso IANA e tipicamente, sarà lo stesso charset del body • encoding può essere sia "Q", ossia quoted-printable, oppure "B", ossia base64 • encoded text è il risultato della trasformazione. Per esempio, 104 Lo strato applicativo di Internet Alessandro Falaschi Subject: =?utf-8?Q?=C2=A1Hola,_se=C3=B1or!?= è interpretabile come Subject: ¡Hola, señor!. Notiamo, in questo caso, che avendo adottato come charset utf-8, la codifica per i caratteri ¡ (punto esclamativo rovesciato) e ñ utilizza due bytes, che con il transfer-encoding di tipo quoted-printable, divengono 6 caratteri US-ASCII. Multipart Content Type Un messaggio MIME multiparte è il formato tipico in cui sono realizzate le email che contengono allegati, o contenuti di tipo diverso dal text/plain. In questo caso, il body del messaggio è suddiviso mediante una stringa speciale detta confine (boundary), definita nella intestazione Content-type:, e che non deve comparire in nessuna delle parti: per questo motivo, il confine tipicamente assume l'aspetto di una sequenza di simboli casuali. Il confine è inserito tra le diverse parti, allo scopo di demarcare la loro separazione, ed all'inizio ed alla fine del corpo del messaggio, come segue: Content-type: multipart/mixed; boundary="frontier" MIME-version: 1.0 This is a multi-part message in MIME format. --frontier Content-type: text/plain This is the body of the message. --frontier Content-type: application/octet-stream Content-transfer-encoding: base64 PGh0bWw+CiAgPGhlYWQ+CiAgPC9oZWFkPgogIDxib2R5PgogICAgPHA+VGhpcyBpcyB0aGUg Ym9keSBvZiB0aGUgbWVzc2FnZS48L3A+CiAgPC9ib2R5Pgo8L2h0bWw+Cg== --frontier-- Ogni parte inizia con una sua propria intestazione Content-type, seguita da una eventuale Content-transfer-encoding, seguita dal corpo della parte. I client dei destinatari che aderiscono alla specifica MIME ignorano (non visualizzano) quanto è presente prima del primo confine, che contiene un messaggio rivolto appunto ai possessori di client che non aderiscono a MIME. Questi ultimi infatti, visualizzano il messaggio per intero, così come l'abbiamo mostrato sopra, ed in cui compare in testa l'avvertimento rivolto loro. Multipart Subtypes Il Content-type multipart può essere di diversi sottotipi, che specificano la natura delle parti, e le loro relazioni reciproche. I sottotipi più comuni sono: • Mixed - usato per inviare files allegati, con Content-Type differenti dal testo del messaggio. Se il diverso tipo è ad esempio una immagine, molti client email saranno in grado di mostrarla direttamente, altrimenti proporranno di eseguire una applicazione specifica. Defininto nella RFC 2046, sezione 5.1.3 • Digest - Multipart/digest è un modo semplice per inviare più messaggi testuali. Il content-type di default per ogni parte è message/rfc822. Definito nella RFC 2046, sezione 5.1.5 • Alternative - Il sottotipo multipart/alternative indica che ogni parte è una versione alternativa dello stesso contenuto, ognuna in un formato diverso, come 105 MIME Posta elettronica indicato dall'header Content-Type corrispondente. I formati sono ordinati in base al grado di aderenza all'originale, con il meno aderente per primo, ed il più aderente per ultimo. In tal modo, il client può scegliere la migliore rappresentazione che è in grado di visualizzare, come l'ultima parte che è in grado di interpretare. Per questo, il formato text/plain viene posto per primo, in quanto il meno aderente possibile, consentendo ai client non in grado di interpretare i messaggi multiparte, di visualizzare comunque qualcosa di leggibile. Definito nella RFC 2046, Sezione 5.1.4 Molto spesso le email multipart/alternative sono composte di due parti, la prima di tipo text/plain, che permette la compatibilità all'indietro con i client meno evoluti, e la seconda di tipo text/html, che permette l'uso di formattazione ed hyperlinks. Anche se l'HTML sicuramente risulta più gradevole e permette una maggiore espressività, è buona norma includere sempre la versione testuale, in modo da potersi far comprendere comunque, a meno di non essere sicuri che tutti i riceventi siano in grado di visualizzare correttamente la parte HTML. • Related - Questo sottotipo è usato per indicare che le parti del messaggio non devono essere considerate individualmente, ma piuttosto come parti di uno stesso aggregato. Il messaggio consiste di una parte radice (la prima), che referenzia in-linea le altre parti, per mezzo dell'header Content-ID presente nelle diverse parti. Un uso tipico del sottotipo Related, è quello che permette di inviare per email una pagina web, completa di tutte le immagini che contiene. Definito nella RFC 2387 • Report - E' il sottotipo usato da un server SMTP per comunicare (via email) eventi di errore, e indica la presenza di dati formattati in modo che possano essere letti da parte di un mail server; tipicamente, il messaggio è suddiviso in una parte di tipo text/plain, ed una di tipo message/delivery-status. Definito nella RFC 3462 • Signed - Questo sottotipo è usato per allegare una firma digitale al messaggio, che una volta firmato, contiene due parti, un body ed una signature. Tutto il body, inclusi gli header MIME, è usato per creare la parte con la firma, prodotta in uno di diversi tipi possibili, come indicato dall'header Content-Type presente nella parte con la firma, che ad es. può riportare application/pgp-signature, o application/ x-pkcs7-signature. Definito in RFC 1847, sezione 2.1 • Encrypted - In questo caso, il messaggio è composto di due parti, dove la prima contiene le informazioni di controllo necessarie per decrittare la seconda, di tipo application/octet-stream. Definito in RFC 1847, Section 2.2 • Form Data - Come implica il nome, questo sottotipo è usato per esprimere valori immessi mediante un modulo di inserimento dati (form), tipicamente compilato mediante una pagina web. Definito in RFC 2388 Content-Disposition Illustriamo l'uso di questa intestazione, nell'ambito dei commenti al seguente capture,in cui le risposte del server SMTP sono visualizzate in corsivo, e corrispondente all'invio di una email contenente in allegato una immagine jpeg, codificata con il metodo base64. 220 smtp-out2.libero.it ESMTP Service (7.3.120) ready EHLO [192.168.120.40] 250-smtp-out2.libero.it 250-DSN 250-8BITMIME 250-PIPELINING 106 Lo strato applicativo di Internet Alessandro Falaschi 250-HELP 250 SIZE 30000000 MAIL FROM:<[email protected]> SIZE=11599 250 MAIL FROM:<[email protected]> OK RCPT TO:<[email protected]> 250 RCPT TO:<[email protected]> OK DATA 354 Start mail input; end with <CRLF>.<CRLF> Message-ID: <[email protected]> Date: Mon, 05 Mar 2007 20:30:12 +0100 From: alef <[email protected]> User-Agent: Mozilla Thunderbird 1.5.0.9 (X11/20070103) MIME-Version: 1.0 To: [email protected] Subject: prova invio con allegato Content-Type: multipart/mixed; boundary="------------060708020103050401060503" This is a multi-part message in MIME format. --------------060708020103050401060503 Content-Type: text/plain; charset=ISO-8859-15 Content-Transfer-Encoding: 7bit vediamo come parte l'allegato --------------060708020103050401060503 Content-Type: image/jpeg; name="logoalef.jpg" Content-Transfer-Encoding: base64 Content-Disposition: inline; filename="logoalef.jpg" /9j/4AAQSkZJRgABAQEASABIAAD/4QAWRXhpZgAATU0AKgAAAAgAAAAAAAD//gAXQ3JlYXRl ZCB3aXRoIFRoZSBHSU1Q/9sAQwADAgIDAgIDAwMDBAMDBAUIBQUEBAUKBwcGCAwKDAwLCgsL DQ4SEA0OEQ4LCxAWEBETFBUVFQwPFxgWFBgSFBUU/9sAQwEDBAQFBAUJBQUJFA0LDRQUFBQU FBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQU/8AAEQgAZAGk AwEhAAIRAQMRAf/EABwAAQADAQEBAQEAAAAAAAAAAAABCAkHBgUEA//EAFUQAAAEBAMEBwIH CQsMAwAAAAABAgMEBhRhBQcRCBITFwkhMVFWldIVcRkiQYGUtNQWIzJVZGWRssQYM0JEYpKh oqOz0yVDRlJTV2Nyc4KTpabB5P/EABwBAQACAgMBAAAAAAAAAAAAAAABBQQGAgMHCP/EADsR AAEDAwECCwYFBAMBAAAAAAABAhEDBBIFIZEUFUFRUlRhcZKx0QYTMTIzwSIjYoHwcqGy8RYk . ..... molte linee omesse . DaSHCT3AMlHCSHCTqIkSo4Se4TwkhIlRwk9wjhJCRKjhJDhJ7gkSo4SQ4adOwSMlHCT3Bwkh IlRwkhw06dgCVHCT3Bwk9XUEiVBNJ7gNtPcAlRw09wnhJ7hEiVINtJBwk9wkSo4ST+QDbSAl Rwkhwk9wgSo4SQ4aQJlQbaSLsAmk9wSRKjhJ17A4aevqEkyo4adOwOEnXsAjJRwk9wcJIiRk o4SQ4adOwJEqfsgiJLR6d4DivxMV3zH/2Q== --------------060708020103050401060503-. 250 <45D997EE01267F19> Mail accepted QUIT 221 smtp-out2.libero.it QUIT Notiamo che quando, come in questo caso, l'intestazione Content-Disposition (RFC 2183) assume il valore inline, il client di posta ricevente è istruito a visualizzare l'immagine subito di seguito alla prima parte testuale. Viceversa, una intestazione ContentDisposition: Attachment avrebbe determinato l'attesa di una esplicita decisione umana su cosa fare. Di seguito all'intestazione Content-Disposition possono essere presenti diversi parametri, come ad esempio il nome del file, da usare come suggerimento qualora si voglia salvare la parte su disco, ma anche eventualmente, data di creazione e di modifica, e dimensione. 107 Insiemi di caratteri Posta elettronica Insiemi di caratteri Il concetto che ad un byte corrisponda univocamente un carattere stampabile, e viceversa, non è nientaffatto corretto. Dai tempi in cui venne definito il primo insieme di caratteri per telecomunicazioni (il codice Morse del 1840), l'associazione tra codici numerici e simboli linguistici si è via via estesa, fino alla definizione di un insieme (Universal Caracter Set, o UCS, o Unicode) che possa rappresentare in modo congiunto i simboli previsti da tutte le lingue del mondo, in modo da poter essere utilizzato per i contenuti da scambiare a livello planetario, come avviene in Internet. Ma andiamo con ordine. ASCII L'insieme di caratteri più universalmente noto è il codice ASCII, standardizzato come X3.4-1986 da ANSI, come ISO 646 da ISO, e come US-ASCII da IANA, che definisce una tabella di corrispondenza tra i codici numerici 0-127 (e dunque rappresentabili con 7 bit) ed i caratteri stampabili. Questi codici rappresentano, per le prime 32 posizioni, i cosiddetti caratteri di controllo, e sono una eredità dell'epoca delle telescriventi. I restanti codici, sono invece associati ai cosidetti caratteri stampabili. Tra questi però, non compaiono ad esempio le lettere accentate, cosicchè ci fu un periodo in cui vennero definite a tale scopo, diverse mappature dei restanti 128 caratteri ancora disponibili, settando ad 1 il bit più significativo di un byte. Codepage Una Codepage è una tabella che stabilisce una corrispondenza i codici a 8 bit aventi il bit più significativo posto ad uno (che non rientrano nella tabella ASCII), con i simboli di un certo alfabeto, compresi anche i caratteri semi-grafici (mediante i quali si poteva produrre una qualche forma di disegno sullo schermo dei terminali di allora). Una codepage di largo uso in Europa, è stata la numero 850, indicata come Multilingual (Latin-1), poi sostituita con windows-1252, e quindi con la sua evoluzione ISO 8859-1, detto anche alfabeto Latin-1, definito da ISO e IEC, e mostrato nella tabella sottostante, in cui notiamo che i codici 00–1F, 7F, e 80–9F non sono assegnati a caratteri. ISO/IEC 8859-1 x0 x1 x2 x3 x4 x5 x6 x7 x8 x9 xA xB xC xD xE xF 0x 1x 2x SP ! 3x 0 1 4x @ A 5x P Q 6x ` a 7x p q 8x 9x Ax NBSP ¡ Bx ° ± Cx À Á Dx Ð Ñ unused " 2 B R b r # 3 C S c s $ 4 D T d t % 5 E U e u & 6 F V f v ' 7 G W g w ( 8 H X h x ) 9 I Y i y * : J Z j z + ; K [ k { , < L \ l | = M ] m } . > N ^ n ~ ª º Ê Ú « » Ë Û ¬ ¼ Ì Ü SHY ® / ? O _ o unused ¢ ² Â Ò £ ³ Ã Ó ¤ ´ Ä Ô ¥ µ Å Õ ¦ ¶ Æ Ö § · Ç × 108 ¨ ¸ È Ø © ¹ É Ù ¯ ½ ¾ ¿ Í Î Ï Ý Þ ß Lo strato applicativo di Internet Ex à Fx ð Alessandro Falaschi á â ã ä å æ ç è é ê ë ì í ñ ò ó ô õ ö ÷ ø ù ú û ü ý î ï þ ÿ Questa, è divenuta la codifica di default anche per le pagine distribuite dai server Web, e supporta: Africano, Basco, Catalano, Danese, Olandese, Inglese, Faeroese, Finlandese, Francese, Galizio, Tedesco, Islandese, Irlandese, Italiano, Norvegese, Portoguese, Scozzese, Spagnolo, e Svedese. Successivamente, l'ISO 8859-1 si è ancora evoluto in ISO 8859-15, o Latin-9, che sostituisce ad alcuni simboli molto poco usati (¤, ¦, ¨, ´, ¸, ¼, ½, and ¾) alcuni rari caratteri francesi e finlandesi, e il simbolo di valuta dell'euro €. In tutti gli alfabeti ISO 8859-x, la codifica dei primi 127 caratteri (non di controllo) è la stessa di quella dell'US-ASCII, quindi i files scritti con il secondo alfabeto, sono automaticamente validi anche nel primo. Unicode Nel 2004, il gruppo di lavoro di ISO/IEC responsabile della manutenzione delle codifiche di carattere ad 8 bit si è sciolto, e tutti gli sforzi si sono indirizzati verso lo Universal Character Set del consorzio Unicode, noto anche come ISO/IEC 10646, che contiene centinaia di migliaia di caratteri di quasi tutte le lingue del mondo, ognuno identificato in modo non ambiguo da un nome, e da un numero chiamato il suo Code Point. Ogni carattere Unicode viene rappresentato con la sequenza "U+", seguita da un numero esadecimale che indica il codepoint del carattere. I primi 256 caratteri, indicati come il blocco latin-script, coincidono con quelli dell'alfabeto ISO 8859-1, e ne condividono l'ordinamento, e quindi la rappresentazione numerica. Piani Sono definiti 17 piani di caratteri, ognuno comprendente 65,536 (= 216) possibili code points, ognuno dei quali può quindi in principio essere rappresentato da (16 + log217=) 21 bits. Ma come vedremo subito, sono definite della regole di trasformazione allo scopo di usare un numero variabile di bits, in modo da rendere la dimensione del testo comparabile a quella "classica" che usava 1 byte per carattere. Basic Multilingual Plane I CodePoints da U+0000 a U+FFFF costituiscono il piano 0, noto anche come Basic Multilingual Plane (BMP), contenente la maggior parte delle assegnazioni eseguite finora, come risulta dalla mappa riportata qui sotto, in cui sono evidenziati gli utilizzi per tutti i singoli blocchi da 256 codepoints. 109 Insiemi di caratteri • • • • • • • • • • • • • • • Posta elettronica Black = Latin scripts and symbols Light Blue = Linguistic scripts Blue = Other European scripts Orange = Middle Eastern and SW Asian scripts Light Orange = African scripts Green = South Asian scripts Purple = Southeast Asian scripts Red = East Asian scripts Light Red = Unified CJK Han Yellow = Canadian Aboriginal scripts Magenta = Symbols Dark Grey = Diacritics Light Grey = UTF-16 surrogates and private use Cyan = Miscellaneous characters White = Unused Unicode Transformation Format Consiste in un insieme di regole per rappresentare i caratteri definiti dalle sequenze numeriche identificate dai codepoint, in un numero variabile di byte, allo scopo di minimizzare l'occupazione di memoria necessaria, e massimizzare la compatibilità con i testi preesistenti. Lo standard de facto è indicato come UTF-8, che usa da uno a quattro bytes per rappresentare un carattere Unicode. Altri formati di trasformazione, sono l'UTF-16, anch'esso a lunghezza variabile, a multipli di 16 bit, e l'UTF-32, con lunghezza fissa pari a 32 bit. Dato che l'SMTP, come più volte ricordato, si basa su caratteri a 7 bit (a meno che il server non supporti l'estensione 8BITMIME), le trasformazioni fin qui citate, se utilizzate come valore del parametro charset della intestazione MIME Content-Type, devono subire un ulteriore processo di trasformazione, indicando un Content-Transfer-Encoding di tipo quoted-printable oppure base64, come ad esempio Content-Type: plain/text; charset=UTF-8 Content-Tranfer-Encoding: quoted-printable Onde evitare trasformazioni, è stato definito (ma sembra poco usato) un ulteriore formato di trasformazione, l'UTF-7, che rappresenta il testo Unicode come una stringa di caratteri ASCII. UTF-8 Questo formato di trasformazione per l'Unicode, definito nella RFC 3629, è del tutto consistente con l'alfabeto US-ASCII, in quanto i primi 128 codepoint da U+0000 a U+007F sono rappresentati da un unico byte, in cui i sette bit diversi da zero rappresentano esattamente gli stessi simboli dell'alfabeto ASCII. Pertanto, tutti i files di testo ASCII sono del tutto validi anche se interpretati come UTF-8. Per le lettere accentate ed i caratteri Latin-1, associati all'intervallo da U+0080 a U+07FF, corrispondente ai primi 2048 codepoint, occorrono due bytes, mentre ne occorrono tre per il resto del Basic Multilingual Plane (da U+0800 to U+FFFF); per caratteri appartenenti a qualunque altro piano di Unicode, occorrono 4 bytes. Lo schema di codifica, è il seguente: Per i primi 127 codepoints, UTF-8 utilizza un solo byte, con il bit più significativo posto a 110 Lo strato applicativo di Internet Alessandro Falaschi zero. Altrimenti, se occorre utilizzare N bytes per uno stesso codepoint, il primo byte ha il bit più significativo posto ad uno, seguito da N bits posti anche essi ad uno, seguiti da uno zero (alla N+2-esima posizione), seguiti dai bit più significativi del codepoint. I bytes successivi, hanno una coppia di bit pari a 10 nella posizione più significativa, seguiti da 6 bit presi in sequenza dalla rappresentazione binaria del codepoint. Lo schema è riassunto nella tabella seguente, in cui la lettera x indica i bit disponibili per codificare il codepoint: Char. number range | n. of | UTF-8 octet sequence (hexadecimal) | bits | (binary) ----------------------+--------------------------------------------0000 0000 - 0000 007F | 7 | 0xxxxxxx 0000 0080 - 0000 07FF | 11 | 110xxxxx 10xxxxxx 0000 0800 - 0000 FFFF | 16 | 1110xxxx 10xxxxxx 10xxxxxx 0001 0000 - 0010 FFFF | 21 | 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx La RFC 3629 riporta alcuni esempi di codifica. Proviamo qui ora ad individuare la codifica per la parola cioè. Le prime tre lettere, che appartengono all'alfabeto ascii, producono un byte ciascuna, pari a 0x63 0x69 0x6F. Alla lettera è compete un codepoint pari a U+00E8, lo stesso che risulta per ISO 8859-1, e che in binario si rappresenta come 11101000. In accordo alla seconda riga della tabella soprastante, i bit che compaiono nel codepoint vengono riscritti da destra a sinistra al posto delle x, ponendo a zero le ultime tre x più significative, ottenendo il risultato 110(00011) 10(101000), ossia 0xC3 0xA8. Pertanto, esprimendo in esadecimale il risultato finale della codifica UTF-8 di cioè, si ottiene 0x63 0x69 0x6F 0xC3 0xA8. Sicurezza Questo concetto si applica a diverse fasi dell'invio delle email, e cioè 1. 2. 3. 4. 5. 6. 7. autenticazione di colui che invia, presso il server di partenza propagazione della informazione di autenticazione verso il server di arrivo notifica al destinatario, se il mittente è autenticato o meno autenticazione del destinatario della email crittografia del contenuto del messaggio (confidenzialità) garanzia a riguardo che il messaggio ricevuto è proprio quello trasmesso (integrità) firma digitale dell'autore del messaggio (autenticità) (non repudiabilità) Ognuno dei passi elencati può essere svolto in accordo ad uno o più protocolli, che si basano su concetti e algoritmi che sono discussi in una sezione apposita. Nel seguito, illustriamo direttamente le soluzioni adottate, rimandando a quella sezione per gli approfondimenti. Autenticazione Osserviamo subito, che verificare l'autenticità di qualcuno/qualcosa, è diverso dall'accordare allo stesso soggetto il potere di fare qualcosa, ovverosia, autorizzarlo. Ma in ciò che segue, i due concetti si fondono, in quanto lo scopo dell'autenticazione è ristretto alle necessità della applicazione che la richiede, e quindi l'autenticazione implica il permesso ad operare. Ma in generale, si può dire soltanto il viceversa, e cioè che un processo di autorizzazione, sottintende un prerequisito di autenticazione. Wikipedia dedica una categoria a parte, sull'argomento della autenticazione delle email. 111 Sicurezza Posta elettronica SASL Il supporto dei meccanismi offerti da SASL può essere aggiunto, a fini di autenticazione, sia nel momento dell'invio della email (punto 1), abilitatandolo presso il server SMTP del proprio ISP, sia nel momento della ricezione della stessa (punto 4), abilitandolo presso il server POP3 o IMAP dell'ISP del destinatario. Nel primo caso, ci si avvale della estensione SMTP-AUTH, e può essere aggiunto un header che dichiara l'avvenuta autenticazione. Nel secondo, si ricade nella definizione standard del protocollo di prelievo email. Una variante di questo caso, si ha quando la lettura della email avviene mediante una interfaccia di tipo Webmail: in questo caso, l'utente viene autenticato dalla applicazione web, che poi opera in veste di agente, usando le credenziali dell'utente per autenticarsi come lui, ed accedere alle email giacenti presso un server IMAP, impersonando l'utente. SMTP-AUTH La RFC 2554 definisce una estensione di SMTP, indicata come SMTP-AUTH, che permette di ottemperare ai primi tre punti espressi nella tabella di cui sopra. Infatti, consente di condizionare l'accettazione di un nuovo messaggio, al buon esito di un passo supplementare di autenticazione, in cui il server di partenza, si accerta della reale identità del soggetto che invia il messaggio. Ciò permette agli utenti abilitati di usare questo server SMTP anche quando si collegano (in roaming) da una postazione esterna alla rete del proprio provider; al tempo stesso, però, si introduce un rischio di sicurezza, perchè un attaccante, indovinando (ad esempio, mediante un metodo di forza bruta) la password di un utente autorizzato, potrebbe poi usare il server SMTP per inoltrare SPAM. Inoltre, sebbene mediante SMTP-AUTH il server SMTP di partenza potrebbe informare quello di arrivo che il mittente è stato correttamente autenticato, in generale non esiste un rapporto di fiducia tra i due server, e così il destinatario, non sarà mai rassicurato con certezza in tal senso. Per questi motivi, l'estensione SMTP-AUTH non è molto usata. Ad ogni modo, esaminiamo come appare una sessione che fa uso di questa estensione: S: 220 smtp.example.com ESMTP server ready C: EHLO jgm.example.com S: 250-smtp.example.com S: 250 AUTH CRAM-MD5 DIGEST-MD5 C: AUTH FOOBAR S: 504 Unrecognized authentication type. C: AUTH CRAM-MD5 S: 334 PENCeUxFREJoU0NnbmhNWitOMjNGNndAZWx3b29kLmlubm9zb2Z0LmNvbT4= C: ZnJlZCA5ZTk1YWVlMDljNDBhZjJiODRhMGMyYjNiYmFlNzg2ZQ== S: 235 Authentication successful. Dopo che il server ESMTP ha annunciato la disponibilità della estensione AUTH, assieme ad una lista dei meccanismi SASL supportati (nell'esempio, CRAM-MD5 e DIGEST-MD5), il client manifesta l'intenzione di usarne uno. Il CRAM-MD5 è tra quelli supportati, ed il server risponde inviando lo challenge, che viene però (reversibilmente) codificato come base64. Il client da parte sua, dopo aver calcolato l'HMAC-MD5 concatenando lo challenge alla password dell'utente, ed avere prefisso il risultato con l'identificativo dell'utente stesso, codifica la stringa risultante in base64, e reinvia il tutto al server. La trasformazione via base64 viene prescritta dalla RFC 4422 (SASL), in modo che se challenge e/o password contengono caratteri non ASCII, per questi venga usata la codifica Unicode, con trasformazione UTF-8, e quindi poi il tutto sia rappresentato con caratteri a 7 bit, appunto mediante codifica base64. 112 Lo strato applicativo di Internet Alessandro Falaschi Confidenzialità, integrità e autenticità Gli ultimi tre punti della tabella posso essere soddisfatti appieno solo se le trasformazioni crittografiche avvengono da estremo ad estremo del collegamento, adottando uno degli standard noti come PGP ed S/MIME. Tuttavia, esiste la possibilità di effettuare ogni singolo trasferimento dal mittente al primo server SMTP, e poi tra server SMTP intermedi fino a quello di destinazione, su collegamenti resi sicuri mediante TLS, come permesso in base alla estensione STARTTLS dell'SMTP. StartTLS Il Transport Layer Security (TLS) è definito nella RFC 4346, ed offre alle applicazioni una modalità di comunicazione sicura. L'estensione STARTTLS di SMTP, descritta nella RFC 3207, permette ad un client SMTP di negoziare con il server i servizi di crittografia ottenibili via TLS, tali da impedire la lettura (da parte di un intercettatore) dei contenuti in transito, ottemperando quindi ai requisiti di confidenzialità. Questo però avviene solo di tratta in tratta, sempre che tutte le tratte lo permettano, e scelgano di usarlo: questa variabilità ne vanifica in parte gli scopi, per quanto rimane comunque un valido meccanismo di protezione nella comunicazione tra il client, ed il server SMTP del proprio provider. Inoltre, dato che l'attivazione di TLS precede l'autenticazione basata su SASL (vedi esercitazioni), l'uso di TLS si dimostra una soluzione valida, per utilizzare il meccanismo plaintext, dato che la password non viene inviata in chiaro, ma crittografata dal TLS. Ricezione dell'email Una volta che l'email è giunta presso il computer indicato dal RR MX associato al dominio di destinazione, questa può essere letta dal destinatario (la cui autenticazione stavolta è obbligatoria) sia in locale, se ha accesso fisico al computer di destinazione, sia da remoto, utilizzando una applicazione come ad es. Outlook, Thunderbird, Evolution, Opera, Eudora, KMail, Pine, Mutt (vedi confronto). In questo secondo caso, si utilizza uno di due procolli, POP3 od IMAP, entrambi di tipo client-server, le cui differenze sostanziali sono illustrate nella figura che segue. 113 Altre architetture di telecomunicazione testuale Posta elettronica POP3 Il Post Office Protocol, definito nella RFC 1939, essenzialemente è nato per permettere di scaricare sul proprio computer le email ricevute, ed anche se è prevista una opzione del tipo lascia sul server, questa è molto inefficiente nel caso di utilizzo di tipo nomadico. In origine, permetteva solo l'invio della password in chiaro, mentre le successive estensioni, permettono l'uso dei metodi SASL, e di TLS. IMAP L'Internet Message Access Protocol è definito dalla RFC 3501, ed offre diversi vantaggi rispetto all'uso del POP, come • la connessione con il server resta attiva per tutto il tempo che è aperto il programma di posta, risparmiando sui tempi di inizializzazione; • un stessa mailbox può essere acceduta in contemporanea da parte di più client differenti; • le diverse parti di un messaggio MIME possono essere scaricate (o meno) in modo indipendente le une dalle altre, permettendo ad esempio di rimandare la ricezione di un allegato voluminoso; • i messaggi lasciati sul server possono essere etichettati con attibuti tali da permettere di riconoscere, ad esempio, i messaggi letti, e quelli a cui si è risposto; • si possono creare cartelle presso il server, in cui ordinare i propri messaggi, eventualmente condividendoli con altri; • un client può richiedere al server di fare ricerche specifiche, senza dover scaricare i messaggi; • un server può annunciare la disponibilità di particolari estensioni, che possono essere usate dai client che pure le supportano; • tra queste estensioni c'è il supporto a SASL ed a TLS, che permette di aggiungere servizi di sicurezza; Webmail E' la contrazione di Webbased email, e consiste in un portale web da cui si può accedere alla propria casella di posta elettronica, in modo da poterla leggere anche da una postazione occasionale, tipicamente pubblica, permettendo inoltre di spedire posta. In questo caso, l'unico protocollo che sembrerebbe essere in gioco, è il dialogo HTTP che si svolge tra il nostro browser, ed il server web che ci mostra le pagine del portale. In effetti però, le pagine sono generate da un CGI, ossia un programma in esecuzione presso il computer che ospita il server web, e che a sua volta opera in modalità client nei confronti dei procolli email, ossia SMTP pe spedire, ed IMAP per ricevere. Altre architetture di telecomunicazione testuale Prima della definizione del World Wide Web, il mondo di Internet funzionava principalmente in formato testo, ed anche così, molte persone scoprivano un mondo del tutto nuovo, e delle formidabili potenzialità di telecomunicazione con gli altri individui in rete. Probabilmente a quei tempi, gli individui che si affacciavano ad Internet erano effettivamente i più 114 Lo strato applicativo di Internet Alessandro Falaschi progressisti ed aperti ai cambiamenti, oppure era solo un effetto generato dalla curiosità per la novità e per la cosa che sta crescendo, oppure ancora gli utenti erano accomunati dallo stesso spirito pionieristico, ma fatto sta che la comunicazione era più ricca ed entusiasta. Citiamo nel seguito, quattro architetture di comunicazione testuale, di gruppo (e non): Mailing list, Newsgroup, Internet Relay Chat, e Instant Messenger. Mailing List Le mailing list non sono altro che liste di indirizzi email, usate in congiunzione ad un meccanismo, tale che uno stesso messaggio email venga distribuito automaticamente a tutti gli indirizzi che compaiono nella lista. Questo può avvenire in modo molto banale, configurando staticamente un server SMTP in modo da associare un singolo nome di utente fittizio (detto alias, o riflettore) alla lista, facendo si che le email dirette all'alias vengano in realtà ricevute da tutti. Il problema in questo caso, è che se nella lista si verificano avvicendamenti frequenti, diventa oltremodo noioso modificare a mano la lista dei componenti. Automazione Per questo, sono sviluppati dei programmi appositi, detti anche mail robot, o mailbot, che vengono eseguiti a seguito della ricezione di una email diretta a speciali indirizzi di gestione, grazie alla modifica del file aliases, in modo da redirigire il contenuto delle email, verso lo standard input dei mailbot. Se nella email sono individuati comandi particolari, come ad esempio la richiesta di iscrizione, o il desiderio di visionare l'elenco degli iscritti, questi comandi sono eseguiti dal mailbot. Gestori Un esempio celebre di questo genere di meccanismo, è majordomo, così chiamato inspirandosi al latino major domus, ossia maestro della casa. Ora che Internet è quasi sinonimo di Web, diventa poco sensato amministrare una mailing list via email, e l'applicazione in assoluto più diffusa per la gestione delle mailing list, è Mailman, scritta in Python, e che permette una personalizzazione molto spinta sia da parte dei singoli iscritti, che da parte dell'amministratore della lista. E' il gestore di mailing list usato anche per questo corso. E' appena il caso di menzionare il fatto che tutto il lavoro preparatorio di discussione per quanto riguarda lo sviluppo degli standard Internet da parte di IETF, avviene per mezzo di mailing lists. Archiviazione E' possibile archiviare la mailing list, in modo che i messaggi che vi transitano siano accessibili tramite interfaccia web. Ciò può essere realizzato o dallo stesso programma che gestisce la ML (come in effetti fa Mailman), oppure utilizzando un servizio di terze parti. Se poi l'interfaccia web all'archivio permette anche a chi legge, di scrivere, allora si ottiene un risultato simile a quello del Newsgroup, a partecipazione aperta. Esempi di questo approccio sono Gmane, MARC, Nabble, Google Groups, Yahoo Groups. Netiquette E' una parola derivata dalla contrazione del vocabolo inglese net (rete) e quello di lingua francese étiquette (buona educazione), e descrive un insieme di regole di buona condotta, 115 Altre architetture di telecomunicazione testuale Posta elettronica che dovrebbero disciplinare il comportamento di un utente di Internet nel rapportarsi agli altri, mediante meccanismi quali newsgroup, mailing list, forum, e-mail, o in genere, mediante qualunque tipo di comunicazione scritta. In questo contesto, assume significato l'uso degli emoticon, (compresi quelli in stile asiatico) per veicolare in modo esplicito il proprio stato d'animo, e disambiguare le circostanze in cui il lettore può equivocare che una frase scherzosa, possa invece essere ritenuta offensiva. Newsgroup Anzichè discutere mediante lo strumento delle mailing list, permettendo solo agli aderenti di partecipare, perché non permettere anche a terze persone di visionare ciò che è descritto dagli altri, e prendere parte alla discussione? Probabilmente per soddisfare questo desiderio, fu definito il protocollo NTTP (Network News Transfer Protocol, RFC 3977, che aggiorna la RFC 977), che ha dato vita al servizio indicato come Usenet, a significare il possesso della rete da parte degli utenti: in effetti, partecipare a Usenet è l'equivalente virtuale dello scendere in piazza e discutere liberamente con gli altri dei fatti e dei temi che si desidera, e ciò rappresenta inequivocabilmente un elemento di democrazia. Tecnicamente, i diversi server NNTP comunicano tra loro secondo il principio di mantenere sincronizzato l'insieme dei messaggi che ogni server riceve da parte degli utenti che vi si connettono, con quello degli altri server NNTP. Presso i server sono definiti i diversi argomenti di discussione, detti newsgroup, organizzati in gerarchie che categorizzano gli argomenti stessi in sotto-argomenti, e sotto-sotto argomenti. Considerando che Usenet ha una propagazione mondiale, e che ogni popolo ha pur sempre il diritto di discutere nella propria lingua, possiamo restringere il campo alla gerarchia in lingua italiana. Per partecipare ad Usenet, la maggior parte dei lettori email offre la possibilità di definire un nuovo account news (anzichè email), configurando il newsserver a cui ci si desidera collegare, che in generale, corrisponde a quello ospitato presso il proprio provider. Infatti, come avviene per i server SMTP, anche i server NNTP negano l'accesso ai client esterni alla propria rete. In alternativa, esistono svariate iniziative che, in analogia al servizio di webmail, offrono una interfaccia web ai newsgroup, come a esempio Google, Mailgate, nntp.it. Anche se l'utilizzo dei newsgroup mediante client personale è più completo, e privo della pubblicità inserita dal portale, l'accesso web ha il vantaggio di non dipendere dalla conoscenza del proprio server NNTP, e può aiutare a farsi una idea. BBS I Bullettin Board Services si sono sviluppati parallelamente ai Newsgruop, e sono ormai quasi del tutto tecnologicamente superati, ma rappresentato tuttora un ottimo esempio di comunicazione assolutamente autogestita. Si tratta infatti, ancora una volta, di una rete di computer che mantengono sincronizzate le rispettive basi di messaggi, ma i collegamenti tra computer non si avvalevano della rete Internet, ma del modem su rete telefonica commutata, rimuovendo qualunque forma di dipendenza da fornitori di connettività dati. Attualmente, molte BBS oltre a fornire ai propri utenti un accesso via modem, sono altresì accessibili (per ironia) via Internet, che spesso usano anche per sincronizzarsi con gli altri nodi. Sebbene diverse BBS si possano accordare per mettersi in rete tra di loro in modo del tutto autonomo, esiste una rete mondiale di BBS che cooperano a mantenere viva una distribuzione globale alternativa, chiamata FidoNet. 116 Lo strato applicativo di Internet Alessandro Falaschi Internet Relay Chat Le chat offerte da siti web e da applicazioni di Instant Messaging sono tutte evoluzioni delle chat con cui chi si collegava via modem ad una BBS, poteva dialogare con il gestore (sysop) della BBS stessa. Queste ultime forme di comunicazione, hanno a loro volta tratto ispirazione dal comando talk, in cui lo schermo del terminale viene suddiviso in due zone, ed i caratteri immessi dall'interlocutore, appaiono uno alla volta. L'IRC (Internet Relay Chat) è la standardizzazione Internet dello stesso concetto, ed è basato su di un network di server chat, interconnessi a formare uno spanning tree, in modo da propagare in modo efficiente i messaggi che ogni server riceve dagli utenti connessi allo stesso. La formalizzazione IETF del protocollo di IRC è la RFC 1459, a cui sono da affiancare le successive evoluzioni, anche se le diverse implementazioni del server IRC tendono sempre a divergere un pò. Ad esempio, fin dall'inizio non si è previsto alcun supporto per i caratteri non-ASCII, con il risultato che possono coesistere clients che usano contemporaneamente codifiche diverse ed incompatibili (ad es. ISO 8859-1 e UTF-8). La comunicazione su IRC avviene nell'ambito dei cosiddetti canali, che possono essere locali ad uno stesso server, oppure propagati a tutti i server che fanno parte delle rete IRC. Di reti IRC, al mondo ne esistono migliaia, e quella italiana più diffusa, è Azzurra, mentre quella con più utenti è IRCnet, a distribuzione mondiale; infine, Freenode offre principalmente supporto ai progetti OpenSource. Ci si collega, con una vasta scelta di client. Instant Messaging Le applicazioni di Messaggistica istantanea hanno guadagnato una grande popolarità in virtù della visibilità di cui godono i service provider a cui fanno riferimento i rispettivi programmi client, ed i cui maggiori esponenti sono • AIM - America On Line Instant Messenger - interopera con ICQ • ICQ - gioco di parole con I seek You (ti sto cercando) - Creato da Mirabilis, venne acquista da AOL, poi fusasi con Time Warner • Windows Live Messenger - indissolubilmente legato a Microsoft, è l'evoluzione del Windows Messenger • Yahoo! Messenger - interopera con MSN • Gtalk - Contrazione di Google talk ed integrato con Gmail, adotta standard aperti come XMPP • C6 - l'unico pienamente Italiano - interopera con XMPP • QQ - più di 160 millioni di persone lo usano in Cina Ognuna di queste applicazioni, consta di una popolazione di utenti, che tutti assieme individuano una community di utilizzatori, la cui numerosità stimata complessiva, ammonta a diverse centinaia di milioni di individui. 117 Altre architetture di telecomunicazione testuale Posta elettronica Topologia e interlavoro La maggior parte di queste applicazioni, opera con una modalità client-server, in cui ogni client, fa capo ad unico server centralizzato, che implementa il protocollo di comunicazione con tutti i client. Inoltre, la politica più diffusa, è quella di ostacolare lo sviluppo di prodotti di terze parti che si possano connettere a queste reti, tentando di fidelizzare i propri utenti, all'uso del proprio client, tramite il quale si possono più facilmente introdurre estensioni e nuove caratteristiche. Solo da poco, alcuni di questi provider stanno seguendo un approccio di "apertura" reciproca (come Yahoo e MSN, oppure AIM e ICQ), comunque sempre dettato da motivazioni commerciali. Client multiprotocollo Cionostante, vi sono applicazioni tipicamente dell'Open Source, come ad esempio • Trillian - shareware ma dal sorgente chiuso • Pidgin - precedentemente noto come Gaim, funziona anche su piattaforme non-Microsoft • Kopete - permette l'uso di una webcam con gli utenti MSN e Yahoo • Miranda - solo per Win • Adium - solo per Mac che a seguito di una operazione di reverse collegamento contemporaneo su diverse di queste reti. engineering, permettono il Standard aperti In contrapposizione alle reti proprietarie sopra descritte, i servizi di messaggistica possono essere realizzati anche in accordo a specifiche pubbliche. Jabber - XMPP Lo IETF ha pubblicato come RFC 3920, 21, 22 e 23, le specifiche dell'Extensible Messaging and Presence Protocol (XMPP), alla base del funzionamento della piattaforma Jabber, e che offre una gamma veramente molto vasta di estensioni, sia stabili, che in via di sviluppo, che allo stadio di proposte. 118 Lo strato applicativo di Internet Alessandro Falaschi Architettura La rete Jabber non prevede un unico server centralizzato, ma ogni utente è individuato da una URI del tipo di quelle previste per l'email, ed i messaggi che gli sono diretti, transitano per il server associato alla parte dominio della proprio indirizzo, proprio come avviene per le e-mail. Interoperabilità La architettura XMPP prevede l'esistenza di Gateway che interfacciano la rete Jabber con quelle basate sui diversi protocolli di Messagging, come ad es. MSN o ICQ, permettendo ad un client Jabber, di comunicare con tutti gli altri. Una volta che un client Jabber si connette ad un server che implementa il gateway verso la rete di messaggistica desiderata, deve comunicare al server jabber l'identità e le credenziali con le quali può accedere all'altra rete; a quel punto, il server Jabber si connetterà alla diversa rete impersonando l'identità del client, comportandosi come un proxy, e convertendo le primitive del protocollo di un lato del gateway, in quelle del protocollo dell'altro versante. SIMPLE Il WG IETF SIMPLE (SIP for Instant Messaging and Presence Leveraging Extensions) definisce come il protocollo SIP, progettato per il VoIP, possa offrire il supporto alle funzioni di Presenza e Messaggistica Istantanea, a loro volta definite nella RFC 2778. In particolare, nelle RFC 3265 e 3856, viene descritto il supporto a queste funzioni, mentre in un recente Draft, si riassume l'intreccio delle specifiche di cui tenere conto. Piuttosto che addentrarci in dettagli, esponiamo i concetti base coinvolti. Presence La funzione di Presenza è definibile come la gestione di una informazione di stato, relativa ad una entità definibile come Presentity, che viene resa nota ad un insieme di altre entità, definibili come Watchers. +---------------------------+ | PRESENCE SERVICE | | | +---------------------------+ ^ | | | | v +------------+ +------------+ | PRESENTITY | | WATCHER | +------------+ +------------+ Tipicamente una Presentity, di propria volontà, mentiene informato il gestore del servizio di 119 Altre architetture di telecomunicazione testuale Posta elettronica presenza a riguardo dei propri cambiamenti di stato, mentre un Watcher può interrogare periodicamente il servizio di presenza, oppure (preferibilmente) sottoscrivere (Subscribe) il servizio, e ricevere delle notifiche (Notify) ogni qualvolta l'informazione sia cambiata. La derivazione di queste definizioni, a partire dal tradizionale comportamento di una applicazione di messaggistica, in grado di mostrarci lo stato dei nostri contatti, è più che evidente. La possibilità di trasmettere a distanza, oltre ai propri scritti, oltra alla propria voce e/o immagine, anche la propria propensione alla comunicazione, apre un nuovo modo di intendere le telecomunicazioni, che meriterebbe senz'altro una approfondita analisi psicosociale. Rimanendo invece sul piano tecnico, notiamo come la formalizzazione delle funzioni Presenza e Messaggistica Istantanea espressa nel contesto della RFC 2778, ha consentito definire nella RFC 3859 un Common Profile for Presence (CPP), in modo tale che applicazioni che aderiscono al CPP, abbiano buone possibilità di poter interoperare, scambiarsi questo tipo di informazione. di di le e Open Mobile Alliance Effettivamente, il lavoro sviluppato del WG SIMPLE aderisce al CPP, così come vi aderiscono le specifiche emesse dall'Open Mobile Alliance (OPA), che definisce standard aperti per l'industria della telefonia mobile. Vi aderiscono tutti i maggiori produttori di telefonini e loro derivati, operatori mobili, e produttori di software, ed intrattiene relazioni con le altre organizzazioni di standardizzazione, come 3GPP, 3GPP2, IETF, W3C. E cosa dire, allora, di Open Handset Alliance? (video) Funzioni Telemediali Senza dover necessariamente ricorrere a tutti i costi al telefonino, le stesse applicazioni di messaging supportano funzioni audio-video del tutto equivalenti, se non superiori, a quelle offerte dagli operatori telefonici, ad un costo assai inferiore, al punto che i telefonini stessi, sono in procinto di saltare sul carro dell'accesso WiFi ad Internet. Infatti, oltre alle applicazioni storicamente ed esplicitamente nate per il Voice over IP (VoIP) come H.323, SIP o Skype, anche i messenger nati per il solo supporto alla comunicazione testuale, si stanno via dotando di funzionalità di comunicazione audio e video, al punto da coniare per questi, il termine di Voice over Instant Messaging (VoIM), mentre qualcuno, prova ad usare il termine di CoIP-Communication over IP (video), a rimarcare la funzione di collante tra testo, voce e dati, svolta dalle applicazioni di messaggistica. Mentre MSN, Yahoo, AIM e ICQ, al solito, adottano soluzioni proprietarie, Gtalk adotta libjingle, una estensione di XMPP. Inoltre, è da segnalare l'iniziativa di Gtalk2VoIP, che offre un gateway per le chiamate audio tra le reti MSN, Yahoo, AIM, ICQ, Gtalk, SIP e PSTN. Realizzato con da Alessandro Falaschi - 120 Lo strato applicativo di Internet Alessandro Falaschi 121 Lo strato applicativo di Internet Dispositivi e Meccanismi di Sicurezza Questa sezione fornisce una panoramica sugli aspetti di sicurezza che intervengono nelle architetture di telecomunicazione Internet, senza per questo affrontare tutti gli aspetti della sicurezza informatica. • Quadro di riferimento ◦ ◦ ◦ ◦ Attacchi Servizi Meccanismi Localizzazione • Crittografia convenzionale ◦ Chiavi di sessione • Autenticazione del messaggio ◦ ◦ ◦ ◦ ◦ ◦ Message Authentication Code Funzioni Hash Hash e segreto condiviso Hash e crittografia convenzionale Hash e crittografia a chiave pubblica HMAC • Crittografia a chiave pubblica ◦ Utilizzi della crittografia asimmetrica ▪ Confidenzialità ▪ Autenticazione ▪ Scambio di chiavi ◦ Algoritmi a chiave pubblica ▪ RSA ▪ Diffie-Hellman ▪ ▪ ▪ ▪ ▪ ▪ Generazione delle chiavi Il segreto condiviso Lo scambio di Diffie-Hellman La chiave di sessione Attacco MITM Autenticazione dello scambio ▪ DSS • Gestione delle chiavi ◦ Certificati digitali ◦ Distribuzione di chiavi di sessione mediante crittografia asimmetrica • Public Key Infrastructure - PKI ◦ X.509 ▪ Catena delle Autorità di Certificazione Lo strato applicativo di Internet ▪ ▪ ▪ ▪ ▪ ▪ Alessandro Falaschi Formato Fingerprint Richiesta e rilascio dei certificati Codifica e consegna di un certificato Revoca dei certificati Certificati autofirmati ◦ Web of trust • Lightweight Directory Access Protocol - LDAP • Strati di sicurezza ◦ IPSEC ◦ TLS ▪ Record Protocol ▪ Handshake Protocol • API di sicurezza ◦ SASL ▪ CRAM-MD5 ◦ PGP e GPG ◦ S/MIME • Riferimenti Quadro di riferimento Prima di tutto, forniamo alcune definizioni dei concetti inerenti gli aspetti di sicurezza, come i tipi di attacchi alla sicurezza, i servizi che la sicurezza può offrire, i meccanismi con cui si attua, e la localizzazione di questi servizi. Attacchi Il normale flusso dell'informazione, da sorgente a destinazione, può essere alterato fraudolentemente, secondo uno dei quattro casi illustrati in figura: • interruzione: quando si rende inutilizzabile il mezzo di comunicazione, ed il messaggio non raggiunge più il destinatario; è un attacco alla disponibilità; • intercettazione: quando pur raggiungendo il destinatario, il messaggio è spiato all'insaputa delle parti in comunicazione. E' un attacco alla confidenzialità; 123 Quadro di riferimento Dispositivi e Meccanismi di Sicurezza • modifica: un "uomo nel mezzo" (man in the middle, o MITM) si appropria del messaggio e lo altera, che così raggiunge il destinatario in forma distorta. E' un attacco alla integrità; • contraffazione: il destinatario riceve un messaggio che non è stato prodotto dal mittente che dichiara. E' un attacco alla autenticità. Questi attacchi possono essere condotti in forma passiva, come nel caso della intercettazione, o della analisi del traffico, oppure in forma attiva, secondo le categorie • impersonificazione (masquerade): qualcuno o qualcosa, si spaccia per qualcun altro, ed esempio, esibendo credenziali intercettate, e ri-utilizzate successivamente; • replica (replay): dopo una intercettazione passiva, il messaggio viene re-inviato per ottenere un effetto indesiderato; • modifica - è il caso tipico del man in the middle; • negazione del servizio (Denial of Service, o DoS): si ostacola od impedisce il normale utilizzo del sistema/servizio di comunicazione. Spesso, questo si ottiene sovraccaricando la risorsa, ad esempio inviando ad un server un numero esagerato di richieste. Servizi Per prevenire e/o ostacolare gli attacchi di sicurezza, vengono messi in opera un serie di espedienti, che realizzano una o più delle seguenti funzioni, o servizi di sicurezza: • confidenzialità: è la protezione contro gli attacchi passivi come le intercettazioni, e viene realizzato mediante tecniche di crittografia dei singoli messaggi in transito, oppure di tutto il traffico tra nodi di rete; • autenticazione: è la protezione contro l'impersonificazione, ed in linea generale, garantisce alla destinazione di un messaggio, che la sorgente sia veramente quella che dice di essere. Può anche servire ad accertare l'identità del destinatario (come quando ad esempio, ci colleghiamo ad un sito di commercio elettronico), garantendo di trasmettere l'informazione esattamente al soggetto a cui pensiamo di rivolgerci; • integrità: protegge contro attacchi di intercettazione, modifica e replay, e come per la confidenzialità, si realizza crittografando il singolo messaggio, o l'intero traffico. • non repudiabilità: impedisce al mittente od al destinatario, di negare la trasmissione o la ricezione di un messaggio. Ha molti aspetti in comune con l'autenticazione • controllo di accesso: nel caso in cui l'identità del soggetto che intende usufruire di una risorsa sia accertata mediante autenticazione, si può procedere ad un passo successivo, in cui si verificano i diritti di accesso che tale individuo vanta nei confronti della risorsa in oggetto. 124 Lo strato applicativo di Internet Alessandro Falaschi Meccanismi Non esiste un singolo meccanismo, od algoritmo, capace di fornire in un colpo solo tutti i servizi di sicurezza elencati, ma si tratta sempre e comunque di ricorrere a funzioni sviluppate nel campo della crittografia, e utilizzate in modo da cooperare alla realizzazione di un modello di architettura di sicurezza, molto sommariamente descritta dalla figura a lato. Una terza parte fidata, si occupa di distribuire alle parti principali in comunicazione una informazione segreta, usata per operare una trasformazione di sicurezza, tale proteggere il canale di comunicazione dall'intervento di un opponente, intenzionato ad portare un attacco. Localizzazione Le trasformazioni di sicurezza possono essere localizzate sia agli estremi (ellissi nere) delle due parti in comunicazione, sia presso gli estremi dei singoli collegamenti (ellissi grigie) che vengono attraversati dalla comunicazione. Il primo caso tipicamente avviene ad opera dello strato applicativo in esecuzione presso le parti in comunicazione, come ad esempio nel caso di TLS, S/MIME, PGP, mentre il secondo è quanto avviene in base alla collezione di protocolli IPSec. 125 Crittografia convenzionale Dispositivi e Meccanismi di Sicurezza Crittografia convenzionale Si basa sull'esistenza di un segreto condiviso, noto alle due parti in comunicazione, e che funziona come seme per una algoritmo crittografico, tale da rendere molto difficile risalire alla chiave, anche venendo in possesso del testo in chiaro, oltre che della sua versione crittografata. Gli algoritmi crittografici in uso sono denominati: • DES - Data Encryption Standard: definito nel 1977 dal National Institute of Standards and Technology (brevemente NIST, un'agenzia del governo USA), e denominato anche Data Encryption Algoritm (DEA). Opera su blocchi di testo di 64 bit, elaborati mediante una chiave di 56 bit. Con il progredire della potenza di calcolo, nel 1998, Electronic Frontier Foundation (EFF) annunciò di essere riuscita a "aprire" il codice, dimostrando di essere stata in grado di risalire alla chiave, a partire dalla conoscenza di plaintext e ciphertext. Ma l'uso di chiavi più lunghe, come ad esempio di 128 bit, rende l'algoritmo ancora molto robusto. • Triple DEA (TDEA): opera applicando per tre volte l'algoritmo DES, con tre chiavi distinte, portando così a 56*3=168 bit la lunghezza della chiave, rendendo il metodo virtualmente inespugnabile. • Advanced Encryption Standard (AES): è il risultato di un bando lanciato da NIST nel 1997, per definire un successore del DES. Opera su blocchi di testo di 128 bit, con una chiave a scelta tra 128, 192 o 256 bit, in funzione del livello di protezione desiderato, e si basa su sequenze di operazioni più facilmente realizzabili in software rispetto al DES. • International Data Encryption Algorithm (IDEA): opera con una chiave di 128 bit, è coperto da brevetto, ma ne esiste una versione liberamente utilizzabile per fini non commerciali. E' stato descritto per la prima volta nel 1991 come una alternativa a DES, e non essendo ancora stato "aperto", è ritenuto molto affidabile. • Blowfish opera su blocchi di 64 bit mediante una chiave di lunghezza variabile, da 32 a 448 bit. E' molto efficiente sia da un punto di vista computazionale che di occupazione di memoria, e molto robusto. Per questi motivi, e per l'assenza di brevetti, è largamente impiegato. • RC5 è brevettato, e definito nella RFC 2040. Opera su blocchi di lunghezza variabile, con chiavi lunghe fino a 2048 bits. 126 Lo strato applicativo di Internet Alessandro Falaschi Chiavi di sessione Nella crittografia convenzionale, i rischi di sicurezza aumentano, quanto più materiale viene crittografato usando sempre la medesima chiave, rendendo sempre meno complicato per un intercettatore, tentare di scoprire la chiave usata. La soluzione più usata contro questo problema, passa dall'utilizzo di una chiave di sessione, che ha una validità che si protrae per la sola durata di una sessione, e viene poi distrutta, in modo da non dare il tempo ad un intercettatore, di forzare il codice. Nella figura che segue è illustrato uno schema di principio, in cui si postula l'esistenza di una entità di distribuzione, che viene contattata dall'entità che intende generare il messaggio, e che fornisce la chiave di sessione, trasmettendola in forma crittografata mediante una chiave permanente, nota sia al distributore che alle altre entità. La stessa chiave di sessione, è fornita anche al destinatario, sempre in forma crittografata, e lo mette in grado di decrittare il messaggio in arrivo. La chiave di sessione non deve essere necessariamente distribuita da una entità separata, ma può anche essere generata da una delle due parti in comunicazione, come vedremo appresso. Autenticazione del messaggio Mentre la crittografia in generale, protegge dagli attacchi di tipo passivo (ad es, l'intercettazione), la garanzia che il messaggio ricevuto è autentico, sia nel contenuto (integrità) che nel mittente (autenticità), ricade nei compiti delle procedure di autenticazione. In linea di principio, impiegando tecniche di crittografia convenzionale, in cui mittente e destinatario condividono la stessa chiave segreta (una per ogni coppia di soggetti), la corretta decrittazione di un messaggio ricevuto, oltre a garantirne l'integrità, garantirebbe anche a riguardo della sua provenienza, e quindi, anche l'integrità potrebbe essere garantita mediante la stesa tecnica usata per la confidenzialità. Viceversa, sussistono dei buoni motivi per mantenere il messaggio in chiaro, tralasciando quindi la confidenzialità, e ottemperare ai requisiti di integrità e autenticità mediante la spedizione, assieme al messaggio, di un Message Authentication Code (MAC). Come buoni motivi, citiamo i casi in cui 127 Autenticazione del messaggio Dispositivi e Meccanismi di Sicurezza • il messaggio ha destinazioni multiple, e solo una entità ha il compito di eseguire l'autenticazione • si risparmia la complessità della crittografia dell'intero messaggio • il messaggio consiste in dati da fornire ad un programma, e lo si può memorizzare in chiaro, senza doverlo decrittare ogni volta, mantenendo la possibilità di verificarne l'autenticità • il messaggio è in realtà un programma, che può essere eseguito senza doverlo prima decrittare Message Authentication Code Un algoritmo MAC accetta in ingresso un messaggio da autenticare, ed una chiave segreta K, e produce in uscita una etichetta (detta appunto, MAC) che dipende dai due ingressi, e che viene allegata al messaggio. Il ricevitore del messaggio, se conosce la stessa chiave segreta, può effettuare lo stesso calcolo a partire dal messaggio ricevuto, e se il MAC risulta identico a quello ricevuto, vuol dire che il mittente è in possesso della stessa chiave usata in ricezione, e che il messaggio ricevuto non è stato modificato; per quest'ultimo motivo, l'etichetta risultante viene anche chiamata Message Integrity Code (MIC), anche se questo termine si riferisce ad un controllo svolto in modo lievemente diverso. Non è necessario che il processo descritto sia reversibile. In particolare, l'algoritmo MAC può essere basato su di una delle funzioni crittografiche convenzionali elencate precedentemente. 128 Lo strato applicativo di Internet Alessandro Falaschi Funzioni Hash Si tratta di una trasformazione in grado di generare, in modo semplice, una breve etichetta identificativa a partire da un generico messaggio, anche molto più lungo. Un suo utilizzo è attinente alla costruzione di tabelle hash per l'accesso rapido ad una raccolta di dati, ma quello è un altro discorso. Per i nostri fini, ci basta dire che una funzione Hash produce una uscita H(M) di lunghezza fissa a partire da una stringa M di lunghezza variabile di ingresso, ed il valore di uscita è chiamato valore di hash, checksum crittografico o message digest. Le proprietà di questa funzione si possono riassumere in • • • • • funziona per messaggi di qualunque dimensione il risultato è di lunghezza fissa è semplice da calcolare la sua inversione è computazionalmente infattibile individuare un diverso ingresso che produce la stessa uscita di un altro è computazionalmente infattibile Alcune funzioni hash usate nelle applicazioni di sicurezza sono • Secure Hash Function (SHA-1): pubblicata nel 1995, elabora l'ingresso in blocchi di 512 bit e produce un risultato di 160 bit, ovvero 20 byte, o 40 caratteri esadecimali. Ha la proprietà che ogni bit di uscita, dipende da ogni bit di ingresso. • Message Digest Algorithm (MD5): sviluppato da Ronald Rivest nel 1991. Nel 1995 sono state scoperte alcune debolezze, delle altre nel 2004, ed altre ancora nel 2007, tanto da metterne in dubbio la validità. Elabora l'ingresso in blocchi di 512 bit e produce un risultato di 128 bit, tipicamente espressi come una stringa di 32 caratteri esadecimali. E' descritto nella RFC 1321. Dato che diverse stringhe di ingresso, possono produrre lo stesso digest, la trasformazione non è reversible. Inoltre, a differenza del MAC, la funzione hash non usa una chiave segreta. Per questo, una funzione hash, da sola, si presta ad applicazioni del tipo • memorizzazione di una password non in chiaro - ad esempio nei sistemi Unix, il file /etc/password (o meglio, /etc/shadow) contiene il risultato della applicazione di una funzione hash alle password associate agli utenti del sistema. Quando un utente immette la sua password, su questa viene applicata la stessa funzione hash, ed il risultato è confrontato con la versione memorizzata. In questo modo, anche qualora il file di password venisse violato da un attaccante, sarebbe comunque impossibile risalire alla conoscenza delle password originarie - se non conducendo un attacco a forza bruta; • generazione di un checksum in grado di garantire l'integrità di un messaggio trasmesso, come ad esempio per verificare se si siano verificati o meno errori di trasmissione, ma senza offrire supporto di sicurezza, in quanto un attaccante che sostituisse al messaggio originario il proprio, potrebbe senza difficoltà generare un hash corretto per il messaggio contraffatto. Per il motivo illustrato nel secondo esempio, il calcolo di un MAC può far uso di una funzione hash, purchè la si associ all'uso di un segreto condiviso, alla crittografia convenzionale, od alla crittografia a chiave pubblica, secondo uno dei seguenti schemi. 129 Autenticazione del messaggio Dispositivi e Meccanismi di Sicurezza Hash e segreto condiviso Il messaggio da autenticare, viene concatenato con un segreto condiviso tra le due parti in comunicazione, e l'hash calcolato su entrambi. Il segreto non viene trasmesso, mentre al messaggio, si concatena l'hash ottenuto come descritto. Il ricevitore a sua volta, concatena lo stesso segreto, con la sola parte di messaggio ricevuto, e confronta il risultato, con Il MAC ricevuto. Il segreto usato viene a volte indicato con il termine di sale (salt), o seme (seed), o vettore di inizializzazione (IV). Il vantaggio principale di questa tecnica, è che non viene usata nessuna funzione crittografica, riducendo il carico computazionale dei dispositivi. Inoltre, le funzioni crittografiche sono spesso ottimizzate per lunghi testi, mentre questa soluzione di presta bene anche nel caso di messaggi particolarmente brevi. Una variazione di questa tecnica è nota con il nome di HMAC. Hash e crittografia convenzionale In questo caso, il valore dell'hash viene calcolato a partire dal solo messaggio da trasmettere, e da questo viene generato un MAC mediante crittografia convenzionale, usando una chiave nota anche al destinatario. Il MAC viene quindi concatenato al messaggio, e trasmesso. Il ricevente, usando la stessa chiave, decritta il MAC, in modo da poterlo confrontare con l'hash che a sua volta ha calcolato, a partire dalla conoscenza del messaggio. Hash e crittografia a chiave pubblica Quest'ultimo caso è simile al precedente, tranne per l'uso della crittografia asimmetica, in cui il mittente usa la propria chiave privata per crittografare l'hash e generare il MAC, e chi 130 Lo strato applicativo di Internet Alessandro Falaschi riceve usa la chiave pubblica del mittente per effettuare la decrittazione. HMAC Il metodo di generare un MAC a partire da una funzione hash e da un segreto condiviso, ha dato origine ad una tecnica particolare di autenticazione, detta HMAC, in cui una funzione hash (SHA-1 o MD5) è usata congiuntamente ad una chiave (la cui conoscenza è nota al destinatario), per generare una etichetta MAC. Se la stessa operazione eseguita a destinazione, fornisce lo stesso risultato, il destinatario ritiene che il messaggio sia autentico, perchè ha verificato la conoscenza della chiave da parte del mittente. Il metodo non fa uso di funzioni crittografiche, e quindi presenta un basso carico computazionale. La tecnica è utilizzata in IPsec, in TLS, e in tecniche di autenticazione client-server come ad esempio CRAM-MD5. Crittografia a chiave pubblica Questa metodologia, detta anche crittografia asimmetrica, e proposta da Diffie e Hellman nel 1976, è stato il primo vero rivoluzionario progresso dopo millenni. Ora non occorre più che le due parti si trovino d'accordo nell'usare una medesima chiave per crittografare, e recuperare la versione in chiaro del testo. Infatti: • ogni entità (A, B, C...) può generare con facilità una coppia di chiavi, di cui una privata (XA) che viene mantenuta segreta, ed una pubblica (YA) che viene comunicata a tutti i propri corrispondenti; • esistono due coppie di algoritmi crittografici semplici, che permettono rispettivamente ◦ di utilizzare una chiave pubblica per recuperare un testo crittografato con una chiave privata, come nel caso della autenticazione, ovvero ◦ di usare una chiave privata per recuperare un testo crittografato con una chiave pubblica, come nel caso del recupero di un messaggio confidenziale; • risulta essere computazionalmente infattibile tentare di risalire alla chiave segreta, a partire dalla conoscenza di quella pubblica; • risulta essere computazionalmente infattibile tentare di risalire al messaggio in chiaro, a partire dalla conoscenza di quello crittografato, e della chiave pubblica. L'esistenza di due diverse chiavi, di cui una pubblica, evita il problema della crittografia convenzionale, di dover trovare il modo di comunicare anche il segreto condiviso. 131 Crittografia a chiave pubblica Dispositivi e Meccanismi di Sicurezza Utilizzi della crittografia asimmetrica Illustriamo ora come un sistema di crittografia a chiave pubblica possa essere usato per offrire un servizio di confidenzialità, di autenticazione, o di scambio di chiavi. Confidenzialità Una simpatica analogia, è quella di pensare alla chiave pubblica come ad una specie di lucchetto, di cui solo il proprietario possiede la chiave (privata). Quindi, se Bob intende spedire un messaggio ad Alice, prima di tutto, si fa spedire da Alice un suo lucchetto aperto. Quindi, usa la chiave pubblica (il lucchetto) di Alice, e con quella crittografa (sigilla) il messaggio, che solo Alice, con la sua chiave privata, potrà aprire. Al contrario, il simbolismo del portachiavi (keyring) mostrato sotto, è usato per indicare un meccanismo con cui vengono conservate e recuperate le chiavi pubbliche dei nostri corrispondenti, da usare per crittografare i messaggi che solo il destinatario designato può decifrare, usando la propria chiave privata. Autenticazione Questo è il caso in cui Bob vuole che chiunque sia in grado di verificare che un suo messaggio è autentico, e prodotto proprio da lui. Stavolta quindi, Bob esegue l'algoritmo crittografico usando la propria chiave privata, di cui è l'unico possessore, mentre chiunque (e quindi anche Alice) potrà usare la chiave pubblica di Bob, per aprire il messaggio. Dato che, se il messaggio si apre con la chiave pubblica di Bob, vuol dire che è stato chiuso usando la rispettiva chiave privata (sempre di Bob), e che Bob ne è l'unico possessore, allora il messaggio, è sicuramente di Bob. 132 Lo strato applicativo di Internet Alessandro Falaschi Spesso, si preferisce non crittografare l'intero messaggio, ma solamente un hash dello stesso, ottenendo un MAC (chiamato in questo caso firma digitale) che ognuno potrà verificare essere stato generato da Bob. Scambio di chiavi Anche se la crittografia a chiave pubblica può essere usata a scopi di confidenzialità, spesso, come abbiamo visto prima, può essere preferibile generare una chiave di sessione, con validità molto limitata nel tempo. Questo permette ad esempio di usare la chiave di sessione nell'ambito di un algoritmo crittografico convenzionale, eventualmente più debole, o più veloce, e crittografare con quello il messaggio; la chiave di sessione, ad esempio, viene trasmessa assieme al messaggio, crittografata con una algoritmo a chiave pubblica, più robusto e/o computazionalmente più oneroso. Un diverso aspetto, riguarda la modalità con cui vengono generate le coppie di chiavi pubblica e privata, che spesso vengono calcolate a partire da un numero casuale. Per alcuni algoritmi, (ad esempio, Diffie-Hellman) se entrambe le parti conoscono, oltre alla chiave pubblica del corrispondente, anche il numero casuale di partenza, è possibile che entrambe le parti calcolino (indipendentemente) anche il medesimo segreto condiviso, da usare poi nell'ambito di uno scambio basato sulla crittografia convenzionale, evitando così il problema della comunicazione del segreto condiviso. Algoritmi a chiave pubblica La seguente tabella indica gli usi più idonei per quattro algoritmi di crittografia a chiave pubblica algoritmo confidenzialità firma digitale scambio chiavi RSA si si si Diffie-Hellman no no si DSS no si no Curve Ellittiche si si si 133 Crittografia a chiave pubblica Dispositivi e Meccanismi di Sicurezza RSA Questo acronimo non ha un significato particolare, se non di essere formato dalle iniziali dei suoi tre autori, ossia Ron Rivest, Adi Shamir e Len Adleman del MIT, che lo hanno formalizzato nel 1977. Al di là dei dettagli tecnici del suo funzionamento, a fronte della sua discreta complessità computazionale, l'algoritmo si presta ad essere usato per scambiarsi una chiave di sessione crittografata con RSA, e poi proseguire la trasmissione sicura utilizzando quella, mediante un algoritmo di crittografia simmetrica. Diffie-Hellman Pubblicato per la prima volta nel 1996, permettere a due entità di scambiarsi in modo sicuro le reciproche chiavi pubbliche, e quindi proseguire con quelle. Generazione delle chiavi Il metodo funziona a partire dalla conoscenza comune di due numeri, q che è un numero primo, e alfa che è una radice primitiva di q. Alice può allora scegliere un intero qualunque XA<q, e calcolare YA = alfaXA mod q, che costituiscono rispettivamente le sue chiavi privata (XA) e pubblica (YA). Allo stesso modo Bob, partendo dalla conoscenza degli stessi due numeri alfa e q, calcola la sua chiave privata (XB) e pubblica (YB), scegliendo casualmente XB<q, e calcolando YB = alfaXB mod q. In linea di principio, non è necessario che i due numeri di partenza, q e alfa, siano gli stessi per le due entità, e dopo che le chiavi pubbliche ottenute sono state scambiate, si può procedere ed utilizzarle ai fini della crittografia e della autenticazione basate sugli algoritmi a chiave pubblica. Il segreto condiviso Se invece le due parti calcolano le chiavi a partire dagli stessi numeri iniziali q ed alfa, è possibile calcolare, da entrambi i lati, un medesimo segreto K, che potrebbe essere poi utilizzato come chiave di sessione, nell'ambito di un contesto di crittografia simmetrica. Si può infatti dimostrare che se Alice effettua il calcolo di K = (YB)XA mod q, utilizzando la sua chiave privata XA e quella pubblica YB di Bob, quest'ultimo può pervenire allo stesso risultato calcolando K = (YA)XB mod q, ovvero utilizzando la sua chiave privata XB, e quella pubblica YA di Alice. Infatti, semplificando un pò, osserviamo che (YA)XB = ( alfaXA )XB è uguale a (YB)XA =(alfaXB)XA. Lo scambio di Diffie-Hellman La figura seguente riassume i passi dello scambio di Diffie-Hellman: le due entità Alice e Bob, a partire dalla comune conoscenza di q e di alfa, calcolano le proprie coppie di chiavi pubblica e privata. Quindi, scambiandosi le chiavi pubbliche, mettono l'altra parte in grado di calcolare il medesimo segreto K. In assenza di un accordo a priori, i valori di q ed alfa possono essere definiti da Alice, e comunicati a Bob assieme alla propria chiave pubblica Y A. 134 Lo strato applicativo di Internet Alessandro Falaschi La chiave di sessione Resta ora il fatto, che il segreto condiviso K potrebbe non essere idoneo ad essere usato in un algoritmo di crittografia simmetrica. Per questo, anziché usare direttamente K come chiave di sessione, una delle due parti (in genere Alice, che ha iniziato lo scambio) sceglie una differente chiave di sessione (indichiamola con M), ed usa quindi K per crittografare M mediante un algoritmo simmetrico. La chiave di sessione M così crittografata viene ora trasmessa a Bob, che a sua volta usa K per recuperarla, e da quel punto in poi, proseguire la comunicazione mediante crittografia simmetrica, basata su M. Attacco MITM Un eventuale Man in the Middle (Eva) che assiste al primo scambio tra Alice e Bob, anche venendo a conoscenza di q, alfa e YA, ma non essendo a conoscenza della scelta fatta da Bob a riguardo di XB, né della chiave privata XA di Alice, non sa come calcolare K. Però, dato che la trasmissione dei numeri iniziali e delle chiavi pubbliche non è autenticata (e per questo, il metodo ora illustrato è detto anonimo), Eva potrebbe modificare i valori q, alfa e YA, inviati da Alice a Bob, con altri valori da lei impostati, e sostituirsi a Bob nel completare lo scambio di Diffie-Hellman con Alice. Quindi, Eva inizia un nuovo scambio con Bob, stavolta fingendosi Alice. Infine, intercetta tutti i messaggi tra le parti, transcrittografandoli durante il transito. Autenticazione dello scambio L'attacco MITM può essere evitato, se durante lo scambio di q, alfa, YA, e YB, le due parti possono autenticarsi vicendevolmente, mettendo Eva fuori gioco. Perché ciò sia possibile, ci sono due alternative: • esiste una PKI in grado di certificare l'autenticità delle parti, ovvero le parti applicano una firma digitale ai dati scambiati, e questa firma può essere verificata come autentica da una parte, utilizzando la chiave pubblica che compare in un certificato digitale dell'altra parte, e firmato da una CA di fiducia, oppure • esiste un altro segreto condiviso a priori tra le parti, come ad esempio una password, mediante la quale aggiungere sale ai messaggi scambiati. DSS Il Digital Signature Standard è stato proposta nel 1991 da una agenzia governativa statunitense (NIST), per offrire dei servizi di firma digitale sulla base del Digital Signature 135 Gestione delle chiavi Dispositivi e Meccanismi di Sicurezza Algorithm (DSA), che utilizza SHA-1. L'ultima revisione è avvenuta nel 2000. Non viene invece usato per fornire servizi di crittografia e scambio di chiavi. Gestione delle chiavi Alcuni aspetti già accennati nella discussione dello scambio di Diffie-Hellman rientrano esattamente in questa definizione, che riguarda i problemi di: • come ottenere la chiave pubblica di un'altra entità in modo fidato • come scambiarsi il valore di una chiave di sessione temporanea La soluzione più generale, in buona analogia con i casi della vita reale, passa per la presenza di una terza parte, con cui le due parti in comunicazione hanno un rapporto di fiducia. Certificati digitali Nella crittografia asimmetrica, sussiste il problema di come ottenere la chiave pubblica di un altro utente, in modo fidato. Infatti se durante la trasmissione della chiave pubblica di Alice a Bob, un man in the middle (Eva) intercettasse il messaggio e lo sostituisse con un altro, contenente la propria chiave pubblica, Eva potrebbe poi spacciarsi per Alice, e leggere i messaggi destinati a lei. Per risolvere questo problema, si ricorre all'esistenza di una terza parte, una sorta di garante, detta Certification Autority (CA), e di cui è possibile procurarsi con un elevato grado di affidabilità la chiave pubblica, ad esempio, per averla ricevuta di persona. Una entità che desidera che la propria identità sia certificata dalla CA, gli consegna a sua volta la propria chiave pubblica, in modo affidabile, come ad esempio affidandogliela di persona, e mostrando un documento di identità. La CA associa quindi alla chiave pubblica dei singoli individui, l'identità del legittimo proprietario, mediante l'emissione di un Certificato Digitale, che viene firmato utilizzando la chiave privata della CA come mostrato in figura, ovvero • si calcola un hash del certificato non firmato • si crittografa l'hash con la chiave privata della CA • si concatena l'hash crittografato al certificato, che risulta così firmato dalla CA 136 Lo strato applicativo di Internet Alessandro Falaschi Chi riceve il certificato firmato può verificare l'autenticità dello stesso, e quindi fidarsi che la chiave pubblica presente nel certificato sia veramente quella dell'individuo a cui il certificato è intestato, svolgendo le operazioni previste per l'autenticazione mediante funzioni Hash associate alla crittografia a chiave pubblica, ovvero • chi vuole verificare il certificato deve disporre della chiave pubblica della CA, scritta ad esempio in un diverso certificato intestato alla CA, ed essere certo che questa chiave pubblica sia veramente della CA; • viene calcolato l'hash del certificato non firmato; • si confronta il risultato, con quello ottenuto decifrando l'hash cifrato ricevuto, mediante la chiave pubblica della CA. Distribuzione di chiavi di sessione mediante crittografia asimmetrica Poco sopra abbiamo illustrato come lo scambio di Diffie-Hellman consente a due parti il calcolo di uno stesso segreto condiviso, senza la necessità che lo stesso venga trasmesso, ma è esposto ad attacchi di tipo MITM, che possono essere sventati ricorrendo ad una Certification Authority. Infatti, se Alice possiede un certificato firmato da una CA, che ne attesta il possesso di una chiave pubblica a lungo termine, può usare la chiave privata associata, per firmare i dati iniziali dello scambio, trasmettendo assieme a questi anche il certificato, in modo che Bob, dopo aver verificato l'autenticità del certificato usando la chiave pubblica della CA, possa ritenere il mittente autentico. Ma, nel caso in cui esista una CA la cui chiave pubblica è distribuita in modo affidabile alle parti in comunicazione, le quali hanno altresì provveduto a certificarsi presso al CA, decade la necessità di effettuare uno scambio di Diffie-Hellman completo. Infatti, è possibile crittografare il messaggio da inviare con un algoritmo simmetrico, e spedirlo assieme alla 137 Public Key Infrastructure - PKI Dispositivi e Meccanismi di Sicurezza chiave necessaria ad aprirlo, crittografando quest'ultima con la chiave pubblica del destinatario, che viene letta dal certificato firmato relativo del destinario, e noto al mittente; in questo modo, il ricevente può decrittare la chiave dell'algoritmo simmetrico, facendo uso della propria chiave privata. Public Key Infrastructure - PKI Una Infrastruttura a Chiave Pubblica (Public Key Infrastructure) permette ad individui ed utenti di asserire la propria identità, verificare quella degli altri, e si basa sulla accessibilità di certificati digitali firmati da una Certificate Authority, abilitando in tal modo le entità a procedere allo sviluppo di una comunicazione sicura facente uso di tecniche crittografiche qualsiasi. X.509 E' uno standard ITU-T che definisce, fra le altre cose, formati standard per i certificati a chiave pubblica, i loro meccanismi di revoca, e la gerarchia delle CA. Nasce nel 1988 in relazione al servizio di directory X.500, ed assume l'esistenza di una rigida organizzazione gerarchica delle CA, tale da prevedere per ognuna di esse la firma del relativo certificato da parte di una CA più importante, su su fino ad una unica CA radice. Viene definita una architettura in cui le CA hanno il solo compito di firmare i certificati, mentre l'emissione degli stessi è delegata ad una diversa entità, indicata come Registration Autority (RA), e la loro conservazione è delegata ad un Certificate Repository (CR). Anche dopo diverse revisioni, questa architettura non si è mai realizzata, mentre il servizio di directory X.500 si è evoluto nel Lightweight Directory Access Protocol (LDAP). Attualmente per certificato X.509 si intende quello definito nell'ambito del profilo formalizzato nella RFC 3280 prodotta dal gruppo di lavoro IETF PKIX, che sta per Public Key Infrastructure (X.509). Catena delle autorità di certificazione Perché il certificato di Alice, emesso da CA1, sia di di qualche utilità per Bob, questi deve conoscere con certezza la chiave pubblica di CA1, in modo da poter verificare la sua firma. In caso negativo, se Bob conosce invece la chiave pubblica di una diversa autorità, ad esempio di CA2, può tentare di trovare un certificato di CA1 che sia firmato da CA2, perché in tal caso può usare la chiave pubblica di CA2 per verificare l'autenticità della chiave pubblica di CA1, e quindi finalmente usare questa, per verificare il certificato di Alice. Nel caso in cui CA1 e CA2 non si siano certificate a vicenda, lo stesso processo può essere ripetuto attraversando un numero qualunque di CA. Formato Un certificato X.509 contiene le informazioni riportate in figura, ossia 138 Lo strato applicativo di Internet Alessandro Falaschi • versione: può essere 1, 2 o 3, in accordo alle specifiche emesse in tempi successivi; • numero di serie: deciso dalla CA; • algoritmo di firma: ripetuto in fondo, nella firma stessa, specifica come decrittarla; • emittente: identifica la CA che ha apposto la firma, e (come anche il Subject name) è espresso con una sintassi X.500 e nota come Distinguished Name (DN), composta da una serie di campi in cui compaiono coppie sigla-valore, in accordo al modello gerarchico originario, in cui le sigle dovrebbero individuare la catena delle CA coinvolte, ed individuare univocamente il soggetto, come: ◦ ◦ ◦ ◦ ◦ ◦ Country C State or province SP Locality L Organisation O Organisational unit OU Common name CN ad esempio: C=US/L=Area Designers/CN=John Doe. 51/O=Hanger 18/OU=X.500 Standards • periodo di validità: oltre il quale il certificato è da ritenersi scaduto; • chiave pubblica del soggetto: è proprio l'oggetto del certificato! • identificatori unici: per disambibuare il caso in cui lo stesso DN sia stato usato per soggetti diversi; • estensioni: nella versione v3 dello standard, si è intordotta la possibilità di aggiungere un numero variabile di informaziioni, come ad esempio un indirizzo email, od un dominio Internet, o delle condizioni (policy) di utilizzo del certificato; • firma: apposta dalla CA, utilizzando la sua chiave privata, in base ad un algoritmo a chiave pubblica. Per avere una idea del risultato finale, possiamo osservare alcuni esempi di certificati, oppure scoprire i certificati delle root CA che troviamo preinstallati nel nostro browser. Fingerprint Può capitare di frequente che ci si ritrovi per le mani un certificato, senza avere a disposizione la chiave pubblica di chi l'ha firmato, oppure, un certificato auto-firmato, e si abbia la necessità di usare la chiave pubblica che vi compare, senza però voler correre il rischio di rimanere vittima di una impersonificazione frudolenta. Allora, la cosa più semplice che si può fare, è quella di tentare di contattare la persona/entità a cui è intestato il certificato, e chiedergli se la chiave pubblica del certificato, è veramente la sua. Dato però che una chiave pubblica può essere molto lunga, si è diffuso il costume di calcolare un HMAC 139 Public Key Infrastructure - PKI Dispositivi e Meccanismi di Sicurezza della versione codificata (ad es DER) del certificato, in genere di lunghezza ridotta (ad es, 32 cifre esadecimali), e di chiamarlo fingerprint (impronta digitale) del certificato. In questo modo, la fingerprint può ad esempio, essere facilmente letta a voce al telefono, o facilmente verificata perché scritta su di un sito web, e può essere presa con sufficiente affidabilità come la prova che il certificato in proprio possesso, è conforme a quello in possesso di chi l'ha emesso. Richiesta e rilascio dei certificati Per ottenere un proprio certificato, occorre generare una propria coppia di chiavi, compilare una richiesta di firma certificato in cui compaiono gli elementi che concorono a formare il proprio Distinguished Name, allegare alla richiesta la propria chiave pubblica, e firmare la richiesta utilizzando la propria chiave privata, che d'ora in poi dovrà essere custodita con la massima cura. Se vogliamo utilizzare il certificato per autenticarci verso altre entità, con cui non abbiamo nessun rapporto preesistente, dobbiamo sottoporre la richiesta ad una CA la cui chiave pubblica è già ampiamente distribuita, come ad esempio quelle preinstallate con il browser. In questo caso la CA (o, più correttamente, la RA) può (e deve) svolgere alcune verifiche formali, come ad esempio accertarsi (mediante il comando whois e/o host) che il richiedente corrisponda all'intestatario del dominio citato nella registrazione, e/o che il richiedente abbia accesso all'indirizzo email che risulta dai comandi di ispezione. Pertanto, la visita con successo di un sito web sicuro, ci garantisce unicamente che esiste un qualche rapporto tra lo sviluppatore del sito, e il manutentore del dominio che compare nella URI del sito. Per un esempio di sito sicuro, si visiti ad es. https://addons.mozilla.org/it/firefox/. Codifica e consegna di un certificato Una volta che la CA, in base alle informazioni presenti nella richiesta, ha generato un nuovo certificato intestato al richiedente, e l'ha firmato, il risultato di queste operazioni viene salvato in un file, che può essere consegnato al richiedente, e/o a quanti potranno richiederlo. Prima di distribirlo, però, queste file viene sottoposto ad una tra diverse possibili modalità di codifica, che determinano altresì il tipo di estensione usato per salvare il file, come ad esempio • TXT - la rappresentazione testuale, idonea ad essere visualizzata da un utente, ma inutile ai fini della autenticazione automatica; • DER - Distinguished Encoding Rules, costituisce una sintassi di trasferimento per dati la cui struttura è descritta mediante la notazione ASN.1, come appunto è il caso dei certificati X.509, allo scopo di permetterne la distribuzione verso sistemi con meccanismi di rappresentazione diversi. Il risultato è una sequenza di byte che rappresenta gli elementi della struttura dati di partenza, nella forma di triplette costituite da tipo-lunghezza-valore (TLV), in cui le etichette sono le stesse valide per la sintassi di trasferimento BER, come illustrato in questo esempio; • PEM - la rappresentazione base64 di una rappresentazione DER, a volte usato anche per trasmettere la chiave privata, debitamente crittografata; • P12 - indica il formato PKCS#12, ovvero il 12-esimo in un gruppo di Public Key Cryptography Standards sviluppati e pubblicati da RSA Security; in particolare, il #12 prende il nome di Personal Information Exchange Syntax Standard e definisce un formato contenitore per memorizzare oggetti multipli, come più certificati, e chiavi private associate, generalmente protetti e crittati con un algoritmo crittografico simmetrico basato su password. Ad esempio, nelle smartcard e derivati (bancomat, SIM) sono conservati assieme sia il certificato di identità che la chiave privata usata dal chip dell'utente per firmare, la quale è protetta con un algoritimo crittografico simmetrico, la cui chiave è il PIN. 140 Lo strato applicativo di Internet Alessandro Falaschi Revoca dei certificati Ad ogni certificato è associato un periodo di validità, e prima che questo scada, deve essere rinnovato, e prodotta una nuova copia. Ma anche se il certificato non è ancora scaduto, può essere revocato in anticipo, perché ad esempio la chiave privata del proprietario è stata compromessa, il proprietario non è più certificato, o si pensa che il certificato sia stato compromesso. Ogni CA mantiene quindi una lista (CRL, Certificate Revocation List), firmata, contenente l'elenco dei certificati emessi, ancora nominalmente validi, ma revocati. Quando si riceve un certificato, sarebbe bene richiedere alla CA che l'ha emesso, l'invio della CRL, in modo da poter verificare se questo non sia stato revocato. A fronte della evidente complicazione di questo modo di operare, nella RCF 2560 è stato definito un Online Certificate Status Protocol (OCSP) che opera tramite messaggi codificati in ASN.1 e trasmessi via HTTP, e che permette le verifiche di revoca di certificato in modo molto più veloce dello scaricamento ed elaborazione delle CRL. I browser web sono distribuiti con preinstallati i certificati autofirmati di diverse CA, con cui i produttori del browser hanno stretto un accordo, in modo da semplificare la verifica di autenticità dei siti che inviano un loro proprio certificato. Certificati autofirmati Per quanto illustrato, chiedere il rilascio (a pagamento) del certificato per il proprio sito ad una CA la cui chiave pubblica è preinstallata nel browser, consente di avere il proprio certificato immediatamente riconosciuto come valido; dato però che queste CA sono tutte root, i loro certificati risulteranno auto-firmati. In alternativa, ci si può rivolgere ad esempio a CaCert, che offre certificati gratuiti, ed il cui certificato autofirmato può essere importato in modo facile dal browser. Oppure, se non ci vuol rivolgere ad una CA esterna, è sempre possibile assolvere a tale funzione in proprio, ad esempio usando gli strumenti offerti dal progetto OpenSSL. Una volta creato il certificato autofirmato dalla propria auto-CA, questo deve essere esportato presso i clients (ad esempio, di una stessa azienda) che poi lo useranno per verificare i certificati che firmeremo. L'esportazione può avvenire sia mediante un diverso canale fisico (consegna fisica di un dischetto, od un CD), oppure creando in proprio una distribuzione Linux installabile da CD, e contenente il certificato, oppure ancora via rete mediante una email sicura, una rete WiFi sicura, una pagina web, descrivendo il certificato mediante un header MIME Content-Type: application/x-x509-ca-cert. L'uso di certificati autofirmati, espone chi li accetta per la prima volta al rischio di un attacco di tipo man-in-the-middle, perché un attaccante potrebbe sostituire il suo certificato a quello legittimo, e poi continuare ad impersonare l'interlocutore. Ma se il certificato è corretto, ed è salvato dal client, gli scambi successivi avvengono senza ulteriori rischi di sicurezza. Web of trust Una web of trust (rete di fiducia) è un concetto utilizzato da PGP, GnuPG, e altri sistemi compatibili con OpenPGP, per stabilire l'autenticità dell'associazione chiave pubblicautente, alternativa all'uso delle Certificate Authority. In questo caso, l'autenticità di una chiave pubblica è sottoscritta (firmata) direttamente da altre persone, in occasione di incontri fisici appositamente organizzati, in cui ognuno firma la chiave pubblica degli altri presenti convenuti; le chiavi pubbiche così fimate, possono quindi essere distribute sia individualmente, oppure essere messe a disposizione su di un keyserver. Quando si viene in possesso di una chiave pubblica altrui, è possibile impostare l'affidabilità 141 Lightweight Directory Access Protocol - LDAP Dispositivi e Meccanismi di Sicurezza da attribuire ai suoi diversi firmatari, considerando massimamente fidate le firme apposte da persone (chiamiamoli amici) che ci hanno consegnato la loro chiave pubblica di persona, e via via meno fidate, le firme apposte da persone che sono solo amici di amici (la cui chiave è firmata da amici), o amici di amici di amici (di amici di amici...). A partire dalla versione 3, anche X.509 ha previsto la possibilità di costruire una rete di fiducia, ma orientata solamente a far firmare alle CA, i certificati di altre CA. Lightweight Directory Access Protocol - LDAP LDAP è un protocollo standard per l'interrogazione e la modifica dei servizi di directory, ovverosia di meccanismi per il recupero di una serie di informazioni relative ad un oggetto che sia possibile specificare in modo univoco, come ad esempio una password associata ad un account email. A questo proposito, il CCITT creò lo standard X.500 nel 1988, che divenne ISO 9594 Data Communications Network Directory Recommendations X.500-X.521 nel 1990. Il lavoro di IEFT a quel punto, fu di semplificare l'X.500 e di adattare la modalità di accesso al TCP. Mentre la RFC 1487 originale (X.500 Lightweight Directory Access Protocol) è del 1993, la sua ri-definizione più recente avviene nel 2006 ed è descritta dalla RFC 4510 e seguenti. Anche se, a prima vista, un servizio di directory appare molto simile a quello offerto da un DataBase, in realtà quest'ultimo è ottimizzato sia per le operazioni di lettura che di scrittura, mentre si presume che le informazioni presenti su Directory Server varino molto più raramente. Inoltre, X.500 organizza le informazioni come una struttura rigidamente ad albero, detto Directory Information Tree o DIT. Ogni nodo dell'albero è associato ad un Relative Distinguished Name (RDN) che è un attributo definito da un tipo ed uno o più valori che lo distinguono dai nodi fratelli posti allo stesso livello della gerarchia. Concatenando tutti gli RDN dal nodo in quesione, su su fino alla radice dell'albero, si ottiene il Distinguished Name (DN) (vedi RFC4514) dell'elemento, che lo rappresenterebbe in modo globalmente unico. La figura a lato esemplifica l'individuazione dell'oggetto cn=Barbara Jenson,ou=Sales,o=Acme,st=California,c=US nell'ambito di una tale organizzazione, come pensata nelle intenzioni originarie, in cui sarebbe dovuta esistere una unica radice universale per tutti gli alberi X.500. Nella pratica attuale, è più comune il caso in cui ai livelli dell'albero si fanno corrispondere i livelli del FQDN presso il quale è localizzata la risorsa, delegando al DNS il compito di mantenere una gerachia globale, e individuando un oggetto mediante la sintassi del tipo uid=babs,ou=People,dc=example,dc=com che rappresenta l'utente (uid) babs presso l'Unità Organizativa (ou) People a cui è intestato il nome a dominio (dc) example.com. In entrambi i casi, nel livello inferiore a quello che individua una persona, si trovano i nodi associati alle diverse informazioni ad esso associate, come indirizzo email, telefono, numero di stanza, eccetera. 142 Lo strato applicativo di Internet Alessandro Falaschi LDAP definisce operazioni per interrogare e aggiornare la directory, aggiungere, modificare e rimuovere elementi; ma principalmente, è usato per cercare, nell'ambito di una porzione dell'albero, le informazioni associate agli elementi il cui DN verifica criteri di ricerca specificati da filtro di ricerca. Inoltre, LDAP supporta un meccanismo che permette ai clients di autenticarsi, facendo eventulmente uso anche di un canale di comunicazione sicuro. La mia intenzione è di sperimentare l'installazione e la configurazione di un server LDAP, che mantenga le credenziali di chi frequenta il laboratorio, da usare per scopi diversi. Per ora mi limito però a raccogliere i links più promettenti: • Tutorial LDAP - di Marco Ferrante e Tiziana Podestà • Integrazione Sistemistica con LDAP Introduzione a LDAP e OpenLDAP, con esempi di configurazione - di Simone Piccardi • OpenLDAP Software 2.4 Administrator's Guide • guida Server OpenLDAP di Ubuntu 8.10 • Guida Ubuntu DovecotLDAP • How-To set up a LDAP server and its clients • Understanding X.500 - The Directory di D W Chadwick • LDAP Linux HOWTO • HowTo/DovecotOpenLdap Strati di sicurezza In accordo al paradigma della stratificazione dei servizi, le funzioni di sicurezza che offrono servizi aggiuntivi di questa natura alle applicazioni Internet, possono essere realizzate in forma di strati funzionali addizionali che offrono i propri servizi a quelli superiori, in accordo al seguente schema, in cui si distingue il caso dei servizi offerti al livello • di rete (IPSEC) • di trasporto (TLS) • applicativo (SASL, PGP, S/MIME) Infine, vale la pena citare la possibilità di creare tunnel sicuri mediante SSH. IPSEC IPsecè l'abbreviazione di IP Security ed è uno standard (RFC 4301) per ottenere connessioni basate su reti IP sicure. La sicurezza viene raggiunta attraverso la cifratura e l'autenticazione dei pacchetti IP, e quindi a livello di rete, rendendo la trasformazione trasparente alle applicazioni, che non devono essere modificate. IPsec definisce • protocolli che forniscono la cifratura del flusso di dati, attuata mediante due protocolli: ◦ Authentication Header (AH), che garantisce l'autenticazione e l'integrità del messaggio, ma non offre la confidenzialità, e 143 Strati di sicurezza Dispositivi e Meccanismi di Sicurezza ◦ Encapsulating Security Payload (ESP) che fornisce autenticazione, confidenzialità e controllo di integrità del messaggio, e per questo motivo ESP è molto più usato di AH; • protocolli che implementano lo scambio delle chiavi per realizzare il flusso crittografato: Attualmente esiste un solo protocollo per questo fine, l'Internet Key Exchange (IKE). Stabilisce uno shared session secret, utilizzando l'algoritmo di DiffieHellman. E' definito in RFC 4306, ed utilizza UDP sulla porta 500. Mediante IPSec/ESP si può realizzare una Virtual Private Network (VPN), configurandolo sulle interfacce esterne dei router che interconnettono, mediante un collegamento ad Internet, le sedi distaccate di una stessa organizzazione. TLS Il Transport Layer Security è definito nella RFC 4346, rappresenta la formalizzazione da parte IETF del Secure Socket Layer definito da Netscape, ed ha lo scopo di realizzare comunicazioni sicure su Internet per applicazioni come il browsing Web e l'email, in modo da evitare intercettazioni (eavesdropping), sabotaggi (tampering) e flasificazioni (forgery). Tra SSL e TLS sussistono differenze minime. L'uso di una connessione resa sicura via SSL/TLS, può avvenire sia su iniziativa del processo applicativo soprastante, come nel caso di STARTTLS, oppure può avvenire fin dall'inizio della connessione, riservando una porta diversa da quella standard (ad esempio, l'HTTP sicuro prende il nome di HTTPS, e risponde sulla porta 443 anziché la 80). Un ulteriore uso di TLS, è quello di creare dei tunnel sicuri nella infrastruttura Internet pubblica, realizzando delle Virtual Private Network (VPN), come ad esempio avviene con OpenVPN, senza necessità di intervenire sui singoli dispositivi di rete, come invece nel caso di IPSec. 144 Lo strato applicativo di Internet Alessandro Falaschi Record Protocol Il Record Protocol offre i servizi di sicurezza allo strato applicativo, così come a tre protocolli ausiliari (Handshake, Change Chiper e Alert) necessari alla gestione della connessioni che fanno uso di TLS. L'handshake protocol si occupa di creare una sessione TLS, ossia un insieme di parametri crittografici, da utilizzare nel corso di una o più connessioni, evitando di negoziare ogni volta gli stessi parametri. Tra questi, menzioniamo • l'identificatore di sessione, una sequenza di byte arbitraria scelta dal server, per identificare uno stato di sessione attiva o risvegliabile • il certificato del peer, di tipo X.509, che può non esserci • il tipo di compressione, ossia l'algoritmo usato per ridurre il volume dei bytes scambiati • il cifrario, ossia l'algoritmo crittografico per la riservatezza, e l'algoritmo hash per il calcolo del MAC • il segreto condiviso tra client e server • se è risvegliabile, ossia se la sessione può essere usata per creare nuove connessioni. Lo stato di una connessione è invece identificato da • random di client e server: scelti a caso dalle due entità per ogni connessione • segreti MAC: le chiavi usate da client e server per calcolare i MAC dei messaggi in uscita • chiavi di scrittura: la chiave di crittografia simmetrica usata dal client e dal server • vettori di inizializzazione: rappresenta lo stato interno per un codificatore a blocchi in modalità CBC • numeri di sequenza: aggiornati da entrambe le parti, per i messaggi inviati e ricevuti in una stessa connessione. Il Record protocol offre confidenzialità, autenticazione e (opzionalmente) compressione, in accordo alle modalità operative mostrate nella figura che segue 145 Strati di sicurezza Dispositivi e Meccanismi di Sicurezza Il MAC è generato mediante funzione hash SHA-1 oppure MD5, ed usando come sale il segreto MAC, in modo del tutto simile all'HMAC. La crittatura può usare uno tra gli algoritmi IDEA, RC2, DES, 3DES, RC4, ed opera anche sul MAC già calcolato. L'aggiunta finale di una intestazione SSL, dà luogo al formato mostrato al lato. Il campo Content Type (8 bits) codifica il protocollo di strato superiore, ed i numeri di versione permettono di tener traccia delle evoluzioni delle specifiche. Infine, viene inserito un campo che descrive la dimensione del pacchetto risultante. Il Record Protocol, anziché incapsulare informazioni provenienti dallo strato applicativo, può trasportare informazioni prodotte da uno dei tre protocolli specifici del TLS (Change Cipher, Handshake e Alert), identificati dal contenuto del campo Content type, e corrispondenti ciascuno al formato mostrato nelle figure che seguono: 146 Lo strato applicativo di Internet Alessandro Falaschi • Change Cipher occupa un solo byte, che se c'è, vale uno. Ha il solo scopo di far si che l'esito della negoziazione dei parametri, che ha dato luogo ad uno stato in attesa, siano copiate nello stato corrente, aggiornando la cipher suite da usare per questa connessione; • Alert Protocol occupa due byte, e può inoltrare informazioni di allarme verso l'altra parte in comunicazione. Il primo byte classifica l'allarme come warning o fatal, nel cui caso la connessione è interrotta; il secondo byte specifica di che tipo di problema si tratti. Handshake Protocol Permette a client e server di autenticarsi vicendevolmente, di negoziare un algoritmo di crittografia, un algoritmo per il calcolo del MAC, e le chiavi crittografiche da usare. Tutti i messaggi scambiati mediante l'handshake protocol, precedenti alla trasmissione di qualsiasi altro dato, hanno il formato mostra sopra, con i campi: • tipo: specifica uno tra 10 diversi messaggi, come ad esempio hello di client e server, catena di certificati X.509, parametri per lo scambio di chiavi, richiesta di certificato, verifica della firma, scambio delle chiavi.... • lunghezza: del campo successivo • contenuto: i parametri associati con il tipo di messaggio descritto nel campo tipo. Nel suo funzionamento, l'handshake protocol può pensarsi come suddiviso in una successione di 4 fasi. 147 Strati di sicurezza Dispositivi e Meccanismi di Sicurezza La prima, è iniziata dal client, che invia il messaggio client_hello, con associati i parametri mostrati in figura. La risposta server_hello, costituisce la negoziazione dei parametri, e contiene un numero di sessione valido, e la scelta della cipher suite. Il primo elemento di quest'ultima, codifica il metodo di scambio chiave, tra cui • RSA: la chiave segreta è crittografata con quella privata del mittente, e per leggere quella segreta, occorre prima procurarsi un certificato dove si possa leggere la chiave pubblica dl mittente • Diffie-Hellman in versione "fissa", effimera o anonima. La cipher suite prosegue quindi con lo specificare, tra l'altro, l'algoritmo crittografico simmetrico scelto, l'algoritmo hash per il calcolo del MAC, e la sua dimensione. Inizia quindi una seconda fase, in cui (se necessario) il server invia il proprio certificato X.509 e la propria chiave (inviando i parametri di Diffie-Hellman, oppure adottando RSA). Nel calcolo dell'hash necessario a generare la firma di autenticazione, oltre ai parametri del 148 Lo strato applicativo di Internet Alessandro Falaschi server, sono utilizzati anche i nonce di client e server, inviati con i messaggi di hello, a protezione di attacchi replay. Nella terza fase, il client può verificare l'autenticità del certificato ricevuto, utilizzando la chiave pubblica della CA che ha firmato il cerificato del server; se questo ha buon esito, procede con l'inviare un PreMasterSecret crittografato con la chiave pubblica del server, oppure i parametri di Diffie-Hellman. La chiave segreta vera e propria verrà poi calcolata da entrambe le parti, a partire dalla versione Pre-, e da entrambi i nonce. Infine nella quarta fase, il messaggio change_chiper-spec segna l'inizio della crittografia di ciò che segue. API di sicurezza Anziché inframmezzare le funzioni di sicurezza tra due strati funzionali preesistenti, a volte si preferisce isolare queste primitive all'interno di librerie richiamabili dai programmi applicativi che le linkano, e contenenti entry points di subroutines che implementano i diversi servizi crittografici di cui l'applicazione può avere bisogno. SASL Il Simple Authentication and Security Layer (SASL) è definito dalla RFC 4422 come una infrastruttura capace di offrire alle applicazioni basate su TCP, servizi di autenticazione e di sicurezza dati, e fornisce uno strato di astrazione che, per mezzo di una interfaccia strutturata, permette a diversi protocolli di far uso di meccanismi crittografici sviluppati indipendentemente. Presso lo IANA è pubblicato un registro che elenca i protocolli applicativi che prevedono modalità di interazione con SASL, e fornisce i riferimenti alle specifiche che definiscono questi aspetti. Tra i procolli che utilizzano SASL, troviamo IMAP, LDAP, POP, SMTP, FTP, NFS, NNTP, e XMPP. 149 API di sicurezza Dispositivi e Meccanismi di Sicurezza SMTP \ LDAP XMPP Other protocols ... | | / \ | | / SASL abstraction layer / | | \ / | | \ EXTERNAL GSSAPI PLAIN Other mechanisms ... I meccanismi supportati anch'essi registrati presso lo IANA, e tra questi troviamo GSSAPI - Generic Security Service Application Program Interface, Kerberos RFC4752 EXTERNAL - l'autenticazione è ottenuta con altri meccanismi (es. TLS) RFC4422 ANONYMOUS - accesso non autenticato, come ospite RFC4505 OTP - One Time Password RFC2444 PLAIN - usa una password di tipo cleartext RFC4616 DIGEST-MD5 - Integrazione dell'HTTP Digest Access in SALS RFC2831 CRAM-MD5 - Challenge-Response Authentication Mechanism basato su HMAC MD5 RFC2195, draft Esistono implementazioni libere di librerie che offrono il supporto a SASL, come ad esempio quelle di Cyrus e GNU. Una fonte di informazioni, si trova presso l'autore delle RFC, SASL e Sendmail, Technoids.org CRAM-MD5 Questo acronimo sta per Challenge-Response Authentication Mechanism ed è definito dalla RFC 2195. Si basa sull'algoritmo HMAC-MD5 che calcola un Message Authentication Code (MAC) utilizzando una funzione crittografica hash in combinazione con una chiave segreta. La funzione Hash utilizzata è il Message-Digest algorithm 5 (MD5), standardizzato nella RFC 1321, e qui utilizzato nella variante keyed illustrata in RFC 2104, e che usa una password nota sia al client che al server come chiave per generare un digest di 16 bytes, rappresentato da 32 caratteri esadecimali. Nel CRAM-MD5 il server invia al client, con una codifica di trasferimento base64, una sfida (challenge) generata ex-novo per l'occasione, e che tipicamente consiste in una stringa di tipo msg-id, come ad esempio <[email protected]>, e che contiene un timestamp sempre diverso. Il client, dopo aver decodificato lo challenge dalla sua rappresentazione base64, ne calcola un digest HMAC applicando l'algoritmo MD5, ossia usa lo challenge come messaggio, ed una password segreta associata all'utente che intende autenticarsi, e nota anche al server, come chiave. I 16 bytes del digest vengono quindi rappresentati come 32 caratteri esadecimali, e dato che ogni diverso utente possiede una diversa password, il nome dell'utente che desidera autenticarsi viene prefisso al digest, separato da questo da uno spazio. Quest'ultimo risultato viene quindi trasformato base64, e finalmente inviato come response al server, il quale dopo aver recuperato la 150 Lo strato applicativo di Internet Alessandro Falaschi password associata all'utente, calcola anch'esso il digest, e lo confronta con quello contenuto nella risposta ricevuta. Un esempio di scambio reale, può essere trovato nella sezione relativa alle esercitazioni. L'utilizzo di MD5-keyed per il calcolo del digest, permette di non memorizzare la password in chiaro presso il server, ma solo una sua versione già crittografata, che compare come calcolo intermedio in MD5, e indicata come contesto. Il protocollo di sfida difende da attacchi di tipo replay, dato che anche se intercettato, il messaggio di risposta alla sfida non può essere riusato successivamente, perché nel frattempo è cambiata la parola di sfida. Dato che ogni sfida è diversa, questa viene a volte indicata con il termine di nonce, ossia di nome utilizzato una sola volta. PGP e GPG La Pretty Good Privacy è una iniziativa imputabile ad una unica persona, Phil Zimmermann, e deve molta della sua popolarità all'approccio del tutto aperto con cui è stato impostato il lavoro, che può essere definito come l'applicazione di crittografia più diffusa al mondo, e che è formalizzato come standard IETF nella RFC 4880. Nella figura che segue riportiamo uno schema di firma e crittografia di un messaggio, ad esempio di una email. Osserviamo che innanzitutto viene calcolato un hash del messaggio, il quale viene crittografato (asimmetricamente) con la chiave privata del mittente, ed il risultato (ossia la firma del messaggio) viene concatenato al messaggio stesso. La chiave privata usata, è stata prelevata (selezionandola in base alla sua identità IDA) da un keyring di chiavi private, dov'era conservata in forma crittografata (PGP consente agli individui, di possedere più coppie di chiavi pubblica/privata). Questa chiave privata viene decrittata usando una passphrase, che viene chiesta ogni volta all'utente, mentre la sua identità è anch'essa concatenata a messaggio + firma. Il risultato complessivo, viene crittografato usando un algoritmo simmetrico, e che opera in base alla chiave prodotta da un generatore di numeri casuali (RNG), la quale chiave viene pure trasmessa concatenata al messaggio, dopo averla crittografata (asimmetricamente) usando la chiave pubblica associata ad una privata del destinatario, la cui identità (IDB) viene pure concatenata al messaggio uscente. Con l'ausilio 151 API di sicurezza Dispositivi e Meccanismi di Sicurezza della figura seguente, descriviamo ora il precesso di ricezione. L'identità della chiave privata del ricevitore viene usata per individuarla, in forma crittografata, all'interno del keyring privato del ricevitore, che quindi chiede all'utente la passphrase, in modo da poterla decrittare. A questo punto, siamo in grado di decrittare (asimmetricamente) la chiave di sessione, grazie alla quale possiamo decrittare il messaggio ricevuto. Quindi, l'identificativo della chiave privata del mittente permette di recuperare, dal keyring pubblico, la chiave pubblica del mittente stesso, e con questa, decrittare l'hash del messaggio, e verificare così la firma digitale apposta appunto dal mittente. Il PGP attribuisce un grado di fiducia alle chiavi pubbliche altrui in accordo alla soluzione basata sul Web of Trust, spesso coadiuvato dalla esistenza di alcuni keyserver che mentengo un deposito di chiavi pubbliche firmate da altri utenti. Per il PGP esistono applicazioni sia commerciali, che libere, descritte nella apposita sezione delle esercitazioni. S/MIME Lo standard S/MIME non offre nulla di più o di meglio del PGP, ossia dei servizi crittografici di sicurezza per le applicazioni di messaggistica elettronica: autenticazione, integrità e nonrepudiazione, mediante firme digitali, e confidenzialità usando la crittografia. E' stato prima sviluppato presso RSA come PKCS #7, e quindi il controllo della definizione dello standard è passato a IETF, che attualmente lo supporta come Cryptographic Message Syntax definito nella RFC 3852, nell'ambito del WG S/MIME di IETF. I messaggi elaborati in accordo a tali specifiche, sono identificati da un header MIME-Type: application/ pkcs7-mime (o "enveloped-data"). La differenza più sostanziale tra PGP e S/MIME risiede nel formato dei certificati e nella loro codifica, che rende i due sistemi tra loro incompatibili. 152 Lo strato applicativo di Internet Alessandro Falaschi S/MIME infatti, esige che i suoi utilizzatori usino certificati X.509, rilasciati da una qualche CA, di cui il client possieda in modo sicuro, la rispettiva chiave pubblica. Riferimenti Gran parte delle illustrazioni utilizzate in questa sezione sono tratte (in modo non autorizzato) dal testo "Network Security Essentials: Applications and Standards, 3/E" di William Stallings, Ed. Prentice Hall, di cui consiglio a tutti la lettura, in virtù della sua estrema chiarezza. Tuttavia, solo ora mi avvedo che tali figure (ed altro interessantissimo materiale) sono comunque già disponibili in rete, in formato pdf, presso il sito mantenuto dall'autore. Inoltre, mi sento di segnalare i seguenti riferimenti • • • • • • • • • Diffie-Hellman Key Exchange – A Non-Mathematician’s Explanation Distributed Security - Microsoft TechNet Godzilla crypto tutorial - di Peter Gutmann Recommendation X.509 (03/00) - ITU The Open–source PKI Book - di Symeon (Simos) Xenitellis SSL/TLS Strong Encryption: An Introduction - Apache Foundation OpenSSL Documentation - di Jeremy Mates IPsec HOWTO - in italiano GARR Certification Authority Realizzato con da Alessandro Falaschi - 153 Riferimenti Dispositivi e Meccanismi di Sicurezza 154 Lo strato applicativo di Internet World Wide Web L'aspetto di Internet, per come è noto ai più, si basa essenzialmente sulla navigazione del World Wide Web (trad. lett. ragnatela mondiale), fondata su tre meccanismi per rendere le informazioni prontamente disponibili: • lo schema di denominazione per individuare le risorse, ossia gli URL; • il protocollo per accedere alle risorse giacenti sul Web, ossia l'HTTP; • il formalismo di rappresentazione ipertestuale, ossia HTML. A questo semplice trittico, si sono via via aggiunte sempre più tecnologie (CSS, CGI, CMS, Web Services, Web Semantico), a cui in questa sezione tenteremo di dare un ordinamento. • Storia ◦ W3C • Markup Language ◦ SGML, XML ◦ HTML ▪ Document Object Model ▪ Tipi di Eementi ◦ CSS ▪ Selettori e regole ▪ Proprietà • Caricare le pagine sul server ◦ FTP, SFTP, NFS, SMB, IMAP • URL, URI, URN • HTTP ◦ ABNF ◦ Da estremo a estremo ◦ Richiesta ▪ Metodi ▪ Intestazioni di richiesta ◦ Risposta ▪ Codici di risposta ▪ Intestazioni di risposta ◦ Incapsulamento risultante ◦ Caching ▪ Intercepting e Reverse Proxy ◦ Redirezione ◦ Negoziazione dei contenuti ◦ Connessioni persistenti ▪ Pipeline ▪ Header Connection Storia World Wide Web ◦ Compressione e Transfer-Encoding ▪ Chunked transfer encoding ◦ Sicurezza ▪ Autenticazione ▪ Basic Access Authentication ▪ Digest Access Authentication ▪ SSL/TLS ▪ Header Upgrade • Dal lato del browser ◦ Gestione dei Mime-Type ed embedding ▪ Plugin ◦ Protocolli registrati ◦ Configurazione di Firefox ◦ Esecuzione codice ▪ Java ▪ JME ▪ Javascript ▪ AJAX ▪ Flash ◦ Riempimento moduli ▪ ▪ ▪ ▪ Action Parametri di chiamata Controlli Esempio di form • Riferimenti Storia Nel 1991 Tim Berners-Lee, durante il suo lavoro al CERN di Ginevra, affrontò il problema di visualizzare congiuntamente i grafici e le tabelle prodotti come risultati di esperimenti scientifici, condotti su macchine diverse ed in laboratori differenti. Per questo, realizzò (assieme ad un suo collega Robert Cailliau) un meccanismo di condivisione delle documentazione scientifica in formato elettronico, tale da renderla accessibile in modo indipendente dal particolare tipo di computer dell'utente. Successivamente, accettò l'offerta di trasferirsi al Massachusetts Institute of Technology (MIT) di Boston, presso cui nel 1994 fondò il World Wide Web Consortium (W3C). Il linguaggio con cui si definisce il contenuto della presentazione è l'HTML, contenuto in pagine definite statiche, e visualizzabili mediante una applicazione browser, detta anche User Agent. Queste pagine sono richieste ad un sistema remoto presso cui è in esecuzione uno Web Server, mediante il protocollo HTTP. La URL è un indirizzo di livello applicativo, che consente di individuare contemporaneamente sia il protocollo, che il computer che ospita il web server, che la specifica pagina richiesta. 156 Lo strato applicativo di Internet Alessandro Falaschi W3C Il World Wide Web Consortium (W3C), è il consorzio nato allo scopo di studiare, migliorare e definire nuove tecnologie relative al WWW, con il fine di conseguire il massimo delle potenzialità offerte da questo mezzo. Non è un ente di standardizzazione, ed i suoi documenti sono delle raccomandazioni a cui i produttori di software sono invitati ad attenersi. D'altra parte, tra i membri del W3C compaiono gli stessi produttori a cui le raccomandazioni sono indirizzate, oltre ad aziende telefoniche, istituzioni di ricerca, e società strategicamente interessate alla crescita del Web. In particolare, mette a disposizione una serie di validatori della sintassi usata nella redazione delle pagine web. Markup Language Un linguaggio di marcatura (markup) ha lo scopo di indicare il modo in cui alcune parti di un testo devono essere visualizzate, mediante l'uso di descrittori appositi detti TAG, o Elementi. Mentre i linguaggi di tipo procedurale contengono le istruzioni da eseguire per la visualizzazione desiderata, i linguaggi descrittivi lasciano al visualizzatore il compito di stabilite il risultato finale effettivo, e danno solo delle indicazioni di tipo semantico sulla natura delle singole parti di testo. In questa seconda categoria, troviamo SGML, XML, HTML. SGML Lo Standard Generalized Markup Language (SGML) è un metalinguaggio standardizzato (ISO 8879), discendente dal Generalized Markup Language (GML) sviluppato in IBM, ed è basato sul concetto di definizione del tipo di documento o Document Type Definition (DTD), che a sua volta definisce quali sono i TAG permessi in un determinato documento SGML, in modo che questo possa essere verificato formalmente, prima di procedere alla sua visualizzazione, in accordo al markup. XML L'eXtensible Markup Language (XML) è una semplificazione e adattamento dell'SGML, da cui è nato, e permette di definire la grammatica di diversi linguaggi specifici derivati. Il suo scopo primario è quello dello scambio di dati strutturati, come ad esempio quelli prelevati da un database, che potrebbero essere descritti nel seguente modo: <?xml version="1.0" encoding="ISO-8859-1"?> <utenti> <utente> <nome>Luca</nome> <cognome>Ruggiero</cognome> </utente> <utente> <nome>Max</nome> <cognome>Rossi</cognome> </utente> </utenti> Nella prima riga, detta prologo, si indica la versione, la codifica dei caratteri, e se esiste, il DTD che ne definisce i tag (anche se per questo scopo, si preferisce fare uso di XML Schema Definition - XSD). Ogni tag può essere corredato da attributi, e lo spezzone di file contenuto tra un tag di apertura ed uno di chiusura, prende il nome di nodo; il nodo più esterno, che racchiude tutti gli altri, prende il nome di Elemento Radice. Un file XML può essere 157 Markup Language World Wide Web visualizzato da parte di un browser così come si trova, oppure in associazione con un foglio di stile (CSS o XSL) che ne definisce l'aspetto. In particolare, l'XHTML è un file XML che usa un insieme ridotto dei tag di HTML, ed una sintassi più rigida, e che può essere visualizzato da un browser web, solo se accompagnato da un foglio di stile CSS. HTML L'Hyper Text Mark-Up Language è stato sviluppato alla fine degli anni '80 da Tim Berners-Lee, come semplificazione dell'SGML, e la cui sintassi, definita dal W3C, è praticamente ferma dal 1999 alla versione 4.01, resa pubblica nel 1999, e non più modificata, perché in un qualche futuro, verrà soppiantato da XHTML e XML. Un file HTML è composto di elementi racchiusi tra tag (tag significa etichetta), uno di apertura ed uno di chiusura, e quest'ultimo, per certi elementi, è opzionale. Ad esempio, l'elemento <b>testo testo testo</b> verrà visualizzato in grassetto, come testo testo testo. Volendo fare un esempio che non c'entra nulla, è come se un documento HTML fosse un banco per la vendita di ortofrutta, esposta in cassette (elementi), ognuno recante una etichetta (tag) con su scritto il tipo del prodotto (pere, pomodori, carote...) assieme a degli attributi (provenienza, prezzo, tasto bilancia...). Molto spesso nel tag di apertura di un elemento viene specificato, oltre al suo tipo, anche uno o più attributi, con lo scopo di arricchire e/o specializzare la semantica standard del tag, assegnando ad una o più proprietà, dei valori definiti in modo esplicito. Per questo, il formato di un elemento HTML avrà il generico aspetto <tipo attr1=valore1 attr2=valore2 ... >testo al quale si applica la semantica del tag</tipo> Si può inoltre dire, che un file HTML, è un file SGML che adotta il DTD di HTML. La prima riga di un documento HTML contiene infatti l'indicazione della DTD adottata, la quale indica al browser qual'è la versione di HTML che stiamo utilizzando. Il documento HTML vero e proprio, è quindi racchiuso tra i tag <html> e </html>, all'interno dei quali, troviamo due sezioni: • quella racchiusa tra i tag <head> e </head>, dove trovano posto le informazioni generali riguardanti l'intero documento, che non vengono visualizzate dal browser; • quella racchiusa tra i tag <body> e </body> contiene invece il testo (ed il markup associato) che verrà mostrato dal browser Ad esempio, il listato seguente raffigura come si presenta il sorgente della pagina che stiamo leggendo, che usa il DTD 4.01 Transitional 158 Lo strato applicativo di Internet Alessandro Falaschi <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html lang="it"> <head> <meta content="text/html; charset=ISO-8859-1" http-equiv="content-type"> <title>World Wide Web</title> <link rel="stylesheet" type="text/css" href="../corso.css"> <meta content="Alessandro Falaschi" name="author"> </head> <body> <a href="../laboratorio.html">Laboratorio di Software per le Telecomunicazioni</a> <br> . . . </body> </html> Document Object Model Come mostrato dall'esempio, i tag possono essere nidificati, nel senso che all'interno di un tag di apertura/ chiusura se ne può inserire un altro, e li dentro, un altro ancora. Questo determina la possibilità di interpretare un documento HTML come una struttura ad albero, in cui tutti i tag interni ad un altro, sono rappresentati come discendenti dal primo. Il risultato è il cosiddetto albero DOM (Document Object Model), che ad esempio, nel caso del semplice codie HTML mostrato appresso, dà luogo all'abero mostrato a fianco. Nel caso in cui invece dell'HTML si debba analizzare allo stesso modo un documento XML, allora il parser da impiegare è SAX (Simple API for XML). <!-- My document --> <html> <head> <title>My Document</title> </head> <body> <h1>Header</h1> <p> Paragraph </p> </body> </html> Sebbene inizialmente l'HTML fosse stato pensato per essere scritto a mano, attualmente è abbastanza comune ricorrere a strumenti wysiswyg, come ad esempio (video) (ora divenuto Kompozer) con cui sono realizzate queste pagine; ciononostante, può accadere di dover modificare manualmente una pagina preesistente, e conoscere qualcosa dei possibili elementi può essere molto utile. 159 Markup Language World Wide Web Tipi di elementi Questo corso non ha nessuna pretesa di includere, tra le altre cose, un corso di HTML. Ma chi volesse approfondire l'argomento, può prendere come ottimo punto di partenza, questo sito con le traduzioni italiane del sito di W3C, e che presenta diverso materiale utile, come un tutorial di HTML e CSS, la raccomandazione HTML 4.01, e la tabella degli elementi da questa definiti, che è riportata qui sotto. Legenda: Facoltativo, Proibito, Vuoto, Disapprovato, DTD Transitoria, DTD Frameset Nome Marcatore iniziale Marcatore finale Vuoto Disap. DTD Descrizione A ancora ABBR forma abbreviata (es., WWW, HTTP, ecc.) ACRONYM ADDRESS informazioni sull'autore APPLET D AREA P V BASE P V BASEFONT P V T applet Java area di una mappa immagine lato-cliente B stile di testo grassetto URI di base del documento D T dimensione di base dei caratteri BDO sovrascrive l'algoritmo bidirezionale BIG stile di testo ingrandito BLOCKQUOTE BODY citazione lunga F BR F P corpo del documento V interruzione di riga forzata BUTTON pulsante CAPTION didascalia di tabella CENTER D T CITE abbreviazione per DIV align=center citazione CODE frammento di codice COL P V COLGROUP F colonna di tabella gruppo di colonne di tabella DD F definizione di un termine DEL testo cancellato DFN racchiude una definizione DIR D T DIV elenco di directory contenitore generico di lingua/stile DL elenco di definizioni DT F termine definito EM enfasi FIELDSET gruppo di controlli di un modulo FONT D T FORM cambiamento locale di carattere modulo interattivo FRAME P V FRAMESET F sottofinestra F suddivisione della finestra H1 titolo H2 titolo H3 titolo H4 titolo H5 titolo H6 HEAD titolo F HR HTML F P F F intestazione del documento V riga orizzontale elemento radice del documento 160 Lo strato applicativo di Internet Alessandro Falaschi I stile italico del testo IFRAME T sottofinestra a livello di riga IMG P V immagine incorporata INPUT P V controllo di modulo P V INS testo aggiunto ISINDEX D T campo di immissione a riga singola KBD testo che deve essere inserito dall'utente LABEL etichetta di un campo modulo LEGEND didascalia di un gruppo di campi modulo LI F LINK P elemento di un elenco V un collegamento indipendente dal mezzo MAP mappa immagine sul lato cliente MENU D META P T V elenco di menu metainformazioni generiche NOFRAMES F contenitore di contenuto alternativo per la riproduzione non basata sui frame NOSCRIPT contenitore di contenuto alternativo per la riproduzione non basata su script OBJECT oggetto generico incorporato OL elenco ordinato OPTGROUP gruppo di opzioni OPTION F P F PARAM P opzione selezionabile paragrafo V valore di proprietà denominata PRE testo preformattato Q breve citazione in riga S D T stile per il testo cancellato SAMP esempio tipico di listato di programma, script, ecc. SCRIPT dichiarazioni di script SELECT selettore di scelte SMALL stile di testo rimpicciolito SPAN generico contenitore di lingua/stile STRIKE D T testo cancellato STRONG forte enfasi STYLE informazioni di stile SUB pedice SUP apice TABLE TBODY TD F F corpo della tabella F cella di dati di una tabella TEXTAREA campo di testo su più righe TFOOT F piede della tabella TH F cella d'intestazione di una tabella THEAD F intestazione di tabella TITLE TR titolo del documento F riga di tabella TT U stile di testo di telescrivente o a spaziatura fissa D T stile di testo sottolineato UL elenco non ordinato VAR instanza di una variabile o argomento di un programma Nella precedente tabella, per ogni elemento, si possono individuare gli attributi supportati. Questi ultimi, hanno lo scopo di modificare e/o specificare meglio la funzione dell'elemento, come ad es. href e target, usati con l'elemento <a>, che specificano rispettivamente il link da seguire, e dove aprire la nuova pagina: 161 Markup Language World Wide Web <p>Avete visto le nostre <a href="../gabbie/uccelli.gif" target="_top">gabbie per uccelli</a>?</p> che produrrà il risultato: Avete visto le nostre gabbie per uccelli? Gli elementi che si usano più di frequente, sono • • • • • • • • <a></a> per inserire un iperlink, <p></p> per separare un paragrafo, <b></b> e <i></i> per il grassetto ed il corsivo, <h1>..<h6> per i titoli, <ul> ed <ol> per le liste con i pallini o con i numeri, <li> per gli elementi delle liste, <table> per le tabelle, assieme a <tr> e <td> per definire righe e colonne, e <form> per i campi di inserimento. Un elenco un pò più dettagliato, è presente presso Wikipedia. CSS I fogli di stile a cascata (CSS = Cascading Style Sheets) contengono le dichiarazioni necessarie a definire lo stile (per es. tipo di carattere, colori e spaziature) da applicare ai documenti HTML e XHTML, e la definizione di queste regole è ancora una volta emanata dal W3C. In questo modo, è possibile sviluppare in modo indipendente i contenuti e la formattazione delle pagine, rendendole più omogenee. Ad esempio, non occorre ripetere una specifica di colore per tutti gli elementi simili, con il rischio che qualora si desideri cambiarla, occorra ripetere ovunque la stessa operazione: al contrario, la specifica del colore viene eseguita una sola volta, nel foglio di stile. Sebbene il W3C abbia pubblicato nel 2006 la versione 2.01 delle specifiche CSS, e che si stia lavorando alla versione 3.0, la traduzione italiana che riportiamo è quella della versione 2.0 del 1998, che pure è molto diffusa. Il foglio di stile viene associato alla pagina HTML nella sua sezione <head>, essenzialmente in uno dei modi seguenti: usando un file separato, eventualmente ri-usabile a partire da pagine HTML differenti <html> <head> <title>Esempio</title> <link rel="stylesheet" type="text/css" href="foglio_di_stile.css"> </head> . . oppure in-linea nella stessa pagina per la quale se ne desidera l'applicazione <html> <head> <title>Esempio</title> <style type="text/css"> ...codice css... ...codice css... ...codice css... </style> 162 Lo strato applicativo di Internet Alessandro Falaschi </head> . . Selettori e regole Il codice CSS consiste in una serie di regole, strutturate secondo il seguente schema, in cui il selettore individua quando applicare la regola, che consiste nell'attribuire un certo valore, ad una determinata proprietà: selettore { proprietà1 : valore1; proprietà2 : valore2, valore3; } Il selettore può corrispondere a • un elemento HTML (es h1, p, li..) (selettore di tipo), che indica come le regole si applichino a tutte le istanze di quell'elemento, • una classe (es. selettore = elemento.nome_classe), indicando che le regole si applicano a quegli specifici elementi, per quali è stato dichiarato un attibuto class=nome_classe, ovvero ◦ una classe generica (es. selettore = *.nome_classe, o selettore = .nome_classe), e che indica come le regole si applichino a qualunque elemento per il quale è definito un attributo class=nome_classe; • un identificatore (es. selettore = #nome_identificatore), indicando che le regole si applicano al solo elemento per il quale è definito un attributo id=nome_identificatore. Solo un elemento in tutta la pagina, può avere l'attributo id pari ad un certo valore; • altri casi come pseudo-elementi e pseudo-classi (es selettore = elemento:pseudo), che permettono di individuare ad es. la prima riga di un paragrafo, o la prima lettera, oppure di identificare condizioni particolari, come il passaggio del mouse. Alcuni esempi di uso dei selettori, possono essere trovati presso HTML.it; qui notiamo invece, che le pagine di questo corso usano un CSS specifico, allo scopo di non ripetere i comandi relativi al colore dello sfondo, ed alla dimensione dei caratteri, per tutti i riquadri di codice. Ciò è ottenuto realizzando i riquadri come una tabella ad una sola cella, ed usando per gli elementi <table> e <td>, un attributo class=pre, al quale sono associati i seguenti selettori nel file corso.css utilizzato: table.pre { border: 1pt solid rgb(253, 152, 253); width: auto; background-color: rgb(244, 255, 252); } td.pre { font-weight: normal; font-size: small; font-family: monospace,Courier New,Courier; } 163 Caricare le pagine sul server World Wide Web Proprietà Nell'esempio su riportato, la proprietà border è utilizzata per impostare il bordo della tabella (qualora all'elemento table sia assegnato un attibuto di valore pre) che delimita il riquadro, ad uno spessore di un pixel, a linea continua, e di colore viola, mentre per lo sfondo è scelto il colore celeste, e la larghezza del riquadro dipenderà da quella del contenuto. Quindi, il selettore td.pre determina l'applicazione delle proprietà sui caratteri contenuti all'interno della colonna, stabilendone il peso, la dimensione, ed il tipo di carattere. Pertanto, un riquadro (vuoto!) sarà realizzato mediante un codice HTML pari a <table class="pre" cellpadding="10" cellspacing="0"> <tbody> <tr> <td class="pre"> --- inserire qui il contentuto del riquadro! --</td> </tr> </tbody> </table> Un elenco delle proprietà esistenti, assieme ai loro possibili valori, ed a degli esempio di utilizzo, è fornita assieme alla raccomandazione di W3C, ed è replicata su diversi siti in rete. Tra i riferimenti, elenchiamo alcuni siti dove scegliere liberamente un template di stile (template letteralmente significa sagoma) da usare per dare un aspetto gradevole alle proprie pagine. Caricare le pagine sul server Ora che abbiamo preparato le pagine che il server web dovrà fornire, su richiesta, al browser, resta il problema di come depositarle presso il server. A questo scopo esistono tutta una serie di possibilità, che elenchiamo. FTP Il File Transfert Protocol è uno tra i protocolli più anziani di Internet, specificato nella RFC 959, e permette ad un programma client di colloquiare con un server in ascolto sulla porta TCP 21, al fine di leggere/scrivere/navigare tra i files del suo filesystem. Può essere invocato da linea di comando, impartendo manualmente i comandi di cui dispone, oppure può essere utilizzata una interfaccia grafica, ormai incorporata nelle stesse applicazioni genericamente usate per navigare nel filesystem locale (ossia, del proprio computer), permettendo in questo caso, operazioni di tipo drag and drop. 164 Lo strato applicativo di Internet Alessandro Falaschi In una prima fase del collegamento, l'utente che sta usando il client si autentica presso il server, trasmettendo la password in chiaro, e questa circostanza è uno dei principali motivi per cui l'uso dell'FTP è sconsigliato/ scoraggiato. E' da notare, tuttavia, che esiste anche una modalità anonima di collegamento, in cui il client usa con nome utente anonymous, e come password tipicamente invia il proprio indirizzo email. Vedi, come esempio, questo file di capture.Quando ancora il web non esisteva, questo era il modo in cui venivano messi pubblicamente a disposizione dati e programmi, ed è tuttora in uso, ad esempio come meccanismo di default utilizzato dai browser web, per accedere una URI il cui schema è ftp:. Due ulteriori possibilità, sono quelle relative ad un collegamento attivo o passivo. La differenza nasce dal fatto che l'FTP utilizza in effetti due diverse connessioni TCP, la prima detta di controllo, su cui avviene l'autenticazione, e dove poi sono inviati i comandi di sessione, ed una seconda connessione, instaurata sotto il controllo della prima, su cui avviene il trasferimento dati vero e proprio. Nel caso della modalità attiva, è il server a chiamare il client, un pò per meglio gestire le richieste di servizio nel caso di elevato traffico, ed un pò per essere sicuro di stare parlando con chi si è presentato con l'IP sorgente. In questo caso, il server usa la porta 20 come sorgente del canale dati, e contatta il client sul numero di porta da esso usato in uscita, incrementato di uno. L'ultima delle figure a fianco, prelevate da un articolo pubblicato su qcipc, mostra il risultato finale di un setup attivo. Questa tecnica però dà luogo a problemi, ogni volta in cui tra client server sia interposto un dispositivo NAT, ovvero quando il client posside un indirizzo IP privato, ed il server uno pubblico. Infatti in questo caso, il server non è in grado di aprire una connessione verso il client, e il trasferimento non può avere luogo. Nel caso specificato (presenza di un NAT), si ricorre allora alla modalità di FTP passivo, mostrata nella figura seguente. In questo caso la connessione dati viene aperta su iniziativa del client, e nella prima fase, il server comunica al client un nuovo numero di porta effimera, su cui aprire la connessione dati. Quindi nel caso passivo, è sempre il client ad aprire entrambi i canali (di controllo e dati), e non si presenta nessun problema, anche in presenza di NAT. 165 Caricare le pagine sul server World Wide Web A volte, per garantire una maggiore sicurezza e confidenzialità, il client si trova ad operare in una modalità cosiddetta chrooted, cioè a dire, come se sul server fosse stato eseguito il comando chroot, abbreviazione di change root. In tal caso, il client non può navigare per tutto il filesystem del server, ma solo a partire (e poi, in giù) da quella che sarebbe stata la sua home directory, se si fosse collegato come terminale, sullo stesso computer che ospita il server FTP. Esistono molte implementazioni di client e server FTP. SFTP Lo SSH File Transfer Protocol non è ancora uno standard Internet (l'ultimo Draft è scaduto), ma è utilizzato estensivamente come un vero e proprio protocollo di accesso remoto ad un filesystem. Non consiste nella esecuzione di FTP su di una connessione resa sicura via SSH, ma è un protocollo sviluppato ex-novo, che sfrutta i meccanismi di sicurezza offerti da uno strato che assicura tale funzione, ma l'accoppiata con SSH è particolamente diffusa e utilizzata. NFS Il Network File system venne inizialmente sviluppato nel 1984 da Sun Microsystem, e da allora ha subito diverse evoluzioni, fino alla ultima versione normativa definita dalla RFC 3530. Prevede che un computer -server NFS- offra parte del suo filesystem, per essere montato da altri computer -client NFS-, i quali da quel momento in poi, accedono al disco remoto come se fosse il proprio. E' utilizzato in special modo per realizzare delle stazioni di lavoro diskless, oppure per condividere dati e programmi su più macchine, senza doverli replicare. SMB Il Server Message Block è il protocollo usato dai prodotti Microsoft per condividere in rete files, stampanti, porte seriali e comunicazioni di varia natura, e include un meccanismo di comunicazione autenticata tra processi. Nato per essere usato principalmente su rete locale in associazione al NetBIOS, funziona anche su TCP/IP. Come per il caso precedente, sebbene possa ben essere usato per salvare le pagine web sul disco del server, ha degli scopi più articolati. 166 Lo strato applicativo di Internet Alessandro Falaschi IMAP L'Internet Message Acces Protocol, sebbe nato per la gestione dell'email, può altrettanto bene essere usato per trasferire file da/verso il proprio computer locale ed un computer remoto ! URL, URI, URN Un indirizzo di livello applicativo che identifica una risorsa accessibile mediante HTTP è chiamata URI (Uniform Resource Identifier), come definito nella RFC 3986, ed estende i concetti di URL (Uniform Resource Locator, utilizzato per indicare anche dove trovare la risorsa, e come utilizzarla) ed URN (Uniform Resource Name, utilizzato esclusivamente per dare un nome univoco alla risorsa) precedentemente usati. La generica sintassi di una URI <scheme>:<scheme-specific-part> ne permette l'uso per identificare non solo un indirizzo web, ma anche (ad es.) un indirizzo email, un identificativo VoIP, un codice ISBN, o tante altre cose, in base al tipo di schema che vi compare, in accordo a quelli registrati presso lo IANA. Lo schema è indicativo del namespace sotto cui ricade la risorsa, e la sintassi della parte scheme-specific-part dipende dallo schema usato, ma per parecchi di questi, ci si può riferire a questo generico esempio relativo ad una URL HTTP: in cui • protocol è un caso particolare di nome di schema, che identifica effettivamente il nome del protocollo applicativo usato; • la coppia user:password, se presente, può essere usata dal servizio, come ad es. per accedere ad un server FTP (se il protocol, è ftp:); • il gruppo user:password@domain:port è anche indicato come authority, ed indica dove si trova la risorsa, e come accedervi; • la porta indica l'indirizzo di trasporto da utilizzare per accedere al servizio che supporta il protocollo indicato, e se assente, è posta pari a quella registrata per lo stesso presso lo IANA; • il path identifica una particolare risorsa sotto il controllo della authority, e può essere assente; • la query è presente in associazione con meccanismi di tipo CGI che fanno uso del metodo GET, e dopo il punto interrogativo, presenta una serie di coppie <chiave>=<valore>, separate da &, e che rappresentano altrettanti parametri formali; • l'anchor identifica una sottoparte specifica nell'ambito di una unica risorsa indivisible, come un punto particolare nell'ambito di una pagina web. Quando una URI compare come valore di un attributo href di un elemento anchor in una pagina HTML, il browser tenta di dereferenziare l'indirizzo, intraprendendo una azione legata allo schema che compare nella URI. Se ad esempio si tratta di un'altra pagina web, ne farà richiesta al server che la ospita, e visualizzerà il body della risposta, mentre se ad esempio si tratta di uno schema mailto (es. mailto:[email protected]), manderà in esecuzione 167 HTTP World Wide Web l'applicazione-client email predefinita, impostando il destinatario a quello che compare nella URI. HTTP L'Hyper Text Transfer Protocol è definito nella sua versione attuale (1.1) dalla RFC 2616, di cui è in corso una attività di revisione documentata in un Draft. La definizione della sintassi dei suoi messaggi è descritta per mezzo di un formalismo chiamato forma aumentata di Backus-Naur (ABNF). ABNF Un breve parentesi, per illustrare come la ABNF è una sintassi formale orientata alla descrizione di grammatiche context-free, e che funziona mediante l'applicazione ripetuta di regole di produzione del tipo simbolo = espressione che indicano come dei simboli non terminali (a sinistra dell'uguale) possono essere riscritti in termini di sequenze di altri (terminali o meno). Partendo da un simbolo non terminale, si applicano ripetutamente le regole, finché non restano solo simboli terminali, per i quali non esistono regole di riscrittura, ed il risultato forma una frase appartenente al linguaggio che si sa descrivendo. L'espressione può indicare il semplice concatenamento di altre regole e terminali, oppure l'alternativa tra essi terno = numero numero numero / ambo numero ambo = numero numero numero = 1 / 2 / 3 / 4 / 5 / 6 / 7 / 8 / 9 / 0 mentre altre notazioni hanno altri significati, come ad esempio un [elemento] tra parentesi quadre è opzionale, un asterisco come in *ALPHA indica la ripetizione di un numero qualsiasi di ALPHA. L'ABNF è spesso usata per descrivere i linguaggi di programmazione, e i formati dei protocoli Internet, e per questo, è documentata nella RFC 5234, ed è stata ulteriormente riassunta, nello stesso documento che descrive l'HTTP. Da estremo a estremo La figura che segue mostra il meccanismo di richiesta/risposta (client/server), a partire da un click su di una pagina, al prelievo dei files dal sever web, ed alla comparsa del risultato sullo schermo. Lo User Agent (browser) nel ruolo di client, invia una richiesta HTTP su di una connessione TCP aperta verso l'indirizzo di trasporto corrispondente alla porta 80, ed il server restituisce sulla stessa connessione la risposta. 168 Lo strato applicativo di Internet Alessandro Falaschi Vi sono quindi due tipi di messaggi HTTP: di richiesta e di risposta. Tanto che la ABNF per l'HTTP, inizia così: generic-message = start-line *(message-header CRLF) CRLF [ message-body ] start-line = Request-Line | Status-Line Nelle prime versioni del protocollo, la connessione veniva chiusa immediatamente dopo che una richiesta era stata soddisfatta, in accordo alla natura senza stato (stateless) del protocollo: il server non mantiene memoria delle richieste precedenti, anche se provenienti dallo stesso client, ma le tratta tutte in modo indipendente l'una dall'altra. D'altra parte, nel caso in cui nella pagina ricevuta siano presenti elementi HTML (come ad es. di tipo <img>), che referenziano oggetti (immagini) da inserire nella pagina da visualizzare, queste vengono allora richieste subito di seguito alla pagina ricevuta. Una buona parte dei siti che visitiamo correntemente, estende questo paradigma, oltre che al prelievo di files, anche all'esecuzione di codice dal lato server, che in quel caso, prende il nome di Application server. Per riassumere, un video. Richiesta In accordo allo schema su riportato, un messaggio di richiesta HTML è composto da tre parti: • riga di richiesta 169 HTTP World Wide Web • sezione header, contenente informazioni aggiuntive, separata dalla sezione seguente da una linea vuota • body, o corpo del messaggio, che nelle richieste è quasi sempre assente La riga di richiesta ha l'aspetto generico descritto dalla ABNF, a cui è affiancato un esempio reale Request-Line es. = Method SP Request-URI SP HTTP-Version CRLF GET /wiki/Pagina_principale HTTP/1.1 ed è composta dal metodo (qui, GET), dal nome della risorsa oggetto della richiesta (/wiki/ Pagina_principale) così come compare nella URL che ha prodotto la richiesta stessa, e dal nome del protocollo con la sua versione. In generale, la risposta viene generata interpretando la risorsa come il nome di un file, completo di path relativo, che giace sul disco del computer che ospita il server, sotto una apposita directory. Metodi Dei metodi possibili descritti dalla RFC, i più frequentemente usati sono • GET - in assoluto il più frequente, serve a chiedere una pagina od una risorsa • POST - oltre a referenziare una risorsa, invia i dati che sono stati raccolti mediante un modulo di inserimento (form) collocandoli nel body. Anche con GET si possono inviare dei dati, ma sotto forma di parametri aggiuntivi nella stessa stringa che identifica la risorsa, collocandoli dopo un punto interrogativo, come ad es. name=ferret&colour=pink in http://example.com/over/ there?name=ferret&colour=pink. La differenza fondamentale, è che con GET si potrebbe generare una stringa di parametri troppo lunga, mentre con POST, tutte le coppie nome=valore che con il GET sono separate da &, vengono invece scritte nel BODY, una per linea • HEAD - produce una risposta identica a quella che si otterrebbe con GET, ma senza ricevere il body associato alla risposta. E' utile per ricevere le meta-informazioni presenti negli header, senza scaricare l'intero contenuto. • OPTIONS - richiede di conoscere i metodi HTTP supportati dal server. Intestazioni di Richiesta Il formato delle intestazioni segue quello specificato per il caso delle email, ossia il nome dell'header, seguito da due punti, e poi una stringa che ne specifica il valore. Mentre nelle email, però, gli header sono a prevalente beneficio del ricevente, nell'HTTP sono realmente funzionali al protocollo, e sono presenti sia nelle richieste, che nelle risposte. L'insieme completo dei Request Header che possono comparire nelle richieste è specificato nella RFC. A partire da un esempioconcreto, possiamo discutere di • Host: tools.ietf.org - la parte della URI relativa al computer ed alla porta dove si trova la risorsa richiesta; • User-Agent: Mozilla/5.0 etc etc - il tipo di browser che utilizziamo: il server potrebbe fornire pagine differenti in base al client; • Accept: text/xml,application/xml,application/xhtml+xml,text/ html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5 - indica i tipi di media (nei termini della classificazione MIME) che il browser è disposto ad accettare, usando il parametro q per indicare un grado di preferenza; 170 Lo strato applicativo di Internet Alessandro Falaschi • Accept-Language: it,en;q=0.7,en-us;q=0.3 - indica la preferenza per la lingua della risorsa che stiamo richiedendo, nel caso in cui il browser ne abbia diverse traduzioni; • Accept-Encoding: gzip,deflate - il nostro browser accetta i contenuti compressi; • Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7 - indica i character encodig che si è disposti a ricevere, e la rispettiva preferenza; • Connection: keep-alive - indica che si desidera mantenere la connessione aperta anche dopo la ricezione della risorsa, in modo che se si continua ad interrogare lo stesso server, non si deve eseguire nuovamente il three-way-handshake; • Keep-Alive: 300 - specifica di voler mantenere la connessione aperta per 300 secondi (5 minuti) • Referer: http://en.wikipedia.org/wiki/URI_scheme - indica la pagina che il browser stava visualizzando, quando è partita questa richiesta Risposta E' composta di tre parti: • Status Line di risposta • sezione header • body, separato con uno spazio dagli header Il body è presente nel caso in cui il codice di risposta indica un successo, ed è posto di seguito alla sezione degli header, separato da questi da una linea vuota, come per l'email. Codici di risposta La prima riga della risposta ha un aspetto del tipo HTTP/1.1 200 OK ed il codice di risposta (nell'esempio, 200 OK) segue le convenzioni già viste per l'email, in cui la prima cifra dà una indicazione relativa all'esito della richiesta, in accordo allo schema. La descrizione dei casi d'uso dei diversi codici è contenuta nella RFC 2616, e wikipedia ne presenta un elenco sintetico. Alcuni codici di risposta particolarmente significativi sono • 200 OK - la richiesta ha avuto successo; • 301 Moved Permanently - la risorsa che abbiamo richiesto non è raggiungibile perché è stata spostata in modo permanente, e la sua nuova URI è indicata nell'header Location. Di norma i browser eseguono la richiesta del nuovo URI in modo automatico, senza interazione dell'utente; • 302 Found - la risorsa è stata temporaneamente spostata presso un'altro URI, anche qui indicato da Location; • 304 Not Modified - usato a seguito di una richiesta condizionata, nel caso in cui la copia della risorsa disponibile presso il server sia allineata con quella che risiede nella cache del client; • 400 Bad Request - la risorsa richiesta non è comprensibile al server; • 401 Unauthorized - simile a 403/Forbidden, ma usato quando è richiesta autenticazione, e questa è fallita; • 403 Forbidden - la risorsa referenziata esiste, ma i privilegi non sono sufficienti; 171 HTTP World Wide Web • 404 Not Found - la risorsa richiesta non è stata trovata, e non se ne conosce l'ubicazione. Di solito si verifica quando l'URI è stato indicato in modo errato, oppure il contenuto è stato rimosso dal server; • 500 Internal Server Error - il server non è in grado di rispondere alla richiesta per un suo problema interno. • 505 HTTP Version Not Supported - la versione di HTTP non è supportata. Intestazioni di risposta Commentiamo assieme il capturerelativo http://infocom.uniroma1.it/alef/tesi/. alla richiesta ripetuta della URI • Date: Tue, 29 May 2007 21:31:20 GMT - è la data in cui viene servita la pagina • Server: Apache/2.0.46 (Red Hat) - il tipo di server web che ha trasmesso la risposta • Last-Modified: Fri, 09 Dec 2005 16:43:23 GMT - la data in cui è avvenuta l'ultima modifica alla pagina • ETag: "808071-1bd-156520c0" - una etichetta (Tag) della Entità richiesta, che il server usa per identificare la versione della risorsa inviata, e che verrà utilizzata successivamente, nell'ambito dei meccanismi di caching. Ad esempio, il server web Apache, di default, calcola l'ETag in base agli attributi associati al file corrispondente alla risorsa richiesta, come data di modifica, dimensione, iNode • Accept-Ranges: bytes - indica che il server è disposto ad accettare richieste parziali delle risorse, specificate mediante l'header di richiesta Range, indicando l'intervallo di bytes desiderato • Content-Length: 445 - la dimensione in bytes del body • Connection: close - il tipo di connessione offerta dal server. In questo caso, alla fine di ogni risposta, viene chiusa la connessione TCP utilizzata • Content-Type: text/html - specifica il Media Type (coincidente con quelli definiti da MIME) della risorsa contenuta nel body Incapsulamento risultante In definitiva, una richiesta o risposta HTTP che entrasse per intero in uno stesso segmento TCP, e nel caso fosse una risposta, contenesse una pagina HTML, avrebbe la composizione mostrata di seguito: Ethernet IP TCP Request o Status line e Body HTTP Header HTTP Head HTML Body 172 HTML Lo strato applicativo di Internet Alessandro Falaschi Caching Nella navigazione web sono presenti una o più cache intermedie, che hanno lo scopo di alleggerire il carico dei server. Una prima cache è interna al browser, ma in rete possono esserne dislocate altre, che prendono il nome di Proxy, a cui il client può indirizzare tutte le richieste (dovunque dirette), e che si fa carico di effettuare le richieste per suo conto, e di fornire la risposta. Se le risorse richieste giacciono già nella cache (del proxy, o del browser), queste possono essere restituite direttamente, senza recuperarle nuovamente dal server di origine, che quindi riduce l'occupazione di banda di uscita. Se diversi client usano lo stesso proxy, e referenziano le stesse pagine, il risparmio di banda può diventare considerevole, e può convenire dislocare un proxy interno ad una sottorete, da far condividere a tutti i client di quella sottorete. Il meccanismo con cui si tenta di accedere direttamente alle repliche presenti in cache, è quello di validazione, ottenuto rendendo i metodi di richiesta condizionali, mediante l'inserimento degli header • If-Modified-Since - viene citata la data che era presente nel Last-Modified ricevuto • If-Match - viene citato il valore di Etag che era stato associato alla risorsa ricevuta In questo modo, se il server verifica che l'oggetto è sempre uguale (ossia, la data è la stessa, e/o l'Etag coincide), risponde con un codice 304 Not Modified, ed il browser (il proxy) visualizza (invia) il contenuto della propria cache. Un esempio di tale comportamento, può essere riscontrato nel file di capturegià utilizzato. Un secondo meccanismo di gestione delle cache si basa invece su di una logica di obsolescenza (expiration), fondata sull'uso di alcuni header specifici, come • Expires - fornito nelle risposte, indica fino a quando (si presume) che l'oggetto abbia validità • Age - è inserito tipicamente da un proxy intermedio quando accede alla sua cache, ed esprime l'anzianità (in secondi) dell'oggetto restituito • Cache-Control - può assumere uno tra diversi valori, ed essere presente sia nelle richieste (no-cache, no-store, max-age) che nelle risposte (no-cache, nostore, max-age, must-revalidate) Quando un client sta per effettuare una richiesta di un oggetto che ha in cache, ma che non è ancora spirato, può scegliere di rinunciare ad effettuare la richiesta, e visualizzare la sua copia locale. Al contrario, un server può tentare di impedire che un oggetto da esso inviato venga memorizzato nelle cache, usando i valori no-cache, no-store, must-revalidate, 173 HTTP World Wide Web come ad esempio avviene nel caso di pagine dinamiche ottenute mediante l'esecuzione di CGI. Intercepting e Reverse Proxy Mentre tutti i browser hanno la possibilità di configurare volontariamente un proxy a cui inoltrare le richieste, può succedere che i router della LAN o del provider provvedano di loro iniziativa a reinstradare i pacchetti IP associati a comunicazioni HTTP, in modo che attraversino un Proxy. Questa funzionalità prende il nome di Intecepting Proxy. Un Reverse Proxy al contrario, invece di essere usato per uscire, è usato per smistare il traffico in ingresso ad una lan, verso una pluralità di web server interni, ognuno che serve pagine per un diverso dominio, e potenzialmente configurati con un IP privato. Questo ha lo scopo di migliorare la sicurezza, di permettere il bilanciamento del carico, oltre naturalmente a realizzare il caching delle pagine in uscita. Redirezione Le risorse associate agli URI presenti su determinate pagine web, possono essere trasferite su computer differenti, e quindi cambiare dominio, oppure possono essere ri-nominate, e pur rimanendo sullo stesso server (e dominio), prendere un URI diverso. In tal caso, le richieste che citano il vecchio indirizzo, si vedrebbero restituire una risposta del tipo 404 Not Found, producendo disappunto e frustrazione. La soluzione offerta da HTTP si basa su di un meccanismo di redirezione, che usa i codici di risposta 301-303 e 307, e che comunica il nuovo indirizzo come valore associato all'header Location presente nella risposta. La differenza tra i codici di risposta, risiede nelle possibilità che il nuovo indirizzo sia temporaneo o permanente (nel qual caso, i collegamenti preferiti dovrebbero aggiornarsi automaticamente), e nella possibilità che il browser segua automaticamente il nuovo indirizzo, oppure chieda prima conferma all'operatore umano. Affinché il server verso cui sono dirette le richieste per risorse che non ci sono più, risponda con un codice di tipo 3xx Moved anziché 404 Not Found, il server stesso deve essere debitamente configurato in tal senso. Negoziazione dei contenuti L'HTTP permette la negoziazione dei contenuti, consentendo allo User Agent di esprimere le proprie preferenze a riguardo delle diverse possibili versioni degli oggetti richiesti, ed al Server di decidere. Oppure, al contrario, si consente al Server di offrire una scelta di 174 Lo strato applicativo di Internet Alessandro Falaschi possibilità, ed allo UA di decidere quale utilizzare. Queste preferenze vengono espresse mediante l'uso di appositi header nella richiesta, come Accept, Accept-Charset, AcceptEncoding, Accept-Language, e User-Agent. Il server quindi, utilizza l'header di risposta Vary per specificare su quali dimensioni si è effettuata la scelta del tipo di contentenuto da inviare. Per una descrizione dei metodi usati per gestire la fase di negoziazione dei contenuti, ci si può riferire alla documentazione relativa al server Web Apache. Connessioni Persistenti Quando uno UA invia più richieste consecutive verso uno stesso server, come nel caso, ad esempio, in cui si vogliano scaricare tutte le immagini contenute in una determinata pagina appena ricevuta, è molto più sensato ri-utilizzare sempre la stessa connessione TCP, piuttosto che aprirne una diversa, per ogni diverso oggetto richiesto. In tal modo, oltre a risparmiare sui tempi necessari al Round Trip Time necessario per l'apertura e la chiusura delle connessioni, si riduce l'occupazione di memoria necessaria ai buffer del TCP, e la gestione del controllo di congestione attuata dal TCP, può evolvere su di un intervallo temporale più ampio. Pipeline Un client può inviare le richieste in pipeline, ossia una di seguito all'altra, senza attendere la fine della risposta alla richiesta precedente; in tutti i modi, è prescritto che il server invii le risposte nello stesso ordine con cui sono pervenute le richieste. Header Connection Nella versione 1.1 di HTTP, le connessioni persistenti sono il funzionamento di default, che può essere sovvertito qualora il client (il server), invii assieme alla richiesta (risposta) l'header Connection: Close. Viceversa, per compatibilità con versioni precedenti del protocollo, il desiderio esplicito di usare connessioni persistenti può essere segnalato mediante l'header Connection: KeepAlive. Un esempio di utilizzo delle conessioni persistenti, è dato dal file di capturerelativo alla richiesta della URI http://www.ubuntu-it.org/index.php?page=Filosofia. 175 HTTP World Wide Web Compressione e Transfer-Encoding Qualora la richiesta lo permetta (dichiarando tramite l'header Accept-Encoding della richiesta, uno o più algoritmi scelti tra gzip, compress, e deflate), il body della risposta HTTP può contenere un oggetto che è memorizzato, e trasmesso, in forma compressa, ed in tal caso l'header Content-Encoding esprimerà quale sia stato l'algoritmo di compressione scelto. Viceversa, l'oggetto richiesto può essere memorizzato presso il server in formato nativo, e compresso solo al momento della trasmissione: questa circostanza, viene segnalata per mezzo dell'header Transfer-Encoding, che oltre ai valori prima elencati, prevede anche la codifica chunked. Chunked transfer encoding Più codifiche di trasferimento, possono essere applicate in cascata, ma chunked deve essere applicata per ultima, perchè questa anziché realizzare una compressione, segmenta il messaggio in blocchi (chunks). Questa tecnica è usata quando il server non conosce a priori la dimensione dell'oggetto inviato, perché questo è il risultato di un calcolo ancora in corso, oppure perché si tratta di un contenuto audio/video generato in tempo reale. Al fine di evitare il calcolo della dimensione totale dell'oggetto, questo viene inviato in modalità chunked, con ogni chunk che all'inizio contiene la dichiarazione della lunghezza del chunk. Sicurezza Nei termini definiti al capitolo precedente, la sicurezza in http è volta ad offrire uno o più dei seguenti servizi: • restrizione dell'accesso a determinati contenuti, ai soli utenti autenticati; • autenticazione del server nei confronti dello UA, in modo da verificare l'autenticità della fonte; • riservatezza dei dati trasmessi, che potrebbero contenere dati sensibili e/o monetari e/o ulteriori credenziali di sicurezza. Mentre per i secondi due punti, si ricorre essenzialmente alla attivazione del TLS, il primo punto, anche se potenzialmente gestibile con meccanismi appositamente definiti per l'HTTP, viene generalmente implementato a cura di uno strato applicativo ancora superiore all'HTTP, mediante le tecniche dei CGI, dei cookie, o del javascript. Ma iniziamo, comunque, dal primo punto. Autenticazione Il meccanismo di autenticazione previsto da HTTP si basa su di uno schema challengeresponse, in cui il server dopo aver ricevuto la richiesta di un oggetto per il quale è stato ristretto l'accesso, anziché inviare la risposta contenente l'oggetto, emette una risposta con la quale sfida il client ad autenticarsi; questi sottomette quindi una nuova richiesta, a cui allega le proprie credenziali. Sono previste due modalità di autenticazione, dette Basic e Digest, descritte nella RFC 2617, entrambe basate sulla conoscenza di un segreto (ossia, di una coppia username-password) condiviso tra client e server. Basic Access Authentication Questo schema è da considerare assolutamente insicuro, e da usare solo in un ambito del tutto fidato, in quanto la password è inviata in chiaro, seppur con codifica base64. La prima 176 Lo strato applicativo di Internet Alessandro Falaschi risposta (di sfida) del server, che riporta un codice 401 Unauthorized, contiene un header WWW-Authenticate: Basic realm="Laboratorio di Cisterna" Il browser allora, presenta all'utente una finestra in cui cita il valore del Realm (in modo che l'utente possa ricordare quali credenziali usare), e richiede l'immissione di una coppia utente-password. Ad esempio, l'identità dell'utente "Aladdin" con password "open sesame" saranno combinati assieme com "Aladdin:open sesame", e quindi trasmessi con codifica base64, come QWxhZGRpbjpvcGVuIHNlc2FtZQ== (come verifica, eseguire da terminale il comando echo QWxhZGRpbjpvcGVuIHNlc2FtZQ== | base64 -d). Pertanto, dopo l'immissione di utente e password da parte di chi siede di fronte al computer, il browser reitera la richiesta precedente, aggiungendo alla stessa una intestazione Authorization: Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ== che questa volta produce nel server l'invio della pagina protetta. Un esempio del comportamento descritto, si può ottenere visitando questa pagina protetta, (username/ password = labsoftel/cisternalab) a cui corrisponde questo filedi capture. Digest Access Authentication Dal punto di vista dell'utente, il funzionamento di questo schema è del tutto identico al precedente; al contrario di quello, però, la password viene inviata in una forma crittograficamente sicura. Anche ora il server risponde alla prima richiesta, con una risposta che riporta il codice 401 Unauthorized, ma che stavolta contiene un header di sfida del tipo WWW-Authenticate: Digest realm="[email protected]", qop="auth,auth-int", nonce="dcd98b7102dd2f0e8b11d0f600bfb0c093", opaque="5ccc069c403ebaf9f0171e9517f40e41 Mentre il realm ha come prima lo scopo di essere mostrato all'utente, il nonce viene usato assieme al nome utente, alla password, al realm, al metodo, all'URI della risorsa, ad un contatore di richiesta (nc), al codice di quality of protection (qop) prescelto, e ad un nonce del client (cnonce), per calcolare (mediante il calcolo ripetuto di un hash MD5), il valore della response da inserire nell'header Authorization da restituire al server: Authorization: Digest username="Mufasa", realm="[email protected]", nonce="dcd98b7102dd2f0e8b11d0f600bfb0c093", uri="/dir/index.html", qop=auth, nc=00000001, cnonce="0a4f113b", response="6629fae49393a05397450978507c4ef1", opaque="5ccc069c403ebaf9f0171e9517f40e41" Il server usa la stessa password (o meglio, una sua versione crittografata) per effettuare lo stesso calcolo, ed autenticare l'utente. 177 Dal lato del browser World Wide Web SSL/TLS Qualora si desideri un livello di sicurezza maggiore di quello offerto dal semplice uso della autenticazione HHTP, si può ricorrere al Secure Sockets Layer (SSL), ovvero alla sua più recente denominazione TLS. In tal caso, la URI presenta una schema di tipo https, che causa l'apertura di una connessione TCP verso la porta 443 anziché la 80. A quel punto, un messaggio di Client_Hello (capture) determinal'inizio dell'handshake protocol, al termine del quale l'applicazione prosegue come se si trattasse di una normale connessione HTTP su TCP, mentre invece tutto il traffico si svolge in modalità crittografata, come descritto nella RFC 2818. Nelle esercitazioni, troviamo la descrizione di come dotare il nostro server web Apache di un certificato X.509, ed attivare un host virtuale che risponda all'HTTPS. Header Upgrade Anzichè usare una diversa porta come per il caso dell'https, la RFC 2817 descrive l'uso di un codice di risposta, 426 Upgrade Required, e dell'header Upgrade, tali da permettere ad una connessione HTTP normale, di passare in una modalità protetta da TLS, sempre usando la stessa porta 80. Questo, oltre che permette di risparmiare un indirizzo di trasporto, consente di ospitare dei server virtuali sicuri su di un medesimo computer, senza necessità di assegnare allo stesso molteplici indirizzi IP. Dal lato del browser Dopo aver illustrato molti dettagli dell'HTTP, svolgiamo un approfondimento di ciò che accade ai due estremi della navigazione web, iniziando da quando una risposta giunge al browser, e come possano entrare in gioco meccanismi differenti dalla semplice visualizzazione della pagina ricevuta. Quindi, illustriamo come possono essere gestiti gli schemi diversi da http. Infine, discutiamo come si possano inviare, assieme ad una richiesta HTTP, anche alcuni dati intenzionalmente immessi dall'operatore umano. Gestione dei Mime-Type ed embedding La risposta HTTP contiene, tra gli altri, l'header Content-Type, che identifica il documento trasportato nel body, come aderente ad uno dei tipi MIME registrati presso IANA. Alcuni di questi sono direttamente visualizzabili dal browser, mentre per altri, viene invocata una Helper Application, posta in corrispondenza al tipo di file dal sistema operativo stesso, oppure memorizzata a seguito di una domanda a riguardo, posta all'utente. Ma non sempre un server web invia un header Content-Type (perchè ciò avvenga, deve essere opportunamente configurato), e così a volte il browser deve tentare di assegnare per suo conto un Mime Type al file ricevuto, in base all'estensione del file, oppure analizzando l'inizio dello stesso. Se tutti i tentativi falliscono, il file viene trattato come application/ octet-stream, e viene suggerito di salvarlo su disco. Plugin Un volta determinato con successo il Mime-Type dell'oggetto ricevuto, e individuata l'applicazione Helper idonea a visualizzare/riprodurre lo stesso, questa può essere eseguita in modo indipendente, o esterno, al browser, oppure direttamente all'interno della finestra del browser. In questo secondo caso, il programma Helper prende il nome di plugin, in quanto non può essere eseguito in modo indipendente, dipendendo dai servizi offerti dal 178 Lo strato applicativo di Internet Alessandro Falaschi browser stesso, come ad esempio l'interfaccia grafica. A volte invece, un plugin ha il solo scopo di far eseguire una applicazione preesistente, che altrimenti verrebbe eseguita esternamente al browser, all'interno dello stesso. La configurazione attuale dei plugin del browser Firefox, ad esempio, può essere visualizzata immettendo l'indirizzo about:plugins nella sua barra degli indirizzi. Protocolli registrati A volte il programma da usare per visualizzare/riprodurre un oggetto richiesto mediante web, non è determinato al momento dell'arrivo della risposta, bensì al momento in cui viene cliccata la URI presente in una pagina web. Se la URI fa riferimento ad uno schema che non è http, allora il browser non invierà nessuna richiesta http, ed invocherà invece un programma apposito, passandogli la URI come argomento, delegando a questo il compito di procedere, in accordo al protocollo associato alla URI. Ad esempio, cliccando una URI per la quale lo schema è mailto:, verrà mandato in esecuzione lo UserAgent di invio email, nella cui finestra di composizione troveremo pre-impostate le informazioni relative all'indirizzo di destinazione, come desunte dal resto della URI (vedi anche RFC2368). Configurazione di Firefox In Firefox, le preferenze per quanto riguarda le modalità di navigazione e di gestione delle informazioni ricevute possono essere impostate inserendo l'indirizzo about:config nella barra degli indirizzi; il risultato, oltre a visualizzare i valori impostati agendo sul menù delle preferenze, ne visualizza anche molti altri, definiti in accordo a queste regole di configurazione. In particolare, • le preferenze della serie network.protocol-handler.* sono quelle che permettono di specificare il comportamento del browser in corrispondenza dei protocolli (schemi) che vengono referenziati nelle pagine web, dopo che per questi è stata registrata l'applicazione che li gestisce; • la preferenza network.http.pipelining e collegate, permette di definire il comportamento per ciò che riguarda le connessioni persistenti; • la preferenza network.dns.disableIPv6 consente di disabilitare le query al DNS per indirizzi IPv6, che in pratica sono quasi per nulla diffusi, velocizzando così la navigazione. Esecuzione codice A volte la risposta HTTP, anziché contenere un oggetto (come ad es. una pagina HTML, una immagine, od un contenuto multimediale) che deve essere semplicemente mostrato dal browser (o da un plugin, o da una helper application), consegna allo UA del vero e proprio codice eseguibile, rendendo così il web, un meccanismo di distribuzione di applicazioni, ossia un application server. Fin qui nulla di straodinario, tranne per il fatto che il codice ricevuto deve essere eseguito indipendentemente dal S.O. presente sul computer presso il quale è utilizzato il browser, ed a questo fine, sono state sviluppate le soluzioni proposte con Java, Javascript e Flash, brevissimamente descritte appresso. 179 Dal lato del browser World Wide Web Java Il linguaggio Java è stato definito da Sun Microsystem, che recentemente l'ha rilasciato con licenza GPL, ma che fin dal 1998 ha adottato un modello di sviluppo aperto indicato come Java Community Process. Java è basato su oggetti, e presenta una sintassi abbastanza simile al c++ (video corso in italiano). La compilazione del codice sorgente Java produce un eseguibile detto bytecode che, anziché essere legato ad un particolare tipo di ambiente hardware/software, deve essere eseguito da un altro programma, denominato Java Virtual Machine (JVM), che costituisce lo strato software che effettivamente interagisce con il computer ospite. Pertanto, se il bytecode è uno, di macchine virtuali ce ne sono tante, quante sono le architetture alternative. Le versioni di JVM più recenti prevedono un ulteriore passo di elaborazione del bytecode, detto compilazione Just In Time (JIT), e che produce il codice macchina relativo alla architettura ospite, direttamente al momento della esecuzione. In una pagina HTML possono essere presenti diverse applet java, inserite nella stessa mediante l'elemento HTML <object>. Il nutrito insieme di librerie di classi disponibili per Java, permette di scrivere applicazioni (dette anche applet) di ogni tipo, anche con accesso alla rete, e che una volta ricevute tramite web, sono eseguite in una cosiddetta sandbox, in modo da restringere i diritti di uso ed accesso alle risorse del computer ospite. JME La Java Micro Edition è la specifica di un sottoinsieme della piattaforma Java, idoneo per lo sviluppo di applicazioni da utilizzare nell'ambito di dispositivi con prestazioni ridotte, come ad es. i telefoni cellulari, ed il cui codice sorgente è stato rilasciato sotto licenza GPL con il nome di PhoneMe; dato che l'ambiente può essere emulato su computer, lo sviluppo di nuove applicazioni mobili è particolarmente facilitato. Le funzionalità di cui una applicazione JME può usufruire sono descritte dalla applicazione di un particolare profilo, alla configurazione della JME di cui è equipaggiato il dispositivo su cui l'applicazione dovrà essere eseguita. Ad esempio, la Connected Limited Device Configuration (CLDC) contiene un sotto-insieme minimo di librerie di classi Java, e quando accoppiata alla Mobile Information Device Profile (MIDP), viene definito un ambiente dotato di una API alla interfaccia grafica (GUI), e di una API orientata alla esecuzione di giochi bi-dimensionali: le applicazioni scritte per questo profilo, sono dette MIDlets. Javascript Mentre per Java, il bytecode ricevuto deve essere eseguito da una JVM (che non è detto sia installata), il codice Javascript è inviato in forma di sorgente, è molto semplificato rispetto a Java (con cui non ha neanche molto a vedere, se è per quello), e viene eseguito direttamente dal browser. Il sorgente viene così interpretato riga per riga dallo UA stesso; per mantenere compatibilità tra i diversi UA, Javascript è stato sottoposto ad un processo di standardizzazione da parte di ECMA, riconosciuto anche da ISO, e che gli ha portato il secondo nome di ECMAscript. Ciononostante, le implementazioni di Javascript su browser diversi sono parzialmente incompatibili, ed il codice Javascript deve fare del suo meglio per tentare di capire dove si trova, ed evitare di eseguire istruzioni che potrebbero essere interpretate per il verso sbagliato. 180 Lo strato applicativo di Internet Alessandro Falaschi L'uso principale di Javascript è stato quello di produrre particolari effetti grafici non ottenibili con il solo HTML, ma che attualmente sono possibili utilizzando il CSS. Javascript viene altresì usato per modificare al volo una pagina HTML ricevuta, prima che questa venga visualizzata, intervenendo direttamente su di una sua rappresentazione interna al Browser, nota con il nome di Document Object Model (DOM). Presso la sezione dei riferimenti troviamo alcuni interessanti links a siti che consentono di sperimentare la programmazione Javascript modificando direttamente degli esempi già pronti. AJAX Rappresenta l'acronimo di Asynchronous JavaScript and XML, e individua l'utilizzo congiunto di diverse tecnologie per realizzare pagine web interattive che si comportano in maniera sensibilmente più pronta di quelle tradizionali, in quanto il ciclo richiesta-risposta non avviene più solo ad opera dell'utente, ma è il browser stesso a condurre una dialogo asincrono con il Server, mentre l'utente interagisce con la rappresentazione dei dati mostrata dal browser. Ad esempio, nel caso in cui si richieda un diverso ordinamento di una tabella dati, anziché chiedere una nuova pagina al server, con i dati ordinati in modo diverso, i dati sono riordinati direttamente dal browser. 181 Dal lato del browser World Wide Web Flash Nel 1995 viene distributo FutureSplash, come un plugin capace di produrre grafica vettoriale animata all'interno del browser. La ditta originaria viene acquisita nel '96 da Macromedia, sviluppatrice del concorrente Shockwave, e i due prodotti vengono fusi in Macromedia Flash; a partire dal 2005, i diritti sulla tecnologia sono acquisiti da Adobe Systems. Il plugin Flash viene prodotto per architetture Windows, Macintosh e Linux, e rappresenta una sorta di browser nel browser. Le animazioni Flash vengono definite per mezzo del linguaggio interpretato ActionScript, molto simile al Javascript, e che viene eseguito da una Actionscript Virtual Machine, che nella sua realizzazione più recente dispone anche di un compilatore Just In Time. La grande diffusione di Flash in moltissimi siti web ne ha favorito la crescita, ed attualmente è la tecnologia in assoluto più usata per la diffusione di contenuti multimediali via web. Riempimento moduli In HTML è definito l'elemento <form> (modulo o modello), tra i cui tag di apertura e chiusura, oltre ad altri elementi HTML qualsiasi, si possono inserire elementi particolari detti 182 Lo strato applicativo di Internet Alessandro Falaschi controlli, che vengono visualizzati dal browser come campi di inserimento all'interno di moduli. Per fissare fin da subito le idee, qui appresso è mostrata la visualizzazione corrispondente al codice mostrato a fianco, da verificare con l'aiuto della descrizione semantica dei controlli. codice HTML <FORM method="get" action="bar.php"> <TABLE border="1"> <TR bgcolor="#CCCCFF"> <TH>Nome</TH> <TH>Valore</TH> </TR> <TR> <TD>Ti chiami</TD> <TD><input name="tichiami" type="text" size="25"></TD> </TR> <TR> <TD>Genere</TD> <TD> <input type="radio" name="sex" value="uomo"> Maschile<BR> <input type="radio" name="sex" value="donna" checked> Femminile</TD> </TR> <TR> <TD>Colore occhi</TD> <TD> <select name="occhi"> <option>blu</option> <option>castani</option> <option selected>verdi</option> <option>altro</option> </select> </TD> </TR> <TR> <TD>Altre caratteristiche</TD> <TD> <input type="checkbox" name="altezza" value="1"> Alto più di 1,80</input><BR> <input type="checkbox" name="peso" value="1"> peso più di 80 Kg</input> </TD> </TR> <TR> <TD colspan="2">Descrivi le tue attitudini atletiche:<BR> <textarea name="athletic" cols="50" rows="4"></textarea> </TD> </TR> <TR> <TD colspan="2" align="center"> <input type="submit" value="Invia le mie informazioni"> </TD> </TR> </TABLE> </FORM> risultato visualizzato Nome Valore Ti chiami Genere Maschile • Femminile Colore occhi blu Alto più di 1,80 peso più di 80 Kg Descrivi le tue attitudini atletiche: Altre caratteristiche Invia le mie informazioni 183 Dal lato del browser World Wide Web Action L'elemento <form> presenta un attributo particolare, denominato action, che identifica una URI da invocare quando l'utente aziona un pulsante di inoltro, a cui corrisponde un programma da eseguire presso il server HTTP (che come vedremo prende il nome di CGI), e che viene citata nella corrispondente richiesta HTTP, usando il metodo definito dall'attributo (sempre di form) method. Parametri di chiamata La particolarità della URI che compare nell'attributo action, è che anziché rappresentare una risorsa statica, individua un programma CGI, invocato con argomenti impostati in base ai contenuti inseriti mediante i campi di inserimento offerti dai controlli: per ogni campo (controllo) della form, è specificato infatti un attributo name, che rappresenta un nome di variabile, a cui viene associato il valore immesso dall'utente in corrispondenza di quel campo, ovvero preimpostato mediante l'attributo value. Controlli Descriviamo la semantica di un sotto-insieme essenziale di controlli HTML, rimandando alla normativa ufficiale per la loro definizione completa, e ad altri siti per la loro sperimentazione interattiva. • <input attr1=val1 attr2=val2 attr3=val3> è di gran lunga l'elemento più frequente, ed in base al valore dei suoi attributi ha molteplici effetti. Elenchiamo dunque questi attributi: ◦ name="variabile" individua il nome di variabile a cui il controllo assegna un valore ◦ value="valore" individua il valore da assegnare alla variabile legata al controllo, se non ne viene impostata nessun'altra ◦ type="tipo" determina il comportamento (sia grafico che concettuale) del controllo, potendo assumere il valore di ▪ text che fa corrispondere al controllo una casella di inserimento testuale, e quindi value assumerà un valore-stringa ▪ checkbox che crea una casella di spunta, quindi value assumerà un valore logico; ▪ radio che individua anch'esso una casella di spunta, ma se esistono più controlli <input type="radio"> con lo stesso name e diversi value, allora solo uno di questi potrà ricevere il segno di spunta, determinando in tal modo quale value usare; ▪ submit che trasforma il controllo in un pulsante cliccable, su cui è mostrato il testo indicato da value, cliccando il quale viene eseguita la action che compare nella form; se nello stesso input è presente anche l'attributo name, allora questo riceverà il valore value ▪ hidden che inibisce la presentazione di elementi grafici per il controllo, mentre il value viene comunque inviato; viene usato per sopperire alla natura senza stato di HTTP; ◦ checked può comparire in uno degli elementi input di tipo radio associati ad uno stesso name, rendendo la casella pre-selezionata 184 Lo strato applicativo di Internet Alessandro Falaschi • <select> contiene l'attributo name, e crea un menù a tendina (vedi esempio), in cui compaiono i possibili valori da associare al name. Ognuno di questi valori è definito per mezzo di un elemento annidato nella select di tipo ◦ <option value="valoreN">frase per scegliere valoreN</option> consente di assegnare al nome di variabile definito dal <select>, il valore indicato dall'attributo value, in corrispondenza della scelta da parte dell'utente della frase corrispondente. Uno tra gli elementi option può contenere l'attributo selected, che rende la scelta relativa -pre-selezionata. Esempio di form Per sperimentare l'effetto della esecuzione di questo e dei prossimi esempi, occorre installare sul proprio computer il server web Apache e configurarlo come indicato nella esercitazione collegata a questo capitolo, e quindi installare nella propria directory public_html gli esempi forniti e che consistono nei programmi CGI invocati mediante l'attibuto action delle form proposte in questo capitolo. Il primo di questi, consiste nel seguente codice HTML: <form method="get" action="http://127.0.0.1/labint/primocgi.cgi"> <table style="background-color: rgb(255, 247, 229); width: 85%; margin-left: auto; margin-right: auto;" border="1" cellpadding="4" cellspacing="2"> <caption><a name="form_get"></a><i>Esempio di Inserimento</i></caption> <tbody> <tr> <td align=right width=30%>email:</td> <td><input maxlength="50" name="email"></td> </tr> <tr> <td align=right>ordine:</td> <td> <table align=left cellpadding="2" cellspacing="2"> <tbody> <tr> <td> <input name="ordine" value="pizza" type="radio">pizza</td> </tr> <tr> <td><input checked="checked" name="ordine" value="lasagna" type="radio">lasagna</td> </tr> <tr> <td><input name="ordine" value="tiramisu" type="radio">tiramisu</td> </tr> <tr> <td><input name="ordine" value="pinta" type="radio">pinta</td> </tr> </tbody> </table> </td> </tr> <tr> <td align=right><input name="ok" value="ok" type="submit"></td> <td align=left><input name="reset" value="reset" type="reset"></td> </tr> </tbody> </table> </form> che produce la visualizzazione seguente: Esempio di Inserimento email: ordine: pizza 185 Riferimenti World Wide Web • lasagna tiramisu pinta ok reset e che, quando viene premuto il tasto ok, condiziona il browser ad effettuare una richiesta HTTP con il metodo GET, verso la URI http://127.0.0.1/labint/primocgi.cgi indicata nell'attributo action dell'elemento form, e che produce come risultato una pagina HTML, che il server web ci invierà come risposta. Dato che l'esistenza del meccanismo dei CGI ha reso il WWW classico (ossia, il trittico composto da URI, HTTP e HTML) una sorta di sottostrato su cui realizzare vere e proprie applicazioni di elaborazione distribuita, dedichiamo a questi ulteriori aspetti il prossimo capitolo. Riferimenti • Guide di HTML.it ◦ HTML di Wolfgang Cecchin ◦ XML di Andrea Chiarelli ◦ XHTML di Cesare Lamanna • CSS ◦ Aggiungere un tocco di stile di Dave Raggett ◦ CSS di base di Cesare Lamanna ◦ tabelle di riferimento per le proprietà CSS ▪ presso w3school ▪ presso tizag ▪ presso stylegala • Template di stile ◦ WebShapes - una risorsa a contribuzione libera, con sezioni orientate a Drupal, Wordpress e menù css ◦ Open Source Web Design - una collezione di progetti web donati al pubblico ◦ Open Web Design - una comunità di progettisti di siti che condividono i loro template. • Esecuzione lato client ◦ ◦ ◦ ◦ ◦ Guida JavaScript di base - di Ilario Valdelli Guida JavaScript per esempi - di Wolfgang Cecchin Esempi Javascript presso w3schools OpenAJAX white paper (in italiano) Guida AJAX - di Andrea Giammarchi Realizzato con da Alessandro Falaschi - 186 Lo strato applicativo di Internet Alessandro Falaschi 187 Lo strato applicativo di Internet Applicazioni Web e Overlay Networks Mentre nel capitolo precedente ci siamo limitati a descrivere ciò che è possibile fare servendoci dei soli strumenti di base del WWW, ossia dell'indirizzamento (URI), del trasporto (HTTP) e della formattazione (HTML) delle informazioni, vogliamo in questo capitolo approfondire gli aspetti relativi alla possibilità di utilizzare il WWW classico per accedere, oltre che a semplici oggetti, anche a servizi di elaborazione, ed a come questi possano arrivare a combinarsi per realizzare veri e propri sistemi distribuiti. • Dal lato del server ◦ Common Gateway Interface ▪ ▪ ▪ ▪ Metodo GET e QUERY_STRING Metodo POST Media Type application/x-www-form-urlencoded Parsing della query e invio file ◦ Linguaggi di scripting ▪ Perl, PHP, Python, Java servlet, ASP ◦ Uso dei database, SQL ▪ Ruby ◦ Stato della sessione ▪ Cookie ▪ Variabili nascoste ◦ Server Virtuali • Log e Statistiche • Content Managment Systems ◦ Blog ◦ Wiki ◦ Feed RSS e Atom ▪ Podcast • Content Delivery Network ◦ Mirror e Geolocation ◦ Gli elementi di una CDN ▪ Approccio basato sul DNS ▪ Approccio basato sul livello applicativo ◦ Multicast, una soluzione mancata ◦ Overlay Networks ▪ Reti Peer to peer • Web Service ◦ Elaborazione distribuita ▪ RPC, CORBA, RMI, DCOM, ActiveX e .Net ◦ SOAP Lo strato applicativo di Internet Alessandro Falaschi ◦ XML-RPC ◦ Mashup • Web Semantico • Riferimenti Dal lato del Server Siamo ora in grado di approfondire lo studio di ciò che avviene dal lato server dell'HTTP, e di come la tecnologia web si sia man mano arricchita di funzionalità in grado di offrire servizi sempre più evoluti, mediante una interfaccia di presentazione sempre più universale. Common Gateway Interface Il CGI è una modalità di eseguire un programma (chiamato anch'esso CGI) residente presso un server web, il cui nome è presente nel path della URI oggetto di una richiesta HTTP, e di visualizzare come risposta una pagina web, il cui contenuto è prodotto dal programma stesso. Pertanto, la pagina visualizzata non esiste in forma fisica, ma è stata generata dinamicamente per l'occasione. Il meccanismo di comunicazione tra utente, server web, e programma CGI, sommariamente descritto dalla figura precedente, tratta da questo testo on-line, merita qualche commento di approfondimento: • il CGI è un programma che può essere eseguito anche da finestra terminale, e che ◦ può accedere al contenuto delle variabili di ambiente definite da chi lo manda in esecuzione, ◦ può ricevere dei dati mediante il canale di standard input ◦ può comunicare i risultati della sua elaborazione mediante operazioni di stampa dirette verso standard output • il browser presso l'utente funge da interfaccia di immissione e di visualizzazione dei risultati, ovvero ◦ i valori immessi nella form posso essere codificati direttamente nella sezione query della URL di richiesta, oppure essere inseriti nel body della richiesta stessa ◦ la pagina HTML che viene visualizzata a seguito della richiesta, e contenuta nel body della risposta HTTP, è generata direttamente dal CGI, in base al risultato delle sue elaborazioni 189 Dal lato del Server Applicazioni Web e Overlay Networks • il server web che fa da intermediario comunica con il cgi ed il broswer ◦ scrivendo i contenuti della URI e delle intestazioni presenti nella richiesta HTTP, nelle variabili di ambiente che possono essere lette dal CGI ◦ ridirigendo il contenuto del body della richiesta, verso lo standard input del CGI ◦ ridirigendo lo standard output del CGI, verso il body della risposta HTTP Il diagramma temporale che segue tenta di rappresentare in forma grafica i passaggi ora esposti: La definizione formale di questa metodologia risale al 1993, e fu definita nell'ambito delle discussioni svolte tramite la mailing list a cui erano iscritti gli sviluppatori web di allora, e pubblicata presso il sito dell'NCSA, che a quei tempi offriva la prima implementazione (a cura di Rob McCool) di server web universalmente diffusa. Da allora, solo nel 2004 si è arrivati ad una formalizzazione ufficiale della interfaccia CGI/1.1, con la RFC 3875. Il termine CGI, oltre che a rifersi alla interfaccia tra server web ed applicazione, esprime bene il concetto che questa applicazione-CGI può costituire una sorta di porta (comune) verso altri universi: uno dei principali utilizzi della tecnica è infatti quello di permettere l'interrogazione di DataBase, formattando i risultati in modo facilmente leggibile, e idoneo a generare ulteriori interrogazioni al database, in modo semplice. La realizzazione di programmi CGI scritti utilizzando linguaggi di scripting, ed abbinati ai database, ha reso possibile lo sviluppo dei Content Managment Systems. Metodo GET e QUERY_STRING Come anticipato, in questo caso il server web comunica al CGI i parametri che gli giungono da parte del client mediante l'uso di variabili di ambiente, che il CGI può leggere, ed usare per i suoi scopi. Un esempio è fornito nelle esercitazioni, dove è proposto lo script perl che viene invocato dalla form precedente, e che ci mostra le variabili di ambiente ricevute a seguito dell'invocazione del pulsante di inoltro. Un elenco di variabili di ambiente a cui il server Web può assegnare un valore, e di quali informazioni sono veicolate in tal modo, è riportato nella RFC 3875. In particolare, la variabile QUERY_STRING riporta la parte di URI successiva al simbolo ?, 190 Lo strato applicativo di Internet Alessandro Falaschi ossia la sequenza di coppie variabile=valore associate ai campi della form, separate dal simbolo &. Inoltre, la variabile REQUEST_METHOD indica il metodo indicato nell'attributo method dell'elemento HTML <form>, che può assumere i valori GET o POST. Metodo POST Mentre nel caso precedente (metodo GET) i campi della form sono trasmessi come parte della URI, con il metodo POST questi sono invece passati nel body della richiesta HTTP, ed il server web a sua volta, li passa al CGI mediante il canale di standard input. Per fare un esempio, replichiamo la form soprastante, utilizzando questa volta il metodo POST, ed invocando una diversa action, modificando quindi, rispetto alla form precedente, solo la prima linea, che diventa <form method="post" action="http://127.0.0.1/labint/secondocgi.cgi"> dando così luogo al modulo seguente Uso del metodo POST email: pizza • lasagna ordine: tiramisu pinta ok reset Come possiamo apprezzare dalla pagina di risposta inviata dallo script invocato nella action, i parametri della chiamata questa volta sono stati inviati nel body del messaggio di richiesta HTTP. Media Type application/x-www-form-urlencoded Se nel campo email delle form precedenti, immettiamo parole separate da spazi, o contenenti lettere accentate, possiamo osservare come questi caratteri siano sostituiti, dal lato del CGI che li riceve, dal carattere + (per lo spazio) oppure da codifiche esadecimali. Questa trasformazione (nota anche come percent encoding) è quella di default, che può essere resa esplicita aggiungendo nel tag dell'elemento form, un attributo enctype con valore application/x-www-form-urlencoded. Parsing della query e invio file Osserviamo ora che, sebbene il body possa essere di lunghezza qualsiasi, e si possa pensare di aver risolto così il problema di avere strighe di opzioni troppo lunghe, restano ancora due questioni: 191 Dal lato del Server Applicazioni Web e Overlay Networks • come fare per interpretare la stringa delle coppie variabile=valore ? Questo, in linea di principio, non è un problema, come mostrato dall'output prodotto da terzocgi, invocato mediante il bottone seguente, che realizza una form con dei controlli nascosti: <form method="post" action="http://127.0.0.1/labint/terzocgi.cgi"> <input name="ok" value="ordina in segreto" type="submit"> <input name="ordine" value="amatriciana" type="hidden"> <input name="email" value="[email protected]" type="hidden"> </form> ordina in segreto • nel caso in cui si voglia inviare, mediante la form, un intero file (usando un controllo di tipo, appunto, file), è probabile che la tecnica attuata nel terzocgi fallisca. Allora, al posto del valore di default x-www-form-urlencoded, si preferisce specificare nella form un attibuto enctype="multipart/form-data", definito nella RFC 2388. Questo fa sì che ogni diverso campo della form, venga trasmesso in una diversa sezione del body della richiesta HTTP, ed ogni sezione suddivisa in modo multipart, come previsto dalle specifiche MIME. Sperimentiamo anche questa tecnica, invocando ora secondocgi, che ci mostra il body così come è stato ricevuto: <form enctype="multipart/form-data" method="post" action="http://127.0.0.1/labint/ secondocgi.cgi"> <input name="ordine" value="pastiera" type="hidden"> <input name="email" value="[email protected]" type="hidden"> Quale file vuoi inviare? <input name="file allegato" type="file"><br> <input name="ok" value="ordina multipart" type="submit"> </form> Quale file vuoi inviare? ordina multipart Browse... Linguaggi di scripting Il termine script, tradotto in italiano come linguaggio interpretato, tra origine dall'uso del termine script nel campo delle arti drammatiche, dove con questo termine, si intende il copione che deve poi essere (appunto) interpretato da un attore. La programmazione degli script, nasce dall'esigenza di abbreviare il ciclo di sviluppo del codice, che ne prevede l'edit, la compilazione, il linkaggio, il debug, e l'esecuzione. Omettendo la fase di compilazione e link, si può verificare immediatamente l'effetto di piccole modifiche apportate, integrando il debug con le fasi di edit e di esecuzione. L'uso dei linguaggi di scripting si è quindi affermato per lo sviluppo dei programmi CGI, che anche se possono essere scritti in qualunque linguaggio, compreso c e c++, beneficiano della adattabilità intrinseca derivante dall'uso degli script. Nel corso della storia del web, si sono accumulate notevoli quantità di librerie di codice già pronto per l'uso, rendendo possibile il rapido sviluppo sia di applicazioni CGI per compiti specifici, che di applicazioni più generali che ricadono nella categoria dei CMS, dei Blog e dei Wiki. Perl Acronimo di Practical Extraction and Report Language (anche se in effetti, è un retronimo), perl nasce nel 1987 su iniziativa di Larry Wall, ed oggi dispone di una miriade di moduli già pronti per l'uso. In particolare, la sezione dedicata alle applicazioni web ed allo sviluppo di applicazioni è particolamente nutrita e pronta a soddisfare quasi ogni esigenza. Allo stesso tempo, esiste un modulo opzionale di Apache, che incorpora in Apache il compilatore- 192 Lo strato applicativo di Internet Alessandro Falaschi interprete perl, offrendo prestazioni superiori. Anche se in origine è nato come un linguaggio interpretato, attualmente l'esecuzione di un programma Perl prevede una prima fase in cui viene prodotto un bytecode ed un grafo di flusso, che sono poi eseguiti da una virtual machine in stile java. La sintassi di Perl può ricordare un pò quella del c, e subisce l'influenza di molti altri linguaggi, anche orientati agli oggetti. Può invocare in modo diretto la valutazione di espressioni regolari. Effettua automaticamente la conversione di tipo delle variabili (es indirizzo/intero/stringa/) in funzione del contesto in cui vengono usate, ed usa il primo carattere delle stesse per identificarne la dimensionalità: ad esempio, @var rappresenta un array di nome var, mentre %var individua un array associativo. PHP Nasce nel 1994 come un insieme di CGI scritti in c, per rimpiazzare degli script perl usati dall'autore per mantenere le proprie pagine personali. Da allora ha ricevuto i contributi di parecchi sviluppatori, ed ora è un potente linguaggio di alto livello, con una sintassi che ricorda il c, un buon supporto del paradigma di programmazione ad oggetti, e l'accesso diretto ai parametri ricevuti mediante l'uso dei controlli delle form. Il solo nucleo di base prevede più di 3000 funzioni, ed offre il supporto alla interrogazione di parecchi diversi tipi di DataBase, così come svariati wrapper verso altri linguaggi/applicazioni/protocolli/ tecnologie, che sono raccolti in un deposito denominato PEAR. E' alla base dello sviluppo di diversi applicativi wiki o cms, come MediaWiki, su cui è basata Wikipedia. L'uso congiunto di Linux, Apache, MySQL e PHP (e/o Perl e/o Python), viene denominato server LAMP, e rappresenta la configurazione preferita per lo sviluppo di siti dinamici, costituendo un stack applicativo tutto Open Source, molto competitivo rispetto alle soluzioni commerciali. Python Nasce alla fine degli anni 80, e quindi, a differenza di PHP, non per risolvere problematiche legate ai CGI. E' particolarmente sensibile alle esigenze di leggibilità del codice, consentendo di affrontare serenamente anche la stesura di progetti complessi. Permette di adottare altrettanto bene paradigmi di programmazione orientata agli oggetti, oppure strutturata, o funzionale. Java servelet Sebbene Java sia nato allo scopo di distribuire codice eseguibile su qualunque tipo di macchina client, in questo particolare caso è invece usato per realizzare codice da eseguire dal lato server, al punto che il termine originario applet si trasforma, in questo caso, in servlet. Le particolarità di Java hanno fatto si che, anziché adattarsi ad un server web preesistente, si sia preferito lo sviluppo di server scritti apposta, e denominati Servlet container. ASP Le Active Server Pages sono l'approccio di Microsoft allo sviluppo di applicazioni server-side, scritte in VB.NET, C# e J#, ed ha subito una graduale evoluzione fino alla attuale versione ASP.NET, che è integrata con la architettura .NET, nata a sua volta, in risposta a Java. 193 Dal lato del Server Applicazioni Web e Overlay Networks Uso dei database, SQL Una delle principali applicazioni dell'approccio-CGI è stato l'accesso ai dati mantenuti in un DBMS, ed alla presentazione dei risultati in pagine web scritte in modo da facilitare ulteriori richieste correlate alla prima. Mentre per DataBase si può intendere una semplice organizzazione indicizzata dei dati, tale da permetterne un accesso velocizzato rispetto alla ricerca sequenziale, un DBMS è una applicazione server che permette l'accesso, la modifica a la manutenzione degli indici dei dati, da parte di più applicazioni client che operano in modo concorrente. In buona sostanza, le applicazioni che fanno uso di un DBMS, di fatto sostituiscono alle proprie variabili e strutture dati, quelle presenti nel database che viene usato, che rappresenta in tal senso una sorta di memoria a lungo termine dell'applicazione. Lo Structured Query Language è un linguaggio definito appositamente per realizzare delle interrogazioni ai database relazionali (RDBMS), ed affonda le sue radici a cavallo tra gli anni 70 ed 80. I database relazionali organizzano i dati in tabelle, le cui righe rappresentano dei record che mettono in relazione tra loro i contenuti che compaiono nelle colonne, ed alcune colonne possono svolgere il ruolo di mettere in relazione tra loro i record presenti in tabelle differenti. Nonostante sia ANSI che ISO abbiano partecipato ad un processo di standardizzazione di SQL, gli sviluppatori di DBMS non vi hanno mai aderito pienamente, cosicchè un programma CGI scritto in uno dei linguaggi fin qui discussi, deve in genere usare una diversa libreria di funzioni per ogni diverso RDBMS che intende usare. Ma nonostante ciò, le operazioni di base che è possibile eseguire hanno gli stessi nomi nei diversi casi, come ad esempio Select, Insert, Update, Delete. Per un confronto tra diversi RDBMS, si veda la tabella presso Wikipedia. Ruby Anche Ruby è un linguaggio di scripting, di tipo interpretato, nato nel 1993 per opera di Yukihiro Matsumoto, che segue un paradigma ad oggetti. Viene citato ora, anzichè nella sezione precedente, perchè il motivo della sua popolarità è legato allo sviluppo (2004) di un ambiente noto come Ruby on Rails, che consente lo sviluppo rapido di applicazioni Web basate su RDBMS. La rapidità si basa su di una filosofia di progetto che tende ad evitare la ripetizione di definizioni altrimenti deducibili, e la necessità di specificare solo ciò che si discosta delle convenzioni. Questo tipo di approccio è stato definito come scaffholding (impalcatura, o ponteggio) e consiste nell'usare strutture dati e relativi nomi di variabile direttamente ereditati a partire dalla struttura delle tabelle e relativa nomenclatura già definita nel database che si intende usare, senza doverla definire nuovamente. In definitiva, questo paradigma si traduce nella generazione automatica della interfaccia di programmazione, a partire dalla struttura del database soggiacente, e quindi nel suo utilizzo da parte della applicazione che si sta realizzando. Stato della sessione Abbiamo affermato fin dall'inizio, che un server HTTP tratta tutte le richieste come indipendenti le une dalle altre, evitando accuratamente di memorizzare informazioni di stato, relative alla evoluzione della sessione che si sta svolgendo con un client che invii richieste ripetute. In tal modo, si permette l'attuazione di tecniche di bilanciamento del carico, come ad esempio nel caso in cui la richiesta HTTP sia inoltrata ad uno tra diversi server in batteria, che condividono gli stessi documenti da inviare, ma non l'informazione di stato relativa al client. 194 Lo strato applicativo di Internet Alessandro Falaschi Per ovviare a questa situazione, e permettere ad esempio lo sviluppo di servizi di commercio elettronico, dove chi naviga mette nel carrello diversi acquisti, oppure per permettere la personalizzazione dell'aspetto di un sito, discutiamo di due soluzioni possibili. Cookie Anche se letteralmente questo termine significa biscottino, ha il senso di un gettone, del tutto simile alla contromarca che ci viene consegnata dall'inserviente di un guardaroba, e che riconsegnamo per ri-ottenere il nostro cappotto. In origine, questo termine è stato usato per autenticare gli utenti di sessioni video remote su macchine Unix. L'applicazione di questo concetto alla navigazione web è stato introdotto da un impiegato di Netscape, e poi è stato ri-definito formalmente nella RFC 2965. Il funzionamento si basa sulla presenza, nella risposta inviata da un server web, di una intestazione Set-Cookie, che specifica una stringa che codifica una serie di attributi, come • • • • • una coppia nome-valore; una data di rimozione, trascorsa la quale, il cookie viene distrutto; un dominio; un percorso; se il cookie deve essere re-inviato solo su connessioni sicure. Nelle successive richieste da parte del client, in cui la URI corrisponde al dominio ed al percorso citati nel cookie, questo viene accluso alla richiesta, associandolo alla intestazione Cookie. Il valore del cookie, contiene una codifica (eventualmente ottenuta con metodi crittografici) dello stato della sessione all'atto della richiesta che ha generato la risposta che ha prodotto la prima istanza del cookie, ed allegando tale codice anche nelle successive richieste, si ottiene che • il server è in grado di condizionare la nuova risposta al valore di tale stato; • il cookie può essere modificato mediante una nuova intestazione Set-Cookie contenuta nella nuova risposta. Qualcuno si può chiedere: ma se l'HTTP è stateless, perché ora un server HTTP dovrebbe essere stateful? In effetti, benchè nulla vieti di utilizzare un server web sviluppato apposta per saper gestire da sè i cookie, è perfettamente possibile utilizzare un server web tradizionale, che non tratta in nessun modo particolare l'header con cui il browser restituisce il valore cookie memorizzato, ma se la URI richiesta individua un CGI, allora il valore del cookie viene passato a quest'ultimo con il meccanismo delle variabili di ambiente; a sua volta, il CGI provvederà a definire un nuovo valore per il cookie, da re-inviare al browser. I linguaggi per la scrittura di CGI prevedono apposite funzioni per la gestione dei cookie, come ad esempio CGI::Cookie di Perl. I propositi di questa tecnica sono molteplici: oltre al caso già citato del commercio elettronico, è ad esempio possibile • autenticare un utente (mendiante un form), ed evitare che debba, in seguito, farlo di nuovo (se seduto allo stesso computer); • personalizzare l'aspetto di un sito, usando l'attestazione di avvenuta autenticazione, come chiave per il recupero del template di formattazione preferito dall'utente; • tracciare i comportamenti individuali. In pagine servite da siti diversi, possono essere presenti banner od altre piccole immagini, prelevate da un server diverso da quello della pagina richiesta, assieme al quale ci viene consegnato anche un cookie che ci identifica (indipendentemente dall'indirizzo IP che usiamo in quel momento). In tal modo, il fornitore del banner (e del cookie) può tenere traccia della sequenza di 195 Log e Statistiche Applicazioni Web e Overlay Networks navigazione tra tutti i siti che ospitano il banner, e provare ad indovinare le nostre preferenze, in modo da poter poi realizzare delle campagne di marketing mirate. Nonostante una serie di controindicazioni all'uso dei cookie, l'attuale navigazione web non sembra poterne fare a meno. Variabili nascoste Una delle più valide alternative ai cookie, applicabile nel caso in cui le pagine di risposta siano sempre generate da un CGI, è quella di inserire nelle stesse, dei moduli (form) contenenti dei controlli nascosti, i cui valori rappresentano le informazioni di stato relative alla navigazione svolta fino al momento della generazione della pagina contenente i controlli nascosti. Inoltre, nel caso in cui la navigazione prosegua utilizzando il metodo POST, questi valori non compaiono nella URI, ottenendo prestazioni del tutto equivalenti a quelle dei cookie, al prezzo di ri-generare ogni volta la pagina di risposta. La differenza sostanziale tra l'uso dei cookie e quello delle variabili nascoste, è che mentre i primi sopravvivono fino alla loro data di scadenza, permettendo di conservare l'informazione di stato anche tra visite distanti nel tempo, le variabili nascoste possono invece accumulare informazioni di stato, solo nel caso in cui si continui a visitare sempre lo stesso sito, in modo che le variabili presenti nella nuova pagina, dipendano dalle scelte operate alla pagina precedente. Server Virtuali Si tratta della possibilità di ospitare su di un medesimo server web, più siti corrispondenti ognuno ad un diverso dominio, ed un diverso intestatario. Questo, può essere ottenuto in due modi. Il primo, consiste nel dotare il computer che ospita il sito web, di più indirizzi IP (indicati con il termine multihoming), ognuno corrispondente ad un diverso dominio, e discriminare le richieste in base all'IP di destinazione del socket. Il secondo metodo, consiste nell'utilizzo di un unico IP, su cui il DNS risolve tutti i domini associati ai siti (che sono quindi alias di un unico CNAME), e nel differenziare le richieste in base alla intestazione Host presente nella richiesta, che appunto riferisce a riguardo del dominio che compare nella URI di richiesta. Log e Statistiche I file di log del server web contengono molte informazioni relative alle pagine che sono richieste, all'orario, al codice di risposta, allo user agent, al referente alla pagina visitata, e sono tipicamente esaminati una volta al giorno, da parte di programmi appositi, allo scopo di generare dei report (accessibili via web) che caratterizzano le modalità di fruizione dei contenuti del sito. L'interesse per questi report può essere di semplice curiosità, o rivestire un fine più concreto, come il monitoraggio dell'esito di una campagna di marketing. Due di queste applicazioni open source, sono Webalizer e Awstats, il primo scritto in c, ed il secondo in perl. Una diversa tecnica di analisi di basa sul page tagging, e consiste nell'inserire nelle pagine un riferimento ad un oggetto, oppure del codice javascript, tale da produrre un accesso ad un server remoto (tipicamente, di terza parte) che effetturà l'analisi, e che altrettanto tipicamente, consegnerà al visitatore un cookie per riconoscerlo le volte successive. In questo blog troviamo un confronto di diverse metodologie di analisi. Un servizio molto diffuso, forse anche perché gratuito, è quello offerto da Google Analytics (video). 196 Lo strato applicativo di Internet Alessandro Falaschi Content Managment Systems La filosofia del CMS nasce al difuori del web, come una metodologia di lavoro collaborativo basato sullo scambio e sulla redazione partecipata di documenti. Un approcio simile si è quindi naturalmente fuso con le possibilità offerte dal web, non ultima quella del telelavoro; inoltre, mettere un CMS su web significa anche rendere i propri risultati editoriali direttamente disponibili alla fruizione del grande pubblico. Si può pensare che il CMS sia una delle pietre angolari di ciò che viene denominato come Web 2.0 (video). Un CMS web è tipicamente realizzato come una applicazione CGI abbastanza articolata, che consente l'editing dei contenuti senza richiedere la conoscenza di HTML, CSS, SQL, e dei linguaggi di scripting, la cui presenza viene nascosta da una funzione di interfaccia utente realizzata sempre via web. L'aspetto di un CMS-web risulta in genere omogeneo, mentre i contenuti sono coerenti anche se presentati sotto viste differenti, questo grazie alla adozione di fogli di stile in comune alle diverse sezioni, ed alla generazione dinamica delle diverse pagine a partire dalle informazioni memorizzate in unico database. Per contro, la manutenzione di un CMS-web necessita la definizione di ruoli editoriali precisi, attribuendo ai singoli individui la gestione di singoli aspetti e/o componenti, nonché dei ruoli di supervisione ed editoriali rispetto ai contenuti che possono essere incorporati anche da fonti esterne. Nella sezione specifica dei riferimenti, possiamo trovare i link ai principali CMS esistenti, ed ai siti che li mettono a confronto. Tra questi, i più diffusi/famosi, sono joomla, mambo, plone, drupal. A partire dal concetto generale di CMS e dai principi su cui si fonda, nel tempo si sono sviluppate applicazioni particolari della tecnologia, orientate alla gestione di problemi specifici, e che offrono supporto ad esigenze particolari. Vediamone alcune. Blog Il termine Blog è una contrazione di web log, a sua volta parafrasi di travel log, o libro di bordo. Infatti, un blog è un CMS, in cui un singolo individuo espone pensieri, riflessioni, punti di vista e sensazioni, accludendo nel sito contenuti di vario genere, link ad altri siti, e permettendo in genere ai visitatori, di arricchire l'esperienza di interazione collettiva, mediante l'inserimento di commenti individuali, trasformando ogni editoriale immesso, nello spunto di discussione di un forum telematico, con il risultato di misurare il polso dei frequentatori del sito. Chi voglia dar vita al proprio blog, può rivolgersi a fornitori di questo servizio specifico, che ospitano siti-blog gratuitamente, oppure installare presso un provider che offre hosting, o su di una propria macchina in rete, un applicativo apposito, come ad esempio Wordpress, di cui abbiamo anche un video. 197 Content Managment Systems Applicazioni Web e Overlay Networks Wiki Il termine è un a parola polinesiana che significa veloce, e chi lo usò per la prima volta per descrivere una modalità di editing di pagine web, attuata mediante il web stesso, lo fece in seguito al fascino che su di lui ebbe l'autobus dell'areoporto di Honolulu, che per l'appunto per la sua velocità, era chiamato wiki-wiki. Sebbene un wiki potrebbe esser fatto ricadere sotto la categoria più generale dei CMS, possiede alcune caratteristiche particolari, che lo differenziano in modo unico. • editing via web - i contenuti possono essere modificati intervenendo direttamente tramite il browser, dovunque ci si trovi, a partire da un qualunque computer. Può essere presente una unica password per tutto il wiki, oppure possono essere definiti degli utenti, eventualmente con privilegi ristretti a solo alcune sotto-sezioni • metalinguaggio semplificato - le pagine del wiki, pur se visualizzate inviando codice HTML al browser, sono scritte usando un linguaggio di markup molto semplificato rispetto all'HTML, consentendo di dedicare più attenzione ai contenuti, che non al markup. La trasformazione dal markup del wiki all'HTML del browser, avviene per opera del CGI che implementa il wiki stesso. • facile creazione di nuove pagine - in ogni pagina creata, possono essere inseriti links a pagine non ancora esistenti, che sono successivamente visualizzate nel wiki come links ad una pagina di edit vuota, in modo da poter procedere con lo sviluppo dei contenuti, rimandando il completamento degli argomenti correlati, ed invogliando a realizzare pagine corte e dedicate ad un aspetto per volta. Un modo celebre per indicare che una parola è da intendersi come un link ad una nuova pagina, è il cosiddetto CamelCase, chiamato così per via della presenza di due lettere maiuscole, che ricordano la sagoma di un cammello: la sequenza in CamelCase costituirà quindi il nome della nuova pagina. Dato che questo modo di procedere può divenire una limitazione, esistono modi diversi per indicare un hyperlink interno al wiki, come ad esempio, racchiudere il nome della pagina tra parentesi quadre doppie, come ad esempio fa MediaWiki, il motore di Wikipedia. • reversibilità delle modifiche - se da un lato l'apertura del wiki ad accogliere contributi provenienti da diverse fonti, migliora la qualità finale dei suoi contenuti, da un'altra parte, espone il sito ad accogliere informazioni errate, oppure peggio ancora, a subire atti di sabotaggio e/o vandalismo. Per questo, un wiki mantiene memoria di tutte le modifiche effettuate ad ogni sua pagina, permettendo di evidenziare la sola parte modificata, per meglio valutarne la congruità. Allo stesso tempo, le modifiche possono essere annullate, e la pagina riportata allo stato in cui appariva prima dell'ultima (la penultima, etc etc) modifica. • ricerca nel sito - quasi sempre, in un wiki esiste la possibilità di immettere una parola chiave, e localizzare le pagine dove questa compare. A queste caratteristiche se ne aggiungono altre, come • i wiki adottano fogli di stile che rendono l'aspetto della pagine uniformi tra loro, riducendo ancora di più il tempo da dedicare agli aspetti estetici, 198 Lo strato applicativo di Internet Alessandro Falaschi • spesso i motori wiki sono corredati da una serie di estensioni che ne potenziano le funzioni, integrando anche caratteristiche più propriamente legate ai CMS propriamente detti, ai blog, ai forum, ai repository, alla documentazione, ai feed RSS, al punto che, al di là dell'approccio collaborativo, le caratteristiche esposte sono ben utili anche nel caso in cui il sito sia mantenuto da una persona sola, oppure da un gruppo di persone che sono coinvolte in un progetto comune, come ad esempio lo sviluppo di un software open source (es con trac), per il quale non valga la pena di progettare un nuovo sito ad-hoc. Anche in questo caso, chi voglia sviluppare il proprio wiki, può rivolgersi ad una wiki farm (es: wikia) che offre ospitalità, oppure installare in proprio uno dei motori esistenti. Feed RSS e Atom Attualmente (nella versione 2.0) RSS è l'acronimo di Really Simple Syndacation, anche se in tempi non troppo lontani, ha avuto anche il significato di Rich Site Summary (ver 0.91) e RDF Site Summary (ver 0.9 ed 1.0). Consiste in un dialetto dell'XML, orientato alla descrizione dei contenuti di un sito web, e da questo punto di vista deve molto alle sue origini, legate alle definizione di metadata e RDF. Un esempio di RSS 2.0 è fornito da wikipedia, ed è riportato appresso: <?xml version="1.0"?> <rss version="2.0"> <channel> <title>Liftoff News</title> <link>http://liftoff.msfc.nasa.gov/</link> <description>Liftoff to Space Exploration.</description> <language>en-us</language> <pubDate>Tue, 10 Jun 2003 04:00:00 GMT</pubDate> <lastBuildDate>Tue, 10 Jun 2003 09:41:01 GMT</lastBuildDate> <docs>http://blogs.law.harvard.edu/tech/rss</docs> <generator>Weblog Editor 2.0</generator> <managingEditor>[email protected]</managingEditor> <webMaster>[email protected]</webMaster> <item> <title>Star City</title> <link>http://liftoff.msfc.nasa.gov/news/2003/news-starcity.asp</link> <description>How do Americans get ready to work with Russians aboard the International Space Station? They take a crash course in culture, language and protocol at Russia's Star City.</description> <pubDate>Tue, 03 Jun 2003 09:39:21 GMT</pubDate> <guid>http://liftoff.msfc.nasa.gov/2003/06/03.html#item573</guid> </item> <item> <title>Space Exploration</title> <link>http://liftoff.msfc.nasa.gov/</link> <description>Sky watchers in Europe, Asia, and parts of Alaska and Canada will experience a partial eclipse of the Sun on Saturday, May 31st.</description> <pubDate>Fri, 30 May 2003 11:06:42 GMT</pubDate> <guid>http://liftoff.msfc.nasa.gov/2003/05/30.html#item572</guid> </item> <item> <title>The Engine That Does More</title> <link>http://liftoff.msfc.nasa.gov/news/2003/news-VASIMR.asp</link> <description>Before man travels to Mars, NASA hopes to design new engines that will let us fly through the Solar System more quickly. The proposed VASIMR engine would do that.</description> <pubDate>Tue, 27 May 2003 08:37:32 GMT</pubDate> <guid>http://liftoff.msfc.nasa.gov/2003/05/27.html#item571</guid> </item> <item> 199 Content Managment Systems Applicazioni Web e Overlay Networks <title>Astronauts' Dirty Laundry</title> <link>http://liftoff.msfc.nasa.gov/news/2003/news-laundry.asp</link> <description>Compared to earlier spacecraft, the International Space Station has many luxuries, but laundry facilities are not one of them. Instead, astronauts have other options.</description> <pubDate>Tue, 20 May 2003 08:56:02 GMT</pubDate> <guid>http://liftoff.msfc.nasa.gov/2003/05/20.html#item570</guid> </item> </channel> </rss> Come si vede, l'XML di un RSS è strutturato in modo da racchiudere, nel contesto di un <channel>, caratterizzato da un insieme di elementi validi per tutti i diversi contenuti di un canale, le descrizioni associate a diversi <item>, fornendo per ciascuno di essi, un'altra serie di elementi che permettono sia di accedere alla pagina web dove è effettivamente esposto l'argomento, sia di descrivere il contenuto in forma sintetica, in modo da permettere a chi legge, di valutare il suo interesse. I documenti XML che descrivono un feed RSS sono prelevabili (con Mime-Type application/rss+xml) da siti web (es Repubblica, Punto Informatico) che espongono il logo mostrato a lato, da parte di programmi detti feed reader (anche se i moderni browsers e client email sono egualmente all'altezza), che consentono di tenere sott'occhio (prelevandoli dalle rispettive fonti) in modo uniforme i feed RSS di diversa provenienza (video). Un approccio simile ma diverso, è quello degli aggregatori, che pure raccolgono i feed RSS provenienti da fonti diverse, ma li compongono a formare una nuova pagina web, i cui contenuti hanno origine a partire dai diversi feed a cui l'aggregatore è iscritto. La pagina web contenente l'aggregatore, può essere personalizzata in associazione all'identità di un utente autenticato del sito web, come ad esempio accade con Bloglines, MyYahoo, YourLiveWire, Google Reader, ovvero quelli riportati da newsonfeed. Un diverso formato dei feed è denominato Atom, che gode del riconoscimento ufficiale della RFC 4287 di IETF, e che nasce a seguito dell'insoddisfazione relativa ai diversi formati incompatibili esistenti per RSS. Una ulteriore RFC 5023 definisce poi l'Atom Publishing Protocol, basato su HTTP, che stabilisce le modalità di pubblicazione e recupero degli oggetti Atom. Podcast Questo termine nasce dalla contrazione (o dalla sinergia?) tra l'iPod di Apple, ed il broadcasting, anche se ha ben poco a vedere con entrambi. Si tratta della ricezione in differita di contenuti multimediali (audio, video) preregistrati, ed annunciati per mezzo di feed RSS o Atom. Chi si iscrive al feed, lascia che il suo podcast-reader (come iTunes) verifichi se ci sono novità, ed in tal caso le scarichi, permettendo di ascoltare/vedere i nuovi contenuti in un secondo tempo. In RSS 2.0 il podcast è abilitato dalla definizione dell'elemento <enclosure>, che consente di specificare la URI del contenuto multimediale, la sua dimensione in byte, ed il MIME-Type associato al file. In Atom, viene usato allo stesso scopo il termine enclosure, come possibile valore dell'attributo rel (che sta per relazione) dell'elemento link. 200 Lo strato applicativo di Internet Alessandro Falaschi Content Delivery Network Questo termine può essere tradotto come rete di consegna dei contenuti, e descrive una filosofia che si discosta dal tradizionale approccio client-server, e utilizza la rete Internet come una infrastruttura globale su cui edificare una rete sovrapposta di elementi di replica, in grado di offrire gli stessi contenuti disponibili su di un server specifico. In questo modo, il client potrà recuperare la copia dell'oggetto richiesto, direttamente presso l'elemento di replica a lui più prossimo, evitando di sovraccaricare la rete internazionale di transito con richieste spesso uguali, e provenienti da client topologicamente vicini tra loro. Sebbene questa illustrata possa sembrare la classica funzione da far svolgere ad un elemento proxy, in questo caso i contenuti da replicare in prossimità del client non sono qualsiasi, ma sono solo quelli per i quali è previsto il servizio. Questa esigenza è particolarmente sentita nel caso di contenuti per i quali si può sviluppare un volume di richieste molto elevata, concentrata in un periodo di tempo molto breve, come nel caso, ad esempio, di un lancio pubblicitario televisivo, oppure per contenuti audio-video erogati in diretta. Mirror e Geolocation Un esempio di CDN è la rete di mirror che sono proposti come siti alternativi da cui scaricare un determinato contenuto, e predisposti da organizzazioni come Sourceforge. Nella sua forma più basilare, al client che richiede il download di un contenuto viene visualizzata una pagina, in cui si offre di scegliere tra un elenco di siti alternativi per il download. Attualmente, si tende ad evitare che il client debba scegliere di sua iniziativa, proponendo direttamente una scelta determinata dall'esito di servizi di geolocalizzazione basati su di una mappatura approssimata associata all'indirizzo da cui proviene la richiesta, come ad esempio quelli offerti da Maxmind, usati ad esempio da GeoTool. Gli elementi di una CDN Come fatto già notare, delegare a dei dispositivi di replica la consegna dell'oggetto richiesto all'utente finale, è molto simile a quanto ottenibile mediante un proxy, che intercetta la richiesta originaria perché si trova sul suo stesso percorso, oppure che è esplicitamente designato dal client a svolgere il ruolo di proxy. In una CDN, invece, si tenta di instradare la richiestastessa del client, verso l'elemento di replica più idoneo (ossia più prossimo al client), che si occuperà quindi di servirla. Presso wikipedia troviamo una breve panoramica della problematica, la RFC 3568 illustra alcuni metodi comunemente in uso, e presso globule troviamo una trattazione assai più approfondita. Ma in linea di massima, il funzionamento di una CDN può essere decomposto nelle seguenti parti: • i dispositivi di consegna dei contenuti: l'insieme degli elementi di replica, detti surrogati, in grado di consegnare copie dei contenuti, ad insiemi di utenti; • una infrastruttura di distribuzione: è il meccanismo che sposta i contenuti dal server di origine ai surrogati, e deve essere specificato in termini di ◦ advertising - ogni surrogato deve comunicare alla infrastruttua di instradamento della richiesta, informazioni relative al suo stato, e chi intende servire ◦ replica - il modo con cui i contenuti si propagano tra surrogati; ◦ segnalazione - la consegna da nodo a nodo deve essere coordinata a seguito della decisione presa dalla funzione di instradamento della richiesta 201 Content Delivery Network Applicazioni Web e Overlay Networks • un sistema di instradamento della richiesta: si tratta del meccanismo che guida il client verso un appuntamento con il server di replica. Sussistono almeno due approcci diversi, quello basato sulla infrastruttura di rete, come la redirezione DNS e gli switch a livello applicativo, e quello basato su soluzioni a livello applicativo, come nel caso discusso tra breve. Oltre a chi fa cosa, occorre però definire anche come lo fa, ovvero, in base a quale metrica viene deciso il miglior server da utilizzare, e come calcolare questa metrica. Approccio basato sul DNS E' il sistema di instradamento della richiesta adottato (più o meno) da Akamai, che gestisce un folto insieme di surrogati, dispiegati per i quattro angoli del pianeta, e che rivende l'uso del servizio a chi intende farne uso. Il suo utilizzo si basa nell'identificare l'indirizzo applicativo dell'oggetto da distribuire, con un nome a dominio appartenente al dominio di cui Akamai è autorevole. Quando un client ne farà richiesta, il DNS autorevole di Akamai risolve l'indirizzo applicativo dell'oggetto, con l'indirizzo IP associato all'elemento di replica più prossimo al client, in base alla conoscenza dell'indirizzo IP del DNS a cui il client ha inoltrato la richiesta di risoluzione originaria. Ognuno dei surrogati, quindi, dovrà identificare la copia dell'oggetto mediante la medesima sezione path della URI che lo identifica. Approccio basato sul livello applicativo In questo caso, l'instradamento della richiesta è attuato dal portale a cui si rivolge il client, in base all'indirizzo IP del client stesso, noto al portale al momento della ricezione della richiesta. Dopo aver inoltrato questa informazione alla entità di gestione della CDN, in modo che questa individui il nodo di replica più idoneo per quel client, il portale risponde alla richiesta, indicando la URI relativa alla copia dell'oggetto localizzata presso il surrogato indicato dall'entità di controllo. 202 Lo strato applicativo di Internet Alessandro Falaschi Multicast, una soluzione mancata Un esempio in cui la realizzazione di un servizio non può prescindere dalla esistenza di una adeguata CDN progettata allo scopo, è quello della distribuzione su scala globale (a più centinaia di miloni di fruitori) di un contenuto multimediale mediante Live Streaming, ossia dell'equivalente di una diretta TV, per il quale è materialmente impossibile replicare il contenuto, che viene generato in tempo reale a partire da una sorgente multimediale. In tal caso infatti, se ci fosse solo un unico server ad erogare i contenuti verso tutti i player, questo dopo qualche migliaio di connessioni, inevitabilmente saturerebbe la propria banda di uscita, rendendo matematicamente impossibile raggiungere bacini di audience dell'ordine dei milioni di persone, come è invece possibile con le trasmissioni radio. Una soluzione a questo problema in realtà esisterebbe, ed è quella basata sulla modalità di istradamento multicast, che è in grado di realizzare un albero di distribuzione dei contenuti, basato sulla replica da parte dei router di Internet dei pacchetti in arrivo, verso ognuna delle diverse destinazioni (reti) in uscita, presso le quali è noto che ci siano ricevitori attivi. Alcune considerazioni, hanno ostacolato l'interconnessione dei protocolli di routing multicast 203 Content Delivery Network Applicazioni Web e Overlay Networks tra ISP differenti, e reso non percorribile questo approccio. Volendo illustrare in due parole (superficiali, approssimate) il funzionamento del multicast, si può dire che si basa sull'uso di un particolare gruppo di indirizzi IP (contenuti nel prefisso 224.0.0./8) non assegnabili a singoli computer. Invece, una applicazione può aprire un socket in ascolto su di un indirizzo multicast (ora chiamato gruppo), provocando l'effetto che il kernel usi l'IGMP per contattare il default gateway, notificandogli l'intenzione di ricevere i pacchetti IP destinati a quel gruppo. Quindi, un protocollo di routing (se abilitato) provvede a propagare tra i router di Internet l'informazione che qualcuno è in ascolto per quel gruppo, in modo che questi memorizzino, per ogni gruppo, su quali interfacce si è manifestato interesse per la ricezione. A questo punto, se un computer inizia a trasmettere pacchetti che hanno il gruppo (ossia l'indirizzo IP multicast) come destinazione, questi vengono inviati dal default gateway usato dalla sorgente (se partecipante al routing multicast) verso le interfacce di uscita dalle quali è arrivata la notizia che più giù qualcuno li vuole: lo stesso avviene anche per gli altri ruoter, e voilà! si realizza così un diverso albero di distribuzione, per ogni sorgente attiva. Notiamo ora due cose: • la distribuzione multicast così definita, è bidirezionale, ed un host può decidere se ricevere, inviare, od entrambe le cose, e • il protocollo di trasporto deve necessariamente essere UDP, non potendo la sorgente gestire i riscontri prevenienti da tutti i destinatari. Overlay networks Dato che i nodi replica di una CDN sono sparsi in giro per Internet, e che i contenuti possono essere distribuiti tra gli stessi, quel che si ottiene è una sorta di rete sovrapposta (Overlay Network), che sfrutta i collegamenti di Internet per permettere lo scambio dei contenuti tra i nodi dell'Overlay. Dato che il risultato finale, più o meno, può permette di realizzare ciò che il multicast ha promesso ma non mantenuto, questa architettura viene spesso indicata come Application Level Multicast, od Overlay Multicast. La differenza tra questi ultimi due termini, risiede nel fatto che con ALM si indica il caso in cui i nodi dell'Overlay sono realizzati dagli stessi computer situati presso gli utenti finali: per questo motivo, un ALM viene anche indicato come End Sytems Multicast. Il risultato, è che la distribuzione ottenibile è caratterizzata da una topologia spesso molto lontana dal caso ideale; inoltre, anche con le attuali conessioni ADSL a banda larga, la velocità dell'UpLink è in generale molto ridotta, al punto da porre un serio limite al numero di flussi uscenti da uno 204 Lo strato applicativo di Internet Alessandro Falaschi stesso nodo. Al contrario, il termine Overlay Multicast è usato per identificare il caso in cui i nodi dell'overlay sono disposti direttamente nella core network, dunque sotto il coordinamento degli stessi ISP, permettendo di realizzare un albero di distribuzione molto più efficente, e con fan-out di uscita per ogni nodo, in grado di limitare di molto la profondità dell'albero di distribuzione. Data l'importanza dell'argomento per lo sviluppo di internet in campo telemediale, si è formato un gruppo di lavoro IRTF, SAMRG (Scalable Adaptive Multicast Research Group), nei cui atti del 66o meeting IETF, troviamo una interessante survey sull'argomento. Reti Peer to peer Un caso particolare di Overlay Network, è quello realizzato da molte applicazioni di file sharing cosiddette peer-to-peer (p2p), denominate così perché mettono tutti i partecipanti su di uno stesso piano (video). Durante l'anno accademico 2006-2007, alcuni studenti di questo corso si sono impegnati nello svolgere delle tesine al riguardo; probabilmente però, può essere più utile approfondire gli aspetti delle diverse architetture, leggendo direttamente i riferimenti più o meno ufficiali, raggiungibili a partire da una tabella comparativa di protocolli ed applicazioni esistenti. In termini molto generali, le reti p2p sono caratterizzate dalla adozione di un metodo di indirizzamento di strato applicativo autonomo e indipendente dal DNS, e si distinguono tra loro in base ai metodi utilizzati per risolvere una serie di problemi: • come identificare i contenuti disponibili e desiderati; • come rendere pubblica la disponibilità dei propri contenuti; • come effettuare la ricerca dei contenuti desiderati: in questo caso, possiamo distinguere in base a ◦ meccanismi per così dire ciechi, in cui le richieste si propagano per inondazione (flooding); ◦ meccanismi basati su criteri metrici e/o di indicizzazione, come per le distributed hash table (DHT); • come organizzare la collaborazione tra i nodi che possono offrire il contenuto; • come effettuare il recupero di un contenuto. Inoltre, le reti peer to peer possono essere classificate in base alla loro topologia, come • reti pure, in cui non esistono nodi di controllo centralizzato, come Gnutella, Freenet, Kad, CAN; • reti centralizzate, in cui pur se i contenuti sono scambiati direttamente tra i peer, esiste un nodo speciale che svolge la funzione di directory server e facilita la funzione di service discovery, come Napster, eDonkey, BitTorrent; • reti ibride, in cui pur non essendoci un unico server centrale, alcuni nodi detti supernodi assumono un ruolo privilegiato rispetto ad altri, come ad es. FastTrack (usato da Kazaa), JXTA. Web Service E' il nome dato ad una metodologia di esecuzione di applicazioni remote, che fa uso di HTTP come strato di trasporto per il recapito delle invocazioni, per il passaggio dei parametri, e per la ricezione dei risultati. Benchè l'invocazione di applicazioni remote non contenga assolutamente nulla di nuovo, rispetto alle preesistenti metodologie di elaborazione distribuita, i Web Service hanno dalla loro la possibilità di far tesoro delle esperienze preesistenti, possiedono una minore cripticità dovuta al formato testuale delle 205 Web Service Applicazioni Web e Overlay Networks comunicazioni, e godono di una universalità di accesso, legata all'uso della porta 80, riuscendo in tal modo a scavalcare i problemi legati ai firewall. Per certi versi, si tratta di una evoluzione della logica dei CGI, a cui è stata aggiunta una formalizzazione più stringente per quanto riguarda il meccanismo di passaggio dei parametri, e dei meccanismi di autodocumentazione dei servizi offerti. Inoltre, mentre i CGI sono pensati per essere invocati da parte di un interattore umano che opera dietro la finestra di un browser web, i Web Service sono pensati per essere utilizzati da parte di procedure software. Elaborazione distribuita Come accennato, il desiderio di definire un metodo che permetta a programmi in esecuzione su computer differenti, con sistemi operativi differenti, di cooperare, ripartendosi il carico computazionale, ed offrendo ognuno la propria tipologia particolare di servizi, nasce assieme ai computer ed alle reti dati. In linea generale, l'elaborazione distribuita è realizzata dopo aver definito un protocollo di comunicazione tra chi usa e chi offre il servizio, con una sua propria sintassi, una sua macchina a stati, una porta ben definita su cui comunicare, un metodo di codifica dei dati indipendente dalla architettura delle entità coinvolte... tutte problematiche analizzate e discusse, praticamente fin dall'inizio del corso. La differenza maggiore è che, mentre le soluzione discusse fin'ora miravano ognuna a risolvere un particolare problema, una architettura di elaborazione distribuita vuole permettere l'invocazione di programmi qualunque. Le soluzioni a questo problema, sono a volte indicate con il temine di middleware, intendendo dire che si tratta di sofware che giace tra il livello applicativo, e quello di rete. Nel corso della storia precedente e/o parallela al web, si sono succedute diverse iniziative in tal senso, tra cui possiamo citare: RPC Il termine Remote Procedure Call rende bene l'idea di ciò che si vorrebbe ottenere, ed il suo primo uso risale al 1976, quando fu menzionato nella RFC 707. L'implementazione più celebre di RPC è stata quella della Sun, chiamata ONC RPC: è basata sullo scambio di messaggi, serializza i dati mediante XDR, utilizza sia UDP che TCP, ed è alla base del metodo di condivisione files NFS. L'accesso ai servizi offerti via RPC è attuato attraverso un demone port mapper, che si pone in ascolto sulla porta ben nota 111 (TCP e UDP), e svolge il ruolo di multiplare le richieste di esecuzione remota, mediante un numero di programma, che è contenuto nella invocazione per identificare il servizio remoto richiesto, e per il quale (numero), il programma (es. NFS) che intende offrire quel servizio, si registra. L'uso di RPC ed NFS è tuttora molto diffuso, prevalentemente in ambito di rete locale. CORBA La Common Object Request Broker Architecture è definita dall'Object Management Group come un metodo che abilita alcuni componenti software scritti in linguaggi diversi, in esecuzione su computer diversi, a lavorare assieme. Si basa sul concetto di interface definition language (IDL), che specifica le interfacce che gli oggetti presentano al mondo, mentre CORBA specifica un mapping da IDL ai diversi specifici linguaggi di programmazione. Nonostante la sua definizione risalga ai primi anni '90, a tutt'oggi non è riuscito ad affermarsi, ed alcune critiche alla sua impostazione non hanno trovato risposta. 206 Lo strato applicativo di Internet Alessandro Falaschi RMI La Remote Method Invocation pone l'accento sulla filosofia ad oggetti (in opposizione a quella di chiamata a procedura di RPC), che determina l'interazione con oggetti remoti mediante l'invocazione di metodi. La RMI consente ad un codice Java (a cui è indissolubilmente legata) di invocare l'esecuzione di un metodo offerto da un oggetto remoto, quasi come se fosse locale. DCOM Una implementazione alternativa ad RPC, è stata il Network Computing System (NCS) da parte di Apollo Computer, che venne successivamente usato come la base per il DCE/RPC nel Distributed Computing Environment di OSF. Un decennio più tardi, Microsoft adotta DCE/ RPC come base per il suo MSRPC, su cui fonda DCOM. Quest'ultima, è stata la risposta di Microsoft a CORBA, anche se attualmente, gli stessi scopi sono invece perseguiti con l'architettura .NET. ActiveX e .NET Sono le due tecnologie con cui Microsoft risponde alle esigenze di elaborazione distribuita, tranne che... non sono aperte ad architetture differenti! SOAP Il Simple Object Access Protocol è un framework che permette l'invocazione di procedure remote mediante lo scambio di messaggi in formato XML, trasportati tipicamente su connessioni HTTP. La struttura dei messaggi segue il paradigma Head-Body, in cui gli Header contengono meta-informazioni riguardanti il routing, la sicurezza e le transazioni, mentre il Body trasporta il contenuto informativo. I protocolli degli Web Sevices, oltre a prevedere SOAP come meccanismo per inviare i messaggi di esecuzione remota, prevedono l'esistenza di altri protocolli, come il Web Services Description Language (WSDL), che descrive come interagire con un determinato servizio, indicando i metodi messi a disposizione, i suoi vincoli (protocollo, formato dei parametri di ingresso e delle risposte), ed il suo punto di accesso, ovvero l'URI del Web Service. Come mostrato in figura, il fornitore del servizio pubblica presso un Service Broker (strutturato in accordo a UDDI) la descrizione della propria offerta, in modo che questo possa essere successivamente interrogato da un potenziale richiedente, che individua così chi offre il servizio di cui ha bisogno, e quindi lo istanzia per mezzo di messaggi SOAP. Presso wikipedia, è mostrato un semplice esempio di transazione SOAP. XML-RPC Prima che venisse definito SOAP, WSDL e UDDI, un certo Dave Winer (sì, lo stesso che ha definto l'RSS 2.0) aveva già completato la definizione di una specifica, chiamata XML-RPC, 207 Web Semantico Applicazioni Web e Overlay Networks per svolgere né più né meno la funzione di invocare l'esecuzione remota di una applicazione, incapsulando la richiesta in HTPP, esprimendo parametri comunque complessi mediante una rappresentazione XML, e ricevendo in risposta strutture dati egualmente complesse. In effetti, fu proprio questa specifica, che poi diede il via ai lavori di definizione di SOAP. Nel caso in cui, molto semplicemente, sussista unicamente l'esigenza di invocare delle procedure remote, e non ci si voglia stare a preoccupare molto di definire un nuovo protocollo per gestire il passaggio dei parametri, la soluzione di utilizzare una delle tante librerie che si occupano della serializzazione dei dati in XML, del loro invio in HTTP, e della formattazione inversa dall'altro lato del collegamento, sembra essere quella meno impegnativa. Presso wikipedia, possiamo trovare una tabella riassuntiva della formattazione necessaria ai diversi tipi di dato, assieme ad alcuni esempi di transazione XML-RPC. Mashup Mentre questo termine, nella musica hip-hop, individua un brano realizzato ri-mixando assieme diversi brani preesistenti, nel Web un Mashup è una applicazione distribuita che utilizza informazioni raccolte da siti diversi, per combinarle assieme e realizzare così un nuovo servizio (video). Le informazioni possono essere raccolte ad es. mediante feed RSS, Web Services, API pubbliche. Il codice software (scritto, ad es., in javascript) che realizza il mashup viene tipicamente distribuito tramite il sito che ospita il servizio, ed eseguito presso il browser dell'utente, come ad esempio è il caso di Twittervision, che usa le API di Twitter e di Google Maps, per localizzare in diretta la provenienza degli inserimenti su Twitter. Web Semantico Il Web tradizionale non prende in considerazione la struttura ed il significato del contenuto delle pagine servite: ad esempio, per un qualsiasi motore di ricerca, non esistono differenze tra il termine Rossi nel contesto Il Sig. Rossi ed il termine Rossi nel contesto Capelli Rossi. Per strutturare meglio i contenuti, sono allora stati definiti alcuni linguaggi, quali Resource Description Framework (RDF) e Web Ontology Language (OWL), entrambi basati su XML, che consentono di esprimere le relazioni tra le informazioni rifacendosi alla logica dei predicati mutuata dall'intelligenza artificiale. 208 Lo strato applicativo di Internet • • • • • Alessandro Falaschi Web Semantico - su Wikipedia semedia - presso DEIT, Dipartimento Università Politecnica delle Marche web:semantico - a cura dell'Osservatorio sulla Comunicazione L'architettura del Semantic Web di Paolo Ceravolo Video ◦ Ben Hammersly (2004) ◦ Sir Tim Berners-Lee (2007) ◦ Knowledge Representation and the Semantic Web (2006) Riferimenti • CGI ◦ ◦ ◦ ◦ Appunti sulla Common Gateway Interface di Maurizio Patrignani CGI Resource Index Ovid's CGI Course HTML Writers Guild • CMS e wiki ◦ OpensourceCMS - permette di sperimentare diversi CMS senza installarli ◦ elenco di CMS - su wikipedia ci sono tutti i links ai CMS esistenti, free e commerciali ◦ CMS Matrix - permette di confrontare testa-a-testa le caratteristiche offerte da svariati CMS ◦ elenco dei wiki - Sempre su wikipedia, sono confrontati anche i wiki ◦ WikiMatrix - confronto testa-a-testa dei diversi motori wiki • RSS e Podcast ◦ ◦ ◦ ◦ ◦ Introduzione a RSS di Cesare Lamanna Guida podcasting di Mirko Corli Making a podcast - da Apple.com Feedvalidator.org RDS Morning Show ◦ ◦ ◦ ◦ ◦ Limelight Networks Highwinds CDN BitGravity SimpleCDN MediaMelon • CDN • Corso di Applicazioni Telematiche - Prof. Roberto Canonico, Università di Napoli • Sistemi peer-to-peer di Leonardo Querzoni, connesso al corso di Sistemi Distribuiti e (1, 2) e seminari ◦ OverSim: The Overlay Simulation Framework Realizzato con da Alessandro Falaschi - 209 Riferimenti Applicazioni Web e Overlay Networks 210 Lo strato applicativo di Internet Voice over IP All'inizio di questo corso, si è fatto notare come la trasmissione a pacchetto dei dati, caratteristica della rete Internet, si pone in diretta contrapposizione alla modalità di trasmissione realizzata dalle reti a commutazione di circuito su cui si è evolta la telefonia e la trasmissione del segnale vocale. Ciononostante, a seguito dello sviluppo di Internet, della banda disponibile, e della velocità dei nodi di commutazione, la trasmissione vocale è divenuta possibile (e di fatto, estensivamente praticata) anche su reti a pacchetto, e nel caso di reti IP, prende il nome di Voice over IP (VoIP). • Evoluzione storica • Elementi architetturali ◦ ◦ ◦ ◦ User Agent Registrar Proxy Gateway ▪ Scomposizione del Gateway ▪ SoftSwitch ◦ Servizi aggiuntivi • Session Initiation Protocol ◦ Messaggi ▪ Sintassi ▪ Trasporto ▪ Macchina a stati ▪ ▪ ▪ ▪ Richieste Metodi Status Line Intestazioni ◦ Stratificazione di SIP ◦ Sessione, dialogo e transazione ▪ Transazione ▪ Dialogo ▪ Sessione ◦ Autenticazione ▪ Federazione ◦ Instradamento ▪ Route Set ▪ Loose routing ◦ Procedure ▪ Registrazione, service discovery, e autenticazione ▪ Invito, creazione del dialogo e route set ▪ Terminazione Evoluzione storica Voice over IP • Session Description Protocol ◦ Contesti di utilizzo ◦ Sintassi e sezioni ◦ Elementi ▪ Rtpmap ◦ Offerta e risposta ▪ Creazione della risposta ▪ Ricezione della risposta ▪ Modifica della sessione ◦ Esempio • Real Time Protocol ◦ Qualità, ritardo, jitter e buffer di riproduzione ▪ Calcolo del jitter RTP ◦ Unità dati applicative ▪ Pacchettizzazione ▪ Sincronizzazione ◦ Intestazioni ◦ Audio Video Profile ▪ Payload statici e dinamici ◦ Real Time Control Protocol • Attraversamento NAT e Firewall ◦ Firewall ◦ NAT ▪ Tipi di NAT ▪ UDP Hole Punching ▪ SIP Keep Alive ▪ RTP simmetrico ▪ Uso di dispositivi esterni ▪ Session Border Controller o B2BUA ▪ STUN ▪ TURN e ICE • Riferimenti Evoluzione storica Per molti, le telefonate Internet sono automaticamente associate all'uso di Skype, che gode di un particolare successo di diffusione e di immagine, ma i cui protocolli operativi sono proprietari e quasi sconosciuti. D'altra parte, la possibilità di usare le reti per dati anche a scopi di trasmissione vocale, è stata a lungo inseguita dagli operatori telefonici tradizionali, prima offrendo un accesso numerico con ISDN, poi integrando la trasmissione di voce e dati su di una stessa rete ATM. Al punto che in sede ITU fu standardizzata una famiglia di protocolli aperti ed interoperabili, l'H.323, orientata ad offrire servizi multimediali su reti a pacchetto, facilitando allo stesso tempo l'interoperabilità con le reti a circuito preesistenti. Nel frattempo, il WG MMUSIC di IETF stava procedendo a investigare le modalità di 212 Lo strato applicativo di Internet Alessandro Falaschi realizzazione di servizi multimediali (come videoconferenze e streaming) su rete Internet, definendo • il controllo di una sorgente multimediale mediante il Real Time Streaming Protocol (RTSP), • la sintassi di descrizione delle modalità di codifica e trasmissione per dati multimediali, mediante il Session Description Protocol (SDP), e • il formato di pacchettizzazione per la trasmissione dei dati multimediali, mediante il Real Time Protocol (RTP). Da un lato, l'ITU adottò e fece proprie alcune scelte di IETF, incorporando il trasporto RTP in H.323; da un altro canto lo scenario preconizzato da IETF, che prevedeva la diffusione globale dell'instradamento multicast, venne in parte a decadere. Allo stesso tempo, i meccanismi previsti dall'H.323 si rivelarono piuttosto macchinosi, a causa del tentativo di replicare su Internet i protocolli di segnalazione SS7 preesistenti sulle reti a circuito. Fu a questo punto, che venne definito in sede IETF un nuovo protocollo di segnalazione, il Session Initiation Protocol (SIP), in grado di declinare le funzioni di controllo chiamata adottando messaggi testuali in stile SMTP e HTTP, e di integrarsi armonicamente con le funzioni già assolte da SDP e RTP. Al giorno d'oggi, mentre sono tuttora in uso dispositivi (MCU) di videoconferenza basati su H.323, praticamente ogni operatore che intende offrire un servizio VoIP si basa sulla adozione di SIP, al punto che questo è anche alla base dello sviluppo dell'IP Multimedia Subsystem (IMS) che rappresenta l'architettura per la fornitura dei servizi multimediali offerti dagli operatori di telefonia mobile di terza generazione (3G). Elementi architetturali La tecnologia VoIP SIP si basa sulla definizione di una serie di entità, mostrate a lato, e descritte nel seguito. User Agent Abbreviato come UA, è il dispositivo mediante il quale gli umani possono comunicare: in questo senso, è l'analogo del tradizionale telefono, con la differenza che mentre un telefono è associato ad un determinato numero, legato alla presa telefonica od alla SIM del cellulare, uno UA viene configurato per corrispondere ad una URI simile a quella di una email, come ad esempio sip:[email protected], che è proprietà dell'utente, dovunque egli la usi, permettendone la mobilità. La parte di URI nome_utente@dominio_voip viene indicata come Address of Record (AoR), e rappresenta l'identità di una persona fisica, immutabile e indipendente dalla posizione effettiva dell'individuo nella rete, e dal dispositivo usato per collegarsi. Uno UA può essere realizzato mediante • una applicazione eseguita su di un computer (softphone), oppure 213 Elementi architetturali Voice over IP • un dispositivo del tutto simile ad un telefono fisso (telefono VoIP), ma connesso ad Internet (eventualmente in modalità Wireless, come nel cordless VoIP) anziché alla PSTN, oppure • una applicazione eseguita su di un telefono mobile capace di connessione dati (GPRS o UMTS); Dato che una chiamata che ha origine da uno UA, viene terminata su di un altro UA, questo può pensarsi scomposto in due componenti: • uno User Agent Client (UAC), che si attiva per l'invio di richieste, come ad esempio nel caso della attivazione di una chiamata uscente, e riceve le relative risposte; • uno User Agent Server (UAS), che rimane in attesa di ricevere delle richieste, come nel caso di una chiamata entrante, ed invia le relative risposte. Per questo motivo, il VoIP non viene classificato come un protocollo Client-Server, ma Peer to Peer. Registrar E' l'elemento a cui uno UA comunica (registra) l'indirizzo IP (indicato anche come Contact Address) da associare all'AoR, e che memorizza questa informazione presso un entità di memorizzazione indicata come Location Server. Il Registrar viene amministrato dal fornitore di servizio (provider) VoIP che ha rilasciato l'AoR all'utente, e che amministra il DNS autorevole per il nome a dominio che compare nell'AoR. Un Registrar è l'analogo di un server SMTP di destinazione, con la differenza che mentre le email in arrivo si fermano lì, quando una chiamata VoIP entrante raggiunge il Registrar, questa viene immediatamente inoltrata verso il Contact Address che è stato registrato in corrispondenza dell'AoR, supportando così la mobilità dell'utente, e l'uso di indirizzi IP dinamici, come nel caso di una connessione on-demand. Spesso, l'entità che ospita il Registrar è la stessa che ospita anche un elemento Proxy. 214 Lo strato applicativo di Internet Alessandro Falaschi Diversi UA possono registrare diversi Contact Address per uno stesso AoR, ed in questo caso la chiamata entrante per un AoR, viene inoltrata a tutti i Contact Address associati, provocando il forking del controllo, in modo da far squillare tutti gli UA presso cui può trovarsi il chiamato. Proxy E' un elemento che ha la capacità di inoltrare i messaggi SIP, ed in questo senso, si può pensare che il suo ruolo sia per certi versi simile a quello di un router di livello applicativo. Un classico esempio di utilizzo si ha quando uno User Agent consegna al Proxy la propria chiamata (uscente), facendogli assumere un ruolo analogo a quello di un server SMTP di uscita: in questo caso, il proxy prende il nome di Outbound Proxy. Tra le prerogative di un Proxy, possiamo elencare quelle di • gestione di diversi tipi di politiche (policy), come ◦ ◦ ◦ ◦ ◦ di autenticazione e autorizzazione, controllo di accesso, tariffazione, instradamento controllo della qualità del servizio; • possibilità di inoltrare la chiamata, anziché al Registrar di destinazione, verso un ulteriore Proxy di Transito, o verso un Gateway; • capacità di deviare la chiamata, comportandosi come un Redirect Proxy, rispondendo con un messaggio in cui viene specificato un diverso Proxy a cui inoltrarla; • funzionamento in modalità stateless, ossia senza conservare traccia dei messaggi già inoltrati, oppure statefull, in modo da poter ricombinare assieme messaggi relativi alla stessa comunicazione, ad esempio ai fini di tariffazione del servizio. 215 Elementi architetturali Voice over IP Gateway La sua funzione è quella di interfacciare il VoIP SIP con altre reti multimediali, come nel caso in cui la rete di destinazione/ provenienza della chiamata, è quella telefonica (PSTN), H.323, di telefonia mobile (GSM o IMS-3G), una rete VoIM, o associata ad altro protocollo VoIP. La presenza di un Gateway verso PSTN consente di raggiungere anche gli utenti di telefonia fissa, usando Internet per trasportare la chiamata il più vicino possibile alla località del numero chiamato, e quindi (lì) attraversare un Gateway, rendendo la chiamata equivalente ad una locale. Scomposizione del Gateway La funzione del Gateway può essere ulteriormente scomposta in • un Media Gateway, che provvede alla transcodifica dei dati che trasportano il segnale multimediale, e • un Media Gateway Controller, che termina i messaggi e le componenti di segnalazione proprie dei due lati del gateway. Al fine di incentivare l'interoperabilità tra apparati, la comunicazione tra MG e MGC può avvenire mediante un ulteriore protocollo pubblico, noto come Media Gateway Control Protocol, la cui implementazione è formalizzata ulteriormente dalla raccomandazione H.248. Nella figura che segue si mostra come le componenti di una rete PSTN (Time Division Multiplex per le reti PDH o SDH, SS7 per la segnalazione a canale comune) possano efficientemente utilizzare una rete IP, qualora la segnalazione venga convertita in SIP o H.323, mentre il segnale audio, opportunamente transcodificato, sia incapsulato in RTP. In particolare, è posto in evidenza il controllo dei MG da parte dei MGC. 216 Lo strato applicativo di Internet Alessandro Falaschi Softswitch Un Gateway che abbia la funzione di interconnettere direttamente dei telefoni (tradizionali o VoIP) di una stessa organizzazione, così come avviene nel caso di un centralino, è a volte indicato con il termine di SoftSwitch (come è, ad es., Asterisk), e può usare interfacce più o meno intelligenti. Anche se il termine Softwsitch viene spesso usato in modo più o meno vago ed intercambiabile, ogni volta che si deve descrivere qualcosa che somigia ad un dispositivo di commutazione telefonica, ma che non lo è, recentemente il suo uso più diffuso si è ristretto ad indicare un dispositivo completamente residente su di un unico computer, e che interconnette fisicamente telefoni IP, rete Internet, telefoni tradizionali, e linee PSTN. Servizi aggiuntivi Alcuni elementi della architettura SIP offrono funzionalità aggiuntive, che ne arricchiscono le potenzialità: • il Conference Server che interconnette tra loro diverse chiamate, realizzando una funzione di regia automatica: ◦ un server di conferenza di capacità ridotte, può essere realizzato direttamente presso uno UA; • il Back to Back User Agent (B2BUA) che può modificare completamente i messaggi in transito: ◦ questa è la modalità di realizzazione per un Application Level Gateway (ALG), Security Gateway (SEG), o Session Border Controlled (SBC) presso i punti di 217 Session Initiation Protocol Voice over IP Ingresso/Uscita dalla rete di un Provider VoIP, ovvero un meccanismo che può essere usato per realizzare funzioni di Third Party Call Control; • il Presence Server, che detiene delle informazioni di stato: ◦ le informazioni possono essere relative allo stato di registrazione degli UA, agli utenti connessi in conferenza, allo stato di libero/occupato di uno UA; ◦ queste informazioni sono notificate agli UA che abbiano sottoscritto l'interesse a esserne mantenuti aggiornati. Session Initiation Protocol Il Session Initiation Protocol (SIP) è un protocollo di segnalazione definito nella RFC 3261, e il suo sviluppo coinvolge praticamente tutti i gruppi di lavoro IETF che fanno parte dell'area Real-time Applications and Infrastructure. La quantità di specifiche che si sono via via aggiunte a quelle iniziali è via via cresciuta, e per orientarsi tra le stesse può essere opportuno consultare prima una guida per l'autostoppista di SIP, pubblicata come RFC 5411. Sebbene l'architettura VoIP definita sia basata su di un modello peer to peer, SIP opera sulla base di messaggi che utilizzano un modello di comunicazione di tipo client-server. Inoltre, nessuno degli elementi su esposti è strettamente indispensabile, a parte ovviamente gli UA: infatti, è possibile per uno UA, inviare una chiamata direttamente verso la URI di un secondo UA, a patto ovviamente che il nome a dominio che compare nell'AoR chiamato, si risolva effettivamente nell'indirizzo IP del computer presso il quale lo UA chiamato è in esecuzione. D'altra parte, sono proprio gli altri elementi che rendono il VoIP interessante e competitivo rispetto alle modalità di comunicazione alternative, come PSTN e VoIM. SIP non si occupa né di negoziare il tipo di formato multimediale da usare nella comunicazione, né di trasportare il segnale digitalizzato: questi due compiti sono invece svolti rispettivamente da SDP e RTP. Al contrario, SIP si occupa di mettere in contatto le parti coinvolte nella comunicazione, in modo da definire una sessione in comune tra loro, e di permettergli di utilizzare i servizi offerti da altre entità. Ciò consente di declinare i principi della Service Creation in accordo al paradigma Internet, secondo cui l'intelligenza è posta ai bordi della rete, in contrapposizione al punto di vista degli operatori di telecomunicazioni, che invece accentrano i servizi all'interno delle reti, relegando i dispositivi di accesso ad un ruolo assolutamente passivo. Messaggi Affrontiamo ora la descrizione dei tipi e dei formati dei messaggi SIP. Sebbene ciò possa risultare tedioso e oltremodo formale, gli esempi forniti di seguito sono un ottimo modo di verificare strada facendo la semantica delle diverse componenti dei messaggi. Pertanto si consiglia di alternare l'esame dei capture forniti, con la descrizione formale delle sintassi incontrate. Sintassi I messaggi possono essere di due tipi, richieste o risposte, e aderiscono alla sintassi definita da una ABNF del tutto simile a quella già vista nel caso dell'HTTP e SMTP, ossia sono composti da • una prima linea in cui viene indicato il Metodo (per le richieste) od una Status Line (per le risposte); 218 Lo strato applicativo di Internet Alessandro Falaschi • un numero variabile di linee contenenti svariati header che caratterizzano il messaggio; • un body che contiene le informazioni aggiuntive, come ad esempio, una descrizione SDP. Trasporto I messaggi SIP sono generati dallo strato applicativo, e tipicamente incapsulati su UDP con porta di destinazione ben nota 5060, in modo da velocizzare al massimo il loro recapito. In alternativa, i messaggi di richiesta-risposta tra due entità SIP possono essere trasportati su TCP, sempre verso la porta 5060; la decisione di usare TCP anziché UDP viene presa dallo UAC, quando ad esempio la dimensione del messaggio è tale da non entrare in un singolo pacchetto UDP, oppure la comunicazione è ritenuta a priori inaffidabile. Infine, lo strato di trasporto può essere reso sicuro mediante l'uso di una connessione TLS, indirizzata questa volta verso la porta ben nota 5061. In quest'ultimo caso, la URI di destinazione userà lo schema sips anziché sip come ad es. sips:[email protected]. Macchina a stati Nel caso del trasporto UDP, SIP fa uso di una macchina a stati che definisce i parametri della seguente modalità di ri-trasmissione. Se lo UAC non riceve risposta ad un suo messaggio entro un tempo T1 (posto a 500 msec), lo re-invia di nuovo, e raddoppia il valore del timer T1. Al nuovo scadere di T1, ripete ancora l'invio del messaggio, e raddoppia di nuovo T1, e così via (ritrasmette e raddoppia il timer) finché • non riceve una risposta valida, oppure • è trascorso più di 1 minuto dal primo invio, oppure • viene ricevuto un errore di tipo ICMP In questo modo, mentre si sfruttano i vantaggi di velocità connessi all'uso di UDP, si aggiunge una funzione di affidabilità al trasporto, per gestire il caso dei pacchetti scartati. Richieste La prima riga di una richiesta SIP presenta il formato Method SP Request-URI SP SIP-Version CRLF in cui Method individua la semantica del messaggio, Request-URI indica il destinatario, espresso nella forma del tipo sip:[email protected], SIP-Version è 2.0 per i messaggi che aderiscono alla RFC 3261, SP rappresenta uno spazio, e CRLF sono i due bytes x0D x0A che codificano un a capo. Metodi I seguenti metodi compaiono nei messaggi SIP inviati (se non indicato diversamente) da uno UAC, il loro elenco completo è mantenuto aggiornato presso IANA. Metodo Commento REGISTER RFC Esempio inviato al Registrar relativo al Dominio di un AoR, per associare l'AoR con un Contact Address 219 3261 registrazione (s10) Session Initiation Protocol Voice over IP INVITE inviato verso il proprio Outbound Proxy, oppure verso il Registrar autorevole per l'AoR del chiamato, allo scopo di instaurare una sessione. Dato che, tipicamente, lo UA chiamato inizierà a squillare, e si dovrà attendere che qualcuno risponda, viene subito inviata una risposta provvisoria, e solo successivamente, una risposta definitiva (positiva o negativa) " invito e route set ACK inviato dallo UA chiamante, verso lo UA chiamato, per confermare la ricezione di una risposta definitiva ad un INVITE. L'INVITE è l'unico metodo che attua questa forma di tree way handshake, inviando una risposta alla risposta. " invito e route set OPTIONS inviato ad un altro UA od un Proxy, per chiedere le funzionalità disponibili all'altro estremo BYE inviato da uno UA coinvolto in una sessione con uno o più altri UA, per manifestare l'intenzione di abbandonare la sessione, e che viene inoltrato agli altri UA in sessione. Non può essere usato per annullare un INVITE che non ha ricevuto ancora una risposta definitiva CANCEL inviato per annullare una richiesta INVITE anche se non è ancora stata ricevuta una risposta definitiva, e può essere emesso oltre che da uno UA, anche da un proxy intermedio 3261 (s9) INFO usato per inviare ad uno UA con cui si è già instaurata una sessione, delle informazioni relative ad eventi che avvengono dall'altro lato, come ad esempio, la pressione dei tasti del telefono. Questi vengono quindi trasmessi nel body di un messaggio INFO, descritto da un apposito header Content-Type: application/dtmf-relay 2976 REFER viene tipicamente usato tra due parti che sono già in comunicazione, mettendo in grado chi lo riceve di eseguire una nuova chiamata, verso una destinazione indicata da chi lo invia mediante l'header Refer-to. Lo UA che riceve il REFER, dopo aver chiesto conferma (all'umano che lo gestisce), di eseguire la nuova chiamata, emette un nuovo INVITE verso la URI indicata, mentre il mittente del REFER viene notificato (mediante il metodo NOTIFY) dell'esito dell'operazione. 3515 consente a chi lo invia, di manifestare l'interesse a ricevere delle notifiche (mediante messaggi NOTIFY) della evoluzione di SUBSCRIBE alcune variabili di stato, indicate mediante l'intestazione Event, in cui si fa riferimento ad un event package NOTIFY PUBLISH tiene uno UA al corrente della evoluzione di alcune variabili di stato, può essere inviato anche senza aver prima ricevuto un SUBSCRIBE consente di aggiornare il valore delle variabili di stato, per le quali altre entità possono aver manifestato interesse mediante un SUBSCRIBE. 3261 (s11) " 3265 " 3903 MESSAGE permette l'invio di messaggi istantanei, ospitati nel body e descritti da una intestazione Content-Type, convertendo lo UA in un 3428 messenger. PRACK un messaggio di richiesta di Provisional Response ACK (PRACK) viene inviato da uno UAC che vuole riscontrare la ricezione di risposte temporanee ad un INVITE già inviato. Infatti, mentre le risposte definitive sono riscontrate dall'ACK, quelle temporanee no, e lo UAS che le invia, resta nel dubbio se siano arrivate. Alla ricezione del PRACK (che è una richiesta), lo UAS invia una risposta 200 OK (che non è quella dell'INVITE, ma del PRACK), e lo UAC cessa di inviare PRACK UPDATE permette di modificare i parametri di una sessione SIP (come l'insieme dei media e dei codec) senza alterare lo stato del dialogo; in particolare, può essere inviato prima di avere ricevuto una risposta definita all'INVITE iniziale. Alternativamente, può 3311 anche essere usato per modificare l'identità di un partecipante [RFC4916], senza determinare un nuovo scambio di offerta/risposta SDP. 220 3262 invito e route set Lo strato applicativo di Internet Alessandro Falaschi Status Line In analogia a SMTP e HTTP, anche per SIP la prima linea dei messaggi di risposta ha un aspetto del tipo SIP-Version SP Status-Code SP Reason-Phrase CRLF in cui la prima cifra dello Status-Code identifica la categoria della risposta in una delle seguenti classi: • 1xx: risposta provvisoria: indica il progresso delle operazioni, come ad esempio lo squillo del telefono remoto, ma l'impossibilità di determinare il tempo necessario al loro completamento; • 2xx: successo, la richiesta è stata accettata; • 3xx: redirezione: sono necessarie ulteriori azioni per completare la richiesta; • 4xx: errore del client: la richiesta non può essere soddisfatta perché contiene qualche errore sintattico; • 5xx: errore del server: la richiesta appare valida, ma non può essere soddisfatta per un problema interno del server • 6xx: errore generale: la richiesta non può essere accettata da parte di nessun server. La RFC 3261 contiene una sezione in cui si descrivono una serie di possibili messaggi. Intestazioni Anche le intestazioni SIP hanno un aspetto del tutto simile a quelle già incontrate per l'SMTP e l'HTTP, sia pure con qualche differenza. Per uno stesso header, infatti possono essere presenti più valori, separati da virgole: header = "header-name" HCOLON header-value *(COMMA header-value) mentre per ogni valore, possono essere specificati dei parametri aggiuntivi, ognuno con il proprio valore, e separati da punto e virgola, in modo da arricchire il loro potere espressivo, e permettere lo sviluppo di nuove estensioni header-value: value *(;parameter-name=parameter-value) Un esempio di questa sintassi, può essere osservato in corrispondenza dell'header Contact del pacchetto n. 10 presente nel capture dell'esempio di registrazione. La RFC 3261 contiene una sezione che elenca una grande quantità di header, assieme ad un tabella che ne specifica l'applicabilità nei contesti dei metodi di richiesta e dei codici di risposta, e del tipo di entità che li possono inserire, rimuovere e modificare; il loro elenco è mantenuto aggiornato presso IANA. In particolare, molte intestazioni sono semplicemente copiate nei pacchetti di risposta, a partire da quelle presenti nei pacchetti di richiesta. Per alcune di esse, ne viene indicato ora l'utilizzo: Intestazione Commento To RFC Esempio la URI del destinatario della richiesta 221 3261 Session Initiation Protocol Voice over IP From rappresenta la URI di chi invia la richiesta; nelle risposte, sia From che To restano invariati " Call-ID Un identificatore semi-casuale, che resta uguale per tutti i messaggi di uno stesso dialogo, ovvero è univocamente associato ad un INVITE iniziale " CSeq è costituito da un numero seguito dal nome del metodo che ha dato inizio alla transazione. Il numero si incrementa di uno ad ogni nuova transazione di uno stesso dialogo, e resta uguale per tutti i messaggi della stessa transazione. " Via inserito da ogni UAC che invia (o inoltra) una richiesta SIP, in cui indica il proprio indirizzo, porta, trasporto, ed un parametro branch utile per distinguere le diramazioni di un messaggio forkato. Ogni elemento di transito che deve inoltrare la risposta, rimuove l'intestazione da lui inserita, ed usa quella rimasta in cima per determinare a chi inviarla. In questo modo non occorre consultare il DNS, ed è sufficiente un Proxy Stateless " Max-Forwards utile per limitare il numero di volte che un messaggio è inoltrato " Contact contiene uno o più URI presso le quali il mittente di una richiesta desidera essere ricontattato " Allow annuncia i metodi supportati da una entità " Supported elenca le estensioni supportate, tra quelle elencate presso IANA via " Record-Route specifica la volontà di un Proxy, di essere mantenuto nel path dei futuri messaggi del dialogo " Route indica la sequenza di Proxy che il messaggio dovrà attraversare, determinato a seguito delle richieste espresse mediante gli header Record-Route, come descritto più avanti " invito e route set Stratificazione di SIP Per la definizione di SIP si è fatto ricorso alla suddivisione delle sue funzioni in termini stratificati, distinguendo, dal basso verso l'alto • uno strato sintattico e di codifica, che provvede alla formattazione ed interpretazione degli elementi dei messaggi; • uno strato di trasporto, che definisce come UAC e UAS si scambiano richieste e risposte, ovvero decide se usare TCP o UDP; • uno strato di transazione, che non esiste nei proxy stateless, e che gestisce le ritrasmissioni in accordo alle macchine a stati finiti che descrivono il comportamento per transazioni iniziate con un INVITE o meno, associa le risposte alle relative richieste, e gestisce i timeout; • uno strato Transaction User (TU), assente nei proxy stateless, che caratterizza il tipo di entità SIP, e che utilizza i servizi offerti dallo strato di trasporto SIP per interagire con le altre entità. Il TU rappresenta il nucleo della entità SIP, identificata dai metodi che è in grado di gestire, e mentre per le entità UA valgono le considerazioni svolte nelle sezioni da 8 a 15 della RFC 3261, le particolarità dei Proxy sono descritte alla sezione 16. 222 Lo strato applicativo di Internet Alessandro Falaschi Sessione, dialogo e transazione Per quanto appena discusso, osserviamo che mentre lo strato di transazione si prende carico dell'effettivo scambio di tutti i messaggi che intercorrono tra UAC e UAS, necessari allo svolgimento di una funzione elementare di SIP, i pacchetti effettivamente scambiati a questo fine, sono visti dal TU come una unica Transazione. Transazione La RFC 3261 distingue i comportamenti associati a transazioni iniziate con un INVITE o meno. Nel primo caso, se la risposta definitiva è 200 OK, la trasmissione dell'ACK non viene considerata parte della transazione, ed è effettuata a cura del TU dello UAC. Dal lato del chiamato, è il TU dello UAS che si prende carico di ritrasmettere il 200 OK, finché non riceve il corrispettivo ACK. Se invece la risposta appartiene alle classi 3xx-6xx, questa viene assorbita dallo strato di transazione dello UAC, che provvede a generare per suo conto l'ACK, mentre lo strato di transazione dello UAS provvede a ritrasmettere le risposte, finché non sono riscontrate dall'ACK. Nel caso, invece, di transazioni non legate ad una richiesta INVITE, lo strato di transazione dello UAC continua autonomamente a reinviare la richiesta, finché non riceve una risposta, mentre è lo strato di transazione dello UAS a re-inviare le risposte, per ogni richiesta ricevuta. Quando un UAC riceve una risposta, deve ricombinarla assieme alla richiesta orginaria. Per questo, devono corrispondere due cose: • il parametro branch associato al primo header Via nella risposta, è lo stesso di quello nella richiesta, e • il numero associato all'header CSeq, ed il parametro method ad esso associato, sono gli stessi che appaiono nella richiesta. 223 Session Initiation Protocol Voice over IP Dialogo Tutti i messaggi scambiati tra due UA tra l'inizio di una comunicazione e la sua conclusione, vanno a costituire un dialogo, che è individuato presso ogni UA in base al valore della intestazione Call-ID, al tag locale, ed al tag remoto. Questi ultimi due, sono stringhe inserite dagli UA, e si scambiano di ruolo: presso il chiamante, il tag locale è espresso come parametro del From, e quello remoto viene letto dal parametro tag associato al To, al momento della ricezione della risposta. Presso il chiamato invece, il tag locale è assocato al To, e quello remoto al From. La tripla di valori Call-ID, from-tag e to-tag inizia ad esistere su entrambi gli UA, a seguito della ricezione da parte dello UA chiamante di una risposta provvisoria 1xx contenente il to-tag, ponendo il dialogo in una fase precoce, che diviene confermata quando lo stesso to-tag è presente anche nella risposta definitiva 200 OK. Il dialogo cessa di esistere nel caso in cui non pervenga nessuna risposta definitiva, o se previene un CANCEL prima della stessa, oppure se la risposta definitiva non è un successo, mentre se è un successo, cessa di esistere a seguito della ricezione di una richiesta di BYE, e della relativa risposta. Durante il dialogo, la tripla che lo individua è presente in tutti i messaggi che vi fanno parte, permettendone la contestualizzazione di chi li riceve, in base alla esistenza di uno stato condiviso rappresentato dagli altri parametri del dialogo, come i valori dei CSeq delle due parti (che si incrementano ad ogni transazione del dialogo, ma restano costanti per i diversi messaggi della stessa transazione), le URI locale e remota, i parametri usati dalle estensioni, e la lista dei Proxy che deve essere attraversata dagli altri messaggi del dialogo. Per un esempio, possiamo fare riferimento al capture della procedura di INVITE. Sessione Una Sessione è individuata, oltre che dalla relazione tra una (o più) coppia di entità definita dal dialogo, anche dal (dai) tipo di media che verrà usato nel corso della stessa. Il desiderio di creare una nuova sessione è espresso con l'invio di un messaggio iniziale di INVITE, mediante il quale avviene la negoziazione dei parametri necessari alla trasmissione multimediale, basata su di un paradigma offerta-risposta; la sessione è quindi definitivamente creata a seguito dell'individuazione dell'insieme dei tipi di media che saranno usati per la comunicazione. L'offerta può essere formalizzata • mediante l'inserimento nel body della richiesta INVITE di una descrizione SDP, oppure • la richiesta può non contenere offerte, formalizzate invece mediante l'inserimento della descrizione SDP nel body del primo messaggio di risposta definitiva (il 200 OK), come ad esempio nel caso in cui l'invito deve attraversare un Gateway verso reti per le quali la formulazione di una offerta è demandata al chiamato. La risposta all'offerta consiste in una scelta tra le possibilità offerte, e viene veicolata nei due casi, rispettivamente, mediante l'inserimento di una descrizione SDP nel body del primo messaggio di risposta definitiva, ovvero nel body del messaggio di ACK. Nel caso in cui pervengano più risposte definitive per lo stesso INVITE, come nel caso in cui questo sia stato forkato, oppure inviato in multicast, ognuna di queste determina la creazione di un diverso dialogo, e tutti i dialoghi vengono a far pare della medesima sessione. Una sessione può essere modificata durante la sua esistenza mediante l'invio di un nuovo 224 Lo strato applicativo di Internet Alessandro Falaschi INVITE (detto re-INVITE), da parte di una delle parti che aderiscono alla sessione, ossia anche su iniziativa del chiamato. La modifica può riguardare, ad esempio, l'aggiunta di un nuovo media (ad esempio, video) alla sessione, e consistere quindi nell'inizio di un nuovo ciclo di offerta-risposta, attuato come già illustrato. A differenza dell'INVITE originario, un re-INVITE non può essere forkato, in quanto diretto alle parti già in comunicazione. Una sessione può essere terminata mediante l'invio di un BYE di una delle due parti dell'unico dialogo della sessione, mentre se la sessione è condivisa tra più parti, il BYE termina unicamente il dialogo corrispondente. Alternativamente, la sessione viene terminata a seguito della ricezione di una risposta definitiva di tipo diverso da 2xx, Infine, la sessione può essere terminata a seguito della ricezione di una richiesta CANCEL. Autenticazione L'associazione di un Contact Address con un AoR, ad opera di uno UA che contatta un Registrar, è una operazione che, se eseguita senza nessuna verifica di autorizzazione, permetterebbe a chiunque di ricevere le telefonate SIP in realtà dirette a qualcun altro. Pertanto, qualunque provider VoIP minimamente serio, intraprende una procedura di verifica del possesso di credenziali di accesso, da parte dello UA che richiede la registrazione. In linea con il resto delle specifiche, anche l'autenticazione SIP ricalca i metodi già visti per l'HTTP, in quanto consiste in una sfida che il Registrar invia allo UA, contenuta nella intestazione WWW-Authenticate presente in una risposta 401 Unauthorized, e basata sul metodo Digest. Così il VoIP provider che gestisce il Registrar, e rilascia l'AoR all'utente dello UA, concorda con quest'ultimo anche un segreto condiviso, o password, da usare in associazione alla parte user dell'AoR, per verificare l'autorizzazione all'uso del Registrar. Allo stesso modo, anche i messaggi inviati da uno UA al proprio Outbound Proxy devono essere autenticati, in modo da impedire al Proxy di comportarsi come un OpenRelay, ed inoltrare richieste provenienti da chiamanti sconosciuti. Nel caso (come è spesso) in cui Registrar e Outbound Proxy coincidano, viene ovviamente usata la stessa password stabilita in precedenza; altrimenti, occorre far riferimento da parte di entrambi, ad uno stesso authentication server. Federazione Resta aperta la questione, di come procedere alla autenticazione tra l'Outbound Proxy di partenza, ed il Registrar di destinazione, senza contare la presenza di eventuali altri Proxy intermedi: infatti, mentre tra UA, Registrar, e Outbound Proxy, i rapporti sono diretti, e mediati da uno stesso soggetto VoIP provider, non invece è pensabile di poter stabilire un segreto condiviso tra tutte le coppie di Proxy presenti in Internet. La soluzione attualmente più percorribile, sembra essere quella allo studio da parte del WG SPEERMINT di IETF, e che consiste nella definizione di Federazioni SIP, a cui i singoli provider VoIP possono aderire, e che determinano l'identità di una Certification Authority che firma i certificati digitali dei provider aderenti, da usare nell'ambito delle connessioni TLS che le coppie di Proxy federati devono usare, quando coinvolti dall'attraversamento della segnalazione SIP. L'uso di una stessa CA in comune tra i provider VoIP aderenti, gli permette di verificare l'autenticità dei certificati ricevuti dagli altri membri. Instradamento Ogni entità che invia (o inoltra) una richiesta, aggiunge una intestazione Via al messaggio, in cima a quelle già presenti, in cui sono indicati l'indirizzo IP, il trasporto, e la porta usati 225 Session Initiation Protocol Voice over IP dallo UAC, più un parametro branch che contiene una stringa creata semi-casualmente per l'occasione, e che viene citata nelle risposte, allo scopo di distinguere tra loro le diverse risposte che possono provenire da UAS situati su rami diversi di un fork. Lo UAS che riceve la richiesta, è in grado di correggere l'intestazione Via, nel caso in cui l'indirizzo IP e/o la porta citati dal mittente, siano differenti da quelli effettivamente osservati dal socket su cui la richiesta è pervenuta, ad es. perché il richiedente è NATtato: in questo caso, viene aggiunto al Via un parametro received= in cui si indicano i dati corretti e/o mancanti, come nell'esempio seguente. Quando una richiesta raggiunge lo UAS di destinazione, e questo genera una risposta, non inserisce nessuna nuova intestazione Via, ma usa quella in cima a tutte, per determinare a chi inviare la risposta, in cui include anche tutte le altre intestazioni Via. Se la risposta è ricevuta da un Proxy intermedio, questo dopo aver rimosso l'intestazione Via che lui stesso aveva inserito, usa a sua volta il Via in cima alla pila, per individuare a chi restituire la risposta, e così via, fino allo UAC originario. Route Set Quando un Proxy inoltra una richiesta che determina la creazione di un dialogo (tipicamente, un INVITE), può richiedere di rimanere nel path dei successivi messaggi appartenenti allo stesso dialogo. Per questo, prima di inoltrare il messaggio, vi inserisce un header RecordRoute in cui dichiara la propria URI. Se anche altri Proxy lungo il tragitto della richiesta di inizio dialogo vogliono restare nel path, aggiungono a loro volta una intestazione RecordRoute in testa alle altre. Quando la richiesta arriva allo UA di destinazione, questo memorizza gli indirizzi trovati nelle intestazioni Record-Route in un route set, che entrerà a far parte del contesto da applicare agli altri messaggi del dialogo, non appena la sua creazione verrà confermata dal messaggio di ACK. La risposta che ora lo UA chiamato invia verso il chiamante, utilizza la catena dei Via per ritrovare la strada verso il chiamante, e contiene inalterata la lista dei Record-Route costruita nel viaggio di andata, dato che i Proxy aggiungono i Record-Route nelle richieste, e non nelle risposte. Quando la risposta arriva allo UA chiamante, se questa è di successo, il route set viene memorizzato anche da questo lato. Da questo momento in poi, quando uno UA invia una richiesta appartenente ad un dialogo, vi inserisce un header Route, in cui copia il route set associato al dialogo; quindi, aggiunge un Via con il proprio indirizzo, ed usa il primo elemento del route set, per determinare a chi inviare la richiesta, indipendentemente dal valore della intestazione To, e della requestURI. Nel caso in cui si sia configurato un Outbound Proxy, questo comparirà come primo elemento del route set. Il Proxy che riceve questa richiesta, rimuove il proprio indirizzo dall'elenco che compare nel Route, ed usa l'indirizzo successivo, per inoltrare a sua volta la richiesta. Loose routing Con questo termine, i Proxy SIP annunciano la propria conformità alla RFC 3261, in contrapposizione a quelli il cui comportamento è definito dalla precedente RFC 2543, detti Stric Routers. Un Proxy annuncia il proprio comportamento da loose router aggiungendo nelle intestazioni che modifica, il parametro ;lr, oppure ;lr=on. 226 Lo strato applicativo di Internet Alessandro Falaschi Procedure Registrazione, service discovery, e autenticazione Nella procedura di registrazione, uno UA invia al proprio Registrar una richiesta (con il metodo REGISTER) con cui si richiede di associare il proprio AoR all'indirizzo IP comunicato mediante l'header Contact: nel caso in cui l'operazione vada a buon fine, il Registrar invia il messaggio di risposta 200 OK. Come risultato, il Registrar ha memorizzato la corrispondenza AoR-Contact in un Location Database, che verrà interrogato in seguito, nel caso di chiamate entranti, per localizzare l'utente. In questo file di capture,relativo alla procedura di registrazione per l'AoR [email protected], possiamo apprezzare il formato dei messaggi, e svolgere una serie di interessanti osservazioni. • il nome a dominio del Registrar è ing.uniroma1.it, e prima di inviare il messaggio SIP, viene interrogato due volte il DNS: ◦ viene prima eseguita una query per il RR di tipo SRV, allo scopo di individuare il Proxy SIP del dominio, e la porta su cui risponde; ◦ quindi, avviene la risoluzione che associa il nome a dominio del Proxy, al suo indirizzo IP ◦ nel caso in cui la query del RR di tipo SRV fosse fallita, la richiesta sarebbe stata direttamente inoltrata al nome a dominio corrispondente all'AoR; • le intestazioni From e To del REGISTER risultano uguali (a meno dei parametri tag), indicando che il chiamato è la stessa URI di colui che sta effettuando la registrazione (il chiamante); • l'intestazione Via presente nel pacchetto 5 è priva del numero di porta, e pubblica un indirizzo IP privato (e quindi irraggiungibile), ma queste informazioni vengono corrette dallo UAS fin dalla risposta temporanea del pacchetto 6, in cui troviamo Via: SIP/2.0/UDP 192.168.1.100;rport=1024;branch=z9hG4bKscwclcls;received=151.49.83.143 • la prima richiesta REGISTER sip:ing.uniroma1.it SIP/2.0 presente al pacchetto 5 viene respinta con una risposta SIP/2.0 401 Unauthorized, in quanto non erano presenti credenziali di autenticazione, e mediante l'header WWW- 227 Session Initiation Protocol Voice over IP Authenticate viene indicato il realm="ing.uniroma1.it" ed il nonce della sfida per procedere; • la richiesta viene reiterata al pacchetto 8 inserendo questa volta l'intestazione Authorization, che contiene la risposta alla sfida, e che stavolta viene accettata al pacchetto 10; • l'intestazione Contact della risposta contiene due diversi indirizzi, ovvero Contact: <sip:[email protected];transport=UDP>;expires=2590;received="sip:151.49.83.143:5060", <sip:[email protected]>;expires=3600;received="sip:151.49.83.143:1024" ◦ il primo è relativo ad una precedente registrazione per lo stesso AoR, effettuato da un diverso UA, mentre il secondo è relativo alla registrazione corrente; ◦ per entrambi i valori di Contact compare un indirizzo IP privato, mentre nel parametro received il Registrar ha annotato l'IP pubblico del router casalingo, ed i numeri di porta usati dagli UAC; • quando, al pacchetto 20, viene inviato un messaggio con l'intenzione di annullare la registrazione, l'intestazione Contact contiene il parametro expires=0, che indica appunto il desiderio di rimuovere l'associazione memorizzata nel Location Database • in effetti, la deregistrazione è tentata anche al pacchetto 17, ma in quel caso mancano le credenziali di autenticazione. Invito, creazione del dialogo e route set Nella procedura di invito, è pratica comune che la richiesta INVITE, prima di raggiungere lo UAS chiamato, attraversi almeno due proxy: l'Outbound Proxy del chiamante, e il Proxy/ Registrar del chiamato. Questa forma di instradamento prende il nome di trapezoide SIP, in base al particolare modo di raffigurare l'instradamento, riportato a fianco. Notiamo che nell'esempio riportato, la richiesta BYE che termina la comunicazione viene inviata direttamente, senza attraversare i due proxy. Questo può avvenire per il verificarsi di due condizioni: • i due telefoni VoIP hanno appreso, leggendo l'intestazione Contact inserita dal lato remoto, i rispettivi indirizzi IP, e • i due Proxy non hanno richiesto di rimane nel percorso di instradamento. Qui sotto, osserviamo un nuovo disegno un pò più dettagliato, che mette in evidenza la fase di richiesta al DNS necessaria a scoprire il proxy server di destinazione. Ciò che invece non è mostrato, è l'interrogazione del Proxy di destinazione al Location Database mantenuto aggiornato dal Registrar di b.com: evidentemente, in questo caso il location database risulta residente nel medesimo computer che ospita il Proxy. 228 Lo strato applicativo di Internet Alessandro Falaschi Nel diagramma di flusso che segue è riportato un esempio in cui l'Outbound Proxy ed il Registrar vengono a coincidere, e viene posta in evidenza la sequenza dei messaggi che vengono scambiati tra UAC del chiamante e lo UAS del Proxy, e quindi tra lo UAC del Proxy e lo UAS del chiamato. Come si vede, il Proxy invia immediatamente una risposta temporanea di tipo 100 Trying, allo scopo di riscontrare lo UAC della avvenuta ricezione della richiesta INVITE, ed arrestare il re-invio della richiesta. Quindi, • l'INVITE è inoltrato a destinazione; • il chiamato lo riscontra immediatamente con 100 Trying; • il chiamato inizia ad inviare una sequenza di risposte temporanee 180 Ringing, che sono immediatamente re-instradate dal Proxy verso il chiamante, mediante l'esame dell'header Via posto in cima agli altri; • quando la persona fisica chiamata risponde, lo UAS chiamato invia la risposta definitiva 200 OK, immediatamente inoltrata; 229 Session Initiation Protocol Voice over IP • il chiamante completa il ciclo del three way handshake inviando un ACK direttamente al chiamato (lo può fare, perché ne conosce il Contact Address, ed il Proxy non ha richiesto di restare nel path) • finalmente, le due parti sono in comunicazione vocale, trasportata da pacchetti RTP instradati direttamente tra le parti. Siamo ora pronti ad esaminare il risultato di un Capture ditraffico reale, relativo alla chiamata che [email protected] esegue verso [email protected], utilizzando come Outbound Proxy il nome a dominio ing.uniroma1.it, così come si ottiene eseguendo il capture presso il chiamante. Possiamo osservare che: • nei pacchetti 1-4 avviene la risoluzione DNS, che individua in smile.ing.uniroma1.it il server SIP delegato dal nome a dominio dell'Outbound Proxy, che viene quindi risolto nell'indirizzo 151.100.122.144; • al pacchetto 5 si inizializza il local tag della sessione, e sono definiti il Call-ID ed il CSeq: From: "alef" <sip:[email protected]>;tag=xglhe Call-ID: [email protected] CSeq: 986 INVITE • al pacchetto 8 sia il local tag che il Call-ID sono ri-usati, mentre il Cseq è incrementato di uno; oltre alla aggiunta di una intestazione Proxy-Authorization, contenente le credenziali di autenticazione, notiamo la presenza delle intestazioni Content-Type: application/sdp Allow: INVITE,ACK,BYE,CANCEL,OPTIONS,PRACK,REFER,NOTIFY,SUBSCRIBE,INFO Supported: replaces,norefersub,100rel che indicano la presenza di una offerta SDP, ed i metodi e le estensioni supportate • al pacchetto 11 viene ricevuta la risposta definitiva 200 OK, in cui troviamo Record-Route: <sip:198.65.166.131;ftag=xglhe;lr;transport=UDP>,<sip:[email protected]:5060;nat=yes;ftag=xglhe;lr=on To: "[email protected]" <sip:[email protected]>;tag=n448pds8f6igkkn3gt0ehij2 Contact: <sip:[email protected]:5060;transport=UDP;nat=yes> Allow: INVITE,ACK,BYE,CANCEL,REFER,NOTIFY,OPTIONS,PRACK e quindi osserviamo come con il parametro tag di To, sia ora definito il remote tag dell'altra parte in comunicazione, mentre in Record-Route siano indicati gli indirizzi dell'Outbound Proxy, e di un proxy del dominio del chiamato; inoltre, notiamo come entrambi si dichiarino dei loose routers. Inoltre, nel body del messaggio possiamo trovare l'SDP di risposta; • al pacchetto 12 viene inviato l'ACK, in cui osserviamo che nella Request-URI compare il Contact appena ricevuto, ma il messaggio viene comunque inviato al proprio Outbound Proxy 151.100.122.144, in virtù della presenza di questo indirizzo come ultimo valore dell'header Record-Route osservato nel 200 OK. Inoltre, il route-set dichiarato mediante l'header Route, ricalca quello dichiarato nel Record-Route ricevuto, ma con ordine invertito. Infine, notiamo come il To ora sia corredato del remote tag appreso con il 200 OK, e come il CSeq contenga ancora lo stesso numero, ma un metodo diverso, in modo da individuare una transazione differente. 230 Lo strato applicativo di Internet Alessandro Falaschi ACK sip:[email protected]:5060;transport=UDP;nat=yes SIP/2.0 Route: <sip:[email protected]:5060;nat=yes;ftag=xglhe;lr=on>,<sip:198.65.166.131;ftag=xglhe;lr;transport=UDP To: "[email protected]" <sip:[email protected]>;tag=n448pds8f6igkkn3gt0ehij2 CSeq: 986 ACK • al pacchetto 13 viene subito inviato un SDES RTP, e quindi inizia la trasmissione dell'RTP. Dopo circa 170 msec, inizia ad arrivare il flusso RTP proveniente dall'altro lato. Terminazione Sempre prendendo ad esempio il Capturegià esaminato, possiamo osservare che al pacchetto numero 1099 lo UA chiamate invia un messaggio SIP di BYE, instradato mediante l'ultimo Proxy presente nella catena descritta dal Record-Route (151.100.122.144), e le cui intestazioni lo mettono in relazione con il dialogo corrente. Quindi, al pacchetto 1103 sempre lo UA chiamate invia un pacchetto RTCP con PT 203 (Goodbye) direttamente allo UA chiamato, cessando al contempo l'invio di pacchetti RTP, e chiudendo il socket sul quale riceveva l'RTP del chiamato, causando da quel momento in poi una serie di messaggi ICMP Port unreachable. Dopo circa 500 msec, al pacchetto numero 1134, il messaggio SIP di BYE viene reiterato, e quindi, sono ricevute (dal proprio Proxy) due risposte 200 OK, una per ogni BYE, in cui l'intestazione RemoteIP comunica l'indirizzo dello UA remoto (151.49.97.148) a cui si riferisce il dialogo (e la sessione) che si sta chiudendo. Session Description Protocol Il Session Description Protocol (SDP) non definisce un insieme di messaggi (come è invece per SIP) che possono essere scambiati tra dispositivi in rete, bensì costituisce una sintassi in grado di descrivere i parametri che caratterizzano una trasmissione multimediale Internet, che a sua volta ha luogo mediante incapsulamento RTP. Contesti di utilizzo SDP è descritto dalla RFC 4566 del 2006, ma la sua definizione iniziale risale al 1998, come meccanismo per distribuire gli annunci di sessione relativi alle conferenze multicast di Mbone, in modo da indicare il tipo di codifica audio-video mediante i quali sono distribuiti i media, e gli indirizzi IP multicast e di trasporto, presso i quali gli aderenti si dovevano (per così dire) sintonizzare. Oltre all'uso originale, una descrizione SDP può essere distribuita mediante una URI HTTP, oppure come parte MIME di una email, utilizzando un header Content-Type: application/sdp. In questo caso, l'SDP può ancora essere usato per ricevere una sessione multicast, ammesso che l'instradamento multicast raggiunga il potenziale partecipante. D'altra parte, non necessariamente una sessione multimediale deve rappresentare una conferenza tra più parti, bidirezionale, ed in tempo reale: più banalmente, può essere unidirezionale, e/o relativa ad un evento registrato. Per questo, è stato definito l'RTSP, mediante il quale dei player unicast possono richiedere la ricezione di un contentuto multimediale, e che pure usa l'SDP come sintassi di descrizione dei contenuti, mentre altri aspetti della negoziazione dei parametri di trasmissione, sono gestiti dall'RTSP. La particolarità dell'uso di SDP nel contesto di SIP, è quello di consentire l'attuazione di un 231 Session Description Protocol Voice over IP meccanismo di offerta/risposta (descritto nella RFC 3264), tale da permettere a due entità di accordarsi su modalità comuni di trasmissione multimediale. Sintassi e sezioni Analizziamo gli elementi di una descrizione SDP, a partire da quella contenuta nel pacchetto numero 8 del capture relativo all'esempio dell'INVITE, a cui per chiarezza sono stati aggiunti dei commenti: v=0 o=alef 326810211 397034952 IN IP4 192.168.1.101 s=c=IN IP4 192.168.1.101 t=0 0 m=audio 8000 RTP/AVP 98 97 8 0 3 101 a=rtpmap:98 speex/16000 a=rtpmap:97 speex/8000 a=rtpmap:8 PCMA/8000 a=rtpmap:0 PCMU/8000 a=rtpmap:3 GSM/8000 a=rtpmap:101 telephone-event/8000 a=fmtp:101 0-15 a=ptime:20 # # # # # # # version origin session name contact tempo di inizio e fine media description attributo sottotipo del Payload Type 98 # formato per il Payload Type 101 # intervallo tra pacchetti di 20 msec Un SDP è composto da una serie di linee, dove il primo carattere individua un elemento sintattico, ed il cui tipo determina la semantica di ciò che segue il segno di uguale. La prima linea v= indica la versione dell'SDP (per ora, esiste solo la versione 0), e individua la sezione di SDP comune a tutta la sessione. Ogni linea che inizia per m=, indica l'inizio della sezione specifica di un media. Gli elementi comuni mostrati nell'esempio sono solo quelli obbligatori, mentre la RFC 4566 ne prevede diversi altri opzionali. Elementi L'elemento s= indica il session name, utile nel caso di conferenze Multicast, mentre per l'uso con SIP è irrilevante, e quindi sostituito da un segno meno; un discorso analogo vale per l'elemento time t=. L'elemento Origin o= ha una sintassi o=<username> <sess-id> <sess-version> <nettype> <addrtype> <unicast-address> e, a parte per il campo <sess-version>, costituisce un identificativo della sessione globalmente unico, mentre <sess-version> permette di distinguere tra versioni successive della stessa sessione. Il campo <unicast-address> indica il computer presso il quale la sessione è state definita. L'elemento contact c= può comparire solo nella sezione generale, oppure in ogni sezione specifica di un media, e indica l'indirizzo del computer che eroga i (il) media, oppure il gruppo multicast verso cui viene trasmessa; in quest'ultimo caso, occorre specificare anche un Time To Live multicast. L'elemento media description m=, ha un formato 232 Lo strato applicativo di Internet Alessandro Falaschi m=<media> <port> <proto> <fmt> ... in cui • media può tipicamente assumere i valori audio e video (ma non solo), • port indica un numero di porta di trasporto, che ◦ per le sessioni multicast, è quello su cui si deve mettere in ascolto chi vuol ricevere, e quindi anche quello verso cui si deve trasmettere, mentre ◦ per le sessioni unicast, è quello su cui la stessa entità che invia l'SDP, si mette in ascolto, ◦ in entrambi i casi, il numero di porta indicato sarà un numero pari, ed il fusso RTCP associato, utilizzerà il numero di porta (dispari) immediatamente successivo; • proto ha quasi sempre il valore RTP/AVP, che indica una pacchettizzazione RTP su UDP, con parametri definiti dall'RTP Profile for Audio and Video Conferences, • fmt rappresenta il media format description, e se <proto> è pari a RTP/AVP, è costituito da una serie di interi detti Payload Type (PT), che verranno successivamente usati per indicare la codifica incapsulata da RTP. Gli elementi attribute a= sono il meccanismo con cui è possibile estendere l'espressività di SDP, per mezzo della definizione di nuovi nomi. Possono comparire sia nella sezione generale, tipicamente per meglio caratterizzare una sessione multicast, ad esempio descrivendola in termini di parole chiave, o categorie gerarchiche, oppure ancora, indicare il verso della comunicazione, come nel caso di a=recvonly, a=sendonly, a=sendrecv, (che è il default, se non specificato). Rtpmap Quando invece gli elementi a= compaiono nella sezione specifica per un media, possono veicolare informazioni relative alla temporizzazione, alla qualità, a parametri del codec, ma l'uso più diffuso è quello di associare a ciascuno dei PT dichiarati dall'elemento m=, e non completamente descritti dall'AVP, il corrispettivo codec, frequenza di campionamento, ed eventuali altri parametri, in accordo alla sintassi a=rtpmap:<payload type> <encoding name>/<clock rate> [/<encoding parameters>] in cui encoding name è scelto in modo che, combinandolo assieme al nome del media che compare nell'elemento m= relativo, si ottenga il nome di un MIME Media Type costruito come media/encoding name, del tipo di quelli registrati presso IANA. Così, tornando ad esaminare l'esempio fornito, osserviamo che chi invia l'SDP comunica l'intenzione di ricevere il media audio sulla porta 8000, pacchettizzato in RTP su UDP, e dichiara di essere in grado di ricevere 6 diversi PT, i primi cinque dei quali sono relativi a codec audio, di cui il primo wideband (campionato a 16 KHz), mentre il sesto PT rappresenta la pressione di un tasto del telefono, e l'elemento a= viene usato per specificare in formato del media (a=fmtp:101 0-15 individua uno dei sedici DTMF). Infine, con a=ptime:20 viene specificato che ogni pacchetto RTP, corrisponde a 20 msec di segnale. 233 Session Description Protocol Voice over IP Offerta e risposta La RFC 3264 descrive le modalità secondo cui SDP può essere usato, assieme a SIP, per negoziare i parametri di una trasmissione multimediale. In questo modo, chi invia l'offerta compila una lista di media e di Payload Type (PT) che è in grado di gestire, in ordine di preferenza, e permette a chi invia la risposta di esprimere la sua scelta, allegando un diverso SDP, creato sulla base di quello ricevuto, ed in cui sono indicati i PT disponibili tra quelli indicati. Creazione della risposta La risposta conterrà una diversa linea o=, indicativa di chi risponde, una linea t= uguale a quella dell'offerta, ed un numero di sezioni media pari al numero di sezioni presenti nell'offerta, disposte secondo lo stesso ordine. Il numero di porta indicato nelle linee m= indica ora la porta su cui chi risponde intende ricevere il media incapsulato in RTP, e per rifiutare in toto un determinato media, viene indicato un numero di porta pari a zero. Nel caso di sessioni unidirezionali, anche chi trasmette solamente dovrà indicare un numero di porta diverso da zero, consentendo comunque la ricezione del canale RTCP. Accettando l'offerta per un media classificato come sendonly o receiveonly, nella risposta si indicherà per lo stesso una modalità rispettivamente receiveonly o sendonly. Chi risponde, può specificare un intervallo di pachettizzazione (con a=ptime:xx) diverso da quello dell'offerta. Ricezione della risposta Una volta inviata la risposta, chi risponde deve essere pronto a ricevere i media per i quali ha individuato dei codec opportuni, usando i parametri indicati nella risposta, e può iniziare a trasmettere, in accordo ai parametri contenuti nell'offerta. Una volta ricevuta la risposta, l'offerente deve iniziare a ricevere sulla porta indicata nell'offerta, ed a trasmettere verso la porta indicata nella risposta. Per ogni media e per ognuna delle due parti, la trasmissione avviene usando uno dei PT presenti in entrambi gli SDP scambiati, con l'ordine di preferenza indicato. Durante la sessione, una delle due parti può cambiare il codec usato per la trasmissione, e usarne un altro scelto tra quelli negoziati, per attuare ad esempio una codifica a bassa velocità nei periodi di silenzio, oppure per inviare dei DTMF via RTP. Modifica della sessione Durante la conversazione, una delle due parti può iniziare un nuovo ciclo di offerta-risposta, inviando un nuovo INVITE, allo scopo di modificare i parametri della sessione multimediale. Può essere proposto un nuovo insieme di PT per gli stessi media (ad esempio, a causa di una variazione di potenza computazionale, in base a considerazioni di risparmio energetico, oppure a mutate condizioni di qualità del collegamento), oppure possono essere aggiunti e/o rimossi nuovi media (ad es., video), e/o modificati indirizzo e porta del collegamento, oppure l'altra parte può essere messa in attesa. Esempio Ritornando ad esaminare il capture relativo all'esempio dell'INVITE, ecco di seguito l'SDP di risposta contenuto al pacchetto numero 11: 234 Lo strato applicativo di Internet Alessandro Falaschi v=0 o=Nokia-SIPUA 326810212 397034953 IN IP4 151.49.97.148 s=c=IN IP4 198.65.166.131 t=0 0 m=audio 40822 RTP/AVP 8 101 a=ptime:20 a=maxptime:200 a=fmtp:101 0-15 a=rtpmap:8 PCMA/8000/1 a=rtpmap:101 telephone-event/8000/1 a=nortpproxy:yes Questo SDP è generato da uno UA cordless VoIP, come risulta dalla linea o=, dove compare l'IP pubblico del router WiFi da cui ottiene la connettività Internet. Ciononostante, nella linea c= è riportato l'indirizzo IP di un elemento RTP proxy inserito nel path dei media, dal provider VoIP. Per quanto riguarda il media audio, la risposta presenta un unico valore di PT, il pcm legge A. Oltre a confermare (a=ptime:20) un intervallo tra pacchetti di 20 msec, l'SDP avverte che questo può crescere fino a 200 msec (a=maxptime:200). Infine, l'attributo non-standard a=nortpproxy:yes segnala che non occorre provvedere all'inserimento di altri elementi di attraversamento NAT. Real Time Protocol L'RTP è attualmente formalizzato dalla RFC 3550 del 2003, ma il suo primo draft risale al 1993. Rappresenta la modalità con cui lo strato applicativo incapsula i dati multimediali, prima di consegnarli ad un trasporto basato su UDP, in accordo allo schema riportato appresso, assieme al quale è indicato il calcolo della quantità minima totale delle informazioni di overhead; notiamo inoltre che il carico utile, ossia i dati multimediali trasportati, è indicato come payload. protocollo byte ethernet 14 IP 20 UDP 8 RTP 12 totale 54 I motivi per cui si preferisce usare UDP piuttosto che TCP, possono riassumersi in • una ridotta dimensione della intestazione; • la possibilità offerta allo strato applicativo di segmentare i dati da trasmettere in modo coerente con la loro pacchettizzazione; • l'inutilità pratica della affidabilità garantita dal TCP, preferendo omettere la riproduzione dei dati mancanti, piuttosto che chiederne la ritrasmissione 235 Real Time Protocol Voice over IP D'altra parte, l'assenza totale di funzionalità di controllo (di flusso, di errore e di congestione) da parte dell'UDP, viene sanata dall'uso congiunto del Real Time Control Protocol (RTCP), che anzichè trasportare dati, invia all'indietro (ad una velocità molto ridotta) informazioni di controllo come numero di pacchetti persi, ritardo e jitter. RTCP viene altresì inviato anche nella stessa direzione dell'RTP, per trasportare informazioni aggiuntive sul media contenuto nell'RTP. Contrariamente a molti altri casi discussi finora, l'RTP non usa un numero di porta ben nota, e l'unica convenzione adottata è di inviarlo su di un numero di porta pari, mentre sul numero di porta dispari immediatamente successivo, viene inviato l'RTCP. Inoltre, le particolarità che i dati multimediali trasportati impongono all'RTP sono descritte da un profilo di utilizzo dell'RTP stesso, ovvero di un documento addizionale che specifica l'interpretazione da dare ad alcuni campi dell'RTP che sono specificatamente previsti per la descrizione della natura del payload, e degli elementi informativi che RTP prevede per quel payload. In particolare, la quasi totalità della applicazioni aderiscono all'Audio Video Profile, o AVP, descritto nel seguito. Infine, una applicazione che voglia trasmettere/riprodurre dati multimediali trasportati su RTP, necessita di una ulteriore specifica, ovvero la definizione dei formati di codifica, che descrivono come una particolare rappresentazione dei dati (codec) determini la modalità di interpretazione dei dati contenuti nel payload, e come questi dati siano segmentati e pacchettizzati. Prima di descrivere il formato delle intestazioni RTP, che concretizzano gli aspetti ora introdotti, svolgiamo alcune considerazioni più generali, relative alla problematica della trasmissione multimediale su IP. Qualità, ritardo, jitter e buffer di riproduzione Mentre nella trasmissione dati è inammissibile che alcuni pacchetti vadano persi, nelle trasmissioni multimediali le eventuali discontinuità nella trasmissione vengono interpolate cognitivamente, e non costituiscono un reale impedimento alla comunicazione (ovviamente, entro certi limiti). Invece, un elemento determinante ai fini della usabilità della comunicazione interattiva, è legato al ritardo di Round Trip Time percepito, che dovrebbe essere mantenuto al disotto dei 200 msec, in modo da preservare la piena interattività dell'interloquio. Infine, occorre tenere conto che il ritardo di attraversamenteo della rete può variare, e la sua quantificazione è indicata con il termine di Jitter, anche se questo termine nasce per indicare solo le irregolarità statistiche di un dispositivo di temporizzazione (ad esempio, nell'audio HiFi). Le cause dell'insorgenza del Jitter in Internet sono le più disparate, ma l'effetto è che, prima di procedere alla riproduzione del segnale ricevuto, questo deve essere accodato in un buffer in modo da assorbire le variazioni di ritardo. 236 Lo strato applicativo di Internet Alessandro Falaschi La figura a lato ci aiuta a definire il criterio di dimensionamento del buffer di riproduzione. Sulle ordinate è riportata la percentuale di pacchetti che pervengono con entro un determinato intevallo temporale, di durata pari alla durata del segnale che è codificato entro il pacchetto, e pari quindi al tempo necessario per riprodurre quel segnale. L'estensione del playout buffer size viene quindi determinata in modo tale che la percentuale di pacchetti pervenuti in tempo utile (per poter riprodurre il segnale che trasportano) sia sufficente elevata, in modo da non penalizzare troppo la qualità dell'ascolto; allo stesso tempo, il ritardo introdotto deve essere mantenuto basso, in modo da non pregiudicare l'interattività della conversazione. Dato che la distribuzione dei ritardi deve essere valutata in base alle condizioni di rete effettive, ed al fine di individuare una soluzione di compromesso tra le due esigenze contrastanti, le soluzioni migliori sono quelle che effettuano un dimensionamento adattativo del buffer di riproduzione, tipicamente riducendone la dimensione nei momenti di inattività vocale, e aumentandola se la percentuale di pacchetti scartati tende ad aumentare. Osserviamo infine che nel caso in cui la quantità di dati (bit) trasportati sia uguale per ogni pacchetto, la dimensione così individuata corrisponde ad una estensione di memoria fisica, mentre se la quantità di bit è variabile, rappresenta unicamente il numero di pacchetti da accodare nel buffer. Calcolo del jitter RTP La RFC 3550 descrive un metodo di calcolo idoneo a fornire come risultato, un intero a 32 bit da inserire nel campo Reception Report dei pacchetti RTCP di tipo Receiver Report, e che esprime la stima del jitter in unità di timestamp, ossia di quanti campioni (in più o in meno) è l'intervallo medio tra i tempi di arrivo di due pacchetti, rispetto all'intervallo nominale previsto. Ad esempio, se il timestamp si incrementa di 160 campioni ad ogni pacchetto (pari a 20 msec per un segnale campionato a 8 kHz), mentre il tempo che intercorre tra l'arrivo di due pacchetti si alterna tra i valori di 30 e 10 msec (equivalenti ad una distanza, espressa in numero di campioni, pari a 240 e 80 campioni rispettivamente), allora il jitter, corrispondente a 10 msec, può essere espresso come pari a 80 campioni. In questi termini, se indichiamo con Si il timestamp del pacchetto i, e Ri il suo tempo di arrivo espresso in unità di timestamp, allora per due pacchetti i e j, la differenza D tra i tempi di arrivo può essere espressa come D(i,j) = (Rj - Ri) - (Sj - Si) = (Rj - Sj) - (Ri - Si) che fornisce zero in assenza di jitter, od un numero positivo o negativo in sua presenza. La stima del jitter medio si esegue conducendo una operazione di media mobile mediante la formula ricorsiva 237 Real Time Protocol Voice over IP J(i) = J(i-1) + (|D(i-1,i)| - J(i-1))/16 che possiamo commentare così: • se |D(i-1,i)| = J(i-1), come nell'esempio portato sopra, la stima J(i) si mantiene costante; • se |D(i-1,i)| > J(i-1), il termine che si somma a J(i-1) è positivo, e la stima J(i) tende a crescere; • se |D(i-1,i)| < J(i-1), il termine che si somma a J(i-1) è negativo, e la stima J(i) tende a diminuire. Unità dati applicative Un codificatore di segnale, opera tipicamente su intervalli temporali costanti, e suoi multipli naturali, e produce in uscita delle unità dati che, essendo generate a livello applicativo, vengono indicate come Application Data Unit (ADU). Ad esempio, un codificatore vocale basato su di un modello di sorgente vocale opera su intervalli di 10-40 msec (pari a 80-320 campioni, se il segnale è campionato a 8 KHz), mentre un codificatore video con frame rate (ad es) di 16 quadri/sec, produce ADU distanziate di 62,5 msec. Pacchettizzazione Il numero effettivo di bit di cui ogni ADU è composta, dipenderà dal tipo di codificatore in uso; in linea generale, le ADU devono essere ricevute (correttamente) per intero, pena l'impossibilità di procedere alla loro riproduzione. Quest'ultima considerazione è un ulteriore motivo fondante della trasmissione via UDP, perché a differenza del TCP, delega allo strato applicativo (in cui è in esecuzione il codificatore) la suddivisione delle ADU nei pacchetti da trasmettere. A questo punto, si possono presentare le seguenti scelte: • un pacchetto può contenere più di una ADU: è il caso del segnale vocale, ed il raggruppamento può migliorare l'efficienza della trasmissione (riducendo l'incidenza dei byte di intestazione rispetto a quelli di dati), ma può peggiorare il ritardo, e la degradazione associata alla perdita di un pacchetto; • una ADU deve essere suddivisa in più pacchetti: è il caso del segnale video, e per evitare il problema mostrato in figura, è sconsigliabile creare pacchetti che contengono frammenti appartenenti a ADU divese. Inoltre, dopo un pacchetto perso, 238 Lo strato applicativo di Internet Alessandro Falaschi quello ricevuto correttamente può iniziare con un frammento di ADU intermedio, e quindi occorre che i pacchetti contengano dei marker tali da consentire la risincronizzazione del flusso multimediale. Sincronizzazione Dal lato della riproduzione, una volta che le ADU sono state ricomposte, il jitter è stato assorbito dal buffer di riproduzione, ed il processo di codifica attuato in partenza è stato invertito, occorre ristabilire l'esatto ordinamento temporale, in modo che gli intervalli di segnale usati in partenza, vengano riprodotti con la medesima cadenza. Per questo, il processo di pacchettizzazione RTP inserisce delle informazioni temporali, tali da mettere in grado il lato ricevente di svolgere questo compito. Nel caso in cui la trasmissione consista in due o più media mutuamente sincronizzati prodotti da una medesima sorgente, (come ad esempio un video, ed il relativo audio), dal lato ricevente occore prevedere l'esistenza di un agente di sincronizzazione, in grado di usare i riferimenti temporali presenti nei due flussi per controllare entrambi i ritardi di riproduzione, ristabilendo la corretta sincronizzazione congiunta. Intestazioni I campi della intestazione RTP sono stati definiti allo scopo di permettere la soluzione delle problematiche fin qui accennate, e sono organizzati in tre (o più) gruppi di quattro byte, in accordo allo schema seguente: 2 3 4 8 9 16bit 32bit V P X CSRC count M Payload type Sequence number Timestamp Synchronization source (SSRC) 239 Real Time Protocol Voice over IP Contributing source (CSRC: variable 0 - 15 items, 2 octets each) i cui campi sono descritti come • V (2 bit) indica la versione, che per la RFC 3550 è 2; • P (1 bit) sta per padding, e se vale 1, indica che la dimensione in bytes del payoad non è un multiplo di quattro, e quindi nella sua parte finale vi sono dei bytes che non ne fanno parte; • X (1 bit) sta per extension, e se vale 1 indica che dopo l'SSRC e gli eventuali CSRC, sono presenti delle informazioni addizionali a disposizione per il progetto di eventuali estensioni al protocollo; • CSRC count o CC (4 bit): il numero di gruppi di 4 bytes identificativi delle Contributing Sources eventualmente presenti; • M (1 bit) è un Marker, ed è a disposizione della applicazione, per segnalare eventi che per il PT in uso rivestono una particolare rilevanza sintattica. Ad esempio, è usato per segnalare l'ultimo dei pacchetti in cui è stata frammentata una ADU relativa ad un frame video, in modo da riassemblarlo e passarlo al decodificatore, oppure per segnalare l'inizio di un talkspurt audio, dopo una sospensione dell'invio, associata all'evento di detezione del silenzio; • Payload Type o PT (7 bit), identifica il formato del payload mediante un codice numerico, in accordo a quanto definito da un apposito profilo, come l'AVP, ovvero a codici che indicano un payload dinamico, a sua volta associato ad un formato specifico mediante un meccanismo non-RTP, come l'SDP. Qualora RTP sia usato in accordo ad un profilo diverso da AVP, il byte contenente PT ed M può essere ridefinito con una nuova semantica; • Sequence number (16 bit), è un contatore monotonicamente crescente che si incrementa di uno ad ogni paccehtto inviato, e che consente al ricevitore di ristabilire l'ordinamento originario, e di rilevare la perdita di pacchetti; • Timestamp (32 bit), è il numero di campione corrispondente all'inizio dell'ADU contenuta nel pacchetto, ed il suo utilizzo è descritto per mezzo di alcuni esempi: ◦ un segnale audio campionato 8000 Hz e pacchettizzato ogni 20 msec, produce pacchetti contenenti 160 campioni, ed il timestamp dei pacchetti successivi si incrementa di 160 unità; ◦ nel caso in cui qualche pacchetto non venisse trasmesso, ad esempio in corrispondenza di pause del parlatore, il timestamp subirà una discontinuità proporzionale al numero di pacchetti soppressi, mentre il sequence number si incrementerà di uno; ◦ se (ad es.) un frame video viene suddiviso tra più pacchetti, tutti questi dichiareranno lo stesso timestamp; ◦ due flussi multimediali (ad es. audio e video) emessi da una medesima sorgente, ma con diversa frequenza di campionamento, possono essere sincronizzati mediante il calcolo, per ognuno di esse, dell'istante di tempo relativo ai timestamp; • Synchronization source o SSRC (32 bit), è un numero scelto in modo da identificare in modo sperabilmente univoco la sorgente che emette i pacchetti relativi ad una medesima sessione, ed è lo stesso per tutti i pacchetti dei diversi flussi emessi da una medesima sorgente; • Contributing source o CSRC (32 bit) contiene una lista di fino a 15 diverse sorgenti, ognuna indicata dal proprio SSRC, che prendono parte ad una operazione di missaggio, come mostrato nella figura seguente. Nel caso in cui un flusso attraversi unicamente un elemento di transcodifica (o translator), la SSRC di uscita è la stessa di quella di ingresso. 240 Lo strato applicativo di Internet Alessandro Falaschi Audio Video Profile La RFC 3551 descrive un profilo chiamato RTP/AVP, che definisce un insieme di corrispondenze di default tra i codici dei Payload Type RTP ed i formati di codifica del segnale audio e video, descrivendo quindi per gli stessi la sintassi con la quale il segnale codificato è rappresentato; inoltre, sono individuate le implementazioni di riferimento dei codificatori. Come anticipato nella discussione sull'SDP, il payload RTP può essere messo in corrispondenza con un MIME Type mediante gli elementi m= ed a=rtpmap: le RFC 4855 e 4856 specificano le procedure per registrare nuovi formati di payload RTP, e presso IANA è accessibile un registro dei parametri RTP che sono stati definiti. Payload statici e dinamici Un intervallo di codici di Payload Type viene riservato per la descrizione di formati di codifica statici, ovvero i cui parametri sono pienamente descritti dalla RFC 3551, e per i quali l'elemento a=rtpmap può essere omesso nell'SDP che descrive la sessione. Via via che vengono definiti nuovi metodi di codifica, l'uso di corrispondenze statiche tra i codici dei PT ed i formati di codifica, potrebbe determinare il rapido esaurimento dei codici disponibili. Pertanto, i codici da 96 a 127 sono riservati per poter dichiarare fino a 32 payload dinamici per una stessa sessione, ovvero associazioni tra un codice di PT ed un formato di codifica valido solo par la durata della sessione, e identificate in base alla corrispondenza tra il codice del PT, e l'elemento a=rtpmap relativo allo stesso codice di Payload dinamico, presente nell'SDP che definisce la sessione di cui il media fa parte. I possibili valori del media subtype mostrato nell'elemento SDP a=rtpmap: sono elencati presso IANA, e per ognuno di essi esiste un documento che li descrive (come ad es. per il codec audio speex). Real Time Control Protocol Come già accennato, l'RTP è affiancato da un protocollo di controllo, l'RTCP, che non trasporta dati multimediali, utilizza la porta UDP dispari immediatamente superiore a quella (pari) utilizzata dal flusso RTP a cui è associato, e viene inviato sia dai ricevitori che dai trasmettitori. Il formato di pacchetto è simile a quello dell'RTP, ed i primi 8 byte hanno la struttura 2 3 8 16bit 32bit V P count Packet Type Length 241 Attraversamento NAT e Firewall Voice over IP SSRC or CSRC a cui poi fanno seguito una serie di count sezioni, la cui composizione dipende dal valore del campo Packet Type, che indica uno tra i seguenti tipi di pacchetto: • Receiver Report o RR (PT 201) contiene count elementi di tipo Reception Report, presenti anche nei SR, che descrivono ◦ ◦ ◦ ◦ ◦ il SSRC a cui si riferisce fraction lost è la percentuale di pacchetti persi dall'ultimo report cumulative number of packets lost a partire dall'inizio trasmissione extended highest sequence number received interarrival jitter è una stima della deviazione standard del tempo di interarrivo tra pacchetti, espressa in unità di timestamp • Sender Report o SR (PT 200) ha le stesse informazioni fornite dai RR, ma in più contiene 20 byte di Sender Information, che descrivono ◦ NTP timestamp: 64 bits, che rappresentano l'ora NTP (espressa in secondi) presso la sorgente; ◦ RTP timestamp: 32 bits, è lo stesso valore usato per l'RTP, corrispondente all'ora NTP; ◦ sender's packet count: 32 bits, dall'inizio della trasmissione; ◦ sender's octet count: 32 bits, dall'inizio della trasmissione; • Source Description o SDES (PT 202) contiene count sezioni, per ognuna della quali viene citato il SSRC o CSRC a cui si riferisce, e sono presentate informazioni ad esso relative, come CNAME, NAME, EMAIL, PHONE, LOC, TOOL, NOTE, PRIV; • BYE (PT 203) segnala che una o più sorgenti non sono più attive; • APP (PT 204) può essere usato per sperimentare estensioni al protocollo. In uno stesso pacchetto RTCP si può trovare più di una delle sezioni descritte sopra, come ad esempio il caso in cui un RR viaggia assieme ad una SDES. Attraversamento NAT e Firewall I problemi del VoIP con i Firewall ed i NAT sono molteplici, e si può dire, che esistono fin dalle origini di entrambi. Firewall Per quanto riguarda i Firewall, questi sono dispostivi Router (eventualmente realizzati in Software come applicazioni eseguite presso lo stesso computer sorgente/destinazione del traffico) che applicano una politica che permette l'ingresso solo del traffico diretto verso un numero ristretto di porte ben note. Se si è a conoscenza dell'intervallo di porte che RTP userà per ricevere, possiamo tentare di includerle nella access list del Firewall; altrimenti, questo dovrebbe essere dotato di un Application Level Gateway in grado di esaminare i messaggi SIP, rilevare le porte RTP annunciate mediante l'offerta/risposta SDP, ed aprirle. Oppure, lasciare le porte aperte. Ma allora, che firewall sarebbe? 242 Lo strato applicativo di Internet Alessandro Falaschi NAT Per quanto riguarda i NAT, anche questi sono dei router, usati come Default Gateway per interconnettere una LAN (ai cui computer corrispondono indirizzi IP privati) con il resto di Internet, ovvero con dispositivi dotati di IP pubblici. Il Source NAT (SNAT) sostituisce alla coppia (IP, porta)LAN del mittente dei pacchetti uscita dalla LAN, una coppia (IP, porta)NAT in cui compare il proprio IP pubblico, ed un numero di porta scelto a caso. Quindi, crea uno stato della connessione, memorizzando questa associazione. Nel caso di una connessione TCP uscente, i pacchetti di ritorno sono diretti verso (IP, porta)NAT, ed il NAT provvede a re-impostare la coppia (IP, porta)LAN originaria. Nel caso dell'UDP usato per l'RTP, e per SIP, questo meccanismo in generale non funziona, perché • se l'RTP entrante si presenta al NAT senza un traffico uscente associato, il NAT non sa a chi mandarlo; • l'RTCP dovrebbe viaggiare su di un numero di porta pari a quello dell'RTP più uno, mentre il NAT lo mappa su di un numero di porta qualsiasi; • l'indirizzo IP che compare nell'elemento Contact dell'SDP presenta un IP privato, che non viene instradato dai router, e che quindi non può essere raggiunto dal resto di Internet; • quando una richiesta SIP su UDP arriva sulla porta 5060 di un NAT, ed è diretta ad uno UA con IP privato, il NAT non sa a chi consegnarla. Certo, se nell'SDP uscente ci fosse scritto l'indirizzo IP pubblico del proprio NAT, anziché quello privato dello UA, le richieste potrebbero essere istradate dai router di Internet. Inoltre, se l'RTP usasse lo stesso numero di porta sia per inviare che per ricevere, allora l'RTP uscente abiliterebbe anche quello entrante, ed i pacchetti potrebbero arrivare. Infine, se le richieste SIP entranti usassero una porta diversa, per ogni UA raggiungibile, il NAT potrebbe distinguerli l'uno dall'altro. In estrema sintesi, abbiamo descritto ciò che viene realizzato con le tecniche che andiamo ad esporre. Tipi di NAT Intanto, prendiamo atto che non tutti i NAT sono uguali, e possiamo distinguerli in 243 Attraversamento NAT e Firewall Voice over IP • full cone - tutto il traffico entrante verso (IP, porta)NAT, di qualunque provenienza, viene diretto verso un unico computer interno, che può così ospitare un server (ad es http, smtp, ftp); • restricted cone - il traffico viene accettato solo se proveniente da un IP pubblico verso il quale è già stato inviato traffico uscente, in tal caso, il traffico entrante è inoltrato al computer interno che ha generato quello uscente. E' il caso tipico dei router casalinghi; • port restricted cone - come sopra, con l'ulteriore restrizione che la porta di origine del traffico entrante, deve corrispondere a quella di destinazione del traffico uscente; • simmetrici - come sopra, con l'ulteriore restrizione che ogni volta che un IP privato contatta un nuovo IP pubblico, anche se usa porte sorgente e destinazione già usate, il NAT sceglie una nuova (diversa) porta come indirizzo sorgente del traffico uscente. Questo è il caso tipico per i NAT aziendali. UDP Hole Punching Letteralmente, si tratta di bucare il NAT, e consiste nella generazione preventiva di traffico UDP uscente, per poter poi ricevere quello entrante. Di fatto, questo metodo funziona a patto che solo uno dei due dispositivi in comunicazione sia NATtato. SIP Keep Alive Quando uno UA si registra, invia un pacchetto UDP verso la porta 5060 del Registrar, ed in questo modo, genera nel NAT la corrispondenza necessaria a ricevere la risposta. Il Registrar osserva la coppia (IP, porta)NAT da cui proviene la richiesta, ed la usa come Contact Address dello UA. Quando successivamente il Registrar riceve un INVITE destinato allo UA, usa questo Contact Address, e l'INVITE viene felicemente inoltrato dal NAT verso lo UA corretto. Sempre a patto che.... non sia scaduto il tempo per il quale il NAT mantiene l'informazione di stato! Infatti, allo scopo di poter ri-usare le porte impegnate, dopo che è trascorso un tempo (dai 30 ai 180 secondi) senza osservare traffico uscente, il NAT dimentica questa corrispondenza. La soluzione a questo problema, consiste nel continuare a generare traffico uscente, detto keep alive, ad esempio mediante messaggi SIP di rinnovo registrazione, oppure usando il metodo OPTION, oppure semplicemente inviando pacchetti UDP vuoti diretti al Registrar. RTP simmetrico Mentre per SIP è previsto di usare lo stesso numero di porta UDP sia come sorgente che come destinazione del traffico, non c'è nulla che prescriva la stessa cosa per l'RTP. D'altra parte, la possibilità di forare il NAT, consentita dall'uso (presso una entità NATata) della stessa porta come sorgente e destinazione, ha determinato la formalizzazione (RFC 4961) di questa pratica, che prescrive per l'entità NATtata, di iniziare a generare traffico in uscita il prima possibile, usando come numero di porta sorgente, lo stesso numero su cui si desidera ricevere il traffico entrante, annunciato nell'SDP. A differenza del SIP, nel caso dell'RTP simmetrico, l'entità corrispondente (non NATtata) può non utilizzare l'RTP simmetrico, e se lo adotta, non è necessario che usi le stesse porte dell'entità NATtata. Restano comunque ancora aperti il problemi: 244 Lo strato applicativo di Internet Alessandro Falaschi • come fare per rendere nota all'altra entità, la coppia (IP, porta)NAT che userà il proprio NAT? • come gestire il fatto che la porta dell'RTCP non viene mappata come quella dell'RTP, più uno? Uso di dispositivi esterni Mentre per SIP, è praticamente certo che il path di segnalazione attraversi un Registrar/ Proxy dotato di IP pubblico, grazie al quale attivare il pinhole del NAT, per l'RTP invece questa soluzione di fatto non funziona, dato che è molto probabile che entrambi gli UA in comunicazione siano NATtati. In questo caso, si aprono due alternative: • fare uso di UA ignari del problema NAT, e delegarne la soluzione ad un elemento aggiuntivo, situato su di un IP pubblico, con la funzione di proxy RTP, oppure • utilizzare degli UA che con l'ausilio di elementi esterni, riescono ad inserire nel Contact Address dell'SDP la coppia (IP, porta)NAT che verrà usata dal proprio NAT. Session Border Controller o B2BUA In questo caso, lo UA è completamente ignaro di risiedere dietro un NAT, e deve adottare la modalità di trasmissione via RTP simmetrico. In queste ipotesi, lo UA può comunicare anche se anche l'altro UA è NATtato, a patto che un elemento di transito (il Registrar, l'Outbound Proxy, od un ALG che si trova nel path) ospiti un elemento B2BUA, e modifichi gli indirizzi presenti nell'SDP in modo da riferire all'entità remota, anziché quelli forniti dall'altro UA, quelli messi a disposizione da una entità (RTPproxy) attraverso la quale verrà triangolato il traffico RTP. In particolare: • nell'SDP di offerta presente nella richiesta INVITE, viene specificato come Contact, l'IP pubblico dell'RTPproxy, e come porte usate dai diversi media, quelle scelte dall'RTPproxy stesso. • nell'SDP di risposta presente nella risposta SIP, è nuovamente specificato come Contatto, quello valido per raggiungere l'RTPproxy A questo punto, entrambi gli UA, pur credendo di inviare l'RTP direttamente al corrispondente, lo inviano invece all'RTPproxy. Quest'ultimo, non appena riceve dei pacchetti RTP da uno degli UA, prende nota dell'IP e della porta mittente, che evidentemente corrispondono al NAT dello UA, in modo da inoltrare verso di esse, l'RTP proveniente dall'altro UA. Dato che in questo modo, nessuno UA può ricevere, se prima non ha iniziato a trasmettere, sia per aprire il pinhole nel NAT, sia per rendere palese l'associazione tra la propria (IP, porta)NAT ed il traffico proveniente dall'altro estremo, entrambi gli UA iniziano ad inviare l'RTP il prima possibile. L'elemento RTPproxy può essere co-residente con il B2BUA, oppure essere scelto da quest'ultimo, tra una serie di alternative possibili, in modo da ridurre al minimo l'aumento di latenza legato al maggior percorso del traffico RTP. 245 Attraversamento NAT e Firewall Voice over IP Nonostante questa tecnica funzioni benissimo, viene criticata in quanto prefigura un vero e proprio attacco MITM, rendendo problematico un controllo di integrità sui messaggi SIP. STUN Simple Traversal of UDP Through NAT (STUN) è l'acronimo della metodologia descritta nella RFC 3489, che utilizza un server STUN presente sulla Internet pubblica, per permettere ad uno UA NATtato di scoprire il tipo di NAT (o Firewall) presente, qual'é il suo IP pubblico, e quale porta esterna viene scelta, in corrispondenza alla porta interna utilizzata dallo UA. STUN permette la comunicazione anche tra UA entrambi NATtati, usando l'RTP simmetrico, ma non funziona se uno UA è dietro ad un NAT simmetrico. Gli altri tipi di NAT infatti, mantengono una specifica coppia (IP, porta)NAT associata con continuità alla stessa coppia (IP, porta)LAN, per tutto il periodo di validità. Pertanto lo UA, prima di effettuare una chiamata, contatta il server STUN usando la stessa (IP, porta)LAN, che userà per l'RTP successivo, in modo che il server STUN gli comunichi la coppia (IP, porta)NAT corrispondente. Quindi, lo UA userà questi ultimi valori, per costruire il proprio SDP. TURN e ICE Per risolvere il problema dei NAT simmetrici, una proposta IETF denominata Traversal Using Relay NAT (TURN) prevede ancora che lo UA, prima di iniziare una comunicazione, contatti un server situato presso l'Internet pubblica (il server TURN), che stavolta offre anche un servizio di RTPproxy. Il server TURN comunica quindi allo UA una coppia (IP, porta)TURN che l'RTPproxy ha riservato per gestire il traffico destinato a quello UA: tutti i pacchetti che l'RTPproxy riceverà su (IP, porta)TURN, verranno re-inviati alla (IP, porta)NAT associata, permettendo al NAT di consegnarli alla (IP, porta)LAN corrispondente. A questo punto, lo UA userà la coppia (IP, porta)TURN per costruire l'SDP da allegare alle chiamate uscenti, come fosse un proprio fermo posta. A differenza della soluzione realizzata mediante il B2BUA, stavolta è lo UA a decidere cosa fare. Dato che l'RTPproxy viene a costituire un punto critico della architettura, con potenziali problemi di scalabilità, e che il suo uso è necessario solo nel caso di un NAT simmetrico, allora è preferibile usare un server STUN ogni volta che è possibile, e solo in caso di fallimento, usare il server TURN. Questo è proprio l'approccio seguito dalla proposta IETF detta Interactive Connectivity Establishment (ICE). 246 Lo strato applicativo di Internet Alessandro Falaschi Riferimenti • Trasmissione multimedia via Internet - RTP, SDP, RTSP, Multicast e Routing, Alessandro Falaschi 2002, seminario Info-Com • Video e voce in rete - H.323 & SIP - Alessandro Falaschi 2002, Workshop AICA • Teledidattica nell'era di SIP - Mbone, OpenMash & SIP - Alessandro Falaschi 2002, WorkShop Garr @ CNR - Bologna • voip.html.it • Applications and Services in Internet, SIP - Jouni Mäenpää • Henning Schulzrinne - pagina personale • Introduzione alle Reti di Prossima Generazione - dal corso di Sistemi Informativi e Servizi in Rete - Univ. di Genova • Mark Handley - pagina personale • VoIP: Ready for prime time - video di Madhu Yarlagadda, Director of Engineering, Yahoo! • Componenti per il VoIP - Presso il Laboratorio di Applicazioni Telematiche ◦ Componenti per il VoIP: i CODEC • Architectural Considerations for a New Generation of Protocols - David D. Clark, David L. Tennenhouse • Terena IP Telephony Cookbook • Sippy B2BUA • Mobile & Wireless Computing • Telefonia Aziendale e VoIP : studio di soluzioni e realizzazione • SIP - Call Transfer Enhancements Using the Refer Method - by CISCO • MediaProxy - by AG Projects • Security in IP Telephony: selected topics di Saverio Niccolini • NAT traversal for the SIP protocol by Diana Cionoiu • Realizzato con da Alessandro Falaschi - 247 Riferimenti Voice over IP 248 Lo strato applicativo di Internet Streaming Questo capitolo è un abbozzo. Prossimamente, i suoi contenuti saranno approfonditi ed estesi. Ciononostante, si desidera inserire fin d'ora qualche accenno e riferimento alla questione. • Introduzione • RTSP • Soluzioni Proprietarie ◦ ◦ ◦ ◦ Real Windows Media Quicktime Flash • Riferimenti Introduzione Per Streaming si intende la trasmissione e la fruizione di contenuti multimediali (audio e/o video) via Internet in modalità unidirezionale, ossia da una unica sorgente verso uno o più destinatari, senza che questi ultimi possano (debbano, vogliano) a loro volta inviare contenuti multimediali indietro al mittente. D'altra parte, sono fatte salve alcune funzionalità che potremmo definire tipiche di video-registratori e riproduttori di CD, MP3 e DVD, come la messa in pausa, il riavvolgimento e il fast forward, e l'accesso diretto ad un determinato istante temporale. Ma anche se l'unidirezionalità di base rende quasi ovvia una impostazione di tipo client-server della architettura, vi sono molti altri aspetti, come • • • • • • • • • erogazione di contenuti Live o On-Demand trasporto basato su UDP o TCP instradamento unicast o multicast eventuale coinvolgimento di una CDN o di una distribuzione Peer to peer compatibiità con altri sistemi di telecomunicazione, come TV analogica o digitale, e telefonia cellulare compatibilità con altri sistemi di immagazzinamento multimediale digitale, come il CD, l'MP3 ed il DVD scelta dei codec, della banda, della qualità di servizio indicizzazione e catalogazione dei contenuti presenza o meno di un sistema di gestione dei diritti (DRM) che determinano importanti alternative realizzative, così variegate, da causare di fatto un rallentamento della affermazione della tecnologia. Dal punto di vista delle applicazioni di erogazione e fruizione dei contenuti, il principale spartiacque può identificarsi nelle alternative tra soluzioni aderenti a standard pubblici e soluzioni proprietarie. Le soluzioni aderenti a standard pubblici sono conformi alle indicazioni contenute in documenti normativi come quelli emessi da IETF, ed hanno l'obbiettivo di favorire l'interoperabilità di apparati ed applicazioni di fornitori diversi, ma accomunati dal supporto a protocolli comuni come quello per il controllo (l'RTSP) dell'interazione tra client e server, quelli comuni al VoIP (l'SDP, l'RTP) per la descrizione ed il trasporto dei contenuti, ed a formati di codifica e pacchettizzazione descritti da standard pubblici. RTSP Streaming Le soluzioni proprietarie tendono invece ad offrire soluzioni integrate, fornendo ad esempio sia i componenti server che i client, adottando protocolli e codec coperti da brevetto, applicando sistemi di protezione che impediscono la fruizione non autorizzata. Infine, è da citare un approccio che di fatto consente di realizzare le funzioni basilari dell streaming (come ad esempio il posizionamento intermedio) anche con un protocollo nato per tutt'altro, come per l'HTTP streaming. RTSP Il Real Time Streaming Protocol è descritto nella RFC 2326, e da allora, soggetto a revisione. E' un protocollo strettamente Client-Server, e si occupa solo del controllo della riproduzione (inizio, pausa, posizionamento, arresto), delegando a SDP ed RTP i compiti di descrivere il contenuto, e di trasportare i dati multimediali. Sotto molti aspetti somiglia all'HTTP, ma a differenza di questo è stateful, in quanto la possibilità per il client di mettere in pausa la riproduzione, obbliga il server a ricordare per la durata della sessione, le informazioni relative ai suoi client. Svolgiamo un rapidissimo esempio del suo funzionamento, discutendo il seguente capture, in cui i messaggi inviati dal client sono indicati in rosso: 1 OPTIONS rtsp://labtel.ing.uniroma1.it/spider-man2.mp4 RTSP/1.0 2 CSeq: 9 3 User-Agent: VLC media player (LIVE555 Streaming Media v2007.02.20) 4 5 RTSP/1.0 200 OK 6 Server: DSS/5.5.4 (Build/489.13; Platform/Linux; Release/Darwin; ) 7 Cseq: 9 8 Public: DESCRIBE, SETUP, TEARDOWN, PLAY, PAUSE, OPTIONS, ANNOUNCE, RECORD 9 10 DESCRIBE rtsp://labtel.ing.uniroma1.it/spider-man2.mp4 RTSP/1.0 11 CSeq: 10 12 Accept: application/sdp 13 User-Agent: VLC media player (LIVE555 Streaming Media v2007.02.20) 14 15 RTSP/1.0 200 OK 16 Server: DSS/5.5.4 (Build/489.13; Platform/Linux; Release/Darwin; ) 17 Cseq: 10 18 Last-Modified: Fri, 13 Aug 2004 03:20:55 GMT 19 Cache-Control: must-revalidate 20 Content-length: 1223 21 Date: Tue, 08 Apr 2008 04:48:00 GMT 22 Expires: Tue, 08 Apr 2008 04:48:00 GMT 23 Content-Type: application/sdp 24 x-Accept-Retransmit: our-retransmit 25 x-Accept-Dynamic-Rate: 1 26 Content-Base: rtsp://labtel.ing.uniroma1.it/spider-man2.mp4/ 27 28 v=0 29 o=StreamingServer 3416618880 1092367255000 IN IP4 151.100.122.171 30 s=/spider-man2.mp4 31 u=http:/// 32 e=admin@ 33 c=IN IP4 0.0.0.0 34 t=0 0 35 a=control:* 36 a=isma-compliance:1,1.0,1 37 a=mpeg4-iod: "data:application/mpeg4-iod;base64,AoCAgxsAT///DwP/ A4CAghgACUD0ZGF0YTphcHBsaWNhdGlvbi9tcGVnNC1vZC1hdTtiYXNlNjQsQVlDQWdSVUJnSUNBT1FLZkE0Q0FnRElBQVFBRWdJQ0FGVUFWQUFFVE 38 a=range:npt=0- 126.45500 39 m=audio 0 RTP/AVP 96 40 a=control:trackID=5 41 a=rtpmap:96 mpeg4-generic/22050/2 42 a=mpeg4-esid:1 43 a=fmtp:96 streamtype=5; profile-level-id=15; mode=AAC-hbr; config=139000; SizeLength=13; IndexLength=3; Index 44 m=video 0 RTP/AVP 97 250 Lo strato applicativo di Internet 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 Alessandro Falaschi a=control:trackID=8 a=rtpmap:97 MP4V-ES/6003 a=mpeg4-esid:2 a=fmtp:97 profile-level-id=1; config=000001b003000001b509000001000000012000845d4c283c2080a31f; SETUP rtsp://labtel.ing.uniroma1.it/spider-man2.mp4/trackID=5 RTSP/1.0 CSeq: 11 Transport: RTP/AVP;unicast;client_port=32906-32907 User-Agent: VLC media player (LIVE555 Streaming Media v2007.02.20) RTSP/1.0 200 OK Server: DSS/5.5.4 (Build/489.13; Platform/Linux; Release/Darwin; ) Cseq: 11 Last-Modified: Fri, 13 Aug 2004 03:20:55 GMT Cache-Control: must-revalidate Session: 6519445514613088315 Date: Tue, 08 Apr 2008 04:48:00 GMT Expires: Tue, 08 Apr 2008 04:48:00 GMT Transport: RTP/AVP;unicast;source=151.100.122.171;client_port=32906-32907;server_port=6970-6971;ssrc=3C06B58A SETUP rtsp://labtel.ing.uniroma1.it/spider-man2.mp4/trackID=8 RTSP/1.0 CSeq: 12 Transport: RTP/AVP;unicast;client_port=32910-32911 Session: 6519445514613088315 User-Agent: VLC media player (LIVE555 Streaming Media v2007.02.20) RTSP/1.0 200 OK Server: DSS/5.5.4 (Build/489.13; Platform/Linux; Release/Darwin; ) Cseq: 12 Session: 6519445514613088315 Last-Modified: Fri, 13 Aug 2004 03:20:55 GMT Cache-Control: must-revalidate Date: Tue, 08 Apr 2008 04:48:00 GMT Expires: Tue, 08 Apr 2008 04:48:00 GMT Transport: RTP/AVP;unicast;source=151.100.122.171;client_port=32910-32911;server_port=6970-6971;ssrc=0E4A8A71 PLAY rtsp://labtel.ing.uniroma1.it/spider-man2.mp4/ RTSP/1.0 CSeq: 13 Session: 6519445514613088315 Range: npt=0.000User-Agent: VLC media player (LIVE555 Streaming Media v2007.02.20) RTSP/1.0 200 OK Server: DSS/5.5.4 (Build/489.13; Platform/Linux; Release/Darwin; ) Cseq: 13 Session: 6519445514613088315 Range: npt=0.00000-126.45500 RTP-Info: url=rtsp://labtel.ing.uniroma1.it/spider-man2.mp4/trackID=5;seq=1461;rtptime=700028915,url=rtsp://l TEARDOWN rtsp://labtel.ing.uniroma1.it/spider-man2.mp4/ RTSP/1.0 CSeq: 14 Session: 6519445514613088315 User-Agent: VLC media player (LIVE555 Streaming Media v2007.02.20) RTSP/1.0 200 OK Server: DSS/5.5.4 (Build/489.13; Platform/Linux; Release/Darwin; ) Cseq: 14 Session: 6519445514613088315 Connection: Close Il client inizia il dialogo interrogando il server a riguardo dei metodi disponibili, mediante il metodo OPTIONS. Come si vede, anche in questo caso i messaggi convogliano informazioni semantiche mediante l'uso di intestazioni, come Cseq, che si incrementa di uno ad ogni nuovo messaggoi del client, ed è citato uguale nella relativa risposta. Alla riga 5 il sever risponde con una sintassi ormai nota, formata da una prima linea contenente un codice numerico di successo, ed alla riga 10 il client passa a chiedere la descrizione di una URI di cui si desidera la riproduzione. Questa descrizione è fornita nella risposta di riga 15, mediante un elemento SDP contenuto nel body della risposta stessa. 251 Soluzioni Proprietarie Streaming Notiamo che negli header di questa risposta sono presenti elementi indicanti la data del contenuto originario, la data attuale, intestazioni dedicate ai proxy ed al controllo cache, ed intestazioni più propriamente dedicare allo streaming. Per quanto riguarda l'SDP, notiamo una serie di linee a= (righe 36-38) mediante le quali si asserisce l'adesione alle specifiche ISMA, un Initial Object Descriptor di mpeg4, e la durata in secondi dell'intero clip. Quindi, alla linea 39 troviamo la linea a= relativa all'audio, associato al payload type dinamico 96, e per il quale non è ancora stata negoziata una porta. Le linee 40-43 indicano che l'audio è associato alla traccia 5 (il file da cui si parte, contiene audio e video del clip multiplati assieme nello stesso file, ma suddivisi in differenti tracce), e che il PT 96 corrisponde ad un audio mpeg4, campionato a 22050 Hz, stereo, più altre indicazioni specifiche. Le righe 44-48 descrivono invece il media video, associato al PT dinamico 97, corrispondente ad un MP4, presente sulla traccia 8 del file originale. Il server RTSP dovrà demultiplare i flussi audio e video presenti nel file originale, e generare per ognuno di essi un diverso flusso RTP, mantenendo per questi la possibilità di rimanere sincronizzati. Alla riga 49 il client inizia a predisporre il server alla trasmissione, comunicando (riga 51) le porte su cui intende ricevere RTP ed RTCP per quanto rigurda l'audio, che sono riscontrate dal server alla riga 62; inoltre alla riga 59 il server assegna alla richiesta un identificativo di sessione. Questo identificativo è citato alla riga 67, assieme alla richiesta di SETUP pe il media video (riga 64), che viene a sua volta riscontrato dal server con la risposta che inizia alla riga 70. Finalmente, alla riga 80 il client richiede il PLAY dei media associati alla sessione appena definita, riscontrato dal sever alla riga 86. Infine, la riga 93 rappresenta la richiesta del client di arrestare la riproduzione. Soluzioni Proprietarie Real Real Networks è il fornitore di una architettura di streaming composta da server (Helix, versione di valutazione), client (RealPlayer), encoder (RealProducer), e codec (RealAudio, RealVideo). Può essere ritenuta la prima soluzione commerciale per la distribuzione multimediale su Internet. Mentre la componente di controllo è attuata in accordo all'RTSP, il trasporto usa (preferenzialmente) un protocollo proprietario (RDT), così come anche il codec non segue gli standard pubblici. Viene inoltre definito un formato di descrizione indicato come Real Audio Metadata (.ram), ed un linguaggio di sincronizzazione tra contenuti multimediali e presentazioni testuali, il Synchronized Multimedia Integration Language (.smil). Infine, il player è in grado di riprodurre anche altri formati e contenitori, e gran parte del codice sorgente alla base della soluzione di Real, è stato rilasciato con licenza OpenSource. Windows Media Vedi Wikipedia Quicktime Vedi Wikipedia 252 Lo strato applicativo di Internet Alessandro Falaschi Flash Il plugin Flash di Adobe risulta installato nel 99% dei browser web, ed anche se la riproduzione video risulta penalizzata rispetto ad applicazioni client indipendenti, la sua estrema diffusione è la ragione principale della sua adozione quasi universale. Per diverso tempo questo plugin si è limitato a riprodurre files ShockWaveFlash (con estensione .swf), un formato proprietario di grafica vettoriale, che si è poi evoluto a formato contenitore di codifiche multimediali. Successivamente, si è definito un ulteriore formato detto Flash Video (con estensione .flv), che può essere riprodotto anche da un player esterno al browser. Un file flv può essere ricevuto come singolo oggetto per una successiva riproduzione, oppure visionato durante la ricezione in forma di download progressivo HTTP, o in streaming RTMP. Nonostante la licenza proprietaria neghi il diritto di sviluppare un Player diverso, ne esistono diverse realizzazioni indipendenti come ad esempio JW FLV Player. Recentemente, Adobe ha annunciato l'intenzione di rendere pubbliche le specifiche di RTMP e, nell'ambito dell'Open Screen Project intende anche inserire un componente peer to peer al suo Flash Player 10. Riferimenti Realizzato con da Alessandro Falaschi - 253 Riferimenti Streaming 254 Lo strato applicativo di Internet Mondo Linux In questa sezione (senza pretese), riportiamo descrizioni, commenti e riferimenti per l'uso di base di Linux come strumento di studio, di lavoro e di sviluppo • Diritti e privilegi ◦ ◦ ◦ ◦ ◦ Diritti degli utenti e permessi dei file Il super-utente root Diritti dei programmi Diritti di apertura dei socket Sudo • Comandi da tastiera ◦ ◦ ◦ ◦ Wildcard Il filesystem Tasti utili La shell ▪ Script ◦ Variabili di ambiente ▪ Genealogia ▪ Variabili famose • Processi ◦ ◦ ◦ ◦ ◦ Redirezione Pipe Process ID Background e terminazione Chiamate di sistema • Avvio ◦ Gli script di Init ◦ Upstart ◦ /etc/rc.local • • • • Sottosistema Cron Sottosistema dei Log Configurazione della Rete Riferimenti Diritti e privilegi Svolgiamo qui una breve digressione a riguardo del sistema degli utenti offerto da Linux, e di come questo si riallacci alle questioni inerenti il lancio di processi server. Mondo Linux Diritti degli utenti e permessi dei file Ad ogni file presente sul disco, sono associati degli attributi, mostrati eseguendo il comando ls con l'opzione -l: [alef@localhost ~]$ ls -l totale 436 drwxrwxr-x 2 alef alef 4096 -rw------- 1 alef alef 96703 drwxrwxr-x 2 alef alef 4096 drwxrwxr-x 2 alef alef 4096 -rw-r--r-- 1 root root 129142 drwxrwxr-x 3 alef alef 4096 drwxrwxr-x 12 alef alef 4096 drwxrwxr-x 4 alef alef 4096 22 19 18 12 13 31 25 24 gen gen gen dic dic ott gen gen 16:04 16:06 09:37 17:09 14:38 18:37 00:17 23:34 autosave autosave.xmi bin citta_utopia enum+39069090611.cap images infocom t Il primo carattere indica in qualche modo il tipo i file, e nel caso di una directory, prende il valore d, mentre per un file regolare, si ha un trattino -. Seguono 3 triple di caratteri del tipo rwx, in cui una o più lettere possono essere sostituite da -. Ogni tripla è una rappresentazione leggibile di tre bit, che se posti ad uno od a zero, rappresentano rispettivamente la possibilità (o meno) per il file di essere letto (read), scritto (write) ed eseguito (executed); la prima tripla assegna questi privilegi al proprietario del file, la seconda al gruppo di utenti che fa parte del gruppo a cui appartiene anche il file, e la terza tripla definisce i diritti per il resto del mondo. Ma per dare un senso alla spiegazione appena fornita, occorre prima illustrare l'organizzazione degli utenti in Linux. Molto spesso, un utente appena nato appartiene ad un solo gruppo, con nome uguale a quello dell'utente. La creazione di un nuovo gruppo (vuoto) denominato (as es.) topolinia può avvenire ad opera dell'utente root, eseguendo il comando addgroup topolinia, mentre l'inserimento (ad es.) dell'utente (esistente) pippo nel gruppo topolinia avviene eseguendo il comando adduser pippo topolinia. Il creatore del file, è quello il cui nome compare nella terza colonna; in Linux, ogni utente può appartenere ad uno o più gruppi, mentre i files possono appartenere ad un solo gruppo, 256 Lo strato applicativo di Internet Alessandro Falaschi descritto dal nome che compare nella quarta colonna. Se un utente vuole operare su di un suo file, si applicano i diritti descritti dal primo gruppo di lettere; se invece il file non è suo, ci sono due possibilità: • o uno dei gruppi di cui l'utente fa parte, è proprio il gruppo a cui è associato il file, ed in tal caso per l'utente valgono i privilegi descritti dal secondo gruppo di lettere, oppure • il file è assegnato ad un diverso gruppo di cui l'utente non fa parte, ed allora valgono i privilegi del terzo gruppo di lettere. Per impostare il proprietario e il gruppo del file prova.txt si può usare il comando chown (change owner) chown utente.gruppo prova.txt mentre per modificare i tre gruppi di permessi, si usa il comando chmod chmod ABC prova.txt in cui ABC rappresenta un numero di tre cifre ottali, ognuna delle quali codifica i tre bit dei permessi rwx, dando alla r peso 4, alla w peso 2 ed alla x peso 1, in accordo allo schema Permessi ABC Descrizione rw-r--r- 644 L'utente proprietario può accedervi in lettura e scrittura (4+2=6), mentre sia gli appartenenti al gruppo, sia gli altri utenti, possono solo accedervi in lettura. rwxr-x-- 750 L'utente proprietario può accedervi in lettura, scrittura ed esecuzione (4+2+1=7); gli utenti appartenenti al gruppo possono accedervi in lettura e in esecuzione (4+1=5); gli altri utenti non possono accedervi in alcun modo. rw------ 600 L'utente proprietario può accedervi in lettura e scrittura (4+2=6), mentre tutti gli altri non possono accedervi affatto. Il super-utente root Si tratta di un utente particolare, il cui nome è appunto root (radice), al quale non si applicano le restrizioni definite dai privilegi necessari per l'accesso ai files, e che quindi può compiere qualunque operazione sugli stessi. In particolare, le operazioni che determinano conseguenze che interessano l'intero computer, e non il singolo utente, possono essere intreprese solamente dall'utente root. Diritti dei programmi Per eseguire un programma, non è sufficiente che il suo codice eseguibile sia contenuto in un file, ma occorre che il file sia anche dichiarato eseguibile, e che l'utente che intende eseguirlo, sia in possesso dei diritti di esecuzione. Quando un programma viene eseguito, eredita l'identità ed il gruppo dell'utente che lo ha lanciato; in particolare, il programma potrà eseguire solo le operazioni che sono possibili per l'utente stesso: potrà cioé leggere/ scrivere solo in directory che sono a loro volta leggibili/scrivibili da tale utente. Quindi, anche se un programma appartiene a root (che è il super utente di amministrazione, che 257 Mondo Linux può fare qualunque cosa, senza rispetto per i permessi vigenti), quando questo viene lanciato da un "utente normale", i diritti del programma sono gli stessi dell'utente che lo ha lanciato. Diritti di apertura dei socket Le porte ben note su cui si pongono in ascolto i demoni di un server, con un indirizzo di trasporto (porta) inferiore a 1024, per essere eseguiti, devono possedere i privilegi di root. Il motivo di questa scelta risiede nella considerazione che, essendo tali programmi in grado di rispondere a richieste di servizio esterne, devono essere sotto il diretto controllo dell'amministratore del computer. Ciò non toglie che sia possibile, per root, di delegare ad un utente qualunque la possibilità di lanciare il programma: ciò è possibile, agendo su di un ulteriore attributo del file, il bit suid (Set User IDentity), che quando è settato, permette al programa di girare con i privilegi del proprietario del programma, anzichè con quelli di chi l'ha lanciato. Un programma lanciato in questo modo, in genere, dopo aver completato le operazioni per le quali sono necessari i privilegi di root, come ad esempio aprire socket su porte ben note ed i files di log, cede i propri privilegi, e torna ad assumere quelli di un utente non privilegiato, invocando la system call setuid(). In questo modo, se durante la sua esecuzione si dovesse verificare una qualche situazione non prevista, che potesse portare il programma ad effettuare operazioni che potrebbero compromettere la sicurezza del sistema, il programma stesso non potrà arrecare danni a nessuna configurazione critica, proprio in virtù dei bassi privilegi di cui gode. Quanto esposto, è una delle ragioni della scarsa attaccabilità di un sistema Unix, rispetto ad altri. Sudo Quando un utente non privilegiato ha necessità di accedere a delle risorse concesse solo a root, oppure di eseguire un programma come root, deve diventare root mediante il comando su - ed inserire la password di root; l'opzione meno (-) provoca l'esecuzione del .bashrc di root, e quindi la definizione delle variabili di ambiente previste per root. Al termine delle operazioni, l'utente può tornare in sé con la combinazione di tasti Control-d. Nel caso si dimentichi di farlo, continuerà a lavorare come root, con il rischio di modificare inavvertitamente files importanti, o di creare files che poi come utente non privilegiato, non riuscirà più a modificare. Questa rigida suddivisione di ruoli tra super-utente e utenti non privilegiati, oltre che offrire una maggiore sicurezza, affonda le sue radici nel tempo in cui un unico computer era usato allo stesso tempo da utenti diversi, ed era amministrato da una terza persona. Attualmente, è molto più probabile che il computer sia usato da una unica persona, contemporaneamente utente ed amministratore; oppure, che chi possiede la password di root non desideri "darla in giro" così facilmente, e nemmeno dover ogni volta andare ad immetterla di persona. Inoltre, l'utente root potrebbe voler esplicitamente delegare a qualche utente non privilegiato, l'esecuzione di alcuni compiti particolari. Per questi motivi, si è nel tempo affermato il meccanismo previsto dal comando sudo (Super User DO), che permette ad un utente non privilegiato di assumere l'identità di root per il tempo strettamente necessario alla esecuzione di un comando, immettendo 258 Lo strato applicativo di Internet Alessandro Falaschi sudo comando seguito dalla propria (di utente non privilegiato) password; infatti, in questo caso occorre solamente sincerarsi che l'utente sia proprio lui, in quanto abilitato ad eseguire quel comando con i privilegi di root, e non qualcun altro che per caso passa davanti al computer lasciato incustodito da chi aveva acceduto inizialmente. Al termine della esecuzione del comando, l'utente riprende automaticamente la propria identità. L'utente root può, editando il file /etc/sudoers, abilitare i diversi utenti, ad eseguire uno o più comandi con i privilegi di root; una sua configurazione particolarmente permissiva, permette di far fare tutto a tutti. Comandi da tastiera Benchè anche con un sistema Linux, si possa fare praticamente tutto senza togliere la mano dal mouse, l'uso dei comandi da tastiera permette da un lato di comprendere meglio ciò che succede, e dall'altro costituisce spesso il modo più rapido di eseguire un comando, indipendentemente dall'interfaccia grafica, a partire dalla memoria della sua sintassi. In rete esistono diversi tutorial relativi a questi aspetti, come MPL, AIL, ML, UFI, IGL, a cui è sicuramente opportuno far riferimento per una trattazione un minimo più dettagliata, rispetto a quella che brevissimamente riassumiamo qui. Si tenga comunque presente che è prassi comune, in caso di dubbio, invocare i comandi con l'opzione -h oppure --help, per avere un breve riassunto della modalità di chiamata. comando utilizzo man comando visualizza la manpage del comando, che ne spiega la funzione, e le opzioni possibili apropos chiave visualizza i comandi per i quali chiave compare nella rispettiva manpage which comando mostra in quale directory (tra quelle elencate in $PATH) si trova il comando echo $VAR mostra il valore della variabile di ambiente VAR, che può essere assegnato mediante il comando set VAR=valore set VAR=valore assegna valore a VAR - anche se 'set' è opzione, e lo stesso risultato si ottiene con 'VAR=valore'. Scrivendo ad es. VAR=$VAR:elevato, otteniamo che VAR contiene 'valore:elevato' ls lista i contenuti di una directory pwd print working directory - dice dove ci si trova cd path change directory - per spostarsi, in modo assoluto (sui path che iniziano per /) oppure relativo (path che iniziano con il nome di sotto-directory, oppure ../ per specificare la directory padre) cp file1 file2 copy - copia file1 in file2. In generale, nei comandi dove compaiono due argomenti, il primo indica quello di partenza (esistene), ed il secondo quello che viene creato mv file1 file2 move - rinomina file1 in file2 rm file remove - cancella (irreversibilmente!) un file mkdir dir make directory - crea una directory rmdir dir remove directory - rimuove una directory (se è vuota) tar tape archive - comprime/scomprime un archivio con estensione .tar o .tar.gz o .tgz (ma esiste anche zip/unzip) ln -s file1 file2 link - crea un collegamento simbolico file2 che punta al file esistente file1 mount cosa dove rende visibile il device o partizione cosa, come contenuto del path dove nel filesystem cat file concatenate - stampa a schermo il contenuto di un file di testo. Se invocato con più argomenti, li stampa uno dopo l'altro, e se stdout è rediretto su di un file, si ottiene il risultato di concatenarli tra loro (da cui, il nome del comando) 259 Mondo Linux tail file (coda) - visualizza le ultime 10 linee di file, molto utile l'opzione -f che non ritorna, e dopo le ultime 10 linee, visualizza anche le aggiunte che qualche altro programma fa al file less file visualizza un file di testo, e permette di navigarci all'interno mediante le frecce della tastiera, o Pgup/Pgdown, o Home/End. Per cercare una stringa dentro il file, inserire /stringa sort ordina stdin e scrive il risultato su sdtout, molto utile se messo in pipe con altri comandi, o se si fa uso della redirezione di ingresso/uscita df disk full - mostra l'occupazione del disco, mentre du (disk usage) mostra le dimensioni di files/directory chmod modo file change mode - modifica i permessi associati al file chown gruppo.utente change owner - modifica i proprietario di un file, settando utente e gruppo (facoltativo) passwd permette di cambiare la propria password su utente permette di diventare un altro utente, fornendo la sua password. Se l'utente non è specificato, si diviene root; se viene inserita l'opzione - (meno) verrano usate le variabili di ambiente di quell'utente lpq line printer queue - mostra la coda dei lavori da stampare lprm numero line printer remove - rimuove una stampa dalla coda. Utile qualora si richieda una stampa per errore, e ci si ripensi kill -segnale pid invia un segnale al processo pid ps ax processes - visualizza un elenco dei processi in esecuzione sul computer. Sono a volte utili anche le opzioni u per conoscere gli utenti, f per capire chi è figlio di chi, w per mostrare il comando per intero e non troncarlo alla larghezza della finestra grep stringa file cerca in file l'occorrenza di stringa. Molto utile se messo in pipe con l'uscita di un altro comando, ad esempio ps, nel qual caso non occorre specificare file, dato che viene usato stdin. Se per file si specifica *, la stringa è cercata in tutti i file della directory, mentre specificando l'opzione -nr, la ricerca avviene in modo recursivo anche in tutte le sotto-directory, e vengono stampati i numeri di linea in cui si è trovata l'occorrenza di stringa. top mostra interattivamente i processi correnti, dando la possibilità di ordinarli in base ad esempio all'uso di CPU, di memoria o di durata Inoltre, la comunità Debian ha prodotto una simpatica Quick Reference Card, da stampare su di un solo foglio. Wildcard Diversi dei comandi riportati, possono accettare come argomento, più di un singolo nome di file. In tal caso, può essere utile descrivere un insieme di files dai nomi simili, per mezzo di caratteri jolly, in grado di corrispondere a caratteri qualunque: in particolare, il simbolo * (asterisco) corrisponde ad una qualsiasi stringa, mentre il simbolo ? (punto interrogativo) corrisponde ad un singolo carattere qualunque. Quindi ad esempio, possiamo scrivere cat * # concatena tutti i files presenti cat fi* # concatena tutti i files presenti, che iniziano per "fi" cat fi?e # concatena tutti i files con nome ad es pari a file, fice, fide, fine, fife... Il filesystem Ogni file è contenuto dentro una directory, che a sua volta può contenere altre sottodirectory. Il termine directory, ad un certo punto della storia, è stato tradotto in italiano 260 Lo strato applicativo di Internet Alessandro Falaschi come "cartella". Il filesystem rappresenta l'ordinamento gerarchico di tutte le directory presenti sul disco. Possiamo spostarci da una directory all'altra mediante il comando cd (change directory). Tra le directory, notiamo • • • • la directory / è quella (root o radice) entro cui sono contenute tutte le altre; ./ indica la directory corrente; ../ indica la directory (genitore) entro cui è ospitata la directory in cui ci troviamo; pwd (print working directory) è il comando che ci informa in quale directory ci troviamo; • la directory /home/utente è la casa di utente, a volte indicata come ~ (il simbolo ~ si ottiene come AltGr+ì), ed infatti ci si può arrivare immettendo cd ~. Quando utente effettua il login, si trova nella sua home, che usualmente contiene files su cui utente ha pieni diritti; • per referenziare un file ◦ presente nella directory in cui ci troviamo, lo indichiamo come file ◦ presente nella directory genitore, lo indichiamo come ../file ◦ presente da tutta una altra parte, lo indichiamo come /dir1/dir2/file. Questo si chiama path (percorso), che pu? essere ▪ assoluto, se inizia con /, oppure ▪ relativo, se non inizia con / . Ad es.: ▪ se inizia con ../ saliamo di un livello, con ../../ di due livelli, per poi eventualmente ri-scendere ▪ se inizia con dir1/, ci stiamo riferendo ad una nostra sottodirectory • i files il cui nome inizia con un punto (es ~/.bashrc) sono chiamati files nascosti, e non vengono mostrati con il normale comando ls (ma compaiono con ls -a). In generale, sono usati per memorizzare le informazioni di configurazione dei programmi utilizzati, e non vengono mostrati, per non creare confusione inutile. Nel mondo Linux e Unix, si è sviluppata una iniziativa per rendere la struttura gerarchica delle directory il più possibile simile tra sistemi diversi, in modo da favorire l'interoperabilità delle piattaforme. Questo ha portato ad una specializzazione delle diverse directory, in accordo a questa tabella: Directory Utilizzo /bin qui risiedono molti dei files eseguibili /etc contiene tutti i files di configurazione, spesso suddivisi in directory individuali per i diversi programmi /lib ospita le librerie, spesso in forma di shared object (estensione .so), ed i links alle versioni specifiche delle stesse /home contiene le sottodirectory associate alle case dei diversi utenti /proc rappresenta un filesystem virtuale, che non esiste fisicamente, ma i cui files sono creati al volo dal kernel quando vengono visitati. Costituisce una modalità di comunicazione tra user-space e kernel /sbin ospita files eseguibili solo da root /var contiene files e sotto-directory i cui contenuti possono variare di molto, come ad esempio /var/log per i files di registro, e /var/spool per l'email e le code di stampa 261 Mondo Linux Directory Utilizzo /usr acronimo di Unix System Resources, è stata definita per ospitare dati condivisi tra diversi computer, e non dovrebbe contenere configurazione specifiche A volte, parte di questa struttura viene ripetuta ricorsivamente, così ad esempio all'interno della /usr, è possibile trovare /usr/bin, /usr/etc, /usr/lib... ma anche cose più specifiche, come /usr/src per i files sorgente, /usr/include per i .h, /usr/share/ doc con la documentazione dei pacchetti installati. Infine, in /usr/local trovano spesso posto dei files creati dall'utente, od importati in forma di sorgente. Tasti utili La shell, ed alcuni programmi, attribuiscono a tasti particolari, funzioni particolarmente utili. Mentre dialoghiamo con la shell • il tasto freccia in su permette di ritrovare i comandi immessi per ultimi, in modo che se li dobbiamo re-impartire di nuovo, non serva di digitarli daccapo; • la combinazione Shift+PageUp (o PageDown) permette di scrollare l'output video più recente, in modo da poter leggere di nuovo le informazioni ormai passate; • il tasto Tab permette l'auto-completamento dei comandi: se ad esempio, dobbiamo immettere un comando particolarmete lungo, o di cui non ricordiamo esattamente l'ortografia, digitando solamente le prime lettere, seguite dal tasto Tab, la shell ci propone una serie di alternative, corrispondenti a tutti i comandi che condividono la stessa sotto-stringa iniziale. Aggiungendo di volta in volta caratteri alla nostra abbreviazione, arriviamo al momento in cui c'è un solo comando con quel prefisso, che ci viene proposto per intero, ed a quel punto, è sufficiente premere Return per eseguirlo; • la combinazione Ctrl+c interrompe il programma in corso; • la combinazione Ctrl+d interrompe una sequenza di input. Esempio: immettiamo il comando sort, che accetta l'insieme di righe da ordinare da standard input. Inseriamo una serie di parole da ordinare, separate dal tasto Retrun (o Invio), e per finire, usiamo la sequenza Ctrl+d. Durante l'osservazione del risultato ottenuto con il comando less: • i tasti > e < portano rispettivamente a fine ed inizio del file • i tasti freccia su e freccia giù scrollano di una riga, PageUp e PageDown di una pagina • mentre sull'ultima riga compaiono i due punti (:), sono possibili comandi e combinazioni di tasti, possibili anche durante la lettura delle pagine di manuale con man, e durante l'editing di un file con vi, come ad esempio: ◦ la sequenza /stringa ricerca l'occorrenza di stringa all'interno dell'output di less ◦ il tasto q (quit) consente l'uscita da less La shell e gli script Vuol dire conchiglia, ed è il programma che interpreta ed esegue i comandi immessi dall'utente dentro una finestra terminale, e viene eseguito automaticamente dal sistema quando un utente effettua il login. Dunque, quando viene aperto un terminale, viene parallelamente eseguita una shell, che si occupa di gestire gli stream stdin, stdout e stderr associati al terminale stesso, 262 Lo strato applicativo di Internet Alessandro Falaschi Quando viene impartito un comando mediante un terminale, questo viene quindi analizzato dalla shell, per vedere se si tratta di un comando interno, ovvero che può essere eseguire in modo autonomo dalla shell: un elenco dei comandi interni può essere scoperto invocando help. Altrimenti (non è un comando interno), viene effettuata una ricerca all'interno delle directory specificate dalla variable di ambiente PATH, per trovare un eseguibile denominato come il comando invocato. Se invece il comando inizia per ./, oppure con un diverso path (relativo o assoluto), allora la shell cerca (nella directory corrente od in quella indicata) un file con nome pari al comando, con il bit di esecuzione settato, e contenente un programma compilato, oppure ad uno script. Script I comandi da far eseguire alla shell sono immessi da tastiera, oppure sono contenuti, uno dopo l'altro, all'interno di un file che pur essendo di solo testo, viene reso eseguibile, ad es. mediante il comando chmod 755. Un file del genere viene chiamato file di comandi o script, e contiene una sequenza di comandi ed istruzioni dello stesso tipo di quelle che potremmo immettere da tastiera, assieme ad instruzioni di controllo, come quelle di loop (es. for) e diramazione (es if), dimodoché uno script costituisce un vero e proprio programma. A differenza dei programmi as es. in C, a cui corrisponde del codice eseguibile solo dopo un processo di compilazione e linking che produce il codice assembler corrispondente, uno script viene detto programma interpretato perché manca la fase di compilazione, e la shell è chiamata ad interpretarlo, in quanto si limita ad eseguire uno dopo l'altro una serie di comandi, ad ognuno dei quali corrisponde del codice già compilato, senza nessuna preoccupazione a riguardo di una possibile ottimizzazione complessiva. Non per nulla, script in inglese significa copione, che viene per l'appunto interpretato dalla shell! Esistono diverse shell sotto Unix, ma la più diffusa con Linux è bash, o Bourne Again Shell, per il quale un buon riferimento è [GAB]. Come un qualunque linguaggio di programmazione che si rispetti, anche la shell ha le sue variabili, che prendono il nome di variabili di ambiente. Variabili di ambiente Le variabili di ambiente prendono questo nome per il fatto che esistono al difuori di un determinato programma, e costituiscono in tal senso una sorta di "ambiente" in cui il programma viene eseguito. Nell'ambiente definito da una finestra terminale, e quindi delimitato da una shell interattiva, possiamo assegnare un valore ad una variabile di ambiente mediante la classica sintassi in cui compare il segno di uguale (senza spazi). Per accedere invece al suo valore, occorre prefissarla con il segno $. Ad esempio ~$ SEDE=Cisterna # assegnamo un valore alla variabile SEDE ~$ echo $SEDE # chiediamo la stampa del suo valore Cisterna # il valore è stampato Due programmi eseguiti in sequenza possono passarsi dei valori mediante le variabili di ambiente, se il primo le setta, ed il secondo le legge. Attribuendo loro un valore mediante la sintassi 263 Mondo Linux ~$ export SEDE=Cisterna ne definiamo l'ereditarietà, ovvero estendiamo l'ambiente in cui sono definite, permettendone la lettura anche da parte dei processi figli. Alcuni esempi di variabili di ambiente, possono essere ottenuti con il comando man environ; mentre la lista di quelle attualmente definite, si ottiene con il comando env. Genealogia Dopo aver chiuso la shell entro la quale abbiamo definito delle nuove variabili di ambiente, queste cessano di esistere. Ci chiediamo allora: da dove hanno origine quelle che osserviamo già esistenti, quando apriamo una nuova finestra terminale ? Hanno due origini: • alcune sono definite dallo script .bashrc presente nella home directory, che viene eseguito da bash al suo avvio. Il suffisso rc è comune a molti files di inizializzazione, e sta per run commands. • altre sono ereditate a partire da processi genitori (od anche nonni) sopra la nostra shell; in particolare, il processo init che parte per primo, è il genitore di tutti, e definisce l'ambiente comune a tutti gli altri processi Variabili famose Una variabile d'ambiente particolarmente importante, è PATH, che contiene una serie di path separati da due punti, che indicano in quali directory va a cercare la shell, per tentare di eseguire un comando immesso da tastiera. Le directory vengono esaminate nell'ordine con cui compaiono, per cui possono esistere due eseguibili con lo stesso nome, in due diverse directory, ma verrà sempre eseguito quello ospitato nella directory esaminata per prima. Se vogliamo eseguire un comando presente nella directory in cui ci troviamo, occorre lanciarlo come ./programma. Non è consigliabile inserire ./ nel PATH, perché potremmo inavvertitamente mascherare un programma dallo stesso nome. Altre variabili famose, sono $HOME, $LD_LIBRARY_PATH, $DISPLAY... Processi Redirezione Ogni programma al momento della sua esecuzione, ha tre file già aperti, denominati standard input, standard output e standard error (stdin, stdout, stderr) che di default, sono collegati all'input da tastiera, all'output su schermo, ed all'output dove scrivere gli errori (sempre lo schermo). E' molto facile redirigere l'uscita, anziché sullo schermo, dentro un file, così come redirigere il contentuto di un file, come se fosse immesso da tastiera, mediante i simboli < e > (minore e maggiore). Ad esempio: ~$ o ~$ ~$ un ls > pippo.txt sovrascritto ls ../ >> pippo.txt comando 2> errori.txt file apposito # redirige lo standard ouput su di un file, creato ex-novo # ora l'output è concatenato al file preesistente # lo standard error del programma comando è rediretto su di 264 Lo strato applicativo di Internet ~$ sort < pippo.txt > ordinato.txt ordinato.txt Alessandro Falaschi # sort accetta pippo.txt come input, e devia l'output su Pipe Le pipe (tubature) permettono di costruire comandi più complessi a partire da quelli a disposizione, redirigento l'uscita di uno verso l'ingresso dell'altro, semplicemente immettendo i due comandi sulla stessa riga, separati dal simbolo di barra (es comando1 | comando2). Ad esempio, possiamo provare ad immettere ~$ cd / ~$ ls -R <control>-c ~$ ls -R | less ~$ ls | sort # # # # # # # ci portiamo alla root del file system scopriamo tutti i files nel nostro computer interrompiamo lo scroll l'uscita di ls viene diretta in ingresso a less, che ne effettua la paginazione l'uscita di ls viene diretta in ingresso a sort, che ne effettua l'ordinamento Process ID Ad ogni processo in esecuzione, viene assegnato un numero chiamato PID, che lo identifica univocamente, e he permette di interagire con lo stesso, ad esempio inviandogli segnali o terminandolo. Ad ognuno di questi, corrisponde una directory nel flesystem virtuale /proc. Per conoscere il PID dei processi in esecuzione si utilizza il comando ps, che senza altre opzioni, indica solamente i processi lanciati a partire dalla shell corrente. Un gruppo di opzioni comunemente usate, è ps auxf, che (a) li mostra tutti, indicandone (u) l'utente che li ha lanciati, comprendendo anche (x) quelli lanciati da un altro processo non-shell, e (f) mostra chi è figlio di chi. Oltre al PID, ps mostra in una serie di colonne, diversi altri parametri associati ai processi, come l'occupazioine di memoria, il tempo totale di esecuzione, lo stato. Un diverso modo per valutare l'attività del sistema, è di eseguire il comando top, disponibile assieme alle utilities del packacage procps. Mediante top, si ha una schermata in real-time in cui i processi sono ordinati in base all'ammontare di risorse impiegate, come ad esempio la CPU, o la memoria. Mentre top è in esecuzione, si può (con il tasto k) inviare un segnale ad un processo, indicandone il numero, ad esempio per terminarlo. Attualmente, esistono applicazioni grafiche che svolgono la stessa funzione, come ad es. il gnome system monitor. Il comando designato ad inviare segnali ad un processo è kill, che con l'opzione -9 causa la terminazione del processo. Background e terminazione Un processo può essere lanciato sullo sfondo (in background) se viene invocato con una & alla fine del comando: in tal modo, si ri-ottiene il prompt dei comandi, e si può eseguire qualche altro comando. Ovviamente, la cosa ha un senso purché il programma mandato in backgound, non richieda un input da tastiera! Per riportate il programa sotto il controllo della tastiera, si può eseguire il comando fg (foreground). Si può mandare un comando (già lanciato in modalità interattiva) in background, prima interrompendolo (premendo 265 Mondo Linux control-z), e quindi digitando bg (che sta per background, appunto). Infine, si può interrompere un programma che è eseguito in foreground, con la combinazione di tasti control-c. Per terminare un programma in background senza riportarlo in foreground, si può verificare il suo pid mediante il comando ps ax, e quindi usare il comando kill pid, oppure kill -9 pid, se il programma non termina. Chiamate di sistema Un programa di utente, oltre alle istruzioni prodotte dalle proprie linee di codice, può eseguire codice contenuto in librerie, e codice che viene eseguito all'interno del kernel del sistema operativo, come ad esempio per accedere a delle risorse fisiche, all'hardware, od alla rete. L'invocazione di questo codice esterno, è indicata come system call, che spesso in realtà non esegue direttamente una funzione offerta dal kernel, ma invoca una funzione di libreria che risiede nello spazio di utente, e che a sua volta invoca la fuzionalità del kernel, come per la glibc. L'invocazione delle system call da parte di un programma di utente, non può avvenire senza che quest'ultimo non includa nella parte iniziale, i riferimenti ai files .h (gli header files) che contengono le definizioni di tipo di dato, ed i prototipi delle chiamate a funzione, in modo che il compilatore possa verificare la correttezza formale delle chiamate, e produrre un codice oggetto che potrà linkarsi con successo alle funzioni esterne. Per osservare le chiamate di sistema che vengono effettuate da un processo, Linux offre il comando strace, con cui lanciare il programma. In alternativa, si può attaccare strace ad un programma già in esecuzione, utilizzando l'opzione -p. Avvio dei processi Quando un computer Linux viene avviato, dopo una serie di operazioni necessarie a caricare il kernel dalla partizione di disco corretta, il primo programma ad essere eseguito è init, che nella impostazione classica di Unix SystemV, determina il dafarsi in base all'esame del file /etc/inittab, che però in Ubuntu non è più presente. Nel file inittab viene definito, tra le altre cose, il runlevel inziale del sistema, che consiste in un numero tra zero e sei, e che determina quali processi debbano essere attivati, e quali arrestati. Questi processi particolari prendono il nome di servizi. Ad ogni servizio che può essere avviato, corrisponde uno script presente nella directory /etc/init.d, che può essere invocato con le opzioni start, stop, restart, status, ed altre. Gli script di Init Per ogni runlevel N, esiste una directory /etc/rcN.d, che contiene una serie di link simbolici agli script presenti in /etc/init.d: i link non esistono per tutti i file, ma solo per quelli rilevanti per quel runlevel. Questi link simbolici, prendono lo stesso nome dello script target, prefisso con una lettera ed un numero. La lettera può essere una S oppure una K, significando Kill o Start di quel servizio, mentre il numero serve a stabilire un ordine temporale per la partenza/uccisione dei diversi servizi. Gli script presenti in /etc/init.d possono essere invocati in qualunque momento, anche dopo il boot, per avviare o terminare i servizi in modo pulito. Ad esempio, per avviare un servizio scriveremo sudo /etc/inid.d/nomeservizio start, oppure stop per arrestarlo. Alcune distribuzioni Linux (Red Hat, Fedora, Ubuntu), 266 Lo strato applicativo di Internet Alessandro Falaschi permettono di usare anche la sintassi service nomeservizio start, un pò più elegante, ma del tutto equivalente alla modalità precedente, tanto è vero che se si va a vedere, gli script di avvio sono ancora tutti al loro posto. Upstart Una particolarità di questa modalità di avvio, è che i servizi partono uno dopo l'altro, provocando un lentezza intrinseca del processo. Recentemente sono state proposte delle alternative, come Initng, oppure Upstart, che è usato da Ubuntu, e che ha il vantaggio di essere compatibile con gli script di SystemV. Gli strumenti grafici per la configurazione degli script di init, e che permettono di lanciare/ arrestare i servizi, ovvero fare in modo che questi partano (o meno) al momento del boot, sono abbastanza variabili per le diverse distribuzioni, come ad esempio descritto presso il wiki di Ubuntu.; uno strumento abbastanza semplice ed efficace per impostare i servizi da far partire (o meno) è BUM (Boot-Up Manager), presente nei repository di Ubuntu. /etc/rc.local Si tratta di uno script che viene eseguito dopo che sono stati lanciati tutti gli altri processi da eseguire all'avvio. Qualora si intenda personalizzare il comportamento del proprio computer eseguendo dei comandi particolari, ad es. per modificare le impostazioni della rete, ma senza per questo andare a toccare altri file di sistema, questo è un buon posto dove inserire i comandi relativi. Sottosistema Cron Sottosistema dei Log Configurazione della Rete Anche Linux, come tutti gli altri sistemi operativi, offre una interfaccia grafica per configurare i parametri di rete, ma in molte occasioni può essere più semplice, più rapido, e più sicuro, usare delle applicazioni che dialogano direttamente con il kernel sia per impostare i parametri, che per interrogarlo a riguardo di quale sia il valore corrente per tali parametri. Storicamente, le applicazioni con questo compito erano ifconfig e route, ma a partire dal 1999, si è iniziato a preferire a queste, i comandi offerti dalla suite iproute, che offrono una sintassi più consistente, e simile a quella dei router Cisco. Questi comandi sono espressi mediante frasi composte da più parole, ordinate in modo da definire una sintassi in grado apputo di esperimere le azioni di configurazione ed interrogazione necessarie alla gestione della conessione in rete. Tutte le frasi iproute iniziano con la parola ip, che rappresenta il programma eseguibile che ne implementa la sintassi. Inserendo da terminale il comando ip da solo, sono mostrati i suggerimenti su come proseguire nella costruzione della frase, ossia Usage: ip [ OPTIONS ] OBJECT { COMMAND | help } ip [ -force ] [-batch filename where OBJECT := { link | addr | addrlabel | route | rule | neigh | ntable | tunnel | maddr | mroute | monitor | xfrm } OPTIONS := { -V[ersion] | -s[tatistics] | -d[etails] | -r[esolve] | 267 Mondo Linux -f[amily] { inet | inet6 | ipx | dnet | link } | -o[neline] | -t[imestamp] } Senza approfondire qui la semantica delle opzioni possibili, notiamo come la seconda parola (OBJECT) definisca su che cosa intendiamo operare, e può essere (oltre ad altre cose) • • • • link -- l'interfaccia di rete address -- l'indirizzo IP associato ad una interfaccia neighbour -- la tabella delle corrispondenze ARP-IP osservate nella stessa LAN route -- la tabella degli instradamenti IP, che individua l'interfaccia corretta per le diverse sottoreti mentre la terza parola è un verbo che esprime l'azione che desideriamo compiere su OBJECT; Per conoscere le azioni possibili, possiamo immettere una frase incompleta, in cui al posto del verbo si usa la parola help, come ad esempio ip addr help. Come esempio di applicazione molto semplice, supponiamo di voler verificare il corretto funzionamento della nostra interaccia di rete, assegnando quindi alla stessa un indirizzo IP nell'ambito di una determinata sottorete, e di definire infine il Default gatway tramite il quale accedere al resto di Internet. In tal caso, possiamo innanzitutto immettere il comando ip link che ci mostra per ogni interfaccia presente, il corrispondente indirizzo Ethernet, e la presenza o meno di una rete connessa a quella interfaccia. Ad esempio, sul mio portatile ottengo alef@alef:~$ ip link 1: lo: <LOOPBACK,UP,LOWER_UP> mtu 16436 qdisc noqueue state UNKNOWN link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 2: eth0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc pfifo_fast state DOWN qlen 1000 link/ether 00:13:d3:f0:9c:04 brd ff:ff:ff:ff:ff:ff 3: eth1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP qlen 1000 link/ether 00:16:6f:54:3b:a5 brd ff:ff:ff:ff:ff:ff 4: pan0: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN link/ether 02:48:e4:71:52:e4 brd ff:ff:ff:ff:ff:ff in cui lo è l'interfaccia di loopback, eth0 quella di rete cablata, eth1 l'interfaccia wireless e pan0 l'interfaccia della Personal Area Network bluetooth. Per ognuna di queste, nella seconda linea è riportato il valore dell'indirizzo ethernet ad essa relativo, mentre nella prima linea viene indicato tra le altre cose un indice di interfaccia, il suo nome, l'MTU mtu, la disciplina di coda qdisc, ed un insieme di flag racchiusi tra parentesi acute, che possono (anche) valere • • • • • UP -- il dispositivo è acceso LOOPBACK -- l'interfaccia non comunica con altri computer BROADCAST -- può inviare pacchetti a tutti MULTICAST -- è possibile usare l'interfaccia per trasmissioni multicast NO-CARRIER -- il cavo di rete è staccato, oppure non c'è copertura wireless Per associare un indirizzo IP (as es. 192.168.0.123) ad una interfaccia (es. eth1) usiamo allora il comando ip addr add 192.168.0.123/24 dev eth1 mediante il quale definiamo anche una network mask (ad esempio) di 24 bits; per verificare il buon esito dell'operazione, impartiamo il comando ip addr senza ulteriori specificazioni, 268 Lo strato applicativo di Internet Alessandro Falaschi ottenendo (a parte le informazioni sulle altre interfacce) alef@alef:~$ ip addr 3: eth1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP qlen 1000 link/ether 00:16:6f:54:3b:a5 brd ff:ff:ff:ff:ff:ff inet 192.168.0.123/24 brd 192.168.0.255 scope global eth1 inet6 fe80::216:6fff:fe54:3ba5/64 scope link valid_lft forever preferred_lft forever che oltre a confermarci l'indirizzo IP e quello MAC, ci mostra tra le altre cose anche l'indirizzo di broadast, l'indirizzo IPv6, e la disciplina di coda applicata. Per rimuovere l'assegnazione di tale indirizzo all'interfaccia, il comando è ip addr del 192.168.0.123/24 dev eth1 Non resta ora che definire qual'è il nostro default gateway, mediante il comando ip route add default via 192.168.0.1 dev eth1 che istruisce il kernel ad inviare i pacchetti IP destinati fuori dalla nostra LAN, all'host con indirizzo 192.168.0.1, tramite l'interfaccia eth1 precedentemete configurata. Per verificare il contenuto della tabella di routing, si può immettere il comando ip route senza ulteriori parametri, ottenendo qualcosa del tipo alef@alef:~$ ip route 192.168.0.0/24 dev eth1 proto kernel scope link default via 192.168.0.1 dev eth1 proto static src 192.168.0.123 metric 2 che ci conferma come i computer della LAN siano raggiungibili direttamente, mentre per gli altri si passi per il DG impostato. Infine, per rimuovere l'impostazione realtiva al Defaul Gateway, il comando è ip route del default Riferimenti • • • • • • [GAB] - Guida avanzata di scripting Bash di Mendel Cooper Guida Linux di Edoardo Valsesia Amministrare GNU/Linux - di Simone Piccardi Master in Tecnologia del Software Libero e Open Source - Stefano Zacchiroli IP Command Reference di Alexey N. Kuznetsov Guide to IP Layer Network Administration with Linux Realizzato con da Alessandro Falaschi - 269 Mondo Linux 270 Lo strato applicativo di Internet Cattura del traffico, compilazione, e socket In questa attività, si tenta di acquisire una visione congiunta dei tre aspetti della programmazione, delle interfacce utente, e dell'analisi di protocollo. • Strumenti di Cattura e di analisi delle intestazioni ◦ ◦ ◦ ◦ ◦ Configurazione delle opzioni di cattura Menù principale e cattura Menù di Analisi Menù statistiche Esercitazione • A tu per tu con il codice ◦ Compilazione ◦ File di comandi ◦ Makefile ▪ Dipendenze e target ▪ Macro e regole ◦ Esecuzione ▪ ▪ ▪ ▪ ▪ ▪ Errori di compilazione Server parallelo TCP Connessioni UDP Diagrammi temporali, UML Select Broadcast • Riferimenti Strumenti di Cattura e di analisi delle intestazioni Sebbene si possa visualizzare il traffico in transito sulle interfacce di rete (operazione in gergo chiamata sniffing) anche mediante strumenti testuali come tcpdump o iptraf, il programma più diffuso e ricco é sempre stato Ethereal, che di recente ha cambiato nome in Wireshark. Mentre tcpdump e wireshark fanno entrambi uso della libreria libpcap, iptraf accede all'interfaccia di rete tramite chiamate dirette al kernel. L'operatività del programma è ampiamente documentata dalla esauriente documentazione presente sul suo sito, e presso il suo wiki, a cui rimandiamo per ogni dubbio. Dopo aver lanciato wireshark in modalità privilegiata (digitando quindi il comando sudo Strumenti di Cattura e di analisi delle intestazioni Cattura del traffico, compilazione, e socket wireshark, o rintracciandolo nel menù Applications/Internet/Wireshark (as root), ed immettendo la nostra password di utente non privilegiato), cliccare sull'icona che permette di vedere le interfacce disponibili, in modo da osservare la finestra sottostante, che elenca le interfacce di cattura, e già mostra su quali di queste si sta osservando traffico. Configurazione delle opzioni di cattura Nel caso in cui si desideri osservare il traffico diretto verso localhost, si scelga l'interfaccia lo; se invece si opera verso un computer remoto. si scelga eth0, o se in dubbio, any (che le cattura entrambe). Si può iniziare subito la cattura, premendo Start, oppure modificare le opzioni, giungendo ad una seconda finestra. 272 Lo strato applicativo di Internet Alessandro Falaschi • la cattura in modo promiscuo significa che osserveremo tutti i pacchetti in transito per la sezione di rete a cui siamo collegati direttamente, e non solo quelli da/verso il nostro computer, e ciò è possibile solo eseguendo WireShark da root. In tutti i modi, operando su di una rete switchata, oltre ai pacchetti da e per noi stessi, potremo vedere solo i pacchetti broadcast, e quindi non tutto il traffico generato dalle altre macchine; • è possibile specificare un capture filter, seguendo la sintassi di tcpdump (si veda la sezione Esempi della pagina man), ovvero come indicato presso il sito di Wireshark, ed esemplificato presso il suo wiki, in modo da di limitare il numero dei pacchetti catturati a solo quelli desiderati, ad esempio selezionando numeri di porta ovvero protocolli di trasporto, o specificando solo alcuni indirizzi IP. Ciò è molto utile nel caso in cui sul segmento di rete a cui siamo affacciati, si verifica un traffico molto intenso, a cui non siamo interessati. Alcuni esempi di filtri di cattura: ◦ host 192.168.120.40: cattura solo il traffico diretto da e verso questo indirizzo IP (che nell'esempio, è il nostro); ◦ port 80: cattura solo il traffico diretto da e verso la porta 80; 273 Strumenti di Cattura e di analisi delle intestazioni Cattura del traffico, compilazione, e socket ◦ arp: solo il traffico di tipo ARP (possiamo specificare un altro nome di protocollo, come ad esempio smtp, dns, etc); ◦ ip: solo il traffico IP, e quindi non, ad esempio, il traffico ARP, che non possiede una intestazione IP; ◦ cliccando sul bottone si apre una finestra di dialogo, che permette di definire un filtro, e di salvarlo con un nome, per un uso futuro • dal riquadro Capture File(s) di sinistra, osserviamo che si può dirigere il risultato direttamente in un (o più) file; • dal riquadro Stop Capture, si può temporizzare la fine della cattura in base al tempo trascorso, oppure al numero di pacchetti, od al volume di traffico; • nelle Display Options, a destra, possiamo scegliere di vedere subito il risultato della cattura, anziché aspettare il termine della stessa, ed anche, vedere i pacchetti catturati scrollare immediatamente. Sebbene questo sia visivamente molto immediato, nel caso di una elevata intensità di traffico, è invece preferibile catturare senza vedere, in modo da non sovraccaricare il computer con compiti grafici, ed eventualmente perdere qualche pacchetto per questo motivo; • sotto Name Resolution infine, si può richiedere che venga mostrata la risoluzione per ◦ gli indirizzi MAC (la parte iniziale dell'indirizzo Ethernet determina il costruttore della scheda di rete); ◦ i nomi dei servizi in ascolto sulle porte ben note degli indirizzi di trasporto; ◦ i nomi di Host associati agli indirizzi IP di rete (quest'ultima opzione è sconsigliata in caso di Live Capture, dato che implica una fase di richiesta al DNS, che rallenta le operazioni). Menù principale e cattura Possiamo ora eseguire un live capture, oppure caricare (File/Open..) in Wireshark questofile ottenuto presso un computer di ingegneria, durante la visita su wikipedia della pagina dell'8 Marzo, della festa della donna, e della mimosa. D'altra parte, il wiki di wireshark contiene molti esempi di file catturati. Nella barra principale troviamo molte utili scorciatoie, tra cui evidenziamo • abilita/disabilta la colorazione dei pacchetti, • modificano la dimensione dei fonts e dell'incolonnamento Menù di Analisi Questo menù presenta delle opzioni che possono spesso essere richiamate anche con il tasto destro del mouse, dopo aver evidenziato nella finestra superiore un particolare pacchetto, oppure una particolare intestazione di un pacchetto, od un particolare campo. Citiamo: • il filtro di display apre una finestra di dialogo accessibile anche dal bottone posto sotto la barra principale , del tutto simile a quella del filtro di cattura, mediante la quale definire e salvare per un uso successivo, un filtro di visualizzazione, che modifica quanto già catturato. Dato però che la sintassi di questo filtro, è 274 Lo strato applicativo di Internet Alessandro Falaschi differente da quella del filtro di cattura, è spesso molto più conveniente definire il filtro di visualizzazione interattivamente, seguendo la procedura spiegata appresso; • possono essere disabilitati alcuni protocolli, oppure si può specificare di forzare il traffico da/verso una certa porta, ad essere decodificato in un modo particolare; è questo il caso ad esempio, dell'RTP nel VoIP; • Follow TCP streamci consente, a partire da un pacchetto, di generare un display filter che mostra solo i pacchetti associati alla medesima connessione: per esempio, possiamo scegliere il pacchetto 14, contenente il SYN di apertura del TCP. Nella finestra risultante, osserviamo, ricomposto, tutto il traffico della connessione, potendo (in basso) scegliere come visualizzare il risultato, e di escludere (o selezionare) solo questo; ◦ lo stesso risultato, si ottiene con il tasto destro; ◦ per rimuovere l'effetto del filtro, si può cliccare sull'icona nella barra dei filtri; ◦ per modificare ulteriormente il filtro ottenuto: ▪ possiamo agire sull'icona nella barra principale, o su in quella dei filtri ▪ oppure possiamo di nuovo operare con il tasto destro, dopo aver evidenziato, un pacchetto, una intestazione, od un campo. Selezionando Apply as Filter, e scegliendo ... and Selected, si mette in and la condizione precedente, con il filtro attuale. Quest'ultimo, realizza la condizione mostrata in basso nella finestra di Wireshark. Possiamo ora sperimentare ad eliminare via via i diversi protocolli presenti, fino a focalizzarci solo sul traffico effettivamente pertinente alla navigazine web di cui all'esempio. Menù statistiche Sotto questo pop-down sono presenti molte utilissime funzioni. Mentre il sommario presenta dei dati riassuntivi, la gerarchia dei protocolli, le conversazioni e gli endpoints mostrano in dettaglio le entità presenti, e le loro relazioni. 275 A tu per tu con il codice Cattura del traffico, compilazione, e socket • Con I/O Graph possiamo studiare la velocità di picco del traffico, potendo eventualmente inserire dei filtri di visualizzazione, al fine di evidenziare eventuali situazioni di sincronizzazione tra diversi tipi di traffico; • Destinations ci mostra, per ogni destinazione, i diversi protocolli di trasporto e le diverse porte sorgente; • Flow Graph crea un diagramma temporale dei pacchetti visualizzati (eventualmente, a seguito della applicazione di un Display Filter), permettendo di navigare nel file di capture, e di analizzare nei dettagli ciò che viene mostrato come valore dei singoli campi delle intestazioni • HTTP crea una statistica dettagliata per il traffico web, così come SIP, RTP e VoIP mostrano risultati attinenti alla telefonia Internet (provare con questo capture relativo ad una chiamataSIP) • Packet Length mostra una interessante statistica, a riguardo della ripartizione del traffico su diverse lunghezze di pacchetto Esercitazione Dopo aver caricato il file di trafficocatturato, eseguire le analisi indicate ai punti 9-18 della prima prova di questa esercitazione. Quindi, sempre a partire dallo stesso file di cattura, eseguire le analisi indicate ai punti 12-16 e 18-20 della seconda prova della stessa esercitazione. A tu per tu con il codice Per sperimentare l'uso di Wireshark, e mettere in pratica i concetti illustrati nel capitolo relativo al Network Programming, procediamo alla compilazione del codice di cui si è discusso, in modo da analizzare il traffico sviluppato utilizzando i programmi client-server, e strada facendo, aggiungiamo varianti e particolarità. Mentre nel precedente capitolo abbiamo illustrato i comandi di base per acquisire un minimo di operatività tramite finestra terminale, descriviamo ora i concetti e le operazioni necessarie per passare da una descrizione di un programma basata su di un linguaggio ad alto livello come il c, al formato eseguibile che può effettivamente essere fatto girare sul computer. Compilazione Apriamo innanzitutto una finestra terminale, dove è possibile impartire comandi con la tastiera. Creiamo quindi una directory di lavoro: $ $ $ $ cd ~ pwd mkdir codice cd codice Rechiamoci quindi sulla directory contenente il codice, e scarichiamo nella directory codice tutti i files ivi presenti, oppure scarichiamo l'unico archiviocompresso, e decomprimiamolo presso il nostro computer. Il file compila è uno script shell, ossia un file contenente gli stessi comandi che è possibile impartire in una finestra terminale, e nel caso non lo sia già (ls -l compila), deve essere reso eseguibile con il comando chmod 755 compila. 276 Lo strato applicativo di Internet Alessandro Falaschi Quindi eseguiamolo, scrivendo ./compila (il ./ è necessario, altrimenti potrebbe esistere un programma con lo stesso nome in una delle directory mostrate con echo $PATH, e verrebbe eseguito quello al suo posto). Risolviamo i possibili errori. Osserviamo il risultato dell'operazione, ed i nuovi files presenti nella directory. Il comando che serve per compilare un generico programma C di nome pippo.c, e che viene eseguito da compila, è $ cc -o eseguibile pippo.c in cui l'opzione -o provvede a dare un nome all'eseguibile (nell'esempio, il nome è eseguibile) che altrimenti, prenderebbe il nome di default a.out. File di comandi Come possiamo osservare, il file compila iniza in un modo un pò particolare, chiamato shebang (o hashbang). In assenza di questa linea, il file potrebbe essere passato come argomento all'interprete dei comandi (eseguendo bash compila), che eseguirebbe tutte le direttive che vi trova. Mandandolo direttamente in esecuzione invece, è sempre l'interprete bash che deve decidere il da farsi. Se si tratta di un vero programma, gli cede il controllo; se invece localizza in testa ai caratteri #!, lancia il programma che è scritto subito appresso (nella fattispecie, un'altra istanza di se stesso), passandogli lo script come argomento, e termina. A questo punto, sembrerebbe che ci siamo cacciati in un loop senza fine, ed invece, dato che il file di comandi è stato appunto passato come parametro, e non mandato diretamente in esecuzione, questo bash-figlio inizia felicemente ed eseguire i comandi che si trovano nel file, ignorando questa volta la prima linea, anche perchè.. il simbolo # è quello che si usa per scrivere i commenti ! Makefile Ora che abbiamo imparato le basi dei file di comandi, ci rendiamo conto che compilare ogni volta tutto quanto anche se si modifica un solo file (come suggerito nel seguito), potrebbe essere uno spreco di risorse, non tanto ora che i programmi sono piccolini, ma più in generale, qualora si abbia a che fare con progetti di dimensioni considerevoli. Ci soccorre allora il comando make, che viene in nostro aiuto usando le informazioni presenti in un file chiamato, per l'appunto, Makefile (con l'iniziale maiuscola), e che rappresenta una sorta di linguaggio di programmazione dichiarativo, in quanto specifica cosa deve fare make (e non come). In rete sono presenti alcuni buoni tutorial a riguardo, citiamo quello di AIL, di LUPG, o quello (ottimo) presso il CEH. Dipendenze e target Il vantaggio di usare il make, è che nel Makefile è definito un albero di dipendenze, descritte dichiarando quale file dipende da quale altro. Così, quando un file di un gruppo è modificato, è possibile ri-generare solo i files che dipendono da quest'ultimo, unicamente osservando se la loro data di creazione è precedente o successiva a quella dei files da cui dipendono. Un primo esempio di Makefile funzionante è 277 A tu per tu con il codice Cattura del traffico, compilazione, e socket default: all all: tcp udp tcp: udp: server client selectserver listener talker broadcaster server: client: listener: talker: selectserver: broadcaster: server.o client.o listener.o talker.o selectserver.o broadcaster.o server.o: client.o: listener.o: talker.o: selectserver.o: broadcaster.o: server.c client.c listener.c talker.c selectserver.c broadcaster.c In questo esempio, alla sinistra dei due punti sono presenti i cosidetti target, ossia obbiettivi, che possono essere citati su di una linea di comando come make target, intendendo con questo richiedere la generazione del target richiesto. A sua volta un target può dipendere da un altro target, e così si definisce come all dipenda da tcp e udp, mentre tcp dipende da server, client e selectserver. A sua volta, sever dipende da server.o, che a sua volta ancora, dipende da server.c. In questo modo, è possibile invocare make all make tcp make make listener # # # # compila tutto compila server e client prende all come target di default compila solamente listener Una perplessità leggittima che può sorgere a questo punto, è come faccia make a capire cosa c'è da fare per soddisfare una dipendenza. La risposta è che esistono delle regole deduttive, per cui make sa he se un .o dipende da un .c, allora il .o si ottiene compilando il .c. Ma andiamo con ordine. Macro e regole Allo scopo di rendere un Makefile più compatto esistono alcune particolarità. Le istruzioni macro consentono di associare una serie di simboli terminali ad una unica macro chiamata (as es.) OBJ, che può essere ri-espansa successivamente, citandola con la notazione $(OBJ). Ad ed esempio, ponendo OBJS = server.o client.o listener.o talker.o selectserver.o broadcaster.o, la stessa sequenza di nomi di codici oggetto è riottenuta successivamente mediante la notazione $(OBJ). Non solo, ma le estensioni dei singoli nomi in cui $(OBJ) si espande, possono essere ulteriormente trasformate al volo: ad esempio, se poniamo OBJ = pippo.c, citando $(OBJ:.c=.o), otteniamo l'espansione in pippo.o. Le regole consentono di abbinare ad un target, la sequenza di comandi che permette di risolvere la dipendenza espressa dal target. La sintassi di questo costrutto è esprimibile come 278 Lo strato applicativo di Internet Alessandro Falaschi target: nome(i) file di ingresso azioni in cui, dopo la linea che esprime una dipendenza, è posta una seconda linea (che inizia con un TAB) che esprime una (o più) azione(i), da applicare perché sia soddisfatta. In base a queste nuove nozioni, al Makefile precedente possono essere aggiunte in testa ed in coda le istruzioni OBJS = server.o client.o listener.o talker.o selectserver.o broadcaster.o . . . clean: -rm -f $(OBJS) $(OBJS:.o=) *~ rebuild: clean all che definiscono la macro OBJS come uguale a tutti i codici oggetto, ed i target clean e rebuild, tali che • al target clean è associata l'azione -rm -f $(OBJS) $(OBJS:.o=) *~ , che esegue il comando di cancellazione rm, applicato a tutti i codici oggetto definiti dalla macro OBJS, a tutti i codici eseguibili ottenuti rimovendo le estensioni .o, ed alle copie di backup dell'editor, che terminano con ~, riportando la directory nello stato iniziale; • il target rebuild prima esegue clean, e quindi ricompila tutto daccapo. L'uso della espansione di una macro, ci evita di dover scrivere parecchie linee di codice uguali, cosicché invece di dover scrivere una regola che specifica come passare da un .c ad un .o, ossia server.o: server.c cc -c server.c per ognuno dei codici oggetto da generare, possiamo scrivere CC = gcc # utilizziamo gcc anziché cc OBJS = server.o client.o listener.o talker.o selectserver.o broadcaster.o obj: $(OBJS) %.o: %.c $(CC) -c $< in modo che, invocando make obj, si ottiene la compilazione di tutti i sorgenti in codice oggetto, in base alla semantica delle seguenti macro predefinte: la macro si espande in %.c qualunque file con estensione .c presente nella directory %.o un file con estensione .o, e con lo stesso nome della dipendenza della regola, e che compare come dipendenza di un'altra regola 279 A tu per tu con il codice Cattura del traffico, compilazione, e socket $(CC) cc, il compilatore standard, o quale altro compilatore definito in una macro cc = $< l'elemento che compare dal lato dipendenza della regola $@ il nome del target Questa logica è poi ulteriormente potenziata dalla esistenza delle regole deduttive, che permettono a make di stabilire autonomamente quale sia l'azione necessaria a soddisfare una determinata dipendenza, cosicché ad esempio, per ottenere un file eseguibile, make prova a likare un codice oggetto dallo stesso nome. Così, anche se dovremmo scrivere %.o: %.c $(CC) -c -o $@ $< per generare i .o a partire dai .c, e %: %.o $(CC) -o $@ $< per generare gli eseguibili, il Makefile definitivo riporta queste istruzioni commentate. Esecuzione Ora che abbiamo gli eseguibili, possiamo verificare il funzionamento dei server parallelo e seriale studiati. Poniamoci nella directory con i files scompattati, ed impartiamo il camando make. Errori di compilazione Come dite? Il comando make (e/o compila) risponde con una serie di errori, il primo dei quali dice "error: stdio.h: nessun file o directory" ?.... aaahhh si, dovete installare con Synaptic, il pacchetto build-essential, che a sua volta determina l'installazione di tutto ciò che occorre per compilare! 280 Lo strato applicativo di Internet Alessandro Falaschi Server parallelo TCP Nella finestra terminale aperta, lanciamo ./server ed in una nuova finestra, ./client 127.0.0.1: constatiamo che il risultato è quello atteso. Ora, proviamo a fare questi esperimenti: • 1. osserviamo il traffico on wireshark, impostiamo un filtro di visualizzazione che mostri solo il traffico relativo al nostro esperimento, salviamo il risultato della cattura nel file server.pcap • 2. chiudiamo il server con control-c, e rilanciamo il client. Cosa dice ? Catturando il traffico con Wireshark, quali sono le differenze rispetto al caso precedente ? Salviamo anche stavolta il risultato nel file noserver.pcap copiamo tutti i files in una nuova directory, che chiamiamo test. Modifichiamo il codice del server, in modo che • 3. anziché il messaggio Hello, word! scriva qualcosa di originale, ad esempio il vostro nome ed un augurio. Ricompilatelo con make, rilanciatelo, collegatevi con il client e notate che succede. Avete modificato il terzo parametro di send, con il numero di caratteri più uno ?? • 4. comunicate il vostro IP ai vicini di postazione, scrivetevi il loro, e contattatevi a vicenda. • 5. salvate ora il codice di server.c in un nuovo file, servermod.c, e modificatelo in modo che esegua il bind ma non il listen, oppure il listen ma non l'accept. ◦ Suggerimento: si usi l'istruzione sleep(secondi) e qualche stampa su schermo, che faccia capire cosa accade ▪ Scorciatoia: se modificare il codice richiede troppo tempo, si usi questa versione già modificata. Per capirecosa è cambiato, si usi il programma kompare. Se non l'avete intallato, cercatelo con Synaptic. ◦ Il messaggio del client è cambiato? Catturiamo il traffico per capire cosa accade, e salviamo il risultato in noaccept.pcap ▪ Risposta: mentre nel caso di noserver.pcap,a seguito del SYN di apertura della connessione TCP, il kernel risponde immediatamente con un pacchetto di ReSeT, nel caso di noaccept.pcapinvece, il kernel porta regolamente a temine il three-way handshake; però, finché non viene eseguita la accept(), il processo non prosegue. • 6. modifichiamo nuovamente ilserver, in modo che non venga armato l'handler del SIGCHLD. Dopo ogni invocazione del client, verifichiamo lo stato dei processi con ps ax (si esegua ps ax | grep server). Cosa notiamo ? Usiamo il comando kill, per eliminare i processi zombie. Funziona ? E se uccido il padre ? ◦ Risposta: in assenza del gestore di SIGCHLD, i processi figli continuano a risiedere in memoria, in uno stato di <defunct>. Non rispondono al kill, e per farli sparire, occorre terminare il processo padre. 281 A tu per tu con il codice Cattura del traffico, compilazione, e socket Connessioni UDP Ora, proviamo a lanciare la coppia di programi che fanno uso di connessioni UDP, ossia listener e talker: • 7. anche in questo caso, verifichiamo che tutto si svolga come previsto, e catturiamo il traffico con Wireshark. Che differenze notiamo ? salviamo il risultato in listener.pcap. • 8. chiudiamo ora il processo listener, eseguiamo nuovamente talker, e salviamo il risultato della cattura in nolistener.pcap. Che accade ? Anche se talker non è cosciente che le sue parole cadono nel vuoto, di cosa ci possiamo accorgere dalla cattura ? ◦ Risposta: nel caso in cui listener non sia attivo, il kernel invia un pacchetto ICMP Port Unreachable, per segnalare appunto che non c'è nessun processo in ascolto sulla porta del listener Diagrammi temporali, UML Lo scambio di pacchetti può essere visualizato con Wireshark in forma di Diagramma Temporale, selezionando Statistics/FlowGraph, e scegliendo quindi una modalità di visualizzazione.Un modo più formale di ottenere lo stesso risultato, è quello di costruire un vero e proprio Diagramma di Sequenza così come definito da UML, ad esempio mediante l'uso di uno strumento apposito (UMbreLlo), con cui editare un modello, e quindi produrre diagrammi temporali come quello mostrato sopra. Select E' ora la volta di sperimentare il funzionamento di selectserver. Il server di chat viene lanciato su di un unico computer, di cui ti annoti l'indirizzo, e su cui è in esecuzione EtherApe, che realizza una visualizzazione spaziale degli host corrispondenti. Quindi, sia tu che tutti gli altri, iniziate a catturare il traffico, eseguite su di una finestra terminale in comando telnet indirizzo-del-server 9034, ed iniziate a chattare. Ad un certo punto, il server viene chiuso. • 9. durante il funzionamento, teniamo d'occhio l'aspetto di etherape • 10. osserviamo il comportamento del TCP in conseguenza della chiusura del server. Osserviamo i flag usati nei pacchetti TCP. Salviamo il capture con il nome chat.pcap. • 11. modifichiamo selectserver in modo che i messaggi ricevuti dai terminali via telnet, siano del tipo ip mittente: messaggio. Telnet Il comando telnet implementa il lato client del protocollo telnet, e consente di collegare lo standard input (la tastiera) ad un socket TCP remoto, stampando su standard output (lo schermo) ciò che la applicazione remota invia, sempre mediante la stessa connessione TCP. Per questo motivo, viene spesso usato per dialogare con applicazioni server che adottano 282 Lo strato applicativo di Internet Alessandro Falaschi protocolli testuali (come, ad esempio SMTP, HTTP, SIP). Dato che ogni carattere immesso da tastiera, viene trasmesso all'altro estremo della comunicazione, si pone il problema di come terminare la comunicazione, usando la tastiera. In questo caso, infatti, il normale control-c non ha effetto. La soluzione, ci viene però indicata al momento della partenza della connessione: alef@alef-laptop:~$ telnet 151.100.122.122 80 Trying 151.100.122.122... Connected to 151.100.122.122. Escape character is '^]'. Sapendo che il carattere ^ è usato per indicare la pressione del tasto Control, capiamo che per terminare il telnet, e riottenere il prompt dei comandi, occorre digitare la sequenza di escape, ossia la combinazione Control+parentesi_quadra_chiusa. Quest'ultima, sulla tastiera italiana si realizza premendo assieme AltGr e ], e quindi in definitiva, occorre premere tre tasti in contemporanea. Quindi, premere il tasto Invio. A questo punto il nostro programma telnet, ci presenta il suo prompt telnet>, ed in questa fase, possiamo impartirgli dei comandi locali, come ad esempio, help. Per uscire, si può invece impartire close, oppure anche solo il tasto q. Broadcast Ora è il momento, di sperimentare il programma broadcaster. • 12. modifichiamo il programma listener, in modo che quando riceve un pacchetto non esca, ma cicli indefinitivamente. Inoltre, quando stampa il contenuto di un pacchetto ricevuto, facciamo entrare tutto su di una unica linea; • 13. ognuno lanci il suo listener, in modo da ricevere il broadcast di tutti, e in una altra finestra terminale, provi innanzitutto a lanciare talker, chiedendo di trasmettere verso l'indirizzo broadcast 192.168.0.255. Verifichiamo il codice d'errore, legato alla mancanza dell'opzione SO_BROADCAST del socket. • 14. Lanciamo quindi una istanza di broadcaster, con il quale si inviano prima messaggi al nostro proprio indirizzo, quindi all'indirizzo di qualcun altro, ed infine all'indirizzo broadcast 192.168.0.255, in modo da inviare messaggi a tutti, e di nuovo, realizzare una specie di chat. • 15. Percepiamo la differenza tra la chat eseguita collegandosi in telnet con selectserver, e questa in cui ognuno ha il suo proprio listener indipendente. • 16. si tenga d'occhio Etherape. Si noteranno i computer della rete che comunicano verso l'indirizzo broacast, oppure delle comunicazioni dirette, se effettuiamo comunicazioni unicast. • 17. Catturiamo il traffico con Wireshark, comprendendo sia traffico unicast (generato e/o ricevuto), che broadcast. Determiniamo i display filter tali da limitare il traffico mostrato alla sola nostra applicazione, e tale da ◦ ◦ ◦ ◦ mostrare mostrare mostrare mostrare solo solo solo solo il il il il traffico traffico traffico traffico unicast entrante unicast uscente broadcast entrante broadcast uscente 283 Riferimenti Cattura del traffico, compilazione, e socket Riferimenti [AIL] - Appunti di Informatica Libera di Daniele Giacomini Realizzato con da Alessandro Falaschi - 284 Lo strato applicativo di Internet Investigazioni di Rete Trattiamo ora di alcuni strumenti utili per verificare la corretta configurazione degli host sia nella nostra rete, che remoti, e che possono aiutare di molto il compito di diagnosticare malfuzionamenti e problemi, investigare sulle possibili soluzioni, e svelare chi contattare. • Arrivo fin lì? Tu ci sei? Pronto? • Controllo delle intrusioni ◦ Su quali porte stanno ascoltando, i miei servizi? ◦ Quale programa sta ascoltando su questa porta? ◦ Nmap e Zenmap ▪ ▪ ▪ ▪ Tipo di scan Intervallo delle porte Indicazione del target Stato delle porte • Esperimenti • Il percorso dei nostri pacchetti • Chi è chi? ◦ Whois • Riferimenti Arrivo fin lì? Tu ci sei? Pronto? Quando in un computer, abbiamo configurato • • • • l'indirizzo IP, la network mask, il default gateway, e il DNS, non ci resta che verificare se possiamo collegarci o meno. Per questo possiamo fare uso del programma ping (man page), che invia dei pacchetti ICMP echo, ne riceve la risposta, e ci informa del ritardo intercorso. Può essere usato per • verificare il corretto funzionamento della nostra interfaccia di rete, dirigendo il ping verso noi stessi; • verificare la connessione alla rete locale, dirigendo il ping verso un computer con uguale prefisso di sottorete; • verificare il corretto instradamento verso il default gateway, dirigendo il ping verso un computer esterno alla LAN; • verificare la corretta configurazione del DNS, dirigendo il ping verso un nome di dominio. Analizzando questo capture, possiamo verificare il traffico prodotto durante l'esecuzione di un ping. Ora, sperimentiamo gli strumenti per la scoperta dei servizi attivi su uno o più computer della nostra rete. Controllo delle intrusioni Investigazioni di Rete Controllo delle intrusioni L'intrusion detection è un aspetto di Network Security spesso trascurato dagli amministratori di rete (ammesso che, nelle piccole realtà, ce ne siano), almeno finchè non si è oggetto di un serio attacco informatico. L'approfondimento della questione non è tra gli scopi di questo corso; ci limitiamo quindi ad indicare in Nessus uno degli applicativi più usati a questo scopo. Analizziamo invece ora una serie di strumenti più di base, ma che potremmo definire indispensabili. Su quali porte stanno ascoltando, i miei servizi? Sebbene con il comando ps possiamo scoprire quali programmi sono in esecuzione presso il nostro computer, il comando netstat -n --udp --tcp -p -l ci mostra su quali numeri di porta (-n) udp e tcp ci sono programmi in ascolto (-l), indicando il PID ed il nome del programma (-p) che ha aperto il socket. Per lo stesso comando, sono possibili diverse combinazioni di opzioni, in grado di mostrare anche i socket non in ascolto (omettendo -l), mostrando il relativo stato, o di mostrare i socket unix, oppure ancora di mostrare le informazioni di instradamento (netstat -r) Quale programa sta ascoltando su questa porta? L'opzione -p di netstat, che ci mostra PID e nome del programma connesso al socket, è di introduzione recente, per la sola architettura Linux; altrimenti, una volta noto un numero di porta, si può ricorrere al comando fuser -n tcp -v numero_di_porta che ci mostra il nome del programma che sta usando il numero di porta specificato. Anche in questo caso, le opzioni della chiamata possono variare: ad es. l'opzione -n udp ci permette di interrogare il namespace udp, o (-n file) quello dei descrittori di file. Nmap e Zenmap Dopo aver investigato il proprio computer, può venire la voglia di curiosare in quello degli altri, e questo può essere fatto, analizzando il tipo di risposta che il kernel invia, nel caso in cui presso una certa porta, ci sia un programma in ascolto o meno. Si badi che questo tipo di analisi, non necessariamente rappresenta una azione di attacco, anzi, al contrario, permette ad un amministratore di verificare lo stato di salute (o di compromissione) delle macchine della propria rete, come se ad esempio, un virus abbia infettato un computer, e sia in esecuzione, mettendosi in ascolto su di una porta non prevista, permettendo ad un attaccante di conettercisi, ed eseguire azioni da remoto. Inoltre, un amministratore che voglia proteggere le proprie macchine da sguardi indiscreti, provvederà senz'altro a configurare un firewall che ne impedisca la raggiungibilità. Un modo semplice di svolgere questo tipo di analisi, ci viene offerto dal programma nmap (sito, HowTo italiano, man in italiano), che è eseguibile anche per mezzo del suo front-end 286 Lo strato applicativo di Internet Alessandro Falaschi grafico Zenmap, (precedentemente chiamato nmapfe). Zenmap ha l'aspetto mostrato sotto, in cui è evidenziato il campo Target in cui inserire l'indirizzo del computer da esaminare, ed il campo Profile che determina il tipo di analisi da effettuare; quindi, nella finestrella Command è mostrato il risultato, ovvero le opzioni con cui il comando nmap sarà effettivamente invocato. Al momento della scrittura di questo testo, la versione di sviluppo (4.85) di Zenmap contiene già caratteristiche molto più avanzate di quelle presenti nella versione (4.62) presente nella distribuzione Linux Ubuntu 8.10 in uso, quindi si preferisce non entrare nella descrizione dei suoi dettagli, e limitare i commenti a solo alcune delle possibili opzioni con le quali invocare nmap, mediante il formato generale nmap [Tipo di scan] [Opzioni] {specifica del target} rimandando alla guida di riferimento per la descrizione competa delle possibilità di utilizzo. Tipo di scan L'opzione -s permette di specificare uno tra diversi Tipi di scan capaci utilizzare tecniche diverse per investigare a riguardo degli host target, e di seguito se ne descrivono le principali: • -sL: List scan - non invia nessun pacchetto verso il(i) taget, ma si limita a chiedere al DNS la risoluzione inversa dei loro indirizzi IP, realizzando così una lista di nomi di host, sia che siano effettivamente attivi, o spenti; • -sP: Ping sweep - una passo in più rispetto al precedente, permette di stabilire quali host siano effettivamente presenti, inviando loro sia una richiesta ICMP echo, che un TCP SYN verso la porta 80; • -sT: Connect Scan - tenta di eseguire il three way handshake del TCP in modo completo, e può essere eseguito anche da utenti non-root; d'altra parte, ha lo svantaggio che il processo applicativo che dovesse rispondere presso il target, può facilmente tenere traccia dell'avvenuto tentativo di connessione; • -sS: Syn-Scan - si limita ad inviare solo il primo pacchetto (SYN) dell'handshake, impiegando così meno tempo del precedente, e determinando la presenza o meno di un server in ascolto sulla porta di destinazione, in funzione del tipo di risposta (ACK, RST, ICMP, o... nulla); inoltre, non completando l'handshake, il kernel neanche notifica l'eventuale servizio in ascolto; 287 Controllo delle intrusioni Investigazioni di Rete • -sU: UDP Scan - per scoprire i servizi offerti via UDP, soffre della limitazione che l'assenza di una risposta può significare sia che c'è un server in ascolto, sia che invece ci sia un firewall che blocca la porta; inoltre, in assenza di firewall, le RFC IETF raccomandano di porre un limite al ritmo dell'invio di messaggi ICMP da parte del kernel, e dunque questo tipo di scan può richiedere tempi molto lunghi. Nella stessa invocazione di nmap, possono essere contemporaneamente specificati più tipi di scan. Intervallo delle porte A parte il caso di Ping Sweep, in cui l'intenzione è quella di determinare se un computer remoto è in rete o meno, negli altri casi lo scan è orientato alla scoperta dei servizi in esecuzione sul target, ed è possibile specificare l'intervallo delle porte da esaminare, potendo ad es. scegliere tra • default - non specificando nulla, lo scan è diretto verso le prime 1024, più quelle elencate nel file /usr/share/nmap/nmap-services • un insieme esplicito - è specificato inserendo l'opzione -p seguita dalla descrizione dell'insieme, ad es. separate da vigole (25,80,110) oppure indicando un intervallo, individuato separandone i numeri con un trattino (es. 20-200); • ordine di scansione - aggiungendo l'opzione -r le porte vengono investigate in ordine crescente; al contrario, non specificando nulla, il loro ordine è casuale. Indicazione del target Possiamo specificare • un indirizzo IP singolo • un intervallo di indirizzi IP (es. 192.168.0.64-128) • una intera sottorete (es.192.168.0.0/24) e nel secondo e terzo caso, lo scan sarà ripetuto per tutti gli host indicati. Stato delle porte In funzione della presenza o meno di un programa sulle porta esplorata, e della presenza o meno di un firewall tra i due computer, lo stato della porta può essere classificato come • open - la porta è aperta, c'è un programma in ascolto: ◦ ad es, il TCP-SYN produce un SYN-ACK di risposta, oppure un UDP non produce nulla • closed - la porta è chiusa, non c'è nessun socket in ascolto: ◦ ad es, TCP-SYN produce un RST, ed un UDP produce un ICMP port unreachable • filtered - la porta non esibisce il comportamento atteso per uno dei due casi precedenti, e questo è da imputare alla presenza di un firewall 288 Lo strato applicativo di Internet Alessandro Falaschi Esperimenti Proviamo a sperimentare l'uso di nmap, lasciando aperti sui nostri computer i programmi server (server, listener e selectserver) che ricordiamo, sono rispettivamente in ascolto sulle porte TCP 3490, UDP 4950 e TCP 9034. 1. Proviamo a dirigere un SYN Stealth Scan (opzione -sS) (il SYN clandestino) verso le porte 0-10000 di 127.0.0.1, ed impostando l'opzione (opzione -r) Ordered Ports. Riusciamo ad individuare i servizi attivi ? ne troviamo aperti altri? Quali? 2. Durante lo scan, teniamo aperto Wireshark. Cosa accade ? 1. Se richiediamo che lo lo scan avvenga con porte ordinate, ci è facile individuareche al pacchetto 5700 si trova il SYN diretto verso la porta 3490, dove è in ascolto server, che a differenza delle altre porte, risponde SYN, ACK anziché RST, ACK, dopodiché nmap interrompe la connessione con un RST. 2. per scoprire i servizi offerti in UDP, selezioniamo UDP Port Scan (opzione -sU) 3. mediante wireshark, etherape, tcpdump, iptraf, scopriamo qualche altro computer nella nostra stessa rete, scegliamone uno, e ripetiamo lo scan. Quali servizi che troviamo attivi? Quindi, ripetiamo la scoperta utilizzando nmap stesso, mediante l'opzione -sP con target 192.168.0.0/24. Mediante Wireshark, notiamo qual'è la tecnica usata da nmap. 4. effettuiamo ora uno scan verso un computer fuori della nostra rete, ad esempio 151.100.122.122. Notiamo nuovi messaggi ? cosa vuol dire filtered ports ? (verifichiamolo con Wireshark). Il percorso dei nostri pacchetti Abbiamo parlato di come l'ICMP può essere usato dal programma traceroute per scoprire, utilizzando dei time to live via via crescenti, la sequenza dei router attraversati pe raggiungere una determinata destinazione. Questo capture,riporta il traffico prodotto durante l'esecuzione di un traceroute indirizzato verso www.fasthit.net, un service provider australiano. Presso traceroute.org è possibile eseguire un traceroute che parte da svariate località del mondo, verso il nostro computer. Partiamo ad esempio dalla Universidad Tecnica Federico Santa Maria che si trova in Cile, e 1. contiamo quanti salti è possibile visualizzare; 2. notiamo i nomi delle macchine attraversate. Notiamo che per alcuni hop, sono elencati più router, in alternativa tra loro; 3. facciamo caso ai tempi che sono mostrati. 4. Osserviamo infine, qual'è l'ultimo router che riusciamo a raggiungere, e l'IP ad esso associato, differente dal nostro. Perchè ? 5. proviamo ora a fare il tragitto inverso, eseguendo traceroutea partire dal nostro computer. Facciamo lo stesso percorso ? 6. mentre proviamo il traceroute che parte dal nosto computer, possiamo tenere aperto Wireshark, ed esaminare cosa avviene 1. una alternativa a traceroute è tracepath, che realizza anche la funzione di path MTU discovery 2. da notare anche xtraceroute, che offre una visione tridimensionale del globo, e tenta di localizzarvi i router incontrati per la strada 289 Chi è chi? Investigazioni di Rete Qualora ci si trovi dietro un firewall che blocca i pacchetti UDP, oppure se gli operatori di rete che gestiscono i router intermedi non generano, o bloccano, le risposte icmp time to live exceeded, possiamo provare ad usare • tcptraceroute, che analogamente a quanto fatto da nmap, usa la tecnica di tentare di aprire una connessione TCP, sempre incapsulandola in una intestazione IP con un valore di TTL via via crescente, oppure • mtr, che riporta su schermo, in modo continuativo, le statistiche sui ritardi medi, minimi e massimi, e la deviazione standard 7. finalmente, possiamo confrontare il percorso per il Cile, con quello dal Cile verso di noi. 8. catturando con Wireshark il traffico generato da tcptraceroute e mtr, possiamo studiarne il funzionamento. • nel corso di questa verifica, ci siamo trovati nel caso in cui sia traceroute che tcptraceroute non funzionavano, mentre invece mtr si. A quanto pare, l'ISP utilizzato, filtra i pacchetti ICMP di tipo icmp time to live exceeded. A seguito della verifica fatta con wireshark, possiamo notare che mentre mtr opera inviando dei veri e propri pacchetti icmp echo request, tcptraceroute invia invece un TCP SYN, sempre con un campo TTL IP troppo basso. In entrambi i casi, i router di transito, nel generare la risposta ICMP, vi inseriscono come PDU l'inizio del pacchetto scartato, dando modo ai router sulla strada di ritorno, di investigare il tipo di traffico che ha prodotto l'icmp. Quindi, evidentemente, il nostro ISP decide di non bloccare l'ICMP prodotto dal ping, e di bloccare invece quello prodotto dal TCP, in quanto in questo secondo caso, non è esplicita l'intenzione del mittente originario, di effettuare una investigazione di rete. Presso cybergeography si trova una interessante rassegna di links a siti e strumenti che svolgono un compito simile, di cui alcuni sviluppati in java, alcuni non più raggiungibili, ed altri che integrano ancora altre funzionalità, come la ricerca whois. Un altro sito (Morgan) offre (oltre a diverse altre cose utili) un servizio di traceroute da loro verso di noi, senza mappatura geografica, ma con la visualizzazione del dove si generano i maggiori ritardi. Chi è chi? Whois E' un comando che si immette da tastiera sui sistemi Unix, che implementa il protocollo descritto dalla RFC 3912 operante via TCP su porta 43, e che permette di risalire alle informazioni riguardanti gli intestatari dei domini, degli indirizzi IP, e dei sistemi autonomi. Per quanto riguarda gli indirizzi IP ed i numeri di sistema autonomo, questi sono registrati presso i Registri Internet regionali (RIR): ce ne sono 5 nel mondo, dislocati nei continenti. Gli Internet Service Provider (ISP), a loro volta, si rivolgono ai RIR per vedersi assegnate risorse di queto tipo. • eseguire il comando whois 151.100.70.22: si scopre che viene interrogato il server whois.ripe.net, del RIR RIPE, che risponde che questo indirizzo, fa parte del lotto 151.100.0.0 - 151.100.255.255 assegnato all'Università la Sapienza, e vengono mostrati i riferimenti al DNS autorevole per il mapping inverso, al contatto tecnico, ed al sistema autonomo di riferimento (AS137) 290 Lo strato applicativo di Internet Alessandro Falaschi • eseguire il comando whois AS137: viene interrogato il server whois.arin.net, e fornita la risposta che gli indirizzi sono stati ulteriormente ri-assegnati, e di consultare http://www.ripe.net/whois • si consulti http://www.ripe.net/whois, immettendo la richiesta AS137, oppure si esegua whois -h whois.ripe.net AS137, che dirige la richiesta verso il server di RIPE: si scoprirà che il numero di sistema autonomo è gestito dal GARR • si vada sul sito del GARR: potremo osservare la topologia della rete, e di li (weathermap/RomaTizii/LaSapienza) l'andamento del traffico giornaliero, mensile e annuale. Notiamo la crescita costante del traffico sul link da 1 Gbit, che si riscontra anche bene ad es. sul link da 2 Gbit del MIX (Peering Internet/Mix). I grafici sono prodotti con MRTG, che basa il funzionamento su di un programma eseguito ad intervalli regolari come un cron job, e che interroga via SNMP gli oggetti reperibili presso i MIB dei router, acquisendo così le informazioni sui byte in transito, che vengono salvate in un file, e da cui i derivano le informazioni differenziali. Quindi, sono invocati i servizi offerti da una libreria in grado di generare i grafici che osserviamo. ◦ Presso il sito del MIX (che è un IXP (lista), dove gli ISP fanno peering, vedi [DrPeering]) troviamo l'elenco degli afferenti alla struttura, la documentazione ed i costi, oltre ad una sezione sulle statistiche ad accesso riservato, a beneficio degli aderenti. Altre interconnessioni internazionali di ricerca, avvengono tramite la rete GEANT (o meglio, Geant2) Ma il comando whois effettua anche ricerche sui domini: • eseguire il comando whois uniroma1.it: viene interrogato il server whois.nic.it, e si ottiene di nuovo l'informazione di assegnazione all'Università Sapienza, con i contatti, ed il DNS autorevole per i nomi di dominio. Riferimenti • • • • • Home Network Security - presso CERT Coordination Center Top 100 Network Security Tools - presso insecure.org mappe della rete Exploring Autonomous System Numbers The Internet Peering Knowledge Center che ospita tra l'altro The Art of Peering - The Peering Playbook • Robtex - swiss army knife internet tool Realizzato con da Alessandro Falaschi - 291 Riferimenti Investigazioni di Rete 292 Lo strato applicativo di Internet Domain Name System • Cercando tra i nomi • Un DNS tutto nostro ◦ il dominio .dg ◦ Gestione di una zona delegata, autorevole per la propria sottorete ▪ La propria zona di secondo livello ▪ La propria sottorete ▪ Il proprio sotto-dominio • DHCP e DDNS ◦ Dnsmasq • Zeroconf Cercando tra i nomi Esistono diversi strumenti che effettuano ricerca tra i DNS. Il più comune e semplice è il comando host, che offre una buona scelta di opzioni, con cui svolgere le query più diverse, e che permette la risoluzione sia diretta che inversa: Usage: host [-aCdlriTwv] [-c class] [-N ndots] [-t type] [-W time] [-R number] hostname [server] -a is equivalent to -v -t * -c specifies query class for non-IN data -C compares SOA records on authoritative nameservers -l lists all hosts in a domain, using AXFR -r disables recursive processing -R specifies number of retries for UDP packets -t specifies the query type -T enables TCP/IP mode -v enables verbose output -w specifies to wait forever for a reply -W specifies how long to wait for a reply • mentre si cattura il traffico con Wireshark, eseguire il comando host -r infocom.uniroma.it. Si osservi l'assenza di risposta dovuta alla assenza di recursione • si esegua ora host -a infocom.uniroma.it, e si noti la risposta. Si osservi come nel protocollo, la risposta contenga di nuovo i campi di domanda, oltre che le risposte. Si osservi che l'opzione -a effettua una query type ANY (provare -t CNAME, ad esempio) • eseguire di nuovo host -r infocom.uniroma.it, e verificare che ora la risoluzione ha successo: il risultato è stato infatti salvato nella cache del proprio DNS • indirizzare ora la richiesta verso un DNS diverso dal proprio (ad es. 151.100.4.2, 151.100.8.33, 195.210.91.100) e confrontare i risultati Un elenco delle possibili opzioni è piuttosto lungo, ma può valere la pena verificarlo. Altri comandi per esplorare il DNS sono dig, nslookup, e dnstracer. Un DNS tutto nostro Domain Name System Un DNS tutto nostro Passiamo a sperimentare la configurazione ed il funzionamento del DNS, con riferimento alla distribuzione Ubuntu Linux che stiamo usando, installando (ad esempio via Synaptic) il pacchetto bind9. Il dominio .dg Possiamo senz'altro impratichirci, replicando la configurazione esempificata nell'esempio del dominio .dg, e quindi 1. editare (con sudo gedit) il file /etc/bind/named.conf.local, in modo da riprodurre la situazione illustrata a lezione; 2. creiamo nella directory /etc/bind i tre files 192.168, dg e brot.dg, con i contenuti citati nell'esempio, oppure, • scaricare ed installare questo archivio(scomprimelo in una propria directory, e poi copiarlo con sudo cp nella dir /etc/bind); • il DNS locale può quindi essere mandato in esecuzione mediante il comando sudo /etc/ init.d/bind9 start, oppure • /etc/init.d/bind9 reload, nel caso in cui stia già girando; • verifichiamo che nel file di log non siano evidenziati errori, ovvero • impartiamo il comando less /var/log/daemon.log ed andiamo in fondo al file con il tasto freccia ->, o con Shift+>, o con PageDown, oppure • teniamo continuamente sott'occhio il file di log, impartendo (da un'altra console) il comando tail -f /var/log/daemon.log; Ora, sarà possibile investigare tra le informazioni immesse, eseguendo ad es. il comando host -a www.brot.dg 127.0.0.1. Gestione di una zona delegata, autorevole per la propria sottorete Un esperimento forse più semplice e utile, può essere quello di definire una propria zona di secondo livello (ubuntuNN.labint) entro la quale includere un insieme di nomi, da associare ad altrettanti indirizzi IP, in modo da simulare all'interno del proprio computer, una intera sottorete (192.168.NN.xx/24). Quindi, tutte le zone definite saranno delegate da parte di un DNS di primo livello, sempre presente in laboratorio, ed ospitato da un computer che provvederà anche a realizzare un software router che interconnette tra loro le sottoreti, e ne permette l'accesso ad Internet, come esemplificato dalla figura seguente: 294 Lo strato applicativo di Internet Alessandro Falaschi Ma andiamo con ordine, realizzando prima la configurazione del proprio DNS, e poi della propria sottorete. La propria zona di secondo livello La situazione di partenza, è che ognuno gestisce un computer di nome ubuntuNN (riscontrabile dal nome che compare nel prompt dei comandi, oppure digitando il comando hostname), a cui è associato l'indirizzo IP 192.168.0.NN/24, e che utilizza 192.168.0.3 sia come default gateway che come DNS. Come primo passo della transizione, vogliamo usare il proprio computer come DNS, autorevole per la zona di secondo ubuntuNN.labint, valida solo localmente, ed in grado di risolvere dei nomi di host del tipo qualunquenome.ubuntuNN.labint, mappandoli tutti sul proprio indirizzo IP 192.168.0.NN. Per raggiungere questo primo risultato, eseguiamo i passi • editare il file che definisce le zone da noi gestite con sudo gedit /etc/bind/ named.conf.local, ed aggiungere la zona ubuntuNN.labint, da associare al file /etc/bind/ubuntuNN.labint, in cui NN è l'ultima cifra del proprio indirizzo IP per la rete labint, che è 192.168.0.NN • editare il file di zona per il nostro dominio, impartendo sudo gedit /etc/bind/ ubuntuNN.labint, ovvero, usare il gedit già lanciato per aprire un nuovo file in una altra linguetta: ◦ impostare il DNS autorevole per la zona di secondo livello, proprio ubuntuNN.labint,ed ◦ inserire nomi applicativi del tipo qualunquenome.ubuntuNN.labint, mettendoli in corrispondenza sempre con il nostro indirizzo 192.168.0.NN ◦ inserire un RR @ A 192.168.0.NN in modo da risolvere anche il proprio nome a dominio di secondo livello ubuntuNN.labint con un indirizzo IP 295 Un DNS tutto nostro Domain Name System • tenere d'occhio i messaggi che compaiono nel file di log di BIND, mediante il comando tail -f /var/log/daemon.log • salvare i file, e farli ri-leggere al nostro DNS con il comando sudo service bind9 reload • verificare che i nuovi nomi immessi vengano effettivamente risolti, impostando una query diretta verso il nostro DNS, con il comando host qualunquenome.ubuntuNN.labint 192.168.0.NN • se funziona, sostituire il nameserver in /etc/resolv.conf con la linea nameserver 192.168.0.NN, e provare a pingare i nuovi nomi Il risultato di questi passi può essere verificato con i files di configurazione per la zona ubuntu152.labint. Ciò che rimane da eseguire per questa prima fase, è • configurazione di un DNS presso 192.168.0.22, autorevole per il dominio di primo livello labint, e contenente i RR di tipo NS che realizzano le delega ai nostri DNS di secondo livello, nonchè i glue record necessari a raggiungerli. Questo passo l'ha realizzato il docente, con questo risultato;E' da notare che è stato usato il file di zona relativo ai RR di tipo PTR per la risoluzione inversa, per inserire tutte le corrispondenze indirizzo IP - nome a dominio presso lo stesso DNS autorevole per il primo livello, in quanto esso deve comunque conoscere gli indirizzi degli altri host, dato che devono essere inseriti nei glue records; • configurazione della propria rete in modo da usare 192.168.0.22 sia come DNS, che come default gateway, ed aggiunta di labint come dominio di ricerca. Questo può essere molto facilmente ottenuto cliccando con il tasto destro del mouse sul Network Applet in alto a destra, e scegliere edit conections, e quindi cliccare su labint, e richiedere edit. Non resta che cliccare la linguetta IPv4 Settings, per ottenere il risultato mostrato a lato, e quindi, disabilitare e riabiitare la rete agendo sul network applet, perché le modifiche abbiano effetto. • verifichiamo il corretto funzionamento, eseguendo dei ping al computer del nostro vicino, con o senza il suffisso labint, e constatiamo la corretta risoluzione del suo indirizzo. 296 Lo strato applicativo di Internet Alessandro Falaschi La propria sottorete Assegnare tanti diversi indirizzi ad uno stesso computer, sebbene possa sembrare un controsenso, consente facilmente di abilitare un determinato servizio a risponde solo quando l'host viene referenziato con un determinato nome, anziché un qualunque altro, perché la diversa risoluzione di indirizzo IP, permette di eseguire la bind() del socket del server, solo sul quel particolare indirizzo. Per realizzare una trasizione dolce verso la situazione raffigurata nella figura precedente, suddividiamo l'operazione nelle seguenti fasi: • aggiunta degli indirizzi per la nuova sottorete. Questo può essere svolto sempre a partire dal Network Applet, aggiungendo i nuovi indirizzi di tipo 192.168.NN.xx/24, con xx =1, 2, 3, ecc... ed impostando per essi il gateway 192.168.NN.254; ◦ per rendere attive le modifiche, disabilitare e riabiitare la rete agendo sul Network applet • sostituzione nel file /etc/bind/ubuntuNN.bind dei RR di tipo A, usando stavolta un diverso indirizzo del tipo 192.168.NN.xx per ogni diverso nome. Allo scopo di semplificare la gestione futura delle sottoreti, possiamo usare tutti gli stessi numeri, ad es. xx=1 per www, 2 per il NS, 3 per il MX; ◦ per rendere attive le modifiche, impartire sudo service bind9 reload • modifica (da parte del docente) della configurazione del DNS di primo livello e del software router (vedi risultato): ◦ aggiunta degli indirizzi di tipo 192.168.NN.254 per eth0 ◦ modifica dei glue record per puntare ai nuovi indirizzi dei nameserver di secondo livello ◦ ipostazione delle deleghe dei sotto-domini NN.168.192.in-addr.arpa alla autorità del DNS residente presso 192.168.NN.2, in modo da permettere la risoluzione inversa degli spazi di indirizzamento associati alle sottoreti • verifica dell'operatività della nuova configurazione di rete e del dns, pingando il nuovo DG, tentando la risoluzione dei nuovi nomi mediante il comando host www.ubuntuNN.labint 127.0.0.1, e verificando l'instradamento con il comando tracepath oppure mtr • rimozione del precedente indirizzo 192.168.0.NN • creazione di un nuovo file di zona /etc/bind/192.168.NN per bind, contenente i RR di tipo PTR per la zona auto-gestita NN.168.192.in-addr.arpa Il proprio sotto-dominio Ora che tutto funziona, non sarebbe più simpatico poter chiamare il computer su cui stiamo lavorando con il proprio nome proprio, come ad esempio giuseppe.labint, in modo (alla prossima esercitazione) di poter ricevere email presso [email protected]? Semplicemente, si può fare... per questo, è sufficiente • presso il DNS autorevole per .labint, per ogni nuovo dominio di secondo livello, si aggiunge un RR di tipo NS che effettua la delega al DNS che è anche autorevole per ubuntuNN.labint: giuseppe NS ubuntuNN 297 DHCP e DDNS Domain Name System • presso i singoli computer ubuntuNN.labint, aggiungere in named.conf.local una ulteriore zona, che usa il medesimo file di zona già realizzato per il dominio di secondo livello ubuntuNN.labin: zone "giuseppe.labint" { type master; file "/etc/bind/ubuntuNN.labint"; }; DHCP e DDNS Proviamo a catturare il traffico osservato dal nostro computer, per scoprire gli eventuali messaggi DHCP di Discovery e Request. Quindi, proviamo a suscitarli noi stessi, mediante l'interfaccia di configurazione della rete (sudo network-admin), configurando una connessione via cavo che faccia uso di un server DHCP, e salvando poi la configurazione così ottenuta. Dnsmasq Si tratta di un serverino molto intelligente (sito, man page, guida alla configurazione), che implementa un forwarder DNS, associato ad un server DHCP, il tutto incorporato in unico programma. In tal modo, si è in grado di risolvere gli IP dei computer della propria rete, in base al nome che questi hanno comunicato nella DHCP Discovery, ed in base all'IP affittato loro. Sperimentiamo ora questa nuova soluzione, in modo da confrontarla con la configurazione statica del file di zona, intrapresa precedentemente. Sul computer con IP 192.168.0.152: • editiamo il file di configurazione di dnsmasq, con sudo gedit dnsmasq.conf, in modo che in fondo allo stesso, appaiano le direttive dhcp-range=192.168.0.151,192.168.0.170,12h dhcp-option=option:router,192.168.0.3 clients selfmx domain=softel nomi di host • • • • /etc/ # L'intervallo degli indirizzi da assegnare # Il default gateway da comunicare ai # Aggiunge un record MX ad ogni IP # il suffisso di domiono da aggiungere ai seguiamo l'evolversi degli eventi, con tail -f /var/log/daemon.log disattiviamo il DNS BIND, con il comando sudo /etc/init.d/bind9 stop mettiamoci in ascolto con wireshark lanciamo dnsmask, con sudo /etc/init.d/dnsmask start Sugli altri computer del laboratorio, invece, lanciamo di nuovo lo strumento grafico Sistema/ Amministrazione/Rete, clicchiamo sulle Proprietà della connessione via cavo, e scegliamo la Configurazione automatica (DHCP). Dovremmo aver ricevuto un nuovo IP, diverso dal precedente. Verifichiamo: • • • • il nostro nuovo IP, con il comando ip addr le informazioni di routing, con il comando ip route l'impostazione del DNS, con il comando cat /etc/resolv.conf che la risoluzione funzioni ancora, con il comando host mionome ◦ nella sperimentazione, avviamo verificato che dnsmask utilizza come DNS a cui inoltrare le richieste per i domini esterni, quello che trova scritto in /etc/ 298 Lo strato applicativo di Internet Alessandro Falaschi resolv.conf. Per questo, prima di lanciare dnsmask, è bene verificare che lì sia scritto qualcosa di sensato Il risultato dello svolgimento della procedura indicata, è riportata in questo file di cattura,ottenuto presso lo stesso computer in cui viene eseguito dnsmask. Zeroconf L'implementazione di Zeroconf per Linux è quella fornita da Avahi, e che prevede di installare i pacchetti • avahi-daemon, che realizza le funzioni di mDNS e DNS-SD • avahi-discover, che offre una applicazione grafica in grado di scoprire i servizi zeroconf presenti in rete • avahi-utils, che comprendono una serie di programmi, che permettono ad esempio di interrogare il mDSN, ovvero di pubblicare un nuovo servizio • libnss-mdns, che offre il supporto mDSN alla resolver library • Un browser web che supporta l'mDNS per la risoluzione dei nomi *.local, è Epiphany A questo punto, abbiamo tutto ciò che occorre per procedere nella nostra sperimentazione. Mantenendo aperto il capture di Wireshark • attivare l'mDNS del proprio host, eseguendo il comando sudo avahi-daemon • osservare che a seguito del lancio dell'mDNS, dopo un primo messaggio IGMP che segna l'iscrizione dell'host al gruppo multicast, seguono delle query in cui nel campo Authoritative si annunciano i dati dell'host, e quindi, in assenza di contese sullo stesso Link Locale, viene generata una risposta. Poi, altre tre query che annunciano il servizio di workstation, ed ancora risposte. • per scoprire i servizi offerti dagli host presenti nella propria LAN, eseguire il comando avahi-discover • per abilitare l'uso di mDNS da parte del resolver, modificare il file /etc/ nsswitch.conf come indicato presso /usr/share/doc/libnss-mdns/ README.html • provare ora a lanciare Epiphany, e referenziare l'indirizo http://nomedelvostrocomputer.local (sempre che abbiate installato il web sever, apache va benissimo!) Realizzato con da Alessandro Falaschi - 299 Zeroconf Domain Name System 300 Lo strato applicativo di Internet Posta elettronica: SMTP, autenticazione IMAP, e Nella esercitazione precedente abbiamo configurato un DNS presso 192.168.0.22, e lo abbiamo reso autorevole per la zona di TLD softel, mentre l'autorità per la risoluzione dei nomi a dominio di secondo livello è stata delegata ai singoli host del laboratorio, presso i quali, per ogni dominio di secondo livello, è stato aggiunto un RR di tipo MX, che definisce ogni computer, come il Mail eXchanger di se stesso. Ora, ci aggingiamo a configurare per ogni computer un server SMTP, in modo che si possano inviare e ricevere email da un computer all'altro, senza dover transitare dall'esterno, come codificato da header del tipo From: alice <[email protected]> To: bruno <[email protected]> In questo esempio, alice è seduta davanti a alice.labint, e vi sta lavorando con login labint. Per inviare una email al suo collega bruno, loggato come labint su bruno.labint, lo User Agent di posta elettronica di alice utilizza • come identità, alice <[email protected]>, e • come server SMTP di uscita, quello residente sul proprio stesso computer. L'SMTP server di alice quindi, interrogando il DNS autorevole per bruno.labint, scopre che il MX è ancora bruno.labint , a cui in definitiva invia a sua volta l'email. Se al contrario, alice vuole inviare una email ad un indirizzo email domiciliato presso un nome a dominio associato ad un IP pubblico, allora dovrà usare • una identità presso la quale possa ottenere una risposta, come ad esempio alice <[email protected]>, ed • un server SMTP di uscita (detto outbound) che inoltri l'email sulla Internet pubblica Stabiliamo dunque di configurare l'SMTP presente su mail.labint in modo che questo, anzichè tentare di contattare direttamente il server di posta indicato dal RR MX del dominio di destinazione (che molto probabilmente rifiuterebbe di accettarla, come difesa dallo spam), invii tutta la posta in uscita ad un suo smarthost fisso, residente sulla Internet pubblica, in modo che sia questo, a consegnare l'email a destinazione. Qualora il destinatario della email di alice (ad es bruno <[email protected]>) scelga di rispondere ad alice.delmar, quest'ultima troverà la risposta presso il dominio gmail.com. Configurazione di un server SMTP - Postfix Posta elettronica: SMTP, IMAP, e autenticazione • Configurazione di un server SMTP - Postfix ◦ Invio interno al dominio locale ◦ Ricezione locale ◦ Analisi del traffico email ▪ ▪ ▪ ▪ ▪ il capture i file di log MIME encoded word transfer encoding • Prelievo email con server IMAP - Dovecot ◦ Architettura del server ◦ Protocollo di accesso ◦ Autenticazione ▪ formato delle password ◦ Ricezione ed autenticazione ◦ SASL ▪ CRAM-MD5 ◦ Abilitazione di TLS ▪ Uso di certificati differenti • Sicurezza per l'SMTP • Risoluzione Problemi Configurazione di un server SMTP - Postfix La distribuzione Ubuntu 8.10 installata al laboratorio, prevede l'utilizzo del server SMTP Postfix. Ci sono tre diversi modi di configurarlo: 1. usando Synaptic per scaricarlo, viene eseguita automaticamente una procedura di configurazione guidata in modalità grafica; 2. seguendo le istruzioni riportate nella documentazione di Ubuntu, dopo l'installazione, si può eseguire il comando 302 Lo strato applicativo di Internet Alessandro Falaschi sudo dpkg-reconfigure postfix, che determina l'esecuzione di una procedura di configurazione guidata in modalità testuale; 3. modificando direttamente il file di configurazione del demone, eseguendo sudo gedit /etc/postfix/main.cf Dato che la prima modalità parte da sola all'atto dello scaricamento, illustriamo cosa rispondere alle domande, tenendo però presente che se questa modalità non dovesse andare a buon fine, possiamo ricorrere ad una delle altre due. Usando Synaptic, dopo aver cercato, selezionato e scaricato Postfix, ci compare una finestra di configurazione, in cui per ogni domanda, possiamo chiedere l'aiuto, che ci chiarifica il senso delle scelte da effettuare. Vediamo ora quindi, le risposte da fornire nei due casi individuati, ossia l'installazione del proprio server, e del server di Outbound: • Profilo generale: rispondiamo Sito Internet. Significa che il computer 1. tenterà di recapitare la posta in uscita direttamente al destinatario finale, 2. per motivi di sicurezza, accetterà di spedirne solo se proveniente dallo stesso computer, e 3. accetterà la posta per il nome a dominio per il quale sarà configurato a riceverne; ◦ nel caso invece della configurazione dell'outbound SMTP, rispondiamo Sito internet con smarthost, in modo che il computer non tenterà di consegnare direttamente l'email al destinatario, ma la invierà tutta ad un ulteriore server SMTP, ospitato sulla Internet pubblica, che realizza una funzione di relay (ponte); • Nome del sistema: inserire (se non è già suggerito) il nome che compare come risposta al comando shell hostname (tipicamente, quello che si è inserito alla esercitazione precedente, e che dovrebbe essere pari a mionome.labint). Questa informazione è necessaria al server SMTP per riconoscersi come il destinatario finale delle email a lui dirette, nel qual caso, tenterà di consegnarle nella inbox dell'utente locale, se esiste. Per domini di destinazione diversi, invece, tenterà di contattare direttamente l'SMTP di destinazione; ◦ l'outbound SMTP invece, quando riceve una email destinata ad un indirizzo diverso dal proprio nome, tenta di inoltrarla al suo smarthost; • SMTP relay host: questa impostazione viene richiesta solo nel caso dell'outbound SMTP, ed è il nome del computer prima indicato come smarthost. Inseriamo qui il nome del server SMTP del provider che ci offre la connettività. Il risultato della configurazione è visualizzabile nel file /etc/postfix/main.cf, la cui lista completa dei parametri di configurazione è ottenibile come man postconf. Nel nostro caso, avremo il risultato: myhostname = mionome.labint mydestination = mionome.labint, ubuntuNN.labint, localhost.labint, localhost relayhost = mynetworks = 127.0.0.0/8 mentre per l'outbound SMTP, occorre aggiungere a mynetworks la rete locale 192.168.0.0/ 16, istruendolo così a permetterne l'uso come Relay da parte degli altri computer della LAN, come illustrato dalla documentazione. Inoltre, l'outbound SMTP deve inserire, alla riga relayhost, quello designato per inoltrare le email sulla Internet pubblica. 303 Configurazione di un server SMTP - Postfix Posta elettronica: SMTP, IMAP, e autenticazione Invio interno al dominio locale Per sperimentare l'invio delle email, utilizziamo un client di posta, ad esempio possiamo usare Evolution. Se è la prima volta che lo apriamo, la configurazione di un nuovo profilo partirà da sola, altrimenti, ci possiamo arrivare dal menù modifica/preferenze. Chiamiamo l'account di questo nuovo profilo [email protected], che corrisponderà anche al proprio indirizzo email, ed impostiamo per la posta in uscita, il server SMTP appena installato, e residente sul nostro stesso computer, ossia ancora mionome.labint; per ora, non richiediamo nessuna opzione di sicurezza. Quindi, usciamo da Evolution, e rientriamo. Monitorando il file di log di Postfix mediante il comando tail -f /var/log/mail.log, ed eventualmente tenendo aperto wireshark con filtro di cattura port 25, proviamo a spedire una email verso l'indirizzo di un nostro collega, come [email protected]. Che succede ? Non parte ? Che messaggio di errore osserviamo ? Proviamo a capire cosa è successo, dall'analisi del nostro file di log, ed eventualmente, anche di quello del nostro collega. Ad esempio, potreste avere impostato male myhostname, nel qual caso il mittente si vedrà tornare indietro l'email, con il motivo che "il destinatario ha problemi di loop". Dopo aver eventualmente modificato il file di configurazione, dobbiamo fare in modo che Postfix lo rilegga: impartiamo allora il comando sudo /etc/init.d/postfix restart. Controllando il file di log, verificate che il processo si riavvii. Ricezione locale Ora che siamo in grado di scrivere ai computer di labint, cerchiamo anche di leggere la posta ricevuta. Dato che siamo seduti davanti allo stesso computer su cui è in esecuzione l'SMTP ricevente, possiamo verificare che l'email che un nostro collega ci ha scritto, si è effettivamente parcheggiata in un file di sistema, immettendo il comando cat /var/ mail/labint. In modo più comodo, potremmo accedervi mediante un client di posta testuale, come ad esempio mutt. Per potervi accedere dal client grafico di email Evolution, dobbiamo ri-aprire Modifica/ preferenze, ed editare l'account, specificando sulla linguetta Ricezione email, di usare un File spool mbox standard Unix, ed indicando il percorso /var/mail/labint. Analisi del traffico email Modifichiamo ora la configurazione dello User Agent Evolution, scegliendo il nostro account in Modifica/preferenze, e nelle preferenze di composizione, indichiamo Formato messaggi in HTML come Comportamento predefinito. Quindi • creiamo ora una nuova email, nuovamente indirizzata al nostro [email protected] (accertandoci che lui faccia lo stesso), contenente ◦ un Subject con (almeno) una lettera accentata ◦ una frase con qualche lettera accentata ◦ il nostro nome evidenziato come Header1 (usando il secondo selettore sotto l'oggetto) ◦ usando la voce del menù in alto Inserisci: ▪ una faccina 304 Lo strato applicativo di Internet ▪ una immagine (ad Ubuntu.png) Alessandro Falaschi esempio, /home/labint/Examples/logo- ◦ e quindi allegare un'altra email, trascinandone (con il mouse) il sommario, dentro alla finestra di composizione • prima di inviare l'email ◦ aspettiamo che anche il nostro collega l'abbia compilata ◦ lanciamo Wireshark, ed assicuriamoci che anche il collega l'abbia lanciato ◦ su una altra finestra terminale, eseguiamo il comando tail -f /var/log/ mail.log • finalmente, spediamo l'email, e poi, fermiamo wireshark. Il capture Ora che nel capture il traffico è incrociato, con gli stessi due IP che si scambiano il ruolo di mittente e destinatario, come facciamo a separare il traffico delle due comunicazioni? Ovviamente, vale sempre il filtro creato con "follow TCP stream"... ma più in generale, i due Client useranno due diverse porte effimere, dunque sarà così, che potremo distinguere i due tipi di traffico. I file di log Se apriamo l'email ricevuta, e scegliamo di visualizzarne il sorgente, ad esempio digitando control-u, possiamo • verificare che il message-id che compare nel file di log del nostro collega, in corrispondenza alla accettazione del messaggio da parte del suo SMTP server, corrisponde a quello associato all'Header SMTP omonimo; ◦ osservando il traffico catturato, potremmo verificare se questo Header fosse già presente nel messaggio, oppure sia stato inserito dall'SMTP ricevente; • verificare come l'id presente nel primo Header Received, corrisponda a quello indicato nel nostro file di log; • apprezzare la struttura annidata prevista dallo standard MIME. MIME Meglio ancora, salviamo il messaggio come un file, e poi apriamolo con il solito editor gedit, in modo da ritrovarci nella stessa situazione ottenibile osservando ad esempio questo messaggio salvato. Osserviamo come • il boundary =-tD+3pVv/anrIvtyoTefF separa l'email vera e propria, dall'allegato (l'altro messaggio) ◦ il boundary =-G0oO6HRE7BjAXqVLfRx3 separa il testo della email, dalla immagine inclusa ▪ il boundary =-OGyz4VI0kdx5dq1STkyx messaggio in text/plain, da quella in HTML separa la versione del Notiamo ora, come nella parte HTML, l'immagine sia referenziata citando il Content-ID: <1197403030.18175.2.camel@alef-laptop> definito nella parte MIME che la rappresenta. Verifichiamo come l'immagine sia rappresentata con una codifica di trasferimento di tipo Base64. 305 Prelievo email con server IMAP - Dovecot Posta elettronica: SMTP, IMAP, e autenticazione Encoded Word Osserviamo come la parola Perù presente nel Subject, sia stata trasformata nella stringa =?ISO-8859-1?Q?Per=F9?=, in accordo al metodo indicato come Encoded Word, utilizzando perdipiù un charset di riferimento (ISO-8859-1) diverso da quello usato nel body della email. Transfer Encoding Facciamo ora caso alla modalità di rappresentazione delle lettere accentate. Nella parte HTML, benchè sia dichiarato l'uso del charset UTF-8, si adotta un Transfer-Encoding a 7bit, dato che non sono presenti caratteri diversi dall'insieme US-ASCII. Infatti, le lettere accentate (e non solo), sono rappresentate per mezzo di Numeric Character Reference, un metodo precedente alla definizione dell'Unicode, e che rappresenta un carattere mediante la sequenza &#nnn; in cui nnn sono due o più cifre numeriche che individuano il codepoint del carattere rappresentato, nell'ambito del charset dichiarato per il documento. Per quanto riguarda la parte in text/plain invece, l'effettiva codifica UTF-8 ad 8 bit, usata per rappresentare i caratteri non-ASCII, può essere apprezzata al meglio, utilizzando un editor binario: adoperiamoci quindi per scaricare il buon khexedit impartendo il comando sudo apt-get install khexedit. Quindi, possiamo aprire il file contenente il testo della email salvata, e verificare come per le lettere accentate, vengano effettivamente usati due bytes. Con un pò di pazienza, potremmo anche verificare come risalire effettivamente, ai codepoints rappresentati. Prelievo email con server IMAP - Dovecot Come illustrato nella guida di ubuntu, la distribuzione è armonizzata con il server Dovecot, che scarichiamo mediante Synaptic, selezionando dovecot-imapd. Architettura del server Dovecot si compone di diversi processi, ed i principali sono • processo Master (dovecot) - legge i files di configurazione, scrive i file di log, lancia gli altri processi e li ri-lancia in caso di errore; • processi di Login (imap-login, pop3-login) - accettano la connessione di un nuovo client, rispondono alle richieste di opzioni, gestiscono l'attivazione di TLS/SSl se richiesto, e quindi comunicano con il processo di autenticazione, dopodiché (in caso di successo) cedono il controllo al processo mail vero e proprio; • processo di Authentication (dovecot-auth) - gestisce tutti i passi relativi alla autenticazione, ossia i meccanismi SASL, la verifica delle password, e le informazioni di utente. Comunica sia con i processi di login, che con il processo master, tenendo memoria se il PID associato al singolo client è stato autenticato o meno; • processo Mail (imap, pop3) - gestisce l'accesso alle email destinate all'utente che ha effettuato con successo il login, in accordo al protocollo selezionato. 306 Lo strato applicativo di Internet Alessandro Falaschi Protocollo di accesso Scegliamo di accedere all'email mediante lo user agent Evolution, adottando il protocollo IMAP. Per questo: • editare /etc/dovecot/dovecot.conf, e decommentare la linea protocols = imap imaps, commentando invece protocols = none • ri-lanciare il server dovecot con il comando sudo /etc/init.d/dovecot restart • in Evolution, configurare come imap server l'IP di loopback del proprio computer 127.0.0.1, e come nome utente, labint • mantenere aperto wireshark, e sniffare sulla interfaccia any: ora, cliccando sulla cartella inbox (o in arrivo), sarà chiesta la password per l'utente indicato, e sarà possibile leggere le email spedite in locale. Verificare con Wireshark, cosa è successo (sulla porta 143). * OK Dovecot ready. A00000 CAPABILITY * CAPABILITY IMAP4rev1 SASL-IR SORT THREAD=REFERENCES MULTIAPPEND UNSELECT LITERAL+ IDLE CHILDREN NAMESPACE LOGIN-REFERRALS STARTTLS AUTH=PLAIN A00000 OK Capability completed. A00001 LOGIN labint password-in-chiaro A00001 OK Logged in. A00002 NAMESPACE * NAMESPACE (("" "/")) NIL NIL A00002 OK Namespace completed. A00003 LIST "" {1+} . .... etc etc etc In rosso, sono indicati i messaggi inviati dal client: come si vede, la password viene trasmessa in chiaro. Proviamo ora a configurare tutti quanti lo stesso server IMAP, corrispondente a ubuntu22.labint, e verifichiamo la possibilità per IMAP, di avere più connessioni contemporanee, alla stessa mailbox. Possiamo ora facilmente trasformare anche questo esperimento, in una nuova forma di chat. Ma... non ci permette di entrare! Sniffando, osservo * OK Dovecot ready. A00000 CAPABILITY * CAPABILITY IMAP4rev1 SASL-IR SORT THREAD=REFERENCES MULTIAPPEND UNSELECT LITERAL+ IDLE CHILDREN NAMESPACE LOGIN-REFERRALS STARTTLS LOGINDISABLED A00000 OK Capability completed. A00001 LOGIN labint password-in-chiaro * BAD [ALERT] Plaintext authentication is disabled, but your client sent password in plaintext anyway. If anyone was listening, the password was exposed. A00001 NO Plaintext authentication disallowed on non-secure connections. A00002 LOGOUT * BYE Logging out A00002 OK Logout completed. Questo comportamento è dettato dalla direttiva di configurazione disable_plaintext_auth = yes, presente nel file /etc/dovecot/dovecot.conf, e attiva di default. In questo caso il Dovecot in esecuzione su ubuntu22.labint, accorgendosi che l'IP di provenienza è differente dal proprio, ci mette in guardia dei rischi di sicurezza, a meno che questo non sia previsto, modificando la direttiva di compilazione. Anziché seguire questa strada, valutiamo le possibilità di mettere in sicurezza la ricezione delle email. 307 Prelievo email con server IMAP - Dovecot Posta elettronica: SMTP, IMAP, e autenticazione Autenticazione L'abilitazione alla autenticazione del ricevente, ci mette in grado di assolvere al punto 4) dei requisiti di sicurezza per la posta elettronica. Se chiediamo a Evolution il tipo di autenticazione supportato dal server IMAP (Modifica/Preferenze, e poi Modifica del profilo email creato, e quindi Ricezione email, e poi Controlla tipi supportati), il capture di Ethereal mostra lo scambio * OK Dovecot ready. A00000 CAPABILITY * CAPABILITY IMAP4rev1 SASL-IR SORT THREAD=REFERENCES MULTIAPPEND UNSELECT LITERAL+ IDLE CHILDREN NAMESPACE LOGIN-REFERRALS STARTTLS LOGINDISABLED A00000 OK Capability completed. in cui osserviamo come il protocollo IMAP permetta al client di investigare sulle Capability del server: quelle che ci interessano, in questa fase, sono SASL e STARTTLS. Dato che per Evolution la risposta ottenuta determina la cancellazione dei tipi di autenticazione diversi dalla password, ed avendo capito che per password, si intende in chiaro, ciò significa che per procedere, occorre abilitare le funzioni crittografiche previste dal server Dovecot, ossia il Simple Authentication and Security Layer (SASL) ed il TLS. Ma prima, conviene svolgere qualche considerazione relativa al formato con cui sono memorizzate le password presso il server. Formato delle password Se le password memorizzate presso il server sono salvate in chiaro, queste possono essere utilizzatate congiuntamente ad un qualunque meccanismo di autenticazione, perché una volta che il server riceve una password su cui è stata operata una traformazione crittografica, non deve far altro che applicare la medesima trasformazione sulla propria copia della password, e verificare se i risultati corrispondono. D'altra parte, nel caso in cui il server venga compromesso, e venisse trafugato il file con le password in chiaro di tutti gli utenti, avremmo combinato un bel casino. Per questo motivo, si preferisce memorizzare (almeno presso il server) le password in forma crittografata. Infatti, se la password ci viene inviata in chiaro, non dobbiamo far altro che ripetere su questa, la trasformazione crittografica utilizzata nella fase di salvataggio, e procedere al confronto. Se invece la password ci arriva già crittografata, allora... in generale, il meccanismo di crittografia usato per memorizzare le password sul server, deve essere legato al meccanismo di crittografia adottato dal meccanismo di autenticazione. Come risultato, se si consente agli utenti di scegliere uno tra diversi meccanismi crittografici, si hanno due possibilità: • mantenere presso il server diversi formati crittografici per lo stesso insieme di password, uno per ogni meccanismo di autenticazione possibile, oppure • tornare al caso della memorizzare di una unica copia delle password in chiaro, da trasformare di volta in volta, in accordo al meccanismo crittografico scelto dall'utente. Infine, nel caso in cui la trasmissione si avvalga dei servizi crittografici offerti da uno strato inferiore, in grado di garantire la riservatezza della comunicazione, come nel caso di IPSec/ ESP o di TLS, allora si può tornare ad effettuare l'invio della password in chiaro, e mantenere le password presso il server in un formato crittografico unico. 308 Lo strato applicativo di Internet Alessandro Falaschi SASL Come illustrato nella parte di teoria, (SASL) è una libreria che offre ai protocolli applicativi, l'uso di meccanimsi crittografici a scelta. Tra i vari possibili meccanismi di autenticazione utilizzabili da Dovecot, scegliamo di attivare CRAM-MD5. CRAM-MD5 Per questo, editiamo (come root) il file /etc/dovecot/dovecot.conf, localizzando la linea che elenca i meccanismi, e modificandola come mechanisms = plain cram-md5 Seguendo le indicazioni di un howto, scegliamo di memorizzare una password criptata con uno schema compatibile con CRAM-MD5, da associare all'utente labint che effettivamente esiste sul computer, in un database di password corrispondente ad un semplice file in formato passwd, che salviamo in /etc/dovecot/cram-md5.pwd. Per fare questo, • invochiamo il comando dovecotpw a cui forniamo il valore di password che intendiamo usare, e prendiamo nota del suo valore crittografato. Ad esempio, scegliendo come password labint, si ottiene {HMAC-MD5}57038d3507ea76f510597155a39f6bf172bcc436e76c43e669f53c98b9a81afe • creiamo un nuovo file con gedit (lanciato come root), in cui inseriamo la linea labint:{HMAC-MD5}57038d3507ea76f510597155a39f6bf172bcc436e76c43e669f53c98b9a81afe • salviamo il file con nome /etc/dovecot/cram-md5.pwd; • modifichiamo i permessi del nuovo file con sudo chmod 0600 /etc/dovecot/ cram-md5.pwd; • aggiungiamo in /etc/dovecot/dovecot.conf la direttiva passdb passwd-file { args = /etc/dovecot/cram-md5.pwd } • ri-lanciamo il server dovecot con il comando sudo restart; /etc/init.d/dovecot Se ora eseguiamo di nuovo la richiesta del tipo di autenticazione supportata, osserviamo che ora CRAM-MD5 non è cancellato, e lo possiamo selezionare. Quindi, sniffando con Wireshark il traffico corrispondente ad una richiesta di Send/Receive, otteniamo il capture * OK Dovecot ready. A00000 CAPABILITY * CAPABILITY IMAP4rev1 SASL-IR SORT THREAD=REFERENCES MULTIAPPEND UNSELECT LITERAL+ IDLE CHILDREN NAMESPACE LOGIN-REFERRALS STARTTLS LOGINDISABLED AUTH=CRAM-MD5 309 Prelievo email con server IMAP - Dovecot Posta elettronica: SMTP, IMAP, e autenticazione A00000 OK Capability completed. A00001 AUTHENTICATE CRAM-MD5 + PDE4NDk0MzMxMjQzNjI0ODMuMTIwMTcwODQzMUBhbGVmPg== bGFic29mdGVsIDU1ZWIzNDYyMDdmMDE4NzEyODVkYjJmMTUxYWY4M2Zl A00001 OK Logged in. A00002 NAMESPACE * NAMESPACE (("" "/")) NIL NIL A00002 OK Namespace completed. . ... etc etc etc in cui osserviamo appunto, l'offerta AUTH=CRAM-MD5, la sua scelta, l'invio della sfida, e la risposta, contenente la codifica base64 del nome dell'utente, e l'HMAC-MD5 calcolato a partire da challenge e password. Utilizzando uno strumento on-line di co-decodifica base64, possiamo verificare che il contenuto di challenge e response, corrisponde a quanto illustrato a lezione. In alternativa al sito suindicato, si può usare un comando per la co-decodifica base64, impartendo il comando echo bGFic29mdGVsIDU1ZWIzNDYyMDdmMDE4NzEyODVkYjJmMTUxYWY4M2Zl | base64 -d - Abilitazione di TLS L'uso di un servizio di sicurezza a livello di trasporto, svincola lo strato applicativo dal doversi preoccupare di possibili attacchi di sicurezza, e permette l'invio di password in chiaro. In questo caso, mentre l'utente si autentica presso il server inviando la propria password, anche il server si autentica presso l'utente, mediante l'invio di un proprio certificato. Perché ciò sia possibile, occorre editare di nuovo il file /etc/dovecot/ dovecot.conf, e decommentare le linee ssl_cert_file = /etc/ssl/certs/ssl-cert-snakeoil.pem ssl_key_file = /etc/ssl/private/ssl-cert-snakeoil.key che contengono i riferimenti ad una chiave privata (.key), ed al certificato X.509 autofirmato (.pem), relativo alla rispettiva chiave pubblica, che sono preinstallati su linux. A questo punto ri-configuriamo Evolution, chiedendo di abilitare TLS, come previsto dalla RFC 2595, passando ancora da Modifica/Preferenze, scegliendo l'account/Modifica, Ricezione email/usa connessione sicura/TLS. Dopo aver richiuso e ri-lanciato Evolution, questo ci mostra una finestra in cui chiede se accettare o meno il certificato autofirmato ricevuto, rilasciato da un fantomatico Office for Complication of Otherwise Simple Affairs, e con fingerprint 12:cf:98:70:fc:81:a7:1f:d2:c7:3c:8f:62:9f:a7:75. Stavolta il capture di Wireshark appare come segue: * OK Dovecot ready. A00000 CAPABILITY * CAPABILITY IMAP4rev1 SASL-IR SORT THREAD=REFERENCES MULTIAPPEND UNSELECT LITERAL+ IDLE CHILDREN NAMESPACE LOGIN-REFERRALS STARTTLS LOGINDISABLED AUTH=CRAM-MD5 A00000 OK Capability completed. A00001 STARTTLS A00001 OK Begin TLS negotiation now. .^....E......9..8..5..f..3..2......../........... . ... etc etc etc 310 Lo strato applicativo di Internet Alessandro Falaschi e dopo la richiesta (confermata) di utilizzare il TLS, il dissettore di IMAP non è più in grado di decodificare il traffico. Invece, qualora in Evolution si richieda l'SSL anzichè il TLS, si può ancora vedere qualcosa, come mostrato in questo capture. Uso di certificati differenti Nelle esercitazioni sugli aspetti di sicurezza, si illustra come generare ed utilizzare un certificato differente. Sicurezza per l'SMTP Torniamo ora al server SMTP di uscita. Anche per questo, possiamo abilitare le opzioni di sicurezza, agendo sul file di configurazione. Postfix non offre un supporto alla sicurezza in modo diretto, ma utilizza invece i servizi offerti da altri componenti software. In particolare, può utilizzare il componente di autenticazione SASL offerto da Dovecot, che abbiamo già configurato correttamente, e quindi sarà proprio quello che useremo. Seguendo le indicazioni fornite in nella documentazione di Postfix e relativa a SASL e TLS, operiamo come segue: • apriamo con sudo gedit /etc/postfix/main.cf il file di configurazione di Postfix • decommentiamo le linee che indicano il certificato e la chiave privata da usare, abilitano il supporto a TLS, e definiscono la memoria cache smtpd_tls_cert_file=/etc/ssl/certs/ssl-cert-snakeoil.pem smtpd_tls_key_file=/etc/ssl/private/ssl-cert-snakeoil.key smtpd_use_tls=yes smtpd_tls_session_cache_database = btree:${queue_directory}/smtpd_scache smtp_tls_session_cache_database = btree:${queue_directory}/smtp_scache • inseriamo delle linee che abilitano l'uso di SASL, stabiliscono le restrizioni per l'accettazione delle email in uscita, e forzano l'aggiunta di un header che attesta l'avvenuta autenticazione smtpd_sasl_auth_enable = yes smtpd_recipient_restrictions = permit_mynetworks, permit_sasl_authenticated, reject smtpd_sasl_authenticated_header = yes • aggiungiamo le due linee che indicano di usare il supporto SASL offerto da Dovecot, e definiscono il path di comunicazione tra Postfix e Dovecot smtpd_sasl_type = dovecot smtpd_sasl_path = private/auth • e salviamo il file così modificato. Ora, apriamo il file di configurazione di Dovecot con sudo gedit /etc/dovecot/ dovecot.conf e, come indicato nella documentazione di Postfix, aggiungiamo nella sezione auth default {} il nome ed i privilegi per il socket Unix di comunicazione tra Dovecot e Postfix: auth default { socket listen { 311 Sicurezza per l'SMTP Posta elettronica: SMTP, IMAP, e autenticazione client { path = /var/spool/postfix/private/auth mode = 0660 user = postfix group = postfix } } } A questo punto, possiamo far ripartire entrambi i server con i comandi sudo /etc/init.d/postfix restart sudo /etc/init.d/dovecot restart e provare a spedire email con Evolution. Innanzitutto, chiediamo (Edit/Preferences, selezioniamo il profilo, Edit, Sending Email) di usare una connessione TLS. Scriviamo un messaggio in uscita, e verifichiamo che il capture prodotto con Wireshark sia simile a questo 220 alef-laptop ESMTP Postfix (Ubuntu) EHLO [127.0.0.1] 250-alef-laptop 250-PIPELINING 250-SIZE 10240000 250-VRFY 250-ETRN 250-STARTTLS 250-AUTH PLAIN CRAM-MD5 250-ENHANCEDSTATUSCODES 250-8BITMIME 250 DSN STARTTLS 220 2.0.0 Ready to start TLS ....]...Y...%U.o.`yy.........4.Z..r....q..# ...\W|(.x...D.>...M..j3. K...b......... dove appunto, notiamo la presenza della estensione AUTH PLAIN CRAM-MD5, identica a come l'avevamo configurata per Dovecot, mentre a seguito della negoziazione TLS, il contenuto diventa indecifrabile. Quindi, riconfiguriamo nuovamente Evolution, rimuovendo la richiesta del TLS, ed inserendo invece quella per l'autenticazione CRAM-MD5. Sniffando di nuovo, notiamo che il messaggio ora viaggia in chiaro, ma prima della sua accettazione, il client si è autenticato: ... 250 DSN AUTH CRAM-MD5 334 PDE3MjU2NDQxMDYyNDYyNjkuMTE3OTI2ODYyNEBhbGVmLWxhcHRvcD4= YWxlZiAyMmQzMjI0MmY4Mjg1MGViZTk5YWNmZDIxMDNlMjc1Nw== 235 2.0.0 Authentication successful MAIL FROM:<[email protected]> 250 2.1.0 Ok ... Inoltre, se andiamo ad aprire gli header del messaggio che è arrivato a destinazione remota, possiamo trovare la traccia della avvenuta autenticazione, nel primo Received: Received: from [127.0.0.1] (alef-laptop [127.0.0.1]) (Authenticated sender: alef) by alef-laptop (Postfix) with ESMTP id CC520186128 for <[email protected]>; Wed, 16 May 2007 00:36:10 +0200 (CEST) 312 Lo strato applicativo di Internet Alessandro Falaschi Pertanto, possiamo concludere che, abilitando l'autenticazione SMTP, siamo riusciti a conseguire i punti 1) e 3) dei requisiti di sicurezza per la posta elettronica, mentre per il 2), questo non può essere garantito, in quanto sarebbe necessaria l'esistenza di un rapprto di fiducia tra tutte le coppie di server SMTP di trandito possibili. Infine, se riconfiguriamo ancora una volta Evolution, per utilizzare sia l'autenticazione CRAMMD5 che il TLS, in ricezione possiamo ancora osservare l'header che dichiara l'avvenuta autenticazione, mentre osservando il capture, verifichiamo che questa ha luogo dopo che si è attivato il TLS, e questo è il motivo per cui con il TLS, l'autenticazione può anche essere basata semplicemente su plaintext. Risoluzione problemi Nel corso della esercitazione, le cose possono non andare così lisce come previsto, ma spesso è sufficiente tenere d'occhio i file /var/log/messages e /var/log/mail.log (o .warn, o .err) per scoprire la natura del problema. Tenerli d'occhio vuol dire leggerli mediante il comando less (per andare ad inizio/fine file, si usino i simboli < e >), oppure (meglio ancora) in una altra finestra terminale, si inserisca il comando tail -f /var/log/mail.log che visualizzerà le righe del file, men mano che queste vengono aggiunte dal demone SMTP. Realizzato con da Alessandro Falaschi - 313 Risoluzione problemi Posta elettronica: SMTP, IMAP, e autenticazione 314 Lo strato applicativo di Internet Utilizzo dei Meccanismi di Sicurezza In questa sezione svolgiamo alcune esperienze di applicazione degli aspetti di sicurezza alle telecomunicazioni Internet • SSH ◦ ◦ ◦ ◦ Autenticazione del server Confidenzialità Autenticazione del client Grafica remota • TLS • Openssl ◦ Creazione di una Certification Autority ▪ Impostazione di Dovecot all'uso del nuovo certificato ◦ Richiesta di un nuovo certificato firmato ◦ Firma del certificato ◦ Interfacce grafiche ▪ Gestione ed utilizzo dei certificati con TinyCA • Virtual Private Network - VPN • Gnu Privacy Guard ◦ Interfaccia testuale ◦ Seahorse ◦ Keyserver SSH Fin dagli albori di Internet, si è potuti entrare su di un altro computer, usando il comando Telnet per collegare standard input ed ouput locali, ad una sessione terminale remota. Questa modalità operativa però, soffre di alcune ingenuità legate al fatto che ai tempi in cui fu definito Telnet, Internet era confinata al mondo accademico e della ricerca, per cui • nella la fase di autenticazione iniziale la password viene trasmessa in chiaro • non è previsto un modo per crittografare la comunicazione A partire dal 1995, si è lavorato ad un diverso metodo di connessione remota, crittograficamente sicuro, ed il risultato, noto come Secure SHell, è stato formalizzato in una serie di documenti emessi dal gruppo di lavoro SECSH di IETF, brevissimamente riassunti qui. L'implementazione più diffusa sui sistemi Unix, è quella offerta dal progetto OpenSSH, e prevede l'esecuzione di un applicativo server (sshd) in ascolto in TCP, sulla porta ben nota 22, e di un programma client (ssh), da invocare da parte di chi vuol connettersi. Per funzionare, non pretende l'esistenza di una PKI, ma si basa sulla autenticazione a chiave pubblica delle due parti in comunicazione, e sulla generazione estemporanea di chiavi simmetriche di sessione. SSH Utilizzo dei Meccanismi di Sicurezza In effetti, lo scopo di ssh non si limita al solo accesso ad una shell remota, ed una delle sue caratteristiche più interessanti, consiste nella possibilità di realizzare dei tunnel sicuri tra computer, ridirezionando servizi basati su socket, da uno all'altro. Autenticazione del server Questo aspetto è per così dire simmetrico a quello della autenticazione del client: se infatti il client fornisse le sue credenziali, anziché al server a cui si rivolge, ad un man in the middle impostore, oltre che non riceverere il servizio richiesto, il client avrebbe anche svelato la sua identità, e la sua password. Per questo, occorre stabilire un modo per permettere al server di dimostrare la sua identità. Quando ci si collega in ssh ad un computer remoto per la prima volta, ci viene chiesto se accettare o meno la chiave pubblica del server, identificata da una fingerprint. In linea di principio, si dovrebbe contattare in modo sicuro (ad es, per telefono) l'amministratore del server e leggergli la fingerprint, chiedendogli se questa corrisponde effettivamente a quella della chiave pubblica del suo computer. alef@alef-laptop:~$ ssh 151.100.122.171 The authenticity of host '151.100.122.171 (151.100.122.171)' can't be established. RSA key fingerprint is 5a:f9:f2:33:68:d7:6f:45:ec:b4:35:d0:cf:00:e3:b9. Are you sure you want to continue connecting (yes/no)? yes Warning: Permanently added '151.100.122.171' (RSA) to the list of known hosts. [email protected]'s password: ******* [alef@labtel alef]$ Nel caso in cui si decida di fidarci della chiave pubblica, questa viene memorizzata, assieme al nome del computer, nel file ~/.ssh/known_hosts. Le volte successive che ci colleghiamo, se il server continua a fornire la stessa chiave pubblica, e che ora corrisponde alla versione memorizzata dal lato client, non ci verrà più chiesto se accettare o meno l'autenticità della parte remota. Viceversa, se la chiave pubblica del server fosse cambiata, ci verrà impedito di collegarci, nel dubbio che qualcun altro stia impersonando il server remoto. Se siamo sicuri che il server sia proprio lui, e che la nuova chiave sia anch'essa autentica, allora la cosa più semplice e rapida da fare per poter entrare, sarà quella di cancellare il file ~/.ssh/known_hosts. Infine, indichiamo come fare, dal lato server, per conoscere la fingerprint della propria chiave pubblica, in modo da poterla confrontare con quella dichiarata (al lato client) al momento del collegamento. La chiave pubblica (per il cifrario RSA dichiarato nella fase iniziale), è memorizzata nel file ssh_host_rsa_key.pub presente nella directory /etc/ ssh (del server), ed il comando per generare la relativa fingerprint è ssh-keygen -lf /etc/ssh/ssh_host_rsa_key.pub che, dopo aver indicato la lunghezza in bit della chiave, ne calcola appunto la fingerprint. Confidenzialità Come risulta da questo file dicattura, prima ancora che venga svolto il dialogo relativo alla chiave del server, il protocollo ha già provveduto ad instaurare una connessione sicura, mediante uno scambio di Diffie-Hellman. Osserviamo che il ritardo di 8 secondi, che 316 Lo strato applicativo di Internet Alessandro Falaschi intercorre tra il pacchetto 17 ed il pacchetto 18, corrisponde al tempo impiegato per rispondere alla domanda se continuare la connessione o meno. Quindi, tutto quel che segue risulta protetto da una chiave di sessione simmetrica. Autenticazione del client Avendo conseguito la confidenzialità della comunicazione, la trasmissione della password dell'utente verso il sistema remoto viene effettuata in forma crittografata dalla strato di sicurezza di SSH. Alternativamente, la richiesta della password può essere evitata, qualora la chiave pubblica del client venga memorizzata presso il server, in modo che quest'ultimo possa verificare autonomamente l'identità del client. Grafica remota Una opzione molto simpatica di ssh, è quella (opzione -Y) che consente di visualizzare sul proprio computer, la finestra di esecuzione di una applicazione lanciata sul computer su cui ci si è collegati. Inoltre, si può specificare (opzione -l) di presentarsi sul computer remoto, con una UserID differente da quella impersonata sul proprio computer. Queste due opzioni, combinate assieme, producono il comando generico ssh -Y -l utenteremoto computerremoto che ci permette appunto, di eseguire programmi presso computerremoto, con l'identità di utenteremoto, e di visualizzare la relativa finestra sul nostro schermo. La cosa ancora più divertente, è che in questo caso funziona anche il copia e incolla, tra le finestre del computer remoto, e quelle del computer locale, dato che entrambe sono visualizzate dallo stesso desktop. TLS Come illustrato a lezione, il TLS è uno strato frapposto tra l'applicazione ed il TCP, che permette di autenticare l'altra parte mediante certificati X.509 e relativa fingerprint, e di negoziare dinamicamente una chiave di sessione a crittografia simmetrica, in modo da garantire la confidenzialità della comunicazione. Riprendendo l'esempio svolto in merito alla attivazione del TLS nel contesto di una connessione IMAP, proviamo ad analizzare più in dettaglio il risultato del captureottenuto. Osserviamo che dopo che nei pacchetti 9-10 il client ed il server IMAP si sono accordati per utilizzare il TLS, il capture non è più in grado di decodificare correttamente quanto accade. E' però possibile istruire Wireshark, affinché tenti di interpretare i pacchetti come la fase iniziale di una comunicazione TLS. Per ottenere questo, occorre posizionarsi con il mouse su di un pacchetto TLS, cliccare con il tasto destro, e scegliere l'opzione decode as; quindi, specificare che le due porte utilizzate agli estremi della comunicazione (in questo caso, la 143 che è la porta ben nota di IMAP, e 45238, che è una porta effimera) identificano pacchetti SSL (il predecessore del TLS). 317 Openssl Utilizzo dei Meccanismi di Sicurezza A questo punto, l'aspetto del capture cambia completamente, e si possono apprezzare i passaggi previsti dall'handshake protocol. Inoltre, possiamo notare ad esempio come, nel pacchetto TCP 12, trovano posto 4 diverse PDU del TLS. No. No. Time Source Destination Protocol 11 0.015362 192.168.0.213 192.168.0.152 SSLv2 SSLv2 Record Layer: Client Hello No. 12 0.031612 192.168.0.152 192.168.0.213 TLSv1 Certificate, Server Key Exchange, Server Hello Done TLSv1 Record Layer: Handshake Protocol: Server Hello TLSv1 Record Layer: Handshake Protocol: Certificate TLSv1 Record Layer: Handshake Protocol: Server Key Exchange TLSv1 Record Layer: Handshake Protocol: Server Hello Done No. 14 4.695011 192.168.0.213 192.168.0.152 TLSv1 Change Cipher Spec, Encrypted Handshake Message TLSv1 Record Layer: Handshake Protocol: Client Key Exchange TLSv1 Record Layer: Change Cipher Spec Protocol: Change Cipher Spec TLSv1 Record Layer: Handshake Protocol: Encrypted Handshake Message No. 15 4.728238 192.168.0.152 192.168.0.213 TLSv1 Encrypted Handshake Message TLSv1 Record Layer: Change Cipher Spec Protocol: Change Cipher Spec TLSv1 Record Layer: Handshake Protocol: Encrypted Handshake Message Info Client Hello Server Hello, Client Key Exchange, Change Cipher Spec, Ulteriori dettagli possono essere apprezzati, espandendo ulteriormente i contenuti delle PDU TLS. Openssl Si tratta di un toolkit OpenSource, cresciuto sulle fondamenta gettate da un altro, che oltre ad implementare gli strati di sicurezza SSL e TLS, offre una potente libreria crittografica che implementa praticamente tutti gli algoritmi di cui si può aver bisogno, e che viene usata da un gran numero di applicazioni crittografiche. Inoltre, è disponibile il comando openssl, che permette l'esecuzione di quasi tutti i comandi e le operazioni crittografiche, a partire da una finestra terminale. Un progetto del tutto analogo, ma sviluppato in accordo ad una licenza più liberale, è GnuTLS. Dato che il comando openssl, per la ricchezza delle sue possibilità, può richiedere un pò troppa pazienza prima di individuare cosa occorre fare per svolgere delle operazioni ben precise, alcune sue funzionalità posso essere accedute per mezzo di interfacce grafiche e web. In particolare, le operazioni relative alla gestione di una Certification Authority, posso essere svolte per mezzo dello script perl CA.pl, come descritto in questo HowTo, ripreso appresso. 318 Lo strato applicativo di Internet Alessandro Falaschi Creazione di una Certification Autority Lo script CA.pl non si trova nel path, e quindi per generare la nostra CA, eseguiamo labsoftel@alef:~$ /usr/lib/ssl/misc/CA.pl -newca CA certificate filename (or enter to create) Making CA certificate ... Generating a 1024 bit RSA private key ..........++++++ ...++++++ writing new private key to './demoCA/private/cakey.pem' Enter PEM pass phrase: <non la dico a nessuno> Verifying - Enter PEM pass phrase: <non la dico a nessuno> ----You are about to be asked to enter information that will be incorporated into your certificate request. What you are about to enter is what is called a Distinguished Name or a DN. There are quite a few fields but you can leave some blank For some fields there will be a default value, If you enter '.', the field will be left blank. ----Country Name (2 letter code) [AU]:IT State or Province Name (full name) [Some-State]:Italy Locality Name (eg, city) []:Cisterna di Latina Organization Name (eg, company) [Internet Widgits Pty Ltd]:La Sapienza - sede di Latina Organizational Unit Name (eg, section) []:Palazzo Caetani Common Name (eg, YOUR name) []:labsoftel Email Address []:[email protected] Please enter the following 'extra' attributes to be sent with your certificate request A challenge password []: An optional company name []: Using configuration from /usr/lib/ssl/openssl.cnf Enter pass phrase for ./demoCA/private/cakey.pem: <non la dico a nessuno> Check that the request matches the signature Signature ok Certificate Details: Serial Number: b7:43:24:f0:9e:f4:b4:53 Validity Not Before: Feb 1 12:56:54 2008 GMT Not After : Jan 31 12:56:54 2011 GMT Subject: countryName = IT stateOrProvinceName = Italy organizationName = La Sapienza - sede di Latina organizationalUnitName = Palazzo Caetani commonName = labsoftel emailAddress = [email protected] X509v3 extensions: X509v3 Subject Key Identifier: D9:B5:FA:BE:7B:10:D7:D9:93:8F:B8:13:7D:E6:82:2E:F8:89:3C:A4 X509v3 Authority Key Identifier: keyid:D9:B5:FA:BE:7B:10:D7:D9:93:8F:B8:13:7D:E6:82:2E:F8:89:3C:A4 DirName:/C=IT/ST=Italy/O=La Sapienza - sede di Latina/OU=Palazzo Caetani/ CN=labsoftel/[email protected] serial:B7:43:24:F0:9E:F4:B4:53 X509v3 Basic Constraints: CA:TRUE Certificate is to be certified until Jan 31 12:56:54 2011 GMT (1095 days) Write out database with 1 new entries Data Base Updated e possiamo verificare ora l'esistenza di una directory demoCA nella nostra home, con il frutto del nostro lavoro. Possiamo già ora, provare ad utilizzare questo certificato auto-firmato per Dovecot, al posto di quello di snakeoil. 319 Openssl Utilizzo dei Meccanismi di Sicurezza Impostazione di Dovecot all'uso del nuovo certificato Per questo, copiamo il certificato e la chiave privata nella directory che viene letta da Dovecot, con sudo cp demoCA/cacert.pem /etc/ssl/certs/softelcert.pem sudo cp demoCA/private/cakey.pem /etc/ssl/private/softelkey.pem e modifichiamo /etc/dovecot/dovecot.conf in modo che utilizzi i nuovi files ssl_cert_file = /etc/ssl/certs/softelcert.pem ssl_key_file = /etc/ssl/private/softelkey.pem ssl_key_password = <non la dico a nessuno> Se ora ci ri-colleghiamo con Evolution, vediamo comparire il nuovo certificato. Se controlliamo il file di log, troviamo ~$ tail -f /var/log/mail.log Feb 1 00:51:56 alef dovecot: Dovecot v1.0.5 starting up Feb 1 01:02:02 alef dovecot: imap-login: Login: user=<labsoftel>, method=PLAIN, rip=192.168.0.213, lip=192.168.0.152, TLS Richiesta di un nuovo certificato firmato Ora, passiamo a compilare una Richiesta di Certificato di Utente, con il comando /usr/ lib/ssl/misc/CA.pl -newreq. Essendo un certificato individuale, ognuno potrà inserire i propri dati, ottenendo come risultato, due files: labsoftelf@alef:~$ /usr/lib/ssl/misc/CA.pl -newreq Generating a 1024 bit RSA private key ...++++++ ... ... etc etc etc... ... Request is in newreq.pem, private key is in newkey.pem che risiedono entrambi nella nostra home directory. Firma del certificato Possiamo scegliere di far emettere il certificato per il quale abbiamo generato la richiesta, alla nostra stessa CA, oppure di farlo firmare da una stessa CA centralizzata, che firmerà anche quelli dei nostri colleghi, e che provvederà a distribuire la propria chiave pubblica in modo fidato. Ad ogni modo, il comando per firmare una richiesta di certificato da parte di una CA, è ~$ /usr/lib/ssl/misc/CA.pl -sign # usa come input, il file newreq.pem nella dir corrente Using configuration from /usr/lib/ssl/openssl.cnf 320 Lo strato applicativo di Internet Alessandro Falaschi Enter pass phrase for ./demoCA/private/cakey.pem: <la passphrase usata alla creazione della CA> Check that the request matches the signature Signature ok Certificate Details: ... ... etc etc etc... ... Certificate is to be certified until Jan 31 00:13:43 2009 GMT (365 days) Sign the certificate? [y/n]:y 1 out of 1 certificate requests certified, commit? [y/n]y Write out database with 1 new entries Data Base Updated Signed certificate is in newcert.pem Come affermato, il certificato si trova in ~/newcert.pem, dove troviamo anche la sua corrispettiva chiave privata newkey.pem (se abbiamo usato la nostra stessa CA). Inoltre, il nuovo certificato si trova anche nella directory ~/demoCA/newcerts, dove è conservato assieme agli altri che verranno emessi, ed a quello emesso per la CA stessa; i certificati, sono contrassegnati da un numero seriale crescente, e che corrisponde a quello presente all'interno del certificato stesso. La chiave privata invece, si trova ancora in ~/newkey.pem, dove è stata generata dal comando precedente. Interfacce grafiche Anziché eseguire dei comandi da terminale, le operazioni associate ad una CA possono esere svolte utilizzando degli strumenti grafici, come ad esempio quelli di TinyCA, e gnoMint. Inoltre, le operazioni di richiesta e di ritiro di un nuovo certificato, possono anche essere svolte interagendo con una CA remota, come nel caso dell'utilizzo di una interfaccia web, ad esempio quella offerta da PyCA e OpenCA. Gestione ed utilizzo dei certificati con TinyCA Allo scopo semplificare il procedimento rispetto a quello necessario utilizzando OpenSSL, installiamo TinyCA mediante il comando sudo apt-get install tinyca e lanciamolo invocando tinyca2. A questo punto, mediante l'interfacia grafica, è semplice • generare una nuova CA - che potremmo chiamare nostronomeCA ◦ ricordiamoci la password della CA, necessaria a crittare/decrittare la chiave privata della nostra CA! • creare un nuovo certificato che useremo per il nostro server IMAP Dovecot, usando come Distingueshed Name il nome a dominio di secondo livello nel nostro computer, ossia nostronome.labint. Anche stavolta, ricordiamoci la password necessaria per accedere alla chiave privata di questo nuovo certificato! • notiamo come usando TinyCA, la richiesta di un nuovo certificato sottintende i passi di ◦ ◦ ◦ ◦ generazione chiavi compilazione della richiesta accettazione della stessa firma ed emissione del certificato da parte della CA 321 Virtual Private Network - VPN Utilizzo dei Meccanismi di Sicurezza • esportiamo il certificato creato per ultimo, in formato PEM, includendo nello stesso file anche la chiave privata • impostiamo Dovecot per utilizzare il nuovo certificato, copiando il file .pem dalla nostra home directory in /etc/ssl/certs/, e modifichiamo il file di configurazione di Dovecot, come suggerito da queste istruzioni ◦ dato che certificato e chiave sono nello stesso file, usare quello stesso nome in tutti e due i casi ◦ ricordiamoci di decommentare anche la linea in cui va inserita la password necessaria ad usare la chiave privata • mantenendo attivo Wireshark sulla porta 143, lanciare Evolution impostandolo in modo che l'accesso al server IMAP sul proprio computer (127.0.0.1) sia reso sicuro mediante TLS • verificare nella richiesta di consenso proposta da wireshark, così come nel capture, che sia stato effettivamente usato il certificato da noi fornito per ultimo Virtual Private Network - VPN Come abbiamo visto, i servizi di crittografia IPSEC che operano allo strato di rete possono essere utilizzati per realizzare una VPN. Una guida alla configurazione della VPN per un sistema Ubuntu è fornita presso il sito della documentazione prodotta dagli utenti. Gnu Privacy Guard La GNU Privacy Guard, è una implementazione Open dello standard OpenPGP descritto nella RFC 4880, e che offre servizi di crittografia per la trasmissione sicura delle email, in grado di ottemperare ai requisiti 5), 6) e 7) elencati nella lista descritta precedentemente. Di base, offre uno strumento usabile da linea di comando, ma presso il sito di GPG, sono documentate le estensioni disponibili, sia per la sua gestione grafica, che per la sua integrazione con altre applicazioni, come i client di email. Interfaccia testuale Il comando da inserire da tastiera per eseguire tutte le operazioni possibili per OpenPGP è gpg, ed anche se la relativa pagina di manuale descrive una quantità di opzioni possibili, le operazioni più comuni (gestione del portachiavi, firma digitale, cifratura) sono molto ben descritte dalla documentazione. Seahorse Questa è probabilmente la migliore interfaccia grafica di GPG, che permette in modo molto semplice di • • • • • • generare una o più coppie di chiavi da usare con PGP esportare la propria chiave pubblica in modo da poterla inviare a terze parti attribuire un grado di fiducia alle chiavi pubbliche ricevute, e firmarle condividere le proprie chiavi sia via rete locale, che per il tramite di un keyserver firmare e cifrare, sia le email che i file, direttamente da Evolution e da Nautilus gestire anche le chiavi utilizzate con SSH 322 Lo strato applicativo di Internet Alessandro Falaschi • eseguire il back di tutto il portachiavi Dopo l'installazione di Seahorse con Synaptics, l'applicazione risulta accessibile tramite il menù Applicazioni, sotto Accessori/Password e chiavi di cifratura. Nella mia sperimentazione, ho notato una forma di instabilità in corrispondenza della fase di sincronizzazione delle chiavi con il keyserver, dunque consiglio di selezionare subito Modifica/Preferenze, poi la linguetta server di chiavi, e quindi de-selezionare le voci Recuperare e Sincronizzare. Possiamo quindi iniziare con il generare un nuova coppia di chiavi PGP, da associare al nostro nome e cognome, ed alla nostra email. L'email indicata, dovrà essere esattamente quella che useremo poi nel seguito, idonea anche alla ricezione; scegliamo allora quella nella forma [email protected], che funziona all'interno del laboratorio: altre email, associate ad altre chiavi, potranno essere definite in un secondo momento. Ci viene quindi chiesto di inserire una passphrase, con la quale verrà crittografata la nostra chiave privata, e che ci sarà chiesta di nuovo, ogni volta che quest'ultima dovrà essere usata - a meno di non chiedere, che venga memorizzata per la durata della sessione. La chiave così generata, si ritrova ora come chiave privata, e mediante il tasto destro del mouse, possiamo investigarne le proprietà, ed esportare la parte pubblica in un file, che potremo inviare in allegato. Infatti, aprendo ora Evolution, ed scegliendo l'account che usa il nostro stesso computer come SMTP ed IMAP, scriviamo delle email ai nostri colleghi, e prima di spedirle, stabiliamo di firmarla, (dalla voce Sicurezza/firma PGP), ed alleghiamo il file in cui abbiamo salvato la chiave pubblica. Alla ricezione di queste email, notiamo in basso la dicitura La firma esiste, ma è necessaria la chiave pubblica, . Clicchiamo allora (con il tasto destro) sull'allegato contenente la chiave pubblica, e scegliamo Apri in importa chiave, e... la chiave appare, come per magia, in Seahorse, tra le chiavi collezionate. A questo punto, se siamo certi della identità del mittente, non ci resta che attribuirgli fiducia (tasto desto, Proprietà, Fiducia), e quindi firmare la chiave, indicando il nostro grado di certezza sulla identità, ed inserendo la passphrase per poter apporre la firma. Potremo quindi verificare che ora il messaggio ricevuto appare come correttamente firmato. Dopo aver ricevuto le chiavi private dei nostri colleghi, potremo quindi iniziare a crittografare i messaggi indirizzati loro, utilizzando la chiave pubblica del destinatario. Proviamo quindi ad inoltrare come allegato una email crittografata e diretta a noi, verso una terza persona, e verifichiamo come quest'ultima, non sia assolutamente in grado di leggere il messaggio. Per verificare il risultato delle nostre operazioni, possiamo usare la combinazione di tasti control-U per visionare il sorgente delle email firmata e/o crittografata, osservando come, nel caso di una email firmata, sia presente l'intestazione Content-Type: multipart/signed; micalg=pgp-sha1; protocol="application/pgp-signature"; boundary="=-/uPEKZ6Vb6K2qdfequMJ" e poi, nella parte che contiene la firma, questa sia identificata come Content-Disposition: attachment; filename=pippo.asc Content-Type: application/pgp-keys; name=pippo.asc Content-Transfer-Encoding: base64 mentre invece, un messaggio crittografato è identificato da una serie di direttive del tipo 323 Gnu Privacy Guard Utilizzo dei Meccanismi di Sicurezza Date: Tue, 05 Feb 2008 23:30:13 +0100 Message-Id: <1202250613.7965.0.camel@alef-laptop> Mime-Version: 1.0 Content-Type: multipart/encrypted; protocol="application/pgp-encrypted"; boundary="=P4aCM6gPf7ob7XiDhP3P" --=-P4aCM6gPf7ob7XiDhP3P Content-Type: application/pgp-encrypted Content-Transfer-Encoding: 7bit Version: 1 --=-P4aCM6gPf7ob7XiDhP3P Content-Type: application/octet-stream; name=encrypted.asc Content-Description: Questa =?ISO-8859-1?Q?=E8?= una parte del messaggio cifrata digitalmente Content-Transfer-Encoding: 7bit -----BEGIN PGP MESSAGE----Version: GnuPG v1.4.6 (GNU/Linux) hQIOA4XIA4bLol0LEAf+KfcJ1BnmzHZAjWPtU6hfKTyPVHeQXCG2ZeCfaZtV1vZN 7sr9YajLqbBUke73FO/OmaMUrj+trkZ2xN7BpY7hk11Ew5+O+gEvu4Z9H63KhIRD OD7twpkd7qzwBqUOq6ErivEFDTvhhl7Nct9I..... . . Infine, osserviamo come dal file manager Nautils, cliccando con il tasto destro sopra di un file, ora viene anche offerta la possibilità di cifrare e/o firmare un file. Keyserver Ogni volta che sulla nostra chiave pubblica viene apposta una nuova firma, può essere utile distribure la nuova versione mediante un keyserver. Qui di seguito indichiamo alcune risorse relativa alla questione • • • • The WWW Based PGP 5.0 Key Server System PGP Global Directory PGP KeyServer HowTo (in italiano) Come usare PGP: i Keyserver Realizzato con da Alessandro Falaschi - 324 Lo strato applicativo di Internet Alessandro Falaschi 325 Lo strato applicativo di Internet HTML e CSS • Esempi già fatti • Le estensioni dal web developer ◦ Aardvark ◦ Firebug • Kompozer • Modifica di una pagina esistente • Salvataggio sul server web e visualizzazione ◦ Collegamento in FTP ◦ Visualizzazione Esempi già fatti La cosa più semplice che possiamo fare per sperimentare la sintassi dell'HTML e dei fogli di stile, è di partire da una pagina già pronta, come quelle che troviamo presso uno dei siti di template free. Prendiamo, ad esempio, metamorph_sunset, quasi completamente privo di tabelle; scarichiamolo, scomprimiamolo, e salviamolo in una directory locale, come ad esempio questa. Possiamo individuare una directory contenente le immagini utilizzate nella pagina, e possiamo visualizzare direttamente il foglio di stile CSS che la pagina stessa utilizza. Le estensioni dal web developer Una delle cose più antipatiche, nella redazione di pagine web che usano i CSS, specialmente di quelle prodotte da altri, è l'individuazione dei selettori, classi, ed identificatori CSS, le cui regole determinano l'aspetto finale dei componenti di quell'elemento. Una delle cose più simpatiche, è che questo problema può essere felicemente risolto, facendo uso di strumenti appositamete sviluppati. Innanzitutto, proviamo ad installare alcune estensioni per il browser Firefox, tali da rendere più facile il mestiere del web developer: tra queste, possono tornare molto utili Aardvark, Firebug, e WebDeveloper. Una volta installati, questi producono la comparse di due nuove voci nel menù Strumenti, così come pure nel menù che si ottiene cliccando con il tasto destro all'interno della pagina. Aardvark Il suo funzionamento è molto semplice, ancorché geniale: si attiva dal menù strumenti, oppure con il tasto destro, e passando con il mouse sopra gli elementi che compaiono nella pagina, un rettangolo rosso evidenzia l'ambito grafico su cui si estendono gli elementi HTML, ne viene visualizzato il nome, e se presenti, anche i selettori CSS in quel momento attivi per quell'elemento. Premendo il tasto h (help), compare un menù che ci ricorda le altre funzioni ottenibili mediante la pressione di altri tasti. Possiamo a questo punto, modificare Lo strato applicativo di Internet Alessandro Falaschi decisamente l'aspetto della pagina, e poi.... richiedendo di ricaricarla, torna tutto come prima. Firebug Nel caso in cui vogliamo modificare l'aspetto di una pagina, ma non sappiamo dove mettere le mani, possiamo posizionarci in quel punto con il mouse, cliccare con il tasto desto, e selezionare inspect element: nella parte inferiore della finestra di Firefox, ci compare (a sinistra) il codice HTML associato, e (a destra) le regole CSS che hanno effetto su quell'elemento, mettendo anche in evidenza quelle regole che non sono applicate, perché rimpiazzate da altre più specifiche. A destra, inoltre, possiamo apprezzare il layout risultante dalla impostazione dei valori per i margini. Ma la cosa più divertente.. è che possiamo modificare la nostra pagine, sia per quanto riguarda l'HTML, che il CSS!! Così, una volta individuati i valori che producono l'effetto desiderato, possiamo riportarli nei files effettivamente presenti su disco, ed ottenere la nostra pagina, così come desideriamo che appaia. Kompozer Mentre le due estensioni ora descritte, ci aiutano nella analisi (ed eventuale modifica) di pagine preesistenti, un editor come Kompozer ci aiuta nella creazione di pagine ex-novo. A causa di modifiche alla libreria GTK, la versione di Kompozer distribuita con Ubuntu 8.10 e 9.04 è divenuta instabile; presso Kaz'hack può essere trovato il rialscio che funziona nuovamente. Modifica di una pagina esistente A questo punto, partendo dal template che abbiamo scaricato, proviamo ad apportare alcune modifiche, come ad esempio: 1. 2. 3. 4. sostituire al link in alto friends, il link a questa pagina del corso sostituire al testo Design by Metamorphosis Design, il vostro nome e cognome allargare la pagina, e la colonna di destra sostituire alla immagine della testata, una diversa immagine (della stessa altezza, ma con la nuova larghezza!) ottenuta catturando una altra immagine di vostro gradimento, eventualmente ritagliandola con the gimp 5. aumentare la dimensioni dei titoli (come Welcome To Our Website e METAMORPHOSIS DESIGN) Ma procediamo con ordine. 1. Aprendo il file index.html (che viene visualizzato di default, se ci limitiamo a referenziare la directory in cui si trova) con kompozer, possiamo aprire una finestra di dialogo con un doppio click sopra al link "friends", ed immettere, al posto del simbolo #, l'indirizzo di questa pagina, http://infocom.uniroma1.it/alef/ cisterna/esercitazioni/html.html. 2. ancora utilizzando kompozer, possiamo scrivere direttamente il nostro nome e cognome nel posto indicato, ma... facendolo, ci accorgiamo che le lettere compaiono tutte minuscole. Come mai? • visualizzando la pagina con Firefox, posizioniamoci sopra il nostro nome che abbiamo appena scritto, e dopo aver cliccato con il tasto destro, scegliamo Inspect element, 327 Modifica di una pagina esistente HTML e CSS attivando così Firebug, che nella parte inferiore della finestra, ci fa vedere il codice HTML corrispondente, e le regole CSS che vi si applicano. • notiamo allora che alla riga 101 di styles.css, è presente un selettore #logo a, che sia applica a quegli elementi HTML con tag <a>, che compaiono all'interno di un altro elemento, per il quale è stato definito l'attributo id=logo. E infatti, id=logo è proprio l'identificatore del tag div, che influisce su tutta la zona in cui compare l'immagine del tramonto. Nella colonna di destra, tra le regole del selettore #logo a, troviamo la regola text-transform:lowercase; che è appunto quella che determina la scomparsa delle maiuscole. • per allargare la pagina, usiamo ancora Firebug, e cliccando su Inspect, ci parte bianca in basso a sinistra, in modo da far comparire un rettangolo tutta la pagina, e clicchiamo. Dalle finestre inferiori, osserviamo l'elemento <div id=content>, che appunto ha influenza sull'intera selettore CSS #content attiva, tra le altre, la regola width:786px; posizioniamo sulla blu che racchiude di aver inviduato pagina, ed il cui • modifichiamo allora questo valore in kompozer, portandolo ad esempio, a 900px; per fare questo: ◦ ◦ ◦ ◦ apriamo la finestra dell'editor CSS, ad es con il tasto F11, individuiamo il selettore #content, modifichiamo il valore width sotto la linguetta box; chiudiamo con ok, ed ammiriamo il risultato. • tocca ora alla larghezza della colonna di destra: usando ancora la funzione inspect di firebug, troviamo che la colonna è controllata dal selettore #right, che contiene le regole float:right; e width:500px;, mentre la prima regola indica di allineare la colonna a destra, la seconda è quella da modificare; dato che il precedente incremento di larghezza è stato di 900-786=114 pixel, possiamo portare width a 614px; per fare questo: ◦ ◦ ◦ ◦ utilizziamo ancora l'editor CSS di kompozer, individuiamo il selettore #right, modifichiamo il valore width sotto la linguetta box; chiudiamo con ok, ed ammiriamo il risultato. • rimane da allargare anche la prima riga della pagina, quella con i link in alto, ma come fare questo, lo trovate da soli... basta ricordare che la nuova larghezza, dovrà ancora incrementarsi di 114 pixel, ovvero arrivare a 892 pixel. • la dimensione originale dell'immagine può essere rilevata • usando ancora Firebug, e cliccando su Inspect, le due finestre inferiori ci mostrano che questa è contenuta nel tag <div id=logo>, a cui corrisponde il selettore #logo, che come prima regola asserisce background:#FFFFFF url(images/ big_pic.jpg) no-repeat scroll 0%; mentre rimandiamo altrove per una descrizione dell'insieme dei valori associabili alla proprietà background, possiamo notare che passando con il mouse sopra la linea del css, ci viene mostrata una versione ridotta dell'immagine, assieme alla sua dimensione in pixel: 778x226. Pertanto, occorre ora produrre una immagine di 892x226 pixel. ◦ possiamo sceglierne una, ad esempio, da flickr: ad esempio scegliendo questa, nella versione più grande, occorre salvarla su disco, e quindi aprirla con Gimp; ◦ ridimensiono l'immagine alla larghezza desiderata (tasto destro, immagine, scala); ◦ usando lo strumento di selezione rettangolare, ritaglio l'altezza alla dimensione desiderata; 328 Lo strato applicativo di Internet Alessandro Falaschi ◦ eseguo modifica/copia, e quindi modifica/incolla come/nuova immagine; ◦ salvo il risultato nella directory delle immagini, con lo stesso nome della precedente; ◦ nell'editor css di Komposer, modifico la proprietà width del selettore #logo, al nuovo valore di 892; ◦ mi godo il risultato.. uhmm c'è un rettangolo bianco a destra.. si aggiusta, portando a 4 il valore della proprietà padding-left: del selettore #logo.... però ancora non va, così si è spiaccicato a sinistra il logo... si corregge aggiungendo la proprietà padding-left = 40px; nel selettore #logo a • per ingrandire Welcome To Our Website, ci accorgiamo che su di esso ha effetto il selettore #right h2, e quindi aggiungiamo a questo la regola font-size:xx-large; • per ingrandire METAMORPHOSIS DESIGN, possiamo intervenire sul selettore #right h4, impostandolo come font-size:large; L'effetto finale, è quello visibile qui. Salvataggio sul server web e visualizzazione Ora che abbiamo presisposto la nostra bella pagina, preoccupiamoci di salvarla presso il nostro server web. Per questo scopo, useremo il computer ubuntu22.labint, predisposto sia come server web che come server ftp. Infatti, sono stati creati diversi utenti, con nome pari ai nomi dati ai computer del laboratorio. A tutti questi utenti, è stata assegnata la stessa password. Il server ftp è configurato in modo tale che quando gli utenti si connettono, possono accedere alla sola porzione di filesystem radicata a partire dalla propria home directory. Collegamento in FTP A questo scopo, abbiamo due possibilità: • usiamo il programma gftp, ◦ se non lo abbiamo già installato, possiamo scaricarlo con synaptic; ◦ quindi, lo lanciamo da Applicazioni/Internet; ◦ poi, inseriamo nel campo host il nome ubuntu22.labint, nel campo utente il nostro nome, e per password, sempre la stessa; ◦ se tutto è andato bene, dovremmo trovarci nella directory remota /home/ nomeutente del computer remoto; ◦ per copiare i files da un computer all'altro, si fa uso delle freccette poste tra le due colonne della finestra. • usiamo il client ftp integrato in Nautilus: ◦ lanciamo Risorse/Connetti al server; ◦ inseriamo come Tipo di servizio: FTP con login, nome del server, e nome utente (il vostro), connetti; ◦ il nome del computer ci appare sotto il menù Risorse; cliccandolo ci viene chiesta la password, e quindi possiamo entrare ◦ se apriamo un altro Nautilus, possiamo copiare i files dal nostro a quello 329 Salvataggio sul server web e visualizzazione HTML e CSS Visualizzazione Affinché le pagine che carichiamo sul server, siano poi visibili, dobbiamo metterle in una directory con un nome particolare, public_html, che creiamo (visto che da sé, non esiste). I files che carichiamo lì, sono visibili a partire dall'indirizzo http://ubuntu22.labintl/ ~nomeutente (suggerimento: il carattere ~ si ottiene, sulla tastiera Linux, con la combinazione di tasti AltGr e ì). Realizzato con da Alessandro Falaschi - 330 Lo strato applicativo di Internet Server Apache Utilizzeremo il server HTTP Apache, il più diffuso in assoluto. • Apache, questo sconosciuto • Installazione ◦ Abilitazione dei componenti ▪ Moduli ▪ Siti • La mia prima pagina ◦ Studiamo il log ▪ I permessi dei files da mostrare ▪ Personalizzazione del Log ◦ Il sito principale ▪ Virtual Host • La documentazione ◦ I file di configurazione ◦ Informazioni sul server ◦ Mappare le URI sui files • Autenticazione ◦ Digest Authentication ◦ Fornitori ◦ Oscuramento • Gestione dei Mime Type • Il mio primo CGI ◦ Metodo POST ◦ Interpretazione della richiesta • Un ristorante virtuale • Tutto il codice prodotto • HTTPS e VirtualHost ◦ Distribuzione del certificato della CA • Riferimenti Apache, questo sconosciuto Si narra che il nome Apache derivi da una contrazione di a patchy server, dato che inizialmente, non era costituito da nient'altro che una serie di patch (pezze) al preesistente Installazione Server Apache server di NCSA. Il suo funzionamento prevede il pre-fork di alcuni processi figli, in modo che le nuove richieste HTTP non vengano rallentate nell'attesa della creazione di un nuovo processo figlio. Oppure, si può ricorrere ad un modulo orientato al multi-threading come worker. In ogni modo, l'architettura di Apache prevede di delegare molti tipi di elaborazioni possibili, ad un insieme di diversi moduli che implementano ognuno una diversa funzionalità. In questo modo, si rende indipendente lo sviluppo dei moduli, e delle funzionalità associate, da quello del nucleo del server web. Installazione Con Synaptic, scarichiamo ed installiamo apache2, apache2-doc e apache2-utils, Impartendo il comando ps axf | grep apache, verifichiamo se è già in esecuzione, ed in caso contrario, lanciamo il server web con il comando sudo /etc/init.d/apache2 start. Apriamo il Browser web, e visitiamo la URI http://127.0.0.1. Si aprirà una pagina, con una sola frase: It works! a conferma il successo dell'operazione. Abilitazione dei componenti Apache basa la sua configurazione su di un nutrito insieme di files, che troviamo a partire dalla directory /etc/apache2, e di cui il principale è (/etc/apache2/apache2.conf) che, oltre ad impostare alcuni parametri di base, provvede a referenziare gli altri files di configurazione, ognuno dedicato ad un aspetto particolare. 332 Lo strato applicativo di Internet Alessandro Falaschi Moduli Per quanto riguarda l'abilitazione moduli, ognuno di essi può richiedere la definizione di particolari variabili e direttive, che sono dichiarate in files separati, e tenuti assieme mediante direttive di Include, e links simbolici presenti nelle directory. Infatti, in /etc/ apache2/apache2.conf, sono presenti le direttive Include /etc/apache2/mods-enabled/*.load Include /etc/apache2/mods-enabled/*.conf che attivano il caricamento (.load) e la configurazione (.conf) dei moduli associati a tutti i files presenti nella directory mods-enabled. Se investighiamo sul contenuto della directory /etc/apache2/mods-enabled però, vi troviamo una serie di collegamenti simbolici a files dallo stesso nome, ma che si trovano in /etc/apache2/mods-available. Per abilitare un modulo quindi, occorre creare il link simbolico dalla seconda directory alla prima, per i due files di configurazione dello stesso. Per semplificare questa operazione, sono disponibili i comandi a2enmod e a2dismod, rispettivamente per abilitare e disabilitare un modulo, come ad esempio sudo a2dismod info sudo a2enmod info # disabilita il modulo info # ri-abilita il modulo info Siti Lo stesso server Apache può ospitare diversi siti virtuali, per ognuno dei quali è possibile avere un diverso file di configurazione, presente presso la directory /etc/apache2/ sites-available. D'altra parte, in fondo al file di configurazione principale /etc/ apache2/apache2.conf troviamo la direttiva Include /etc/apache2/sitesenabled/ che provvede ad attivare solo i siti definiti dai files presenti in questa seconda directory. In modo del tutto analogo a prima, i diversi siti vengono vitalizzati creando un link simbolico all'interno della directory /etc/apache2/sites-enabled verso i loro file di configurazione, mediante il comando sudo a2ensite info. A seguito della istallazione, risulta già pre-impostato un unico sito principale. La mia prima pagina Come già fatto in una precedente esercitazione, pubblichiamo le nostre pagine sotto la propria directory di utente. Prima di tutto, dobbiamo abilitare il modulo userdir, che consente appunto l'uso delle directory di utente. Per questo, eseguiamo i comandi sudo a2enmod userdir sudo /etc/init.d/apache2 force-reload Creiamo quindi la directory public_html in /home/labint, scriviamo al suo interno un file di test (es prova.txt) contenente una frase simpatica, e proviamo a visualizzare il file, referenziando la URI http://127.0.0.1/~labint/prova.txt. Come dite? Compare un messaggio Forbidden ?? 333 La mia prima pagina Server Apache Studiamo il log Per verificare l'esito delle operazioni in corso, possiamo andare a leggere le informazioni salvate nei file di log, ossia /var/log/apache2/access.log che documenta gli accessi, e /var/log/apache2/error.log, che documenta le circostanze di errore. Quest'ultimo, contiene l'informazione che cercavamo, ossia il messaggio [Wed May 30 08:32:02 2007] [error] [client 127.0.0.1] (13): file permissions deny server access: /home/labint/public_html/prova.txt, referer: http://127.0.0.1/~labint/ I permessi dei files da mostrare Infatti, il file che abbiamo creato va reso leggibile dall'utente con i cui privilegi è in esecuzione il server web. Per modificare i permessi del file, si può agire mediante una applicazione di file manager grafico, oppure impartire il comando chmod 644 /home/ labint/public_html/prova.txt. Fatto ciò, possiamo riprovare a visualizzare il nostro file ! Tenere d'occhio il log, può essere un buon modo per accellerare lo sviluppo di un sito: a tal fine, può essere molto utile impartire il comando tail -f /var/log/apache2/ error.log, che ha l'effetto di mantenere la schermata agganciata allo stato corrente del file, scrollando quando vi vengono aggiunte nuove linee. Personalizzazione del Log Le informazioni che compaiono nei file di log, come access.log, possono essere personalizzate mediante le direttive LogFormat and CustomLog anche allo scopo di particolarizzare il tipo di report generato dagli appositi programmi di analisi giornaliera. Il sito principale Ma, oltre alle directory degli utenti, come si fa ad usare direttamente il FQDN del sito, con una URL del tipo http://localhost/pagina.html ? Le definizioni legate a questa possibilità, si trovano nel file /etc/apache2/sites-enabled/000-default, in cui si dichiara, tra le altre cose, che la DocumentRoot del web server, corrisponde alla directory /var/www/, e quindi è lì che dobbiamo salvare i files che vogliamo compaiano direttamente dopo il nome del computer. Virtual Host Abbiamo parlato di sito principale, perché presso lo stesso computer, possono essere ospitati siti diversi, corrispondenti a nomi di dominio diversi, ma relativi (mediante dei RR del DNS di tipo CNAME) allo stesso indirizzo IP. Questa tecnica prende il nome di Virtual Host, e si basa sul fatto che il nome a dominio che compare nella URL di richiesta, è comunque indicato nell'header Host della stessa. In virtù di questo, Apache si basa sulle impostazioni contenute nei files di configurazione presenti in /etc/apache2/sites-enabled/, per individuare la configurazione del Virtual Host corrispondente, e mappare la richiesta su diverse parti del filesytem. 334 Lo strato applicativo di Internet Alessandro Falaschi La documentazione Il package di documentazione che abbiamo installato, installa sullo stesso server web le pagine accessibii a partire da http://127.0.0.1/manual/, che sono copie conformi di quanto compare presso il sito della fondazione Apache. Per seguire ciò che ha consentito la comparsa della nostra pagina on-line, possiamo leggere l'How-to corrispondente a Per-user Web Directories (public_html), che ci guida nel primo impatto con la configurazione di Apache. I file di configurazione Il primo impatto con le direttive, i moduli, i contesti, che definiscono il comportamento di apache, può scoraggiare chi è abituato alle interfacce di configurazione grafica: d'altra parte, affrontando un problema alla volta, dopo qualche tempo si apprezza l'elevato grado di configurabilità ottenibile. Dei moduli abbiamo già parlato; per quanto riguarda le direttive (ad es userdir), la documentazione relativa utilizza una terminologia il cui significato è documentato a parte. In particolare, le direttive sono attivabili all'interno di specifici contesti, come ad esempio <Directory> o <Location>, che individuano il loro campo di applicabilità. Per esempio, in userdir.conf troviamo UserDir public_html UserDir disabled root # la dir di utente da rendere accessibile # per l'utente root non è accessibile <Directory /home/*/public_html> AllowOverride FileInfo AuthConfig Limit Options MultiViews Indexes SymLinksIfOwnerMatch IncludesNoExec </Directory> che indica come, a seguito della attivazione del modulo userdir, le direttive AllowOverride etc etc, si applichino a tutte le directory di utente /home/*/ public_html. Questo tipo di meccanismo, permette di limitare l'effetto delle direttive di configurazione, solo a particolari sezioni del sito. Ma oltre ai files presenti in /etc/apache2/mods-enabled, la configurazione complessiva può essere dichiarata anche mediante dei file .htaccess, dislocati nelle stesse directory in cui si trovano i documenti da servire, in modo da permettere agli utenti stessi di impostare le loro preferenze. Informazioni sul server Apache offre due URI mediante le quali ci mostra i dettagli sullo stato delle connessioni che sta servendo, e l'effetto delle diverse direttive di configurazione: • lo stato delle connessioni è visualizzato visitando http://127.0.0.1/server-status, ed il suo funzionamento si abilita, dopo essersi accertati della presenza di /etc/ apache2/mods-enabled/status.conf (in caso contrario, eseguire sudo a2enmod status), e modificando in questo file, la direttiva Allow in (ad es.) Allow from 127.0.0.1; • l'effetto delle direttive di configurazione è visualizzato visitando http://127.0.0.1/ server-info/, ed il suo funzionamento si abilita verificando che il modulo info sia attivo, e modificando come indicato sopra il file info.conf, oppure usando localhost nella URI di richiesta. Notiamo come nella pagina visualizzata, si possano 335 Autenticazione Server Apache verificare tutte le impostazioni di configurazione impartite al server, e si possa anche visualizzare l'auto-documentazione pubblicata da parte dei moduli attivi. Mappare le URI sui files Sicuramente un indirizzo in cui compare una tilde (~) non è dei più belli da dare in giro, e per costruire un indirizzo più leggibile, possiamo ricorrere ad una tra due direttive che ci vengono in soccorso: Alias e Redirect, in cui la prima serve per mettere in corrispondenza la parte path della URI, con un percorso nel filesystem locale, mentre la seconda serve per inviare risposte di tipo 30x Redirect, anche verso siti esterni, in corrispondenza di URI particolari. Mentre l'argomento nel suo complesso, è ben trattato nella documentazione, indichiamo qui la direttiva da inserire nel file nostro alias.conf (sudo gedit /etc/ apache2/mods-enabled/alias.conf) , per far corrispondere all'indirizzo http://127.0.0.1/~labint, un più leggibile http://127.0.0.1/labint: Alias /labint "/home/labint/public_html" e quindi, far rileggere la configurazione ad apache con il comando sudo service apache2 reload. Autenticazione Anche se la nostra prima pagina non è un granché, possiamo comunque proteggerla da sguardi indiscreti. Nella documentazione di Apache troviamo le indicazioni su come procedere, e seguendo quei consigli, possiamo fare così: • creiamo un file di password, contenente la password per l'utente labint, presso la nostra home directory htpasswd -c /home/labint/passwords labint e quando ci viene chiesta, inseriamo la password che vogliamo ci venga richiesta. Proviamo a guardare ora, cosa contiene quel file ? • creiamo nella directory dove si trova la nostra pagina, il file .htaccess che serve ad Apache per capire che la directory è protetta, dove trovare le password, e quali utenti sono ammessi AuthType Basic AuthName "Laboratorio di Cisterna" AuthUserFile /home/labint/passwords Require user labint in cui AuthName definisce il realm che verrà indicato nella finestra di richiesta del browser, la direttiva AuthUserFile definisce il file di password che consente l'autenticazione dell'utente, e Require definisce le restrizioni di accesso (autorizzazione) successive alla fase di autenticazione. • apriamo wireshark, e mentre visitiamo di nuovo la nostra pagina, rispondiamo alla richiesta di password, e osserviamo i pacchetti in transito Notiamo che non è stato necessario ri-lanciare il server apache perché le modifiche avessero effetto: questo vuol dire che i file .htaccess vengono ri-letti ad ogni nuova richiesta, e 336 Lo strato applicativo di Internet Alessandro Falaschi questo può essere un elemento di inefficienza all'aumentare del carico, per cui se si ha la possibilità di intervenire sui files di configurazione di sistema, è preferible modificare quelli. Digest Authentication Nel caso si scelga questo tipo più sicuro di autenticazione, la versione delle password residente presso il server dovà essere generata mediante l'utility htdigest anziché htpasswd, dato il diverso meccanismo utilizzato, ed usata anche la direttiva AuthDigestDomain. Fornitori Nell'esempio svolto, le credenziali degli utenti presso il server sono memorizzate su di un semplice file piatto. All'aumentare delle dimensioni di questo, e della sua frequenza di utilizzo, questo metodo può divenire inefficiente. Esiste allora la possibilità di accedere alle credenziali da verificare mediante un file indicizzato (modulo authn_dbm), un DBMS (modulo authn_dbd), od un server LDAP (modulo authnz_ldap). Oscuramento Può succedere che si voglia interdire l'accesso ad una pagina, od una directory, nei riguardi delle richieste che giungono da un certo computer, o da un gruppo di computer. Le regole generali, prevedono l'uso delle direttive Allow, Deny e Order, ma per una sperimentazione semplice semplice, ci si può limitare ad inserire nel file /home/labint/public_html/ .htaccess la direttiva Deny from All e quindi, provando ad accedere di nuovo alla nostra pagina, possiamo verificare come ora l'accesso sia divenuto proibito. Una volta fatta la prova, commentiamo la direttiva, anteponendo un #. Gestione dei Mime Type Nel file /etc/apache2/mods-enabled/mime.conf, compare la direttiva TypesConfig /etc/mime.types che individua in che file sono dichiarate le corrispondenze tra le estensioni dei nomi di file ed i Mime-Type. Questa corrispondenza sarà quindi usata per determinare il Mime-Type da usare come argomento dell'Header Content-Type nella risposta inviata ad un browser che avesse richiesto un file con una delle estensioni presenti. Nel caso in cui si desideri erogare un contenuto con una estensione che non rientra tra quelle elencate in /etc/mime.types, si possono definire delle ulteriori associazioni, inserendo in mime.conf una o più direttive del tipo AddType MIME-type extension [extension] 337 Il mio primo CGI Server Apache Il mio primo CGI Seguendo (più o meno) i suggerimenti riportati nell'How-To fornito con Apache, per sperimentare l'esecuzione di codice da parte di un server web, possiamo modificare il file di configurazione relativo alle directory degli utenti (sudo gedit /etc/apache2/modsenabled/userdir.conf) in modo che appaia come segue: <IfModule mod_userdir.c> UserDir public_html UserDir disabled root <Directory /home/*/public_html> AllowOverride FileInfo AuthConfig Limit Indexes Options ExecCGI MultiViews Indexes SymLinksIfOwnerMatch IncludesNoExec AddHandler cgi-script .cgi </Directory> </IfModule> In particolare, la direttiva Options ExecCGI è quella che abilita l'esecuzione di codice CGI, mentre AddHandler istruisce Apache a trattare i files con estensione .cgi come programmi eseguibili, eseguirli, e tentare di inviare lo standard output risultante, al browser che aveva referenziato tale programma. Quindi, non tutti i programmi presenti in /home/ labint/public_html saranno eseguiti, ma solo quelli con estensione .cgi. Se nella propria versione del file ci sono cose in più, non importa; verifichiamo invece che non ce ne siano in meno e, se abbiamo apportato modifiche, chiediamo ad Apache di rileggere la configurazione, con il comando sudo service apache2 reload Creiamo ora il nostro CGI (gedit primocgi.cgi) utilizzando il linguaggio Perl, in modo che appaia come segue: #!/usr/bin/perl # # - primocgi - visualizzazione delle variabili di ambiente ricevute # print "Content-type: text/plain\n\n"; print "Salute a tutti.\n\n"; print "State osservano lo standard output prodotto da $0.\n"; print "I files presenti in questa directory sono:\n\n"; system "ls -l"; print "\n o o o o o o o o o o o o o o o o o o o o \n\n"; print "Qui sotto, mostriamo i valori associati alle variabili di ambiente che apache rende accessibili a questo script:\n\n"; foreach $key (keys %ENV) { # scandisce la lista delle chiavi di %ENV print "$key --> $ENV{$key}\n"; # mostra le coppie chiave-valore } e quindi, ricordiamoci di rendere eseguibile il programma, anche da parte di Apache: chmod 755 primocgi.cgi. In alternativa, possiamo direttamente installare presso /home/ labint/public_html questo e gli altri CGI discussi appresso, scomprimendo l'archivio fornito più avanti. Nel caso in cui i CGI sembrano non funzionare, è probabilmente possibile investigare sulla natura del problema, analizzando il contenuto dell'error.log, verso cui viene re-diretto lo standard error del CGI. Ora verifichiamone il corretto funzionamento, referenziando la URI http://127.0.0.1/labint/ primocgi.cgi, oppure invocandolo mediante la form di esempio mostrata a lezione. Eventualmente, qui troviamo un esempio del risultato che si dovrebbe ottenere. Notiamo che: 338 Lo strato applicativo di Internet Alessandro Falaschi • lo script usa il linguaggio perl, e prima di eseguirlo come CGI, è possibile verificarne il funzionamento, tentandone l'esecuzione da linea di comandi, con ./primocgi.cgi; • la specifica di un header Content-Type: text/plain fa si che il browser visualizzi il risultato con un font monospaced; • la presenza di una linea vuota dopo il Content-Type, separa la fine degli header dall'inizio del body, e se è assente, il cgi non funziona (provare per credere); • il comando system permette a perl di eseguire comandi come se fossero immessi dalla shell in cui è eseguito; • mediante il ciclo foreach si scandisce ad un array associativo (%ENV) o hash, in cui si trovano i valori delle variabili di ambiente definite da Apache ed a cui lo script accede per questa via; l'accesso ai valori dell'hash avviene quindi usando i nomi delle variabili stesse, come chiavi. L'iterazione su tutti i nomi si ottiene quindi scandendone la lista, che è generata sul posto mediante l'espressione keys %ENV; • l'elenco della variabili di ambiente a cui è possibile accedere può essere trovato presso il sito delle specifiche originali. Come risultato della esecuzione del CGI, possiamo trovare tutti i nomi ed i valori delle variabili di ambiente, come ad esempio alcuni degli header contenuti nella richiesta HTTP. In particolare, se abbiamo invocato il CGI a partire da una form usando il metodo GET, nella variabile QUERY_STRING possiamo osservare la componente della URI che contiene i nomi ed i valori dei parametri di chiamata, corrispondenti a quelli impostati mediante i controlli della form stessa. Pertanto, primocgi.cgi può essere utilizzato al posto del reale CGI che stiamo progettando, per verificare che almeno il passaggio dei parametri produca l'effetto desiderato. Metodo POST Come illustrato nella parte di teoria, utilizzando il metodo POST, i valori impostati mediante i controlli della form sono passati nel body HTTP, e non nella URI: pertanto la modalità di accesso agli stessi usata da primocgi.cgi non è più idonea. Questo secondo esempio, che chiameremo secondocgi.cgi (ispirato da questo corso), ha appunto lo scopo di mostrare l'effetto di una tale chiamata. #!/usr/bin/perl # # - secondocgi - visualizza il contenuto del body HTTP # use strict; print print print print "Content-type: text/plain\n\n"; "Salute a tutti.\n\n"; "State osservando lo standard output prodotto da $0.\n"; "\n o o o o o o o o o o o o o o o o o o o o \n\n"; print "Qui sotto, lo standard input ricevuto dal server web\n\n"; my $buffer; read (STDIN, $buffer, $ENV{'CONTENT_LENGTH'}); print $buffer; print "\n\ned ora, alcune delle variabili di ambiente ricevute:\n\n"; my $key; foreach $key ('REQUEST_METHOD', 'QUERY_STRING', 'CONTENT_LENGTH', 'CONTENT_TYPE') { print "$key --> $ENV{$key}\n"; } L'istruzione read ha lo scopo di copiare nella variabile-stringa $buffer il contenuto del body della richiesta HTTP, che ha la dimensione specificata dall'header HTTP Contentlenght, il cui nome è copiato da Apache nella omonima variabile di ambiente, che viene 339 Il mio primo CGI Server Apache letta dal CGI accedendo all'hash %ENV. Una form che invoca questo script è visibile nella parte di teoria, mentre qui possiamo osservare il risultato della invocazione. Come osserviamo, ora i parametri impostati mediante i controlli della form sono presentati nel body HTTP. Interpretazione della richiesta A prima vista, quella stringa del tipo nome1=valore1&nome2=valore2& etc etc ... non sembra molto masticabile da un programma, ma non è poi così difficile, cavarne qualcosa di buono, come viene fatto con questo che chiameremo terzocgi.cgi, che funziona sia con il metodo GET che con il POST, e... si limita a farci vedere ciò che sa fare. #!/usr/bin/perl # # - terzocgi - effettua la decodifica dei parametri ricevuti, sia con GET che con POST # use strict; print print print print "Content-type: text/plain\n\n"; "Salute a tutti.\n\n"; "State osservando lo standard output prodotto da $0.\n"; "\n o o o o o o o o o o o o o o o o o o o o \n\n"; print "Qui sotto, lo standard input ricevuto dal server web\n\n"; my $buffer; read (STDIN, $buffer, $ENV{'CONTENT_LENGTH'}); print $buffer; print "\n\nPoi, alcune delle variabili di ambiente ricevute:\n\n"; my $key; foreach $key ('REQUEST_METHOD', 'QUERY_STRING', 'CONTENT_LENGTH', 'CONTENT_TYPE') { print "$key --> $ENV{$key}\n"; } print "\n\nInfine, la decodifica dei campi ricevuti\n\n"; my @data; # suddivide le coppie name/value tra loro if ( $ENV{ 'REQUEST_METHOD' } eq "GET" ) { @data = split /&/, $ENV{ 'QUERY_STRING' }; } elsif ( $ENV{ 'REQUEST_METHOD' } eq "POST" ) { @data = split /&/, $buffer; } else { print "Sono permessi solo i metodi GET e POST!"; exit; } my (%data, $value, $item); foreach $item ( @data ) { # Suddivide le coppie e traduce i + in spazi ($key, $value) = split /=/, $item; $key =~ tr/\+/ /; $value =~ tr/\+/ /; # Converte %XX da numeri hex ad alfanumerico $key =~ s/%([\da-f][\da-f])/pack("c",hex($1))/gei; $value =~ s/%([\da-f][\da-f])/pack("c",hex($1))/gei; # Assegna il valore ottenuto in corrispondenza della chiave relativa $data{$key} = $value; } foreach $key (keys %data) { print "$key ==>> $data{$key}\n"; } Notiamo che le conversioni effettuate sono presenti per de-codificare i parametri che sono 340 Lo strato applicativo di Internet Alessandro Falaschi stati inviati nel body HTTP con un MediaType MIME di tipo application/x-www-formurlencoded, come possiamo verificare sniffando il traffico relativo, od anche aggiungendo al CGI la stampa della variabile di ambiente HTTP_CONTENT_TYPE. La form che invoca terzocgi.cgi è mostrata nella parte di teoria, e qui è mostrato il risultato della invocazione. Un ristorante virtuale A partire dagli esempi forniti fin qui, ed usando il modulo Perl Email::Send, scriviamo un CGI in grado di recapitare per email l'ordine ricevuto. Il sapore non sarà un gran che, però farà un gran bell'effetto :-) Per prima cosa, scarichiamo ed installiamo il modulo con le sue dipendenze mediante il comando sudo cpan Email::Send, che vi chiederà un pò di volte la vostra opinione, ma dovrebbe essere più che sufficiente che voi rispondiate sempre di si, premendo enter. Quindi, inseriamo in una pagina HTML il codice della form (sempre quello proposto a lezione), facendogli però ora invocare (mediante l'attributo action) il nuovo cgi, che chiamiamo vristo.cgi Il mio primo ristorante virtuale email: pizza • lasagna ordine: tiramisu pinta ok reset Infine, editiamo il nostro nuovo cgi, vristo.cgi, a partire dal codice già incontrato in terzocgi, di cui abbiamo corretto un errore nella espressione regolare, ed il cui codice è riportato sotto. Dopo aver salvato vristo.cgi nella directory public_html ed aver correttamente configurato i permessi, possiamo provare ad immettere un ordine nella form soprastante, e verificare se l'email ci arriva! Ad ogni modo, anche stavolta riportiamo l'esito della esecuzione. #!/usr/bin/perl use strict; use Email::Send; # impostare qui, quello che sarà il mittente dell'email my $from = '[email protected]'; print print print print "Content-type: text/plain\n\n"; "Salute a tutti.\n\n"; "State osservando lo standard output prodotto da $0.\n"; "\n o o o o o o o o o o o o o o o o o o o o \n\n"; # Leggiamo lo standard input ricevuto dal server web\n\n"; my $buffer; read (STDIN, $buffer, $ENV{'CONTENT_LENGTH'}); my @data; 341 Tutto il codice prodotto Server Apache if ( $ENV{ 'REQUEST_METHOD' } eq "GET" ) { # suddivide le coppie name/value tra loro @data = split /&/, $ENV{ 'QUERY_STRING' }; } elsif ( $ENV{ 'REQUEST_METHOD' } eq "POST" ) { @data = split /&/, $buffer; } else { print "Sono permessi solo i metodi GET e POST!\n"; exit; } my (%data, $value, $item, $key, $message, $sender, $result); foreach $item ( @data ) { # suddivide le coppie e traduce i + in spazi ($key, $value) = split /=/, $item; $key =~ tr/\+/ /; $value =~ tr/\+/ /; # Converte %XX da numeri hex ad alfanumerico $key =~ s/%([\da-f][\da-f])/pack("c",hex($1))/gei; $value =~ s/%([\da-f][\da-f])/pack("c",hex($1))/gei; # Nell'esempio originale non c'erano le parentesi tonde dopo il % $data{$key} = $value; } $message = "To: $data{'email'}\nFrom: $from\nSubject: Buon Appetito!\n\nIl CGI $0 ha ricevuto un ordine per te, eccoti servito: $data{'ordine'}!\n"; $sender = Email::Send->new({ mailer => 'SMTP', mailer_args => [ Host => '127.0.0.1']}); print "Ecco il messaggio che intendiamo spedire:\n\n$message\n"; $result = $sender->send($message) ; print ("L'invio ha avuto esito: $result\n"); Dato che la spedizione della email avverrà a carico del server SMTP presente sul nostro stesso computer, accertiamoci che questo sia attivo, e ricordiamoci di usare un indirizzo email di destinazione il server non rifiuti (per protezione dallo spam) le nostre conessioni. Tutto il codice prodotto Nell'archivio cgi.tar.gzsi può trovare il codice discusso in questa pagina. Per far funzionare gli esempi che invocano i cgi, scomprimere l'archivio nella directory public_html del proprio computer, a meno che non ne sia stata configurata una diversa, ed assicurarsi di aver impostato correttamente i permessi di accesso al file. HTTPS e VirtualHost Non resta ora che da configurare il nostro server web in modo da offrire la possibilità di collegarsi in modo crittograficamente sicuro, come previsto da HTTP su TLS (HTTPS), ovvero • consentire l'autenticazione del server; • creare una chiave crittografica di sessione; • cifrare i successivi messaggi in transito sulla connessione. Lo scenario generale della infrastruttura di sicurezza web è particolarmente ben riassunto nella documentazione di Apache, che offre questi servizi mediante il modulo ssl. Inoltre, usare lo stesso server Apache per offrire contemporaneamente sia l'HTTP su porta 80, che l'HTTPS su porta 443, richiede la configurazione di due diversi Virtual Host, di cui il primo è quello (default) utilizzato finora, ed il secondo è quello che ci accingiamo a definire nel file 342 Lo strato applicativo di Internet Alessandro Falaschi /etc/apache2/sites-available/secure. Pertanto, impartiamo ora i comandi sudo sudo sudo sudo a2enmod ssl cp /etc/apache2/sites-available/default /etc/apache2/sites-available/secure a2ensite secure gedit /etc/apache2/sites-enabled/secure che abilitano sia il modulo ssl che il sito secure, il cui file di configurazione è ottenuto a partire da una copia di quello di default. L'ultimo comando della serie, ci consente di editare la configurazione del file che definisce il funzionamento del server HTTPS. Innanzitutto, modifichiamo le prime due linee, in modo da dichiarare esplicitamente l'uso della porta 443, e quindi abilitiamo l'uso di SSL su questa porta, mediante la direttiva SSLEngine, in modo che ora l'inizio del file secure appaia come segue: NameVirtualHost *:443 <VirtualHost *:443> SSLEngine on Se ora proviamo a rilanciare Apache (sudo /etc/init.d/apache2 restart), questo si rifiuta di andare in esecuzione, notificando nel file di log il messaggio: Server should be SSL-aware but has no certificate configured [Hint: SSLCertificateFile]. Infatti, occorre specificare il certificato e la chiave da usare, inserendo di seguito alla direttiva SSLEngine, le direttive SSLCertificateFile /etc/ssl/certs/ssl-cert-snakeoil.pem SSLCertificateKeyFile /etc/ssl/private/ssl-cert-snakeoil.key Stavolta Apache accetta di partire, ed invocando una delle URI servite (es https://localhost), la pagina arriva, e il campo di inserimento indirizzo si colora di giallo, e si arricchisce del disegno di un lucchetto, a segnalare la sicurezza della navigazione. Ma... prima di arrivare a questo bel risultato, nel file di log abbiamo ossevato il messaggio RSA server certificate CommonName (CN) `terranova' does NOT match server name!? ed infatti, il certificato che abbiamo utilizzato non è rilasciato al nome a dominio che viene servito da Apache. Questo fatto comporta che anche il browser, prima di visualizzare la pagina richiesta, notifica alcune finestre di avvertimento, mediante le quali dichiara • l'impossibilità di verificare il sito come affidabile, e si specifica che • il nome a dominio del sito, è differente dal nome dell'intestatario del certificato, ipotizzando che potrebbe esserci una terza parte che sta tentando di impersonare il server stesso. Mentre il primo fatto è evidentemente legato alla assenza (presso il browser) del certificato della CA che ha firmato il certificato del server, il secondo è risolvibile avendo l'accortezza di richiedere un certificato per il quale il Common Name corrisponda esattamente al nome a domino per cui si intenderà usarlo. Per questo, possiamo generare una richiesta di certificato e firmarla, usando lo strumento TinyCA, ed utilizzando per la firma la chiave privata di labsoftelCA, che importiamo in TinyCA. Una volta esportati certificato e chiave privata intestati al CN uguale al nostro dominio, sostituiamo (nel path dichiarato mediante la direttiva SSLCertificateFile presente nel file secure) questi nuovi valori, a quelli 343 Riferimenti Server Apache relativi a ssl-cert-snakeoil, e ri-lanciamo Apache. Sullo schermo, si sviluppa il dialogo alef@alef-laptop:~$ sudo /etc/init.d/apache2 restart * Starting web server apache2 Apache/2.2.4 mod_ssl/2.2.4 (Pass Phrase Dialog) Some of your private key files are encrypted for security reasons. In order to read them you have to provide the pass phrases. Server alef-laptop.softel:443 (RSA) Enter pass phrase: OK: Pass Phrase Dialog successful. [ OK ] alef@alef-laptop:~$ che rappresenta la richiesta, da parte di Apache, di conoscere la passphrase con cui è crittografata la chiave privata associata al nuovo cerificato usato, che nell'esempio precedente era invece lasciata in chiaro. Distribuzione del certificato della CA Finalmente ora, immettendo (nel caso di chi scrive) la URI https://alef-laptop.softel, non si ottiene più l'avvertimento relativo al dominio sbagliato! Ora, non resta che importare nel browser, il certificato della CA che ha firmato il certificato con cui abbiamo configurato Apache. Cliccando sul link del certificato appena fornito, il certificato stesso viene visualizzato dal Browser, ma non importato dallo stesso. Pertanto, occorre prima salvarlo in un file, e quindi importarlo in modo esplicito, ossia (in firefox) • cliccare su modifica/preferenze • cliccare su avanzate/cifratura/mostra certificati • cliccare su autorità/importa, e quindi selezionare il file di certificato appena scaricato Alernativamente, si può configurare Apache affinchè cliccando sul link del certificato, questo venga automaticamente importato dal browser, senza necessità dei passi ora illustrati. A questo scopo, occorre modificare il file /etc/apache2/apache2.conf, inserendo la direttiva AddType application/x-x509-ca-cert pem in modo che il file con estensione .pem sia inviato con il MIME-Type corretto. Riferimenti • Guida Apache - di Edoardo Valsesia • I servizi web - di Simone Piccardi Realizzato con da Alessandro Falaschi - 344 Lo strato applicativo di Internet Alessandro Falaschi 345 Lo strato applicativo di Internet OpenSER e Twinkle Svolgiamo alcuni esperimenti relativi al VoIP SIP. Per concentrarci sulla segnalazione SIP e RTP, scegliamo di non utilizzare componenti di autenticazione, né di sfruttare il supporto offerto dal DNS. • Scelta dello UA • Chiamata diretta • Servizi aggiuntivi ◦ ◦ ◦ ◦ ◦ Messa in attesa Chiamata su altra linea Conferenza a tre Trasferimento di chiamata Invio di DTMF • Registrazione su di un hub • Configuriamo un Outbound Proxy ◦ Trapezisti! • Riferimenti Scelta dello UA Presso il Wiki del progetto Sapientel, è stata stilata una tabella di comparazione dei softphone SIP disponibili in forma libera, e tra quelli, scegliamo di usare Twinkle, che scarichiamo mediante Synaptics. La scelta di Twinke è dettata dalla sua implementazione diretta degli standard, senza mascherarli troppo dietro interfacce utente oltremodo semplificate. Inoltre, sembra offrire caratteristiche interessanti, come la conferenza a tre, svariate funzioni di controllo chiamata, confidenzialità del media, e possibilità di eseguire degli script in corrispondenza alla ricezione della chiamata. Chiamata diretta Per provare subito se funziona, sperimentiamo una chiamata diretta, che non faccia uso di Proxy intermedi. Per questo, creiamo (ad esempio, con il wizard) un profilo di utente che potremmo chiamare diretto in cui sceglimo come SIP service provider none, indichiamo un Address of Record con nome labint, e con dominio quello del nostro computer (mionome.labint). Se invece stiamo usando un profilo per il quale abbiamo scelto un SIP service provider, dovremo modicarlo (menù File/change user/nomeprofilo/Edit/Sip Server), disabilitando l'interruttore Register at startup. Ora, possiamo provare a chiamare i nostri colleghi, con AoR [email protected]. Se catturiamo il traffico, possiamo verificare come viene comunque fatto il tentativo di interrogare il DNS, ma non trovando nessun RR di tipo SRV, lo UA Twinkle rinuncia al Lo strato applicativo di Internet Alessandro Falaschi tentativo di usare il Registrar del dominio di destinazione, e procede ad inviare allo stesso la chiamata inmodo diretto. Per abbattere la chiamata, usiamo il tasto . Risoluzione problemi Non funziona? ...niente di più normale... ad esempio, verifichiamo che il dominio di secondo livello (mionome.labint, suonome.labint) possa essere risolto con un indirizzo IP. Cioè, che il file di zona ospitato presso il DNS autorevole per questi domini, contenga la linea @ A 192.168.NN.1. Servizi aggiuntivi Sperimentiamo alcune funzioni di Twinkle, contraddistinte dalle icone riportate di seguito, e cioè • Messa in attesa • Chiamata su altra linea • Conferenza a tre • Trasferimento di chiamata • Invio di DTMF I capture forniti, sono per il caso in cui alice@dock chiama bruno@dock, e sono prodotti presso lo UA di alice. Nella sezione dei Riferimenti, troviamo una nutrita casistica di servizi aggiuntivi realizzabili con SIP Messa in attesa In questo caso (hold), viene eseguito un re-INVITE da parte di alice, nel cui nuovo SDP la sessione viene dichiarata sendonly, e che quando accettato da bruno, produce un SDP di risposta che dichiara (dal suo punto di vista) la sessione receiveonly. Lo sviluppo attuale di Twinkle non sembra (ancora) prevedere l'invio di una musica di attesa, però ci starebbe bene... Chiamata su altra linea Mentre alice e bruno sono in conversazione, alice decide di chiamare una amica, e clicca sulla seconda linea. Il capture svela cheanche in questo caso, bruno è stato messo on hold con la stessa tecnica di prima. Conferenza a tre L'amica chiamata da alice, in realtà è l'annuncio preregistrato ascoltabile presso sip:[email protected]... beh poco male, proviamo a creare una conferenza a tre tra alice, bruno, e l'annuncio. La tecnica svelata dal captureè sempre la stessa, bruno viene messo on hold, parte l'INVITE verso test, e poi, quando si attiva la conferenza a tre... 347 Wireshark ed il VoIP OpenSER e Twinkle bruno è re-invitato per la terza volta, e viene riaperto il flusso RTP verso di lui, che ora è mixato assieme a quello di test. Trasferimento di chiamata In questo caso alice, dopo aver chiamato bruno, decide di trasferire la chiamata verso sip:[email protected]. In questo caso il capturesvela dei particolari interessanti: dopo aver messo, come al solito, bruno on hold, alice clicca sul trasferimento di chiamata, e sceglie di voler effettuare un blind transfer, indicando la nuova URI di destinazione: il risultato è che bruno riceve un REFER, che nella intestazione refer-to, indica la nuova URI. Lo UA di bruno risponde al REFER con un 202 Accepted, e dopo un pò, bruno (l'umano) risponde affermativamente alla finestra di notifica che gli propone il trasferimento, causando l'emissione di un INVITE verso la nuova URI. Per alice, l'invio del REFER sottindente la sottoscrizione alle notifiche relative allo stato della nuova chiamata, ed infatti bruno inizia a tenere informata alice della evoluzione della nuova chiamata, per mezzo di messaggi NOTIFY, nel cui body sono contenute le risposte che bruno riceve per la nuova chiamata. Finché bruno, non termina il dialogo con alice, con un BYE. Invio di DTMF Nel caso ci si trovi a dover interagire con un IVR, può capitare di dover immettere dei toni da tastiera. In questo caso, il capture cimostra come questo corrisponda ad inviare, nei pacchetti RTP, un diverso payload type, che contiene la rappresentazione letterale del tasto premuto. Wireshark ed il VoIP Prendiamo l'esempio fornito per la conferenzaa tre, per illustrare le funzioni che Wireshark (la mia è la 1.03) offre in supporto all'analisi del traffico VoIP: • notiamo innanzitutto che più o meno a metà del menù Statistics, è presente la voce SIP, che se selezionata, genera un rapporto riguardante il numero di messaggi SIP presenti, suddivisi per tipo; • dopo aver applicato un display filter che rimuove i protocolli non essenziali alla segnalazione SIP (ad es. (((!(rtp)) && !(cups)) && !(arp)) && !(icmp)), possiamo selezionare (sempre nel menù Statistics) l'operazione Flow Graph e, richiedendo i displayed packets, generare un interessante diagramma temporale della evoluzione delle chiamate, che può anche essere salvato su di un file di testo. In particolare, il display filter indicato prima, non rimuove i pacchetti RTCP, e la cui analisi può aiutare a comprenderne il funzioinamento; • rimuoviamo ora il display filter, e invochiamo (ancora dal menù Statistics) l'opzione Voip Calls: osserviamo che sono individuate due distinte chiamate, la prima verso bruno e la seconda verso test, complete di istanti di inizio, e di durata. Proviamo a ◦ selezionarle entrambe, e chiedere il grafico: ci viene mostrato un diagramma temporale in cui le chiamate sono separate mediante colori, ed i flussi RTP riassunti in un unico evento; ◦ selezionarne una (ad es., la seconda) o entrambe, e richiedere il player. Ci viene data l'opportuntità di simulare un jitter buffer della dimensione desiderata, in modo da poter verificare l'effetto (in termini di pacchetti persi) al variare della sua dimensione. Selezionando Decode ci appare una nuova finestra che mostra la forma d'onda audio corrispondente nei due versi, e 348 Lo strato applicativo di Internet Alessandro Falaschi selezionandone una (o più), ci viene data anche la possibilità di ascoltare il segnale corrispondente. Eventualmente, è possibile modificare la dimensione del jitter buffer, e valutare l'effetto risultante. • ancora mediante il menù Statistics, scegliamo questa volta RTP, Show all streams, ed osserviamo che ne sono individuati 6, ossia due direzioni per tre comunicazioni, considerando come nuovo quello iniziato dopo il re-invito, dato che in quel caso sono presenti due Contributing Sources ◦ scegliamo ora l'unico flusso contenente segnale, il quarto, e richiediamo Analyze: per ogni pacchetto, ci viene mostrato il ritardo rispetto al precedente, ed il calcolo del jitter corrispondente, ottenuto in accordo alla RFC 3550 eseguendo una media mobile relativa agli scostamenti tra i ritardi dei pacchetti, ed il loro ritardo medio; ◦ scegliendo ora Graph, otteniamo l'evoluzione del valore stimato per il jitter. Registrazione su di un hub Ora, possiamo creare un nuovo profilo utente, che potremmo chiamare hub, che usa come nome, il vostronome, e come dominio, ubuntu22.labint; Tenendo aperto wireshark, proviamo a 1. registrarci/deregistrarci mediante la quarta voce del menù in alto 2. interrogare le nostre registrazioni mediante l'icona-stellina all'estrema destra del nome del profilo Possiamo verificare come, anche senza aver specificato un Registrar, viene per default utilizzato quello corrispondente al proprio dominio. Nel primo caso, l'intestazione Contact contiene un parametro, expires, pari rispettivamente a 3600, ed a zero. Nel secondo caso, invece, la richiesta non contiene nessuna intestazione Contact, e viene quindi usata per recuperare dal Registrar le registrazioni attive. Proviamo ora, sempre con wireshark attivo, a chiamare un nostro collega, alla URI sip:[email protected]. In base alle intestazioni Server e UserAgent, determiniamo quali risposte ci vengono inviate dal Registrar, e quali dallo UA chiamato. 349 Configuriamo un Outbound Proxy OpenSER e Twinkle Configuriamo un Outbound Proxy Il proxy SIP che possiamo scaricare con Synaptic, è OpenSER. Dopo averlo fatto, dobbiamo abilitarne il funzionamento, per cui editiamo un primo file di configurazione (sudo gedit /etc/default/openser), e modifichiamo una delle prime linee come RUN_OPENSER=yes e quindi possiamo lanciare OpenSER, con il consueto sudo /etc/init.d/openser start, e controllare l'esito con il comando sudo /etc/init.d/openser status. Nel caso non sia in esecuzione, proviamo a determinare perché. Risoluzione problemi Per avere un feedback di ciò che succede, possiamo modificare l'inizio di /etc/openser/ openser.cfg, impostando /* uncomment the following lines to enable debugging */ debug=6 fork=no log_stderror=yes e quindi ri-lanciare Openser con il comando sudo service openser debug che ci mostra a schermo tutta una serie di messaggi, ed in particolare alle linee contrassegnate con ERROR: ci indica cos'è che non va. Ad esempio (almeno nella versione 1.3.2-2_i386.deb), possiamo trovare il messaggio ERROR:acc:init_acc_rad: failed to open radius config file: /usr/local/ etc/radiusclient-ng/radiusclient.conf che si riferisce ad un componente di cui non abbiamo bisogno. In tal caso, è sufficiente inserire in /etc/openser/openser.cfg, subito dopo il commento # ----- acc params -----, la direttiva modparam("acc", "radius_config", "") e quindi eseguire di nuovo sudo service openser debug per vedere se ora va. Oppure, dopo aver impartito sudo service openser debug lo schermo potrebbe rimanere muto: in tal caso, la causa più probabile è una impossibilità di effettuare la risoluzione DNS inversa degli indirizzi IP su cui OpenSer tenta di mettersi in ascolto, da verificare (ad es) sniffando il traffico che si sviluppa verso il DNS. In tal caso, la soluzione è quella di indicare in modo esplicito un solo indirizzo, inserendo in /etc/openser/ openser.cfg la direttiva listen=udp:192.168.NN.1:5060 in cui l'indirizzo IP utilizzato è lo stesso a cui corrisponde la risoluzione diretta per nostronome.labint. Anche stavolta, eseguiamo sudo service openser debug per vedere se ora va. 350 Lo strato applicativo di Internet Alessandro Falaschi Infine, potremmo osservare il messaggio del tipo ERROR: udp_init: bind(5, 0x813a7fc, 16) on 127.0.0.1: Address already in use ? Ha ragione lui: la porta 5060 è già occupata da Twinkle, ed OpenSER non può aprire il suo socket. Per rimediare, riconfiguriamo Twinkle: con la voce di menù Edit/System settings/Network, scegliamo una diversa porta per il SIP, e quindi chiudiamo e riapriamo Twinkle. Per l'ultima volta (si spera) eseguiamo sudo service openser debug per vedere se ora va. Quando tutto è a posto, possiamo tornare a commentare le tre linee con cui abbiamo attivato la modalità di debug, e lanciare di nuovo sudo /etc/init.d/openser start. Uso dell'Outbound Proxy Finalmente ci siamo. Creiamo un nuovo profio su Twinkle, che potremo chiamare Proxy, che usa anch'esso come nome il proprionome, e come dominio, ubuntu22.labint; quindi • impostiamo Registrar e Outbound Proxy mediante la voce di menù di Twinkle Edit/ User profile/SIP server, selezionando ◦ il Registrar ubuntu22.labint ◦ la casella use outbound proxy, specificando proprionome.labint; e chiedendo di far passare dal proxy le richieste del dialogo • registriamo Twinkle. Trapezisti! Ora, possiamo sperimentare l'evoluzione delle intestazioni di segnalazione, sniffando contemporaneamente sul proprio computer, e (tramite ssh) presso ubuntu22.labint. Sul proprio computer, ricordiamoci di sniffare sull'interfaccia any, dato che il traffico sviluppato tra lo UA Twinkle ed Openser viaggia sulla interfaccia lo:. Come esempio di quel che avviene, ecco due capture, ottenuti rispettivamente pressol'outbound proxy colocato con lo UA chiamante, e presso il Register,per una chiamata da sip:[email protected], verso sip:[email protected]. Riferimenti • Session Initiation Protocol (SIP) Basic Call Flow Examples - RFC 3665 • Session Initiation Protocol Service Examples - RFC 5359 • Best Current Practices for Third Party Call Control (3pcc) in the Session Initiation Protocol (SIP) - RFC 3725 • A Call Control and Multi-party usage framework for the Session Initiation Protocol (SIP) - draft IETF 2009 Realizzato con da Alessandro Falaschi - 351 Riferimenti OpenSER e Twinkle 352