/src/libcharon/plugins/vici/python/vici/protocol.py

https://gitlab.com/freedmpure/strongswan · Python · 203 lines · 164 code · 38 blank · 1 comment · 22 complexity · 8787e9ba3d65707c0ae8b629189da5c0 MD5 · raw file

  1. import io
  2. import socket
  3. import struct
  4. from collections import namedtuple
  5. from collections import OrderedDict
  6. from .compat import iteritems
  7. from .exception import DeserializationException
  8. class Transport(object):
  9. HEADER_LENGTH = 4
  10. MAX_SEGMENT = 512 * 1024
  11. def __init__(self, sock):
  12. self.socket = sock
  13. def send(self, packet):
  14. self.socket.sendall(struct.pack("!I", len(packet)) + packet)
  15. def receive(self):
  16. raw_length = self._recvall(self.HEADER_LENGTH)
  17. length, = struct.unpack("!I", raw_length)
  18. payload = self._recvall(length)
  19. return payload
  20. def close(self):
  21. self.socket.shutdown(socket.SHUT_RDWR)
  22. self.socket.close()
  23. def _recvall(self, count):
  24. """Ensure to read count bytes from the socket"""
  25. data = b""
  26. while len(data) < count:
  27. data += self.socket.recv(count - len(data))
  28. return data
  29. class Packet(object):
  30. CMD_REQUEST = 0 # Named request message
  31. CMD_RESPONSE = 1 # Unnamed response message for a request
  32. CMD_UNKNOWN = 2 # Unnamed response if requested command is unknown
  33. EVENT_REGISTER = 3 # Named event registration request
  34. EVENT_UNREGISTER = 4 # Named event de-registration request
  35. EVENT_CONFIRM = 5 # Unnamed confirmation for event (de-)registration
  36. EVENT_UNKNOWN = 6 # Unnamed response if event (de-)registration failed
  37. EVENT = 7 # Named event message
  38. ParsedPacket = namedtuple(
  39. "ParsedPacket",
  40. ["response_type", "payload"]
  41. )
  42. ParsedEventPacket = namedtuple(
  43. "ParsedEventPacket",
  44. ["response_type", "event_type", "payload"]
  45. )
  46. @classmethod
  47. def _named_request(cls, request_type, request, message=None):
  48. request = request.encode()
  49. payload = struct.pack("!BB", request_type, len(request)) + request
  50. if message is not None:
  51. return payload + message
  52. else:
  53. return payload
  54. @classmethod
  55. def request(cls, command, message=None):
  56. return cls._named_request(cls.CMD_REQUEST, command, message)
  57. @classmethod
  58. def register_event(cls, event_type):
  59. return cls._named_request(cls.EVENT_REGISTER, event_type)
  60. @classmethod
  61. def unregister_event(cls, event_type):
  62. return cls._named_request(cls.EVENT_UNREGISTER, event_type)
  63. @classmethod
  64. def parse(cls, packet):
  65. stream = FiniteStream(packet)
  66. response_type, = struct.unpack("!B", stream.read(1))
  67. if response_type == cls.EVENT:
  68. length, = struct.unpack("!B", stream.read(1))
  69. event_type = stream.read(length)
  70. return cls.ParsedEventPacket(response_type, event_type, stream)
  71. else:
  72. return cls.ParsedPacket(response_type, stream)
  73. class Message(object):
  74. SECTION_START = 1 # Begin a new section having a name
  75. SECTION_END = 2 # End a previously started section
  76. KEY_VALUE = 3 # Define a value for a named key in the section
  77. LIST_START = 4 # Begin a named list for list items
  78. LIST_ITEM = 5 # Define an unnamed item value in the current list
  79. LIST_END = 6 # End a previously started list
  80. @classmethod
  81. def serialize(cls, message):
  82. def encode_named_type(marker, name):
  83. name = name.encode()
  84. return struct.pack("!BB", marker, len(name)) + name
  85. def encode_blob(value):
  86. if not isinstance(value, bytes):
  87. value = str(value).encode()
  88. return struct.pack("!H", len(value)) + value
  89. def serialize_list(lst):
  90. segment = bytes()
  91. for item in lst:
  92. segment += struct.pack("!B", cls.LIST_ITEM) + encode_blob(item)
  93. return segment
  94. def serialize_dict(d):
  95. segment = bytes()
  96. for key, value in iteritems(d):
  97. if isinstance(value, dict):
  98. segment += (
  99. encode_named_type(cls.SECTION_START, key)
  100. + serialize_dict(value)
  101. + struct.pack("!B", cls.SECTION_END)
  102. )
  103. elif isinstance(value, list):
  104. segment += (
  105. encode_named_type(cls.LIST_START, key)
  106. + serialize_list(value)
  107. + struct.pack("!B", cls.LIST_END)
  108. )
  109. else:
  110. segment += (
  111. encode_named_type(cls.KEY_VALUE, key)
  112. + encode_blob(value)
  113. )
  114. return segment
  115. return serialize_dict(message)
  116. @classmethod
  117. def deserialize(cls, stream):
  118. def decode_named_type(stream):
  119. length, = struct.unpack("!B", stream.read(1))
  120. return stream.read(length).decode()
  121. def decode_blob(stream):
  122. length, = struct.unpack("!H", stream.read(2))
  123. return stream.read(length)
  124. def decode_list_item(stream):
  125. marker, = struct.unpack("!B", stream.read(1))
  126. while marker == cls.LIST_ITEM:
  127. yield decode_blob(stream)
  128. marker, = struct.unpack("!B", stream.read(1))
  129. if marker != cls.LIST_END:
  130. raise DeserializationException(
  131. "Expected end of list at {pos}".format(pos=stream.tell())
  132. )
  133. section = OrderedDict()
  134. section_stack = []
  135. while stream.has_more():
  136. element_type, = struct.unpack("!B", stream.read(1))
  137. if element_type == cls.SECTION_START:
  138. section_name = decode_named_type(stream)
  139. new_section = OrderedDict()
  140. section[section_name] = new_section
  141. section_stack.append(section)
  142. section = new_section
  143. elif element_type == cls.LIST_START:
  144. list_name = decode_named_type(stream)
  145. section[list_name] = [item for item in decode_list_item(stream)]
  146. elif element_type == cls.KEY_VALUE:
  147. key = decode_named_type(stream)
  148. section[key] = decode_blob(stream)
  149. elif element_type == cls.SECTION_END:
  150. if len(section_stack):
  151. section = section_stack.pop()
  152. else:
  153. raise DeserializationException(
  154. "Unexpected end of section at {pos}".format(
  155. pos=stream.tell()
  156. )
  157. )
  158. if len(section_stack):
  159. raise DeserializationException("Expected end of section")
  160. return section
  161. class FiniteStream(io.BytesIO):
  162. def __len__(self):
  163. return len(self.getvalue())
  164. def has_more(self):
  165. return self.tell() < len(self)