This discussion is archived
1 2 Previous Next 15 Replies Latest reply: Feb 6, 2011 11:11 PM by EJP RSS

RMI through firewall, ConnectException, force HTTP

ahaubold Newbie
Currently Being Moderated
Hi,

I am hoping to get some suggestions on RMI through a firewall. The client/server RMI application I have implemented already works fine between computers without firewall restrictions.

I would like to know whether (and how) it is possible to force an RMI HTTP connection over port 80 (java-rmi.cgi), perhaps by creating a custom RMIClientSocketFactory. The reason for forcing it is explained below:

The setup:
1. RMI Server: no firewall; freely accessible
2. RMI Client: behind very restrictive firewall. Able to connect to outside port 80 for HTTP traffic though. Simulating connection to RMI Server via "telnet HOSTNAME:PORT" results in "Connection refused". (this is the firewall's doing, not the server; HTTP traffic flows freely when contacting outside port 80)

Exception on client during RMI connection behind restrictive firewall:

[2011-01-20 09:58:15] Connection refused to host: HOSTNAME; nested exception is:
     java.net.ConnectException: Connection refused
sun.rmi.transport.tcp.TCPEndpoint.newSocket(Unknown Source)
sun.rmi.transport.tcp.TCPChannel.createConnection(Unknown Source)
sun.rmi.transport.tcp.TCPChannel.newConnection(Unknown Source)
sun.rmi.server.UnicastRef.newCall(Unknown Source)
sun.rmi.registry.RegistryImpl_Stub.lookup(Unknown Source)
java.rmi.Naming.lookup(Unknown Source)
...

I've read up on RMI through firewall (http://download.oracle.com/javase/1.5.0/docs/guide/rmi/spec/rmi-arch6.html). I understand that using the default implementation of RMISocketFactory, the connection is attempted in the following tiered structure:
1. Direct socket connection.
2. HTTP connection with a direct socket, if: java.net.NoRouteToHostException or java.net.UnknownHostException
3. If other exception, such as a java.net.ConnectException, the implementation may attempt an HTTP connection.

Clearly, I'm dealing with a "ConnectException", so tier #3 applies. However, I'm having some difficulty with the language: "... _may_ attempt ...". So it may or may not. In my case, I believe it does not. I don't see corresponding attempts for connections in the server's Apache logs (looking for "java-rmi.cgi").

From my Apache logs, I see that my application was used by folks who have some firewall restrictions, and for those connections, an HTTP connection over port 80 was attempted (log entry: POST /cgi-bin/java-rmi.cgi?forward ...). So I see that the 3-tiered structure with a resulting HTTP port 80 connection does work in general for my application.

If the client may or may not attempt the HTTP port 80 connection, I would like to try to now force it, so that there is no more possibility for "_may not_".

I appreciate your help.

Thank you.

Edited by: 829888 on Jan 20, 2011 12:46 PM
  • 1. Re: RMI through firewall, ConnectException, force HTTP
    EJP Guru
    Currently Being Moderated
    You don't need a custom socket factory for that. There's a system property for HTTP only.
  • 2. Re: RMI through firewall, ConnectException, force HTTP
    ahaubold Newbie
    Currently Being Moderated
    Hi,

    I see that there exists a property by which HTTP can be disabled (java.rmi.server.disableHttp=true|false). Are you referring to the property http.proxyHost? I have tried setting it to the hostname of the server, which is host to the RMI server (not behind a firewall). Unfortunately, without success.

    I have now implemented a different socket factory on the client: RMISocketFactory.setSocketFactory(new sun.rmi.transport.proxy.RMIHttpToCGISocketFactory());

    On an otherwise unrestricted client, this solution works as flawlessly. I can see appropriate calls to java-rmi.cgi on the server, and the connection is established without problems. On my test client machine, which is behind a firewall, I still am unable to connect to the RMI server. When I monitor the web server's log, I see appropriate entries for java-rmi.cgi, but on the client side, I get a "Connection refused" exception:

    [2011-01-25 10:03:51] Connection refused to host: HOSTNAME; nested exception is:
         java.net.ConnectException: Connection refused
    sun.rmi.transport.tcp.TCPEndpoint.newSocket(Unknown Source)
    sun.rmi.transport.tcp.TCPChannel.createConnection(Unknown Source)
    sun.rmi.transport.tcp.TCPChannel.newConnection(Unknown Source)
    sun.rmi.server.UnicastRef.invoke(Unknown Source)
    ........Server_Stub.acceptsConnections(Server_Stub.java:169)

    Does someone have an idea what could be malfunctioning, when I am able to connect to port 80 of web servers, browse web pages, watch YouTube videos, etc., but am unable to establish an HTTP connection over port 80 to the RMI Server?

    What I have also noticed, and what I would very much like to have an expert comment on, is the following: When I successfully tested RMI over HTTP on the client without a firewall, I noticed that only a few initial calls to java-rmi.cgi were made. On subsequent RMI calls, I found no entries in the web server's log, but clearly, remote methods were called and data was exchanged. Is java-rmi.cgi only used to establish a connection once, after which it remains persistent? Could this create any reasonable problem for a client behind firewall?

    Thank you for your time and help.
  • 3. Re: RMI through firewall, ConnectException, force HTTP
    EJP Guru
    Currently Being Moderated
    Sorry. I was referring to disableHttp, got it back to front. Re your second question, RMI does connection pooling, so if you're just logging connects via java-RMI.cgi you won't see as many as you think. NB I assume you are using the RMI CGI servlet?
  • 4. Re: RMI through firewall, ConnectException, force HTTP
    ahaubold Newbie
    Currently Being Moderated
    Hi,

    Thanks for your response.

    At the moment, I am looking only at the Apache logs for relevant java-rmi.cgi entries. I am in fact using the java.rmi.cgi script part of the Java distribution. However, I am running it as a CGI script in Apache, and not in a Servlet environment, such as Apache Tomcat. Is it necessary to run it in a Tomcat environment? Is there a need to maintain state outside of the RMI server, i.e. in the servlet?

    As I had mentioned, when I use the RMI client from an otherwise unrestricted host, the connect to java-rmi.cgi works out fine. Only behind the firewall is it still not working.

    What other logging can I perform, besides Apache and exceptions?

    I would also like to note the following: For direct port connections to the RMI server, the client was unable to make any connection and failed at Naming.lookup(). With the HTTP connection, it looks like the connection exception occurs after Naming.lookup(), i.e. when I attempt to invoke a method remotely. Perhaps a step in the right direction?

    Thank you for your comments thus far - I'd love to hear more about how I could tackle this issue.
  • 5. Re: RMI through firewall, ConnectException, force HTTP
    EJP Guru
    Currently Being Moderated
    I was under the impression that the cgi wasn't distributed any more. If it is, ignore me and use it.

    I'm unclear what your current status is. Can you post the stack trace for the current failure with the cgi script?
  • 6. Re: RMI through firewall, ConnectException, force HTTP
    ahaubold Newbie
    Currently Being Moderated
    Hi,

    The current status is:

    Naming.lookup(...) appears to succeed. No exception there.

    Upon first remote method invocation, the following exception is thrown:

    Connection refused to host: <FULLY QUALIFIED DOMAIN NAME>; nested exception is:
         java.net.ConnectException: Connection refused
    sun.rmi.transport.tcp.TCPEndpoint.newSocket(Unknown Source)
    sun.rmi.transport.tcp.TCPChannel.createConnection(Unknown Source)
    sun.rmi.transport.tcp.TCPChannel.newConnection(Unknown Source)
    sun.rmi.server.UnicastRef.invoke(Unknown Source)
    myapp.server.Server_Stub.acceptsConnections(Server_Stub.java:169)
    myapp.client.Client.areYouThere(Client.java:119)
    myapp.client.Client$3.construct(Client.java:273)
    myapp.SwingWorker$2.run(SwingWorker.java:108)
    java.lang.Thread.run(Unknown Source)

    On the server side, the Apache log reads:

    207.171.180.101 - - [27/Jan/2011:09:24:14 -0500] "POST /cgi-bin/java-rmi.cgi?forward=12345 HTTP/1.1" 200 406 "-" "Java/1.6.0_13"
    207.171.180.101 - - [27/Jan/2011:09:24:16 -0500] "POST /cgi-bin/java-rmi.cgi?forward=12345 HTTP/1.1" 200 1 "-" "Java/1.6.0_13"

    where port 12345 is the port my RMI Registry is running on.

    Thanks.
  • 7. Re: RMI through firewall, ConnectException, force HTTP
    EJP Guru
    Currently Being Moderated
    It's curious that your socket factory doesn't appear in the stack trace.

    (a) are your remote objects also exported on port 12345?
    (b) do they use RMIClientSocketFactories?
    (c) are the host and port displayed in the exception what you expected?
  • 8. Re: RMI through firewall, ConnectException, force HTTP
    ahaubold Newbie
    Currently Being Moderated
    Hi,

    Interesting observations.

    Re 1. I'm not sure. I've implemented the RMIHttpToCGISocketFactory only on the Client, but not on the Server. On the Server, I am using the standard RMI Socket Factory. Is that incorrect? If so, how should the Server's RMI Socket Factory look like?
    Re 2. Again, not sure. With the standard RMI Socket Factory implementation, I figured that the server sets up some Client Socket Factory.
    Re 3. The exception mentions only the host, but not a port. The hostname is correct though.

    Thanks for all your help!
  • 9. Re: RMI through firewall, ConnectException, force HTTP
    EJP Guru
    Currently Being Moderated
    Re 1 you haven't answered the question. The port is a separate parameter from the socket factory.
    Re 2 I do not understand.

    Time to post some code. Post the constuctor of your remote object. If it doesn't extend UnicastRemoteObject also post the line where you call UnicastRemoteObject.exportObject().
  • 10. Re: RMI through firewall, ConnectException, force HTTP
    ahaubold Newbie
    Currently Being Moderated
    Hi,

    Re 1. Sorry; indeed I haven't answered the question. Remote objects are also exported on port 12345.

    Code excerpt:
    HOSTNAME = fully qualified host name, firewall-free

    Server, uses default RMISocketFactory:
    public class Server extends UnicastRemoteObject implements ServerInterface {
      public Server() {
        try {
          Naming.rebind("//HOSTNAME:12345/MyService", this);
        } catch (Exception e) {}
      }
      public String remoteMethod() throws RemoteException {
        return "Hello client!";
      }
    }
    Client, forced to use HTTP/CGI
    public class Client {
      public Client() {
        try {
          RMISocketFactory.setSocketFactory(new sun.rmi.transport.proxy.RMIHttpToCGISocketFactory());
          ServerInterface serverInterface = (ServerInterface)Naming.lookup("//HOSTNAME:12345/MyService");
          String s = serverInterface.remoteMethod();
        } catch (Exception e) {}
      }
    }
    Please let me know if I should post more code; I made an excerpt of what seemed the most important.

    Thank you.
  • 11. Re: RMI through firewall, ConnectException, force HTTP
    EJP Guru
    Currently Being Moderated
    Re 1. Sorry; indeed I haven't answered the question. Remote objects are also exported on port 12345.
    No they're not. See below.
    public class Server extends UnicastRemoteObject implements ServerInterface {
    public Server() {
    super(12345);
    try {
    Naming.rebind("//HOSTNAME:12345/MyService", this);
    That's binding, not exporting.
    } catch (Exception e) {}
    I assume you're not really ignoring exceptions? here and in the client?

    Re the client, you only need to set the socket factory once for the life of the JVM. You could even define it at the server via:
    super(12345, new sun.rmi.transport.proxy.RMIHttpToCGISocketFactory(), null);
    and removing the setSocketFactory from the client completely, if that suits your toplogy (i.e. if all clients will need the CGI only).
  • 12. Re: RMI through firewall, ConnectException, force HTTP
    ahaubold Newbie
    Currently Being Moderated
    I've modified the Server code to
    super(12345);
    Now, when starting the server, I receive an exception that the port is already in use. RMIRegistry is running on 12345.
    I then tried to use a different port, 12346:
    super(12346);
    The server starts fine and once again works with the client in a non-firewall environment. When the client is behind the firewall, the same exception as before is thrown: "java.net.ConnectException: Connection refused". At the same time, I see log entries in the server's Apache log: " ... POST /cgi-bin/java-rmi.cgi?forward=12345 HTTP/1.1" 200 1 "-" "Java/1.6.0_13".

    From your earlier question "are your remote objects also exported on port 12345?" it seems as though I should be able to use the same port as the RMI Registry. How can I do that? In order to use get communication between server and firewalled client to work, do remote objects have to be exported on the same port as the RMI Registry? Does forwarding with java-rmi.cgi not work on other ports? I thought that the process was similar to loading a website: contact web server on port 80 and establish a connection on a different port (likely a high port) for data transfer to the client. I would appreciate your insight into this issue.

    Re: exceptions: I'm not ignoring the exceptions on the client or the server - just didn't include the code for brevity.

    For now, I've kept "new sun.rmi.transport.proxy.RMIHttpToCGISocketFactory()" in the client only and not in the server.

    Thank you.

    Edited by: ahaubold on Feb 4, 2011 1:33 PM
  • 13. Re: RMI through firewall, ConnectException, force HTTP
    EJP Guru
    Currently Being Moderated
    Now, when starting the server, I receive an exception that the port is already in use. RMIRegistry is running on 12345.
    So you need to use a different port, unless you are starting the Registry in the same JVM via LocateRegistry.createRegistry().

    It's curious that you don't see any log entries for java-rmi.cgi with port 123456. That means that the setSocketFactory() method hasn't taken effect for some reason. Can you post all the client code including that, the lookup, and the method call?

    I would still like you to try this:
    super(123456, new sun.rmi.transport.proxy.RMIHttpToCGISocketFactory(), null);
  • 14. Re: RMI through firewall, ConnectException, force HTTP
    ahaubold Newbie
    Currently Being Moderated
    Hi,

    I will post some code in a day or so.

    In the meantime, I've tried this in the Server:
    super(123456, new sun.rmi.transport.proxy.RMIHttpToCGISocketFactory(), null);
    When starting the server, I get the following exception:

    error marshalling arguments; nested exception is:
    java.io.NotSerializableException: sun.rmi.transport.proxy.RMIHttpToCGISocketFactory
    sun.rmi.registry.RegistryImpl_Stub.rebind(Unknown Source)
    java.rmi.Naming.rebind(Naming.java:160)
    ...
1 2 Previous Next

Legend

  • Correct Answers - 10 points
  • Helpful Answers - 5 points