github.com/mdempsky/go@v0.0.0-20151201204031-5dd372bd1e70/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 if c.netGo { 128 return hostLookupFilesDNS 129 } 130 if c.forceCgoLookupHost || c.resolv.unknownOpt || c.goos == "android" { 131 return hostLookupCgo 132 } 133 if byteIndex(hostname, '\\') != -1 || byteIndex(hostname, '%') != -1 { 134 // Don't deal with special form hostnames with backslashes 135 // or '%'. 136 return hostLookupCgo 137 } 138 139 // OpenBSD is unique and doesn't use nsswitch.conf. 140 // It also doesn't support mDNS. 141 if c.goos == "openbsd" { 142 // OpenBSD's resolv.conf manpage says that a non-existent 143 // resolv.conf means "lookup" defaults to only "files", 144 // without DNS lookups. 145 if os.IsNotExist(c.resolv.err) { 146 return hostLookupFiles 147 } 148 lookup := c.resolv.lookup 149 if len(lookup) == 0 { 150 // http://www.openbsd.org/cgi-bin/man.cgi/OpenBSD-current/man5/resolv.conf.5 151 // "If the lookup keyword is not used in the 152 // system's resolv.conf file then the assumed 153 // order is 'bind file'" 154 return hostLookupDNSFiles 155 } 156 if len(lookup) < 1 || len(lookup) > 2 { 157 return hostLookupCgo 158 } 159 switch lookup[0] { 160 case "bind": 161 if len(lookup) == 2 { 162 if lookup[1] == "file" { 163 return hostLookupDNSFiles 164 } 165 return hostLookupCgo 166 } 167 return hostLookupDNS 168 case "file": 169 if len(lookup) == 2 { 170 if lookup[1] == "bind" { 171 return hostLookupFilesDNS 172 } 173 return hostLookupCgo 174 } 175 return hostLookupFiles 176 default: 177 return hostLookupCgo 178 } 179 } 180 181 hasDot := byteIndex(hostname, '.') != -1 182 183 // Canonicalize the hostname by removing any trailing dot. 184 if stringsHasSuffix(hostname, ".") { 185 hostname = hostname[:len(hostname)-1] 186 } 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. 192 return hostLookupCgo 193 } 194 195 nss := c.nss 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" 202 return hostLookupCgo 203 } 204 if c.goos == "linux" { 205 // glibc says the default is "dns [!UNAVAIL=return] files" 206 // http://www.gnu.org/software/libc/manual/html_node/Notes-on-NSS-Configuration-File.html. 207 return hostLookupDNSFiles 208 } 209 return hostLookupFilesDNS 210 } 211 if nss.err != nil { 212 // We failed to parse or open nsswitch.conf, so 213 // conservatively assume we should use cgo if it's 214 // available. 215 return hostLookupCgo 216 } 217 218 var mdnsSource, filesSource, dnsSource bool 219 var first string 220 for _, src := range srcs { 221 if src.source == "myhostname" { 222 if hasDot { 223 continue 224 } 225 return hostLookupCgo 226 } 227 if src.source == "files" || src.source == "dns" { 228 if !src.standardCriteria() { 229 return hostLookupCgo // non-standard; let libc deal with it. 230 } 231 if src.source == "files" { 232 filesSource = true 233 } else if src.source == "dns" { 234 dnsSource = true 235 } 236 if first == "" { 237 first = src.source 238 } 239 continue 240 } 241 if stringsHasPrefix(src.source, "mdns") { 242 // e.g. "mdns4", "mdns4_minimal" 243 // We already returned true before if it was *.local. 244 // libc wouldn't have found a hit on this anyway. 245 mdnsSource = true 246 continue 247 } 248 // Some source we don't know how to deal with. 249 return hostLookupCgo 250 } 251 252 // We don't parse mdns.allow files. They're rare. If one 253 // exists, it might list other TLDs (besides .local) or even 254 // '*', so just let libc deal with it. 255 if mdnsSource && c.hasMDNSAllow { 256 return hostLookupCgo 257 } 258 259 // Cases where Go can handle it without cgo and C thread 260 // overhead. 261 switch { 262 case filesSource && dnsSource: 263 if first == "files" { 264 return hostLookupFilesDNS 265 } else { 266 return hostLookupDNSFiles 267 } 268 case filesSource: 269 return hostLookupFiles 270 case dnsSource: 271 return hostLookupDNS 272 } 273 274 // Something weird. Let libc deal with it. 275 return hostLookupCgo 276 } 277 278 // goDebugNetDNS parses the value of the GODEBUG "netdns" value. 279 // The netdns value can be of the form: 280 // 1 // debug level 1 281 // 2 // debug level 2 282 // cgo // use cgo for DNS lookups 283 // go // use go for DNS lookups 284 // cgo+1 // use cgo for DNS lookups + debug level 1 285 // 1+cgo // same 286 // cgo+2 // same, but debug level 2 287 // etc. 288 func goDebugNetDNS() (dnsMode string, debugLevel int) { 289 goDebug := goDebugString("netdns") 290 parsePart := func(s string) { 291 if s == "" { 292 return 293 } 294 if '0' <= s[0] && s[0] <= '9' { 295 debugLevel, _, _ = dtoi(s, 0) 296 } else { 297 dnsMode = s 298 } 299 } 300 if i := byteIndex(goDebug, '+'); i != -1 { 301 parsePart(goDebug[:i]) 302 parsePart(goDebug[i+1:]) 303 return 304 } 305 parsePart(goDebug) 306 return 307 }