message.py 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237
  1. # Copyright (C) 2009 Internet Systems Consortium.
  2. #
  3. # Permission to use, copy, modify, and distribute this software for any
  4. # purpose with or without fee is hereby granted, provided that the above
  5. # copyright notice and this permission notice appear in all copies.
  6. #
  7. # THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SYSTEMS CONSORTIUM
  8. # DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
  9. # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
  10. # INTERNET SYSTEMS CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT,
  11. # INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING
  12. # FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
  13. # NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
  14. # WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  15. import sys
  16. import struct
  17. class DecodeError(Exception): pass
  18. PROTOCOL_VERSION = 0x536b616e
  19. _ITEM_BLOB = 0x01
  20. _ITEM_HASH = 0x02
  21. _ITEM_LIST = 0x03
  22. _ITEM_NULL = 0x04
  23. _ITEM_BOOL = 0x05
  24. _ITEM_INT = 0x06
  25. _ITEM_REAL = 0x07
  26. _ITEM_UTF8 = 0x08
  27. _ITEM_MASK = 0x0f
  28. _ITEM_LENGTH_32 = 0x00
  29. _ITEM_LENGTH_16 = 0x10
  30. _ITEM_LENGTH_8 = 0x20
  31. _ITEM_LENGTH_MASK = 0x30
  32. def to_wire(items):
  33. """Encode a dict into wire format.
  34. >>> wire_format = Message.to_wire({"a": "b"})
  35. """
  36. return struct.pack(">I", PROTOCOL_VERSION) + _encode_hash(items)
  37. def _encode_tag(tag):
  38. """Encode a single UTF-8 tag.
  39. ... wire_partial = Message._encode_tag('this')
  40. """
  41. binary = bytes(tag, 'utf-8')
  42. if len(binary) > 255:
  43. raise ArgumentError("tag is too long (max 255 encoded bytes)")
  44. return(struct.pack(">B", len(binary))) + binary
  45. def _encode_length_and_type(data, datatype):
  46. """Helper method to handle the length encoding in one place."""
  47. if data == None:
  48. return(struct.pack(">B", _ITEM_NULL))
  49. length = len(data)
  50. if length < 0x0000100:
  51. return(struct.pack(">B B", datatype | _ITEM_LENGTH_8, length) + data)
  52. elif length < 0x00010000:
  53. return(struct.pack(">B H", datatype | _ITEM_LENGTH_16, length) + data)
  54. else:
  55. return(struct.pack(">B I", datatype | _ITEM_LENGTH_32, length) + data)
  56. def _pack_utf8(item):
  57. """Pack a string (utf-8) and its type/length prefix."""
  58. return (_encode_length_and_type(bytes(item, 'utf-8'), _ITEM_UTF8))
  59. def _pack_blob(item):
  60. """Pack a blob (binary data) and its type/length prefix."""
  61. return (_encode_length_and_type(item, _ITEM_BLOB))
  62. def _pack_bool(item):
  63. """Pack a bool and its type/length prefix."""
  64. return (_encode_length_and_type(_encode_bool(item), _ITEM_BOOL))
  65. def _pack_int(item):
  66. """Pack an integer and its type/length prefix."""
  67. return (_encode_length_and_type(bytes(str(item), 'utf-8'), _ITEM_INT))
  68. def _pack_real(item):
  69. """Pack an integer and its type/length prefix."""
  70. return (_encode_length_and_type(bytes(str(item), 'utf-8'), _ITEM_REAL))
  71. def _pack_array(item):
  72. """Pack a list (array) and its type/length prefix."""
  73. return (_encode_length_and_type(_encode_array(item), _ITEM_LIST))
  74. def _pack_hash(item):
  75. """Pack a dict (hash) and its type/length prefix."""
  76. data = _encode_hash(item)
  77. return (_encode_length_and_type(data, _ITEM_HASH))
  78. def _pack_nil():
  79. """Encode a nil (NULL, None) item."""
  80. return _encode_length_and_type(None, None)
  81. def _encode_item(item):
  82. """Encode each item depending on its type"""
  83. if item == None:
  84. return (_pack_nil())
  85. elif type(item) == bool:
  86. return (_pack_bool(item))
  87. elif type(item) == int:
  88. return (_pack_int(item))
  89. elif type(item) == float:
  90. return (_pack_real(item))
  91. elif type(item) == dict:
  92. return (_pack_hash(item))
  93. elif type(item) == list:
  94. return (_pack_array(item))
  95. elif type(item) in (bytes, bytearray):
  96. return (_pack_blob(item))
  97. else:
  98. return (_pack_utf8(str(item)))
  99. def _encode_bool(item):
  100. """Encode a boolean value into a bytearray of one byte (0=false)"""
  101. if item:
  102. return b'1'
  103. else:
  104. return b'0'
  105. def _encode_array(item):
  106. """Encode an array, where each value is encoded recursively"""
  107. ret = bytes()
  108. for i in item:
  109. ret += _encode_item(i)
  110. return ret
  111. def _encode_hash(item):
  112. """Encode a hash, where each value is encoded recursively"""
  113. ret = bytes()
  114. for key, value in item.items():
  115. ret += _encode_tag(key)
  116. ret += _encode_item(value)
  117. return ret
  118. #
  119. # decode methods
  120. #
  121. def from_wire(data):
  122. if len(data) < 5:
  123. raise DecodeError("Data is too short to decode")
  124. wire_version, data = data[0:4], data[4:]
  125. wire_version = struct.unpack(">I", wire_version)[0]
  126. if wire_version != PROTOCOL_VERSION:
  127. raise DecodeError("Incorrect protocol version")
  128. return _decode_hash(data)
  129. def _decode_tag(data):
  130. if len(data) < 1:
  131. raise DecodeError("Data underrun while decoding")
  132. length = data[0]
  133. if len(data) - 1 < length:
  134. raise DecodeError("Data underrun while decoding")
  135. return [data[1:length + 1].decode(), data[length + 1:]]
  136. def _decode_item(data):
  137. if len(data) < 1:
  138. raise DecodeError("Data underrun while decoding")
  139. type_and_length_format = data[0]
  140. item_type = type_and_length_format & _ITEM_MASK
  141. length_format = type_and_length_format & _ITEM_LENGTH_MASK
  142. if item_type == _ITEM_NULL:
  143. data = data[1:]
  144. else:
  145. if length_format == _ITEM_LENGTH_8:
  146. if len(data) - 1 < 1:
  147. raise DecodeError("Data underrun while decoding")
  148. length = data[1]
  149. data = data[2:]
  150. elif length_format == _ITEM_LENGTH_16:
  151. if len(data) - 1 < 2:
  152. raise DecodeError("Data underrun while decoding")
  153. length = struct.unpack(">H", data[1:3])[0]
  154. data = data[3:]
  155. elif length_format == _ITEM_LENGTH_32:
  156. if len(data) - 1 < 4:
  157. raise DecodeError("Data underrun while decoding")
  158. length = struct.unpack(">I", data[1:5])[0]
  159. data = data[5:]
  160. if len(data) < length:
  161. raise DecodeError("Data underrun while decoding")
  162. item = data[0:length]
  163. data = data[length:]
  164. if item_type == _ITEM_BLOB:
  165. value = item
  166. elif item_type == _ITEM_BOOL:
  167. value = _decode_bool(item)
  168. elif item_type == _ITEM_INT:
  169. value = _decode_int(item)
  170. elif item_type == _ITEM_REAL:
  171. value = _decode_real(item)
  172. elif item_type == _ITEM_UTF8:
  173. value = str(item, 'utf-8')
  174. elif item_type == _ITEM_HASH:
  175. value = _decode_hash(item)
  176. elif item_type == _ITEM_LIST:
  177. value = _decode_array(item)
  178. elif item_type == _ITEM_NULL:
  179. value = None
  180. else:
  181. raise DecodeError("Unknown item type in decode: %02x" % item_type)
  182. return (value, data)
  183. def _decode_bool(data):
  184. return data == b'1'
  185. def _decode_int(data):
  186. return int(str(data, 'utf-8'))
  187. def _decode_real(data):
  188. return float(str(data, 'utf-8'))
  189. def _decode_hash(data):
  190. ret = {}
  191. while len(data) > 0:
  192. tag, data = _decode_tag(data)
  193. value, data = _decode_item(data)
  194. ret[tag] = value
  195. return ret
  196. def _decode_array(data):
  197. ret = []
  198. while len(data) > 0:
  199. value, data = _decode_item(data)
  200. ret.append(value)
  201. return ret
  202. if __name__ == "__main__":
  203. import doctest
  204. doctest.testmod()