OLD | NEW |
| (Empty) |
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 """ | |
8 This module contains the implementation of SSHSession, which (by default) | |
9 allows access to a shell and a python interpreter over SSH. | |
10 | |
11 Maintainer: U{Paul Swartz<mailto:z3p@twistedmatrix.com>} | |
12 """ | |
13 | |
14 import struct | |
15 | |
16 from twisted.internet import protocol | |
17 from twisted.python import log | |
18 from twisted.conch.interfaces import ISession | |
19 from twisted.conch.ssh import common, channel | |
20 | |
21 class SSHSession(channel.SSHChannel): | |
22 | |
23 name = 'session' | |
24 def __init__(self, *args, **kw): | |
25 channel.SSHChannel.__init__(self, *args, **kw) | |
26 self.buf = '' | |
27 self.client = None | |
28 self.session = None | |
29 | |
30 def request_subsystem(self, data): | |
31 subsystem, ignored= common.getNS(data) | |
32 log.msg('asking for subsystem "%s"' % subsystem) | |
33 client = self.avatar.lookupSubsystem(subsystem, data) | |
34 if client: | |
35 pp = SSHSessionProcessProtocol(self) | |
36 proto = wrapProcessProtocol(pp) | |
37 client.makeConnection(proto) | |
38 pp.makeConnection(wrapProtocol(client)) | |
39 self.client = pp | |
40 return 1 | |
41 else: | |
42 log.msg('failed to get subsystem') | |
43 return 0 | |
44 | |
45 def request_shell(self, data): | |
46 log.msg('getting shell') | |
47 if not self.session: | |
48 self.session = ISession(self.avatar) | |
49 try: | |
50 pp = SSHSessionProcessProtocol(self) | |
51 self.session.openShell(pp) | |
52 except: | |
53 log.deferr() | |
54 return 0 | |
55 else: | |
56 self.client = pp | |
57 return 1 | |
58 | |
59 def request_exec(self, data): | |
60 if not self.session: | |
61 self.session = ISession(self.avatar) | |
62 f,data = common.getNS(data) | |
63 log.msg('executing command "%s"' % f) | |
64 try: | |
65 pp = SSHSessionProcessProtocol(self) | |
66 self.session.execCommand(pp, f) | |
67 except: | |
68 log.deferr() | |
69 return 0 | |
70 else: | |
71 self.client = pp | |
72 return 1 | |
73 | |
74 def request_pty_req(self, data): | |
75 if not self.session: | |
76 self.session = ISession(self.avatar) | |
77 term, windowSize, modes = parseRequest_pty_req(data) | |
78 log.msg('pty request: %s %s' % (term, windowSize)) | |
79 try: | |
80 self.session.getPty(term, windowSize, modes) | |
81 except: | |
82 log.err() | |
83 return 0 | |
84 else: | |
85 return 1 | |
86 | |
87 def request_window_change(self, data): | |
88 if not self.session: | |
89 self.session = ISession(self.avatar) | |
90 winSize = parseRequest_window_change(data) | |
91 try: | |
92 self.session.windowChanged(winSize) | |
93 except: | |
94 log.msg('error changing window size') | |
95 log.err() | |
96 return 0 | |
97 else: | |
98 return 1 | |
99 | |
100 def dataReceived(self, data): | |
101 if not self.client: | |
102 #self.conn.sendClose(self) | |
103 self.buf += data | |
104 return | |
105 self.client.transport.write(data) | |
106 | |
107 def extReceived(self, dataType, data): | |
108 if dataType == connection.EXTENDED_DATA_STDERR: | |
109 if self.client and hasattr(self.client.transport, 'writeErr'): | |
110 self.client.transport.writeErr(data) | |
111 else: | |
112 log.msg('weird extended data: %s'%dataType) | |
113 | |
114 def eofReceived(self): | |
115 if self.session: | |
116 self.session.eofReceived() | |
117 elif self.client: | |
118 self.conn.sendClose(self) | |
119 | |
120 def closed(self): | |
121 if self.session: | |
122 self.session.closed() | |
123 elif self.client: | |
124 self.client.transport.loseConnection() | |
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.pa
ck('!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 r
ange(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 | |
OLD | NEW |