// 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.package netimport ()// The net package's name resolution is rather complicated.// There are two main approaches, go and cgo.// The cgo resolver uses C functions like getaddrinfo.// The go resolver reads system files directly and// sends DNS packets directly to servers.//// The netgo build tag prefers the go resolver.// The netcgo build tag prefers the cgo resolver.//// The netgo build tag also prohibits the use of the cgo tool.// However, on Darwin, Plan 9, and Windows the cgo resolver is still available.// On those systems the cgo resolver does not require the cgo tool.// (The term "cgo resolver" was locked in by GODEBUG settings// at a time when the cgo resolver did require the cgo tool.)//// Adding netdns=go to GODEBUG will prefer the go resolver.// Adding netdns=cgo to GODEBUG will prefer the cgo resolver.//// The Resolver struct has a PreferGo field that user code// may set to prefer the go resolver. It is documented as being// equivalent to adding netdns=go to GODEBUG.//// When deciding which resolver to use, we first check the PreferGo field.// If that is not set, we check the GODEBUG setting.// If that is not set, we check the netgo or netcgo build tag.// If none of those are set, we normally prefer the go resolver by default.// However, if the cgo resolver is available,// there is a complex set of conditions for which we prefer the cgo resolver.//// Other files define the netGoBuildTag, netCgoBuildTag, and cgoAvailable// constants.// conf is used to determine name resolution configuration.type conf struct { netGo bool// prefer go approach, based on build tag and GODEBUG netCgo bool// prefer cgo approach, based on build tag and GODEBUG dnsDebugLevel int// from GODEBUG preferCgo bool// if no explicit preference, use cgo goos string// copy of runtime.GOOS, used for testing mdnsTest mdnsTest// assume /etc/mdns.allow exists, for testing}// mdnsTest is for testing only.type mdnsTest intconst ( mdnsFromSystem mdnsTest = iota mdnsAssumeExists mdnsAssumeDoesNotExist)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)returnconfVal}// initConfVal initializes confVal based on the environment// that will not change during program execution.func initConfVal() { , := goDebugNetDNS()confVal.netGo = netGoBuildTag || == "go"confVal.netCgo = netCgoBuildTag || == "cgo"confVal.dnsDebugLevel = ifconfVal.dnsDebugLevel > 0 {deferfunc() {ifconfVal.dnsDebugLevel > 1 {println("go package net: confVal.netCgo =", confVal.netCgo, " netGo =", confVal.netGo) }switch {caseconfVal.netGo:ifnetGoBuildTag {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 !cgoAvailable:println("go package net: cgo resolver not supported; using Go's DNS resolver")caseconfVal.netCgo || confVal.preferCgo:println("go package net: using cgo DNS resolver")default:println("go package net: dynamic selection of DNS resolver") } }() }// The remainder of this function sets preferCgo based on // conditions that will not change during program execution.// By default, prefer the go resolver.confVal.preferCgo = false// If the cgo resolver is not available, we can't prefer it.if !cgoAvailable {return }// Some operating systems always prefer the cgo resolver.ifgoosPrefersCgo() {confVal.preferCgo = truereturn }// The remaining checks are specific to Unix systems.switchruntime.GOOS {case"plan9", "windows", "js", "wasip1":return }// If any environment-specified resolver options are specified, // prefer the cgo resolver. // 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.preferCgo = truereturn }// OpenBSD apparently lets you override the location of resolv.conf // with ASR_CONFIG. If we notice that, defer to libc.ifruntime.GOOS == "openbsd" && os.Getenv("ASR_CONFIG") != "" {confVal.preferCgo = truereturn }}// goosPrefersCgo reports whether the GOOS value passed in prefers// the cgo resolver.func goosPrefersCgo() bool {switchruntime.GOOS {// Historically on Windows and Plan 9 we prefer the // cgo resolver (which doesn't use the cgo tool) rather than // the go resolver. This is because originally these // systems did not support the go resolver. // Keep it this way for better compatibility. // Perhaps we can revisit this some day.case"windows", "plan9":returntrue// Darwin pops up annoying dialog boxes if programs try to // do their own DNS requests, so prefer cgo.case"darwin", "ios":returntrue// DNS requests don't work on Android, so prefer the cgo resolver. // Issue #10714.case"android":returntruedefault:returnfalse }}// mustUseGoResolver reports whether a DNS lookup of any sort is// required to use the go resolver. The provided Resolver is optional.// This will report true if the cgo resolver is not available.func ( *conf) ( *Resolver) bool {if !cgoAvailable {returntrue }ifruntime.GOOS == "plan9" {// TODO(bradfitz): for now we only permit use of the PreferGo // implementation when there's a non-nil Resolver with a // non-nil Dialer. This is a sign that the code is trying // to use their DNS-speaking net.Conn (such as an in-memory // DNS cache) and they don't want to actually hit the network. // Once we add support for looking the default DNS servers // from plan9, though, then we can relax this.if == nil || .Dial == nil {returnfalse } }return .netGo || .preferGo()}// addrLookupOrder determines which strategy to use to resolve addresses.// 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 {deferfunc() {print("go package net: addrLookupOrder(", , ") = ", .String(), "\n") }() }return .lookupOrder(, "")}// 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 {deferfunc() {print("go package net: hostLookupOrder(", , ") = ", .String(), "\n") }() }return .lookupOrder(, )}func ( *conf) ( *Resolver, string) ( hostLookupOrder, *dnsConfig) {// fallbackOrder is the order we return if we can't figure it out.varhostLookupOrdervarboolif .mustUseGoResolver() {// Go resolver was explicitly requested // or cgo resolver is not available. // Figure out the order below. = hostLookupFilesDNS = false } elseif .netCgo {// Cgo resolver was explicitly requested.returnhostLookupCgo, nil } elseif .preferCgo {// Given a choice, we prefer the cgo resolver.returnhostLookupCgo, nil } else {// Neither resolver was explicitly requested // and we have no preference.ifbytealg.IndexByteString(, '\\') != -1 || bytealg.IndexByteString(, '%') != -1 {// Don't deal with special form hostnames // with backslashes or '%'.returnhostLookupCgo, nil }// If something is unrecognized, use cgo. = hostLookupCgo = true }// On systems that don't use /etc/resolv.conf or /etc/nsswitch.conf, we are done.switch .goos {case"windows", "plan9", "android", "ios":return , nil }// Try to figure out the order to use for searches. // If we don't recognize something, use fallbackOrder. // That will use cgo unless the Go resolver was explicitly requested. // If we do figure out the order, return something other // than fallbackOrder to use the Go resolver with that order. = getSystemDNSConfig()if && .err != nil && !errors.Is(.err, fs.ErrNotExist) && !errors.Is(.err, fs.ErrPermission) {// We can't read the resolv.conf file, so use cgo if we can.returnhostLookupCgo, }if && .unknownOpt {// We didn't recognize something in resolv.conf, // so use cgo if we can.returnhostLookupCgo, }// 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.iferrors.Is(.err, fs.ErrNotExist) {returnhostLookupFiles, } := .lookupiflen() == 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'"returnhostLookupDNSFiles, }iflen() < 1 || len() > 2 {// We don't recognize this format.return , }switch [0] {case"bind":iflen() == 2 {if [1] == "file" {returnhostLookupDNSFiles, }// Unrecognized.return , }returnhostLookupDNS, case"file":iflen() == 2 {if [1] == "bind" {returnhostLookupFilesDNS, }// Unrecognized.return , }returnhostLookupFiles, default:// Unrecognized.return , }// We always return before this point. // The code below is for non-OpenBSD. }// Canonicalize the hostname by removing any trailing dot. = stringslite.TrimSuffix(, ".") := 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.iferrors.Is(.err, fs.ErrNotExist) || (.err == nil && len() == 0) {if && .goos == "solaris" {// illumos defaults to // "nis [NOTFOUND=return] files", // which the go resolver doesn't support.returnhostLookupCgo, }returnhostLookupFilesDNS, }if .err != nil {// We failed to parse or open nsswitch.conf, so // we have nothing to base an order on.return , }varboolvarboolvar , boolvarstringfor , := range {if .source == "files" || .source == "dns" {if && !.standardCriteria() {// non-standard; let libc deal with it.returnhostLookupCgo, }if .source == "files" { = true } else { = true = true = true }if == "" { = .source }continue }if {switch {case != "" && .source == "myhostname":// Let the cgo resolver handle myhostname // if we are looking up the local hostname.ifisLocalhost() || isGateway() || isOutbound() {returnhostLookupCgo, } , := getHostname()if != nil || stringsEqualFold(, ) {returnhostLookupCgo, }continuecase != "" && stringslite.HasPrefix(.source, "mdns"):ifstringsHasSuffixFold(, ".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.returnhostLookupCgo, }// 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.varboolswitch .mdnsTest {casemdnsFromSystem: , := os.Stat("/etc/mdns.allow")if != nil && !errors.Is(, fs.ErrNotExist) {// Let libc figure out what is going on.returnhostLookupCgo, } = == nilcasemdnsAssumeExists: = truecasemdnsAssumeDoesNotExist: = false }if {returnhostLookupCgo, }continuedefault:// Some source we don't know how to deal with.returnhostLookupCgo, } }if ! { = truefor , := range [+1:] {if .source == "dns" { = truebreak } } }// If we saw a source we don't recognize, which can only // happen if we can't use the cgo resolver, treat it as DNS, // but only when there is no dns in all other sources.if ! { = trueif == "" { = "dns" } } }// Cases where Go can handle it without cgo and C thread overhead, // or where the Go resolver has been forced.switch {case && :if == "files" {returnhostLookupFilesDNS, } else {returnhostLookupDNSFiles, }case :returnhostLookupFiles, case :returnhostLookupDNS, }// Something weird. Fallback to the default.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 {returnstringsEqualFold(, "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 {returnstringsEqualFold(, "_gateway")}// isOutbound reports whether h should be considered an "outbound"// name for the myhostname NSS module.func isOutbound( string) bool {returnstringsEqualFold(, "_outbound")}
The pages are generated with Goldsv0.7.0-preview. (GOOS=linux GOARCH=amd64)
Golds is a Go 101 project developed by Tapir Liu.
PR and bug reports are welcome and can be submitted to the issue list.
Please follow @zigo_101 (reachable from the left QR code) to get the latest news of Golds.