Message.py 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192
  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. # XXX
  16. from ISC.Util import hexdump
  17. import sys
  18. import struct
  19. class DecodeError(Exception): pass
  20. PROTOCOL_VERSION = 0x536b616e
  21. _ITEM_DATA = 0x01
  22. _ITEM_HASH = 0x02
  23. _ITEM_LIST = 0x03
  24. _ITEM_NULL = 0x04
  25. _ITEM_MASK = 0x0f
  26. _ITEM_LENGTH_32 = 0x00
  27. _ITEM_LENGTH_16 = 0x10
  28. _ITEM_LENGTH_8 = 0x20
  29. _ITEM_LENGTH_MASK = 0x30
  30. def to_wire(items):
  31. """Encode a dict into wire format.
  32. >>> wire_format = Message.to_wire({"a": "b"})
  33. """
  34. return struct.pack(">I", PROTOCOL_VERSION) + _encode_hash(items)
  35. def _encode_tag(tag):
  36. """Encode a single UTF-8 tag.
  37. ... wire_partial = Message._encode_tag('this')
  38. """
  39. return(struct.pack(">B", len(tag)) + bytearray(tag, 'utf-8'))
  40. def _encode_length_and_type(data, datatype):
  41. """Helper method to handle the length encoding in one place."""
  42. if data == None:
  43. return(struct.pack(">B", _ITEM_NULL))
  44. length = len(data)
  45. if length < 0x0000100:
  46. return(struct.pack(">B B", datatype | _ITEM_LENGTH_8, length) + data)
  47. elif length < 0x00010000:
  48. return(struct.pack(">B H", datatype | _ITEM_LENGTH_16, length) + data)
  49. else:
  50. return(struct.pack(">B I", datatype, length) + data)
  51. def _pack_string(item):
  52. """Pack a string (data) and its type/length prefix."""
  53. return (_encode_length_and_type(bytearray(item, 'utf-8'), _ITEM_DATA))
  54. def _pack_array(item):
  55. """Pack a list (array) and its type/length prefix."""
  56. return (_encode_length_and_type(_encode_array(item), _ITEM_LIST))
  57. def _pack_hash(item):
  58. """Pack a dict (hash) and its type/length prefix."""
  59. data = _encode_hash(item)
  60. return (_encode_length_and_type(data, _ITEM_HASH))
  61. def _encode_string(item):
  62. """Encode a string. More or less identity."""
  63. return (item)
  64. def _pack_nil():
  65. """Encode a nil (NULL, None) item."""
  66. return _encode_length_and_type(None, None)
  67. def _encode_item(item):
  68. """Encode each item depending on its type"""
  69. if item == None:
  70. return (_pack_nil())
  71. elif type(item) == dict:
  72. return (_pack_hash(item))
  73. elif type(item) == list:
  74. return (_pack_array(item))
  75. elif type(item) in (bytearray, bytes):
  76. return (_pack_string(item.decode()))
  77. else:
  78. return (_pack_string(str(item)))
  79. def _encode_array(item):
  80. """Encode an array, where each value is encoded recursively"""
  81. ret = bytearray()
  82. for i in item:
  83. ret += _encode_item(i)
  84. return ret
  85. def _encode_hash(item):
  86. """Encode a hash, where each value is encoded recursively"""
  87. ret = bytearray()
  88. for key, value in item.items():
  89. ret += _encode_tag(key)
  90. ret += _encode_item(value)
  91. return ret
  92. #
  93. # decode methods
  94. #
  95. def from_wire(data):
  96. if len(data) < 5:
  97. raise DecodeError("Data is too short to decode")
  98. wire_version, data = data[0:4], data[4:]
  99. wire_version = struct.unpack(">I", wire_version)[0]
  100. if wire_version != PROTOCOL_VERSION:
  101. raise DecodeError("Incorrect protocol version")
  102. return _decode_hash(data)
  103. def _decode_tag(data):
  104. if len(data) < 1:
  105. raise DecodeError("Data underrun while decoding")
  106. length = data[0]
  107. if len(data) - 1 < length:
  108. raise DecodeError("Data underrun while decoding")
  109. return [data[1:length + 1].decode(), data[length + 1:]]
  110. def _decode_item(data):
  111. if len(data) < 1:
  112. raise DecodeError("Data underrun while decoding")
  113. type_and_length_format = data[0]
  114. item_type = type_and_length_format & _ITEM_MASK
  115. length_format = type_and_length_format & _ITEM_LENGTH_MASK
  116. if item_type == _ITEM_NULL:
  117. data = data[1:]
  118. else:
  119. if length_format == _ITEM_LENGTH_8:
  120. if len(data) - 1 < 1:
  121. raise DecodeError("Data underrun while decoding")
  122. length = data[1]
  123. data = data[2:]
  124. elif length_format == _ITEM_LENGTH_16:
  125. if len(data) - 1 < 2:
  126. raise DecodeError("Data underrun while decoding")
  127. length = struct.unpack(">H", data[1:3])[0]
  128. data = data[3:]
  129. elif length_format == _ITEM_LENGTH_32:
  130. if len(data) - 1 < 4:
  131. raise DecodeError("Data underrun while decoding")
  132. length = struct.unpack(">I", data[1:5])[0]
  133. data = data[5:]
  134. if len(data) < length:
  135. raise DecodeError("Data underrun while decoding")
  136. item = data[0:length]
  137. data = data[length:]
  138. if item_type == _ITEM_DATA:
  139. value = item
  140. elif item_type == _ITEM_HASH:
  141. value = _decode_hash(item)
  142. elif item_type == _ITEM_LIST:
  143. value = _decode_array(item)
  144. elif item_type == _ITEM_NULL:
  145. value = None
  146. else:
  147. raise DecodeError("Unknown item type in decode: %02x" % item_type)
  148. return (value, data)
  149. def _decode_hash(data):
  150. ret = {}
  151. while len(data) > 0:
  152. tag, data = _decode_tag(data)
  153. value, data = _decode_item(data)
  154. ret[tag] = value
  155. return ret
  156. def _decode_array(data):
  157. ret = []
  158. while len(data) > 0:
  159. value, data = _decode_item(data)
  160. ret.append(value)
  161. return ret
  162. if __name__ == "__main__":
  163. import doctest
  164. doctest.testmod()