github.com/megatontech/mynoteforgo@v0.0.0-20200507084910-5d0c6ea6e890/源码/net/dnsclient_unix_test.go (about) 1 // Copyright 2013 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 // +build aix darwin dragonfly freebsd linux netbsd openbsd solaris 6 7 package net 8 9 import ( 10 "context" 11 "errors" 12 "fmt" 13 "internal/poll" 14 "io/ioutil" 15 "os" 16 "path" 17 "reflect" 18 "strings" 19 "sync" 20 "testing" 21 "time" 22 23 "internal/x/net/dns/dnsmessage" 24 ) 25 26 var goResolver = Resolver{PreferGo: true} 27 28 // Test address from 192.0.2.0/24 block, reserved by RFC 5737 for documentation. 29 var TestAddr = [4]byte{0xc0, 0x00, 0x02, 0x01} 30 31 // Test address from 2001:db8::/32 block, reserved by RFC 3849 for documentation. 32 var TestAddr6 = [16]byte{0x20, 0x01, 0x0d, 0xb8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1} 33 34 func mustNewName(name string) dnsmessage.Name { 35 nn, err := dnsmessage.NewName(name) 36 if err != nil { 37 panic(fmt.Sprint("creating name: ", err)) 38 } 39 return nn 40 } 41 42 func mustQuestion(name string, qtype dnsmessage.Type, class dnsmessage.Class) dnsmessage.Question { 43 return dnsmessage.Question{ 44 Name: mustNewName(name), 45 Type: qtype, 46 Class: class, 47 } 48 } 49 50 var dnsTransportFallbackTests = []struct { 51 server string 52 question dnsmessage.Question 53 timeout int 54 rcode dnsmessage.RCode 55 }{ 56 // Querying "com." with qtype=255 usually makes an answer 57 // which requires more than 512 bytes. 58 {"8.8.8.8:53", mustQuestion("com.", dnsmessage.TypeALL, dnsmessage.ClassINET), 2, dnsmessage.RCodeSuccess}, 59 {"8.8.4.4:53", mustQuestion("com.", dnsmessage.TypeALL, dnsmessage.ClassINET), 4, dnsmessage.RCodeSuccess}, 60 } 61 62 func TestDNSTransportFallback(t *testing.T) { 63 fake := fakeDNSServer{ 64 rh: func(n, _ string, q dnsmessage.Message, _ time.Time) (dnsmessage.Message, error) { 65 r := dnsmessage.Message{ 66 Header: dnsmessage.Header{ 67 ID: q.Header.ID, 68 Response: true, 69 RCode: dnsmessage.RCodeSuccess, 70 }, 71 Questions: q.Questions, 72 } 73 if n == "udp" { 74 r.Header.Truncated = true 75 } 76 return r, nil 77 }, 78 } 79 r := Resolver{PreferGo: true, Dial: fake.DialContext} 80 for _, tt := range dnsTransportFallbackTests { 81 ctx, cancel := context.WithCancel(context.Background()) 82 defer cancel() 83 _, h, err := r.exchange(ctx, tt.server, tt.question, time.Second) 84 if err != nil { 85 t.Error(err) 86 continue 87 } 88 if h.RCode != tt.rcode { 89 t.Errorf("got %v from %v; want %v", h.RCode, tt.server, tt.rcode) 90 continue 91 } 92 } 93 } 94 95 // See RFC 6761 for further information about the reserved, pseudo 96 // domain names. 97 var specialDomainNameTests = []struct { 98 question dnsmessage.Question 99 rcode dnsmessage.RCode 100 }{ 101 // Name resolution APIs and libraries should not recognize the 102 // followings as special. 103 {mustQuestion("1.0.168.192.in-addr.arpa.", dnsmessage.TypePTR, dnsmessage.ClassINET), dnsmessage.RCodeNameError}, 104 {mustQuestion("test.", dnsmessage.TypeALL, dnsmessage.ClassINET), dnsmessage.RCodeNameError}, 105 {mustQuestion("example.com.", dnsmessage.TypeALL, dnsmessage.ClassINET), dnsmessage.RCodeSuccess}, 106 107 // Name resolution APIs and libraries should recognize the 108 // followings as special and should not send any queries. 109 // Though, we test those names here for verifying negative 110 // answers at DNS query-response interaction level. 111 {mustQuestion("localhost.", dnsmessage.TypeALL, dnsmessage.ClassINET), dnsmessage.RCodeNameError}, 112 {mustQuestion("invalid.", dnsmessage.TypeALL, dnsmessage.ClassINET), dnsmessage.RCodeNameError}, 113 } 114 115 func TestSpecialDomainName(t *testing.T) { 116 fake := fakeDNSServer{rh: func(_, _ string, q dnsmessage.Message, _ time.Time) (dnsmessage.Message, error) { 117 r := dnsmessage.Message{ 118 Header: dnsmessage.Header{ 119 ID: q.ID, 120 Response: true, 121 }, 122 Questions: q.Questions, 123 } 124 125 switch q.Questions[0].Name.String() { 126 case "example.com.": 127 r.Header.RCode = dnsmessage.RCodeSuccess 128 default: 129 r.Header.RCode = dnsmessage.RCodeNameError 130 } 131 132 return r, nil 133 }} 134 r := Resolver{PreferGo: true, Dial: fake.DialContext} 135 server := "8.8.8.8:53" 136 for _, tt := range specialDomainNameTests { 137 ctx, cancel := context.WithCancel(context.Background()) 138 defer cancel() 139 _, h, err := r.exchange(ctx, server, tt.question, 3*time.Second) 140 if err != nil { 141 t.Error(err) 142 continue 143 } 144 if h.RCode != tt.rcode { 145 t.Errorf("got %v from %v; want %v", h.RCode, server, tt.rcode) 146 continue 147 } 148 } 149 } 150 151 // Issue 13705: don't try to resolve onion addresses, etc 152 func TestAvoidDNSName(t *testing.T) { 153 tests := []struct { 154 name string 155 avoid bool 156 }{ 157 {"foo.com", false}, 158 {"foo.com.", false}, 159 160 {"foo.onion.", true}, 161 {"foo.onion", true}, 162 {"foo.ONION", true}, 163 {"foo.ONION.", true}, 164 165 // But do resolve *.local address; Issue 16739 166 {"foo.local.", false}, 167 {"foo.local", false}, 168 {"foo.LOCAL", false}, 169 {"foo.LOCAL.", false}, 170 171 {"", true}, // will be rejected earlier too 172 173 // Without stuff before onion/local, they're fine to 174 // use DNS. With a search path, 175 // "onion.vegegtables.com" can use DNS. Without a 176 // search path (or with a trailing dot), the queries 177 // are just kinda useless, but don't reveal anything 178 // private. 179 {"local", false}, 180 {"onion", false}, 181 {"local.", false}, 182 {"onion.", false}, 183 } 184 for _, tt := range tests { 185 got := avoidDNS(tt.name) 186 if got != tt.avoid { 187 t.Errorf("avoidDNS(%q) = %v; want %v", tt.name, got, tt.avoid) 188 } 189 } 190 } 191 192 var fakeDNSServerSuccessful = fakeDNSServer{rh: func(_, _ string, q dnsmessage.Message, _ time.Time) (dnsmessage.Message, error) { 193 r := dnsmessage.Message{ 194 Header: dnsmessage.Header{ 195 ID: q.ID, 196 Response: true, 197 }, 198 Questions: q.Questions, 199 } 200 if len(q.Questions) == 1 && q.Questions[0].Type == dnsmessage.TypeA { 201 r.Answers = []dnsmessage.Resource{ 202 { 203 Header: dnsmessage.ResourceHeader{ 204 Name: q.Questions[0].Name, 205 Type: dnsmessage.TypeA, 206 Class: dnsmessage.ClassINET, 207 Length: 4, 208 }, 209 Body: &dnsmessage.AResource{ 210 A: TestAddr, 211 }, 212 }, 213 } 214 } 215 return r, nil 216 }} 217 218 // Issue 13705: don't try to resolve onion addresses, etc 219 func TestLookupTorOnion(t *testing.T) { 220 defer dnsWaitGroup.Wait() 221 r := Resolver{PreferGo: true, Dial: fakeDNSServerSuccessful.DialContext} 222 addrs, err := r.LookupIPAddr(context.Background(), "foo.onion") 223 if err != nil { 224 t.Fatalf("lookup = %v; want nil", err) 225 } 226 if len(addrs) > 0 { 227 t.Errorf("unexpected addresses: %v", addrs) 228 } 229 } 230 231 type resolvConfTest struct { 232 dir string 233 path string 234 *resolverConfig 235 } 236 237 func newResolvConfTest() (*resolvConfTest, error) { 238 dir, err := ioutil.TempDir("", "go-resolvconftest") 239 if err != nil { 240 return nil, err 241 } 242 conf := &resolvConfTest{ 243 dir: dir, 244 path: path.Join(dir, "resolv.conf"), 245 resolverConfig: &resolvConf, 246 } 247 conf.initOnce.Do(conf.init) 248 return conf, nil 249 } 250 251 func (conf *resolvConfTest) writeAndUpdate(lines []string) error { 252 f, err := os.OpenFile(conf.path, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0600) 253 if err != nil { 254 return err 255 } 256 if _, err := f.WriteString(strings.Join(lines, "\n")); err != nil { 257 f.Close() 258 return err 259 } 260 f.Close() 261 if err := conf.forceUpdate(conf.path, time.Now().Add(time.Hour)); err != nil { 262 return err 263 } 264 return nil 265 } 266 267 func (conf *resolvConfTest) forceUpdate(name string, lastChecked time.Time) error { 268 dnsConf := dnsReadConfig(name) 269 conf.mu.Lock() 270 conf.dnsConfig = dnsConf 271 conf.mu.Unlock() 272 for i := 0; i < 5; i++ { 273 if conf.tryAcquireSema() { 274 conf.lastChecked = lastChecked 275 conf.releaseSema() 276 return nil 277 } 278 } 279 return fmt.Errorf("tryAcquireSema for %s failed", name) 280 } 281 282 func (conf *resolvConfTest) servers() []string { 283 conf.mu.RLock() 284 servers := conf.dnsConfig.servers 285 conf.mu.RUnlock() 286 return servers 287 } 288 289 func (conf *resolvConfTest) teardown() error { 290 err := conf.forceUpdate("/etc/resolv.conf", time.Time{}) 291 os.RemoveAll(conf.dir) 292 return err 293 } 294 295 var updateResolvConfTests = []struct { 296 name string // query name 297 lines []string // resolver configuration lines 298 servers []string // expected name servers 299 }{ 300 { 301 name: "golang.org", 302 lines: []string{"nameserver 8.8.8.8"}, 303 servers: []string{"8.8.8.8:53"}, 304 }, 305 { 306 name: "", 307 lines: nil, // an empty resolv.conf should use defaultNS as name servers 308 servers: defaultNS, 309 }, 310 { 311 name: "www.example.com", 312 lines: []string{"nameserver 8.8.4.4"}, 313 servers: []string{"8.8.4.4:53"}, 314 }, 315 } 316 317 func TestUpdateResolvConf(t *testing.T) { 318 defer dnsWaitGroup.Wait() 319 320 r := Resolver{PreferGo: true, Dial: fakeDNSServerSuccessful.DialContext} 321 322 conf, err := newResolvConfTest() 323 if err != nil { 324 t.Fatal(err) 325 } 326 defer conf.teardown() 327 328 for i, tt := range updateResolvConfTests { 329 if err := conf.writeAndUpdate(tt.lines); err != nil { 330 t.Error(err) 331 continue 332 } 333 if tt.name != "" { 334 var wg sync.WaitGroup 335 const N = 10 336 wg.Add(N) 337 for j := 0; j < N; j++ { 338 go func(name string) { 339 defer wg.Done() 340 ips, err := r.LookupIPAddr(context.Background(), name) 341 if err != nil { 342 t.Error(err) 343 return 344 } 345 if len(ips) == 0 { 346 t.Errorf("no records for %s", name) 347 return 348 } 349 }(tt.name) 350 } 351 wg.Wait() 352 } 353 servers := conf.servers() 354 if !reflect.DeepEqual(servers, tt.servers) { 355 t.Errorf("#%d: got %v; want %v", i, servers, tt.servers) 356 continue 357 } 358 } 359 } 360 361 var goLookupIPWithResolverConfigTests = []struct { 362 name string 363 lines []string // resolver configuration lines 364 error 365 a, aaaa bool // whether response contains A, AAAA-record 366 }{ 367 // no records, transport timeout 368 { 369 "jgahvsekduiv9bw4b3qhn4ykdfgj0493iohkrjfhdvhjiu4j", 370 []string{ 371 "options timeout:1 attempts:1", 372 "nameserver 255.255.255.255", // please forgive us for abuse of limited broadcast address 373 }, 374 &DNSError{Name: "jgahvsekduiv9bw4b3qhn4ykdfgj0493iohkrjfhdvhjiu4j", Server: "255.255.255.255:53", IsTimeout: true}, 375 false, false, 376 }, 377 378 // no records, non-existent domain 379 { 380 "jgahvsekduiv9bw4b3qhn4ykdfgj0493iohkrjfhdvhjiu4j", 381 []string{ 382 "options timeout:3 attempts:1", 383 "nameserver 8.8.8.8", 384 }, 385 &DNSError{Name: "jgahvsekduiv9bw4b3qhn4ykdfgj0493iohkrjfhdvhjiu4j", Server: "8.8.8.8:53", IsTimeout: false}, 386 false, false, 387 }, 388 389 // a few A records, no AAAA records 390 { 391 "ipv4.google.com.", 392 []string{ 393 "nameserver 8.8.8.8", 394 "nameserver 2001:4860:4860::8888", 395 }, 396 nil, 397 true, false, 398 }, 399 { 400 "ipv4.google.com", 401 []string{ 402 "domain golang.org", 403 "nameserver 2001:4860:4860::8888", 404 "nameserver 8.8.8.8", 405 }, 406 nil, 407 true, false, 408 }, 409 { 410 "ipv4.google.com", 411 []string{ 412 "search x.golang.org y.golang.org", 413 "nameserver 2001:4860:4860::8888", 414 "nameserver 8.8.8.8", 415 }, 416 nil, 417 true, false, 418 }, 419 420 // no A records, a few AAAA records 421 { 422 "ipv6.google.com.", 423 []string{ 424 "nameserver 2001:4860:4860::8888", 425 "nameserver 8.8.8.8", 426 }, 427 nil, 428 false, true, 429 }, 430 { 431 "ipv6.google.com", 432 []string{ 433 "domain golang.org", 434 "nameserver 8.8.8.8", 435 "nameserver 2001:4860:4860::8888", 436 }, 437 nil, 438 false, true, 439 }, 440 { 441 "ipv6.google.com", 442 []string{ 443 "search x.golang.org y.golang.org", 444 "nameserver 8.8.8.8", 445 "nameserver 2001:4860:4860::8888", 446 }, 447 nil, 448 false, true, 449 }, 450 451 // both A and AAAA records 452 { 453 "hostname.as112.net", // see RFC 7534 454 []string{ 455 "domain golang.org", 456 "nameserver 2001:4860:4860::8888", 457 "nameserver 8.8.8.8", 458 }, 459 nil, 460 true, true, 461 }, 462 { 463 "hostname.as112.net", // see RFC 7534 464 []string{ 465 "search x.golang.org y.golang.org", 466 "nameserver 2001:4860:4860::8888", 467 "nameserver 8.8.8.8", 468 }, 469 nil, 470 true, true, 471 }, 472 } 473 474 func TestGoLookupIPWithResolverConfig(t *testing.T) { 475 defer dnsWaitGroup.Wait() 476 fake := fakeDNSServer{rh: func(n, s string, q dnsmessage.Message, _ time.Time) (dnsmessage.Message, error) { 477 switch s { 478 case "[2001:4860:4860::8888]:53", "8.8.8.8:53": 479 break 480 default: 481 time.Sleep(10 * time.Millisecond) 482 return dnsmessage.Message{}, poll.ErrTimeout 483 } 484 r := dnsmessage.Message{ 485 Header: dnsmessage.Header{ 486 ID: q.ID, 487 Response: true, 488 }, 489 Questions: q.Questions, 490 } 491 for _, question := range q.Questions { 492 switch question.Type { 493 case dnsmessage.TypeA: 494 switch question.Name.String() { 495 case "hostname.as112.net.": 496 break 497 case "ipv4.google.com.": 498 r.Answers = append(r.Answers, dnsmessage.Resource{ 499 Header: dnsmessage.ResourceHeader{ 500 Name: q.Questions[0].Name, 501 Type: dnsmessage.TypeA, 502 Class: dnsmessage.ClassINET, 503 Length: 4, 504 }, 505 Body: &dnsmessage.AResource{ 506 A: TestAddr, 507 }, 508 }) 509 default: 510 511 } 512 case dnsmessage.TypeAAAA: 513 switch question.Name.String() { 514 case "hostname.as112.net.": 515 break 516 case "ipv6.google.com.": 517 r.Answers = append(r.Answers, dnsmessage.Resource{ 518 Header: dnsmessage.ResourceHeader{ 519 Name: q.Questions[0].Name, 520 Type: dnsmessage.TypeAAAA, 521 Class: dnsmessage.ClassINET, 522 Length: 16, 523 }, 524 Body: &dnsmessage.AAAAResource{ 525 AAAA: TestAddr6, 526 }, 527 }) 528 } 529 } 530 } 531 return r, nil 532 }} 533 r := Resolver{PreferGo: true, Dial: fake.DialContext} 534 535 conf, err := newResolvConfTest() 536 if err != nil { 537 t.Fatal(err) 538 } 539 defer conf.teardown() 540 541 for _, tt := range goLookupIPWithResolverConfigTests { 542 if err := conf.writeAndUpdate(tt.lines); err != nil { 543 t.Error(err) 544 continue 545 } 546 addrs, err := r.LookupIPAddr(context.Background(), tt.name) 547 if err != nil { 548 if err, ok := err.(*DNSError); !ok || tt.error != nil && (err.Name != tt.error.(*DNSError).Name || err.Server != tt.error.(*DNSError).Server || err.IsTimeout != tt.error.(*DNSError).IsTimeout) { 549 t.Errorf("got %v; want %v", err, tt.error) 550 } 551 continue 552 } 553 if len(addrs) == 0 { 554 t.Errorf("no records for %s", tt.name) 555 } 556 if !tt.a && !tt.aaaa && len(addrs) > 0 { 557 t.Errorf("unexpected %v for %s", addrs, tt.name) 558 } 559 for _, addr := range addrs { 560 if !tt.a && addr.IP.To4() != nil { 561 t.Errorf("got %v; must not be IPv4 address", addr) 562 } 563 if !tt.aaaa && addr.IP.To16() != nil && addr.IP.To4() == nil { 564 t.Errorf("got %v; must not be IPv6 address", addr) 565 } 566 } 567 } 568 } 569 570 // Test that goLookupIPOrder falls back to the host file when no DNS servers are available. 571 func TestGoLookupIPOrderFallbackToFile(t *testing.T) { 572 defer dnsWaitGroup.Wait() 573 574 fake := fakeDNSServer{rh: func(n, s string, q dnsmessage.Message, tm time.Time) (dnsmessage.Message, error) { 575 r := dnsmessage.Message{ 576 Header: dnsmessage.Header{ 577 ID: q.ID, 578 Response: true, 579 }, 580 Questions: q.Questions, 581 } 582 return r, nil 583 }} 584 r := Resolver{PreferGo: true, Dial: fake.DialContext} 585 586 // Add a config that simulates no dns servers being available. 587 conf, err := newResolvConfTest() 588 if err != nil { 589 t.Fatal(err) 590 } 591 if err := conf.writeAndUpdate([]string{}); err != nil { 592 t.Fatal(err) 593 } 594 // Redirect host file lookups. 595 defer func(orig string) { testHookHostsPath = orig }(testHookHostsPath) 596 testHookHostsPath = "testdata/hosts" 597 598 for _, order := range []hostLookupOrder{hostLookupFilesDNS, hostLookupDNSFiles} { 599 name := fmt.Sprintf("order %v", order) 600 601 // First ensure that we get an error when contacting a non-existent host. 602 _, _, err := r.goLookupIPCNAMEOrder(context.Background(), "notarealhost", order) 603 if err == nil { 604 t.Errorf("%s: expected error while looking up name not in hosts file", name) 605 continue 606 } 607 608 // Now check that we get an address when the name appears in the hosts file. 609 addrs, _, err := r.goLookupIPCNAMEOrder(context.Background(), "thor", order) // entry is in "testdata/hosts" 610 if err != nil { 611 t.Errorf("%s: expected to successfully lookup host entry", name) 612 continue 613 } 614 if len(addrs) != 1 { 615 t.Errorf("%s: expected exactly one result, but got %v", name, addrs) 616 continue 617 } 618 if got, want := addrs[0].String(), "127.1.1.1"; got != want { 619 t.Errorf("%s: address doesn't match expectation. got %v, want %v", name, got, want) 620 } 621 } 622 defer conf.teardown() 623 } 624 625 // Issue 12712. 626 // When using search domains, return the error encountered 627 // querying the original name instead of an error encountered 628 // querying a generated name. 629 func TestErrorForOriginalNameWhenSearching(t *testing.T) { 630 defer dnsWaitGroup.Wait() 631 632 const fqdn = "doesnotexist.domain" 633 634 conf, err := newResolvConfTest() 635 if err != nil { 636 t.Fatal(err) 637 } 638 defer conf.teardown() 639 640 if err := conf.writeAndUpdate([]string{"search servfail"}); err != nil { 641 t.Fatal(err) 642 } 643 644 fake := fakeDNSServer{rh: func(_, _ string, q dnsmessage.Message, _ time.Time) (dnsmessage.Message, error) { 645 r := dnsmessage.Message{ 646 Header: dnsmessage.Header{ 647 ID: q.ID, 648 Response: true, 649 }, 650 Questions: q.Questions, 651 } 652 653 switch q.Questions[0].Name.String() { 654 case fqdn + ".servfail.": 655 r.Header.RCode = dnsmessage.RCodeServerFailure 656 default: 657 r.Header.RCode = dnsmessage.RCodeNameError 658 } 659 660 return r, nil 661 }} 662 663 cases := []struct { 664 strictErrors bool 665 wantErr *DNSError 666 }{ 667 {true, &DNSError{Name: fqdn, Err: "server misbehaving", IsTemporary: true}}, 668 {false, &DNSError{Name: fqdn, Err: errNoSuchHost.Error()}}, 669 } 670 for _, tt := range cases { 671 r := Resolver{PreferGo: true, StrictErrors: tt.strictErrors, Dial: fake.DialContext} 672 _, err = r.LookupIPAddr(context.Background(), fqdn) 673 if err == nil { 674 t.Fatal("expected an error") 675 } 676 677 want := tt.wantErr 678 if err, ok := err.(*DNSError); !ok || err.Name != want.Name || err.Err != want.Err || err.IsTemporary != want.IsTemporary { 679 t.Errorf("got %v; want %v", err, want) 680 } 681 } 682 } 683 684 // Issue 15434. If a name server gives a lame referral, continue to the next. 685 func TestIgnoreLameReferrals(t *testing.T) { 686 defer dnsWaitGroup.Wait() 687 688 conf, err := newResolvConfTest() 689 if err != nil { 690 t.Fatal(err) 691 } 692 defer conf.teardown() 693 694 if err := conf.writeAndUpdate([]string{"nameserver 192.0.2.1", // the one that will give a lame referral 695 "nameserver 192.0.2.2"}); err != nil { 696 t.Fatal(err) 697 } 698 699 fake := fakeDNSServer{rh: func(_, s string, q dnsmessage.Message, _ time.Time) (dnsmessage.Message, error) { 700 t.Log(s, q) 701 r := dnsmessage.Message{ 702 Header: dnsmessage.Header{ 703 ID: q.ID, 704 Response: true, 705 }, 706 Questions: q.Questions, 707 } 708 709 if s == "192.0.2.2:53" { 710 r.Header.RecursionAvailable = true 711 if q.Questions[0].Type == dnsmessage.TypeA { 712 r.Answers = []dnsmessage.Resource{ 713 { 714 Header: dnsmessage.ResourceHeader{ 715 Name: q.Questions[0].Name, 716 Type: dnsmessage.TypeA, 717 Class: dnsmessage.ClassINET, 718 Length: 4, 719 }, 720 Body: &dnsmessage.AResource{ 721 A: TestAddr, 722 }, 723 }, 724 } 725 } 726 } 727 728 return r, nil 729 }} 730 r := Resolver{PreferGo: true, Dial: fake.DialContext} 731 732 addrs, err := r.LookupIPAddr(context.Background(), "www.golang.org") 733 if err != nil { 734 t.Fatal(err) 735 } 736 737 if got := len(addrs); got != 1 { 738 t.Fatalf("got %d addresses, want 1", got) 739 } 740 741 if got, want := addrs[0].String(), "192.0.2.1"; got != want { 742 t.Fatalf("got address %v, want %v", got, want) 743 } 744 } 745 746 func BenchmarkGoLookupIP(b *testing.B) { 747 testHookUninstaller.Do(uninstallTestHooks) 748 ctx := context.Background() 749 b.ReportAllocs() 750 751 for i := 0; i < b.N; i++ { 752 goResolver.LookupIPAddr(ctx, "www.example.com") 753 } 754 } 755 756 func BenchmarkGoLookupIPNoSuchHost(b *testing.B) { 757 testHookUninstaller.Do(uninstallTestHooks) 758 ctx := context.Background() 759 b.ReportAllocs() 760 761 for i := 0; i < b.N; i++ { 762 goResolver.LookupIPAddr(ctx, "some.nonexistent") 763 } 764 } 765 766 func BenchmarkGoLookupIPWithBrokenNameServer(b *testing.B) { 767 testHookUninstaller.Do(uninstallTestHooks) 768 769 conf, err := newResolvConfTest() 770 if err != nil { 771 b.Fatal(err) 772 } 773 defer conf.teardown() 774 775 lines := []string{ 776 "nameserver 203.0.113.254", // use TEST-NET-3 block, see RFC 5737 777 "nameserver 8.8.8.8", 778 } 779 if err := conf.writeAndUpdate(lines); err != nil { 780 b.Fatal(err) 781 } 782 ctx := context.Background() 783 b.ReportAllocs() 784 785 for i := 0; i < b.N; i++ { 786 goResolver.LookupIPAddr(ctx, "www.example.com") 787 } 788 } 789 790 type fakeDNSServer struct { 791 rh func(n, s string, q dnsmessage.Message, t time.Time) (dnsmessage.Message, error) 792 alwaysTCP bool 793 } 794 795 func (server *fakeDNSServer) DialContext(_ context.Context, n, s string) (Conn, error) { 796 if server.alwaysTCP || n == "tcp" || n == "tcp4" || n == "tcp6" { 797 return &fakeDNSConn{tcp: true, server: server, n: n, s: s}, nil 798 } 799 return &fakeDNSPacketConn{fakeDNSConn: fakeDNSConn{tcp: false, server: server, n: n, s: s}}, nil 800 } 801 802 type fakeDNSConn struct { 803 Conn 804 tcp bool 805 server *fakeDNSServer 806 n string 807 s string 808 q dnsmessage.Message 809 t time.Time 810 buf []byte 811 } 812 813 func (f *fakeDNSConn) Close() error { 814 return nil 815 } 816 817 func (f *fakeDNSConn) Read(b []byte) (int, error) { 818 if len(f.buf) > 0 { 819 n := copy(b, f.buf) 820 f.buf = f.buf[n:] 821 return n, nil 822 } 823 824 resp, err := f.server.rh(f.n, f.s, f.q, f.t) 825 if err != nil { 826 return 0, err 827 } 828 829 bb := make([]byte, 2, 514) 830 bb, err = resp.AppendPack(bb) 831 if err != nil { 832 return 0, fmt.Errorf("cannot marshal DNS message: %v", err) 833 } 834 835 if f.tcp { 836 l := len(bb) - 2 837 bb[0] = byte(l >> 8) 838 bb[1] = byte(l) 839 f.buf = bb 840 return f.Read(b) 841 } 842 843 bb = bb[2:] 844 if len(b) < len(bb) { 845 return 0, errors.New("read would fragment DNS message") 846 } 847 848 copy(b, bb) 849 return len(bb), nil 850 } 851 852 func (f *fakeDNSConn) Write(b []byte) (int, error) { 853 if f.tcp && len(b) >= 2 { 854 b = b[2:] 855 } 856 if f.q.Unpack(b) != nil { 857 return 0, fmt.Errorf("cannot unmarshal DNS message fake %s (%d)", f.n, len(b)) 858 } 859 return len(b), nil 860 } 861 862 func (f *fakeDNSConn) SetDeadline(t time.Time) error { 863 f.t = t 864 return nil 865 } 866 867 type fakeDNSPacketConn struct { 868 PacketConn 869 fakeDNSConn 870 } 871 872 func (f *fakeDNSPacketConn) SetDeadline(t time.Time) error { 873 return f.fakeDNSConn.SetDeadline(t) 874 } 875 876 func (f *fakeDNSPacketConn) Close() error { 877 return f.fakeDNSConn.Close() 878 } 879 880 // UDP round-tripper algorithm should ignore invalid DNS responses (issue 13281). 881 func TestIgnoreDNSForgeries(t *testing.T) { 882 c, s := Pipe() 883 go func() { 884 b := make([]byte, 512) 885 n, err := s.Read(b) 886 if err != nil { 887 t.Error(err) 888 return 889 } 890 891 var msg dnsmessage.Message 892 if msg.Unpack(b[:n]) != nil { 893 t.Error("invalid DNS query:", err) 894 return 895 } 896 897 s.Write([]byte("garbage DNS response packet")) 898 899 msg.Header.Response = true 900 msg.Header.ID++ // make invalid ID 901 902 if b, err = msg.Pack(); err != nil { 903 t.Error("failed to pack DNS response:", err) 904 return 905 } 906 s.Write(b) 907 908 msg.Header.ID-- // restore original ID 909 msg.Answers = []dnsmessage.Resource{ 910 { 911 Header: dnsmessage.ResourceHeader{ 912 Name: mustNewName("www.example.com."), 913 Type: dnsmessage.TypeA, 914 Class: dnsmessage.ClassINET, 915 Length: 4, 916 }, 917 Body: &dnsmessage.AResource{ 918 A: TestAddr, 919 }, 920 }, 921 } 922 923 b, err = msg.Pack() 924 if err != nil { 925 t.Error("failed to pack DNS response:", err) 926 return 927 } 928 s.Write(b) 929 }() 930 931 msg := dnsmessage.Message{ 932 Header: dnsmessage.Header{ 933 ID: 42, 934 }, 935 Questions: []dnsmessage.Question{ 936 { 937 Name: mustNewName("www.example.com."), 938 Type: dnsmessage.TypeA, 939 Class: dnsmessage.ClassINET, 940 }, 941 }, 942 } 943 944 b, err := msg.Pack() 945 if err != nil { 946 t.Fatal("Pack failed:", err) 947 } 948 949 p, _, err := dnsPacketRoundTrip(c, 42, msg.Questions[0], b) 950 if err != nil { 951 t.Fatalf("dnsPacketRoundTrip failed: %v", err) 952 } 953 954 p.SkipAllQuestions() 955 as, err := p.AllAnswers() 956 if err != nil { 957 t.Fatal("AllAnswers failed:", err) 958 } 959 if got := as[0].Body.(*dnsmessage.AResource).A; got != TestAddr { 960 t.Errorf("got address %v, want %v", got, TestAddr) 961 } 962 } 963 964 // Issue 16865. If a name server times out, continue to the next. 965 func TestRetryTimeout(t *testing.T) { 966 defer dnsWaitGroup.Wait() 967 968 conf, err := newResolvConfTest() 969 if err != nil { 970 t.Fatal(err) 971 } 972 defer conf.teardown() 973 974 testConf := []string{ 975 "nameserver 192.0.2.1", // the one that will timeout 976 "nameserver 192.0.2.2", 977 } 978 if err := conf.writeAndUpdate(testConf); err != nil { 979 t.Fatal(err) 980 } 981 982 var deadline0 time.Time 983 984 fake := fakeDNSServer{rh: func(_, s string, q dnsmessage.Message, deadline time.Time) (dnsmessage.Message, error) { 985 t.Log(s, q, deadline) 986 987 if deadline.IsZero() { 988 t.Error("zero deadline") 989 } 990 991 if s == "192.0.2.1:53" { 992 deadline0 = deadline 993 time.Sleep(10 * time.Millisecond) 994 return dnsmessage.Message{}, poll.ErrTimeout 995 } 996 997 if deadline.Equal(deadline0) { 998 t.Error("deadline didn't change") 999 } 1000 1001 return mockTXTResponse(q), nil 1002 }} 1003 r := &Resolver{PreferGo: true, Dial: fake.DialContext} 1004 1005 _, err = r.LookupTXT(context.Background(), "www.golang.org") 1006 if err != nil { 1007 t.Fatal(err) 1008 } 1009 1010 if deadline0.IsZero() { 1011 t.Error("deadline0 still zero", deadline0) 1012 } 1013 } 1014 1015 func TestRotate(t *testing.T) { 1016 // without rotation, always uses the first server 1017 testRotate(t, false, []string{"192.0.2.1", "192.0.2.2"}, []string{"192.0.2.1:53", "192.0.2.1:53", "192.0.2.1:53"}) 1018 1019 // with rotation, rotates through back to first 1020 testRotate(t, true, []string{"192.0.2.1", "192.0.2.2"}, []string{"192.0.2.1:53", "192.0.2.2:53", "192.0.2.1:53"}) 1021 } 1022 1023 func testRotate(t *testing.T, rotate bool, nameservers, wantServers []string) { 1024 defer dnsWaitGroup.Wait() 1025 1026 conf, err := newResolvConfTest() 1027 if err != nil { 1028 t.Fatal(err) 1029 } 1030 defer conf.teardown() 1031 1032 var confLines []string 1033 for _, ns := range nameservers { 1034 confLines = append(confLines, "nameserver "+ns) 1035 } 1036 if rotate { 1037 confLines = append(confLines, "options rotate") 1038 } 1039 1040 if err := conf.writeAndUpdate(confLines); err != nil { 1041 t.Fatal(err) 1042 } 1043 1044 var usedServers []string 1045 fake := fakeDNSServer{rh: func(_, s string, q dnsmessage.Message, deadline time.Time) (dnsmessage.Message, error) { 1046 usedServers = append(usedServers, s) 1047 return mockTXTResponse(q), nil 1048 }} 1049 r := Resolver{PreferGo: true, Dial: fake.DialContext} 1050 1051 // len(nameservers) + 1 to allow rotation to get back to start 1052 for i := 0; i < len(nameservers)+1; i++ { 1053 if _, err := r.LookupTXT(context.Background(), "www.golang.org"); err != nil { 1054 t.Fatal(err) 1055 } 1056 } 1057 1058 if !reflect.DeepEqual(usedServers, wantServers) { 1059 t.Errorf("rotate=%t got used servers:\n%v\nwant:\n%v", rotate, usedServers, wantServers) 1060 } 1061 } 1062 1063 func mockTXTResponse(q dnsmessage.Message) dnsmessage.Message { 1064 r := dnsmessage.Message{ 1065 Header: dnsmessage.Header{ 1066 ID: q.ID, 1067 Response: true, 1068 RecursionAvailable: true, 1069 }, 1070 Questions: q.Questions, 1071 Answers: []dnsmessage.Resource{ 1072 { 1073 Header: dnsmessage.ResourceHeader{ 1074 Name: q.Questions[0].Name, 1075 Type: dnsmessage.TypeTXT, 1076 Class: dnsmessage.ClassINET, 1077 }, 1078 Body: &dnsmessage.TXTResource{ 1079 TXT: []string{"ok"}, 1080 }, 1081 }, 1082 }, 1083 } 1084 1085 return r 1086 } 1087 1088 // Issue 17448. With StrictErrors enabled, temporary errors should make 1089 // LookupIP fail rather than return a partial result. 1090 func TestStrictErrorsLookupIP(t *testing.T) { 1091 defer dnsWaitGroup.Wait() 1092 1093 conf, err := newResolvConfTest() 1094 if err != nil { 1095 t.Fatal(err) 1096 } 1097 defer conf.teardown() 1098 1099 confData := []string{ 1100 "nameserver 192.0.2.53", 1101 "search x.golang.org y.golang.org", 1102 } 1103 if err := conf.writeAndUpdate(confData); err != nil { 1104 t.Fatal(err) 1105 } 1106 1107 const name = "test-issue19592" 1108 const server = "192.0.2.53:53" 1109 const searchX = "test-issue19592.x.golang.org." 1110 const searchY = "test-issue19592.y.golang.org." 1111 const ip4 = "192.0.2.1" 1112 const ip6 = "2001:db8::1" 1113 1114 type resolveWhichEnum int 1115 const ( 1116 resolveOK resolveWhichEnum = iota 1117 resolveOpError 1118 resolveServfail 1119 resolveTimeout 1120 ) 1121 1122 makeTempError := func(err string) error { 1123 return &DNSError{ 1124 Err: err, 1125 Name: name, 1126 Server: server, 1127 IsTemporary: true, 1128 } 1129 } 1130 makeTimeout := func() error { 1131 return &DNSError{ 1132 Err: poll.ErrTimeout.Error(), 1133 Name: name, 1134 Server: server, 1135 IsTimeout: true, 1136 } 1137 } 1138 makeNxDomain := func() error { 1139 return &DNSError{ 1140 Err: errNoSuchHost.Error(), 1141 Name: name, 1142 Server: server, 1143 } 1144 } 1145 1146 cases := []struct { 1147 desc string 1148 resolveWhich func(quest dnsmessage.Question) resolveWhichEnum 1149 wantStrictErr error 1150 wantLaxErr error 1151 wantIPs []string 1152 }{ 1153 { 1154 desc: "No errors", 1155 resolveWhich: func(quest dnsmessage.Question) resolveWhichEnum { 1156 return resolveOK 1157 }, 1158 wantIPs: []string{ip4, ip6}, 1159 }, 1160 { 1161 desc: "searchX error fails in strict mode", 1162 resolveWhich: func(quest dnsmessage.Question) resolveWhichEnum { 1163 if quest.Name.String() == searchX { 1164 return resolveTimeout 1165 } 1166 return resolveOK 1167 }, 1168 wantStrictErr: makeTimeout(), 1169 wantIPs: []string{ip4, ip6}, 1170 }, 1171 { 1172 desc: "searchX IPv4-only timeout fails in strict mode", 1173 resolveWhich: func(quest dnsmessage.Question) resolveWhichEnum { 1174 if quest.Name.String() == searchX && quest.Type == dnsmessage.TypeA { 1175 return resolveTimeout 1176 } 1177 return resolveOK 1178 }, 1179 wantStrictErr: makeTimeout(), 1180 wantIPs: []string{ip4, ip6}, 1181 }, 1182 { 1183 desc: "searchX IPv6-only servfail fails in strict mode", 1184 resolveWhich: func(quest dnsmessage.Question) resolveWhichEnum { 1185 if quest.Name.String() == searchX && quest.Type == dnsmessage.TypeAAAA { 1186 return resolveServfail 1187 } 1188 return resolveOK 1189 }, 1190 wantStrictErr: makeTempError("server misbehaving"), 1191 wantIPs: []string{ip4, ip6}, 1192 }, 1193 { 1194 desc: "searchY error always fails", 1195 resolveWhich: func(quest dnsmessage.Question) resolveWhichEnum { 1196 if quest.Name.String() == searchY { 1197 return resolveTimeout 1198 } 1199 return resolveOK 1200 }, 1201 wantStrictErr: makeTimeout(), 1202 wantLaxErr: makeNxDomain(), // This one reaches the "test." FQDN. 1203 }, 1204 { 1205 desc: "searchY IPv4-only socket error fails in strict mode", 1206 resolveWhich: func(quest dnsmessage.Question) resolveWhichEnum { 1207 if quest.Name.String() == searchY && quest.Type == dnsmessage.TypeA { 1208 return resolveOpError 1209 } 1210 return resolveOK 1211 }, 1212 wantStrictErr: makeTempError("write: socket on fire"), 1213 wantIPs: []string{ip6}, 1214 }, 1215 { 1216 desc: "searchY IPv6-only timeout fails in strict mode", 1217 resolveWhich: func(quest dnsmessage.Question) resolveWhichEnum { 1218 if quest.Name.String() == searchY && quest.Type == dnsmessage.TypeAAAA { 1219 return resolveTimeout 1220 } 1221 return resolveOK 1222 }, 1223 wantStrictErr: makeTimeout(), 1224 wantIPs: []string{ip4}, 1225 }, 1226 } 1227 1228 for i, tt := range cases { 1229 fake := fakeDNSServer{rh: func(_, s string, q dnsmessage.Message, deadline time.Time) (dnsmessage.Message, error) { 1230 t.Log(s, q) 1231 1232 switch tt.resolveWhich(q.Questions[0]) { 1233 case resolveOK: 1234 // Handle below. 1235 case resolveOpError: 1236 return dnsmessage.Message{}, &OpError{Op: "write", Err: fmt.Errorf("socket on fire")} 1237 case resolveServfail: 1238 return dnsmessage.Message{ 1239 Header: dnsmessage.Header{ 1240 ID: q.ID, 1241 Response: true, 1242 RCode: dnsmessage.RCodeServerFailure, 1243 }, 1244 Questions: q.Questions, 1245 }, nil 1246 case resolveTimeout: 1247 return dnsmessage.Message{}, poll.ErrTimeout 1248 default: 1249 t.Fatal("Impossible resolveWhich") 1250 } 1251 1252 switch q.Questions[0].Name.String() { 1253 case searchX, name + ".": 1254 // Return NXDOMAIN to utilize the search list. 1255 return dnsmessage.Message{ 1256 Header: dnsmessage.Header{ 1257 ID: q.ID, 1258 Response: true, 1259 RCode: dnsmessage.RCodeNameError, 1260 }, 1261 Questions: q.Questions, 1262 }, nil 1263 case searchY: 1264 // Return records below. 1265 default: 1266 return dnsmessage.Message{}, fmt.Errorf("Unexpected Name: %v", q.Questions[0].Name) 1267 } 1268 1269 r := dnsmessage.Message{ 1270 Header: dnsmessage.Header{ 1271 ID: q.ID, 1272 Response: true, 1273 }, 1274 Questions: q.Questions, 1275 } 1276 switch q.Questions[0].Type { 1277 case dnsmessage.TypeA: 1278 r.Answers = []dnsmessage.Resource{ 1279 { 1280 Header: dnsmessage.ResourceHeader{ 1281 Name: q.Questions[0].Name, 1282 Type: dnsmessage.TypeA, 1283 Class: dnsmessage.ClassINET, 1284 Length: 4, 1285 }, 1286 Body: &dnsmessage.AResource{ 1287 A: TestAddr, 1288 }, 1289 }, 1290 } 1291 case dnsmessage.TypeAAAA: 1292 r.Answers = []dnsmessage.Resource{ 1293 { 1294 Header: dnsmessage.ResourceHeader{ 1295 Name: q.Questions[0].Name, 1296 Type: dnsmessage.TypeAAAA, 1297 Class: dnsmessage.ClassINET, 1298 Length: 16, 1299 }, 1300 Body: &dnsmessage.AAAAResource{ 1301 AAAA: TestAddr6, 1302 }, 1303 }, 1304 } 1305 default: 1306 return dnsmessage.Message{}, fmt.Errorf("Unexpected Type: %v", q.Questions[0].Type) 1307 } 1308 return r, nil 1309 }} 1310 1311 for _, strict := range []bool{true, false} { 1312 r := Resolver{PreferGo: true, StrictErrors: strict, Dial: fake.DialContext} 1313 ips, err := r.LookupIPAddr(context.Background(), name) 1314 1315 var wantErr error 1316 if strict { 1317 wantErr = tt.wantStrictErr 1318 } else { 1319 wantErr = tt.wantLaxErr 1320 } 1321 if !reflect.DeepEqual(err, wantErr) { 1322 t.Errorf("#%d (%s) strict=%v: got err %#v; want %#v", i, tt.desc, strict, err, wantErr) 1323 } 1324 1325 gotIPs := map[string]struct{}{} 1326 for _, ip := range ips { 1327 gotIPs[ip.String()] = struct{}{} 1328 } 1329 wantIPs := map[string]struct{}{} 1330 if wantErr == nil { 1331 for _, ip := range tt.wantIPs { 1332 wantIPs[ip] = struct{}{} 1333 } 1334 } 1335 if !reflect.DeepEqual(gotIPs, wantIPs) { 1336 t.Errorf("#%d (%s) strict=%v: got ips %v; want %v", i, tt.desc, strict, gotIPs, wantIPs) 1337 } 1338 } 1339 } 1340 } 1341 1342 // Issue 17448. With StrictErrors enabled, temporary errors should make 1343 // LookupTXT stop walking the search list. 1344 func TestStrictErrorsLookupTXT(t *testing.T) { 1345 defer dnsWaitGroup.Wait() 1346 1347 conf, err := newResolvConfTest() 1348 if err != nil { 1349 t.Fatal(err) 1350 } 1351 defer conf.teardown() 1352 1353 confData := []string{ 1354 "nameserver 192.0.2.53", 1355 "search x.golang.org y.golang.org", 1356 } 1357 if err := conf.writeAndUpdate(confData); err != nil { 1358 t.Fatal(err) 1359 } 1360 1361 const name = "test" 1362 const server = "192.0.2.53:53" 1363 const searchX = "test.x.golang.org." 1364 const searchY = "test.y.golang.org." 1365 const txt = "Hello World" 1366 1367 fake := fakeDNSServer{rh: func(_, s string, q dnsmessage.Message, deadline time.Time) (dnsmessage.Message, error) { 1368 t.Log(s, q) 1369 1370 switch q.Questions[0].Name.String() { 1371 case searchX: 1372 return dnsmessage.Message{}, poll.ErrTimeout 1373 case searchY: 1374 return mockTXTResponse(q), nil 1375 default: 1376 return dnsmessage.Message{}, fmt.Errorf("Unexpected Name: %v", q.Questions[0].Name) 1377 } 1378 }} 1379 1380 for _, strict := range []bool{true, false} { 1381 r := Resolver{StrictErrors: strict, Dial: fake.DialContext} 1382 p, _, err := r.lookup(context.Background(), name, dnsmessage.TypeTXT) 1383 var wantErr error 1384 var wantRRs int 1385 if strict { 1386 wantErr = &DNSError{ 1387 Err: poll.ErrTimeout.Error(), 1388 Name: name, 1389 Server: server, 1390 IsTimeout: true, 1391 } 1392 } else { 1393 wantRRs = 1 1394 } 1395 if !reflect.DeepEqual(err, wantErr) { 1396 t.Errorf("strict=%v: got err %#v; want %#v", strict, err, wantErr) 1397 } 1398 a, err := p.AllAnswers() 1399 if err != nil { 1400 a = nil 1401 } 1402 if len(a) != wantRRs { 1403 t.Errorf("strict=%v: got %v; want %v", strict, len(a), wantRRs) 1404 } 1405 } 1406 } 1407 1408 // Test for a race between uninstalling the test hooks and closing a 1409 // socket connection. This used to fail when testing with -race. 1410 func TestDNSGoroutineRace(t *testing.T) { 1411 defer dnsWaitGroup.Wait() 1412 1413 fake := fakeDNSServer{rh: func(n, s string, q dnsmessage.Message, t time.Time) (dnsmessage.Message, error) { 1414 time.Sleep(10 * time.Microsecond) 1415 return dnsmessage.Message{}, poll.ErrTimeout 1416 }} 1417 r := Resolver{PreferGo: true, Dial: fake.DialContext} 1418 1419 // The timeout here is less than the timeout used by the server, 1420 // so the goroutine started to query the (fake) server will hang 1421 // around after this test is done if we don't call dnsWaitGroup.Wait. 1422 ctx, cancel := context.WithTimeout(context.Background(), 2*time.Microsecond) 1423 defer cancel() 1424 _, err := r.LookupIPAddr(ctx, "where.are.they.now") 1425 if err == nil { 1426 t.Fatal("fake DNS lookup unexpectedly succeeded") 1427 } 1428 } 1429 1430 func lookupWithFake(fake fakeDNSServer, name string, typ dnsmessage.Type) error { 1431 r := Resolver{PreferGo: true, Dial: fake.DialContext} 1432 1433 resolvConf.mu.RLock() 1434 conf := resolvConf.dnsConfig 1435 resolvConf.mu.RUnlock() 1436 1437 ctx, cancel := context.WithCancel(context.Background()) 1438 defer cancel() 1439 1440 _, _, err := r.tryOneName(ctx, conf, name, typ) 1441 return err 1442 } 1443 1444 // Issue 8434: verify that Temporary returns true on an error when rcode 1445 // is SERVFAIL 1446 func TestIssue8434(t *testing.T) { 1447 err := lookupWithFake(fakeDNSServer{ 1448 rh: func(n, _ string, q dnsmessage.Message, _ time.Time) (dnsmessage.Message, error) { 1449 return dnsmessage.Message{ 1450 Header: dnsmessage.Header{ 1451 ID: q.ID, 1452 Response: true, 1453 RCode: dnsmessage.RCodeServerFailure, 1454 }, 1455 Questions: q.Questions, 1456 }, nil 1457 }, 1458 }, "golang.org.", dnsmessage.TypeALL) 1459 if err == nil { 1460 t.Fatal("expected an error") 1461 } 1462 if ne, ok := err.(Error); !ok { 1463 t.Fatalf("err = %#v; wanted something supporting net.Error", err) 1464 } else if !ne.Temporary() { 1465 t.Fatalf("Temporary = false for err = %#v; want Temporary == true", err) 1466 } 1467 if de, ok := err.(*DNSError); !ok { 1468 t.Fatalf("err = %#v; wanted a *net.DNSError", err) 1469 } else if !de.IsTemporary { 1470 t.Fatalf("IsTemporary = false for err = %#v; want IsTemporary == true", err) 1471 } 1472 } 1473 1474 // TestNoSuchHost verifies that tryOneName works correctly when the domain does 1475 // not exist. 1476 // 1477 // Issue 12778: verify that NXDOMAIN without RA bit errors as "no such host" 1478 // and not "server misbehaving" 1479 // 1480 // Issue 25336: verify that NXDOMAIN errors fail fast. 1481 // 1482 // Issue 27525: verify that empty answers fail fast. 1483 func TestNoSuchHost(t *testing.T) { 1484 tests := []struct { 1485 name string 1486 f func(string, string, dnsmessage.Message, time.Time) (dnsmessage.Message, error) 1487 }{ 1488 { 1489 "NXDOMAIN", 1490 func(n, _ string, q dnsmessage.Message, _ time.Time) (dnsmessage.Message, error) { 1491 return dnsmessage.Message{ 1492 Header: dnsmessage.Header{ 1493 ID: q.ID, 1494 Response: true, 1495 RCode: dnsmessage.RCodeNameError, 1496 RecursionAvailable: false, 1497 }, 1498 Questions: q.Questions, 1499 }, nil 1500 }, 1501 }, 1502 { 1503 "no answers", 1504 func(n, _ string, q dnsmessage.Message, _ time.Time) (dnsmessage.Message, error) { 1505 return dnsmessage.Message{ 1506 Header: dnsmessage.Header{ 1507 ID: q.ID, 1508 Response: true, 1509 RCode: dnsmessage.RCodeSuccess, 1510 RecursionAvailable: false, 1511 Authoritative: true, 1512 }, 1513 Questions: q.Questions, 1514 }, nil 1515 }, 1516 }, 1517 } 1518 1519 for _, test := range tests { 1520 t.Run(test.name, func(t *testing.T) { 1521 lookups := 0 1522 err := lookupWithFake(fakeDNSServer{ 1523 rh: func(n, s string, q dnsmessage.Message, d time.Time) (dnsmessage.Message, error) { 1524 lookups++ 1525 return test.f(n, s, q, d) 1526 }, 1527 }, ".", dnsmessage.TypeALL) 1528 1529 if lookups != 1 { 1530 t.Errorf("got %d lookups, wanted 1", lookups) 1531 } 1532 1533 if err == nil { 1534 t.Fatal("expected an error") 1535 } 1536 de, ok := err.(*DNSError) 1537 if !ok { 1538 t.Fatalf("err = %#v; wanted a *net.DNSError", err) 1539 } 1540 if de.Err != errNoSuchHost.Error() { 1541 t.Fatalf("Err = %#v; wanted %q", de.Err, errNoSuchHost.Error()) 1542 } 1543 }) 1544 } 1545 } 1546 1547 // Issue 26573: verify that Conns that don't implement PacketConn are treated 1548 // as streams even when udp was requested. 1549 func TestDNSDialTCP(t *testing.T) { 1550 fake := fakeDNSServer{ 1551 rh: func(n, _ string, q dnsmessage.Message, _ time.Time) (dnsmessage.Message, error) { 1552 r := dnsmessage.Message{ 1553 Header: dnsmessage.Header{ 1554 ID: q.Header.ID, 1555 Response: true, 1556 RCode: dnsmessage.RCodeSuccess, 1557 }, 1558 Questions: q.Questions, 1559 } 1560 return r, nil 1561 }, 1562 alwaysTCP: true, 1563 } 1564 r := Resolver{PreferGo: true, Dial: fake.DialContext} 1565 ctx := context.Background() 1566 _, _, err := r.exchange(ctx, "0.0.0.0", mustQuestion("com.", dnsmessage.TypeALL, dnsmessage.ClassINET), time.Second) 1567 if err != nil { 1568 t.Fatal("exhange failed:", err) 1569 } 1570 } 1571 1572 // Issue 27763: verify that two strings in one TXT record are concatenated. 1573 func TestTXTRecordTwoStrings(t *testing.T) { 1574 fake := fakeDNSServer{ 1575 rh: func(n, _ string, q dnsmessage.Message, _ time.Time) (dnsmessage.Message, error) { 1576 r := dnsmessage.Message{ 1577 Header: dnsmessage.Header{ 1578 ID: q.Header.ID, 1579 Response: true, 1580 RCode: dnsmessage.RCodeSuccess, 1581 }, 1582 Questions: q.Questions, 1583 Answers: []dnsmessage.Resource{ 1584 { 1585 Header: dnsmessage.ResourceHeader{ 1586 Name: q.Questions[0].Name, 1587 Type: dnsmessage.TypeA, 1588 Class: dnsmessage.ClassINET, 1589 }, 1590 Body: &dnsmessage.TXTResource{ 1591 TXT: []string{"string1 ", "string2"}, 1592 }, 1593 }, 1594 { 1595 Header: dnsmessage.ResourceHeader{ 1596 Name: q.Questions[0].Name, 1597 Type: dnsmessage.TypeA, 1598 Class: dnsmessage.ClassINET, 1599 }, 1600 Body: &dnsmessage.TXTResource{ 1601 TXT: []string{"onestring"}, 1602 }, 1603 }, 1604 }, 1605 } 1606 return r, nil 1607 }, 1608 } 1609 r := Resolver{PreferGo: true, Dial: fake.DialContext} 1610 txt, err := r.lookupTXT(context.Background(), "golang.org") 1611 if err != nil { 1612 t.Fatal("LookupTXT failed:", err) 1613 } 1614 if want := 2; len(txt) != want { 1615 t.Fatalf("len(txt), got %d, want %d", len(txt), want) 1616 } 1617 if want := "string1 string2"; txt[0] != want { 1618 t.Errorf("txt[0], got %q, want %q", txt[0], want) 1619 } 1620 if want := "onestring"; txt[1] != want { 1621 t.Errorf("txt[1], got %q, want %q", txt[1], want) 1622 } 1623 }