github.com/icodeface/tls@v0.0.0-20230910023335-34df9250cd12/internal/x/net/dns/dnsmessage/message_test.go (about)

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