Coverage for /pythoncovmergedfiles/medio/medio/src/paramiko/paramiko/server.py: 39%

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

87 statements  

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. 

18 

19""" 

20`.ServerInterface` is an interface to override for server support. 

21""" 

22 

23import threading 

24 

25from paramiko import util 

26from paramiko.common import ( 

27 AUTH_FAILED, 

28 DEBUG, 

29 ERROR, 

30 OPEN_FAILED_ADMINISTRATIVELY_PROHIBITED, 

31) 

32 

33 

34class ServerInterface: 

35 """ 

36 This class defines an interface for controlling the behavior of Paramiko 

37 in server mode. 

38 

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 """ 

43 

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. 

50 

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: 

55 

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` 

62 

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. 

68 

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: 

72 

73 - ``OPEN_FAILED_ADMINISTRATIVELY_PROHIBITED`` 

74 - ``OPEN_FAILED_CONNECT_FAILED`` 

75 - ``OPEN_FAILED_UNKNOWN_CHANNEL_TYPE`` 

76 - ``OPEN_FAILED_RESOURCE_SHORTAGE`` 

77 

78 The default implementation always returns 

79 ``OPEN_FAILED_ADMINISTRATIVELY_PROHIBITED``. 

80 

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 

88 

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. 

94 

95 The "list" is actually a string of comma-separated names of types of 

96 authentication. Possible values are ``"password"``, ``"publickey"``, 

97 and ``"none"``. 

98 

99 The default implementation always returns ``"password"``. 

100 

101 :param str username: the username requesting authentication. 

102 :return: a comma-separated `str` of authentication types 

103 """ 

104 return "password" 

105 

106 def check_auth_none(self, username): 

107 """ 

108 Determine if a client may open channels with no (further) 

109 authentication. 

110 

111 Return ``AUTH_FAILED`` if the client must authenticate, or 

112 ``AUTH_SUCCESSFUL`` if it's okay for the client to not 

113 authenticate. 

114 

115 The default implementation always returns ``AUTH_FAILED``. 

116 

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 

124 

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. 

129 

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.) 

137 

138 The default implementation always returns ``AUTH_FAILED``. 

139 

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 

149 

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. 

156 

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.) 

164 

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. 

168 

169 The default implementation always returns ``AUTH_FAILED``. 

170 

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 

181 

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. 

188 

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`. 

193 

194 The default implementation always returns ``AUTH_FAILED``. 

195 

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 

206 

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. 

212 

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.) 

220 

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. 

225 

226 The default implementation always returns ``AUTH_FAILED``. 

227 

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 

237 

238 def check_port_forward_request(self, address, port): 

239 """ 

240 Handle a request for port forwarding. The client is asking that 

241 connections to the given address and port be forwarded back across 

242 this ssh connection. An address of ``"0.0.0.0"`` indicates a global 

243 address (any address associated with this server) and a port of ``0`` 

244 indicates that no specific port is requested (usually the OS will pick 

245 a port). 

246 

247 The default implementation always returns ``False``, rejecting the 

248 port forwarding request. If the request is accepted, you should return 

249 the port opened for listening. 

250 

251 :param str address: the requested address 

252 :param int port: the requested port 

253 :return: 

254 the port number (`int`) that was opened for listening, or ``False`` 

255 to reject 

256 """ 

257 return False 

258 

259 def cancel_port_forward_request(self, address, port): 

260 """ 

261 The client would like to cancel a previous port-forwarding request. 

262 If the given address and port is being forwarded across this ssh 

263 connection, the port should be closed. 

264 

265 :param str address: the forwarded address 

266 :param int port: the forwarded port 

267 """ 

268 pass 

269 

270 def check_global_request(self, kind, msg): 

271 """ 

272 Handle a global request of the given ``kind``. This method is called 

273 in server mode and client mode, whenever the remote host makes a global 

274 request. If there are any arguments to the request, they will be in 

275 ``msg``. 

276 

277 There aren't any useful global requests defined, aside from port 

278 forwarding, so usually this type of request is an extension to the 

279 protocol. 

280 

281 If the request was successful and you would like to return contextual 

282 data to the remote host, return a tuple. Items in the tuple will be 

283 sent back with the successful result. (Note that the items in the 

284 tuple can only be strings, ints, or bools.) 

285 

286 The default implementation always returns ``False``, indicating that it 

287 does not support any global requests. 

288 

289 .. note:: Port forwarding requests are handled separately, in 

290 `check_port_forward_request`. 

291 

292 :param str kind: the kind of global request being made. 

293 :param .Message msg: any extra arguments to the request. 

294 :return: 

295 ``True`` or a `tuple` of data if the request was granted; ``False`` 

296 otherwise. 

297 """ 

298 return False 

299 

300 # ...Channel requests... 

301 

302 def check_channel_pty_request( 

303 self, channel, term, width, height, pixelwidth, pixelheight, modes 

304 ): 

305 """ 

306 Determine if a pseudo-terminal of the given dimensions (usually 

307 requested for shell access) can be provided on the given channel. 

308 

309 The default implementation always returns ``False``. 

310 

311 :param .Channel channel: the `.Channel` the pty request arrived on. 

312 :param str term: type of terminal requested (for example, ``"vt100"``). 

313 :param int width: width of screen in characters. 

314 :param int height: height of screen in characters. 

315 :param int pixelwidth: 

316 width of screen in pixels, if known (may be ``0`` if unknown). 

317 :param int pixelheight: 

318 height of screen in pixels, if known (may be ``0`` if unknown). 

319 :return: 

320 ``True`` if the pseudo-terminal has been allocated; ``False`` 

321 otherwise. 

322 """ 

323 return False 

324 

325 def check_channel_shell_request(self, channel): 

326 """ 

327 Determine if a shell will be provided to the client on the given 

328 channel. If this method returns ``True``, the channel should be 

329 connected to the stdin/stdout of a shell (or something that acts like 

330 a shell). 

331 

332 The default implementation always returns ``False``. 

333 

334 :param .Channel channel: the `.Channel` the request arrived on. 

335 :return: 

336 ``True`` if this channel is now hooked up to a shell; ``False`` if 

337 a shell can't or won't be provided. 

338 """ 

339 return False 

340 

341 def check_channel_exec_request(self, channel, command): 

342 """ 

343 Determine if a shell command will be executed for the client. If this 

344 method returns ``True``, the channel should be connected to the stdin, 

345 stdout, and stderr of the shell command. 

346 

347 The default implementation always returns ``False``. 

348 

349 :param .Channel channel: the `.Channel` the request arrived on. 

350 :param str command: the command to execute. 

351 :return: 

352 ``True`` if this channel is now hooked up to the stdin, stdout, and 

353 stderr of the executing command; ``False`` if the command will not 

354 be executed. 

355 

356 .. versionadded:: 1.1 

357 """ 

358 return False 

359 

360 def check_channel_subsystem_request(self, channel, name): 

361 """ 

362 Determine if a requested subsystem will be provided to the client on 

363 the given channel. If this method returns ``True``, all future I/O 

364 through this channel will be assumed to be connected to the requested 

365 subsystem. An example of a subsystem is ``sftp``. 

366 

367 The default implementation checks for a subsystem handler assigned via 

368 `.Transport.set_subsystem_handler`. 

369 If one has been set, the handler is invoked and this method returns 

370 ``True``. Otherwise it returns ``False``. 

371 

372 .. note:: Because the default implementation uses the `.Transport` to 

373 identify valid subsystems, you probably won't need to override this 

374 method. 

375 

376 :param .Channel channel: the `.Channel` the pty request arrived on. 

377 :param str name: name of the requested subsystem. 

378 :return: 

379 ``True`` if this channel is now hooked up to the requested 

380 subsystem; ``False`` if that subsystem can't or won't be provided. 

381 """ 

382 transport = channel.get_transport() 

383 handler_class, args, kwargs = transport._get_subsystem_handler(name) 

384 if handler_class is None: 

385 return False 

386 handler = handler_class(channel, name, self, *args, **kwargs) 

387 handler.start() 

388 return True 

389 

390 def check_channel_window_change_request( 

391 self, channel, width, height, pixelwidth, pixelheight 

392 ): 

393 """ 

394 Determine if the pseudo-terminal on the given channel can be resized. 

395 This only makes sense if a pty was previously allocated on it. 

396 

397 The default implementation always returns ``False``. 

398 

399 :param .Channel channel: the `.Channel` the pty request arrived on. 

400 :param int width: width of screen in characters. 

401 :param int height: height of screen in characters. 

402 :param int pixelwidth: 

403 width of screen in pixels, if known (may be ``0`` if unknown). 

404 :param int pixelheight: 

405 height of screen in pixels, if known (may be ``0`` if unknown). 

406 :return: ``True`` if the terminal was resized; ``False`` if not. 

407 """ 

408 return False 

409 

410 def check_channel_x11_request( 

411 self, 

412 channel, 

413 single_connection, 

414 auth_protocol, 

415 auth_cookie, 

416 screen_number, 

417 ): 

418 """ 

419 Determine if the client will be provided with an X11 session. If this 

420 method returns ``True``, X11 applications should be routed through new 

421 SSH channels, using `.Transport.open_x11_channel`. 

422 

423 The default implementation always returns ``False``. 

424 

425 :param .Channel channel: the `.Channel` the X11 request arrived on 

426 :param bool single_connection: 

427 ``True`` if only a single X11 channel should be opened, else 

428 ``False``. 

429 :param str auth_protocol: the protocol used for X11 authentication 

430 :param str auth_cookie: the cookie used to authenticate to X11 

431 :param int screen_number: the number of the X11 screen to connect to 

432 :return: ``True`` if the X11 session was opened; ``False`` if not 

433 """ 

434 return False 

435 

436 def check_channel_forward_agent_request(self, channel): 

437 """ 

438 Determine if the client will be provided with an forward agent session. 

439 If this method returns ``True``, the server will allow SSH Agent 

440 forwarding. 

441 

442 The default implementation always returns ``False``. 

443 

444 :param .Channel channel: the `.Channel` the request arrived on 

445 :return: ``True`` if the AgentForward was loaded; ``False`` if not 

446 

447 If ``True`` is returned, the server should create an 

448 :class:`AgentServerProxy` to access the agent. 

449 """ 

450 return False 

451 

452 def check_channel_direct_tcpip_request(self, chanid, origin, destination): 

453 """ 

454 Determine if a local port forwarding channel will be granted, and 

455 return ``OPEN_SUCCEEDED`` or an error code. This method is 

456 called in server mode when the client requests a channel, after 

457 authentication is complete. 

458 

459 The ``chanid`` parameter is a small number that uniquely identifies the 

460 channel within a `.Transport`. A `.Channel` object is not created 

461 unless this method returns ``OPEN_SUCCEEDED`` -- once a 

462 `.Channel` object is created, you can call `.Channel.get_id` to 

463 retrieve the channel ID. 

464 

465 The origin and destination parameters are (ip_address, port) tuples 

466 that correspond to both ends of the TCP connection in the forwarding 

467 tunnel. 

468 

469 The return value should either be ``OPEN_SUCCEEDED`` (or 

470 ``0``) to allow the channel request, or one of the following error 

471 codes to reject it: 

472 

473 - ``OPEN_FAILED_ADMINISTRATIVELY_PROHIBITED`` 

474 - ``OPEN_FAILED_CONNECT_FAILED`` 

475 - ``OPEN_FAILED_UNKNOWN_CHANNEL_TYPE`` 

476 - ``OPEN_FAILED_RESOURCE_SHORTAGE`` 

477 

478 The default implementation always returns 

479 ``OPEN_FAILED_ADMINISTRATIVELY_PROHIBITED``. 

480 

481 :param int chanid: ID of the channel 

482 :param tuple origin: 

483 2-tuple containing the IP address and port of the originator 

484 (client side) 

485 :param tuple destination: 

486 2-tuple containing the IP address and port of the destination 

487 (server side) 

488 :return: an `int` success or failure code (listed above) 

489 """ 

490 return OPEN_FAILED_ADMINISTRATIVELY_PROHIBITED 

491 

492 def check_channel_env_request(self, channel, name, value): 

493 """ 

494 Check whether a given environment variable can be specified for the 

495 given channel. This method should return ``True`` if the server 

496 is willing to set the specified environment variable. Note that 

497 some environment variables (e.g., PATH) can be exceedingly 

498 dangerous, so blindly allowing the client to set the environment 

499 is almost certainly not a good idea. 

500 

501 The default implementation always returns ``False``. 

502 

503 :param channel: the `.Channel` the env request arrived on 

504 :param str name: name 

505 :param str value: Channel value 

506 :returns: A boolean 

507 """ 

508 return False 

509 

510 def get_banner(self): 

511 """ 

512 A pre-login banner to display to the user. The message may span 

513 multiple lines separated by crlf pairs. The language should be in 

514 rfc3066 style, for example: en-US 

515 

516 The default implementation always returns ``(None, None)``. 

517 

518 :returns: A tuple containing the banner and language code. 

519 

520 .. versionadded:: 2.3 

521 """ 

522 return (None, None) 

523 

524 

525class InteractiveQuery: 

526 """ 

527 A query (set of prompts) for a user during interactive authentication. 

528 """ 

529 

530 def __init__(self, name="", instructions="", *prompts): 

531 """ 

532 Create a new interactive query to send to the client. The name and 

533 instructions are optional, but are generally displayed to the end 

534 user. A list of prompts may be included, or they may be added via 

535 the `add_prompt` method. 

536 

537 :param str name: name of this query 

538 :param str instructions: 

539 user instructions (usually short) about this query 

540 :param str prompts: one or more authentication prompts 

541 """ 

542 self.name = name 

543 self.instructions = instructions 

544 self.prompts = [] 

545 for x in prompts: 

546 if isinstance(x, str): 

547 self.add_prompt(x) 

548 else: 

549 self.add_prompt(x[0], x[1]) 

550 

551 def add_prompt(self, prompt, echo=True): 

552 """ 

553 Add a prompt to this query. The prompt should be a (reasonably short) 

554 string. Multiple prompts can be added to the same query. 

555 

556 :param str prompt: the user prompt 

557 :param bool echo: 

558 ``True`` (default) if the user's response should be echoed; 

559 ``False`` if not (for a password or similar) 

560 """ 

561 self.prompts.append((prompt, echo)) 

562 

563 

564class SubsystemHandler(threading.Thread): 

565 """ 

566 Handler for a subsystem in server mode. If you create a subclass of this 

567 class and pass it to `.Transport.set_subsystem_handler`, an object of this 

568 class will be created for each request for this subsystem. Each new object 

569 will be executed within its own new thread by calling `start_subsystem`. 

570 When that method completes, the channel is closed. 

571 

572 For example, if you made a subclass ``MP3Handler`` and registered it as the 

573 handler for subsystem ``"mp3"``, then whenever a client has successfully 

574 authenticated and requests subsystem ``"mp3"``, an object of class 

575 ``MP3Handler`` will be created, and `start_subsystem` will be called on 

576 it from a new thread. 

577 """ 

578 

579 def __init__(self, channel, name, server): 

580 """ 

581 Create a new handler for a channel. This is used by `.ServerInterface` 

582 to start up a new handler when a channel requests this subsystem. You 

583 don't need to override this method, but if you do, be sure to pass the 

584 ``channel`` and ``name`` parameters through to the original 

585 ``__init__`` method here. 

586 

587 :param .Channel channel: the channel associated with this 

588 subsystem request. 

589 :param str name: name of the requested subsystem. 

590 :param .ServerInterface server: 

591 the server object for the session that started this subsystem 

592 """ 

593 threading.Thread.__init__(self, target=self._run) 

594 self.__channel = channel 

595 self.__transport = channel.get_transport() 

596 self.__name = name 

597 self.__server = server 

598 

599 def get_server(self): 

600 """ 

601 Return the `.ServerInterface` object associated with this channel and 

602 subsystem. 

603 """ 

604 return self.__server 

605 

606 def _run(self): 

607 try: 

608 self.__transport._log( 

609 DEBUG, "Starting handler for subsystem {}".format(self.__name) 

610 ) 

611 self.start_subsystem(self.__name, self.__transport, self.__channel) 

612 except Exception as e: 

613 self.__transport._log( 

614 ERROR, 

615 'Exception in subsystem handler for "{}": {}'.format( 

616 self.__name, e 

617 ), 

618 ) 

619 self.__transport._log(ERROR, util.tb_strings()) 

620 try: 

621 self.finish_subsystem() 

622 except: 

623 pass 

624 

625 def start_subsystem(self, name, transport, channel): 

626 """ 

627 Process an ssh subsystem in server mode. This method is called on a 

628 new object (and in a new thread) for each subsystem request. It is 

629 assumed that all subsystem logic will take place here, and when the 

630 subsystem is finished, this method will return. After this method 

631 returns, the channel is closed. 

632 

633 The combination of ``transport`` and ``channel`` are unique; this 

634 handler corresponds to exactly one `.Channel` on one `.Transport`. 

635 

636 .. note:: 

637 It is the responsibility of this method to exit if the underlying 

638 `.Transport` is closed. This can be done by checking 

639 `.Transport.is_active` or noticing an EOF on the `.Channel`. If 

640 this method loops forever without checking for this case, your 

641 Python interpreter may refuse to exit because this thread will 

642 still be running. 

643 

644 :param str name: name of the requested subsystem. 

645 :param .Transport transport: the server-mode `.Transport`. 

646 :param .Channel channel: the channel associated with this subsystem 

647 request. 

648 """ 

649 pass 

650 

651 def finish_subsystem(self): 

652 """ 

653 Perform any cleanup at the end of a subsystem. The default 

654 implementation just closes the channel. 

655 

656 .. versionadded:: 1.1 

657 """ 

658 self.__channel.close()