Commit 2f9dee92 authored by Matthew Dempsky's avatar Matthew Dempsky

net: make LookupCNAME's native behavior match its cgo behavior

Fixes #18172.

Change-Id: I4a21fb5c0753cced025a03d88a6dd1aa3ee01d05
Reviewed-on: https://go-review.googlesource.com/34650Reviewed-by: 's avatarBrad Fitzpatrick <bradfitz@golang.org>
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
parent 0c942e8f
...@@ -444,7 +444,7 @@ func goLookupHostOrder(ctx context.Context, name string, order hostLookupOrder) ...@@ -444,7 +444,7 @@ func goLookupHostOrder(ctx context.Context, name string, order hostLookupOrder)
return return
} }
} }
ips, err := goLookupIPOrder(ctx, name, order) ips, _, err := goLookupIPCNAMEOrder(ctx, name, order)
if err != nil { if err != nil {
return return
} }
...@@ -472,27 +472,28 @@ func goLookupIPFiles(name string) (addrs []IPAddr) { ...@@ -472,27 +472,28 @@ func goLookupIPFiles(name string) (addrs []IPAddr) {
// The libc versions are in cgo_*.go. // The libc versions are in cgo_*.go.
func goLookupIP(ctx context.Context, host string) (addrs []IPAddr, err error) { func goLookupIP(ctx context.Context, host string) (addrs []IPAddr, err error) {
order := systemConf().hostLookupOrder(host) order := systemConf().hostLookupOrder(host)
return goLookupIPOrder(ctx, host, order) addrs, _, err = goLookupIPCNAMEOrder(ctx, host, order)
return
} }
func goLookupIPOrder(ctx context.Context, name string, order hostLookupOrder) (addrs []IPAddr, err error) { func goLookupIPCNAMEOrder(ctx context.Context, name string, order hostLookupOrder) (addrs []IPAddr, cname string, err error) {
if order == hostLookupFilesDNS || order == hostLookupFiles { if order == hostLookupFilesDNS || order == hostLookupFiles {
addrs = goLookupIPFiles(name) addrs = goLookupIPFiles(name)
if len(addrs) > 0 || order == hostLookupFiles { if len(addrs) > 0 || order == hostLookupFiles {
return addrs, nil return addrs, name, nil
} }
} }
if !isDomainName(name) { if !isDomainName(name) {
// See comment in func lookup above about use of errNoSuchHost. // See comment in func lookup above about use of errNoSuchHost.
return nil, &DNSError{Err: errNoSuchHost.Error(), Name: name} return nil, "", &DNSError{Err: errNoSuchHost.Error(), Name: name}
} }
resolvConf.tryUpdate("/etc/resolv.conf") resolvConf.tryUpdate("/etc/resolv.conf")
resolvConf.mu.RLock() resolvConf.mu.RLock()
conf := resolvConf.dnsConfig conf := resolvConf.dnsConfig
resolvConf.mu.RUnlock() resolvConf.mu.RUnlock()
type racer struct { type racer struct {
fqdn string cname string
rrs []dnsRR rrs []dnsRR
error error
} }
lane := make(chan racer, 1) lane := make(chan racer, 1)
...@@ -501,20 +502,23 @@ func goLookupIPOrder(ctx context.Context, name string, order hostLookupOrder) (a ...@@ -501,20 +502,23 @@ func goLookupIPOrder(ctx context.Context, name string, order hostLookupOrder) (a
for _, fqdn := range conf.nameList(name) { for _, fqdn := range conf.nameList(name) {
for _, qtype := range qtypes { for _, qtype := range qtypes {
go func(qtype uint16) { go func(qtype uint16) {
_, rrs, err := tryOneName(ctx, conf, fqdn, qtype) cname, rrs, err := tryOneName(ctx, conf, fqdn, qtype)
lane <- racer{fqdn, rrs, err} lane <- racer{cname, rrs, err}
}(qtype) }(qtype)
} }
for range qtypes { for range qtypes {
racer := <-lane racer := <-lane
if racer.error != nil { if racer.error != nil {
// Prefer error for original name. // Prefer error for original name.
if lastErr == nil || racer.fqdn == name+"." { if lastErr == nil || fqdn == name+"." {
lastErr = racer.error lastErr = racer.error
} }
continue continue
} }
addrs = append(addrs, addrRecordList(racer.rrs)...) addrs = append(addrs, addrRecordList(racer.rrs)...)
if cname == "" {
cname = racer.cname
}
} }
if len(addrs) > 0 { if len(addrs) > 0 {
break break
...@@ -532,24 +536,16 @@ func goLookupIPOrder(ctx context.Context, name string, order hostLookupOrder) (a ...@@ -532,24 +536,16 @@ func goLookupIPOrder(ctx context.Context, name string, order hostLookupOrder) (a
addrs = goLookupIPFiles(name) addrs = goLookupIPFiles(name)
} }
if len(addrs) == 0 && lastErr != nil { if len(addrs) == 0 && lastErr != nil {
return nil, lastErr return nil, "", lastErr
} }
} }
return addrs, nil return addrs, cname, nil
} }
// goLookupCNAME is the native Go implementation of LookupCNAME. // goLookupCNAME is the native Go (non-cgo) implementation of LookupCNAME.
// Used only if cgoLookupCNAME refuses to handle the request func goLookupCNAME(ctx context.Context, host string) (cname string, err error) {
// (that is, only if cgoLookupCNAME is the stub in cgo_stub.go). order := systemConf().hostLookupOrder(host)
// Normally we let cgo use the C library resolver instead of _, cname, err = goLookupIPCNAMEOrder(ctx, host, order)
// depending on our lookup code, so that Go and C get the same
// answers.
func goLookupCNAME(ctx context.Context, name string) (cname string, err error) {
_, rrs, err := lookup(ctx, name, dnsTypeCNAME)
if err != nil {
return
}
cname = rrs[0].(*dnsRR_CNAME).Cname
return return
} }
......
...@@ -455,14 +455,14 @@ func TestGoLookupIPOrderFallbackToFile(t *testing.T) { ...@@ -455,14 +455,14 @@ func TestGoLookupIPOrderFallbackToFile(t *testing.T) {
name := fmt.Sprintf("order %v", order) name := fmt.Sprintf("order %v", order)
// First ensure that we get an error when contacting a non-existent host. // First ensure that we get an error when contacting a non-existent host.
_, err := goLookupIPOrder(context.Background(), "notarealhost", order) _, _, err := goLookupIPCNAMEOrder(context.Background(), "notarealhost", order)
if err == nil { if err == nil {
t.Errorf("%s: expected error while looking up name not in hosts file", name) t.Errorf("%s: expected error while looking up name not in hosts file", name)
continue continue
} }
// Now check that we get an address when the name appears in the hosts file. // Now check that we get an address when the name appears in the hosts file.
addrs, err := goLookupIPOrder(context.Background(), "thor", order) // entry is in "testdata/hosts" addrs, _, err := goLookupIPCNAMEOrder(context.Background(), "thor", order) // entry is in "testdata/hosts"
if err != nil { if err != nil {
t.Errorf("%s: expected to successfully lookup host entry", name) t.Errorf("%s: expected to successfully lookup host entry", name)
continue continue
...@@ -744,8 +744,11 @@ func TestRetryTimeout(t *testing.T) { ...@@ -744,8 +744,11 @@ func TestRetryTimeout(t *testing.T) {
} }
defer conf.teardown() defer conf.teardown()
if err := conf.writeAndUpdate([]string{"nameserver 192.0.2.1", // the one that will timeout testConf := []string{
"nameserver 192.0.2.2"}); err != nil { "nameserver 192.0.2.1", // the one that will timeout
"nameserver 192.0.2.2",
}
if err := conf.writeAndUpdate(testConf); err != nil {
t.Fatal(err) t.Fatal(err)
} }
...@@ -771,28 +774,10 @@ func TestRetryTimeout(t *testing.T) { ...@@ -771,28 +774,10 @@ func TestRetryTimeout(t *testing.T) {
t.Error("deadline didn't change") t.Error("deadline didn't change")
} }
r := &dnsMsg{ return mockTXTResponse(q), nil
dnsMsgHdr: dnsMsgHdr{
id: q.id,
response: true,
recursion_available: true,
},
question: q.question,
answer: []dnsRR{
&dnsRR_CNAME{
Hdr: dnsRR_Header{
Name: q.question[0].Name,
Rrtype: dnsTypeCNAME,
Class: dnsClassINET,
},
Cname: "golang.org",
},
},
}
return r, nil
} }
_, err = goLookupCNAME(context.Background(), "www.golang.org") _, err = LookupTXT("www.golang.org")
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
...@@ -838,36 +823,40 @@ func testRotate(t *testing.T, rotate bool, nameservers, wantServers []string) { ...@@ -838,36 +823,40 @@ func testRotate(t *testing.T, rotate bool, nameservers, wantServers []string) {
var usedServers []string var usedServers []string
d.rh = func(s string, q *dnsMsg, _ time.Time) (*dnsMsg, error) { d.rh = func(s string, q *dnsMsg, _ time.Time) (*dnsMsg, error) {
usedServers = append(usedServers, s) usedServers = append(usedServers, s)
return mockTXTResponse(q), nil
r := &dnsMsg{
dnsMsgHdr: dnsMsgHdr{
id: q.id,
response: true,
recursion_available: true,
},
question: q.question,
answer: []dnsRR{
&dnsRR_CNAME{
Hdr: dnsRR_Header{
Name: q.question[0].Name,
Rrtype: dnsTypeCNAME,
Class: dnsClassINET,
},
Cname: "golang.org",
},
},
}
return r, nil
} }
// len(nameservers) + 1 to allow rotation to get back to start // len(nameservers) + 1 to allow rotation to get back to start
for i := 0; i < len(nameservers)+1; i++ { for i := 0; i < len(nameservers)+1; i++ {
if _, err := goLookupCNAME(context.Background(), "www.golang.org"); err != nil { if _, err := LookupTXT("www.golang.org"); err != nil {
t.Fatal(err) t.Fatal(err)
} }
} }
if !reflect.DeepEqual(usedServers, wantServers) { if !reflect.DeepEqual(usedServers, wantServers) {
t.Fatalf("rotate=%t got used servers:\n%v\nwant:\n%v", rotate, usedServers, wantServers) t.Errorf("rotate=%t got used servers:\n%v\nwant:\n%v", rotate, usedServers, wantServers)
} }
} }
func mockTXTResponse(q *dnsMsg) *dnsMsg {
r := &dnsMsg{
dnsMsgHdr: dnsMsgHdr{
id: q.id,
response: true,
recursion_available: true,
},
question: q.question,
answer: []dnsRR{
&dnsRR_TXT{
Hdr: dnsRR_Header{
Name: q.question[0].Name,
Rrtype: dnsTypeTXT,
Class: dnsClassINET,
},
Txt: "ok",
},
},
}
return r
}
...@@ -233,20 +233,32 @@ func (r *Resolver) LookupPort(ctx context.Context, network, service string) (por ...@@ -233,20 +233,32 @@ func (r *Resolver) LookupPort(ctx context.Context, network, service string) (por
return port, nil return port, nil
} }
// LookupCNAME returns the canonical DNS host for the given name. // LookupCNAME returns the canonical name for the given host.
// Callers that do not care about the canonical name can call // Callers that do not care about the canonical name can call
// LookupHost or LookupIP directly; both take care of resolving // LookupHost or LookupIP directly; both take care of resolving
// the canonical name as part of the lookup. // the canonical name as part of the lookup.
func LookupCNAME(name string) (cname string, err error) { //
return DefaultResolver.lookupCNAME(context.Background(), name) // A canonical name is the final name after following zero
// or more CNAME records.
// LookupCNAME does not return an error if host does not
// contain DNS "CNAME" records, as long as host resolves to
// address records.
func LookupCNAME(host string) (cname string, err error) {
return DefaultResolver.lookupCNAME(context.Background(), host)
} }
// LookupCNAME returns the canonical DNS host for the given name. // LookupCNAME returns the canonical name for the given host.
// Callers that do not care about the canonical name can call // Callers that do not care about the canonical name can call
// LookupHost or LookupIP directly; both take care of resolving // LookupHost or LookupIP directly; both take care of resolving
// the canonical name as part of the lookup. // the canonical name as part of the lookup.
func (r *Resolver) LookupCNAME(ctx context.Context, name string) (cname string, err error) { //
return r.lookupCNAME(ctx, name) // A canonical name is the final name after following zero
// or more CNAME records.
// LookupCNAME does not return an error if host does not
// contain DNS "CNAME" records, as long as host resolves to
// address records.
func (r *Resolver) LookupCNAME(ctx context.Context, host string) (cname string, err error) {
return r.lookupCNAME(ctx, host)
} }
// LookupSRV tries to resolve an SRV query of the given service, // LookupSRV tries to resolve an SRV query of the given service,
......
...@@ -243,14 +243,15 @@ func TestLookupIPv6LinkLocalAddr(t *testing.T) { ...@@ -243,14 +243,15 @@ func TestLookupIPv6LinkLocalAddr(t *testing.T) {
} }
} }
var lookupIANACNAMETests = []struct { var lookupCNAMETests = []struct {
name, cname string name, cname string
}{ }{
{"www.iana.org", "icann.org."}, {"www.iana.org", "icann.org."},
{"www.iana.org.", "icann.org."}, {"www.iana.org.", "icann.org."},
{"www.google.com", "google.com."},
} }
func TestLookupIANACNAME(t *testing.T) { func TestLookupCNAME(t *testing.T) {
if testenv.Builder() == "" { if testenv.Builder() == "" {
testenv.MustHaveExternalNetwork(t) testenv.MustHaveExternalNetwork(t)
} }
...@@ -259,7 +260,7 @@ func TestLookupIANACNAME(t *testing.T) { ...@@ -259,7 +260,7 @@ func TestLookupIANACNAME(t *testing.T) {
t.Skip("IPv4 is required") t.Skip("IPv4 is required")
} }
for _, tt := range lookupIANACNAMETests { for _, tt := range lookupCNAMETests {
cname, err := LookupCNAME(tt.name) cname, err := LookupCNAME(tt.name)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
......
...@@ -72,7 +72,8 @@ func (r *Resolver) lookupIP(ctx context.Context, host string) (addrs []IPAddr, e ...@@ -72,7 +72,8 @@ func (r *Resolver) lookupIP(ctx context.Context, host string) (addrs []IPAddr, e
// cgo not available (or netgo); fall back to Go's DNS resolver // cgo not available (or netgo); fall back to Go's DNS resolver
order = hostLookupFilesDNS order = hostLookupFilesDNS
} }
return goLookupIPOrder(ctx, host, order) addrs, _, err = goLookupIPCNAMEOrder(ctx, host, order)
return
} }
func (r *Resolver) lookupPort(ctx context.Context, network, service string) (int, error) { func (r *Resolver) lookupPort(ctx context.Context, network, service string) (int, error) {
......
...@@ -24,7 +24,7 @@ func toJson(v interface{}) string { ...@@ -24,7 +24,7 @@ func toJson(v interface{}) string {
return string(data) return string(data)
} }
func TestLookupMX(t *testing.T) { func TestNSLookupMX(t *testing.T) {
testenv.MustHaveExternalNetwork(t) testenv.MustHaveExternalNetwork(t)
for _, server := range nslookupTestServers { for _, server := range nslookupTestServers {
...@@ -49,7 +49,7 @@ func TestLookupMX(t *testing.T) { ...@@ -49,7 +49,7 @@ func TestLookupMX(t *testing.T) {
} }
} }
func TestLookupCNAME(t *testing.T) { func TestNSLookupCNAME(t *testing.T) {
testenv.MustHaveExternalNetwork(t) testenv.MustHaveExternalNetwork(t)
for _, server := range nslookupTestServers { for _, server := range nslookupTestServers {
...@@ -72,7 +72,7 @@ func TestLookupCNAME(t *testing.T) { ...@@ -72,7 +72,7 @@ func TestLookupCNAME(t *testing.T) {
} }
} }
func TestLookupNS(t *testing.T) { func TestNSLookupNS(t *testing.T) {
testenv.MustHaveExternalNetwork(t) testenv.MustHaveExternalNetwork(t)
for _, server := range nslookupTestServers { for _, server := range nslookupTestServers {
...@@ -98,7 +98,7 @@ func TestLookupNS(t *testing.T) { ...@@ -98,7 +98,7 @@ func TestLookupNS(t *testing.T) {
} }
} }
func TestLookupTXT(t *testing.T) { func TestNSLookupTXT(t *testing.T) {
testenv.MustHaveExternalNetwork(t) testenv.MustHaveExternalNetwork(t)
for _, server := range nslookupTestServers { for _, server := range nslookupTestServers {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment