patch from david guerizec for direct-tcpip forwarding support, and a unit
test added by yours truly.
This commit is contained in:
Robey Pointer 2007-12-30 21:29:50 -08:00
parent 06d3471b46
commit ba1fd0d61b
3 changed files with 88 additions and 4 deletions

View File

@ -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):

View File

@ -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

View File

@ -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.
"""