| // Copyright 2015 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 ( |
| "io/fs" |
| "os" |
| "testing" |
| "time" |
| ) |
| |
| type nssHostTest struct { |
| host string |
| localhost string |
| want hostLookupOrder |
| } |
| |
| func nssStr(t *testing.T, s string) *nssConf { |
| f, err := os.CreateTemp(t.TempDir(), "nss") |
| if err != nil { |
| t.Fatal(err) |
| } |
| if _, err := f.WriteString(s); err != nil { |
| t.Fatal(err) |
| } |
| if err := f.Close(); err != nil { |
| t.Fatal(err) |
| } |
| return parseNSSConfFile(f.Name()) |
| } |
| |
| // represents a dnsConfig returned by parsing a nonexistent resolv.conf |
| var defaultResolvConf = &dnsConfig{ |
| servers: defaultNS, |
| ndots: 1, |
| timeout: 5, |
| attempts: 2, |
| err: fs.ErrNotExist, |
| } |
| |
| func TestConfHostLookupOrder(t *testing.T) { |
| // These tests are written for a system with cgo available, |
| // without using the netgo tag. |
| if netGoBuildTag { |
| t.Skip("skipping test because net package built with netgo tag") |
| } |
| if !cgoAvailable { |
| t.Skip("skipping test because cgo resolver not available") |
| } |
| |
| tests := []struct { |
| name string |
| c *conf |
| nss *nssConf |
| resolver *Resolver |
| resolv *dnsConfig |
| hostTests []nssHostTest |
| }{ |
| { |
| name: "force", |
| c: &conf{ |
| preferCgo: true, |
| netCgo: true, |
| }, |
| resolv: defaultResolvConf, |
| nss: nssStr(t, "foo: bar"), |
| hostTests: []nssHostTest{ |
| {"foo.local", "myhostname", hostLookupCgo}, |
| {"google.com", "myhostname", hostLookupCgo}, |
| }, |
| }, |
| { |
| name: "netgo_dns_before_files", |
| c: &conf{ |
| netGo: true, |
| }, |
| resolv: defaultResolvConf, |
| nss: nssStr(t, "hosts: dns files"), |
| hostTests: []nssHostTest{ |
| {"x.com", "myhostname", hostLookupDNSFiles}, |
| }, |
| }, |
| { |
| name: "netgo_fallback_on_cgo", |
| c: &conf{ |
| netGo: true, |
| }, |
| resolv: defaultResolvConf, |
| nss: nssStr(t, "hosts: dns files something_custom"), |
| hostTests: []nssHostTest{ |
| {"x.com", "myhostname", hostLookupDNSFiles}, |
| }, |
| }, |
| { |
| name: "ubuntu_trusty_avahi", |
| c: &conf{ |
| mdnsTest: mdnsAssumeDoesNotExist, |
| }, |
| resolv: defaultResolvConf, |
| nss: nssStr(t, "hosts: files mdns4_minimal [NOTFOUND=return] dns mdns4"), |
| hostTests: []nssHostTest{ |
| {"foo.local", "myhostname", hostLookupCgo}, |
| {"foo.local.", "myhostname", hostLookupCgo}, |
| {"foo.LOCAL", "myhostname", hostLookupCgo}, |
| {"foo.LOCAL.", "myhostname", hostLookupCgo}, |
| {"google.com", "myhostname", hostLookupFilesDNS}, |
| }, |
| }, |
| { |
| name: "freebsdlinux_no_resolv_conf", |
| c: &conf{ |
| goos: "freebsd", |
| }, |
| resolv: defaultResolvConf, |
| nss: nssStr(t, "foo: bar"), |
| hostTests: []nssHostTest{{"google.com", "myhostname", hostLookupFilesDNS}}, |
| }, |
| // On OpenBSD, no resolv.conf means no DNS. |
| { |
| name: "openbsd_no_resolv_conf", |
| c: &conf{ |
| goos: "openbsd", |
| }, |
| resolv: defaultResolvConf, |
| hostTests: []nssHostTest{{"google.com", "myhostname", hostLookupFiles}}, |
| }, |
| { |
| name: "solaris_no_nsswitch", |
| c: &conf{ |
| goos: "solaris", |
| }, |
| resolv: defaultResolvConf, |
| nss: &nssConf{err: fs.ErrNotExist}, |
| hostTests: []nssHostTest{{"google.com", "myhostname", hostLookupCgo}}, |
| }, |
| { |
| name: "openbsd_lookup_bind_file", |
| c: &conf{ |
| goos: "openbsd", |
| }, |
| resolv: &dnsConfig{lookup: []string{"bind", "file"}}, |
| hostTests: []nssHostTest{ |
| {"google.com", "myhostname", hostLookupDNSFiles}, |
| {"foo.local", "myhostname", hostLookupDNSFiles}, |
| }, |
| }, |
| { |
| name: "openbsd_lookup_file_bind", |
| c: &conf{ |
| goos: "openbsd", |
| }, |
| resolv: &dnsConfig{lookup: []string{"file", "bind"}}, |
| hostTests: []nssHostTest{{"google.com", "myhostname", hostLookupFilesDNS}}, |
| }, |
| { |
| name: "openbsd_lookup_bind", |
| c: &conf{ |
| goos: "openbsd", |
| }, |
| resolv: &dnsConfig{lookup: []string{"bind"}}, |
| hostTests: []nssHostTest{{"google.com", "myhostname", hostLookupDNS}}, |
| }, |
| { |
| name: "openbsd_lookup_file", |
| c: &conf{ |
| goos: "openbsd", |
| }, |
| resolv: &dnsConfig{lookup: []string{"file"}}, |
| hostTests: []nssHostTest{{"google.com", "myhostname", hostLookupFiles}}, |
| }, |
| { |
| name: "openbsd_lookup_yp", |
| c: &conf{ |
| goos: "openbsd", |
| }, |
| resolv: &dnsConfig{lookup: []string{"file", "bind", "yp"}}, |
| hostTests: []nssHostTest{{"google.com", "myhostname", hostLookupCgo}}, |
| }, |
| { |
| name: "openbsd_lookup_two", |
| c: &conf{ |
| goos: "openbsd", |
| }, |
| resolv: &dnsConfig{lookup: []string{"file", "foo"}}, |
| hostTests: []nssHostTest{{"google.com", "myhostname", hostLookupCgo}}, |
| }, |
| { |
| name: "openbsd_lookup_empty", |
| c: &conf{ |
| goos: "openbsd", |
| }, |
| resolv: &dnsConfig{lookup: nil}, |
| hostTests: []nssHostTest{{"google.com", "myhostname", hostLookupDNSFiles}}, |
| }, |
| { |
| name: "linux_no_nsswitch.conf", |
| c: &conf{ |
| goos: "linux", |
| }, |
| resolv: defaultResolvConf, |
| nss: &nssConf{err: fs.ErrNotExist}, |
| hostTests: []nssHostTest{{"google.com", "myhostname", hostLookupFilesDNS}}, |
| }, |
| { |
| name: "linux_empty_nsswitch.conf", |
| c: &conf{ |
| goos: "linux", |
| }, |
| resolv: defaultResolvConf, |
| nss: nssStr(t, ""), |
| hostTests: []nssHostTest{{"google.com", "myhostname", hostLookupFilesDNS}}, |
| }, |
| { |
| name: "files_mdns_dns", |
| c: &conf{ |
| mdnsTest: mdnsAssumeDoesNotExist, |
| }, |
| resolv: defaultResolvConf, |
| nss: nssStr(t, "hosts: files mdns dns"), |
| hostTests: []nssHostTest{ |
| {"x.com", "myhostname", hostLookupFilesDNS}, |
| {"x.local", "myhostname", hostLookupCgo}, |
| }, |
| }, |
| { |
| name: "dns_special_hostnames", |
| c: &conf{}, |
| resolv: defaultResolvConf, |
| nss: nssStr(t, "hosts: dns"), |
| hostTests: []nssHostTest{ |
| {"x.com", "myhostname", hostLookupDNS}, |
| {"x\\.com", "myhostname", hostLookupCgo}, // punt on weird glibc escape |
| {"foo.com%en0", "myhostname", hostLookupCgo}, // and IPv6 zones |
| }, |
| }, |
| { |
| name: "mdns_allow", |
| c: &conf{ |
| mdnsTest: mdnsAssumeExists, |
| }, |
| resolv: defaultResolvConf, |
| nss: nssStr(t, "hosts: files mdns dns"), |
| hostTests: []nssHostTest{ |
| {"x.com", "myhostname", hostLookupCgo}, |
| {"x.local", "myhostname", hostLookupCgo}, |
| }, |
| }, |
| { |
| name: "files_dns", |
| c: &conf{}, |
| resolv: defaultResolvConf, |
| nss: nssStr(t, "hosts: files dns"), |
| hostTests: []nssHostTest{ |
| {"x.com", "myhostname", hostLookupFilesDNS}, |
| {"x", "myhostname", hostLookupFilesDNS}, |
| {"x.local", "myhostname", hostLookupFilesDNS}, |
| }, |
| }, |
| { |
| name: "dns_files", |
| c: &conf{}, |
| resolv: defaultResolvConf, |
| nss: nssStr(t, "hosts: dns files"), |
| hostTests: []nssHostTest{ |
| {"x.com", "myhostname", hostLookupDNSFiles}, |
| {"x", "myhostname", hostLookupDNSFiles}, |
| {"x.local", "myhostname", hostLookupDNSFiles}, |
| }, |
| }, |
| { |
| name: "something_custom", |
| c: &conf{}, |
| resolv: defaultResolvConf, |
| nss: nssStr(t, "hosts: dns files something_custom"), |
| hostTests: []nssHostTest{ |
| {"x.com", "myhostname", hostLookupCgo}, |
| }, |
| }, |
| { |
| name: "myhostname", |
| c: &conf{}, |
| resolv: defaultResolvConf, |
| nss: nssStr(t, "hosts: files dns myhostname"), |
| hostTests: []nssHostTest{ |
| {"x.com", "myhostname", hostLookupFilesDNS}, |
| {"myhostname", "myhostname", hostLookupCgo}, |
| {"myHostname", "myhostname", hostLookupCgo}, |
| {"myhostname.dot", "myhostname.dot", hostLookupCgo}, |
| {"myHostname.dot", "myhostname.dot", hostLookupCgo}, |
| {"_gateway", "myhostname", hostLookupCgo}, |
| {"_Gateway", "myhostname", hostLookupCgo}, |
| {"_outbound", "myhostname", hostLookupCgo}, |
| {"_Outbound", "myhostname", hostLookupCgo}, |
| {"localhost", "myhostname", hostLookupCgo}, |
| {"Localhost", "myhostname", hostLookupCgo}, |
| {"anything.localhost", "myhostname", hostLookupCgo}, |
| {"Anything.localhost", "myhostname", hostLookupCgo}, |
| {"localhost.localdomain", "myhostname", hostLookupCgo}, |
| {"Localhost.Localdomain", "myhostname", hostLookupCgo}, |
| {"anything.localhost.localdomain", "myhostname", hostLookupCgo}, |
| {"Anything.Localhost.Localdomain", "myhostname", hostLookupCgo}, |
| {"somehostname", "myhostname", hostLookupFilesDNS}, |
| }, |
| }, |
| { |
| name: "ubuntu14.04.02", |
| c: &conf{ |
| mdnsTest: mdnsAssumeDoesNotExist, |
| }, |
| resolv: defaultResolvConf, |
| nss: nssStr(t, "hosts: files myhostname mdns4_minimal [NOTFOUND=return] dns mdns4"), |
| hostTests: []nssHostTest{ |
| {"x.com", "myhostname", hostLookupFilesDNS}, |
| {"somehostname", "myhostname", hostLookupFilesDNS}, |
| {"myhostname", "myhostname", hostLookupCgo}, |
| }, |
| }, |
| // Debian Squeeze is just "dns,files", but lists all |
| // the default criteria for dns, but then has a |
| // non-standard but redundant notfound=return for the |
| // files. |
| { |
| name: "debian_squeeze", |
| c: &conf{}, |
| resolv: defaultResolvConf, |
| nss: nssStr(t, "hosts: dns [success=return notfound=continue unavail=continue tryagain=continue] files [notfound=return]"), |
| hostTests: []nssHostTest{ |
| {"x.com", "myhostname", hostLookupDNSFiles}, |
| {"somehostname", "myhostname", hostLookupDNSFiles}, |
| }, |
| }, |
| { |
| name: "resolv.conf-unknown", |
| c: &conf{}, |
| resolv: &dnsConfig{servers: defaultNS, ndots: 1, timeout: 5, attempts: 2, unknownOpt: true}, |
| nss: nssStr(t, "foo: bar"), |
| hostTests: []nssHostTest{{"google.com", "myhostname", hostLookupCgo}}, |
| }, |
| // Issue 24393: make sure "Resolver.PreferGo = true" acts like netgo. |
| { |
| name: "resolver-prefergo", |
| resolver: &Resolver{PreferGo: true}, |
| c: &conf{ |
| preferCgo: true, |
| netCgo: true, |
| }, |
| resolv: defaultResolvConf, |
| nss: nssStr(t, ""), |
| hostTests: []nssHostTest{ |
| {"localhost", "myhostname", hostLookupFilesDNS}, |
| }, |
| }, |
| { |
| name: "unknown-source", |
| resolver: &Resolver{PreferGo: true}, |
| c: &conf{}, |
| resolv: defaultResolvConf, |
| nss: nssStr(t, "hosts: resolve files"), |
| hostTests: []nssHostTest{ |
| {"x.com", "myhostname", hostLookupDNSFiles}, |
| }, |
| }, |
| { |
| name: "dns-among-unknown-sources", |
| resolver: &Resolver{PreferGo: true}, |
| c: &conf{}, |
| resolv: defaultResolvConf, |
| nss: nssStr(t, "hosts: mymachines files dns"), |
| hostTests: []nssHostTest{ |
| {"x.com", "myhostname", hostLookupFilesDNS}, |
| }, |
| }, |
| { |
| name: "dns-among-unknown-sources-2", |
| resolver: &Resolver{PreferGo: true}, |
| c: &conf{}, |
| resolv: defaultResolvConf, |
| nss: nssStr(t, "hosts: dns mymachines files"), |
| hostTests: []nssHostTest{ |
| {"x.com", "myhostname", hostLookupDNSFiles}, |
| }, |
| }, |
| } |
| |
| origGetHostname := getHostname |
| defer func() { getHostname = origGetHostname }() |
| defer setSystemNSS(getSystemNSS(), 0) |
| conf, err := newResolvConfTest() |
| if err != nil { |
| t.Fatal(err) |
| } |
| defer conf.teardown() |
| |
| for _, tt := range tests { |
| if !conf.forceUpdateConf(tt.resolv, time.Now().Add(time.Hour)) { |
| t.Errorf("%s: failed to change resolv config", tt.name) |
| } |
| for _, ht := range tt.hostTests { |
| getHostname = func() (string, error) { return ht.localhost, nil } |
| setSystemNSS(tt.nss, time.Hour) |
| |
| gotOrder, _ := tt.c.hostLookupOrder(tt.resolver, ht.host) |
| if gotOrder != ht.want { |
| t.Errorf("%s: hostLookupOrder(%q) = %v; want %v", tt.name, ht.host, gotOrder, ht.want) |
| } |
| } |
| } |
| } |
| |
| func TestAddrLookupOrder(t *testing.T) { |
| // This test is written for a system with cgo available, |
| // without using the netgo tag. |
| if netGoBuildTag { |
| t.Skip("skipping test because net package built with netgo tag") |
| } |
| if !cgoAvailable { |
| t.Skip("skipping test because cgo resolver not available") |
| } |
| |
| defer setSystemNSS(getSystemNSS(), 0) |
| c, err := newResolvConfTest() |
| if err != nil { |
| t.Fatal(err) |
| } |
| defer c.teardown() |
| |
| if !c.forceUpdateConf(defaultResolvConf, time.Now().Add(time.Hour)) { |
| t.Fatal("failed to change resolv config") |
| } |
| |
| setSystemNSS(nssStr(t, "hosts: files myhostname dns"), time.Hour) |
| cnf := &conf{} |
| order, _ := cnf.addrLookupOrder(nil, "192.0.2.1") |
| if order != hostLookupCgo { |
| t.Errorf("addrLookupOrder returned: %v, want cgo", order) |
| } |
| |
| setSystemNSS(nssStr(t, "hosts: files mdns4 dns"), time.Hour) |
| order, _ = cnf.addrLookupOrder(nil, "192.0.2.1") |
| if order != hostLookupCgo { |
| t.Errorf("addrLookupOrder returned: %v, want cgo", order) |
| } |
| |
| } |
| |
| func setSystemNSS(nss *nssConf, addDur time.Duration) { |
| nssConfig.mu.Lock() |
| nssConfig.nssConf = nss |
| nssConfig.mu.Unlock() |
| nssConfig.acquireSema() |
| nssConfig.lastChecked = time.Now().Add(addDur) |
| nssConfig.releaseSema() |
| } |
| |
| func TestSystemConf(t *testing.T) { |
| systemConf() |
| } |