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

//go:build !js

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 // non-go DNS resolution forced (cgo, or win32)

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

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

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.netGo && !confVal.netCgo && (runtime.GOOS == "windows" || runtime.GOOS == "plan9") {
		// Neither of these platforms actually use cgo.
		//
		// The meaning of "cgo" mode in the net package is
		// really "the native OS way", which for libc meant
		// cgo on the original platforms that motivated
		// PreferGo support before Windows and Plan9 got support,
		// at which time the GODEBUG=netdns=go and GODEBUG=netdns=cgo
		// names were already kinda locked in.
		confVal.netCgo = true
	}

	if confVal.dnsDebugLevel > 0 {
		defer func() {
			if confVal.dnsDebugLevel > 1 {
				println("go package net: confVal.netCgo =", confVal.netCgo, " netGo =", confVal.netGo)
			}
			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 runtime.GOOS == "windows" || runtime.GOOS == "plan9" {
		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 ,  := os.Stat("/etc/mdns.allow");  == nil {
		confVal.hasMDNSAllow = true
	}
}

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

// hostLookupOrder determines which strategy to use to resolve hostname.
// The provided Resolver is optional. nil means to not consider its options.
// It also returns dnsConfig when it was used to determine the lookup order.
func ( *conf) ( *Resolver,  string) ( hostLookupOrder,  *dnsConfig) {
	if .dnsDebugLevel > 1 {
		defer func() {
			print("go package net: hostLookupOrder(", , ") = ", .String(), "\n")
		}()
	}
	 := hostLookupCgo
	if .netGo || .preferGo() {
		switch .goos {
		case "windows":
			// TODO(bradfitz): implement files-based
			// lookup on Windows too? I guess /etc/hosts
			// kinda exists on Windows. But for now, only
			// do DNS.
			 = hostLookupDNS
		default:
			 = hostLookupFilesDNS
		}
	}
	if .forceCgoLookupHost || .goos == "android" || .goos == "windows" || .goos == "plan9" {
		return , nil
	}
	if bytealg.IndexByteString(, '\\') != -1 || bytealg.IndexByteString(, '%') != -1 {
		// Don't deal with special form hostnames with backslashes
		// or '%'.
		return , nil
	}

	 := getSystemDNSConfig()
	if .err != nil && !os.IsNotExist(.err) && !os.IsPermission(.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.
		return , 
	}

	if .unknownOpt {
		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(.err) {
			return hostLookupFiles, 
		}

		 := .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 , 
	}

	 := getSystemNSS()
	 := .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() || isOutbound() {
				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 , 
}

var netdns = godebug.New("netdns")

// 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) {
	 := netdns.Value()
	 := 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")
}

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