Commit d998aa6c authored by Miek Gieben's avatar Miek Gieben Committed by GitHub

plugin/erratic: add axfr support (#1977)

* plugin/erratic: add axfr support

Add support for axfr. This to fix and test long standing axfr issues
that are hard to test if we don't support it directly in coredns.

The most intriguing feature is withholding the last SOA from a response
so the client needs to wait; drop (no reply) and delay is also
supported. All TTLs are set to zero.

Add simple tests that checks if first record is a SOA.
Signed-off-by: default avatarMiek Gieben <miek@miek.nl>

* more test coverage
Signed-off-by: default avatarMiek Gieben <miek@miek.nl>
parent f3134da4
...@@ -10,7 +10,8 @@ ...@@ -10,7 +10,8 @@
The *erratic* plugin will respond to every A or AAAA query. For any other type it will return 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 a SERVFAIL response. The reply for A will return 192.0.2.53 (see [RFC
5737](https://tools.ietf.org/html/rfc5737), 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 *erratic* can also be used in conjunction with the *autopath* plugin. This is mostly to aid in
testing. testing.
...@@ -30,6 +31,8 @@ erratic { ...@@ -30,6 +31,8 @@ erratic {
* `delay`: delay 1 per **AMOUNT** of queries for **DURATION**, the default for **AMOUNT** is 2 and * `delay`: delay 1 per **AMOUNT** of queries for **DURATION**, the default for **AMOUNT** is 2 and
the default for **DURATION** is 100ms. 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 ## Health
This plugin implements dynamic health checking. For every dropped query it turns unhealthy. This plugin implements dynamic health checking. For every dropped query it turns unhealthy.
......
...@@ -61,14 +61,26 @@ func (e *Erratic) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Msg ...@@ -61,14 +61,26 @@ func (e *Erratic) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Msg
rr := *(rrAAAA.(*dns.AAAA)) rr := *(rrAAAA.(*dns.AAAA))
rr.Header().Name = state.QName() rr.Header().Name = state.QName()
m.Answer = append(m.Answer, &rr) 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: default:
if !drop { if drop {
if delay { return 0, nil
time.Sleep(e.duration) }
} if delay {
// coredns will return error. time.Sleep(e.duration)
return dns.RcodeServerFailure, nil
} }
// coredns will return error.
return dns.RcodeServerFailure, nil
} }
if drop { if drop {
......
...@@ -14,19 +14,22 @@ func TestErraticDrop(t *testing.T) { ...@@ -14,19 +14,22 @@ func TestErraticDrop(t *testing.T) {
e := &Erratic{drop: 2} // 50% drops e := &Erratic{drop: 2} // 50% drops
tests := []struct { tests := []struct {
rrtype uint16
expectedCode int expectedCode int
expectedErr error expectedErr error
drop bool drop bool
}{ }{
{expectedCode: dns.RcodeSuccess, expectedErr: nil, drop: true}, {rrtype: dns.TypeA, expectedCode: dns.RcodeSuccess, expectedErr: nil, drop: true},
{expectedCode: dns.RcodeSuccess, expectedErr: nil, drop: false}, {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() ctx := context.TODO()
for i, tc := range tests { for i, tc := range tests {
req := new(dns.Msg) req := new(dns.Msg)
req.SetQuestion("example.org.", dns.TypeA) req.SetQuestion("example.org.", tc.rrtype)
rec := dnstest.NewRecorder(&test.ResponseWriter{}) rec := dnstest.NewRecorder(&test.ResponseWriter{})
code, err := e.ServeDNS(ctx, rec, req) code, err := e.ServeDNS(ctx, rec, req)
...@@ -77,3 +80,21 @@ func TestErraticTruncate(t *testing.T) { ...@@ -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)
}
}
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
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment