Funktionierender Prototyp des Serious Games zur Vermittlung von Wissen zu Software-Engineering-Arbeitsmodellen.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

connection.py 25KB

1 year ago
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679
  1. # -*- test-case-name: twisted.conch.test.test_connection -*-
  2. # Copyright (c) Twisted Matrix Laboratories.
  3. # See LICENSE for details.
  4. """
  5. This module contains the implementation of the ssh-connection service, which
  6. allows access to the shell and port-forwarding.
  7. Maintainer: Paul Swartz
  8. """
  9. import string
  10. import struct
  11. import twisted.internet.error
  12. from twisted.conch import error
  13. from twisted.conch.ssh import common, service
  14. from twisted.internet import defer
  15. from twisted.logger import Logger
  16. from twisted.python.compat import nativeString, networkString
  17. class SSHConnection(service.SSHService):
  18. """
  19. An implementation of the 'ssh-connection' service. It is used to
  20. multiplex multiple channels over the single SSH connection.
  21. @ivar localChannelID: the next number to use as a local channel ID.
  22. @type localChannelID: L{int}
  23. @ivar channels: a L{dict} mapping a local channel ID to C{SSHChannel}
  24. subclasses.
  25. @type channels: L{dict}
  26. @ivar localToRemoteChannel: a L{dict} mapping a local channel ID to a
  27. remote channel ID.
  28. @type localToRemoteChannel: L{dict}
  29. @ivar channelsToRemoteChannel: a L{dict} mapping a C{SSHChannel} subclass
  30. to remote channel ID.
  31. @type channelsToRemoteChannel: L{dict}
  32. @ivar deferreds: a L{dict} mapping a local channel ID to a C{list} of
  33. C{Deferreds} for outstanding channel requests. Also, the 'global'
  34. key stores the C{list} of pending global request C{Deferred}s.
  35. """
  36. name = b"ssh-connection"
  37. _log = Logger()
  38. def __init__(self):
  39. self.localChannelID = 0 # this is the current # to use for channel ID
  40. # local channel ID -> remote channel ID
  41. self.localToRemoteChannel = {}
  42. # local channel ID -> subclass of SSHChannel
  43. self.channels = {}
  44. # subclass of SSHChannel -> remote channel ID
  45. self.channelsToRemoteChannel = {}
  46. # local channel -> list of deferreds for pending requests
  47. # or 'global' -> list of deferreds for global requests
  48. self.deferreds = {"global": []}
  49. self.transport = None # gets set later
  50. def serviceStarted(self):
  51. if hasattr(self.transport, "avatar"):
  52. self.transport.avatar.conn = self
  53. def serviceStopped(self):
  54. """
  55. Called when the connection is stopped.
  56. """
  57. # Close any fully open channels
  58. for channel in list(self.channelsToRemoteChannel.keys()):
  59. self.channelClosed(channel)
  60. # Indicate failure to any channels that were in the process of
  61. # opening but not yet open.
  62. while self.channels:
  63. (_, channel) = self.channels.popitem()
  64. channel.openFailed(twisted.internet.error.ConnectionLost())
  65. # Errback any unfinished global requests.
  66. self._cleanupGlobalDeferreds()
  67. def _cleanupGlobalDeferreds(self):
  68. """
  69. All pending requests that have returned a deferred must be errbacked
  70. when this service is stopped, otherwise they might be left uncalled and
  71. uncallable.
  72. """
  73. for d in self.deferreds["global"]:
  74. d.errback(error.ConchError("Connection stopped."))
  75. del self.deferreds["global"][:]
  76. # packet methods
  77. def ssh_GLOBAL_REQUEST(self, packet):
  78. """
  79. The other side has made a global request. Payload::
  80. string request type
  81. bool want reply
  82. <request specific data>
  83. This dispatches to self.gotGlobalRequest.
  84. """
  85. requestType, rest = common.getNS(packet)
  86. wantReply, rest = ord(rest[0:1]), rest[1:]
  87. ret = self.gotGlobalRequest(requestType, rest)
  88. if wantReply:
  89. reply = MSG_REQUEST_FAILURE
  90. data = b""
  91. if ret:
  92. reply = MSG_REQUEST_SUCCESS
  93. if isinstance(ret, (tuple, list)):
  94. data = ret[1]
  95. self.transport.sendPacket(reply, data)
  96. def ssh_REQUEST_SUCCESS(self, packet):
  97. """
  98. Our global request succeeded. Get the appropriate Deferred and call
  99. it back with the packet we received.
  100. """
  101. self._log.debug("global request success")
  102. self.deferreds["global"].pop(0).callback(packet)
  103. def ssh_REQUEST_FAILURE(self, packet):
  104. """
  105. Our global request failed. Get the appropriate Deferred and errback
  106. it with the packet we received.
  107. """
  108. self._log.debug("global request failure")
  109. self.deferreds["global"].pop(0).errback(
  110. error.ConchError("global request failed", packet)
  111. )
  112. def ssh_CHANNEL_OPEN(self, packet):
  113. """
  114. The other side wants to get a channel. Payload::
  115. string channel name
  116. uint32 remote channel number
  117. uint32 remote window size
  118. uint32 remote maximum packet size
  119. <channel specific data>
  120. We get a channel from self.getChannel(), give it a local channel number
  121. and notify the other side. Then notify the channel by calling its
  122. channelOpen method.
  123. """
  124. channelType, rest = common.getNS(packet)
  125. senderChannel, windowSize, maxPacket = struct.unpack(">3L", rest[:12])
  126. packet = rest[12:]
  127. try:
  128. channel = self.getChannel(channelType, windowSize, maxPacket, packet)
  129. localChannel = self.localChannelID
  130. self.localChannelID += 1
  131. channel.id = localChannel
  132. self.channels[localChannel] = channel
  133. self.channelsToRemoteChannel[channel] = senderChannel
  134. self.localToRemoteChannel[localChannel] = senderChannel
  135. openConfirmPacket = (
  136. struct.pack(
  137. ">4L",
  138. senderChannel,
  139. localChannel,
  140. channel.localWindowSize,
  141. channel.localMaxPacket,
  142. )
  143. + channel.specificData
  144. )
  145. self.transport.sendPacket(MSG_CHANNEL_OPEN_CONFIRMATION, openConfirmPacket)
  146. channel.channelOpen(packet)
  147. except Exception as e:
  148. self._log.failure("channel open failed")
  149. if isinstance(e, error.ConchError):
  150. textualInfo, reason = e.args
  151. if isinstance(textualInfo, int):
  152. # See #3657 and #3071
  153. textualInfo, reason = reason, textualInfo
  154. else:
  155. reason = OPEN_CONNECT_FAILED
  156. textualInfo = "unknown failure"
  157. self.transport.sendPacket(
  158. MSG_CHANNEL_OPEN_FAILURE,
  159. struct.pack(">2L", senderChannel, reason)
  160. + common.NS(networkString(textualInfo))
  161. + common.NS(b""),
  162. )
  163. def ssh_CHANNEL_OPEN_CONFIRMATION(self, packet):
  164. """
  165. The other side accepted our MSG_CHANNEL_OPEN request. Payload::
  166. uint32 local channel number
  167. uint32 remote channel number
  168. uint32 remote window size
  169. uint32 remote maximum packet size
  170. <channel specific data>
  171. Find the channel using the local channel number and notify its
  172. channelOpen method.
  173. """
  174. (localChannel, remoteChannel, windowSize, maxPacket) = struct.unpack(
  175. ">4L", packet[:16]
  176. )
  177. specificData = packet[16:]
  178. channel = self.channels[localChannel]
  179. channel.conn = self
  180. self.localToRemoteChannel[localChannel] = remoteChannel
  181. self.channelsToRemoteChannel[channel] = remoteChannel
  182. channel.remoteWindowLeft = windowSize
  183. channel.remoteMaxPacket = maxPacket
  184. channel.channelOpen(specificData)
  185. def ssh_CHANNEL_OPEN_FAILURE(self, packet):
  186. """
  187. The other side did not accept our MSG_CHANNEL_OPEN request. Payload::
  188. uint32 local channel number
  189. uint32 reason code
  190. string reason description
  191. Find the channel using the local channel number and notify it by
  192. calling its openFailed() method.
  193. """
  194. localChannel, reasonCode = struct.unpack(">2L", packet[:8])
  195. reasonDesc = common.getNS(packet[8:])[0]
  196. channel = self.channels[localChannel]
  197. del self.channels[localChannel]
  198. channel.conn = self
  199. reason = error.ConchError(reasonDesc, reasonCode)
  200. channel.openFailed(reason)
  201. def ssh_CHANNEL_WINDOW_ADJUST(self, packet):
  202. """
  203. The other side is adding bytes to its window. Payload::
  204. uint32 local channel number
  205. uint32 bytes to add
  206. Call the channel's addWindowBytes() method to add new bytes to the
  207. remote window.
  208. """
  209. localChannel, bytesToAdd = struct.unpack(">2L", packet[:8])
  210. channel = self.channels[localChannel]
  211. channel.addWindowBytes(bytesToAdd)
  212. def ssh_CHANNEL_DATA(self, packet):
  213. """
  214. The other side is sending us data. Payload::
  215. uint32 local channel number
  216. string data
  217. Check to make sure the other side hasn't sent too much data (more
  218. than what's in the window, or more than the maximum packet size). If
  219. they have, close the channel. Otherwise, decrease the available
  220. window and pass the data to the channel's dataReceived().
  221. """
  222. localChannel, dataLength = struct.unpack(">2L", packet[:8])
  223. channel = self.channels[localChannel]
  224. # XXX should this move to dataReceived to put client in charge?
  225. if (
  226. dataLength > channel.localWindowLeft or dataLength > channel.localMaxPacket
  227. ): # more data than we want
  228. self._log.error("too much data")
  229. self.sendClose(channel)
  230. return
  231. # packet = packet[:channel.localWindowLeft+4]
  232. data = common.getNS(packet[4:])[0]
  233. channel.localWindowLeft -= dataLength
  234. if channel.localWindowLeft < channel.localWindowSize // 2:
  235. self.adjustWindow(
  236. channel, channel.localWindowSize - channel.localWindowLeft
  237. )
  238. channel.dataReceived(data)
  239. def ssh_CHANNEL_EXTENDED_DATA(self, packet):
  240. """
  241. The other side is sending us exteneded data. Payload::
  242. uint32 local channel number
  243. uint32 type code
  244. string data
  245. Check to make sure the other side hasn't sent too much data (more
  246. than what's in the window, or than the maximum packet size). If
  247. they have, close the channel. Otherwise, decrease the available
  248. window and pass the data and type code to the channel's
  249. extReceived().
  250. """
  251. localChannel, typeCode, dataLength = struct.unpack(">3L", packet[:12])
  252. channel = self.channels[localChannel]
  253. if dataLength > channel.localWindowLeft or dataLength > channel.localMaxPacket:
  254. self._log.error("too much extdata")
  255. self.sendClose(channel)
  256. return
  257. data = common.getNS(packet[8:])[0]
  258. channel.localWindowLeft -= dataLength
  259. if channel.localWindowLeft < channel.localWindowSize // 2:
  260. self.adjustWindow(
  261. channel, channel.localWindowSize - channel.localWindowLeft
  262. )
  263. channel.extReceived(typeCode, data)
  264. def ssh_CHANNEL_EOF(self, packet):
  265. """
  266. The other side is not sending any more data. Payload::
  267. uint32 local channel number
  268. Notify the channel by calling its eofReceived() method.
  269. """
  270. localChannel = struct.unpack(">L", packet[:4])[0]
  271. channel = self.channels[localChannel]
  272. channel.eofReceived()
  273. def ssh_CHANNEL_CLOSE(self, packet):
  274. """
  275. The other side is closing its end; it does not want to receive any
  276. more data. Payload::
  277. uint32 local channel number
  278. Notify the channnel by calling its closeReceived() method. If
  279. the channel has also sent a close message, call self.channelClosed().
  280. """
  281. localChannel = struct.unpack(">L", packet[:4])[0]
  282. channel = self.channels[localChannel]
  283. channel.closeReceived()
  284. channel.remoteClosed = True
  285. if channel.localClosed and channel.remoteClosed:
  286. self.channelClosed(channel)
  287. def ssh_CHANNEL_REQUEST(self, packet):
  288. """
  289. The other side is sending a request to a channel. Payload::
  290. uint32 local channel number
  291. string request name
  292. bool want reply
  293. <request specific data>
  294. Pass the message to the channel's requestReceived method. If the
  295. other side wants a reply, add callbacks which will send the
  296. reply.
  297. """
  298. localChannel = struct.unpack(">L", packet[:4])[0]
  299. requestType, rest = common.getNS(packet[4:])
  300. wantReply = ord(rest[0:1])
  301. channel = self.channels[localChannel]
  302. d = defer.maybeDeferred(channel.requestReceived, requestType, rest[1:])
  303. if wantReply:
  304. d.addCallback(self._cbChannelRequest, localChannel)
  305. d.addErrback(self._ebChannelRequest, localChannel)
  306. return d
  307. def _cbChannelRequest(self, result, localChannel):
  308. """
  309. Called back if the other side wanted a reply to a channel request. If
  310. the result is true, send a MSG_CHANNEL_SUCCESS. Otherwise, raise
  311. a C{error.ConchError}
  312. @param result: the value returned from the channel's requestReceived()
  313. method. If it's False, the request failed.
  314. @type result: L{bool}
  315. @param localChannel: the local channel ID of the channel to which the
  316. request was made.
  317. @type localChannel: L{int}
  318. @raises ConchError: if the result is False.
  319. """
  320. if not result:
  321. raise error.ConchError("failed request")
  322. self.transport.sendPacket(
  323. MSG_CHANNEL_SUCCESS,
  324. struct.pack(">L", self.localToRemoteChannel[localChannel]),
  325. )
  326. def _ebChannelRequest(self, result, localChannel):
  327. """
  328. Called if the other wisde wanted a reply to the channel requeset and
  329. the channel request failed.
  330. @param result: a Failure, but it's not used.
  331. @param localChannel: the local channel ID of the channel to which the
  332. request was made.
  333. @type localChannel: L{int}
  334. """
  335. self.transport.sendPacket(
  336. MSG_CHANNEL_FAILURE,
  337. struct.pack(">L", self.localToRemoteChannel[localChannel]),
  338. )
  339. def ssh_CHANNEL_SUCCESS(self, packet):
  340. """
  341. Our channel request to the other side succeeded. Payload::
  342. uint32 local channel number
  343. Get the C{Deferred} out of self.deferreds and call it back.
  344. """
  345. localChannel = struct.unpack(">L", packet[:4])[0]
  346. if self.deferreds.get(localChannel):
  347. d = self.deferreds[localChannel].pop(0)
  348. d.callback("")
  349. def ssh_CHANNEL_FAILURE(self, packet):
  350. """
  351. Our channel request to the other side failed. Payload::
  352. uint32 local channel number
  353. Get the C{Deferred} out of self.deferreds and errback it with a
  354. C{error.ConchError}.
  355. """
  356. localChannel = struct.unpack(">L", packet[:4])[0]
  357. if self.deferreds.get(localChannel):
  358. d = self.deferreds[localChannel].pop(0)
  359. d.errback(error.ConchError("channel request failed"))
  360. # methods for users of the connection to call
  361. def sendGlobalRequest(self, request, data, wantReply=0):
  362. """
  363. Send a global request for this connection. Current this is only used
  364. for remote->local TCP forwarding.
  365. @type request: L{bytes}
  366. @type data: L{bytes}
  367. @type wantReply: L{bool}
  368. @rtype: C{Deferred}/L{None}
  369. """
  370. self.transport.sendPacket(
  371. MSG_GLOBAL_REQUEST,
  372. common.NS(request) + (wantReply and b"\xff" or b"\x00") + data,
  373. )
  374. if wantReply:
  375. d = defer.Deferred()
  376. self.deferreds["global"].append(d)
  377. return d
  378. def openChannel(self, channel, extra=b""):
  379. """
  380. Open a new channel on this connection.
  381. @type channel: subclass of C{SSHChannel}
  382. @type extra: L{bytes}
  383. """
  384. self._log.info(
  385. "opening channel {id} with {localWindowSize} {localMaxPacket}",
  386. id=self.localChannelID,
  387. localWindowSize=channel.localWindowSize,
  388. localMaxPacket=channel.localMaxPacket,
  389. )
  390. self.transport.sendPacket(
  391. MSG_CHANNEL_OPEN,
  392. common.NS(channel.name)
  393. + struct.pack(
  394. ">3L",
  395. self.localChannelID,
  396. channel.localWindowSize,
  397. channel.localMaxPacket,
  398. )
  399. + extra,
  400. )
  401. channel.id = self.localChannelID
  402. self.channels[self.localChannelID] = channel
  403. self.localChannelID += 1
  404. def sendRequest(self, channel, requestType, data, wantReply=0):
  405. """
  406. Send a request to a channel.
  407. @type channel: subclass of C{SSHChannel}
  408. @type requestType: L{bytes}
  409. @type data: L{bytes}
  410. @type wantReply: L{bool}
  411. @rtype: C{Deferred}/L{None}
  412. """
  413. if channel.localClosed:
  414. return
  415. self._log.debug("sending request {requestType}", requestType=requestType)
  416. self.transport.sendPacket(
  417. MSG_CHANNEL_REQUEST,
  418. struct.pack(">L", self.channelsToRemoteChannel[channel])
  419. + common.NS(requestType)
  420. + (b"\1" if wantReply else b"\0")
  421. + data,
  422. )
  423. if wantReply:
  424. d = defer.Deferred()
  425. self.deferreds.setdefault(channel.id, []).append(d)
  426. return d
  427. def adjustWindow(self, channel, bytesToAdd):
  428. """
  429. Tell the other side that we will receive more data. This should not
  430. normally need to be called as it is managed automatically.
  431. @type channel: subclass of L{SSHChannel}
  432. @type bytesToAdd: L{int}
  433. """
  434. if channel.localClosed:
  435. return # we're already closed
  436. packet = struct.pack(">2L", self.channelsToRemoteChannel[channel], bytesToAdd)
  437. self.transport.sendPacket(MSG_CHANNEL_WINDOW_ADJUST, packet)
  438. self._log.debug(
  439. "adding {bytesToAdd} to {localWindowLeft} in channel {id}",
  440. bytesToAdd=bytesToAdd,
  441. localWindowLeft=channel.localWindowLeft,
  442. id=channel.id,
  443. )
  444. channel.localWindowLeft += bytesToAdd
  445. def sendData(self, channel, data):
  446. """
  447. Send data to a channel. This should not normally be used: instead use
  448. channel.write(data) as it manages the window automatically.
  449. @type channel: subclass of L{SSHChannel}
  450. @type data: L{bytes}
  451. """
  452. if channel.localClosed:
  453. return # we're already closed
  454. self.transport.sendPacket(
  455. MSG_CHANNEL_DATA,
  456. struct.pack(">L", self.channelsToRemoteChannel[channel]) + common.NS(data),
  457. )
  458. def sendExtendedData(self, channel, dataType, data):
  459. """
  460. Send extended data to a channel. This should not normally be used:
  461. instead use channel.writeExtendedData(data, dataType) as it manages
  462. the window automatically.
  463. @type channel: subclass of L{SSHChannel}
  464. @type dataType: L{int}
  465. @type data: L{bytes}
  466. """
  467. if channel.localClosed:
  468. return # we're already closed
  469. self.transport.sendPacket(
  470. MSG_CHANNEL_EXTENDED_DATA,
  471. struct.pack(">2L", self.channelsToRemoteChannel[channel], dataType)
  472. + common.NS(data),
  473. )
  474. def sendEOF(self, channel):
  475. """
  476. Send an EOF (End of File) for a channel.
  477. @type channel: subclass of L{SSHChannel}
  478. """
  479. if channel.localClosed:
  480. return # we're already closed
  481. self._log.debug("sending eof")
  482. self.transport.sendPacket(
  483. MSG_CHANNEL_EOF, struct.pack(">L", self.channelsToRemoteChannel[channel])
  484. )
  485. def sendClose(self, channel):
  486. """
  487. Close a channel.
  488. @type channel: subclass of L{SSHChannel}
  489. """
  490. if channel.localClosed:
  491. return # we're already closed
  492. self._log.info("sending close {id}", id=channel.id)
  493. self.transport.sendPacket(
  494. MSG_CHANNEL_CLOSE, struct.pack(">L", self.channelsToRemoteChannel[channel])
  495. )
  496. channel.localClosed = True
  497. if channel.localClosed and channel.remoteClosed:
  498. self.channelClosed(channel)
  499. # methods to override
  500. def getChannel(self, channelType, windowSize, maxPacket, data):
  501. """
  502. The other side requested a channel of some sort.
  503. channelType is the type of channel being requested,
  504. windowSize is the initial size of the remote window,
  505. maxPacket is the largest packet we should send,
  506. data is any other packet data (often nothing).
  507. We return a subclass of L{SSHChannel}.
  508. By default, this dispatches to a method 'channel_channelType' with any
  509. non-alphanumerics in the channelType replace with _'s. If it cannot
  510. find a suitable method, it returns an OPEN_UNKNOWN_CHANNEL_TYPE error.
  511. The method is called with arguments of windowSize, maxPacket, data.
  512. @type channelType: L{bytes}
  513. @type windowSize: L{int}
  514. @type maxPacket: L{int}
  515. @type data: L{bytes}
  516. @rtype: subclass of L{SSHChannel}/L{tuple}
  517. """
  518. self._log.debug("got channel {channelType!r} request", channelType=channelType)
  519. if hasattr(self.transport, "avatar"): # this is a server!
  520. chan = self.transport.avatar.lookupChannel(
  521. channelType, windowSize, maxPacket, data
  522. )
  523. else:
  524. channelType = channelType.translate(TRANSLATE_TABLE)
  525. attr = "channel_%s" % nativeString(channelType)
  526. f = getattr(self, attr, None)
  527. if f is not None:
  528. chan = f(windowSize, maxPacket, data)
  529. else:
  530. chan = None
  531. if chan is None:
  532. raise error.ConchError("unknown channel", OPEN_UNKNOWN_CHANNEL_TYPE)
  533. else:
  534. chan.conn = self
  535. return chan
  536. def gotGlobalRequest(self, requestType, data):
  537. """
  538. We got a global request. pretty much, this is just used by the client
  539. to request that we forward a port from the server to the client.
  540. Returns either:
  541. - 1: request accepted
  542. - 1, <data>: request accepted with request specific data
  543. - 0: request denied
  544. By default, this dispatches to a method 'global_requestType' with
  545. -'s in requestType replaced with _'s. The found method is passed data.
  546. If this method cannot be found, this method returns 0. Otherwise, it
  547. returns the return value of that method.
  548. @type requestType: L{bytes}
  549. @type data: L{bytes}
  550. @rtype: L{int}/L{tuple}
  551. """
  552. self._log.debug("got global {requestType} request", requestType=requestType)
  553. if hasattr(self.transport, "avatar"): # this is a server!
  554. return self.transport.avatar.gotGlobalRequest(requestType, data)
  555. requestType = nativeString(requestType.replace(b"-", b"_"))
  556. f = getattr(self, "global_%s" % requestType, None)
  557. if not f:
  558. return 0
  559. return f(data)
  560. def channelClosed(self, channel):
  561. """
  562. Called when a channel is closed.
  563. It clears the local state related to the channel, and calls
  564. channel.closed().
  565. MAKE SURE YOU CALL THIS METHOD, even if you subclass L{SSHConnection}.
  566. If you don't, things will break mysteriously.
  567. @type channel: L{SSHChannel}
  568. """
  569. if channel in self.channelsToRemoteChannel: # actually open
  570. channel.localClosed = channel.remoteClosed = True
  571. del self.localToRemoteChannel[channel.id]
  572. del self.channels[channel.id]
  573. del self.channelsToRemoteChannel[channel]
  574. for d in self.deferreds.pop(channel.id, []):
  575. d.errback(error.ConchError("Channel closed."))
  576. channel.closed()
  577. MSG_GLOBAL_REQUEST = 80
  578. MSG_REQUEST_SUCCESS = 81
  579. MSG_REQUEST_FAILURE = 82
  580. MSG_CHANNEL_OPEN = 90
  581. MSG_CHANNEL_OPEN_CONFIRMATION = 91
  582. MSG_CHANNEL_OPEN_FAILURE = 92
  583. MSG_CHANNEL_WINDOW_ADJUST = 93
  584. MSG_CHANNEL_DATA = 94
  585. MSG_CHANNEL_EXTENDED_DATA = 95
  586. MSG_CHANNEL_EOF = 96
  587. MSG_CHANNEL_CLOSE = 97
  588. MSG_CHANNEL_REQUEST = 98
  589. MSG_CHANNEL_SUCCESS = 99
  590. MSG_CHANNEL_FAILURE = 100
  591. OPEN_ADMINISTRATIVELY_PROHIBITED = 1
  592. OPEN_CONNECT_FAILED = 2
  593. OPEN_UNKNOWN_CHANNEL_TYPE = 3
  594. OPEN_RESOURCE_SHORTAGE = 4
  595. # From RFC 4254
  596. EXTENDED_DATA_STDERR = 1
  597. messages = {}
  598. for name, value in locals().copy().items():
  599. if name[:4] == "MSG_":
  600. messages[value] = name # Doesn't handle doubles
  601. alphanums = networkString(string.ascii_letters + string.digits)
  602. TRANSLATE_TABLE = bytes(i if i in alphanums else ord("_") for i in range(256))
  603. SSHConnection.protocolMessages = messages