github.com/riscv/riscv-go@v0.0.0-20200123204226-124ebd6fcc8e/src/net/dnsclient_unix_test.go (about)

     1  // Copyright 2013 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  // +build darwin dragonfly freebsd linux netbsd openbsd solaris
     6  
     7  package net
     8  
     9  import (
    10  	"context"
    11  	"fmt"
    12  	"internal/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  }