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