Coverage for /pythoncovmergedfiles/medio/medio/src/paramiko/paramiko/server.py: 38%
Shortcuts on this page
r m x toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
Shortcuts on this page
r m x toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1# Copyright (C) 2003-2007 Robey Pointer <robeypointer@gmail.com>
2#
3# This file is part of paramiko.
4#
5# Paramiko is free software; you can redistribute it and/or modify it under the
6# terms of the GNU Lesser General Public License as published by the Free
7# Software Foundation; either version 2.1 of the License, or (at your option)
8# any later version.
9#
10# Paramiko is distributed in the hope that it will be useful, but WITHOUT ANY
11# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
12# A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
13# details.
14#
15# You should have received a copy of the GNU Lesser General Public License
16# along with Paramiko; if not, write to the Free Software Foundation, Inc.,
17# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19"""
20`.ServerInterface` is an interface to override for server support.
21"""
23import threading
24from paramiko import util
25from paramiko.common import (
26 DEBUG,
27 ERROR,
28 OPEN_FAILED_ADMINISTRATIVELY_PROHIBITED,
29 AUTH_FAILED,
30 AUTH_SUCCESSFUL,
31)
34class ServerInterface:
35 """
36 This class defines an interface for controlling the behavior of Paramiko
37 in server mode.
39 Methods on this class are called from Paramiko's primary thread, so you
40 shouldn't do too much work in them. (Certainly nothing that blocks or
41 sleeps.)
42 """
44 def check_channel_request(self, kind, chanid):
45 """
46 Determine if a channel request of a given type will be granted, and
47 return ``OPEN_SUCCEEDED`` or an error code. This method is
48 called in server mode when the client requests a channel, after
49 authentication is complete.
51 If you allow channel requests (and an ssh server that didn't would be
52 useless), you should also override some of the channel request methods
53 below, which are used to determine which services will be allowed on
54 a given channel:
56 - `check_channel_pty_request`
57 - `check_channel_shell_request`
58 - `check_channel_subsystem_request`
59 - `check_channel_window_change_request`
60 - `check_channel_x11_request`
61 - `check_channel_forward_agent_request`
63 The ``chanid`` parameter is a small number that uniquely identifies the
64 channel within a `.Transport`. A `.Channel` object is not created
65 unless this method returns ``OPEN_SUCCEEDED`` -- once a
66 `.Channel` object is created, you can call `.Channel.get_id` to
67 retrieve the channel ID.
69 The return value should either be ``OPEN_SUCCEEDED`` (or
70 ``0``) to allow the channel request, or one of the following error
71 codes to reject it:
73 - ``OPEN_FAILED_ADMINISTRATIVELY_PROHIBITED``
74 - ``OPEN_FAILED_CONNECT_FAILED``
75 - ``OPEN_FAILED_UNKNOWN_CHANNEL_TYPE``
76 - ``OPEN_FAILED_RESOURCE_SHORTAGE``
78 The default implementation always returns
79 ``OPEN_FAILED_ADMINISTRATIVELY_PROHIBITED``.
81 :param str kind:
82 the kind of channel the client would like to open (usually
83 ``"session"``).
84 :param int chanid: ID of the channel
85 :return: an `int` success or failure code (listed above)
86 """
87 return OPEN_FAILED_ADMINISTRATIVELY_PROHIBITED
89 def get_allowed_auths(self, username):
90 """
91 Return a list of authentication methods supported by the server.
92 This list is sent to clients attempting to authenticate, to inform them
93 of authentication methods that might be successful.
95 The "list" is actually a string of comma-separated names of types of
96 authentication. Possible values are ``"password"``, ``"publickey"``,
97 and ``"none"``.
99 The default implementation always returns ``"password"``.
101 :param str username: the username requesting authentication.
102 :return: a comma-separated `str` of authentication types
103 """
104 return "password"
106 def check_auth_none(self, username):
107 """
108 Determine if a client may open channels with no (further)
109 authentication.
111 Return ``AUTH_FAILED`` if the client must authenticate, or
112 ``AUTH_SUCCESSFUL`` if it's okay for the client to not
113 authenticate.
115 The default implementation always returns ``AUTH_FAILED``.
117 :param str username: the username of the client.
118 :return:
119 ``AUTH_FAILED`` if the authentication fails; ``AUTH_SUCCESSFUL`` if
120 it succeeds.
121 :rtype: int
122 """
123 return AUTH_FAILED
125 def check_auth_password(self, username, password):
126 """
127 Determine if a given username and password supplied by the client is
128 acceptable for use in authentication.
130 Return ``AUTH_FAILED`` if the password is not accepted,
131 ``AUTH_SUCCESSFUL`` if the password is accepted and completes
132 the authentication, or ``AUTH_PARTIALLY_SUCCESSFUL`` if your
133 authentication is stateful, and this key is accepted for
134 authentication, but more authentication is required. (In this latter
135 case, `get_allowed_auths` will be called to report to the client what
136 options it has for continuing the authentication.)
138 The default implementation always returns ``AUTH_FAILED``.
140 :param str username: the username of the authenticating client.
141 :param str password: the password given by the client.
142 :return:
143 ``AUTH_FAILED`` if the authentication fails; ``AUTH_SUCCESSFUL`` if
144 it succeeds; ``AUTH_PARTIALLY_SUCCESSFUL`` if the password auth is
145 successful, but authentication must continue.
146 :rtype: int
147 """
148 return AUTH_FAILED
150 def check_auth_publickey(self, username, key):
151 """
152 Determine if a given key supplied by the client is acceptable for use
153 in authentication. You should override this method in server mode to
154 check the username and key and decide if you would accept a signature
155 made using this key.
157 Return ``AUTH_FAILED`` if the key is not accepted,
158 ``AUTH_SUCCESSFUL`` if the key is accepted and completes the
159 authentication, or ``AUTH_PARTIALLY_SUCCESSFUL`` if your
160 authentication is stateful, and this password is accepted for
161 authentication, but more authentication is required. (In this latter
162 case, `get_allowed_auths` will be called to report to the client what
163 options it has for continuing the authentication.)
165 Note that you don't have to actually verify any key signtature here.
166 If you're willing to accept the key, Paramiko will do the work of
167 verifying the client's signature.
169 The default implementation always returns ``AUTH_FAILED``.
171 :param str username: the username of the authenticating client
172 :param .PKey key: the key object provided by the client
173 :return:
174 ``AUTH_FAILED`` if the client can't authenticate with this key;
175 ``AUTH_SUCCESSFUL`` if it can; ``AUTH_PARTIALLY_SUCCESSFUL`` if it
176 can authenticate with this key but must continue with
177 authentication
178 :rtype: int
179 """
180 return AUTH_FAILED
182 def check_auth_interactive(self, username, submethods):
183 """
184 Begin an interactive authentication challenge, if supported. You
185 should override this method in server mode if you want to support the
186 ``"keyboard-interactive"`` auth type, which requires you to send a
187 series of questions for the client to answer.
189 Return ``AUTH_FAILED`` if this auth method isn't supported. Otherwise,
190 you should return an `.InteractiveQuery` object containing the prompts
191 and instructions for the user. The response will be sent via a call
192 to `check_auth_interactive_response`.
194 The default implementation always returns ``AUTH_FAILED``.
196 :param str username: the username of the authenticating client
197 :param str submethods:
198 a comma-separated list of methods preferred by the client (usually
199 empty)
200 :return:
201 ``AUTH_FAILED`` if this auth method isn't supported; otherwise an
202 object containing queries for the user
203 :rtype: int or `.InteractiveQuery`
204 """
205 return AUTH_FAILED
207 def check_auth_interactive_response(self, responses):
208 """
209 Continue or finish an interactive authentication challenge, if
210 supported. You should override this method in server mode if you want
211 to support the ``"keyboard-interactive"`` auth type.
213 Return ``AUTH_FAILED`` if the responses are not accepted,
214 ``AUTH_SUCCESSFUL`` if the responses are accepted and complete
215 the authentication, or ``AUTH_PARTIALLY_SUCCESSFUL`` if your
216 authentication is stateful, and this set of responses is accepted for
217 authentication, but more authentication is required. (In this latter
218 case, `get_allowed_auths` will be called to report to the client what
219 options it has for continuing the authentication.)
221 If you wish to continue interactive authentication with more questions,
222 you may return an `.InteractiveQuery` object, which should cause the
223 client to respond with more answers, calling this method again. This
224 cycle can continue indefinitely.
226 The default implementation always returns ``AUTH_FAILED``.
228 :param responses: list of `str` responses from the client
229 :return:
230 ``AUTH_FAILED`` if the authentication fails; ``AUTH_SUCCESSFUL`` if
231 it succeeds; ``AUTH_PARTIALLY_SUCCESSFUL`` if the interactive auth
232 is successful, but authentication must continue; otherwise an
233 object containing queries for the user
234 :rtype: int or `.InteractiveQuery`
235 """
236 return AUTH_FAILED
238 def check_auth_gssapi_with_mic(
239 self, username, gss_authenticated=AUTH_FAILED, cc_file=None
240 ):
241 """
242 Authenticate the given user to the server if he is a valid krb5
243 principal.
245 :param str username: The username of the authenticating client
246 :param int gss_authenticated: The result of the krb5 authentication
247 :param str cc_filename: The krb5 client credentials cache filename
248 :return: ``AUTH_FAILED`` if the user is not authenticated otherwise
249 ``AUTH_SUCCESSFUL``
250 :rtype: int
251 :note: Kerberos credential delegation is not supported.
252 :see: `.ssh_gss`
253 :note: : We are just checking in L{AuthHandler} that the given user is
254 a valid krb5 principal!
255 We don't check if the krb5 principal is allowed to log in on
256 the server, because there is no way to do that in python. So
257 if you develop your own SSH server with paramiko for a certain
258 platform like Linux, you should call C{krb5_kuserok()} in
259 your local kerberos library to make sure that the
260 krb5_principal has an account on the server and is allowed to
261 log in as a user.
262 :see: http://www.unix.com/man-page/all/3/krb5_kuserok/
263 """
264 if gss_authenticated == AUTH_SUCCESSFUL:
265 return AUTH_SUCCESSFUL
266 return AUTH_FAILED
268 def check_auth_gssapi_keyex(
269 self, username, gss_authenticated=AUTH_FAILED, cc_file=None
270 ):
271 """
272 Authenticate the given user to the server if he is a valid krb5
273 principal and GSS-API Key Exchange was performed.
274 If GSS-API Key Exchange was not performed, this authentication method
275 won't be available.
277 :param str username: The username of the authenticating client
278 :param int gss_authenticated: The result of the krb5 authentication
279 :param str cc_filename: The krb5 client credentials cache filename
280 :return: ``AUTH_FAILED`` if the user is not authenticated otherwise
281 ``AUTH_SUCCESSFUL``
282 :rtype: int
283 :note: Kerberos credential delegation is not supported.
284 :see: `.ssh_gss` `.kex_gss`
285 :note: : We are just checking in L{AuthHandler} that the given user is
286 a valid krb5 principal!
287 We don't check if the krb5 principal is allowed to log in on
288 the server, because there is no way to do that in python. So
289 if you develop your own SSH server with paramiko for a certain
290 platform like Linux, you should call C{krb5_kuserok()} in
291 your local kerberos library to make sure that the
292 krb5_principal has an account on the server and is allowed
293 to log in as a user.
294 :see: http://www.unix.com/man-page/all/3/krb5_kuserok/
295 """
296 if gss_authenticated == AUTH_SUCCESSFUL:
297 return AUTH_SUCCESSFUL
298 return AUTH_FAILED
300 def enable_auth_gssapi(self):
301 """
302 Overwrite this function in your SSH server to enable GSSAPI
303 authentication.
304 The default implementation always returns false.
306 :returns bool: Whether GSSAPI authentication is enabled.
307 :see: `.ssh_gss`
308 """
309 UseGSSAPI = False
310 return UseGSSAPI
312 def check_port_forward_request(self, address, port):
313 """
314 Handle a request for port forwarding. The client is asking that
315 connections to the given address and port be forwarded back across
316 this ssh connection. An address of ``"0.0.0.0"`` indicates a global
317 address (any address associated with this server) and a port of ``0``
318 indicates that no specific port is requested (usually the OS will pick
319 a port).
321 The default implementation always returns ``False``, rejecting the
322 port forwarding request. If the request is accepted, you should return
323 the port opened for listening.
325 :param str address: the requested address
326 :param int port: the requested port
327 :return:
328 the port number (`int`) that was opened for listening, or ``False``
329 to reject
330 """
331 return False
333 def cancel_port_forward_request(self, address, port):
334 """
335 The client would like to cancel a previous port-forwarding request.
336 If the given address and port is being forwarded across this ssh
337 connection, the port should be closed.
339 :param str address: the forwarded address
340 :param int port: the forwarded port
341 """
342 pass
344 def check_global_request(self, kind, msg):
345 """
346 Handle a global request of the given ``kind``. This method is called
347 in server mode and client mode, whenever the remote host makes a global
348 request. If there are any arguments to the request, they will be in
349 ``msg``.
351 There aren't any useful global requests defined, aside from port
352 forwarding, so usually this type of request is an extension to the
353 protocol.
355 If the request was successful and you would like to return contextual
356 data to the remote host, return a tuple. Items in the tuple will be
357 sent back with the successful result. (Note that the items in the
358 tuple can only be strings, ints, or bools.)
360 The default implementation always returns ``False``, indicating that it
361 does not support any global requests.
363 .. note:: Port forwarding requests are handled separately, in
364 `check_port_forward_request`.
366 :param str kind: the kind of global request being made.
367 :param .Message msg: any extra arguments to the request.
368 :return:
369 ``True`` or a `tuple` of data if the request was granted; ``False``
370 otherwise.
371 """
372 return False
374 # ...Channel requests...
376 def check_channel_pty_request(
377 self, channel, term, width, height, pixelwidth, pixelheight, modes
378 ):
379 """
380 Determine if a pseudo-terminal of the given dimensions (usually
381 requested for shell access) can be provided on the given channel.
383 The default implementation always returns ``False``.
385 :param .Channel channel: the `.Channel` the pty request arrived on.
386 :param str term: type of terminal requested (for example, ``"vt100"``).
387 :param int width: width of screen in characters.
388 :param int height: height of screen in characters.
389 :param int pixelwidth:
390 width of screen in pixels, if known (may be ``0`` if unknown).
391 :param int pixelheight:
392 height of screen in pixels, if known (may be ``0`` if unknown).
393 :return:
394 ``True`` if the pseudo-terminal has been allocated; ``False``
395 otherwise.
396 """
397 return False
399 def check_channel_shell_request(self, channel):
400 """
401 Determine if a shell will be provided to the client on the given
402 channel. If this method returns ``True``, the channel should be
403 connected to the stdin/stdout of a shell (or something that acts like
404 a shell).
406 The default implementation always returns ``False``.
408 :param .Channel channel: the `.Channel` the request arrived on.
409 :return:
410 ``True`` if this channel is now hooked up to a shell; ``False`` if
411 a shell can't or won't be provided.
412 """
413 return False
415 def check_channel_exec_request(self, channel, command):
416 """
417 Determine if a shell command will be executed for the client. If this
418 method returns ``True``, the channel should be connected to the stdin,
419 stdout, and stderr of the shell command.
421 The default implementation always returns ``False``.
423 :param .Channel channel: the `.Channel` the request arrived on.
424 :param str command: the command to execute.
425 :return:
426 ``True`` if this channel is now hooked up to the stdin, stdout, and
427 stderr of the executing command; ``False`` if the command will not
428 be executed.
430 .. versionadded:: 1.1
431 """
432 return False
434 def check_channel_subsystem_request(self, channel, name):
435 """
436 Determine if a requested subsystem will be provided to the client on
437 the given channel. If this method returns ``True``, all future I/O
438 through this channel will be assumed to be connected to the requested
439 subsystem. An example of a subsystem is ``sftp``.
441 The default implementation checks for a subsystem handler assigned via
442 `.Transport.set_subsystem_handler`.
443 If one has been set, the handler is invoked and this method returns
444 ``True``. Otherwise it returns ``False``.
446 .. note:: Because the default implementation uses the `.Transport` to
447 identify valid subsystems, you probably won't need to override this
448 method.
450 :param .Channel channel: the `.Channel` the pty request arrived on.
451 :param str name: name of the requested subsystem.
452 :return:
453 ``True`` if this channel is now hooked up to the requested
454 subsystem; ``False`` if that subsystem can't or won't be provided.
455 """
456 transport = channel.get_transport()
457 handler_class, args, kwargs = transport._get_subsystem_handler(name)
458 if handler_class is None:
459 return False
460 handler = handler_class(channel, name, self, *args, **kwargs)
461 handler.start()
462 return True
464 def check_channel_window_change_request(
465 self, channel, width, height, pixelwidth, pixelheight
466 ):
467 """
468 Determine if the pseudo-terminal on the given channel can be resized.
469 This only makes sense if a pty was previously allocated on it.
471 The default implementation always returns ``False``.
473 :param .Channel channel: the `.Channel` the pty request arrived on.
474 :param int width: width of screen in characters.
475 :param int height: height of screen in characters.
476 :param int pixelwidth:
477 width of screen in pixels, if known (may be ``0`` if unknown).
478 :param int pixelheight:
479 height of screen in pixels, if known (may be ``0`` if unknown).
480 :return: ``True`` if the terminal was resized; ``False`` if not.
481 """
482 return False
484 def check_channel_x11_request(
485 self,
486 channel,
487 single_connection,
488 auth_protocol,
489 auth_cookie,
490 screen_number,
491 ):
492 """
493 Determine if the client will be provided with an X11 session. If this
494 method returns ``True``, X11 applications should be routed through new
495 SSH channels, using `.Transport.open_x11_channel`.
497 The default implementation always returns ``False``.
499 :param .Channel channel: the `.Channel` the X11 request arrived on
500 :param bool single_connection:
501 ``True`` if only a single X11 channel should be opened, else
502 ``False``.
503 :param str auth_protocol: the protocol used for X11 authentication
504 :param str auth_cookie: the cookie used to authenticate to X11
505 :param int screen_number: the number of the X11 screen to connect to
506 :return: ``True`` if the X11 session was opened; ``False`` if not
507 """
508 return False
510 def check_channel_forward_agent_request(self, channel):
511 """
512 Determine if the client will be provided with an forward agent session.
513 If this method returns ``True``, the server will allow SSH Agent
514 forwarding.
516 The default implementation always returns ``False``.
518 :param .Channel channel: the `.Channel` the request arrived on
519 :return: ``True`` if the AgentForward was loaded; ``False`` if not
521 If ``True`` is returned, the server should create an
522 :class:`AgentServerProxy` to access the agent.
523 """
524 return False
526 def check_channel_direct_tcpip_request(self, chanid, origin, destination):
527 """
528 Determine if a local port forwarding channel will be granted, and
529 return ``OPEN_SUCCEEDED`` or an error code. This method is
530 called in server mode when the client requests a channel, after
531 authentication is complete.
533 The ``chanid`` parameter is a small number that uniquely identifies the
534 channel within a `.Transport`. A `.Channel` object is not created
535 unless this method returns ``OPEN_SUCCEEDED`` -- once a
536 `.Channel` object is created, you can call `.Channel.get_id` to
537 retrieve the channel ID.
539 The origin and destination parameters are (ip_address, port) tuples
540 that correspond to both ends of the TCP connection in the forwarding
541 tunnel.
543 The return value should either be ``OPEN_SUCCEEDED`` (or
544 ``0``) to allow the channel request, or one of the following error
545 codes to reject it:
547 - ``OPEN_FAILED_ADMINISTRATIVELY_PROHIBITED``
548 - ``OPEN_FAILED_CONNECT_FAILED``
549 - ``OPEN_FAILED_UNKNOWN_CHANNEL_TYPE``
550 - ``OPEN_FAILED_RESOURCE_SHORTAGE``
552 The default implementation always returns
553 ``OPEN_FAILED_ADMINISTRATIVELY_PROHIBITED``.
555 :param int chanid: ID of the channel
556 :param tuple origin:
557 2-tuple containing the IP address and port of the originator
558 (client side)
559 :param tuple destination:
560 2-tuple containing the IP address and port of the destination
561 (server side)
562 :return: an `int` success or failure code (listed above)
563 """
564 return OPEN_FAILED_ADMINISTRATIVELY_PROHIBITED
566 def check_channel_env_request(self, channel, name, value):
567 """
568 Check whether a given environment variable can be specified for the
569 given channel. This method should return ``True`` if the server
570 is willing to set the specified environment variable. Note that
571 some environment variables (e.g., PATH) can be exceedingly
572 dangerous, so blindly allowing the client to set the environment
573 is almost certainly not a good idea.
575 The default implementation always returns ``False``.
577 :param channel: the `.Channel` the env request arrived on
578 :param str name: name
579 :param str value: Channel value
580 :returns: A boolean
581 """
582 return False
584 def get_banner(self):
585 """
586 A pre-login banner to display to the user. The message may span
587 multiple lines separated by crlf pairs. The language should be in
588 rfc3066 style, for example: en-US
590 The default implementation always returns ``(None, None)``.
592 :returns: A tuple containing the banner and language code.
594 .. versionadded:: 2.3
595 """
596 return (None, None)
599class InteractiveQuery:
600 """
601 A query (set of prompts) for a user during interactive authentication.
602 """
604 def __init__(self, name="", instructions="", *prompts):
605 """
606 Create a new interactive query to send to the client. The name and
607 instructions are optional, but are generally displayed to the end
608 user. A list of prompts may be included, or they may be added via
609 the `add_prompt` method.
611 :param str name: name of this query
612 :param str instructions:
613 user instructions (usually short) about this query
614 :param str prompts: one or more authentication prompts
615 """
616 self.name = name
617 self.instructions = instructions
618 self.prompts = []
619 for x in prompts:
620 if isinstance(x, str):
621 self.add_prompt(x)
622 else:
623 self.add_prompt(x[0], x[1])
625 def add_prompt(self, prompt, echo=True):
626 """
627 Add a prompt to this query. The prompt should be a (reasonably short)
628 string. Multiple prompts can be added to the same query.
630 :param str prompt: the user prompt
631 :param bool echo:
632 ``True`` (default) if the user's response should be echoed;
633 ``False`` if not (for a password or similar)
634 """
635 self.prompts.append((prompt, echo))
638class SubsystemHandler(threading.Thread):
639 """
640 Handler for a subsystem in server mode. If you create a subclass of this
641 class and pass it to `.Transport.set_subsystem_handler`, an object of this
642 class will be created for each request for this subsystem. Each new object
643 will be executed within its own new thread by calling `start_subsystem`.
644 When that method completes, the channel is closed.
646 For example, if you made a subclass ``MP3Handler`` and registered it as the
647 handler for subsystem ``"mp3"``, then whenever a client has successfully
648 authenticated and requests subsystem ``"mp3"``, an object of class
649 ``MP3Handler`` will be created, and `start_subsystem` will be called on
650 it from a new thread.
651 """
653 def __init__(self, channel, name, server):
654 """
655 Create a new handler for a channel. This is used by `.ServerInterface`
656 to start up a new handler when a channel requests this subsystem. You
657 don't need to override this method, but if you do, be sure to pass the
658 ``channel`` and ``name`` parameters through to the original
659 ``__init__`` method here.
661 :param .Channel channel: the channel associated with this
662 subsystem request.
663 :param str name: name of the requested subsystem.
664 :param .ServerInterface server:
665 the server object for the session that started this subsystem
666 """
667 threading.Thread.__init__(self, target=self._run)
668 self.__channel = channel
669 self.__transport = channel.get_transport()
670 self.__name = name
671 self.__server = server
673 def get_server(self):
674 """
675 Return the `.ServerInterface` object associated with this channel and
676 subsystem.
677 """
678 return self.__server
680 def _run(self):
681 try:
682 self.__transport._log(
683 DEBUG, "Starting handler for subsystem {}".format(self.__name)
684 )
685 self.start_subsystem(self.__name, self.__transport, self.__channel)
686 except Exception as e:
687 self.__transport._log(
688 ERROR,
689 'Exception in subsystem handler for "{}": {}'.format(
690 self.__name, e
691 ),
692 )
693 self.__transport._log(ERROR, util.tb_strings())
694 try:
695 self.finish_subsystem()
696 except:
697 pass
699 def start_subsystem(self, name, transport, channel):
700 """
701 Process an ssh subsystem in server mode. This method is called on a
702 new object (and in a new thread) for each subsystem request. It is
703 assumed that all subsystem logic will take place here, and when the
704 subsystem is finished, this method will return. After this method
705 returns, the channel is closed.
707 The combination of ``transport`` and ``channel`` are unique; this
708 handler corresponds to exactly one `.Channel` on one `.Transport`.
710 .. note::
711 It is the responsibility of this method to exit if the underlying
712 `.Transport` is closed. This can be done by checking
713 `.Transport.is_active` or noticing an EOF on the `.Channel`. If
714 this method loops forever without checking for this case, your
715 Python interpreter may refuse to exit because this thread will
716 still be running.
718 :param str name: name of the requested subsystem.
719 :param .Transport transport: the server-mode `.Transport`.
720 :param .Channel channel: the channel associated with this subsystem
721 request.
722 """
723 pass
725 def finish_subsystem(self):
726 """
727 Perform any cleanup at the end of a subsystem. The default
728 implementation just closes the channel.
730 .. versionadded:: 1.1
731 """
732 self.__channel.close()