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.
5 // +build aix darwin dragonfly freebsd hurd linux netbsd openbsd solaris
17 // conf represents a system's network configuration.
19 // forceCgoLookupHost forces CGO to always be used, if available.
20 forceCgoLookupHost
bool
22 netGo
bool // go DNS resolution forced
23 netCgo
bool // cgo DNS resolution forced
25 // machine has an /etc/mdns.allow file
28 goos
string // the runtime.GOOS, to ease testing
36 confOnce sync
.Once
// guards init of confVal via initConfVal
37 confVal
= &conf
{goos
: runtime
.GOOS
}
40 // systemConf returns the machine's network configuration.
41 func systemConf() *conf
{
42 confOnce
.Do(initConfVal
)
47 dnsMode
, debugLevel
:= goDebugNetDNS()
48 confVal
.dnsDebugLevel
= debugLevel
49 confVal
.netGo
= netGo || dnsMode
== "go"
50 confVal
.netCgo
= netCgo || dnsMode
== "cgo"
52 if confVal
.dnsDebugLevel
> 0 {
57 println("go package net: built with netgo build tag; using Go's DNS resolver")
59 println("go package net: GODEBUG setting forcing use of Go's resolver")
61 case confVal
.forceCgoLookupHost
:
62 println("go package net: using cgo DNS resolver")
64 println("go package net: dynamic selection of DNS resolver")
69 // Darwin pops up annoying dialog boxes if programs try to do
70 // their own DNS requests. So always use cgo instead, which
72 if runtime
.GOOS
== "darwin" {
73 confVal
.forceCgoLookupHost
= true
77 // If any environment-specified resolver options are specified,
78 // force cgo. Note that LOCALDOMAIN can change behavior merely
79 // by being specified with the empty string.
80 _
, localDomainDefined
:= syscall
.Getenv("LOCALDOMAIN")
81 if os
.Getenv("RES_OPTIONS") != "" ||
82 os
.Getenv("HOSTALIASES") != "" ||
85 confVal
.forceCgoLookupHost
= true
89 // OpenBSD apparently lets you override the location of resolv.conf
90 // with ASR_CONFIG. If we notice that, defer to libc.
91 if runtime
.GOOS
== "openbsd" && os
.Getenv("ASR_CONFIG") != "" {
92 confVal
.forceCgoLookupHost
= true
96 if runtime
.GOOS
!= "openbsd" {
97 confVal
.nss
= parseNSSConfFile("/etc/nsswitch.conf")
100 confVal
.resolv
= dnsReadConfig("/etc/resolv.conf")
101 if confVal
.resolv
.err
!= nil && !os
.IsNotExist(confVal
.resolv
.err
) &&
102 !os
.IsPermission(confVal
.resolv
.err
) {
103 // If we can't read the resolv.conf file, assume it
104 // had something important in it and defer to cgo.
105 // libc's resolver might then fail too, but at least
106 // it wasn't our fault.
107 confVal
.forceCgoLookupHost
= true
110 if _
, err
:= os
.Stat("/etc/mdns.allow"); err
== nil {
111 confVal
.hasMDNSAllow
= true
115 // canUseCgo reports whether calling cgo functions is allowed
116 // for non-hostname lookups.
117 func (c
*conf
) canUseCgo() bool {
118 return c
.hostLookupOrder(nil, "") == hostLookupCgo
121 // hostLookupOrder determines which strategy to use to resolve hostname.
122 // The provided Resolver is optional. nil means to not consider its options.
123 func (c
*conf
) hostLookupOrder(r
*Resolver
, hostname
string) (ret hostLookupOrder
) {
124 if c
.dnsDebugLevel
> 1 {
126 print("go package net: hostLookupOrder(", hostname
, ") = ", ret
.String(), "\n")
129 fallbackOrder
:= hostLookupCgo
130 if c
.netGo || r
.preferGo() {
131 fallbackOrder
= hostLookupFilesDNS
133 if c
.forceCgoLookupHost || c
.resolv
.unknownOpt || c
.goos
== "android" {
136 if bytealg
.IndexByteString(hostname
, '\\') != -1 || bytealg
.IndexByteString(hostname
, '%') != -1 {
137 // Don't deal with special form hostnames with backslashes
142 // OpenBSD is unique and doesn't use nsswitch.conf.
143 // It also doesn't support mDNS.
144 if c
.goos
== "openbsd" {
145 // OpenBSD's resolv.conf manpage says that a non-existent
146 // resolv.conf means "lookup" defaults to only "files",
147 // without DNS lookups.
148 if os
.IsNotExist(c
.resolv
.err
) {
149 return hostLookupFiles
151 lookup
:= c
.resolv
.lookup
152 if len(lookup
) == 0 {
153 // https://www.openbsd.org/cgi-bin/man.cgi/OpenBSD-current/man5/resolv.conf.5
154 // "If the lookup keyword is not used in the
155 // system's resolv.conf file then the assumed
156 // order is 'bind file'"
157 return hostLookupDNSFiles
159 if len(lookup
) < 1 ||
len(lookup
) > 2 {
164 if len(lookup
) == 2 {
165 if lookup
[1] == "file" {
166 return hostLookupDNSFiles
172 if len(lookup
) == 2 {
173 if lookup
[1] == "bind" {
174 return hostLookupFilesDNS
178 return hostLookupFiles
184 // Canonicalize the hostname by removing any trailing dot.
185 if stringsHasSuffix(hostname
, ".") {
186 hostname
= hostname
[:len(hostname
)-1]
188 if stringsHasSuffixFold(hostname
, ".local") {
189 // Per RFC 6762, the ".local" TLD is special. And
190 // because Go's native resolver doesn't do mDNS or
191 // similar local resolution mechanisms, assume that
192 // libc might (via Avahi, etc) and use cgo.
197 srcs
:= nss
.sources
["hosts"]
198 // If /etc/nsswitch.conf doesn't exist or doesn't specify any
199 // sources for "hosts", assume Go's DNS will work fine.
200 if os
.IsNotExist(nss
.err
) ||
(nss
.err
== nil && len(srcs
) == 0) {
201 if c
.goos
== "solaris" {
202 // illumos defaults to "nis [NOTFOUND=return] files"
205 if c
.goos
== "linux" {
206 // glibc says the default is "dns [!UNAVAIL=return] files"
207 // https://www.gnu.org/software/libc/manual/html_node/Notes-on-NSS-Configuration-File.html.
208 return hostLookupDNSFiles
210 return hostLookupFilesDNS
213 // We failed to parse or open nsswitch.conf, so
214 // conservatively assume we should use cgo if it's
219 var mdnsSource
, filesSource
, dnsSource
bool
221 for _
, src
:= range srcs
{
222 if src
.source
== "myhostname" {
223 if isLocalhost(hostname
) ||
isGateway(hostname
) {
226 hn
, err
:= getHostname()
227 if err
!= nil ||
stringsEqualFold(hostname
, hn
) {
232 if src
.source
== "files" || src
.source
== "dns" {
233 if !src
.standardCriteria() {
234 return fallbackOrder
// non-standard; let libc deal with it.
236 if src
.source
== "files" {
238 } else if src
.source
== "dns" {
246 if stringsHasPrefix(src
.source
, "mdns") {
247 // e.g. "mdns4", "mdns4_minimal"
248 // We already returned true before if it was *.local.
249 // libc wouldn't have found a hit on this anyway.
253 // Some source we don't know how to deal with.
257 // We don't parse mdns.allow files. They're rare. If one
258 // exists, it might list other TLDs (besides .local) or even
259 // '*', so just let libc deal with it.
260 if mdnsSource
&& c
.hasMDNSAllow
{
264 // Cases where Go can handle it without cgo and C thread
267 case filesSource
&& dnsSource
:
268 if first
== "files" {
269 return hostLookupFilesDNS
271 return hostLookupDNSFiles
274 return hostLookupFiles
279 // Something weird. Let libc deal with it.
283 // goDebugNetDNS parses the value of the GODEBUG "netdns" value.
284 // The netdns value can be of the form:
285 // 1 // debug level 1
286 // 2 // debug level 2
287 // cgo // use cgo for DNS lookups
288 // go // use go for DNS lookups
289 // cgo+1 // use cgo for DNS lookups + debug level 1
291 // cgo+2 // same, but debug level 2
293 func goDebugNetDNS() (dnsMode
string, debugLevel
int) {
294 goDebug
:= goDebugString("netdns")
295 parsePart
:= func(s
string) {
299 if '0' <= s
[0] && s
[0] <= '9' {
300 debugLevel
, _
, _
= dtoi(s
)
305 if i
:= bytealg
.IndexByteString(goDebug
, '+'); i
!= -1 {
306 parsePart(goDebug
[:i
])
307 parsePart(goDebug
[i
+1:])
314 // isLocalhost reports whether h should be considered a "localhost"
315 // name for the myhostname NSS module.
316 func isLocalhost(h
string) bool {
317 return stringsEqualFold(h
, "localhost") ||
stringsEqualFold(h
, "localhost.localdomain") ||
stringsHasSuffixFold(h
, ".localhost") ||
stringsHasSuffixFold(h
, ".localhost.localdomain")
320 // isGateway reports whether h should be considered a "gateway"
321 // name for the myhostname NSS module.
322 func isGateway(h
string) bool {
323 return stringsEqualFold(h
, "gateway")