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. 4 5// +build aix darwin dragonfly freebsd hurd linux netbsd openbsd solaris 6 7package net 8 9import ( 10 "internal/bytealg" 11 "os" 12 "runtime" 13 "sync" 14 "syscall" 15) 16 17// conf represents a system's network configuration. 18type conf struct { 19 // forceCgoLookupHost forces CGO to always be used, if available. 20 forceCgoLookupHost bool 21 22 netGo bool // go DNS resolution forced 23 netCgo bool // cgo DNS resolution forced 24 25 // machine has an /etc/mdns.allow file 26 hasMDNSAllow bool 27 28 goos string // the runtime.GOOS, to ease testing 29 dnsDebugLevel int 30 31 nss *nssConf 32 resolv *dnsConfig 33} 34 35var ( 36 confOnce sync.Once // guards init of confVal via initConfVal 37 confVal = &conf{goos: runtime.GOOS} 38) 39 40// systemConf returns the machine's network configuration. 41func systemConf() *conf { 42 confOnce.Do(initConfVal) 43 return confVal 44} 45 46func initConfVal() { 47 dnsMode, debugLevel := goDebugNetDNS() 48 confVal.dnsDebugLevel = debugLevel 49 confVal.netGo = netGo || dnsMode == "go" 50 confVal.netCgo = netCgo || dnsMode == "cgo" 51 52 if confVal.dnsDebugLevel > 0 { 53 defer func() { 54 switch { 55 case confVal.netGo: 56 if netGo { 57 println("go package net: built with netgo build tag; using Go's DNS resolver") 58 } else { 59 println("go package net: GODEBUG setting forcing use of Go's resolver") 60 } 61 case confVal.forceCgoLookupHost: 62 println("go package net: using cgo DNS resolver") 63 default: 64 println("go package net: dynamic selection of DNS resolver") 65 } 66 }() 67 } 68 69 // Darwin pops up annoying dialog boxes if programs try to do 70 // their own DNS requests. So always use cgo instead, which 71 // avoids that. 72 if runtime.GOOS == "darwin" { 73 confVal.forceCgoLookupHost = true 74 return 75 } 76 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") != "" || 83 confVal.netCgo || 84 localDomainDefined { 85 confVal.forceCgoLookupHost = true 86 return 87 } 88 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 93 return 94 } 95 96 if runtime.GOOS != "openbsd" { 97 confVal.nss = parseNSSConfFile("/etc/nsswitch.conf") 98 } 99 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 108 } 109 110 if _, err := os.Stat("/etc/mdns.allow"); err == nil { 111 confVal.hasMDNSAllow = true 112 } 113} 114 115// canUseCgo reports whether calling cgo functions is allowed 116// for non-hostname lookups. 117func (c *conf) canUseCgo() bool { 118 return c.hostLookupOrder(nil, "") == hostLookupCgo 119} 120 121// hostLookupOrder determines which strategy to use to resolve hostname. 122// The provided Resolver is optional. nil means to not consider its options. 123func (c *conf) hostLookupOrder(r *Resolver, hostname string) (ret hostLookupOrder) { 124 if c.dnsDebugLevel > 1 { 125 defer func() { 126 print("go package net: hostLookupOrder(", hostname, ") = ", ret.String(), "\n") 127 }() 128 } 129 fallbackOrder := hostLookupCgo 130 if c.netGo || r.preferGo() { 131 fallbackOrder = hostLookupFilesDNS 132 } 133 if c.forceCgoLookupHost || c.resolv.unknownOpt || c.goos == "android" { 134 return fallbackOrder 135 } 136 if bytealg.IndexByteString(hostname, '\\') != -1 || bytealg.IndexByteString(hostname, '%') != -1 { 137 // Don't deal with special form hostnames with backslashes 138 // or '%'. 139 return fallbackOrder 140 } 141 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 150 } 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 158 } 159 if len(lookup) < 1 || len(lookup) > 2 { 160 return fallbackOrder 161 } 162 switch lookup[0] { 163 case "bind": 164 if len(lookup) == 2 { 165 if lookup[1] == "file" { 166 return hostLookupDNSFiles 167 } 168 return fallbackOrder 169 } 170 return hostLookupDNS 171 case "file": 172 if len(lookup) == 2 { 173 if lookup[1] == "bind" { 174 return hostLookupFilesDNS 175 } 176 return fallbackOrder 177 } 178 return hostLookupFiles 179 default: 180 return fallbackOrder 181 } 182 } 183 184 // Canonicalize the hostname by removing any trailing dot. 185 if stringsHasSuffix(hostname, ".") { 186 hostname = hostname[:len(hostname)-1] 187 } 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. 193 return fallbackOrder 194 } 195 196 nss := c.nss 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" 203 return fallbackOrder 204 } 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 209 } 210 return hostLookupFilesDNS 211 } 212 if nss.err != nil { 213 // We failed to parse or open nsswitch.conf, so 214 // conservatively assume we should use cgo if it's 215 // available. 216 return fallbackOrder 217 } 218 219 var mdnsSource, filesSource, dnsSource bool 220 var first string 221 for _, src := range srcs { 222 if src.source == "myhostname" { 223 if isLocalhost(hostname) || isGateway(hostname) { 224 return fallbackOrder 225 } 226 hn, err := getHostname() 227 if err != nil || stringsEqualFold(hostname, hn) { 228 return fallbackOrder 229 } 230 continue 231 } 232 if src.source == "files" || src.source == "dns" { 233 if !src.standardCriteria() { 234 return fallbackOrder // non-standard; let libc deal with it. 235 } 236 if src.source == "files" { 237 filesSource = true 238 } else if src.source == "dns" { 239 dnsSource = true 240 } 241 if first == "" { 242 first = src.source 243 } 244 continue 245 } 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. 250 mdnsSource = true 251 continue 252 } 253 // Some source we don't know how to deal with. 254 return fallbackOrder 255 } 256 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 { 261 return fallbackOrder 262 } 263 264 // Cases where Go can handle it without cgo and C thread 265 // overhead. 266 switch { 267 case filesSource && dnsSource: 268 if first == "files" { 269 return hostLookupFilesDNS 270 } else { 271 return hostLookupDNSFiles 272 } 273 case filesSource: 274 return hostLookupFiles 275 case dnsSource: 276 return hostLookupDNS 277 } 278 279 // Something weird. Let libc deal with it. 280 return fallbackOrder 281} 282 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 290// 1+cgo // same 291// cgo+2 // same, but debug level 2 292// etc. 293func goDebugNetDNS() (dnsMode string, debugLevel int) { 294 goDebug := goDebugString("netdns") 295 parsePart := func(s string) { 296 if s == "" { 297 return 298 } 299 if '0' <= s[0] && s[0] <= '9' { 300 debugLevel, _, _ = dtoi(s) 301 } else { 302 dnsMode = s 303 } 304 } 305 if i := bytealg.IndexByteString(goDebug, '+'); i != -1 { 306 parsePart(goDebug[:i]) 307 parsePart(goDebug[i+1:]) 308 return 309 } 310 parsePart(goDebug) 311 return 312} 313 314// isLocalhost reports whether h should be considered a "localhost" 315// name for the myhostname NSS module. 316func isLocalhost(h string) bool { 317 return stringsEqualFold(h, "localhost") || stringsEqualFold(h, "localhost.localdomain") || stringsHasSuffixFold(h, ".localhost") || stringsHasSuffixFold(h, ".localhost.localdomain") 318} 319 320// isGateway reports whether h should be considered a "gateway" 321// name for the myhostname NSS module. 322func isGateway(h string) bool { 323 return stringsEqualFold(h, "gateway") 324} 325