...

Source file src/pkg/net/conf.go

     1	// Copyright 2015 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	// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris
     6	
     7	package net
     8	
     9	import (
    10		"internal/bytealg"
    11		"os"
    12		"runtime"
    13		"sync"
    14		"syscall"
    15	)
    16	
    17	// conf represents a system's network configuration.
    18	type conf struct {
    19		// forceCgoLookupHost forces CGO to always be used, if available.
    20		forceCgoLookupHost bool
    21	
    22		netGo  bool // go DNS resolution forced
    23		netCgo bool // cgo DNS resolution forced
    24	
    25		// machine has an /etc/mdns.allow file
    26		hasMDNSAllow bool
    27	
    28		goos          string // the runtime.GOOS, to ease testing
    29		dnsDebugLevel int
    30	
    31		nss    *nssConf
    32		resolv *dnsConfig
    33	}
    34	
    35	var (
    36		confOnce sync.Once // guards init of confVal via initConfVal
    37		confVal  = &conf{goos: runtime.GOOS}
    38	)
    39	
    40	// systemConf returns the machine's network configuration.
    41	func systemConf() *conf {
    42		confOnce.Do(initConfVal)
    43		return confVal
    44	}
    45	
    46	func initConfVal() {
    47		dnsMode, debugLevel := goDebugNetDNS()
    48		confVal.dnsDebugLevel = debugLevel
    49		confVal.netGo = netGo || dnsMode == "go"
    50		confVal.netCgo = netCgo || dnsMode == "cgo"
    51	
    52		if confVal.dnsDebugLevel > 0 {
    53			defer func() {
    54				switch {
    55				case confVal.netGo:
    56					if netGo {
    57						println("go package net: built with netgo build tag; using Go's DNS resolver")
    58					} else {
    59						println("go package net: GODEBUG setting forcing use of Go's resolver")
    60					}
    61				case confVal.forceCgoLookupHost:
    62					println("go package net: using cgo DNS resolver")
    63				default:
    64					println("go package net: dynamic selection of DNS resolver")
    65				}
    66			}()
    67		}
    68	
    69		// Darwin pops up annoying dialog boxes if programs try to do
    70		// their own DNS requests. So always use cgo instead, which
    71		// avoids that.
    72		if runtime.GOOS == "darwin" {
    73			confVal.forceCgoLookupHost = true
    74			return
    75		}
    76	
    77		// If any environment-specified resolver options are specified,
    78		// force cgo. Note that LOCALDOMAIN can change behavior merely
    79		// by being specified with the empty string.
    80		_, localDomainDefined := syscall.Getenv("LOCALDOMAIN")
    81		if os.Getenv("RES_OPTIONS") != "" ||
    82			os.Getenv("HOSTALIASES") != "" ||
    83			confVal.netCgo ||
    84			localDomainDefined {
    85			confVal.forceCgoLookupHost = true
    86			return
    87		}
    88	
    89		// OpenBSD apparently lets you override the location of resolv.conf
    90		// with ASR_CONFIG. If we notice that, defer to libc.
    91		if runtime.GOOS == "openbsd" && os.Getenv("ASR_CONFIG") != "" {
    92			confVal.forceCgoLookupHost = true
    93			return
    94		}
    95	
    96		if runtime.GOOS != "openbsd" {
    97			confVal.nss = parseNSSConfFile("/etc/nsswitch.conf")
    98		}
    99	
   100		confVal.resolv = dnsReadConfig("/etc/resolv.conf")
   101		if confVal.resolv.err != nil && !os.IsNotExist(confVal.resolv.err) &&
   102			!os.IsPermission(confVal.resolv.err) {
   103			// If we can't read the resolv.conf file, assume it
   104			// had something important in it and defer to cgo.
   105			// libc's resolver might then fail too, but at least
   106			// it wasn't our fault.
   107			confVal.forceCgoLookupHost = true
   108		}
   109	
   110		if _, err := os.Stat("/etc/mdns.allow"); err == nil {
   111			confVal.hasMDNSAllow = true
   112		}
   113	}
   114	
   115	// canUseCgo reports whether calling cgo functions is allowed
   116	// for non-hostname lookups.
   117	func (c *conf) canUseCgo() bool {
   118		return c.hostLookupOrder(nil, "") == hostLookupCgo
   119	}
   120	
   121	// hostLookupOrder determines which strategy to use to resolve hostname.
   122	// The provided Resolver is optional. nil means to not consider its options.
   123	func (c *conf) hostLookupOrder(r *Resolver, hostname string) (ret hostLookupOrder) {
   124		if c.dnsDebugLevel > 1 {
   125			defer func() {
   126				print("go package net: hostLookupOrder(", hostname, ") = ", ret.String(), "\n")
   127			}()
   128		}
   129		fallbackOrder := hostLookupCgo
   130		if c.netGo || r.preferGo() {
   131			fallbackOrder = hostLookupFilesDNS
   132		}
   133		if c.forceCgoLookupHost || c.resolv.unknownOpt || c.goos == "android" {
   134			return fallbackOrder
   135		}
   136		if bytealg.IndexByteString(hostname, '\\') != -1 || bytealg.IndexByteString(hostname, '%') != -1 {
   137			// Don't deal with special form hostnames with backslashes
   138			// or '%'.
   139			return fallbackOrder
   140		}
   141	
   142		// OpenBSD is unique and doesn't use nsswitch.conf.
   143		// It also doesn't support mDNS.
   144		if c.goos == "openbsd" {
   145			// OpenBSD's resolv.conf manpage says that a non-existent
   146			// resolv.conf means "lookup" defaults to only "files",
   147			// without DNS lookups.
   148			if os.IsNotExist(c.resolv.err) {
   149				return hostLookupFiles
   150			}
   151			lookup := c.resolv.lookup
   152			if len(lookup) == 0 {
   153				// https://www.openbsd.org/cgi-bin/man.cgi/OpenBSD-current/man5/resolv.conf.5
   154				// "If the lookup keyword is not used in the
   155				// system's resolv.conf file then the assumed
   156				// order is 'bind file'"
   157				return hostLookupDNSFiles
   158			}
   159			if len(lookup) < 1 || len(lookup) > 2 {
   160				return fallbackOrder
   161			}
   162			switch lookup[0] {
   163			case "bind":
   164				if len(lookup) == 2 {
   165					if lookup[1] == "file" {
   166						return hostLookupDNSFiles
   167					}
   168					return fallbackOrder
   169				}
   170				return hostLookupDNS
   171			case "file":
   172				if len(lookup) == 2 {
   173					if lookup[1] == "bind" {
   174						return hostLookupFilesDNS
   175					}
   176					return fallbackOrder
   177				}
   178				return hostLookupFiles
   179			default:
   180				return fallbackOrder
   181			}
   182		}
   183	
   184		// Canonicalize the hostname by removing any trailing dot.
   185		if stringsHasSuffix(hostname, ".") {
   186			hostname = hostname[:len(hostname)-1]
   187		}
   188		if stringsHasSuffixFold(hostname, ".local") {
   189			// Per RFC 6762, the ".local" TLD is special. And
   190			// because Go's native resolver doesn't do mDNS or
   191			// similar local resolution mechanisms, assume that
   192			// libc might (via Avahi, etc) and use cgo.
   193			return fallbackOrder
   194		}
   195	
   196		nss := c.nss
   197		srcs := nss.sources["hosts"]
   198		// If /etc/nsswitch.conf doesn't exist or doesn't specify any
   199		// sources for "hosts", assume Go's DNS will work fine.
   200		if os.IsNotExist(nss.err) || (nss.err == nil && len(srcs) == 0) {
   201			if c.goos == "solaris" {
   202				// illumos defaults to "nis [NOTFOUND=return] files"
   203				return fallbackOrder
   204			}
   205			if c.goos == "linux" {
   206				// glibc says the default is "dns [!UNAVAIL=return] files"
   207				// https://www.gnu.org/software/libc/manual/html_node/Notes-on-NSS-Configuration-File.html.
   208				return hostLookupDNSFiles
   209			}
   210			return hostLookupFilesDNS
   211		}
   212		if nss.err != nil {
   213			// We failed to parse or open nsswitch.conf, so
   214			// conservatively assume we should use cgo if it's
   215			// available.
   216			return fallbackOrder
   217		}
   218	
   219		var mdnsSource, filesSource, dnsSource bool
   220		var first string
   221		for _, src := range srcs {
   222			if src.source == "myhostname" {
   223				if isLocalhost(hostname) || isGateway(hostname) {
   224					return fallbackOrder
   225				}
   226				hn, err := getHostname()
   227				if err != nil || stringsEqualFold(hostname, hn) {
   228					return fallbackOrder
   229				}
   230				continue
   231			}
   232			if src.source == "files" || src.source == "dns" {
   233				if !src.standardCriteria() {
   234					return fallbackOrder // non-standard; let libc deal with it.
   235				}
   236				if src.source == "files" {
   237					filesSource = true
   238				} else if src.source == "dns" {
   239					dnsSource = true
   240				}
   241				if first == "" {
   242					first = src.source
   243				}
   244				continue
   245			}
   246			if stringsHasPrefix(src.source, "mdns") {
   247				// e.g. "mdns4", "mdns4_minimal"
   248				// We already returned true before if it was *.local.
   249				// libc wouldn't have found a hit on this anyway.
   250				mdnsSource = true
   251				continue
   252			}
   253			// Some source we don't know how to deal with.
   254			return fallbackOrder
   255		}
   256	
   257		// We don't parse mdns.allow files. They're rare. If one
   258		// exists, it might list other TLDs (besides .local) or even
   259		// '*', so just let libc deal with it.
   260		if mdnsSource && c.hasMDNSAllow {
   261			return fallbackOrder
   262		}
   263	
   264		// Cases where Go can handle it without cgo and C thread
   265		// overhead.
   266		switch {
   267		case filesSource && dnsSource:
   268			if first == "files" {
   269				return hostLookupFilesDNS
   270			} else {
   271				return hostLookupDNSFiles
   272			}
   273		case filesSource:
   274			return hostLookupFiles
   275		case dnsSource:
   276			return hostLookupDNS
   277		}
   278	
   279		// Something weird. Let libc deal with it.
   280		return fallbackOrder
   281	}
   282	
   283	// goDebugNetDNS parses the value of the GODEBUG "netdns" value.
   284	// The netdns value can be of the form:
   285	//    1       // debug level 1
   286	//    2       // debug level 2
   287	//    cgo     // use cgo for DNS lookups
   288	//    go      // use go for DNS lookups
   289	//    cgo+1   // use cgo for DNS lookups + debug level 1
   290	//    1+cgo   // same
   291	//    cgo+2   // same, but debug level 2
   292	// etc.
   293	func goDebugNetDNS() (dnsMode string, debugLevel int) {
   294		goDebug := goDebugString("netdns")
   295		parsePart := func(s string) {
   296			if s == "" {
   297				return
   298			}
   299			if '0' <= s[0] && s[0] <= '9' {
   300				debugLevel, _, _ = dtoi(s)
   301			} else {
   302				dnsMode = s
   303			}
   304		}
   305		if i := bytealg.IndexByteString(goDebug, '+'); i != -1 {
   306			parsePart(goDebug[:i])
   307			parsePart(goDebug[i+1:])
   308			return
   309		}
   310		parsePart(goDebug)
   311		return
   312	}
   313	
   314	// isLocalhost reports whether h should be considered a "localhost"
   315	// name for the myhostname NSS module.
   316	func isLocalhost(h string) bool {
   317		return stringsEqualFold(h, "localhost") || stringsEqualFold(h, "localhost.localdomain") || stringsHasSuffixFold(h, ".localhost") || stringsHasSuffixFold(h, ".localhost.localdomain")
   318	}
   319	
   320	// isGateway reports whether h should be considered a "gateway"
   321	// name for the myhostname NSS module.
   322	func isGateway(h string) bool {
   323		return stringsEqualFold(h, "gateway")
   324	}
   325	

View as plain text