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