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()
    }
}