mirror of
https://github.com/golang/go
synced 2024-11-19 07:04:43 -07:00
net: add mechanisms to force go or cgo lookup, and to debug default strategy
GODEBUG=netdns=1 prints a one-time strategy decision. (cgo or go DNS lookups) GODEBUG=netdns=2 prints the per-lookup strategy as a function of the hostname. The new "netcgo" build tag forces cgo DNS lookups. GODEBUG=netdns=go (or existing build tag "netgo") forces Go DNS resolution. GODEBUG=netdns=cgo (or new build tag "netcgo") forces libc DNS resolution. Options can be combined with e.g. GODEBUG=netdns=go+1 or GODEBUG=netdns=2+cgo. Fixes #11322 Fixes #11450 Change-Id: I7a67e9f759fd0a02320e7803f9ded1638b19e861 Reviewed-on: https://go-review.googlesource.com/11584 Reviewed-by: Russ Cox <rsc@golang.org> Run-TryBot: Brad Fitzpatrick <bradfitz@golang.org>
This commit is contained in:
parent
4c332508f9
commit
b615ad8fd5
@ -6,6 +6,8 @@
|
||||
|
||||
package net
|
||||
|
||||
func init() { netGo = true }
|
||||
|
||||
type addrinfoErrno int
|
||||
|
||||
func (eai addrinfoErrno) Error() string { return "<nil>" }
|
||||
|
@ -9,6 +9,7 @@ package net
|
||||
import (
|
||||
"os"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"sync"
|
||||
"syscall"
|
||||
)
|
||||
@ -18,10 +19,14 @@ type conf struct {
|
||||
// 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
|
||||
@ -39,6 +44,28 @@ func systemConf() *conf {
|
||||
}
|
||||
|
||||
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.
|
||||
@ -51,7 +78,9 @@ func initConfVal() {
|
||||
// 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
|
||||
@ -84,7 +113,15 @@ func initConfVal() {
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
@ -232,3 +269,34 @@ func (c *conf) hostLookupOrder(hostname string) hostLookupOrder {
|
||||
// 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
|
||||
}
|
||||
|
18
src/net/conf_netcgo.go
Normal file
18
src/net/conf_netcgo.go
Normal file
@ -0,0 +1,18 @@
|
||||
// Copyright 2015 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build netcgo
|
||||
|
||||
package net
|
||||
|
||||
/*
|
||||
|
||||
// Fail if cgo isn't available.
|
||||
|
||||
*/
|
||||
import "C"
|
||||
|
||||
// The build tag "netcgo" forces use of the cgo DNS resolver.
|
||||
// It is the opposite of "netgo".
|
||||
func init() { netCgo = true }
|
@ -295,3 +295,7 @@ func TestConfHostLookupOrder(t *testing.T) {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestSystemConf(t *testing.T) {
|
||||
systemConf()
|
||||
}
|
||||
|
@ -46,6 +46,14 @@ import (
|
||||
"time"
|
||||
)
|
||||
|
||||
// netGo and netCgo contain the state of the build tags used
|
||||
// to build this binary, and whether cgo is available.
|
||||
// conf.go mirrors these into conf for easier testing.
|
||||
var (
|
||||
netGo bool // set true in cgo_stub.go for build tag "netgo" (or no cgo)
|
||||
netCgo bool // set true in conf_netcgo.go for build tag "netcgo"
|
||||
)
|
||||
|
||||
func init() {
|
||||
sysInit()
|
||||
supportsIPv4 = probeIPv4Stack()
|
||||
|
@ -361,3 +361,26 @@ func readFull(r io.Reader) (all []byte, err error) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 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 ""
|
||||
}
|
||||
|
@ -50,3 +50,30 @@ func TestReadLine(t *testing.T) {
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user