[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).
This commit is contained in:
parent
6cef2f1259
commit
8dbab50233
|
@ -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
|
||||
"""
|
||||
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):
|
||||
"""
|
||||
|
|
|
@ -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,
|
||||
|
|
Loading…
Reference in New Issue