]> Cypherpunks repositories - gostls13.git/commitdiff
net: add mechanisms to force go or cgo lookup, and to debug default strategy
authorBrad Fitzpatrick <bradfitz@golang.org>
Thu, 25 Jun 2015 10:52:54 +0000 (12:52 +0200)
committerBrad Fitzpatrick <bradfitz@golang.org>
Thu, 9 Jul 2015 22:19:41 +0000 (22:19 +0000)
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>

src/net/cgo_stub.go
src/net/conf.go
src/net/conf_netcgo.go [new file with mode: 0644]
src/net/conf_test.go
src/net/net.go
src/net/parse.go
src/net/parse_test.go

index 6ee052d138399a1beee6031bd727e2b2a5c7f2b9..b86ff7daf19362b252fc9104da1abfecb9f21e62 100644 (file)
@@ -6,6 +6,8 @@
 
 package net
 
+func init() { netGo = true }
+
 type addrinfoErrno int
 
 func (eai addrinfoErrno) Error() string   { return "<nil>" }
index ca7fa8708fab6cc5d80c0978ea346df340963824..e70178d34c3b44ab21824009fc5f17ef6b5e1354 100644 (file)
@@ -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
+}
diff --git a/src/net/conf_netcgo.go b/src/net/conf_netcgo.go
new file mode 100644 (file)
index 0000000..678361f
--- /dev/null
@@ -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 }
index 003c615eb841a49b3a96d8920ddcd0901fbd3d29..86904bffde7a7e2a2dc06c67ac252854535e3c9c 100644 (file)
@@ -295,3 +295,7 @@ func TestConfHostLookupOrder(t *testing.T) {
        }
 
 }
+
+func TestSystemConf(t *testing.T) {
+       systemConf()
+}
index cd1372fd02423e832ce6765157be953b84837908..75510a22232d741e8497ee05753d20f18fc4b02b 100644 (file)
@@ -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()
index 5b834e64d4a4adc23dc7111c326a502816821ba1..c72e1c2eaf0150e3ec8cced64bc698d091ae25d1 100644 (file)
@@ -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 ""
+}
index f5359d8c36e7fa3a3bb3fe4f364d4f63dbfbe566..0f048fcea0bbb0cb78eeb30f926565a01cc54abf 100644 (file)
@@ -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)
+               }
+       }
+}