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