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