xref: /webrtc/mdns/src/message/message_test.rs (revision d7232c2b)
1 // Silence warning on complex types:
2 #![allow(clippy::type_complexity)]
3 
4 use super::builder::*;
5 use super::header::*;
6 use super::name::*;
7 use super::parser::*;
8 use super::question::*;
9 use super::resource::{
10     a::*, aaaa::*, cname::*, mx::*, ns::*, opt::*, ptr::*, soa::*, srv::*, txt::*, *,
11 };
12 use super::*;
13 use crate::error::*;
14 
15 use std::collections::HashMap;
16 
17 fn small_test_msg() -> Result<Message> {
18     let name = Name::new("example.com.")?;
19     Ok(Message {
20         header: Header {
21             response: true,
22             authoritative: true,
23             ..Default::default()
24         },
25         questions: vec![Question {
26             name: name.clone(),
27             typ: DnsType::A,
28             class: DNSCLASS_INET,
29         }],
30         answers: vec![Resource {
31             header: ResourceHeader {
32                 name: name.clone(),
33                 typ: DnsType::A,
34                 class: DNSCLASS_INET,
35                 ..Default::default()
36             },
37             body: Some(Box::new(AResource { a: [127, 0, 0, 1] })),
38         }],
39         authorities: vec![Resource {
40             header: ResourceHeader {
41                 name: name.clone(),
42                 typ: DnsType::A,
43                 class: DNSCLASS_INET,
44                 ..Default::default()
45             },
46             body: Some(Box::new(AResource { a: [127, 0, 0, 1] })),
47         }],
48         additionals: vec![Resource {
49             header: ResourceHeader {
50                 name,
51                 typ: DnsType::A,
52                 class: DNSCLASS_INET,
53                 ..Default::default()
54             },
55             body: Some(Box::new(AResource { a: [127, 0, 0, 1] })),
56         }],
57     })
58 }
59 
60 fn large_test_msg() -> Result<Message> {
61     let name = Name::new("foo.bar.example.com.")?;
62     Ok(Message {
63         header: Header {
64             response: true,
65             authoritative: true,
66             ..Default::default()
67         },
68         questions: vec![Question {
69             name: name.clone(),
70             typ: DnsType::A,
71             class: DNSCLASS_INET,
72         }],
73         answers: vec![
74             Resource {
75                 header: ResourceHeader {
76                     name: name.clone(),
77                     typ: DnsType::A,
78                     class: DNSCLASS_INET,
79                     ..Default::default()
80                 },
81                 body: Some(Box::new(AResource { a: [127, 0, 0, 1] })),
82             },
83             Resource {
84                 header: ResourceHeader {
85                     name: name.clone(),
86                     typ: DnsType::A,
87                     class: DNSCLASS_INET,
88                     ..Default::default()
89                 },
90                 body: Some(Box::new(AResource { a: [127, 0, 0, 2] })),
91             },
92             Resource {
93                 header: ResourceHeader {
94                     name: name.clone(),
95                     typ: DnsType::Aaaa,
96                     class: DNSCLASS_INET,
97                     ..Default::default()
98                 },
99                 body: Some(Box::new(AaaaResource {
100                     aaaa: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16],
101                 })),
102             },
103             Resource {
104                 header: ResourceHeader {
105                     name: name.clone(),
106                     typ: DnsType::Cname,
107                     class: DNSCLASS_INET,
108                     ..Default::default()
109                 },
110                 body: Some(Box::new(CnameResource {
111                     cname: Name::new("alias.example.com.")?,
112                 })),
113             },
114             Resource {
115                 header: ResourceHeader {
116                     name: name.clone(),
117                     typ: DnsType::Soa,
118                     class: DNSCLASS_INET,
119                     ..Default::default()
120                 },
121                 body: Some(Box::new(SoaResource {
122                     ns: Name::new("ns1.example.com.")?,
123                     mbox: Name::new("mb.example.com.")?,
124                     serial: 1,
125                     refresh: 2,
126                     retry: 3,
127                     expire: 4,
128                     min_ttl: 5,
129                 })),
130             },
131             Resource {
132                 header: ResourceHeader {
133                     name: name.clone(),
134                     typ: DnsType::Ptr,
135                     class: DNSCLASS_INET,
136                     ..Default::default()
137                 },
138                 body: Some(Box::new(PtrResource {
139                     ptr: Name::new("ptr.example.com.")?,
140                 })),
141             },
142             Resource {
143                 header: ResourceHeader {
144                     name: name.clone(),
145                     typ: DnsType::Mx,
146                     class: DNSCLASS_INET,
147                     ..Default::default()
148                 },
149                 body: Some(Box::new(MxResource {
150                     pref: 7,
151                     mx: Name::new("mx.example.com.")?,
152                 })),
153             },
154             Resource {
155                 header: ResourceHeader {
156                     name: name.clone(),
157                     typ: DnsType::Srv,
158                     class: DNSCLASS_INET,
159                     ..Default::default()
160                 },
161                 body: Some(Box::new(SrvResource {
162                     priority: 8,
163                     weight: 9,
164                     port: 11,
165                     target: Name::new("srv.example.com.")?,
166                 })),
167             },
168         ],
169         authorities: vec![
170             Resource {
171                 header: ResourceHeader {
172                     name: name.clone(),
173                     typ: DnsType::Ns,
174                     class: DNSCLASS_INET,
175                     ..Default::default()
176                 },
177                 body: Some(Box::new(NsResource {
178                     ns: Name::new("ns1.example.com.")?,
179                 })),
180             },
181             Resource {
182                 header: ResourceHeader {
183                     name: name.clone(),
184                     typ: DnsType::Ns,
185                     class: DNSCLASS_INET,
186                     ..Default::default()
187                 },
188                 body: Some(Box::new(NsResource {
189                     ns: Name::new("ns2.example.com.")?,
190                 })),
191             },
192         ],
193         additionals: vec![
194             Resource {
195                 header: ResourceHeader {
196                     name: name.clone(),
197                     typ: DnsType::Txt,
198                     class: DNSCLASS_INET,
199                     ..Default::default()
200                 },
201                 body: Some(Box::new(TxtResource {
202                     txt: vec!["So Long, and Thanks for All the Fish".into()],
203                 })),
204             },
205             Resource {
206                 header: ResourceHeader {
207                     name,
208                     typ: DnsType::Txt,
209                     class: DNSCLASS_INET,
210                     ..Default::default()
211                 },
212                 body: Some(Box::new(TxtResource {
213                     txt: vec!["Hamster Huey and the Gooey Kablooie".into()],
214                 })),
215             },
216             Resource {
217                 header: must_edns0_resource_header(4096, 0xfe0 | (RCode::Success as u32), false)?,
218                 body: Some(Box::new(OptResource {
219                     options: vec![DnsOption {
220                         code: 10, // see RFC 7873
221                         data: vec![0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef],
222                     }],
223                 })),
224             },
225         ],
226     })
227 }
228 
229 fn must_edns0_resource_header(l: u16, extrc: u32, d: bool) -> Result<ResourceHeader> {
230     let mut h = ResourceHeader {
231         class: DNSCLASS_INET,
232         ..Default::default()
233     };
234     h.set_edns0(l, extrc, d)?;
235     Ok(h)
236 }
237 
238 #[test]
239 fn test_name_string() -> Result<()> {
240     let want = "foo";
241     let name = Name::new(want)?;
242     assert_eq!(name.to_string(), want);
243 
244     Ok(())
245 }
246 
247 #[test]
248 fn test_question_pack_unpack() -> Result<()> {
249     let want = Question {
250         name: Name::new(".")?,
251         typ: DnsType::A,
252         class: DNSCLASS_INET,
253     };
254     let buf = want.pack(vec![0; 1], &mut Some(HashMap::new()), 1)?;
255     let mut p = Parser {
256         msg: &buf,
257         header: HeaderInternal {
258             questions: 1,
259             ..Default::default()
260         },
261         section: Section::Questions,
262         off: 1,
263         ..Default::default()
264     };
265 
266     let got = p.question()?;
267     assert_eq!(
268         p.off,
269         buf.len(),
270         "unpacked different amount than packed: got = {}, want = {}",
271         p.off,
272         buf.len(),
273     );
274     assert_eq!(
275         got, want,
276         "got from Parser.Question() = {}, want = {}",
277         got, want
278     );
279 
280     Ok(())
281 }
282 
283 #[test]
284 fn test_name() -> Result<()> {
285     let tests = vec![
286         "",
287         ".",
288         "google..com",
289         "google.com",
290         "google..com.",
291         "google.com.",
292         ".google.com.",
293         "www..google.com.",
294         "www.google.com.",
295     ];
296 
297     for test in tests {
298         let name = Name::new(test)?;
299         let ns = name.to_string();
300         assert_eq!(ns, test, "got {} = {}, want = {}", name, ns, test);
301     }
302 
303     Ok(())
304 }
305 
306 #[test]
307 fn test_name_pack_unpack() -> Result<()> {
308     let tests: Vec<(&str, &str, Option<Error>)> = vec![
309         ("", "", Some(Error::ErrNonCanonicalName)),
310         (".", ".", None),
311         ("google..com", "", Some(Error::ErrNonCanonicalName)),
312         ("google.com", "", Some(Error::ErrNonCanonicalName)),
313         ("google..com.", "", Some(Error::ErrZeroSegLen)),
314         ("google.com.", "google.com.", None),
315         (".google.com.", "", Some(Error::ErrZeroSegLen)),
316         ("www..google.com.", "", Some(Error::ErrZeroSegLen)),
317         ("www.google.com.", "www.google.com.", None),
318     ];
319 
320     for (input, want, want_err) in tests {
321         let input = Name::new(input)?;
322         let result = input.pack(vec![], &mut Some(HashMap::new()), 0);
323         if let Some(want_err) = want_err {
324             if let Err(actual_err) = result {
325                 assert_eq!(actual_err, want_err);
326             } else {
327                 panic!();
328             }
329             continue;
330         } else {
331             assert!(result.is_ok());
332         }
333 
334         let buf = result.unwrap();
335 
336         let want = Name::new(want)?;
337 
338         let mut got = Name::default();
339         let n = got.unpack(&buf, 0)?;
340         assert_eq!(
341             n,
342             buf.len(),
343             "unpacked different amount than packed for {}: got = {}, want = {}",
344             input,
345             n,
346             buf.len(),
347         );
348 
349         assert_eq!(
350             got, want,
351             "unpacking packing of {}: got = {}, want = {}",
352             input, got, want
353         );
354     }
355 
356     Ok(())
357 }
358 
359 #[test]
360 fn test_incompressible_name() -> Result<()> {
361     let name = Name::new("example.com.")?;
362     let mut compression = Some(HashMap::new());
363     let buf = name.pack(vec![], &mut compression, 0)?;
364     let buf = name.pack(buf, &mut compression, 0)?;
365     let mut n1 = Name::default();
366     let off = n1.unpack_compressed(&buf, 0, false /* allowCompression */)?;
367     let mut n2 = Name::default();
368     let result = n2.unpack_compressed(&buf, off, false /* allowCompression */);
369     if let Err(err) = result {
370         assert_eq!(
371             Error::ErrCompressedSrv,
372             err,
373             "unpacking compressed incompressible name with pointers: got {}, want = {}",
374             err,
375             Error::ErrCompressedSrv
376         );
377     } else {
378         panic!();
379     }
380 
381     Ok(())
382 }
383 
384 #[test]
385 fn test_header_unpack_error() -> Result<()> {
386     let wants = vec![
387         "id",
388         "bits",
389         "questions",
390         "answers",
391         "authorities",
392         "additionals",
393     ];
394 
395     let mut buf = vec![];
396     for want in wants {
397         let mut h = HeaderInternal::default();
398         let result = h.unpack(&buf, 0);
399         assert!(result.is_err(), "{}", want);
400         buf.extend_from_slice(&[0, 0]);
401     }
402 
403     Ok(())
404 }
405 
406 #[test]
407 fn test_parser_start() -> Result<()> {
408     let mut p = Parser::default();
409     let result = p.start(&[]);
410     assert!(result.is_err());
411 
412     Ok(())
413 }
414 
415 #[test]
416 fn test_resource_not_started() -> Result<()> {
417     let tests: Vec<(&str, Box<dyn Fn(&mut Parser<'_>) -> Result<()>>)> = vec![
418         (
419             "CNAMEResource",
420             Box::new(|p: &mut Parser<'_>| -> Result<()> { p.resource_body().map(|_| ()) }),
421         ),
422         (
423             "MXResource",
424             Box::new(|p: &mut Parser<'_>| -> Result<()> { p.resource_body().map(|_| ()) }),
425         ),
426         (
427             "NSResource",
428             Box::new(|p: &mut Parser<'_>| -> Result<()> { p.resource_body().map(|_| ()) }),
429         ),
430         (
431             "PTRResource",
432             Box::new(|p: &mut Parser<'_>| -> Result<()> { p.resource_body().map(|_| ()) }),
433         ),
434         (
435             "SOAResource",
436             Box::new(|p: &mut Parser<'_>| -> Result<()> { p.resource_body().map(|_| ()) }),
437         ),
438         (
439             "TXTResource",
440             Box::new(|p: &mut Parser<'_>| -> Result<()> { p.resource_body().map(|_| ()) }),
441         ),
442         (
443             "SRVResource",
444             Box::new(|p: &mut Parser<'_>| -> Result<()> { p.resource_body().map(|_| ()) }),
445         ),
446         (
447             "AResource",
448             Box::new(|p: &mut Parser<'_>| -> Result<()> { p.resource_body().map(|_| ()) }),
449         ),
450         (
451             "AAAAResource",
452             Box::new(|p: &mut Parser<'_>| -> Result<()> { p.resource_body().map(|_| ()) }),
453         ),
454     ];
455 
456     for (name, test_fn) in tests {
457         let mut p = Parser::default();
458         if let Err(err) = test_fn(&mut p) {
459             assert_eq!(err, Error::ErrNotStarted, "{}", name);
460         }
461     }
462 
463     Ok(())
464 }
465 
466 #[test]
467 fn test_srv_pack_unpack() -> Result<()> {
468     let want = Box::new(SrvResource {
469         priority: 8,
470         weight: 9,
471         port: 11,
472         target: Name::new("srv.example.com.")?,
473     });
474 
475     let b = want.pack(vec![], &mut None, 0)?;
476     let mut got = SrvResource::default();
477     got.unpack(&b, 0, 0)?;
478     assert_eq!(got.to_string(), want.to_string(),);
479 
480     Ok(())
481 }
482 
483 #[test]
484 fn test_dns_pack_unpack() -> Result<()> {
485     let wants = vec![
486         Message {
487             header: Header::default(),
488             questions: vec![Question {
489                 name: Name::new(".")?,
490                 typ: DnsType::Aaaa,
491                 class: DNSCLASS_INET,
492             }],
493             answers: vec![],
494             authorities: vec![],
495             additionals: vec![],
496         },
497         large_test_msg()?,
498     ];
499 
500     for mut want in wants {
501         let b = want.pack()?;
502         let mut got = Message::default();
503         got.unpack(&b)?;
504         assert_eq!(got.to_string(), want.to_string());
505     }
506 
507     Ok(())
508 }
509 
510 #[test]
511 fn test_dns_append_pack_unpack() -> Result<()> {
512     let wants = vec![
513         Message {
514             header: Header::default(),
515             questions: vec![Question {
516                 name: Name::new(".")?,
517                 typ: DnsType::Aaaa,
518                 class: DNSCLASS_INET,
519             }],
520             answers: vec![],
521             authorities: vec![],
522             additionals: vec![],
523         },
524         large_test_msg()?,
525     ];
526 
527     for mut want in wants {
528         let mut b = vec![0; 2];
529         b = want.append_pack(b)?;
530         let mut got = Message::default();
531         got.unpack(&b[2..])?;
532         assert_eq!(got.to_string(), want.to_string());
533     }
534 
535     Ok(())
536 }
537 
538 #[test]
539 fn test_skip_all() -> Result<()> {
540     let mut msg = large_test_msg()?;
541     let buf = msg.pack()?;
542     let mut p = Parser::default();
543     p.start(&buf)?;
544 
545     for _ in 1..=3 {
546         p.skip_all_questions()?;
547     }
548     for _ in 1..=3 {
549         p.skip_all_answers()?;
550     }
551     for _ in 1..=3 {
552         p.skip_all_authorities()?;
553     }
554     for _ in 1..=3 {
555         p.skip_all_additionals()?;
556     }
557 
558     Ok(())
559 }
560 
561 #[test]
562 fn test_skip_each() -> Result<()> {
563     let mut msg = small_test_msg()?;
564     let buf = msg.pack()?;
565     let mut p = Parser::default();
566     p.start(&buf)?;
567 
568     //	{"SkipQuestion", p.SkipQuestion},
569     //	{"SkipAnswer", p.SkipAnswer},
570     //	{"SkipAuthority", p.SkipAuthority},
571     //  {"SkipAdditional", p.SkipAdditional},
572 
573     p.skip_question()?;
574     if let Err(err) = p.skip_question() {
575         assert_eq!(err, Error::ErrSectionDone);
576     } else {
577         panic!("expected error, but got ok");
578     }
579 
580     p.skip_answer()?;
581     if let Err(err) = p.skip_answer() {
582         assert_eq!(err, Error::ErrSectionDone);
583     } else {
584         panic!("expected error, but got ok");
585     }
586 
587     p.skip_authority()?;
588     if let Err(err) = p.skip_authority() {
589         assert_eq!(err, Error::ErrSectionDone);
590     } else {
591         panic!("expected error, but got ok");
592     }
593 
594     p.skip_additional()?;
595     if let Err(err) = p.skip_additional() {
596         assert_eq!(err, Error::ErrSectionDone);
597     } else {
598         panic!("expected error, but got ok");
599     }
600 
601     Ok(())
602 }
603 
604 #[test]
605 fn test_skip_after_read() -> Result<()> {
606     let mut msg = small_test_msg()?;
607     let buf = msg.pack()?;
608     let mut p = Parser::default();
609     p.start(&buf)?;
610 
611     let tests: Vec<(&str, Box<dyn Fn(&mut Parser<'_>) -> Result<()>>)> = vec![
612         (
613             "Question",
614             Box::new(|p: &mut Parser<'_>| -> Result<()> { p.question().map(|_| ()) }),
615         ),
616         (
617             "Answer",
618             Box::new(|p: &mut Parser<'_>| -> Result<()> { p.answer().map(|_| ()) }),
619         ),
620         (
621             "Authority",
622             Box::new(|p: &mut Parser<'_>| -> Result<()> { p.authority().map(|_| ()) }),
623         ),
624         (
625             "Additional",
626             Box::new(|p: &mut Parser<'_>| -> Result<()> { p.additional().map(|_| ()) }),
627         ),
628     ];
629 
630     for (name, read_fn) in tests {
631         read_fn(&mut p)?;
632 
633         let result = match name {
634             "Question" => p.skip_question(),
635             "Answer" => p.skip_answer(),
636             "Authority" => p.skip_authority(),
637             _ => p.skip_additional(),
638         };
639 
640         if let Err(err) = result {
641             assert_eq!(err, Error::ErrSectionDone);
642         } else {
643             panic!("expected error, but got ok");
644         }
645     }
646 
647     Ok(())
648 }
649 
650 #[test]
651 fn test_skip_not_started() -> Result<()> {
652     let tests: Vec<(&str, Box<dyn Fn(&mut Parser<'_>) -> Result<()>>)> = vec![
653         (
654             "SkipAllQuestions",
655             Box::new(|p: &mut Parser<'_>| -> Result<()> { p.skip_all_questions() }),
656         ),
657         (
658             "SkipAllAnswers",
659             Box::new(|p: &mut Parser<'_>| -> Result<()> { p.skip_all_answers() }),
660         ),
661         (
662             "SkipAllAuthorities",
663             Box::new(|p: &mut Parser<'_>| -> Result<()> { p.skip_all_authorities() }),
664         ),
665         (
666             "SkipAllAdditionals",
667             Box::new(|p: &mut Parser<'_>| -> Result<()> { p.skip_all_additionals() }),
668         ),
669     ];
670 
671     let mut p = Parser::default();
672     for (name, test_fn) in tests {
673         if let Err(err) = test_fn(&mut p) {
674             assert_eq!(err, Error::ErrNotStarted);
675         } else {
676             panic!("{} expected error, but got ok", name);
677         }
678     }
679 
680     Ok(())
681 }
682 
683 #[test]
684 fn test_too_many_records() -> Result<()> {
685     let recs: usize = u16::MAX as usize + 1;
686     let tests = vec![
687         (
688             "Questions",
689             Message {
690                 questions: vec![Question::default(); recs],
691                 ..Default::default()
692             },
693             Error::ErrTooManyQuestions,
694         ),
695         (
696             "Answers",
697             Message {
698                 answers: {
699                     let mut a = vec![];
700                     for _ in 0..recs {
701                         a.push(Resource::default());
702                     }
703                     a
704                 },
705                 ..Default::default()
706             },
707             Error::ErrTooManyAnswers,
708         ),
709         (
710             "Authorities",
711             Message {
712                 authorities: {
713                     let mut a = vec![];
714                     for _ in 0..recs {
715                         a.push(Resource::default());
716                     }
717                     a
718                 },
719                 ..Default::default()
720             },
721             Error::ErrTooManyAuthorities,
722         ),
723         (
724             "Additionals",
725             Message {
726                 additionals: {
727                     let mut a = vec![];
728                     for _ in 0..recs {
729                         a.push(Resource::default());
730                     }
731                     a
732                 },
733                 ..Default::default()
734             },
735             Error::ErrTooManyAdditionals,
736         ),
737     ];
738 
739     for (name, mut msg, want) in tests {
740         if let Err(got) = msg.pack() {
741             assert_eq!(
742                 got, want,
743                 "got Message.Pack() for {} = {}, want = {}",
744                 name, got, want
745             )
746         } else {
747             panic!("expected error, but got ok");
748         }
749     }
750 
751     Ok(())
752 }
753 
754 #[test]
755 fn test_very_long_txt() -> Result<()> {
756     let mut str255 = String::new();
757     for _ in 0..255 {
758         str255.push('.');
759     }
760 
761     let mut want = Resource {
762         header: ResourceHeader {
763             name: Name::new("foo.bar.example.com.")?,
764             typ: DnsType::Txt,
765             class: DNSCLASS_INET,
766             ..Default::default()
767         },
768         body: Some(Box::new(TxtResource {
769             txt: vec![
770                 "".to_owned(),
771                 "".to_owned(),
772                 "foo bar".to_owned(),
773                 "".to_owned(),
774                 "www.example.com".to_owned(),
775                 "www.example.com.".to_owned(),
776                 str255,
777             ],
778         })),
779     };
780 
781     let buf = want.pack(vec![], &mut Some(HashMap::new()), 0)?;
782     let mut got = Resource::default();
783     let off = got.header.unpack(&buf, 0, 0)?;
784     let (body, n) = unpack_resource_body(got.header.typ, &buf, off, got.header.length as usize)?;
785     got.body = Some(body);
786     assert_eq!(
787         n,
788         buf.len(),
789         "unpacked different amount than packed: got = {}, want = {}",
790         n,
791         buf.len(),
792     );
793     assert_eq!(got.to_string(), want.to_string());
794 
795     Ok(())
796 }
797 
798 #[test]
799 fn test_too_long_txt() -> Result<()> {
800     let mut str256 = String::new();
801     for _ in 0..256 {
802         str256.push('.');
803     }
804     let rb = TxtResource { txt: vec![str256] };
805     if let Err(err) = rb.pack(vec![], &mut Some(HashMap::new()), 0) {
806         assert_eq!(err, Error::ErrStringTooLong);
807     } else {
808         panic!("expected error, but got ok");
809     }
810 
811     Ok(())
812 }
813 
814 #[test]
815 fn test_start_error() -> Result<()> {
816     let tests: Vec<(&str, Box<dyn Fn(&mut Builder) -> Result<()>>)> = vec![
817         (
818             "Questions",
819             Box::new(|b: &mut Builder| -> Result<()> { b.start_questions() }),
820         ),
821         (
822             "Answers",
823             Box::new(|b: &mut Builder| -> Result<()> { b.start_answers() }),
824         ),
825         (
826             "Authorities",
827             Box::new(|b: &mut Builder| -> Result<()> { b.start_authorities() }),
828         ),
829         (
830             "Additionals",
831             Box::new(|b: &mut Builder| -> Result<()> { b.start_additionals() }),
832         ),
833     ];
834 
835     let envs: Vec<(&str, Box<dyn Fn() -> Builder>, Error)> = vec![
836         (
837             "sectionNotStarted",
838             Box::new(|| -> Builder {
839                 Builder {
840                     section: Section::NotStarted,
841                     ..Default::default()
842                 }
843             }),
844             Error::ErrNotStarted,
845         ),
846         (
847             "sectionDone",
848             Box::new(|| -> Builder {
849                 Builder {
850                     section: Section::Done,
851                     ..Default::default()
852                 }
853             }),
854             Error::ErrSectionDone,
855         ),
856     ];
857 
858     for (env_name, env_fn, env_err) in &envs {
859         for (test_name, test_fn) in &tests {
860             let mut b = env_fn();
861             if let Err(got_err) = test_fn(&mut b) {
862                 assert_eq!(
863                     got_err, *env_err,
864                     "got Builder{}.{} = {}, want = {}",
865                     env_name, test_name, got_err, env_err
866                 );
867             } else {
868                 panic!("{}.{}expected error, but got ok", env_name, test_name);
869             }
870         }
871     }
872 
873     Ok(())
874 }
875 
876 #[test]
877 fn test_builder_resource_error() -> Result<()> {
878     let tests: Vec<(&str, Box<dyn Fn(&mut Builder) -> Result<()>>)> = vec![
879         (
880             "CNAMEResource",
881             Box::new(|b: &mut Builder| -> Result<()> {
882                 b.add_resource(&mut Resource {
883                     header: ResourceHeader::default(),
884                     body: Some(Box::<CnameResource>::default()),
885                 })
886             }),
887         ),
888         (
889             "MXResource",
890             Box::new(|b: &mut Builder| -> Result<()> {
891                 b.add_resource(&mut Resource {
892                     header: ResourceHeader::default(),
893                     body: Some(Box::<MxResource>::default()),
894                 })
895             }),
896         ),
897         (
898             "NSResource",
899             Box::new(|b: &mut Builder| -> Result<()> {
900                 b.add_resource(&mut Resource {
901                     header: ResourceHeader::default(),
902                     body: Some(Box::<NsResource>::default()),
903                 })
904             }),
905         ),
906         (
907             "PTRResource",
908             Box::new(|b: &mut Builder| -> Result<()> {
909                 b.add_resource(&mut Resource {
910                     header: ResourceHeader::default(),
911                     body: Some(Box::<PtrResource>::default()),
912                 })
913             }),
914         ),
915         (
916             "SOAResource",
917             Box::new(|b: &mut Builder| -> Result<()> {
918                 b.add_resource(&mut Resource {
919                     header: ResourceHeader::default(),
920                     body: Some(Box::<SoaResource>::default()),
921                 })
922             }),
923         ),
924         (
925             "TXTResource",
926             Box::new(|b: &mut Builder| -> Result<()> {
927                 b.add_resource(&mut Resource {
928                     header: ResourceHeader::default(),
929                     body: Some(Box::<TxtResource>::default()),
930                 })
931             }),
932         ),
933         (
934             "SRVResource",
935             Box::new(|b: &mut Builder| -> Result<()> {
936                 b.add_resource(&mut Resource {
937                     header: ResourceHeader::default(),
938                     body: Some(Box::<SrvResource>::default()),
939                 })
940             }),
941         ),
942         (
943             "AResource",
944             Box::new(|b: &mut Builder| -> Result<()> {
945                 b.add_resource(&mut Resource {
946                     header: ResourceHeader::default(),
947                     body: Some(Box::<AResource>::default()),
948                 })
949             }),
950         ),
951         (
952             "AAAAResource",
953             Box::new(|b: &mut Builder| -> Result<()> {
954                 b.add_resource(&mut Resource {
955                     header: ResourceHeader::default(),
956                     body: Some(Box::<AaaaResource>::default()),
957                 })
958             }),
959         ),
960         (
961             "OPTResource",
962             Box::new(|b: &mut Builder| -> Result<()> {
963                 b.add_resource(&mut Resource {
964                     header: ResourceHeader::default(),
965                     body: Some(Box::<OptResource>::default()),
966                 })
967             }),
968         ),
969     ];
970 
971     let envs: Vec<(&str, Box<dyn Fn() -> Builder>, Error)> = vec![
972         (
973             "sectionNotStarted",
974             Box::new(|| -> Builder {
975                 Builder {
976                     section: Section::NotStarted,
977                     ..Default::default()
978                 }
979             }),
980             Error::ErrNotStarted,
981         ),
982         (
983             "sectionHeader",
984             Box::new(|| -> Builder {
985                 Builder {
986                     section: Section::Header,
987                     ..Default::default()
988                 }
989             }),
990             Error::ErrNotStarted,
991         ),
992         (
993             "sectionQuestions",
994             Box::new(|| -> Builder {
995                 Builder {
996                     section: Section::Questions,
997                     ..Default::default()
998                 }
999             }),
1000             Error::ErrNotStarted,
1001         ),
1002         (
1003             "sectionDone",
1004             Box::new(|| -> Builder {
1005                 Builder {
1006                     section: Section::Done,
1007                     ..Default::default()
1008                 }
1009             }),
1010             Error::ErrSectionDone,
1011         ),
1012     ];
1013 
1014     for (env_name, env_fn, env_err) in &envs {
1015         for (test_name, test_fn) in &tests {
1016             let mut b = env_fn();
1017             if let Err(got_err) = test_fn(&mut b) {
1018                 assert_eq!(
1019                     got_err, *env_err,
1020                     "got Builder{}.{} = {}, want = {}",
1021                     env_name, test_name, got_err, env_err
1022                 );
1023             } else {
1024                 panic!("{}.{}expected error, but got ok", env_name, test_name);
1025             }
1026         }
1027     }
1028 
1029     Ok(())
1030 }
1031 
1032 #[test]
1033 fn test_finish_error() -> Result<()> {
1034     let mut b = Builder::default();
1035     let want = Error::ErrNotStarted;
1036     if let Err(got) = b.finish() {
1037         assert_eq!(got, want, "got Builder.Finish() = {}, want = {}", got, want);
1038     } else {
1039         panic!("expected error, but got ok");
1040     }
1041 
1042     Ok(())
1043 }
1044 
1045 #[test]
1046 fn test_builder() -> Result<()> {
1047     let mut msg = large_test_msg()?;
1048     let want = msg.pack()?;
1049 
1050     let mut b = Builder::new(&msg.header);
1051     b.enable_compression();
1052 
1053     b.start_questions()?;
1054     for q in &msg.questions {
1055         b.add_question(q)?;
1056     }
1057 
1058     b.start_answers()?;
1059     for r in &mut msg.answers {
1060         b.add_resource(r)?;
1061     }
1062 
1063     b.start_authorities()?;
1064     for r in &mut msg.authorities {
1065         b.add_resource(r)?;
1066     }
1067 
1068     b.start_additionals()?;
1069     for r in &mut msg.additionals {
1070         b.add_resource(r)?;
1071     }
1072 
1073     let got = b.finish()?;
1074     assert_eq!(
1075         got,
1076         want,
1077         "got.len()={}, want.len()={}",
1078         got.len(),
1079         want.len()
1080     );
1081 
1082     Ok(())
1083 }
1084 
1085 #[test]
1086 fn test_resource_pack() -> Result<()> {
1087     let tests = vec![
1088         (
1089             Message {
1090                 questions: vec![Question {
1091                     name: Name::new(".")?,
1092                     typ: DnsType::Aaaa,
1093                     class: DNSCLASS_INET,
1094                 }],
1095                 answers: vec![Resource {
1096                     header: ResourceHeader::default(),
1097                     body: None,
1098                 }],
1099                 ..Default::default()
1100             },
1101             Error::ErrNilResourceBody,
1102         ),
1103         (
1104             Message {
1105                 questions: vec![Question {
1106                     name: Name::new(".")?,
1107                     typ: DnsType::Aaaa,
1108                     class: DNSCLASS_INET,
1109                 }],
1110                 authorities: vec![Resource {
1111                     header: ResourceHeader::default(),
1112                     body: Some(Box::<NsResource>::default()),
1113                 }],
1114                 ..Default::default()
1115             },
1116             Error::ErrNonCanonicalName,
1117         ),
1118         (
1119             Message {
1120                 questions: vec![Question {
1121                     name: Name::new(".")?,
1122                     typ: DnsType::A,
1123                     class: DNSCLASS_INET,
1124                 }],
1125                 additionals: vec![Resource {
1126                     header: ResourceHeader::default(),
1127                     body: None,
1128                 }],
1129                 ..Default::default()
1130             },
1131             Error::ErrNilResourceBody,
1132         ),
1133     ];
1134 
1135     for (mut m, want_err) in tests {
1136         if let Err(err) = m.pack() {
1137             assert_eq!(err, want_err);
1138         } else {
1139             panic!("expected error, but got ok");
1140         }
1141     }
1142 
1143     Ok(())
1144 }
1145 
1146 #[test]
1147 fn test_resource_pack_length() -> Result<()> {
1148     let mut r = Resource {
1149         header: ResourceHeader {
1150             name: Name::new(".")?,
1151             typ: DnsType::A,
1152             class: DNSCLASS_INET,
1153             ..Default::default()
1154         },
1155         body: Some(Box::new(AResource { a: [127, 0, 0, 2] })),
1156     };
1157 
1158     let (hb, _) = r.header.pack(vec![], &mut None, 0)?;
1159     let buf = r.pack(vec![], &mut None, 0)?;
1160 
1161     let mut hdr = ResourceHeader::default();
1162     hdr.unpack(&buf, 0, 0)?;
1163 
1164     let (got, want) = (hdr.length as usize, buf.len() - hb.len());
1165     assert_eq!(got, want, "got hdr.Length = {}, want = {}", got, want);
1166 
1167     Ok(())
1168 }
1169 
1170 #[test]
1171 fn test_option_pack_unpack() -> Result<()> {
1172     let tests = vec![
1173         (
1174             "without EDNS(0) options",
1175             vec![
1176                 0x00, 0x00, 0x29, 0x10, 0x00, 0xfe, 0x00, 0x80, 0x00, 0x00, 0x00,
1177             ],
1178             Message {
1179                 header: Header {
1180                     rcode: RCode::FormatError,
1181                     ..Default::default()
1182                 },
1183                 questions: vec![Question {
1184                     name: Name::new(".")?,
1185                     typ: DnsType::A,
1186                     class: DNSCLASS_INET,
1187                 }],
1188                 additionals: vec![Resource {
1189                     header: must_edns0_resource_header(
1190                         4096,
1191                         0xfe0 | RCode::FormatError as u32,
1192                         true,
1193                     )?,
1194                     body: Some(Box::<OptResource>::default()),
1195                 }],
1196                 ..Default::default()
1197             },
1198             //true,
1199             //0xfe0 | RCode::FormatError as u32,
1200         ),
1201         (
1202             "with EDNS(0) options",
1203             vec![
1204                 0x00, 0x00, 0x29, 0x10, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x0c, 0x00,
1205                 0x02, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x02, 0x12, 0x34,
1206             ],
1207             Message {
1208                 header: Header {
1209                     rcode: RCode::ServerFailure,
1210                     ..Default::default()
1211                 },
1212                 questions: vec![Question {
1213                     name: Name::new(".")?,
1214                     typ: DnsType::Aaaa,
1215                     class: DNSCLASS_INET,
1216                 }],
1217                 additionals: vec![Resource {
1218                     header: must_edns0_resource_header(
1219                         4096,
1220                         0xff0 | RCode::ServerFailure as u32,
1221                         false,
1222                     )?,
1223                     body: Some(Box::new(OptResource {
1224                         options: vec![
1225                             DnsOption {
1226                                 code: 12, // see RFC 7828
1227                                 data: vec![0x00, 0x00],
1228                             },
1229                             DnsOption {
1230                                 code: 11, // see RFC 7830
1231                                 data: vec![0x12, 0x34],
1232                             },
1233                         ],
1234                     })),
1235                 }],
1236                 ..Default::default()
1237             },
1238             //dnssecOK: false,
1239             //extRCode: 0xff0 | RCodeServerFailure,
1240         ),
1241         (
1242             // Containing multiple OPT resources in a
1243             // message is invalid, but it's necessary for
1244             // protocol conformance testing.
1245             "with multiple OPT resources",
1246             vec![
1247                 0x00, 0x00, 0x29, 0x10, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x0b, 0x00,
1248                 0x02, 0x12, 0x34, 0x00, 0x00, 0x29, 0x10, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x06,
1249                 0x00, 0x0c, 0x00, 0x02, 0x00, 0x00,
1250             ],
1251             Message {
1252                 header: Header {
1253                     rcode: RCode::NameError,
1254                     ..Default::default()
1255                 },
1256                 questions: vec![Question {
1257                     name: Name::new(".")?,
1258                     typ: DnsType::Aaaa,
1259                     class: DNSCLASS_INET,
1260                 }],
1261                 additionals: vec![
1262                     Resource {
1263                         header: must_edns0_resource_header(
1264                             4096,
1265                             0xff0 | RCode::NameError as u32,
1266                             false,
1267                         )?,
1268                         body: Some(Box::new(OptResource {
1269                             options: vec![DnsOption {
1270                                 code: 11, // see RFC 7830
1271                                 data: vec![0x12, 0x34],
1272                             }],
1273                         })),
1274                     },
1275                     Resource {
1276                         header: must_edns0_resource_header(
1277                             4096,
1278                             0xff0 | RCode::NameError as u32,
1279                             false,
1280                         )?,
1281                         body: Some(Box::new(OptResource {
1282                             options: vec![DnsOption {
1283                                 code: 12, // see RFC 7828
1284                                 data: vec![0x00, 0x00],
1285                             }],
1286                         })),
1287                     },
1288                 ],
1289                 ..Default::default()
1290             },
1291         ),
1292     ];
1293 
1294     for (_tt_name, tt_w, mut tt_m) in tests {
1295         let w = tt_m.pack()?;
1296 
1297         assert_eq!(&w[w.len() - tt_w.len()..], &tt_w[..]);
1298 
1299         let mut m = Message::default();
1300         m.unpack(&w)?;
1301 
1302         let ms: Vec<String> = m.additionals.iter().map(|s| s.to_string()).collect();
1303         let tt_ms: Vec<String> = tt_m.additionals.iter().map(|s| s.to_string()).collect();
1304         assert_eq!(ms, tt_ms);
1305     }
1306 
1307     Ok(())
1308 }
1309