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::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 MissingCryptoFrames,
54}
55
56/// Parsed Quic connections
57#[derive(Debug, Serialize)]
58pub struct QuicConn {
59 // All packets associated with the connection
60 pub packets: Vec<QuicPacket>,
61
62 // All cids, both src and destination, seen in Long Header packets
63 pub cids: HashSet<String>,
64
65 // Parsed TLS messsages
66 pub tls: Tls,
67
68 // Crypto needed to decrypt initial packets sent by client
69 pub client_opener: Option<Open>,
70
71 // Crypto needed to decrypt initial packets sent by server
72 pub server_opener: Option<Open>,
73
74 // Client buffer for multi-packet TLS messages
75 #[serde(skip_serializing)]
76 pub client_buffer: Vec<u8>,
77
78 // Server buffer for multi-packet TLS messages
79 #[serde(skip_serializing)]
80 pub server_buffer: Vec<u8>,
81}
82
83/// Parsed Quic Packet contents
84#[derive(Debug, Serialize)]
85pub struct QuicPacket {
86 /// Quic Short header
87 pub short_header: Option<QuicShortHeader>,
88
89 /// Quic Long header
90 pub long_header: Option<QuicLongHeader>,
91
92 /// The number of bytes contained in the estimated payload
93 pub payload_bytes_count: Option<u64>,
94
95 pub frames: Option<Vec<QuicFrame>>,
96}
97
98impl QuicPacket {
99 /// Returns the header type of the Quic packet (ie. "long" or "short")
100 pub fn header_type(&self) -> &str {
101 match &self.long_header {
102 Some(_) => "long",
103 None => match &self.short_header {
104 Some(_) => "short",
105 None => "",
106 },
107 }
108 }
109
110 /// Returns the packet type of the Quic packet
111 pub fn packet_type(&self) -> Result<LongHeaderPacketType, QuicError> {
112 match &self.long_header {
113 Some(long_header) => Ok(long_header.packet_type),
114 None => Err(QuicError::NoLongHeader),
115 }
116 }
117
118 /// Returns the version of the Quic packet
119 pub fn version(&self) -> u32 {
120 match &self.long_header {
121 Some(long_header) => long_header.version,
122 None => 0,
123 }
124 }
125
126 /// Returns the destination connection ID of the Quic packet or an empty string if it does not exist
127 pub fn dcid(&self) -> &str {
128 match &self.long_header {
129 Some(long_header) => {
130 if long_header.dcid_len > 0 {
131 &long_header.dcid
132 } else {
133 ""
134 }
135 }
136 None => {
137 if let Some(short_header) = &self.short_header {
138 short_header.dcid.as_deref().unwrap_or("")
139 } else {
140 ""
141 }
142 }
143 }
144 }
145
146 /// Returns the source connection ID of the Quic packet or an empty string if it does not exist
147 pub fn scid(&self) -> &str {
148 match &self.long_header {
149 Some(long_header) if long_header.scid_len > 0 => &long_header.scid,
150 Some(_) => "",
151 None => "",
152 }
153 }
154
155 /// Returns the number of bytes in the payload of the Quic packet
156 pub fn payload_bytes_count(&self) -> u64 {
157 self.payload_bytes_count.unwrap_or_default()
158 }
159}