1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 """
20 Implementation of an SSH2 "message".
21 """
22
23 import struct
24 import cStringIO
25
26 from paramiko import util
27
28
30 """
31 An SSH2 I{Message} is a stream of bytes that encodes some combination of
32 strings, integers, bools, and infinite-precision integers (known in python
33 as I{long}s). This class builds or breaks down such a byte stream.
34
35 Normally you don't need to deal with anything this low-level, but it's
36 exposed for people implementing custom extensions, or features that
37 paramiko doesn't support yet.
38 """
39
41 """
42 Create a new SSH2 Message.
43
44 @param content: the byte stream to use as the Message content (passed
45 in only when decomposing a Message).
46 @type content: string
47 """
48 if content != None:
49 self.packet = cStringIO.StringIO(content)
50 else:
51 self.packet = cStringIO.StringIO()
52
54 """
55 Return the byte stream content of this Message, as a string.
56
57 @return: the contents of this Message.
58 @rtype: string
59 """
60 return self.packet.getvalue()
61
63 """
64 Returns a string representation of this object, for debugging.
65
66 @rtype: string
67 """
68 return 'paramiko.Message(' + repr(self.packet.getvalue()) + ')'
69
71 """
72 Rewind the message to the beginning as if no items had been parsed
73 out of it yet.
74 """
75 self.packet.seek(0)
76
77 - def get_remainder(self):
78 """
79 Return the bytes of this Message that haven't already been parsed and
80 returned.
81
82 @return: a string of the bytes not parsed yet.
83 @rtype: string
84 """
85 position = self.packet.tell()
86 remainder = self.packet.read()
87 self.packet.seek(position)
88 return remainder
89
91 """
92 Returns the bytes of this Message that have been parsed and returned.
93 The string passed into a Message's constructor can be regenerated by
94 concatenating C{get_so_far} and L{get_remainder}.
95
96 @return: a string of the bytes parsed so far.
97 @rtype: string
98 """
99 position = self.packet.tell()
100 self.rewind()
101 return self.packet.read(position)
102
104 """
105 Return the next C{n} bytes of the Message, without decomposing into
106 an int, string, etc. Just the raw bytes are returned.
107
108 @return: a string of the next C{n} bytes of the Message, or a string
109 of C{n} zero bytes, if there aren't C{n} bytes remaining.
110 @rtype: string
111 """
112 b = self.packet.read(n)
113 if len(b) < n:
114 return b + '\x00' * (n - len(b))
115 return b
116
118 """
119 Return the next byte of the Message, without decomposing it. This
120 is equivalent to L{get_bytes(1)<get_bytes>}.
121
122 @return: the next byte of the Message, or C{'\000'} if there aren't
123 any bytes remaining.
124 @rtype: string
125 """
126 return self.get_bytes(1)
127
129 """
130 Fetch a boolean from the stream.
131
132 @return: C{True} or C{False} (from the Message).
133 @rtype: bool
134 """
135 b = self.get_bytes(1)
136 return b != '\x00'
137
139 """
140 Fetch an int from the stream.
141
142 @return: a 32-bit unsigned integer.
143 @rtype: int
144 """
145 return struct.unpack('>I', self.get_bytes(4))[0]
146
148 """
149 Fetch a 64-bit int from the stream.
150
151 @return: a 64-bit unsigned integer.
152 @rtype: long
153 """
154 return struct.unpack('>Q', self.get_bytes(8))[0]
155
157 """
158 Fetch a long int (mpint) from the stream.
159
160 @return: an arbitrary-length integer.
161 @rtype: long
162 """
163 return util.inflate_long(self.get_string())
164
166 """
167 Fetch a string from the stream. This could be a byte string and may
168 contain unprintable characters. (It's not unheard of for a string to
169 contain another byte-stream Message.)
170
171 @return: a string.
172 @rtype: string
173 """
174 return self.get_bytes(self.get_int())
175
177 """
178 Fetch a list of strings from the stream. These are trivially encoded
179 as comma-separated values in a string.
180
181 @return: a list of strings.
182 @rtype: list of strings
183 """
184 return self.get_string().split(',')
185
187 """
188 Write bytes to the stream, without any formatting.
189
190 @param b: bytes to add
191 @type b: str
192 """
193 self.packet.write(b)
194 return self
195
197 """
198 Write a single byte to the stream, without any formatting.
199
200 @param b: byte to add
201 @type b: str
202 """
203 self.packet.write(b)
204 return self
205
207 """
208 Add a boolean value to the stream.
209
210 @param b: boolean value to add
211 @type b: bool
212 """
213 if b:
214 self.add_byte('\x01')
215 else:
216 self.add_byte('\x00')
217 return self
218
220 """
221 Add an integer to the stream.
222
223 @param n: integer to add
224 @type n: int
225 """
226 self.packet.write(struct.pack('>I', n))
227 return self
228
230 """
231 Add a 64-bit int to the stream.
232
233 @param n: long int to add
234 @type n: long
235 """
236 self.packet.write(struct.pack('>Q', n))
237 return self
238
240 """
241 Add a long int to the stream, encoded as an infinite-precision
242 integer. This method only works on positive numbers.
243
244 @param z: long int to add
245 @type z: long
246 """
247 self.add_string(util.deflate_long(z))
248 return self
249
251 """
252 Add a string to the stream.
253
254 @param s: string to add
255 @type s: str
256 """
257 self.add_int(len(s))
258 self.packet.write(s)
259 return self
260
262 """
263 Add a list of strings to the stream. They are encoded identically to
264 a single string of values separated by commas. (Yes, really, that's
265 how SSH2 does it.)
266
267 @param l: list of strings to add
268 @type l: list(str)
269 """
270 self.add_string(','.join(l))
271 return self
272
274 if type(i) is str:
275 return self.add_string(i)
276 elif type(i) is int:
277 return self.add_int(i)
278 elif type(i) is long:
279 if i > 0xffffffffL:
280 return self.add_mpint(i)
281 else:
282 return self.add_int(i)
283 elif type(i) is bool:
284 return self.add_boolean(i)
285 elif type(i) is list:
286 return self.add_list(i)
287 else:
288 raise Exception('Unknown type')
289
290 - def add(self, *seq):
291 """
292 Add a sequence of items to the stream. The values are encoded based
293 on their type: str, int, bool, list, or long.
294
295 @param seq: the sequence of items
296 @type seq: sequence
297
298 @bug: longs are encoded non-deterministically. Don't use this method.
299 """
300 for item in seq:
301 self._add(item)
302