Browse Source

Support RFC 2782 for prepared query DNS lookups (#14465)

Format:
	_<query id or name>._tcp.query[.<datacenter>].<domain>
add-downstream-service-meta
Jared Kirschner 2 years ago committed by GitHub
parent
commit
3e7e8ae9c5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 3
      .changelog/14465.txt
  2. 22
      agent/dns.go
  3. 8
      agent/dns_test.go
  4. 18
      website/content/docs/discovery/dns.mdx

3
.changelog/14465.txt

@ -0,0 +1,3 @@
```release-note:improvement
dns: support RFC 2782 SRV lookups for prepared queries using format `_<query id or name>._tcp.query[.<datacenter>].<domain>`.
```

22
agent/dns.go

@ -908,10 +908,11 @@ func (d *DNSServer) dispatch(remoteAddr net.Addr, req, resp *dns.Msg, maxRecursi
return d.nodeLookup(cfg, lookup, req, resp) return d.nodeLookup(cfg, lookup, req, resp)
case "query": case "query":
n := len(queryParts)
datacenter := d.agent.config.Datacenter datacenter := d.agent.config.Datacenter
// ensure we have a query name // ensure we have a query name
if len(queryParts) < 1 { if n < 1 {
return invalid() return invalid()
} }
@ -919,8 +920,23 @@ func (d *DNSServer) dispatch(remoteAddr net.Addr, req, resp *dns.Msg, maxRecursi
return invalid() return invalid()
} }
// Allow a "." in the query name, just join all the parts. query := ""
query := strings.Join(queryParts, ".")
// If the first and last DNS query parts begin with _, this is an RFC 2782 style SRV lookup.
// This allows for prepared query names to include "." (for backwards compatibility).
// Otherwise, this is a standard prepared query lookup.
if n >= 2 && strings.HasPrefix(queryParts[0], "_") && strings.HasPrefix(queryParts[n-1], "_") {
// The last DNS query part is the protocol field (ignored).
// All prior parts are the prepared query name or ID.
query = strings.Join(queryParts[:n-1], ".")
// Strip leading underscore
query = query[1:]
} else {
// Allow a "." in the query name, just join all the parts.
query = strings.Join(queryParts, ".")
}
err := d.preparedQueryLookup(cfg, datacenter, query, remoteAddr, req, resp, maxRecursionLevel) err := d.preparedQueryLookup(cfg, datacenter, query, remoteAddr, req, resp, maxRecursionLevel)
return ecsNotGlobalError{error: err} return ecsNotGlobalError{error: err}

8
agent/dns_test.go

@ -2743,13 +2743,16 @@ func TestDNS_ServiceLookup_ServiceAddress_SRV(t *testing.T) {
} }
// Register an equivalent prepared query. // Register an equivalent prepared query.
// Specify prepared query name containing "." to test
// since that is technically supported (though atypical).
var id string var id string
preparedQueryName := "query.name.with.dots"
{ {
args := &structs.PreparedQueryRequest{ args := &structs.PreparedQueryRequest{
Datacenter: "dc1", Datacenter: "dc1",
Op: structs.PreparedQueryCreate, Op: structs.PreparedQueryCreate,
Query: &structs.PreparedQuery{ Query: &structs.PreparedQuery{
Name: "test", Name: preparedQueryName,
Service: structs.ServiceQuery{ Service: structs.ServiceQuery{
Service: "db", Service: "db",
}, },
@ -2764,6 +2767,9 @@ func TestDNS_ServiceLookup_ServiceAddress_SRV(t *testing.T) {
questions := []string{ questions := []string{
"db.service.consul.", "db.service.consul.",
id + ".query.consul.", id + ".query.consul.",
preparedQueryName + ".query.consul.",
fmt.Sprintf("_%s._tcp.query.consul.", id),
fmt.Sprintf("_%s._tcp.query.consul.", preparedQueryName),
} }
for _, question := range questions { for _, question := range questions {
m := new(dns.Msg) m := new(dns.Msg)

18
website/content/docs/discovery/dns.mdx

@ -396,16 +396,24 @@ you can use the following query formats specify namespace but not partition:
### Prepared Query Lookups ### Prepared Query Lookups
The format of a prepared query lookup is: The following formats are valid for prepared query lookups:
```text - Standard lookup
<query or name>.query[.<datacenter>].<domain>
``` ```text
<query name or id>.query[.<datacenter>].<domain>
```
- [RFC 2782](https://tools.ietf.org/html/rfc2782) SRV lookup
```text
_<query name or id>._tcp.query[.<datacenter>].<domain>
```
The `datacenter` is optional, and if not provided, the datacenter of this Consul The `datacenter` is optional, and if not provided, the datacenter of this Consul
agent is assumed. agent is assumed.
The `query or name` is the ID or given name of an existing The `query name or id` is the given name or ID of an existing
[Prepared Query](/api-docs/query). These behave like standard service [Prepared Query](/api-docs/query). These behave like standard service
queries but provide a much richer set of features, such as filtering by multiple queries but provide a much richer set of features, such as filtering by multiple
tags and automatically failing over to look for services in remote datacenters if tags and automatically failing over to look for services in remote datacenters if

Loading…
Cancel
Save