connection.py 26 KB

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