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?&nbsp;&nbsp;<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