1
# Copyright (c) 2001-2004 Twisted Matrix Laboratories.
2
# See LICENSE for details.
5
"""The parent class for all the SSH Channels. Currently implemented channels
6
are session. direct-tcp, and forwarded-tcp.
8
This module is unstable.
10
Maintainer: U{Paul Swartz<mailto:z3p@twistedmatrix.com>}
13
from twisted.python import log, context
15
class SSHChannel(log.Logger):
16
name = None # only needed for client channels
17
def __init__(self, localWindow = 0, localMaxPacket = 0,
18
remoteWindow = 0, remoteMaxPacket = 0,
19
conn = None, data=None, avatar = None):
20
self.localWindowSize = localWindow or 131072
21
self.localWindowLeft = self.localWindowSize
22
self.localMaxPacket = localMaxPacket or 32768
23
self.remoteWindowLeft = remoteWindow
24
self.remoteMaxPacket = remoteMaxPacket
29
self.specificData = ''
35
self.id = None # gets set later by SSHConnection
38
return '%s (lw %i rw %i)' % (self.name, self.localWindowLeft, self.remoteWindowLeft)
41
id = (self.id is not None and str(self.id)) or "unknown"
42
return "SSHChannel %s (%s) on %s" % (self.name, id, self.conn.logPrefix())
44
def channelOpen(self, specificData):
46
Called when the channel is opened. specificData is any data that the
47
other side sent us when opening the channel.
49
@type specificData: C{str}
51
log.msg('channel open')
53
def openFailed(self, reason):
55
Called when the the open failed for some reason.
56
reason.desc is a string descrption, reason.code the the SSH error code.
58
@type reason: L{error.ConchError}
60
log.msg('other side refused open\nreason: %s'% reason)
62
def addWindowBytes(self, bytes):
64
Called when bytes are added to the remote window. By default it clears
69
self.remoteWindowLeft = self.remoteWindowLeft+bytes
70
if not self.areWriting and not self.closing:
81
self.writeExtended(*i)
83
def requestReceived(self, requestType, data):
85
Called when a request is sent to this channel. By default it delegates
86
to self.request_<requestType>.
87
If this function returns true, the request succeeded, otherwise it
90
@type requestType: C{str}
94
foo = requestType.replace('-', '_')
95
f = getattr(self, 'request_%s'%foo, None)
98
log.msg('unhandled request for %s'%requestType)
101
def dataReceived(self, data):
103
Called when we receive data.
107
log.msg('got data %s'%repr(data))
109
def extReceived(self, dataType, data):
111
Called when we receive extended data (usually standard error).
113
@type dataType: C{int}
116
log.msg('got extended data %s %s'%(dataType, repr(data)))
118
def eofReceived(self):
120
Called when the other side will send no more data.
122
log.msg('remote eof')
124
def closeReceived(self):
126
Called when the other side has closed the channel.
128
log.msg('remote close')
129
self.loseConnection()
133
Called when the channel is closed. This means that both our side and
134
the remote side have closed the channel.
139
def write(self, data):
141
Write some data to the channel. If there is not enough remote window
142
available, buffer until it is.
151
if top > self.remoteWindowLeft:
152
data, self.buf = data[:self.remoteWindowLeft], data[self.remoteWindowLeft:]
155
top = self.remoteWindowLeft
156
rmp = self.remoteMaxPacket
157
write = self.conn.sendData
158
r = range(0, top, rmp)
160
write(self, data[offset: offset+rmp])
161
self.remoteWindowLeft-=top
162
if self.closing and not self.buf:
163
self.loseConnection() # try again
165
def writeExtended(self, dataType, data):
167
Send extended data to this channel. If there is not enough remote
168
window available, buffer until there is.
170
@type dataType: C{int}
174
if self.extBuf[-1][0] == dataType:
175
self.extBuf[-1][1]+=data
177
self.extBuf.append([dataType, data])
179
if len(data) > self.remoteWindowLeft:
180
data, self.extBuf = data[:self.remoteWindowLeft], \
181
[[dataType, data[self.remoteWindowLeft:]]]
185
while len(data) > self.remoteMaxPacket:
186
self.conn.sendExtendedData(self, dataType,
187
data[:self.remoteMaxPacket])
188
data = data[self.remoteMaxPacket:]
189
self.remoteWindowLeft-=self.remoteMaxPacket
191
self.conn.sendExtendedData(self, dataType, data)
192
self.remoteWindowLeft-=len(data)
194
self.loseConnection() # try again
196
def writeSequence(self, data):
198
Part of the Transport interface. Write a list of strings to the
201
@type data: C{list} of C{str}
203
self.write(''.join(data))
205
def loseConnection(self):
210
if not self.buf and not self.extBuf:
211
self.conn.sendClose(self)
215
Return a tuple describing the other side of the connection.
219
return('SSH', )+self.conn.transport.getPeer()
223
Return a tuple describing our side of the connection.
227
return('SSH', )+self.conn.transport.getHost()
229
def stopWriting(self):
231
Called when the remote buffer is full, as a hint to stop writing.
232
This can be ignored, but it can be helpful.
235
def startWriting(self):
237
Called when the remote buffer has more room, as a hint to continue