potential port forwarding support; probably buggly cuz i haven't added unit tests yet :)
This commit is contained in:
Robey Pointer 2006-12-02 18:33:34 -08:00
parent aaa97d72c0
commit 76285309cf
2 changed files with 133 additions and 8 deletions

View File

@ -41,6 +41,8 @@ class InteractiveQuery (object):
@type name: str
@param instructions: user instructions (usually short) about this query
@type instructions: str
@param prompts: one or more authentication prompts
@type prompts: str
"""
self.name = name
self.instructions = instructions
@ -273,6 +275,41 @@ class ServerInterface (object):
"""
return AUTH_FAILED
def check_port_forward_request(self, address, port):
"""
Handle a request for port forwarding. The client is asking that
connections to the given address and port be forwarded back across
this ssh connection. An address of C{"0.0.0.0"} indicates a global
address (any address associated with this server) and a port of C{0}
indicates any port.
The default implementation always returns C{False}, rejecting the
port forwarding request. If the request is accepted, you should return
the port opened for listening.
@param address: the requested address
@type address: str
@param port: the requested port
@type port: int
@return: the port number that was opened for listening, or C{False} to
reject
@rtype: int
"""
return False
def cancel_port_forward_request(self, address, port):
"""
The client would like to cancel a previous port-forwarding request.
If the given address and port is being forwarded across this ssh
connection, the port should be closed.
@param address: the forwarded address
@type address: str
@param port: the forwarded port
@type port: int
"""
pass
def check_global_request(self, kind, msg):
"""
Handle a global request of the given C{kind}. This method is called
@ -291,6 +328,9 @@ class ServerInterface (object):
The default implementation always returns C{False}, indicating that it
does not support any global requests.
@note: Port forwarding requests are handled separately, in
L{check_port_forward_request}.
@param kind: the kind of global request being made.
@type kind: str

View File

@ -278,6 +278,7 @@ class Transport (threading.Thread):
self.window_size = 65536
self.max_packet_size = 34816
self._x11_handler = None
self._tcp_handler = None
self.saved_exception = None
self.clear_to_send = threading.Event()
@ -548,9 +549,9 @@ class Transport (threading.Thread):
return
self.active = False
self.packetizer.close()
self.join()
for chan in self.channels.values():
chan._unlink()
self.join()
def get_remote_server_key(self):
"""
@ -617,14 +618,14 @@ class Transport (threading.Thread):
L{connect} or L{start_client}) and authenticating.
@param kind: the kind of channel requested (usually C{"session"},
C{"forwarded-tcpip"} or C{"direct-tcpip"})
C{"forwarded-tcpip"}, C{"direct-tcpip"}, or C{"x11"})
@type kind: str
@param dest_addr: the destination address of this port forwarding,
if C{kind} is C{"forwarded-tcpip"} or C{"direct-tcpip"} (ignored
for other channel types)
@type dest_addr: (str, int)
@param src_addr: the source address of this port forwarding, if
C{kind} is C{"forwarded-tcpip"} or C{"direct-tcpip"}
C{kind} is C{"forwarded-tcpip"}, C{"direct-tcpip"}, or C{"x11"}
@type src_addr: (str, int)
@return: a new L{Channel} on success
@rtype: L{Channel}
@ -681,6 +682,67 @@ class Transport (threading.Thread):
e = SSHException('Unable to open channel.')
raise e
def request_port_forward(self, address, port, handler=None):
"""
Ask the server to forward TCP connections from a listening port on
the server, across this SSH session.
If a handler is given, that handler is called from a different thread
whenever a forwarded connection arrives. The handler parameters are::
handler(channel, (origin_addr, origin_port), (server_addr, server_port))
where C{server_addr} and C{server_port} are the address and port that
the server was listening on.
If no handler is set, the default behavior is to send new incoming
forwarded connections into the accept queue, to be picked up via
L{accept}.
@param address: the address to bind when forwarding
@type address: str
@param port: the port to forward, or 0 to ask the server to allocate
any port
@type port: int
@param handler: optional handler for incoming forwarded connections
@type handler: function(Channel, (str, int), (str, int))
@return: the port # allocated by the server
@rtype: int
@raise SSHException: if the server refused the TCP forward request
"""
if not self.active:
raise SSHException('SSH session not active')
address = str(address)
port = int(port)
response = self.global_request('tcpip-forward', (address, port), wait=True)
if response is None:
raise SSHException('TCP forwarding request denied')
if port == 0:
port = response.get_int()
if handler is None:
def default_handler(channel, (src_addr, src_port), (dest_addr, dest_port)):
self._queue_incoming_channel(channel)
handler = default_handler
self._tcp_handler = handler
return port
def cancel_port_forward(self, address, port):
"""
Ask the server to cancel a previous port-forwarding request. No more
connections to the given address & port will be forwarded across this
ssh connection.
@param address: the address to stop forwarding
@type address: str
@param port: the port to stop forwarding
@type port: int
"""
if not self.active:
return
self._tcp_handler = None
self.global_request('cancel-tcpip-forward', (address, port), wait=False)
def open_sftp_client(self):
"""
Create an SFTP client channel from an open transport. On success,
@ -1343,12 +1405,11 @@ class Transport (threading.Thread):
# only called if a channel has turned on x11 forwarding
if handler is None:
# by default, use the same mechanism as accept()
self._x11_handler = self._default_x11_handler
def default_handler(channel, (src_addr, src_port)):
self._queue_incoming_channel(channel)
self._x11_handler = default_handler
else:
self._x11_hanlder = handler
def _default_x11_handler(self, channel, (src_addr, src_port)):
self._queue_incoming_channel(channel)
self._x11_handler = handler
def _queue_incoming_channel(self, channel):
self.lock.acquire()
@ -1756,6 +1817,17 @@ class Transport (threading.Thread):
if not self.server_mode:
self._log(DEBUG, 'Rejecting "%s" global request from server.' % kind)
ok = False
elif kind == 'tcpip-forward':
address = m.get_string()
port = m.get_int()
ok = self.server_object.check_port_forward_request(address, port)
if ok != False:
ok = (ok,)
elif kind == 'cancel-tcpip-forward':
address = m.get_string()
port = m.get_int()
self.server_object.cancel_port_forward_request(address, port)
ok = True
else:
ok = self.server_object.check_global_request(kind, m)
extra = ()
@ -1837,6 +1909,17 @@ class Transport (threading.Thread):
my_chanid = self._next_channel()
finally:
self.lock.release()
elif (kind == 'forwarded-tcpip') and (self._tcp_handler is not None):
server_addr = m.get_string()
server_port = m.get_int()
origin_addr = m.get_string()
origin_port = m.get_int()
self._log(DEBUG, 'Incoming tcp forwarded connection from %s:%d' % (origin_addr, origin_port))
self.lock.acquire()
try:
my_chanid = self._next_channel()
finally:
self.lock.release()
elif not self.server_mode:
self._log(DEBUG, 'Rejecting "%s" channel request from server.' % kind)
reject = True
@ -1881,6 +1964,8 @@ class Transport (threading.Thread):
self._log(INFO, 'Secsh channel %d (%s) opened.', my_chanid, kind)
if kind == 'x11':
self._x11_handler(chan, (origin_addr, origin_port))
elif kind == 'forwarded-tcpip':
self._tcp_handler(chan, (origin_addr, origin_port), (server_addr, server_port))
else:
self._queue_incoming_channel(chan)