| // Copyright 2013 The Go Authors. All rights reserved. | |
| // Use of this source code is governed by a BSD-style | |
| // license that can be found in the LICENSE file. | |
| //go:build unix | |
| package net | |
| import ( | |
| "errors" | |
| "io/fs" | |
| "os" | |
| "reflect" | |
| "slices" | |
| "strings" | |
| "testing" | |
| "time" | |
| ) | |
| var dnsReadConfigTests = []struct { | |
| name string | |
| want *dnsConfig | |
| }{ | |
| { | |
| name: "testdata/resolv.conf", | |
| want: &dnsConfig{ | |
| servers: []string{"8.8.8.8:53", "[2001:4860:4860::8888]:53", "[fe80::1%lo0]:53"}, | |
| search: []string{"localdomain."}, | |
| ndots: 5, | |
| timeout: 10 * time.Second, | |
| attempts: 3, | |
| rotate: true, | |
| unknownOpt: true, // the "options attempts 3" line | |
| }, | |
| }, | |
| { | |
| name: "testdata/domain-resolv.conf", | |
| want: &dnsConfig{ | |
| servers: []string{"8.8.8.8:53"}, | |
| search: []string{"localdomain."}, | |
| ndots: 1, | |
| timeout: 5 * time.Second, | |
| attempts: 2, | |
| }, | |
| }, | |
| { | |
| name: "testdata/search-resolv.conf", | |
| want: &dnsConfig{ | |
| servers: []string{"8.8.8.8:53"}, | |
| search: []string{"test.", "invalid."}, | |
| ndots: 1, | |
| timeout: 5 * time.Second, | |
| attempts: 2, | |
| }, | |
| }, | |
| { | |
| name: "testdata/search-single-dot-resolv.conf", | |
| want: &dnsConfig{ | |
| servers: []string{"8.8.8.8:53"}, | |
| search: []string{}, | |
| ndots: 1, | |
| timeout: 5 * time.Second, | |
| attempts: 2, | |
| }, | |
| }, | |
| { | |
| name: "testdata/empty-resolv.conf", | |
| want: &dnsConfig{ | |
| servers: defaultNS, | |
| ndots: 1, | |
| timeout: 5 * time.Second, | |
| attempts: 2, | |
| search: []string{"domain.local."}, | |
| }, | |
| }, | |
| { | |
| name: "testdata/invalid-ndots-resolv.conf", | |
| want: &dnsConfig{ | |
| servers: defaultNS, | |
| ndots: 0, | |
| timeout: 5 * time.Second, | |
| attempts: 2, | |
| search: []string{"domain.local."}, | |
| }, | |
| }, | |
| { | |
| name: "testdata/large-ndots-resolv.conf", | |
| want: &dnsConfig{ | |
| servers: defaultNS, | |
| ndots: 15, | |
| timeout: 5 * time.Second, | |
| attempts: 2, | |
| search: []string{"domain.local."}, | |
| }, | |
| }, | |
| { | |
| name: "testdata/negative-ndots-resolv.conf", | |
| want: &dnsConfig{ | |
| servers: defaultNS, | |
| ndots: 0, | |
| timeout: 5 * time.Second, | |
| attempts: 2, | |
| search: []string{"domain.local."}, | |
| }, | |
| }, | |
| { | |
| name: "testdata/openbsd-resolv.conf", | |
| want: &dnsConfig{ | |
| ndots: 1, | |
| timeout: 5 * time.Second, | |
| attempts: 2, | |
| lookup: []string{"file", "bind"}, | |
| servers: []string{"169.254.169.254:53", "10.240.0.1:53"}, | |
| search: []string{"c.symbolic-datum-552.internal."}, | |
| }, | |
| }, | |
| { | |
| name: "testdata/single-request-resolv.conf", | |
| want: &dnsConfig{ | |
| servers: defaultNS, | |
| ndots: 1, | |
| singleRequest: true, | |
| timeout: 5 * time.Second, | |
| attempts: 2, | |
| search: []string{"domain.local."}, | |
| }, | |
| }, | |
| { | |
| name: "testdata/single-request-reopen-resolv.conf", | |
| want: &dnsConfig{ | |
| servers: defaultNS, | |
| ndots: 1, | |
| singleRequest: true, | |
| timeout: 5 * time.Second, | |
| attempts: 2, | |
| search: []string{"domain.local."}, | |
| }, | |
| }, | |
| { | |
| name: "testdata/linux-use-vc-resolv.conf", | |
| want: &dnsConfig{ | |
| servers: defaultNS, | |
| ndots: 1, | |
| useTCP: true, | |
| timeout: 5 * time.Second, | |
| attempts: 2, | |
| search: []string{"domain.local."}, | |
| }, | |
| }, | |
| { | |
| name: "testdata/freebsd-usevc-resolv.conf", | |
| want: &dnsConfig{ | |
| servers: defaultNS, | |
| ndots: 1, | |
| useTCP: true, | |
| timeout: 5 * time.Second, | |
| attempts: 2, | |
| search: []string{"domain.local."}, | |
| }, | |
| }, | |
| { | |
| name: "testdata/openbsd-tcp-resolv.conf", | |
| want: &dnsConfig{ | |
| servers: defaultNS, | |
| ndots: 1, | |
| useTCP: true, | |
| timeout: 5 * time.Second, | |
| attempts: 2, | |
| search: []string{"domain.local."}, | |
| }, | |
| }, | |
| } | |
| func TestDNSReadConfig(t *testing.T) { | |
| origGetHostname := getHostname | |
| defer func() { getHostname = origGetHostname }() | |
| getHostname = func() (string, error) { return "host.domain.local", nil } | |
| for _, tt := range dnsReadConfigTests { | |
| want := *tt.want | |
| if len(want.search) == 0 { | |
| want.search = dnsDefaultSearch() | |
| } | |
| conf := dnsReadConfig(tt.name) | |
| if conf.err != nil { | |
| t.Fatal(conf.err) | |
| } | |
| conf.mtime = time.Time{} | |
| if !reflect.DeepEqual(conf, &want) { | |
| t.Errorf("%s:\ngot: %+v\nwant: %+v", tt.name, conf, want) | |
| } | |
| } | |
| } | |
| func TestDNSReadMissingFile(t *testing.T) { | |
| origGetHostname := getHostname | |
| defer func() { getHostname = origGetHostname }() | |
| getHostname = func() (string, error) { return "host.domain.local", nil } | |
| conf := dnsReadConfig("a-nonexistent-file") | |
| if !os.IsNotExist(conf.err) { | |
| t.Errorf("missing resolv.conf:\ngot: %v\nwant: %v", conf.err, fs.ErrNotExist) | |
| } | |
| conf.err = nil | |
| want := &dnsConfig{ | |
| servers: defaultNS, | |
| ndots: 1, | |
| timeout: 5 * time.Second, | |
| attempts: 2, | |
| search: []string{"domain.local."}, | |
| } | |
| if !reflect.DeepEqual(conf, want) { | |
| t.Errorf("missing resolv.conf:\ngot: %+v\nwant: %+v", conf, want) | |
| } | |
| } | |
| var dnsDefaultSearchTests = []struct { | |
| name string | |
| err error | |
| want []string | |
| }{ | |
| { | |
| name: "host.long.domain.local", | |
| want: []string{"long.domain.local."}, | |
| }, | |
| { | |
| name: "host.local", | |
| want: []string{"local."}, | |
| }, | |
| { | |
| name: "host", | |
| want: nil, | |
| }, | |
| { | |
| name: "host.domain.local", | |
| err: errors.New("errored"), | |
| want: nil, | |
| }, | |
| { | |
| // ensures we don't return []string{""} | |
| // which causes duplicate lookups | |
| name: "foo.", | |
| want: nil, | |
| }, | |
| } | |
| func TestDNSDefaultSearch(t *testing.T) { | |
| origGetHostname := getHostname | |
| defer func() { getHostname = origGetHostname }() | |
| for _, tt := range dnsDefaultSearchTests { | |
| getHostname = func() (string, error) { return tt.name, tt.err } | |
| got := dnsDefaultSearch() | |
| if !slices.Equal(got, tt.want) { | |
| t.Errorf("dnsDefaultSearch with hostname %q and error %+v = %q, wanted %q", tt.name, tt.err, got, tt.want) | |
| } | |
| } | |
| } | |
| func TestDNSNameLength(t *testing.T) { | |
| origGetHostname := getHostname | |
| defer func() { getHostname = origGetHostname }() | |
| getHostname = func() (string, error) { return "host.domain.local", nil } | |
| var char63 = "" | |
| for i := 0; i < 63; i++ { | |
| char63 += "a" | |
| } | |
| longDomain := strings.Repeat(char63+".", 5) + "example" | |
| for _, tt := range dnsReadConfigTests { | |
| conf := dnsReadConfig(tt.name) | |
| if conf.err != nil { | |
| t.Fatal(conf.err) | |
| } | |
| suffixList := tt.want.search | |
| if len(suffixList) == 0 { | |
| suffixList = dnsDefaultSearch() | |
| } | |
| var shortestSuffix int | |
| for _, suffix := range suffixList { | |
| if shortestSuffix == 0 || len(suffix) < shortestSuffix { | |
| shortestSuffix = len(suffix) | |
| } | |
| } | |
| // Test a name that will be maximally long when prefixing the shortest | |
| // suffix (accounting for the intervening dot). | |
| longName := longDomain[len(longDomain)-254+1+shortestSuffix:] | |
| if longName[0] == '.' || longName[1] == '.' { | |
| longName = "aa." + longName[3:] | |
| } | |
| for _, fqdn := range conf.nameList(longName) { | |
| if len(fqdn) > 254 { | |
| t.Errorf("got %d; want less than or equal to 254", len(fqdn)) | |
| } | |
| } | |
| // Now test a name that's too long for suffixing. | |
| unsuffixable := "a." + longName[1:] | |
| unsuffixableResults := conf.nameList(unsuffixable) | |
| if len(unsuffixableResults) != 1 { | |
| t.Errorf("suffixed names %v; want []", unsuffixableResults[1:]) | |
| } | |
| // Now test a name that's too long for DNS. | |
| tooLong := "a." + longDomain | |
| tooLongResults := conf.nameList(tooLong) | |
| if tooLongResults != nil { | |
| t.Errorf("suffixed names %v; want nil", tooLongResults) | |
| } | |
| } | |
| } |