Source file src/net/lookup_test.go

     1  // Copyright 2009 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  //go:build !js
     6  
     7  package net
     8  
     9  import (
    10  	"bytes"
    11  	"context"
    12  	"fmt"
    13  	"internal/testenv"
    14  	"reflect"
    15  	"runtime"
    16  	"sort"
    17  	"strings"
    18  	"sync"
    19  	"sync/atomic"
    20  	"testing"
    21  	"time"
    22  )
    23  
    24  func hasSuffixFold(s, suffix string) bool {
    25  	return strings.HasSuffix(strings.ToLower(s), strings.ToLower(suffix))
    26  }
    27  
    28  func lookupLocalhost(ctx context.Context, fn func(context.Context, string, string) ([]IPAddr, error), network, host string) ([]IPAddr, error) {
    29  	switch host {
    30  	case "localhost":
    31  		return []IPAddr{
    32  			{IP: IPv4(127, 0, 0, 1)},
    33  			{IP: IPv6loopback},
    34  		}, nil
    35  	default:
    36  		return fn(ctx, network, host)
    37  	}
    38  }
    39  
    40  // The Lookup APIs use various sources such as local database, DNS or
    41  // mDNS, and may use platform-dependent DNS stub resolver if possible.
    42  // The APIs accept any of forms for a query; host name in various
    43  // encodings, UTF-8 encoded net name, domain name, FQDN or absolute
    44  // FQDN, but the result would be one of the forms and it depends on
    45  // the circumstances.
    46  
    47  var lookupGoogleSRVTests = []struct {
    48  	service, proto, name string
    49  	cname, target        string
    50  }{
    51  	{
    52  		"xmpp-server", "tcp", "google.com",
    53  		"google.com.", "google.com.",
    54  	},
    55  	{
    56  		"xmpp-server", "tcp", "google.com.",
    57  		"google.com.", "google.com.",
    58  	},
    59  
    60  	// non-standard back door
    61  	{
    62  		"", "", "_xmpp-server._tcp.google.com",
    63  		"google.com.", "google.com.",
    64  	},
    65  	{
    66  		"", "", "_xmpp-server._tcp.google.com.",
    67  		"google.com.", "google.com.",
    68  	},
    69  }
    70  
    71  var backoffDuration = [...]time.Duration{time.Second, 5 * time.Second, 30 * time.Second}
    72  
    73  func TestLookupGoogleSRV(t *testing.T) {
    74  	// TODO(mknyszek): Figure out next steps for this test. This is just
    75  	// a quick fix.
    76  	t.Skip("fails consistently due to an upstream DNS change; see #56707.")
    77  
    78  	t.Parallel()
    79  	mustHaveExternalNetwork(t)
    80  
    81  	if iOS() {
    82  		t.Skip("no resolv.conf on iOS")
    83  	}
    84  
    85  	if !supportsIPv4() || !*testIPv4 {
    86  		t.Skip("IPv4 is required")
    87  	}
    88  
    89  	attempts := 0
    90  	for i := 0; i < len(lookupGoogleSRVTests); i++ {
    91  		tt := lookupGoogleSRVTests[i]
    92  		cname, srvs, err := LookupSRV(tt.service, tt.proto, tt.name)
    93  		if err != nil {
    94  			testenv.SkipFlakyNet(t)
    95  			if attempts < len(backoffDuration) {
    96  				dur := backoffDuration[attempts]
    97  				t.Logf("backoff %v after failure %v\n", dur, err)
    98  				time.Sleep(dur)
    99  				attempts++
   100  				i--
   101  				continue
   102  			}
   103  			t.Fatal(err)
   104  		}
   105  		if len(srvs) == 0 {
   106  			t.Error("got no record")
   107  		}
   108  		if !hasSuffixFold(cname, tt.cname) {
   109  			t.Errorf("got %s; want %s", cname, tt.cname)
   110  		}
   111  		for _, srv := range srvs {
   112  			if !hasSuffixFold(srv.Target, tt.target) {
   113  				t.Errorf("got %v; want a record containing %s", srv, tt.target)
   114  			}
   115  		}
   116  	}
   117  }
   118  
   119  var lookupGmailMXTests = []struct {
   120  	name, host string
   121  }{
   122  	{"gmail.com", "google.com."},
   123  	{"gmail.com.", "google.com."},
   124  }
   125  
   126  func TestLookupGmailMX(t *testing.T) {
   127  	t.Parallel()
   128  	mustHaveExternalNetwork(t)
   129  
   130  	if iOS() {
   131  		t.Skip("no resolv.conf on iOS")
   132  	}
   133  
   134  	if !supportsIPv4() || !*testIPv4 {
   135  		t.Skip("IPv4 is required")
   136  	}
   137  
   138  	attempts := 0
   139  	for i := 0; i < len(lookupGmailMXTests); i++ {
   140  		tt := lookupGmailMXTests[i]
   141  		mxs, err := LookupMX(tt.name)
   142  		if err != nil {
   143  			testenv.SkipFlakyNet(t)
   144  			if attempts < len(backoffDuration) {
   145  				dur := backoffDuration[attempts]
   146  				t.Logf("backoff %v after failure %v\n", dur, err)
   147  				time.Sleep(dur)
   148  				attempts++
   149  				i--
   150  				continue
   151  			}
   152  			t.Fatal(err)
   153  		}
   154  		if len(mxs) == 0 {
   155  			t.Error("got no record")
   156  		}
   157  		for _, mx := range mxs {
   158  			if !hasSuffixFold(mx.Host, tt.host) {
   159  				t.Errorf("got %v; want a record containing %s", mx, tt.host)
   160  			}
   161  		}
   162  	}
   163  }
   164  
   165  var lookupGmailNSTests = []struct {
   166  	name, host string
   167  }{
   168  	{"gmail.com", "google.com."},
   169  	{"gmail.com.", "google.com."},
   170  }
   171  
   172  func TestLookupGmailNS(t *testing.T) {
   173  	t.Parallel()
   174  	mustHaveExternalNetwork(t)
   175  
   176  	if iOS() {
   177  		t.Skip("no resolv.conf on iOS")
   178  	}
   179  
   180  	if !supportsIPv4() || !*testIPv4 {
   181  		t.Skip("IPv4 is required")
   182  	}
   183  
   184  	attempts := 0
   185  	for i := 0; i < len(lookupGmailNSTests); i++ {
   186  		tt := lookupGmailNSTests[i]
   187  		nss, err := LookupNS(tt.name)
   188  		if err != nil {
   189  			testenv.SkipFlakyNet(t)
   190  			if attempts < len(backoffDuration) {
   191  				dur := backoffDuration[attempts]
   192  				t.Logf("backoff %v after failure %v\n", dur, err)
   193  				time.Sleep(dur)
   194  				attempts++
   195  				i--
   196  				continue
   197  			}
   198  			t.Fatal(err)
   199  		}
   200  		if len(nss) == 0 {
   201  			t.Error("got no record")
   202  		}
   203  		for _, ns := range nss {
   204  			if !hasSuffixFold(ns.Host, tt.host) {
   205  				t.Errorf("got %v; want a record containing %s", ns, tt.host)
   206  			}
   207  		}
   208  	}
   209  }
   210  
   211  var lookupGmailTXTTests = []struct {
   212  	name, txt, host string
   213  }{
   214  	{"gmail.com", "spf", "google.com"},
   215  	{"gmail.com.", "spf", "google.com"},
   216  }
   217  
   218  func TestLookupGmailTXT(t *testing.T) {
   219  	if runtime.GOOS == "plan9" {
   220  		t.Skip("skipping on plan9; see https://golang.org/issue/29722")
   221  	}
   222  	t.Parallel()
   223  	mustHaveExternalNetwork(t)
   224  
   225  	if iOS() {
   226  		t.Skip("no resolv.conf on iOS")
   227  	}
   228  
   229  	if !supportsIPv4() || !*testIPv4 {
   230  		t.Skip("IPv4 is required")
   231  	}
   232  
   233  	attempts := 0
   234  	for i := 0; i < len(lookupGmailTXTTests); i++ {
   235  		tt := lookupGmailTXTTests[i]
   236  		txts, err := LookupTXT(tt.name)
   237  		if err != nil {
   238  			testenv.SkipFlakyNet(t)
   239  			if attempts < len(backoffDuration) {
   240  				dur := backoffDuration[attempts]
   241  				t.Logf("backoff %v after failure %v\n", dur, err)
   242  				time.Sleep(dur)
   243  				attempts++
   244  				i--
   245  				continue
   246  			}
   247  			t.Fatal(err)
   248  		}
   249  		if len(txts) == 0 {
   250  			t.Error("got no record")
   251  		}
   252  		found := false
   253  		for _, txt := range txts {
   254  			if strings.Contains(txt, tt.txt) && (strings.HasSuffix(txt, tt.host) || strings.HasSuffix(txt, tt.host+".")) {
   255  				found = true
   256  				break
   257  			}
   258  		}
   259  		if !found {
   260  			t.Errorf("got %v; want a record containing %s, %s", txts, tt.txt, tt.host)
   261  		}
   262  	}
   263  }
   264  
   265  var lookupGooglePublicDNSAddrTests = []string{
   266  	"8.8.8.8",
   267  	"8.8.4.4",
   268  	"2001:4860:4860::8888",
   269  	"2001:4860:4860::8844",
   270  }
   271  
   272  func TestLookupGooglePublicDNSAddr(t *testing.T) {
   273  	mustHaveExternalNetwork(t)
   274  
   275  	if !supportsIPv4() || !supportsIPv6() || !*testIPv4 || !*testIPv6 {
   276  		t.Skip("both IPv4 and IPv6 are required")
   277  	}
   278  
   279  	defer dnsWaitGroup.Wait()
   280  
   281  	for _, ip := range lookupGooglePublicDNSAddrTests {
   282  		names, err := LookupAddr(ip)
   283  		if err != nil {
   284  			t.Fatal(err)
   285  		}
   286  		if len(names) == 0 {
   287  			t.Error("got no record")
   288  		}
   289  		for _, name := range names {
   290  			if !hasSuffixFold(name, ".google.com.") && !hasSuffixFold(name, ".google.") {
   291  				t.Errorf("got %q; want a record ending in .google.com. or .google.", name)
   292  			}
   293  		}
   294  	}
   295  }
   296  
   297  func TestLookupIPv6LinkLocalAddr(t *testing.T) {
   298  	if !supportsIPv6() || !*testIPv6 {
   299  		t.Skip("IPv6 is required")
   300  	}
   301  
   302  	defer dnsWaitGroup.Wait()
   303  
   304  	addrs, err := LookupHost("localhost")
   305  	if err != nil {
   306  		t.Fatal(err)
   307  	}
   308  	found := false
   309  	for _, addr := range addrs {
   310  		if addr == "fe80::1%lo0" {
   311  			found = true
   312  			break
   313  		}
   314  	}
   315  	if !found {
   316  		t.Skipf("not supported on %s", runtime.GOOS)
   317  	}
   318  	if _, err := LookupAddr("fe80::1%lo0"); err != nil {
   319  		t.Error(err)
   320  	}
   321  }
   322  
   323  func TestLookupIPv6LinkLocalAddrWithZone(t *testing.T) {
   324  	if !supportsIPv6() || !*testIPv6 {
   325  		t.Skip("IPv6 is required")
   326  	}
   327  
   328  	ipaddrs, err := DefaultResolver.LookupIPAddr(context.Background(), "fe80::1%lo0")
   329  	if err != nil {
   330  		t.Error(err)
   331  	}
   332  	for _, addr := range ipaddrs {
   333  		if e, a := "lo0", addr.Zone; e != a {
   334  			t.Errorf("wrong zone: want %q, got %q", e, a)
   335  		}
   336  	}
   337  
   338  	addrs, err := DefaultResolver.LookupHost(context.Background(), "fe80::1%lo0")
   339  	if err != nil {
   340  		t.Error(err)
   341  	}
   342  	for _, addr := range addrs {
   343  		if e, a := "fe80::1%lo0", addr; e != a {
   344  			t.Errorf("wrong host: want %q got %q", e, a)
   345  		}
   346  	}
   347  }
   348  
   349  var lookupCNAMETests = []struct {
   350  	name, cname string
   351  }{
   352  	{"www.iana.org", "icann.org."},
   353  	{"www.iana.org.", "icann.org."},
   354  	{"www.google.com", "google.com."},
   355  }
   356  
   357  func TestLookupCNAME(t *testing.T) {
   358  	mustHaveExternalNetwork(t)
   359  	testenv.SkipFlakyNet(t)
   360  
   361  	if !supportsIPv4() || !*testIPv4 {
   362  		t.Skip("IPv4 is required")
   363  	}
   364  
   365  	defer dnsWaitGroup.Wait()
   366  
   367  	attempts := 0
   368  	for i := 0; i < len(lookupCNAMETests); i++ {
   369  		tt := lookupCNAMETests[i]
   370  		cname, err := LookupCNAME(tt.name)
   371  		if err != nil {
   372  			testenv.SkipFlakyNet(t)
   373  			if attempts < len(backoffDuration) {
   374  				dur := backoffDuration[attempts]
   375  				t.Logf("backoff %v after failure %v\n", dur, err)
   376  				time.Sleep(dur)
   377  				attempts++
   378  				i--
   379  				continue
   380  			}
   381  			t.Fatal(err)
   382  		}
   383  		if !hasSuffixFold(cname, tt.cname) {
   384  			t.Errorf("got %s; want a record containing %s", cname, tt.cname)
   385  		}
   386  	}
   387  }
   388  
   389  var lookupGoogleHostTests = []struct {
   390  	name string
   391  }{
   392  	{"google.com"},
   393  	{"google.com."},
   394  }
   395  
   396  func TestLookupGoogleHost(t *testing.T) {
   397  	mustHaveExternalNetwork(t)
   398  	testenv.SkipFlakyNet(t)
   399  
   400  	if !supportsIPv4() || !*testIPv4 {
   401  		t.Skip("IPv4 is required")
   402  	}
   403  
   404  	defer dnsWaitGroup.Wait()
   405  
   406  	for _, tt := range lookupGoogleHostTests {
   407  		addrs, err := LookupHost(tt.name)
   408  		if err != nil {
   409  			t.Fatal(err)
   410  		}
   411  		if len(addrs) == 0 {
   412  			t.Error("got no record")
   413  		}
   414  		for _, addr := range addrs {
   415  			if ParseIP(addr) == nil {
   416  				t.Errorf("got %q; want a literal IP address", addr)
   417  			}
   418  		}
   419  	}
   420  }
   421  
   422  func TestLookupLongTXT(t *testing.T) {
   423  	testenv.SkipFlaky(t, 22857)
   424  	mustHaveExternalNetwork(t)
   425  
   426  	defer dnsWaitGroup.Wait()
   427  
   428  	txts, err := LookupTXT("golang.rsc.io")
   429  	if err != nil {
   430  		t.Fatal(err)
   431  	}
   432  	sort.Strings(txts)
   433  	want := []string{
   434  		strings.Repeat("abcdefghijklmnopqrstuvwxyABCDEFGHJIKLMNOPQRSTUVWXY", 10),
   435  		"gophers rule",
   436  	}
   437  	if !reflect.DeepEqual(txts, want) {
   438  		t.Fatalf("LookupTXT golang.rsc.io incorrect\nhave %q\nwant %q", txts, want)
   439  	}
   440  }
   441  
   442  var lookupGoogleIPTests = []struct {
   443  	name string
   444  }{
   445  	{"google.com"},
   446  	{"google.com."},
   447  }
   448  
   449  func TestLookupGoogleIP(t *testing.T) {
   450  	mustHaveExternalNetwork(t)
   451  	testenv.SkipFlakyNet(t)
   452  
   453  	if !supportsIPv4() || !*testIPv4 {
   454  		t.Skip("IPv4 is required")
   455  	}
   456  
   457  	defer dnsWaitGroup.Wait()
   458  
   459  	for _, tt := range lookupGoogleIPTests {
   460  		ips, err := LookupIP(tt.name)
   461  		if err != nil {
   462  			t.Fatal(err)
   463  		}
   464  		if len(ips) == 0 {
   465  			t.Error("got no record")
   466  		}
   467  		for _, ip := range ips {
   468  			if ip.To4() == nil && ip.To16() == nil {
   469  				t.Errorf("got %v; want an IP address", ip)
   470  			}
   471  		}
   472  	}
   473  }
   474  
   475  var revAddrTests = []struct {
   476  	Addr      string
   477  	Reverse   string
   478  	ErrPrefix string
   479  }{
   480  	{"1.2.3.4", "4.3.2.1.in-addr.arpa.", ""},
   481  	{"245.110.36.114", "114.36.110.245.in-addr.arpa.", ""},
   482  	{"::ffff:12.34.56.78", "78.56.34.12.in-addr.arpa.", ""},
   483  	{"::1", "1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.ip6.arpa.", ""},
   484  	{"1::", "0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.1.0.0.0.ip6.arpa.", ""},
   485  	{"1234:567::89a:bcde", "e.d.c.b.a.9.8.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.7.6.5.0.4.3.2.1.ip6.arpa.", ""},
   486  	{"1234:567:fefe:bcbc:adad:9e4a:89a:bcde", "e.d.c.b.a.9.8.0.a.4.e.9.d.a.d.a.c.b.c.b.e.f.e.f.7.6.5.0.4.3.2.1.ip6.arpa.", ""},
   487  	{"1.2.3", "", "unrecognized address"},
   488  	{"1.2.3.4.5", "", "unrecognized address"},
   489  	{"1234:567:bcbca::89a:bcde", "", "unrecognized address"},
   490  	{"1234:567::bcbc:adad::89a:bcde", "", "unrecognized address"},
   491  }
   492  
   493  func TestReverseAddress(t *testing.T) {
   494  	defer dnsWaitGroup.Wait()
   495  	for i, tt := range revAddrTests {
   496  		a, err := reverseaddr(tt.Addr)
   497  		if len(tt.ErrPrefix) > 0 && err == nil {
   498  			t.Errorf("#%d: expected %q, got <nil> (error)", i, tt.ErrPrefix)
   499  			continue
   500  		}
   501  		if len(tt.ErrPrefix) == 0 && err != nil {
   502  			t.Errorf("#%d: expected <nil>, got %q (error)", i, err)
   503  		}
   504  		if err != nil && err.(*DNSError).Err != tt.ErrPrefix {
   505  			t.Errorf("#%d: expected %q, got %q (mismatched error)", i, tt.ErrPrefix, err.(*DNSError).Err)
   506  		}
   507  		if a != tt.Reverse {
   508  			t.Errorf("#%d: expected %q, got %q (reverse address)", i, tt.Reverse, a)
   509  		}
   510  	}
   511  }
   512  
   513  func TestDNSFlood(t *testing.T) {
   514  	if !*testDNSFlood {
   515  		t.Skip("test disabled; use -dnsflood to enable")
   516  	}
   517  
   518  	defer dnsWaitGroup.Wait()
   519  
   520  	var N = 5000
   521  	if runtime.GOOS == "darwin" || runtime.GOOS == "ios" {
   522  		// On Darwin this test consumes kernel threads much
   523  		// than other platforms for some reason.
   524  		// When we monitor the number of allocated Ms by
   525  		// observing on runtime.newm calls, we can see that it
   526  		// easily reaches the per process ceiling
   527  		// kern.num_threads when CGO_ENABLED=1 and
   528  		// GODEBUG=netdns=go.
   529  		N = 500
   530  	}
   531  
   532  	const timeout = 3 * time.Second
   533  	ctxHalfTimeout, cancel := context.WithTimeout(context.Background(), timeout/2)
   534  	defer cancel()
   535  	ctxTimeout, cancel := context.WithTimeout(context.Background(), timeout)
   536  	defer cancel()
   537  
   538  	c := make(chan error, 2*N)
   539  	for i := 0; i < N; i++ {
   540  		name := fmt.Sprintf("%d.net-test.golang.org", i)
   541  		go func() {
   542  			_, err := DefaultResolver.LookupIPAddr(ctxHalfTimeout, name)
   543  			c <- err
   544  		}()
   545  		go func() {
   546  			_, err := DefaultResolver.LookupIPAddr(ctxTimeout, name)
   547  			c <- err
   548  		}()
   549  	}
   550  	qstats := struct {
   551  		succeeded, failed         int
   552  		timeout, temporary, other int
   553  		unknown                   int
   554  	}{}
   555  	deadline := time.After(timeout + time.Second)
   556  	for i := 0; i < 2*N; i++ {
   557  		select {
   558  		case <-deadline:
   559  			t.Fatal("deadline exceeded")
   560  		case err := <-c:
   561  			switch err := err.(type) {
   562  			case nil:
   563  				qstats.succeeded++
   564  			case Error:
   565  				qstats.failed++
   566  				if err.Timeout() {
   567  					qstats.timeout++
   568  				}
   569  				if err.Temporary() {
   570  					qstats.temporary++
   571  				}
   572  				if !err.Timeout() && !err.Temporary() {
   573  					qstats.other++
   574  				}
   575  			default:
   576  				qstats.failed++
   577  				qstats.unknown++
   578  			}
   579  		}
   580  	}
   581  
   582  	// A high volume of DNS queries for sub-domain of golang.org
   583  	// would be coordinated by authoritative or recursive server,
   584  	// or stub resolver which implements query-response rate
   585  	// limitation, so we can expect some query successes and more
   586  	// failures including timeout, temporary and other here.
   587  	// As a rule, unknown must not be shown but it might possibly
   588  	// happen due to issue 4856 for now.
   589  	t.Logf("%v succeeded, %v failed (%v timeout, %v temporary, %v other, %v unknown)", qstats.succeeded, qstats.failed, qstats.timeout, qstats.temporary, qstats.other, qstats.unknown)
   590  }
   591  
   592  func TestLookupDotsWithLocalSource(t *testing.T) {
   593  	if !supportsIPv4() || !*testIPv4 {
   594  		t.Skip("IPv4 is required")
   595  	}
   596  
   597  	mustHaveExternalNetwork(t)
   598  
   599  	defer dnsWaitGroup.Wait()
   600  
   601  	for i, fn := range []func() func(){forceGoDNS, forceCgoDNS} {
   602  		fixup := fn()
   603  		if fixup == nil {
   604  			continue
   605  		}
   606  		names, err := LookupAddr("127.0.0.1")
   607  		fixup()
   608  		if err != nil {
   609  			t.Logf("#%d: %v", i, err)
   610  			continue
   611  		}
   612  		mode := "netgo"
   613  		if i == 1 {
   614  			mode = "netcgo"
   615  		}
   616  	loop:
   617  		for i, name := range names {
   618  			if strings.Index(name, ".") == len(name)-1 { // "localhost" not "localhost."
   619  				for j := range names {
   620  					if j == i {
   621  						continue
   622  					}
   623  					if names[j] == name[:len(name)-1] {
   624  						// It's OK if we find the name without the dot,
   625  						// as some systems say 127.0.0.1 localhost localhost.
   626  						continue loop
   627  					}
   628  				}
   629  				t.Errorf("%s: got %s; want %s", mode, name, name[:len(name)-1])
   630  			} else if strings.Contains(name, ".") && !strings.HasSuffix(name, ".") { // "localhost.localdomain." not "localhost.localdomain"
   631  				t.Errorf("%s: got %s; want name ending with trailing dot", mode, name)
   632  			}
   633  		}
   634  	}
   635  }
   636  
   637  func TestLookupDotsWithRemoteSource(t *testing.T) {
   638  	// TODO(mknyszek): Figure out next steps for this test. This is just
   639  	// a quick fix.
   640  	t.Skip("fails consistently due to an upstream DNS change; see #56707.")
   641  
   642  	if runtime.GOOS == "darwin" || runtime.GOOS == "ios" {
   643  		testenv.SkipFlaky(t, 27992)
   644  	}
   645  	mustHaveExternalNetwork(t)
   646  	testenv.SkipFlakyNet(t)
   647  
   648  	if !supportsIPv4() || !*testIPv4 {
   649  		t.Skip("IPv4 is required")
   650  	}
   651  
   652  	if iOS() {
   653  		t.Skip("no resolv.conf on iOS")
   654  	}
   655  
   656  	defer dnsWaitGroup.Wait()
   657  
   658  	if fixup := forceGoDNS(); fixup != nil {
   659  		testDots(t, "go")
   660  		fixup()
   661  	}
   662  	if fixup := forceCgoDNS(); fixup != nil {
   663  		testDots(t, "cgo")
   664  		fixup()
   665  	}
   666  }
   667  
   668  func testDots(t *testing.T, mode string) {
   669  	names, err := LookupAddr("8.8.8.8") // Google dns server
   670  	if err != nil {
   671  		t.Errorf("LookupAddr(8.8.8.8): %v (mode=%v)", err, mode)
   672  	} else {
   673  		for _, name := range names {
   674  			if !hasSuffixFold(name, ".google.com.") && !hasSuffixFold(name, ".google.") {
   675  				t.Errorf("LookupAddr(8.8.8.8) = %v, want names ending in .google.com or .google with trailing dot (mode=%v)", names, mode)
   676  				break
   677  			}
   678  		}
   679  	}
   680  
   681  	cname, err := LookupCNAME("www.mit.edu")
   682  	if err != nil {
   683  		t.Errorf("LookupCNAME(www.mit.edu, mode=%v): %v", mode, err)
   684  	} else if !strings.HasSuffix(cname, ".") {
   685  		t.Errorf("LookupCNAME(www.mit.edu) = %v, want cname ending in . with trailing dot (mode=%v)", cname, mode)
   686  	}
   687  
   688  	mxs, err := LookupMX("google.com")
   689  	if err != nil {
   690  		t.Errorf("LookupMX(google.com): %v (mode=%v)", err, mode)
   691  	} else {
   692  		for _, mx := range mxs {
   693  			if !hasSuffixFold(mx.Host, ".google.com.") {
   694  				t.Errorf("LookupMX(google.com) = %v, want names ending in .google.com. with trailing dot (mode=%v)", mxString(mxs), mode)
   695  				break
   696  			}
   697  		}
   698  	}
   699  
   700  	nss, err := LookupNS("google.com")
   701  	if err != nil {
   702  		t.Errorf("LookupNS(google.com): %v (mode=%v)", err, mode)
   703  	} else {
   704  		for _, ns := range nss {
   705  			if !hasSuffixFold(ns.Host, ".google.com.") {
   706  				t.Errorf("LookupNS(google.com) = %v, want names ending in .google.com. with trailing dot (mode=%v)", nsString(nss), mode)
   707  				break
   708  			}
   709  		}
   710  	}
   711  
   712  	cname, srvs, err := LookupSRV("xmpp-server", "tcp", "google.com")
   713  	if err != nil {
   714  		t.Errorf("LookupSRV(xmpp-server, tcp, google.com): %v (mode=%v)", err, mode)
   715  	} else {
   716  		if !hasSuffixFold(cname, ".google.com.") {
   717  			t.Errorf("LookupSRV(xmpp-server, tcp, google.com) returned cname=%v, want name ending in .google.com. with trailing dot (mode=%v)", cname, mode)
   718  		}
   719  		for _, srv := range srvs {
   720  			if !hasSuffixFold(srv.Target, ".google.com.") {
   721  				t.Errorf("LookupSRV(xmpp-server, tcp, google.com) returned addrs=%v, want names ending in .google.com. with trailing dot (mode=%v)", srvString(srvs), mode)
   722  				break
   723  			}
   724  		}
   725  	}
   726  }
   727  
   728  func mxString(mxs []*MX) string {
   729  	var buf bytes.Buffer
   730  	sep := ""
   731  	fmt.Fprintf(&buf, "[")
   732  	for _, mx := range mxs {
   733  		fmt.Fprintf(&buf, "%s%s:%d", sep, mx.Host, mx.Pref)
   734  		sep = " "
   735  	}
   736  	fmt.Fprintf(&buf, "]")
   737  	return buf.String()
   738  }
   739  
   740  func nsString(nss []*NS) string {
   741  	var buf bytes.Buffer
   742  	sep := ""
   743  	fmt.Fprintf(&buf, "[")
   744  	for _, ns := range nss {
   745  		fmt.Fprintf(&buf, "%s%s", sep, ns.Host)
   746  		sep = " "
   747  	}
   748  	fmt.Fprintf(&buf, "]")
   749  	return buf.String()
   750  }
   751  
   752  func srvString(srvs []*SRV) string {
   753  	var buf bytes.Buffer
   754  	sep := ""
   755  	fmt.Fprintf(&buf, "[")
   756  	for _, srv := range srvs {
   757  		fmt.Fprintf(&buf, "%s%s:%d:%d:%d", sep, srv.Target, srv.Port, srv.Priority, srv.Weight)
   758  		sep = " "
   759  	}
   760  	fmt.Fprintf(&buf, "]")
   761  	return buf.String()
   762  }
   763  
   764  func TestLookupPort(t *testing.T) {
   765  	// See https://www.iana.org/assignments/service-names-port-numbers/service-names-port-numbers.xhtml
   766  	//
   767  	// Please be careful about adding new test cases.
   768  	// There are platforms which have incomplete mappings for
   769  	// restricted resource access and security reasons.
   770  	type test struct {
   771  		network string
   772  		name    string
   773  		port    int
   774  		ok      bool
   775  	}
   776  	var tests = []test{
   777  		{"tcp", "0", 0, true},
   778  		{"udp", "0", 0, true},
   779  		{"udp", "domain", 53, true},
   780  
   781  		{"--badnet--", "zzz", 0, false},
   782  		{"tcp", "--badport--", 0, false},
   783  		{"tcp", "-1", 0, false},
   784  		{"tcp", "65536", 0, false},
   785  		{"udp", "-1", 0, false},
   786  		{"udp", "65536", 0, false},
   787  		{"tcp", "123456789", 0, false},
   788  
   789  		// Issue 13610: LookupPort("tcp", "")
   790  		{"tcp", "", 0, true},
   791  		{"tcp4", "", 0, true},
   792  		{"tcp6", "", 0, true},
   793  		{"udp", "", 0, true},
   794  		{"udp4", "", 0, true},
   795  		{"udp6", "", 0, true},
   796  	}
   797  
   798  	switch runtime.GOOS {
   799  	case "android":
   800  		if netGo {
   801  			t.Skipf("not supported on %s without cgo; see golang.org/issues/14576", runtime.GOOS)
   802  		}
   803  	default:
   804  		tests = append(tests, test{"tcp", "http", 80, true})
   805  	}
   806  
   807  	for _, tt := range tests {
   808  		port, err := LookupPort(tt.network, tt.name)
   809  		if port != tt.port || (err == nil) != tt.ok {
   810  			t.Errorf("LookupPort(%q, %q) = %d, %v; want %d, error=%t", tt.network, tt.name, port, err, tt.port, !tt.ok)
   811  		}
   812  		if err != nil {
   813  			if perr := parseLookupPortError(err); perr != nil {
   814  				t.Error(perr)
   815  			}
   816  		}
   817  	}
   818  }
   819  
   820  // Like TestLookupPort but with minimal tests that should always pass
   821  // because the answers are baked-in to the net package.
   822  func TestLookupPort_Minimal(t *testing.T) {
   823  	type test struct {
   824  		network string
   825  		name    string
   826  		port    int
   827  	}
   828  	var tests = []test{
   829  		{"tcp", "http", 80},
   830  		{"tcp", "HTTP", 80}, // case shouldn't matter
   831  		{"tcp", "https", 443},
   832  		{"tcp", "ssh", 22},
   833  		{"tcp", "gopher", 70},
   834  		{"tcp4", "http", 80},
   835  		{"tcp6", "http", 80},
   836  	}
   837  
   838  	for _, tt := range tests {
   839  		port, err := LookupPort(tt.network, tt.name)
   840  		if port != tt.port || err != nil {
   841  			t.Errorf("LookupPort(%q, %q) = %d, %v; want %d, error=nil", tt.network, tt.name, port, err, tt.port)
   842  		}
   843  	}
   844  }
   845  
   846  func TestLookupProtocol_Minimal(t *testing.T) {
   847  	type test struct {
   848  		name string
   849  		want int
   850  	}
   851  	var tests = []test{
   852  		{"tcp", 6},
   853  		{"TcP", 6}, // case shouldn't matter
   854  		{"icmp", 1},
   855  		{"igmp", 2},
   856  		{"udp", 17},
   857  		{"ipv6-icmp", 58},
   858  	}
   859  
   860  	for _, tt := range tests {
   861  		got, err := lookupProtocol(context.Background(), tt.name)
   862  		if got != tt.want || err != nil {
   863  			t.Errorf("LookupProtocol(%q) = %d, %v; want %d, error=nil", tt.name, got, err, tt.want)
   864  		}
   865  	}
   866  
   867  }
   868  
   869  func TestLookupNonLDH(t *testing.T) {
   870  	defer dnsWaitGroup.Wait()
   871  
   872  	if fixup := forceGoDNS(); fixup != nil {
   873  		defer fixup()
   874  	}
   875  
   876  	// "LDH" stands for letters, digits, and hyphens and is the usual
   877  	// description of standard DNS names.
   878  	// This test is checking that other kinds of names are reported
   879  	// as not found, not reported as invalid names.
   880  	addrs, err := LookupHost("!!!.###.bogus..domain.")
   881  	if err == nil {
   882  		t.Fatalf("lookup succeeded: %v", addrs)
   883  	}
   884  	if !strings.HasSuffix(err.Error(), errNoSuchHost.Error()) {
   885  		t.Fatalf("lookup error = %v, want %v", err, errNoSuchHost)
   886  	}
   887  	if !err.(*DNSError).IsNotFound {
   888  		t.Fatalf("lookup error = %v, want true", err.(*DNSError).IsNotFound)
   889  	}
   890  }
   891  
   892  func TestLookupContextCancel(t *testing.T) {
   893  	mustHaveExternalNetwork(t)
   894  	testenv.SkipFlakyNet(t)
   895  
   896  	origTestHookLookupIP := testHookLookupIP
   897  	defer func() {
   898  		dnsWaitGroup.Wait()
   899  		testHookLookupIP = origTestHookLookupIP
   900  	}()
   901  
   902  	lookupCtx, cancelLookup := context.WithCancel(context.Background())
   903  	unblockLookup := make(chan struct{})
   904  
   905  	// Set testHookLookupIP to start a new, concurrent call to LookupIPAddr
   906  	// and cancel the original one, then block until the canceled call has returned
   907  	// (ensuring that it has performed any synchronous cleanup).
   908  	testHookLookupIP = func(
   909  		ctx context.Context,
   910  		fn func(context.Context, string, string) ([]IPAddr, error),
   911  		network string,
   912  		host string,
   913  	) ([]IPAddr, error) {
   914  		select {
   915  		case <-unblockLookup:
   916  		default:
   917  			// Start a concurrent LookupIPAddr for the same host while the caller is
   918  			// still blocked, and sleep a little to give it time to be deduplicated
   919  			// before we cancel (and unblock) the caller.
   920  			// (If the timing doesn't quite work out, we'll end up testing sequential
   921  			// calls instead of concurrent ones, but the test should still pass.)
   922  			t.Logf("starting concurrent LookupIPAddr")
   923  			dnsWaitGroup.Add(1)
   924  			go func() {
   925  				defer dnsWaitGroup.Done()
   926  				_, err := DefaultResolver.LookupIPAddr(context.Background(), host)
   927  				if err != nil {
   928  					t.Error(err)
   929  				}
   930  			}()
   931  			time.Sleep(1 * time.Millisecond)
   932  		}
   933  
   934  		cancelLookup()
   935  		<-unblockLookup
   936  		// If the concurrent lookup above is deduplicated to this one
   937  		// (as we expect to happen most of the time), it is important
   938  		// that the original call does not cancel the shared Context.
   939  		// (See https://go.dev/issue/22724.) Explicitly check for
   940  		// cancellation now, just in case fn itself doesn't notice it.
   941  		if err := ctx.Err(); err != nil {
   942  			t.Logf("testHookLookupIP canceled")
   943  			return nil, err
   944  		}
   945  		t.Logf("testHookLookupIP performing lookup")
   946  		return fn(ctx, network, host)
   947  	}
   948  
   949  	_, err := DefaultResolver.LookupIPAddr(lookupCtx, "google.com")
   950  	if dnsErr, ok := err.(*DNSError); !ok || dnsErr.Err != errCanceled.Error() {
   951  		t.Errorf("unexpected error from canceled, blocked LookupIPAddr: %v", err)
   952  	}
   953  	close(unblockLookup)
   954  }
   955  
   956  // Issue 24330: treat the nil *Resolver like a zero value. Verify nothing
   957  // crashes if nil is used.
   958  func TestNilResolverLookup(t *testing.T) {
   959  	mustHaveExternalNetwork(t)
   960  	var r *Resolver = nil
   961  	ctx := context.Background()
   962  
   963  	// Don't care about the results, just that nothing panics:
   964  	r.LookupAddr(ctx, "8.8.8.8")
   965  	r.LookupCNAME(ctx, "google.com")
   966  	r.LookupHost(ctx, "google.com")
   967  	r.LookupIPAddr(ctx, "google.com")
   968  	r.LookupIP(ctx, "ip", "google.com")
   969  	r.LookupMX(ctx, "gmail.com")
   970  	r.LookupNS(ctx, "google.com")
   971  	r.LookupPort(ctx, "tcp", "smtp")
   972  	r.LookupSRV(ctx, "service", "proto", "name")
   973  	r.LookupTXT(ctx, "gmail.com")
   974  }
   975  
   976  // TestLookupHostCancel verifies that lookup works even after many
   977  // canceled lookups (see golang.org/issue/24178 for details).
   978  func TestLookupHostCancel(t *testing.T) {
   979  	mustHaveExternalNetwork(t)
   980  	testenv.SkipFlakyNet(t)
   981  	t.Parallel() // Executes 600ms worth of sequential sleeps.
   982  
   983  	const (
   984  		google        = "www.google.com"
   985  		invalidDomain = "invalid.invalid" // RFC 2606 reserves .invalid
   986  		n             = 600               // this needs to be larger than threadLimit size
   987  	)
   988  
   989  	_, err := LookupHost(google)
   990  	if err != nil {
   991  		t.Fatal(err)
   992  	}
   993  
   994  	ctx, cancel := context.WithCancel(context.Background())
   995  	cancel()
   996  	for i := 0; i < n; i++ {
   997  		addr, err := DefaultResolver.LookupHost(ctx, invalidDomain)
   998  		if err == nil {
   999  			t.Fatalf("LookupHost(%q): returns %v, but should fail", invalidDomain, addr)
  1000  		}
  1001  
  1002  		// Don't verify what the actual error is.
  1003  		// We know that it must be non-nil because the domain is invalid,
  1004  		// but we don't have any guarantee that LookupHost actually bothers
  1005  		// to check for cancellation on the fast path.
  1006  		// (For example, it could use a local cache to avoid blocking entirely.)
  1007  
  1008  		// The lookup may deduplicate in-flight requests, so give it time to settle
  1009  		// in between.
  1010  		time.Sleep(time.Millisecond * 1)
  1011  	}
  1012  
  1013  	_, err = LookupHost(google)
  1014  	if err != nil {
  1015  		t.Fatal(err)
  1016  	}
  1017  }
  1018  
  1019  type lookupCustomResolver struct {
  1020  	*Resolver
  1021  	mu     sync.RWMutex
  1022  	dialed bool
  1023  }
  1024  
  1025  func (lcr *lookupCustomResolver) dial() func(ctx context.Context, network, address string) (Conn, error) {
  1026  	return func(ctx context.Context, network, address string) (Conn, error) {
  1027  		lcr.mu.Lock()
  1028  		lcr.dialed = true
  1029  		lcr.mu.Unlock()
  1030  		return Dial(network, address)
  1031  	}
  1032  }
  1033  
  1034  // TestConcurrentPreferGoResolversDial tests that multiple resolvers with the
  1035  // PreferGo option used concurrently are all dialed properly.
  1036  func TestConcurrentPreferGoResolversDial(t *testing.T) {
  1037  	// The windows and plan9 implementation of the resolver does not use
  1038  	// the Dial function.
  1039  	switch runtime.GOOS {
  1040  	case "windows", "plan9":
  1041  		t.Skipf("skip on %v", runtime.GOOS)
  1042  	}
  1043  
  1044  	testenv.MustHaveExternalNetwork(t)
  1045  	testenv.SkipFlakyNet(t)
  1046  
  1047  	defer dnsWaitGroup.Wait()
  1048  
  1049  	resolvers := make([]*lookupCustomResolver, 2)
  1050  	for i := range resolvers {
  1051  		cs := lookupCustomResolver{Resolver: &Resolver{PreferGo: true}}
  1052  		cs.Dial = cs.dial()
  1053  		resolvers[i] = &cs
  1054  	}
  1055  
  1056  	var wg sync.WaitGroup
  1057  	wg.Add(len(resolvers))
  1058  	for i, resolver := range resolvers {
  1059  		go func(r *Resolver, index int) {
  1060  			defer wg.Done()
  1061  			_, err := r.LookupIPAddr(context.Background(), "google.com")
  1062  			if err != nil {
  1063  				t.Errorf("lookup failed for resolver %d: %q", index, err)
  1064  			}
  1065  		}(resolver.Resolver, i)
  1066  	}
  1067  	wg.Wait()
  1068  
  1069  	if t.Failed() {
  1070  		t.FailNow()
  1071  	}
  1072  
  1073  	for i, resolver := range resolvers {
  1074  		if !resolver.dialed {
  1075  			t.Errorf("custom resolver %d not dialed during lookup", i)
  1076  		}
  1077  	}
  1078  }
  1079  
  1080  var ipVersionTests = []struct {
  1081  	network string
  1082  	version byte
  1083  }{
  1084  	{"tcp", 0},
  1085  	{"tcp4", '4'},
  1086  	{"tcp6", '6'},
  1087  	{"udp", 0},
  1088  	{"udp4", '4'},
  1089  	{"udp6", '6'},
  1090  	{"ip", 0},
  1091  	{"ip4", '4'},
  1092  	{"ip6", '6'},
  1093  	{"ip7", 0},
  1094  	{"", 0},
  1095  }
  1096  
  1097  func TestIPVersion(t *testing.T) {
  1098  	for _, tt := range ipVersionTests {
  1099  		if version := ipVersion(tt.network); version != tt.version {
  1100  			t.Errorf("Family for: %s. Expected: %s, Got: %s", tt.network,
  1101  				string(tt.version), string(version))
  1102  		}
  1103  	}
  1104  }
  1105  
  1106  // Issue 28600: The context that is used to lookup ips should always
  1107  // preserve the values from the context that was passed into LookupIPAddr.
  1108  func TestLookupIPAddrPreservesContextValues(t *testing.T) {
  1109  	origTestHookLookupIP := testHookLookupIP
  1110  	defer func() { testHookLookupIP = origTestHookLookupIP }()
  1111  
  1112  	keyValues := []struct {
  1113  		key, value any
  1114  	}{
  1115  		{"key-1", 12},
  1116  		{384, "value2"},
  1117  		{new(float64), 137},
  1118  	}
  1119  	ctx := context.Background()
  1120  	for _, kv := range keyValues {
  1121  		ctx = context.WithValue(ctx, kv.key, kv.value)
  1122  	}
  1123  
  1124  	wantIPs := []IPAddr{
  1125  		{IP: IPv4(127, 0, 0, 1)},
  1126  		{IP: IPv6loopback},
  1127  	}
  1128  
  1129  	checkCtxValues := func(ctx_ context.Context, fn func(context.Context, string, string) ([]IPAddr, error), network, host string) ([]IPAddr, error) {
  1130  		for _, kv := range keyValues {
  1131  			g, w := ctx_.Value(kv.key), kv.value
  1132  			if !reflect.DeepEqual(g, w) {
  1133  				t.Errorf("Value lookup:\n\tGot:  %v\n\tWant: %v", g, w)
  1134  			}
  1135  		}
  1136  		return wantIPs, nil
  1137  	}
  1138  	testHookLookupIP = checkCtxValues
  1139  
  1140  	resolvers := []*Resolver{
  1141  		nil,
  1142  		new(Resolver),
  1143  	}
  1144  
  1145  	for i, resolver := range resolvers {
  1146  		gotIPs, err := resolver.LookupIPAddr(ctx, "golang.org")
  1147  		if err != nil {
  1148  			t.Errorf("Resolver #%d: unexpected error: %v", i, err)
  1149  		}
  1150  		if !reflect.DeepEqual(gotIPs, wantIPs) {
  1151  			t.Errorf("#%d: mismatched IPAddr results\n\tGot: %v\n\tWant: %v", i, gotIPs, wantIPs)
  1152  		}
  1153  	}
  1154  }
  1155  
  1156  // Issue 30521: The lookup group should call the resolver for each network.
  1157  func TestLookupIPAddrConcurrentCallsForNetworks(t *testing.T) {
  1158  	origTestHookLookupIP := testHookLookupIP
  1159  	defer func() { testHookLookupIP = origTestHookLookupIP }()
  1160  
  1161  	queries := [][]string{
  1162  		{"udp", "golang.org"},
  1163  		{"udp4", "golang.org"},
  1164  		{"udp6", "golang.org"},
  1165  		{"udp", "golang.org"},
  1166  		{"udp", "golang.org"},
  1167  	}
  1168  	results := map[[2]string][]IPAddr{
  1169  		{"udp", "golang.org"}: {
  1170  			{IP: IPv4(127, 0, 0, 1)},
  1171  			{IP: IPv6loopback},
  1172  		},
  1173  		{"udp4", "golang.org"}: {
  1174  			{IP: IPv4(127, 0, 0, 1)},
  1175  		},
  1176  		{"udp6", "golang.org"}: {
  1177  			{IP: IPv6loopback},
  1178  		},
  1179  	}
  1180  	calls := int32(0)
  1181  	waitCh := make(chan struct{})
  1182  	testHookLookupIP = func(ctx context.Context, fn func(context.Context, string, string) ([]IPAddr, error), network, host string) ([]IPAddr, error) {
  1183  		// We'll block until this is called one time for each different
  1184  		// expected result. This will ensure that the lookup group would wait
  1185  		// for the existing call if it was to be reused.
  1186  		if atomic.AddInt32(&calls, 1) == int32(len(results)) {
  1187  			close(waitCh)
  1188  		}
  1189  		select {
  1190  		case <-waitCh:
  1191  		case <-ctx.Done():
  1192  			return nil, ctx.Err()
  1193  		}
  1194  		return results[[2]string{network, host}], nil
  1195  	}
  1196  
  1197  	ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
  1198  	defer cancel()
  1199  	wg := sync.WaitGroup{}
  1200  	for _, q := range queries {
  1201  		network := q[0]
  1202  		host := q[1]
  1203  		wg.Add(1)
  1204  		go func() {
  1205  			defer wg.Done()
  1206  			gotIPs, err := DefaultResolver.lookupIPAddr(ctx, network, host)
  1207  			if err != nil {
  1208  				t.Errorf("lookupIPAddr(%v, %v): unexpected error: %v", network, host, err)
  1209  			}
  1210  			wantIPs := results[[2]string{network, host}]
  1211  			if !reflect.DeepEqual(gotIPs, wantIPs) {
  1212  				t.Errorf("lookupIPAddr(%v, %v): mismatched IPAddr results\n\tGot: %v\n\tWant: %v", network, host, gotIPs, wantIPs)
  1213  			}
  1214  		}()
  1215  	}
  1216  	wg.Wait()
  1217  }
  1218  
  1219  func TestWithUnexpiredValuesPreserved(t *testing.T) {
  1220  	ctx, cancel := context.WithCancel(context.Background())
  1221  
  1222  	// Insert a value into it.
  1223  	key, value := "key-1", 2
  1224  	ctx = context.WithValue(ctx, key, value)
  1225  
  1226  	// Now use the "values preserving context" like
  1227  	// we would for LookupIPAddr. See Issue 28600.
  1228  	ctx = withUnexpiredValuesPreserved(ctx)
  1229  
  1230  	// Lookup before expiry.
  1231  	if g, w := ctx.Value(key), value; g != w {
  1232  		t.Errorf("Lookup before expiry: Got %v Want %v", g, w)
  1233  	}
  1234  
  1235  	// Cancel the context.
  1236  	cancel()
  1237  
  1238  	// Lookup after expiry should return nil
  1239  	if g := ctx.Value(key); g != nil {
  1240  		t.Errorf("Lookup after expiry: Got %v want nil", g)
  1241  	}
  1242  }
  1243  
  1244  // Issue 31597: don't panic on null byte in name
  1245  func TestLookupNullByte(t *testing.T) {
  1246  	testenv.MustHaveExternalNetwork(t)
  1247  	testenv.SkipFlakyNet(t)
  1248  	LookupHost("foo\x00bar") // check that it doesn't panic; it used to on Windows
  1249  }
  1250  
  1251  func TestResolverLookupIP(t *testing.T) {
  1252  	testenv.MustHaveExternalNetwork(t)
  1253  
  1254  	v4Ok := supportsIPv4() && *testIPv4
  1255  	v6Ok := supportsIPv6() && *testIPv6
  1256  
  1257  	defer dnsWaitGroup.Wait()
  1258  
  1259  	for _, impl := range []struct {
  1260  		name string
  1261  		fn   func() func()
  1262  	}{
  1263  		{"go", forceGoDNS},
  1264  		{"cgo", forceCgoDNS},
  1265  	} {
  1266  		t.Run("implementation: "+impl.name, func(t *testing.T) {
  1267  			fixup := impl.fn()
  1268  			if fixup == nil {
  1269  				t.Skip("not supported")
  1270  			}
  1271  			defer fixup()
  1272  
  1273  			for _, network := range []string{"ip", "ip4", "ip6"} {
  1274  				t.Run("network: "+network, func(t *testing.T) {
  1275  					switch {
  1276  					case network == "ip4" && !v4Ok:
  1277  						t.Skip("IPv4 is not supported")
  1278  					case network == "ip6" && !v6Ok:
  1279  						t.Skip("IPv6 is not supported")
  1280  					}
  1281  
  1282  					// google.com has both A and AAAA records.
  1283  					const host = "google.com"
  1284  					ips, err := DefaultResolver.LookupIP(context.Background(), network, host)
  1285  					if err != nil {
  1286  						testenv.SkipFlakyNet(t)
  1287  						t.Fatalf("DefaultResolver.LookupIP(%q, %q): failed with unexpected error: %v", network, host, err)
  1288  					}
  1289  
  1290  					var v4Addrs []IP
  1291  					var v6Addrs []IP
  1292  					for _, ip := range ips {
  1293  						switch {
  1294  						case ip.To4() != nil:
  1295  							// We need to skip the test below because To16 will
  1296  							// convent an IPv4 address to an IPv4-mapped IPv6
  1297  							// address.
  1298  							v4Addrs = append(v4Addrs, ip)
  1299  						case ip.To16() != nil:
  1300  							v6Addrs = append(v6Addrs, ip)
  1301  						default:
  1302  							t.Fatalf("IP=%q is neither IPv4 nor IPv6", ip)
  1303  						}
  1304  					}
  1305  
  1306  					// Check that we got the expected addresses.
  1307  					if network == "ip4" || network == "ip" && v4Ok {
  1308  						if len(v4Addrs) == 0 {
  1309  							t.Errorf("DefaultResolver.LookupIP(%q, %q): no IPv4 addresses", network, host)
  1310  						}
  1311  					}
  1312  					if network == "ip6" || network == "ip" && v6Ok {
  1313  						if len(v6Addrs) == 0 {
  1314  							t.Errorf("DefaultResolver.LookupIP(%q, %q): no IPv6 addresses", network, host)
  1315  						}
  1316  					}
  1317  
  1318  					// Check that we didn't get any unexpected addresses.
  1319  					if network == "ip6" && len(v4Addrs) > 0 {
  1320  						t.Errorf("DefaultResolver.LookupIP(%q, %q): unexpected IPv4 addresses: %v", network, host, v4Addrs)
  1321  					}
  1322  					if network == "ip4" && len(v6Addrs) > 0 {
  1323  						t.Errorf("DefaultResolver.LookupIP(%q, %q): unexpected IPv6 addresses: %v", network, host, v6Addrs)
  1324  					}
  1325  				})
  1326  			}
  1327  		})
  1328  	}
  1329  }
  1330  
  1331  // A context timeout should still return a DNSError.
  1332  func TestDNSTimeout(t *testing.T) {
  1333  	origTestHookLookupIP := testHookLookupIP
  1334  	defer func() { testHookLookupIP = origTestHookLookupIP }()
  1335  	defer dnsWaitGroup.Wait()
  1336  
  1337  	timeoutHookGo := make(chan bool, 1)
  1338  	timeoutHook := func(ctx context.Context, fn func(context.Context, string, string) ([]IPAddr, error), network, host string) ([]IPAddr, error) {
  1339  		<-timeoutHookGo
  1340  		return nil, context.DeadlineExceeded
  1341  	}
  1342  	testHookLookupIP = timeoutHook
  1343  
  1344  	checkErr := func(err error) {
  1345  		t.Helper()
  1346  		if err == nil {
  1347  			t.Error("expected an error")
  1348  		} else if dnserr, ok := err.(*DNSError); !ok {
  1349  			t.Errorf("got error type %T, want %T", err, (*DNSError)(nil))
  1350  		} else if !dnserr.IsTimeout {
  1351  			t.Errorf("got error %#v, want IsTimeout == true", dnserr)
  1352  		} else if isTimeout := dnserr.Timeout(); !isTimeout {
  1353  			t.Errorf("got err.Timeout() == %t, want true", isTimeout)
  1354  		}
  1355  	}
  1356  
  1357  	// Single lookup.
  1358  	timeoutHookGo <- true
  1359  	_, err := LookupIP("golang.org")
  1360  	checkErr(err)
  1361  
  1362  	// Double lookup.
  1363  	var err1, err2 error
  1364  	var wg sync.WaitGroup
  1365  	wg.Add(2)
  1366  	go func() {
  1367  		defer wg.Done()
  1368  		_, err1 = LookupIP("golang1.org")
  1369  	}()
  1370  	go func() {
  1371  		defer wg.Done()
  1372  		_, err2 = LookupIP("golang1.org")
  1373  	}()
  1374  	close(timeoutHookGo)
  1375  	wg.Wait()
  1376  	checkErr(err1)
  1377  	checkErr(err2)
  1378  
  1379  	// Double lookup with context.
  1380  	timeoutHookGo = make(chan bool)
  1381  	ctx, cancel := context.WithTimeout(context.Background(), time.Nanosecond)
  1382  	wg.Add(2)
  1383  	go func() {
  1384  		defer wg.Done()
  1385  		_, err1 = DefaultResolver.LookupIPAddr(ctx, "golang2.org")
  1386  	}()
  1387  	go func() {
  1388  		defer wg.Done()
  1389  		_, err2 = DefaultResolver.LookupIPAddr(ctx, "golang2.org")
  1390  	}()
  1391  	time.Sleep(10 * time.Nanosecond)
  1392  	close(timeoutHookGo)
  1393  	wg.Wait()
  1394  	checkErr(err1)
  1395  	checkErr(err2)
  1396  	cancel()
  1397  }
  1398  

View as plain text