iris_core/protocols/stream/quic/mod.rs
1//! QUIC protocol parser.
2//!
3//! ## Remarks
4//! - [QUIC-INVARIANTS](https://datatracker.ietf.org/doc/rfc8999/)
5//! - [QUIC-RFC9000](https://datatracker.ietf.org/doc/rfc9000/) (Quic V1)
6//! Iris currently only parses Quic Long and Short Headers and does not attempt to parse TLS or HTTP/3 out of
7//! Quic packets. The Quic protocol parser makes several assumptions about the way that quic
8//! packets will behave:
9//! - Assume that the Quic version is one as listed in the QuicVersion Enum in the quic/parser.rs file
10//! - Assume that the dcid of a short header is a maximum of 20 bytes.
11//! - Assume that the packet will not try to grease the fixed bit.
12//! [QUIC-GREASE](https://www.rfc-editor.org/rfc/rfc9287.html)
13//!
14//! Additionally, there are a couple decisions made in the design of the quic parser:
15//! - The parser will not parse a short header dcid if it is not a part of a pre-identified connection
16//! - The payload bytes count is a lazy counter which does not try to exclude tokens for encryption,
17//! which is a process that happens in wireshark.
18/*
19NICE-TO-HAVE: support parsing the tls out of the initial quic packet setup
20NICE-TO-HAVE support dns over quic
21NICE-TO-HAVE: support HTTP/3
22*/
23pub(crate) mod parser;
24
25use std::collections::{BTreeMap, HashSet};
26
27pub use self::header::{QuicLongHeader, QuicShortHeader};
28use crypto::Open;
29use frame::QuicFrame;
30use header::LongHeaderPacketType;
31use serde::Serialize;
32
33use super::tls::Tls;
34pub(crate) mod crypto;
35pub(crate) mod frame;
36pub(crate) mod header;
37
38/// Errors Thrown throughout QUIC parsing. These are handled by Iris and used to skip packets.
39#[derive(Debug)]
40pub enum QuicError {
41 FixedBitNotSet,
42 PacketTooShort,
43 UnknownVersion,
44 ShortHeader,
45 UnknowLongHeaderPacketType,
46 NoLongHeader,
47 UnsupportedVarLen,
48 InvalidDataIndices,
49 CryptoFail,
50 FailedHeaderProtection,
51 UnknownFrameType,
52 TlsParseFail,
53}
54
55/// Parsed Quic connections
56#[derive(Debug, Serialize)]
57pub struct QuicConn {
58 // All packets associated with the connection
59 pub packets: Vec<QuicPacket>,
60
61 // All cids, both src and destination, seen in Long Header packets
62 pub cids: HashSet<String>,
63
64 // Parsed TLS messsages
65 pub tls: Tls,
66
67 // Crypto needed to decrypt initial packets sent by client
68 pub client_opener: Option<Open>,
69
70 // Crypto needed to decrypt initial packets sent by server
71 pub server_opener: Option<Open>,
72
73 // Sparse cryptostream chunks (offset -> bytes) reassembled across
74 // packets. CRYPTO frames within a single packet can arrive at
75 // non-contiguous offsets and interleaved with PING/PADDING (e.g. Chrome
76 // QUIC), so a flat Vec doesn't work — we have to wait until [0..N] is
77 // contiguous before feeding it to the TLS parser.
78 #[serde(skip_serializing)]
79 pub client_crypto: BTreeMap<u64, Vec<u8>>,
80
81 #[serde(skip_serializing)]
82 pub server_crypto: BTreeMap<u64, Vec<u8>>,
83
84 // Number of bytes already fed into the TLS parser from each direction.
85 #[serde(skip_serializing)]
86 pub client_consumed: u64,
87
88 #[serde(skip_serializing)]
89 pub server_consumed: u64,
90}
91
92/// Parsed Quic Packet contents
93#[derive(Debug, Serialize)]
94pub struct QuicPacket {
95 /// Quic Short header
96 pub short_header: Option<QuicShortHeader>,
97
98 /// Quic Long header
99 pub long_header: Option<QuicLongHeader>,
100
101 /// The number of bytes contained in the estimated payload
102 pub payload_bytes_count: Option<u64>,
103
104 pub frames: Option<Vec<QuicFrame>>,
105}
106
107impl QuicPacket {
108 /// Returns the header type of the Quic packet (ie. "long" or "short")
109 pub fn header_type(&self) -> &str {
110 match &self.long_header {
111 Some(_) => "long",
112 None => match &self.short_header {
113 Some(_) => "short",
114 None => "",
115 },
116 }
117 }
118
119 /// Returns the packet type of the Quic packet
120 pub fn packet_type(&self) -> Result<LongHeaderPacketType, QuicError> {
121 match &self.long_header {
122 Some(long_header) => Ok(long_header.packet_type),
123 None => Err(QuicError::NoLongHeader),
124 }
125 }
126
127 /// Returns the version of the Quic packet
128 pub fn version(&self) -> u32 {
129 match &self.long_header {
130 Some(long_header) => long_header.version,
131 None => 0,
132 }
133 }
134
135 /// Returns the destination connection ID of the Quic packet or an empty string if it does not exist
136 pub fn dcid(&self) -> &str {
137 match &self.long_header {
138 Some(long_header) => {
139 if long_header.dcid_len > 0 {
140 &long_header.dcid
141 } else {
142 ""
143 }
144 }
145 None => {
146 if let Some(short_header) = &self.short_header {
147 short_header.dcid.as_deref().unwrap_or("")
148 } else {
149 ""
150 }
151 }
152 }
153 }
154
155 /// Returns the source connection ID of the Quic packet or an empty string if it does not exist
156 pub fn scid(&self) -> &str {
157 match &self.long_header {
158 Some(long_header) if long_header.scid_len > 0 => &long_header.scid,
159 Some(_) => "",
160 None => "",
161 }
162 }
163
164 /// Returns the number of bytes in the payload of the Quic packet
165 pub fn payload_bytes_count(&self) -> u64 {
166 self.payload_bytes_count.unwrap_or_default()
167 }
168}