Skip to main content

huginn_net_tls/parser/
client_hello_reader.rs

1use crate::error::HuginnNetTlsError;
2use crate::fingerprint::Signature;
3use crate::process::tls::parse_tls_client_hello;
4use tracing::{debug, error};
5
6/// TLS ClientHello reader with incremental parsing support
7///
8/// This struct manages reading and parsing TLS ClientHello messages incrementally,
9/// handling cases where the ClientHello arrives in multiple TCP packets.
10///
11/// # Example
12/// ```no_run
13/// use huginn_net_tls::TlsClientHelloReader;
14///
15/// let mut reader = TlsClientHelloReader::new();
16///
17/// // Add bytes incrementally
18/// reader.add_bytes(&[0x16, 0x03, 0x01, 0x00, 0x4a]);
19/// reader.add_bytes(&[/* more bytes */]);
20///
21/// if let Some(signature) = reader.get_signature() {
22///     println!("Got TLS signature");
23/// }
24/// ```
25pub struct TlsClientHelloReader {
26    buffer: Vec<u8>,
27    signature: Option<Signature>,
28}
29
30impl TlsClientHelloReader {
31    /// Create a new TLS ClientHello reader
32    ///
33    /// # Returns
34    /// A new `TlsClientHelloReader` instance ready to process TLS ClientHello data
35    #[must_use]
36    pub fn new() -> Self {
37        Self { buffer: Vec::with_capacity(8192), signature: None }
38    }
39
40    /// Add bytes to the buffer and attempt to parse ClientHello
41    ///
42    /// This method handles incremental data arrival, parsing the ClientHello as soon
43    /// as enough data is available.
44    ///
45    /// # Parameters
46    /// - `data`: New bytes to add to the buffer
47    ///
48    /// # Returns
49    /// - `Ok(Some(Signature))` if ClientHello was successfully parsed
50    /// - `Ok(None)` if more data is needed or signature already parsed
51    /// - `Err(HuginnNetTlsError)` if parsing fails
52    pub fn add_bytes(&mut self, data: &[u8]) -> Result<Option<Signature>, HuginnNetTlsError> {
53        if self.signature.is_some() {
54            debug!("Signature already parsed, skipping new bytes.");
55            return Ok(None);
56        }
57
58        self.buffer.extend_from_slice(data);
59
60        if self.buffer.len() < 5 {
61            debug!("Not enough bytes for TLS record header (have {}, need 5)", self.buffer.len());
62            return Ok(None);
63        }
64
65        let content_type = self.buffer[0];
66        let record_len = u16::from_be_bytes([self.buffer[3], self.buffer[4]]) as usize;
67        let needed = record_len.saturating_add(5);
68
69        if content_type != 0x16 {
70            debug!(
71                "First byte is not TLS Handshake (0x16), got 0x{:02x}. Might be continuation data.",
72                content_type
73            );
74            return Ok(None);
75        }
76
77        if self.buffer.len() < needed {
78            debug!(
79                "Incomplete TLS record: have {} bytes, need {} bytes. Accumulating...",
80                self.buffer.len(),
81                needed
82            );
83            return Ok(None);
84        }
85
86        if needed > 64 * 1024 {
87            error!("TLS record too large ({} bytes), resetting buffer.", needed);
88            self.reset();
89            return Err(HuginnNetTlsError::Parse("TLS record too large".to_string()));
90        }
91
92        debug!(
93            "Complete TLS record detected: record_len={}, total_available={}",
94            record_len,
95            self.buffer.len()
96        );
97
98        match parse_tls_client_hello(&self.buffer[..needed]) {
99            Ok(signature) => {
100                debug!("Successfully parsed TLS ClientHello from reassembled buffer");
101                self.signature = Some(signature.clone());
102                self.buffer.drain(..needed);
103                Ok(Some(signature))
104            }
105            Err(HuginnNetTlsError::NotClientHello) => {
106                debug!("TLS record is not a ClientHello (likely ServerHello or Application Data), ignoring");
107                self.reset();
108                Ok(None)
109            }
110            Err(e) => {
111                error!("Failed to parse TLS ClientHello from reassembled buffer: {:?}", e);
112                debug!(
113                    "Buffer (first 200 bytes): {:02x?}",
114                    self.buffer
115                        .get(0..200.min(needed))
116                        .map(|s| s.to_vec())
117                        .unwrap_or_default()
118                );
119                Err(e)
120            }
121        }
122    }
123
124    /// Get the parsed signature if available
125    ///
126    /// # Returns
127    /// - `Some(Signature)` if signature has been parsed
128    /// - `None` if signature not yet available
129    #[must_use]
130    pub fn get_signature(&self) -> Option<&Signature> {
131        self.signature.as_ref()
132    }
133
134    /// Check if signature has been parsed
135    ///
136    /// # Returns
137    /// `true` if signature is available, `false` otherwise
138    #[must_use]
139    pub fn signature_parsed(&self) -> bool {
140        self.signature.is_some()
141    }
142
143    /// Reset the reader to process a new ClientHello
144    ///
145    /// Clears the buffer and resets parsing state, allowing the reader to be reused.
146    pub fn reset(&mut self) {
147        self.buffer.clear();
148        self.signature = None;
149    }
150
151    /// Get the current buffer size
152    ///
153    /// Returns the number of bytes currently accumulated in the buffer.
154    #[must_use]
155    pub fn buffer_len(&self) -> usize {
156        self.buffer.len()
157    }
158}
159
160impl Default for TlsClientHelloReader {
161    fn default() -> Self {
162        Self::new()
163    }
164}