summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorRobey Pointer <robey@lag.net>2004-09-05 07:41:45 +0000
committerRobey Pointer <robey@lag.net>2004-09-05 07:41:45 +0000
commit8dbab502339064816a391ba45463c87360e88e80 (patch)
treef8e51a8db6d4a26d4470736954b55166814ea9ff
parent6cef2f12591f5384f92457aa8cc49e153348d07b (diff)
[project @ Arch-1:robey@lag.net--2003-public%secsh--dev--1.0--patch-72]
some framework for adding subsystem handlers in server mode you can now register a subsystem with a Transport by passing in the name (like "sftp") and a class (like a hypothetical SFTPServer). the default ServerInterface.check_channel_request_subsystem now checks this table in Transport, and if it finds a match, it creates a new thread for the handler and calls into it. a new class SubsystemHandler is added for this purpose (to be subclassed).
-rw-r--r--paramiko/server.py17
-rw-r--r--paramiko/transport.py92
2 files changed, 107 insertions, 2 deletions
diff --git a/paramiko/server.py b/paramiko/server.py
index 25924b61..03daba71 100644
--- a/paramiko/server.py
+++ b/paramiko/server.py
@@ -23,6 +23,7 @@ L{ServerInterface} is an interface to override for server support.
"""
from common import *
+from transport import BaseTransport
from auth_transport import Transport
class ServerInterface (object):
@@ -229,7 +230,14 @@ class ServerInterface (object):
through this channel will be assumed to be connected to the requested
subsystem. An example of a subsystem is C{sftp}.
- The default implementation always returns C{False}.
+ The default implementation checks for a subsystem handler assigned via
+ L{Transport.set_subsystem_handler <BaseTransport.set_subsystem_handler>}.
+ If one has been set, the handler is invoked and this method returns
+ C{True}. Otherwise it returns C{False}.
+
+ @note: Because the default implementation uses the L{Transport} to
+ identify valid subsystems, you probably won't need to override this
+ method.
@param channel: the L{Channel} the pty request arrived on.
@type channel: L{Channel}
@@ -239,7 +247,12 @@ class ServerInterface (object):
subsystem; C{False} if that subsystem can't or won't be provided.
@rtype: boolean
"""
- return False
+ handler_class = channel.get_transport()._get_subsystem_handler(name)
+ if handler_class is None:
+ return False
+ handler = handler_class(channel, name)
+ handler.start()
+ return True
def check_channel_window_change_request(self, channel, width, height, pixelwidth, pixelheight):
"""
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,