patch from david guerizec for direct-tcpip forwarding support, and a unit test added by yours truly.
This commit is contained in:
parent
06d3471b46
commit
ba1fd0d61b
|
@ -492,6 +492,47 @@ class ServerInterface (object):
|
|||
@rtype: bool
|
||||
"""
|
||||
return False
|
||||
|
||||
def check_channel_direct_tcpip_request(self, chanid, origin, destination):
|
||||
"""
|
||||
Determine if a local port forwarding channel will be granted, and
|
||||
return C{OPEN_SUCCEEDED} or an error code. This method is
|
||||
called in server mode when the client requests a channel, after
|
||||
authentication is complete.
|
||||
|
||||
The C{chanid} parameter is a small number that uniquely identifies the
|
||||
channel within a L{Transport}. A L{Channel} object is not created
|
||||
unless this method returns C{OPEN_SUCCEEDED} -- once a
|
||||
L{Channel} object is created, you can call L{Channel.get_id} to
|
||||
retrieve the channel ID.
|
||||
|
||||
The origin and destination parameters are (ip_address, port) tuples
|
||||
that correspond to both ends of the TCP connection in the forwarding
|
||||
tunnel.
|
||||
|
||||
The return value should either be C{OPEN_SUCCEEDED} (or
|
||||
C{0}) to allow the channel request, or one of the following error
|
||||
codes to reject it:
|
||||
- C{OPEN_FAILED_ADMINISTRATIVELY_PROHIBITED}
|
||||
- C{OPEN_FAILED_CONNECT_FAILED}
|
||||
- C{OPEN_FAILED_UNKNOWN_CHANNEL_TYPE}
|
||||
- C{OPEN_FAILED_RESOURCE_SHORTAGE}
|
||||
|
||||
The default implementation always returns
|
||||
C{OPEN_FAILED_ADMINISTRATIVELY_PROHIBITED}.
|
||||
|
||||
@param chanid: ID of the channel
|
||||
@type chanid: int
|
||||
@param origin: 2-tuple containing the IP address and port of the
|
||||
originator (client side)
|
||||
@type origin: tuple
|
||||
@param destination: 2-tuple containing the IP address and port of the
|
||||
destination (server side)
|
||||
@type destination: tuple
|
||||
@return: a success or failure code (listed above)
|
||||
@rtype: int
|
||||
"""
|
||||
return OPEN_FAILED_ADMINISTRATIVELY_PROHIBITED
|
||||
|
||||
|
||||
class SubsystemHandler (threading.Thread):
|
||||
|
|
|
@ -1942,7 +1942,17 @@ class Transport (threading.Thread):
|
|||
my_chanid = self._next_channel()
|
||||
finally:
|
||||
self.lock.release()
|
||||
reason = self.server_object.check_channel_request(kind, my_chanid)
|
||||
if kind == 'direct-tcpip':
|
||||
# handle direct-tcpip requests comming from the client
|
||||
dest_addr = m.get_string()
|
||||
dest_port = m.get_int()
|
||||
origin_addr = m.get_string()
|
||||
origin_port = m.get_int()
|
||||
reason = self.server_object.check_channel_direct_tcpip_request(
|
||||
my_chanid, (origin_addr, origin_port),
|
||||
(dest_addr, dest_port))
|
||||
else:
|
||||
reason = self.server_object.check_channel_request(kind, my_chanid)
|
||||
if reason != OPEN_SUCCEEDED:
|
||||
self._log(DEBUG, 'Rejecting "%s" channel request from client.' % kind)
|
||||
reject = True
|
||||
|
|
|
@ -119,6 +119,10 @@ class NullServer (ServerInterface):
|
|||
self._listen.close()
|
||||
self._listen = None
|
||||
|
||||
def check_channel_direct_tcpip_request(self, chanid, origin, destination):
|
||||
self._tcpip_dest = destination
|
||||
return OPEN_SUCCEEDED
|
||||
|
||||
|
||||
class TransportTest (unittest.TestCase):
|
||||
|
||||
|
@ -625,7 +629,7 @@ class TransportTest (unittest.TestCase):
|
|||
cs = socket.socket()
|
||||
cs.connect(('', port))
|
||||
ss, _ = self.server._listen.accept()
|
||||
sch = self.ts.open_forwarded_tcpip_channel(ss.getpeername(), ss.getsockname())
|
||||
sch = self.ts.open_forwarded_tcpip_channel(ss.getsockname(), ss.getpeername())
|
||||
cch = self.tc.accept()
|
||||
|
||||
sch.send('hello')
|
||||
|
@ -639,7 +643,36 @@ class TransportTest (unittest.TestCase):
|
|||
self.tc.cancel_port_forward('', port)
|
||||
self.assertTrue(self.server._listen is None)
|
||||
|
||||
def test_K_stderr_select(self):
|
||||
def test_K_port_forwarding(self):
|
||||
"""
|
||||
verify that a client can forward new connections from a locally-
|
||||
forwarded port.
|
||||
"""
|
||||
self.setup_test_server()
|
||||
chan = self.tc.open_session()
|
||||
chan.exec_command('yes')
|
||||
schan = self.ts.accept(1.0)
|
||||
|
||||
# open a port on the "server" that the client will ask to forward to.
|
||||
greeting_server = socket.socket()
|
||||
greeting_server.listen(1)
|
||||
greeting_port = greeting_server.getsockname()[1]
|
||||
|
||||
cs = self.tc.open_channel('direct-tcpip', ('', greeting_port), ('', 9000))
|
||||
sch = self.ts.accept(1.0)
|
||||
cch = socket.socket()
|
||||
cch.connect(self.server._tcpip_dest)
|
||||
|
||||
ss, _ = greeting_server.accept()
|
||||
ss.send('Hello!\n')
|
||||
ss.close()
|
||||
sch.send(cch.recv(8192))
|
||||
sch.close()
|
||||
|
||||
self.assertEquals('Hello!\n', cs.recv(7))
|
||||
cs.close()
|
||||
|
||||
def test_L_stderr_select(self):
|
||||
"""
|
||||
verify that select() on a channel works even if only stderr is
|
||||
receiving data.
|
||||
|
@ -678,7 +711,7 @@ class TransportTest (unittest.TestCase):
|
|||
schan.close()
|
||||
chan.close()
|
||||
|
||||
def test_L_send_ready(self):
|
||||
def test_M_send_ready(self):
|
||||
"""
|
||||
verify that send_ready() indicates when a send would not block.
|
||||
"""
|
||||
|
|
Loading…
Reference in New Issue