aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--plugin/erratic/README.md5
-rw-r--r--plugin/erratic/erratic.go24
-rw-r--r--plugin/erratic/erratic_test.go27
-rw-r--r--plugin/erratic/xfr.go52
4 files changed, 98 insertions, 10 deletions
diff --git a/plugin/erratic/README.md b/plugin/erratic/README.md
index ab58b161b..62625c1d0 100644
--- a/plugin/erratic/README.md
+++ b/plugin/erratic/README.md
@@ -10,7 +10,8 @@
The *erratic* plugin will respond to every A or AAAA query. For any other type it will return
a SERVFAIL response. The reply for A will return 192.0.2.53 (see [RFC
5737](https://tools.ietf.org/html/rfc5737),
-for AAAA it returns 2001:DB8::53 (see [RFC 3849](https://tools.ietf.org/html/rfc3849)).
+for AAAA it returns 2001:DB8::53 (see [RFC 3849](https://tools.ietf.org/html/rfc3849)) and for an
+AXFR request it will respond with a small zone transfer.
*erratic* can also be used in conjunction with the *autopath* plugin. This is mostly to aid in
testing.
@@ -30,6 +31,8 @@ erratic {
* `delay`: delay 1 per **AMOUNT** of queries for **DURATION**, the default for **AMOUNT** is 2 and
the default for **DURATION** is 100ms.
+In case of a zone transfer and truncate the final SOA record *isn't* added to the response.
+
## Health
This plugin implements dynamic health checking. For every dropped query it turns unhealthy.
diff --git a/plugin/erratic/erratic.go b/plugin/erratic/erratic.go
index 290e0c2cc..3460f3bca 100644
--- a/plugin/erratic/erratic.go
+++ b/plugin/erratic/erratic.go
@@ -61,14 +61,26 @@ func (e *Erratic) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Msg
rr := *(rrAAAA.(*dns.AAAA))
rr.Header().Name = state.QName()
m.Answer = append(m.Answer, &rr)
+ case dns.TypeAXFR:
+ if drop {
+ return 0, nil
+ }
+ if delay {
+ time.Sleep(e.duration)
+ }
+
+ xfr(state, trunc)
+ return 0, nil
+
default:
- if !drop {
- if delay {
- time.Sleep(e.duration)
- }
- // coredns will return error.
- return dns.RcodeServerFailure, nil
+ if drop {
+ return 0, nil
+ }
+ if delay {
+ time.Sleep(e.duration)
}
+ // coredns will return error.
+ return dns.RcodeServerFailure, nil
}
if drop {
diff --git a/plugin/erratic/erratic_test.go b/plugin/erratic/erratic_test.go
index 8a3b4e011..406fd8774 100644
--- a/plugin/erratic/erratic_test.go
+++ b/plugin/erratic/erratic_test.go
@@ -14,19 +14,22 @@ func TestErraticDrop(t *testing.T) {
e := &Erratic{drop: 2} // 50% drops
tests := []struct {
+ rrtype uint16
expectedCode int
expectedErr error
drop bool
}{
- {expectedCode: dns.RcodeSuccess, expectedErr: nil, drop: true},
- {expectedCode: dns.RcodeSuccess, expectedErr: nil, drop: false},
+ {rrtype: dns.TypeA, expectedCode: dns.RcodeSuccess, expectedErr: nil, drop: true},
+ {rrtype: dns.TypeA, expectedCode: dns.RcodeSuccess, expectedErr: nil, drop: false},
+ {rrtype: dns.TypeAAAA, expectedCode: dns.RcodeSuccess, expectedErr: nil, drop: true},
+ {rrtype: dns.TypeHINFO, expectedCode: dns.RcodeServerFailure, expectedErr: nil, drop: false},
}
ctx := context.TODO()
for i, tc := range tests {
req := new(dns.Msg)
- req.SetQuestion("example.org.", dns.TypeA)
+ req.SetQuestion("example.org.", tc.rrtype)
rec := dnstest.NewRecorder(&test.ResponseWriter{})
code, err := e.ServeDNS(ctx, rec, req)
@@ -77,3 +80,21 @@ func TestErraticTruncate(t *testing.T) {
}
}
}
+
+func TestAxfr(t *testing.T) {
+ e := &Erratic{truncate: 0} // nothing, just check if we can get an axfr
+
+ ctx := context.TODO()
+
+ req := new(dns.Msg)
+ req.SetQuestion("example.org.", dns.TypeAXFR)
+
+ rec := dnstest.NewRecorder(&test.ResponseWriter{})
+ _, err := e.ServeDNS(ctx, rec, req)
+ if err != nil {
+ t.Errorf("Failed to set up AXFR: %s", err)
+ }
+ if x := rec.Msg.Answer[0].Header().Rrtype; x != dns.TypeSOA {
+ t.Errorf("Expected for record to be %d, got %d", dns.TypeSOA, x)
+ }
+}
diff --git a/plugin/erratic/xfr.go b/plugin/erratic/xfr.go
new file mode 100644
index 000000000..8e2b31794
--- /dev/null
+++ b/plugin/erratic/xfr.go
@@ -0,0 +1,52 @@
+package erratic
+
+import (
+ "strings"
+
+ "github.com/coredns/coredns/plugin/test"
+ "github.com/coredns/coredns/request"
+
+ "github.com/miekg/dns"
+)
+
+// allRecords returns a small zone file. The first RR must be a SOA.
+func allRecords(name string) []dns.RR {
+ var rrs = []dns.RR{
+ test.SOA("xx. 0 IN SOA sns.dns.icann.org. noc.dns.icann.org. 2018050825 7200 3600 1209600 3600"),
+ test.NS("xx. 0 IN NS b.xx."),
+ test.NS("xx. 0 IN NS a.xx."),
+ test.AAAA("a.xx. 0 IN AAAA 2001:bd8::53"),
+ test.AAAA("b.xx. 0 IN AAAA 2001:500::54"),
+ }
+
+ for _, r := range rrs {
+ r.Header().Name = strings.Replace(r.Header().Name, "xx.", name, 1)
+
+ if n, ok := r.(*dns.NS); ok {
+ n.Ns = strings.Replace(n.Ns, "xx.", name, 1)
+ }
+ }
+ return rrs
+}
+
+func xfr(state request.Request, truncate bool) {
+ rrs := allRecords(state.QName())
+
+ ch := make(chan *dns.Envelope)
+ tr := new(dns.Transfer)
+
+ go func() {
+ // So the rrs we have don't have a closing SOA, only add that when truncate is false,
+ // so we send an incomplete AXFR.
+ if !truncate {
+ rrs = append(rrs, rrs[0])
+ }
+
+ ch <- &dns.Envelope{RR: rrs}
+ close(ch)
+ }()
+
+ tr.Out(state.W, state.Req, ch)
+ state.W.Hijack()
+ return
+}