// 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.

// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris

package net

import (
	
	
	
	
	
)

// conf represents a system's network configuration.
type conf struct {
	// forceCgoLookupHost forces CGO to always be used, if available.
	forceCgoLookupHost bool

	netGo  bool // go DNS resolution forced
	netCgo bool // cgo DNS resolution forced

	// machine has an /etc/mdns.allow file
	hasMDNSAllow bool

	goos          string // the runtime.GOOS, to ease testing
	dnsDebugLevel int

	nss    *nssConf
	resolv *dnsConfig
}

var (
	confOnce sync.Once // guards init of confVal via initConfVal
	confVal  = &conf{goos: runtime.GOOS}
)

// systemConf returns the machine's network configuration.
func systemConf() *conf {
	confOnce.Do(initConfVal)
	return confVal
}

func initConfVal() {
	,  := goDebugNetDNS()
	confVal.dnsDebugLevel = 
	confVal.netGo = netGo ||  == "go"
	confVal.netCgo = netCgo ||  == "cgo"

	if confVal.dnsDebugLevel > 0 {
		defer func() {
			switch {
			case confVal.netGo:
				if netGo {
					println("go package net: built with netgo build tag; using Go's DNS resolver")
				} else {
					println("go package net: GODEBUG setting forcing use of Go's resolver")
				}
			case confVal.forceCgoLookupHost:
				println("go package net: using cgo DNS resolver")
			default:
				println("go package net: dynamic selection of DNS resolver")
			}
		}()
	}

	// Darwin pops up annoying dialog boxes if programs try to do
	// their own DNS requests. So always use cgo instead, which
	// avoids that.
	if runtime.GOOS == "darwin" || runtime.GOOS == "ios" {
		confVal.forceCgoLookupHost = true
		return
	}

	// If any environment-specified resolver options are specified,
	// force cgo. Note that LOCALDOMAIN can change behavior merely
	// by being specified with the empty string.
	,  := syscall.Getenv("LOCALDOMAIN")
	if os.Getenv("RES_OPTIONS") != "" ||
		os.Getenv("HOSTALIASES") != "" ||
		confVal.netCgo ||
		 {
		confVal.forceCgoLookupHost = true
		return
	}

	// OpenBSD apparently lets you override the location of resolv.conf
	// with ASR_CONFIG. If we notice that, defer to libc.
	if runtime.GOOS == "openbsd" && os.Getenv("ASR_CONFIG") != "" {
		confVal.forceCgoLookupHost = true
		return
	}

	if runtime.GOOS != "openbsd" {
		confVal.nss = parseNSSConfFile("/etc/nsswitch.conf")
	}

	confVal.resolv = dnsReadConfig("/etc/resolv.conf")
	if confVal.resolv.err != nil && !os.IsNotExist(confVal.resolv.err) &&
		!os.IsPermission(confVal.resolv.err) {
		// If we can't read the resolv.conf file, assume it
		// had something important in it and defer to cgo.
		// libc's resolver might then fail too, but at least
		// it wasn't our fault.
		confVal.forceCgoLookupHost = true
	}

	if ,  := os.Stat("/etc/mdns.allow");  == nil {
		confVal.hasMDNSAllow = true
	}
}

// canUseCgo reports whether calling cgo functions is allowed
// for non-hostname lookups.
func ( *conf) () bool {
	return .hostLookupOrder(nil, "") == hostLookupCgo
}

// hostLookupOrder determines which strategy to use to resolve hostname.
// The provided Resolver is optional. nil means to not consider its options.
func ( *conf) ( *Resolver,  string) ( hostLookupOrder) {
	if .dnsDebugLevel > 1 {
		defer func() {
			print("go package net: hostLookupOrder(", , ") = ", .String(), "\n")
		}()
	}
	 := hostLookupCgo
	if .netGo || .preferGo() {
		 = hostLookupFilesDNS
	}
	if .forceCgoLookupHost || .resolv.unknownOpt || .goos == "android" {
		return 
	}
	if bytealg.IndexByteString(, '\\') != -1 || bytealg.IndexByteString(, '%') != -1 {
		// Don't deal with special form hostnames with backslashes
		// or '%'.
		return 
	}

	// OpenBSD is unique and doesn't use nsswitch.conf.
	// It also doesn't support mDNS.
	if .goos == "openbsd" {
		// OpenBSD's resolv.conf manpage says that a non-existent
		// resolv.conf means "lookup" defaults to only "files",
		// without DNS lookups.
		if os.IsNotExist(.resolv.err) {
			return hostLookupFiles
		}
		 := .resolv.lookup
		if len() == 0 {
			// https://www.openbsd.org/cgi-bin/man.cgi/OpenBSD-current/man5/resolv.conf.5
			// "If the lookup keyword is not used in the
			// system's resolv.conf file then the assumed
			// order is 'bind file'"
			return hostLookupDNSFiles
		}
		if len() < 1 || len() > 2 {
			return 
		}
		switch [0] {
		case "bind":
			if len() == 2 {
				if [1] == "file" {
					return hostLookupDNSFiles
				}
				return 
			}
			return hostLookupDNS
		case "file":
			if len() == 2 {
				if [1] == "bind" {
					return hostLookupFilesDNS
				}
				return 
			}
			return hostLookupFiles
		default:
			return 
		}
	}

	// Canonicalize the hostname by removing any trailing dot.
	if stringsHasSuffix(, ".") {
		 = [:len()-1]
	}
	if stringsHasSuffixFold(, ".local") {
		// Per RFC 6762, the ".local" TLD is special. And
		// because Go's native resolver doesn't do mDNS or
		// similar local resolution mechanisms, assume that
		// libc might (via Avahi, etc) and use cgo.
		return 
	}

	 := .nss
	 := .sources["hosts"]
	// If /etc/nsswitch.conf doesn't exist or doesn't specify any
	// sources for "hosts", assume Go's DNS will work fine.
	if os.IsNotExist(.err) || (.err == nil && len() == 0) {
		if .goos == "solaris" {
			// illumos defaults to "nis [NOTFOUND=return] files"
			return 
		}
		return hostLookupFilesDNS
	}
	if .err != nil {
		// We failed to parse or open nsswitch.conf, so
		// conservatively assume we should use cgo if it's
		// available.
		return 
	}

	var , ,  bool
	var  string
	for ,  := range  {
		if .source == "myhostname" {
			if isLocalhost() || isGateway() {
				return 
			}
			,  := getHostname()
			if  != nil || stringsEqualFold(, ) {
				return 
			}
			continue
		}
		if .source == "files" || .source == "dns" {
			if !.standardCriteria() {
				return  // non-standard; let libc deal with it.
			}
			if .source == "files" {
				 = true
			} else if .source == "dns" {
				 = true
			}
			if  == "" {
				 = .source
			}
			continue
		}
		if stringsHasPrefix(.source, "mdns") {
			// e.g. "mdns4", "mdns4_minimal"
			// We already returned true before if it was *.local.
			// libc wouldn't have found a hit on this anyway.
			 = true
			continue
		}
		// Some source we don't know how to deal with.
		return 
	}

	// We don't parse mdns.allow files. They're rare. If one
	// exists, it might list other TLDs (besides .local) or even
	// '*', so just let libc deal with it.
	if  && .hasMDNSAllow {
		return 
	}

	// Cases where Go can handle it without cgo and C thread
	// overhead.
	switch {
	case  && :
		if  == "files" {
			return hostLookupFilesDNS
		} else {
			return hostLookupDNSFiles
		}
	case :
		return hostLookupFiles
	case :
		return hostLookupDNS
	}

	// Something weird. Let libc deal with it.
	return 
}

// goDebugNetDNS parses the value of the GODEBUG "netdns" value.
// The netdns value can be of the form:
//    1       // debug level 1
//    2       // debug level 2
//    cgo     // use cgo for DNS lookups
//    go      // use go for DNS lookups
//    cgo+1   // use cgo for DNS lookups + debug level 1
//    1+cgo   // same
//    cgo+2   // same, but debug level 2
// etc.
func goDebugNetDNS() ( string,  int) {
	 := goDebugString("netdns")
	 := func( string) {
		if  == "" {
			return
		}
		if '0' <= [0] && [0] <= '9' {
			, _, _ = dtoi()
		} else {
			 = 
		}
	}
	if  := bytealg.IndexByteString(, '+');  != -1 {
		([:])
		([+1:])
		return
	}
	()
	return
}

// isLocalhost reports whether h should be considered a "localhost"
// name for the myhostname NSS module.
func isLocalhost( string) bool {
	return stringsEqualFold(, "localhost") || stringsEqualFold(, "localhost.localdomain") || stringsHasSuffixFold(, ".localhost") || stringsHasSuffixFold(, ".localhost.localdomain")
}

// isGateway reports whether h should be considered a "gateway"
// name for the myhostname NSS module.
func isGateway( string) bool {
	return stringsEqualFold(, "gateway")
}