mirror of
https://github.com/coredns/coredns.git
synced 2025-10-27 16:24:19 -04:00
plugin/template : add support for extended DNS errors (#5659)
* plugin/template : add support for extended DNS errors Signed-off-by: Ondřej Benkovský <ondrej.benkovsky@jamf.com>
This commit is contained in:
@@ -17,6 +17,7 @@ template CLASS TYPE [ZONE...] {
|
||||
additional RR
|
||||
authority RR
|
||||
rcode CODE
|
||||
ederror EXTENDED_ERROR_CODE [EXTRA_REASON]
|
||||
fallthrough [FALLTHROUGH-ZONE...]
|
||||
}
|
||||
~~~
|
||||
@@ -31,6 +32,8 @@ template CLASS TYPE [ZONE...] {
|
||||
in a response with an empty answer section.
|
||||
* `rcode` **CODE** A response code (`NXDOMAIN, SERVFAIL, ...`). The default is `NOERROR`. Valid response code values are
|
||||
per the `RcodeToString` map defined by the `miekg/dns` package in `msg.go`.
|
||||
* `ederror` **EXTENDED_ERROR_CODE** is an extended DNS error code as a number defined in `RFC8914` (0, 1, 2,..., 24).
|
||||
**EXTRA_REASON** is an additional string explaining the reason for returning the error.
|
||||
* `fallthrough` Continue with the next _template_ instance if the _template_'s **ZONE** matches a query name but no regex match.
|
||||
If there is no next _template_, continue resolution with the next plugin. If **[FALLTHROUGH-ZONE...]** are listed (for example
|
||||
`in-addr.arpa` and `ip6.arpa`), then only queries for those zones will be subject to fallthrough. Without
|
||||
@@ -104,6 +107,7 @@ The `.invalid` domain is a reserved TLD (see [RFC 2606 Reserved Top Level DNS Na
|
||||
template ANY ANY invalid {
|
||||
rcode NXDOMAIN
|
||||
authority "invalid. 60 {{ .Class }} SOA ns.invalid. hostmaster.invalid. (1 60 60 60 60)"
|
||||
ederror 21 "Blocked according to RFC2606"
|
||||
}
|
||||
}
|
||||
~~~
|
||||
|
||||
@@ -2,6 +2,7 @@ package template
|
||||
|
||||
import (
|
||||
"regexp"
|
||||
"strconv"
|
||||
gotmpl "text/template"
|
||||
|
||||
"github.com/coredns/caddy"
|
||||
@@ -123,6 +124,22 @@ func templateParse(c *caddy.Controller) (handler Handler, err error) {
|
||||
}
|
||||
t.rcode = rcode
|
||||
|
||||
case "ederror":
|
||||
args := c.RemainingArgs()
|
||||
if len(args) != 1 && len(args) != 2 {
|
||||
return handler, c.ArgErr()
|
||||
}
|
||||
|
||||
code, err := strconv.ParseUint(args[0], 10, 16)
|
||||
if err != nil {
|
||||
return handler, c.Errf("error parsing extended DNS error code %s, %v\n", c.Val(), err)
|
||||
}
|
||||
if len(args) == 2 {
|
||||
t.ederror = &ederror{code: uint16(code), reason: args[1]}
|
||||
} else {
|
||||
t.ederror = &ederror{code: uint16(code)}
|
||||
}
|
||||
|
||||
case "fallthrough":
|
||||
t.fall.SetZonesFromArgs(c.RemainingArgs())
|
||||
|
||||
|
||||
@@ -161,6 +161,30 @@ func TestSetupParse(t *testing.T) {
|
||||
}`,
|
||||
false,
|
||||
},
|
||||
{
|
||||
`template ANY ANY invalid {
|
||||
rcode NXDOMAIN
|
||||
authority "invalid. 60 {{ .Class }} SOA ns.invalid. hostmaster.invalid. (1 60 60 60 60)"
|
||||
ederror 21 "Blocked according to RFC2606"
|
||||
}`,
|
||||
false,
|
||||
},
|
||||
{
|
||||
`template ANY ANY invalid {
|
||||
rcode NXDOMAIN
|
||||
authority "invalid. 60 {{ .Class }} SOA ns.invalid. hostmaster.invalid. (1 60 60 60 60)"
|
||||
ederror invalid "Blocked according to RFC2606"
|
||||
}`,
|
||||
true,
|
||||
},
|
||||
{
|
||||
`template ANY ANY invalid {
|
||||
rcode NXDOMAIN
|
||||
authority "invalid. 60 {{ .Class }} SOA ns.invalid. hostmaster.invalid. (1 60 60 60 60)"
|
||||
ederror too many arguments
|
||||
}`,
|
||||
true,
|
||||
},
|
||||
}
|
||||
for i, test := range tests {
|
||||
c := caddy.NewTestController("dns", test.inputFileRules)
|
||||
|
||||
@@ -33,10 +33,16 @@ type template struct {
|
||||
authority []*gotmpl.Template
|
||||
qclass uint16
|
||||
qtype uint16
|
||||
ederror *ederror
|
||||
fall fall.F
|
||||
upstream Upstreamer
|
||||
}
|
||||
|
||||
type ederror struct {
|
||||
code uint16
|
||||
reason string
|
||||
}
|
||||
|
||||
// Upstreamer looks up targets of CNAME templates
|
||||
type Upstreamer interface {
|
||||
Lookup(ctx context.Context, state request.Request, name string, typ uint16) (*dns.Msg, error)
|
||||
@@ -125,6 +131,12 @@ func (h Handler) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Msg)
|
||||
msg.Ns = append(msg.Ns, rr)
|
||||
}
|
||||
|
||||
if template.ederror != nil {
|
||||
msg = msg.SetEdns0(4096, true)
|
||||
ede := dns.EDNS0_EDE{InfoCode: template.ederror.code, ExtraText: template.ederror.reason}
|
||||
msg.IsEdns0().Option = append(msg.IsEdns0().Option, &ede)
|
||||
}
|
||||
|
||||
w.WriteMsg(msg)
|
||||
return template.rcode, nil
|
||||
}
|
||||
|
||||
@@ -143,6 +143,16 @@ func TestHandler(t *testing.T) {
|
||||
fall: fall.Root,
|
||||
zones: []string{"."},
|
||||
}
|
||||
templateWithEDE := template{
|
||||
rcode: dns.RcodeNameError,
|
||||
regex: []*regexp.Regexp{regexp.MustCompile(".*")},
|
||||
authority: []*gotmpl.Template{gotmpl.Must(newTemplate("authority", "invalid. 60 {{ .Class }} SOA ns.invalid. hostmaster.invalid. (1 60 60 60 60)"))},
|
||||
qclass: dns.ClassANY,
|
||||
qtype: dns.TypeANY,
|
||||
fall: fall.Root,
|
||||
zones: []string{"."},
|
||||
ederror: &ederror{code: 21, reason: "Blocked due to RFC2606"},
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
tmpl template
|
||||
@@ -442,6 +452,33 @@ func TestHandler(t *testing.T) {
|
||||
"foo": "myfoo",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "EDNS error",
|
||||
tmpl: templateWithEDE,
|
||||
qclass: dns.ClassINET,
|
||||
qtype: dns.TypeA,
|
||||
qname: "test.invalid.",
|
||||
expectedCode: dns.RcodeNameError,
|
||||
verifyResponse: func(r *dns.Msg) error {
|
||||
if opt := r.IsEdns0(); opt != nil {
|
||||
matched := false
|
||||
for _, ednsopt := range opt.Option {
|
||||
if ede, ok := ednsopt.(*dns.EDNS0_EDE); ok {
|
||||
if ede.InfoCode != dns.ExtendedErrorCodeNotSupported {
|
||||
return fmt.Errorf("unexpected EDE code = %v, want %v", ede.InfoCode, dns.ExtendedErrorCodeNotSupported)
|
||||
}
|
||||
matched = true
|
||||
}
|
||||
}
|
||||
if !matched {
|
||||
t.Error("Error: acl.ServeDNS() missing Extended DNS Error option")
|
||||
}
|
||||
} else {
|
||||
return fmt.Errorf("expected EDNS enabled")
|
||||
}
|
||||
return nil
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
ctx := context.TODO()
|
||||
|
||||
Reference in New Issue
Block a user