Skip to main content

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}