github.com/slayercat/go@v0.0.0-20170428012452-c51559813f61/src/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 darwin dragonfly freebsd linux netbsd openbsd solaris 6 7 package net 8 9 import ( 10 "context" 11 "fmt" 12 "internal/poll" 13 "internal/testenv" 14 "io/ioutil" 15 "os" 16 "path" 17 "reflect" 18 "strings" 19 "sync" 20 "testing" 21 "time" 22 ) 23 24 // Test address from 192.0.2.0/24 block, reserved by RFC 5737 for documentation. 25 const TestAddr uint32 = 0xc0000201 26 27 // Test address from 2001:db8::/32 block, reserved by RFC 3849 for documentation. 28 var TestAddr6 = [16]byte{0x20, 0x01, 0x0d, 0xb8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1} 29 30 var dnsTransportFallbackTests = []struct { 31 server string 32 name string 33 qtype uint16 34 timeout int 35 rcode int 36 }{ 37 // Querying "com." with qtype=255 usually makes an answer 38 // which requires more than 512 bytes. 39 {"8.8.8.8:53", "com.", dnsTypeALL, 2, dnsRcodeSuccess}, 40 {"8.8.4.4:53", "com.", dnsTypeALL, 4, dnsRcodeSuccess}, 41 } 42 43 func TestDNSTransportFallback(t *testing.T) { 44 testenv.MustHaveExternalNetwork(t) 45 46 for _, tt := range dnsTransportFallbackTests { 47 ctx, cancel := context.WithCancel(context.Background()) 48 defer cancel() 49 msg, err := exchange(ctx, tt.server, tt.name, tt.qtype, time.Second) 50 if err != nil { 51 t.Error(err) 52 continue 53 } 54 switch msg.rcode { 55 case tt.rcode, dnsRcodeServerFailure: 56 default: 57 t.Errorf("got %v from %v; want %v", msg.rcode, tt.server, tt.rcode) 58 continue 59 } 60 } 61 } 62 63 // See RFC 6761 for further information about the reserved, pseudo 64 // domain names. 65 var specialDomainNameTests = []struct { 66 name string 67 qtype uint16 68 rcode int 69 }{ 70 // Name resolution APIs and libraries should not recognize the 71 // followings as special. 72 {"1.0.168.192.in-addr.arpa.", dnsTypePTR, dnsRcodeNameError}, 73 {"test.", dnsTypeALL, dnsRcodeNameError}, 74 {"example.com.", dnsTypeALL, dnsRcodeSuccess}, 75 76 // Name resolution APIs and libraries should recognize the 77 // followings as special and should not send any queries. 78 // Though, we test those names here for verifying negative 79 // answers at DNS query-response interaction level. 80 {"localhost.", dnsTypeALL, dnsRcodeNameError}, 81 {"invalid.", dnsTypeALL, dnsRcodeNameError}, 82 } 83 84 func TestSpecialDomainName(t *testing.T) { 85 testenv.MustHaveExternalNetwork(t) 86 87 server := "8.8.8.8:53" 88 for _, tt := range specialDomainNameTests { 89 ctx, cancel := context.WithCancel(context.Background()) 90 defer cancel() 91 msg, err := exchange(ctx, server, tt.name, tt.qtype, 3*time.Second) 92 if err != nil { 93 t.Error(err) 94 continue 95 } 96 switch msg.rcode { 97 case tt.rcode, dnsRcodeServerFailure: 98 default: 99 t.Errorf("got %v from %v; want %v", msg.rcode, server, tt.rcode) 100 continue 101 } 102 } 103 } 104 105 // Issue 13705: don't try to resolve onion addresses, etc 106 func TestAvoidDNSName(t *testing.T) { 107 tests := []struct { 108 name string 109 avoid bool 110 }{ 111 {"foo.com", false}, 112 {"foo.com.", false}, 113 114 {"foo.onion.", true}, 115 {"foo.onion", true}, 116 {"foo.ONION", true}, 117 {"foo.ONION.", true}, 118 119 // But do resolve *.local address; Issue 16739 120 {"foo.local.", false}, 121 {"foo.local", false}, 122 {"foo.LOCAL", false}, 123 {"foo.LOCAL.", false}, 124 125 {"", true}, // will be rejected earlier too 126 127 // Without stuff before onion/local, they're fine to 128 // use DNS. With a search path, 129 // "onion.vegegtables.com" can use DNS. Without a 130 // search path (or with a trailing dot), the queries 131 // are just kinda useless, but don't reveal anything 132 // private. 133 {"local", false}, 134 {"onion", false}, 135 {"local.", false}, 136 {"onion.", false}, 137 } 138 for _, tt := range tests { 139 got := avoidDNS(tt.name) 140 if got != tt.avoid { 141 t.Errorf("avoidDNS(%q) = %v; want %v", tt.name, got, tt.avoid) 142 } 143 } 144 } 145 146 // Issue 13705: don't try to resolve onion addresses, etc 147 func TestLookupTorOnion(t *testing.T) { 148 addrs, err := DefaultResolver.goLookupIP(context.Background(), "foo.onion") 149 if len(addrs) > 0 { 150 t.Errorf("unexpected addresses: %v", addrs) 151 } 152 if err != nil { 153 t.Fatalf("lookup = %v; want nil", err) 154 } 155 } 156 157 type resolvConfTest struct { 158 dir string 159 path string 160 *resolverConfig 161 } 162 163 func newResolvConfTest() (*resolvConfTest, error) { 164 dir, err := ioutil.TempDir("", "go-resolvconftest") 165 if err != nil { 166 return nil, err 167 } 168 conf := &resolvConfTest{ 169 dir: dir, 170 path: path.Join(dir, "resolv.conf"), 171 resolverConfig: &resolvConf, 172 } 173 conf.initOnce.Do(conf.init) 174 return conf, nil 175 } 176 177 func (conf *resolvConfTest) writeAndUpdate(lines []string) error { 178 f, err := os.OpenFile(conf.path, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0600) 179 if err != nil { 180 return err 181 } 182 if _, err := f.WriteString(strings.Join(lines, "\n")); err != nil { 183 f.Close() 184 return err 185 } 186 f.Close() 187 if err := conf.forceUpdate(conf.path, time.Now().Add(time.Hour)); err != nil { 188 return err 189 } 190 return nil 191 } 192 193 func (conf *resolvConfTest) forceUpdate(name string, lastChecked time.Time) error { 194 dnsConf := dnsReadConfig(name) 195 conf.mu.Lock() 196 conf.dnsConfig = dnsConf 197 conf.mu.Unlock() 198 for i := 0; i < 5; i++ { 199 if conf.tryAcquireSema() { 200 conf.lastChecked = lastChecked 201 conf.releaseSema() 202 return nil 203 } 204 } 205 return fmt.Errorf("tryAcquireSema for %s failed", name) 206 } 207 208 func (conf *resolvConfTest) servers() []string { 209 conf.mu.RLock() 210 servers := conf.dnsConfig.servers 211 conf.mu.RUnlock() 212 return servers 213 } 214 215 func (conf *resolvConfTest) teardown() error { 216 err := conf.forceUpdate("/etc/resolv.conf", time.Time{}) 217 os.RemoveAll(conf.dir) 218 return err 219 } 220 221 var updateResolvConfTests = []struct { 222 name string // query name 223 lines []string // resolver configuration lines 224 servers []string // expected name servers 225 }{ 226 { 227 name: "golang.org", 228 lines: []string{"nameserver 8.8.8.8"}, 229 servers: []string{"8.8.8.8:53"}, 230 }, 231 { 232 name: "", 233 lines: nil, // an empty resolv.conf should use defaultNS as name servers 234 servers: defaultNS, 235 }, 236 { 237 name: "www.example.com", 238 lines: []string{"nameserver 8.8.4.4"}, 239 servers: []string{"8.8.4.4:53"}, 240 }, 241 } 242 243 func TestUpdateResolvConf(t *testing.T) { 244 testenv.MustHaveExternalNetwork(t) 245 246 conf, err := newResolvConfTest() 247 if err != nil { 248 t.Fatal(err) 249 } 250 defer conf.teardown() 251 252 for i, tt := range updateResolvConfTests { 253 if err := conf.writeAndUpdate(tt.lines); err != nil { 254 t.Error(err) 255 continue 256 } 257 if tt.name != "" { 258 var wg sync.WaitGroup 259 const N = 10 260 wg.Add(N) 261 for j := 0; j < N; j++ { 262 go func(name string) { 263 defer wg.Done() 264 ips, err := DefaultResolver.goLookupIP(context.Background(), name) 265 if err != nil { 266 t.Error(err) 267 return 268 } 269 if len(ips) == 0 { 270 t.Errorf("no records for %s", name) 271 return 272 } 273 }(tt.name) 274 } 275 wg.Wait() 276 } 277 servers := conf.servers() 278 if !reflect.DeepEqual(servers, tt.servers) { 279 t.Errorf("#%d: got %v; want %v", i, servers, tt.servers) 280 continue 281 } 282 } 283 } 284 285 var goLookupIPWithResolverConfigTests = []struct { 286 name string 287 lines []string // resolver configuration lines 288 error 289 a, aaaa bool // whether response contains A, AAAA-record 290 }{ 291 // no records, transport timeout 292 { 293 "jgahvsekduiv9bw4b3qhn4ykdfgj0493iohkrjfhdvhjiu4j", 294 []string{ 295 "options timeout:1 attempts:1", 296 "nameserver 255.255.255.255", // please forgive us for abuse of limited broadcast address 297 }, 298 &DNSError{Name: "jgahvsekduiv9bw4b3qhn4ykdfgj0493iohkrjfhdvhjiu4j", Server: "255.255.255.255:53", IsTimeout: true}, 299 false, false, 300 }, 301 302 // no records, non-existent domain 303 { 304 "jgahvsekduiv9bw4b3qhn4ykdfgj0493iohkrjfhdvhjiu4j", 305 []string{ 306 "options timeout:3 attempts:1", 307 "nameserver 8.8.8.8", 308 }, 309 &DNSError{Name: "jgahvsekduiv9bw4b3qhn4ykdfgj0493iohkrjfhdvhjiu4j", Server: "8.8.8.8:53", IsTimeout: false}, 310 false, false, 311 }, 312 313 // a few A records, no AAAA records 314 { 315 "ipv4.google.com.", 316 []string{ 317 "nameserver 8.8.8.8", 318 "nameserver 2001:4860:4860::8888", 319 }, 320 nil, 321 true, false, 322 }, 323 { 324 "ipv4.google.com", 325 []string{ 326 "domain golang.org", 327 "nameserver 2001:4860:4860::8888", 328 "nameserver 8.8.8.8", 329 }, 330 nil, 331 true, false, 332 }, 333 { 334 "ipv4.google.com", 335 []string{ 336 "search x.golang.org y.golang.org", 337 "nameserver 2001:4860:4860::8888", 338 "nameserver 8.8.8.8", 339 }, 340 nil, 341 true, false, 342 }, 343 344 // no A records, a few AAAA records 345 { 346 "ipv6.google.com.", 347 []string{ 348 "nameserver 2001:4860:4860::8888", 349 "nameserver 8.8.8.8", 350 }, 351 nil, 352 false, true, 353 }, 354 { 355 "ipv6.google.com", 356 []string{ 357 "domain golang.org", 358 "nameserver 8.8.8.8", 359 "nameserver 2001:4860:4860::8888", 360 }, 361 nil, 362 false, true, 363 }, 364 { 365 "ipv6.google.com", 366 []string{ 367 "search x.golang.org y.golang.org", 368 "nameserver 8.8.8.8", 369 "nameserver 2001:4860:4860::8888", 370 }, 371 nil, 372 false, true, 373 }, 374 375 // both A and AAAA records 376 { 377 "hostname.as112.net", // see RFC 7534 378 []string{ 379 "domain golang.org", 380 "nameserver 2001:4860:4860::8888", 381 "nameserver 8.8.8.8", 382 }, 383 nil, 384 true, true, 385 }, 386 { 387 "hostname.as112.net", // see RFC 7534 388 []string{ 389 "search x.golang.org y.golang.org", 390 "nameserver 2001:4860:4860::8888", 391 "nameserver 8.8.8.8", 392 }, 393 nil, 394 true, true, 395 }, 396 } 397 398 func TestGoLookupIPWithResolverConfig(t *testing.T) { 399 testenv.MustHaveExternalNetwork(t) 400 401 conf, err := newResolvConfTest() 402 if err != nil { 403 t.Fatal(err) 404 } 405 defer conf.teardown() 406 407 for _, tt := range goLookupIPWithResolverConfigTests { 408 if err := conf.writeAndUpdate(tt.lines); err != nil { 409 t.Error(err) 410 continue 411 } 412 addrs, err := DefaultResolver.goLookupIP(context.Background(), tt.name) 413 if err != nil { 414 // This test uses external network connectivity. 415 // We need to take care with errors on both 416 // DNS message exchange layer and DNS 417 // transport layer because goLookupIP may fail 418 // when the IP connectivity on node under test 419 // gets lost during its run. 420 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) { 421 t.Errorf("got %v; want %v", err, tt.error) 422 } 423 continue 424 } 425 if len(addrs) == 0 { 426 t.Errorf("no records for %s", tt.name) 427 } 428 if !tt.a && !tt.aaaa && len(addrs) > 0 { 429 t.Errorf("unexpected %v for %s", addrs, tt.name) 430 } 431 for _, addr := range addrs { 432 if !tt.a && addr.IP.To4() != nil { 433 t.Errorf("got %v; must not be IPv4 address", addr) 434 } 435 if !tt.aaaa && addr.IP.To16() != nil && addr.IP.To4() == nil { 436 t.Errorf("got %v; must not be IPv6 address", addr) 437 } 438 } 439 } 440 } 441 442 // Test that goLookupIPOrder falls back to the host file when no DNS servers are available. 443 func TestGoLookupIPOrderFallbackToFile(t *testing.T) { 444 testenv.MustHaveExternalNetwork(t) 445 446 // Add a config that simulates no dns servers being available. 447 conf, err := newResolvConfTest() 448 if err != nil { 449 t.Fatal(err) 450 } 451 if err := conf.writeAndUpdate([]string{}); err != nil { 452 t.Fatal(err) 453 } 454 // Redirect host file lookups. 455 defer func(orig string) { testHookHostsPath = orig }(testHookHostsPath) 456 testHookHostsPath = "testdata/hosts" 457 458 for _, order := range []hostLookupOrder{hostLookupFilesDNS, hostLookupDNSFiles} { 459 name := fmt.Sprintf("order %v", order) 460 461 // First ensure that we get an error when contacting a non-existent host. 462 _, _, err := DefaultResolver.goLookupIPCNAMEOrder(context.Background(), "notarealhost", order) 463 if err == nil { 464 t.Errorf("%s: expected error while looking up name not in hosts file", name) 465 continue 466 } 467 468 // Now check that we get an address when the name appears in the hosts file. 469 addrs, _, err := DefaultResolver.goLookupIPCNAMEOrder(context.Background(), "thor", order) // entry is in "testdata/hosts" 470 if err != nil { 471 t.Errorf("%s: expected to successfully lookup host entry", name) 472 continue 473 } 474 if len(addrs) != 1 { 475 t.Errorf("%s: expected exactly one result, but got %v", name, addrs) 476 continue 477 } 478 if got, want := addrs[0].String(), "127.1.1.1"; got != want { 479 t.Errorf("%s: address doesn't match expectation. got %v, want %v", name, got, want) 480 } 481 } 482 defer conf.teardown() 483 } 484 485 // Issue 12712. 486 // When using search domains, return the error encountered 487 // querying the original name instead of an error encountered 488 // querying a generated name. 489 func TestErrorForOriginalNameWhenSearching(t *testing.T) { 490 const fqdn = "doesnotexist.domain" 491 492 origTestHookDNSDialer := testHookDNSDialer 493 defer func() { testHookDNSDialer = origTestHookDNSDialer }() 494 495 conf, err := newResolvConfTest() 496 if err != nil { 497 t.Fatal(err) 498 } 499 defer conf.teardown() 500 501 if err := conf.writeAndUpdate([]string{"search servfail"}); err != nil { 502 t.Fatal(err) 503 } 504 505 d := &fakeDNSDialer{} 506 testHookDNSDialer = func() dnsDialer { return d } 507 508 d.rh = func(s string, q *dnsMsg, _ time.Time) (*dnsMsg, error) { 509 r := &dnsMsg{ 510 dnsMsgHdr: dnsMsgHdr{ 511 id: q.id, 512 }, 513 } 514 515 switch q.question[0].Name { 516 case fqdn + ".servfail.": 517 r.rcode = dnsRcodeServerFailure 518 default: 519 r.rcode = dnsRcodeNameError 520 } 521 522 return r, nil 523 } 524 525 cases := []struct { 526 strictErrors bool 527 wantErr *DNSError 528 }{ 529 {true, &DNSError{Name: fqdn, Err: "server misbehaving", IsTemporary: true}}, 530 {false, &DNSError{Name: fqdn, Err: errNoSuchHost.Error()}}, 531 } 532 for _, tt := range cases { 533 r := Resolver{StrictErrors: tt.strictErrors} 534 _, err = r.goLookupIP(context.Background(), fqdn) 535 if err == nil { 536 t.Fatal("expected an error") 537 } 538 539 want := tt.wantErr 540 if err, ok := err.(*DNSError); !ok || err.Name != want.Name || err.Err != want.Err || err.IsTemporary != want.IsTemporary { 541 t.Errorf("got %v; want %v", err, want) 542 } 543 } 544 } 545 546 // Issue 15434. If a name server gives a lame referral, continue to the next. 547 func TestIgnoreLameReferrals(t *testing.T) { 548 origTestHookDNSDialer := testHookDNSDialer 549 defer func() { testHookDNSDialer = origTestHookDNSDialer }() 550 551 conf, err := newResolvConfTest() 552 if err != nil { 553 t.Fatal(err) 554 } 555 defer conf.teardown() 556 557 if err := conf.writeAndUpdate([]string{"nameserver 192.0.2.1", // the one that will give a lame referral 558 "nameserver 192.0.2.2"}); err != nil { 559 t.Fatal(err) 560 } 561 562 d := &fakeDNSDialer{} 563 testHookDNSDialer = func() dnsDialer { return d } 564 565 d.rh = func(s string, q *dnsMsg, _ time.Time) (*dnsMsg, error) { 566 t.Log(s, q) 567 r := &dnsMsg{ 568 dnsMsgHdr: dnsMsgHdr{ 569 id: q.id, 570 response: true, 571 }, 572 question: q.question, 573 } 574 575 if s == "192.0.2.2:53" { 576 r.recursion_available = true 577 if q.question[0].Qtype == dnsTypeA { 578 r.answer = []dnsRR{ 579 &dnsRR_A{ 580 Hdr: dnsRR_Header{ 581 Name: q.question[0].Name, 582 Rrtype: dnsTypeA, 583 Class: dnsClassINET, 584 Rdlength: 4, 585 }, 586 A: TestAddr, 587 }, 588 } 589 } 590 } 591 592 return r, nil 593 } 594 595 addrs, err := DefaultResolver.goLookupIP(context.Background(), "www.golang.org") 596 if err != nil { 597 t.Fatal(err) 598 } 599 600 if got := len(addrs); got != 1 { 601 t.Fatalf("got %d addresses, want 1", got) 602 } 603 604 if got, want := addrs[0].String(), "192.0.2.1"; got != want { 605 t.Fatalf("got address %v, want %v", got, want) 606 } 607 } 608 609 func BenchmarkGoLookupIP(b *testing.B) { 610 testHookUninstaller.Do(uninstallTestHooks) 611 ctx := context.Background() 612 613 for i := 0; i < b.N; i++ { 614 DefaultResolver.goLookupIP(ctx, "www.example.com") 615 } 616 } 617 618 func BenchmarkGoLookupIPNoSuchHost(b *testing.B) { 619 testHookUninstaller.Do(uninstallTestHooks) 620 ctx := context.Background() 621 622 for i := 0; i < b.N; i++ { 623 DefaultResolver.goLookupIP(ctx, "some.nonexistent") 624 } 625 } 626 627 func BenchmarkGoLookupIPWithBrokenNameServer(b *testing.B) { 628 testHookUninstaller.Do(uninstallTestHooks) 629 630 conf, err := newResolvConfTest() 631 if err != nil { 632 b.Fatal(err) 633 } 634 defer conf.teardown() 635 636 lines := []string{ 637 "nameserver 203.0.113.254", // use TEST-NET-3 block, see RFC 5737 638 "nameserver 8.8.8.8", 639 } 640 if err := conf.writeAndUpdate(lines); err != nil { 641 b.Fatal(err) 642 } 643 ctx := context.Background() 644 645 for i := 0; i < b.N; i++ { 646 DefaultResolver.goLookupIP(ctx, "www.example.com") 647 } 648 } 649 650 type fakeDNSDialer struct { 651 // reply handler 652 rh func(s string, q *dnsMsg, t time.Time) (*dnsMsg, error) 653 } 654 655 func (f *fakeDNSDialer) dialDNS(_ context.Context, n, s string) (dnsConn, error) { 656 return &fakeDNSConn{f.rh, s, time.Time{}}, nil 657 } 658 659 type fakeDNSConn struct { 660 rh func(s string, q *dnsMsg, t time.Time) (*dnsMsg, error) 661 s string 662 t time.Time 663 } 664 665 func (f *fakeDNSConn) Close() error { 666 return nil 667 } 668 669 func (f *fakeDNSConn) SetDeadline(t time.Time) error { 670 f.t = t 671 return nil 672 } 673 674 func (f *fakeDNSConn) dnsRoundTrip(q *dnsMsg) (*dnsMsg, error) { 675 return f.rh(f.s, q, f.t) 676 } 677 678 // UDP round-tripper algorithm should ignore invalid DNS responses (issue 13281). 679 func TestIgnoreDNSForgeries(t *testing.T) { 680 c, s := Pipe() 681 go func() { 682 b := make([]byte, 512) 683 n, err := s.Read(b) 684 if err != nil { 685 t.Error(err) 686 return 687 } 688 689 msg := &dnsMsg{} 690 if !msg.Unpack(b[:n]) { 691 t.Error("invalid DNS query") 692 return 693 } 694 695 s.Write([]byte("garbage DNS response packet")) 696 697 msg.response = true 698 msg.id++ // make invalid ID 699 b, ok := msg.Pack() 700 if !ok { 701 t.Error("failed to pack DNS response") 702 return 703 } 704 s.Write(b) 705 706 msg.id-- // restore original ID 707 msg.answer = []dnsRR{ 708 &dnsRR_A{ 709 Hdr: dnsRR_Header{ 710 Name: "www.example.com.", 711 Rrtype: dnsTypeA, 712 Class: dnsClassINET, 713 Rdlength: 4, 714 }, 715 A: TestAddr, 716 }, 717 } 718 719 b, ok = msg.Pack() 720 if !ok { 721 t.Error("failed to pack DNS response") 722 return 723 } 724 s.Write(b) 725 }() 726 727 msg := &dnsMsg{ 728 dnsMsgHdr: dnsMsgHdr{ 729 id: 42, 730 }, 731 question: []dnsQuestion{ 732 { 733 Name: "www.example.com.", 734 Qtype: dnsTypeA, 735 Qclass: dnsClassINET, 736 }, 737 }, 738 } 739 740 resp, err := dnsRoundTripUDP(c, msg) 741 if err != nil { 742 t.Fatalf("dnsRoundTripUDP failed: %v", err) 743 } 744 745 if got := resp.answer[0].(*dnsRR_A).A; got != TestAddr { 746 t.Errorf("got address %v, want %v", got, TestAddr) 747 } 748 } 749 750 // Issue 16865. If a name server times out, continue to the next. 751 func TestRetryTimeout(t *testing.T) { 752 origTestHookDNSDialer := testHookDNSDialer 753 defer func() { testHookDNSDialer = origTestHookDNSDialer }() 754 755 conf, err := newResolvConfTest() 756 if err != nil { 757 t.Fatal(err) 758 } 759 defer conf.teardown() 760 761 testConf := []string{ 762 "nameserver 192.0.2.1", // the one that will timeout 763 "nameserver 192.0.2.2", 764 } 765 if err := conf.writeAndUpdate(testConf); err != nil { 766 t.Fatal(err) 767 } 768 769 d := &fakeDNSDialer{} 770 testHookDNSDialer = func() dnsDialer { return d } 771 772 var deadline0 time.Time 773 774 d.rh = func(s string, q *dnsMsg, deadline time.Time) (*dnsMsg, error) { 775 t.Log(s, q, deadline) 776 777 if deadline.IsZero() { 778 t.Error("zero deadline") 779 } 780 781 if s == "192.0.2.1:53" { 782 deadline0 = deadline 783 time.Sleep(10 * time.Millisecond) 784 return nil, poll.ErrTimeout 785 } 786 787 if deadline == deadline0 { 788 t.Error("deadline didn't change") 789 } 790 791 return mockTXTResponse(q), nil 792 } 793 794 _, err = LookupTXT("www.golang.org") 795 if err != nil { 796 t.Fatal(err) 797 } 798 799 if deadline0.IsZero() { 800 t.Error("deadline0 still zero", deadline0) 801 } 802 } 803 804 func TestRotate(t *testing.T) { 805 // without rotation, always uses the first server 806 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"}) 807 808 // with rotation, rotates through back to first 809 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"}) 810 } 811 812 func testRotate(t *testing.T, rotate bool, nameservers, wantServers []string) { 813 origTestHookDNSDialer := testHookDNSDialer 814 defer func() { testHookDNSDialer = origTestHookDNSDialer }() 815 816 conf, err := newResolvConfTest() 817 if err != nil { 818 t.Fatal(err) 819 } 820 defer conf.teardown() 821 822 var confLines []string 823 for _, ns := range nameservers { 824 confLines = append(confLines, "nameserver "+ns) 825 } 826 if rotate { 827 confLines = append(confLines, "options rotate") 828 } 829 830 if err := conf.writeAndUpdate(confLines); err != nil { 831 t.Fatal(err) 832 } 833 834 d := &fakeDNSDialer{} 835 testHookDNSDialer = func() dnsDialer { return d } 836 837 var usedServers []string 838 d.rh = func(s string, q *dnsMsg, _ time.Time) (*dnsMsg, error) { 839 usedServers = append(usedServers, s) 840 return mockTXTResponse(q), nil 841 } 842 843 // len(nameservers) + 1 to allow rotation to get back to start 844 for i := 0; i < len(nameservers)+1; i++ { 845 if _, err := LookupTXT("www.golang.org"); err != nil { 846 t.Fatal(err) 847 } 848 } 849 850 if !reflect.DeepEqual(usedServers, wantServers) { 851 t.Errorf("rotate=%t got used servers:\n%v\nwant:\n%v", rotate, usedServers, wantServers) 852 } 853 } 854 855 func mockTXTResponse(q *dnsMsg) *dnsMsg { 856 r := &dnsMsg{ 857 dnsMsgHdr: dnsMsgHdr{ 858 id: q.id, 859 response: true, 860 recursion_available: true, 861 }, 862 question: q.question, 863 answer: []dnsRR{ 864 &dnsRR_TXT{ 865 Hdr: dnsRR_Header{ 866 Name: q.question[0].Name, 867 Rrtype: dnsTypeTXT, 868 Class: dnsClassINET, 869 }, 870 Txt: "ok", 871 }, 872 }, 873 } 874 875 return r 876 } 877 878 // Issue 17448. With StrictErrors enabled, temporary errors should make 879 // LookupIP fail rather than return a partial result. 880 func TestStrictErrorsLookupIP(t *testing.T) { 881 origTestHookDNSDialer := testHookDNSDialer 882 defer func() { testHookDNSDialer = origTestHookDNSDialer }() 883 884 conf, err := newResolvConfTest() 885 if err != nil { 886 t.Fatal(err) 887 } 888 defer conf.teardown() 889 890 confData := []string{ 891 "nameserver 192.0.2.53", 892 "search x.golang.org y.golang.org", 893 } 894 if err := conf.writeAndUpdate(confData); err != nil { 895 t.Fatal(err) 896 } 897 898 const name = "test-issue19592" 899 const server = "192.0.2.53:53" 900 const searchX = "test-issue19592.x.golang.org." 901 const searchY = "test-issue19592.y.golang.org." 902 const ip4 = "192.0.2.1" 903 const ip6 = "2001:db8::1" 904 905 type resolveWhichEnum int 906 const ( 907 resolveOK resolveWhichEnum = iota 908 resolveOpError 909 resolveServfail 910 resolveTimeout 911 ) 912 913 makeTempError := func(err string) error { 914 return &DNSError{ 915 Err: err, 916 Name: name, 917 Server: server, 918 IsTemporary: true, 919 } 920 } 921 makeTimeout := func() error { 922 return &DNSError{ 923 Err: poll.ErrTimeout.Error(), 924 Name: name, 925 Server: server, 926 IsTimeout: true, 927 } 928 } 929 makeNxDomain := func() error { 930 return &DNSError{ 931 Err: errNoSuchHost.Error(), 932 Name: name, 933 Server: server, 934 } 935 } 936 937 cases := []struct { 938 desc string 939 resolveWhich func(quest *dnsQuestion) resolveWhichEnum 940 wantStrictErr error 941 wantLaxErr error 942 wantIPs []string 943 }{ 944 { 945 desc: "No errors", 946 resolveWhich: func(quest *dnsQuestion) resolveWhichEnum { 947 return resolveOK 948 }, 949 wantIPs: []string{ip4, ip6}, 950 }, 951 { 952 desc: "searchX error fails in strict mode", 953 resolveWhich: func(quest *dnsQuestion) resolveWhichEnum { 954 if quest.Name == searchX { 955 return resolveTimeout 956 } 957 return resolveOK 958 }, 959 wantStrictErr: makeTimeout(), 960 wantIPs: []string{ip4, ip6}, 961 }, 962 { 963 desc: "searchX IPv4-only timeout fails in strict mode", 964 resolveWhich: func(quest *dnsQuestion) resolveWhichEnum { 965 if quest.Name == searchX && quest.Qtype == dnsTypeA { 966 return resolveTimeout 967 } 968 return resolveOK 969 }, 970 wantStrictErr: makeTimeout(), 971 wantIPs: []string{ip4, ip6}, 972 }, 973 { 974 desc: "searchX IPv6-only servfail fails in strict mode", 975 resolveWhich: func(quest *dnsQuestion) resolveWhichEnum { 976 if quest.Name == searchX && quest.Qtype == dnsTypeAAAA { 977 return resolveServfail 978 } 979 return resolveOK 980 }, 981 wantStrictErr: makeTempError("server misbehaving"), 982 wantIPs: []string{ip4, ip6}, 983 }, 984 { 985 desc: "searchY error always fails", 986 resolveWhich: func(quest *dnsQuestion) resolveWhichEnum { 987 if quest.Name == searchY { 988 return resolveTimeout 989 } 990 return resolveOK 991 }, 992 wantStrictErr: makeTimeout(), 993 wantLaxErr: makeNxDomain(), // This one reaches the "test." FQDN. 994 }, 995 { 996 desc: "searchY IPv4-only socket error fails in strict mode", 997 resolveWhich: func(quest *dnsQuestion) resolveWhichEnum { 998 if quest.Name == searchY && quest.Qtype == dnsTypeA { 999 return resolveOpError 1000 } 1001 return resolveOK 1002 }, 1003 wantStrictErr: makeTempError("write: socket on fire"), 1004 wantIPs: []string{ip6}, 1005 }, 1006 { 1007 desc: "searchY IPv6-only timeout fails in strict mode", 1008 resolveWhich: func(quest *dnsQuestion) resolveWhichEnum { 1009 if quest.Name == searchY && quest.Qtype == dnsTypeAAAA { 1010 return resolveTimeout 1011 } 1012 return resolveOK 1013 }, 1014 wantStrictErr: makeTimeout(), 1015 wantIPs: []string{ip4}, 1016 }, 1017 } 1018 1019 for i, tt := range cases { 1020 d := &fakeDNSDialer{} 1021 testHookDNSDialer = func() dnsDialer { return d } 1022 1023 d.rh = func(s string, q *dnsMsg, deadline time.Time) (*dnsMsg, error) { 1024 t.Log(s, q) 1025 1026 switch tt.resolveWhich(&q.question[0]) { 1027 case resolveOK: 1028 // Handle below. 1029 case resolveOpError: 1030 return nil, &OpError{Op: "write", Err: fmt.Errorf("socket on fire")} 1031 case resolveServfail: 1032 return &dnsMsg{dnsMsgHdr: dnsMsgHdr{id: q.id, rcode: dnsRcodeServerFailure}}, nil 1033 case resolveTimeout: 1034 return nil, poll.ErrTimeout 1035 default: 1036 t.Fatal("Impossible resolveWhich") 1037 } 1038 1039 switch q.question[0].Name { 1040 case searchX, name + ".": 1041 // Return NXDOMAIN to utilize the search list. 1042 return &dnsMsg{dnsMsgHdr: dnsMsgHdr{id: q.id, rcode: dnsRcodeNameError}}, nil 1043 case searchY: 1044 // Return records below. 1045 default: 1046 return nil, fmt.Errorf("Unexpected Name: %v", q.question[0].Name) 1047 } 1048 1049 r := &dnsMsg{ 1050 dnsMsgHdr: dnsMsgHdr{ 1051 id: q.id, 1052 response: true, 1053 }, 1054 question: q.question, 1055 } 1056 switch q.question[0].Qtype { 1057 case dnsTypeA: 1058 r.answer = []dnsRR{ 1059 &dnsRR_A{ 1060 Hdr: dnsRR_Header{ 1061 Name: q.question[0].Name, 1062 Rrtype: dnsTypeA, 1063 Class: dnsClassINET, 1064 Rdlength: 4, 1065 }, 1066 A: TestAddr, 1067 }, 1068 } 1069 case dnsTypeAAAA: 1070 r.answer = []dnsRR{ 1071 &dnsRR_AAAA{ 1072 Hdr: dnsRR_Header{ 1073 Name: q.question[0].Name, 1074 Rrtype: dnsTypeAAAA, 1075 Class: dnsClassINET, 1076 Rdlength: 16, 1077 }, 1078 AAAA: TestAddr6, 1079 }, 1080 } 1081 default: 1082 return nil, fmt.Errorf("Unexpected Qtype: %v", q.question[0].Qtype) 1083 } 1084 return r, nil 1085 } 1086 1087 for _, strict := range []bool{true, false} { 1088 r := Resolver{StrictErrors: strict} 1089 ips, err := r.goLookupIP(context.Background(), name) 1090 1091 var wantErr error 1092 if strict { 1093 wantErr = tt.wantStrictErr 1094 } else { 1095 wantErr = tt.wantLaxErr 1096 } 1097 if !reflect.DeepEqual(err, wantErr) { 1098 t.Errorf("#%d (%s) strict=%v: got err %#v; want %#v", i, tt.desc, strict, err, wantErr) 1099 } 1100 1101 gotIPs := map[string]struct{}{} 1102 for _, ip := range ips { 1103 gotIPs[ip.String()] = struct{}{} 1104 } 1105 wantIPs := map[string]struct{}{} 1106 if wantErr == nil { 1107 for _, ip := range tt.wantIPs { 1108 wantIPs[ip] = struct{}{} 1109 } 1110 } 1111 if !reflect.DeepEqual(gotIPs, wantIPs) { 1112 t.Errorf("#%d (%s) strict=%v: got ips %v; want %v", i, tt.desc, strict, gotIPs, wantIPs) 1113 } 1114 } 1115 } 1116 } 1117 1118 // Issue 17448. With StrictErrors enabled, temporary errors should make 1119 // LookupTXT stop walking the search list. 1120 func TestStrictErrorsLookupTXT(t *testing.T) { 1121 origTestHookDNSDialer := testHookDNSDialer 1122 defer func() { testHookDNSDialer = origTestHookDNSDialer }() 1123 1124 conf, err := newResolvConfTest() 1125 if err != nil { 1126 t.Fatal(err) 1127 } 1128 defer conf.teardown() 1129 1130 confData := []string{ 1131 "nameserver 192.0.2.53", 1132 "search x.golang.org y.golang.org", 1133 } 1134 if err := conf.writeAndUpdate(confData); err != nil { 1135 t.Fatal(err) 1136 } 1137 1138 const name = "test" 1139 const server = "192.0.2.53:53" 1140 const searchX = "test.x.golang.org." 1141 const searchY = "test.y.golang.org." 1142 const txt = "Hello World" 1143 1144 d := &fakeDNSDialer{} 1145 testHookDNSDialer = func() dnsDialer { return d } 1146 1147 d.rh = func(s string, q *dnsMsg, deadline time.Time) (*dnsMsg, error) { 1148 t.Log(s, q) 1149 1150 switch q.question[0].Name { 1151 case searchX: 1152 return nil, poll.ErrTimeout 1153 case searchY: 1154 return mockTXTResponse(q), nil 1155 default: 1156 return nil, fmt.Errorf("Unexpected Name: %v", q.question[0].Name) 1157 } 1158 } 1159 1160 for _, strict := range []bool{true, false} { 1161 r := Resolver{StrictErrors: strict} 1162 _, rrs, err := r.lookup(context.Background(), name, dnsTypeTXT) 1163 var wantErr error 1164 var wantRRs int 1165 if strict { 1166 wantErr = &DNSError{ 1167 Err: poll.ErrTimeout.Error(), 1168 Name: name, 1169 Server: server, 1170 IsTimeout: true, 1171 } 1172 } else { 1173 wantRRs = 1 1174 } 1175 if !reflect.DeepEqual(err, wantErr) { 1176 t.Errorf("strict=%v: got err %#v; want %#v", strict, err, wantErr) 1177 } 1178 if len(rrs) != wantRRs { 1179 t.Errorf("strict=%v: got %v; want %v", strict, len(rrs), wantRRs) 1180 } 1181 } 1182 }