...
Run Format

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

View as plain text