KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > sslexplorer > agent > client > tunneling > LocalTunnelServer


1 /*
2  * SSL-Explorer
3  *
4  * Copyright (C) 2003-2006 3SP LTD. All Rights Reserved
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License
8  * as published by the Free Software Foundation; either version 2 of
9  * the License, or (at your option) any later version.
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public
16  * License along with this program; if not, write to the Free Software
17  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18  */

19             
20 package com.sslexplorer.agent.client.tunneling;
21
22 import java.io.IOException JavaDoc;
23 import java.net.DatagramSocket JavaDoc;
24 import java.net.InetAddress JavaDoc;
25 import java.net.ServerSocket JavaDoc;
26 import java.net.Socket JavaDoc;
27 import java.util.Enumeration JavaDoc;
28 import java.util.Vector JavaDoc;
29
30 import com.maverick.multiplex.Channel;
31 import com.maverick.multiplex.channels.LocalForwardingChannel;
32 import com.sslexplorer.agent.client.Agent;
33 import com.sslexplorer.agent.client.util.IOStreamConnectorListener;
34 import com.sslexplorer.agent.client.util.TunnelConfiguration;
35
36
37 /**
38  * Sets up maintains a single listening server socket to support <i>Local
39  * Tunnels</i>.
40  * <p>
41  * This listener will accept connections from either only localhost or from
42  * any host (depending on how the tunnel was configured) and forward them
43  * to the SSl-Explorer server the Agent is connected to.
44  * <p>
45  * When constructed, two {@link IOStreamConnectorListener}s must be provided.
46  * These are used to monitor events such as when data travels
47  * through the listener in either direction.
48  * <p>
49  * You must also provide a {@link TunnelConfiguration}. The listener is configured from
50  * the details obtained from this object. The listener will be running on
51  * the port specified in {@link TunnelConfiguration#getSourcePort()}.
52  * <p>
53  * Before any connections can be made to this listener, it must be started.
54  * Invoked the {@link #start()} method. The listener may be stopped at
55  * any time using the {@link #stop()} method.
56  * <p>
57  * NOTE UDP tunneling does not currently work.
58  *
59  * @author Lee David Painter <a HREF="mailto: lee@3sp.com">&lt;lee@3sp.com&gt;</a>
60  * @author Brett Smith <a HREF="mailto: brett@3sp.com">&lt;brett@3sp.com&gt;</a>
61  */

62 public class LocalTunnelServer implements LocalTunnelConnectionEventListener {
63     
64     // Private instance variables
65
private Agent vpn;
66     private ServerSocket JavaDoc server;
67     private Thread JavaDoc thread;
68     private boolean listening;
69     private Vector JavaDoc activeTunnels;
70     private IOStreamConnectorListener txListener;
71     private IOStreamConnectorListener rxListener;
72     private String JavaDoc ticket;
73     private TunnelConfiguration listeningSocketConfiguration;
74     private long dataLastTransferred;
75     private DatagramSocket JavaDoc datagramSocket;
76     private boolean stopping = false;
77     private int totalTunnels;
78     private Vector JavaDoc listeners;
79
80     // #ifdef DEBUG
81
static org.apache.commons.logging.Log log = org.apache.commons.logging.LogFactory.getLog(LocalTunnelServer.class);
82     // #endif
83

84     /**
85      * Constructor.
86      *
87      * @param vpn vpn
88      * @param txListener transmit listener
89      * @param rxListener receive listener
90      * @param listeningSocketConfiguration tunnel to configure listener from.
91      */

92     public LocalTunnelServer(Agent vpn, IOStreamConnectorListener txListener, IOStreamConnectorListener rxListener,
93                                  TunnelConfiguration listeningSocketConfiguration) {
94         this.vpn = vpn;
95         this.listeningSocketConfiguration = listeningSocketConfiguration;
96         this.txListener = txListener;
97         this.rxListener = rxListener;
98         listeners = new Vector JavaDoc();
99         this.activeTunnels = new Vector JavaDoc();
100         dataLastTransferred = System.currentTimeMillis();
101     }
102
103     /**
104      * Add a listener
105      *
106      * @param listener
107      */

108     public void addListener(LocalTunnelServerListener listener) {
109         if (listener != null)
110             listeners.addElement(listener);
111     }
112     
113     /**
114      * Get the number of tunnels that are currently active on this listener.
115      *
116      * @return active tunnel count
117      */

118     public int getActiveTunnelCount() {
119         return activeTunnels.size();
120     }
121     
122     /**
123      * Get the total number of tunnels that have ever been connected to
124      * this listener.
125      *
126      * @return total tunnel count
127      */

128     public int getTotalTunnelCount() {
129         return totalTunnels;
130     }
131
132     /**
133      * Get the tunnel that was used to configured this listener.
134      *
135      * @return tunnel
136      */

137     public TunnelConfiguration getTunnel() {
138         return listeningSocketConfiguration;
139     }
140
141     /**
142      * Get the resource ID of the tunnel. This will not be known until the
143      * tunnel has been started.
144      *
145      * @return resource ID
146      */

147     public int getId() {
148         return listeningSocketConfiguration.getId();
149     }
150
151     /**
152      * Get the port on which the listener is running. This should be the
153      * same as the source port specified in the {@link TunnelConfiguration} provided during
154      * construction.
155      *
156      * @return local port
157      */

158     public int getLocalPort() {
159         return (server == null) ? (datagramSocket == null ? -1 : datagramSocket.getLocalPort()) : server.getLocalPort();
160     }
161
162     /**
163      * Get if this listener is currently accepting connections. Note, it may
164      * be possible for a listener <b>not</b> to be listening but to still
165      * be running ({@link #isRunning()}. This may happen while the listener
166      * is shutting down.
167      *
168      * @return listening
169      */

170     public boolean isListening() {
171         return listening;
172     }
173
174     /**
175      * Get the time (in MS since Jan. 1 1970) data was last transferred over
176      * this listener.
177      *
178      * @return time (in MS since Jan. 1 1970) data was last transferred over
179      * this listener.
180      */

181     public long getDataLastTransferredTime() {
182         return dataLastTransferred;
183     }
184
185     /**
186      * Get if this listener is currently running. Note, it may
187      * be possible for a listener to be running but to <b>not</b>
188      * be listenign ({@link #isListening()}. This may happen while the listener
189      * is shutting down.
190      *
191      * @return running
192      */

193     public boolean isRunning() {
194         return (thread != null) && thread.isAlive();
195     }
196
197     /**
198      * Get the ticket assigned to this listener by the SSL-Explorer server.
199      * This will be <code>null</code> until the listener has been started.
200      *
201      * @return ticket
202      */

203     public String JavaDoc getTicket() {
204         return ticket;
205     }
206
207     /**
208      * Start listening for incoming connections to this listener. When
209      * successful, this method will return immediately.
210
211      * @throws IOException
212      */

213     public void start() throws IOException JavaDoc {
214         if(stopping) {
215             throw new IOException JavaDoc("Local forwarding is currently stopping.");
216         }
217         if(isListening()) {
218             throw new IOException JavaDoc("Local forwarding is already listening.");
219         }
220         
221         dataLastTransferred = System.currentTimeMillis();
222
223         // #ifdef DEBUG
224
if(listeningSocketConfiguration.isPermanent()) {
225             log.info("Starting permanent listening socket on port " + listeningSocketConfiguration.getSourcePort()); //$NON-NLS-1$
226
}
227         else {
228             log.info("Starting temporary listening socket on port " + listeningSocketConfiguration.getSourcePort()); //$NON-NLS-1$
229
}
230         // #endif
231

232         /* Bind server socket */
233         if (listeningSocketConfiguration.getTransport().equals(TunnelConfiguration.UDP_TUNNEL)) {
234             // #ifdef DEBUG
235
log.info("Creating UDP server socket on port " + listeningSocketConfiguration.getSourcePort()); //$NON-NLS-1$
236
// #endif
237
datagramSocket = new DatagramSocket JavaDoc(listeningSocketConfiguration.getSourcePort());
238         } else {
239             // #ifdef DEBUG
240
if(listeningSocketConfiguration.getSourcePort() == 0)
241                 log.info("Creating TCP server socket random port") ; //$NON-NLS-1$
242
else
243                 log.info("Creating TCP server socket on port " + listeningSocketConfiguration.getSourcePort()) ; //$NON-NLS-1$
244
// #endif
245
/* If the specified port is 0 then ServerSocket will select the
246              * next free port. We then need to store the port actually used
247              * back into the configuration so application launching can
248              * work.
249              */

250             boolean resetPort = listeningSocketConfiguration.getSourcePort() == 0;
251             server = new ServerSocket JavaDoc(listeningSocketConfiguration.getSourcePort(), 50, InetAddress.getByName(getAddressToBind()));
252             if(resetPort) {
253                 // #ifdef DEBUG
254
log.info("Chosen port " + server.getLocalPort()) ; //$NON-NLS-1$
255
// #endif
256
listeningSocketConfiguration.setSourcePort(server.getLocalPort());
257             }
258         }
259
260         fireLocalTunnelServerStarted();
261         thread = new Thread JavaDoc(new Runnable JavaDoc() {
262             public void run() {
263                 tunnelTCP();
264             }
265         });
266         thread.setDaemon(true);
267         thread.setName("SocketListener " + getAddressToBind() + ":" + String.valueOf(listeningSocketConfiguration.getSourcePort())); //$NON-NLS-1$ //$NON-NLS-2$
268
thread.start();
269     }
270     
271     /**
272      * Get if this listener is currently stopping
273      *
274      * @return stopping
275      */

276     public boolean isStopping() {
277         return stopping;
278     }
279
280     /**
281      * Stop accepting connections to this listener. All current connections
282      * will be severed.
283      * <p>
284      * When this method exist, the listener will no longer be listening,
285      * ({@link #isListening()}) but may still be running ({@link #isRunning()}).
286      */

287     public void stop() {
288         try {
289             stopping = true;
290
291             // #ifdef DEBUG
292
if(listeningSocketConfiguration.isPermanent()) {
293                 log.info("Stopping permanent listening socket on port " + listeningSocketConfiguration.getSourcePort()); //$NON-NLS-1$
294
}
295             else {
296                 log.info("Stopping temporary listening socket on port " + listeningSocketConfiguration.getSourcePort()); //$NON-NLS-1$
297
}
298             // #endif
299

300             synchronized(activeTunnels) {
301                 /* Stop all of the tunnels */
302                 for (Enumeration JavaDoc e = activeTunnels.elements(); e.hasMoreElements();) {
303                     ((LocalTunnelConnection) e.nextElement()).stop();
304                 }
305             
306                 activeTunnels.removeAllElements();
307             }
308
309             /* Close the server socket to prevent new connections */
310             if (server != null) {
311                 // #ifdef DEBUG
312
log.info("Closing server socket on port " + server.getLocalPort());
313                 // #endif
314
server.close();
315             }
316         } catch (IOException JavaDoc ioe) {
317         }
318         
319         server = null;
320         thread = null;
321         listening = false;
322         fireLocalTunnelServerStopped();
323         stopping = false;
324     }
325
326     /* (non-Javadoc)
327      * @see java.lang.Runnable#run()
328      */

329     public void run() {
330          tunnelTCP();
331     }
332
333     private Channel openChannel(TunnelConfiguration conf) throws IOException JavaDoc {
334         
335         try {
336             LocalForwardingChannel channel = new LocalForwardingChannel(conf.getDestinationHost(), conf.getDestinationPort());
337             vpn.getConnection().openChannel(channel);
338             return channel;
339         } catch (Exception JavaDoc e) {
340             throw new IOException JavaDoc("Failed to open direct-tcpip channel to " + conf.getDestinationHost() + ":" + conf.getDestinationPort());
341         }
342     }
343
344     void tunnelTCP() {
345         stopping = false;
346         Socket JavaDoc socket = null;
347         try {
348
349             listening = true;
350
351             while (listening) {
352                 try {
353                     socket = server.accept();
354                     if (!listening || (socket == null)) {
355                         break;
356                     }
357     
358                     try {
359                         // Open an SSL tunnel and connect the socket to the tunnel
360
LocalTunnelConnection vpntunnel = new LocalTunnelConnection(this, openChannel(listeningSocketConfiguration), socket, getTunnel(), txListener, rxListener);
361                         vpntunnel.addListener(this);
362                         vpntunnel.start();
363     
364                     } catch (Throwable JavaDoc ex) {
365                         // #ifdef DEBUG
366
log.info(Messages.getString("LocalTunnelConnectionListener.failedToConnectTunnelingRequest"), ex); //$NON-NLS-1$
367
// #endif
368
try {
369                             socket.close();
370                         } catch (IOException JavaDoc ioe) {
371                         }
372                         
373                         if (listeningSocketConfiguration.isTemporarySingleConnect()) {
374                             throw ex;
375                         }
376                     }
377     
378                     if(listeningSocketConfiguration.isTemporarySingleConnect()) {
379                         // #ifdef DEBUG
380
log.info(Messages.getString("LocalTunnelConnectionListener.notAcceptingMoreAsTemp")); //$NON-NLS-1$
381
// #endif
382
break;
383                     }
384                 } catch (IOException JavaDoc ioe) {
385                     // #ifdef DEBUG
386
log.info(Messages.getString("LocalTunnelConnectionListener.failedToConnectTunnelingRequest")); //$NON-NLS-1$
387
// #endif
388
}
389             }
390         } catch (Throwable JavaDoc ex) {
391             if (!stopping) {
392                 // #ifdef DEBUG
393
log.info(Messages.getString("LocalTunnelConnectionListener.connectionListenerThreadFailed"), ex); //$NON-NLS-1$
394
// #endif
395
stop();
396             }
397         }
398     }
399     
400     void fireLocalTunnelServerStopped() {
401         for(Enumeration JavaDoc e = listeners.elements(); e.hasMoreElements(); ) {
402             ((LocalTunnelServerListener)e.nextElement()).localTunnelStopped(this);
403         }
404     }
405     
406     void fireLocalTunnelServerStarted() {
407         for(Enumeration JavaDoc e = listeners.elements(); e.hasMoreElements(); ) {
408             ((LocalTunnelServerListener)e.nextElement()).localTunnelServerStarted(this);
409         }
410     }
411     
412     void fireLocalTunnelDataTransferred(byte[] buf, int count, boolean sent) {
413         for(Enumeration JavaDoc e = listeners.elements(); e.hasMoreElements(); ) {
414             ((LocalTunnelServerListener)e.nextElement()).localTunnelDataTransferred(this, buf, count, sent);
415         }
416     }
417     
418     void fireActiveTunnelStarted(LocalTunnelConnection activeTunnel) {
419         for(Enumeration JavaDoc e = listeners.elements(); e.hasMoreElements(); ) {
420             ((LocalTunnelServerListener)e.nextElement()).localTunnelConnectionStarted(this, activeTunnel);
421         }
422     }
423     
424     void fireActiveTunnelDataTransferred(LocalTunnelConnection activeTunnel, byte[] buf, int count, boolean sent) {
425         for(Enumeration JavaDoc e = listeners.elements(); e.hasMoreElements(); ) {
426             ((LocalTunnelServerListener)e.nextElement()).localTunnelConnectionDataTransferred(this, activeTunnel, buf, count, sent);
427         }
428     }
429     
430     void fireActiveTunnelStopped(LocalTunnelConnection activeTunnel) {
431         for(Enumeration JavaDoc e = listeners.elements(); e.hasMoreElements(); ) {
432             ((LocalTunnelServerListener)e.nextElement()).localTunnelConnectionStopped(this, activeTunnel);
433         }
434     }
435
436     String JavaDoc getAddressToBind() {
437         if(listeningSocketConfiguration.getSourceInterface() != null &&
438                 !listeningSocketConfiguration.getSourceInterface().equals("")) { //$NON-NLS-1$
439
return listeningSocketConfiguration.getSourceInterface();
440         }
441         else {
442             return "0.0.0.0";
443         }
444     }
445         
446     public void localTunnelConnectionStarted(LocalTunnelConnection tunnel) {
447         synchronized (activeTunnels) {
448             totalTunnels++;
449             activeTunnels.addElement(tunnel);
450             fireActiveTunnelStarted(tunnel);
451         }
452     }
453
454     public void localTunnelConnectionStopped(LocalTunnelConnection tunnel) {
455         synchronized (activeTunnels) {
456             if(!stopping)
457                 activeTunnels.removeElement(tunnel);
458             fireActiveTunnelStopped(tunnel);
459         }
460     }
461
462     public void localTunnelConnectionDataTransferred(LocalTunnelConnection tunnel, byte[] buffer, int count, boolean sent) {
463         dataLastTransferred = System.currentTimeMillis();
464         fireActiveTunnelDataTransferred(tunnel, buffer, count, sent);
465         fireLocalTunnelDataTransferred(buffer, count, sent);
466     }
467 }
468
Popular Tags