blob: 052897f3cf55d95bdcf033d1ce895230eed71807 [file] [log] [blame]
Dan Willemsenc7413322018-08-27 23:21:26 -07001// 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
5package dnsmessage
6
7import (
8 "bytes"
9 "fmt"
10 "reflect"
11 "strings"
12 "testing"
13)
14
15func mustNewName(name string) Name {
16 n, err := NewName(name)
17 if err != nil {
18 panic(err)
19 }
20 return n
21}
22
23func (m *Message) String() string {
24 s := fmt.Sprintf("Message: %#v\n", &m.Header)
25 if len(m.Questions) > 0 {
26 s += "-- Questions\n"
27 for _, q := range m.Questions {
28 s += fmt.Sprintf("%#v\n", q)
29 }
30 }
31 if len(m.Answers) > 0 {
32 s += "-- Answers\n"
33 for _, a := range m.Answers {
34 s += fmt.Sprintf("%#v\n", a)
35 }
36 }
37 if len(m.Authorities) > 0 {
38 s += "-- Authorities\n"
39 for _, ns := range m.Authorities {
40 s += fmt.Sprintf("%#v\n", ns)
41 }
42 }
43 if len(m.Additionals) > 0 {
44 s += "-- Additionals\n"
45 for _, e := range m.Additionals {
46 s += fmt.Sprintf("%#v\n", e)
47 }
48 }
49 return s
50}
51
52func TestNameString(t *testing.T) {
53 want := "foo"
54 name := mustNewName(want)
55 if got := fmt.Sprint(name); got != want {
56 t.Errorf("got fmt.Sprint(%#v) = %s, want = %s", name, got, want)
57 }
58}
59
60func TestQuestionPackUnpack(t *testing.T) {
61 want := Question{
62 Name: mustNewName("."),
63 Type: TypeA,
64 Class: ClassINET,
65 }
66 buf, err := want.pack(make([]byte, 1, 50), map[string]int{}, 1)
67 if err != nil {
68 t.Fatal("Packing failed:", err)
69 }
70 var p Parser
71 p.msg = buf
72 p.header.questions = 1
73 p.section = sectionQuestions
74 p.off = 1
75 got, err := p.Question()
76 if err != nil {
77 t.Fatalf("Unpacking failed: %v\n%s", err, string(buf[1:]))
78 }
79 if p.off != len(buf) {
80 t.Errorf("Unpacked different amount than packed: got n = %d, want = %d", p.off, len(buf))
81 }
82 if !reflect.DeepEqual(got, want) {
83 t.Errorf("Got = %+v, want = %+v", got, want)
84 }
85}
86
87func TestName(t *testing.T) {
88 tests := []string{
89 "",
90 ".",
91 "google..com",
92 "google.com",
93 "google..com.",
94 "google.com.",
95 ".google.com.",
96 "www..google.com.",
97 "www.google.com.",
98 }
99
100 for _, test := range tests {
101 n, err := NewName(test)
102 if err != nil {
103 t.Errorf("Creating name for %q: %v", test, err)
104 continue
105 }
106 if ns := n.String(); ns != test {
107 t.Errorf("Got %#v.String() = %q, want = %q", n, ns, test)
108 continue
109 }
110 }
111}
112
113func TestNamePackUnpack(t *testing.T) {
114 tests := []struct {
115 in string
116 want string
117 err error
118 }{
119 {"", "", errNonCanonicalName},
120 {".", ".", nil},
121 {"google..com", "", errNonCanonicalName},
122 {"google.com", "", errNonCanonicalName},
123 {"google..com.", "", errZeroSegLen},
124 {"google.com.", "google.com.", nil},
125 {".google.com.", "", errZeroSegLen},
126 {"www..google.com.", "", errZeroSegLen},
127 {"www.google.com.", "www.google.com.", nil},
128 }
129
130 for _, test := range tests {
131 in := mustNewName(test.in)
132 want := mustNewName(test.want)
133 buf, err := in.pack(make([]byte, 0, 30), map[string]int{}, 0)
134 if err != test.err {
135 t.Errorf("Packing of %q: got err = %v, want err = %v", test.in, err, test.err)
136 continue
137 }
138 if test.err != nil {
139 continue
140 }
141 var got Name
142 n, err := got.unpack(buf, 0)
143 if err != nil {
144 t.Errorf("Unpacking for %q failed: %v", test.in, err)
145 continue
146 }
147 if n != len(buf) {
148 t.Errorf(
149 "Unpacked different amount than packed for %q: got n = %d, want = %d",
150 test.in,
151 n,
152 len(buf),
153 )
154 }
155 if got != want {
156 t.Errorf("Unpacking packing of %q: got = %#v, want = %#v", test.in, got, want)
157 }
158 }
159}
160
161func TestIncompressibleName(t *testing.T) {
162 name := mustNewName("example.com.")
163 compression := map[string]int{}
164 buf, err := name.pack(make([]byte, 0, 100), compression, 0)
165 if err != nil {
166 t.Fatal("First packing failed:", err)
167 }
168 buf, err = name.pack(buf, compression, 0)
169 if err != nil {
170 t.Fatal("Second packing failed:", err)
171 }
172 var n1 Name
173 off, err := n1.unpackCompressed(buf, 0, false /* allowCompression */)
174 if err != nil {
175 t.Fatal("Unpacking incompressible name without pointers failed:", err)
176 }
177 var n2 Name
178 if _, err := n2.unpackCompressed(buf, off, false /* allowCompression */); err != errCompressedSRV {
179 t.Errorf("Unpacking compressed incompressible name with pointers: got err = %v, want = %v", err, errCompressedSRV)
180 }
181}
182
183func checkErrorPrefix(err error, prefix string) bool {
184 e, ok := err.(*nestedError)
185 return ok && e.s == prefix
186}
187
188func TestHeaderUnpackError(t *testing.T) {
189 wants := []string{
190 "id",
191 "bits",
192 "questions",
193 "answers",
194 "authorities",
195 "additionals",
196 }
197 var buf []byte
198 var h header
199 for _, want := range wants {
200 n, err := h.unpack(buf, 0)
201 if n != 0 || !checkErrorPrefix(err, want) {
202 t.Errorf("got h.unpack([%d]byte, 0) = %d, %v, want = 0, %s", len(buf), n, err, want)
203 }
204 buf = append(buf, 0, 0)
205 }
206}
207
208func TestParserStart(t *testing.T) {
209 const want = "unpacking header"
210 var p Parser
211 for i := 0; i <= 1; i++ {
212 _, err := p.Start([]byte{})
213 if !checkErrorPrefix(err, want) {
214 t.Errorf("got p.Start(nil) = _, %v, want = _, %s", err, want)
215 }
216 }
217}
218
219func TestResourceNotStarted(t *testing.T) {
220 tests := []struct {
221 name string
222 fn func(*Parser) error
223 }{
224 {"CNAMEResource", func(p *Parser) error { _, err := p.CNAMEResource(); return err }},
225 {"MXResource", func(p *Parser) error { _, err := p.MXResource(); return err }},
226 {"NSResource", func(p *Parser) error { _, err := p.NSResource(); return err }},
227 {"PTRResource", func(p *Parser) error { _, err := p.PTRResource(); return err }},
228 {"SOAResource", func(p *Parser) error { _, err := p.SOAResource(); return err }},
229 {"TXTResource", func(p *Parser) error { _, err := p.TXTResource(); return err }},
230 {"SRVResource", func(p *Parser) error { _, err := p.SRVResource(); return err }},
231 {"AResource", func(p *Parser) error { _, err := p.AResource(); return err }},
232 {"AAAAResource", func(p *Parser) error { _, err := p.AAAAResource(); return err }},
233 }
234
235 for _, test := range tests {
236 if err := test.fn(&Parser{}); err != ErrNotStarted {
237 t.Errorf("got _, %v = p.%s(), want = _, %v", err, test.name, ErrNotStarted)
238 }
239 }
240}
241
242func TestDNSPackUnpack(t *testing.T) {
243 wants := []Message{
244 {
245 Questions: []Question{
246 {
247 Name: mustNewName("."),
248 Type: TypeAAAA,
249 Class: ClassINET,
250 },
251 },
252 Answers: []Resource{},
253 Authorities: []Resource{},
254 Additionals: []Resource{},
255 },
256 largeTestMsg(),
257 }
258 for i, want := range wants {
259 b, err := want.Pack()
260 if err != nil {
261 t.Fatalf("%d: packing failed: %v", i, err)
262 }
263 var got Message
264 err = got.Unpack(b)
265 if err != nil {
266 t.Fatalf("%d: unpacking failed: %v", i, err)
267 }
268 if !reflect.DeepEqual(got, want) {
269 t.Errorf("%d: got = %+v, want = %+v", i, &got, &want)
270 }
271 }
272}
273
274func TestDNSAppendPackUnpack(t *testing.T) {
275 wants := []Message{
276 {
277 Questions: []Question{
278 {
279 Name: mustNewName("."),
280 Type: TypeAAAA,
281 Class: ClassINET,
282 },
283 },
284 Answers: []Resource{},
285 Authorities: []Resource{},
286 Additionals: []Resource{},
287 },
288 largeTestMsg(),
289 }
290 for i, want := range wants {
291 b := make([]byte, 2, 514)
292 b, err := want.AppendPack(b)
293 if err != nil {
294 t.Fatalf("%d: packing failed: %v", i, err)
295 }
296 b = b[2:]
297 var got Message
298 err = got.Unpack(b)
299 if err != nil {
300 t.Fatalf("%d: unpacking failed: %v", i, err)
301 }
302 if !reflect.DeepEqual(got, want) {
303 t.Errorf("%d: got = %+v, want = %+v", i, &got, &want)
304 }
305 }
306}
307
308func TestSkipAll(t *testing.T) {
309 msg := largeTestMsg()
310 buf, err := msg.Pack()
311 if err != nil {
312 t.Fatal("Packing large test message:", err)
313 }
314 var p Parser
315 if _, err := p.Start(buf); err != nil {
316 t.Fatal(err)
317 }
318
319 tests := []struct {
320 name string
321 f func() error
322 }{
323 {"SkipAllQuestions", p.SkipAllQuestions},
324 {"SkipAllAnswers", p.SkipAllAnswers},
325 {"SkipAllAuthorities", p.SkipAllAuthorities},
326 {"SkipAllAdditionals", p.SkipAllAdditionals},
327 }
328 for _, test := range tests {
329 for i := 1; i <= 3; i++ {
330 if err := test.f(); err != nil {
331 t.Errorf("Call #%d to %s(): %v", i, test.name, err)
332 }
333 }
334 }
335}
336
337func TestSkipEach(t *testing.T) {
338 msg := smallTestMsg()
339
340 buf, err := msg.Pack()
341 if err != nil {
342 t.Fatal("Packing test message:", err)
343 }
344 var p Parser
345 if _, err := p.Start(buf); err != nil {
346 t.Fatal(err)
347 }
348
349 tests := []struct {
350 name string
351 f func() error
352 }{
353 {"SkipQuestion", p.SkipQuestion},
354 {"SkipAnswer", p.SkipAnswer},
355 {"SkipAuthority", p.SkipAuthority},
356 {"SkipAdditional", p.SkipAdditional},
357 }
358 for _, test := range tests {
359 if err := test.f(); err != nil {
360 t.Errorf("First call: got %s() = %v, want = %v", test.name, err, nil)
361 }
362 if err := test.f(); err != ErrSectionDone {
363 t.Errorf("Second call: got %s() = %v, want = %v", test.name, err, ErrSectionDone)
364 }
365 }
366}
367
368func TestSkipAfterRead(t *testing.T) {
369 msg := smallTestMsg()
370
371 buf, err := msg.Pack()
372 if err != nil {
373 t.Fatal("Packing test message:", err)
374 }
375 var p Parser
376 if _, err := p.Start(buf); err != nil {
377 t.Fatal(err)
378 }
379
380 tests := []struct {
381 name string
382 skip func() error
383 read func() error
384 }{
385 {"Question", p.SkipQuestion, func() error { _, err := p.Question(); return err }},
386 {"Answer", p.SkipAnswer, func() error { _, err := p.Answer(); return err }},
387 {"Authority", p.SkipAuthority, func() error { _, err := p.Authority(); return err }},
388 {"Additional", p.SkipAdditional, func() error { _, err := p.Additional(); return err }},
389 }
390 for _, test := range tests {
391 if err := test.read(); err != nil {
392 t.Errorf("Got %s() = _, %v, want = _, %v", test.name, err, nil)
393 }
394 if err := test.skip(); err != ErrSectionDone {
395 t.Errorf("Got Skip%s() = %v, want = %v", test.name, err, ErrSectionDone)
396 }
397 }
398}
399
400func TestSkipNotStarted(t *testing.T) {
401 var p Parser
402
403 tests := []struct {
404 name string
405 f func() error
406 }{
407 {"SkipAllQuestions", p.SkipAllQuestions},
408 {"SkipAllAnswers", p.SkipAllAnswers},
409 {"SkipAllAuthorities", p.SkipAllAuthorities},
410 {"SkipAllAdditionals", p.SkipAllAdditionals},
411 }
412 for _, test := range tests {
413 if err := test.f(); err != ErrNotStarted {
414 t.Errorf("Got %s() = %v, want = %v", test.name, err, ErrNotStarted)
415 }
416 }
417}
418
419func TestTooManyRecords(t *testing.T) {
420 const recs = int(^uint16(0)) + 1
421 tests := []struct {
422 name string
423 msg Message
424 want error
425 }{
426 {
427 "Questions",
428 Message{
429 Questions: make([]Question, recs),
430 },
431 errTooManyQuestions,
432 },
433 {
434 "Answers",
435 Message{
436 Answers: make([]Resource, recs),
437 },
438 errTooManyAnswers,
439 },
440 {
441 "Authorities",
442 Message{
443 Authorities: make([]Resource, recs),
444 },
445 errTooManyAuthorities,
446 },
447 {
448 "Additionals",
449 Message{
450 Additionals: make([]Resource, recs),
451 },
452 errTooManyAdditionals,
453 },
454 }
455
456 for _, test := range tests {
457 if _, got := test.msg.Pack(); got != test.want {
458 t.Errorf("Packing %d %s: got = %v, want = %v", recs, test.name, got, test.want)
459 }
460 }
461}
462
463func TestVeryLongTxt(t *testing.T) {
464 want := Resource{
465 ResourceHeader{
466 Name: mustNewName("foo.bar.example.com."),
467 Type: TypeTXT,
468 Class: ClassINET,
469 },
470 &TXTResource{[]string{
471 "",
472 "",
473 "foo bar",
474 "",
475 "www.example.com",
476 "www.example.com.",
477 strings.Repeat(".", 255),
478 }},
479 }
480 buf, err := want.pack(make([]byte, 0, 8000), map[string]int{}, 0)
481 if err != nil {
482 t.Fatal("Packing failed:", err)
483 }
484 var got Resource
485 off, err := got.Header.unpack(buf, 0)
486 if err != nil {
487 t.Fatal("Unpacking ResourceHeader failed:", err)
488 }
489 body, n, err := unpackResourceBody(buf, off, got.Header)
490 if err != nil {
491 t.Fatal("Unpacking failed:", err)
492 }
493 got.Body = body
494 if n != len(buf) {
495 t.Errorf("Unpacked different amount than packed: got n = %d, want = %d", n, len(buf))
496 }
497 if !reflect.DeepEqual(got, want) {
498 t.Errorf("Got = %#v, want = %#v", got, want)
499 }
500}
501
502func TestTooLongTxt(t *testing.T) {
503 rb := TXTResource{[]string{strings.Repeat(".", 256)}}
504 if _, err := rb.pack(make([]byte, 0, 8000), map[string]int{}, 0); err != errStringTooLong {
505 t.Errorf("Packing TXTRecord with 256 character string: got err = %v, want = %v", err, errStringTooLong)
506 }
507}
508
509func TestStartAppends(t *testing.T) {
510 buf := make([]byte, 2, 514)
511 wantBuf := []byte{4, 44}
512 copy(buf, wantBuf)
513
514 b := NewBuilder(buf, Header{})
515 b.EnableCompression()
516
517 buf, err := b.Finish()
518 if err != nil {
519 t.Fatal("Building failed:", err)
520 }
521 if got, want := len(buf), headerLen+2; got != want {
522 t.Errorf("Got len(buf} = %d, want = %d", got, want)
523 }
524 if string(buf[:2]) != string(wantBuf) {
525 t.Errorf("Original data not preserved, got = %v, want = %v", buf[:2], wantBuf)
526 }
527}
528
529func TestStartError(t *testing.T) {
530 tests := []struct {
531 name string
532 fn func(*Builder) error
533 }{
534 {"Questions", func(b *Builder) error { return b.StartQuestions() }},
535 {"Answers", func(b *Builder) error { return b.StartAnswers() }},
536 {"Authorities", func(b *Builder) error { return b.StartAuthorities() }},
537 {"Additionals", func(b *Builder) error { return b.StartAdditionals() }},
538 }
539
540 envs := []struct {
541 name string
542 fn func() *Builder
543 want error
544 }{
545 {"sectionNotStarted", func() *Builder { return &Builder{section: sectionNotStarted} }, ErrNotStarted},
546 {"sectionDone", func() *Builder { return &Builder{section: sectionDone} }, ErrSectionDone},
547 }
548
549 for _, env := range envs {
550 for _, test := range tests {
551 if got := test.fn(env.fn()); got != env.want {
552 t.Errorf("got Builder{%s}.Start%s = %v, want = %v", env.name, test.name, got, env.want)
553 }
554 }
555 }
556}
557
558func TestBuilderResourceError(t *testing.T) {
559 tests := []struct {
560 name string
561 fn func(*Builder) error
562 }{
563 {"CNAMEResource", func(b *Builder) error { return b.CNAMEResource(ResourceHeader{}, CNAMEResource{}) }},
564 {"MXResource", func(b *Builder) error { return b.MXResource(ResourceHeader{}, MXResource{}) }},
565 {"NSResource", func(b *Builder) error { return b.NSResource(ResourceHeader{}, NSResource{}) }},
566 {"PTRResource", func(b *Builder) error { return b.PTRResource(ResourceHeader{}, PTRResource{}) }},
567 {"SOAResource", func(b *Builder) error { return b.SOAResource(ResourceHeader{}, SOAResource{}) }},
568 {"TXTResource", func(b *Builder) error { return b.TXTResource(ResourceHeader{}, TXTResource{}) }},
569 {"SRVResource", func(b *Builder) error { return b.SRVResource(ResourceHeader{}, SRVResource{}) }},
570 {"AResource", func(b *Builder) error { return b.AResource(ResourceHeader{}, AResource{}) }},
571 {"AAAAResource", func(b *Builder) error { return b.AAAAResource(ResourceHeader{}, AAAAResource{}) }},
572 }
573
574 envs := []struct {
575 name string
576 fn func() *Builder
577 want error
578 }{
579 {"sectionNotStarted", func() *Builder { return &Builder{section: sectionNotStarted} }, ErrNotStarted},
580 {"sectionHeader", func() *Builder { return &Builder{section: sectionHeader} }, ErrNotStarted},
581 {"sectionQuestions", func() *Builder { return &Builder{section: sectionQuestions} }, ErrNotStarted},
582 {"sectionDone", func() *Builder { return &Builder{section: sectionDone} }, ErrSectionDone},
583 }
584
585 for _, env := range envs {
586 for _, test := range tests {
587 if got := test.fn(env.fn()); got != env.want {
588 t.Errorf("got Builder{%s}.%s = %v, want = %v", env.name, test.name, got, env.want)
589 }
590 }
591 }
592}
593
594func TestFinishError(t *testing.T) {
595 var b Builder
596 want := ErrNotStarted
597 if _, got := b.Finish(); got != want {
598 t.Errorf("got Builder{}.Finish() = %v, want = %v", got, want)
599 }
600}
601
602func TestBuilder(t *testing.T) {
603 msg := largeTestMsg()
604 want, err := msg.Pack()
605 if err != nil {
606 t.Fatal("Packing without builder:", err)
607 }
608
609 b := NewBuilder(nil, msg.Header)
610 b.EnableCompression()
611
612 if err := b.StartQuestions(); err != nil {
613 t.Fatal("b.StartQuestions():", err)
614 }
615 for _, q := range msg.Questions {
616 if err := b.Question(q); err != nil {
617 t.Fatalf("b.Question(%#v): %v", q, err)
618 }
619 }
620
621 if err := b.StartAnswers(); err != nil {
622 t.Fatal("b.StartAnswers():", err)
623 }
624 for _, a := range msg.Answers {
625 switch a.Header.Type {
626 case TypeA:
627 if err := b.AResource(a.Header, *a.Body.(*AResource)); err != nil {
628 t.Fatalf("b.AResource(%#v): %v", a, err)
629 }
630 case TypeNS:
631 if err := b.NSResource(a.Header, *a.Body.(*NSResource)); err != nil {
632 t.Fatalf("b.NSResource(%#v): %v", a, err)
633 }
634 case TypeCNAME:
635 if err := b.CNAMEResource(a.Header, *a.Body.(*CNAMEResource)); err != nil {
636 t.Fatalf("b.CNAMEResource(%#v): %v", a, err)
637 }
638 case TypeSOA:
639 if err := b.SOAResource(a.Header, *a.Body.(*SOAResource)); err != nil {
640 t.Fatalf("b.SOAResource(%#v): %v", a, err)
641 }
642 case TypePTR:
643 if err := b.PTRResource(a.Header, *a.Body.(*PTRResource)); err != nil {
644 t.Fatalf("b.PTRResource(%#v): %v", a, err)
645 }
646 case TypeMX:
647 if err := b.MXResource(a.Header, *a.Body.(*MXResource)); err != nil {
648 t.Fatalf("b.MXResource(%#v): %v", a, err)
649 }
650 case TypeTXT:
651 if err := b.TXTResource(a.Header, *a.Body.(*TXTResource)); err != nil {
652 t.Fatalf("b.TXTResource(%#v): %v", a, err)
653 }
654 case TypeAAAA:
655 if err := b.AAAAResource(a.Header, *a.Body.(*AAAAResource)); err != nil {
656 t.Fatalf("b.AAAAResource(%#v): %v", a, err)
657 }
658 case TypeSRV:
659 if err := b.SRVResource(a.Header, *a.Body.(*SRVResource)); err != nil {
660 t.Fatalf("b.SRVResource(%#v): %v", a, err)
661 }
662 }
663 }
664
665 if err := b.StartAuthorities(); err != nil {
666 t.Fatal("b.StartAuthorities():", err)
667 }
668 for _, a := range msg.Authorities {
669 if err := b.NSResource(a.Header, *a.Body.(*NSResource)); err != nil {
670 t.Fatalf("b.NSResource(%#v): %v", a, err)
671 }
672 }
673
674 if err := b.StartAdditionals(); err != nil {
675 t.Fatal("b.StartAdditionals():", err)
676 }
677 for _, a := range msg.Additionals {
678 if err := b.TXTResource(a.Header, *a.Body.(*TXTResource)); err != nil {
679 t.Fatalf("b.TXTResource(%#v): %v", a, err)
680 }
681 }
682
683 got, err := b.Finish()
684 if err != nil {
685 t.Fatal("b.Finish():", err)
686 }
687 if !bytes.Equal(got, want) {
688 t.Fatalf("Got from Builder: %#v\nwant = %#v", got, want)
689 }
690}
691
692func TestResourcePack(t *testing.T) {
693 for _, tt := range []struct {
694 m Message
695 err error
696 }{
697 {
698 Message{
699 Questions: []Question{
700 {
701 Name: mustNewName("."),
702 Type: TypeAAAA,
703 Class: ClassINET,
704 },
705 },
706 Answers: []Resource{{ResourceHeader{}, nil}},
707 },
708 &nestedError{"packing Answer", errNilResouceBody},
709 },
710 {
711 Message{
712 Questions: []Question{
713 {
714 Name: mustNewName("."),
715 Type: TypeAAAA,
716 Class: ClassINET,
717 },
718 },
719 Authorities: []Resource{{ResourceHeader{}, (*NSResource)(nil)}},
720 },
721 &nestedError{"packing Authority",
722 &nestedError{"ResourceHeader",
723 &nestedError{"Name", errNonCanonicalName},
724 },
725 },
726 },
727 {
728 Message{
729 Questions: []Question{
730 {
731 Name: mustNewName("."),
732 Type: TypeA,
733 Class: ClassINET,
734 },
735 },
736 Additionals: []Resource{{ResourceHeader{}, nil}},
737 },
738 &nestedError{"packing Additional", errNilResouceBody},
739 },
740 } {
741 _, err := tt.m.Pack()
742 if !reflect.DeepEqual(err, tt.err) {
743 t.Errorf("got %v for %v; want %v", err, tt.m, tt.err)
744 }
745 }
746}
747
748func benchmarkParsingSetup() ([]byte, error) {
749 name := mustNewName("foo.bar.example.com.")
750 msg := Message{
751 Header: Header{Response: true, Authoritative: true},
752 Questions: []Question{
753 {
754 Name: name,
755 Type: TypeA,
756 Class: ClassINET,
757 },
758 },
759 Answers: []Resource{
760 {
761 ResourceHeader{
762 Name: name,
763 Class: ClassINET,
764 },
765 &AResource{[4]byte{}},
766 },
767 {
768 ResourceHeader{
769 Name: name,
770 Class: ClassINET,
771 },
772 &AAAAResource{[16]byte{}},
773 },
774 {
775 ResourceHeader{
776 Name: name,
777 Class: ClassINET,
778 },
779 &CNAMEResource{name},
780 },
781 {
782 ResourceHeader{
783 Name: name,
784 Class: ClassINET,
785 },
786 &NSResource{name},
787 },
788 },
789 }
790
791 buf, err := msg.Pack()
792 if err != nil {
793 return nil, fmt.Errorf("msg.Pack(): %v", err)
794 }
795 return buf, nil
796}
797
798func benchmarkParsing(tb testing.TB, buf []byte) {
799 var p Parser
800 if _, err := p.Start(buf); err != nil {
801 tb.Fatal("p.Start(buf):", err)
802 }
803
804 for {
805 _, err := p.Question()
806 if err == ErrSectionDone {
807 break
808 }
809 if err != nil {
810 tb.Fatal("p.Question():", err)
811 }
812 }
813
814 for {
815 h, err := p.AnswerHeader()
816 if err == ErrSectionDone {
817 break
818 }
819 if err != nil {
820 panic(err)
821 }
822
823 switch h.Type {
824 case TypeA:
825 if _, err := p.AResource(); err != nil {
826 tb.Fatal("p.AResource():", err)
827 }
828 case TypeAAAA:
829 if _, err := p.AAAAResource(); err != nil {
830 tb.Fatal("p.AAAAResource():", err)
831 }
832 case TypeCNAME:
833 if _, err := p.CNAMEResource(); err != nil {
834 tb.Fatal("p.CNAMEResource():", err)
835 }
836 case TypeNS:
837 if _, err := p.NSResource(); err != nil {
838 tb.Fatal("p.NSResource():", err)
839 }
840 default:
841 tb.Fatalf("unknown type: %T", h)
842 }
843 }
844}
845
846func BenchmarkParsing(b *testing.B) {
847 buf, err := benchmarkParsingSetup()
848 if err != nil {
849 b.Fatal(err)
850 }
851
852 b.ReportAllocs()
853 for i := 0; i < b.N; i++ {
854 benchmarkParsing(b, buf)
855 }
856}
857
858func TestParsingAllocs(t *testing.T) {
859 buf, err := benchmarkParsingSetup()
860 if err != nil {
861 t.Fatal(err)
862 }
863
864 if allocs := testing.AllocsPerRun(100, func() { benchmarkParsing(t, buf) }); allocs > 0.5 {
865 t.Errorf("Allocations during parsing: got = %f, want ~0", allocs)
866 }
867}
868
869func benchmarkBuildingSetup() (Name, []byte) {
870 name := mustNewName("foo.bar.example.com.")
871 buf := make([]byte, 0, packStartingCap)
872 return name, buf
873}
874
875func benchmarkBuilding(tb testing.TB, name Name, buf []byte) {
876 bld := NewBuilder(buf, Header{Response: true, Authoritative: true})
877
878 if err := bld.StartQuestions(); err != nil {
879 tb.Fatal("bld.StartQuestions():", err)
880 }
881 q := Question{
882 Name: name,
883 Type: TypeA,
884 Class: ClassINET,
885 }
886 if err := bld.Question(q); err != nil {
887 tb.Fatalf("bld.Question(%+v): %v", q, err)
888 }
889
890 hdr := ResourceHeader{
891 Name: name,
892 Class: ClassINET,
893 }
894 if err := bld.StartAnswers(); err != nil {
895 tb.Fatal("bld.StartQuestions():", err)
896 }
897
898 ar := AResource{[4]byte{}}
899 if err := bld.AResource(hdr, ar); err != nil {
900 tb.Fatalf("bld.AResource(%+v, %+v): %v", hdr, ar, err)
901 }
902
903 aaar := AAAAResource{[16]byte{}}
904 if err := bld.AAAAResource(hdr, aaar); err != nil {
905 tb.Fatalf("bld.AAAAResource(%+v, %+v): %v", hdr, aaar, err)
906 }
907
908 cnr := CNAMEResource{name}
909 if err := bld.CNAMEResource(hdr, cnr); err != nil {
910 tb.Fatalf("bld.CNAMEResource(%+v, %+v): %v", hdr, cnr, err)
911 }
912
913 nsr := NSResource{name}
914 if err := bld.NSResource(hdr, nsr); err != nil {
915 tb.Fatalf("bld.NSResource(%+v, %+v): %v", hdr, nsr, err)
916 }
917
918 if _, err := bld.Finish(); err != nil {
919 tb.Fatal("bld.Finish():", err)
920 }
921}
922
923func BenchmarkBuilding(b *testing.B) {
924 name, buf := benchmarkBuildingSetup()
925 b.ReportAllocs()
926 for i := 0; i < b.N; i++ {
927 benchmarkBuilding(b, name, buf)
928 }
929}
930
931func TestBuildingAllocs(t *testing.T) {
932 name, buf := benchmarkBuildingSetup()
933 if allocs := testing.AllocsPerRun(100, func() { benchmarkBuilding(t, name, buf) }); allocs > 0.5 {
934 t.Errorf("Allocations during building: got = %f, want ~0", allocs)
935 }
936}
937
938func smallTestMsg() Message {
939 name := mustNewName("example.com.")
940 return Message{
941 Header: Header{Response: true, Authoritative: true},
942 Questions: []Question{
943 {
944 Name: name,
945 Type: TypeA,
946 Class: ClassINET,
947 },
948 },
949 Answers: []Resource{
950 {
951 ResourceHeader{
952 Name: name,
953 Type: TypeA,
954 Class: ClassINET,
955 },
956 &AResource{[4]byte{127, 0, 0, 1}},
957 },
958 },
959 Authorities: []Resource{
960 {
961 ResourceHeader{
962 Name: name,
963 Type: TypeA,
964 Class: ClassINET,
965 },
966 &AResource{[4]byte{127, 0, 0, 1}},
967 },
968 },
969 Additionals: []Resource{
970 {
971 ResourceHeader{
972 Name: name,
973 Type: TypeA,
974 Class: ClassINET,
975 },
976 &AResource{[4]byte{127, 0, 0, 1}},
977 },
978 },
979 }
980}
981
982func BenchmarkPack(b *testing.B) {
983 msg := largeTestMsg()
984
985 b.ReportAllocs()
986
987 for i := 0; i < b.N; i++ {
988 if _, err := msg.Pack(); err != nil {
989 b.Fatal(err)
990 }
991 }
992}
993
994func BenchmarkAppendPack(b *testing.B) {
995 msg := largeTestMsg()
996 buf := make([]byte, 0, packStartingCap)
997
998 b.ReportAllocs()
999
1000 for i := 0; i < b.N; i++ {
1001 if _, err := msg.AppendPack(buf[:0]); err != nil {
1002 b.Fatal(err)
1003 }
1004 }
1005}
1006
1007func largeTestMsg() Message {
1008 name := mustNewName("foo.bar.example.com.")
1009 return Message{
1010 Header: Header{Response: true, Authoritative: true},
1011 Questions: []Question{
1012 {
1013 Name: name,
1014 Type: TypeA,
1015 Class: ClassINET,
1016 },
1017 },
1018 Answers: []Resource{
1019 {
1020 ResourceHeader{
1021 Name: name,
1022 Type: TypeA,
1023 Class: ClassINET,
1024 },
1025 &AResource{[4]byte{127, 0, 0, 1}},
1026 },
1027 {
1028 ResourceHeader{
1029 Name: name,
1030 Type: TypeA,
1031 Class: ClassINET,
1032 },
1033 &AResource{[4]byte{127, 0, 0, 2}},
1034 },
1035 {
1036 ResourceHeader{
1037 Name: name,
1038 Type: TypeAAAA,
1039 Class: ClassINET,
1040 },
1041 &AAAAResource{[16]byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}},
1042 },
1043 {
1044 ResourceHeader{
1045 Name: name,
1046 Type: TypeCNAME,
1047 Class: ClassINET,
1048 },
1049 &CNAMEResource{mustNewName("alias.example.com.")},
1050 },
1051 {
1052 ResourceHeader{
1053 Name: name,
1054 Type: TypeSOA,
1055 Class: ClassINET,
1056 },
1057 &SOAResource{
1058 NS: mustNewName("ns1.example.com."),
1059 MBox: mustNewName("mb.example.com."),
1060 Serial: 1,
1061 Refresh: 2,
1062 Retry: 3,
1063 Expire: 4,
1064 MinTTL: 5,
1065 },
1066 },
1067 {
1068 ResourceHeader{
1069 Name: name,
1070 Type: TypePTR,
1071 Class: ClassINET,
1072 },
1073 &PTRResource{mustNewName("ptr.example.com.")},
1074 },
1075 {
1076 ResourceHeader{
1077 Name: name,
1078 Type: TypeMX,
1079 Class: ClassINET,
1080 },
1081 &MXResource{
1082 7,
1083 mustNewName("mx.example.com."),
1084 },
1085 },
1086 {
1087 ResourceHeader{
1088 Name: name,
1089 Type: TypeSRV,
1090 Class: ClassINET,
1091 },
1092 &SRVResource{
1093 8,
1094 9,
1095 11,
1096 mustNewName("srv.example.com."),
1097 },
1098 },
1099 },
1100 Authorities: []Resource{
1101 {
1102 ResourceHeader{
1103 Name: name,
1104 Type: TypeNS,
1105 Class: ClassINET,
1106 },
1107 &NSResource{mustNewName("ns1.example.com.")},
1108 },
1109 {
1110 ResourceHeader{
1111 Name: name,
1112 Type: TypeNS,
1113 Class: ClassINET,
1114 },
1115 &NSResource{mustNewName("ns2.example.com.")},
1116 },
1117 },
1118 Additionals: []Resource{
1119 {
1120 ResourceHeader{
1121 Name: name,
1122 Type: TypeTXT,
1123 Class: ClassINET,
1124 },
1125 &TXTResource{[]string{"So Long, and Thanks for All the Fish"}},
1126 },
1127 {
1128 ResourceHeader{
1129 Name: name,
1130 Type: TypeTXT,
1131 Class: ClassINET,
1132 },
1133 &TXTResource{[]string{"Hamster Huey and the Gooey Kablooie"}},
1134 },
1135 },
1136 }
1137}