~soren/nova/iptables-security-groups

« back to all changes in this revision

Viewing changes to vendor/Twisted-10.0.0/twisted/conch/ssh/channel.py

  • Committer: Jesse Andrews
  • Date: 2010-05-28 06:05:26 UTC
  • Revision ID: git-v1:bf6e6e718cdc7488e2da87b21e258ccc065fe499
initial commit

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# -*- test-case-name: twisted.conch.test.test_channel -*-
 
2
# Copyright (c) 2001-2008 Twisted Matrix Laboratories.
 
3
# See LICENSE for details.
 
4
 
 
5
#
 
6
"""
 
7
The parent class for all the SSH Channels.  Currently implemented channels
 
8
are session. direct-tcp, and forwarded-tcp.
 
9
 
 
10
Maintainer: Paul Swartz
 
11
"""
 
12
 
 
13
from twisted.python import log
 
14
from twisted.internet import interfaces
 
15
from zope.interface import implements
 
16
 
 
17
 
 
18
class 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
        """