Skip to content

Commit b615ad8

Browse files
committed
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 <[email protected]> Run-TryBot: Brad Fitzpatrick <[email protected]>
1 parent 4c33250 commit b615ad8

File tree

7 files changed

+153
-3
lines changed

7 files changed

+153
-3
lines changed

src/net/cgo_stub.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@
66

77
package net
88

9+
func init() { netGo = true }
10+
911
type addrinfoErrno int
1012

1113
func (eai addrinfoErrno) Error() string { return "<nil>" }

src/net/conf.go

Lines changed: 71 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ package net
99
import (
1010
"os"
1111
"runtime"
12+
"strconv"
1213
"sync"
1314
"syscall"
1415
)
@@ -18,10 +19,14 @@ type conf struct {
1819
// forceCgoLookupHost forces CGO to always be used, if available.
1920
forceCgoLookupHost bool
2021

22+
netGo bool // "netgo" build tag in use (or no cgo)
23+
netCgo bool // cgo DNS resolution forced
24+
2125
// machine has an /etc/mdns.allow file
2226
hasMDNSAllow bool
2327

24-
goos string // the runtime.GOOS, to ease testing
28+
goos string // the runtime.GOOS, to ease testing
29+
dnsDebugLevel int
2530

2631
nss *nssConf
2732
resolv *dnsConfig
@@ -39,6 +44,28 @@ func systemConf() *conf {
3944
}
4045

4146
func initConfVal() {
47+
dnsMode, debugLevel := goDebugNetDNS()
48+
confVal.dnsDebugLevel = debugLevel
49+
confVal.netGo = netGo || dnsMode == "go"
50+
confVal.netCgo = netCgo || dnsMode == "cgo"
51+
52+
if confVal.dnsDebugLevel > 0 {
53+
defer func() {
54+
switch {
55+
case confVal.netGo:
56+
if netGo {
57+
println("go package net: built with netgo build tag; using Go's DNS resolver")
58+
} else {
59+
println("go package net: GODEBUG setting forcing use of Go's resolver")
60+
}
61+
case confVal.forceCgoLookupHost:
62+
println("go package net: using cgo DNS resolver")
63+
default:
64+
println("go package net: dynamic selection of DNS resolver")
65+
}
66+
}()
67+
}
68+
4269
// Darwin pops up annoying dialog boxes if programs try to do
4370
// their own DNS requests. So always use cgo instead, which
4471
// avoids that.
@@ -51,7 +78,9 @@ func initConfVal() {
5178
// force cgo. Note that LOCALDOMAIN can change behavior merely
5279
// by being specified with the empty string.
5380
_, localDomainDefined := syscall.Getenv("LOCALDOMAIN")
54-
if os.Getenv("RES_OPTIONS") != "" || os.Getenv("HOSTALIASES") != "" ||
81+
if os.Getenv("RES_OPTIONS") != "" ||
82+
os.Getenv("HOSTALIASES") != "" ||
83+
netCgo ||
5584
localDomainDefined {
5685
confVal.forceCgoLookupHost = true
5786
return
@@ -84,7 +113,15 @@ func initConfVal() {
84113
}
85114

86115
// hostLookupOrder determines which strategy to use to resolve hostname.
87-
func (c *conf) hostLookupOrder(hostname string) hostLookupOrder {
116+
func (c *conf) hostLookupOrder(hostname string) (ret hostLookupOrder) {
117+
if c.dnsDebugLevel > 1 {
118+
defer func() {
119+
print("go package net: hostLookupOrder(", hostname, ") = ", ret.String(), "\n")
120+
}()
121+
}
122+
if c.netGo {
123+
return hostLookupFilesDNS
124+
}
88125
if c.forceCgoLookupHost || c.resolv.unknownOpt || c.goos == "android" {
89126
return hostLookupCgo
90127
}
@@ -232,3 +269,34 @@ func (c *conf) hostLookupOrder(hostname string) hostLookupOrder {
232269
// Something weird. Let libc deal with it.
233270
return hostLookupCgo
234271
}
272+
273+
// goDebugNetDNS parses the value of the GODEBUG "netdns" value.
274+
// The netdns value can be of the form:
275+
// 1 // debug level 1
276+
// 2 // debug level 2
277+
// cgo // use cgo for DNS lookups
278+
// go // use go for DNS lookups
279+
// cgo+1 // use cgo for DNS lookups + debug level 1
280+
// 1+cgo // same
281+
// cgo+2 // same, but debug level 2
282+
// etc.
283+
func goDebugNetDNS() (dnsMode string, debugLevel int) {
284+
goDebug := goDebugString("netdns")
285+
parsePart := func(s string) {
286+
if s == "" {
287+
return
288+
}
289+
if '0' <= s[0] && s[0] <= '9' {
290+
debugLevel, _ = strconv.Atoi(s)
291+
} else {
292+
dnsMode = s
293+
}
294+
}
295+
if i := byteIndex(goDebug, '+'); i != -1 {
296+
parsePart(goDebug[:i])
297+
parsePart(goDebug[i+1:])
298+
return
299+
}
300+
parsePart(goDebug)
301+
return
302+
}

src/net/conf_netcgo.go

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
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 netcgo
6+
7+
package net
8+
9+
/*
10+
11+
// Fail if cgo isn't available.
12+
13+
*/
14+
import "C"
15+
16+
// The build tag "netcgo" forces use of the cgo DNS resolver.
17+
// It is the opposite of "netgo".
18+
func init() { netCgo = true }

src/net/conf_test.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -295,3 +295,7 @@ func TestConfHostLookupOrder(t *testing.T) {
295295
}
296296

297297
}
298+
299+
func TestSystemConf(t *testing.T) {
300+
systemConf()
301+
}

src/net/net.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,14 @@ import (
4646
"time"
4747
)
4848

49+
// netGo and netCgo contain the state of the build tags used
50+
// to build this binary, and whether cgo is available.
51+
// conf.go mirrors these into conf for easier testing.
52+
var (
53+
netGo bool // set true in cgo_stub.go for build tag "netgo" (or no cgo)
54+
netCgo bool // set true in conf_netcgo.go for build tag "netcgo"
55+
)
56+
4957
func init() {
5058
sysInit()
5159
supportsIPv4 = probeIPv4Stack()

src/net/parse.go

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -361,3 +361,26 @@ func readFull(r io.Reader) (all []byte, err error) {
361361
}
362362
}
363363
}
364+
365+
// goDebugString returns the value of the named GODEBUG key.
366+
// GODEBUG is of the form "key=val,key2=val2"
367+
func goDebugString(key string) string {
368+
s := os.Getenv("GODEBUG")
369+
for i := 0; i < len(s)-len(key)-1; i++ {
370+
if i > 0 && s[i-1] != ',' {
371+
continue
372+
}
373+
afterKey := s[i+len(key):]
374+
if afterKey[0] != '=' || s[i:i+len(key)] != key {
375+
continue
376+
}
377+
val := afterKey[1:]
378+
for i, b := range val {
379+
if b == ',' {
380+
return val[:i]
381+
}
382+
}
383+
return val
384+
}
385+
return ""
386+
}

src/net/parse_test.go

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,3 +50,30 @@ func TestReadLine(t *testing.T) {
5050
byteno += len(line) + 1
5151
}
5252
}
53+
54+
func TestGoDebugString(t *testing.T) {
55+
defer os.Setenv("GODEBUG", os.Getenv("GODEBUG"))
56+
tests := []struct {
57+
godebug string
58+
key string
59+
want string
60+
}{
61+
{"", "foo", ""},
62+
{"foo=", "foo", ""},
63+
{"foo=bar", "foo", "bar"},
64+
{"foo=bar,", "foo", "bar"},
65+
{"foo,foo=bar,", "foo", "bar"},
66+
{"foo1=bar,foo=bar,", "foo", "bar"},
67+
{"foo=bar,foo=bar,", "foo", "bar"},
68+
{"foo=", "foo", ""},
69+
{"foo", "foo", ""},
70+
{",foo", "foo", ""},
71+
{"foo=bar,baz", "loooooooong", ""},
72+
}
73+
for _, tt := range tests {
74+
os.Setenv("GODEBUG", tt.godebug)
75+
if got := goDebugString(tt.key); got != tt.want {
76+
t.Errorf("for %q, goDebugString(%q) = %q; want %q", tt.godebug, tt.key, got, tt.want)
77+
}
78+
}
79+
}

0 commit comments

Comments
 (0)