Index: third_party/twisted_8_1/twisted/conch/ssh/connection.py |
diff --git a/third_party/twisted_8_1/twisted/conch/ssh/connection.py b/third_party/twisted_8_1/twisted/conch/ssh/connection.py |
deleted file mode 100644 |
index bc86f02b5e0e2a1a0d2032209bbdd98613c0ed01..0000000000000000000000000000000000000000 |
--- a/third_party/twisted_8_1/twisted/conch/ssh/connection.py |
+++ /dev/null |
@@ -1,613 +0,0 @@ |
-# -*- test-case-name: twisted.conch.test.test_connection -*- |
-# Copyright (c) 2001-2007 Twisted Matrix Laboratories. |
-# See LICENSE for details. |
- |
-# |
- |
-""" |
-This module contains the implementation of the ssh-connection service, which |
-allows access to the shell and port-forwarding. |
- |
-Maintainer: U{Paul Swartz<mailto:z3p@twistedmatrix.com>} |
-""" |
- |
-import struct |
- |
-from twisted.conch.ssh import service, common |
-from twisted.conch import error |
-from twisted.internet import defer |
-from twisted.python import log |
- |
-class SSHConnection(service.SSHService): |
- """ |
- An implementation of the 'ssh-connection' service. It is used to |
- multiplex multiple channels over the single SSH connection. |
- |
- @ivar localChannelID: the next number to use as a local channel ID. |
- @type localChannelID: C{int} |
- @ivar channels: a C{dict} mapping a local channel ID to C{SSHChannel} |
- subclasses. |
- @type channels: C{dict} |
- @ivar localToRemoteChannel: a C{dict} mapping a local channel ID to a |
- remote channel ID. |
- @type localToRemoteChannel: C{dict} |
- @ivar channelsToRemoteChannel: a C{dict} mapping a C{SSHChannel} subclass |
- to remote channel ID. |
- @type channelsToRemoteChannel: C{dict} |
- @ivar deferreds: a C{dict} mapping a local channel ID to a C{list} of |
- C{Deferreds} for outstanding channel requests. Also, the 'global' |
- key stores the C{list} of pending global request C{Deferred}s. |
- """ |
- name = 'ssh-connection' |
- |
- def __init__(self): |
- self.localChannelID = 0 # this is the current # to use for channel ID |
- self.localToRemoteChannel = {} # local channel ID -> remote channel ID |
- self.channels = {} # local channel ID -> subclass of SSHChannel |
- self.channelsToRemoteChannel = {} # subclass of SSHChannel -> |
- # remote channel ID |
- self.deferreds = {} # local channel -> list of deferreds for pending |
- # requests or 'global' -> list of deferreds for |
- # global requests |
- self.transport = None # gets set later |
- |
- def serviceStarted(self): |
- if hasattr(self.transport, 'avatar'): |
- self.transport.avatar.conn = self |
- |
- def serviceStopped(self): |
- map(self.channelClosed, self.channels.values()) |
- |
- # packet methods |
- def ssh_GLOBAL_REQUEST(self, packet): |
- """ |
- The other side has made a global request. Payload:: |
- string request type |
- bool want reply |
- <request specific data> |
- |
- This dispatches to self.gotGlobalRequest. |
- """ |
- requestType, rest = common.getNS(packet) |
- wantReply, rest = ord(rest[0]), rest[1:] |
- ret = self.gotGlobalRequest(requestType, rest) |
- if wantReply: |
- reply = MSG_REQUEST_FAILURE |
- data = '' |
- if ret: |
- reply = MSG_REQUEST_SUCCESS |
- if isinstance(ret, (tuple, list)): |
- data = ret[1] |
- self.transport.sendPacket(reply, data) |
- |
- def ssh_REQUEST_SUCCESS(self, packet): |
- """ |
- Our global request succeeded. Get the appropriate Deferred and call |
- it back with the packet we received. |
- """ |
- log.msg('RS') |
- self.deferreds['global'].pop(0).callback(packet) |
- |
- def ssh_REQUEST_FAILURE(self, packet): |
- """ |
- Our global request failed. Get the appropriate Deferred and errback |
- it with the packet we received. |
- """ |
- log.msg('RF') |
- self.deferreds['global'].pop(0).errback( |
- error.ConchError('global request failed', packet)) |
- |
- def ssh_CHANNEL_OPEN(self, packet): |
- """ |
- The other side wants to get a channel. Payload:: |
- string channel name |
- uint32 remote channel number |
- uint32 remote window size |
- uint32 remote maximum packet size |
- <channel specific data> |
- |
- We get a channel from self.getChannel(), give it a local channel number |
- and notify the other side. Then notify the channel by calling its |
- channelOpen method. |
- """ |
- channelType, rest = common.getNS(packet) |
- senderChannel, windowSize, maxPacket = struct.unpack('>3L', rest[:12]) |
- packet = rest[12:] |
- try: |
- channel = self.getChannel(channelType, windowSize, maxPacket, |
- packet) |
- localChannel = self.localChannelID |
- self.localChannelID += 1 |
- channel.id = localChannel |
- self.channels[localChannel] = channel |
- self.channelsToRemoteChannel[channel] = senderChannel |
- self.localToRemoteChannel[localChannel] = senderChannel |
- self.transport.sendPacket(MSG_CHANNEL_OPEN_CONFIRMATION, |
- struct.pack('>4L', senderChannel, localChannel, |
- channel.localWindowSize, |
- channel.localMaxPacket)+channel.specificData) |
- log.callWithLogger(channel, channel.channelOpen, packet) |
- except Exception, e: |
- log.msg('channel open failed') |
- log.err(e) |
- if isinstance(e, error.ConchError): |
- textualInfo, reason = e.args |
- else: |
- reason = OPEN_CONNECT_FAILED |
- textualInfo = "unknown failure" |
- self.transport.sendPacket(MSG_CHANNEL_OPEN_FAILURE, |
- struct.pack('>2L', senderChannel, reason) + |
- common.NS(textualInfo) + common.NS('')) |
- |
- def ssh_CHANNEL_OPEN_CONFIRMATION(self, packet): |
- """ |
- The other side accepted our MSG_CHANNEL_OPEN request. Payload:: |
- uint32 local channel number |
- uint32 remote channel number |
- uint32 remote window size |
- uint32 remote maximum packet size |
- <channel specific data> |
- |
- Find the channel using the local channel number and notify its |
- channelOpen method. |
- """ |
- (localChannel, remoteChannel, windowSize, |
- maxPacket) = struct.unpack('>4L', packet[: 16]) |
- specificData = packet[16:] |
- channel = self.channels[localChannel] |
- channel.conn = self |
- self.localToRemoteChannel[localChannel] = remoteChannel |
- self.channelsToRemoteChannel[channel] = remoteChannel |
- channel.remoteWindowLeft = windowSize |
- channel.remoteMaxPacket = maxPacket |
- log.callWithLogger(channel, channel.channelOpen, specificData) |
- |
- def ssh_CHANNEL_OPEN_FAILURE(self, packet): |
- """ |
- The other side did not accept our MSG_CHANNEL_OPEN request. Payload:: |
- uint32 local channel number |
- uint32 reason code |
- string reason description |
- |
- Find the channel using the local channel number and notify it by |
- calling its openFailed() method. |
- """ |
- localChannel, reasonCode = struct.unpack('>2L', packet[:8]) |
- reasonDesc = common.getNS(packet[8:])[0] |
- channel = self.channels[localChannel] |
- del self.channels[localChannel] |
- channel.conn = self |
- reason = error.ConchError(reasonDesc, reasonCode) |
- log.callWithLogger(channel, channel.openFailed, reason) |
- |
- def ssh_CHANNEL_WINDOW_ADJUST(self, packet): |
- """ |
- The other side is adding bytes to its window. Payload:: |
- uint32 local channel number |
- uint32 bytes to add |
- |
- Call the channel's addWindowBytes() method to add new bytes to the |
- remote window. |
- """ |
- localChannel, bytesToAdd = struct.unpack('>2L', packet[:8]) |
- channel = self.channels[localChannel] |
- log.callWithLogger(channel, channel.addWindowBytes, bytesToAdd) |
- |
- def ssh_CHANNEL_DATA(self, packet): |
- """ |
- The other side is sending us data. Payload:: |
- uint32 local channel number |
- string data |
- |
- Check to make sure the other side hasn't sent too much data (more |
- than what's in the window, or more than the maximum packet size). If |
- they have, close the channel. Otherwise, decrease the available |
- window and pass the data to the channel's dataReceived(). |
- """ |
- localChannel, dataLength = struct.unpack('>2L', packet[:8]) |
- channel = self.channels[localChannel] |
- # XXX should this move to dataReceived to put client in charge? |
- if (dataLength > channel.localWindowLeft or |
- dataLength > channel.localMaxPacket): # more data than we want |
- log.callWithLogger(channel, log.msg, 'too much data') |
- self.sendClose(channel) |
- return |
- #packet = packet[:channel.localWindowLeft+4] |
- data = common.getNS(packet[4:])[0] |
- channel.localWindowLeft -= dataLength |
- if channel.localWindowLeft < channel.localWindowSize / 2: |
- self.adjustWindow(channel, channel.localWindowSize - \ |
- channel.localWindowLeft) |
- #log.msg('local window left: %s/%s' % (channel.localWindowLeft, |
- # channel.localWindowSize)) |
- log.callWithLogger(channel, channel.dataReceived, data) |
- |
- def ssh_CHANNEL_EXTENDED_DATA(self, packet): |
- """ |
- The other side is sending us exteneded data. Payload:: |
- uint32 local channel number |
- uint32 type code |
- string data |
- |
- Check to make sure the other side hasn't sent too much data (more |
- than what's in the window, or or than the maximum packet size). If |
- they have, close the channel. Otherwise, decrease the available |
- window and pass the data and type code to the channel's |
- extReceived(). |
- """ |
- localChannel, typeCode, dataLength = struct.unpack('>3L', packet[:12]) |
- channel = self.channels[localChannel] |
- if (dataLength > channel.localWindowLeft or |
- dataLength > channel.localMaxPacket): |
- log.callWithLogger(channel, log.msg, 'too much extdata') |
- self.sendClose(channel) |
- return |
- data = common.getNS(packet[8:])[0] |
- channel.localWindowLeft -= dataLength |
- if channel.localWindowLeft < channel.localWindowSize / 2: |
- self.adjustWindow(channel, channel.localWindowSize - |
- channel.localWindowLeft) |
- log.callWithLogger(channel, channel.extReceived, typeCode, data) |
- |
- def ssh_CHANNEL_EOF(self, packet): |
- """ |
- The other side is not sending any more data. Payload:: |
- uint32 local channel number |
- |
- Notify the channel by calling its eofReceived() method. |
- """ |
- localChannel = struct.unpack('>L', packet[:4])[0] |
- channel = self.channels[localChannel] |
- log.callWithLogger(channel, channel.eofReceived) |
- |
- def ssh_CHANNEL_CLOSE(self, packet): |
- """ |
- The other side is closing its end; it does not want to receive any |
- more data. Payload:: |
- uint32 local channel number |
- |
- Notify the channnel by calling its closeReceived() method. If |
- the channel has also sent a close message, call self.channelClosed(). |
- """ |
- localChannel = struct.unpack('>L', packet[:4])[0] |
- channel = self.channels[localChannel] |
- log.callWithLogger(channel, channel.closeReceived) |
- channel.remoteClosed = True |
- if channel.localClosed and channel.remoteClosed: |
- self.channelClosed(channel) |
- |
- def ssh_CHANNEL_REQUEST(self, packet): |
- """ |
- The other side is sending a request to a channel. Payload:: |
- uint32 local channel number |
- string request name |
- bool want reply |
- <request specific data> |
- |
- Pass the message to the channel's requestReceived method. If the |
- other side wants a reply, add callbacks which will send the |
- reply. |
- """ |
- localChannel = struct.unpack('>L', packet[: 4])[0] |
- requestType, rest = common.getNS(packet[4:]) |
- wantReply = ord(rest[0]) |
- channel = self.channels[localChannel] |
- d = defer.maybeDeferred(log.callWithLogger, channel, |
- channel.requestReceived, requestType, rest[1:]) |
- if wantReply: |
- d.addCallback(self._cbChannelRequest, localChannel) |
- d.addErrback(self._ebChannelRequest, localChannel) |
- return d |
- |
- def _cbChannelRequest(self, result, localChannel): |
- """ |
- Called back if the other side wanted a reply to a channel request. If |
- the result is true, send a MSG_CHANNEL_SUCCESS. Otherwise, raise |
- a C{error.ConchError} |
- |
- @param result: the value returned from the channel's requestReceived() |
- method. If it's False, the request failed. |
- @type result: C{bool} |
- @param localChannel: the local channel ID of the channel to which the |
- request was made. |
- @type localChannel: C{int} |
- @raises ConchError: if the result is False. |
- """ |
- if not result: |
- raise error.ConchError('failed request') |
- self.transport.sendPacket(MSG_CHANNEL_SUCCESS, struct.pack('>L', |
- self.localToRemoteChannel[localChannel])) |
- |
- def _ebChannelRequest(self, result, localChannel): |
- """ |
- Called if the other wisde wanted a reply to the channel requeset and |
- the channel request failed. |
- |
- @param result: a Failure, but it's not used. |
- @param localChannel: the local channel ID of the channel to which the |
- request was made. |
- @type localChannel: C{int} |
- """ |
- self.transport.sendPacket(MSG_CHANNEL_FAILURE, struct.pack('>L', |
- self.localToRemoteChannel[localChannel])) |
- |
- def ssh_CHANNEL_SUCCESS(self, packet): |
- """ |
- Our channel request to the other other side succeeded. Payload:: |
- uint32 local channel number |
- |
- Get the C{Deferred} out of self.deferreds and call it back. |
- """ |
- localChannel = struct.unpack('>L', packet[:4])[0] |
- if self.deferreds.get(localChannel): |
- d = self.deferreds[localChannel].pop(0) |
- log.callWithLogger(self.channels[localChannel], |
- d.callback, '') |
- |
- def ssh_CHANNEL_FAILURE(self, packet): |
- """ |
- Our channel request to the other side failed. Payload:: |
- uint32 local channel number |
- |
- Get the C{Deferred} out of self.deferreds and errback it with a |
- C{error.ConchError}. |
- """ |
- localChannel = struct.unpack('>L', packet[:4])[0] |
- if self.deferreds.get(localChannel): |
- d = self.deferreds[localChannel].pop(0) |
- log.callWithLogger(self.channels[localChannel], |
- d.errback, |
- error.ConchError('channel request failed')) |
- |
- # methods for users of the connection to call |
- |
- def sendGlobalRequest(self, request, data, wantReply=0): |
- """ |
- Send a global request for this connection. Current this is only used |
- for remote->local TCP forwarding. |
- |
- @type request: C{str} |
- @type data: C{str} |
- @type wantReply: C{bool} |
- @rtype C{Deferred}/C{None} |
- """ |
- self.transport.sendPacket(MSG_GLOBAL_REQUEST, |
- common.NS(request) |
- + (wantReply and '\xff' or '\x00') |
- + data) |
- if wantReply: |
- d = defer.Deferred() |
- self.deferreds.setdefault('global', []).append(d) |
- return d |
- |
- def openChannel(self, channel, extra=''): |
- """ |
- Open a new channel on this connection. |
- |
- @type channel: subclass of C{SSHChannel} |
- @type extra: C{str} |
- """ |
- log.msg('opening channel %s with %s %s'%(self.localChannelID, |
- channel.localWindowSize, channel.localMaxPacket)) |
- self.transport.sendPacket(MSG_CHANNEL_OPEN, common.NS(channel.name) |
- + struct.pack('>3L', self.localChannelID, |
- channel.localWindowSize, channel.localMaxPacket) |
- + extra) |
- channel.id = self.localChannelID |
- self.channels[self.localChannelID] = channel |
- self.localChannelID += 1 |
- |
- def sendRequest(self, channel, requestType, data, wantReply=0): |
- """ |
- Send a request to a channel. |
- |
- @type channel: subclass of C{SSHChannel} |
- @type requestType: C{str} |
- @type data: C{str} |
- @type wantReply: C{bool} |
- @rtype C{Deferred}/C{None} |
- """ |
- if channel.localClosed: |
- return |
- log.msg('sending request %s' % requestType) |
- self.transport.sendPacket(MSG_CHANNEL_REQUEST, struct.pack('>L', |
- self.channelsToRemoteChannel[channel]) |
- + common.NS(requestType)+chr(wantReply) |
- + data) |
- if wantReply: |
- d = defer.Deferred() |
- self.deferreds.setdefault(channel.id, []).append(d) |
- return d |
- |
- def adjustWindow(self, channel, bytesToAdd): |
- """ |
- Tell the other side that we will receive more data. This should not |
- normally need to be called as it is managed automatically. |
- |
- @type channel: subclass of L{SSHChannel} |
- @type bytesToAdd: C{int} |
- """ |
- if channel.localClosed: |
- return # we're already closed |
- self.transport.sendPacket(MSG_CHANNEL_WINDOW_ADJUST, struct.pack('>2L', |
- self.channelsToRemoteChannel[channel], |
- bytesToAdd)) |
- log.msg('adding %i to %i in channel %i' % (bytesToAdd, |
- channel.localWindowLeft, channel.id)) |
- channel.localWindowLeft += bytesToAdd |
- |
- def sendData(self, channel, data): |
- """ |
- Send data to a channel. This should not normally be used: instead use |
- channel.write(data) as it manages the window automatically. |
- |
- @type channel: subclass of L{SSHChannel} |
- @type data: C{str} |
- """ |
- if channel.localClosed: |
- return # we're already closed |
- self.transport.sendPacket(MSG_CHANNEL_DATA, struct.pack('>L', |
- self.channelsToRemoteChannel[channel]) + |
- common.NS(data)) |
- |
- def sendExtendedData(self, channel, dataType, data): |
- """ |
- Send extended data to a channel. This should not normally be used: |
- instead use channel.writeExtendedData(data, dataType) as it manages |
- the window automatically. |
- |
- @type channel: subclass of L{SSHChannel} |
- @type dataType: C{int} |
- @type data: C{str} |
- """ |
- if channel.localClosed: |
- return # we're already closed |
- self.transport.sendPacket(MSG_CHANNEL_EXTENDED_DATA, struct.pack('>2L', |
- self.channelsToRemoteChannel[channel],dataType) \ |
- + common.NS(data)) |
- |
- def sendEOF(self, channel): |
- """ |
- Send an EOF (End of File) for a channel. |
- |
- @type channel: subclass of L{SSHChannel} |
- """ |
- if channel.localClosed: |
- return # we're already closed |
- log.msg('sending eof') |
- self.transport.sendPacket(MSG_CHANNEL_EOF, struct.pack('>L', |
- self.channelsToRemoteChannel[channel])) |
- |
- def sendClose(self, channel): |
- """ |
- Close a channel. |
- |
- @type channel: subclass of L{SSHChannel} |
- """ |
- if channel.localClosed: |
- return # we're already closed |
- log.msg('sending close %i' % channel.id) |
- self.transport.sendPacket(MSG_CHANNEL_CLOSE, struct.pack('>L', |
- self.channelsToRemoteChannel[channel])) |
- channel.localClosed = True |
- if channel.localClosed and channel.remoteClosed: |
- self.channelClosed(channel) |
- |
- # methods to override |
- def getChannel(self, channelType, windowSize, maxPacket, data): |
- """ |
- The other side requested a channel of some sort. |
- channelType is the type of channel being requested, |
- windowSize is the initial size of the remote window, |
- maxPacket is the largest packet we should send, |
- data is any other packet data (often nothing). |
- |
- We return a subclass of L{SSHChannel}. |
- |
- By default, this dispatches to a method 'channel_channelType' with any |
- non-alphanumerics in the channelType replace with _'s. If it cannot |
- find a suitable method, it returns an OPEN_UNKNOWN_CHANNEL_TYPE error. |
- The method is called with arguments of windowSize, maxPacket, data. |
- |
- @type channelType: C{str} |
- @type windowSize: C{int} |
- @type maxPacket: C{int} |
- @type data: C{str} |
- @rtype: subclass of L{SSHChannel}/C{tuple} |
- """ |
- log.msg('got channel %s request' % channelType) |
- if hasattr(self.transport, "avatar"): # this is a server! |
- chan = self.transport.avatar.lookupChannel(channelType, |
- windowSize, |
- maxPacket, |
- data) |
- else: |
- channelType = channelType.translate(TRANSLATE_TABLE) |
- f = getattr(self, 'channel_%s' % channelType, None) |
- if f is not None: |
- chan = f(windowSize, maxPacket, data) |
- else: |
- chan = None |
- if chan is None: |
- raise error.ConchError('unknown channel', |
- OPEN_UNKNOWN_CHANNEL_TYPE) |
- else: |
- chan.conn = self |
- return chan |
- |
- def gotGlobalRequest(self, requestType, data): |
- """ |
- We got a global request. pretty much, this is just used by the client |
- to request that we forward a port from the server to the client. |
- Returns either: |
- - 1: request accepted |
- - 1, <data>: request accepted with request specific data |
- - 0: request denied |
- |
- By default, this dispatches to a method 'global_requestType' with |
- -'s in requestType replaced with _'s. The found method is passed data. |
- If this method cannot be found, this method returns 0. Otherwise, it |
- returns the return value of that method. |
- |
- @type requestType: C{str} |
- @type data: C{str} |
- @rtype: C{int}/C{tuple} |
- """ |
- log.msg('got global %s request' % requestType) |
- if hasattr(self.transport, 'avatar'): # this is a server! |
- return self.transport.avatar.gotGlobalRequest(requestType, data) |
- |
- requestType = requestType.replace('-','_') |
- f = getattr(self, 'global_%s' % requestType, None) |
- if not f: |
- return 0 |
- return f(data) |
- |
- def channelClosed(self, channel): |
- """ |
- Called when a channel is closed. |
- It clears the local state related to the channel, and calls |
- channel.closed(). |
- MAKE SURE YOU CALL THIS METHOD, even if you subclass L{SSHConnection}. |
- If you don't, things will break mysteriously. |
- """ |
- if channel in self.channelsToRemoteChannel: # actually open |
- channel.localClosed = channel.remoteClosed = True |
- del self.localToRemoteChannel[channel.id] |
- del self.channels[channel.id] |
- del self.channelsToRemoteChannel[channel] |
- self.deferreds[channel.id] = [] |
- log.callWithLogger(channel, channel.closed) |
- |
-MSG_GLOBAL_REQUEST = 80 |
-MSG_REQUEST_SUCCESS = 81 |
-MSG_REQUEST_FAILURE = 82 |
-MSG_CHANNEL_OPEN = 90 |
-MSG_CHANNEL_OPEN_CONFIRMATION = 91 |
-MSG_CHANNEL_OPEN_FAILURE = 92 |
-MSG_CHANNEL_WINDOW_ADJUST = 93 |
-MSG_CHANNEL_DATA = 94 |
-MSG_CHANNEL_EXTENDED_DATA = 95 |
-MSG_CHANNEL_EOF = 96 |
-MSG_CHANNEL_CLOSE = 97 |
-MSG_CHANNEL_REQUEST = 98 |
-MSG_CHANNEL_SUCCESS = 99 |
-MSG_CHANNEL_FAILURE = 100 |
- |
-OPEN_ADMINISTRATIVELY_PROHIBITED = 1 |
-OPEN_CONNECT_FAILED = 2 |
-OPEN_UNKNOWN_CHANNEL_TYPE = 3 |
-OPEN_RESOURCE_SHORTAGE = 4 |
- |
-EXTENDED_DATA_STDERR = 1 |
- |
-messages = {} |
-for name, value in locals().copy().items(): |
- if name[:4] == 'MSG_': |
- messages[value] = name # doesn't handle doubles |
- |
-import string |
-alphanums = string.letters + string.digits |
-TRANSLATE_TABLE = ''.join([chr(i) in alphanums and chr(i) or '_' |
- for i in range(256)]) |
-SSHConnection.protocolMessages = messages |