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
5package dnsmessage
6
7import (
8	"bytes"
9	"fmt"
10	"reflect"
11	"strings"
12	"testing"
13)
14
15func 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
39func 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
60func 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
81func 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
108func 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
116func (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
145func 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
153func 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
180func 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
206func 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
254func 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
276func checkErrorPrefix(err error, prefix string) bool {
277	e, ok := err.(*nestedError)
278	return ok && e.s == prefix
279}
280
281func 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
301func 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
312func 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
335func 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
367func 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
401func 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
430func 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
461func 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
493func 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
512func 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
556func 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
595func 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
602func 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
622func 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
651func 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
688func 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
696func 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
793func 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
849func 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
879func 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.
1026func 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
1038func 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
1088func 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
1140func 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
1152func 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
1163func benchmarkBuildingSetup() (Name, []byte) {
1164	name := MustNewName("foo.bar.example.com.")
1165	buf := make([]byte, 0, packStartingCap)
1166	return name, buf
1167}
1168
1169func 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
1226func 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
1234func 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
1241func 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
1285func 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
1297func 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
1310func 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}
1452