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//go:build unix 6 7package net 8 9import ( 10 "context" 11 "errors" 12 "fmt" 13 "os" 14 "path" 15 "path/filepath" 16 "reflect" 17 "runtime" 18 "slices" 19 "strings" 20 "sync" 21 "sync/atomic" 22 "testing" 23 "time" 24 25 "golang.org/x/net/dns/dnsmessage" 26) 27 28// Test address from 192.0.2.0/24 block, reserved by RFC 5737 for documentation. 29var TestAddr = [4]byte{0xc0, 0x00, 0x02, 0x01} 30 31// Test address from 2001:db8::/32 block, reserved by RFC 3849 for documentation. 32var TestAddr6 = [16]byte{0x20, 0x01, 0x0d, 0xb8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1} 33 34func 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 42func 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 50var 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 62func 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, useUDPOrTCP, false) 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 95func TestDNSTransportNoFallbackOnTCP(t *testing.T) { 96 fake := fakeDNSServer{ 97 rh: func(n, _ string, q dnsmessage.Message, _ time.Time) (dnsmessage.Message, error) { 98 r := dnsmessage.Message{ 99 Header: dnsmessage.Header{ 100 ID: q.Header.ID, 101 Response: true, 102 RCode: dnsmessage.RCodeSuccess, 103 Truncated: true, 104 }, 105 Questions: q.Questions, 106 } 107 if n == "tcp" { 108 r.Answers = []dnsmessage.Resource{ 109 { 110 Header: dnsmessage.ResourceHeader{ 111 Name: q.Questions[0].Name, 112 Type: dnsmessage.TypeA, 113 Class: dnsmessage.ClassINET, 114 Length: 4, 115 }, 116 Body: &dnsmessage.AResource{ 117 A: TestAddr, 118 }, 119 }, 120 } 121 } 122 return r, nil 123 }, 124 } 125 r := Resolver{PreferGo: true, Dial: fake.DialContext} 126 for _, tt := range dnsTransportFallbackTests { 127 ctx, cancel := context.WithCancel(context.Background()) 128 defer cancel() 129 p, h, err := r.exchange(ctx, tt.server, tt.question, time.Second, useUDPOrTCP, false) 130 if err != nil { 131 t.Error(err) 132 continue 133 } 134 if h.RCode != tt.rcode { 135 t.Errorf("got %v from %v; want %v", h.RCode, tt.server, tt.rcode) 136 continue 137 } 138 a, err := p.AllAnswers() 139 if err != nil { 140 t.Errorf("unexpected error %v getting all answers from %v", err, tt.server) 141 continue 142 } 143 if len(a) != 1 { 144 t.Errorf("got %d answers from %v; want 1", len(a), tt.server) 145 continue 146 } 147 } 148} 149 150// See RFC 6761 for further information about the reserved, pseudo 151// domain names. 152var specialDomainNameTests = []struct { 153 question dnsmessage.Question 154 rcode dnsmessage.RCode 155}{ 156 // Name resolution APIs and libraries should not recognize the 157 // followings as special. 158 {mustQuestion("1.0.168.192.in-addr.arpa.", dnsmessage.TypePTR, dnsmessage.ClassINET), dnsmessage.RCodeNameError}, 159 {mustQuestion("test.", dnsmessage.TypeALL, dnsmessage.ClassINET), dnsmessage.RCodeNameError}, 160 {mustQuestion("example.com.", dnsmessage.TypeALL, dnsmessage.ClassINET), dnsmessage.RCodeSuccess}, 161 162 // Name resolution APIs and libraries should recognize the 163 // followings as special and should not send any queries. 164 // Though, we test those names here for verifying negative 165 // answers at DNS query-response interaction level. 166 {mustQuestion("localhost.", dnsmessage.TypeALL, dnsmessage.ClassINET), dnsmessage.RCodeNameError}, 167 {mustQuestion("invalid.", dnsmessage.TypeALL, dnsmessage.ClassINET), dnsmessage.RCodeNameError}, 168} 169 170func TestSpecialDomainName(t *testing.T) { 171 fake := fakeDNSServer{rh: func(_, _ string, q dnsmessage.Message, _ time.Time) (dnsmessage.Message, error) { 172 r := dnsmessage.Message{ 173 Header: dnsmessage.Header{ 174 ID: q.ID, 175 Response: true, 176 }, 177 Questions: q.Questions, 178 } 179 180 switch q.Questions[0].Name.String() { 181 case "example.com.": 182 r.Header.RCode = dnsmessage.RCodeSuccess 183 default: 184 r.Header.RCode = dnsmessage.RCodeNameError 185 } 186 187 return r, nil 188 }} 189 r := Resolver{PreferGo: true, Dial: fake.DialContext} 190 server := "8.8.8.8:53" 191 for _, tt := range specialDomainNameTests { 192 ctx, cancel := context.WithCancel(context.Background()) 193 defer cancel() 194 _, h, err := r.exchange(ctx, server, tt.question, 3*time.Second, useUDPOrTCP, false) 195 if err != nil { 196 t.Error(err) 197 continue 198 } 199 if h.RCode != tt.rcode { 200 t.Errorf("got %v from %v; want %v", h.RCode, server, tt.rcode) 201 continue 202 } 203 } 204} 205 206// Issue 13705: don't try to resolve onion addresses, etc 207func TestAvoidDNSName(t *testing.T) { 208 tests := []struct { 209 name string 210 avoid bool 211 }{ 212 {"foo.com", false}, 213 {"foo.com.", false}, 214 215 {"foo.onion.", true}, 216 {"foo.onion", true}, 217 {"foo.ONION", true}, 218 {"foo.ONION.", true}, 219 220 // But do resolve *.local address; Issue 16739 221 {"foo.local.", false}, 222 {"foo.local", false}, 223 {"foo.LOCAL", false}, 224 {"foo.LOCAL.", false}, 225 226 {"", true}, // will be rejected earlier too 227 228 // Without stuff before onion/local, they're fine to 229 // use DNS. With a search path, 230 // "onion.vegetables.com" can use DNS. Without a 231 // search path (or with a trailing dot), the queries 232 // are just kinda useless, but don't reveal anything 233 // private. 234 {"local", false}, 235 {"onion", false}, 236 {"local.", false}, 237 {"onion.", false}, 238 } 239 for _, tt := range tests { 240 got := avoidDNS(tt.name) 241 if got != tt.avoid { 242 t.Errorf("avoidDNS(%q) = %v; want %v", tt.name, got, tt.avoid) 243 } 244 } 245} 246 247func TestNameListAvoidDNS(t *testing.T) { 248 c := &dnsConfig{search: []string{"go.dev.", "onion."}} 249 got := c.nameList("www") 250 if !slices.Equal(got, []string{"www.", "www.go.dev."}) { 251 t.Fatalf(`nameList("www") = %v, want "www.", "www.go.dev."`, got) 252 } 253 254 got = c.nameList("www.onion") 255 if !slices.Equal(got, []string{"www.onion.go.dev."}) { 256 t.Fatalf(`nameList("www.onion") = %v, want "www.onion.go.dev."`, got) 257 } 258} 259 260var fakeDNSServerSuccessful = fakeDNSServer{rh: func(_, _ string, q dnsmessage.Message, _ time.Time) (dnsmessage.Message, error) { 261 r := dnsmessage.Message{ 262 Header: dnsmessage.Header{ 263 ID: q.ID, 264 Response: true, 265 }, 266 Questions: q.Questions, 267 } 268 if len(q.Questions) == 1 && q.Questions[0].Type == dnsmessage.TypeA { 269 r.Answers = []dnsmessage.Resource{ 270 { 271 Header: dnsmessage.ResourceHeader{ 272 Name: q.Questions[0].Name, 273 Type: dnsmessage.TypeA, 274 Class: dnsmessage.ClassINET, 275 Length: 4, 276 }, 277 Body: &dnsmessage.AResource{ 278 A: TestAddr, 279 }, 280 }, 281 } 282 } 283 return r, nil 284}} 285 286// Issue 13705: don't try to resolve onion addresses, etc 287func TestLookupTorOnion(t *testing.T) { 288 defer dnsWaitGroup.Wait() 289 r := Resolver{PreferGo: true, Dial: fakeDNSServerSuccessful.DialContext} 290 addrs, err := r.LookupIPAddr(context.Background(), "foo.onion.") 291 if err != nil { 292 t.Fatalf("lookup = %v; want nil", err) 293 } 294 if len(addrs) > 0 { 295 t.Errorf("unexpected addresses: %v", addrs) 296 } 297} 298 299type resolvConfTest struct { 300 dir string 301 path string 302 *resolverConfig 303} 304 305func newResolvConfTest() (*resolvConfTest, error) { 306 dir, err := os.MkdirTemp("", "go-resolvconftest") 307 if err != nil { 308 return nil, err 309 } 310 conf := &resolvConfTest{ 311 dir: dir, 312 path: path.Join(dir, "resolv.conf"), 313 resolverConfig: &resolvConf, 314 } 315 conf.initOnce.Do(conf.init) 316 return conf, nil 317} 318 319func (conf *resolvConfTest) write(lines []string) error { 320 f, err := os.OpenFile(conf.path, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0600) 321 if err != nil { 322 return err 323 } 324 if _, err := f.WriteString(strings.Join(lines, "\n")); err != nil { 325 f.Close() 326 return err 327 } 328 f.Close() 329 return nil 330} 331 332func (conf *resolvConfTest) writeAndUpdate(lines []string) error { 333 return conf.writeAndUpdateWithLastCheckedTime(lines, time.Now().Add(time.Hour)) 334} 335 336func (conf *resolvConfTest) writeAndUpdateWithLastCheckedTime(lines []string, lastChecked time.Time) error { 337 if err := conf.write(lines); err != nil { 338 return err 339 } 340 return conf.forceUpdate(conf.path, lastChecked) 341} 342 343func (conf *resolvConfTest) forceUpdate(name string, lastChecked time.Time) error { 344 dnsConf := dnsReadConfig(name) 345 if !conf.forceUpdateConf(dnsConf, lastChecked) { 346 return fmt.Errorf("tryAcquireSema for %s failed", name) 347 } 348 return nil 349} 350 351func (conf *resolvConfTest) forceUpdateConf(c *dnsConfig, lastChecked time.Time) bool { 352 conf.dnsConfig.Store(c) 353 for i := 0; i < 5; i++ { 354 if conf.tryAcquireSema() { 355 conf.lastChecked = lastChecked 356 conf.releaseSema() 357 return true 358 } 359 } 360 return false 361} 362 363func (conf *resolvConfTest) servers() []string { 364 return conf.dnsConfig.Load().servers 365} 366 367func (conf *resolvConfTest) teardown() error { 368 err := conf.forceUpdate("/etc/resolv.conf", time.Time{}) 369 os.RemoveAll(conf.dir) 370 return err 371} 372 373var updateResolvConfTests = []struct { 374 name string // query name 375 lines []string // resolver configuration lines 376 servers []string // expected name servers 377}{ 378 { 379 name: "golang.org", 380 lines: []string{"nameserver 8.8.8.8"}, 381 servers: []string{"8.8.8.8:53"}, 382 }, 383 { 384 name: "", 385 lines: nil, // an empty resolv.conf should use defaultNS as name servers 386 servers: defaultNS, 387 }, 388 { 389 name: "www.example.com", 390 lines: []string{"nameserver 8.8.4.4"}, 391 servers: []string{"8.8.4.4:53"}, 392 }, 393} 394 395func TestUpdateResolvConf(t *testing.T) { 396 defer dnsWaitGroup.Wait() 397 398 r := Resolver{PreferGo: true, Dial: fakeDNSServerSuccessful.DialContext} 399 400 conf, err := newResolvConfTest() 401 if err != nil { 402 t.Fatal(err) 403 } 404 defer conf.teardown() 405 406 for i, tt := range updateResolvConfTests { 407 if err := conf.writeAndUpdate(tt.lines); err != nil { 408 t.Error(err) 409 continue 410 } 411 if tt.name != "" { 412 var wg sync.WaitGroup 413 const N = 10 414 wg.Add(N) 415 for j := 0; j < N; j++ { 416 go func(name string) { 417 defer wg.Done() 418 ips, err := r.LookupIPAddr(context.Background(), name) 419 if err != nil { 420 t.Error(err) 421 return 422 } 423 if len(ips) == 0 { 424 t.Errorf("no records for %s", name) 425 return 426 } 427 }(tt.name) 428 } 429 wg.Wait() 430 } 431 servers := conf.servers() 432 if !reflect.DeepEqual(servers, tt.servers) { 433 t.Errorf("#%d: got %v; want %v", i, servers, tt.servers) 434 continue 435 } 436 } 437} 438 439var goLookupIPWithResolverConfigTests = []struct { 440 name string 441 lines []string // resolver configuration lines 442 error 443 a, aaaa bool // whether response contains A, AAAA-record 444}{ 445 // no records, transport timeout 446 { 447 "jgahvsekduiv9bw4b3qhn4ykdfgj0493iohkrjfhdvhjiu4j", 448 []string{ 449 "options timeout:1 attempts:1", 450 "nameserver 255.255.255.255", // please forgive us for abuse of limited broadcast address 451 }, 452 &DNSError{Name: "jgahvsekduiv9bw4b3qhn4ykdfgj0493iohkrjfhdvhjiu4j", Server: "255.255.255.255:53", IsTimeout: true}, 453 false, false, 454 }, 455 456 // no records, non-existent domain 457 { 458 "jgahvsekduiv9bw4b3qhn4ykdfgj0493iohkrjfhdvhjiu4j", 459 []string{ 460 "options timeout:3 attempts:1", 461 "nameserver 8.8.8.8", 462 }, 463 &DNSError{Name: "jgahvsekduiv9bw4b3qhn4ykdfgj0493iohkrjfhdvhjiu4j", Server: "8.8.8.8:53", IsTimeout: false}, 464 false, false, 465 }, 466 467 // a few A records, no AAAA records 468 { 469 "ipv4.google.com.", 470 []string{ 471 "nameserver 8.8.8.8", 472 "nameserver 2001:4860:4860::8888", 473 }, 474 nil, 475 true, false, 476 }, 477 { 478 "ipv4.google.com", 479 []string{ 480 "domain golang.org", 481 "nameserver 2001:4860:4860::8888", 482 "nameserver 8.8.8.8", 483 }, 484 nil, 485 true, false, 486 }, 487 { 488 "ipv4.google.com", 489 []string{ 490 "search x.golang.org y.golang.org", 491 "nameserver 2001:4860:4860::8888", 492 "nameserver 8.8.8.8", 493 }, 494 nil, 495 true, false, 496 }, 497 498 // no A records, a few AAAA records 499 { 500 "ipv6.google.com.", 501 []string{ 502 "nameserver 2001:4860:4860::8888", 503 "nameserver 8.8.8.8", 504 }, 505 nil, 506 false, true, 507 }, 508 { 509 "ipv6.google.com", 510 []string{ 511 "domain golang.org", 512 "nameserver 8.8.8.8", 513 "nameserver 2001:4860:4860::8888", 514 }, 515 nil, 516 false, true, 517 }, 518 { 519 "ipv6.google.com", 520 []string{ 521 "search x.golang.org y.golang.org", 522 "nameserver 8.8.8.8", 523 "nameserver 2001:4860:4860::8888", 524 }, 525 nil, 526 false, true, 527 }, 528 529 // both A and AAAA records 530 { 531 "hostname.as112.net", // see RFC 7534 532 []string{ 533 "domain golang.org", 534 "nameserver 2001:4860:4860::8888", 535 "nameserver 8.8.8.8", 536 }, 537 nil, 538 true, true, 539 }, 540 { 541 "hostname.as112.net", // see RFC 7534 542 []string{ 543 "search x.golang.org y.golang.org", 544 "nameserver 2001:4860:4860::8888", 545 "nameserver 8.8.8.8", 546 }, 547 nil, 548 true, true, 549 }, 550} 551 552func TestGoLookupIPWithResolverConfig(t *testing.T) { 553 defer dnsWaitGroup.Wait() 554 fake := fakeDNSServer{rh: func(n, s string, q dnsmessage.Message, _ time.Time) (dnsmessage.Message, error) { 555 switch s { 556 case "[2001:4860:4860::8888]:53", "8.8.8.8:53": 557 break 558 default: 559 time.Sleep(10 * time.Millisecond) 560 return dnsmessage.Message{}, os.ErrDeadlineExceeded 561 } 562 r := dnsmessage.Message{ 563 Header: dnsmessage.Header{ 564 ID: q.ID, 565 Response: true, 566 }, 567 Questions: q.Questions, 568 } 569 for _, question := range q.Questions { 570 switch question.Type { 571 case dnsmessage.TypeA: 572 switch question.Name.String() { 573 case "hostname.as112.net.": 574 break 575 case "ipv4.google.com.": 576 r.Answers = append(r.Answers, dnsmessage.Resource{ 577 Header: dnsmessage.ResourceHeader{ 578 Name: q.Questions[0].Name, 579 Type: dnsmessage.TypeA, 580 Class: dnsmessage.ClassINET, 581 Length: 4, 582 }, 583 Body: &dnsmessage.AResource{ 584 A: TestAddr, 585 }, 586 }) 587 default: 588 589 } 590 case dnsmessage.TypeAAAA: 591 switch question.Name.String() { 592 case "hostname.as112.net.": 593 break 594 case "ipv6.google.com.": 595 r.Answers = append(r.Answers, dnsmessage.Resource{ 596 Header: dnsmessage.ResourceHeader{ 597 Name: q.Questions[0].Name, 598 Type: dnsmessage.TypeAAAA, 599 Class: dnsmessage.ClassINET, 600 Length: 16, 601 }, 602 Body: &dnsmessage.AAAAResource{ 603 AAAA: TestAddr6, 604 }, 605 }) 606 } 607 } 608 } 609 return r, nil 610 }} 611 r := Resolver{PreferGo: true, Dial: fake.DialContext} 612 613 conf, err := newResolvConfTest() 614 if err != nil { 615 t.Fatal(err) 616 } 617 defer conf.teardown() 618 619 for _, tt := range goLookupIPWithResolverConfigTests { 620 if err := conf.writeAndUpdate(tt.lines); err != nil { 621 t.Error(err) 622 continue 623 } 624 addrs, err := r.LookupIPAddr(context.Background(), tt.name) 625 if err != nil { 626 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) { 627 t.Errorf("got %v; want %v", err, tt.error) 628 } 629 continue 630 } 631 if len(addrs) == 0 { 632 t.Errorf("no records for %s", tt.name) 633 } 634 if !tt.a && !tt.aaaa && len(addrs) > 0 { 635 t.Errorf("unexpected %v for %s", addrs, tt.name) 636 } 637 for _, addr := range addrs { 638 if !tt.a && addr.IP.To4() != nil { 639 t.Errorf("got %v; must not be IPv4 address", addr) 640 } 641 if !tt.aaaa && addr.IP.To16() != nil && addr.IP.To4() == nil { 642 t.Errorf("got %v; must not be IPv6 address", addr) 643 } 644 } 645 } 646} 647 648// Test that goLookupIPOrder falls back to the host file when no DNS servers are available. 649func TestGoLookupIPOrderFallbackToFile(t *testing.T) { 650 defer dnsWaitGroup.Wait() 651 652 fake := fakeDNSServer{rh: func(n, s string, q dnsmessage.Message, tm time.Time) (dnsmessage.Message, error) { 653 r := dnsmessage.Message{ 654 Header: dnsmessage.Header{ 655 ID: q.ID, 656 Response: true, 657 }, 658 Questions: q.Questions, 659 } 660 return r, nil 661 }} 662 r := Resolver{PreferGo: true, Dial: fake.DialContext} 663 664 // Add a config that simulates no dns servers being available. 665 conf, err := newResolvConfTest() 666 if err != nil { 667 t.Fatal(err) 668 } 669 defer conf.teardown() 670 671 if err := conf.writeAndUpdate([]string{}); err != nil { 672 t.Fatal(err) 673 } 674 // Redirect host file lookups. 675 defer func(orig string) { hostsFilePath = orig }(hostsFilePath) 676 hostsFilePath = "testdata/hosts" 677 678 for _, order := range []hostLookupOrder{hostLookupFilesDNS, hostLookupDNSFiles} { 679 name := fmt.Sprintf("order %v", order) 680 // First ensure that we get an error when contacting a non-existent host. 681 _, _, err := r.goLookupIPCNAMEOrder(context.Background(), "ip", "notarealhost", order, nil) 682 if err == nil { 683 t.Errorf("%s: expected error while looking up name not in hosts file", name) 684 continue 685 } 686 687 // Now check that we get an address when the name appears in the hosts file. 688 addrs, _, err := r.goLookupIPCNAMEOrder(context.Background(), "ip", "thor", order, nil) // entry is in "testdata/hosts" 689 if err != nil { 690 t.Errorf("%s: expected to successfully lookup host entry", name) 691 continue 692 } 693 if len(addrs) != 1 { 694 t.Errorf("%s: expected exactly one result, but got %v", name, addrs) 695 continue 696 } 697 if got, want := addrs[0].String(), "127.1.1.1"; got != want { 698 t.Errorf("%s: address doesn't match expectation. got %v, want %v", name, got, want) 699 } 700 } 701} 702 703// Issue 12712. 704// When using search domains, return the error encountered 705// querying the original name instead of an error encountered 706// querying a generated name. 707func TestErrorForOriginalNameWhenSearching(t *testing.T) { 708 defer dnsWaitGroup.Wait() 709 710 const fqdn = "doesnotexist.domain" 711 712 conf, err := newResolvConfTest() 713 if err != nil { 714 t.Fatal(err) 715 } 716 defer conf.teardown() 717 718 if err := conf.writeAndUpdate([]string{"search servfail"}); err != nil { 719 t.Fatal(err) 720 } 721 722 fake := fakeDNSServer{rh: func(_, _ string, q dnsmessage.Message, _ time.Time) (dnsmessage.Message, error) { 723 r := dnsmessage.Message{ 724 Header: dnsmessage.Header{ 725 ID: q.ID, 726 Response: true, 727 }, 728 Questions: q.Questions, 729 } 730 731 switch q.Questions[0].Name.String() { 732 case fqdn + ".servfail.": 733 r.Header.RCode = dnsmessage.RCodeServerFailure 734 default: 735 r.Header.RCode = dnsmessage.RCodeNameError 736 } 737 738 return r, nil 739 }} 740 741 cases := []struct { 742 strictErrors bool 743 wantErr *DNSError 744 }{ 745 {true, &DNSError{Name: fqdn, Err: "server misbehaving", IsTemporary: true}}, 746 {false, &DNSError{Name: fqdn, Err: errNoSuchHost.Error(), IsNotFound: true}}, 747 } 748 for _, tt := range cases { 749 r := Resolver{PreferGo: true, StrictErrors: tt.strictErrors, Dial: fake.DialContext} 750 _, err = r.LookupIPAddr(context.Background(), fqdn) 751 if err == nil { 752 t.Fatal("expected an error") 753 } 754 755 want := tt.wantErr 756 if err, ok := err.(*DNSError); !ok || err.Name != want.Name || err.Err != want.Err || err.IsTemporary != want.IsTemporary { 757 t.Errorf("got %v; want %v", err, want) 758 } 759 } 760} 761 762// Issue 15434. If a name server gives a lame referral, continue to the next. 763func TestIgnoreLameReferrals(t *testing.T) { 764 defer dnsWaitGroup.Wait() 765 766 conf, err := newResolvConfTest() 767 if err != nil { 768 t.Fatal(err) 769 } 770 defer conf.teardown() 771 772 if err := conf.writeAndUpdate([]string{"nameserver 192.0.2.1", // the one that will give a lame referral 773 "nameserver 192.0.2.2"}); err != nil { 774 t.Fatal(err) 775 } 776 777 fake := fakeDNSServer{rh: func(_, s string, q dnsmessage.Message, _ time.Time) (dnsmessage.Message, error) { 778 t.Log(s, q) 779 r := dnsmessage.Message{ 780 Header: dnsmessage.Header{ 781 ID: q.ID, 782 Response: true, 783 }, 784 Questions: q.Questions, 785 } 786 787 if s == "192.0.2.2:53" { 788 r.Header.RecursionAvailable = true 789 if q.Questions[0].Type == dnsmessage.TypeA { 790 r.Answers = []dnsmessage.Resource{ 791 { 792 Header: dnsmessage.ResourceHeader{ 793 Name: q.Questions[0].Name, 794 Type: dnsmessage.TypeA, 795 Class: dnsmessage.ClassINET, 796 Length: 4, 797 }, 798 Body: &dnsmessage.AResource{ 799 A: TestAddr, 800 }, 801 }, 802 } 803 } 804 } else if s == "192.0.2.1:53" { 805 if q.Questions[0].Type == dnsmessage.TypeA && strings.HasPrefix(q.Questions[0].Name.String(), "empty.com.") { 806 var edns0Hdr dnsmessage.ResourceHeader 807 edns0Hdr.SetEDNS0(maxDNSPacketSize, dnsmessage.RCodeSuccess, false) 808 809 r.Additionals = []dnsmessage.Resource{ 810 { 811 Header: edns0Hdr, 812 Body: &dnsmessage.OPTResource{}, 813 }, 814 } 815 } 816 } 817 818 return r, nil 819 }} 820 r := Resolver{PreferGo: true, Dial: fake.DialContext} 821 822 addrs, err := r.LookupIP(context.Background(), "ip4", "www.golang.org") 823 if err != nil { 824 t.Fatal(err) 825 } 826 827 if got := len(addrs); got != 1 { 828 t.Fatalf("got %d addresses, want 1", got) 829 } 830 831 if got, want := addrs[0].String(), "192.0.2.1"; got != want { 832 t.Fatalf("got address %v, want %v", got, want) 833 } 834 835 _, err = r.LookupIP(context.Background(), "ip4", "empty.com") 836 de, ok := err.(*DNSError) 837 if !ok { 838 t.Fatalf("err = %#v; wanted a *net.DNSError", err) 839 } 840 if de.Err != errNoSuchHost.Error() { 841 t.Fatalf("Err = %#v; wanted %q", de.Err, errNoSuchHost.Error()) 842 } 843} 844 845func BenchmarkGoLookupIP(b *testing.B) { 846 testHookUninstaller.Do(uninstallTestHooks) 847 ctx := context.Background() 848 b.ReportAllocs() 849 850 for i := 0; i < b.N; i++ { 851 goResolver.LookupIPAddr(ctx, "www.example.com") 852 } 853} 854 855func BenchmarkGoLookupIPNoSuchHost(b *testing.B) { 856 testHookUninstaller.Do(uninstallTestHooks) 857 ctx := context.Background() 858 b.ReportAllocs() 859 860 for i := 0; i < b.N; i++ { 861 goResolver.LookupIPAddr(ctx, "some.nonexistent") 862 } 863} 864 865func BenchmarkGoLookupIPWithBrokenNameServer(b *testing.B) { 866 testHookUninstaller.Do(uninstallTestHooks) 867 868 conf, err := newResolvConfTest() 869 if err != nil { 870 b.Fatal(err) 871 } 872 defer conf.teardown() 873 874 lines := []string{ 875 "nameserver 203.0.113.254", // use TEST-NET-3 block, see RFC 5737 876 "nameserver 8.8.8.8", 877 } 878 if err := conf.writeAndUpdate(lines); err != nil { 879 b.Fatal(err) 880 } 881 ctx := context.Background() 882 b.ReportAllocs() 883 884 for i := 0; i < b.N; i++ { 885 goResolver.LookupIPAddr(ctx, "www.example.com") 886 } 887} 888 889type fakeDNSServer struct { 890 rh func(n, s string, q dnsmessage.Message, t time.Time) (dnsmessage.Message, error) 891 alwaysTCP bool 892} 893 894func (server *fakeDNSServer) DialContext(_ context.Context, n, s string) (Conn, error) { 895 if server.alwaysTCP || n == "tcp" || n == "tcp4" || n == "tcp6" { 896 return &fakeDNSConn{tcp: true, server: server, n: n, s: s}, nil 897 } 898 return &fakeDNSPacketConn{fakeDNSConn: fakeDNSConn{tcp: false, server: server, n: n, s: s}}, nil 899} 900 901type fakeDNSConn struct { 902 Conn 903 tcp bool 904 server *fakeDNSServer 905 n string 906 s string 907 q dnsmessage.Message 908 t time.Time 909 buf []byte 910} 911 912func (f *fakeDNSConn) Close() error { 913 return nil 914} 915 916func (f *fakeDNSConn) Read(b []byte) (int, error) { 917 if len(f.buf) > 0 { 918 n := copy(b, f.buf) 919 f.buf = f.buf[n:] 920 return n, nil 921 } 922 923 resp, err := f.server.rh(f.n, f.s, f.q, f.t) 924 if err != nil { 925 return 0, err 926 } 927 928 bb := make([]byte, 2, 514) 929 bb, err = resp.AppendPack(bb) 930 if err != nil { 931 return 0, fmt.Errorf("cannot marshal DNS message: %v", err) 932 } 933 934 if f.tcp { 935 l := len(bb) - 2 936 bb[0] = byte(l >> 8) 937 bb[1] = byte(l) 938 f.buf = bb 939 return f.Read(b) 940 } 941 942 bb = bb[2:] 943 if len(b) < len(bb) { 944 return 0, errors.New("read would fragment DNS message") 945 } 946 947 copy(b, bb) 948 return len(bb), nil 949} 950 951func (f *fakeDNSConn) Write(b []byte) (int, error) { 952 if f.tcp && len(b) >= 2 { 953 b = b[2:] 954 } 955 if f.q.Unpack(b) != nil { 956 return 0, fmt.Errorf("cannot unmarshal DNS message fake %s (%d)", f.n, len(b)) 957 } 958 return len(b), nil 959} 960 961func (f *fakeDNSConn) SetDeadline(t time.Time) error { 962 f.t = t 963 return nil 964} 965 966type fakeDNSPacketConn struct { 967 PacketConn 968 fakeDNSConn 969} 970 971func (f *fakeDNSPacketConn) SetDeadline(t time.Time) error { 972 return f.fakeDNSConn.SetDeadline(t) 973} 974 975func (f *fakeDNSPacketConn) Close() error { 976 return f.fakeDNSConn.Close() 977} 978 979// UDP round-tripper algorithm should ignore invalid DNS responses (issue 13281). 980func TestIgnoreDNSForgeries(t *testing.T) { 981 c, s := Pipe() 982 go func() { 983 b := make([]byte, maxDNSPacketSize) 984 n, err := s.Read(b) 985 if err != nil { 986 t.Error(err) 987 return 988 } 989 990 var msg dnsmessage.Message 991 if msg.Unpack(b[:n]) != nil { 992 t.Error("invalid DNS query:", err) 993 return 994 } 995 996 s.Write([]byte("garbage DNS response packet")) 997 998 msg.Header.Response = true 999 msg.Header.ID++ // make invalid ID 1000 1001 if b, err = msg.Pack(); err != nil { 1002 t.Error("failed to pack DNS response:", err) 1003 return 1004 } 1005 s.Write(b) 1006 1007 msg.Header.ID-- // restore original ID 1008 msg.Answers = []dnsmessage.Resource{ 1009 { 1010 Header: dnsmessage.ResourceHeader{ 1011 Name: mustNewName("www.example.com."), 1012 Type: dnsmessage.TypeA, 1013 Class: dnsmessage.ClassINET, 1014 Length: 4, 1015 }, 1016 Body: &dnsmessage.AResource{ 1017 A: TestAddr, 1018 }, 1019 }, 1020 } 1021 1022 b, err = msg.Pack() 1023 if err != nil { 1024 t.Error("failed to pack DNS response:", err) 1025 return 1026 } 1027 s.Write(b) 1028 }() 1029 1030 msg := dnsmessage.Message{ 1031 Header: dnsmessage.Header{ 1032 ID: 42, 1033 }, 1034 Questions: []dnsmessage.Question{ 1035 { 1036 Name: mustNewName("www.example.com."), 1037 Type: dnsmessage.TypeA, 1038 Class: dnsmessage.ClassINET, 1039 }, 1040 }, 1041 } 1042 1043 b, err := msg.Pack() 1044 if err != nil { 1045 t.Fatal("Pack failed:", err) 1046 } 1047 1048 p, _, err := dnsPacketRoundTrip(c, 42, msg.Questions[0], b) 1049 if err != nil { 1050 t.Fatalf("dnsPacketRoundTrip failed: %v", err) 1051 } 1052 1053 p.SkipAllQuestions() 1054 as, err := p.AllAnswers() 1055 if err != nil { 1056 t.Fatal("AllAnswers failed:", err) 1057 } 1058 if got := as[0].Body.(*dnsmessage.AResource).A; got != TestAddr { 1059 t.Errorf("got address %v, want %v", got, TestAddr) 1060 } 1061} 1062 1063// Issue 16865. If a name server times out, continue to the next. 1064func TestRetryTimeout(t *testing.T) { 1065 defer dnsWaitGroup.Wait() 1066 1067 conf, err := newResolvConfTest() 1068 if err != nil { 1069 t.Fatal(err) 1070 } 1071 defer conf.teardown() 1072 1073 testConf := []string{ 1074 "nameserver 192.0.2.1", // the one that will timeout 1075 "nameserver 192.0.2.2", 1076 } 1077 if err := conf.writeAndUpdate(testConf); err != nil { 1078 t.Fatal(err) 1079 } 1080 1081 var deadline0 time.Time 1082 1083 fake := fakeDNSServer{rh: func(_, s string, q dnsmessage.Message, deadline time.Time) (dnsmessage.Message, error) { 1084 t.Log(s, q, deadline) 1085 1086 if deadline.IsZero() { 1087 t.Error("zero deadline") 1088 } 1089 1090 if s == "192.0.2.1:53" { 1091 deadline0 = deadline 1092 time.Sleep(10 * time.Millisecond) 1093 return dnsmessage.Message{}, os.ErrDeadlineExceeded 1094 } 1095 1096 if deadline.Equal(deadline0) { 1097 t.Error("deadline didn't change") 1098 } 1099 1100 return mockTXTResponse(q), nil 1101 }} 1102 r := &Resolver{PreferGo: true, Dial: fake.DialContext} 1103 1104 _, err = r.LookupTXT(context.Background(), "www.golang.org") 1105 if err != nil { 1106 t.Fatal(err) 1107 } 1108 1109 if deadline0.IsZero() { 1110 t.Error("deadline0 still zero", deadline0) 1111 } 1112} 1113 1114func TestRotate(t *testing.T) { 1115 // without rotation, always uses the first server 1116 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"}) 1117 1118 // with rotation, rotates through back to first 1119 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"}) 1120} 1121 1122func testRotate(t *testing.T, rotate bool, nameservers, wantServers []string) { 1123 defer dnsWaitGroup.Wait() 1124 1125 conf, err := newResolvConfTest() 1126 if err != nil { 1127 t.Fatal(err) 1128 } 1129 defer conf.teardown() 1130 1131 var confLines []string 1132 for _, ns := range nameservers { 1133 confLines = append(confLines, "nameserver "+ns) 1134 } 1135 if rotate { 1136 confLines = append(confLines, "options rotate") 1137 } 1138 1139 if err := conf.writeAndUpdate(confLines); err != nil { 1140 t.Fatal(err) 1141 } 1142 1143 var usedServers []string 1144 fake := fakeDNSServer{rh: func(_, s string, q dnsmessage.Message, deadline time.Time) (dnsmessage.Message, error) { 1145 usedServers = append(usedServers, s) 1146 return mockTXTResponse(q), nil 1147 }} 1148 r := Resolver{PreferGo: true, Dial: fake.DialContext} 1149 1150 // len(nameservers) + 1 to allow rotation to get back to start 1151 for i := 0; i < len(nameservers)+1; i++ { 1152 if _, err := r.LookupTXT(context.Background(), "www.golang.org"); err != nil { 1153 t.Fatal(err) 1154 } 1155 } 1156 1157 if !reflect.DeepEqual(usedServers, wantServers) { 1158 t.Errorf("rotate=%t got used servers:\n%v\nwant:\n%v", rotate, usedServers, wantServers) 1159 } 1160} 1161 1162func mockTXTResponse(q dnsmessage.Message) dnsmessage.Message { 1163 r := dnsmessage.Message{ 1164 Header: dnsmessage.Header{ 1165 ID: q.ID, 1166 Response: true, 1167 RecursionAvailable: true, 1168 }, 1169 Questions: q.Questions, 1170 Answers: []dnsmessage.Resource{ 1171 { 1172 Header: dnsmessage.ResourceHeader{ 1173 Name: q.Questions[0].Name, 1174 Type: dnsmessage.TypeTXT, 1175 Class: dnsmessage.ClassINET, 1176 }, 1177 Body: &dnsmessage.TXTResource{ 1178 TXT: []string{"ok"}, 1179 }, 1180 }, 1181 }, 1182 } 1183 1184 return r 1185} 1186 1187// Issue 17448. With StrictErrors enabled, temporary errors should make 1188// LookupIP fail rather than return a partial result. 1189func TestStrictErrorsLookupIP(t *testing.T) { 1190 defer dnsWaitGroup.Wait() 1191 1192 conf, err := newResolvConfTest() 1193 if err != nil { 1194 t.Fatal(err) 1195 } 1196 defer conf.teardown() 1197 1198 confData := []string{ 1199 "nameserver 192.0.2.53", 1200 "search x.golang.org y.golang.org", 1201 } 1202 if err := conf.writeAndUpdate(confData); err != nil { 1203 t.Fatal(err) 1204 } 1205 1206 const name = "test-issue19592" 1207 const server = "192.0.2.53:53" 1208 const searchX = "test-issue19592.x.golang.org." 1209 const searchY = "test-issue19592.y.golang.org." 1210 const ip4 = "192.0.2.1" 1211 const ip6 = "2001:db8::1" 1212 1213 type resolveWhichEnum int 1214 const ( 1215 resolveOK resolveWhichEnum = iota 1216 resolveOpError 1217 resolveServfail 1218 resolveTimeout 1219 ) 1220 1221 makeTempError := func(err string) error { 1222 return &DNSError{ 1223 Err: err, 1224 Name: name, 1225 Server: server, 1226 IsTemporary: true, 1227 } 1228 } 1229 makeTimeout := func() error { 1230 return &DNSError{ 1231 Err: os.ErrDeadlineExceeded.Error(), 1232 Name: name, 1233 Server: server, 1234 IsTimeout: true, 1235 IsTemporary: true, 1236 } 1237 } 1238 makeNxDomain := func() error { 1239 return &DNSError{ 1240 Err: errNoSuchHost.Error(), 1241 Name: name, 1242 Server: server, 1243 IsNotFound: true, 1244 } 1245 } 1246 1247 cases := []struct { 1248 desc string 1249 resolveWhich func(quest dnsmessage.Question) resolveWhichEnum 1250 wantStrictErr error 1251 wantLaxErr error 1252 wantIPs []string 1253 }{ 1254 { 1255 desc: "No errors", 1256 resolveWhich: func(quest dnsmessage.Question) resolveWhichEnum { 1257 return resolveOK 1258 }, 1259 wantIPs: []string{ip4, ip6}, 1260 }, 1261 { 1262 desc: "searchX error fails in strict mode", 1263 resolveWhich: func(quest dnsmessage.Question) resolveWhichEnum { 1264 if quest.Name.String() == searchX { 1265 return resolveTimeout 1266 } 1267 return resolveOK 1268 }, 1269 wantStrictErr: makeTimeout(), 1270 wantIPs: []string{ip4, ip6}, 1271 }, 1272 { 1273 desc: "searchX IPv4-only timeout fails in strict mode", 1274 resolveWhich: func(quest dnsmessage.Question) resolveWhichEnum { 1275 if quest.Name.String() == searchX && quest.Type == dnsmessage.TypeA { 1276 return resolveTimeout 1277 } 1278 return resolveOK 1279 }, 1280 wantStrictErr: makeTimeout(), 1281 wantIPs: []string{ip4, ip6}, 1282 }, 1283 { 1284 desc: "searchX IPv6-only servfail fails in strict mode", 1285 resolveWhich: func(quest dnsmessage.Question) resolveWhichEnum { 1286 if quest.Name.String() == searchX && quest.Type == dnsmessage.TypeAAAA { 1287 return resolveServfail 1288 } 1289 return resolveOK 1290 }, 1291 wantStrictErr: makeTempError("server misbehaving"), 1292 wantIPs: []string{ip4, ip6}, 1293 }, 1294 { 1295 desc: "searchY error always fails", 1296 resolveWhich: func(quest dnsmessage.Question) resolveWhichEnum { 1297 if quest.Name.String() == searchY { 1298 return resolveTimeout 1299 } 1300 return resolveOK 1301 }, 1302 wantStrictErr: makeTimeout(), 1303 wantLaxErr: makeNxDomain(), // This one reaches the "test." FQDN. 1304 }, 1305 { 1306 desc: "searchY IPv4-only socket error fails in strict mode", 1307 resolveWhich: func(quest dnsmessage.Question) resolveWhichEnum { 1308 if quest.Name.String() == searchY && quest.Type == dnsmessage.TypeA { 1309 return resolveOpError 1310 } 1311 return resolveOK 1312 }, 1313 wantStrictErr: makeTempError("write: socket on fire"), 1314 wantIPs: []string{ip6}, 1315 }, 1316 { 1317 desc: "searchY IPv6-only timeout fails in strict mode", 1318 resolveWhich: func(quest dnsmessage.Question) resolveWhichEnum { 1319 if quest.Name.String() == searchY && quest.Type == dnsmessage.TypeAAAA { 1320 return resolveTimeout 1321 } 1322 return resolveOK 1323 }, 1324 wantStrictErr: makeTimeout(), 1325 wantIPs: []string{ip4}, 1326 }, 1327 } 1328 1329 for i, tt := range cases { 1330 fake := fakeDNSServer{rh: func(_, s string, q dnsmessage.Message, deadline time.Time) (dnsmessage.Message, error) { 1331 t.Log(s, q) 1332 1333 switch tt.resolveWhich(q.Questions[0]) { 1334 case resolveOK: 1335 // Handle below. 1336 case resolveOpError: 1337 return dnsmessage.Message{}, &OpError{Op: "write", Err: fmt.Errorf("socket on fire")} 1338 case resolveServfail: 1339 return dnsmessage.Message{ 1340 Header: dnsmessage.Header{ 1341 ID: q.ID, 1342 Response: true, 1343 RCode: dnsmessage.RCodeServerFailure, 1344 }, 1345 Questions: q.Questions, 1346 }, nil 1347 case resolveTimeout: 1348 return dnsmessage.Message{}, os.ErrDeadlineExceeded 1349 default: 1350 t.Fatal("Impossible resolveWhich") 1351 } 1352 1353 switch q.Questions[0].Name.String() { 1354 case searchX, name + ".": 1355 // Return NXDOMAIN to utilize the search list. 1356 return dnsmessage.Message{ 1357 Header: dnsmessage.Header{ 1358 ID: q.ID, 1359 Response: true, 1360 RCode: dnsmessage.RCodeNameError, 1361 }, 1362 Questions: q.Questions, 1363 }, nil 1364 case searchY: 1365 // Return records below. 1366 default: 1367 return dnsmessage.Message{}, fmt.Errorf("Unexpected Name: %v", q.Questions[0].Name) 1368 } 1369 1370 r := dnsmessage.Message{ 1371 Header: dnsmessage.Header{ 1372 ID: q.ID, 1373 Response: true, 1374 }, 1375 Questions: q.Questions, 1376 } 1377 switch q.Questions[0].Type { 1378 case dnsmessage.TypeA: 1379 r.Answers = []dnsmessage.Resource{ 1380 { 1381 Header: dnsmessage.ResourceHeader{ 1382 Name: q.Questions[0].Name, 1383 Type: dnsmessage.TypeA, 1384 Class: dnsmessage.ClassINET, 1385 Length: 4, 1386 }, 1387 Body: &dnsmessage.AResource{ 1388 A: TestAddr, 1389 }, 1390 }, 1391 } 1392 case dnsmessage.TypeAAAA: 1393 r.Answers = []dnsmessage.Resource{ 1394 { 1395 Header: dnsmessage.ResourceHeader{ 1396 Name: q.Questions[0].Name, 1397 Type: dnsmessage.TypeAAAA, 1398 Class: dnsmessage.ClassINET, 1399 Length: 16, 1400 }, 1401 Body: &dnsmessage.AAAAResource{ 1402 AAAA: TestAddr6, 1403 }, 1404 }, 1405 } 1406 default: 1407 return dnsmessage.Message{}, fmt.Errorf("Unexpected Type: %v", q.Questions[0].Type) 1408 } 1409 return r, nil 1410 }} 1411 1412 for _, strict := range []bool{true, false} { 1413 r := Resolver{PreferGo: true, StrictErrors: strict, Dial: fake.DialContext} 1414 ips, err := r.LookupIPAddr(context.Background(), name) 1415 1416 var wantErr error 1417 if strict { 1418 wantErr = tt.wantStrictErr 1419 } else { 1420 wantErr = tt.wantLaxErr 1421 } 1422 if !reflect.DeepEqual(err, wantErr) { 1423 t.Errorf("#%d (%s) strict=%v: got err %#v; want %#v", i, tt.desc, strict, err, wantErr) 1424 } 1425 1426 gotIPs := map[string]struct{}{} 1427 for _, ip := range ips { 1428 gotIPs[ip.String()] = struct{}{} 1429 } 1430 wantIPs := map[string]struct{}{} 1431 if wantErr == nil { 1432 for _, ip := range tt.wantIPs { 1433 wantIPs[ip] = struct{}{} 1434 } 1435 } 1436 if !reflect.DeepEqual(gotIPs, wantIPs) { 1437 t.Errorf("#%d (%s) strict=%v: got ips %v; want %v", i, tt.desc, strict, gotIPs, wantIPs) 1438 } 1439 } 1440 } 1441} 1442 1443// Issue 17448. With StrictErrors enabled, temporary errors should make 1444// LookupTXT stop walking the search list. 1445func TestStrictErrorsLookupTXT(t *testing.T) { 1446 defer dnsWaitGroup.Wait() 1447 1448 conf, err := newResolvConfTest() 1449 if err != nil { 1450 t.Fatal(err) 1451 } 1452 defer conf.teardown() 1453 1454 confData := []string{ 1455 "nameserver 192.0.2.53", 1456 "search x.golang.org y.golang.org", 1457 } 1458 if err := conf.writeAndUpdate(confData); err != nil { 1459 t.Fatal(err) 1460 } 1461 1462 const name = "test" 1463 const server = "192.0.2.53:53" 1464 const searchX = "test.x.golang.org." 1465 const searchY = "test.y.golang.org." 1466 const txt = "Hello World" 1467 1468 fake := fakeDNSServer{rh: func(_, s string, q dnsmessage.Message, deadline time.Time) (dnsmessage.Message, error) { 1469 t.Log(s, q) 1470 1471 switch q.Questions[0].Name.String() { 1472 case searchX: 1473 return dnsmessage.Message{}, os.ErrDeadlineExceeded 1474 case searchY: 1475 return mockTXTResponse(q), nil 1476 default: 1477 return dnsmessage.Message{}, fmt.Errorf("Unexpected Name: %v", q.Questions[0].Name) 1478 } 1479 }} 1480 1481 for _, strict := range []bool{true, false} { 1482 r := Resolver{StrictErrors: strict, Dial: fake.DialContext} 1483 p, _, err := r.lookup(context.Background(), name, dnsmessage.TypeTXT, nil) 1484 var wantErr error 1485 var wantRRs int 1486 if strict { 1487 wantErr = &DNSError{ 1488 Err: os.ErrDeadlineExceeded.Error(), 1489 Name: name, 1490 Server: server, 1491 IsTimeout: true, 1492 IsTemporary: true, 1493 } 1494 } else { 1495 wantRRs = 1 1496 } 1497 if !reflect.DeepEqual(err, wantErr) { 1498 t.Errorf("strict=%v: got err %#v; want %#v", strict, err, wantErr) 1499 } 1500 a, err := p.AllAnswers() 1501 if err != nil { 1502 a = nil 1503 } 1504 if len(a) != wantRRs { 1505 t.Errorf("strict=%v: got %v; want %v", strict, len(a), wantRRs) 1506 } 1507 } 1508} 1509 1510// Test for a race between uninstalling the test hooks and closing a 1511// socket connection. This used to fail when testing with -race. 1512func TestDNSGoroutineRace(t *testing.T) { 1513 defer dnsWaitGroup.Wait() 1514 1515 fake := fakeDNSServer{rh: func(n, s string, q dnsmessage.Message, t time.Time) (dnsmessage.Message, error) { 1516 time.Sleep(10 * time.Microsecond) 1517 return dnsmessage.Message{}, os.ErrDeadlineExceeded 1518 }} 1519 r := Resolver{PreferGo: true, Dial: fake.DialContext} 1520 1521 // The timeout here is less than the timeout used by the server, 1522 // so the goroutine started to query the (fake) server will hang 1523 // around after this test is done if we don't call dnsWaitGroup.Wait. 1524 ctx, cancel := context.WithTimeout(context.Background(), 2*time.Microsecond) 1525 defer cancel() 1526 _, err := r.LookupIPAddr(ctx, "where.are.they.now") 1527 if err == nil { 1528 t.Fatal("fake DNS lookup unexpectedly succeeded") 1529 } 1530} 1531 1532func lookupWithFake(fake fakeDNSServer, name string, typ dnsmessage.Type) error { 1533 r := Resolver{PreferGo: true, Dial: fake.DialContext} 1534 1535 conf := getSystemDNSConfig() 1536 1537 ctx, cancel := context.WithCancel(context.Background()) 1538 defer cancel() 1539 1540 _, _, err := r.tryOneName(ctx, conf, name, typ) 1541 return err 1542} 1543 1544// Issue 8434: verify that Temporary returns true on an error when rcode 1545// is SERVFAIL 1546func TestIssue8434(t *testing.T) { 1547 err := lookupWithFake(fakeDNSServer{ 1548 rh: func(n, _ string, q dnsmessage.Message, _ time.Time) (dnsmessage.Message, error) { 1549 return dnsmessage.Message{ 1550 Header: dnsmessage.Header{ 1551 ID: q.ID, 1552 Response: true, 1553 RCode: dnsmessage.RCodeServerFailure, 1554 }, 1555 Questions: q.Questions, 1556 }, nil 1557 }, 1558 }, "golang.org.", dnsmessage.TypeALL) 1559 if err == nil { 1560 t.Fatal("expected an error") 1561 } 1562 if ne, ok := err.(Error); !ok { 1563 t.Fatalf("err = %#v; wanted something supporting net.Error", err) 1564 } else if !ne.Temporary() { 1565 t.Fatalf("Temporary = false for err = %#v; want Temporary == true", err) 1566 } 1567 if de, ok := err.(*DNSError); !ok { 1568 t.Fatalf("err = %#v; wanted a *net.DNSError", err) 1569 } else if !de.IsTemporary { 1570 t.Fatalf("IsTemporary = false for err = %#v; want IsTemporary == true", err) 1571 } 1572} 1573 1574func TestIssueNoSuchHostExists(t *testing.T) { 1575 err := lookupWithFake(fakeDNSServer{ 1576 rh: func(n, _ string, q dnsmessage.Message, _ time.Time) (dnsmessage.Message, error) { 1577 return dnsmessage.Message{ 1578 Header: dnsmessage.Header{ 1579 ID: q.ID, 1580 Response: true, 1581 RCode: dnsmessage.RCodeNameError, 1582 }, 1583 Questions: q.Questions, 1584 }, nil 1585 }, 1586 }, "golang.org.", dnsmessage.TypeALL) 1587 if err == nil { 1588 t.Fatal("expected an error") 1589 } 1590 if _, ok := err.(Error); !ok { 1591 t.Fatalf("err = %#v; wanted something supporting net.Error", err) 1592 } 1593 if de, ok := err.(*DNSError); !ok { 1594 t.Fatalf("err = %#v; wanted a *net.DNSError", err) 1595 } else if !de.IsNotFound { 1596 t.Fatalf("IsNotFound = false for err = %#v; want IsNotFound == true", err) 1597 } 1598} 1599 1600// TestNoSuchHost verifies that tryOneName works correctly when the domain does 1601// not exist. 1602// 1603// Issue 12778: verify that NXDOMAIN without RA bit errors as "no such host" 1604// and not "server misbehaving" 1605// 1606// Issue 25336: verify that NXDOMAIN errors fail fast. 1607// 1608// Issue 27525: verify that empty answers fail fast. 1609func TestNoSuchHost(t *testing.T) { 1610 tests := []struct { 1611 name string 1612 f func(string, string, dnsmessage.Message, time.Time) (dnsmessage.Message, error) 1613 }{ 1614 { 1615 "NXDOMAIN", 1616 func(n, _ string, q dnsmessage.Message, _ time.Time) (dnsmessage.Message, error) { 1617 return dnsmessage.Message{ 1618 Header: dnsmessage.Header{ 1619 ID: q.ID, 1620 Response: true, 1621 RCode: dnsmessage.RCodeNameError, 1622 RecursionAvailable: false, 1623 }, 1624 Questions: q.Questions, 1625 }, nil 1626 }, 1627 }, 1628 { 1629 "no answers", 1630 func(n, _ string, q dnsmessage.Message, _ time.Time) (dnsmessage.Message, error) { 1631 return dnsmessage.Message{ 1632 Header: dnsmessage.Header{ 1633 ID: q.ID, 1634 Response: true, 1635 RCode: dnsmessage.RCodeSuccess, 1636 RecursionAvailable: false, 1637 Authoritative: true, 1638 }, 1639 Questions: q.Questions, 1640 }, nil 1641 }, 1642 }, 1643 } 1644 1645 for _, test := range tests { 1646 t.Run(test.name, func(t *testing.T) { 1647 lookups := 0 1648 err := lookupWithFake(fakeDNSServer{ 1649 rh: func(n, s string, q dnsmessage.Message, d time.Time) (dnsmessage.Message, error) { 1650 lookups++ 1651 return test.f(n, s, q, d) 1652 }, 1653 }, ".", dnsmessage.TypeALL) 1654 1655 if lookups != 1 { 1656 t.Errorf("got %d lookups, wanted 1", lookups) 1657 } 1658 1659 if err == nil { 1660 t.Fatal("expected an error") 1661 } 1662 de, ok := err.(*DNSError) 1663 if !ok { 1664 t.Fatalf("err = %#v; wanted a *net.DNSError", err) 1665 } 1666 if de.Err != errNoSuchHost.Error() { 1667 t.Fatalf("Err = %#v; wanted %q", de.Err, errNoSuchHost.Error()) 1668 } 1669 if !de.IsNotFound { 1670 t.Fatalf("IsNotFound = %v wanted true", de.IsNotFound) 1671 } 1672 }) 1673 } 1674} 1675 1676// Issue 26573: verify that Conns that don't implement PacketConn are treated 1677// as streams even when udp was requested. 1678func TestDNSDialTCP(t *testing.T) { 1679 fake := fakeDNSServer{ 1680 rh: func(n, _ string, q dnsmessage.Message, _ time.Time) (dnsmessage.Message, error) { 1681 r := dnsmessage.Message{ 1682 Header: dnsmessage.Header{ 1683 ID: q.Header.ID, 1684 Response: true, 1685 RCode: dnsmessage.RCodeSuccess, 1686 }, 1687 Questions: q.Questions, 1688 } 1689 return r, nil 1690 }, 1691 alwaysTCP: true, 1692 } 1693 r := Resolver{PreferGo: true, Dial: fake.DialContext} 1694 ctx := context.Background() 1695 _, _, err := r.exchange(ctx, "0.0.0.0", mustQuestion("com.", dnsmessage.TypeALL, dnsmessage.ClassINET), time.Second, useUDPOrTCP, false) 1696 if err != nil { 1697 t.Fatal("exchange failed:", err) 1698 } 1699} 1700 1701// Issue 27763: verify that two strings in one TXT record are concatenated. 1702func TestTXTRecordTwoStrings(t *testing.T) { 1703 fake := fakeDNSServer{ 1704 rh: func(n, _ string, q dnsmessage.Message, _ time.Time) (dnsmessage.Message, error) { 1705 r := dnsmessage.Message{ 1706 Header: dnsmessage.Header{ 1707 ID: q.Header.ID, 1708 Response: true, 1709 RCode: dnsmessage.RCodeSuccess, 1710 }, 1711 Questions: q.Questions, 1712 Answers: []dnsmessage.Resource{ 1713 { 1714 Header: dnsmessage.ResourceHeader{ 1715 Name: q.Questions[0].Name, 1716 Type: dnsmessage.TypeA, 1717 Class: dnsmessage.ClassINET, 1718 }, 1719 Body: &dnsmessage.TXTResource{ 1720 TXT: []string{"string1 ", "string2"}, 1721 }, 1722 }, 1723 { 1724 Header: dnsmessage.ResourceHeader{ 1725 Name: q.Questions[0].Name, 1726 Type: dnsmessage.TypeA, 1727 Class: dnsmessage.ClassINET, 1728 }, 1729 Body: &dnsmessage.TXTResource{ 1730 TXT: []string{"onestring"}, 1731 }, 1732 }, 1733 }, 1734 } 1735 return r, nil 1736 }, 1737 } 1738 r := Resolver{PreferGo: true, Dial: fake.DialContext} 1739 txt, err := r.lookupTXT(context.Background(), "golang.org") 1740 if err != nil { 1741 t.Fatal("LookupTXT failed:", err) 1742 } 1743 if want := 2; len(txt) != want { 1744 t.Fatalf("len(txt), got %d, want %d", len(txt), want) 1745 } 1746 if want := "string1 string2"; txt[0] != want { 1747 t.Errorf("txt[0], got %q, want %q", txt[0], want) 1748 } 1749 if want := "onestring"; txt[1] != want { 1750 t.Errorf("txt[1], got %q, want %q", txt[1], want) 1751 } 1752} 1753 1754// Issue 29644: support single-request resolv.conf option in pure Go resolver. 1755// The A and AAAA queries will be sent sequentially, not in parallel. 1756func TestSingleRequestLookup(t *testing.T) { 1757 defer dnsWaitGroup.Wait() 1758 var ( 1759 firstcalled int32 1760 ipv4 int32 = 1 1761 ipv6 int32 = 2 1762 ) 1763 fake := fakeDNSServer{rh: func(n, s string, q dnsmessage.Message, _ time.Time) (dnsmessage.Message, error) { 1764 r := dnsmessage.Message{ 1765 Header: dnsmessage.Header{ 1766 ID: q.ID, 1767 Response: true, 1768 }, 1769 Questions: q.Questions, 1770 } 1771 for _, question := range q.Questions { 1772 switch question.Type { 1773 case dnsmessage.TypeA: 1774 if question.Name.String() == "slowipv4.example.net." { 1775 time.Sleep(10 * time.Millisecond) 1776 } 1777 if !atomic.CompareAndSwapInt32(&firstcalled, 0, ipv4) { 1778 t.Errorf("the A query was received after the AAAA query !") 1779 } 1780 r.Answers = append(r.Answers, dnsmessage.Resource{ 1781 Header: dnsmessage.ResourceHeader{ 1782 Name: q.Questions[0].Name, 1783 Type: dnsmessage.TypeA, 1784 Class: dnsmessage.ClassINET, 1785 Length: 4, 1786 }, 1787 Body: &dnsmessage.AResource{ 1788 A: TestAddr, 1789 }, 1790 }) 1791 case dnsmessage.TypeAAAA: 1792 atomic.CompareAndSwapInt32(&firstcalled, 0, ipv6) 1793 r.Answers = append(r.Answers, dnsmessage.Resource{ 1794 Header: dnsmessage.ResourceHeader{ 1795 Name: q.Questions[0].Name, 1796 Type: dnsmessage.TypeAAAA, 1797 Class: dnsmessage.ClassINET, 1798 Length: 16, 1799 }, 1800 Body: &dnsmessage.AAAAResource{ 1801 AAAA: TestAddr6, 1802 }, 1803 }) 1804 } 1805 } 1806 return r, nil 1807 }} 1808 r := Resolver{PreferGo: true, Dial: fake.DialContext} 1809 1810 conf, err := newResolvConfTest() 1811 if err != nil { 1812 t.Fatal(err) 1813 } 1814 defer conf.teardown() 1815 if err := conf.writeAndUpdate([]string{"options single-request"}); err != nil { 1816 t.Fatal(err) 1817 } 1818 for _, name := range []string{"hostname.example.net", "slowipv4.example.net"} { 1819 firstcalled = 0 1820 _, err := r.LookupIPAddr(context.Background(), name) 1821 if err != nil { 1822 t.Error(err) 1823 } 1824 } 1825} 1826 1827// Issue 29358. Add configuration knob to force TCP-only DNS requests in the pure Go resolver. 1828func TestDNSUseTCP(t *testing.T) { 1829 fake := fakeDNSServer{ 1830 rh: func(n, _ string, q dnsmessage.Message, _ time.Time) (dnsmessage.Message, error) { 1831 r := dnsmessage.Message{ 1832 Header: dnsmessage.Header{ 1833 ID: q.Header.ID, 1834 Response: true, 1835 RCode: dnsmessage.RCodeSuccess, 1836 }, 1837 Questions: q.Questions, 1838 } 1839 if n == "udp" { 1840 t.Fatal("udp protocol was used instead of tcp") 1841 } 1842 return r, nil 1843 }, 1844 } 1845 r := Resolver{PreferGo: true, Dial: fake.DialContext} 1846 ctx, cancel := context.WithCancel(context.Background()) 1847 defer cancel() 1848 _, _, err := r.exchange(ctx, "0.0.0.0", mustQuestion("com.", dnsmessage.TypeALL, dnsmessage.ClassINET), time.Second, useTCPOnly, false) 1849 if err != nil { 1850 t.Fatal("exchange failed:", err) 1851 } 1852} 1853 1854func TestDNSUseTCPTruncated(t *testing.T) { 1855 fake := fakeDNSServer{ 1856 rh: func(n, _ string, q dnsmessage.Message, _ time.Time) (dnsmessage.Message, error) { 1857 r := dnsmessage.Message{ 1858 Header: dnsmessage.Header{ 1859 ID: q.Header.ID, 1860 Response: true, 1861 RCode: dnsmessage.RCodeSuccess, 1862 Truncated: true, 1863 }, 1864 Questions: q.Questions, 1865 Answers: []dnsmessage.Resource{ 1866 { 1867 Header: dnsmessage.ResourceHeader{ 1868 Name: q.Questions[0].Name, 1869 Type: dnsmessage.TypeA, 1870 Class: dnsmessage.ClassINET, 1871 Length: 4, 1872 }, 1873 Body: &dnsmessage.AResource{ 1874 A: TestAddr, 1875 }, 1876 }, 1877 }, 1878 } 1879 if n == "udp" { 1880 t.Fatal("udp protocol was used instead of tcp") 1881 } 1882 return r, nil 1883 }, 1884 } 1885 r := Resolver{PreferGo: true, Dial: fake.DialContext} 1886 ctx, cancel := context.WithCancel(context.Background()) 1887 defer cancel() 1888 p, _, err := r.exchange(ctx, "0.0.0.0", mustQuestion("com.", dnsmessage.TypeALL, dnsmessage.ClassINET), time.Second, useTCPOnly, false) 1889 if err != nil { 1890 t.Fatal("exchange failed:", err) 1891 } 1892 a, err := p.AllAnswers() 1893 if err != nil { 1894 t.Fatalf("unexpected error %v getting all answers", err) 1895 } 1896 if len(a) != 1 { 1897 t.Fatalf("got %d answers; want 1", len(a)) 1898 } 1899} 1900 1901// Issue 34660: PTR response with non-PTR answers should ignore non-PTR 1902func TestPTRandNonPTR(t *testing.T) { 1903 fake := fakeDNSServer{ 1904 rh: func(n, _ string, q dnsmessage.Message, _ time.Time) (dnsmessage.Message, error) { 1905 r := dnsmessage.Message{ 1906 Header: dnsmessage.Header{ 1907 ID: q.Header.ID, 1908 Response: true, 1909 RCode: dnsmessage.RCodeSuccess, 1910 }, 1911 Questions: q.Questions, 1912 Answers: []dnsmessage.Resource{ 1913 { 1914 Header: dnsmessage.ResourceHeader{ 1915 Name: q.Questions[0].Name, 1916 Type: dnsmessage.TypePTR, 1917 Class: dnsmessage.ClassINET, 1918 }, 1919 Body: &dnsmessage.PTRResource{ 1920 PTR: dnsmessage.MustNewName("golang.org."), 1921 }, 1922 }, 1923 { 1924 Header: dnsmessage.ResourceHeader{ 1925 Name: q.Questions[0].Name, 1926 Type: dnsmessage.TypeTXT, 1927 Class: dnsmessage.ClassINET, 1928 }, 1929 Body: &dnsmessage.TXTResource{ 1930 TXT: []string{"PTR 8 6 60 ..."}, // fake RRSIG 1931 }, 1932 }, 1933 }, 1934 } 1935 return r, nil 1936 }, 1937 } 1938 r := Resolver{PreferGo: true, Dial: fake.DialContext} 1939 names, err := r.lookupAddr(context.Background(), "192.0.2.123") 1940 if err != nil { 1941 t.Fatalf("LookupAddr: %v", err) 1942 } 1943 if want := []string{"golang.org."}; !reflect.DeepEqual(names, want) { 1944 t.Errorf("names = %q; want %q", names, want) 1945 } 1946} 1947 1948func TestCVE202133195(t *testing.T) { 1949 fake := fakeDNSServer{ 1950 rh: func(n, _ string, q dnsmessage.Message, _ time.Time) (dnsmessage.Message, error) { 1951 r := dnsmessage.Message{ 1952 Header: dnsmessage.Header{ 1953 ID: q.Header.ID, 1954 Response: true, 1955 RCode: dnsmessage.RCodeSuccess, 1956 RecursionAvailable: true, 1957 }, 1958 Questions: q.Questions, 1959 } 1960 switch q.Questions[0].Type { 1961 case dnsmessage.TypeCNAME: 1962 r.Answers = []dnsmessage.Resource{} 1963 case dnsmessage.TypeA: // CNAME lookup uses a A/AAAA as a proxy 1964 r.Answers = append(r.Answers, 1965 dnsmessage.Resource{ 1966 Header: dnsmessage.ResourceHeader{ 1967 Name: dnsmessage.MustNewName("<html>.golang.org."), 1968 Type: dnsmessage.TypeA, 1969 Class: dnsmessage.ClassINET, 1970 Length: 4, 1971 }, 1972 Body: &dnsmessage.AResource{ 1973 A: TestAddr, 1974 }, 1975 }, 1976 ) 1977 case dnsmessage.TypeSRV: 1978 n := q.Questions[0].Name 1979 if n.String() == "_hdr._tcp.golang.org." { 1980 n = dnsmessage.MustNewName("<html>.golang.org.") 1981 } 1982 r.Answers = append(r.Answers, 1983 dnsmessage.Resource{ 1984 Header: dnsmessage.ResourceHeader{ 1985 Name: n, 1986 Type: dnsmessage.TypeSRV, 1987 Class: dnsmessage.ClassINET, 1988 Length: 4, 1989 }, 1990 Body: &dnsmessage.SRVResource{ 1991 Target: dnsmessage.MustNewName("<html>.golang.org."), 1992 }, 1993 }, 1994 dnsmessage.Resource{ 1995 Header: dnsmessage.ResourceHeader{ 1996 Name: n, 1997 Type: dnsmessage.TypeSRV, 1998 Class: dnsmessage.ClassINET, 1999 Length: 4, 2000 }, 2001 Body: &dnsmessage.SRVResource{ 2002 Target: dnsmessage.MustNewName("good.golang.org."), 2003 }, 2004 }, 2005 ) 2006 case dnsmessage.TypeMX: 2007 r.Answers = append(r.Answers, 2008 dnsmessage.Resource{ 2009 Header: dnsmessage.ResourceHeader{ 2010 Name: dnsmessage.MustNewName("<html>.golang.org."), 2011 Type: dnsmessage.TypeMX, 2012 Class: dnsmessage.ClassINET, 2013 Length: 4, 2014 }, 2015 Body: &dnsmessage.MXResource{ 2016 MX: dnsmessage.MustNewName("<html>.golang.org."), 2017 }, 2018 }, 2019 dnsmessage.Resource{ 2020 Header: dnsmessage.ResourceHeader{ 2021 Name: dnsmessage.MustNewName("good.golang.org."), 2022 Type: dnsmessage.TypeMX, 2023 Class: dnsmessage.ClassINET, 2024 Length: 4, 2025 }, 2026 Body: &dnsmessage.MXResource{ 2027 MX: dnsmessage.MustNewName("good.golang.org."), 2028 }, 2029 }, 2030 ) 2031 case dnsmessage.TypeNS: 2032 r.Answers = append(r.Answers, 2033 dnsmessage.Resource{ 2034 Header: dnsmessage.ResourceHeader{ 2035 Name: dnsmessage.MustNewName("<html>.golang.org."), 2036 Type: dnsmessage.TypeNS, 2037 Class: dnsmessage.ClassINET, 2038 Length: 4, 2039 }, 2040 Body: &dnsmessage.NSResource{ 2041 NS: dnsmessage.MustNewName("<html>.golang.org."), 2042 }, 2043 }, 2044 dnsmessage.Resource{ 2045 Header: dnsmessage.ResourceHeader{ 2046 Name: dnsmessage.MustNewName("good.golang.org."), 2047 Type: dnsmessage.TypeNS, 2048 Class: dnsmessage.ClassINET, 2049 Length: 4, 2050 }, 2051 Body: &dnsmessage.NSResource{ 2052 NS: dnsmessage.MustNewName("good.golang.org."), 2053 }, 2054 }, 2055 ) 2056 case dnsmessage.TypePTR: 2057 r.Answers = append(r.Answers, 2058 dnsmessage.Resource{ 2059 Header: dnsmessage.ResourceHeader{ 2060 Name: dnsmessage.MustNewName("<html>.golang.org."), 2061 Type: dnsmessage.TypePTR, 2062 Class: dnsmessage.ClassINET, 2063 Length: 4, 2064 }, 2065 Body: &dnsmessage.PTRResource{ 2066 PTR: dnsmessage.MustNewName("<html>.golang.org."), 2067 }, 2068 }, 2069 dnsmessage.Resource{ 2070 Header: dnsmessage.ResourceHeader{ 2071 Name: dnsmessage.MustNewName("good.golang.org."), 2072 Type: dnsmessage.TypePTR, 2073 Class: dnsmessage.ClassINET, 2074 Length: 4, 2075 }, 2076 Body: &dnsmessage.PTRResource{ 2077 PTR: dnsmessage.MustNewName("good.golang.org."), 2078 }, 2079 }, 2080 ) 2081 } 2082 return r, nil 2083 }, 2084 } 2085 2086 r := Resolver{PreferGo: true, Dial: fake.DialContext} 2087 // Change the default resolver to match our manipulated resolver 2088 originalDefault := DefaultResolver 2089 DefaultResolver = &r 2090 defer func() { DefaultResolver = originalDefault }() 2091 // Redirect host file lookups. 2092 defer func(orig string) { hostsFilePath = orig }(hostsFilePath) 2093 hostsFilePath = "testdata/hosts" 2094 2095 tests := []struct { 2096 name string 2097 f func(*testing.T) 2098 }{ 2099 { 2100 name: "CNAME", 2101 f: func(t *testing.T) { 2102 expectedErr := &DNSError{Err: errMalformedDNSRecordsDetail, Name: "golang.org"} 2103 _, err := r.LookupCNAME(context.Background(), "golang.org") 2104 if err.Error() != expectedErr.Error() { 2105 t.Fatalf("unexpected error: %s", err) 2106 } 2107 _, err = LookupCNAME("golang.org") 2108 if err.Error() != expectedErr.Error() { 2109 t.Fatalf("unexpected error: %s", err) 2110 } 2111 }, 2112 }, 2113 { 2114 name: "SRV (bad record)", 2115 f: func(t *testing.T) { 2116 expected := []*SRV{ 2117 { 2118 Target: "good.golang.org.", 2119 }, 2120 } 2121 expectedErr := &DNSError{Err: errMalformedDNSRecordsDetail, Name: "golang.org"} 2122 _, records, err := r.LookupSRV(context.Background(), "target", "tcp", "golang.org") 2123 if err.Error() != expectedErr.Error() { 2124 t.Fatalf("unexpected error: %s", err) 2125 } 2126 if !reflect.DeepEqual(records, expected) { 2127 t.Error("Unexpected record set") 2128 } 2129 _, records, err = LookupSRV("target", "tcp", "golang.org") 2130 if err.Error() != expectedErr.Error() { 2131 t.Errorf("unexpected error: %s", err) 2132 } 2133 if !reflect.DeepEqual(records, expected) { 2134 t.Error("Unexpected record set") 2135 } 2136 }, 2137 }, 2138 { 2139 name: "SRV (bad header)", 2140 f: func(t *testing.T) { 2141 _, _, err := r.LookupSRV(context.Background(), "hdr", "tcp", "golang.org.") 2142 if expected := "lookup golang.org.: SRV header name is invalid"; err == nil || err.Error() != expected { 2143 t.Errorf("Resolver.LookupSRV returned unexpected error, got %q, want %q", err, expected) 2144 } 2145 _, _, err = LookupSRV("hdr", "tcp", "golang.org.") 2146 if expected := "lookup golang.org.: SRV header name is invalid"; err == nil || err.Error() != expected { 2147 t.Errorf("LookupSRV returned unexpected error, got %q, want %q", err, expected) 2148 } 2149 }, 2150 }, 2151 { 2152 name: "MX", 2153 f: func(t *testing.T) { 2154 expected := []*MX{ 2155 { 2156 Host: "good.golang.org.", 2157 }, 2158 } 2159 expectedErr := &DNSError{Err: errMalformedDNSRecordsDetail, Name: "golang.org"} 2160 records, err := r.LookupMX(context.Background(), "golang.org") 2161 if err.Error() != expectedErr.Error() { 2162 t.Fatalf("unexpected error: %s", err) 2163 } 2164 if !reflect.DeepEqual(records, expected) { 2165 t.Error("Unexpected record set") 2166 } 2167 records, err = LookupMX("golang.org") 2168 if err.Error() != expectedErr.Error() { 2169 t.Fatalf("unexpected error: %s", err) 2170 } 2171 if !reflect.DeepEqual(records, expected) { 2172 t.Error("Unexpected record set") 2173 } 2174 }, 2175 }, 2176 { 2177 name: "NS", 2178 f: func(t *testing.T) { 2179 expected := []*NS{ 2180 { 2181 Host: "good.golang.org.", 2182 }, 2183 } 2184 expectedErr := &DNSError{Err: errMalformedDNSRecordsDetail, Name: "golang.org"} 2185 records, err := r.LookupNS(context.Background(), "golang.org") 2186 if err.Error() != expectedErr.Error() { 2187 t.Fatalf("unexpected error: %s", err) 2188 } 2189 if !reflect.DeepEqual(records, expected) { 2190 t.Error("Unexpected record set") 2191 } 2192 records, err = LookupNS("golang.org") 2193 if err.Error() != expectedErr.Error() { 2194 t.Fatalf("unexpected error: %s", err) 2195 } 2196 if !reflect.DeepEqual(records, expected) { 2197 t.Error("Unexpected record set") 2198 } 2199 }, 2200 }, 2201 { 2202 name: "Addr", 2203 f: func(t *testing.T) { 2204 expected := []string{"good.golang.org."} 2205 expectedErr := &DNSError{Err: errMalformedDNSRecordsDetail, Name: "192.0.2.42"} 2206 records, err := r.LookupAddr(context.Background(), "192.0.2.42") 2207 if err.Error() != expectedErr.Error() { 2208 t.Fatalf("unexpected error: %s", err) 2209 } 2210 if !reflect.DeepEqual(records, expected) { 2211 t.Error("Unexpected record set") 2212 } 2213 records, err = LookupAddr("192.0.2.42") 2214 if err.Error() != expectedErr.Error() { 2215 t.Fatalf("unexpected error: %s", err) 2216 } 2217 if !reflect.DeepEqual(records, expected) { 2218 t.Error("Unexpected record set") 2219 } 2220 }, 2221 }, 2222 } 2223 2224 for _, tc := range tests { 2225 t.Run(tc.name, tc.f) 2226 } 2227 2228} 2229 2230func TestNullMX(t *testing.T) { 2231 fake := fakeDNSServer{ 2232 rh: func(n, _ string, q dnsmessage.Message, _ time.Time) (dnsmessage.Message, error) { 2233 r := dnsmessage.Message{ 2234 Header: dnsmessage.Header{ 2235 ID: q.Header.ID, 2236 Response: true, 2237 RCode: dnsmessage.RCodeSuccess, 2238 }, 2239 Questions: q.Questions, 2240 Answers: []dnsmessage.Resource{ 2241 { 2242 Header: dnsmessage.ResourceHeader{ 2243 Name: q.Questions[0].Name, 2244 Type: dnsmessage.TypeMX, 2245 Class: dnsmessage.ClassINET, 2246 }, 2247 Body: &dnsmessage.MXResource{ 2248 MX: dnsmessage.MustNewName("."), 2249 }, 2250 }, 2251 }, 2252 } 2253 return r, nil 2254 }, 2255 } 2256 r := Resolver{PreferGo: true, Dial: fake.DialContext} 2257 rrset, err := r.LookupMX(context.Background(), "golang.org") 2258 if err != nil { 2259 t.Fatalf("LookupMX: %v", err) 2260 } 2261 if want := []*MX{&MX{Host: "."}}; !reflect.DeepEqual(rrset, want) { 2262 records := []string{} 2263 for _, rr := range rrset { 2264 records = append(records, fmt.Sprintf("%v", rr)) 2265 } 2266 t.Errorf("records = [%v]; want [%v]", strings.Join(records, " "), want[0]) 2267 } 2268} 2269 2270func TestRootNS(t *testing.T) { 2271 // See https://golang.org/issue/45715. 2272 fake := fakeDNSServer{ 2273 rh: func(n, _ string, q dnsmessage.Message, _ time.Time) (dnsmessage.Message, error) { 2274 r := dnsmessage.Message{ 2275 Header: dnsmessage.Header{ 2276 ID: q.Header.ID, 2277 Response: true, 2278 RCode: dnsmessage.RCodeSuccess, 2279 }, 2280 Questions: q.Questions, 2281 Answers: []dnsmessage.Resource{ 2282 { 2283 Header: dnsmessage.ResourceHeader{ 2284 Name: q.Questions[0].Name, 2285 Type: dnsmessage.TypeNS, 2286 Class: dnsmessage.ClassINET, 2287 }, 2288 Body: &dnsmessage.NSResource{ 2289 NS: dnsmessage.MustNewName("i.root-servers.net."), 2290 }, 2291 }, 2292 }, 2293 } 2294 return r, nil 2295 }, 2296 } 2297 r := Resolver{PreferGo: true, Dial: fake.DialContext} 2298 rrset, err := r.LookupNS(context.Background(), ".") 2299 if err != nil { 2300 t.Fatalf("LookupNS: %v", err) 2301 } 2302 if want := []*NS{&NS{Host: "i.root-servers.net."}}; !reflect.DeepEqual(rrset, want) { 2303 records := []string{} 2304 for _, rr := range rrset { 2305 records = append(records, fmt.Sprintf("%v", rr)) 2306 } 2307 t.Errorf("records = [%v]; want [%v]", strings.Join(records, " "), want[0]) 2308 } 2309} 2310 2311func TestGoLookupIPCNAMEOrderHostsAliasesFilesOnlyMode(t *testing.T) { 2312 defer func(orig string) { hostsFilePath = orig }(hostsFilePath) 2313 hostsFilePath = "testdata/aliases" 2314 mode := hostLookupFiles 2315 2316 for _, v := range lookupStaticHostAliasesTest { 2317 testGoLookupIPCNAMEOrderHostsAliases(t, mode, v.lookup, absDomainName(v.res)) 2318 } 2319} 2320 2321func TestGoLookupIPCNAMEOrderHostsAliasesFilesDNSMode(t *testing.T) { 2322 defer func(orig string) { hostsFilePath = orig }(hostsFilePath) 2323 hostsFilePath = "testdata/aliases" 2324 mode := hostLookupFilesDNS 2325 2326 for _, v := range lookupStaticHostAliasesTest { 2327 testGoLookupIPCNAMEOrderHostsAliases(t, mode, v.lookup, absDomainName(v.res)) 2328 } 2329} 2330 2331var goLookupIPCNAMEOrderDNSFilesModeTests = []struct { 2332 lookup, res string 2333}{ 2334 // 127.0.1.1 2335 {"invalid.invalid", "invalid.test"}, 2336} 2337 2338func TestGoLookupIPCNAMEOrderHostsAliasesDNSFilesMode(t *testing.T) { 2339 defer func(orig string) { hostsFilePath = orig }(hostsFilePath) 2340 hostsFilePath = "testdata/aliases" 2341 mode := hostLookupDNSFiles 2342 2343 for _, v := range goLookupIPCNAMEOrderDNSFilesModeTests { 2344 testGoLookupIPCNAMEOrderHostsAliases(t, mode, v.lookup, absDomainName(v.res)) 2345 } 2346} 2347 2348func testGoLookupIPCNAMEOrderHostsAliases(t *testing.T, mode hostLookupOrder, lookup, lookupRes string) { 2349 fake := fakeDNSServer{ 2350 rh: func(_, _ string, q dnsmessage.Message, _ time.Time) (dnsmessage.Message, error) { 2351 var answers []dnsmessage.Resource 2352 2353 if mode != hostLookupDNSFiles { 2354 t.Fatal("received unexpected DNS query") 2355 } 2356 2357 return dnsmessage.Message{ 2358 Header: dnsmessage.Header{ 2359 ID: q.Header.ID, 2360 Response: true, 2361 }, 2362 Questions: []dnsmessage.Question{q.Questions[0]}, 2363 Answers: answers, 2364 }, nil 2365 }, 2366 } 2367 2368 r := Resolver{PreferGo: true, Dial: fake.DialContext} 2369 ins := []string{lookup, absDomainName(lookup), strings.ToLower(lookup), strings.ToUpper(lookup)} 2370 for _, in := range ins { 2371 _, res, err := r.goLookupIPCNAMEOrder(context.Background(), "ip", in, mode, nil) 2372 if err != nil { 2373 t.Errorf("expected err == nil, but got error: %v", err) 2374 } 2375 if res.String() != lookupRes { 2376 t.Errorf("goLookupIPCNAMEOrder(%v): got %v, want %v", in, res, lookupRes) 2377 } 2378 } 2379} 2380 2381// Test that we advertise support for a larger DNS packet size. 2382// This isn't a great test as it just tests the dnsmessage package 2383// against itself. 2384func TestDNSPacketSize(t *testing.T) { 2385 t.Run("enabled", func(t *testing.T) { 2386 testDNSPacketSize(t, false) 2387 }) 2388 t.Run("disabled", func(t *testing.T) { 2389 testDNSPacketSize(t, true) 2390 }) 2391} 2392 2393func testDNSPacketSize(t *testing.T, disable bool) { 2394 fake := fakeDNSServer{ 2395 rh: func(_, _ string, q dnsmessage.Message, _ time.Time) (dnsmessage.Message, error) { 2396 if disable { 2397 if len(q.Additionals) > 0 { 2398 t.Error("unexpected additional record") 2399 } 2400 } else { 2401 if len(q.Additionals) == 0 { 2402 t.Error("missing EDNS record") 2403 } else if opt, ok := q.Additionals[0].Body.(*dnsmessage.OPTResource); !ok { 2404 t.Errorf("additional record type %T, expected OPTResource", q.Additionals[0]) 2405 } else if len(opt.Options) != 0 { 2406 t.Errorf("found %d Options, expected none", len(opt.Options)) 2407 } else { 2408 got := int(q.Additionals[0].Header.Class) 2409 t.Logf("EDNS packet size == %d", got) 2410 if got != maxDNSPacketSize { 2411 t.Errorf("EDNS packet size == %d, want %d", got, maxDNSPacketSize) 2412 } 2413 } 2414 } 2415 2416 // Hand back a dummy answer to verify that 2417 // LookupIPAddr completes. 2418 r := dnsmessage.Message{ 2419 Header: dnsmessage.Header{ 2420 ID: q.Header.ID, 2421 Response: true, 2422 RCode: dnsmessage.RCodeSuccess, 2423 }, 2424 Questions: q.Questions, 2425 } 2426 if q.Questions[0].Type == dnsmessage.TypeA { 2427 r.Answers = []dnsmessage.Resource{ 2428 { 2429 Header: dnsmessage.ResourceHeader{ 2430 Name: q.Questions[0].Name, 2431 Type: dnsmessage.TypeA, 2432 Class: dnsmessage.ClassINET, 2433 Length: 4, 2434 }, 2435 Body: &dnsmessage.AResource{ 2436 A: TestAddr, 2437 }, 2438 }, 2439 } 2440 } 2441 return r, nil 2442 }, 2443 } 2444 2445 if disable { 2446 t.Setenv("GODEBUG", "netedns0=0") 2447 } 2448 2449 r := &Resolver{PreferGo: true, Dial: fake.DialContext} 2450 if _, err := r.LookupIPAddr(context.Background(), "go.dev"); err != nil { 2451 t.Errorf("lookup failed: %v", err) 2452 } 2453} 2454 2455func TestLongDNSNames(t *testing.T) { 2456 const longDNSsuffix = ".go.dev." 2457 const longDNSsuffixNoEndingDot = ".go.dev" 2458 2459 var longDNSPrefix = strings.Repeat("verylongdomainlabel.", 20) 2460 2461 var longDNSNamesTests = []struct { 2462 req string 2463 fail bool 2464 }{ 2465 {req: longDNSPrefix[:255-len(longDNSsuffix)] + longDNSsuffix, fail: true}, 2466 {req: longDNSPrefix[:254-len(longDNSsuffix)] + longDNSsuffix}, 2467 {req: longDNSPrefix[:253-len(longDNSsuffix)] + longDNSsuffix}, 2468 2469 {req: longDNSPrefix[:253-len(longDNSsuffixNoEndingDot)] + longDNSsuffixNoEndingDot}, 2470 {req: longDNSPrefix[:254-len(longDNSsuffixNoEndingDot)] + longDNSsuffixNoEndingDot, fail: true}, 2471 } 2472 2473 fake := fakeDNSServer{ 2474 rh: func(_, _ string, q dnsmessage.Message, _ time.Time) (dnsmessage.Message, error) { 2475 r := dnsmessage.Message{ 2476 Header: dnsmessage.Header{ 2477 ID: q.Header.ID, 2478 Response: true, 2479 RCode: dnsmessage.RCodeSuccess, 2480 }, 2481 Questions: q.Questions, 2482 Answers: []dnsmessage.Resource{ 2483 { 2484 Header: dnsmessage.ResourceHeader{ 2485 Name: q.Questions[0].Name, 2486 Type: q.Questions[0].Type, 2487 Class: dnsmessage.ClassINET, 2488 }, 2489 }, 2490 }, 2491 } 2492 2493 switch q.Questions[0].Type { 2494 case dnsmessage.TypeA: 2495 r.Answers[0].Body = &dnsmessage.AResource{A: TestAddr} 2496 case dnsmessage.TypeAAAA: 2497 r.Answers[0].Body = &dnsmessage.AAAAResource{AAAA: TestAddr6} 2498 case dnsmessage.TypeTXT: 2499 r.Answers[0].Body = &dnsmessage.TXTResource{TXT: []string{"."}} 2500 case dnsmessage.TypeMX: 2501 r.Answers[0].Body = &dnsmessage.MXResource{ 2502 MX: dnsmessage.MustNewName("go.dev."), 2503 } 2504 case dnsmessage.TypeNS: 2505 r.Answers[0].Body = &dnsmessage.NSResource{ 2506 NS: dnsmessage.MustNewName("go.dev."), 2507 } 2508 case dnsmessage.TypeSRV: 2509 r.Answers[0].Body = &dnsmessage.SRVResource{ 2510 Target: dnsmessage.MustNewName("go.dev."), 2511 } 2512 case dnsmessage.TypeCNAME: 2513 r.Answers[0].Body = &dnsmessage.CNAMEResource{ 2514 CNAME: dnsmessage.MustNewName("fake.cname."), 2515 } 2516 default: 2517 panic("unknown dnsmessage type") 2518 } 2519 2520 return r, nil 2521 }, 2522 } 2523 2524 r := &Resolver{PreferGo: true, Dial: fake.DialContext} 2525 2526 methodTests := []string{"CNAME", "Host", "IP", "IPAddr", "MX", "NS", "NetIP", "SRV", "TXT"} 2527 query := func(t string, req string) error { 2528 switch t { 2529 case "CNAME": 2530 _, err := r.LookupCNAME(context.Background(), req) 2531 return err 2532 case "Host": 2533 _, err := r.LookupHost(context.Background(), req) 2534 return err 2535 case "IP": 2536 _, err := r.LookupIP(context.Background(), "ip", req) 2537 return err 2538 case "IPAddr": 2539 _, err := r.LookupIPAddr(context.Background(), req) 2540 return err 2541 case "MX": 2542 _, err := r.LookupMX(context.Background(), req) 2543 return err 2544 case "NS": 2545 _, err := r.LookupNS(context.Background(), req) 2546 return err 2547 case "NetIP": 2548 _, err := r.LookupNetIP(context.Background(), "ip", req) 2549 return err 2550 case "SRV": 2551 const service = "service" 2552 const proto = "proto" 2553 req = req[len(service)+len(proto)+4:] 2554 _, _, err := r.LookupSRV(context.Background(), service, proto, req) 2555 return err 2556 case "TXT": 2557 _, err := r.LookupTXT(context.Background(), req) 2558 return err 2559 } 2560 panic("unknown query method") 2561 } 2562 2563 for i, v := range longDNSNamesTests { 2564 for _, testName := range methodTests { 2565 err := query(testName, v.req) 2566 if v.fail { 2567 if err == nil { 2568 t.Errorf("%v: Lookup%v: unexpected success", i, testName) 2569 break 2570 } 2571 2572 expectedErr := DNSError{Err: errNoSuchHost.Error(), Name: v.req, IsNotFound: true} 2573 var dnsErr *DNSError 2574 errors.As(err, &dnsErr) 2575 if dnsErr == nil || *dnsErr != expectedErr { 2576 t.Errorf("%v: Lookup%v: unexpected error: %v", i, testName, err) 2577 } 2578 break 2579 } 2580 if err != nil { 2581 t.Errorf("%v: Lookup%v: unexpected error: %v", i, testName, err) 2582 } 2583 } 2584 } 2585} 2586 2587func TestDNSTrustAD(t *testing.T) { 2588 fake := fakeDNSServer{ 2589 rh: func(_, _ string, q dnsmessage.Message, _ time.Time) (dnsmessage.Message, error) { 2590 if q.Questions[0].Name.String() == "notrustad.go.dev." && q.Header.AuthenticData { 2591 t.Error("unexpected AD bit") 2592 } 2593 2594 if q.Questions[0].Name.String() == "trustad.go.dev." && !q.Header.AuthenticData { 2595 t.Error("expected AD bit") 2596 } 2597 2598 r := dnsmessage.Message{ 2599 Header: dnsmessage.Header{ 2600 ID: q.Header.ID, 2601 Response: true, 2602 RCode: dnsmessage.RCodeSuccess, 2603 }, 2604 Questions: q.Questions, 2605 } 2606 if q.Questions[0].Type == dnsmessage.TypeA { 2607 r.Answers = []dnsmessage.Resource{ 2608 { 2609 Header: dnsmessage.ResourceHeader{ 2610 Name: q.Questions[0].Name, 2611 Type: dnsmessage.TypeA, 2612 Class: dnsmessage.ClassINET, 2613 Length: 4, 2614 }, 2615 Body: &dnsmessage.AResource{ 2616 A: TestAddr, 2617 }, 2618 }, 2619 } 2620 } 2621 2622 return r, nil 2623 }} 2624 2625 r := &Resolver{PreferGo: true, Dial: fake.DialContext} 2626 2627 conf, err := newResolvConfTest() 2628 if err != nil { 2629 t.Fatal(err) 2630 } 2631 defer conf.teardown() 2632 2633 err = conf.writeAndUpdate([]string{"nameserver 127.0.0.1"}) 2634 if err != nil { 2635 t.Fatal(err) 2636 } 2637 2638 if _, err := r.LookupIPAddr(context.Background(), "notrustad.go.dev"); err != nil { 2639 t.Errorf("lookup failed: %v", err) 2640 } 2641 2642 err = conf.writeAndUpdate([]string{"nameserver 127.0.0.1", "options trust-ad"}) 2643 if err != nil { 2644 t.Fatal(err) 2645 } 2646 2647 if _, err := r.LookupIPAddr(context.Background(), "trustad.go.dev"); err != nil { 2648 t.Errorf("lookup failed: %v", err) 2649 } 2650} 2651 2652func TestDNSConfigNoReload(t *testing.T) { 2653 r := &Resolver{PreferGo: true, Dial: func(ctx context.Context, network, address string) (Conn, error) { 2654 if address != "192.0.2.1:53" { 2655 return nil, errors.New("configuration unexpectedly changed") 2656 } 2657 return fakeDNSServerSuccessful.DialContext(ctx, network, address) 2658 }} 2659 2660 conf, err := newResolvConfTest() 2661 if err != nil { 2662 t.Fatal(err) 2663 } 2664 defer conf.teardown() 2665 2666 err = conf.writeAndUpdateWithLastCheckedTime([]string{"nameserver 192.0.2.1", "options no-reload"}, time.Now().Add(-time.Hour)) 2667 if err != nil { 2668 t.Fatal(err) 2669 } 2670 2671 if _, err = r.LookupHost(context.Background(), "go.dev"); err != nil { 2672 t.Fatal(err) 2673 } 2674 2675 err = conf.write([]string{"nameserver 192.0.2.200"}) 2676 if err != nil { 2677 t.Fatal(err) 2678 } 2679 2680 if _, err = r.LookupHost(context.Background(), "go.dev"); err != nil { 2681 t.Fatal(err) 2682 } 2683} 2684 2685func TestLookupOrderFilesNoSuchHost(t *testing.T) { 2686 defer func(orig string) { hostsFilePath = orig }(hostsFilePath) 2687 if runtime.GOOS != "openbsd" { 2688 defer setSystemNSS(getSystemNSS(), 0) 2689 setSystemNSS(nssStr(t, "hosts: files"), time.Hour) 2690 } 2691 2692 conf, err := newResolvConfTest() 2693 if err != nil { 2694 t.Fatal(err) 2695 } 2696 defer conf.teardown() 2697 2698 resolvConf := dnsConfig{servers: defaultNS} 2699 if runtime.GOOS == "openbsd" { 2700 // Set error to ErrNotExist, so that the hostLookupOrder 2701 // returns hostLookupFiles for openbsd. 2702 resolvConf.err = os.ErrNotExist 2703 } 2704 2705 if !conf.forceUpdateConf(&resolvConf, time.Now().Add(time.Hour)) { 2706 t.Fatal("failed to update resolv config") 2707 } 2708 2709 tmpFile := filepath.Join(t.TempDir(), "hosts") 2710 if err := os.WriteFile(tmpFile, []byte{}, 0660); err != nil { 2711 t.Fatal(err) 2712 } 2713 hostsFilePath = tmpFile 2714 2715 const testName = "test.invalid" 2716 2717 order, _ := systemConf().hostLookupOrder(DefaultResolver, testName) 2718 if order != hostLookupFiles { 2719 // skip test for systems which do not return hostLookupFiles 2720 t.Skipf("hostLookupOrder did not return hostLookupFiles") 2721 } 2722 2723 var lookupTests = []struct { 2724 name string 2725 lookup func(name string) error 2726 }{ 2727 { 2728 name: "Host", 2729 lookup: func(name string) error { 2730 _, err = DefaultResolver.LookupHost(context.Background(), name) 2731 return err 2732 }, 2733 }, 2734 { 2735 name: "IP", 2736 lookup: func(name string) error { 2737 _, err = DefaultResolver.LookupIP(context.Background(), "ip", name) 2738 return err 2739 }, 2740 }, 2741 { 2742 name: "IPAddr", 2743 lookup: func(name string) error { 2744 _, err = DefaultResolver.LookupIPAddr(context.Background(), name) 2745 return err 2746 }, 2747 }, 2748 { 2749 name: "NetIP", 2750 lookup: func(name string) error { 2751 _, err = DefaultResolver.LookupNetIP(context.Background(), "ip", name) 2752 return err 2753 }, 2754 }, 2755 } 2756 2757 for _, v := range lookupTests { 2758 err := v.lookup(testName) 2759 2760 if err == nil { 2761 t.Errorf("Lookup%v: unexpected success", v.name) 2762 continue 2763 } 2764 2765 expectedErr := DNSError{Err: errNoSuchHost.Error(), Name: testName, IsNotFound: true} 2766 var dnsErr *DNSError 2767 errors.As(err, &dnsErr) 2768 if dnsErr == nil || *dnsErr != expectedErr { 2769 t.Errorf("Lookup%v: unexpected error: %v", v.name, err) 2770 } 2771 } 2772} 2773 2774func TestExtendedRCode(t *testing.T) { 2775 fake := fakeDNSServer{ 2776 rh: func(_, _ string, q dnsmessage.Message, _ time.Time) (dnsmessage.Message, error) { 2777 fraudSuccessCode := dnsmessage.RCodeSuccess | 1<<10 2778 2779 var edns0Hdr dnsmessage.ResourceHeader 2780 edns0Hdr.SetEDNS0(maxDNSPacketSize, fraudSuccessCode, false) 2781 2782 return dnsmessage.Message{ 2783 Header: dnsmessage.Header{ 2784 ID: q.Header.ID, 2785 Response: true, 2786 RCode: fraudSuccessCode, 2787 }, 2788 Questions: []dnsmessage.Question{q.Questions[0]}, 2789 Additionals: []dnsmessage.Resource{{ 2790 Header: edns0Hdr, 2791 Body: &dnsmessage.OPTResource{}, 2792 }}, 2793 }, nil 2794 }, 2795 } 2796 2797 r := &Resolver{PreferGo: true, Dial: fake.DialContext} 2798 _, _, err := r.tryOneName(context.Background(), getSystemDNSConfig(), "go.dev.", dnsmessage.TypeA) 2799 var dnsErr *DNSError 2800 if !(errors.As(err, &dnsErr) && dnsErr.Err == errServerMisbehaving.Error()) { 2801 t.Fatalf("r.tryOneName(): unexpected error: %v", err) 2802 } 2803} 2804