1use crate::request::normalise_name;
10
11pub const MAX_HELLO: usize = 64 * 1024;
15
16const RECORD_HEADER: usize = 5;
17const MAX_RECORD: usize = 16 * 1024 + 2048;
18const CONTENT_HANDSHAKE: u8 = 22;
19const HANDSHAKE_CLIENT_HELLO: u8 = 1;
20const EXT_SERVER_NAME: u16 = 0;
21const NAME_TYPE_HOST: u8 = 0;
22const EXT_ECH: u16 = 0xfe0d;
25const EXT_ECH_DRAFTS: core::ops::RangeInclusive<u16> = 0xfe08..=0xfe0c;
30const EXT_ESNI: u16 = 0xffce;
32
33#[derive(Debug, Clone, PartialEq, Eq)]
35pub enum Hello {
36 Incomplete,
38 ServerName(Option<String>),
40 Encrypted(Option<String>),
43}
44
45#[derive(Debug, Clone, Copy, PartialEq, Eq, thiserror::Error)]
47#[non_exhaustive]
48pub enum HelloError {
49 #[error("the tunnel does not start with a TLS handshake")]
51 NotTls,
52 #[error("malformed TLS ClientHello")]
54 Malformed,
55 #[error("TLS ClientHello larger than {MAX_HELLO} bytes")]
57 TooLarge,
58}
59
60pub fn server_name(buf: &[u8]) -> Result<Hello, HelloError> {
62 let mut handshake = Vec::new();
63 let mut pos = 0;
64 loop {
65 let Some(&[content, major, _, len_hi, len_lo]) = buf.get(pos..pos + RECORD_HEADER) else {
66 return Ok(Hello::Incomplete);
67 };
68 if content != CONTENT_HANDSHAKE || major != 3 {
69 return Err(HelloError::NotTls);
70 }
71 let len = usize::from(u16::from_be_bytes([len_hi, len_lo]));
72 if len == 0 || len > MAX_RECORD {
73 return Err(HelloError::Malformed);
74 }
75 let start = pos + RECORD_HEADER;
76 let Some(fragment) = buf.get(start..start + len) else {
77 return Ok(Hello::Incomplete);
78 };
79 handshake.extend_from_slice(fragment);
80 pos = start + len;
81
82 if let Some(&[kind, len0, len1, len2]) = handshake.get(..4) {
83 if kind != HANDSHAKE_CLIENT_HELLO {
84 return Err(HelloError::Malformed);
85 }
86 let body_len = usize::from(len0) << 16 | usize::from(len1) << 8 | usize::from(len2);
87 if body_len > MAX_HELLO {
88 return Err(HelloError::TooLarge);
89 }
90 if let Some(body) = handshake.get(4..4 + body_len) {
91 return client_hello_sni(body).map(|(name, encrypted)| {
92 if encrypted {
93 Hello::Encrypted(name)
94 } else {
95 Hello::ServerName(name)
96 }
97 });
98 }
99 }
100 }
101}
102
103fn client_hello_sni(body: &[u8]) -> Result<(Option<String>, bool), HelloError> {
105 let mut r = Reader(body);
106 r.take(2 + 32)?; let session_id_len = r.u8()?;
108 r.take(session_id_len.into())?;
109 let suites_len = r.u16()?;
110 r.take(suites_len.into())?;
111 let compression_len = r.u8()?;
112 r.take(compression_len.into())?;
113 if r.0.is_empty() {
114 return Ok((None, false));
115 }
116 let ext_len = r.u16()?;
117 let mut exts = Reader(r.take(ext_len.into())?);
118 r.end()?;
119 let mut name = None;
120 let mut encrypted = false;
121 while !exts.0.is_empty() {
122 let kind = exts.u16()?;
123 let len = exts.u16()?;
124 let data = exts.take(len.into())?;
125 if kind == EXT_SERVER_NAME {
126 if name.is_some() {
127 return Err(HelloError::Malformed);
128 }
129 name = Some(server_name_list(data)?);
130 }
131 encrypted |= kind == EXT_ECH || kind == EXT_ESNI || EXT_ECH_DRAFTS.contains(&kind);
132 }
133 Ok((name.flatten(), encrypted))
134}
135
136fn server_name_list(data: &[u8]) -> Result<Option<String>, HelloError> {
138 let mut r = Reader(data);
139 let list_len = r.u16()?;
140 let mut list = Reader(r.take(list_len.into())?);
141 r.end()?;
142 let mut host = None;
143 while !list.0.is_empty() {
144 let kind = list.u8()?;
145 let len = list.u16()?;
146 let value = list.take(len.into())?;
147 if kind != NAME_TYPE_HOST {
148 continue;
149 }
150 if host.is_some() {
151 return Err(HelloError::Malformed);
152 }
153 let name = std::str::from_utf8(value)
156 .ok()
157 .and_then(normalise_name)
158 .ok_or(HelloError::Malformed)?;
159 host = Some(name);
160 }
161 Ok(host)
162}
163
164struct Reader<'a>(&'a [u8]);
166
167impl<'a> Reader<'a> {
168 fn take(&mut self, n: usize) -> Result<&'a [u8], HelloError> {
169 let (head, rest) = self.0.split_at_checked(n).ok_or(HelloError::Malformed)?;
170 self.0 = rest;
171 Ok(head)
172 }
173
174 fn array<const N: usize>(&mut self) -> Result<[u8; N], HelloError> {
175 let (head, rest) = self.0.split_first_chunk().ok_or(HelloError::Malformed)?;
176 self.0 = rest;
177 Ok(*head)
178 }
179
180 fn u8(&mut self) -> Result<u8, HelloError> {
181 let [b] = self.array()?;
182 Ok(b)
183 }
184
185 fn u16(&mut self) -> Result<u16, HelloError> {
186 self.array().map(u16::from_be_bytes)
187 }
188
189 fn end(&self) -> Result<(), HelloError> {
190 if self.0.is_empty() {
191 Ok(())
192 } else {
193 Err(HelloError::Malformed)
194 }
195 }
196}
197
198#[cfg(test)]
199mod tests;