...
Run Format

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  // +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(nil, "") == hostLookupCgo
   118  }
   119  
   120  // hostLookupOrder determines which strategy to use to resolve hostname.
   121  // The provided Resolver is optional. nil means to not consider its options.
   122  func (c *conf) hostLookupOrder(r *Resolver, hostname string) (ret hostLookupOrder) {
   123  	if c.dnsDebugLevel > 1 {
   124  		defer func() {
   125  			print("go package net: hostLookupOrder(", hostname, ") = ", ret.String(), "\n")
   126  		}()
   127  	}
   128  	fallbackOrder := hostLookupCgo
   129  	if c.netGo || r.preferGo() {
   130  		fallbackOrder = hostLookupFilesDNS
   131  	}
   132  	if c.forceCgoLookupHost || c.resolv.unknownOpt || c.goos == "android" {
   133  		return fallbackOrder
   134  	}
   135  	if byteIndex(hostname, '\\') != -1 || byteIndex(hostname, '%') != -1 {
   136  		// Don't deal with special form hostnames with backslashes
   137  		// or '%'.
   138  		return fallbackOrder
   139  	}
   140  
   141  	// OpenBSD is unique and doesn't use nsswitch.conf.
   142  	// It also doesn't support mDNS.
   143  	if c.goos == "openbsd" {
   144  		// OpenBSD's resolv.conf manpage says that a non-existent
   145  		// resolv.conf means "lookup" defaults to only "files",
   146  		// without DNS lookups.
   147  		if os.IsNotExist(c.resolv.err) {
   148  			return hostLookupFiles
   149  		}
   150  		lookup := c.resolv.lookup
   151  		if len(lookup) == 0 {
   152  			// https://www.openbsd.org/cgi-bin/man.cgi/OpenBSD-current/man5/resolv.conf.5
   153  			// "If the lookup keyword is not used in the
   154  			// system's resolv.conf file then the assumed
   155  			// order is 'bind file'"
   156  			return hostLookupDNSFiles
   157  		}
   158  		if len(lookup) < 1 || len(lookup) > 2 {
   159  			return fallbackOrder
   160  		}
   161  		switch lookup[0] {
   162  		case "bind":
   163  			if len(lookup) == 2 {
   164  				if lookup[1] == "file" {
   165  					return hostLookupDNSFiles
   166  				}
   167  				return fallbackOrder
   168  			}
   169  			return hostLookupDNS
   170  		case "file":
   171  			if len(lookup) == 2 {
   172  				if lookup[1] == "bind" {
   173  					return hostLookupFilesDNS
   174  				}
   175  				return fallbackOrder
   176  			}
   177  			return hostLookupFiles
   178  		default:
   179  			return fallbackOrder
   180  		}
   181  	}
   182  
   183  	// Canonicalize the hostname by removing any trailing dot.
   184  	if stringsHasSuffix(hostname, ".") {
   185  		hostname = hostname[:len(hostname)-1]
   186  	}
   187  	if stringsHasSuffixFold(hostname, ".local") {
   188  		// Per RFC 6762, the ".local" TLD is special. And
   189  		// because Go's native resolver doesn't do mDNS or
   190  		// similar local resolution mechanisms, assume that
   191  		// libc might (via Avahi, etc) and use cgo.
   192  		return fallbackOrder
   193  	}
   194  
   195  	nss := c.nss
   196  	srcs := nss.sources["hosts"]
   197  	// If /etc/nsswitch.conf doesn't exist or doesn't specify any
   198  	// sources for "hosts", assume Go's DNS will work fine.
   199  	if os.IsNotExist(nss.err) || (nss.err == nil && len(srcs) == 0) {
   200  		if c.goos == "solaris" {
   201  			// illumos defaults to "nis [NOTFOUND=return] files"
   202  			return fallbackOrder
   203  		}
   204  		if c.goos == "linux" {
   205  			// glibc says the default is "dns [!UNAVAIL=return] files"
   206  			// https://www.gnu.org/software/libc/manual/html_node/Notes-on-NSS-Configuration-File.html.
   207  			return hostLookupDNSFiles
   208  		}
   209  		return hostLookupFilesDNS
   210  	}
   211  	if nss.err != nil {
   212  		// We failed to parse or open nsswitch.conf, so
   213  		// conservatively assume we should use cgo if it's
   214  		// available.
   215  		return fallbackOrder
   216  	}
   217  
   218  	var mdnsSource, filesSource, dnsSource bool
   219  	var first string
   220  	for _, src := range srcs {
   221  		if src.source == "myhostname" {
   222  			if isLocalhost(hostname) || isGateway(hostname) {
   223  				return fallbackOrder
   224  			}
   225  			hn, err := getHostname()
   226  			if err != nil || stringsEqualFold(hostname, hn) {
   227  				return fallbackOrder
   228  			}
   229  			continue
   230  		}
   231  		if src.source == "files" || src.source == "dns" {
   232  			if !src.standardCriteria() {
   233  				return fallbackOrder // non-standard; let libc deal with it.
   234  			}
   235  			if src.source == "files" {
   236  				filesSource = true
   237  			} else if src.source == "dns" {
   238  				dnsSource = true
   239  			}
   240  			if first == "" {
   241  				first = src.source
   242  			}
   243  			continue
   244  		}
   245  		if stringsHasPrefix(src.source, "mdns") {
   246  			// e.g. "mdns4", "mdns4_minimal"
   247  			// We already returned true before if it was *.local.
   248  			// libc wouldn't have found a hit on this anyway.
   249  			mdnsSource = true
   250  			continue
   251  		}
   252  		// Some source we don't know how to deal with.
   253  		return fallbackOrder
   254  	}
   255  
   256  	// We don't parse mdns.allow files. They're rare. If one
   257  	// exists, it might list other TLDs (besides .local) or even
   258  	// '*', so just let libc deal with it.
   259  	if mdnsSource && c.hasMDNSAllow {
   260  		return fallbackOrder
   261  	}
   262  
   263  	// Cases where Go can handle it without cgo and C thread
   264  	// overhead.
   265  	switch {
   266  	case filesSource && dnsSource:
   267  		if first == "files" {
   268  			return hostLookupFilesDNS
   269  		} else {
   270  			return hostLookupDNSFiles
   271  		}
   272  	case filesSource:
   273  		return hostLookupFiles
   274  	case dnsSource:
   275  		return hostLookupDNS
   276  	}
   277  
   278  	// Something weird. Let libc deal with it.
   279  	return fallbackOrder
   280  }
   281  
   282  // goDebugNetDNS parses the value of the GODEBUG "netdns" value.
   283  // The netdns value can be of the form:
   284  //    1       // debug level 1
   285  //    2       // debug level 2
   286  //    cgo     // use cgo for DNS lookups
   287  //    go      // use go for DNS lookups
   288  //    cgo+1   // use cgo for DNS lookups + debug level 1
   289  //    1+cgo   // same
   290  //    cgo+2   // same, but debug level 2
   291  // etc.
   292  func goDebugNetDNS() (dnsMode string, debugLevel int) {
   293  	goDebug := goDebugString("netdns")
   294  	parsePart := func(s string) {
   295  		if s == "" {
   296  			return
   297  		}
   298  		if '0' <= s[0] && s[0] <= '9' {
   299  			debugLevel, _, _ = dtoi(s)
   300  		} else {
   301  			dnsMode = s
   302  		}
   303  	}
   304  	if i := byteIndex(goDebug, '+'); i != -1 {
   305  		parsePart(goDebug[:i])
   306  		parsePart(goDebug[i+1:])
   307  		return
   308  	}
   309  	parsePart(goDebug)
   310  	return
   311  }
   312  
   313  // isLocalhost reports whether h should be considered a "localhost"
   314  // name for the myhostname NSS module.
   315  func isLocalhost(h string) bool {
   316  	return stringsEqualFold(h, "localhost") || stringsEqualFold(h, "localhost.localdomain") || stringsHasSuffixFold(h, ".localhost") || stringsHasSuffixFold(h, ".localhost.localdomain")
   317  }
   318  
   319  // isGateway reports whether h should be considered a "gateway"
   320  // name for the myhostname NSS module.
   321  func isGateway(h string) bool {
   322  	return stringsEqualFold(h, "gateway")
   323  }
   324  

View as plain text