[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 common import *
|
||||||
|
from transport import BaseTransport
|
||||||
from auth_transport import Transport
|
from auth_transport import Transport
|
||||||
|
|
||||||
class ServerInterface (object):
|
class ServerInterface (object):
|
||||||
|
@ -229,7 +230,14 @@ class ServerInterface (object):
|
||||||
through this channel will be assumed to be connected to the requested
|
through this channel will be assumed to be connected to the requested
|
||||||
subsystem. An example of a subsystem is C{sftp}.
|
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.
|
@param channel: the L{Channel} the pty request arrived on.
|
||||||
@type channel: L{Channel}
|
@type channel: L{Channel}
|
||||||
|
@ -239,7 +247,12 @@ class ServerInterface (object):
|
||||||
subsystem; C{False} if that subsystem can't or won't be provided.
|
subsystem; C{False} if that subsystem can't or won't be provided.
|
||||||
@rtype: boolean
|
@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):
|
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")
|
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):
|
class BaseTransport (threading.Thread):
|
||||||
"""
|
"""
|
||||||
Handles protocol negotiation, key exchange, encryption, and the creation
|
Handles protocol negotiation, key exchange, encryption, and the creation
|
||||||
|
@ -258,6 +324,7 @@ class BaseTransport (threading.Thread):
|
||||||
self.server_key_dict = { }
|
self.server_key_dict = { }
|
||||||
self.server_accepts = [ ]
|
self.server_accepts = [ ]
|
||||||
self.server_accept_cv = threading.Condition(self.lock)
|
self.server_accept_cv = threading.Condition(self.lock)
|
||||||
|
self.subsystem_table = { }
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
"""
|
"""
|
||||||
|
@ -779,6 +846,22 @@ class BaseTransport (threading.Thread):
|
||||||
|
|
||||||
return
|
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...
|
### internals...
|
||||||
|
|
||||||
|
@ -1456,6 +1539,15 @@ class BaseTransport (threading.Thread):
|
||||||
lang = m.get_string()
|
lang = m.get_string()
|
||||||
self._log(DEBUG, 'Debug msg: ' + util.safe_string(msg))
|
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 = {
|
_handler_table = {
|
||||||
MSG_NEWKEYS: _parse_newkeys,
|
MSG_NEWKEYS: _parse_newkeys,
|
||||||
MSG_GLOBAL_REQUEST: _parse_global_request,
|
MSG_GLOBAL_REQUEST: _parse_global_request,
|
||||||
|
|
Loading…
Reference in New Issue