Home | History | Annotate | Download | only in net
      1 // Copyright 2013 The Go Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style
      3 // license that can be found in the LICENSE file.
      4 
      5 // +build darwin dragonfly freebsd linux netbsd openbsd solaris
      6 
      7 package net
      8 
      9 import (
     10 	"context"
     11 	"fmt"
     12 	"internal/testenv"
     13 	"io/ioutil"
     14 	"os"
     15 	"path"
     16 	"reflect"
     17 	"strings"
     18 	"sync"
     19 	"testing"
     20 	"time"
     21 )
     22 
     23 // Test address from 192.0.2.0/24 block, reserved by RFC 5737 for documentation.
     24 const TestAddr uint32 = 0xc0000201
     25 
     26 var dnsTransportFallbackTests = []struct {
     27 	server  string
     28 	name    string
     29 	qtype   uint16
     30 	timeout int
     31 	rcode   int
     32 }{
     33 	// Querying "com." with qtype=255 usually makes an answer
     34 	// which requires more than 512 bytes.
     35 	{"8.8.8.8:53", "com.", dnsTypeALL, 2, dnsRcodeSuccess},
     36 	{"8.8.4.4:53", "com.", dnsTypeALL, 4, dnsRcodeSuccess},
     37 }
     38 
     39 func TestDNSTransportFallback(t *testing.T) {
     40 	testenv.MustHaveExternalNetwork(t)
     41 
     42 	for _, tt := range dnsTransportFallbackTests {
     43 		ctx, cancel := context.WithCancel(context.Background())
     44 		defer cancel()
     45 		msg, err := exchange(ctx, tt.server, tt.name, tt.qtype, time.Second)
     46 		if err != nil {
     47 			t.Error(err)
     48 			continue
     49 		}
     50 		switch msg.rcode {
     51 		case tt.rcode, dnsRcodeServerFailure:
     52 		default:
     53 			t.Errorf("got %v from %v; want %v", msg.rcode, tt.server, tt.rcode)
     54 			continue
     55 		}
     56 	}
     57 }
     58 
     59 // See RFC 6761 for further information about the reserved, pseudo
     60 // domain names.
     61 var specialDomainNameTests = []struct {
     62 	name  string
     63 	qtype uint16
     64 	rcode int
     65 }{
     66 	// Name resolution APIs and libraries should not recognize the
     67 	// followings as special.
     68 	{"1.0.168.192.in-addr.arpa.", dnsTypePTR, dnsRcodeNameError},
     69 	{"test.", dnsTypeALL, dnsRcodeNameError},
     70 	{"example.com.", dnsTypeALL, dnsRcodeSuccess},
     71 
     72 	// Name resolution APIs and libraries should recognize the
     73 	// followings as special and should not send any queries.
     74 	// Though, we test those names here for verifying negative
     75 	// answers at DNS query-response interaction level.
     76 	{"localhost.", dnsTypeALL, dnsRcodeNameError},
     77 	{"invalid.", dnsTypeALL, dnsRcodeNameError},
     78 }
     79 
     80 func TestSpecialDomainName(t *testing.T) {
     81 	testenv.MustHaveExternalNetwork(t)
     82 
     83 	server := "8.8.8.8:53"
     84 	for _, tt := range specialDomainNameTests {
     85 		ctx, cancel := context.WithCancel(context.Background())
     86 		defer cancel()
     87 		msg, err := exchange(ctx, server, tt.name, tt.qtype, 3*time.Second)
     88 		if err != nil {
     89 			t.Error(err)
     90 			continue
     91 		}
     92 		switch msg.rcode {
     93 		case tt.rcode, dnsRcodeServerFailure:
     94 		default:
     95 			t.Errorf("got %v from %v; want %v", msg.rcode, server, tt.rcode)
     96 			continue
     97 		}
     98 	}
     99 }
    100 
    101 // Issue 13705: don't try to resolve onion addresses, etc
    102 func TestAvoidDNSName(t *testing.T) {
    103 	tests := []struct {
    104 		name  string
    105 		avoid bool
    106 	}{
    107 		{"foo.com", false},
    108 		{"foo.com.", false},
    109 
    110 		{"foo.onion.", true},
    111 		{"foo.onion", true},
    112 		{"foo.ONION", true},
    113 		{"foo.ONION.", true},
    114 
    115 		// But do resolve *.local address; Issue 16739
    116 		{"foo.local.", false},
    117 		{"foo.local", false},
    118 		{"foo.LOCAL", false},
    119 		{"foo.LOCAL.", false},
    120 
    121 		{"", true}, // will be rejected earlier too
    122 
    123 		// Without stuff before onion/local, they're fine to
    124 		// use DNS. With a search path,
    125 		// "onion.vegegtables.com" can use DNS. Without a
    126 		// search path (or with a trailing dot), the queries
    127 		// are just kinda useless, but don't reveal anything
    128 		// private.
    129 		{"local", false},
    130 		{"onion", false},
    131 		{"local.", false},
    132 		{"onion.", false},
    133 	}
    134 	for _, tt := range tests {
    135 		got := avoidDNS(tt.name)
    136 		if got != tt.avoid {
    137 			t.Errorf("avoidDNS(%q) = %v; want %v", tt.name, got, tt.avoid)
    138 		}
    139 	}
    140 }
    141 
    142 // Issue 13705: don't try to resolve onion addresses, etc
    143 func TestLookupTorOnion(t *testing.T) {
    144 	addrs, err := goLookupIP(context.Background(), "foo.onion")
    145 	if len(addrs) > 0 {
    146 		t.Errorf("unexpected addresses: %v", addrs)
    147 	}
    148 	if err != nil {
    149 		t.Fatalf("lookup = %v; want nil", err)
    150 	}
    151 }
    152 
    153 type resolvConfTest struct {
    154 	dir  string
    155 	path string
    156 	*resolverConfig
    157 }
    158 
    159 func newResolvConfTest() (*resolvConfTest, error) {
    160 	dir, err := ioutil.TempDir("", "go-resolvconftest")
    161 	if err != nil {
    162 		return nil, err
    163 	}
    164 	conf := &resolvConfTest{
    165 		dir:            dir,
    166 		path:           path.Join(dir, "resolv.conf"),
    167 		resolverConfig: &resolvConf,
    168 	}
    169 	conf.initOnce.Do(conf.init)
    170 	return conf, nil
    171 }
    172 
    173 func (conf *resolvConfTest) writeAndUpdate(lines []string) error {
    174 	f, err := os.OpenFile(conf.path, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0600)
    175 	if err != nil {
    176 		return err
    177 	}
    178 	if _, err := f.WriteString(strings.Join(lines, "\n")); err != nil {
    179 		f.Close()
    180 		return err
    181 	}
    182 	f.Close()
    183 	if err := conf.forceUpdate(conf.path, time.Now().Add(time.Hour)); err != nil {
    184 		return err
    185 	}
    186 	return nil
    187 }
    188 
    189 func (conf *resolvConfTest) forceUpdate(name string, lastChecked time.Time) error {
    190 	dnsConf := dnsReadConfig(name)
    191 	conf.mu.Lock()
    192 	conf.dnsConfig = dnsConf
    193 	conf.mu.Unlock()
    194 	for i := 0; i < 5; i++ {
    195 		if conf.tryAcquireSema() {
    196 			conf.lastChecked = lastChecked
    197 			conf.releaseSema()
    198 			return nil
    199 		}
    200 	}
    201 	return fmt.Errorf("tryAcquireSema for %s failed", name)
    202 }
    203 
    204 func (conf *resolvConfTest) servers() []string {
    205 	conf.mu.RLock()
    206 	servers := conf.dnsConfig.servers
    207 	conf.mu.RUnlock()
    208 	return servers
    209 }
    210 
    211 func (conf *resolvConfTest) teardown() error {
    212 	err := conf.forceUpdate("/etc/resolv.conf", time.Time{})
    213 	os.RemoveAll(conf.dir)
    214 	return err
    215 }
    216 
    217 var updateResolvConfTests = []struct {
    218 	name    string   // query name
    219 	lines   []string // resolver configuration lines
    220 	servers []string // expected name servers
    221 }{
    222 	{
    223 		name:    "golang.org",
    224 		lines:   []string{"nameserver 8.8.8.8"},
    225 		servers: []string{"8.8.8.8:53"},
    226 	},
    227 	{
    228 		name:    "",
    229 		lines:   nil, // an empty resolv.conf should use defaultNS as name servers
    230 		servers: defaultNS,
    231 	},
    232 	{
    233 		name:    "www.example.com",
    234 		lines:   []string{"nameserver 8.8.4.4"},
    235 		servers: []string{"8.8.4.4:53"},
    236 	},
    237 }
    238 
    239 func TestUpdateResolvConf(t *testing.T) {
    240 	testenv.MustHaveExternalNetwork(t)
    241 
    242 	conf, err := newResolvConfTest()
    243 	if err != nil {
    244 		t.Fatal(err)
    245 	}
    246 	defer conf.teardown()
    247 
    248 	for i, tt := range updateResolvConfTests {
    249 		if err := conf.writeAndUpdate(tt.lines); err != nil {
    250 			t.Error(err)
    251 			continue
    252 		}
    253 		if tt.name != "" {
    254 			var wg sync.WaitGroup
    255 			const N = 10
    256 			wg.Add(N)
    257 			for j := 0; j < N; j++ {
    258 				go func(name string) {
    259 					defer wg.Done()
    260 					ips, err := goLookupIP(context.Background(), name)
    261 					if err != nil {
    262 						t.Error(err)
    263 						return
    264 					}
    265 					if len(ips) == 0 {
    266 						t.Errorf("no records for %s", name)
    267 						return
    268 					}
    269 				}(tt.name)
    270 			}
    271 			wg.Wait()
    272 		}
    273 		servers := conf.servers()
    274 		if !reflect.DeepEqual(servers, tt.servers) {
    275 			t.Errorf("#%d: got %v; want %v", i, servers, tt.servers)
    276 			continue
    277 		}
    278 	}
    279 }
    280 
    281 var goLookupIPWithResolverConfigTests = []struct {
    282 	name  string
    283 	lines []string // resolver configuration lines
    284 	error
    285 	a, aaaa bool // whether response contains A, AAAA-record
    286 }{
    287 	// no records, transport timeout
    288 	{
    289 		"jgahvsekduiv9bw4b3qhn4ykdfgj0493iohkrjfhdvhjiu4j",
    290 		[]string{
    291 			"options timeout:1 attempts:1",
    292 			"nameserver 255.255.255.255", // please forgive us for abuse of limited broadcast address
    293 		},
    294 		&DNSError{Name: "jgahvsekduiv9bw4b3qhn4ykdfgj0493iohkrjfhdvhjiu4j", Server: "255.255.255.255:53", IsTimeout: true},
    295 		false, false,
    296 	},
    297 
    298 	// no records, non-existent domain
    299 	{
    300 		"jgahvsekduiv9bw4b3qhn4ykdfgj0493iohkrjfhdvhjiu4j",
    301 		[]string{
    302 			"options timeout:3 attempts:1",
    303 			"nameserver 8.8.8.8",
    304 		},
    305 		&DNSError{Name: "jgahvsekduiv9bw4b3qhn4ykdfgj0493iohkrjfhdvhjiu4j", Server: "8.8.8.8:53", IsTimeout: false},
    306 		false, false,
    307 	},
    308 
    309 	// a few A records, no AAAA records
    310 	{
    311 		"ipv4.google.com.",
    312 		[]string{
    313 			"nameserver 8.8.8.8",
    314 			"nameserver 2001:4860:4860::8888",
    315 		},
    316 		nil,
    317 		true, false,
    318 	},
    319 	{
    320 		"ipv4.google.com",
    321 		[]string{
    322 			"domain golang.org",
    323 			"nameserver 2001:4860:4860::8888",
    324 			"nameserver 8.8.8.8",
    325 		},
    326 		nil,
    327 		true, false,
    328 	},
    329 	{
    330 		"ipv4.google.com",
    331 		[]string{
    332 			"search x.golang.org y.golang.org",
    333 			"nameserver 2001:4860:4860::8888",
    334 			"nameserver 8.8.8.8",
    335 		},
    336 		nil,
    337 		true, false,
    338 	},
    339 
    340 	// no A records, a few AAAA records
    341 	{
    342 		"ipv6.google.com.",
    343 		[]string{
    344 			"nameserver 2001:4860:4860::8888",
    345 			"nameserver 8.8.8.8",
    346 		},
    347 		nil,
    348 		false, true,
    349 	},
    350 	{
    351 		"ipv6.google.com",
    352 		[]string{
    353 			"domain golang.org",
    354 			"nameserver 8.8.8.8",
    355 			"nameserver 2001:4860:4860::8888",
    356 		},
    357 		nil,
    358 		false, true,
    359 	},
    360 	{
    361 		"ipv6.google.com",
    362 		[]string{
    363 			"search x.golang.org y.golang.org",
    364 			"nameserver 8.8.8.8",
    365 			"nameserver 2001:4860:4860::8888",
    366 		},
    367 		nil,
    368 		false, true,
    369 	},
    370 
    371 	// both A and AAAA records
    372 	{
    373 		"hostname.as112.net", // see RFC 7534
    374 		[]string{
    375 			"domain golang.org",
    376 			"nameserver 2001:4860:4860::8888",
    377 			"nameserver 8.8.8.8",
    378 		},
    379 		nil,
    380 		true, true,
    381 	},
    382 	{
    383 		"hostname.as112.net", // see RFC 7534
    384 		[]string{
    385 			"search x.golang.org y.golang.org",
    386 			"nameserver 2001:4860:4860::8888",
    387 			"nameserver 8.8.8.8",
    388 		},
    389 		nil,
    390 		true, true,
    391 	},
    392 }
    393 
    394 func TestGoLookupIPWithResolverConfig(t *testing.T) {
    395 	testenv.MustHaveExternalNetwork(t)
    396 
    397 	conf, err := newResolvConfTest()
    398 	if err != nil {
    399 		t.Fatal(err)
    400 	}
    401 	defer conf.teardown()
    402 
    403 	for _, tt := range goLookupIPWithResolverConfigTests {
    404 		if err := conf.writeAndUpdate(tt.lines); err != nil {
    405 			t.Error(err)
    406 			continue
    407 		}
    408 		addrs, err := goLookupIP(context.Background(), tt.name)
    409 		if err != nil {
    410 			// This test uses external network connectivity.
    411 			// We need to take care with errors on both
    412 			// DNS message exchange layer and DNS
    413 			// transport layer because goLookupIP may fail
    414 			// when the IP connectivity on node under test
    415 			// gets lost during its run.
    416 			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) {
    417 				t.Errorf("got %v; want %v", err, tt.error)
    418 			}
    419 			continue
    420 		}
    421 		if len(addrs) == 0 {
    422 			t.Errorf("no records for %s", tt.name)
    423 		}
    424 		if !tt.a && !tt.aaaa && len(addrs) > 0 {
    425 			t.Errorf("unexpected %v for %s", addrs, tt.name)
    426 		}
    427 		for _, addr := range addrs {
    428 			if !tt.a && addr.IP.To4() != nil {
    429 				t.Errorf("got %v; must not be IPv4 address", addr)
    430 			}
    431 			if !tt.aaaa && addr.IP.To16() != nil && addr.IP.To4() == nil {
    432 				t.Errorf("got %v; must not be IPv6 address", addr)
    433 			}
    434 		}
    435 	}
    436 }
    437 
    438 // Test that goLookupIPOrder falls back to the host file when no DNS servers are available.
    439 func TestGoLookupIPOrderFallbackToFile(t *testing.T) {
    440 	testenv.MustHaveExternalNetwork(t)
    441 
    442 	// Add a config that simulates no dns servers being available.
    443 	conf, err := newResolvConfTest()
    444 	if err != nil {
    445 		t.Fatal(err)
    446 	}
    447 	if err := conf.writeAndUpdate([]string{}); err != nil {
    448 		t.Fatal(err)
    449 	}
    450 	// Redirect host file lookups.
    451 	defer func(orig string) { testHookHostsPath = orig }(testHookHostsPath)
    452 	testHookHostsPath = "testdata/hosts"
    453 
    454 	for _, order := range []hostLookupOrder{hostLookupFilesDNS, hostLookupDNSFiles} {
    455 		name := fmt.Sprintf("order %v", order)
    456 
    457 		// First ensure that we get an error when contacting a non-existent host.
    458 		_, _, err := goLookupIPCNAMEOrder(context.Background(), "notarealhost", order)
    459 		if err == nil {
    460 			t.Errorf("%s: expected error while looking up name not in hosts file", name)
    461 			continue
    462 		}
    463 
    464 		// Now check that we get an address when the name appears in the hosts file.
    465 		addrs, _, err := goLookupIPCNAMEOrder(context.Background(), "thor", order) // entry is in "testdata/hosts"
    466 		if err != nil {
    467 			t.Errorf("%s: expected to successfully lookup host entry", name)
    468 			continue
    469 		}
    470 		if len(addrs) != 1 {
    471 			t.Errorf("%s: expected exactly one result, but got %v", name, addrs)
    472 			continue
    473 		}
    474 		if got, want := addrs[0].String(), "127.1.1.1"; got != want {
    475 			t.Errorf("%s: address doesn't match expectation. got %v, want %v", name, got, want)
    476 		}
    477 	}
    478 	defer conf.teardown()
    479 }
    480 
    481 // Issue 12712.
    482 // When using search domains, return the error encountered
    483 // querying the original name instead of an error encountered
    484 // querying a generated name.
    485 func TestErrorForOriginalNameWhenSearching(t *testing.T) {
    486 	const fqdn = "doesnotexist.domain"
    487 
    488 	origTestHookDNSDialer := testHookDNSDialer
    489 	defer func() { testHookDNSDialer = origTestHookDNSDialer }()
    490 
    491 	conf, err := newResolvConfTest()
    492 	if err != nil {
    493 		t.Fatal(err)
    494 	}
    495 	defer conf.teardown()
    496 
    497 	if err := conf.writeAndUpdate([]string{"search servfail"}); err != nil {
    498 		t.Fatal(err)
    499 	}
    500 
    501 	d := &fakeDNSDialer{}
    502 	testHookDNSDialer = func() dnsDialer { return d }
    503 
    504 	d.rh = func(s string, q *dnsMsg, _ time.Time) (*dnsMsg, error) {
    505 		r := &dnsMsg{
    506 			dnsMsgHdr: dnsMsgHdr{
    507 				id: q.id,
    508 			},
    509 		}
    510 
    511 		switch q.question[0].Name {
    512 		case fqdn + ".servfail.":
    513 			r.rcode = dnsRcodeServerFailure
    514 		default:
    515 			r.rcode = dnsRcodeNameError
    516 		}
    517 
    518 		return r, nil
    519 	}
    520 
    521 	_, err = goLookupIP(context.Background(), fqdn)
    522 	if err == nil {
    523 		t.Fatal("expected an error")
    524 	}
    525 
    526 	want := &DNSError{Name: fqdn, Err: errNoSuchHost.Error()}
    527 	if err, ok := err.(*DNSError); !ok || err.Name != want.Name || err.Err != want.Err {
    528 		t.Errorf("got %v; want %v", err, want)
    529 	}
    530 }
    531 
    532 // Issue 15434. If a name server gives a lame referral, continue to the next.
    533 func TestIgnoreLameReferrals(t *testing.T) {
    534 	origTestHookDNSDialer := testHookDNSDialer
    535 	defer func() { testHookDNSDialer = origTestHookDNSDialer }()
    536 
    537 	conf, err := newResolvConfTest()
    538 	if err != nil {
    539 		t.Fatal(err)
    540 	}
    541 	defer conf.teardown()
    542 
    543 	if err := conf.writeAndUpdate([]string{"nameserver 192.0.2.1", // the one that will give a lame referral
    544 		"nameserver 192.0.2.2"}); err != nil {
    545 		t.Fatal(err)
    546 	}
    547 
    548 	d := &fakeDNSDialer{}
    549 	testHookDNSDialer = func() dnsDialer { return d }
    550 
    551 	d.rh = func(s string, q *dnsMsg, _ time.Time) (*dnsMsg, error) {
    552 		t.Log(s, q)
    553 		r := &dnsMsg{
    554 			dnsMsgHdr: dnsMsgHdr{
    555 				id:       q.id,
    556 				response: true,
    557 			},
    558 			question: q.question,
    559 		}
    560 
    561 		if s == "192.0.2.2:53" {
    562 			r.recursion_available = true
    563 			if q.question[0].Qtype == dnsTypeA {
    564 				r.answer = []dnsRR{
    565 					&dnsRR_A{
    566 						Hdr: dnsRR_Header{
    567 							Name:     q.question[0].Name,
    568 							Rrtype:   dnsTypeA,
    569 							Class:    dnsClassINET,
    570 							Rdlength: 4,
    571 						},
    572 						A: TestAddr,
    573 					},
    574 				}
    575 			}
    576 		}
    577 
    578 		return r, nil
    579 	}
    580 
    581 	addrs, err := goLookupIP(context.Background(), "www.golang.org")
    582 	if err != nil {
    583 		t.Fatal(err)
    584 	}
    585 
    586 	if got := len(addrs); got != 1 {
    587 		t.Fatalf("got %d addresses, want 1", got)
    588 	}
    589 
    590 	if got, want := addrs[0].String(), "192.0.2.1"; got != want {
    591 		t.Fatalf("got address %v, want %v", got, want)
    592 	}
    593 }
    594 
    595 func BenchmarkGoLookupIP(b *testing.B) {
    596 	testHookUninstaller.Do(uninstallTestHooks)
    597 	ctx := context.Background()
    598 
    599 	for i := 0; i < b.N; i++ {
    600 		goLookupIP(ctx, "www.example.com")
    601 	}
    602 }
    603 
    604 func BenchmarkGoLookupIPNoSuchHost(b *testing.B) {
    605 	testHookUninstaller.Do(uninstallTestHooks)
    606 	ctx := context.Background()
    607 
    608 	for i := 0; i < b.N; i++ {
    609 		goLookupIP(ctx, "some.nonexistent")
    610 	}
    611 }
    612 
    613 func BenchmarkGoLookupIPWithBrokenNameServer(b *testing.B) {
    614 	testHookUninstaller.Do(uninstallTestHooks)
    615 
    616 	conf, err := newResolvConfTest()
    617 	if err != nil {
    618 		b.Fatal(err)
    619 	}
    620 	defer conf.teardown()
    621 
    622 	lines := []string{
    623 		"nameserver 203.0.113.254", // use TEST-NET-3 block, see RFC 5737
    624 		"nameserver 8.8.8.8",
    625 	}
    626 	if err := conf.writeAndUpdate(lines); err != nil {
    627 		b.Fatal(err)
    628 	}
    629 	ctx := context.Background()
    630 
    631 	for i := 0; i < b.N; i++ {
    632 		goLookupIP(ctx, "www.example.com")
    633 	}
    634 }
    635 
    636 type fakeDNSDialer struct {
    637 	// reply handler
    638 	rh func(s string, q *dnsMsg, t time.Time) (*dnsMsg, error)
    639 }
    640 
    641 func (f *fakeDNSDialer) dialDNS(_ context.Context, n, s string) (dnsConn, error) {
    642 	return &fakeDNSConn{f.rh, s, time.Time{}}, nil
    643 }
    644 
    645 type fakeDNSConn struct {
    646 	rh func(s string, q *dnsMsg, t time.Time) (*dnsMsg, error)
    647 	s  string
    648 	t  time.Time
    649 }
    650 
    651 func (f *fakeDNSConn) Close() error {
    652 	return nil
    653 }
    654 
    655 func (f *fakeDNSConn) SetDeadline(t time.Time) error {
    656 	f.t = t
    657 	return nil
    658 }
    659 
    660 func (f *fakeDNSConn) dnsRoundTrip(q *dnsMsg) (*dnsMsg, error) {
    661 	return f.rh(f.s, q, f.t)
    662 }
    663 
    664 // UDP round-tripper algorithm should ignore invalid DNS responses (issue 13281).
    665 func TestIgnoreDNSForgeries(t *testing.T) {
    666 	c, s := Pipe()
    667 	go func() {
    668 		b := make([]byte, 512)
    669 		n, err := s.Read(b)
    670 		if err != nil {
    671 			t.Error(err)
    672 			return
    673 		}
    674 
    675 		msg := &dnsMsg{}
    676 		if !msg.Unpack(b[:n]) {
    677 			t.Error("invalid DNS query")
    678 			return
    679 		}
    680 
    681 		s.Write([]byte("garbage DNS response packet"))
    682 
    683 		msg.response = true
    684 		msg.id++ // make invalid ID
    685 		b, ok := msg.Pack()
    686 		if !ok {
    687 			t.Error("failed to pack DNS response")
    688 			return
    689 		}
    690 		s.Write(b)
    691 
    692 		msg.id-- // restore original ID
    693 		msg.answer = []dnsRR{
    694 			&dnsRR_A{
    695 				Hdr: dnsRR_Header{
    696 					Name:     "www.example.com.",
    697 					Rrtype:   dnsTypeA,
    698 					Class:    dnsClassINET,
    699 					Rdlength: 4,
    700 				},
    701 				A: TestAddr,
    702 			},
    703 		}
    704 
    705 		b, ok = msg.Pack()
    706 		if !ok {
    707 			t.Error("failed to pack DNS response")
    708 			return
    709 		}
    710 		s.Write(b)
    711 	}()
    712 
    713 	msg := &dnsMsg{
    714 		dnsMsgHdr: dnsMsgHdr{
    715 			id: 42,
    716 		},
    717 		question: []dnsQuestion{
    718 			{
    719 				Name:   "www.example.com.",
    720 				Qtype:  dnsTypeA,
    721 				Qclass: dnsClassINET,
    722 			},
    723 		},
    724 	}
    725 
    726 	resp, err := dnsRoundTripUDP(c, msg)
    727 	if err != nil {
    728 		t.Fatalf("dnsRoundTripUDP failed: %v", err)
    729 	}
    730 
    731 	if got := resp.answer[0].(*dnsRR_A).A; got != TestAddr {
    732 		t.Errorf("got address %v, want %v", got, TestAddr)
    733 	}
    734 }
    735 
    736 // Issue 16865. If a name server times out, continue to the next.
    737 func TestRetryTimeout(t *testing.T) {
    738 	origTestHookDNSDialer := testHookDNSDialer
    739 	defer func() { testHookDNSDialer = origTestHookDNSDialer }()
    740 
    741 	conf, err := newResolvConfTest()
    742 	if err != nil {
    743 		t.Fatal(err)
    744 	}
    745 	defer conf.teardown()
    746 
    747 	testConf := []string{
    748 		"nameserver 192.0.2.1", // the one that will timeout
    749 		"nameserver 192.0.2.2",
    750 	}
    751 	if err := conf.writeAndUpdate(testConf); err != nil {
    752 		t.Fatal(err)
    753 	}
    754 
    755 	d := &fakeDNSDialer{}
    756 	testHookDNSDialer = func() dnsDialer { return d }
    757 
    758 	var deadline0 time.Time
    759 
    760 	d.rh = func(s string, q *dnsMsg, deadline time.Time) (*dnsMsg, error) {
    761 		t.Log(s, q, deadline)
    762 
    763 		if deadline.IsZero() {
    764 			t.Error("zero deadline")
    765 		}
    766 
    767 		if s == "192.0.2.1:53" {
    768 			deadline0 = deadline
    769 			time.Sleep(10 * time.Millisecond)
    770 			return nil, errTimeout
    771 		}
    772 
    773 		if deadline == deadline0 {
    774 			t.Error("deadline didn't change")
    775 		}
    776 
    777 		return mockTXTResponse(q), nil
    778 	}
    779 
    780 	_, err = LookupTXT("www.golang.org")
    781 	if err != nil {
    782 		t.Fatal(err)
    783 	}
    784 
    785 	if deadline0.IsZero() {
    786 		t.Error("deadline0 still zero", deadline0)
    787 	}
    788 }
    789 
    790 func TestRotate(t *testing.T) {
    791 	// without rotation, always uses the first server
    792 	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"})
    793 
    794 	// with rotation, rotates through back to first
    795 	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"})
    796 }
    797 
    798 func testRotate(t *testing.T, rotate bool, nameservers, wantServers []string) {
    799 	origTestHookDNSDialer := testHookDNSDialer
    800 	defer func() { testHookDNSDialer = origTestHookDNSDialer }()
    801 
    802 	conf, err := newResolvConfTest()
    803 	if err != nil {
    804 		t.Fatal(err)
    805 	}
    806 	defer conf.teardown()
    807 
    808 	var confLines []string
    809 	for _, ns := range nameservers {
    810 		confLines = append(confLines, "nameserver "+ns)
    811 	}
    812 	if rotate {
    813 		confLines = append(confLines, "options rotate")
    814 	}
    815 
    816 	if err := conf.writeAndUpdate(confLines); err != nil {
    817 		t.Fatal(err)
    818 	}
    819 
    820 	d := &fakeDNSDialer{}
    821 	testHookDNSDialer = func() dnsDialer { return d }
    822 
    823 	var usedServers []string
    824 	d.rh = func(s string, q *dnsMsg, _ time.Time) (*dnsMsg, error) {
    825 		usedServers = append(usedServers, s)
    826 		return mockTXTResponse(q), nil
    827 	}
    828 
    829 	// len(nameservers) + 1 to allow rotation to get back to start
    830 	for i := 0; i < len(nameservers)+1; i++ {
    831 		if _, err := LookupTXT("www.golang.org"); err != nil {
    832 			t.Fatal(err)
    833 		}
    834 	}
    835 
    836 	if !reflect.DeepEqual(usedServers, wantServers) {
    837 		t.Errorf("rotate=%t got used servers:\n%v\nwant:\n%v", rotate, usedServers, wantServers)
    838 	}
    839 }
    840 
    841 func mockTXTResponse(q *dnsMsg) *dnsMsg {
    842 	r := &dnsMsg{
    843 		dnsMsgHdr: dnsMsgHdr{
    844 			id:                  q.id,
    845 			response:            true,
    846 			recursion_available: true,
    847 		},
    848 		question: q.question,
    849 		answer: []dnsRR{
    850 			&dnsRR_TXT{
    851 				Hdr: dnsRR_Header{
    852 					Name:   q.question[0].Name,
    853 					Rrtype: dnsTypeTXT,
    854 					Class:  dnsClassINET,
    855 				},
    856 				Txt: "ok",
    857 			},
    858 		},
    859 	}
    860 
    861 	return r
    862 }
    863