[project @ Arch-1:robey@lag.net--2003-public%secsh--dev--1.0--patch-39]

add global request mechanism
add transport.global_request() to make a global-style request (usually an
extension to the protocol -- like keepalives) and handle requests from the
remote host.  incoming requests are now handled and responded to correctly,
which should make openssh-style keepalives work.  (before, we would silently
ignore them, which was wrong.)
This commit is contained in:
Robey Pointer 2004-04-05 10:37:18 +00:00
parent c9d301b782
commit 70faf02f3e
3 changed files with 105 additions and 3 deletions

View File

@ -28,6 +28,7 @@ MSG_KEXINIT, MSG_NEWKEYS = range(20, 22)
MSG_USERAUTH_REQUEST, MSG_USERAUTH_FAILURE, MSG_USERAUTH_SUCCESS, \ MSG_USERAUTH_REQUEST, MSG_USERAUTH_FAILURE, MSG_USERAUTH_SUCCESS, \
MSG_USERAUTH_BANNER = range(50, 54) MSG_USERAUTH_BANNER = range(50, 54)
MSG_USERAUTH_PK_OK = 60 MSG_USERAUTH_PK_OK = 60
MSG_GLOBAL_REQUEST, MSG_REQUEST_SUCCESS, MSG_REQUEST_FAILURE = range(80, 83)
MSG_CHANNEL_OPEN, MSG_CHANNEL_OPEN_SUCCESS, MSG_CHANNEL_OPEN_FAILURE, \ MSG_CHANNEL_OPEN, MSG_CHANNEL_OPEN_SUCCESS, MSG_CHANNEL_OPEN_FAILURE, \
MSG_CHANNEL_WINDOW_ADJUST, MSG_CHANNEL_DATA, MSG_CHANNEL_EXTENDED_DATA, \ MSG_CHANNEL_WINDOW_ADJUST, MSG_CHANNEL_DATA, MSG_CHANNEL_EXTENDED_DATA, \
MSG_CHANNEL_EOF, MSG_CHANNEL_CLOSE, MSG_CHANNEL_REQUEST, \ MSG_CHANNEL_EOF, MSG_CHANNEL_CLOSE, MSG_CHANNEL_REQUEST, \

View File

@ -243,6 +243,8 @@ class Message (object):
return self.add_mpint(i) return self.add_mpint(i)
else: else:
return self.add_int(i) return self.add_int(i)
elif type(i) is bool:
return self.add_boolean(i)
elif type(i) == types.ListType: elif type(i) == types.ListType:
return self.add_list(i) return self.add_list(i)
else: else:

View File

@ -307,7 +307,7 @@ class BaseTransport (threading.Thread):
@type filename: string @type filename: string
@return: True if a moduli file was successfully loaded; False @return: True if a moduli file was successfully loaded; False
otherwise. otherwise.
@rtype: boolean @rtype: bool
@since: doduo @since: doduo
@ -364,7 +364,7 @@ class BaseTransport (threading.Thread):
Return true if this session is active (open). Return true if this session is active (open).
@return: True if the session is still active (open); False if the session is closed. @return: True if the session is still active (open); False if the session is closed.
@rtype: boolean @rtype: bool
""" """
return self.active return self.active
@ -454,7 +454,7 @@ class BaseTransport (threading.Thread):
@return: True if the renegotiation was successful, and the link is @return: True if the renegotiation was successful, and the link is
using new keys; False if the session dropped during renegotiation. using new keys; False if the session dropped during renegotiation.
@rtype: boolean @rtype: bool
""" """
self.completion_event = threading.Event() self.completion_event = threading.Event()
self._send_kex_init() self._send_kex_init()
@ -466,6 +466,44 @@ class BaseTransport (threading.Thread):
break break
return True return True
def global_request(self, kind, data=None, wait=True):
"""
Make a global request to the remote host. These are normally
extensions to the SSH2 protocol.
@param kind: name of the request.
@type kind: string
@param data: an optional tuple containing additional data to attach
to the request.
@type data: tuple
@param wait: C{True} if this method should not return until a response
is received; C{False} otherwise.
@type wait: bool
@return: a L{Message} containing possible additional data if the
request was successful (or an empty L{Message} if C{wait} was
C{False}); C{None} if the request was denied.
@rtype: L{Message}
"""
if wait:
self.completion_event = threading.Event()
m = Message()
m.add_byte(chr(MSG_GLOBAL_REQUEST))
m.add_string(kind)
m.add_boolean(wait)
if data is not None:
for item in data:
m.add(item)
self._send_message(m)
if not wait:
return True
while True:
self.completion_event.wait(0.1)
if not self.active:
return False
if self.completion_event.isSet():
break
return self.global_response
def check_channel_request(self, kind, chanid): def check_channel_request(self, kind, chanid):
""" """
I{(subclass override)} I{(subclass override)}
@ -496,6 +534,36 @@ class BaseTransport (threading.Thread):
""" """
return None return None
def check_global_request(self, kind, msg):
"""
I{(subclass override)}
Handle a global request of the given C{kind}. This method is called
in server mode and client mode, whenever the remote host makes a global
request. If there are any arguments to the request, they will be in
C{msg}.
There aren't any useful global requests defined, aside from port
forwarding, so usually this type of request is an extension to the
protocol.
If the request was successful and you would like to return contextual
data to the remote host, return a tuple. Items in the tuple will be
sent back with the successful result. (Note that the items in the
tuple can only be strings, ints, longs, or bools.)
The default implementation always returns C{False}, indicating that it
does not support any global requests.
@param kind: the kind of global request being made.
@type kind: string
@param msg: any extra arguments to the request.
@type msg: L{Message}
@return: C{True} or a tuple of data if the request was granted;
C{False} otherwise.
@rtype: bool
"""
return False
def accept(self, timeout=None): def accept(self, timeout=None):
try: try:
self.lock.acquire() self.lock.acquire()
@ -1081,6 +1149,34 @@ class BaseTransport (threading.Thread):
desc = m.get_string() desc = m.get_string()
self._log(INFO, 'Disconnect (code %d): %s' % (code, desc)) self._log(INFO, 'Disconnect (code %d): %s' % (code, desc))
def _parse_global_request(self, m):
kind = m.get_string()
want_reply = m.get_boolean()
ok = self.check_global_request(kind, m)
extra = ()
if type(ok) is tuple:
extra = ok
ok = True
if want_reply:
msg = Message()
if ok:
msg.add_byte(chr(MSG_REQUEST_SUCCESS))
for item in extra:
msg.add(item)
else:
msg.add_byte(chr(MSG_REQUEST_FAILURE))
self._send_message(msg)
def _parse_request_success(self, m):
self.global_response = m
if self.completion_event is not None:
self.completion_event.set()
def _parse_request_failure(self, m):
self.global_response = None
if self.completion_event is not None:
self.completion_event.set()
def _parse_channel_open_success(self, m): def _parse_channel_open_success(self, m):
chanid = m.get_int() chanid = m.get_int()
server_chanid = m.get_int() server_chanid = m.get_int()
@ -1187,6 +1283,9 @@ class BaseTransport (threading.Thread):
_handler_table = { _handler_table = {
MSG_NEWKEYS: _parse_newkeys, MSG_NEWKEYS: _parse_newkeys,
MSG_GLOBAL_REQUEST: _parse_global_request,
MSG_REQUEST_SUCCESS: _parse_request_success,
MSG_REQUEST_FAILURE: _parse_request_failure,
MSG_CHANNEL_OPEN_SUCCESS: _parse_channel_open_success, MSG_CHANNEL_OPEN_SUCCESS: _parse_channel_open_success,
MSG_CHANNEL_OPEN_FAILURE: _parse_channel_open_failure, MSG_CHANNEL_OPEN_FAILURE: _parse_channel_open_failure,
MSG_CHANNEL_OPEN: _parse_channel_open, MSG_CHANNEL_OPEN: _parse_channel_open,