github.com/mtsmfm/go/src@v0.0.0-20221020090648-44bdcb9f8fde/net/conf.go (about) 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 //go:build !js 6 7 package net 8 9 import ( 10 "internal/bytealg" 11 "internal/godebug" 12 "os" 13 "runtime" 14 "sync" 15 "syscall" 16 ) 17 18 // conf represents a system's network configuration. 19 type conf struct { 20 // forceCgoLookupHost forces CGO to always be used, if available. 21 forceCgoLookupHost bool 22 23 netGo bool // go DNS resolution forced 24 netCgo bool // non-go DNS resolution forced (cgo, or win32) 25 26 // machine has an /etc/mdns.allow file 27 hasMDNSAllow bool 28 29 goos string // the runtime.GOOS, to ease testing 30 dnsDebugLevel int 31 32 nss *nssConf 33 resolv *dnsConfig 34 } 35 36 var ( 37 confOnce sync.Once // guards init of confVal via initConfVal 38 confVal = &conf{goos: runtime.GOOS} 39 ) 40 41 // systemConf returns the machine's network configuration. 42 func systemConf() *conf { 43 confOnce.Do(initConfVal) 44 return confVal 45 } 46 47 func initConfVal() { 48 dnsMode, debugLevel := goDebugNetDNS() 49 confVal.dnsDebugLevel = debugLevel 50 confVal.netGo = netGo || dnsMode == "go" 51 confVal.netCgo = netCgo || dnsMode == "cgo" 52 if !confVal.netGo && !confVal.netCgo && (runtime.GOOS == "windows" || runtime.GOOS == "plan9") { 53 // Neither of these platforms actually use cgo. 54 // 55 // The meaning of "cgo" mode in the net package is 56 // really "the native OS way", which for libc meant 57 // cgo on the original platforms that motivated 58 // PreferGo support before Windows and Plan9 got support, 59 // at which time the GODEBUG=netdns=go and GODEBUG=netdns=cgo 60 // names were already kinda locked in. 61 confVal.netCgo = true 62 } 63 64 if confVal.dnsDebugLevel > 0 { 65 defer func() { 66 if confVal.dnsDebugLevel > 1 { 67 println("go package net: confVal.netCgo =", confVal.netCgo, " netGo =", confVal.netGo) 68 } 69 switch { 70 case confVal.netGo: 71 if netGo { 72 println("go package net: built with netgo build tag; using Go's DNS resolver") 73 } else { 74 println("go package net: GODEBUG setting forcing use of Go's resolver") 75 } 76 case confVal.forceCgoLookupHost: 77 println("go package net: using cgo DNS resolver") 78 default: 79 println("go package net: dynamic selection of DNS resolver") 80 } 81 }() 82 } 83 84 // Darwin pops up annoying dialog boxes if programs try to do 85 // their own DNS requests. So always use cgo instead, which 86 // avoids that. 87 if runtime.GOOS == "darwin" || runtime.GOOS == "ios" { 88 confVal.forceCgoLookupHost = true 89 return 90 } 91 92 if runtime.GOOS == "windows" || runtime.GOOS == "plan9" { 93 return 94 } 95 96 // If any environment-specified resolver options are specified, 97 // force cgo. Note that LOCALDOMAIN can change behavior merely 98 // by being specified with the empty string. 99 _, localDomainDefined := syscall.Getenv("LOCALDOMAIN") 100 if os.Getenv("RES_OPTIONS") != "" || 101 os.Getenv("HOSTALIASES") != "" || 102 confVal.netCgo || 103 localDomainDefined { 104 confVal.forceCgoLookupHost = true 105 return 106 } 107 108 // OpenBSD apparently lets you override the location of resolv.conf 109 // with ASR_CONFIG. If we notice that, defer to libc. 110 if runtime.GOOS == "openbsd" && os.Getenv("ASR_CONFIG") != "" { 111 confVal.forceCgoLookupHost = true 112 return 113 } 114 115 if runtime.GOOS != "openbsd" { 116 confVal.nss = parseNSSConfFile("/etc/nsswitch.conf") 117 } 118 119 confVal.resolv = dnsReadConfig("/etc/resolv.conf") 120 if confVal.resolv.err != nil && !os.IsNotExist(confVal.resolv.err) && 121 !os.IsPermission(confVal.resolv.err) { 122 // If we can't read the resolv.conf file, assume it 123 // had something important in it and defer to cgo. 124 // libc's resolver might then fail too, but at least 125 // it wasn't our fault. 126 confVal.forceCgoLookupHost = true 127 } 128 129 if _, err := os.Stat("/etc/mdns.allow"); err == nil { 130 confVal.hasMDNSAllow = true 131 } 132 } 133 134 // canUseCgo reports whether calling cgo functions is allowed 135 // for non-hostname lookups. 136 func (c *conf) canUseCgo() bool { 137 return c.hostLookupOrder(nil, "") == hostLookupCgo 138 } 139 140 // hostLookupOrder determines which strategy to use to resolve hostname. 141 // The provided Resolver is optional. nil means to not consider its options. 142 func (c *conf) hostLookupOrder(r *Resolver, hostname string) (ret hostLookupOrder) { 143 if c.dnsDebugLevel > 1 { 144 defer func() { 145 print("go package net: hostLookupOrder(", hostname, ") = ", ret.String(), "\n") 146 }() 147 } 148 fallbackOrder := hostLookupCgo 149 if c.netGo || r.preferGo() { 150 switch c.goos { 151 case "windows": 152 // TODO(bradfitz): implement files-based 153 // lookup on Windows too? I guess /etc/hosts 154 // kinda exists on Windows. But for now, only 155 // do DNS. 156 fallbackOrder = hostLookupDNS 157 default: 158 fallbackOrder = hostLookupFilesDNS 159 } 160 } 161 if c.goos == "windows" || c.goos == "plan9" { 162 return fallbackOrder 163 } 164 if c.forceCgoLookupHost || c.resolv.unknownOpt || c.goos == "android" { 165 return fallbackOrder 166 } 167 if bytealg.IndexByteString(hostname, '\\') != -1 || bytealg.IndexByteString(hostname, '%') != -1 { 168 // Don't deal with special form hostnames with backslashes 169 // or '%'. 170 return fallbackOrder 171 } 172 173 // OpenBSD is unique and doesn't use nsswitch.conf. 174 // It also doesn't support mDNS. 175 if c.goos == "openbsd" { 176 // OpenBSD's resolv.conf manpage says that a non-existent 177 // resolv.conf means "lookup" defaults to only "files", 178 // without DNS lookups. 179 if os.IsNotExist(c.resolv.err) { 180 return hostLookupFiles 181 } 182 lookup := c.resolv.lookup 183 if len(lookup) == 0 { 184 // https://www.openbsd.org/cgi-bin/man.cgi/OpenBSD-current/man5/resolv.conf.5 185 // "If the lookup keyword is not used in the 186 // system's resolv.conf file then the assumed 187 // order is 'bind file'" 188 return hostLookupDNSFiles 189 } 190 if len(lookup) < 1 || len(lookup) > 2 { 191 return fallbackOrder 192 } 193 switch lookup[0] { 194 case "bind": 195 if len(lookup) == 2 { 196 if lookup[1] == "file" { 197 return hostLookupDNSFiles 198 } 199 return fallbackOrder 200 } 201 return hostLookupDNS 202 case "file": 203 if len(lookup) == 2 { 204 if lookup[1] == "bind" { 205 return hostLookupFilesDNS 206 } 207 return fallbackOrder 208 } 209 return hostLookupFiles 210 default: 211 return fallbackOrder 212 } 213 } 214 215 // Canonicalize the hostname by removing any trailing dot. 216 if stringsHasSuffix(hostname, ".") { 217 hostname = hostname[:len(hostname)-1] 218 } 219 if stringsHasSuffixFold(hostname, ".local") { 220 // Per RFC 6762, the ".local" TLD is special. And 221 // because Go's native resolver doesn't do mDNS or 222 // similar local resolution mechanisms, assume that 223 // libc might (via Avahi, etc) and use cgo. 224 return fallbackOrder 225 } 226 227 nss := c.nss 228 srcs := nss.sources["hosts"] 229 // If /etc/nsswitch.conf doesn't exist or doesn't specify any 230 // sources for "hosts", assume Go's DNS will work fine. 231 if os.IsNotExist(nss.err) || (nss.err == nil && len(srcs) == 0) { 232 if c.goos == "solaris" { 233 // illumos defaults to "nis [NOTFOUND=return] files" 234 return fallbackOrder 235 } 236 return hostLookupFilesDNS 237 } 238 if nss.err != nil { 239 // We failed to parse or open nsswitch.conf, so 240 // conservatively assume we should use cgo if it's 241 // available. 242 return fallbackOrder 243 } 244 245 var mdnsSource, filesSource, dnsSource bool 246 var first string 247 for _, src := range srcs { 248 if src.source == "myhostname" { 249 if isLocalhost(hostname) || isGateway(hostname) { 250 return fallbackOrder 251 } 252 hn, err := getHostname() 253 if err != nil || stringsEqualFold(hostname, hn) { 254 return fallbackOrder 255 } 256 continue 257 } 258 if src.source == "files" || src.source == "dns" { 259 if !src.standardCriteria() { 260 return fallbackOrder // non-standard; let libc deal with it. 261 } 262 if src.source == "files" { 263 filesSource = true 264 } else if src.source == "dns" { 265 dnsSource = true 266 } 267 if first == "" { 268 first = src.source 269 } 270 continue 271 } 272 if stringsHasPrefix(src.source, "mdns") { 273 // e.g. "mdns4", "mdns4_minimal" 274 // We already returned true before if it was *.local. 275 // libc wouldn't have found a hit on this anyway. 276 mdnsSource = true 277 continue 278 } 279 // Some source we don't know how to deal with. 280 return fallbackOrder 281 } 282 283 // We don't parse mdns.allow files. They're rare. If one 284 // exists, it might list other TLDs (besides .local) or even 285 // '*', so just let libc deal with it. 286 if mdnsSource && c.hasMDNSAllow { 287 return fallbackOrder 288 } 289 290 // Cases where Go can handle it without cgo and C thread 291 // overhead. 292 switch { 293 case filesSource && dnsSource: 294 if first == "files" { 295 return hostLookupFilesDNS 296 } else { 297 return hostLookupDNSFiles 298 } 299 case filesSource: 300 return hostLookupFiles 301 case dnsSource: 302 return hostLookupDNS 303 } 304 305 // Something weird. Let libc deal with it. 306 return fallbackOrder 307 } 308 309 // goDebugNetDNS parses the value of the GODEBUG "netdns" value. 310 // The netdns value can be of the form: 311 // 312 // 1 // debug level 1 313 // 2 // debug level 2 314 // cgo // use cgo for DNS lookups 315 // go // use go for DNS lookups 316 // cgo+1 // use cgo for DNS lookups + debug level 1 317 // 1+cgo // same 318 // cgo+2 // same, but debug level 2 319 // 320 // etc. 321 func goDebugNetDNS() (dnsMode string, debugLevel int) { 322 goDebug := godebug.Get("netdns") 323 parsePart := func(s string) { 324 if s == "" { 325 return 326 } 327 if '0' <= s[0] && s[0] <= '9' { 328 debugLevel, _, _ = dtoi(s) 329 } else { 330 dnsMode = s 331 } 332 } 333 if i := bytealg.IndexByteString(goDebug, '+'); i != -1 { 334 parsePart(goDebug[:i]) 335 parsePart(goDebug[i+1:]) 336 return 337 } 338 parsePart(goDebug) 339 return 340 } 341 342 // isLocalhost reports whether h should be considered a "localhost" 343 // name for the myhostname NSS module. 344 func isLocalhost(h string) bool { 345 return stringsEqualFold(h, "localhost") || stringsEqualFold(h, "localhost.localdomain") || stringsHasSuffixFold(h, ".localhost") || stringsHasSuffixFold(h, ".localhost.localdomain") 346 } 347 348 // isGateway reports whether h should be considered a "gateway" 349 // name for the myhostname NSS module. 350 func isGateway(h string) bool { 351 return stringsEqualFold(h, "gateway") 352 }