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 }