A character on the TV show "L.A. Law" once said, "It was the 60s. Safe sex meant keeping the parking brake on." In the early days of public networks security carried a similar sense of urgency. In fact, the network was its own best defense. One of the authors remembers accommodating a colleague by spending an afternoon plotting the hops, gateways, and contorted syntax necessary to send an e-mail from Brooklyn to Michigan. (How many computer scientists does it take to send an e-mail?)
Those unlamented days have given way to a world in which an early protocol like FTP, which, in active mode, requires a data transfer connection from the server to an anonymous port on the client, seems either charmingly naive or shockingly promiscuous (depending, perhaps, on when you were born). Of course, to be fair, passive mode, in which the client makes the data connection, was introduced by 1973, but we'll focus on the active mode as a case study in using virtual sockets to overcome, safely, the restrictions imposed by firewalls.
JBoss Remoting
Virtual sockets and their relatives are provided by the Multiplex sub-project of the JBoss Remoting project. JBoss Remoting is a general-purpose framework for distributed method invocations and asynchronous callbacks, featuring pluggable transports, marshallers, and object serializers. It currently ships with eight built-in transports, four marshallers, and two serializers, and custom versions are easily integrated.
Two of the primary design goals of Remoting, which is envisioned as the unified communications framework for the JBoss Enterprise Middleware Suite (JEMS) of software products, including the JBoss Application Server, are (1) to support a variety of communication models, and (2) to allow any combination of pluggable components to be exchanged for any other purely through configuration changes, leaving the application code untouched.
A variety of protocols are implemented at the transport level, each by a pair of descendants of the RemoteClientInvoker and ServerInvoker classes, which, as their names imply, run on the client and server, respectively. These pairs of classes work together to transmit a method invocation from the client to an application-specific handler on the server and return the hander's response to the calling program on the client. Together they constitute an invoker. For example, the socket invoker is comprised of the SocketClientInvoker and SocketServerInvoker classes, and HTTPClientInvoker and HTTPServerInvoker make up the HTTP invoker. Callbacks are handled by the same components with an inversion of client and server roles.
The Multiplex Sub-Project
A Remoting feature request, submitted by the JBoss JMS group, was for an invoker that supports multiplexed network connections, allowing the server to send callbacks to the client on a network connection initiated by the client. The request was motivated by the need to support asynchronous message consumption in the context of firewalls configured to prohibit incoming connection requests on non-well known ports, thereby defeating a server's ability to make callbacks. Although the request was just for an invoker with multiplexing abilities, a design decision was made to push multiplexing down to the socket level, which, since all of the other transports ultimately use sockets, extends the multiplexing feature to other transport protocols. From this feature request and design decision came the Multiplex sub-project.
Public Multiplex Classes
From the user's perspective, the core of the Multiplex subsystem is a set of classes derived from their counterparts in the java.net and javax.net packages, including a virtual socket, two server socket implementations, input and output streams, and factories for virtual sockets and virtual server sockets. The primary design goal was to make these classes as functionally similar as possible to their non-multiplexing parents, so that, for example, a virtual socket could be passed without disruption to a method expecting a real socket, and for most of the classes this goal is largely satisfied. For example, the VirtualSocket.getInputStream() method returns a multiplexing input stream that supports read timeouts. The main source of complexity arises in the treatment of server sockets, which we discuss in the next section.
Virtual Socket Groups
For server sockets the situation is more complicated. Multiplexed connections are built using real sockets, so we need a server socket that can create real sockets. On the other hand, we need a server socket that can create virtual sockets. The classes MasterServerSocket and VirtualServerSocket, both derived from java.net.ServerSocket, fulfill these two roles, respectively.
Multiplexing a single TCP/IP connection between two actual sockets s1 and s2 is achieved by commingling the communications of multiple pairs (v1, v'1), ..., (vn, v'n) of virtual sockets. The sets {v1,...,vn} and {v'1,...,v'n} are called virtual socket groups, where the members of one set are based on s1 and the members of the other are based on s2. These sets are denoted G(s1) and G(s2), respectively.
A virtual socket group is built in two stages, (1) creating the actual socket on which it's based and installing its first member, and (2) populating it with additional virtual sockets, and each stage is affected by the interaction of a virtual socket constructor with an instance of one of the two virtual server socket classes. The first stage involves the creation of a real socket and, accordingly, the accept() method of the MasterServerSocket inherits much of its functionality from super.accept(). When a virtual socket constructor finds no existing virtual socket group to join and it connects to a MasterServerSocket, the following sequence of events occurs:
The constructor creates an actual socket s1 and connects it to the MasterServerSocket;
The MasterServerSocket accepts the connection, creates an actual socket s2, builds the infrastructure of a virtual socket group G(s2) based on s2, creates a virtual socket v'1, and adds v'1 to G(s2);
The constructor builds the infrastructure of a virtual socket group G(s1) based on s1, creates a virtual socket v1 connected to v'1, and adds v1 to G(s1).
Note that each connection to a MasterServerSocket results in the creation of a pair of new virtual socket groups. If the socket groups have to grow beyond their initial members, then one or both must be joined by a VirtualServerSocket, which is done by creating it and binding it to the port to which the underlying actual socket is bound. For example, suppose s1 is bound to bluemonkey.acme.com:6060 and s2 is bound to demo.jboss.com:8080. Then executing VirtualServerSocket vss = new VirtualServerSocket(6060); vss.accept(); on bluemonkey.acme.com will add a VirtualServerSocket to G(s1) and open it up for business. A subsequent constructor call VirtualSocket v = new VirtualSocket("bluemonkey.acme.com", 6060); made on demo.jboss.com will add a new pair of communicating virtual sockets to G(s1) and G(s2).
The difference between the two classes of server sockets may be expressed succinctly by categorizing their public methods.
MasterServerSocket
The methods accept() and toString() are implemented directly. All other public methods are inherited except for acceptServerSocketConnection(), which is specific to Multiplex.
VirtualServerSocket
The methods accept(), bind(), close(), getSoTimeout(), setSoTimeout(), isBound(), isClosed(), and toString() are implemented in the class. getInetAddress(), getLocalPort(), getLocalSocketAddress(), getReceiveBufferSize(), getReuseAddress(), setReceiveBufferSize(), and setReuseAddress() are delegated to the underlying socket. getChannel() returns null, and the methods connect() and isConnected() are specific to Multiplex. There is also a constructor with the signature public VirtualServerSocket(InetSocketAddress remoteAddress, InetSocketAddress localAddress, int timeout), which allows a new VirtualServerSocket to bind and connect at the same time.
The striking thing here is the existence of the delegated methods and connection-oriented methods in the VirtualServerSocket class, both of which seem out of place for a server socket. In fact, they reveal the true, rather hybrid, nature of VirtualServerSocket, which uses a real socket to implement the behavior of a server socket. The important functional implication is that, like a socket, a VirtualServerSocket has a connection to another host, and so it can accept connect requests only from virtual sockets on that host.
The rules for creating and joining socket groups are spelled out in detail in the Multiplex documentation (see http://labs.jboss.com/portal/jbossremoting/docs/multiplex/multiplex.html), including a discussion of the accidental connection problem, which necessitates the constructors and methods that support binding and connecting in a single atomic action.
The Prime Scenario
The typical application of multiplexing in a Remoting context is illustrated by the Prime Scenario in which a client requiring both synchronous and asynchronous responses from a server is behind a firewall blocking connections to anonymous ports. Without the firewall, we would have the situation in Figure 1, which requires no multiplexing. With the firewall we have to construct the Prime Scenario as in Figure 2.
Every Muliplex scenario begins with a MasterServerSocket that creates the underlying actual socket. Figure 3 illustrates the first step in creating the Prime Scenario, in which a virtual socket v1 connects to a MasterServerSocket that creates and returns a reference to a new virtual socket v2.
In Figure 3 we have a connection between v1 and v2, which can support synchronous communication but which offers nothing not provided by actual sockets. To create a second connection for callbacks, we add a VirtualServerSocket to the client's socket group. The second step in constructing the Prime Scenario is illustrated in Figure 4, in which the constructor (or factory method, which calls a constructor) is called on the server to create virtual socket v3 to support callbacks. The constructor sends a connection request to the VirtualServerSocket on the client, which creates new virtual socket v4 and sends a reference to v4 back to v3. At this point we have a socket group on the client with two virtual sockets and a VirtualServerSocket, a socket group on the server with two virtual sockets, and the Prime Scenario is set up.
FTP Case Study
As a test of the usability of the Multiplex system, we modified an FTP client and an FTP server, both Open Source, so that an active mode data connection could be opened from the server to the client reusing the control connection made from the client to the server. The idea was to use existing code to see how disruptive the necessary changes would turn out to be. For the client we chose the Apache Jakarta Commons Net library (http://jakarta.apache.org/commons/net/) and for the server we chose the Apache FTP server (http://incubator.apache.org/ftpserver/index.html). The Jakarta Commons Net library doesn't have a complete FTP client, just the classes necessary to build a client and a sample class, examples.ftp, which logs into an FTP server and transfers a single file, so that's what we used.
We made the changes in Listing 3 for the client. Note that red lines were deleted, green lines were inserted, and the yellow lines are particularly relevant to our discussion.
And we can make the following observations about our experiment:
Multiplex works. We were able to create a control connection and a data connection over a single shared TCP/IP connection.
The changes were fairly minor and localized, restricted almost, though not quite, to swapping in virtual sockets and server sockets to replace their real counterparts. There were two variations on these changes.
- Changing the client's server socket and the server's client socket involved adding classes and modifying code to replace explicit references to their respective classes.
- Changing the server's server socket was more pleasant, since it involved adding a new class and changing a configuration file.
It should be noted that, had the code been more configurable, none of these changes would have required changes to existing code.
The code change that configures the client to run in active mode would be unnecessary for a real FTP client, since the mode is settable at runtime by user command.
The one code change that seems to be unavoidable in the current release is the one that requires the client's ser-ver socket to bind to the port used by the con-trol connection, since that's how the VirtualServerSocket is told to join the existing virtual socket group.
Conclusion
The Multiplex sub-project of JBoss Remoting provides multiplexing versions of various classes in the standard java.net and javax.net packages, allowing multiple virtual network connections to share a single real TCP/IP connection. Though they were developed to support a JBoss Remoting feature request, these classes can be applied independently and, in particular, they can be used to open connections to anonymous ports in the presence of a firewall while violating neither the letter nor the spirit of any security policy.
For this article we selected an Open Source FTP client and an Open Source FTP server and modified them slightly, replacing standard sockets and server sockets with their virtual counterparts. We found that if the target code is sufficiently configurable, the use of socket and server socket factories can almost eliminate the need to change existing code.
We found one necessary code change that can't be configured away. We can derive from this observation that a desirable feature for the next JBoss Remoting release would be default behavior that allows a VirtualSocket or VirtualServerSocket that binds to port 0 to join an arbitrary existing socket group. In the current case the new VirtualServerSocket would find exactly one existing socket group and join it, which is just what we want.
Which shows that there's no substitute for kicking the tires and taking it for a drive.
About Tom Elrod Tom Elrod is a core developer for JBoss, Inc and lead of the JBoss Remoting project.
About Ron Sigal Ron Sigal has taught mathematical logic and computer science at fine colleges and universities everywhere. A co-author of ""Computability, Complexity, and Languages, 2nd edition" (in use on five or more continents), he is proud to be a JBoss developer.
SUBSCRIBE TO THE WORLD'S MOST POWERFUL NEWSLETTERS
SUBSCRIBE TO OUR RSS FEEDS & GET YOUR SYS-CON NEWS LIVE!
Click to Add our RSS Feeds to the Service of Your Choice: