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

View as plain text