diff --git a/command/agent/dns.go b/command/agent/dns.go index 60ef97420b..90b156eb2f 100644 --- a/command/agent/dns.go +++ b/command/agent/dns.go @@ -767,6 +767,12 @@ func (d *DNSServer) serviceNodeRecords(dc string, nodes structs.CheckServiceNode addr = node.Service.Address } + // If the service address is a CNAME for the service we are looking + // for then use the node address. + if qName == strings.TrimSuffix(addr, ".")+"." { + addr = node.Node.Address + } + // Avoid duplicate entries, possible if a node has // the same service on multiple ports, etc. if _, ok := handled[addr]; ok { diff --git a/command/agent/dns_test.go b/command/agent/dns_test.go index 31ccf3333c..e5ee307671 100644 --- a/command/agent/dns_test.go +++ b/command/agent/dns_test.go @@ -14,6 +14,7 @@ import ( "github.com/hashicorp/consul/lib" "github.com/hashicorp/consul/testutil/retry" "github.com/miekg/dns" + "github.com/pascaldekloe/goe/verify" ) const ( @@ -609,6 +610,66 @@ func TestDNS_ServiceLookup(t *testing.T) { } } +func TestDNS_ServiceLookupWithInternalServiceAddress(t *testing.T) { + t.Parallel() + a := NewTestAgent(t.Name(), nil) + defer a.Shutdown() + + // Register a node with a service. + // The service is using the consul DNS name as service address + // which triggers a lookup loop and a subsequent stack overflow + // crash. + args := &structs.RegisterRequest{ + Datacenter: "dc1", + Node: "foo", + Address: "127.0.0.1", + Service: &structs.NodeService{ + Service: "db", + Address: "db.service.consul", + Port: 12345, + }, + } + + var out struct{} + if err := a.RPC("Catalog.Register", args, &out); err != nil { + t.Fatalf("err: %v", err) + } + + // Looking up the service should not trigger a loop + m := new(dns.Msg) + m.SetQuestion("db.service.consul.", dns.TypeSRV) + + c := new(dns.Client) + addr, _ := a.Config.ClientListener("", a.Config.Ports.DNS) + in, _, err := c.Exchange(m, addr.String()) + if err != nil { + t.Fatalf("err: %v", err) + } + + wantAnswer := []dns.RR{ + &dns.SRV{ + Hdr: dns.RR_Header{Name: "db.service.consul.", Rrtype: 0x21, Class: 0x1, Rdlength: 0x15}, + Priority: 0x1, + Weight: 0x1, + Port: 12345, + Target: "foo.node.dc1.consul.", + }, + } + verify.Values(t, "answer", in.Answer, wantAnswer) + + wantExtra := []dns.RR{ + &dns.CNAME{ + Hdr: dns.RR_Header{Name: "foo.node.dc1.consul.", Rrtype: 0x5, Class: 0x1, Rdlength: 0x2}, + Target: "db.service.consul.", + }, + &dns.A{ + Hdr: dns.RR_Header{Name: "db.service.consul.", Rrtype: 0x1, Class: 0x1, Rdlength: 0x4}, + A: []byte{0x7f, 0x0, 0x0, 0x1}, // 127.0.0.1 + }, + } + verify.Values(t, "extra", in.Extra, wantExtra) +} + func TestDNS_ExternalServiceLookup(t *testing.T) { t.Parallel() a := NewTestAgent(t.Name(), nil)