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 linux netbsd openbsd solaris
16 // conf represents a system's network configuration.
18 // forceCgoLookupHost forces CGO to always be used, if available.
19 forceCgoLookupHost
bool
21 netGo
bool // go DNS resolution forced
22 netCgo
bool // cgo DNS resolution forced
24 // machine has an /etc/mdns.allow file
27 goos
string // the runtime.GOOS, to ease testing
35 confOnce sync
.Once
// guards init of confVal via initConfVal
36 confVal
= &conf
{goos
: runtime
.GOOS
}
39 // systemConf returns the machine's network configuration.
40 func systemConf() *conf
{
41 confOnce
.Do(initConfVal
)
46 dnsMode
, debugLevel
:= goDebugNetDNS()
47 confVal
.dnsDebugLevel
= debugLevel
48 confVal
.netGo
= netGo || dnsMode
== "go"
49 confVal
.netCgo
= netCgo || dnsMode
== "cgo"
51 if confVal
.dnsDebugLevel
> 0 {
56 println("go package net: built with netgo build tag; using Go's DNS resolver")
58 println("go package net: GODEBUG setting forcing use of Go's resolver")
60 case confVal
.forceCgoLookupHost
:
61 println("go package net: using cgo DNS resolver")
63 println("go package net: dynamic selection of DNS resolver")
68 // Darwin pops up annoying dialog boxes if programs try to do
69 // their own DNS requests. So always use cgo instead, which
71 if runtime
.GOOS
== "darwin" {
72 confVal
.forceCgoLookupHost
= true
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") != "" ||
84 confVal
.forceCgoLookupHost
= true
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
95 if runtime
.GOOS
!= "openbsd" {
96 confVal
.nss
= parseNSSConfFile("/etc/nsswitch.conf")
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
109 if _
, err
:= os
.Stat("/etc/mdns.allow"); err
== nil {
110 confVal
.hasMDNSAllow
= true
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
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 {
125 print("go package net: hostLookupOrder(", hostname
, ") = ", ret
.String(), "\n")
128 fallbackOrder
:= hostLookupCgo
129 if c
.netGo || r
.preferGo() {
130 fallbackOrder
= hostLookupFilesDNS
132 if c
.forceCgoLookupHost || c
.resolv
.unknownOpt || c
.goos
== "android" {
135 if byteIndex(hostname
, '\\') != -1 ||
byteIndex(hostname
, '%') != -1 {
136 // Don't deal with special form hostnames with backslashes
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
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
158 if len(lookup
) < 1 ||
len(lookup
) > 2 {
163 if len(lookup
) == 2 {
164 if lookup
[1] == "file" {
165 return hostLookupDNSFiles
171 if len(lookup
) == 2 {
172 if lookup
[1] == "bind" {
173 return hostLookupFilesDNS
177 return hostLookupFiles
183 // Canonicalize the hostname by removing any trailing dot.
184 if stringsHasSuffix(hostname
, ".") {
185 hostname
= hostname
[:len(hostname
)-1]
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.
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"
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
209 return hostLookupFilesDNS
212 // We failed to parse or open nsswitch.conf, so
213 // conservatively assume we should use cgo if it's
218 var mdnsSource
, filesSource
, dnsSource
bool
220 for _
, src
:= range srcs
{
221 if src
.source
== "myhostname" {
222 if isLocalhost(hostname
) ||
isGateway(hostname
) {
225 hn
, err
:= getHostname()
226 if err
!= nil ||
stringsEqualFold(hostname
, hn
) {
231 if src
.source
== "files" || src
.source
== "dns" {
232 if !src
.standardCriteria() {
233 return fallbackOrder
// non-standard; let libc deal with it.
235 if src
.source
== "files" {
237 } else if src
.source
== "dns" {
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.
252 // Some source we don't know how to deal with.
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
{
263 // Cases where Go can handle it without cgo and C thread
266 case filesSource
&& dnsSource
:
267 if first
== "files" {
268 return hostLookupFilesDNS
270 return hostLookupDNSFiles
273 return hostLookupFiles
278 // Something weird. Let libc deal with it.
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
290 // cgo+2 // same, but debug level 2
292 func goDebugNetDNS() (dnsMode
string, debugLevel
int) {
293 goDebug
:= goDebugString("netdns")
294 parsePart
:= func(s
string) {
298 if '0' <= s
[0] && s
[0] <= '9' {
299 debugLevel
, _
, _
= dtoi(s
)
304 if i
:= byteIndex(goDebug
, '+'); i
!= -1 {
305 parsePart(goDebug
[:i
])
306 parsePart(goDebug
[i
+1:])
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")
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")