~certify-web-dev/twisted/certify-trunk

« back to all changes in this revision

Viewing changes to twisted/conch/ssh/session.py

  • Committer: Bazaar Package Importer
  • Author(s): Matthias Klose
  • Date: 2007-01-17 14:52:35 UTC
  • mto: (2.2.3 sid)
  • mto: This revision was merged to the branch mainline in revision 15.
  • Revision ID: james.westby@ubuntu.com-20070117145235-7gaj253qxi5wiq16
Tags: upstream-2.5.0
ImportĀ upstreamĀ versionĀ 2.5.0

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# -*- test-case-name: twisted.conch.test.test_conch -*-
 
2
# Copyright (c) 2001-2004 Twisted Matrix Laboratories.
 
3
# See LICENSE for details.
 
4
 
 
5
 
6
 
 
7
"""This module contains the implementation of SSHSession, which (by default)
 
8
allows access to a shell and a python interpreter over SSH.
 
9
 
 
10
This module is unstable.
 
11
 
 
12
Maintainer: U{Paul Swartz<mailto:z3p@twistedmatrix.com>}
 
13
"""
 
14
 
 
15
import struct
 
16
 
 
17
from twisted.internet import protocol, reactor
 
18
from twisted.python import log
 
19
from twisted.conch.interfaces import ISession
 
20
import common, channel
 
21
 
 
22
class SSHSession(channel.SSHChannel):
 
23
 
 
24
    name = 'session'
 
25
    def __init__(self, *args, **kw):
 
26
        channel.SSHChannel.__init__(self, *args, **kw)
 
27
        self.buf = ''
 
28
        self.client = None
 
29
        self.session = None
 
30
 
 
31
    def request_subsystem(self, data):
 
32
        subsystem, ignored= common.getNS(data)
 
33
        log.msg('asking for subsystem "%s"' % subsystem)
 
34
        client = self.avatar.lookupSubsystem(subsystem, data)
 
35
        if client:
 
36
            pp = SSHSessionProcessProtocol(self)
 
37
            proto = wrapProcessProtocol(pp)
 
38
            client.makeConnection(proto)
 
39
            pp.makeConnection(wrapProtocol(client))
 
40
            self.client = pp
 
41
            return 1
 
42
        else:
 
43
            log.msg('failed to get subsystem')
 
44
            return 0
 
45
 
 
46
    def request_shell(self, data):
 
47
        log.msg('getting shell')
 
48
        if not self.session:
 
49
            self.session = ISession(self.avatar)
 
50
        try:
 
51
            pp = SSHSessionProcessProtocol(self)
 
52
            self.session.openShell(pp)
 
53
        except:
 
54
            log.deferr()
 
55
            return 0
 
56
        else:
 
57
            self.client = pp
 
58
            return 1
 
59
 
 
60
    def request_exec(self, data):
 
61
        if not self.session:
 
62
            self.session = ISession(self.avatar)
 
63
        f,data = common.getNS(data)
 
64
        log.msg('executing command "%s"' % f)
 
65
        try:
 
66
            pp = SSHSessionProcessProtocol(self)
 
67
            self.session.execCommand(pp, f)
 
68
        except:
 
69
            log.deferr()
 
70
            return 0
 
71
        else:
 
72
            self.client = pp
 
73
            return 1
 
74
 
 
75
    def request_pty_req(self, data):
 
76
        if not self.session:
 
77
            self.session = ISession(self.avatar)
 
78
        term, windowSize, modes = parseRequest_pty_req(data)
 
79
        log.msg('pty request: %s %s' % (term, windowSize))
 
80
        try:
 
81
            self.session.getPty(term, windowSize, modes) 
 
82
        except:
 
83
            log.err()
 
84
            return 0
 
85
        else:
 
86
            return 1
 
87
 
 
88
    def request_window_change(self, data):
 
89
        if not self.session:
 
90
            self.session = ISession(self.avatar)
 
91
        import fcntl, tty
 
92
        winSize = parseRequest_window_change(data)
 
93
        try:
 
94
            self.session.windowChanged(winSize)
 
95
        except:
 
96
            log.msg('error changing window size')
 
97
            log.err()
 
98
            return 0
 
99
        else:
 
100
            return 1
 
101
 
 
102
    def dataReceived(self, data):
 
103
        if not self.client:
 
104
            #self.conn.sendClose(self)
 
105
            self.buf += data
 
106
            return
 
107
        self.client.transport.write(data)
 
108
 
 
109
    def extReceived(self, dataType, data):
 
110
        if dataType == connection.EXTENDED_DATA_STDERR:
 
111
            if self.client and hasattr(self.client.transport, 'writeErr'):
 
112
                self.client.transport.writeErr(data)
 
113
        else:
 
114
            log.msg('weird extended data: %s'%dataType)
 
115
 
 
116
    def eofReceived(self):
 
117
        if self.session:
 
118
            self.session.eofReceived()
 
119
        elif self.client:
 
120
            self.conn.sendClose(self)
 
121
 
 
122
    def closed(self):
 
123
        if self.session:
 
124
            self.session.closed()
 
125
 
 
126
    #def closeReceived(self):
 
127
    #    self.loseConnection() # don't know what to do with this
 
128
 
 
129
    def loseConnection(self):
 
130
        if self.client:
 
131
            self.client.transport.loseConnection()
 
132
        channel.SSHChannel.loseConnection(self)
 
133
 
 
134
class _ProtocolWrapper(protocol.ProcessProtocol):
 
135
    """
 
136
    This class wraps a L{Protocol} instance in a L{ProcessProtocol} instance.
 
137
    """
 
138
    def __init__(self, proto):
 
139
        self.proto = proto
 
140
 
 
141
    def connectionMade(self): self.proto.connectionMade()
 
142
    
 
143
    def outReceived(self, data): self.proto.dataReceived(data)
 
144
 
 
145
    def processEnded(self, reason): self.proto.connectionLost(reason)
 
146
 
 
147
class _DummyTransport:
 
148
 
 
149
    def __init__(self, proto):
 
150
        self.proto = proto
 
151
 
 
152
    def dataReceived(self, data):
 
153
        self.proto.transport.write(data)
 
154
 
 
155
    def write(self, data):
 
156
        self.proto.dataReceived(data)
 
157
 
 
158
    def writeSequence(self, seq):
 
159
        self.write(''.join(seq))
 
160
 
 
161
    def loseConnection(self):
 
162
        self.proto.connectionLost(protocol.connectionDone)
 
163
    
 
164
def wrapProcessProtocol(inst):
 
165
    if isinstance(inst, protocol.Protocol):
 
166
        return _ProtocolWrapper(inst)
 
167
    else:
 
168
        return inst
 
169
 
 
170
def wrapProtocol(proto):
 
171
    return _DummyTransport(proto)
 
172
 
 
173
class SSHSessionProcessProtocol(protocol.ProcessProtocol):
 
174
 
 
175
#    __implements__ = I
 
176
    def __init__(self, session):
 
177
        self.session = session
 
178
 
 
179
    def connectionMade(self):
 
180
        if self.session.buf:
 
181
            self.transport.write(self.session.buf)
 
182
            self.session.buf = None
 
183
 
 
184
    def outReceived(self, data):
 
185
        self.session.write(data)
 
186
 
 
187
    def errReceived(self, err):
 
188
        self.session.writeExtended(connection.EXTENDED_DATA_STDERR, err)
 
189
 
 
190
    def inConnectionLost(self):
 
191
        self.session.conn.sendEOF(self.session)
 
192
 
 
193
    def connectionLost(self, reason = None):
 
194
        self.session.loseConnection()
 
195
 
 
196
    def processEnded(self, reason = None):
 
197
        if reason and hasattr(reason.value, 'exitCode'): 
 
198
            log.msg('exitCode: %s' % repr(reason.value.exitCode))
 
199
            self.session.conn.sendRequest(self.session, 'exit-status', struct.pack('!L', reason.value.exitCode))
 
200
        self.session.loseConnection()
 
201
 
 
202
    # transport stuff (we are also a transport!)
 
203
 
 
204
    def write(self, data):
 
205
        self.session.write(data)
 
206
 
 
207
    def writeSequence(self, seq):
 
208
        self.session.write(''.join(seq))
 
209
 
 
210
    def loseConnection(self):
 
211
        self.session.loseConnection()
 
212
 
 
213
class SSHSessionClient(protocol.Protocol):
 
214
 
 
215
    def dataReceived(self, data):
 
216
        if self.transport:
 
217
            self.transport.write(data)
 
218
 
 
219
# methods factored out to make live easier on server writers
 
220
def parseRequest_pty_req(data):
 
221
    """Parse the data from a pty-req request into usable data.
 
222
 
 
223
    @returns: a tuple of (terminal type, (rows, cols, xpixel, ypixel), modes)
 
224
    """
 
225
    term, rest = common.getNS(data)
 
226
    cols, rows, xpixel, ypixel = struct.unpack('>4L', rest[: 16])
 
227
    modes, ignored= common.getNS(rest[16:])
 
228
    winSize = (rows, cols, xpixel, ypixel)
 
229
    modes = [(ord(modes[i]), struct.unpack('>L', modes[i+1: i+5])[0]) for i in range(0, len(modes)-1, 5)]
 
230
    return term, winSize, modes
 
231
 
 
232
def packRequest_pty_req(term, (rows, cols, xpixel, ypixel), modes):
 
233
    """Pack a pty-req request so that it is suitable for sending.
 
234
 
 
235
    NOTE: modes must be packed before being sent here.
 
236
    """
 
237
    termPacked = common.NS(term)
 
238
    winSizePacked = struct.pack('>4L', cols, rows, xpixel, ypixel)
 
239
    modesPacked = common.NS(modes) # depend on the client packing modes
 
240
    return termPacked + winSizePacked + modesPacked
 
241
 
 
242
def parseRequest_window_change(data):
 
243
    """Parse the data from a window-change request into usuable data.
 
244
 
 
245
    @returns: a tuple of (rows, cols, xpixel, ypixel)
 
246
    """
 
247
    cols, rows, xpixel, ypixel = struct.unpack('>4L', data)
 
248
    return rows, cols, xpixel, ypixel
 
249
 
 
250
def packRequest_window_change((rows, cols, xpixel, ypixel)):
 
251
    """Pack a window-change request so that it is suitable for sending.
 
252
    """
 
253
    return struct.pack('>4L', cols, rows, xpixel, ypixel)
 
254
 
 
255
import connection