diff --git a/command/agent/dns.go b/command/agent/dns.go index 7f373cd411..cd7dd801d5 100644 --- a/command/agent/dns.go +++ b/command/agent/dns.go @@ -304,12 +304,20 @@ func (d *DNSServer) dispatch(network string, req, resp *dns.Msg) { // Split into the label parts labels := dns.SplitDomainName(qName) - // The last label is either "node", "service", "query", or a datacenter name + // The last label is either "node", "service", "query", "_", or a datacenter name PARSE: n := len(labels) if n == 0 { goto INVALID } + + // If this is a SRV query the "service" label is optional, we add it back to use the + // existing code-path. + if req.Question[0].Qtype == dns.TypeSRV && strings.HasPrefix(labels[n-1], "_") { + labels = append(labels, "service") + n = n + 1 + } + switch labels[n-1] { case "service": if n == 1 { diff --git a/command/agent/dns_test.go b/command/agent/dns_test.go index 8eabfa804f..a6f31938a3 100644 --- a/command/agent/dns_test.go +++ b/command/agent/dns_test.go @@ -3393,47 +3393,57 @@ func TestDNS_ServiceLookup_SRV_RFC(t *testing.T) { t.Fatalf("err: %v", err) } - m := new(dns.Msg) - m.SetQuestion("_db._master.service.consul.", dns.TypeSRV) - - c := new(dns.Client) - addr, _ := srv.agent.config.ClientListener("", srv.agent.config.Ports.DNS) - in, _, err := c.Exchange(m, addr.String()) - if err != nil { - t.Fatalf("err: %v", err) + questions := []string{ + "_db._master.service.dc1.consul.", + "_db._master.service.consul.", + "_db._master.dc1.consul.", + "_db._master.consul.", } - if len(in.Answer) != 1 { - t.Fatalf("Bad: %#v", in) + for _, question := range questions { + m := new(dns.Msg) + m.SetQuestion(question, dns.TypeSRV) + + c := new(dns.Client) + addr, _ := srv.agent.config.ClientListener("", srv.agent.config.Ports.DNS) + in, _, err := c.Exchange(m, addr.String()) + if err != nil { + t.Fatalf("err: %v", err) + } + + if len(in.Answer) != 1 { + t.Fatalf("Bad: %#v", in) + } + + srvRec, ok := in.Answer[0].(*dns.SRV) + if !ok { + t.Fatalf("Bad: %#v", in.Answer[0]) + } + if srvRec.Port != 12345 { + t.Fatalf("Bad: %#v", srvRec) + } + if srvRec.Target != "foo.node.dc1.consul." { + t.Fatalf("Bad: %#v", srvRec) + } + if srvRec.Hdr.Ttl != 0 { + t.Fatalf("Bad: %#v", in.Answer[0]) + } + + aRec, ok := in.Extra[0].(*dns.A) + if !ok { + t.Fatalf("Bad: %#v", in.Extra[0]) + } + if aRec.Hdr.Name != "foo.node.dc1.consul." { + t.Fatalf("Bad: %#v", in.Extra[0]) + } + if aRec.A.String() != "127.0.0.1" { + t.Fatalf("Bad: %#v", in.Extra[0]) + } + if aRec.Hdr.Ttl != 0 { + t.Fatalf("Bad: %#v", in.Extra[0]) + } } - srvRec, ok := in.Answer[0].(*dns.SRV) - if !ok { - t.Fatalf("Bad: %#v", in.Answer[0]) - } - if srvRec.Port != 12345 { - t.Fatalf("Bad: %#v", srvRec) - } - if srvRec.Target != "foo.node.dc1.consul." { - t.Fatalf("Bad: %#v", srvRec) - } - if srvRec.Hdr.Ttl != 0 { - t.Fatalf("Bad: %#v", in.Answer[0]) - } - - aRec, ok := in.Extra[0].(*dns.A) - if !ok { - t.Fatalf("Bad: %#v", in.Extra[0]) - } - if aRec.Hdr.Name != "foo.node.dc1.consul." { - t.Fatalf("Bad: %#v", in.Extra[0]) - } - if aRec.A.String() != "127.0.0.1" { - t.Fatalf("Bad: %#v", in.Extra[0]) - } - if aRec.Hdr.Ttl != 0 { - t.Fatalf("Bad: %#v", in.Extra[0]) - } } func TestDNS_ServiceLookup_SRV_RFC_TCP_Default(t *testing.T) { @@ -3460,47 +3470,57 @@ func TestDNS_ServiceLookup_SRV_RFC_TCP_Default(t *testing.T) { t.Fatalf("err: %v", err) } - m := new(dns.Msg) - m.SetQuestion("_db._tcp.service.consul.", dns.TypeSRV) - - c := new(dns.Client) - addr, _ := srv.agent.config.ClientListener("", srv.agent.config.Ports.DNS) - in, _, err := c.Exchange(m, addr.String()) - if err != nil { - t.Fatalf("err: %v", err) + questions := []string{ + "_db._tcp.service.dc1.consul.", + "_db._tcp.service.consul.", + "_db._tcp.dc1.consul.", + "_db._tcp.consul.", } - if len(in.Answer) != 1 { - t.Fatalf("Bad: %#v", in) + for _, question := range questions { + m := new(dns.Msg) + m.SetQuestion(question, dns.TypeSRV) + + c := new(dns.Client) + addr, _ := srv.agent.config.ClientListener("", srv.agent.config.Ports.DNS) + in, _, err := c.Exchange(m, addr.String()) + if err != nil { + t.Fatalf("err: %v", err) + } + + if len(in.Answer) != 1 { + t.Fatalf("Bad: %#v", in) + } + + srvRec, ok := in.Answer[0].(*dns.SRV) + if !ok { + t.Fatalf("Bad: %#v", in.Answer[0]) + } + if srvRec.Port != 12345 { + t.Fatalf("Bad: %#v", srvRec) + } + if srvRec.Target != "foo.node.dc1.consul." { + t.Fatalf("Bad: %#v", srvRec) + } + if srvRec.Hdr.Ttl != 0 { + t.Fatalf("Bad: %#v", in.Answer[0]) + } + + aRec, ok := in.Extra[0].(*dns.A) + if !ok { + t.Fatalf("Bad: %#v", in.Extra[0]) + } + if aRec.Hdr.Name != "foo.node.dc1.consul." { + t.Fatalf("Bad: %#v", in.Extra[0]) + } + if aRec.A.String() != "127.0.0.1" { + t.Fatalf("Bad: %#v", in.Extra[0]) + } + if aRec.Hdr.Ttl != 0 { + t.Fatalf("Bad: %#v", in.Extra[0]) + } } - srvRec, ok := in.Answer[0].(*dns.SRV) - if !ok { - t.Fatalf("Bad: %#v", in.Answer[0]) - } - if srvRec.Port != 12345 { - t.Fatalf("Bad: %#v", srvRec) - } - if srvRec.Target != "foo.node.dc1.consul." { - t.Fatalf("Bad: %#v", srvRec) - } - if srvRec.Hdr.Ttl != 0 { - t.Fatalf("Bad: %#v", in.Answer[0]) - } - - aRec, ok := in.Extra[0].(*dns.A) - if !ok { - t.Fatalf("Bad: %#v", in.Extra[0]) - } - if aRec.Hdr.Name != "foo.node.dc1.consul." { - t.Fatalf("Bad: %#v", in.Extra[0]) - } - if aRec.A.String() != "127.0.0.1" { - t.Fatalf("Bad: %#v", in.Extra[0]) - } - if aRec.Hdr.Ttl != 0 { - t.Fatalf("Bad: %#v", in.Extra[0]) - } } func TestDNS_ServiceLookup_FilterACL(t *testing.T) { diff --git a/website/source/docs/agent/dns.html.markdown b/website/source/docs/agent/dns.html.markdown index e3c4b15ee0..9914051da1 100644 --- a/website/source/docs/agent/dns.html.markdown +++ b/website/source/docs/agent/dns.html.markdown @@ -138,7 +138,7 @@ foobar.node.dc1.consul. 0 IN A 10.1.10.12 The format for RFC 2782 SRV lookups is: - _._.service[.datacenter][.domain] + _._[.service][.datacenter][.domain] Per [RFC 2782](https://tools.ietf.org/html/rfc2782), SRV queries should use underscores, `_`, as a prefix to the `service` and `protocol` values in a query to