update packaging
[platform/core/system/edge-orchestration.git] / vendor / golang.org / x / net / dns / dnsmessage / message_test.go
1 // Copyright 2009 The Go Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style
3 // license that can be found in the LICENSE file.
4
5 package dnsmessage
6
7 import (
8         "bytes"
9         "fmt"
10         "reflect"
11         "strings"
12         "testing"
13 )
14
15 func TestPrintPaddedUint8(t *testing.T) {
16         tests := []struct {
17                 num  uint8
18                 want string
19         }{
20                 {0, "000"},
21                 {1, "001"},
22                 {9, "009"},
23                 {10, "010"},
24                 {99, "099"},
25                 {100, "100"},
26                 {124, "124"},
27                 {104, "104"},
28                 {120, "120"},
29                 {255, "255"},
30         }
31
32         for _, test := range tests {
33                 if got := printPaddedUint8(test.num); got != test.want {
34                         t.Errorf("got printPaddedUint8(%d) = %s, want = %s", test.num, got, test.want)
35                 }
36         }
37 }
38
39 func TestPrintUint8Bytes(t *testing.T) {
40         tests := []uint8{
41                 0,
42                 1,
43                 9,
44                 10,
45                 99,
46                 100,
47                 124,
48                 104,
49                 120,
50                 255,
51         }
52
53         for _, test := range tests {
54                 if got, want := string(printUint8Bytes(nil, test)), fmt.Sprint(test); got != want {
55                         t.Errorf("got printUint8Bytes(%d) = %s, want = %s", test, got, want)
56                 }
57         }
58 }
59
60 func TestPrintUint16(t *testing.T) {
61         tests := []uint16{
62                 65535,
63                 0,
64                 1,
65                 10,
66                 100,
67                 1000,
68                 10000,
69                 324,
70                 304,
71                 320,
72         }
73
74         for _, test := range tests {
75                 if got, want := printUint16(test), fmt.Sprint(test); got != want {
76                         t.Errorf("got printUint16(%d) = %s, want = %s", test, got, want)
77                 }
78         }
79 }
80
81 func TestPrintUint32(t *testing.T) {
82         tests := []uint32{
83                 4294967295,
84                 65535,
85                 0,
86                 1,
87                 10,
88                 100,
89                 1000,
90                 10000,
91                 100000,
92                 1000000,
93                 10000000,
94                 100000000,
95                 1000000000,
96                 324,
97                 304,
98                 320,
99         }
100
101         for _, test := range tests {
102                 if got, want := printUint32(test), fmt.Sprint(test); got != want {
103                         t.Errorf("got printUint32(%d) = %s, want = %s", test, got, want)
104                 }
105         }
106 }
107
108 func mustEDNS0ResourceHeader(l int, extrc RCode, do bool) ResourceHeader {
109         h := ResourceHeader{Class: ClassINET}
110         if err := h.SetEDNS0(l, extrc, do); err != nil {
111                 panic(err)
112         }
113         return h
114 }
115
116 func (m *Message) String() string {
117         s := fmt.Sprintf("Message: %#v\n", &m.Header)
118         if len(m.Questions) > 0 {
119                 s += "-- Questions\n"
120                 for _, q := range m.Questions {
121                         s += fmt.Sprintf("%#v\n", q)
122                 }
123         }
124         if len(m.Answers) > 0 {
125                 s += "-- Answers\n"
126                 for _, a := range m.Answers {
127                         s += fmt.Sprintf("%#v\n", a)
128                 }
129         }
130         if len(m.Authorities) > 0 {
131                 s += "-- Authorities\n"
132                 for _, ns := range m.Authorities {
133                         s += fmt.Sprintf("%#v\n", ns)
134                 }
135         }
136         if len(m.Additionals) > 0 {
137                 s += "-- Additionals\n"
138                 for _, e := range m.Additionals {
139                         s += fmt.Sprintf("%#v\n", e)
140                 }
141         }
142         return s
143 }
144
145 func TestNameString(t *testing.T) {
146         want := "foo"
147         name := MustNewName(want)
148         if got := fmt.Sprint(name); got != want {
149                 t.Errorf("got fmt.Sprint(%#v) = %s, want = %s", name, got, want)
150         }
151 }
152
153 func TestQuestionPackUnpack(t *testing.T) {
154         want := Question{
155                 Name:  MustNewName("."),
156                 Type:  TypeA,
157                 Class: ClassINET,
158         }
159         buf, err := want.pack(make([]byte, 1, 50), map[string]int{}, 1)
160         if err != nil {
161                 t.Fatal("Question.pack() =", err)
162         }
163         var p Parser
164         p.msg = buf
165         p.header.questions = 1
166         p.section = sectionQuestions
167         p.off = 1
168         got, err := p.Question()
169         if err != nil {
170                 t.Fatalf("Parser{%q}.Question() = %v", string(buf[1:]), err)
171         }
172         if p.off != len(buf) {
173                 t.Errorf("unpacked different amount than packed: got = %d, want = %d", p.off, len(buf))
174         }
175         if !reflect.DeepEqual(got, want) {
176                 t.Errorf("got from Parser.Question() = %+v, want = %+v", got, want)
177         }
178 }
179
180 func TestName(t *testing.T) {
181         tests := []string{
182                 "",
183                 ".",
184                 "google..com",
185                 "google.com",
186                 "google..com.",
187                 "google.com.",
188                 ".google.com.",
189                 "www..google.com.",
190                 "www.google.com.",
191         }
192
193         for _, test := range tests {
194                 n, err := NewName(test)
195                 if err != nil {
196                         t.Errorf("NewName(%q) = %v", test, err)
197                         continue
198                 }
199                 if ns := n.String(); ns != test {
200                         t.Errorf("got %#v.String() = %q, want = %q", n, ns, test)
201                         continue
202                 }
203         }
204 }
205
206 func TestNamePackUnpack(t *testing.T) {
207         tests := []struct {
208                 in   string
209                 want string
210                 err  error
211         }{
212                 {"", "", errNonCanonicalName},
213                 {".", ".", nil},
214                 {"google..com", "", errNonCanonicalName},
215                 {"google.com", "", errNonCanonicalName},
216                 {"google..com.", "", errZeroSegLen},
217                 {"google.com.", "google.com.", nil},
218                 {".google.com.", "", errZeroSegLen},
219                 {"www..google.com.", "", errZeroSegLen},
220                 {"www.google.com.", "www.google.com.", nil},
221         }
222
223         for _, test := range tests {
224                 in := MustNewName(test.in)
225                 want := MustNewName(test.want)
226                 buf, err := in.pack(make([]byte, 0, 30), map[string]int{}, 0)
227                 if err != test.err {
228                         t.Errorf("got %q.pack() = %v, want = %v", test.in, err, test.err)
229                         continue
230                 }
231                 if test.err != nil {
232                         continue
233                 }
234                 var got Name
235                 n, err := got.unpack(buf, 0)
236                 if err != nil {
237                         t.Errorf("%q.unpack() = %v", test.in, err)
238                         continue
239                 }
240                 if n != len(buf) {
241                         t.Errorf(
242                                 "unpacked different amount than packed for %q: got = %d, want = %d",
243                                 test.in,
244                                 n,
245                                 len(buf),
246                         )
247                 }
248                 if got != want {
249                         t.Errorf("unpacking packing of %q: got = %#v, want = %#v", test.in, got, want)
250                 }
251         }
252 }
253
254 func TestIncompressibleName(t *testing.T) {
255         name := MustNewName("example.com.")
256         compression := map[string]int{}
257         buf, err := name.pack(make([]byte, 0, 100), compression, 0)
258         if err != nil {
259                 t.Fatal("first Name.pack() =", err)
260         }
261         buf, err = name.pack(buf, compression, 0)
262         if err != nil {
263                 t.Fatal("second Name.pack() =", err)
264         }
265         var n1 Name
266         off, err := n1.unpackCompressed(buf, 0, false /* allowCompression */)
267         if err != nil {
268                 t.Fatal("unpacking incompressible name without pointers failed:", err)
269         }
270         var n2 Name
271         if _, err := n2.unpackCompressed(buf, off, false /* allowCompression */); err != errCompressedSRV {
272                 t.Errorf("unpacking compressed incompressible name with pointers: got %v, want = %v", err, errCompressedSRV)
273         }
274 }
275
276 func checkErrorPrefix(err error, prefix string) bool {
277         e, ok := err.(*nestedError)
278         return ok && e.s == prefix
279 }
280
281 func TestHeaderUnpackError(t *testing.T) {
282         wants := []string{
283                 "id",
284                 "bits",
285                 "questions",
286                 "answers",
287                 "authorities",
288                 "additionals",
289         }
290         var buf []byte
291         var h header
292         for _, want := range wants {
293                 n, err := h.unpack(buf, 0)
294                 if n != 0 || !checkErrorPrefix(err, want) {
295                         t.Errorf("got header.unpack([%d]byte, 0) = %d, %v, want = 0, %s", len(buf), n, err, want)
296                 }
297                 buf = append(buf, 0, 0)
298         }
299 }
300
301 func TestParserStart(t *testing.T) {
302         const want = "unpacking header"
303         var p Parser
304         for i := 0; i <= 1; i++ {
305                 _, err := p.Start([]byte{})
306                 if !checkErrorPrefix(err, want) {
307                         t.Errorf("got Parser.Start(nil) = _, %v, want = _, %s", err, want)
308                 }
309         }
310 }
311
312 func TestResourceNotStarted(t *testing.T) {
313         tests := []struct {
314                 name string
315                 fn   func(*Parser) error
316         }{
317                 {"CNAMEResource", func(p *Parser) error { _, err := p.CNAMEResource(); return err }},
318                 {"MXResource", func(p *Parser) error { _, err := p.MXResource(); return err }},
319                 {"NSResource", func(p *Parser) error { _, err := p.NSResource(); return err }},
320                 {"PTRResource", func(p *Parser) error { _, err := p.PTRResource(); return err }},
321                 {"SOAResource", func(p *Parser) error { _, err := p.SOAResource(); return err }},
322                 {"TXTResource", func(p *Parser) error { _, err := p.TXTResource(); return err }},
323                 {"SRVResource", func(p *Parser) error { _, err := p.SRVResource(); return err }},
324                 {"AResource", func(p *Parser) error { _, err := p.AResource(); return err }},
325                 {"AAAAResource", func(p *Parser) error { _, err := p.AAAAResource(); return err }},
326         }
327
328         for _, test := range tests {
329                 if err := test.fn(&Parser{}); err != ErrNotStarted {
330                         t.Errorf("got Parser.%s() = _ , %v, want = _, %v", test.name, err, ErrNotStarted)
331                 }
332         }
333 }
334
335 func TestDNSPackUnpack(t *testing.T) {
336         wants := []Message{
337                 {
338                         Questions: []Question{
339                                 {
340                                         Name:  MustNewName("."),
341                                         Type:  TypeAAAA,
342                                         Class: ClassINET,
343                                 },
344                         },
345                         Answers:     []Resource{},
346                         Authorities: []Resource{},
347                         Additionals: []Resource{},
348                 },
349                 largeTestMsg(),
350         }
351         for i, want := range wants {
352                 b, err := want.Pack()
353                 if err != nil {
354                         t.Fatalf("%d: Message.Pack() = %v", i, err)
355                 }
356                 var got Message
357                 err = got.Unpack(b)
358                 if err != nil {
359                         t.Fatalf("%d: Message.Unapck() = %v", i, err)
360                 }
361                 if !reflect.DeepEqual(got, want) {
362                         t.Errorf("%d: Message.Pack/Unpack() roundtrip: got = %+v, want = %+v", i, &got, &want)
363                 }
364         }
365 }
366
367 func TestDNSAppendPackUnpack(t *testing.T) {
368         wants := []Message{
369                 {
370                         Questions: []Question{
371                                 {
372                                         Name:  MustNewName("."),
373                                         Type:  TypeAAAA,
374                                         Class: ClassINET,
375                                 },
376                         },
377                         Answers:     []Resource{},
378                         Authorities: []Resource{},
379                         Additionals: []Resource{},
380                 },
381                 largeTestMsg(),
382         }
383         for i, want := range wants {
384                 b := make([]byte, 2, 514)
385                 b, err := want.AppendPack(b)
386                 if err != nil {
387                         t.Fatalf("%d: Message.AppendPack() = %v", i, err)
388                 }
389                 b = b[2:]
390                 var got Message
391                 err = got.Unpack(b)
392                 if err != nil {
393                         t.Fatalf("%d: Message.Unapck() = %v", i, err)
394                 }
395                 if !reflect.DeepEqual(got, want) {
396                         t.Errorf("%d: Message.AppendPack/Unpack() roundtrip: got = %+v, want = %+v", i, &got, &want)
397                 }
398         }
399 }
400
401 func TestSkipAll(t *testing.T) {
402         msg := largeTestMsg()
403         buf, err := msg.Pack()
404         if err != nil {
405                 t.Fatal("Message.Pack() =", err)
406         }
407         var p Parser
408         if _, err := p.Start(buf); err != nil {
409                 t.Fatal("Parser.Start(non-nil) =", err)
410         }
411
412         tests := []struct {
413                 name string
414                 f    func() error
415         }{
416                 {"SkipAllQuestions", p.SkipAllQuestions},
417                 {"SkipAllAnswers", p.SkipAllAnswers},
418                 {"SkipAllAuthorities", p.SkipAllAuthorities},
419                 {"SkipAllAdditionals", p.SkipAllAdditionals},
420         }
421         for _, test := range tests {
422                 for i := 1; i <= 3; i++ {
423                         if err := test.f(); err != nil {
424                                 t.Errorf("%d: Parser.%s() = %v", i, test.name, err)
425                         }
426                 }
427         }
428 }
429
430 func TestSkipEach(t *testing.T) {
431         msg := smallTestMsg()
432
433         buf, err := msg.Pack()
434         if err != nil {
435                 t.Fatal("Message.Pack() =", err)
436         }
437         var p Parser
438         if _, err := p.Start(buf); err != nil {
439                 t.Fatal("Parser.Start(non-nil) =", err)
440         }
441
442         tests := []struct {
443                 name string
444                 f    func() error
445         }{
446                 {"SkipQuestion", p.SkipQuestion},
447                 {"SkipAnswer", p.SkipAnswer},
448                 {"SkipAuthority", p.SkipAuthority},
449                 {"SkipAdditional", p.SkipAdditional},
450         }
451         for _, test := range tests {
452                 if err := test.f(); err != nil {
453                         t.Errorf("first Parser.%s() = %v, want = nil", test.name, err)
454                 }
455                 if err := test.f(); err != ErrSectionDone {
456                         t.Errorf("second Parser.%s() = %v, want = %v", test.name, err, ErrSectionDone)
457                 }
458         }
459 }
460
461 func TestSkipAfterRead(t *testing.T) {
462         msg := smallTestMsg()
463
464         buf, err := msg.Pack()
465         if err != nil {
466                 t.Fatal("Message.Pack() =", err)
467         }
468         var p Parser
469         if _, err := p.Start(buf); err != nil {
470                 t.Fatal("Parser.Srart(non-nil) =", err)
471         }
472
473         tests := []struct {
474                 name string
475                 skip func() error
476                 read func() error
477         }{
478                 {"Question", p.SkipQuestion, func() error { _, err := p.Question(); return err }},
479                 {"Answer", p.SkipAnswer, func() error { _, err := p.Answer(); return err }},
480                 {"Authority", p.SkipAuthority, func() error { _, err := p.Authority(); return err }},
481                 {"Additional", p.SkipAdditional, func() error { _, err := p.Additional(); return err }},
482         }
483         for _, test := range tests {
484                 if err := test.read(); err != nil {
485                         t.Errorf("got Parser.%s() = _, %v, want = _, nil", test.name, err)
486                 }
487                 if err := test.skip(); err != ErrSectionDone {
488                         t.Errorf("got Parser.Skip%s() = %v, want = %v", test.name, err, ErrSectionDone)
489                 }
490         }
491 }
492
493 func TestSkipNotStarted(t *testing.T) {
494         var p Parser
495
496         tests := []struct {
497                 name string
498                 f    func() error
499         }{
500                 {"SkipAllQuestions", p.SkipAllQuestions},
501                 {"SkipAllAnswers", p.SkipAllAnswers},
502                 {"SkipAllAuthorities", p.SkipAllAuthorities},
503                 {"SkipAllAdditionals", p.SkipAllAdditionals},
504         }
505         for _, test := range tests {
506                 if err := test.f(); err != ErrNotStarted {
507                         t.Errorf("got Parser.%s() = %v, want = %v", test.name, err, ErrNotStarted)
508                 }
509         }
510 }
511
512 func TestTooManyRecords(t *testing.T) {
513         const recs = int(^uint16(0)) + 1
514         tests := []struct {
515                 name string
516                 msg  Message
517                 want error
518         }{
519                 {
520                         "Questions",
521                         Message{
522                                 Questions: make([]Question, recs),
523                         },
524                         errTooManyQuestions,
525                 },
526                 {
527                         "Answers",
528                         Message{
529                                 Answers: make([]Resource, recs),
530                         },
531                         errTooManyAnswers,
532                 },
533                 {
534                         "Authorities",
535                         Message{
536                                 Authorities: make([]Resource, recs),
537                         },
538                         errTooManyAuthorities,
539                 },
540                 {
541                         "Additionals",
542                         Message{
543                                 Additionals: make([]Resource, recs),
544                         },
545                         errTooManyAdditionals,
546                 },
547         }
548
549         for _, test := range tests {
550                 if _, got := test.msg.Pack(); got != test.want {
551                         t.Errorf("got Message.Pack() for %d %s = %v, want = %v", recs, test.name, got, test.want)
552                 }
553         }
554 }
555
556 func TestVeryLongTxt(t *testing.T) {
557         want := Resource{
558                 ResourceHeader{
559                         Name:  MustNewName("foo.bar.example.com."),
560                         Type:  TypeTXT,
561                         Class: ClassINET,
562                 },
563                 &TXTResource{[]string{
564                         "",
565                         "",
566                         "foo bar",
567                         "",
568                         "www.example.com",
569                         "www.example.com.",
570                         strings.Repeat(".", 255),
571                 }},
572         }
573         buf, err := want.pack(make([]byte, 0, 8000), map[string]int{}, 0)
574         if err != nil {
575                 t.Fatal("Resource.pack() =", err)
576         }
577         var got Resource
578         off, err := got.Header.unpack(buf, 0)
579         if err != nil {
580                 t.Fatal("ResourceHeader.unpack() =", err)
581         }
582         body, n, err := unpackResourceBody(buf, off, got.Header)
583         if err != nil {
584                 t.Fatal("unpackResourceBody() =", err)
585         }
586         got.Body = body
587         if n != len(buf) {
588                 t.Errorf("unpacked different amount than packed: got = %d, want = %d", n, len(buf))
589         }
590         if !reflect.DeepEqual(got, want) {
591                 t.Errorf("Resource.pack/unpack() roundtrip: got = %#v, want = %#v", got, want)
592         }
593 }
594
595 func TestTooLongTxt(t *testing.T) {
596         rb := TXTResource{[]string{strings.Repeat(".", 256)}}
597         if _, err := rb.pack(make([]byte, 0, 8000), map[string]int{}, 0); err != errStringTooLong {
598                 t.Errorf("packing TXTResource with 256 character string: got err = %v, want = %v", err, errStringTooLong)
599         }
600 }
601
602 func TestStartAppends(t *testing.T) {
603         buf := make([]byte, 2, 514)
604         wantBuf := []byte{4, 44}
605         copy(buf, wantBuf)
606
607         b := NewBuilder(buf, Header{})
608         b.EnableCompression()
609
610         buf, err := b.Finish()
611         if err != nil {
612                 t.Fatal("Builder.Finish() =", err)
613         }
614         if got, want := len(buf), headerLen+2; got != want {
615                 t.Errorf("got len(buf) = %d, want = %d", got, want)
616         }
617         if string(buf[:2]) != string(wantBuf) {
618                 t.Errorf("original data not preserved, got = %#v, want = %#v", buf[:2], wantBuf)
619         }
620 }
621
622 func TestStartError(t *testing.T) {
623         tests := []struct {
624                 name string
625                 fn   func(*Builder) error
626         }{
627                 {"Questions", func(b *Builder) error { return b.StartQuestions() }},
628                 {"Answers", func(b *Builder) error { return b.StartAnswers() }},
629                 {"Authorities", func(b *Builder) error { return b.StartAuthorities() }},
630                 {"Additionals", func(b *Builder) error { return b.StartAdditionals() }},
631         }
632
633         envs := []struct {
634                 name string
635                 fn   func() *Builder
636                 want error
637         }{
638                 {"sectionNotStarted", func() *Builder { return &Builder{section: sectionNotStarted} }, ErrNotStarted},
639                 {"sectionDone", func() *Builder { return &Builder{section: sectionDone} }, ErrSectionDone},
640         }
641
642         for _, env := range envs {
643                 for _, test := range tests {
644                         if got := test.fn(env.fn()); got != env.want {
645                                 t.Errorf("got Builder{%s}.Start%s() = %v, want = %v", env.name, test.name, got, env.want)
646                         }
647                 }
648         }
649 }
650
651 func TestBuilderResourceError(t *testing.T) {
652         tests := []struct {
653                 name string
654                 fn   func(*Builder) error
655         }{
656                 {"CNAMEResource", func(b *Builder) error { return b.CNAMEResource(ResourceHeader{}, CNAMEResource{}) }},
657                 {"MXResource", func(b *Builder) error { return b.MXResource(ResourceHeader{}, MXResource{}) }},
658                 {"NSResource", func(b *Builder) error { return b.NSResource(ResourceHeader{}, NSResource{}) }},
659                 {"PTRResource", func(b *Builder) error { return b.PTRResource(ResourceHeader{}, PTRResource{}) }},
660                 {"SOAResource", func(b *Builder) error { return b.SOAResource(ResourceHeader{}, SOAResource{}) }},
661                 {"TXTResource", func(b *Builder) error { return b.TXTResource(ResourceHeader{}, TXTResource{}) }},
662                 {"SRVResource", func(b *Builder) error { return b.SRVResource(ResourceHeader{}, SRVResource{}) }},
663                 {"AResource", func(b *Builder) error { return b.AResource(ResourceHeader{}, AResource{}) }},
664                 {"AAAAResource", func(b *Builder) error { return b.AAAAResource(ResourceHeader{}, AAAAResource{}) }},
665                 {"OPTResource", func(b *Builder) error { return b.OPTResource(ResourceHeader{}, OPTResource{}) }},
666         }
667
668         envs := []struct {
669                 name string
670                 fn   func() *Builder
671                 want error
672         }{
673                 {"sectionNotStarted", func() *Builder { return &Builder{section: sectionNotStarted} }, ErrNotStarted},
674                 {"sectionHeader", func() *Builder { return &Builder{section: sectionHeader} }, ErrNotStarted},
675                 {"sectionQuestions", func() *Builder { return &Builder{section: sectionQuestions} }, ErrNotStarted},
676                 {"sectionDone", func() *Builder { return &Builder{section: sectionDone} }, ErrSectionDone},
677         }
678
679         for _, env := range envs {
680                 for _, test := range tests {
681                         if got := test.fn(env.fn()); got != env.want {
682                                 t.Errorf("got Builder{%s}.%s() = %v, want = %v", env.name, test.name, got, env.want)
683                         }
684                 }
685         }
686 }
687
688 func TestFinishError(t *testing.T) {
689         var b Builder
690         want := ErrNotStarted
691         if _, got := b.Finish(); got != want {
692                 t.Errorf("got Builder.Finish() = %v, want = %v", got, want)
693         }
694 }
695
696 func TestBuilder(t *testing.T) {
697         msg := largeTestMsg()
698         want, err := msg.Pack()
699         if err != nil {
700                 t.Fatal("Message.Pack() =", err)
701         }
702
703         b := NewBuilder(nil, msg.Header)
704         b.EnableCompression()
705
706         if err := b.StartQuestions(); err != nil {
707                 t.Fatal("Builder.StartQuestions() =", err)
708         }
709         for _, q := range msg.Questions {
710                 if err := b.Question(q); err != nil {
711                         t.Fatalf("Builder.Question(%#v) = %v", q, err)
712                 }
713         }
714
715         if err := b.StartAnswers(); err != nil {
716                 t.Fatal("Builder.StartAnswers() =", err)
717         }
718         for _, a := range msg.Answers {
719                 switch a.Header.Type {
720                 case TypeA:
721                         if err := b.AResource(a.Header, *a.Body.(*AResource)); err != nil {
722                                 t.Fatalf("Builder.AResource(%#v) = %v", a, err)
723                         }
724                 case TypeNS:
725                         if err := b.NSResource(a.Header, *a.Body.(*NSResource)); err != nil {
726                                 t.Fatalf("Builder.NSResource(%#v) = %v", a, err)
727                         }
728                 case TypeCNAME:
729                         if err := b.CNAMEResource(a.Header, *a.Body.(*CNAMEResource)); err != nil {
730                                 t.Fatalf("Builder.CNAMEResource(%#v) = %v", a, err)
731                         }
732                 case TypeSOA:
733                         if err := b.SOAResource(a.Header, *a.Body.(*SOAResource)); err != nil {
734                                 t.Fatalf("Builder.SOAResource(%#v) = %v", a, err)
735                         }
736                 case TypePTR:
737                         if err := b.PTRResource(a.Header, *a.Body.(*PTRResource)); err != nil {
738                                 t.Fatalf("Builder.PTRResource(%#v) = %v", a, err)
739                         }
740                 case TypeMX:
741                         if err := b.MXResource(a.Header, *a.Body.(*MXResource)); err != nil {
742                                 t.Fatalf("Builder.MXResource(%#v) = %v", a, err)
743                         }
744                 case TypeTXT:
745                         if err := b.TXTResource(a.Header, *a.Body.(*TXTResource)); err != nil {
746                                 t.Fatalf("Builder.TXTResource(%#v) = %v", a, err)
747                         }
748                 case TypeAAAA:
749                         if err := b.AAAAResource(a.Header, *a.Body.(*AAAAResource)); err != nil {
750                                 t.Fatalf("Builder.AAAAResource(%#v) = %v", a, err)
751                         }
752                 case TypeSRV:
753                         if err := b.SRVResource(a.Header, *a.Body.(*SRVResource)); err != nil {
754                                 t.Fatalf("Builder.SRVResource(%#v) = %v", a, err)
755                         }
756                 }
757         }
758
759         if err := b.StartAuthorities(); err != nil {
760                 t.Fatal("Builder.StartAuthorities() =", err)
761         }
762         for _, a := range msg.Authorities {
763                 if err := b.NSResource(a.Header, *a.Body.(*NSResource)); err != nil {
764                         t.Fatalf("Builder.NSResource(%#v) = %v", a, err)
765                 }
766         }
767
768         if err := b.StartAdditionals(); err != nil {
769                 t.Fatal("Builder.StartAdditionals() =", err)
770         }
771         for _, a := range msg.Additionals {
772                 switch a.Body.(type) {
773                 case *TXTResource:
774                         if err := b.TXTResource(a.Header, *a.Body.(*TXTResource)); err != nil {
775                                 t.Fatalf("Builder.TXTResource(%#v) = %v", a, err)
776                         }
777                 case *OPTResource:
778                         if err := b.OPTResource(a.Header, *a.Body.(*OPTResource)); err != nil {
779                                 t.Fatalf("Builder.OPTResource(%#v) = %v", a, err)
780                         }
781                 }
782         }
783
784         got, err := b.Finish()
785         if err != nil {
786                 t.Fatal("Builder.Finish() =", err)
787         }
788         if !bytes.Equal(got, want) {
789                 t.Fatalf("got from Builder.Finish() = %#v\nwant = %#v", got, want)
790         }
791 }
792
793 func TestResourcePack(t *testing.T) {
794         for _, tt := range []struct {
795                 m   Message
796                 err error
797         }{
798                 {
799                         Message{
800                                 Questions: []Question{
801                                         {
802                                                 Name:  MustNewName("."),
803                                                 Type:  TypeAAAA,
804                                                 Class: ClassINET,
805                                         },
806                                 },
807                                 Answers: []Resource{{ResourceHeader{}, nil}},
808                         },
809                         &nestedError{"packing Answer", errNilResouceBody},
810                 },
811                 {
812                         Message{
813                                 Questions: []Question{
814                                         {
815                                                 Name:  MustNewName("."),
816                                                 Type:  TypeAAAA,
817                                                 Class: ClassINET,
818                                         },
819                                 },
820                                 Authorities: []Resource{{ResourceHeader{}, (*NSResource)(nil)}},
821                         },
822                         &nestedError{"packing Authority",
823                                 &nestedError{"ResourceHeader",
824                                         &nestedError{"Name", errNonCanonicalName},
825                                 },
826                         },
827                 },
828                 {
829                         Message{
830                                 Questions: []Question{
831                                         {
832                                                 Name:  MustNewName("."),
833                                                 Type:  TypeA,
834                                                 Class: ClassINET,
835                                         },
836                                 },
837                                 Additionals: []Resource{{ResourceHeader{}, nil}},
838                         },
839                         &nestedError{"packing Additional", errNilResouceBody},
840                 },
841         } {
842                 _, err := tt.m.Pack()
843                 if !reflect.DeepEqual(err, tt.err) {
844                         t.Errorf("got Message{%v}.Pack() = %v, want %v", tt.m, err, tt.err)
845                 }
846         }
847 }
848
849 func TestResourcePackLength(t *testing.T) {
850         r := Resource{
851                 ResourceHeader{
852                         Name:  MustNewName("."),
853                         Type:  TypeA,
854                         Class: ClassINET,
855                 },
856                 &AResource{[4]byte{127, 0, 0, 2}},
857         }
858
859         hb, _, err := r.Header.pack(nil, nil, 0)
860         if err != nil {
861                 t.Fatal("ResourceHeader.pack() =", err)
862         }
863         buf := make([]byte, 0, len(hb))
864         buf, err = r.pack(buf, nil, 0)
865         if err != nil {
866                 t.Fatal("Resource.pack() =", err)
867         }
868
869         var hdr ResourceHeader
870         if _, err := hdr.unpack(buf, 0); err != nil {
871                 t.Fatal("ResourceHeader.unpack() =", err)
872         }
873
874         if got, want := int(hdr.Length), len(buf)-len(hb); got != want {
875                 t.Errorf("got hdr.Length = %d, want = %d", got, want)
876         }
877 }
878
879 func TestOptionPackUnpack(t *testing.T) {
880         for _, tt := range []struct {
881                 name     string
882                 w        []byte // wire format of m.Additionals
883                 m        Message
884                 dnssecOK bool
885                 extRCode RCode
886         }{
887                 {
888                         name: "without EDNS(0) options",
889                         w: []byte{
890                                 0x00, 0x00, 0x29, 0x10, 0x00, 0xfe, 0x00, 0x80,
891                                 0x00, 0x00, 0x00,
892                         },
893                         m: Message{
894                                 Header: Header{RCode: RCodeFormatError},
895                                 Questions: []Question{
896                                         {
897                                                 Name:  MustNewName("."),
898                                                 Type:  TypeA,
899                                                 Class: ClassINET,
900                                         },
901                                 },
902                                 Additionals: []Resource{
903                                         {
904                                                 mustEDNS0ResourceHeader(4096, 0xfe0|RCodeFormatError, true),
905                                                 &OPTResource{},
906                                         },
907                                 },
908                         },
909                         dnssecOK: true,
910                         extRCode: 0xfe0 | RCodeFormatError,
911                 },
912                 {
913                         name: "with EDNS(0) options",
914                         w: []byte{
915                                 0x00, 0x00, 0x29, 0x10, 0x00, 0xff, 0x00, 0x00,
916                                 0x00, 0x00, 0x0c, 0x00, 0x0c, 0x00, 0x02, 0x00,
917                                 0x00, 0x00, 0x0b, 0x00, 0x02, 0x12, 0x34,
918                         },
919                         m: Message{
920                                 Header: Header{RCode: RCodeServerFailure},
921                                 Questions: []Question{
922                                         {
923                                                 Name:  MustNewName("."),
924                                                 Type:  TypeAAAA,
925                                                 Class: ClassINET,
926                                         },
927                                 },
928                                 Additionals: []Resource{
929                                         {
930                                                 mustEDNS0ResourceHeader(4096, 0xff0|RCodeServerFailure, false),
931                                                 &OPTResource{
932                                                         Options: []Option{
933                                                                 {
934                                                                         Code: 12, // see RFC 7828
935                                                                         Data: []byte{0x00, 0x00},
936                                                                 },
937                                                                 {
938                                                                         Code: 11, // see RFC 7830
939                                                                         Data: []byte{0x12, 0x34},
940                                                                 },
941                                                         },
942                                                 },
943                                         },
944                                 },
945                         },
946                         dnssecOK: false,
947                         extRCode: 0xff0 | RCodeServerFailure,
948                 },
949                 {
950                         // Containing multiple OPT resources in a
951                         // message is invalid, but it's necessary for
952                         // protocol conformance testing.
953                         name: "with multiple OPT resources",
954                         w: []byte{
955                                 0x00, 0x00, 0x29, 0x10, 0x00, 0xff, 0x00, 0x00,
956                                 0x00, 0x00, 0x06, 0x00, 0x0b, 0x00, 0x02, 0x12,
957                                 0x34, 0x00, 0x00, 0x29, 0x10, 0x00, 0xff, 0x00,
958                                 0x00, 0x00, 0x00, 0x06, 0x00, 0x0c, 0x00, 0x02,
959                                 0x00, 0x00,
960                         },
961                         m: Message{
962                                 Header: Header{RCode: RCodeNameError},
963                                 Questions: []Question{
964                                         {
965                                                 Name:  MustNewName("."),
966                                                 Type:  TypeAAAA,
967                                                 Class: ClassINET,
968                                         },
969                                 },
970                                 Additionals: []Resource{
971                                         {
972                                                 mustEDNS0ResourceHeader(4096, 0xff0|RCodeNameError, false),
973                                                 &OPTResource{
974                                                         Options: []Option{
975                                                                 {
976                                                                         Code: 11, // see RFC 7830
977                                                                         Data: []byte{0x12, 0x34},
978                                                                 },
979                                                         },
980                                                 },
981                                         },
982                                         {
983                                                 mustEDNS0ResourceHeader(4096, 0xff0|RCodeNameError, false),
984                                                 &OPTResource{
985                                                         Options: []Option{
986                                                                 {
987                                                                         Code: 12, // see RFC 7828
988                                                                         Data: []byte{0x00, 0x00},
989                                                                 },
990                                                         },
991                                                 },
992                                         },
993                                 },
994                         },
995                 },
996         } {
997                 w, err := tt.m.Pack()
998                 if err != nil {
999                         t.Errorf("Message.Pack() for %s = %v", tt.name, err)
1000                         continue
1001                 }
1002                 if !bytes.Equal(w[len(w)-len(tt.w):], tt.w) {
1003                         t.Errorf("got Message.Pack() for %s = %#v, want %#v", tt.name, w[len(w)-len(tt.w):], tt.w)
1004                         continue
1005                 }
1006                 var m Message
1007                 if err := m.Unpack(w); err != nil {
1008                         t.Errorf("Message.Unpack() for %s = %v", tt.name, err)
1009                         continue
1010                 }
1011                 if !reflect.DeepEqual(m.Additionals, tt.m.Additionals) {
1012                         t.Errorf("got Message.Pack/Unpack() roundtrip for %s = %+v, want %+v", tt.name, m, tt.m)
1013                         continue
1014                 }
1015         }
1016 }
1017
1018 // TestGoString tests that Message.GoString produces Go code that compiles to
1019 // reproduce the Message.
1020 //
1021 // This test was produced as follows:
1022 // 1. Run (*Message).GoString on largeTestMsg().
1023 // 2. Remove "dnsmessage." from the output.
1024 // 3. Paste the result in the test to store it in msg.
1025 // 4. Also put the original output in the test to store in want.
1026 func TestGoString(t *testing.T) {
1027         msg := Message{Header: Header{ID: 0, Response: true, OpCode: 0, Authoritative: true, Truncated: false, RecursionDesired: false, RecursionAvailable: false, RCode: RCodeSuccess}, Questions: []Question{{Name: MustNewName("foo.bar.example.com."), Type: TypeA, Class: ClassINET}}, Answers: []Resource{{Header: ResourceHeader{Name: MustNewName("foo.bar.example.com."), Type: TypeA, Class: ClassINET, TTL: 0, Length: 0}, Body: &AResource{A: [4]byte{127, 0, 0, 1}}}, {Header: ResourceHeader{Name: MustNewName("foo.bar.example.com."), Type: TypeA, Class: ClassINET, TTL: 0, Length: 0}, Body: &AResource{A: [4]byte{127, 0, 0, 2}}}, {Header: ResourceHeader{Name: MustNewName("foo.bar.example.com."), Type: TypeAAAA, Class: ClassINET, TTL: 0, Length: 0}, Body: &AAAAResource{AAAA: [16]byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}}}, {Header: ResourceHeader{Name: MustNewName("foo.bar.example.com."), Type: TypeCNAME, Class: ClassINET, TTL: 0, Length: 0}, Body: &CNAMEResource{CNAME: MustNewName("alias.example.com.")}}, {Header: ResourceHeader{Name: MustNewName("foo.bar.example.com."), Type: TypeSOA, Class: ClassINET, TTL: 0, Length: 0}, Body: &SOAResource{NS: MustNewName("ns1.example.com."), MBox: MustNewName("mb.example.com."), Serial: 1, Refresh: 2, Retry: 3, Expire: 4, MinTTL: 5}}, {Header: ResourceHeader{Name: MustNewName("foo.bar.example.com."), Type: TypePTR, Class: ClassINET, TTL: 0, Length: 0}, Body: &PTRResource{PTR: MustNewName("ptr.example.com.")}}, {Header: ResourceHeader{Name: MustNewName("foo.bar.example.com."), Type: TypeMX, Class: ClassINET, TTL: 0, Length: 0}, Body: &MXResource{Pref: 7, MX: MustNewName("mx.example.com.")}}, {Header: ResourceHeader{Name: MustNewName("foo.bar.example.com."), Type: TypeSRV, Class: ClassINET, TTL: 0, Length: 0}, Body: &SRVResource{Priority: 8, Weight: 9, Port: 11, Target: MustNewName("srv.example.com.")}}}, Authorities: []Resource{{Header: ResourceHeader{Name: MustNewName("foo.bar.example.com."), Type: TypeNS, Class: ClassINET, TTL: 0, Length: 0}, Body: &NSResource{NS: MustNewName("ns1.example.com.")}}, {Header: ResourceHeader{Name: MustNewName("foo.bar.example.com."), Type: TypeNS, Class: ClassINET, TTL: 0, Length: 0}, Body: &NSResource{NS: MustNewName("ns2.example.com.")}}}, Additionals: []Resource{{Header: ResourceHeader{Name: MustNewName("foo.bar.example.com."), Type: TypeTXT, Class: ClassINET, TTL: 0, Length: 0}, Body: &TXTResource{TXT: []string{"So Long\x2c and Thanks for All the Fish"}}}, {Header: ResourceHeader{Name: MustNewName("foo.bar.example.com."), Type: TypeTXT, Class: ClassINET, TTL: 0, Length: 0}, Body: &TXTResource{TXT: []string{"Hamster Huey and the Gooey Kablooie"}}}, {Header: ResourceHeader{Name: MustNewName("."), Type: TypeOPT, Class: 4096, TTL: 4261412864, Length: 0}, Body: &OPTResource{Options: []Option{{Code: 10, Data: []byte{1, 35, 69, 103, 137, 171, 205, 239}}}}}}}
1028         if !reflect.DeepEqual(msg, largeTestMsg()) {
1029                 t.Error("Message.GoString lost information or largeTestMsg changed: msg != largeTestMsg()")
1030         }
1031         got := msg.GoString()
1032         want := `dnsmessage.Message{Header: dnsmessage.Header{ID: 0, Response: true, OpCode: 0, Authoritative: true, Truncated: false, RecursionDesired: false, RecursionAvailable: false, RCode: dnsmessage.RCodeSuccess}, Questions: []dnsmessage.Question{dnsmessage.Question{Name: dnsmessage.MustNewName("foo.bar.example.com."), Type: dnsmessage.TypeA, Class: dnsmessage.ClassINET}}, Answers: []dnsmessage.Resource{dnsmessage.Resource{Header: dnsmessage.ResourceHeader{Name: dnsmessage.MustNewName("foo.bar.example.com."), Type: dnsmessage.TypeA, Class: dnsmessage.ClassINET, TTL: 0, Length: 0}, Body: &dnsmessage.AResource{A: [4]byte{127, 0, 0, 1}}}, dnsmessage.Resource{Header: dnsmessage.ResourceHeader{Name: dnsmessage.MustNewName("foo.bar.example.com."), Type: dnsmessage.TypeA, Class: dnsmessage.ClassINET, TTL: 0, Length: 0}, Body: &dnsmessage.AResource{A: [4]byte{127, 0, 0, 2}}}, dnsmessage.Resource{Header: dnsmessage.ResourceHeader{Name: dnsmessage.MustNewName("foo.bar.example.com."), Type: dnsmessage.TypeAAAA, Class: dnsmessage.ClassINET, TTL: 0, Length: 0}, Body: &dnsmessage.AAAAResource{AAAA: [16]byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}}}, dnsmessage.Resource{Header: dnsmessage.ResourceHeader{Name: dnsmessage.MustNewName("foo.bar.example.com."), Type: dnsmessage.TypeCNAME, Class: dnsmessage.ClassINET, TTL: 0, Length: 0}, Body: &dnsmessage.CNAMEResource{CNAME: dnsmessage.MustNewName("alias.example.com.")}}, dnsmessage.Resource{Header: dnsmessage.ResourceHeader{Name: dnsmessage.MustNewName("foo.bar.example.com."), Type: dnsmessage.TypeSOA, Class: dnsmessage.ClassINET, TTL: 0, Length: 0}, Body: &dnsmessage.SOAResource{NS: dnsmessage.MustNewName("ns1.example.com."), MBox: dnsmessage.MustNewName("mb.example.com."), Serial: 1, Refresh: 2, Retry: 3, Expire: 4, MinTTL: 5}}, dnsmessage.Resource{Header: dnsmessage.ResourceHeader{Name: dnsmessage.MustNewName("foo.bar.example.com."), Type: dnsmessage.TypePTR, Class: dnsmessage.ClassINET, TTL: 0, Length: 0}, Body: &dnsmessage.PTRResource{PTR: dnsmessage.MustNewName("ptr.example.com.")}}, dnsmessage.Resource{Header: dnsmessage.ResourceHeader{Name: dnsmessage.MustNewName("foo.bar.example.com."), Type: dnsmessage.TypeMX, Class: dnsmessage.ClassINET, TTL: 0, Length: 0}, Body: &dnsmessage.MXResource{Pref: 7, MX: dnsmessage.MustNewName("mx.example.com.")}}, dnsmessage.Resource{Header: dnsmessage.ResourceHeader{Name: dnsmessage.MustNewName("foo.bar.example.com."), Type: dnsmessage.TypeSRV, Class: dnsmessage.ClassINET, TTL: 0, Length: 0}, Body: &dnsmessage.SRVResource{Priority: 8, Weight: 9, Port: 11, Target: dnsmessage.MustNewName("srv.example.com.")}}}, Authorities: []dnsmessage.Resource{dnsmessage.Resource{Header: dnsmessage.ResourceHeader{Name: dnsmessage.MustNewName("foo.bar.example.com."), Type: dnsmessage.TypeNS, Class: dnsmessage.ClassINET, TTL: 0, Length: 0}, Body: &dnsmessage.NSResource{NS: dnsmessage.MustNewName("ns1.example.com.")}}, dnsmessage.Resource{Header: dnsmessage.ResourceHeader{Name: dnsmessage.MustNewName("foo.bar.example.com."), Type: dnsmessage.TypeNS, Class: dnsmessage.ClassINET, TTL: 0, Length: 0}, Body: &dnsmessage.NSResource{NS: dnsmessage.MustNewName("ns2.example.com.")}}}, Additionals: []dnsmessage.Resource{dnsmessage.Resource{Header: dnsmessage.ResourceHeader{Name: dnsmessage.MustNewName("foo.bar.example.com."), Type: dnsmessage.TypeTXT, Class: dnsmessage.ClassINET, TTL: 0, Length: 0}, Body: &dnsmessage.TXTResource{TXT: []string{"So Long\x2c and Thanks for All the Fish"}}}, dnsmessage.Resource{Header: dnsmessage.ResourceHeader{Name: dnsmessage.MustNewName("foo.bar.example.com."), Type: dnsmessage.TypeTXT, Class: dnsmessage.ClassINET, TTL: 0, Length: 0}, Body: &dnsmessage.TXTResource{TXT: []string{"Hamster Huey and the Gooey Kablooie"}}}, dnsmessage.Resource{Header: dnsmessage.ResourceHeader{Name: dnsmessage.MustNewName("."), Type: dnsmessage.TypeOPT, Class: 4096, TTL: 4261412864, Length: 0}, Body: &dnsmessage.OPTResource{Options: []dnsmessage.Option{dnsmessage.Option{Code: 10, Data: []byte{1, 35, 69, 103, 137, 171, 205, 239}}}}}}}`
1033         if got != want {
1034                 t.Errorf("got msg1.GoString() = %s\nwant = %s", got, want)
1035         }
1036 }
1037
1038 func benchmarkParsingSetup() ([]byte, error) {
1039         name := MustNewName("foo.bar.example.com.")
1040         msg := Message{
1041                 Header: Header{Response: true, Authoritative: true},
1042                 Questions: []Question{
1043                         {
1044                                 Name:  name,
1045                                 Type:  TypeA,
1046                                 Class: ClassINET,
1047                         },
1048                 },
1049                 Answers: []Resource{
1050                         {
1051                                 ResourceHeader{
1052                                         Name:  name,
1053                                         Class: ClassINET,
1054                                 },
1055                                 &AResource{[4]byte{}},
1056                         },
1057                         {
1058                                 ResourceHeader{
1059                                         Name:  name,
1060                                         Class: ClassINET,
1061                                 },
1062                                 &AAAAResource{[16]byte{}},
1063                         },
1064                         {
1065                                 ResourceHeader{
1066                                         Name:  name,
1067                                         Class: ClassINET,
1068                                 },
1069                                 &CNAMEResource{name},
1070                         },
1071                         {
1072                                 ResourceHeader{
1073                                         Name:  name,
1074                                         Class: ClassINET,
1075                                 },
1076                                 &NSResource{name},
1077                         },
1078                 },
1079         }
1080
1081         buf, err := msg.Pack()
1082         if err != nil {
1083                 return nil, fmt.Errorf("Message.Pack() = %v", err)
1084         }
1085         return buf, nil
1086 }
1087
1088 func benchmarkParsing(tb testing.TB, buf []byte) {
1089         var p Parser
1090         if _, err := p.Start(buf); err != nil {
1091                 tb.Fatal("Parser.Start(non-nil) =", err)
1092         }
1093
1094         for {
1095                 _, err := p.Question()
1096                 if err == ErrSectionDone {
1097                         break
1098                 }
1099                 if err != nil {
1100                         tb.Fatal("Parser.Question() =", err)
1101                 }
1102         }
1103
1104         for {
1105                 h, err := p.AnswerHeader()
1106                 if err == ErrSectionDone {
1107                         break
1108                 }
1109                 if err != nil {
1110                         tb.Fatal("Parser.AnswerHeader() =", err)
1111                 }
1112
1113                 switch h.Type {
1114                 case TypeA:
1115                         if _, err := p.AResource(); err != nil {
1116                                 tb.Fatal("Parser.AResource() =", err)
1117                         }
1118                 case TypeAAAA:
1119                         if _, err := p.AAAAResource(); err != nil {
1120                                 tb.Fatal("Parser.AAAAResource() =", err)
1121                         }
1122                 case TypeCNAME:
1123                         if _, err := p.CNAMEResource(); err != nil {
1124                                 tb.Fatal("Parser.CNAMEResource() =", err)
1125                         }
1126                 case TypeNS:
1127                         if _, err := p.NSResource(); err != nil {
1128                                 tb.Fatal("Parser.NSResource() =", err)
1129                         }
1130                 case TypeOPT:
1131                         if _, err := p.OPTResource(); err != nil {
1132                                 tb.Fatal("Parser.OPTResource() =", err)
1133                         }
1134                 default:
1135                         tb.Fatalf("got unknown type: %T", h)
1136                 }
1137         }
1138 }
1139
1140 func BenchmarkParsing(b *testing.B) {
1141         buf, err := benchmarkParsingSetup()
1142         if err != nil {
1143                 b.Fatal(err)
1144         }
1145
1146         b.ReportAllocs()
1147         for i := 0; i < b.N; i++ {
1148                 benchmarkParsing(b, buf)
1149         }
1150 }
1151
1152 func TestParsingAllocs(t *testing.T) {
1153         buf, err := benchmarkParsingSetup()
1154         if err != nil {
1155                 t.Fatal(err)
1156         }
1157
1158         if allocs := testing.AllocsPerRun(100, func() { benchmarkParsing(t, buf) }); allocs > 0.5 {
1159                 t.Errorf("allocations during parsing: got = %f, want ~0", allocs)
1160         }
1161 }
1162
1163 func benchmarkBuildingSetup() (Name, []byte) {
1164         name := MustNewName("foo.bar.example.com.")
1165         buf := make([]byte, 0, packStartingCap)
1166         return name, buf
1167 }
1168
1169 func benchmarkBuilding(tb testing.TB, name Name, buf []byte) {
1170         bld := NewBuilder(buf, Header{Response: true, Authoritative: true})
1171
1172         if err := bld.StartQuestions(); err != nil {
1173                 tb.Fatal("Builder.StartQuestions() =", err)
1174         }
1175         q := Question{
1176                 Name:  name,
1177                 Type:  TypeA,
1178                 Class: ClassINET,
1179         }
1180         if err := bld.Question(q); err != nil {
1181                 tb.Fatalf("Builder.Question(%+v) = %v", q, err)
1182         }
1183
1184         hdr := ResourceHeader{
1185                 Name:  name,
1186                 Class: ClassINET,
1187         }
1188         if err := bld.StartAnswers(); err != nil {
1189                 tb.Fatal("Builder.StartQuestions() =", err)
1190         }
1191
1192         ar := AResource{[4]byte{}}
1193         if err := bld.AResource(hdr, ar); err != nil {
1194                 tb.Fatalf("Builder.AResource(%+v, %+v) = %v", hdr, ar, err)
1195         }
1196
1197         aaar := AAAAResource{[16]byte{}}
1198         if err := bld.AAAAResource(hdr, aaar); err != nil {
1199                 tb.Fatalf("Builder.AAAAResource(%+v, %+v) = %v", hdr, aaar, err)
1200         }
1201
1202         cnr := CNAMEResource{name}
1203         if err := bld.CNAMEResource(hdr, cnr); err != nil {
1204                 tb.Fatalf("Builder.CNAMEResource(%+v, %+v) = %v", hdr, cnr, err)
1205         }
1206
1207         nsr := NSResource{name}
1208         if err := bld.NSResource(hdr, nsr); err != nil {
1209                 tb.Fatalf("Builder.NSResource(%+v, %+v) = %v", hdr, nsr, err)
1210         }
1211
1212         extrc := 0xfe0 | RCodeNotImplemented
1213         if err := (&hdr).SetEDNS0(4096, extrc, true); err != nil {
1214                 tb.Fatalf("ResourceHeader.SetEDNS0(4096, %#x, true) = %v", extrc, err)
1215         }
1216         optr := OPTResource{}
1217         if err := bld.OPTResource(hdr, optr); err != nil {
1218                 tb.Fatalf("Builder.OPTResource(%+v, %+v) = %v", hdr, optr, err)
1219         }
1220
1221         if _, err := bld.Finish(); err != nil {
1222                 tb.Fatal("Builder.Finish() =", err)
1223         }
1224 }
1225
1226 func BenchmarkBuilding(b *testing.B) {
1227         name, buf := benchmarkBuildingSetup()
1228         b.ReportAllocs()
1229         for i := 0; i < b.N; i++ {
1230                 benchmarkBuilding(b, name, buf)
1231         }
1232 }
1233
1234 func TestBuildingAllocs(t *testing.T) {
1235         name, buf := benchmarkBuildingSetup()
1236         if allocs := testing.AllocsPerRun(100, func() { benchmarkBuilding(t, name, buf) }); allocs > 0.5 {
1237                 t.Errorf("allocations during building: got = %f, want ~0", allocs)
1238         }
1239 }
1240
1241 func smallTestMsg() Message {
1242         name := MustNewName("example.com.")
1243         return Message{
1244                 Header: Header{Response: true, Authoritative: true},
1245                 Questions: []Question{
1246                         {
1247                                 Name:  name,
1248                                 Type:  TypeA,
1249                                 Class: ClassINET,
1250                         },
1251                 },
1252                 Answers: []Resource{
1253                         {
1254                                 ResourceHeader{
1255                                         Name:  name,
1256                                         Type:  TypeA,
1257                                         Class: ClassINET,
1258                                 },
1259                                 &AResource{[4]byte{127, 0, 0, 1}},
1260                         },
1261                 },
1262                 Authorities: []Resource{
1263                         {
1264                                 ResourceHeader{
1265                                         Name:  name,
1266                                         Type:  TypeA,
1267                                         Class: ClassINET,
1268                                 },
1269                                 &AResource{[4]byte{127, 0, 0, 1}},
1270                         },
1271                 },
1272                 Additionals: []Resource{
1273                         {
1274                                 ResourceHeader{
1275                                         Name:  name,
1276                                         Type:  TypeA,
1277                                         Class: ClassINET,
1278                                 },
1279                                 &AResource{[4]byte{127, 0, 0, 1}},
1280                         },
1281                 },
1282         }
1283 }
1284
1285 func BenchmarkPack(b *testing.B) {
1286         msg := largeTestMsg()
1287
1288         b.ReportAllocs()
1289
1290         for i := 0; i < b.N; i++ {
1291                 if _, err := msg.Pack(); err != nil {
1292                         b.Fatal("Message.Pack() =", err)
1293                 }
1294         }
1295 }
1296
1297 func BenchmarkAppendPack(b *testing.B) {
1298         msg := largeTestMsg()
1299         buf := make([]byte, 0, packStartingCap)
1300
1301         b.ReportAllocs()
1302
1303         for i := 0; i < b.N; i++ {
1304                 if _, err := msg.AppendPack(buf[:0]); err != nil {
1305                         b.Fatal("Message.AppendPack() = ", err)
1306                 }
1307         }
1308 }
1309
1310 func largeTestMsg() Message {
1311         name := MustNewName("foo.bar.example.com.")
1312         return Message{
1313                 Header: Header{Response: true, Authoritative: true},
1314                 Questions: []Question{
1315                         {
1316                                 Name:  name,
1317                                 Type:  TypeA,
1318                                 Class: ClassINET,
1319                         },
1320                 },
1321                 Answers: []Resource{
1322                         {
1323                                 ResourceHeader{
1324                                         Name:  name,
1325                                         Type:  TypeA,
1326                                         Class: ClassINET,
1327                                 },
1328                                 &AResource{[4]byte{127, 0, 0, 1}},
1329                         },
1330                         {
1331                                 ResourceHeader{
1332                                         Name:  name,
1333                                         Type:  TypeA,
1334                                         Class: ClassINET,
1335                                 },
1336                                 &AResource{[4]byte{127, 0, 0, 2}},
1337                         },
1338                         {
1339                                 ResourceHeader{
1340                                         Name:  name,
1341                                         Type:  TypeAAAA,
1342                                         Class: ClassINET,
1343                                 },
1344                                 &AAAAResource{[16]byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}},
1345                         },
1346                         {
1347                                 ResourceHeader{
1348                                         Name:  name,
1349                                         Type:  TypeCNAME,
1350                                         Class: ClassINET,
1351                                 },
1352                                 &CNAMEResource{MustNewName("alias.example.com.")},
1353                         },
1354                         {
1355                                 ResourceHeader{
1356                                         Name:  name,
1357                                         Type:  TypeSOA,
1358                                         Class: ClassINET,
1359                                 },
1360                                 &SOAResource{
1361                                         NS:      MustNewName("ns1.example.com."),
1362                                         MBox:    MustNewName("mb.example.com."),
1363                                         Serial:  1,
1364                                         Refresh: 2,
1365                                         Retry:   3,
1366                                         Expire:  4,
1367                                         MinTTL:  5,
1368                                 },
1369                         },
1370                         {
1371                                 ResourceHeader{
1372                                         Name:  name,
1373                                         Type:  TypePTR,
1374                                         Class: ClassINET,
1375                                 },
1376                                 &PTRResource{MustNewName("ptr.example.com.")},
1377                         },
1378                         {
1379                                 ResourceHeader{
1380                                         Name:  name,
1381                                         Type:  TypeMX,
1382                                         Class: ClassINET,
1383                                 },
1384                                 &MXResource{
1385                                         7,
1386                                         MustNewName("mx.example.com."),
1387                                 },
1388                         },
1389                         {
1390                                 ResourceHeader{
1391                                         Name:  name,
1392                                         Type:  TypeSRV,
1393                                         Class: ClassINET,
1394                                 },
1395                                 &SRVResource{
1396                                         8,
1397                                         9,
1398                                         11,
1399                                         MustNewName("srv.example.com."),
1400                                 },
1401                         },
1402                 },
1403                 Authorities: []Resource{
1404                         {
1405                                 ResourceHeader{
1406                                         Name:  name,
1407                                         Type:  TypeNS,
1408                                         Class: ClassINET,
1409                                 },
1410                                 &NSResource{MustNewName("ns1.example.com.")},
1411                         },
1412                         {
1413                                 ResourceHeader{
1414                                         Name:  name,
1415                                         Type:  TypeNS,
1416                                         Class: ClassINET,
1417                                 },
1418                                 &NSResource{MustNewName("ns2.example.com.")},
1419                         },
1420                 },
1421                 Additionals: []Resource{
1422                         {
1423                                 ResourceHeader{
1424                                         Name:  name,
1425                                         Type:  TypeTXT,
1426                                         Class: ClassINET,
1427                                 },
1428                                 &TXTResource{[]string{"So Long, and Thanks for All the Fish"}},
1429                         },
1430                         {
1431                                 ResourceHeader{
1432                                         Name:  name,
1433                                         Type:  TypeTXT,
1434                                         Class: ClassINET,
1435                                 },
1436                                 &TXTResource{[]string{"Hamster Huey and the Gooey Kablooie"}},
1437                         },
1438                         {
1439                                 mustEDNS0ResourceHeader(4096, 0xfe0|RCodeSuccess, false),
1440                                 &OPTResource{
1441                                         Options: []Option{
1442                                                 {
1443                                                         Code: 10, // see RFC 7873
1444                                                         Data: []byte{0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef},
1445                                                 },
1446                                         },
1447                                 },
1448                         },
1449                 },
1450         }
1451 }