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 = length as usize;
48
49				if cursor + length > decrypted.len() {
50					return Err(Error::TruncatedFrame { offset });
51				}
52
53				let data = decrypted[cursor..cursor + length].to_vec();
54				frames.push(CryptoFrame { offset, data });
55				cursor += length;
56			}
57			0x00 | 0x01 => {}
58			_ => break,
59		}
60	}
61
62	Ok(frames)
63}
64
65/// Reassemble CRYPTO frames into a contiguous byte stream.
66///
67/// Frames are sorted by offset and concatenated in order. Only contiguous
68/// fragments starting from offset zero are included; gaps cause the
69/// reassembly to stop at the gap boundary.
70#[must_use]
71pub fn reassemble_crypto_stream(frames: &[CryptoFrame]) -> Vec<u8> {
72	let mut sorted: Vec<&CryptoFrame> = frames.iter().collect();
73	sorted.sort_by_key(|f| f.offset);
74
75	let mut stream = Vec::new();
76	let mut next_offset: u64 = 0;
77
78	for frame in sorted {
79		if frame.offset == next_offset {
80			stream.extend_from_slice(&frame.data);
81			next_offset += frame.data.len() as u64;
82		} else if frame.offset < next_offset {
83			let skip = (next_offset - frame.offset) as usize;
84			if skip < frame.data.len() {
85				stream.extend_from_slice(&frame.data[skip..]);
86				next_offset += (frame.data.len() - skip) as u64;
87			}
88		} else {
89			break;
90		}
91	}
92
93	stream
94}