OLD | NEW |
| (Empty) |
1 # -*- test-case-name: twisted.test.test_banana -*- | |
2 # | |
3 # Copyright (c) 2001-2004 Twisted Matrix Laboratories. | |
4 # See LICENSE for details. | |
5 | |
6 | |
7 """Banana -- s-exp based protocol. | |
8 | |
9 Future Plans: This module is almost entirely stable. The same caveat applies | |
10 to it as applies to L{twisted.spread.jelly}, however. Read its future plans | |
11 for more details. | |
12 | |
13 @author: U{Glyph Lefkowitz<mailto:glyph@twistedmatrix.com>} | |
14 """ | |
15 | |
16 __version__ = "$Revision: 1.37 $"[11:-2] | |
17 | |
18 from twisted.internet import protocol | |
19 from twisted.persisted import styles | |
20 from twisted.python import log | |
21 | |
22 import copy, cStringIO, struct | |
23 | |
24 class BananaError(Exception): | |
25 pass | |
26 | |
27 def int2b128(integer, stream): | |
28 if integer == 0: | |
29 stream(chr(0)) | |
30 return | |
31 assert integer > 0, "can only encode positive integers" | |
32 while integer: | |
33 stream(chr(integer & 0x7f)) | |
34 integer = integer >> 7 | |
35 | |
36 def b1282int(st): | |
37 oneHundredAndTwentyEight = 128l | |
38 i = 0 | |
39 place = 0 | |
40 for char in st: | |
41 num = ord(char) | |
42 i = i + (num * (oneHundredAndTwentyEight ** place)) | |
43 place = place + 1 | |
44 if i <= 2147483647: | |
45 return int(i) | |
46 else: | |
47 return i | |
48 | |
49 # delimiter characters. | |
50 LIST = chr(0x80) | |
51 INT = chr(0x81) | |
52 STRING = chr(0x82) | |
53 NEG = chr(0x83) | |
54 FLOAT = chr(0x84) | |
55 # "optional" -- these might be refused by a low-level implementation. | |
56 LONGINT = chr(0x85) | |
57 LONGNEG = chr(0x86) | |
58 # really optional; this is is part of the 'pb' vocabulary | |
59 VOCAB = chr(0x87) | |
60 | |
61 HIGH_BIT_SET = chr(0x80) | |
62 | |
63 def setPrefixLimit(limit): | |
64 """ | |
65 Set the limit on the prefix length for all Banana connections | |
66 established after this call. | |
67 | |
68 The prefix length limit determines how many bytes of prefix a banana | |
69 decoder will allow before rejecting a potential object as too large. | |
70 | |
71 @type limit: C{int} | |
72 @param limit: The number of bytes of prefix for banana to allow when | |
73 decoding. | |
74 """ | |
75 global _PREFIX_LIMIT | |
76 _PREFIX_LIMIT = limit | |
77 setPrefixLimit(64) | |
78 | |
79 SIZE_LIMIT = 640 * 1024 # 640k is all you'll ever need :-) | |
80 | |
81 class Banana(protocol.Protocol, styles.Ephemeral): | |
82 knownDialects = ["pb", "none"] | |
83 | |
84 prefixLimit = None | |
85 sizeLimit = SIZE_LIMIT | |
86 | |
87 def setPrefixLimit(self, limit): | |
88 """ | |
89 Set the prefix limit for decoding done by this protocol instance. | |
90 | |
91 @see: L{setPrefixLimit} | |
92 """ | |
93 self.prefixLimit = limit | |
94 self._smallestLongInt = -2 ** (limit * 7) + 1 | |
95 self._smallestInt = -2 ** 31 | |
96 self._largestInt = 2 ** 31 - 1 | |
97 self._largestLongInt = 2 ** (limit * 7) - 1 | |
98 | |
99 | |
100 def connectionReady(self): | |
101 """Surrogate for connectionMade | |
102 Called after protocol negotiation. | |
103 """ | |
104 | |
105 def _selectDialect(self, dialect): | |
106 self.currentDialect = dialect | |
107 self.connectionReady() | |
108 | |
109 def callExpressionReceived(self, obj): | |
110 if self.currentDialect: | |
111 self.expressionReceived(obj) | |
112 else: | |
113 # this is the first message we've received | |
114 if self.isClient: | |
115 # if I'm a client I have to respond | |
116 for serverVer in obj: | |
117 if serverVer in self.knownDialects: | |
118 self.sendEncoded(serverVer) | |
119 self._selectDialect(serverVer) | |
120 break | |
121 else: | |
122 # I can't speak any of those dialects. | |
123 log.msg('error losing') | |
124 self.transport.loseConnection() | |
125 else: | |
126 if obj in self.knownDialects: | |
127 self._selectDialect(obj) | |
128 else: | |
129 # the client just selected a protocol that I did not suggest
. | |
130 log.msg('freaky losing') | |
131 self.transport.loseConnection() | |
132 | |
133 | |
134 def connectionMade(self): | |
135 self.setPrefixLimit(_PREFIX_LIMIT) | |
136 self.currentDialect = None | |
137 if not self.isClient: | |
138 self.sendEncoded(self.knownDialects) | |
139 | |
140 | |
141 def gotItem(self, item): | |
142 l = self.listStack | |
143 if l: | |
144 l[-1][1].append(item) | |
145 else: | |
146 self.callExpressionReceived(item) | |
147 | |
148 buffer = '' | |
149 | |
150 def dataReceived(self, chunk): | |
151 buffer = self.buffer + chunk | |
152 listStack = self.listStack | |
153 gotItem = self.gotItem | |
154 while buffer: | |
155 assert self.buffer != buffer, "This ain't right: %s %s" % (repr(self
.buffer), repr(buffer)) | |
156 self.buffer = buffer | |
157 pos = 0 | |
158 for ch in buffer: | |
159 if ch >= HIGH_BIT_SET: | |
160 break | |
161 pos = pos + 1 | |
162 else: | |
163 if pos > self.prefixLimit: | |
164 raise BananaError("Security precaution: more than %d bytes o
f prefix" % (self.prefixLimit,)) | |
165 return | |
166 num = buffer[:pos] | |
167 typebyte = buffer[pos] | |
168 rest = buffer[pos+1:] | |
169 if len(num) > self.prefixLimit: | |
170 raise BananaError("Security precaution: longer than %d bytes wor
th of prefix" % (self.prefixLimit,)) | |
171 if typebyte == LIST: | |
172 num = b1282int(num) | |
173 if num > SIZE_LIMIT: | |
174 raise BananaError("Security precaution: List too long.") | |
175 listStack.append((num, [])) | |
176 buffer = rest | |
177 elif typebyte == STRING: | |
178 num = b1282int(num) | |
179 if num > SIZE_LIMIT: | |
180 raise BananaError("Security precaution: String too long.") | |
181 if len(rest) >= num: | |
182 buffer = rest[num:] | |
183 gotItem(rest[:num]) | |
184 else: | |
185 return | |
186 elif typebyte == INT: | |
187 buffer = rest | |
188 num = b1282int(num) | |
189 gotItem(int(num)) | |
190 elif typebyte == LONGINT: | |
191 buffer = rest | |
192 num = b1282int(num) | |
193 gotItem(long(num)) | |
194 elif typebyte == LONGNEG: | |
195 buffer = rest | |
196 num = b1282int(num) | |
197 gotItem(-long(num)) | |
198 elif typebyte == NEG: | |
199 buffer = rest | |
200 num = -b1282int(num) | |
201 gotItem(num) | |
202 elif typebyte == VOCAB: | |
203 buffer = rest | |
204 num = b1282int(num) | |
205 gotItem(self.incomingVocabulary[num]) | |
206 elif typebyte == FLOAT: | |
207 if len(rest) >= 8: | |
208 buffer = rest[8:] | |
209 gotItem(struct.unpack("!d", rest[:8])[0]) | |
210 else: | |
211 return | |
212 else: | |
213 raise NotImplementedError(("Invalid Type Byte %r" % (typebyte,))
) | |
214 while listStack and (len(listStack[-1][1]) == listStack[-1][0]): | |
215 item = listStack.pop()[1] | |
216 gotItem(item) | |
217 self.buffer = '' | |
218 | |
219 | |
220 def expressionReceived(self, lst): | |
221 """Called when an expression (list, string, or int) is received. | |
222 """ | |
223 raise NotImplementedError() | |
224 | |
225 | |
226 outgoingVocabulary = { | |
227 # Jelly Data Types | |
228 'None' : 1, | |
229 'class' : 2, | |
230 'dereference' : 3, | |
231 'reference' : 4, | |
232 'dictionary' : 5, | |
233 'function' : 6, | |
234 'instance' : 7, | |
235 'list' : 8, | |
236 'module' : 9, | |
237 'persistent' : 10, | |
238 'tuple' : 11, | |
239 'unpersistable' : 12, | |
240 | |
241 # PB Data Types | |
242 'copy' : 13, | |
243 'cache' : 14, | |
244 'cached' : 15, | |
245 'remote' : 16, | |
246 'local' : 17, | |
247 'lcache' : 18, | |
248 | |
249 # PB Protocol Messages | |
250 'version' : 19, | |
251 'login' : 20, | |
252 'password' : 21, | |
253 'challenge' : 22, | |
254 'logged_in' : 23, | |
255 'not_logged_in' : 24, | |
256 'cachemessage' : 25, | |
257 'message' : 26, | |
258 'answer' : 27, | |
259 'error' : 28, | |
260 'decref' : 29, | |
261 'decache' : 30, | |
262 'uncache' : 31, | |
263 } | |
264 | |
265 incomingVocabulary = {} | |
266 for k, v in outgoingVocabulary.items(): | |
267 incomingVocabulary[v] = k | |
268 | |
269 def __init__(self, isClient=1): | |
270 self.listStack = [] | |
271 self.outgoingSymbols = copy.copy(self.outgoingVocabulary) | |
272 self.outgoingSymbolCount = 0 | |
273 self.isClient = isClient | |
274 | |
275 def sendEncoded(self, obj): | |
276 io = cStringIO.StringIO() | |
277 self._encode(obj, io.write) | |
278 value = io.getvalue() | |
279 self.transport.write(value) | |
280 | |
281 def _encode(self, obj, write): | |
282 if isinstance(obj, (list, tuple)): | |
283 if len(obj) > SIZE_LIMIT: | |
284 raise BananaError( | |
285 "list/tuple is too long to send (%d)" % (len(obj),)) | |
286 int2b128(len(obj), write) | |
287 write(LIST) | |
288 for elem in obj: | |
289 self._encode(elem, write) | |
290 elif isinstance(obj, (int, long)): | |
291 if obj < self._smallestLongInt or obj > self._largestLongInt: | |
292 raise BananaError( | |
293 "int/long is too large to send (%d)" % (obj,)) | |
294 if obj < self._smallestInt: | |
295 int2b128(-obj, write) | |
296 write(LONGNEG) | |
297 elif obj < 0: | |
298 int2b128(-obj, write) | |
299 write(NEG) | |
300 elif obj <= self._largestInt: | |
301 int2b128(obj, write) | |
302 write(INT) | |
303 else: | |
304 int2b128(obj, write) | |
305 write(LONGINT) | |
306 elif isinstance(obj, float): | |
307 write(FLOAT) | |
308 write(struct.pack("!d", obj)) | |
309 elif isinstance(obj, str): | |
310 # TODO: an API for extending banana... | |
311 if self.currentDialect == "pb" and obj in self.outgoingSymbols: | |
312 symbolID = self.outgoingSymbols[obj] | |
313 int2b128(symbolID, write) | |
314 write(VOCAB) | |
315 else: | |
316 if len(obj) > SIZE_LIMIT: | |
317 raise BananaError( | |
318 "string is too long to send (%d)" % (len(obj),)) | |
319 int2b128(len(obj), write) | |
320 write(STRING) | |
321 write(obj) | |
322 else: | |
323 raise BananaError("could not send object: %r" % (obj,)) | |
324 | |
325 | |
326 # For use from the interactive interpreter | |
327 _i = Banana() | |
328 _i.connectionMade() | |
329 _i._selectDialect("none") | |
330 | |
331 | |
332 def encode(lst): | |
333 """Encode a list s-expression.""" | |
334 io = cStringIO.StringIO() | |
335 _i.transport = io | |
336 _i.sendEncoded(lst) | |
337 return io.getvalue() | |
338 | |
339 | |
340 def decode(st): | |
341 """ | |
342 Decode a banana-encoded string. | |
343 """ | |
344 l = [] | |
345 _i.expressionReceived = l.append | |
346 try: | |
347 _i.dataReceived(st) | |
348 finally: | |
349 _i.buffer = '' | |
350 del _i.expressionReceived | |
351 return l[0] | |
OLD | NEW |