A Python port of the Invisible Internet Project (I2P)
at main 74 lines 2.5 kB view raw
1"""Stream packet types — wire format for I2P streaming protocol.""" 2 3import struct 4 5 6class Flags: 7 """Stream packet flag constants.""" 8 SYNCHRONIZE = 0x0001 9 CLOSE = 0x0002 10 RESET = 0x0004 11 SIGNATURE_INCLUDED = 0x0008 12 SIGNATURE_REQUESTED = 0x0010 13 FROM_INCLUDED = 0x0020 14 DELAY_REQUESTED = 0x0040 15 MAX_PACKET_SIZE_INCLUDED = 0x0080 16 PROFILE_INTERACTIVE = 0x0100 17 ECHO = 0x0200 18 NO_ACK = 0x0400 19 20 21class StreamPacket: 22 """I2P streaming protocol packet. 23 24 Wire format: 25 send_id(4) + recv_id(4) + seq_num(4) + ack_through(4) + 26 nack_count(1) + nack_list(nack_count*4) + resend_delay(1) + 27 flags(2) + option_size(2) + options + payload 28 """ 29 30 def __init__(self, send_id: int, recv_id: int, seq_num: int, 31 ack_through: int, flags: int, payload: bytes = b"", 32 nacks: list[int] | None = None, resend_delay: int = 0, 33 options: bytes = b""): 34 self.send_id = send_id 35 self.recv_id = recv_id 36 self.seq_num = seq_num 37 self.ack_through = ack_through 38 self.nacks = nacks or [] 39 self.resend_delay = resend_delay 40 self.flags = flags 41 self.options = options 42 self.payload = payload 43 44 def to_bytes(self) -> bytes: 45 parts = [ 46 struct.pack("!IIII", self.send_id, self.recv_id, 47 self.seq_num, self.ack_through), 48 struct.pack("!B", len(self.nacks)), 49 ] 50 for nack in self.nacks: 51 parts.append(struct.pack("!I", nack)) 52 parts.append(struct.pack("!BHH", self.resend_delay, 53 self.flags, len(self.options))) 54 parts.append(self.options) 55 parts.append(self.payload) 56 return b"".join(parts) 57 58 @classmethod 59 def from_bytes(cls, data: bytes) -> "StreamPacket": 60 send_id, recv_id, seq_num, ack_through = struct.unpack("!IIII", data[:16]) 61 offset = 16 62 nack_count = data[offset] 63 offset += 1 64 nacks = [] 65 for _ in range(nack_count): 66 nacks.append(struct.unpack("!I", data[offset:offset + 4])[0]) 67 offset += 4 68 resend_delay, flags, option_size = struct.unpack("!BHH", data[offset:offset + 5]) 69 offset += 5 70 options = data[offset:offset + option_size] 71 offset += option_size 72 payload = data[offset:] 73 return cls(send_id, recv_id, seq_num, ack_through, flags, 74 payload, nacks, resend_delay, options)