| 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 |