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 = usize::try_from(token_len).map_err(|_| Error::BufferTooShort {
76		need: usize::MAX,
77		have: packet.len(),
78	})?;
79
80	if cursor + token_len > packet.len() {
81		return Err(Error::BufferTooShort {
82			need: cursor + token_len,
83			have: packet.len(),
84		});
85	}
86	let token = &packet[cursor..cursor + token_len];
87	let cursor = cursor + token_len;
88
89	let (remaining_len, varint_len) =
90		read_varint(packet.get(cursor..).ok_or(Error::BufferTooShort {
91			need: cursor + 1,
92			have: packet.len(),
93		})?)?;
94	let cursor = cursor + varint_len;
95	let remaining_len = usize::try_from(remaining_len).map_err(|_| Error::BufferTooShort {
96		need: usize::MAX,
97		have: packet.len(),
98	})?;
99
100	if cursor + remaining_len > packet.len() {
101		return Err(Error::BufferTooShort {
102			need: cursor + remaining_len,
103			have: packet.len(),
104		});
105	}
106
107	let header_bytes = &packet[..cursor];
108	let payload = &packet[cursor..cursor + remaining_len];
109
110	Ok(InitialHeader {
111		version,
112		dcid,
113		scid,
114		token,
115		payload,
116		header_bytes,
117		first_byte,
118	})
119}
120
121/// Extract the Destination Connection ID from a QUIC Long Header packet
122/// without performing a full parse.
123///
124/// Returns `None` when the buffer is too short or the CID length field is
125/// zero or exceeds 20.
126#[must_use]
127pub fn peek_long_header_dcid(packet: &[u8]) -> Option<&[u8]> {
128	if packet.len() < 6 {
129		return None;
130	}
131	let dcid_len = packet[5] as usize;
132	if dcid_len == 0 || dcid_len > 20 {
133		return None;
134	}
135	packet.get(6..6 + dcid_len)
136}
137
138/// Extract the Destination Connection ID from a QUIC Short Header packet.
139///
140/// Short headers do not carry an explicit CID length, so the caller must
141/// supply the expected `cid_len`. Returns `None` when the buffer is too
142/// short.
143#[must_use]
144pub fn peek_short_header_dcid(packet: &[u8], cid_len: usize) -> Option<&[u8]> {
145	packet.get(1..1 + cid_len)
146}
147
148fn read_cid(packet: &[u8], offset: usize) -> Result<(&[u8], usize), Error> {
149	let &cid_len_byte = packet.get(offset).ok_or(Error::BufferTooShort {
150		need: offset + 1,
151		have: packet.len(),
152	})?;
153	if cid_len_byte > 20 {
154		return Err(Error::InvalidCidLength(cid_len_byte));
155	}
156	let cid_len = cid_len_byte as usize;
157	let start = offset + 1;
158	let end = start + cid_len;
159	if end > packet.len() {
160		return Err(Error::BufferTooShort {
161			need: end,
162			have: packet.len(),
163		});
164	}
165	Ok((&packet[start..end], end))
166}