github.com/slayercat/go@v0.0.0-20170428012452-c51559813f61/src/net/dnsclient_unix_test.go (about)

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