SSHClient: add 'sock' parameter to connect() for tunneling

Re #77

This parameter, if set, can be used to make Paramiko wrap an existing socket
connected to a remote SSH server. For instance, you could set up another
SSHClient directly connected to a "gateway" host, and then create a direct-tcpip
tunnel to a "target" host directly accessible from the gateway's perspective
(e.g. think of trying to establish an SSH connection to hosts behind a NAT).
The gateway host would then establish a TCP connection to the target host
directly, and a channel is exposed on the client side. This channel could be
wrapped by an SSHClient class using the connect() function, avoiding the need
to establish a new TCP connnection.

This effectively allows you to create tunneled SSH connections.

Based on work by Oskari Saarenmaa <os@ohmu.fi>, in Paramiko pull request #39.

Signed-off-by: Steven Noonan <steven@uplinklabs.net>
This commit is contained in:
Steven Noonan 2012-10-15 04:09:33 -07:00 committed by Jeff Forcier
parent fd5e29b5a8
commit 31ea4f0734
1 changed files with 21 additions and 16 deletions

View File

@ -229,7 +229,7 @@ class SSHClient (object):
def connect(self, hostname, port=SSH_PORT, username=None, password=None, pkey=None,
key_filename=None, timeout=None, allow_agent=True, look_for_keys=True,
compress=False):
compress=False, sock=None):
"""
Connect to an SSH server and authenticate to it. The server's host key
is checked against the system host keys (see L{load_system_host_keys})
@ -272,6 +272,9 @@ class SSHClient (object):
@type look_for_keys: bool
@param compress: set to True to turn on compression
@type compress: bool
@param sock: an open socket or direct-tcpip channel from another
SSHClient class to use for communication with the target host.
@type channel: socket
@raise BadHostKeyException: if the server's host key could not be
verified
@ -280,21 +283,23 @@ class SSHClient (object):
establishing an SSH session
@raise socket.error: if a socket error occurred while connecting
"""
for (family, socktype, proto, canonname, sockaddr) in socket.getaddrinfo(hostname, port, socket.AF_UNSPEC, socket.SOCK_STREAM):
if socktype == socket.SOCK_STREAM:
af = family
addr = sockaddr
break
else:
# some OS like AIX don't indicate SOCK_STREAM support, so just guess. :(
af, _, _, _, addr = socket.getaddrinfo(hostname, port, socket.AF_UNSPEC, socket.SOCK_STREAM)
sock = socket.socket(af, socket.SOCK_STREAM)
if timeout is not None:
try:
sock.settimeout(timeout)
except:
pass
retry_on_signal(lambda: sock.connect(addr))
if not sock:
for (family, socktype, proto, canonname, sockaddr) in socket.getaddrinfo(hostname, port, socket.AF_UNSPEC, socket.SOCK_STREAM):
if socktype == socket.SOCK_STREAM:
af = family
addr = sockaddr
break
else:
# some OS like AIX don't indicate SOCK_STREAM support, so just guess. :(
af, _, _, _, addr = socket.getaddrinfo(hostname, port, socket.AF_UNSPEC, socket.SOCK_STREAM)
sock = socket.socket(af, socket.SOCK_STREAM)
if timeout is not None:
try:
sock.settimeout(timeout)
except:
pass
retry_on_signal(lambda: sock.connect(addr))
t = self._transport = Transport(sock)
t.use_compression(compress=compress)
if self._log_channel is not None: