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.