venerdì 19 ottobre 2012

Sticking fingers in the socket...

Una delle primissime cose che mi son sentito ripetere sin da bambino, è stata che non si devono MAI mettere le dita nelle prese di corrente. Il mio problema, però, è che sin da bambino adoravo divertirmi scoprendo dei modi nuovi ed inusuali di utilizzare ciò che mi trovavo per le mani.
Un giorno, mentre stavo ascoltando con il mio fido mangiacassette un nastro con una fiaba, inciampai nel cavo di alimentazione, staccandolo dalla presa. Mi divertì molto sentire la voce del narratore storpiarsi lentamente prima di cascare nel silenzio. Mi divertì talmente tanto, che decisi che il mio passatempo per il resto del pomeriggio sarebbe stato quello di sentire la fiaba storpiata dal mio continuo staccare e riattaccare la spina dalla presa di corrente. Ricordo che mia madre era al telefono in camera sua, quando accadde il fattaccio: le mie ditine di bambino finirono troppo vicine ai contatti della spina mentre la reinserivo nella presa. Vidi un leggero lampo azzurrino e sentii un dolore istantaneo e fortissimo alla mano ed al braccio. Fortunatamente il salvavita intervenne e tutto si concluse solo con un grosso spavento.
Cosa c'entra tutto ciò con l'informatica? Beh, in inglese le prese elettriche si chiamano "socket" ed io oggi metterò le dita dentro le prese elettriche dell'informatica distribuita: i network socket.
Mi servirò dei seguenti strumenti: uno sniffer ed  analizzatore di protocollo (Wireshark), un linguaggio di scripting ad alto livello (Ruby) ed un paio di computer connessi in rete. Tutto può essere simulato anche su un computer singolo, dato che i network socket operano perfettamente anche sull'interfaccia loopback ed anzi, sono usatissimi proprio per gestire la comunicazione IPC.
Cominciamo dalla teoria:

Cos'è un network socket?

Un network socket è uno strumento concettuale di alto livello, che serve ad identificare il punto finale di una comunicazione. Esso in sostanza è una astrazione della struttura e dei processi necessari a trasmettere un pacchetto da un punto ad un altro di una rete. 
Se andiamo ad analizzare un pacchetto dati, scopriremo che esso è in realtà molto più complesso di quanto si possa immaginare. Esso è composto da tutta una serie di headers che, di fatto, "avvolgono" i nostri dati nei vari livelli OSI.
Non intendo mettermi qui a trattare del modello OSI. Se non lo conoscete, qui troverete più di ciò che vi serve sapere. Ad ogni modo, capirne qualcosa è fondamentale per andare avanti a seguire questo post.
Quando io voglio trasmettere qualcosa ad un altro sistema, devo specificare almeno chi sono (sorgente), dove voglio trasmettere (destinazione), a chi voglio trasmettere (porta) ed il contenuto della trasmissione (Dati). Questo è tutto ciò che normalmente mi serve per poter avviare una comunicazione.

Quello che segue è un semplicissimo client scritto in ruby che consente di aprire una connessione TCP, inviare dei dati e ricevere una risposta:

require 'socket'
hostname = (if ARGV.length == 2; ARGV.shift; else "10.136.128.90"; end)
puts hostname
comando = ""
s = TCPSocket.open(hostname, ARGV.shift)
while comando != "chiudi"
 comando = gets.chomp
 s.puts(comando)
 buffer = ""
 while buffer
  if buffer.chomp != ".term"
   buffer = s.gets
   puts buffer
  else
   buffer = nil
  end
 end
end
s.close

Questo qui sotto invece è un semplice server che "emula" in maniera molto rozza un servizio telnet, in quanto riceve i comandi dal client, li esegue in una shell locale e trasmette l'output del comando eseguito al client:

require 'socket' # prende socket dalla libreria

server = TCPServer.open(34444) # apre il socket server in ascolto sulla porta 19000

while true
 Thread.new(server.accept) do |connection|
  puts "connessione in ingresso da: #{connection.peeraddr[2]}"
  begin
   while connection
    c = connection.gets()
    if c != nil
     c = c.chomp
    end
    puts "dati in ingresso: #{c}"
    if c == "chiudi"
     puts "ricevuto: chiudi, connessione terminata"
     connection.close
     break
    else
     if c != ""
      # connection.puts "#{c}"
      uscita = %x(#{c})
      puts uscita
      connection.puts "eseguito: #{c}"
      connection.puts "#{uscita}"
      uscita = nil
      connection.puts ".term"
      connection.flush
     else
      connection.puts "nessun comando eseguito"
      connection.puts ".term"
     end
    end
   end
  rescue Exception => e
   puts "#{ e } (#{ e.class})"
  ensure
   connection.close
   puts "assicura: chiusura!"
  end
 end
end

Prima di far partire il server o il client, ho avviato il mio sniffer per catturare tutti i pacchetti scambiati tra il client ed il server.
A questo punto, lancio lo script server sulla mia macchina linux, quindi mi troverò con il seguente terminale attivo:


Il client lo avvio dalla mia macchina windows, pertanto mi si presenterà il seguente terminale:


Appena avviata la connessione, noto che il mio terminale mi segnala l'avviamento di una connessione in ingresso dal client:


Lo sniffer nel frattempo ha raccolto 3 pacchetti.

Andando a vedere il contenuto di questi pacchetti, scopriamo che non contengono dati. Ed allora perchè sono 3? Qui si deve entrare nella specificità del protocollo Internet e dei socket TCP. I socket TCP sono utili a stabilire delle trasmissioni orientate alla connessione (connection-oriented), pertanto per parlarsi, i due sistemi devono essere reciprocamente certi di poterlo fare. Come lo fanno? Con quello che si chiama normalmente un Three Way Handshacking.
Chi chiama manda un segnale (SYN) al server, come per dire:"Ciao! sei in ascolto?".. Il server risponde con un segnale (SYN, ACK), che dice "Ciao! Si, sono in ascolto.." A questo punto, il client manda un altro segnale (ACK) che "conferma" che adesso lui è al corrente del fatto che il server è effettivamente attivo e che è disposto a ricevere dei dati.
Dato che lo script server che ho creato è un emulatore "molto grezzo" di Telnet, proviamo allora a listare il contenuto della directory con il comando bash

ls -l

Sul teminale server apparirà l'output del comando...


...che verrà quindi trasmesso al client:


Ma cosa è successo sulla rete? Andiamo ad analizzare i pacchetti trasmessi:
Se osserviamo la colonna "Info" della nostra cattura, notiamo che il quarto pacchetto della nostra cattura è flaggato PSH, ACK. PSH sta per "push". I pacchetti PSH sono quei pacchetti che "spingono" i dati verso il server.



Osservando la finestra centrale, vedremo il nostro pacchetto scomposto secondo i vari layer OSI: Fisico (Frame), Data Link (Ethernet), Network (IPV4), Trasporto (TCP) e Sessione. In fondo vediamo anche un campo Data, con l'indicazione che contiene 5 bytes. Nella finestra più in basso, ci viene mostrato il nostro pacchetto per intero, sia in esadecimale che in ASCII. Guardate un po' quali sono gli ultimi 5 caratteri? Il nostro comando: "ls -l", più un terminatore (0x00) che ci viene mostrato in ASCII come un punto (.).
In pratica, per trasmettere 5 bytes di dati, abbiamo messo "sul filo" la bellezza di 60 bytes.
A questo punto, però, ci manca di capire cosa fa il server dopo che ha ricevuto il nostro pacchetto dati contenente la stringa "ls -l".
Lo scopriamo, ovviamente, col pacchetto successivo che, come si può ben vedere, è un ACK. Lascio a chi lo vorrà fare, il gusto di analizzare il resto della sessione.
Ci tengo a parlare però di altri due pacchetti particolari: FIN e RST.
Se per avviare e gestire una comunicazione abbiamo visto che esistono i pacchetti SYN, ACK e PSH, per terminare una sessione occorre un altro segnale. Questo segnale è il FIN, a cui si aggiunge l'ACK per chiedere al server di confermare di aver ricevuto l'intezione di cessare il socket. Il segnale FIN ACK dice al server:"Grazie di tutto, non ho più bisogno di mandarti o di ricevere dati". Ovviamente, il server, risponderà al FIN ACK con un ACK, per dire "Ok, grazie a te, addio!"
Ma cosa succede se a questo punto proviamo ancora a "spingere" un pacchetto dati? Qui entra il segnale RST.
RST sta per Reset e, sostanzialmente è il " 'zzo vuoi?" della comunicazione in rete. Dato che ogni sessione deve cominciare con un SYN, se io mando un pacchetto senza aver avviato educatamente la sessione con SYN, il server risponderà sgarbatamente con un RST, troncando la connessione.
Il pacchetto RST, vedremo prossimamente, è molto prezioso perchè consente di capire se un servizio è attivo senza necessariamente avviare una sessione, evitando così di lasciare una propria "impronta" nei log delle sessioni dei server analizzati.

2 commenti:

  1. Microgaming takes over the Sega Genesis - Games Sites
    It's all coming to a new generation of consoles. For a brand new console to boot, it took the casinosites Nintendo Life series 메리트카지노 a leap  Rating: 4.4 · ‎Review by John Goodman

    RispondiElimina
  2. Guests who make qualifying palms on the Party Pit tables might be eligible for prizes. Prizes embrace merchandise and/or gaming cheques valued a lot as} $25. We like to maintain things contemporary and full of cash and prizes, so we're always cooking up exciting methods that you simply can} win big. Take advantage of mychoice® promotions and other offers at Hollywood Casino Bangor and you could win cash, wonderful prizes, Free SlotPlay® and rather more. Check back typically, so that you never miss one tasty bite 카지노사이트 of motion.

    RispondiElimina