diff options
Diffstat (limited to 'paramiko/transport.py')
-rw-r--r-- | paramiko/transport.py | 92 |
1 files changed, 92 insertions, 0 deletions
diff --git a/paramiko/transport.py b/paramiko/transport.py index 15486712..e017ac0c 100644 --- a/paramiko/transport.py +++ b/paramiko/transport.py @@ -123,6 +123,72 @@ class SecurityOptions (object): kex = property(_get_kex, _set_kex, None, "Key exchange algorithms") +class SubsystemHandler (threading.Thread): + """ + Handler for a subsytem in server mode. If you create a subclass of this + class and pass it to + L{Transport.set_subsystem_handler <BaseTransport.set_subsystem_handler>}, + an object of this + class will be created for each request for this subsystem. Each new object + will be executed within its own new thread by calling L{start_subsystem}. + When that method completes, the channel is closed. + + For example, if you made a subclass C{MP3Handler} and registered it as the + handler for subsystem C{"mp3"}, then whenever a client has successfully + authenticated and requests subsytem C{"mp3"}, an object of class + C{MP3Handler} will be created, and L{start_subsystem} will be called on + it from a new thread. + + @since: ivysaur + """ + def __init__(self, channel, name): + threading.Thread.__init__(self, target=self._run) + self.__channel = channel + self.__transport = channel.get_transport() + self.__name = name + + def _run(self): + try: + self.__transport._log(DEBUG, 'Starting handler for subsystem %s' % self.__name) + self.start_subsystem(self.__name, self.__transport, self.__channel) + except Exception, e: + self.__transport._log(ERROR, 'Exception in subsystem handler for "%s": %s' % + (self.__name, str(e))) + self.__transport._log(ERROR, util.tb_strings()) + try: + self.__channel.close() + except: + pass + + def start_subsystem(self, name, transport, channel): + """ + Process an ssh subsystem in server mode. This method is called on a + new object (and in a new thread) for each subsystem request. It is + assumed that all subsystem logic will take place here, and when the + subsystem is finished, this method will return. After this method + returns, the channel is closed. + + The combination of C{transport} and C{channel} are unique; this handler + corresponds to exactly one L{Channel} on one L{Transport}. + + @note: It is the responsibility of this method to exit if the + underlying L{Transport} is closed. This can be done by checking + L{Transport.is_active <BaseTransport.is_active>} or noticing an EOF + on the L{Channel}. + If this method loops forever without checking for this case, your + python interpreter may refuse to exit because this thread will still + be running. + + @param name: name of the requested subsystem. + @type name: str + @param transport: the server-mode L{Transport}. + @type transport: L{Transport} + @param channel: the channel associated with this subsystem request. + @type channel: L{Channel} + """ + pass + + class BaseTransport (threading.Thread): """ Handles protocol negotiation, key exchange, encryption, and the creation @@ -258,6 +324,7 @@ class BaseTransport (threading.Thread): self.server_key_dict = { } self.server_accepts = [ ] self.server_accept_cv = threading.Condition(self.lock) + self.subsystem_table = { } def __repr__(self): """ @@ -779,6 +846,22 @@ class BaseTransport (threading.Thread): return + def set_subsystem_handler(self, name, handler): + """ + Set the handler class for a subsystem in server mode. + + @param name: name of the subsystem. + @type name: str + @param handler: subclass of L{SubsystemHandler} that handles this + subsystem. + @type handler: class + """ + try: + self.lock.acquire() + self.subsystem_table[name] = handler + finally: + self.lock.release() + ### internals... @@ -1456,6 +1539,15 @@ class BaseTransport (threading.Thread): lang = m.get_string() self._log(DEBUG, 'Debug msg: ' + util.safe_string(msg)) + def _get_subsystem_handler(self, name): + try: + self.lock.acquire() + if not self.subsystem_table.has_key(name): + return None + return self.subsystem_table[name] + finally: + self.lock.release() + _handler_table = { MSG_NEWKEYS: _parse_newkeys, MSG_GLOBAL_REQUEST: _parse_global_request, |