import (
"os"
"runtime"
+ "strconv"
"sync"
"syscall"
)
// forceCgoLookupHost forces CGO to always be used, if available.
forceCgoLookupHost bool
+ netGo bool // "netgo" build tag in use (or no cgo)
+ netCgo bool // cgo DNS resolution forced
+
// machine has an /etc/mdns.allow file
hasMDNSAllow bool
- goos string // the runtime.GOOS, to ease testing
+ goos string // the runtime.GOOS, to ease testing
+ dnsDebugLevel int
nss *nssConf
resolv *dnsConfig
}
func initConfVal() {
+ dnsMode, debugLevel := goDebugNetDNS()
+ confVal.dnsDebugLevel = debugLevel
+ confVal.netGo = netGo || dnsMode == "go"
+ confVal.netCgo = netCgo || dnsMode == "cgo"
+
+ if confVal.dnsDebugLevel > 0 {
+ defer func() {
+ switch {
+ case confVal.netGo:
+ if netGo {
+ println("go package net: built with netgo build tag; using Go's DNS resolver")
+ } else {
+ println("go package net: GODEBUG setting forcing use of Go's resolver")
+ }
+ case confVal.forceCgoLookupHost:
+ println("go package net: using cgo DNS resolver")
+ default:
+ println("go package net: dynamic selection of DNS resolver")
+ }
+ }()
+ }
+
// Darwin pops up annoying dialog boxes if programs try to do
// their own DNS requests. So always use cgo instead, which
// avoids that.
// force cgo. Note that LOCALDOMAIN can change behavior merely
// by being specified with the empty string.
_, localDomainDefined := syscall.Getenv("LOCALDOMAIN")
- if os.Getenv("RES_OPTIONS") != "" || os.Getenv("HOSTALIASES") != "" ||
+ if os.Getenv("RES_OPTIONS") != "" ||
+ os.Getenv("HOSTALIASES") != "" ||
+ netCgo ||
localDomainDefined {
confVal.forceCgoLookupHost = true
return
}
// hostLookupOrder determines which strategy to use to resolve hostname.
-func (c *conf) hostLookupOrder(hostname string) hostLookupOrder {
+func (c *conf) hostLookupOrder(hostname string) (ret hostLookupOrder) {
+ if c.dnsDebugLevel > 1 {
+ defer func() {
+ print("go package net: hostLookupOrder(", hostname, ") = ", ret.String(), "\n")
+ }()
+ }
+ if c.netGo {
+ return hostLookupFilesDNS
+ }
if c.forceCgoLookupHost || c.resolv.unknownOpt || c.goos == "android" {
return hostLookupCgo
}
// Something weird. Let libc deal with it.
return hostLookupCgo
}
+
+// goDebugNetDNS parses the value of the GODEBUG "netdns" value.
+// The netdns value can be of the form:
+// 1 // debug level 1
+// 2 // debug level 2
+// cgo // use cgo for DNS lookups
+// go // use go for DNS lookups
+// cgo+1 // use cgo for DNS lookups + debug level 1
+// 1+cgo // same
+// cgo+2 // same, but debug level 2
+// etc.
+func goDebugNetDNS() (dnsMode string, debugLevel int) {
+ goDebug := goDebugString("netdns")
+ parsePart := func(s string) {
+ if s == "" {
+ return
+ }
+ if '0' <= s[0] && s[0] <= '9' {
+ debugLevel, _ = strconv.Atoi(s)
+ } else {
+ dnsMode = s
+ }
+ }
+ if i := byteIndex(goDebug, '+'); i != -1 {
+ parsePart(goDebug[:i])
+ parsePart(goDebug[i+1:])
+ return
+ }
+ parsePart(goDebug)
+ return
+}
}
}
}
+
+// goDebugString returns the value of the named GODEBUG key.
+// GODEBUG is of the form "key=val,key2=val2"
+func goDebugString(key string) string {
+ s := os.Getenv("GODEBUG")
+ for i := 0; i < len(s)-len(key)-1; i++ {
+ if i > 0 && s[i-1] != ',' {
+ continue
+ }
+ afterKey := s[i+len(key):]
+ if afterKey[0] != '=' || s[i:i+len(key)] != key {
+ continue
+ }
+ val := afterKey[1:]
+ for i, b := range val {
+ if b == ',' {
+ return val[:i]
+ }
+ }
+ return val
+ }
+ return ""
+}
byteno += len(line) + 1
}
}
+
+func TestGoDebugString(t *testing.T) {
+ defer os.Setenv("GODEBUG", os.Getenv("GODEBUG"))
+ tests := []struct {
+ godebug string
+ key string
+ want string
+ }{
+ {"", "foo", ""},
+ {"foo=", "foo", ""},
+ {"foo=bar", "foo", "bar"},
+ {"foo=bar,", "foo", "bar"},
+ {"foo,foo=bar,", "foo", "bar"},
+ {"foo1=bar,foo=bar,", "foo", "bar"},
+ {"foo=bar,foo=bar,", "foo", "bar"},
+ {"foo=", "foo", ""},
+ {"foo", "foo", ""},
+ {",foo", "foo", ""},
+ {"foo=bar,baz", "loooooooong", ""},
+ }
+ for _, tt := range tests {
+ os.Setenv("GODEBUG", tt.godebug)
+ if got := goDebugString(tt.key); got != tt.want {
+ t.Errorf("for %q, goDebugString(%q) = %q; want %q", tt.godebug, tt.key, got, tt.want)
+ }
+ }
+}