...

Source file src/net/conf.go

Documentation: net

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

View as plain text