diff --git a/squinnondation/squinnondation.py b/squinnondation/squinnondation.py index 5a341a3..0418168 100644 --- a/squinnondation/squinnondation.py +++ b/squinnondation/squinnondation.py @@ -48,10 +48,79 @@ class Squinnondation: while True: data, addr = squirrel.receive_raw_data() - print("received message: %s" % data) + pkt = Packet.unmarshal(data) + print("received message: %d %d %d %s" % (pkt.magic, pkt.version, pkt.body_length, pkt.body.raw_data)) + print(str(pkt.marshal())) # squirrel.send_raw_data(hazelnut, b"Hello squirrel!") +class TLV: + """ + The Tag-Length-Value contains the different type of data that can be sent. + TODO: add subclasses for each type of TLV + """ + type: int + raw_data: bytes + + def validate_data(self) -> bool: + """ + Ensure that the TLV is well-formed. + Raises a ValueError if it is not the case. + TODO: Make some tests + """ + return True + + +class Packet: + """ + A Packet is a wrapper around the + """ + magic: int + version: int + body_length: int + body: TLV + + def validate_data(self) -> bool: + """ + Ensure that the packet is well-formed. + Raises a ValueError if the packet contains bad data. + """ + if self.magic != 95: + raise ValueError("The magic code of the packet must be 95, found: {:d}".format(self.magic)) + if self.version != 0: + raise ValueError("The version of the packet is not supported: {:d}".format(self.version)) + if not (0 <= self.body_length <= 120): + raise ValueError("The body length of the packet is negative or too high. It must be between 0 and 1020," + "found: {:d}".format(self.body_length)) + return self.body.validate_data() + + @staticmethod + def unmarshal(data: bytes) -> "Packet": + """ + Read raw data and build the packet wrapper. + Raises a ValueError whenever the data is invalid. + """ + pkt = Packet() + pkt.magic = data[0] + pkt.version = data[1] + pkt.body_length = int.from_bytes(data[2:4], byteorder="big") + pkt.body = TLV() + pkt.body.raw_data = data[4:4+pkt.body_length] + + pkt.validate_data() + + return pkt + + def marshal(self) -> bytes: + """ + Compute the byte array data associated to the packet. + """ + data = bytes([self.magic, self.version]) + data += self.body_length.to_bytes(2, "big") + data += self.body.raw_data + return data + + class Hazelnut: """ A hazelnut is a connected client, with its socket.