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