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
|
@rtype: bool
|
||||||
"""
|
"""
|
||||||
return False
|
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):
|
class SubsystemHandler (threading.Thread):
|
||||||
|
|
|
@ -1942,7 +1942,17 @@ class Transport (threading.Thread):
|
||||||
my_chanid = self._next_channel()
|
my_chanid = self._next_channel()
|
||||||
finally:
|
finally:
|
||||||
self.lock.release()
|
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:
|
if reason != OPEN_SUCCEEDED:
|
||||||
self._log(DEBUG, 'Rejecting "%s" channel request from client.' % kind)
|
self._log(DEBUG, 'Rejecting "%s" channel request from client.' % kind)
|
||||||
reject = True
|
reject = True
|
||||||
|
|
|
@ -119,6 +119,10 @@ class NullServer (ServerInterface):
|
||||||
self._listen.close()
|
self._listen.close()
|
||||||
self._listen = None
|
self._listen = None
|
||||||
|
|
||||||
|
def check_channel_direct_tcpip_request(self, chanid, origin, destination):
|
||||||
|
self._tcpip_dest = destination
|
||||||
|
return OPEN_SUCCEEDED
|
||||||
|
|
||||||
|
|
||||||
class TransportTest (unittest.TestCase):
|
class TransportTest (unittest.TestCase):
|
||||||
|
|
||||||
|
@ -625,7 +629,7 @@ class TransportTest (unittest.TestCase):
|
||||||
cs = socket.socket()
|
cs = socket.socket()
|
||||||
cs.connect(('', port))
|
cs.connect(('', port))
|
||||||
ss, _ = self.server._listen.accept()
|
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()
|
cch = self.tc.accept()
|
||||||
|
|
||||||
sch.send('hello')
|
sch.send('hello')
|
||||||
|
@ -639,7 +643,36 @@ class TransportTest (unittest.TestCase):
|
||||||
self.tc.cancel_port_forward('', port)
|
self.tc.cancel_port_forward('', port)
|
||||||
self.assertTrue(self.server._listen is None)
|
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
|
verify that select() on a channel works even if only stderr is
|
||||||
receiving data.
|
receiving data.
|
||||||
|
@ -678,7 +711,7 @@ class TransportTest (unittest.TestCase):
|
||||||
schan.close()
|
schan.close()
|
||||||
chan.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.
|
verify that send_ready() indicates when a send would not block.
|
||||||
"""
|
"""
|
||||||
|
|
Loading…
Reference in New Issue