From 70faf02f3eb9ecd6d33f23881fca9b9057ff297f Mon Sep 17 00:00:00 2001 From: Robey Pointer Date: Mon, 5 Apr 2004 10:37:18 +0000 Subject: [PATCH] [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.) --- paramiko/common.py | 1 + paramiko/message.py | 2 + paramiko/transport.py | 105 ++++++++++++++++++++++++++++++++++++++++-- 3 files changed, 105 insertions(+), 3 deletions(-) diff --git a/paramiko/common.py b/paramiko/common.py index 54c086c..4aa2c34 100644 --- a/paramiko/common.py +++ b/paramiko/common.py @@ -28,6 +28,7 @@ MSG_KEXINIT, MSG_NEWKEYS = range(20, 22) MSG_USERAUTH_REQUEST, MSG_USERAUTH_FAILURE, MSG_USERAUTH_SUCCESS, \ MSG_USERAUTH_BANNER = range(50, 54) 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_WINDOW_ADJUST, MSG_CHANNEL_DATA, MSG_CHANNEL_EXTENDED_DATA, \ MSG_CHANNEL_EOF, MSG_CHANNEL_CLOSE, MSG_CHANNEL_REQUEST, \ diff --git a/paramiko/message.py b/paramiko/message.py index 8615464..bb79d60 100644 --- a/paramiko/message.py +++ b/paramiko/message.py @@ -243,6 +243,8 @@ class Message (object): return self.add_mpint(i) else: return self.add_int(i) + elif type(i) is bool: + return self.add_boolean(i) elif type(i) == types.ListType: return self.add_list(i) else: diff --git a/paramiko/transport.py b/paramiko/transport.py index 58508c0..63762cc 100644 --- a/paramiko/transport.py +++ b/paramiko/transport.py @@ -307,7 +307,7 @@ class BaseTransport (threading.Thread): @type filename: string @return: True if a moduli file was successfully loaded; False otherwise. - @rtype: boolean + @rtype: bool @since: doduo @@ -364,7 +364,7 @@ class BaseTransport (threading.Thread): Return true if this session is active (open). @return: True if the session is still active (open); False if the session is closed. - @rtype: boolean + @rtype: bool """ return self.active @@ -454,7 +454,7 @@ class BaseTransport (threading.Thread): @return: True if the renegotiation was successful, and the link is using new keys; False if the session dropped during renegotiation. - @rtype: boolean + @rtype: bool """ self.completion_event = threading.Event() self._send_kex_init() @@ -466,6 +466,44 @@ class BaseTransport (threading.Thread): break 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): """ I{(subclass override)} @@ -496,6 +534,36 @@ class BaseTransport (threading.Thread): """ 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): try: self.lock.acquire() @@ -1081,6 +1149,34 @@ class BaseTransport (threading.Thread): desc = m.get_string() 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): chanid = m.get_int() server_chanid = m.get_int() @@ -1187,6 +1283,9 @@ class BaseTransport (threading.Thread): _handler_table = { 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_FAILURE: _parse_channel_open_failure, MSG_CHANNEL_OPEN: _parse_channel_open,