Skip to main content

quic_parser/
header.rs

1/* src/header.rs */
2
3use crate::error::Error;
4use crate::varint::read_varint;
5
6/// Parsed QUIC Initial packet header with zero-copy references into the
7/// original packet buffer.
8#[derive(Debug, Clone, PartialEq)]
9pub struct InitialHeader<'a> {
10	/// QUIC version field (e.g. `0x00000001` for v1, `0x6b3343cf` for v2).
11	pub version: u32,
12	/// Destination Connection ID bytes.
13	pub dcid: &'a [u8],
14	/// Source Connection ID bytes.
15	pub scid: &'a [u8],
16	/// Token bytes. An empty slice indicates no token was present.
17	pub token: &'a [u8],
18	/// Encrypted payload including the protected packet number.
19	pub payload: &'a [u8],
20	/// Raw header bytes from the first byte up to (but not including) the
21	/// payload. Required when constructing the AEAD additional authenticated
22	/// data during decryption.
23	pub header_bytes: &'a [u8],
24	/// The first byte of the packet, still under header protection.
25	pub first_byte: u8,
26}
27
28/// Parse a QUIC Long Header Initial packet from a raw datagram.
29///
30/// Only the header fields are extracted; no decryption is performed. The
31/// returned [`InitialHeader`] borrows directly from `packet`.
32///
33/// # Errors
34///
35/// Returns an error when the packet is too short, is not a long-header
36/// Initial packet, or contains a connection ID length exceeding 20 bytes.
37pub fn parse_initial(packet: &[u8]) -> Result<InitialHeader<'_>, Error> {
38	if packet.len() < 7 {
39		return Err(Error::BufferTooShort {
40			need: 7,
41			have: packet.len(),
42		});
43	}
44
45	let first_byte = packet[0];
46
47	if (first_byte & 0x80) == 0 {
48		return Err(Error::NotLongHeader);
49	}
50
51	let packet_type = (first_byte & 0x30) >> 4;
52	if packet_type != 0 {
53		return Err(Error::NotInitialPacket(packet_type));
54	}
55
56	let mut cursor = 1;
57
58	let version = u32::from_be_bytes([
59		packet[cursor],
60		packet[cursor + 1],
61		packet[cursor + 2],
62		packet[cursor + 3],
63	]);
64	cursor += 4;
65
66	let (dcid, cursor) = read_cid(packet, cursor)?;
67	let (scid, cursor) = read_cid(packet, cursor)?;
68
69	let (token_len, varint_len) =
70		read_varint(packet.get(cursor..).ok_or(Error::BufferTooShort {
71			need: cursor + 1,
72			have: packet.len(),
73		})?)?;
74	let cursor = cursor + varint_len;
75	let token_len = token_len as usize;
76
77	if cursor + token_len > packet.len() {
78		return Err(Error::BufferTooShort {
79			need: cursor + token_len,
80			have: packet.len(),
81		});
82	}
83	let token = &packet[cursor..cursor + token_len];
84	let cursor = cursor + token_len;
85
86	let (remaining_len, varint_len) =
87		read_varint(packet.get(cursor..).ok_or(Error::BufferTooShort {
88			need: cursor + 1,
89			have: packet.len(),
90		})?)?;
91	let cursor = cursor + varint_len;
92	let remaining_len = remaining_len as usize;
93
94	if cursor + remaining_len > packet.len() {
95		return Err(Error::BufferTooShort {
96			need: cursor + remaining_len,
97			have: packet.len(),
98		});
99	}
100
101	let header_bytes = &packet[..cursor];
102	let payload = &packet[cursor..cursor + remaining_len];
103
104	Ok(InitialHeader {
105		version,
106		dcid,
107		scid,
108		token,
109		payload,
110		header_bytes,
111		first_byte,
112	})
113}
114
115/// Extract the Destination Connection ID from a QUIC Long Header packet
116/// without performing a full parse.
117///
118/// Returns `None` when the buffer is too short or the CID length field is
119/// zero or exceeds 20.
120#[must_use]
121pub fn peek_long_header_dcid(packet: &[u8]) -> Option<&[u8]> {
122	if packet.len() < 6 {
123		return None;
124	}
125	let dcid_len = packet[5] as usize;
126	if dcid_len == 0 || dcid_len > 20 {
127		return None;
128	}
129	packet.get(6..6 + dcid_len)
130}
131
132/// Extract the Destination Connection ID from a QUIC Short Header packet.
133///
134/// Short headers do not carry an explicit CID length, so the caller must
135/// supply the expected `cid_len`. Returns `None` when the buffer is too
136/// short.
137#[must_use]
138pub fn peek_short_header_dcid(packet: &[u8], cid_len: usize) -> Option<&[u8]> {
139	packet.get(1..1 + cid_len)
140}
141
142fn read_cid(packet: &[u8], offset: usize) -> Result<(&[u8], usize), Error> {
143	let &cid_len_byte = packet.get(offset).ok_or(Error::BufferTooShort {
144		need: offset + 1,
145		have: packet.len(),
146	})?;
147	if cid_len_byte > 20 {
148		return Err(Error::InvalidCidLength(cid_len_byte));
149	}
150	let cid_len = cid_len_byte as usize;
151	let start = offset + 1;
152	let end = start + cid_len;
153	if end > packet.len() {
154		return Err(Error::BufferTooShort {
155			need: end,
156			have: packet.len(),
157		});
158	}
159	Ok((&packet[start..end], end))
160}