root/trunk/twisted/conch/ssh/channel.py

Revision 30752, 9.1 KB (checked in by exarkun, 15 months ago)

Rewrite the copyright headers to exclude date information.

Author: exarkun
Reviewer: glyph
Fixes: #4857

To avoid the need to perpetually update copyright dates in each file in Twisted,
remove the dates from most files and just leave them in the LICENSE file.

As a side effect, some files also have had a trailing newline added where it was
missing before.

Line 
1# -*- test-case-name: twisted.conch.test.test_channel -*-
2# Copyright (c) Twisted Matrix Laboratories.
3# See LICENSE for details.
4
5#
6"""
7The parent class for all the SSH Channels.  Currently implemented channels
8are session. direct-tcp, and forwarded-tcp.
9
10Maintainer: Paul Swartz
11"""
12
13from twisted.python import log
14from twisted.internet import interfaces
15from zope.interface import implements
16
17
18class SSHChannel(log.Logger):
19    """
20    A class that represents a multiplexed channel over an SSH connection.
21    The channel has a local window which is the maximum amount of data it will
22    receive, and a remote which is the maximum amount of data the remote side
23    will accept.  There is also a maximum packet size for any individual data
24    packet going each way.
25
26    @ivar name: the name of the channel.
27    @type name: C{str}
28    @ivar localWindowSize: the maximum size of the local window in bytes.
29    @type localWindowSize: C{int}
30    @ivar localWindowLeft: how many bytes are left in the local window.
31    @type localWindowLeft: C{int}
32    @ivar localMaxPacket: the maximum size of packet we will accept in bytes.
33    @type localMaxPacket: C{int}
34    @ivar remoteWindowLeft: how many bytes are left in the remote window.
35    @type remoteWindowLeft: C{int}
36    @ivar remoteMaxPacket: the maximum size of a packet the remote side will
37        accept in bytes.
38    @type remoteMaxPacket: C{int}
39    @ivar conn: the connection this channel is multiplexed through.
40    @type conn: L{SSHConnection}
41    @ivar data: any data to send to the other size when the channel is
42        requested.
43    @type data: C{str}
44    @ivar avatar: an avatar for the logged-in user (if a server channel)
45    @ivar localClosed: True if we aren't accepting more data.
46    @type localClosed: C{bool}
47    @ivar remoteClosed: True if the other size isn't accepting more data.
48    @type remoteClosed: C{bool}
49    """
50
51    implements(interfaces.ITransport)
52
53    name = None # only needed for client channels
54
55    def __init__(self, localWindow = 0, localMaxPacket = 0,
56                       remoteWindow = 0, remoteMaxPacket = 0,
57                       conn = None, data=None, avatar = None):
58        self.localWindowSize = localWindow or 131072
59        self.localWindowLeft = self.localWindowSize
60        self.localMaxPacket = localMaxPacket or 32768
61        self.remoteWindowLeft = remoteWindow
62        self.remoteMaxPacket = remoteMaxPacket
63        self.areWriting = 1
64        self.conn = conn
65        self.data = data
66        self.avatar = avatar
67        self.specificData = ''
68        self.buf = ''
69        self.extBuf = []
70        self.closing = 0
71        self.localClosed = 0
72        self.remoteClosed = 0
73        self.id = None # gets set later by SSHConnection
74
75    def __str__(self):
76        return '<SSHChannel %s (lw %i rw %i)>' % (self.name,
77                self.localWindowLeft, self.remoteWindowLeft)
78
79    def logPrefix(self):
80        id = (self.id is not None and str(self.id)) or "unknown"
81        return "SSHChannel %s (%s) on %s" % (self.name, id,
82                self.conn.logPrefix())
83
84    def channelOpen(self, specificData):
85        """
86        Called when the channel is opened.  specificData is any data that the
87        other side sent us when opening the channel.
88
89        @type specificData: C{str}
90        """
91        log.msg('channel open')
92
93    def openFailed(self, reason):
94        """
95        Called when the the open failed for some reason.
96        reason.desc is a string descrption, reason.code the the SSH error code.
97
98        @type reason: L{error.ConchError}
99        """
100        log.msg('other side refused open\nreason: %s'% reason)
101
102    def addWindowBytes(self, bytes):
103        """
104        Called when bytes are added to the remote window.  By default it clears
105        the data buffers.
106
107        @type bytes:    C{int}
108        """
109        self.remoteWindowLeft = self.remoteWindowLeft+bytes
110        if not self.areWriting and not self.closing:
111            self.areWriting = True
112            self.startWriting()
113        if self.buf:
114            b = self.buf
115            self.buf = ''
116            self.write(b)
117        if self.extBuf:
118            b = self.extBuf
119            self.extBuf = []
120            for (type, data) in b:
121                self.writeExtended(type, data)
122
123    def requestReceived(self, requestType, data):
124        """
125        Called when a request is sent to this channel.  By default it delegates
126        to self.request_<requestType>.
127        If this function returns true, the request succeeded, otherwise it
128        failed.
129
130        @type requestType:  C{str}
131        @type data:         C{str}
132        @rtype:             C{bool}
133        """
134        foo = requestType.replace('-', '_')
135        f = getattr(self, 'request_%s'%foo, None)
136        if f:
137            return f(data)
138        log.msg('unhandled request for %s'%requestType)
139        return 0
140
141    def dataReceived(self, data):
142        """
143        Called when we receive data.
144
145        @type data: C{str}
146        """
147        log.msg('got data %s'%repr(data))
148
149    def extReceived(self, dataType, data):
150        """
151        Called when we receive extended data (usually standard error).
152
153        @type dataType: C{int}
154        @type data:     C{str}
155        """
156        log.msg('got extended data %s %s'%(dataType, repr(data)))
157
158    def eofReceived(self):
159        """
160        Called when the other side will send no more data.
161        """
162        log.msg('remote eof')
163
164    def closeReceived(self):
165        """
166        Called when the other side has closed the channel.
167        """
168        log.msg('remote close')
169        self.loseConnection()
170
171    def closed(self):
172        """
173        Called when the channel is closed.  This means that both our side and
174        the remote side have closed the channel.
175        """
176        log.msg('closed')
177
178    # transport stuff
179    def write(self, data):
180        """
181        Write some data to the channel.  If there is not enough remote window
182        available, buffer until it is.  Otherwise, split the data into
183        packets of length remoteMaxPacket and send them.
184
185        @type data: C{str}
186        """
187        if self.buf:
188            self.buf += data
189            return
190        top = len(data)
191        if top > self.remoteWindowLeft:
192            data, self.buf = (data[:self.remoteWindowLeft],
193                data[self.remoteWindowLeft:])
194            self.areWriting = 0
195            self.stopWriting()
196            top = self.remoteWindowLeft
197        rmp = self.remoteMaxPacket
198        write = self.conn.sendData
199        r = range(0, top, rmp)
200        for offset in r:
201            write(self, data[offset: offset+rmp])
202        self.remoteWindowLeft -= top
203        if self.closing and not self.buf:
204            self.loseConnection() # try again
205
206    def writeExtended(self, dataType, data):
207        """
208        Send extended data to this channel.  If there is not enough remote
209        window available, buffer until there is.  Otherwise, split the data
210        into packets of length remoteMaxPacket and send them.
211
212        @type dataType: C{int}
213        @type data:     C{str}
214        """
215        if self.extBuf:
216            if self.extBuf[-1][0] == dataType:
217                self.extBuf[-1][1] += data
218            else:
219                self.extBuf.append([dataType, data])
220            return
221        if len(data) > self.remoteWindowLeft:
222            data, self.extBuf = (data[:self.remoteWindowLeft],
223                                [[dataType, data[self.remoteWindowLeft:]]])
224            self.areWriting = 0
225            self.stopWriting()
226        while len(data) > self.remoteMaxPacket:
227            self.conn.sendExtendedData(self, dataType,
228                                             data[:self.remoteMaxPacket])
229            data = data[self.remoteMaxPacket:]
230            self.remoteWindowLeft -= self.remoteMaxPacket
231        if data:
232            self.conn.sendExtendedData(self, dataType, data)
233            self.remoteWindowLeft -= len(data)
234        if self.closing:
235            self.loseConnection() # try again
236
237    def writeSequence(self, data):
238        """
239        Part of the Transport interface.  Write a list of strings to the
240        channel.
241
242        @type data: C{list} of C{str}
243        """
244        self.write(''.join(data))
245
246    def loseConnection(self):
247        """
248        Close the channel if there is no buferred data.  Otherwise, note the
249        request and return.
250        """
251        self.closing = 1
252        if not self.buf and not self.extBuf:
253            self.conn.sendClose(self)
254
255    def getPeer(self):
256        """
257        Return a tuple describing the other side of the connection.
258
259        @rtype: C{tuple}
260        """
261        return('SSH', )+self.conn.transport.getPeer()
262
263    def getHost(self):
264        """
265        Return a tuple describing our side of the connection.
266
267        @rtype: C{tuple}
268        """
269        return('SSH', )+self.conn.transport.getHost()
270
271    def stopWriting(self):
272        """
273        Called when the remote buffer is full, as a hint to stop writing.
274        This can be ignored, but it can be helpful.
275        """
276
277    def startWriting(self):
278        """
279        Called when the remote buffer has more room, as a hint to continue
280        writing.
281        """
Note: See TracBrowser for help on using the browser.