From: Björn Schuberg Date: Sat, 14 Feb 2015 11:54:31 +0000 (+0100) Subject: vici: Add vici python protocol handler X-Git-Tag: 5.3.0rc1~28^2~26 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=b26e1428638a309acb7a3744c831082e32085d39;p=thirdparty%2Fstrongswan.git vici: Add vici python protocol handler --- diff --git a/src/libcharon/plugins/vici/python/.gitignore b/src/libcharon/plugins/vici/python/.gitignore new file mode 100644 index 0000000000..0d20b6487c --- /dev/null +++ b/src/libcharon/plugins/vici/python/.gitignore @@ -0,0 +1 @@ +*.pyc diff --git a/src/libcharon/plugins/vici/python/vici/__init__.py b/src/libcharon/plugins/vici/python/vici/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/src/libcharon/plugins/vici/python/vici/exception.py b/src/libcharon/plugins/vici/python/vici/exception.py new file mode 100644 index 0000000000..25d73490b4 --- /dev/null +++ b/src/libcharon/plugins/vici/python/vici/exception.py @@ -0,0 +1,4 @@ +"""Exception types that may be thrown by this library.""" + +class DeserializationException(Exception): + """Encountered an unexpected byte sequence or missing element type.""" \ No newline at end of file diff --git a/src/libcharon/plugins/vici/python/vici/protocol.py b/src/libcharon/plugins/vici/python/vici/protocol.py new file mode 100644 index 0000000000..fe4e5d7b50 --- /dev/null +++ b/src/libcharon/plugins/vici/python/vici/protocol.py @@ -0,0 +1,194 @@ +import io +import socket +import struct + +from collections import namedtuple + +from .exception import DeserializationException + + +class Transport(object): + HEADER_LENGTH = 4 + MAX_SEGMENT = 512 * 1024 + + def __init__(self, address="/var/run/charon.vici"): + self.address = address + self.socket = socket.socket(socket.AF_UNIX) + self.socket.connect(address) + + def send(self, packet): + self.socket.sendall(struct.pack("!I", len(packet)) + packet) + + def receive(self): + raw_length = self.socket.recv(self.HEADER_LENGTH) + length, = struct.unpack("!I", raw_length) + payload = self.socket.recv(length) + return payload + + def close(self): + self.socket.shutdown(socket.SHUT_RDWR) + self.socket.close() + + +class Packet(object): + CMD_REQUEST = 0 # Named request message + CMD_RESPONSE = 1 # Unnamed response message for a request + CMD_UNKNOWN = 2 # Unnamed response if requested command is unknown + EVENT_REGISTER = 3 # Named event registration request + EVENT_UNREGISTER = 4 # Named event de-registration request + EVENT_CONFIRM = 5 # Unnamed confirmation for event (de-)registration + EVENT_UNKNOWN = 6 # Unnamed response if event (de-)registration failed + EVENT = 7 # Named event message + + ParsedPacket = namedtuple( + "ParsedPacket", + ["response_type", "payload"] + ) + + ParsedEventPacket = namedtuple( + "ParsedEventPacket", + ["response_type", "event_type", "payload"] + ) + + @classmethod + def _named_request(cls, request_type, request, message=None): + payload = struct.pack("!BB", request_type, len(request)) + request + if message is not None: + return payload + message + else: + return payload + + @classmethod + def request(cls, command, message=None): + return cls._named_request(cls.CMD_REQUEST, command, message) + + @classmethod + def register_event(cls, event_type): + return cls._named_request(cls.EVENT_REGISTER, event_type) + + @classmethod + def unregister_event(cls, event_type): + return cls._named_request(cls.EVENT_UNREGISTER, event_type) + + @classmethod + def parse(cls, packet): + stream = FiniteStream(packet) + response_type, = struct.unpack("!B", stream.read(1)) + + if response_type == cls.EVENT: + length, = struct.unpack("!B", stream.read(1)) + event_type = stream.read(length) + return cls.ParsedEventPacket(response_type, event_type, stream) + else: + return cls.ParsedPacket(response_type, stream) + + +class Message(object): + SECTION_START = 1 # Begin a new section having a name + SECTION_END = 2 # End a previously started section + KEY_VALUE = 3 # Define a value for a named key in the section + LIST_START = 4 # Begin a named list for list items + LIST_ITEM = 5 # Define an unnamed item value in the current list + LIST_END = 6 # End a previously started list + + @classmethod + def serialize(cls, message): + def encode_named_type(marker, name): + name = str(name) + return struct.pack("!BB", marker, len(name)) + name + + def encode_blob(value): + value = str(value) + return struct.pack("!H", len(value)) + value + + def serialize_list(lst): + segment = str() + for item in lst: + segment += struct.pack("!B", cls.LIST_ITEM) + encode_blob(item) + return segment + + def serialize_dict(d): + segment = str() + for key, value in d.iteritems(): + if isinstance(value, dict): + segment += ( + encode_named_type(cls.SECTION_START, key) + + serialize_dict(value) + + struct.pack("!B", cls.SECTION_END) + ) + elif isinstance(value, list): + segment += ( + encode_named_type(cls.LIST_START, key) + + serialize_list(value) + + struct.pack("!B", cls.LIST_END) + ) + else: + segment += ( + encode_named_type(cls.KEY_VALUE, key) + + encode_blob(value) + ) + return segment + + return serialize_dict(message) + + @classmethod + def deserialize(cls, stream): + def decode_named_type(stream): + length, = struct.unpack("!B", stream.read(1)) + return stream.read(length) + + def decode_blob(stream): + length, = struct.unpack("!H", stream.read(2)) + return stream.read(length) + + def decode_list_item(stream): + marker, = struct.unpack("!B", stream.read(1)) + while marker == cls.LIST_ITEM: + yield decode_blob(stream) + marker, = struct.unpack("!B", stream.read(1)) + + if marker != cls.LIST_END: + raise DeserializationException( + "Expected end of list at {pos}".format(pos=stream.tell()) + ) + + section = {} + section_stack = [] + while stream.has_more(): + element_type, = struct.unpack("!B", stream.read(1)) + if element_type == cls.SECTION_START: + section_name = decode_named_type(stream) + new_section = {} + section[section_name] = new_section + section_stack.append(section) + section = new_section + + elif element_type == cls.LIST_START: + list_name = decode_named_type(stream) + section[list_name] = [item for item in decode_list_item(stream)] + + elif element_type == cls.KEY_VALUE: + key = decode_named_type(stream) + section[key] = decode_blob(stream) + + elif element_type == cls.SECTION_END: + if len(section_stack): + section = section_stack.pop() + else: + raise DeserializationException( + "Unexpected end of section at {pos}".format( + pos=stream.tell() + ) + ) + + if len(section_stack): + raise DeserializationException("Expected end of section") + return section + + +class FiniteStream(io.BytesIO): + def __len__(self): + return len(self.getvalue()) + + def has_more(self): + return self.tell() < len(self) \ No newline at end of file