1use crate::layer::field::FieldError;
7use crate::layer::field::MacAddress;
8
9#[derive(Debug, Clone, PartialEq)]
11pub enum EdnsOption {
12 NSID(Vec<u8>),
14
15 DAU(Vec<u8>),
17
18 DHU(Vec<u8>),
20
21 N3U(Vec<u8>),
23
24 ClientSubnet {
26 family: u16, source_prefix: u8,
28 scope_prefix: u8,
29 address: Vec<u8>,
30 },
31
32 Cookie {
34 client: Vec<u8>, server: Vec<u8>, },
37
38 ExtendedDnsError { info_code: u16, extra_text: String },
40
41 Owner {
43 version: u8,
44 seq: u8,
45 primary_mac: MacAddress,
46 },
47
48 Unknown { code: u16, data: Vec<u8> },
50}
51
52pub mod option_code {
54 pub const LLQ: u16 = 1;
55 pub const UL: u16 = 2;
56 pub const NSID: u16 = 3;
57 pub const OWNER: u16 = 4;
58 pub const DAU: u16 = 5;
59 pub const DHU: u16 = 6;
60 pub const N3U: u16 = 7;
61 pub const CLIENT_SUBNET: u16 = 8;
62 pub const COOKIE: u16 = 10;
63 pub const TCP_KEEPALIVE: u16 = 11;
64 pub const PADDING: u16 = 12;
65 pub const CHAIN: u16 = 13;
66 pub const KEY_TAG: u16 = 14;
67 pub const EXTENDED_DNS_ERROR: u16 = 15;
68}
69
70pub mod ede_code {
72 pub const OTHER: u16 = 0;
73 pub const UNSUPPORTED_DNSKEY_ALGORITHM: u16 = 1;
74 pub const UNSUPPORTED_DS_DIGEST_TYPE: u16 = 2;
75 pub const STALE_ANSWER: u16 = 3;
76 pub const FORGED_ANSWER: u16 = 4;
77 pub const DNSSEC_INDETERMINATE: u16 = 5;
78 pub const DNSSEC_BOGUS: u16 = 6;
79 pub const SIGNATURE_EXPIRED: u16 = 7;
80 pub const SIGNATURE_NOT_YET_VALID: u16 = 8;
81 pub const DNSKEY_MISSING: u16 = 9;
82 pub const RRSIGS_MISSING: u16 = 10;
83 pub const NO_ZONE_KEY_BIT_SET: u16 = 11;
84 pub const NSEC_MISSING: u16 = 12;
85 pub const CACHED_ERROR: u16 = 13;
86 pub const NOT_READY: u16 = 14;
87 pub const BLOCKED: u16 = 15;
88 pub const CENSORED: u16 = 16;
89 pub const FILTERED: u16 = 17;
90 pub const PROHIBITED: u16 = 18;
91 pub const STALE_NXDOMAIN_ANSWER: u16 = 19;
92 pub const NOT_AUTHORITATIVE: u16 = 20;
93 pub const NOT_SUPPORTED: u16 = 21;
94 pub const NO_REACHABLE_AUTHORITY: u16 = 22;
95 pub const NETWORK_ERROR: u16 = 23;
96 pub const INVALID_DATA: u16 = 24;
97}
98
99impl EdnsOption {
100 pub fn parse(code: u16, data: &[u8]) -> Result<Self, FieldError> {
103 match code {
104 option_code::NSID => Ok(EdnsOption::NSID(data.to_vec())),
105
106 option_code::DAU => Ok(EdnsOption::DAU(data.to_vec())),
107
108 option_code::DHU => Ok(EdnsOption::DHU(data.to_vec())),
109
110 option_code::N3U => Ok(EdnsOption::N3U(data.to_vec())),
111
112 option_code::CLIENT_SUBNET => {
113 if data.len() < 4 {
114 return Err(FieldError::BufferTooShort {
115 offset: 0,
116 need: 4,
117 have: data.len(),
118 });
119 }
120 let family = u16::from_be_bytes([data[0], data[1]]);
121 let source_prefix = data[2];
122 let scope_prefix = data[3];
123 let address = data[4..].to_vec();
124 Ok(EdnsOption::ClientSubnet {
125 family,
126 source_prefix,
127 scope_prefix,
128 address,
129 })
130 },
131
132 option_code::COOKIE => {
133 if data.len() < 8 {
134 return Err(FieldError::BufferTooShort {
135 offset: 0,
136 need: 8,
137 have: data.len(),
138 });
139 }
140 let client = data[..8].to_vec();
141 let server = if data.len() > 8 {
142 data[8..].to_vec()
143 } else {
144 Vec::new()
145 };
146 Ok(EdnsOption::Cookie { client, server })
147 },
148
149 option_code::EXTENDED_DNS_ERROR => {
150 if data.len() < 2 {
151 return Err(FieldError::BufferTooShort {
152 offset: 0,
153 need: 2,
154 have: data.len(),
155 });
156 }
157 let info_code = u16::from_be_bytes([data[0], data[1]]);
158 let extra_text = if data.len() > 2 {
159 String::from_utf8_lossy(&data[2..]).into_owned()
160 } else {
161 String::new()
162 };
163 Ok(EdnsOption::ExtendedDnsError {
164 info_code,
165 extra_text,
166 })
167 },
168
169 option_code::OWNER => {
170 if data.len() < 8 {
171 return Err(FieldError::BufferTooShort {
172 offset: 0,
173 need: 8,
174 have: data.len(),
175 });
176 }
177 let version = data[0];
178 let seq = data[1];
179 let primary_mac =
180 MacAddress::new([data[2], data[3], data[4], data[5], data[6], data[7]]);
181 Ok(EdnsOption::Owner {
182 version,
183 seq,
184 primary_mac,
185 })
186 },
187
188 _ => Ok(EdnsOption::Unknown {
189 code,
190 data: data.to_vec(),
191 }),
192 }
193 }
194
195 #[must_use]
197 pub fn code(&self) -> u16 {
198 match self {
199 EdnsOption::NSID(_) => option_code::NSID,
200 EdnsOption::DAU(_) => option_code::DAU,
201 EdnsOption::DHU(_) => option_code::DHU,
202 EdnsOption::N3U(_) => option_code::N3U,
203 EdnsOption::ClientSubnet { .. } => option_code::CLIENT_SUBNET,
204 EdnsOption::Cookie { .. } => option_code::COOKIE,
205 EdnsOption::ExtendedDnsError { .. } => option_code::EXTENDED_DNS_ERROR,
206 EdnsOption::Owner { .. } => option_code::OWNER,
207 EdnsOption::Unknown { code, .. } => *code,
208 }
209 }
210
211 #[must_use]
213 pub fn build_data(&self) -> Vec<u8> {
214 match self {
215 EdnsOption::NSID(data) => data.clone(),
216 EdnsOption::DAU(data) => data.clone(),
217 EdnsOption::DHU(data) => data.clone(),
218 EdnsOption::N3U(data) => data.clone(),
219 EdnsOption::ClientSubnet {
220 family,
221 source_prefix,
222 scope_prefix,
223 address,
224 } => {
225 let mut out = Vec::with_capacity(4 + address.len());
226 out.extend_from_slice(&family.to_be_bytes());
227 out.push(*source_prefix);
228 out.push(*scope_prefix);
229 out.extend_from_slice(address);
230 out
231 },
232 EdnsOption::Cookie { client, server } => {
233 let mut out = Vec::with_capacity(client.len() + server.len());
234 out.extend_from_slice(client);
235 out.extend_from_slice(server);
236 out
237 },
238 EdnsOption::ExtendedDnsError {
239 info_code,
240 extra_text,
241 } => {
242 let mut out = Vec::with_capacity(2 + extra_text.len());
243 out.extend_from_slice(&info_code.to_be_bytes());
244 out.extend_from_slice(extra_text.as_bytes());
245 out
246 },
247 EdnsOption::Owner {
248 version,
249 seq,
250 primary_mac,
251 } => {
252 let mut out = vec![*version, *seq];
253 out.extend_from_slice(primary_mac.as_bytes());
254 out
255 },
256 EdnsOption::Unknown { data, .. } => data.clone(),
257 }
258 }
259
260 #[must_use]
262 pub fn build(&self) -> Vec<u8> {
263 let data = self.build_data();
264 let mut out = Vec::with_capacity(4 + data.len());
265 out.extend_from_slice(&self.code().to_be_bytes());
266 out.extend_from_slice(&(data.len() as u16).to_be_bytes());
267 out.extend_from_slice(&data);
268 out
269 }
270
271 pub fn parse_all(data: &[u8]) -> Result<Vec<Self>, FieldError> {
273 let mut options = Vec::new();
274 let mut pos = 0;
275
276 while pos < data.len() {
277 if pos + 4 > data.len() {
278 return Err(FieldError::BufferTooShort {
279 offset: pos,
280 need: 4,
281 have: data.len(),
282 });
283 }
284 let code = u16::from_be_bytes([data[pos], data[pos + 1]]);
285 let opt_len = u16::from_be_bytes([data[pos + 2], data[pos + 3]]) as usize;
286 pos += 4;
287
288 if pos + opt_len > data.len() {
289 return Err(FieldError::BufferTooShort {
290 offset: pos,
291 need: opt_len,
292 have: data.len() - pos,
293 });
294 }
295
296 let option = Self::parse(code, &data[pos..pos + opt_len])?;
297 options.push(option);
298 pos += opt_len;
299 }
300
301 Ok(options)
302 }
303
304 #[must_use]
306 pub fn summary(&self) -> String {
307 match self {
308 EdnsOption::NSID(data) => format!("NSID: {:?}", String::from_utf8_lossy(data)),
309 EdnsOption::DAU(algs) => format!("DAU: {algs:?}"),
310 EdnsOption::DHU(algs) => format!("DHU: {algs:?}"),
311 EdnsOption::N3U(algs) => format!("N3U: {algs:?}"),
312 EdnsOption::ClientSubnet {
313 family,
314 source_prefix,
315 scope_prefix,
316 ..
317 } => {
318 format!(
319 "ClientSubnet: family={family} source=/{source_prefix} scope=/{scope_prefix}"
320 )
321 },
322 EdnsOption::Cookie { client, server } => {
323 format!("Cookie: client={} server={}", hex(client), hex(server))
324 },
325 EdnsOption::ExtendedDnsError {
326 info_code,
327 extra_text,
328 } => {
329 format!("EDE: code={info_code} text={extra_text:?}")
330 },
331 EdnsOption::Owner {
332 version,
333 seq,
334 primary_mac,
335 } => {
336 format!("Owner: v={version} seq={seq} mac={primary_mac}")
337 },
338 EdnsOption::Unknown { code, data } => {
339 format!("Option({}): {} bytes", code, data.len())
340 },
341 }
342 }
343}
344
345fn hex(data: &[u8]) -> String {
346 data.iter().map(|b| format!("{b:02x}")).collect()
347}
348
349#[cfg(test)]
350mod tests {
351 use super::*;
352
353 #[test]
354 fn test_client_subnet_parse() {
355 let data = vec![0x00, 0x01, 24, 0, 192, 168, 1];
357 let opt = EdnsOption::parse(option_code::CLIENT_SUBNET, &data).unwrap();
358 match opt {
359 EdnsOption::ClientSubnet {
360 family,
361 source_prefix,
362 scope_prefix,
363 address,
364 } => {
365 assert_eq!(family, 1);
366 assert_eq!(source_prefix, 24);
367 assert_eq!(scope_prefix, 0);
368 assert_eq!(address, vec![192, 168, 1]);
369 },
370 _ => panic!("wrong variant"),
371 }
372 }
373
374 #[test]
375 fn test_client_subnet_roundtrip() {
376 let opt = EdnsOption::ClientSubnet {
377 family: 1,
378 source_prefix: 24,
379 scope_prefix: 0,
380 address: vec![192, 168, 1],
381 };
382 let built = opt.build();
383 assert_eq!(built[0..2], [0x00, 0x08]); assert_eq!(built[2..4], [0x00, 0x07]); let parsed = EdnsOption::parse(option_code::CLIENT_SUBNET, &built[4..]).unwrap();
386 assert_eq!(parsed, opt);
387 }
388
389 #[test]
390 fn test_cookie_parse() {
391 let mut data = vec![0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08]; data.extend_from_slice(&[0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18]); let opt = EdnsOption::parse(option_code::COOKIE, &data).unwrap();
394 match &opt {
395 EdnsOption::Cookie { client, server } => {
396 assert_eq!(client.len(), 8);
397 assert_eq!(server.len(), 8);
398 },
399 _ => panic!("wrong variant"),
400 }
401 }
402
403 #[test]
404 fn test_extended_dns_error_parse() {
405 let mut data = vec![0x00, 0x06]; data.extend_from_slice(b"signature verification failed");
407 let opt = EdnsOption::parse(option_code::EXTENDED_DNS_ERROR, &data).unwrap();
408 match opt {
409 EdnsOption::ExtendedDnsError {
410 info_code,
411 extra_text,
412 } => {
413 assert_eq!(info_code, 6);
414 assert_eq!(extra_text, "signature verification failed");
415 },
416 _ => panic!("wrong variant"),
417 }
418 }
419
420 #[test]
421 fn test_dau_roundtrip() {
422 let opt = EdnsOption::DAU(vec![8, 10, 13, 14]); let built = opt.build();
424 let parsed_opts = EdnsOption::parse_all(&built).unwrap();
425 assert_eq!(parsed_opts.len(), 1);
426 assert_eq!(parsed_opts[0], opt);
427 }
428
429 #[test]
430 fn test_parse_multiple_options() {
431 let opt1 = EdnsOption::DAU(vec![8, 10]);
432 let opt2 = EdnsOption::DHU(vec![1, 2]);
433 let mut data = opt1.build();
434 data.extend_from_slice(&opt2.build());
435
436 let parsed = EdnsOption::parse_all(&data).unwrap();
437 assert_eq!(parsed.len(), 2);
438 assert_eq!(parsed[0], opt1);
439 assert_eq!(parsed[1], opt2);
440 }
441
442 #[test]
443 fn test_unknown_option() {
444 let opt = EdnsOption::parse(9999, &[0x01, 0x02, 0x03]).unwrap();
445 match opt {
446 EdnsOption::Unknown { code, data } => {
447 assert_eq!(code, 9999);
448 assert_eq!(data, vec![1, 2, 3]);
449 },
450 _ => panic!("wrong variant"),
451 }
452 }
453}