RMI Through a Firewall

If you ever tried using RMI through a firewall you must have noticed a ‘strange’ behaviour concering the usage of ports.
Opening the rmi port (in most cases this is 1099) in the firewall, doesn’t solve the problem.
As you may already know, your RMI Server opens up a serversocket on port 1099 and then listens here for incoming requests. If a request comes in, ANOTHER port is used to handle the request and send back the response. In the default implementation, this is done on port 0. (any available port, chosen (randomly?) by the underlying OS)

Let’s try it
So, how can we test this ? Well, we’ll write a custom RMI Socket Factory.
First, we’ll make a simple RMI Server :

String serverObjectName = “server”;
try {
UnicastRemoteObject.exportObject(this);
Naming.rebind(serverObjectName, this);
} catch (RemoteException e) {
e.printStackTrace();
} catch (java.net.MalformedURLException e) {
e.printStackTrace();
}

This code snippet simply exports ‘this’ as a remote object and binds it’s name in the rmiregistry. (which is assumes to be running) When you run this code, the default RMISocketFactory is used for handling the connections (ports). We wish to use our own RMISocketFactory to control the used ports.
Let’s start by creating an RMISocketFactory:

package server;

import java.rmi.server.RMISocketFactory;
import java.net.Socket;
import java.net.ServerSocket;
import java.io.IOException;

/*
* User: Tim Goffings
* Date: Oct 3, 2002 – 3:51:34 PM
*/

public class FixedPortRMISocketFactory extends RMISocketFactory {

/**
* Creates a client socket connected to the specified host and port and writes out debugging info
* @param host the host name
* @param port the port number
* @return a socket connected to the specified host and port.
* @exception IOException if an I/O error occurs during socket creation
*/
public Socket createSocket(String host, int port)
throws IOException {
System.out.println(“creating socket to host : ” + host + ” on port ” + port);
return new Socket(host, port);
}

/**
* Create a server socket on the specified port (port 0 indicates
* an anonymous port) and writes out some debugging info
* @param port the port number
* @return the server socket on the specified port
* @exception IOException if an I/O error occurs during server socket
* creation
*/
public ServerSocket createServerSocket(int port)
throws IOException {

System.out.println(“creating ServerSocket on port ” + port);
return new ServerSocket(port);

}
}

If use this in your rmi server, you see something like the following printed out to your console :

> creating ServerSocket on port 1099
> creating ServerSocket on port 0
> creating socket to host : 136.131.243.125 on port 1099
> creating socket to host : 136.131.243.125 on port 1505

If you abort and run again, you’ll notice that port 1505 changes almost every time you try it. So it’s impossible to know what port to open in your firewall between the client and server.

Let’s solve it!
The solution is simple. Now that we can control the creation of the sockets, we can intercept the default usage of port 0 and replace it with our own implementation. For example, we change the: Socket createSocket(String host, int port) method to:

public ServerSocket createServerSocket(int port)
throws IOException {
port = (port == 0 ? 1098 : port);
System.out.println(“creating ServerSocket on port ” + port);
return new ServerSocket(port);

}

So, instead of using some ‘random’ open port, we use port 1089.
One more step has to be completed to use our custom FixedPortRMISocketFactory and this is before you export your object, using this code:

try {
RMISocketFactory.setSocketFactory(new FixedPortRMISocketFactory());
} catch (IOException e) {
e.printStackTrace();
}

by Tim Goffings