Esercizio 4: Custom Socket Factory


In questo esercizio ci occuperemo di utilizzare custom socket factories nell'esercizio precedente.
Iniziamo creando un package socketfactory nella vostra directory javarmi (sia sulla macchina
client che su quella server).
Tutti i codici che vedrete dovete metterli sulla macchina server. Poiche'
e' il server che si occupa di definire i sockets,

1) Implementazione di una custom ServerSocket e Socket

La decisione sul tipo di socket da usare in una applicazione dipende dall'applicazione stessa.
Se il server spedisce e riceve dati riservati puo' essere opportuno criptare i dati trasmessi.
Nel seguente esempio lavoreremo con sockets che eseguono una semplice crittografia XOR.
Nota che questo tipo di codifica e' relativamente facile da decriptare per un hacker esperto.
In questo esercizio, lo scopo e' di mostrare un esempio di custom socket factory. Alla fine
verranno fornite dei puntatori per approfiondire forme di sockets piu' sicure.

Avremo bisogno dei seguenti sorgenti. Notate che XOR sockets usano speciali implementazioni
degli stream di input ed output.
Mettete allora nel vostro package del server i seguenti codici:

2) Adesso implementiamo una custom RMIClientSocketFactory

Lavoriamo sempre sulla macchina del server.
La socket factory , XorClientSocketFactory, deve implementare l'interfaccia java.rmi.server.RMIClientSocketFactory.  
Avendo cura di implementare il metodo createSocket che ritorna un'appropriata istanza di XorSocket per il client.
La client socket factory deve implementare l'interfaccia java.io.Serializable in modo che i sockets possano essere
serializzati insieme agli stub, quando quest'ultimi vengono passati al client. E' anche importante reimplementare i metodi
equals e hashCode. Percio', sempre sulla macchina del server mettiamo il seguente codice
XorClientSocketFactory.java

package socketfactory;

import java.io.*;
import java.net.*;
import java.rmi.server.*;

public class XorClientSocketFactory
implements RMIClientSocketFactory, Serializable {

private byte pattern;

public XorClientSocketFactory(byte pattern) {
this.pattern = pattern;
}

public Socket createSocket(String host, int port)
throws IOException
{
return new XorSocket(host, port, pattern);
}

public int hashCode() {
return (int) pattern;
}

public boolean equals(Object obj) {
return (getClass() == obj.getClass() &&
pattern == ((XorClientSocketFactory) obj).pattern);
}
}
Come potete vedere la variabile pattern conterra' l'informazione protetta utilizzata per la codifica.
Studiate il codice per comprendere cosa accade e come sono implementati i metodi.


3) Implementiamo adesso una custom RMIServerSocketFactory.

La socket factory, XorServerSocketFactory, deve implementare l'interfaccia  java.rmi.server.RMIServerSocketFactory.
Avendo cura di  implementare il metodo createServerSocket che ritorna un'appropriata istanza di
XorServerSocket.

Non e' necessario che la (classe) server socket factory implementi l'interfaccia Serializable poiche' i server socket factory non
sono contenuti in stub e non vengono trasmessi (restano presso il server). E' comunque essenziale reimplementare i metodi
equals e hashcode. Ecco il codice XorServerSocketFactory.java.

package socketfactory;

import java.io.*;
import java.net.*;
import java.rmi.server.*;

public class XorServerSocketFactory
implements RMIServerSocketFactory {

private byte pattern;

public XorServerSocketFactory(byte pattern) {
this.pattern = pattern;
}

public ServerSocket createServerSocket(int port)
throws IOException
{
return new XorServerSocket(port, pattern);
}

public int hashCode() {
return (int) pattern;
}

public boolean equals(Object obj) {
return (getClass() == obj.getClass() &&
pattern == ((XorServerSocketFactory) obj).pattern);
}



}

4) Usiamo una custom socket factory in un'applicazione.

4.1) Scriviamo allora un'applicazione che crea ed esporta un server remoto usando le
socket factory appena definite. Prenderemo l'esempio visto nella prima lezione.
Diamo l'interfaccia Hello.java.

package socketfactory;
      import java.rmi.Remote;
      import java.rmi.RemoteException;

      public interface Hello extends Remote {
       String sayHello() throws RemoteException;
       String givemeFive()
throws RemoteException;
     }

Il codice della classe del server HelloImpl.java e' di seguito. Si noti come non
viene estesa direttamente la classe UnicastRemoteObject in quanto il server verra'
esportato esplicitamente attraverso il metodo exportObject ed utilizzando i nuovi
sockets.


package socketfactory;
   
import java.rmi.Naming;
    import java.rmi.RemoteException;
    import java.rmi.RMISecurityManager;
    import java.rmi.server.*;


    public class HelloImpl implements Hello {

        public HelloImpl() throws RemoteException {
            super();
        }

        public String sayHello() {
            return  "Hello World:  questa frase proviene dall'invocazione del metodo sayHello
                       del server remoto";
        }

       public String givemeFive() {
            return  "FIVE!!!!";
        }


        public static void main(String args[]) {

            // Crea ed installa un security manager.
            if (System.getSecurityManager() == null) {
                System.setSecurityManager(new RMISecurityManager());
            }
            byte pattern = (byte) 0xAC;
            try {
               
// Creo un'istanza del server remoto
                Hello obj = new HelloImpl();                          
                // Creo le socket factory
               
RMIClientSocketFactory csf = new XorClientSocketFactory(pattern);
                RMIServerSocketFactory ssf = new XorServerSocketFactory(pattern);
               
Hello stub =
                          (Hello) UnicastRemoteObject.exportObject(obj, 0, csf, ssf);
                Naming.rebind("//:2378/HelloServer", obj);  
                // faccio una rebind del registro alla porta 2378.
                //Se il numero di porta non fosse stato indicato, di default sarebbe
                // stato lanciato, per default, alla porta 1099.
               
// Nel registro RMI il nome "HelloServer" viene associato allo stub dell'istanza dell'oggetto remoto creato.

                System.out.println("L'oggetto remoto HelloServer e' stato registrato nel registro RMI");
               } catch (Exception e) {
                System.out.println("HelloImpl err: " + e.getMessage());
                e.printStackTrace();
            }
        }
    }


5) Fate la compilazione javac di tutto il codice presente nella cartella del server.

6) Fate la compilazione rmic -d /home/...../public_html/common/  socketfactory.HelloImpl
nella directory javarmi.

7) Copiare nel codebase tutte le socket factory, e l'interfaccia remota Hello.class.

8) A questo punto lanciate dalla vostra home:   rmiregistry 2378 &

9) Infine lanciate il codice client presso la macchina cliente. Prendete il codice client
dell'esercizio 1 avendo cura di cambiare il package nel codice,
mettendo socketfactory invece di hello.