MokaByte
Numero 24 - Novembre 98
|
|||
|
|
||
Piergiuseppe Spinelli |
|
||
Ai nostri giorni, specialmente con le nuove leggi sulla privacy, appare assai scorretto introdursi furtivamente nelle conversazioni altrui, per spiare, controllare, annotare… Ciò non è sempre vero: esistono validi motivi per intromettersi nelle comunicazioni tra un server ed i suoi client; eccone alcuni: 1. estendere i controlli di accesso (o addirittura fungere da firewall) 2. estendere le capacità di log del server 3.registrare annotazioni in formati indipendenti dal particolare server 4. conservare le informazioni più frequentemente accedute per accelerarne il rilascio.
Proxy generalizzato
Viene spesso chiamato proxy un programma che intercetti una comunicazione client/server in modo trasparente ad entrambe le parti redirigendo rispettivamente l’input di ogni lato sull’output dell’altro. Il nome è dovuto al fatto che tale oggetto viene aggiunto in prossimità di uno dei due device comunicanti, ad esempio su un server della rete locale dotato di un collegamento modem con un internet provider: in questo modo i vari client si connettono via LAN e ricevono le informazioni richieste senza sapere quando il proxy le preleva da internet o dalla propria cache. Secondo il linguaggio dei pattern, l'intento di un oggetto proxy è quello di "fornire un surrogato o segnaposto per un altro oggetto per controllare l'accesso ad esso" (vedi http://st-www.cs.uiuc.edu/cgi-bin/wikic/wikic?ProxyPattern).
Ad un vecchio programmatore come me, scrivere un programma proxy può apparire, istintivamente, un compito arduo: sono passati appena pochi anni da quando si realizzavano applicazioni client/server scambiando file (di richiesta e risposta) via FTP per evitare la complessità della programmazione di rete (poco conosciuta, poco documentata, facilmente preda di problemi di condivisione, difficilissima da implementare senza ricorrere a processi concorrenti, ai tempi in cui si parlava di thread, o processi leggeri, con reverenziale timore). Appare quindi quasi miracolosa la semplicità con cui è possibile metter su l’ossatura di un programma proxy in Java. Esistono almeno quattro caratteristiche del linguaggio di SUN che possono aiutarci al riguardo:
Multithreading nativo
Caricamento dinamico delle classi
Classi innestate.
Prendiamo ad esempio il VisualBasic 5: è adesso possibile scrivere oggetti ActiveX multithreaded, accedere a classi di comunicazione, caricare dinamicamente controlli OLE. Eppure sfido chiunque a scrivere in VB un programma di comunicazione con la stessa semplicità ed eleganza resa possibile da Java. Si tratta in fondo di un problema generazionale. I primi linguaggi di programmazione esprimevano tutta la loro potenza nel calcolo. I loro immediati successori si sbizzarrivano nella manipolazione dei dati. Quelli della generazione visuale curavano il look innanzi tutto mentre i linguaggi contemporanei sono dei tipi particolarmente comunicativi.
Chi ha detto "Il computer è la rete"?
Petrosino tesse la sua rete
Ho chiamato Petrosino questo piccolo scheleton di proxy perché è pensato per mettersi in mezzo un po’ a tutto (almeno nella famiglia di protocolli TCP/IP) , proprio come il prezzemolo e l’omonimo celeberrimo poliziotto.
Si tratta di un’applicazione stand-alone che, in configurazione base, non fa assolutamente nulla (tranne rallentare la comunicazione….) offrendo però una piattaforma per costruire semplici programmi di filtro o di log per i vari protocolli. Ad esempio, digitando:
>java Petrosino 25 mailserver.com 25 1 filtro1 filtro2 …attiviamo sulla porta 25 del nostro computer un proxy per il server mailserver.com (sempre sulla porta 25) caricando una lista di moduli filtro che leggono ogni riga scambiata tra client e sever per svolgere azioni custom. E' possibile far coesistere sullo stesso computer il server ed il proxy, naturalmente su porte diverse:
>java Petrosino 8080 localhost 80 1 HttpFilterDescrizione delle classi
La classe principale, Petrosino, apre un server socket sulla porta indicata dal primo parametro e resta in attesa di connessioni da parte dei client (il limite consiste nel poter monitorare solo sessioni aperte dai client, come avviene per i protocolli HTTP o SMTP, ma questo va bene per la maggior parte delle situazioni). Provvede inoltre al caricamento dinamico delle classi filtro previa controllo che esse implementino l’interfaccia Petrosino.Listener.
Se il quarto parametro è diverso da 0, Petrosino visualizzerà sul video (monitor Java) tutte le stringhe che passano nei due sensi della connessione. Ogni volta che un client ignaro si connette (convinto di parlare direttamente col server), il proxy crea un istanza della classe Petrosino.Twins che a sua volta apre una connessione socket con il server sulla porta specificata (parametri 2 e 3) ed i rispettivi stream di I/O sia parte client che server.
Il nome Twins è dovuto al fatto che tale istanza, una volta aperti tutti i canali di comunicazione, provvede a creare due thread simmetrici di tipo Petrosino.Twins.ConnectionTask. La simmetria sta nel fatto che un’istanza riceve l’input stream del client e l’output del server mentre l’altra resta in attesa di dati dal server e scrive sul client.
Ogni volta che viene ricevuta una stringa da uno dei due estremi della comunicazione, il gemello delegato al suo monitoraggio la passa iterativamente a tutti i filtri caricati e controlla il flag da essi ritornato per verificare se continuare il reindirizzamento del canale verso l'altro capo o se, addiritttura, interrompere la comunicazione. Ogni oggetto di tipo ConnectionTask è quindi sempre abbinato ad un gemello simmetrico con il quale condivide i dati dell’outer class Twins. In coda all'articolo vedremo di approfondire questa tecnica che ho chiamato condivisione controllata.
Il codice di
Petrosino
Qualche applicazione pratica
Tanto per inquadrare i possibili usi di un proxy generico, pensiamo ad un monitoraggio delle comunicazione di mail interne ad un'azienda. In partcolare vogliamo:
impedire che vengano spedite fuori dal dominio intranet delle mail contenenti allegati.
Mail
senza allegati
(oggetto: "prova", contenuto: "prova") |
Mail
con un piccolo file grafico in allegato
(oggetto: "Prova", contenuto: "Prova", allegato: " BULLETB1.GIF") |
C>HELO
pce21
S>250 mailserver.ditta.com C>RSET S>250 OK C>MAIL FROM:<spinelli.p@ditta.com> S>250 OK C>RCPT TO:<amico.mio> S>250 OK C>DATA S>354 Enter Mail, end by a line with only '.' C>X-Sender: spinelli.p@smtp.ditta.com (Unverified) C>X-Mailer: Windows Eudora Light Version 1.5.2 C>Mime-Version: 1.0 C>Content-Type: text/plain; charset="us-ascii" C>To: amico.mio C>From: Spinelli Piero <spinelli.p@ditta.com> C>Subject: prova C>prova C>. S>250 Message received OK. C>QUIT S>221 GoodBye |
C>HELO
pce21
S>250 mailserver.ditta.com C>RSET S>250 OK C>MAIL FROM:<spinelli.p@ditta.com> S>250 OK C>RCPT TO:<amico.mio> S>250 OK C>DATA S>354 Enter Mail, end by a line with only '.' C>X-Sender: spinelli.p@smtp.ditta.com (Unverified) C>X-Mailer: Windows Eudora Light Version 1.5.2 C>Mime-Version: 1.0 C>Content-Type: multipart/mixed; boundary="=====================_906569772==_" C>To: amico.mio C>From: Spinelli Piero <spinelli.p@ditta.com> C>Subject: prova C>X-Attachments: C:\TRANSITO\EUDORA\BULLETB1.GIF; C>--=====================_906569772==_ C>Content-Type: text/plain; charset="us-ascii" C>prova C>--=====================_906569772==_ C>Content-Type: image/gif; name="BULLETB1.GIF"; C> x-mac-type="47494666"; x-mac-creator="4A565752" C>Content-Transfer-Encoding: base64 C>Content-Disposition: attachment; filename="BULLETB1.GIF" C>R0lGODlhGQANALP/AMDAwGbM/2bMzDPM/zPMzDOZzDNmzDMzzDMzmQBmzAAzzAAzmQAAmQAAAAAA C>AAAAACH5BAEAAAAALAAAAAAZAA0AQAQ/0AhAq70YC1KKWUomVkfnGcl4eWprGQTnzp2BggzjEgI7 C>Y4XB5DeScHzElymBOCySAJMNoXj+AkdDKNkrQC0RADs= C>--=====================_906569772==_-- C>. S>250 Message received OK. C>QUIT S>221 GoodBye |
Ed ecco il listato
di MailExtFilter, che registra su file il log delle mail spedite ed impedisce
di spedire allegati all’esterno della ditta:
import java.io.*; |
Per attivare il filtro bisigna digitare, ad esempio:
>java Petrosino 25 mailserver 25 0 MailExtFilterAmmettiamo adesso di aver rilevato una nuova esigenza, ovvero quella di monitorare il carico di rete dovuto alla mail. E’ possibile scrivere un nuovo filtro, CountExtFilter, per registrare su file (o su db, via JDBC) i byte scambiati ad ogni invio. I due filtri sono attivabili a cascata:
>java Petrosino 25 mailserver 25 0 MailExtFilter CountExtFilterIn questo modo è possibile aggiungere al proxy generico un diverso filtro per ogni nuova esigenza di monitoraggio o filtratura (ovviamente riguardanti lo stesso servizio).
Riassumendo
Scrivere programmi di comunicazione, come questo mini proxy, è più semplice in Java che in altri linguaggi grazie alle classi di comunicazione, ai thread, al link dinamico, all’innestamento delle classi e, da non dimenticare, alla possibilità di far migrare senza modifiche il modulo che girava su UNIX sul nuovo server NT (o meglio, da NT al nuovo server UNIX, ….). Come esercizio, chi fosse interessato, potrebbe provare a scrivere un filtro per Web-Server che tenga traccia di tutte le pagine scaricate. La specifica HTTP è lunga e tediosa quindi suggerisco di far girare Petrosino, senza alcun filtro sul vostro client:
>java Petrosino 80 WebServer 80 1(dove WebServer è l’indirizzo di un qualsiasi server web), e richiedere pagine dal browser usando la URL iniziale:
http://localhost/
Credo che l’output del programma sia, ai fini didattici, un ottimo inizio per capire i protocolli internet e, perché no, per pasticciarci un po’.
Risorse
MokaByte Web 1998 - www.mokabyte.it MokaByte ricerca nuovi collaboratori. Chi volesse mettersi in contatto con noi può farlo scrivendo a mokainfo@mokabyte.it |