// 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) { tests := []struct { name string c *conf nss *nssConf resolver *Resolver resolv *dnsConfig hostTests []nssHostTest }{ { name: "force", c: &conf{ forceCgoLookupHost: 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", hostLookupFilesDNS}, }, }, { name: "ubuntu_trusty_avahi", c: &conf{}, 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{}, 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{ hasMDNSAllow: true, }, 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", hostLookupCgo}, }, }, { 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", hostLookupCgo}, }, }, { 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}, {"", "myhostname", hostLookupFilesDNS}, // Issue 13623 }, }, { name: "ubuntu14.04.02", c: &conf{}, 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}}, }, // Android should always use cgo. { name: "android", c: &conf{ goos: "android", }, resolv: defaultResolvConf, nss: nssStr(t, ""), hostTests: []nssHostTest{ {"x.com", "myhostname", hostLookupCgo}, }, }, // Issue 24393: make sure "Resolver.PreferGo = true" acts like netgo. { name: "resolver-prefergo", resolver: &Resolver{PreferGo: true}, c: &conf{ goos: "darwin", forceCgoLookupHost: true, // always true for darwin netCgo: true, }, resolv: defaultResolvConf, nss: nssStr(t, ""), hostTests: []nssHostTest{ {"localhost", "myhostname", hostLookupFilesDNS}, }, }, } 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 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() }