1use super::extensions::TlsExtensionsIter;
4use super::handshake::TlsHandshake;
5use super::record::TlsRecord;
6
7#[derive(Debug, Clone)]
8pub struct TlsClientHello<'a> { buf: &'a [u8], client_hello_start: usize }
9
10impl<'a> TlsClientHello<'a> {
11 pub fn parse(buf: &'a [u8]) -> Option<Self> {
13 let record = TlsRecord::parse(buf)?;
14 let handshake = TlsHandshake::parse(record.fragment())?;
15 if handshake.handshake_type() != 1 { return None; } let body = handshake.body();
17 if body.len() < 38 { return None; } Some(Self { buf, client_hello_start: body.as_ptr() as usize - buf.as_ptr() as usize })
19 }
20
21 fn body(&self) -> &'a [u8] { &self.buf[self.client_hello_start..] }
22
23 pub fn client_version(&self) -> u16 {
24 let b = self.body();
25 u16::from_be_bytes([b[0], b[1]])
26 }
27
28 pub fn random(&self) -> Option<&[u8; 32]> {
29 self.body().get(2..34)?.try_into().ok()
30 }
31
32 pub fn session_id(&self) -> &[u8] {
33 let b = self.body();
34 let len = b[34] as usize;
35 let end = (35 + len).min(b.len());
36 &b[35..end]
37 }
38
39 pub fn cipher_suites(&self) -> &[u16] {
40 let b = self.body();
41 let pos = 35 + b[34] as usize; if pos + 2 > b.len() { return &[]; }
43 let len = u16::from_be_bytes([b[pos], b[pos+1]]) as usize;
44 let data = &b[pos+2..];
45 let count = (len / 2).min(data.len() / 2);
46 unsafe { std::slice::from_raw_parts(data.as_ptr() as *const u16, count) }
48 }
49
50 pub fn compression_methods(&self) -> &[u8] {
51 let b = self.body();
52 let mut pos = 35 + b[34] as usize;
53 if pos + 2 > b.len() { return &[]; }
54 let cs_len = u16::from_be_bytes([b[pos], b[pos+1]]) as usize;
55 pos += 2 + cs_len;
56 if pos + 1 > b.len() { return &[]; }
57 let cm_len = b[pos] as usize;
58 let end = (pos + 1 + cm_len).min(b.len());
59 &b[pos+1..end]
60 }
61
62 pub fn extensions(&self) -> TlsExtensionsIter<'a> {
64 let b = self.body();
65 let mut pos = 35 + b[34] as usize;
66 if pos + 2 > b.len() { return TlsExtensionsIter::new(&[]); }
67 let cs_len = u16::from_be_bytes([b[pos], b[pos+1]]) as usize;
68 pos += 2 + cs_len;
69 if pos + 1 > b.len() { return TlsExtensionsIter::new(&[]); }
70 let cm_len = b[pos] as usize;
71 pos += 1 + cm_len;
72 if pos + 2 > b.len() { return TlsExtensionsIter::new(&[]); }
73 let ext_len = u16::from_be_bytes([b[pos], b[pos+1]]) as usize;
74 let end = (pos + 2 + ext_len).min(b.len());
75 TlsExtensionsIter::new(&b[pos+2..end])
76 }
77
78 pub fn server_name(&self) -> Option<&str> {
80 for ext in self.extensions() {
81 if ext.extension_type == 0x0000 && ext.data.len() >= 5 {
82 let list_len = u16::from_be_bytes([ext.data[0], ext.data[1]]) as usize;
84 if list_len + 2 <= ext.data.len() {
85 let entry = &ext.data[2..2 + list_len];
86 if entry.len() >= 3 && entry[0] == 0 { let name_len = u16::from_be_bytes([entry[1], entry[2]]) as usize;
88 if 3 + name_len <= entry.len() {
89 return std::str::from_utf8(&entry[3..3 + name_len]).ok();
90 }
91 }
92 }
93 }
94 }
95 None
96 }
97
98 pub fn supported_versions(&self) -> Option<Vec<u16>> {
100 for ext in self.extensions() {
101 if ext.extension_type == 0x002b && !ext.data.is_empty() {
102 let len = ext.data[0] as usize;
103 if len < ext.data.len() {
104 let versions: Vec<u16> = ext.data[1..1+len]
105 .chunks_exact(2)
106 .map(|c| u16::from_be_bytes([c[0], c[1]]))
107 .collect();
108 return Some(versions);
109 }
110 }
111 }
112 None
113 }
114}
115
116#[cfg(test)]
117mod tests {
118 use super::*;
119
120 fn tls12_sample() -> Vec<u8> {
122 let ch = [
123 0x03u8, 0x03, ];
126 let mut data = vec![0x16, 0x03, 0x03, 0x00, 0x00]; let mut ch_body = Vec::new();
128 ch_body.extend_from_slice(&ch);
129 ch_body.extend_from_slice(&[0xAAu8; 32]); ch_body.push(0); ch_body.extend_from_slice(&[0x00u8, 0x04]); ch_body.extend_from_slice(&0xC02Bu16.to_be_bytes()); ch_body.extend_from_slice(&0x009Cu16.to_be_bytes()); ch_body.push(1); ch_body.push(0); let sn = b"example.com";
137 let sn_len = sn.len();
139 let sn_ext_len = (sn_len + 5) as u16;
140 let sn_list_len = (sn_len + 3) as u16;
141 let mut ext_data = vec![0x00u8, 0x00]; ext_data.extend_from_slice(&sn_ext_len.to_be_bytes());
143 ext_data.extend_from_slice(&sn_list_len.to_be_bytes());
144 ext_data.push(0x00); ext_data.extend_from_slice(&(sn_len as u16).to_be_bytes());
146 ext_data.extend_from_slice(sn);
147 ch_body.extend_from_slice(&(ext_data.len() as u16).to_be_bytes());
149 ch_body.extend_from_slice(&ext_data);
150 let handshake_len = ch_body.len();
152 data.extend_from_slice(&[0x01]); data.extend_from_slice(&(handshake_len as u32).to_be_bytes()[1..]); data.extend_from_slice(&ch_body);
155 let rec_len = data.len() - 5;
157 data[3] = (rec_len >> 8) as u8;
158 data[4] = rec_len as u8;
159 data
160 }
161
162 #[test]
163 fn parse_tls12_client_hello() {
164 let data = tls12_sample();
165 let ch = TlsClientHello::parse(&data).unwrap();
166 assert_eq!(ch.client_version(), 0x0303);
167 assert_eq!(ch.cipher_suites().len(), 2);
168 assert_eq!(ch.session_id().len(), 0);
169 assert_eq!(ch.server_name(), Some("example.com"));
170 assert!(ch.supported_versions().is_none());
171 }
172
173 #[test]
174 fn parse_tls13_client_hello() {
175 let data = &[
177 0x16, 0x03, 0x01, 0x00, 0x44, 0x01, 0x00, 0x00, 0x40, 0x03, 0x03, 0xAA,0xAA,0xAA,0xAA,0xAA,0xAA,0xAA,0xAA,0xAA,0xAA,0xAA,0xAA,0xAA,0xAA,0xAA,0xAA,
182 0xAA,0xAA,0xAA,0xAA,0xAA,0xAA,0xAA,0xAA,0xAA,0xAA,0xAA,0xAA,0xAA,0xAA,0xAA,0xAA,
183 0x00, 0x00, 0x02, 0x13, 0x01, 0x01, 0x00, 0x00, 0x07, 0x00, 0x2b, 0x00, 0x03, 0x02, 0x03, 0x04, ];
189 let ch = TlsClientHello::parse(data).unwrap();
190 assert!(ch.server_name().is_none());
191 let sv = ch.supported_versions().unwrap();
192 assert_eq!(sv, vec![0x0304]);
193 }
194
195 #[test]
196 fn parse_too_short() {
197 assert!(TlsClientHello::parse(&[]).is_none());
198 assert!(TlsClientHello::parse(&[0x16, 0x03, 0x03, 0x00, 0x00]).is_none());
199 }
200}