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