huginn_net_tls/parser/
client_hello_reader.rs1use crate::error::HuginnNetTlsError;
2use crate::fingerprint::Signature;
3use crate::process::tls::parse_tls_client_hello;
4use tracing::{debug, error};
5
6pub struct TlsClientHelloReader {
26 buffer: Vec<u8>,
27 signature: Option<Signature>,
28}
29
30impl TlsClientHelloReader {
31 #[must_use]
36 pub fn new() -> Self {
37 Self { buffer: Vec::with_capacity(8192), signature: None }
38 }
39
40 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 #[must_use]
130 pub fn get_signature(&self) -> Option<&Signature> {
131 self.signature.as_ref()
132 }
133
134 #[must_use]
139 pub fn signature_parsed(&self) -> bool {
140 self.signature.is_some()
141 }
142
143 pub fn reset(&mut self) {
147 self.buffer.clear();
148 self.signature = None;
149 }
150
151 #[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}