mirror of
https://github.com/coredns/coredns.git
synced 2025-10-28 08:44:17 -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
|
additional RR
|
||||||
authority RR
|
authority RR
|
||||||
rcode CODE
|
rcode CODE
|
||||||
|
ederror EXTENDED_ERROR_CODE [EXTRA_REASON]
|
||||||
fallthrough [FALLTHROUGH-ZONE...]
|
fallthrough [FALLTHROUGH-ZONE...]
|
||||||
}
|
}
|
||||||
~~~
|
~~~
|
||||||
@@ -31,6 +32,8 @@ template CLASS TYPE [ZONE...] {
|
|||||||
in a response with an empty answer section.
|
in a response with an empty answer section.
|
||||||
* `rcode` **CODE** A response code (`NXDOMAIN, SERVFAIL, ...`). The default is `NOERROR`. Valid response code values are
|
* `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`.
|
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.
|
* `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
|
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
|
`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 {
|
template ANY ANY invalid {
|
||||||
rcode NXDOMAIN
|
rcode NXDOMAIN
|
||||||
authority "invalid. 60 {{ .Class }} SOA ns.invalid. hostmaster.invalid. (1 60 60 60 60)"
|
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 (
|
import (
|
||||||
"regexp"
|
"regexp"
|
||||||
|
"strconv"
|
||||||
gotmpl "text/template"
|
gotmpl "text/template"
|
||||||
|
|
||||||
"github.com/coredns/caddy"
|
"github.com/coredns/caddy"
|
||||||
@@ -123,6 +124,22 @@ func templateParse(c *caddy.Controller) (handler Handler, err error) {
|
|||||||
}
|
}
|
||||||
t.rcode = rcode
|
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":
|
case "fallthrough":
|
||||||
t.fall.SetZonesFromArgs(c.RemainingArgs())
|
t.fall.SetZonesFromArgs(c.RemainingArgs())
|
||||||
|
|
||||||
|
|||||||
@@ -161,6 +161,30 @@ func TestSetupParse(t *testing.T) {
|
|||||||
}`,
|
}`,
|
||||||
false,
|
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 {
|
for i, test := range tests {
|
||||||
c := caddy.NewTestController("dns", test.inputFileRules)
|
c := caddy.NewTestController("dns", test.inputFileRules)
|
||||||
|
|||||||
@@ -33,10 +33,16 @@ type template struct {
|
|||||||
authority []*gotmpl.Template
|
authority []*gotmpl.Template
|
||||||
qclass uint16
|
qclass uint16
|
||||||
qtype uint16
|
qtype uint16
|
||||||
|
ederror *ederror
|
||||||
fall fall.F
|
fall fall.F
|
||||||
upstream Upstreamer
|
upstream Upstreamer
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type ederror struct {
|
||||||
|
code uint16
|
||||||
|
reason string
|
||||||
|
}
|
||||||
|
|
||||||
// Upstreamer looks up targets of CNAME templates
|
// Upstreamer looks up targets of CNAME templates
|
||||||
type Upstreamer interface {
|
type Upstreamer interface {
|
||||||
Lookup(ctx context.Context, state request.Request, name string, typ uint16) (*dns.Msg, error)
|
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)
|
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)
|
w.WriteMsg(msg)
|
||||||
return template.rcode, nil
|
return template.rcode, nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -143,6 +143,16 @@ func TestHandler(t *testing.T) {
|
|||||||
fall: fall.Root,
|
fall: fall.Root,
|
||||||
zones: []string{"."},
|
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 {
|
tests := []struct {
|
||||||
tmpl template
|
tmpl template
|
||||||
@@ -442,6 +452,33 @@ func TestHandler(t *testing.T) {
|
|||||||
"foo": "myfoo",
|
"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()
|
ctx := context.TODO()
|
||||||
|
|||||||
Reference in New Issue
Block a user