Skip to main content

quic_parser/
frame.rs

1/* src/frame.rs */
2
3use crate::error::Error;
4use crate::varint::read_varint;
5
6/// A single CRYPTO frame extracted from decrypted QUIC payload.
7#[derive(Debug, Clone, PartialEq)]
8pub struct CryptoFrame {
9	/// Byte offset within the crypto stream where this fragment begins.
10	pub offset: u64,
11	/// The raw data carried by this frame.
12	pub data: Vec<u8>,
13}
14
15/// Parse all CRYPTO frames from a decrypted Initial packet payload.
16///
17/// PADDING (0x00) and PING (0x01) frames are silently skipped. ACK frames
18/// (0x02, 0x03) are skipped by consuming their fields. Parsing stops when an
19/// unrecognised frame type is encountered or the buffer is exhausted.
20///
21/// # Errors
22///
23/// Returns [`Error::TruncatedFrame`] if a CRYPTO frame extends beyond the
24/// available data. Returns [`Error::InvalidVarint`] if a varint field is
25/// malformed.
26pub fn parse_crypto_frames(decrypted: &[u8]) -> Result<Vec<CryptoFrame>, Error> {
27	let mut cursor = 0;
28	let mut frames = Vec::new();
29
30	while cursor < decrypted.len() {
31		let (frame_type, len) = read_varint(&decrypted[cursor..])?;
32		cursor += len;
33
34		match frame_type {
35			0x06 => {
36				let (offset, off_len) =
37					read_varint(decrypted.get(cursor..).ok_or(Error::TruncatedFrame {
38						offset: cursor as u64,
39					})?)?;
40				cursor += off_len;
41
42				let (length, len_len) =
43					read_varint(decrypted.get(cursor..).ok_or(Error::TruncatedFrame {
44						offset: cursor as u64,
45					})?)?;
46				cursor += len_len;
47				let length = usize::try_from(length).map_err(|_| Error::TruncatedFrame {
48					offset: cursor as u64,
49				})?;
50
51				if cursor + length > decrypted.len() {
52					return Err(Error::TruncatedFrame { offset });
53				}
54
55				let data = decrypted[cursor..cursor + length].to_vec();
56				frames.push(CryptoFrame { offset, data });
57				cursor += length;
58			}
59			// PADDING / PING
60			0x00 | 0x01 => {}
61			// ACK (0x02) and ACK_ECN (0x03): consume all varint fields
62			0x02 | 0x03 => {
63				cursor = skip_ack_frame(decrypted, cursor, frame_type == 0x03)?;
64			}
65			_ => break,
66		}
67	}
68
69	Ok(frames)
70}
71
72/// Skip over an ACK frame by consuming all its varint fields.
73///
74/// Layout (RFC 9000 Section 19.3):
75///   Largest Acknowledged (i), ACK Delay (i), ACK Range Count (i),
76///   First ACK Range (i), { Gap (i), ACK Range Length (i) } * count,
77///   [ECN Counts: ECT0 (i), ECT1 (i), ECN-CE (i)]  — only for type 0x03.
78fn skip_ack_frame(buf: &[u8], mut cursor: usize, has_ecn: bool) -> Result<usize, Error> {
79	let trunc = |pos: usize| Error::TruncatedFrame { offset: pos as u64 };
80
81	// Largest Acknowledged
82	let (_, len) = read_varint(buf.get(cursor..).ok_or_else(|| trunc(cursor))?)?;
83	cursor += len;
84	// ACK Delay
85	let (_, len) = read_varint(buf.get(cursor..).ok_or_else(|| trunc(cursor))?)?;
86	cursor += len;
87	// ACK Range Count
88	let (range_count, len) = read_varint(buf.get(cursor..).ok_or_else(|| trunc(cursor))?)?;
89	cursor += len;
90	// First ACK Range
91	let (_, len) = read_varint(buf.get(cursor..).ok_or_else(|| trunc(cursor))?)?;
92	cursor += len;
93
94	// Each additional ACK Range: Gap (i) + ACK Range Length (i)
95	for _ in 0..range_count {
96		let (_, len) = read_varint(buf.get(cursor..).ok_or_else(|| trunc(cursor))?)?;
97		cursor += len;
98		let (_, len) = read_varint(buf.get(cursor..).ok_or_else(|| trunc(cursor))?)?;
99		cursor += len;
100	}
101
102	// ECN Counts (only for ACK_ECN, type 0x03)
103	if has_ecn {
104		for _ in 0..3 {
105			let (_, len) = read_varint(buf.get(cursor..).ok_or_else(|| trunc(cursor))?)?;
106			cursor += len;
107		}
108	}
109
110	Ok(cursor)
111}
112
113/// Reassemble CRYPTO frames into a contiguous byte stream.
114///
115/// Frames are sorted by offset and concatenated in order. Only contiguous
116/// fragments starting from offset zero are included; gaps cause the
117/// reassembly to stop at the gap boundary.
118#[must_use]
119pub fn reassemble_crypto_stream(frames: &[CryptoFrame]) -> Vec<u8> {
120	let mut sorted: Vec<&CryptoFrame> = frames.iter().collect();
121	sorted.sort_by_key(|f| f.offset);
122
123	let mut stream = Vec::new();
124	let mut next_offset: u64 = 0;
125
126	for frame in sorted {
127		if frame.offset == next_offset {
128			stream.extend_from_slice(&frame.data);
129			next_offset += frame.data.len() as u64;
130		} else if frame.offset < next_offset {
131			let skip = (next_offset - frame.offset) as usize;
132			if skip < frame.data.len() {
133				stream.extend_from_slice(&frame.data[skip..]);
134				next_offset += (frame.data.len() - skip) as u64;
135			}
136		} else {
137			break;
138		}
139	}
140
141	stream
142}