A Python port of the Invisible Internet Project (I2P)
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)