1use itertools::Itertools;
10use std::fmt;
11use std::net::{Ipv4Addr, Ipv6Addr};
12
13use crate::{DnsOptDataType, DnsRr, DnsRrKey};
14
15#[derive(Clone, Debug, PartialEq, Eq)]
17pub struct OptParseError(String);
18
19impl fmt::Display for OptParseError {
20 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
21 write!(f, "{}", self.0)
22 }
23}
24
25impl std::error::Error for OptParseError {}
26
27#[derive(Clone, Debug, PartialEq, Eq)]
29pub enum OptValue {
30 None,
32 StrList(Vec<String>),
34 U8List(Vec<u8>),
36 U16(u16),
38 U16List(Vec<u16>),
40 U32(u32),
42 U32List(Vec<u32>),
44 InAddr4List(Vec<Ipv4Addr>),
46 InAddr6List(Vec<Ipv6Addr>),
48 Bin(Vec<u8>),
50 Name(String),
52}
53
54impl fmt::Display for OptValue {
55 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
56 match self {
57 OptValue::None => Ok(()),
58 OptValue::StrList(v) => {
59 let vals = v.iter().format(", ");
60 write!(f, "[{vals}]")
61 }
62 OptValue::U8List(v) => {
63 let vals = v.iter().format(", ");
64 write!(f, "[{vals}]")
65 }
66 OptValue::U16(v) => write!(f, "{v}"),
67 OptValue::U16List(v) => {
68 let vals = v.iter().format(", ");
69 write!(f, "[{vals}]")
70 }
71 OptValue::U32(v) => write!(f, "{v}"),
72 OptValue::U32List(v) => {
73 let vals = v.iter().format(", ");
74 write!(f, "[{vals}]")
75 }
76 OptValue::InAddr4List(v) => {
77 let vals = v.iter().format(", ");
78 write!(f, "[{vals}]")
79 }
80 OptValue::InAddr6List(v) => {
81 let vals = v.iter().format(", ");
82 write!(f, "[{vals}]")
83 }
84 OptValue::Bin(v) => {
85 for b in v {
86 write!(f, "{b:02x}")?;
87 }
88 Ok(())
89 }
90 OptValue::Name(s) => write!(f, "{s}"),
91 }
92 }
93}
94
95pub fn parse_opt_value(key: DnsRrKey, opt: u16, data: &[u8]) -> Result<OptValue, OptParseError> {
100 match DnsRr::opt_datatype(key, opt) {
101 DnsOptDataType::None => parse_none(data),
102 DnsOptDataType::StrList => parse_str_list(data).map(OptValue::StrList),
103 DnsOptDataType::U8List => Ok(OptValue::U8List(data.to_vec())),
104 DnsOptDataType::U16 => parse_u16(data).map(OptValue::U16),
105 DnsOptDataType::U16List => parse_u16_list(data).map(OptValue::U16List),
106 DnsOptDataType::U32 => parse_u32(data).map(OptValue::U32),
107 DnsOptDataType::U32List => parse_u32_list(data).map(OptValue::U32List),
108 DnsOptDataType::InAddr4List => parse_inaddr4_list(data).map(OptValue::InAddr4List),
109 DnsOptDataType::InAddr6List => parse_inaddr6_list(data).map(OptValue::InAddr6List),
110 DnsOptDataType::Bin => Ok(OptValue::Bin(data.to_vec())),
111 DnsOptDataType::Name => parse_name(data).map(OptValue::Name),
112 }
113}
114
115fn parse_none(data: &[u8]) -> Result<OptValue, OptParseError> {
116 if !data.is_empty() {
117 return Err(OptParseError(format!(
118 "expected no data, got {} bytes",
119 data.len()
120 )));
121 }
122 Ok(OptValue::None)
123}
124
125fn parse_str_list(data: &[u8]) -> Result<Vec<String>, OptParseError> {
126 let mut result = Vec::new();
127 let mut pos = 0;
128 while pos < data.len() {
129 let (buf, consumed) = crate::expand_string(data, pos)
130 .map_err(|e| OptParseError(format!("invalid string in list: {e}")))?;
131 let s = std::str::from_utf8(&buf)
132 .map_err(|e| OptParseError(format!("invalid UTF-8 in string list: {e}")))?;
133 result.push(s.to_owned());
134 pos += consumed;
135 }
136 Ok(result)
137}
138
139fn parse_u16(data: &[u8]) -> Result<u16, OptParseError> {
140 data.try_into()
141 .map_err(|_| OptParseError(format!("Expected 2 bytes, got {}", data.len())))
142 .map(u16::from_be_bytes)
143}
144
145fn parse_u16_list(data: &[u8]) -> Result<Vec<u16>, OptParseError> {
146 if !data.len().is_multiple_of(2) {
147 return Err(OptParseError(format!(
148 "u16 list length {} is not a multiple of 2",
149 data.len()
150 )));
151 }
152
153 Ok(data
154 .as_chunks::<2>()
155 .0
156 .iter()
157 .copied()
158 .map(u16::from_be_bytes)
159 .collect())
160}
161
162fn parse_u32(data: &[u8]) -> Result<u32, OptParseError> {
163 data.try_into()
164 .map_err(|_| OptParseError(format!("Expected 4 bytes, got {}", data.len())))
165 .map(u32::from_be_bytes)
166}
167
168fn parse_u32_list(data: &[u8]) -> Result<Vec<u32>, OptParseError> {
169 if !data.len().is_multiple_of(4) {
170 return Err(OptParseError(format!(
171 "u32 list length {} is not a multiple of 4",
172 data.len()
173 )));
174 }
175
176 Ok(data
177 .as_chunks::<4>()
178 .0
179 .iter()
180 .copied()
181 .map(u32::from_be_bytes)
182 .collect())
183}
184
185fn parse_inaddr4_list(data: &[u8]) -> Result<Vec<Ipv4Addr>, OptParseError> {
186 if !data.len().is_multiple_of(4) {
187 return Err(OptParseError(format!(
188 "IPv4 address list length {} is not a multiple of 4",
189 data.len()
190 )));
191 }
192
193 Ok(data
194 .as_chunks::<4>()
195 .0
196 .iter()
197 .copied()
198 .map(Ipv4Addr::from)
199 .collect())
200}
201
202fn parse_inaddr6_list(data: &[u8]) -> Result<Vec<Ipv6Addr>, OptParseError> {
203 if !data.len().is_multiple_of(16) {
204 return Err(OptParseError(format!(
205 "IPv6 address list length {} is not a multiple of 16",
206 data.len()
207 )));
208 }
209
210 Ok(data
211 .as_chunks::<16>()
212 .0
213 .iter()
214 .copied()
215 .map(Ipv6Addr::from)
216 .collect())
217}
218
219fn parse_name(data: &[u8]) -> Result<String, OptParseError> {
220 let (name, consumed) =
221 crate::expand_name(data, 0).map_err(|e| OptParseError(format!("invalid DNS name: {e}")))?;
222 if consumed != data.len() {
223 return Err(OptParseError(format!(
224 "DNS name consumed {consumed} bytes, but buffer is {} bytes",
225 data.len()
226 )));
227 }
228 Ok(name.to_owned())
229}
230
231#[cfg(test)]
232mod tests {
233 use super::*;
234
235 #[test]
236 fn parse_none_empty() {
237 assert_eq!(parse_none(&[]).unwrap(), OptValue::None);
238 }
239
240 #[test]
241 fn parse_none_nonempty() {
242 assert!(parse_none(&[0x01]).is_err());
243 }
244
245 #[test]
246 fn parse_str_list_basic() {
247 let data = [2, b'h', b'3', 2, b'h', b'2'];
249 assert_eq!(
250 parse_str_list(&data).unwrap(),
251 vec!["h3".to_string(), "h2".to_string()]
252 );
253 }
254
255 #[test]
256 fn parse_str_list_invalid_utf8() {
257 let data = [2, 0xff, 0xfe];
258 assert!(parse_str_list(&data).is_err());
259 }
260
261 #[test]
262 fn parse_str_list_truncated() {
263 let data = [5, b'h', b'i'];
265 assert!(parse_str_list(&data).is_err());
266 }
267
268 #[test]
269 fn parse_str_list_trailing_data() {
270 let data = [2, b'h', b'i', 0xff];
272 assert!(parse_str_list(&data).is_err());
273 }
274
275 #[test]
276 fn parse_u16_basic() {
277 assert_eq!(parse_u16(&[0x01, 0x00]).unwrap(), 256);
278 }
279
280 #[test]
281 fn parse_u16_bad_length() {
282 assert!(parse_u16(&[0x01]).is_err());
283 assert!(parse_u16(&[0x01, 0x00, 0x00]).is_err());
284 }
285
286 #[test]
287 fn parse_u16_list_basic() {
288 assert_eq!(
289 parse_u16_list(&[0x00, 0x01, 0x00, 0x02]).unwrap(),
290 vec![1, 2]
291 );
292 }
293
294 #[test]
295 fn parse_u16_list_bad_length() {
296 assert!(parse_u16_list(&[0x00, 0x01, 0x00]).is_err());
297 }
298
299 #[test]
300 fn parse_u32_basic() {
301 assert_eq!(parse_u32(&[0x00, 0x00, 0x01, 0x00]).unwrap(), 256);
302 }
303
304 #[test]
305 fn parse_u32_bad_length() {
306 assert!(parse_u32(&[0x00, 0x00, 0x01]).is_err());
307 assert!(parse_u32(&[0x00, 0x00, 0x01, 0x00, 0x00]).is_err());
308 }
309
310 #[test]
311 fn parse_u32_list_bad_length() {
312 assert!(parse_u32_list(&[0x00, 0x00, 0x01]).is_err());
313 }
314
315 #[test]
316 fn parse_inaddr4_list_basic() {
317 let data = [127, 0, 0, 1, 10, 0, 0, 1];
318 assert_eq!(
319 parse_inaddr4_list(&data).unwrap(),
320 vec![Ipv4Addr::LOCALHOST, Ipv4Addr::new(10, 0, 0, 1),]
321 );
322 }
323
324 #[test]
325 fn parse_inaddr4_list_bad_length() {
326 assert!(parse_inaddr4_list(&[1, 2, 3]).is_err());
327 }
328
329 #[test]
330 fn parse_inaddr6_list_basic() {
331 let mut data = [0u8; 16];
332 data[0] = 0x20;
333 data[1] = 0x01;
334 let result = parse_inaddr6_list(&data).unwrap();
335 assert_eq!(result.len(), 1);
336 assert_eq!(result[0].octets()[0], 0x20);
337 }
338
339 #[test]
340 fn parse_inaddr6_list_bad_length() {
341 assert!(parse_inaddr6_list(&[0u8; 15]).is_err());
342 }
343
344 #[test]
345 fn parse_name_basic() {
346 let data = [
348 3, b'w', b'w', b'w', 7, b'e', b'x', b'a', b'm', b'p', b'l', b'e', 3, b'c', b'o', b'm',
349 0,
350 ];
351 assert_eq!(parse_name(&data).unwrap(), "www.example.com");
352 }
353
354 #[test]
355 fn parse_name_truncated() {
356 let data = [5, b'a', b'b', 0];
358 assert!(parse_name(&data).is_err());
359 }
360
361 #[test]
362 fn parse_name_trailing_data() {
363 let data = [3, b'c', b'o', b'm', 0, 0xff];
365 assert!(parse_name(&data).is_err());
366 }
367
368 #[test]
369 fn format_opt_value_display() {
370 let val = OptValue::InAddr4List(vec![Ipv4Addr::new(1, 2, 3, 4)]);
371 assert_eq!(val.to_string(), "[1.2.3.4]");
372 }
373
374 #[test]
375 fn format_opt_value_bin() {
376 let val = OptValue::Bin(vec![0xca, 0xfe]);
377 assert_eq!(val.to_string(), "cafe");
378 }
379
380 #[test]
381 fn display_none() {
382 assert_eq!(OptValue::None.to_string(), "");
383 }
384
385 #[test]
386 fn display_str_list() {
387 let val = OptValue::StrList(vec!["h3".to_owned(), "h2".to_owned()]);
388 assert_eq!(val.to_string(), "[h3, h2]");
389 }
390
391 #[test]
392 fn display_u8_list() {
393 let val = OptValue::U8List(vec![1, 2, 3]);
394 assert_eq!(val.to_string(), "[1, 2, 3]");
395 }
396
397 #[test]
398 fn display_u16() {
399 assert_eq!(OptValue::U16(42).to_string(), "42");
400 }
401
402 #[test]
403 fn display_u16_list() {
404 let val = OptValue::U16List(vec![100, 200]);
405 assert_eq!(val.to_string(), "[100, 200]");
406 }
407
408 #[test]
409 fn display_u32() {
410 assert_eq!(OptValue::U32(123456).to_string(), "123456");
411 }
412
413 #[test]
414 fn display_u32_list() {
415 let val = OptValue::U32List(vec![1, 2]);
416 assert_eq!(val.to_string(), "[1, 2]");
417 }
418
419 #[test]
420 fn display_inaddr6_list() {
421 let val = OptValue::InAddr6List(vec![Ipv6Addr::LOCALHOST]);
422 assert_eq!(val.to_string(), "[::1]");
423 }
424
425 #[test]
426 fn display_name() {
427 let val = OptValue::Name("example.com".to_owned());
428 assert_eq!(val.to_string(), "example.com");
429 }
430
431 #[test]
432 fn display_bin_empty() {
433 assert_eq!(OptValue::Bin(vec![]).to_string(), "");
434 }
435
436 #[test]
437 fn opt_parse_error_display() {
438 let err = OptParseError("test error".to_owned());
439 assert_eq!(err.to_string(), "test error");
440 }
441
442 #[test]
443 fn opt_parse_error_is_error() {
444 let err = OptParseError("test".to_owned());
445 let _: &dyn std::error::Error = &err;
446 }
447
448 #[test]
449 fn parse_str_list_empty() {
450 assert_eq!(parse_str_list(&[]).unwrap(), Vec::<String>::new());
451 }
452
453 #[test]
454 fn parse_u16_list_empty() {
455 assert_eq!(parse_u16_list(&[]).unwrap(), Vec::<u16>::new());
456 }
457
458 #[test]
459 fn parse_u32_list_basic() {
460 assert_eq!(
461 parse_u32_list(&[0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02]).unwrap(),
462 vec![1, 2]
463 );
464 }
465
466 #[test]
467 fn parse_u32_list_empty() {
468 assert_eq!(parse_u32_list(&[]).unwrap(), Vec::<u32>::new());
469 }
470
471 #[test]
472 fn parse_inaddr4_list_empty() {
473 assert_eq!(parse_inaddr4_list(&[]).unwrap(), Vec::<Ipv4Addr>::new());
474 }
475
476 #[test]
477 fn parse_inaddr6_list_empty() {
478 assert_eq!(parse_inaddr6_list(&[]).unwrap(), Vec::<Ipv6Addr>::new());
479 }
480
481 #[test]
482 fn parse_inaddr6_list_multiple() {
483 let mut data = [0u8; 32];
484 data[15] = 1;
486 data[31] = 2;
488 let result = parse_inaddr6_list(&data).unwrap();
489 assert_eq!(
490 result,
491 vec![Ipv6Addr::LOCALHOST, Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 2)]
492 );
493 }
494
495 #[test]
499 fn parse_opt_value_none() {
500 let result = parse_opt_value(DnsRrKey::HTTPS_PARAMS, 2, &[]).unwrap();
502 assert_eq!(result, OptValue::None);
503 }
504
505 #[test]
506 fn parse_opt_value_none_rejects_data() {
507 assert!(parse_opt_value(DnsRrKey::HTTPS_PARAMS, 2, &[0x01]).is_err());
508 }
509
510 #[test]
511 fn parse_opt_value_str_list() {
512 let data = [2, b'h', b'3'];
514 let result = parse_opt_value(DnsRrKey::HTTPS_PARAMS, 1, &data).unwrap();
515 assert_eq!(result, OptValue::StrList(vec!["h3".to_owned()]));
516 }
517
518 #[test]
519 fn parse_opt_value_u16() {
520 let result = parse_opt_value(DnsRrKey::HTTPS_PARAMS, 3, &[0x01, 0xBB]).unwrap();
522 assert_eq!(result, OptValue::U16(443));
523 }
524
525 #[test]
526 fn parse_opt_value_u16_bad_length() {
527 assert!(parse_opt_value(DnsRrKey::HTTPS_PARAMS, 3, &[0x01]).is_err());
528 }
529
530 #[test]
531 fn parse_opt_value_u16_list() {
532 let result = parse_opt_value(DnsRrKey::HTTPS_PARAMS, 0, &[0x00, 0x01, 0x00, 0x03]).unwrap();
534 assert_eq!(result, OptValue::U16List(vec![1, 3]));
535 }
536
537 #[test]
538 fn parse_opt_value_inaddr4_list() {
539 let result = parse_opt_value(DnsRrKey::HTTPS_PARAMS, 4, &[127, 0, 0, 1]).unwrap();
541 assert_eq!(result, OptValue::InAddr4List(vec![Ipv4Addr::LOCALHOST]));
542 }
543
544 #[test]
545 fn parse_opt_value_inaddr4_list_bad_length() {
546 assert!(parse_opt_value(DnsRrKey::HTTPS_PARAMS, 4, &[1, 2, 3]).is_err());
547 }
548
549 #[test]
550 fn parse_opt_value_bin() {
551 let result = parse_opt_value(DnsRrKey::HTTPS_PARAMS, 5, &[0xca, 0xfe]).unwrap();
553 assert_eq!(result, OptValue::Bin(vec![0xca, 0xfe]));
554 }
555
556 #[test]
557 fn parse_opt_value_inaddr6_list() {
558 let mut data = [0u8; 16];
560 data[15] = 1;
561 let result = parse_opt_value(DnsRrKey::HTTPS_PARAMS, 6, &data).unwrap();
562 assert_eq!(result, OptValue::InAddr6List(vec![Ipv6Addr::LOCALHOST]));
563 }
564
565 #[test]
566 fn parse_opt_value_inaddr6_list_bad_length() {
567 assert!(parse_opt_value(DnsRrKey::HTTPS_PARAMS, 6, &[0u8; 15]).is_err());
568 }
569
570 #[test]
571 fn parse_opt_value_u32() {
572 let result = parse_opt_value(DnsRrKey::OPT_OPTIONS, 2, &[0x00, 0x01, 0x51, 0x80]).unwrap();
574 assert_eq!(result, OptValue::U32(86400));
575 }
576
577 #[test]
578 fn parse_opt_value_u32_bad_length() {
579 assert!(parse_opt_value(DnsRrKey::OPT_OPTIONS, 2, &[0x00, 0x01, 0x51]).is_err());
580 }
581
582 #[test]
583 fn parse_opt_value_u8_list() {
584 let result = parse_opt_value(DnsRrKey::OPT_OPTIONS, 5, &[8, 13, 14]).unwrap();
586 assert_eq!(result, OptValue::U8List(vec![8, 13, 14]));
587 }
588
589 #[test]
590 fn parse_opt_value_name() {
591 let data = [
593 7, b'e', b'x', b'a', b'm', b'p', b'l', b'e', 3, b'c', b'o', b'm', 0,
594 ];
595 let result = parse_opt_value(DnsRrKey::OPT_OPTIONS, 13, &data).unwrap();
596 assert_eq!(result, OptValue::Name("example.com".to_owned()));
597 }
598}