return 0, nil, false
}
-func cgoLookupIP(ctx context.Context, name string) (addrs []IPAddr, err error, completed bool) {
+func cgoLookupIP(ctx context.Context, network, name string) (addrs []IPAddr, err error, completed bool) {
return nil, nil, false
}
}
func cgoLookupHost(ctx context.Context, name string) (hosts []string, err error, completed bool) {
- addrs, err, completed := cgoLookupIP(ctx, name)
+ addrs, err, completed := cgoLookupIP(ctx, "ip", name)
for _, addr := range addrs {
hosts = append(hosts, addr.String())
}
default:
return 0, &DNSError{Err: "unknown network", Name: network + "/" + service}, true
}
- if len(network) >= 4 {
- switch network[3] {
- case '4':
- hints.ai_family = C.AF_INET
- case '6':
- hints.ai_family = C.AF_INET6
- }
+ switch ipVersion(network) {
+ case '4':
+ hints.ai_family = C.AF_INET
+ case '6':
+ hints.ai_family = C.AF_INET6
}
if ctx.Done() == nil {
port, err := cgoLookupServicePort(&hints, network, service)
result <- portLookupResult{port, err}
}
-func cgoLookupIPCNAME(name string) (addrs []IPAddr, cname string, err error) {
+func cgoLookupIPCNAME(network, name string) (addrs []IPAddr, cname string, err error) {
acquireThread()
defer releaseThread()
var hints C.struct_addrinfo
hints.ai_flags = cgoAddrInfoFlags
hints.ai_socktype = C.SOCK_STREAM
+ hints.ai_family = C.AF_UNSPEC
+ switch ipVersion(network) {
+ case '4':
+ hints.ai_family = C.AF_INET
+ case '6':
+ hints.ai_family = C.AF_INET6
+ }
h := make([]byte, len(name)+1)
copy(h, name)
return addrs, cname, nil
}
-func cgoIPLookup(result chan<- ipLookupResult, name string) {
- addrs, cname, err := cgoLookupIPCNAME(name)
+func cgoIPLookup(result chan<- ipLookupResult, network, name string) {
+ addrs, cname, err := cgoLookupIPCNAME(network, name)
result <- ipLookupResult{addrs, cname, err}
}
-func cgoLookupIP(ctx context.Context, name string) (addrs []IPAddr, err error, completed bool) {
+func cgoLookupIP(ctx context.Context, network, name string) (addrs []IPAddr, err error, completed bool) {
if ctx.Done() == nil {
- addrs, _, err = cgoLookupIPCNAME(name)
+ addrs, _, err = cgoLookupIPCNAME(network, name)
return addrs, err, true
}
result := make(chan ipLookupResult, 1)
- go cgoIPLookup(result, name)
+ go cgoIPLookup(result, network, name)
select {
case r := <-result:
return r.addrs, r.err, true
func cgoLookupCNAME(ctx context.Context, name string) (cname string, err error, completed bool) {
if ctx.Done() == nil {
- _, cname, err = cgoLookupIPCNAME(name)
+ _, cname, err = cgoLookupIPCNAME("ip", name)
return cname, err, true
}
result := make(chan ipLookupResult, 1)
- go cgoIPLookup(result, name)
+ go cgoIPLookup(result, "ip", name)
select {
case r := <-result:
return r.cname, r.err, true
func TestCgoLookupIP(t *testing.T) {
defer dnsWaitGroup.Wait()
ctx := context.Background()
- _, err, ok := cgoLookupIP(ctx, "localhost")
+ _, err, ok := cgoLookupIP(ctx, "ip", "localhost")
if !ok {
t.Errorf("cgoLookupIP must not be a placeholder")
}
defer dnsWaitGroup.Wait()
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
- _, err, ok := cgoLookupIP(ctx, "localhost")
+ _, err, ok := cgoLookupIP(ctx, "ip", "localhost")
if !ok {
t.Errorf("cgoLookupIP must not be a placeholder")
}
}
}
-func lookupSlowFast(ctx context.Context, fn func(context.Context, string) ([]IPAddr, error), host string) ([]IPAddr, error) {
+func lookupSlowFast(ctx context.Context, fn func(context.Context, string, string) ([]IPAddr, error), network, host string) ([]IPAddr, error) {
switch host {
case "slow6loopback4":
// Returns a slow IPv6 address, and a local IPv4 address.
{IP: ParseIP("127.0.0.1")},
}, nil
default:
- return fn(ctx, host)
+ return fn(ctx, network, host)
}
}
origTestHookLookupIP := testHookLookupIP
defer func() { testHookLookupIP = origTestHookLookupIP }()
- testHookLookupIP = func(ctx context.Context, fn func(context.Context, string) ([]IPAddr, error), host string) ([]IPAddr, error) {
+ testHookLookupIP = func(ctx context.Context, fn func(context.Context, string, string) ([]IPAddr, error), network, host string) ([]IPAddr, error) {
return nil, &DNSError{Err: "dial error test", Name: "name", Server: "server", IsTimeout: true}
}
sw.Set(socktest.FilterConnect, func(so *socktest.Status) (socktest.AfterFilter, error) {
origTestHookLookupIP := testHookLookupIP
defer func() { testHookLookupIP = origTestHookLookupIP }()
- testHookLookupIP = func(_ context.Context, fn func(context.Context, string) ([]IPAddr, error), host string) ([]IPAddr, error) {
+ testHookLookupIP = func(_ context.Context, fn func(context.Context, string, string) ([]IPAddr, error), network, host string) ([]IPAddr, error) {
return nil, &DNSError{Err: "listen error test", Name: "name", Server: "server", IsTimeout: true}
}
sw.Set(socktest.FilterListen, func(so *socktest.Status) (socktest.AfterFilter, error) {
origTestHookLookupIP := testHookLookupIP
defer func() { testHookLookupIP = origTestHookLookupIP }()
- testHookLookupIP = func(_ context.Context, fn func(context.Context, string) ([]IPAddr, error), host string) ([]IPAddr, error) {
+ testHookLookupIP = func(_ context.Context, fn func(context.Context, string, string) ([]IPAddr, error), network, host string) ([]IPAddr, error) {
return nil, &DNSError{Err: "listen error test", Name: "name", Server: "server", IsTimeout: true}
}
testHookHostsPath = "/etc/hosts"
testHookLookupIP = func(
ctx context.Context,
- fn func(context.Context, string) ([]IPAddr, error),
+ fn func(context.Context, string, string) ([]IPAddr, error),
+ network string,
host string,
) ([]IPAddr, error) {
- return fn(ctx, host)
+ return fn(ctx, network, host)
}
testHookSetKeepAlive = func() {}
)
}
// Install a fake DNS server.
- ctx := context.WithValue(context.Background(), nettrace.LookupIPAltResolverKey{}, func(ctx context.Context, host string) ([]net.IPAddr, error) {
+ ctx := context.WithValue(context.Background(), nettrace.LookupIPAltResolverKey{}, func(ctx context.Context, network, host string) ([]net.IPAddr, error) {
if host != "dns-is-faked.golang" {
- t.Errorf("unexpected DNS host lookup for %q", host)
+ t.Errorf("unexpected DNS host lookup for %q/%q", network, host)
return nil, nil
}
return []net.IPAddr{{IP: net.ParseIP(ip)}}, nil
if err != nil {
t.Fatal(err)
}
- ctx := context.WithValue(context.Background(), nettrace.LookupIPAltResolverKey{}, func(ctx context.Context, host string) ([]net.IPAddr, error) {
+ ctx := context.WithValue(context.Background(), nettrace.LookupIPAltResolverKey{}, func(ctx context.Context, _, host string) ([]net.IPAddr, error) {
return []net.IPAddr{{IP: net.ParseIP(ip)}}, nil
})
}
// Install a fake DNS server.
- ctx := context.WithValue(context.Background(), nettrace.LookupIPAltResolverKey{}, func(ctx context.Context, host string) ([]net.IPAddr, error) {
+ ctx := context.WithValue(context.Background(), nettrace.LookupIPAltResolverKey{}, func(ctx context.Context, network, host string) ([]net.IPAddr, error) {
if host != punyDomain {
- t.Errorf("got DNS host lookup for %q; want %q", host, punyDomain)
+ t.Errorf("got DNS host lookup for %q/%q; want %q", network, host, punyDomain)
return nil, nil
}
return []net.IPAddr{{IP: net.ParseIP(ip)}}, nil
}
// Try as a literal IP address, then as a DNS name.
- ips, err := r.LookupIPAddr(ctx, host)
+ ips, err := r.lookupIPAddr(ctx, net, host)
if err != nil {
return nil, err
}
return 0, &AddrError{Err: "unknown port", Addr: network + "/" + service}
}
+// ipVersion returns the provided network's IP version: '4', '6' or 0
+// if network does not end in a '4' or '6' byte.
+func ipVersion(network string) byte {
+ if network == "" {
+ return 0
+ }
+ n := network[len(network)-1]
+ if n != '4' && n != '6' {
+ n = 0
+ }
+ return n
+}
+
// DefaultResolver is the resolver used by the package-level Lookup
// functions and by Dialers without a specified Resolver.
var DefaultResolver = &Resolver{}
// LookupIPAddr looks up host using the local resolver.
// It returns a slice of that host's IPv4 and IPv6 addresses.
func (r *Resolver) LookupIPAddr(ctx context.Context, host string) ([]IPAddr, error) {
+ return r.lookupIPAddr(ctx, "ip", host)
+}
+
+// lookupIPAddr looks up host using the local resolver and particular network.
+// It returns a slice of that host's IPv4 and IPv6 addresses.
+func (r *Resolver) lookupIPAddr(ctx context.Context, network, host string) ([]IPAddr, error) {
// Make sure that no matter what we do later, host=="" is rejected.
// parseIP, for example, does accept empty strings.
if host == "" {
// can be overridden by tests. This is needed by net/http, so it
// uses a context key instead of unexported variables.
resolverFunc := r.lookupIP
- if alt, _ := ctx.Value(nettrace.LookupIPAltResolverKey{}).(func(context.Context, string) ([]IPAddr, error)); alt != nil {
+ if alt, _ := ctx.Value(nettrace.LookupIPAltResolverKey{}).(func(context.Context, string, string) ([]IPAddr, error)); alt != nil {
resolverFunc = alt
}
dnsWaitGroup.Add(1)
ch, called := r.getLookupGroup().DoChan(host, func() (interface{}, error) {
defer dnsWaitGroup.Done()
- return testHookLookupIP(lookupGroupCtx, resolverFunc, host)
+ return testHookLookupIP(lookupGroupCtx, resolverFunc, network, host)
})
if !called {
dnsWaitGroup.Done()
func (r *Resolver) LookupPort(ctx context.Context, network, service string) (port int, err error) {
port, needsLookup := parsePort(service)
if needsLookup {
+ switch network {
+ case "tcp", "tcp4", "tcp6", "udp", "udp4", "udp6":
+ case "": // a hint wildcard for Go 1.0 undocumented behavior
+ network = "ip"
+ default:
+ return 0, &AddrError{Err: "unknown network", Addr: network}
+ }
port, err = r.lookupPort(ctx, network, service)
if err != nil {
return 0, err
return nil, syscall.ENOPROTOOPT
}
-func (*Resolver) lookupIP(ctx context.Context, host string) (addrs []IPAddr, err error) {
+func (*Resolver) lookupIP(ctx context.Context, network, host string) (addrs []IPAddr, err error) {
return nil, syscall.ENOPROTOOPT
}
return
}
-func (r *Resolver) lookupIP(ctx context.Context, host string) (addrs []IPAddr, err error) {
+func (r *Resolver) lookupIP(ctx context.Context, _, host string) (addrs []IPAddr, err error) {
lits, err := r.lookupHost(ctx, host)
if err != nil {
return
"time"
)
-func lookupLocalhost(ctx context.Context, fn func(context.Context, string) ([]IPAddr, error), host string) ([]IPAddr, error) {
+func lookupLocalhost(ctx context.Context, fn func(context.Context, string, string) ([]IPAddr, error), network, host string) ([]IPAddr, error) {
switch host {
case "localhost":
return []IPAddr{
{IP: IPv6loopback},
}, nil
default:
- return fn(ctx, host)
+ return fn(ctx, network, host)
}
}
}
}
}
+
+var ipVersionTests = []struct {
+ network string
+ version byte
+}{
+ {"tcp", 0},
+ {"tcp4", '4'},
+ {"tcp6", '6'},
+ {"udp", 0},
+ {"udp4", '4'},
+ {"udp6", '6'},
+ {"ip", 0},
+ {"ip4", '4'},
+ {"ip6", '6'},
+ {"ip7", 0},
+ {"", 0},
+}
+
+func TestIPVersion(t *testing.T) {
+ for _, tt := range ipVersionTests {
+ if version := ipVersion(tt.network); version != tt.version {
+ t.Errorf("Family for: %s. Expected: %s, Got: %s", tt.network,
+ string(tt.version), string(version))
+ }
+ }
+}
return r.goLookupHostOrder(ctx, host, order)
}
-func (r *Resolver) lookupIP(ctx context.Context, host string) (addrs []IPAddr, err error) {
+func (r *Resolver) lookupIP(ctx context.Context, network, host string) (addrs []IPAddr, err error) {
if r.preferGo() {
return r.goLookupIP(ctx, host)
}
order := systemConf().hostLookupOrder(r, host)
if order == hostLookupCgo {
- if addrs, err, ok := cgoLookupIP(ctx, host); ok {
+ if addrs, err, ok := cgoLookupIP(ctx, network, host); ok {
return addrs, err
}
// cgo not available (or netgo); fall back to Go's DNS resolver
}
func (r *Resolver) lookupHost(ctx context.Context, name string) ([]string, error) {
- ips, err := r.lookupIP(ctx, name)
+ ips, err := r.lookupIP(ctx, "ip", name)
if err != nil {
return nil, err
}
return addrs, nil
}
-func (r *Resolver) lookupIP(ctx context.Context, name string) ([]IPAddr, error) {
+func (r *Resolver) lookupIP(ctx context.Context, network, name string) ([]IPAddr, error) {
// TODO(bradfitz,brainman): use ctx more. See TODO below.
+ var family int32 = syscall.AF_UNSPEC
+ switch ipVersion(network) {
+ case '4':
+ family = syscall.AF_INET
+ case '6':
+ family = syscall.AF_INET6
+ }
+
getaddr := func() ([]IPAddr, error) {
acquireThread()
defer releaseThread()
hints := syscall.AddrinfoW{
- Family: syscall.AF_UNSPEC,
+ Family: family,
Socktype: syscall.SOCK_STREAM,
Protocol: syscall.IPPROTO_IP,
}
defer dnsWaitGroup.Wait()
host := "localhost"
ctx := context.Background()
- _, err, ok := cgoLookupIP(ctx, host)
+ _, err, ok := cgoLookupIP(ctx, "ip", host)
if ok {
t.Errorf("cgoLookupIP must be a placeholder")
}