diff options
| author | Kenny Root <kenny@the-b.org> | 2008-11-08 10:01:55 +0000 | 
|---|---|---|
| committer | Kenny Root <kenny@the-b.org> | 2008-11-08 10:01:55 +0000 | 
| commit | 9b3322b1b870b3e677723d33ea6502c2faf22173 (patch) | |
| tree | 177b1a82536f7570279c74c9977aedd5fe5403ea /lib/src | |
| parent | fd7e5a93b95ca7ddb51f0306b14aa0fe5c9a300c (diff) | |
| download | sshlib-9b3322b1b870b3e677723d33ea6502c2faf22173.tar.gz sshlib-9b3322b1b870b3e677723d33ea6502c2faf22173.tar.bz2 sshlib-9b3322b1b870b3e677723d33ea6502c2faf22173.zip | |
* Add dynamic port forwarding.
Diffstat (limited to 'lib/src')
3 files changed, 318 insertions, 0 deletions
| diff --git a/lib/src/main/java/com/trilead/ssh2/Connection.java b/lib/src/main/java/com/trilead/ssh2/Connection.java index 8fbf6fa..2b244c2 100644 --- a/lib/src/main/java/com/trilead/ssh2/Connection.java +++ b/lib/src/main/java/com/trilead/ssh2/Connection.java @@ -933,6 +933,58 @@ public class Connection  	}
  	/**
 +	 * Creates a new {@link DynamicPortForwarder}. A
 +	 * <code>DynamicPortForwarder</code> forwards TCP/IP connections that arrive
 +	 * at a local port via the secure tunnel to another host that is chosen via
 +	 * the SOCKS protocol.
 +	 * <p>
 +	 * This method must only be called after one has passed successfully the
 +	 * authentication step. There is no limit on the number of concurrent
 +	 * forwardings.
 +	 * 
 +	 * @param local_port
 +	 * @return A {@link DynamicPortForwarder} object.
 +	 * @throws IOException
 +	 */
 +	public synchronized DynamicPortForwarder createDynamicPortForwarder(int local_port) throws IOException
 +	{
 +		if (tm == null)
 +			throw new IllegalStateException("Cannot forward ports, you need to establish a connection first.");
 +
 +		if (!authenticated)
 +			throw new IllegalStateException("Cannot forward ports, connection is not authenticated.");
 +
 +		return new DynamicPortForwarder(cm, local_port);
 +	}
 +	
 +	/**
 +	 * Creates a new {@link DynamicPortForwarder}. A
 +	 * <code>DynamicPortForwarder</code> forwards TCP/IP connections that arrive
 +	 * at a local port via the secure tunnel to another host that is chosen via
 +	 * the SOCKS protocol.
 +	 * <p>
 +	 * This method must only be called after one has passed successfully the
 +	 * authentication step. There is no limit on the number of concurrent
 +	 * forwardings.
 +	 * 
 +	 * @param addr
 +	 *            specifies the InetSocketAddress where the local socket shall
 +	 *            be bound to.
 +	 * @return A {@link DynamicPortForwarder} object.
 +	 * @throws IOException
 +	 */
 +	public synchronized DynamicPortForwarder createDynamicPortForwarder(InetSocketAddress addr) throws IOException
 +	{
 +		if (tm == null)
 +			throw new IllegalStateException("Cannot forward ports, you need to establish a connection first.");
 +
 +		if (!authenticated)
 +			throw new IllegalStateException("Cannot forward ports, connection is not authenticated.");
 +
 +		return new DynamicPortForwarder(cm, addr);
 +	}
 +	
 +	/**
  	 * Create a very basic {@link SCPClient} that can be used to copy files
  	 * from/to the SSH-2 server.
  	 * <p>
 diff --git a/lib/src/main/java/com/trilead/ssh2/DynamicPortForwarder.java b/lib/src/main/java/com/trilead/ssh2/DynamicPortForwarder.java new file mode 100644 index 0000000..1fece14 --- /dev/null +++ b/lib/src/main/java/com/trilead/ssh2/DynamicPortForwarder.java @@ -0,0 +1,67 @@ +/* +	ConnectBot: simple, powerful, open-source SSH client for Android +	Copyright (C) 2007-2008 Kenny Root, Jeffrey Sharkey +	 +	This program is free software: you can redistribute it and/or modify +	it under the terms of the GNU General Public License as published by +	the Free Software Foundation, either version 3 of the License, or +	(at your option) any dater version. +	 +	This program is distributed in the hope that it will be useful, +	but WITHOUT ANY WARRANTY; without even the implied warranty of +	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +	GNU General Public License for more details. +	 +	You should have received a copy of the GNU General Public License +	along with this program.  If not, see <http://www.gnu.org/licenses/>. +*/ +package com.trilead.ssh2; + +import java.io.IOException; +import java.net.InetSocketAddress; + +import com.trilead.ssh2.channel.ChannelManager; +import com.trilead.ssh2.channel.DynamicAcceptThread; + +/** + * A <code>DynamicPortForwarder</code> forwards TCP/IP connections to a local + * port via the secure tunnel to another host which is selected via the + * SOCKS protocol. Checkout {@link Connection#createDynamicPortForwarder(int)} + * on how to create one. + *  + * @author Kenny Root + * @version $Id: $ + */ +public class DynamicPortForwarder { +	ChannelManager cm; + +	DynamicAcceptThread dat; + +	DynamicPortForwarder(ChannelManager cm, int local_port) +			throws IOException +	{ +		this.cm = cm; + +		dat = new DynamicAcceptThread(cm, local_port); +		dat.setDaemon(true); +		dat.start(); +	} +	 +	DynamicPortForwarder(ChannelManager cm, InetSocketAddress addr) throws IOException { +		this.cm = cm; + +		dat = new DynamicAcceptThread(cm, addr); +		dat.setDaemon(true); +		dat.start(); +	} + +	/** +	 * Stop TCP/IP forwarding of newly arriving connections. +	 *  +	 * @throws IOException +	 */ +	public void close() throws IOException +	{ +		dat.stopWorking(); +	} +} diff --git a/lib/src/main/java/com/trilead/ssh2/channel/DynamicAcceptThread.java b/lib/src/main/java/com/trilead/ssh2/channel/DynamicAcceptThread.java new file mode 100644 index 0000000..18fa279 --- /dev/null +++ b/lib/src/main/java/com/trilead/ssh2/channel/DynamicAcceptThread.java @@ -0,0 +1,199 @@ +/* +	ConnectBot: simple, powerful, open-source SSH client for Android +	Copyright (C) 2007-2008 Kenny Root, Jeffrey Sharkey +	 +	This program is free software: you can redistribute it and/or modify +	it under the terms of the GNU General Public License as published by +	the Free Software Foundation, either version 3 of the License, or +	(at your option) any later version. +	 +	This program is distributed in the hope that it will be useful, +	but WITHOUT ANY WARRANTY; without even the implied warranty of +	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +	GNU General Public License for more details. +	 +	You should have received a copy of the GNU General Public License +	along with this program.  If not, see <http://www.gnu.org/licenses/>. + */ +package com.trilead.ssh2.channel; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.InetSocketAddress; +import java.net.ServerSocket; +import java.net.Socket; + +import net.sourceforge.jsocks.Proxy; +import net.sourceforge.jsocks.ProxyMessage; +import net.sourceforge.jsocks.ProxyServer; +import net.sourceforge.jsocks.Socks4Message; +import net.sourceforge.jsocks.Socks5Message; +import net.sourceforge.jsocks.server.ServerAuthenticator; +import net.sourceforge.jsocks.server.ServerAuthenticatorNone; + +/** + * DynamicAcceptThread. + *  + * @author Kenny Root + * @version $Id: + */ +public class DynamicAcceptThread extends Thread implements IChannelWorkerThread { +	private ChannelManager cm; +	private ServerSocket ss; +	 +	class DynamicAcceptRunnable extends ProxyServer { +		private ServerAuthenticator auth; +		private Socket sock; +		private InputStream in; +		private OutputStream out; +		private ProxyMessage msg; +		 +		public DynamicAcceptRunnable(ServerAuthenticator auth, Socket sock) { +			super(auth, sock); +			this.auth = auth; +			this.sock = sock; +		} + +		protected void startSession() throws IOException { +			sock.setSoTimeout(ProxyServer.iddleTimeout); + +			try { +				auth = auth.startSession(sock); +			} catch (IOException ioe) { +				System.out.println("Could not start SOCKS session"); +				ioe.printStackTrace(); +				auth = null; +				return; +			} + +			if (auth == null) { // Authentication failed +				System.out.println("SOCKS auth failed"); +				return; +			} + +			in = auth.getInputStream(); +			out = auth.getOutputStream(); + +			msg = readMsg(in); +			handleRequest(msg); +		} +		 +		protected void onConnect(ProxyMessage msg) throws IOException { +			ProxyMessage response = null; +			Channel cn = null; +			StreamForwarder r2l = null; +			StreamForwarder l2r = null; + +			try { +				/* +				 * This may fail, e.g., if the remote port is closed (in +				 * optimistic terms: not open yet) +				 */ + +				cn = cm.openDirectTCPIPChannel(msg.host, msg.port, +						sock.getInetAddress().getHostAddress(), +						sock.getPort()); + +			} catch (IOException e) { +				/* +				 * Simply close the local socket and wait for the next incoming +				 * connection +				 */ + +				try { +					sock.close(); +				} catch (IOException ignore) { +				} + +				return; +			} + +			try { +				r2l = new StreamForwarder(cn, null, null, cn.stdoutStream, out, "RemoteToLocal"); +				l2r = new StreamForwarder(cn, r2l, sock, in, cn.stdinStream, "LocalToRemote"); +			} catch (IOException e) { +				try { +					/* +					 * This message is only visible during debugging, since we +					 * discard the channel immediatelly +					 */ +					cn.cm.closeChannel(cn, +							"Weird error during creation of StreamForwarder (" +									+ e.getMessage() + ")", true); +				} catch (IOException ignore) { +				} + +				return; +			} + +			r2l.setDaemon(true); +			l2r.setDaemon(true); +			r2l.start(); +			l2r.start(); +			 +			if (msg instanceof Socks5Message) { +				response = new Socks5Message(Proxy.SOCKS_SUCCESS, sock +						.getLocalAddress(), sock.getLocalPort()); +			} else { +				response = new Socks4Message(Socks4Message.REPLY_OK, sock +						.getLocalAddress(), sock.getLocalPort()); + +			} +			response.write(out); +		} +	} +	 +	public DynamicAcceptThread(ChannelManager cm, int local_port) +			throws IOException { +		this.cm = cm; + +		ss = new ServerSocket(local_port); +	} +	 +	public DynamicAcceptThread(ChannelManager cm, InetSocketAddress localAddress) +			throws IOException { +		this.cm = cm; + +		ss = new ServerSocket(); +		ss.bind(localAddress); +	} + +	public void run() { +		try { +			cm.registerThread(this); +		} catch (IOException e) { +			stopWorking(); +			return; +		} + +		while (true) { +			Socket sock = null; + +			try { +				sock = ss.accept(); +			} catch (IOException e) { +				stopWorking(); +				return; +			} + +			DynamicAcceptRunnable dar = new DynamicAcceptRunnable(new ServerAuthenticatorNone(), sock); +			Thread t = new Thread(dar); +			t.setDaemon(true); +			t.start(); +		} +	} +	 +	/* +	 * (non-Javadoc) +	 *  +	 * @see com.trilead.ssh2.channel.IChannelWorkerThread#stopWorking() +	 */ +	public void stopWorking() { +		try { +			/* This will lead to an IOException in the ss.accept() call */ +			ss.close(); +		} catch (IOException e) { +		} +	} +} | 
