potential port forwarding support; probably buggly cuz i haven't added unit tests yet :)
This commit is contained in:
parent
aaa97d72c0
commit
76285309cf
|
@ -41,6 +41,8 @@ class InteractiveQuery (object):
|
||||||
@type name: str
|
@type name: str
|
||||||
@param instructions: user instructions (usually short) about this query
|
@param instructions: user instructions (usually short) about this query
|
||||||
@type instructions: str
|
@type instructions: str
|
||||||
|
@param prompts: one or more authentication prompts
|
||||||
|
@type prompts: str
|
||||||
"""
|
"""
|
||||||
self.name = name
|
self.name = name
|
||||||
self.instructions = instructions
|
self.instructions = instructions
|
||||||
|
@ -273,6 +275,41 @@ class ServerInterface (object):
|
||||||
"""
|
"""
|
||||||
return AUTH_FAILED
|
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):
|
def check_global_request(self, kind, msg):
|
||||||
"""
|
"""
|
||||||
Handle a global request of the given C{kind}. This method is called
|
Handle a global request of the given C{kind}. This method is called
|
||||||
|
@ -292,6 +329,9 @@ class ServerInterface (object):
|
||||||
The default implementation always returns C{False}, indicating that it
|
The default implementation always returns C{False}, indicating that it
|
||||||
does not support any global requests.
|
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.
|
@param kind: the kind of global request being made.
|
||||||
@type kind: str
|
@type kind: str
|
||||||
@param msg: any extra arguments to the request.
|
@param msg: any extra arguments to the request.
|
||||||
|
|
|
@ -278,6 +278,7 @@ class Transport (threading.Thread):
|
||||||
self.window_size = 65536
|
self.window_size = 65536
|
||||||
self.max_packet_size = 34816
|
self.max_packet_size = 34816
|
||||||
self._x11_handler = None
|
self._x11_handler = None
|
||||||
|
self._tcp_handler = None
|
||||||
|
|
||||||
self.saved_exception = None
|
self.saved_exception = None
|
||||||
self.clear_to_send = threading.Event()
|
self.clear_to_send = threading.Event()
|
||||||
|
@ -548,9 +549,9 @@ class Transport (threading.Thread):
|
||||||
return
|
return
|
||||||
self.active = False
|
self.active = False
|
||||||
self.packetizer.close()
|
self.packetizer.close()
|
||||||
|
self.join()
|
||||||
for chan in self.channels.values():
|
for chan in self.channels.values():
|
||||||
chan._unlink()
|
chan._unlink()
|
||||||
self.join()
|
|
||||||
|
|
||||||
def get_remote_server_key(self):
|
def get_remote_server_key(self):
|
||||||
"""
|
"""
|
||||||
|
@ -617,14 +618,14 @@ class Transport (threading.Thread):
|
||||||
L{connect} or L{start_client}) and authenticating.
|
L{connect} or L{start_client}) and authenticating.
|
||||||
|
|
||||||
@param kind: the kind of channel requested (usually C{"session"},
|
@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
|
@type kind: str
|
||||||
@param dest_addr: the destination address of this port forwarding,
|
@param dest_addr: the destination address of this port forwarding,
|
||||||
if C{kind} is C{"forwarded-tcpip"} or C{"direct-tcpip"} (ignored
|
if C{kind} is C{"forwarded-tcpip"} or C{"direct-tcpip"} (ignored
|
||||||
for other channel types)
|
for other channel types)
|
||||||
@type dest_addr: (str, int)
|
@type dest_addr: (str, int)
|
||||||
@param src_addr: the source address of this port forwarding, if
|
@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)
|
@type src_addr: (str, int)
|
||||||
@return: a new L{Channel} on success
|
@return: a new L{Channel} on success
|
||||||
@rtype: L{Channel}
|
@rtype: L{Channel}
|
||||||
|
@ -681,6 +682,67 @@ class Transport (threading.Thread):
|
||||||
e = SSHException('Unable to open channel.')
|
e = SSHException('Unable to open channel.')
|
||||||
raise e
|
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):
|
def open_sftp_client(self):
|
||||||
"""
|
"""
|
||||||
Create an SFTP client channel from an open transport. On success,
|
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
|
# only called if a channel has turned on x11 forwarding
|
||||||
if handler is None:
|
if handler is None:
|
||||||
# by default, use the same mechanism as accept()
|
# by default, use the same mechanism as accept()
|
||||||
self._x11_handler = self._default_x11_handler
|
def default_handler(channel, (src_addr, src_port)):
|
||||||
else:
|
|
||||||
self._x11_hanlder = handler
|
|
||||||
|
|
||||||
def _default_x11_handler(self, channel, (src_addr, src_port)):
|
|
||||||
self._queue_incoming_channel(channel)
|
self._queue_incoming_channel(channel)
|
||||||
|
self._x11_handler = default_handler
|
||||||
|
else:
|
||||||
|
self._x11_handler = handler
|
||||||
|
|
||||||
def _queue_incoming_channel(self, channel):
|
def _queue_incoming_channel(self, channel):
|
||||||
self.lock.acquire()
|
self.lock.acquire()
|
||||||
|
@ -1756,6 +1817,17 @@ class Transport (threading.Thread):
|
||||||
if not self.server_mode:
|
if not self.server_mode:
|
||||||
self._log(DEBUG, 'Rejecting "%s" global request from server.' % kind)
|
self._log(DEBUG, 'Rejecting "%s" global request from server.' % kind)
|
||||||
ok = False
|
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:
|
else:
|
||||||
ok = self.server_object.check_global_request(kind, m)
|
ok = self.server_object.check_global_request(kind, m)
|
||||||
extra = ()
|
extra = ()
|
||||||
|
@ -1837,6 +1909,17 @@ class Transport (threading.Thread):
|
||||||
my_chanid = self._next_channel()
|
my_chanid = self._next_channel()
|
||||||
finally:
|
finally:
|
||||||
self.lock.release()
|
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:
|
elif not self.server_mode:
|
||||||
self._log(DEBUG, 'Rejecting "%s" channel request from server.' % kind)
|
self._log(DEBUG, 'Rejecting "%s" channel request from server.' % kind)
|
||||||
reject = True
|
reject = True
|
||||||
|
@ -1881,6 +1964,8 @@ class Transport (threading.Thread):
|
||||||
self._log(INFO, 'Secsh channel %d (%s) opened.', my_chanid, kind)
|
self._log(INFO, 'Secsh channel %d (%s) opened.', my_chanid, kind)
|
||||||
if kind == 'x11':
|
if kind == 'x11':
|
||||||
self._x11_handler(chan, (origin_addr, origin_port))
|
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:
|
else:
|
||||||
self._queue_incoming_channel(chan)
|
self._queue_incoming_channel(chan)
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue