View Javadoc

1   /*
2    * Copyright 2000 Computer System Services, Inc.
3    *
4    * Permission to use this software for any purpose is granted provided that
5    * this copyright notice is preserved.
6    *
7    * This software is provided as-is and without warranty as to its
8    * fitness for any purpose.  In other words, Computer System Services,
9    * Inc. does not guarantee that this software works.  It is provided
10   * only in the hope that it may be found useful by someone.
11   *
12   * Please e-mail tttaylor@cssassociates.com if you find any errors
13   * or want to request changes/enhancements.
14   */
15  package twcsckernel.projectbase.rmi.socketfactory;
16  
17  import java.io.IOException;
18  import java.net.ServerSocket;
19  import java.net.Socket;
20  import java.rmi.server.RMISocketFactory;
21  import java.util.Collections;
22  import java.util.HashMap;
23  import java.util.Map;
24  
25  /***
26   * Socket factory for clients who want servers to be able to call them
27   * back even if the client's address/port are not reachable from the
28   * server.  This happens with firewalls and dial-up networking, for
29   * example. <p>
30   *
31   * The factory provides two main capabilities: <p>
32   * <ul>
33   * <li>A gateway registry for going through firewalls</li>
34   * <li>A signalling channel for establishing callback sockets</li>
35   * </ul>
36   * <p>
37   * The gateway registry allows a mapping to be made between two
38   * endpoints.  For example, the endpoint cssassociates.com:3453 can
39   * be mapped to somewhereElse.com:2356.  Client connections made
40   * to the first endpoint from RMI will actually be connected to the
41   * second endpoint.  This situation occurs when a port on a firewall
42   * is "opened" and forwarded to a host within the firewall. <p>
43   *
44   * A signalling channel is a socket from the client to the server
45   * and carries the "two-way" protocol (for lack of a better name).
46   * The client establishes a signalling channel to the server,
47   * which must be running the ServerTwoWaySocketFactory.  When the server
48   * needs to make a callback to the client, it requests a socket via
49   * the channel instead of trying to directly connect to the client.
50   * The client establishes a socket to the server for the server to
51   * use in the callback.
52   *
53   * @author Tim Taylor  -- tttaylor@cssassociates.com
54   */
55  public class ClientTwoWaySocketFactory extends RMISocketFactory {
56      /*** Registered gateways */
57      private Map<String,EndpointInfo> gatewayMap = Collections.synchronizedMap(new HashMap<String,EndpointInfo>());
58  
59      /***
60       * Signalling channel used to handshake with server.  Currently
61       * supports one server and one channel.  I.e., each client
62       * can have only one server (without modifying this code
63       * to support multiple channels).  However, the server
64       * can have as many clients as it wants.
65       */
66      private SignallingChannel channel;
67      
68      /***
69       * Register a gateway.  Client connections requested via
70       * <code>createSocket</code> for <code>host:port</code> will
71       * instead be made to <code>gateHost:gatePort</code>
72       * @param host The destination host.
73       * @param port The destination port.
74       * @param gateHost The gateway host (proxy, firewall, etc.).
75       * @param gatePort The gateway port.
76       */
77      public void registerGateway(String host, int port,
78                                  String gateHost, int gatePort) {
79          gatewayMap.put(EndpointInfo.getEndpointString(host, port),
80                         new EndpointInfo(gateHost, gatePort));
81      }
82  
83      /***
84       * Establish a two-way protocol signalling channel to the
85       * server at <code>
86       */
87      public void establishSignallingChannel(String address, int port)
88          throws IOException {
89  
90          channel = new SignallingChannel(address, port);
91  
92          // The direct host/port and destination host/port may
93          // or may not be the same.  The will be different
94          // if we are initially connecting to a port forwarding
95          // firewall, SOCKS, or whatever.
96          registerGateway(channel.getDestinationAddress(),
97                          channel.getDestinationPort(),
98                          channel.getDirectAddress(),
99                          channel.getDirectPort());
100         
101         channel.start();
102     }
103 
104     /***
105      * Returns the endpoint associated with the <code>host</code>
106      * and <code>port</code>.  The endpoint returned is the endpoint
107      * to be directly contacted.  If the <code>host</code> and
108      * <code>port</code> have an associated gateway endpoint, that
109      * endpoint is returned.  Otherwise an endpoint for the provided
110      * <code>host</code> and <code>port</code> is returned.
111      *
112      * @return The endpoint to be connected to.
113      */
114     public EndpointInfo getDirectEndpoint(String host, int port) {
115         EndpointInfo endpointInfo = (EndpointInfo)
116             gatewayMap.get(EndpointInfo.getEndpointString(host, port));
117 
118         if (endpointInfo != null) {
119             return endpointInfo;
120         }
121         else {
122             return new EndpointInfo(host, port);
123         }
124     }
125 
126     /***
127      * Creates a socket to the specified <code>host</code> and <code>
128      * port</code>.  If a gateway has been registered using
129      * <code>registerGateway</code> then the socket will be connected
130      * to that gateway.  Otherwise, the socket is connected directly
131      * to the specified <code>host</code> and <code>port</code>.
132      *
133      * @see #registerGateway()
134      */
135     public Socket createSocket(String host, int port) throws IOException {
136         EndpointInfo ep = getDirectEndpoint(host, port);
137 
138         return new Socket(ep.getHost(), ep.getPort());
139     }
140 
141     /***
142      * @return a server socket for the specified port
143      */
144     public ServerSocket createServerSocket(int port) throws IOException {
145         ServerSocket sock = new ServerSocket(port);
146         if (channel != null) {
147             // Inform server we are exporting a callback
148             channel.getOutputStream().writeInt(sock.getLocalPort());
149         }
150         return sock;
151     }
152 }
153 
154 
155 
156