SERVIZIO DI NOMI


                                  Progetto di :

LOTTI CHRISTIAN MAT: 2148059986
E-MAIL : LCHRISTIAN@LIBERO.IT

 

INDICE

          descrizione DNS          schemi
         fasi del progetto
           1° fase DnsServer1+DnsLookUp
                  Name Server
                  Resolver
           2° fase DnsServer1+DnsServer2+DnsAdministrator+DnsLookUp
                   coordinamento DnsServer1---DnsAdministrator
                   coordinamento DnsServer1---DnsServer2
 
 

INTRODUZIONE:
 

Il progetto ,svillupato in java ,vuole implementare un servizio di nomi (name service) ,partendo dalle specifiche date nel documento RFC 1035 relativo al DOMAIN NAME SYSTEM.

DESCRIZIONE DNS:
Il servizio si compone di richieste (queries) fatte da utente e risposte (responses) fornite dal server.
L'obbiettivo principale è l'attuazione di corrispondenze tra le coppie nome_logico-indirizzo_IP .
Ogni richiesta viene fatta al servizio di nomi, cioè il riferimento da mappare viene passato al resolver che
- o recupera la corrispondenza dalla propria cache ;
- o la trova attraverso una richiesta ad un name server;
I domini sono divisi in zone di autorità soggette a diversi servitori.
Un name server ha solo una visione locale del dominio (requisito di località nel DNS) , cioè per richieste di corrispondenze esterne alla sua zona o fornisce un riferimento al richidente circa  un name server gerarchicamente a lui superiore o si incarica lui stesso di effetuare la richiesta a tale name server.
In genere , ogni zona ha un primary master responsabile per i dati della zona ,ma in più ci sono una serie di secondary master che sono copie del primary , con consistenza garantita dal protocollo DNS.
AFFIDABILITA':

Allo startup il secondario chiede i dati al primario e può far fronte al traffico in caso di guasto;
ad intervalli prefissati , i secondari chiedono le informazioni al primario (modello PULL)

EFFICIENZA SU LOCALITA':

I server mantengono informazioni sui dati già richiesti in precedenza

PROTOCOLLI COMUNICAZIONE :

Le richieste e le risposte per il name server usano protocollo UDP ,sulla porta 53;
mentre per il coordinamento si usa il protocollo TCP ,sempre sulla porta 53.

Un server mantiene una registrazione (record) per ogni risorsa; le richieste (query) consultano l'insieme dei record;
i record sono composti dai seguenti campi :

Il servizio distribuito e a dati partizionati ottenuto scambiando query tra i DNS server .

Esistono diversi tipi di server :


Dopo questa breve descrizione riguardante il DNS , mi sono imposto di articolare il progetto  in due parti principali:

  1. Implementare un servizio di nomi con la implementazione di un Name Server (DnsServer) ,e di un Resolver(DnsLookUp) ;
  2. Replicare il servizio con l'uso di 2 Name Server, ed implementare il coordinamento tra i due Name Server che forniscono il servizio ;

PROGETTO:

SCHEMI

Configurazione sistema con un solo name server:
 
 
 

                   Local Host                      |  Foreign
                                                   |
    +---------+               +----------+         |  +--------+
    |         | user queries  |          |queries  |  |        |
    |  User   |-------------->|          |---------|->|Foreign |     +----------+
    | Program |               | Resolver |         |  |  Name  |<----|MasterFile|
    |         |<--------------|          |<--------|--| Server |     +----------+
    |         | user responses|          |responses|  |        |
    +---------+               +----------+         |  +--------+
                                |     A            |
                cache additions |     | references |
                                V     |            |
                              +----------+         |
                              |  cache   |         |
                              +----------+         |
 
 
 
 

Configurazione con due name server:
 
 
 
 

      +---------+                 local host              |    forreign
     /         /|                                  |
    +---------+ |             +----------+         |  +--------+
    |         | |             |  primary |responses|  |        |
    |         | |             |   Name   |---------|->|Foreign |
    |  Master |-------------->|  Server  |         |  |Resolver|
    |  file   | |             |          |<--------|--|        |
    |         |/              |          | queries |  +--------+
    +---------+               +----------+         |
                                A     |maintenance |  +---------+
                                |     +------------|->|Foreign  |
                                |      responses   |  |secondary|
                                |                  |  |  Name   |
                                +------------------|--| Server  |
                             maintenance queries   |  +---------+
 

Fasi del progetto:
 
 

1°FASE:
Implementazione DnsServer1+DnsLookUp

Il primo passo nella progettazione del Name Server ,è stato quello di costruire un package di strumenti in grado di rispondere alle specifiche ,definite nella RFC1035 , riguardanti il formato dei messaggi ricevuti e spediti dai server.
Il package è cosi' composto da:
la  classe DnsConnection,  implementa il Domain Name Services come specificato nella RFC-1035. La classe DnsConnection  può interrogare( query) un  DNS server usando l' UDP protocol. Il server e la porta sono configurabili dentro l'oggetto DnsConnection. Sia DNS queries che responses sono rappresentate dalla classe DnsMessage.

Un messaggio DNS è composto da diverse classi. La prima è il  messagio stesso, implementato dalla classe DnsMessage.  Essa contiene metodi come getResponseCode, isRecursionAvailable, getAnswerCount, etc, che permettono di estrarre dal   messagio suoi  particolari settaggi. I messaggi contengono liste di questions, answers, authorities, e additionals. Una Interrogazione( query) generalmente contiene solo una domanda(question). Le Questions sono instanze di DnsQuestion. Sono composte di un domain name, type, e class. Il type è il tipo di Resource Record (RR) che è stato richiesto. Le Answers, authorities, and additionals sono le RRs spedite come risposta ad una interrogazione( query).Ciascun RR (instanza di DnsResource) estende DnsQuestion con il valore di time to live a alcuni dati  specifici dipendenti dal tipo di RR. Quando una DnsResource è spacchettata (unpacked), essa analizza la rappresentazione in byte impacchettati del messagio e istanzia una specifica sottoclasse di DnsResource dipendente dal valore di tipo che rappresenta. Tale sottoclasse allora spacchetta (unpacks) il resto del  RR.

Le  Resource Records implementate come classi sono: A (Address), CNAME (CanonicalName), HINFO (HardwareInfo), MX (MailExchange), NS (NameServer), PTR (Pointer), and SOA (StartOfAuthority).

Ora mostro uno scema delle relazioni tra le classi del package:
 


 
 
 
 

Definito il package , ho implementato il Name Server ed il Resolver .

NAME SERVER
Le caratteristiche principali imposte al Name Server implementato sono le seguenti:
 


La classe dove il name server è implementato è la DnsServer1 ,come "master file " utilizzo un file del local file system
chiamato "dns.properties" .Il file è organizzato come una lista di nomi (sarebbero i nomi di dominio) a cui è associato un
indirizzo_IP:  es.       christian.com=1.2.3.4

  public class DnsServer1 extends Thread
{ ..
public static void main(String[] args)
..
names = new Properties();
    //CARICO DA MASTER FILE( dns.properties ) DATI DI LAVORO
       FileInputStream in = new FileInputStream(new File("dns.properties"));
          names.load(in); socket = new DatagramSocket(53);
..
new DnsServer1().start(); public void run()
{ ..
         byte[] buf = new byte[512];
        DatagramPacket packet = new DatagramPacket(buf, 512);
       socket.receive(packet);
      new Service1(packet);
.. class Service1 extends DnsServer1
{
public void run()
   {..
          DnsMessage query = new DnsMessage(pack.getData(), pack.getLength());
        DnsMessage response = createResponse(query);
        pack = new DatagramPacket(response.getData(), response.getLength(), address, port);
      socket.send(pack);
   ..
 


private void answerQuery(DnsMessage query, DnsMessage response)  throws Exception
   {
    DnsQuestion question = (DnsQuestion) query.getQuestions().elementAt(0);
    response.addQuestion(question);
    String address = null;
 


address = names.getProperty(question.getName());}

 if(address == null)

           { address = names1.getProperty(question.getName());}
 System.out.println("address=" + address);
    if (address != null)
    { flag = false;          response.addAnswer(new Address(question.getName(),
         question.getType(),
         question.getDnsClass(),
         0,
         address));
    } else {
         flag = true;}
   }
 
  private DnsMessage createResponse(DnsMessage query)
    throws Exception
  {
    DnsMessage response = new DnsMessage(query);
  DnsQuestion question1 = (DnsQuestion) query.getQuestions().elementAt(0);    switch(query.getQueryCode()) {
      case DnsQuery.QUERY  :
               if (question1.getType() == DnsType.A)                 {answerQuery(query, response);}               else {response.setResponseCode(DnsResponse.NOT_IMPLEMENTED);}
           break;
      case DnsQuery.IQUERY:
      case DnsQuery.STATUS:
      default:
       response.setResponseCode(DnsResponse.NOT_IMPLEMENTED);

   }


        if (flag == true)
         {try{
        DnsQuestion question = (DnsQuestion) query.getQuestions().elementAt(0);

         dns = new DnsConnection("scm.com");
               System.out.println("sono a SCM.COM, DnsServer1 run.");
              DnsMessage resp = dns.query(question.getName(),question.getType());
        for(int i = 0;i < resp.getAnswerCount();i++)
        response.addAnswer((DnsResource)resp.getAnswers().elementAt(i));
        System.out.println("dopo query DnsServer1 ready.");
        if (response.getAnswerCount() == 0)
 

{
     System.out.println("DnsServer1 ready."+ response.getAnswerCount()+"no answer");
     response.setResponseCode(DnsResponse.NAME_ERROR);
       }
         else {  DnsResource ris= (DnsResource)resp.getAnswers().elementAt(0);
                 Address ad = (Address) ris;
                 names1.setProperty(question.getName(),ad.getAddress());
           }       } catch(DnsException d){response.setResponseCode(DnsResponse.NAME_ERROR);}
   }

 printResponse(response);
 


  response.pack();

 return response;
  }
 
 
 
 

RESOLVER


Il Resolver implementato ha il compito di interfacciarsi tra l'utente ed il name server ,  trasformando le query di utente in query secondo il formato riconosciuto dal name server , ricevere la risposta e renderla visibile all'utente.
Il Resolver costruito è implementato nella classe DnsLookUp ,naturalmente  importa come la classe DnsServer1, il package
che permette l'utilizzo dei messaggi formato name server (import mylib.net.dns.*;).
Può essere utilizzato in due modalità differenti : una interattiva ,dove viene avviata una sessione di query; ed una in cui al ricevimento della risposta l'applicativo termina.
Internamente alla sessione di query l'utente può settare 3 tipi di parametri:
                                                                                      - il name server a cui collegarsi ;
                                                                                      - il tipo di query che si vuole eseguire ;
                                                                                      - ed il livello di debug (che informazioni visualizzare nella risposta).

Viene implementata una cache dove sono memorizzate le risposte precedenti;
Il collegamento con il name server è impostato con un time-out ,  se tale tempo di attesa scade l'applicativo finisce l'esecuzione .
 
 
 
 

2°FASE:
Implementazione replicazione servizio DnsServer1+DnsServer2+DnsAdministrator+DnsLookUp

La seconda parte del progetto prevede la replicazione del servizio di nomi introducendo un secondo Name Server (DnsServer2) , avente le stesse carratteristiche di servizio e operante con gli stessi dati di DnsServer1 . Questa è una specifica richiesta per la maggiore efficienza ed affidabilità del sistema .L'ingresso di un secondo Name server nel sistema ha implicato l'utilizzo di un protocollo di cordinamento   tra i due ,ho inoltre introdotto anche la presenza di un Amministratore del sistema , gestore del "master file", che dovrà anch'esso coordinare i suoi rapporti con i server.
Prima di tutto occorre definire i ruoli di ogni componente del sistema:

Quindi definiti i ruoli , ho implementato un protocollo di coordinamento tra i vari elementi.

I rapporti tra Resolvers e Name Servers si svolgono sempre come descritto nella prima fase , utilizzando una connessione UDP sulla porta 53 , che anche se non garantisce affidabilità , implica un basso overhead , e vista la dimensione dei pacchetti di dati scambiati è accettabile .
Le caratteristiche del collegamento sono tali che se accade un guasto al lato Resolver il Server non se ne preoccupa ,mentre se si guasta il lato Server o vi è un problema sulla rete di connessione , il Resolver inviata la richiesta attende un TIME-OUT e quindi termina l'esecuzione , che porta all'unico inconveniente della perdita dei dati memorizzati nella cache di quest'ultimo. L'utente quindi , se vorrà riaprire una sessione di query potrà farlo rilanciando l'applicativo e riprovare a collegarsi allo stesso Server o ad un altro.

Per quanto riguarda i 2 Name Server e l'Amministratore la gestione dei rapporti si svolge utilizzando una connessione TCP sulla porta 53 , è preferita una connessione di tale tipo poichè la gestione di tale protocollo coinvolge la trasmissione di quantità di dati più rilevanti in termini di importanza e dimensione , rispetto alla precedente.
Il punto focale di coordinamento è il primary name server (DnsServer1) le altre due entità devono relazionarsi a lui per la gestione della risorsa comune rappresentata dal contenuto del "master file".
Quindi ho deciso di istanziare la gestione dei rapporti per il DnsServer1 in un demone (Demone1) che si occupa di :             - ascoltare le comunicazioni che l'Amministratore inoltra e istanziare la procedura conseguente;
- ascoltare le richieste che giungono dal secondary name server e istanziare le procedure conseguenti;
Il Demone serve una richiesta alla volta  ,quindi o interagisce con l'Amministratore oppure con il secondary name server.
In questo modo non si avranno inconsistenze nei dati trasmessi.

L'Amministratore si relaziona con il DnsServer1, dopo l'editazione,da parte sua, di cambiamenti nel "Master File" e si avvale di un applicativo DnsAdministrator per notificare il cambiamento.

Il DnsServer2  ha la necessità di comunicare con il primary server in due particolari occasioni :
- all'avvio ,lo START-UP, per richiedere i dati con cui servire le richieste dei Resolvers  ;
- durante l'eseguzione del servizio ,periodicamente  ,per richiedere l' eventuale l'aggiornamento dei dati , l'UP-DATE.
Il secondary si avvale per svolgere tali operazione di un demone (Demone2) , istanziato ed avviato nel main();
 
 

Coordinamento DnsServer1---DnsAdministrator

Il coordinamento tra il primary (DnsServer1) ed l'Amministratore è necessario in quanto è prevista la possibilità di editare cambiamenti sul "master file" da parte dell'amministratore del sistema .
DnsServer1 utilizza per tale coordinamento un demone (Demone1) istanziato e messo in eseguzione all'avvio nel main().Tale demone apre una ServerSocket sulla porta 53 (connessione TCP) e si mette in ascolto ,in attesa di un  collegamento  da parte  di DnsAdministrator .
Il demone riconosce la sequenza di due stringhe chiave per identificare il DnsAdministrator : la prima stringa ricevuta  identifica l'Amministratore(uno username) , la seconda un ulteriore riconoscimento (una password).
Terminato il riconoscimento in modo positivo il demone avvia l'aggiornamento dei dati ricaricando il master file ,
ed aggiornando il serial number.
Il serial number identifica la versione dei dati caricati , viene incrementato ad ogni aggiornamento ,vedremo che servirà successivamente.
Durante l'aggiornamento  , viene impedito l'accesso ai dati del thread Service1 che svolge il servizio di name server ,
attraverso il settaggio della variabile permess ,in questo modo Service1 devia la query al secondary server.
Ora mostro la parte di codice che descrive l'interazione:

Per il Demone1:
class Demone1 extends DnsServer1
{..


if( Request.equalsIgnoreCase("ADMINISTRATOR"))  // se vera amministratore connesso
        {String mess = "GIVE ME THE PASSWORD";
      sockout.println(mess);
      Request = sockin.readLine();

      if( Request.equalsIgnoreCase("mypass"))
         {
       System.out.println("\n Password esatta =  "+ Request);

       FileInputStream in = new FileInputStream(new File("dns.properties"));
       permess=false;
       names.clear();
       names.load(in);
        names.list(System.out);
       names1.list(System.out);
       Date now = new Date();
                         serno = now.getTime();
       PrintWriter temp1 = new PrintWriter(
                          new BufferedWriter(new FileWriter("Soa.properties",false)));
                      temp1.print(serno);
       temp1.close();
       in.close();
       aggiorna();
        permess=true;
      }

Per il DnsAdministrator:

public class DnsAdministrator
{
  static Socket sock;
  static String mess1 = "ADMINISTRATOR";
  static String mess2 = "mypass";
 

  public static void main(String[] args)
   {
    try{
         sock = new Socket("localhost",53);

   BufferedReader in = new BufferedReader(new InputStreamReader(sock.getInputStream()));
   PrintWriter out =new PrintWriter(new BufferedWriter(new OutputStreamWriter(sock.getOutputStream())),true);

         out.println(mess1);
   String str = in.readLine();
   System.out.println("   " + str);

         out.println(mess2);
         sock.close();
..
..}

Ora mostro il diagramma delle interazioni:


 

Coordinamento DnsServer1---DnsServer2

Il coordinamento tra il primary server ed il secondary server si svolge attraverso l'interazione di due demoni (Demone1,Demone2) facenti  le veci dei rispettivi Server .Come già preannunciato si utilizza sempre la connessione TCP sulla porta 53 , per entrambi i tipi di interazioni (START-UP,UP-DATE).

La procedura di  START-UP viene avviata con l'instanziamento e la messa in esecuzione del Demone2 ,nel main() del DnsServer2 . L'interazione del  Demone2 con il Demone1 si compone di una prima fase di riconoscimento con l'invio del messaggio "STARTUP" da parte del Demone2 ,seguita dalla fase di trasferimento dati(contenuto del master file) dal Demone1 al Demone2 e come ultimo passo vi è l'invio del serial number associato ai dati ,sempre da Demone1 a Demone2. Se la fase di start-up dovesse fallire a causa di un guasto del DnsServer1 o della linea ,per non lasciare il DnsServer2 fuori servizio viene avviata una procedura di emergenza ,che fa utilizzare come dati di lavoro e serial number gli ultimi salvati  rispettivamente nei file dns1.properties, Soa1.properties .
Il serial number verrà utilizzato nell'UP-DATE ,per controllare la versione dei dati.

Ora mostro il diagramma delle interazioni dello START-UP:

La procedura di UP-DATE si compone ach'essa di una prima fase di riconoscimento del Demone2 che invia il messaggio
"REFRESH" ,a questo punto il Demone1 riconosciuto chi si è collegato  e la procedura richiesta invia il serial number corrente al Demone2 , che lo confronta con quello in suo possesso.
Se i numeri sono uguali (cioè la versione dei dati posseduti dal DnsServer2 è aggiornata) la procedura di UP-DATE termina , e viene ripetuta dopo REFRESH secondi.
Se il numero spedito dal Demone1 è maggiore (cioè il DnsServer2 ha dati non aggiornati) la procedura di UP-DATE viene completata , con l' invio da parte del Demone2 della stringa "UPDATE"  che viene interpretata dal Demone1 come richiesta di invio dati .Il Demone2 completa la procedura salvando ,i dati ed il serial number speditigli,  nei file dns1.properties ,e Soa1.properties , che sono utilizzati nella procedura d'emergenza , anche in questo caso la procedura di UP-DATE viene ripetuta dopo REFRESH secondi.
Se la fase di UP-DATE dovesse fallire a causa di un guasto del DnsServer1 o della linea ,per non lasciare il DnsServer2 con dati e serial number inconsistenti viene avviata una procedura di emergenza ,che fa utilizzare come dati di lavoro e serial number gli ultimi salvati  rispettivamente nei file dns1.properties, Soa1.properties .

Ora mostro il diagramma di interazione della fase di UP-DATE:
 
 

NOTA : la procedura di UP-DATE utilizza 2 parametri quali serial number e REFRESH  , impostati rispettivamente uno dal DnsServer1  e l'altro dal DnsServer2. In realtà nella RFC1035 tale procedura è cosi' composta : prima il secondary server invia una query del  di tipo SOA verso il primary server, quindi il secondary server controlla il campo serial di tale record inviatogli dal primary e lo utilizza come nel mio progetto  per capire se deve eseguire l'aggiornamento ,in caso di aggiornamento deve eseguire una query di tipo AXFR che equivale a chiedere il trasferimento della zona gestita dal primary server .Il record SOA contiene inoltre il parametro REFRESH che deve essere utilizzato per impostare l'intervallo con cui va ripetuta la procedura di UP-DATE.

Nel progetto eseguito come serial number utilizzo la variabile di tipo long  serno impostata con il metodo getTime() della classe Date che ritorna un valore temporale in millisecondi dal 1/1/1970:
..
           Date now = new Date();
          serno = now.getTime();

..

Ho scelto questo tipo di implementazione poichè viene richiesto che il serial number sia incrementato ad ogni caricamento del master file da parte del primary server , anche in sessioni di lavoro diverse.

Per quanto riguarda il REFRESH ho impostato un tempo che mi permettesse di controllare il corretto funzionamento del sistema.
E' stata necessaria questa implementazione viste le caratteristiche del master file da me implementato.
 
 
 
 
 

SPECIFICHE DI LAVORO


Le specifiche di lavoro del sistema , in parte già descritte nella relazione, sono queste:
 

Per la funzione di  NAME SERVER:
 

Per il COORDINAMENTO:
 
 
 

MODO D’USO:

1. Lanciare il DnsServer1 : “java DnsServer1 indirizzo_DnsServer2
2. Lanciare il DnsServer2 : “java DnsServer2 indirizzo_DnsServer1
3. Lanciare il DnslookUp : “ java DnsLookUp - indirizzo_server_desiderato"  (per il modo interrativo)
                             oppure    “ java DnsLookUp  nome_dominio indirizzo_server_desiderato"  ( per query unica)

4. Lanciare il DnsAdministrator: "java DnsAdministrator"
  Questo applicativo va lanciato dopo aver editato cambiamenti nel "master file" : dns.properties.
  E' impostato per essere lanciato sulla stessa macchina dove esegue DnsServer1.
Nota:va aggiunta nel classpath la directory con il percorso c:\projava\lib

ESEMPIO:

Come provare il sistema su un’ unica macchina.
1. java DnsServer1
2. java DnsServer2
3. Per collegarsi al DnsrServer1 : java DnsLookUp - "localhost"
4. Per collegarsi al DnsServer2 : java DnsLookUp2  - "localhost"
Nota: DnsLookUp2 è identico a DnsLookUp solo che si collega alla porta 1053 essendoci la necessità di far lavore su tale porta il DnsServer2 ,la porta 53 è già occupata dal DnsServer1.