github.com/gocuntian/go@v0.0.0-20160610041250-fee02d270bf8/src/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 // +build darwin dragonfly freebsd linux netbsd openbsd solaris 6 7 package net 8 9 import ( 10 "os" 11 "runtime" 12 "sync" 13 "syscall" 14 ) 15 16 // conf represents a system's network configuration. 17 type conf struct { 18 // forceCgoLookupHost forces CGO to always be used, if available. 19 forceCgoLookupHost bool 20 21 netGo bool // go DNS resolution forced 22 netCgo bool // cgo DNS resolution forced 23 24 // machine has an /etc/mdns.allow file 25 hasMDNSAllow bool 26 27 goos string // the runtime.GOOS, to ease testing 28 dnsDebugLevel int 29 30 nss *nssConf 31 resolv *dnsConfig 32 } 33 34 var ( 35 confOnce sync.Once // guards init of confVal via initConfVal 36 confVal = &conf{goos: runtime.GOOS} 37 ) 38 39 // systemConf returns the machine's network configuration. 40 func systemConf() *conf { 41 confOnce.Do(initConfVal) 42 return confVal 43 } 44 45 func initConfVal() { 46 dnsMode, debugLevel := goDebugNetDNS() 47 confVal.dnsDebugLevel = debugLevel 48 confVal.netGo = netGo || dnsMode == "go" 49 confVal.netCgo = netCgo || dnsMode == "cgo" 50 51 if confVal.dnsDebugLevel > 0 { 52 defer func() { 53 switch { 54 case confVal.netGo: 55 if netGo { 56 println("go package net: built with netgo build tag; using Go's DNS resolver") 57 } else { 58 println("go package net: GODEBUG setting forcing use of Go's resolver") 59 } 60 case confVal.forceCgoLookupHost: 61 println("go package net: using cgo DNS resolver") 62 default: 63 println("go package net: dynamic selection of DNS resolver") 64 } 65 }() 66 } 67 68 // Darwin pops up annoying dialog boxes if programs try to do 69 // their own DNS requests. So always use cgo instead, which 70 // avoids that. 71 if runtime.GOOS == "darwin" { 72 confVal.forceCgoLookupHost = true 73 return 74 } 75 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") != "" || 82 confVal.netCgo || 83 localDomainDefined { 84 confVal.forceCgoLookupHost = true 85 return 86 } 87 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 92 return 93 } 94 95 if runtime.GOOS != "openbsd" { 96 confVal.nss = parseNSSConfFile("/etc/nsswitch.conf") 97 } 98 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 107 } 108 109 if _, err := os.Stat("/etc/mdns.allow"); err == nil { 110 confVal.hasMDNSAllow = true 111 } 112 } 113 114 // canUseCgo reports whether calling cgo functions is allowed 115 // for non-hostname lookups. 116 func (c *conf) canUseCgo() bool { 117 return c.hostLookupOrder("") == hostLookupCgo 118 } 119 120 // hostLookupOrder determines which strategy to use to resolve hostname. 121 func (c *conf) hostLookupOrder(hostname string) (ret hostLookupOrder) { 122 if c.dnsDebugLevel > 1 { 123 defer func() { 124 print("go package net: hostLookupOrder(", hostname, ") = ", ret.String(), "\n") 125 }() 126 } 127 fallbackOrder := hostLookupCgo 128 if c.netGo { 129 fallbackOrder = hostLookupFilesDNS 130 } 131 if c.forceCgoLookupHost || c.resolv.unknownOpt || c.goos == "android" { 132 return fallbackOrder 133 } 134 if byteIndex(hostname, '\\') != -1 || byteIndex(hostname, '%') != -1 { 135 // Don't deal with special form hostnames with backslashes 136 // or '%'. 137 return fallbackOrder 138 } 139 140 // OpenBSD is unique and doesn't use nsswitch.conf. 141 // It also doesn't support mDNS. 142 if c.goos == "openbsd" { 143 // OpenBSD's resolv.conf manpage says that a non-existent 144 // resolv.conf means "lookup" defaults to only "files", 145 // without DNS lookups. 146 if os.IsNotExist(c.resolv.err) { 147 return hostLookupFiles 148 } 149 lookup := c.resolv.lookup 150 if len(lookup) == 0 { 151 // http://www.openbsd.org/cgi-bin/man.cgi/OpenBSD-current/man5/resolv.conf.5 152 // "If the lookup keyword is not used in the 153 // system's resolv.conf file then the assumed 154 // order is 'bind file'" 155 return hostLookupDNSFiles 156 } 157 if len(lookup) < 1 || len(lookup) > 2 { 158 return fallbackOrder 159 } 160 switch lookup[0] { 161 case "bind": 162 if len(lookup) == 2 { 163 if lookup[1] == "file" { 164 return hostLookupDNSFiles 165 } 166 return fallbackOrder 167 } 168 return hostLookupDNS 169 case "file": 170 if len(lookup) == 2 { 171 if lookup[1] == "bind" { 172 return hostLookupFilesDNS 173 } 174 return fallbackOrder 175 } 176 return hostLookupFiles 177 default: 178 return fallbackOrder 179 } 180 } 181 182 hasDot := byteIndex(hostname, '.') != -1 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 // http://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 hostname == "" || hasDot { 224 continue 225 } 226 return fallbackOrder 227 } 228 if src.source == "files" || src.source == "dns" { 229 if !src.standardCriteria() { 230 return fallbackOrder // non-standard; let libc deal with it. 231 } 232 if src.source == "files" { 233 filesSource = true 234 } else if src.source == "dns" { 235 dnsSource = true 236 } 237 if first == "" { 238 first = src.source 239 } 240 continue 241 } 242 if stringsHasPrefix(src.source, "mdns") { 243 // e.g. "mdns4", "mdns4_minimal" 244 // We already returned true before if it was *.local. 245 // libc wouldn't have found a hit on this anyway. 246 mdnsSource = true 247 continue 248 } 249 // Some source we don't know how to deal with. 250 return fallbackOrder 251 } 252 253 // We don't parse mdns.allow files. They're rare. If one 254 // exists, it might list other TLDs (besides .local) or even 255 // '*', so just let libc deal with it. 256 if mdnsSource && c.hasMDNSAllow { 257 return fallbackOrder 258 } 259 260 // Cases where Go can handle it without cgo and C thread 261 // overhead. 262 switch { 263 case filesSource && dnsSource: 264 if first == "files" { 265 return hostLookupFilesDNS 266 } else { 267 return hostLookupDNSFiles 268 } 269 case filesSource: 270 return hostLookupFiles 271 case dnsSource: 272 return hostLookupDNS 273 } 274 275 // Something weird. Let libc deal with it. 276 return fallbackOrder 277 } 278 279 // goDebugNetDNS parses the value of the GODEBUG "netdns" value. 280 // The netdns value can be of the form: 281 // 1 // debug level 1 282 // 2 // debug level 2 283 // cgo // use cgo for DNS lookups 284 // go // use go for DNS lookups 285 // cgo+1 // use cgo for DNS lookups + debug level 1 286 // 1+cgo // same 287 // cgo+2 // same, but debug level 2 288 // etc. 289 func goDebugNetDNS() (dnsMode string, debugLevel int) { 290 goDebug := goDebugString("netdns") 291 parsePart := func(s string) { 292 if s == "" { 293 return 294 } 295 if '0' <= s[0] && s[0] <= '9' { 296 debugLevel, _, _ = dtoi(s, 0) 297 } else { 298 dnsMode = s 299 } 300 } 301 if i := byteIndex(goDebug, '+'); i != -1 { 302 parsePart(goDebug[:i]) 303 parsePart(goDebug[i+1:]) 304 return 305 } 306 parsePart(goDebug) 307 return 308 }