Cherry-pick: Implement notifies for transfer plugin (#3972) (#4142)

* Implement notifies for transfer plugin (#3972)

* Fix notifies in transfer plugin

Signed-off-by: Miek Gieben <miek@miek.nl>

* Make it compile

Signed-off-by: Miek Gieben <miek@miek.nl>

* Port more plugins

Signed-off-by: Miek Gieben <miek@miek.nl>

* golint

Signed-off-by: Miek Gieben <miek@miek.nl>

* Fix tests

Signed-off-by: Miek Gieben <miek@miek.nl>

* Fix notifies in transfer plugin

Signed-off-by: Miek Gieben <miek@miek.nl>

* Make it compile

Signed-off-by: Miek Gieben <miek@miek.nl>

* Port more plugins

Signed-off-by: Miek Gieben <miek@miek.nl>

* golint

Signed-off-by: Miek Gieben <miek@miek.nl>

* Fix tests

Signed-off-by: Miek Gieben <miek@miek.nl>

* Fix tests

Signed-off-by: Miek Gieben <miek@miek.nl>

* really fix test

Signed-off-by: Miek Gieben <miek@miek.nl>

* Implement ixfr fallback and unify file and auto for transfering

Signed-off-by: Miek Gieben <miek@miek.nl>

* Add transfer tests

copied and modified from #3452

Signed-off-by: Miek Gieben <miek@miek.nl>

* Test correct selection of plugin

Signed-off-by: Miek Gieben <miek@miek.nl>

* add upstream back in

Signed-off-by: Miek Gieben <miek@miek.nl>

* Implement ixfr fallback and unify file and auto for transfering

Signed-off-by: Miek Gieben <miek@miek.nl>

* fix test

Signed-off-by: Miek Gieben <miek@miek.nl>

* properly merge

Signed-off-by: Miek Gieben <miek@miek.nl>

* Remove plugin/kubernetes/setup_transfer_test.go

Signed-off-by: Yong Tang <yong.tang.github@outlook.com>

Co-authored-by: Miek Gieben <miek@miek.nl>
This commit is contained in:
Yong Tang
2020-09-24 11:30:39 -07:00
committed by GitHub
parent 279194f2e4
commit 9798dd067f
42 changed files with 705 additions and 986 deletions

View File

@@ -2,34 +2,38 @@
## Name
*transfer* - perform zone transfers for other plugins.
*transfer* - perform (outgoing) zone transfers for other plugins.
## Description
This plugin answers zone transfers for authoritative plugins that implement
`transfer.Transferer`. Currently, no internal plugins implement this interface.
This plugin answers zone transfers for authoritative plugins that implement `transfer.Transferer`.
Transfer answers full zone transfer (AXFR) requests and incremental zone transfer (IXFR) requests
*transfer* answers full zone transfer (AXFR) requests and incremental zone transfer (IXFR) requests
with AXFR fallback if the zone has changed.
Notifies are not currently supported.
When a plugin wants to notify it's secondaries it will call back into the *transfer* plugin.
The following plugins implement zone transfers using this plugin: *file*, *auto*, *secondary*, and
*kubernetes*. See `transfer.go` for implementation details if you are a plugin author that wants to
use this plugin.
## Syntax
~~~
transfer [ZONE...] {
to HOST...
to ADDRESS...
}
~~~
* **ZONES** The zones *transfer* will answer zone requests for. If left blank,
the zones are inherited from the enclosing server block. To answer zone
transfers for a given zone, there must be another plugin in the same server
block that serves the same zone, and implements `transfer.Transferer`.
* **ZONE** The zones *transfer* will answer zone transfer requests for. If left blank, the zones
are inherited from the enclosing server block. To answer zone transfers for a given zone,
there must be another plugin in the same server block that serves the same zone, and implements
`transfer.Transferer`.
* `to ` **HOST...** The hosts *transfer* will transfer to. Use `*` to permit
transfers to all hosts.
* `to` **ADDRESS...** The hosts *transfer* will transfer to. Use `*` to permit transfers to all
addresses. **ADDRESS** must be denoted in CIDR notation (e.g., 127.0.0.1/32) or just as plain
addresses. `to` may be specified multiple times.
## Examples
TODO
See the specific plugins using this plugin for examples on it's usage.

58
plugin/transfer/notify.go Normal file
View File

@@ -0,0 +1,58 @@
package transfer
import (
"fmt"
"github.com/coredns/coredns/plugin/pkg/rcode"
"github.com/miekg/dns"
)
// Notify will send notifies to all configured to hosts IP addresses. If the zone isn't known
// to t an error will be returned. The string zone must be lowercased.
func (t *Transfer) Notify(zone string) error {
if t == nil { // t might be nil, mostly expected in tests, so intercept and to a noop in that case
return nil
}
m := new(dns.Msg)
m.SetNotify(zone)
c := new(dns.Client)
x := longestMatch(t.xfrs, zone)
if x == nil {
return fmt.Errorf("no such zone registred in the transfer plugin: %s", zone)
}
var err1 error
for _, t := range x.to {
if t == "*" {
continue
}
if err := sendNotify(c, m, t); err != nil {
err1 = err
}
}
log.Debugf("Sent notifies for zone %q to %v", zone, x.to)
return err1 // this only captures the last error
}
func sendNotify(c *dns.Client, m *dns.Msg, s string) error {
var err error
code := dns.RcodeServerFailure
for i := 0; i < 3; i++ {
ret, _, err := c.Exchange(m, s)
if err != nil {
continue
}
code = ret.Rcode
if code == dns.RcodeSuccess {
return nil
}
}
if err != nil {
return fmt.Errorf("notify for zone %q was not accepted by %q: %q", m.Question[0].Name, s, err)
}
return fmt.Errorf("notify for zone %q was not accepted by %q: rcode was %q", m.Question[0].Name, s, rcode.ToString(code))
}

View File

@@ -0,0 +1,58 @@
package transfer
import (
"context"
"fmt"
"testing"
"github.com/coredns/coredns/plugin/pkg/dnstest"
"github.com/coredns/coredns/plugin/test"
"github.com/miekg/dns"
)
type (
t1 struct{}
t2 struct{}
)
func (t t1) Transfer(zone string, serial uint32) (<-chan []dns.RR, error) {
const z = "example.org."
if zone != z {
return nil, ErrNotAuthoritative
}
return nil, fmt.Errorf(z)
}
func (t t2) Transfer(zone string, serial uint32) (<-chan []dns.RR, error) {
const z = "sub.example.org."
if zone != z {
return nil, ErrNotAuthoritative
}
return nil, fmt.Errorf(z)
}
func TestZoneSelection(t *testing.T) {
tr := &Transfer{
Transferers: []Transferer{t1{}, t2{}},
xfrs: []*xfr{
{
Zones: []string{"example.org."},
to: []string{"192.0.2.1"}, // RFC 5737 IP, no interface should have this address.
},
{
Zones: []string{"sub.example.org."},
to: []string{"*"},
},
},
}
r := new(dns.Msg)
r.SetAxfr("sub.example.org.")
w := dnstest.NewRecorder(&test.ResponseWriter{})
_, err := tr.ServeDNS(context.TODO(), w, r)
if err == nil {
t.Fatal("Expected error, got nil")
}
if x := err.Error(); x != "sub.example.org." {
t.Errorf("Expected transfer for zone %s, got %s", "sub.example.org", x)
}
}

View File

@@ -4,7 +4,7 @@ import (
"github.com/coredns/caddy"
"github.com/coredns/coredns/core/dnsserver"
"github.com/coredns/coredns/plugin"
parsepkg "github.com/coredns/coredns/plugin/pkg/parse"
"github.com/coredns/coredns/plugin/pkg/parse"
"github.com/coredns/coredns/plugin/pkg/transport"
)
@@ -16,7 +16,7 @@ func init() {
}
func setup(c *caddy.Controller) error {
t, err := parse(c)
t, err := parseTransfer(c)
if err != nil {
return plugin.Error("transfer", err)
@@ -43,8 +43,7 @@ func setup(c *caddy.Controller) error {
return nil
}
func parse(c *caddy.Controller) (*Transfer, error) {
func parseTransfer(c *caddy.Controller) (*Transfer, error) {
t := &Transfer{}
for c.Next() {
x := &xfr{}
@@ -82,14 +81,14 @@ func parse(c *caddy.Controller) (*Transfer, error) {
x.to = append(x.to, host)
continue
}
normalized, err := parsepkg.HostPort(host, transport.Port)
normalized, err := parse.HostPort(host, transport.Port)
if err != nil {
return nil, err
}
x.to = append(x.to, normalized)
}
default:
return nil, plugin.Error("transfer", c.Errf("unknown property '%s'", c.Val()))
return nil, plugin.Error("transfer", c.Errf("unknown property %q", c.Val()))
}
}
if len(x.to) == 0 {

View File

@@ -6,12 +6,6 @@ import (
"github.com/coredns/caddy"
)
func newTestControllerWithZones(input string, zones []string) *caddy.Controller {
ctr := caddy.NewTestController("dns", input)
ctr.ServerBlockKeys = append(ctr.ServerBlockKeys, zones...)
return ctr
}
func TestParse(t *testing.T) {
tests := []struct {
input string
@@ -75,8 +69,10 @@ func TestParse(t *testing.T) {
},
}
for i, tc := range tests {
c := newTestControllerWithZones(tc.input, tc.zones)
transfer, err := parse(c)
c := caddy.NewTestController("dns", tc.input)
c.ServerBlockKeys = append(c.ServerBlockKeys, tc.zones...)
transfer, err := parseTransfer(c)
if err == nil && tc.shouldErr {
t.Fatalf("Test %d expected errors, but got no error", i)

View File

@@ -3,7 +3,6 @@ package transfer
import (
"context"
"errors"
"fmt"
"net"
"sync"
@@ -18,9 +17,9 @@ var log = clog.NewWithPlugin("transfer")
// Transfer is a plugin that handles zone transfers.
type Transfer struct {
Transferers []Transferer // the list of plugins that implement Transferer
Transferers []Transferer // List of plugins that implement Transferer
xfrs []*xfr
Next plugin.Handler // the next plugin in the chain
Next plugin.Handler
}
type xfr struct {
@@ -32,53 +31,53 @@ type xfr struct {
type Transferer interface {
// Transfer returns a channel to which it writes responses to the transfer request.
// If the plugin is not authoritative for the zone, it should immediately return the
// Transfer.ErrNotAuthoritative error.
// transfer.ErrNotAuthoritative error. This is important otherwise the transfer plugin can
// use plugin X while it should transfer the data from plugin Y.
//
// If serial is 0, handle as an AXFR request. Transfer should send all records
// in the zone to the channel. The SOA should be written to the channel first, followed
// by all other records, including all NS + glue records.
// by all other records, including all NS + glue records. The implemenation is also responsible
// for sending the last SOA record (to signal end of the transfer). This plugin will just grab
// these records and send them back to the requester, there is little validation done.
//
// If serial is not 0, handle as an IXFR request. If the serial is equal to or greater (newer) than
// the current serial for the zone, send a single SOA record to the channel.
// If serial is not 0, it will be handled as an IXFR request. If the serial is equal to or greater (newer) than
// the current serial for the zone, send a single SOA record to the channel and then close it.
// If the serial is less (older) than the current serial for the zone, perform an AXFR fallback
// by proceeding as if an AXFR was requested (as above).
Transfer(zone string, serial uint32) (<-chan []dns.RR, error)
}
var (
// ErrNotAuthoritative is returned by Transfer() when the plugin is not authoritative for the zone
// ErrNotAuthoritative is returned by Transfer() when the plugin is not authoritative for the zone.
ErrNotAuthoritative = errors.New("not authoritative for zone")
)
// ServeDNS implements the plugin.Handler interface.
func (t Transfer) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Msg) (int, error) {
func (t *Transfer) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Msg) (int, error) {
state := request.Request{W: w, Req: r}
if state.QType() != dns.TypeAXFR && state.QType() != dns.TypeIXFR {
return plugin.NextOrFailure(t.Name(), t.Next, ctx, w, r)
}
// Find the first transfer instance for which the queried zone is a subdomain.
var x *xfr
for _, xfr := range t.xfrs {
zone := plugin.Zones(xfr.Zones).Matches(state.Name())
if zone == "" {
continue
}
x = xfr
}
x := longestMatch(t.xfrs, state.QName())
if x == nil {
// Requested zone did not match any transfer instance zones.
// Pass request down chain in case later plugins are capable of handling transfer requests themselves.
return plugin.NextOrFailure(t.Name(), t.Next, ctx, w, r)
}
if !x.allowed(state) {
return dns.RcodeRefused, nil
// write msg here, so logging will pick it up
m := new(dns.Msg)
m.SetRcode(r, dns.RcodeRefused)
w.WriteMsg(m)
return 0, nil
}
// Get serial from request if this is an IXFR
// Get serial from request if this is an IXFR.
var serial uint32
if state.QType() == dns.TypeIXFR {
if len(r.Ns) != 1 {
return dns.RcodeServerFailure, nil
}
soa, ok := r.Ns[0].(*dns.SOA)
if !ok {
return dns.RcodeServerFailure, nil
@@ -86,11 +85,11 @@ func (t Transfer) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Msg
serial = soa.Serial
}
// Get a receiving channel from the first Transferer plugin that returns one
var fromPlugin <-chan []dns.RR
// Get a receiving channel from the first Transferer plugin that returns one.
var pchan <-chan []dns.RR
var err error
for _, p := range t.Transferers {
var err error
fromPlugin, err = p.Transfer(state.QName(), serial)
pchan, err = p.Transfer(state.QName(), serial)
if err == ErrNotAuthoritative {
// plugin was not authoritative for the zone, try next plugin
continue
@@ -101,7 +100,7 @@ func (t Transfer) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Msg
break
}
if fromPlugin == nil {
if pchan == nil {
return plugin.NextOrFailure(t.Name(), t.Next, ctx, w, r)
}
@@ -115,26 +114,51 @@ func (t Transfer) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Msg
wg.Done()
}()
var soa *dns.SOA
rrs := []dns.RR{}
l := 0
receive:
for records := range fromPlugin {
for _, record := range records {
if soa == nil {
if soa = record.(*dns.SOA); soa == nil {
break receive
}
serial = soa.Serial
}
rrs = append(rrs, record)
if len(rrs) > 500 {
ch <- &dns.Envelope{RR: rrs}
l += len(rrs)
rrs = []dns.RR{}
}
var soa *dns.SOA
for records := range pchan {
if x, ok := records[0].(*dns.SOA); ok && soa == nil {
soa = x
}
rrs = append(rrs, records...)
if len(rrs) > 500 {
ch <- &dns.Envelope{RR: rrs}
l += len(rrs)
rrs = []dns.RR{}
}
}
// if we are here and we only hold 1 soa (len(rrs) == 1) and soa != nil, and IXFR fallback should
// be performed. We haven't send anything on ch yet, so that can be closed (and waited for), and we only
// need to return the SOA back to the client and return.
if len(rrs) == 1 && soa != nil { // soa should never be nil...
close(ch)
wg.Wait()
m := new(dns.Msg)
m.SetReply(r)
m.Answer = []dns.RR{soa}
w.WriteMsg(m)
log.Infof("Outgoing incremental transfer for up to date zone %q to %s for %d SOA serial", state.QName(), state.IP(), serial)
return 0, nil
}
// if we are here and we only hold 1 soa (len(rrs) == 1) and soa != nil, and IXFR fallback should
// be performed. We haven't send anything on ch yet, so that can be closed (and waited for), and we only
// need to return the SOA back to the client and return.
if len(rrs) == 1 && soa != nil { // soa should never be nil...
close(ch)
wg.Wait()
m := new(dns.Msg)
m.SetReply(r)
m.Answer = []dns.RR{soa}
w.WriteMsg(m)
log.Infof("Outgoing noop, incremental transfer for up to date zone %q to %s for %d SOA serial", state.QName(), state.IP(), soa.Serial)
return 0, nil
}
if len(rrs) > 0 {
@@ -142,20 +166,15 @@ receive:
l += len(rrs)
}
if soa != nil {
ch <- &dns.Envelope{RR: []dns.RR{soa}} // closing SOA.
l++
}
close(ch) // Even though we close the channel here, we still have
wg.Wait() // to wait before we can return and close the connection.
if soa == nil {
return dns.RcodeServerFailure, fmt.Errorf("first record in zone %s is not SOA", state.QName())
logserial := uint32(0)
if soa != nil {
logserial = soa.Serial
}
log.Infof("Outgoing transfer of %d records of zone %s to %s with %d SOA serial", l, state.QName(), state.IP(), serial)
return dns.RcodeSuccess, nil
log.Infof("Outgoing transfer of %d records of zone %q to %s for %d SOA serial", l, state.QName(), state.IP(), logserial)
return 0, nil
}
func (x xfr) allowed(state request.Request) bool {
@@ -167,14 +186,30 @@ func (x xfr) allowed(state request.Request) bool {
if err != nil {
return false
}
// If remote IP matches we accept.
remote := state.IP()
if to == remote {
// If remote IP matches we accept. TODO(): make this works with ranges
if to == state.IP() {
return true
}
}
return false
}
// Find the first transfer instance for which the queried zone is the longest match. When nothing
// is found nil is returned.
func longestMatch(xfrs []*xfr, name string) *xfr {
// TODO(xxx): optimize and make it a map (or maps)
var x *xfr
zone := "" // longest zone match wins
for _, xfr := range xfrs {
if z := plugin.Zones(xfr.Zones).Matches(name); z != "" {
if z > zone {
zone = z
x = xfr
}
}
}
return x
}
// Name implements the Handler interface.
func (Transfer) Name() string { return "transfer" }

View File

@@ -12,18 +12,18 @@ import (
"github.com/miekg/dns"
)
// transfererPlugin implements transfer.Transferer and plugin.Handler
// transfererPlugin implements transfer.Transferer and plugin.Handler.
type transfererPlugin struct {
Zone string
Serial uint32
Next plugin.Handler
}
// Name implements plugin.Handler
func (transfererPlugin) Name() string { return "transfererplugin" }
// Name implements plugin.Handler.
func (*transfererPlugin) Name() string { return "transfererplugin" }
// ServeDNS implements plugin.Handler
func (p transfererPlugin) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Msg) (int, error) {
// ServeDNS implements plugin.Handler.
func (p *transfererPlugin) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Msg) (int, error) {
if r.Question[0].Name != p.Zone {
return p.Next.ServeDNS(ctx, w, r)
}
@@ -31,12 +31,12 @@ func (p transfererPlugin) ServeDNS(ctx context.Context, w dns.ResponseWriter, r
}
// Transfer implements transfer.Transferer - it returns a static AXFR response, or
// if serial is current, an abbreviated IXFR response
func (p transfererPlugin) Transfer(zone string, serial uint32) (<-chan []dns.RR, error) {
// if serial is current, an abbreviated IXFR response.
func (p *transfererPlugin) Transfer(zone string, serial uint32) (<-chan []dns.RR, error) {
if zone != p.Zone {
return nil, ErrNotAuthoritative
}
ch := make(chan []dns.RR, 2)
ch := make(chan []dns.RR, 3) // sending 3 bits and don't want to block, nor do a waitgroup
defer close(ch)
ch <- []dns.RR{test.SOA(fmt.Sprintf("%s 100 IN SOA ns.dns.%s hostmaster.%s %d 7200 1800 86400 100", p.Zone, p.Zone, p.Zone, p.Serial))}
if serial >= p.Serial {
@@ -46,30 +46,31 @@ func (p transfererPlugin) Transfer(zone string, serial uint32) (<-chan []dns.RR,
test.NS(fmt.Sprintf("%s 100 IN NS ns.dns.%s", p.Zone, p.Zone)),
test.A(fmt.Sprintf("ns.dns.%s 100 IN A 1.2.3.4", p.Zone)),
}
ch <- []dns.RR{test.SOA(fmt.Sprintf("%s 100 IN SOA ns.dns.%s hostmaster.%s %d 7200 1800 86400 100", p.Zone, p.Zone, p.Zone, p.Serial))}
return ch, nil
}
type terminatingPlugin struct{}
// Name implements plugin.Handler
func (terminatingPlugin) Name() string { return "testplugin" }
// Name implements plugin.Handler.
func (*terminatingPlugin) Name() string { return "testplugin" }
// ServeDNS implements plugin.Handler that returns NXDOMAIN for all requests
func (terminatingPlugin) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Msg) (int, error) {
// ServeDNS implements plugin.Handler that returns NXDOMAIN for all requests.
func (*terminatingPlugin) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Msg) (int, error) {
m := new(dns.Msg)
m.SetRcode(r, dns.RcodeNameError)
w.WriteMsg(m)
return dns.RcodeNameError, nil
}
func newTestTransfer() Transfer {
func newTestTransfer() *Transfer {
nextPlugin1 := transfererPlugin{Zone: "example.com.", Serial: 12345}
nextPlugin2 := transfererPlugin{Zone: "example.org.", Serial: 12345}
nextPlugin2.Next = terminatingPlugin{}
nextPlugin1.Next = nextPlugin2
nextPlugin2.Next = &terminatingPlugin{}
nextPlugin1.Next = &nextPlugin2
transfer := Transfer{
Transferers: []Transferer{nextPlugin1, nextPlugin2},
transfer := &Transfer{
Transferers: []Transferer{&nextPlugin1, &nextPlugin2},
xfrs: []*xfr{
{
Zones: []string{"example.org."},
@@ -80,22 +81,21 @@ func newTestTransfer() Transfer {
to: []string{"*"},
},
},
Next: nextPlugin1,
Next: &nextPlugin1,
}
return transfer
}
func TestTransferNonZone(t *testing.T) {
transfer := newTestTransfer()
ctx := context.TODO()
for _, tc := range []string{"sub.example.org.", "example.test."} {
w := dnstest.NewRecorder(&test.ResponseWriter{})
dnsmsg := &dns.Msg{}
dnsmsg.SetAxfr(tc)
m := &dns.Msg{}
m.SetAxfr(tc)
_, err := transfer.ServeDNS(ctx, w, dnsmsg)
_, err := transfer.ServeDNS(ctx, w, m)
if err != nil {
t.Error(err)
}
@@ -111,15 +111,14 @@ func TestTransferNonZone(t *testing.T) {
}
func TestTransferNotAXFRorIXFR(t *testing.T) {
transfer := newTestTransfer()
ctx := context.TODO()
w := dnstest.NewRecorder(&test.ResponseWriter{})
dnsmsg := &dns.Msg{}
dnsmsg.SetQuestion("test.domain.", dns.TypeA)
m := &dns.Msg{}
m.SetQuestion("test.domain.", dns.TypeA)
_, err := transfer.ServeDNS(ctx, w, dnsmsg)
_, err := transfer.ServeDNS(ctx, w, m)
if err != nil {
t.Error(err)
}
@@ -134,15 +133,14 @@ func TestTransferNotAXFRorIXFR(t *testing.T) {
}
func TestTransferAXFRExampleOrg(t *testing.T) {
transfer := newTestTransfer()
ctx := context.TODO()
w := dnstest.NewMultiRecorder(&test.ResponseWriter{})
dnsmsg := &dns.Msg{}
dnsmsg.SetAxfr(transfer.xfrs[0].Zones[0])
m := &dns.Msg{}
m.SetAxfr(transfer.xfrs[0].Zones[0])
_, err := transfer.ServeDNS(ctx, w, dnsmsg)
_, err := transfer.ServeDNS(ctx, w, m)
if err != nil {
t.Error(err)
}
@@ -151,39 +149,14 @@ func TestTransferAXFRExampleOrg(t *testing.T) {
}
func TestTransferAXFRExampleCom(t *testing.T) {
transfer := newTestTransfer()
ctx := context.TODO()
w := dnstest.NewMultiRecorder(&test.ResponseWriter{})
dnsmsg := &dns.Msg{}
dnsmsg.SetAxfr(transfer.xfrs[1].Zones[0])
m := &dns.Msg{}
m.SetAxfr(transfer.xfrs[1].Zones[0])
_, err := transfer.ServeDNS(ctx, w, dnsmsg)
if err != nil {
t.Error(err)
}
validateAXFRResponse(t, w)
}
func TestTransferIXFRFallback(t *testing.T) {
transfer := newTestTransfer()
testPlugin := transfer.Transferers[0].(transfererPlugin)
ctx := context.TODO()
w := dnstest.NewMultiRecorder(&test.ResponseWriter{})
dnsmsg := &dns.Msg{}
dnsmsg.SetIxfr(
transfer.xfrs[0].Zones[0],
testPlugin.Serial-1,
"ns.dns."+testPlugin.Zone,
"hostmaster.dns."+testPlugin.Zone,
)
_, err := transfer.ServeDNS(ctx, w, dnsmsg)
_, err := transfer.ServeDNS(ctx, w, m)
if err != nil {
t.Error(err)
}
@@ -192,28 +165,21 @@ func TestTransferIXFRFallback(t *testing.T) {
}
func TestTransferIXFRCurrent(t *testing.T) {
transfer := newTestTransfer()
testPlugin := transfer.Transferers[0].(transfererPlugin)
testPlugin := transfer.Transferers[0].(*transfererPlugin)
ctx := context.TODO()
w := dnstest.NewMultiRecorder(&test.ResponseWriter{})
dnsmsg := &dns.Msg{}
dnsmsg.SetIxfr(
transfer.xfrs[0].Zones[0],
testPlugin.Serial,
"ns.dns."+testPlugin.Zone,
"hostmaster.dns."+testPlugin.Zone,
)
m := &dns.Msg{}
m.SetIxfr(transfer.xfrs[0].Zones[0], testPlugin.Serial, "ns.dns."+testPlugin.Zone, "hostmaster.dns."+testPlugin.Zone)
_, err := transfer.ServeDNS(ctx, w, dnsmsg)
_, err := transfer.ServeDNS(ctx, w, m)
if err != nil {
t.Error(err)
}
if len(w.Msgs) == 0 {
t.Logf("%+v\n", w)
t.Fatal("Did not get back a zone response")
}
@@ -228,9 +194,31 @@ func TestTransferIXFRCurrent(t *testing.T) {
}
}
func TestTransferIXFRFallback(t *testing.T) {
transfer := newTestTransfer()
testPlugin := transfer.Transferers[0].(*transfererPlugin)
ctx := context.TODO()
w := dnstest.NewMultiRecorder(&test.ResponseWriter{})
m := &dns.Msg{}
m.SetIxfr(
transfer.xfrs[0].Zones[0],
testPlugin.Serial-1,
"ns.dns."+testPlugin.Zone,
"hostmaster.dns."+testPlugin.Zone,
)
_, err := transfer.ServeDNS(ctx, w, m)
if err != nil {
t.Error(err)
}
validateAXFRResponse(t, w)
}
func validateAXFRResponse(t *testing.T, w *dnstest.MultiRecorder) {
if len(w.Msgs) == 0 {
t.Logf("%+v\n", w)
t.Fatal("Did not get back a zone response")
}
@@ -263,29 +251,28 @@ func TestTransferNotAllowed(t *testing.T) {
nextPlugin := transfererPlugin{Zone: "example.org.", Serial: 12345}
transfer := Transfer{
Transferers: []Transferer{nextPlugin},
Transferers: []Transferer{&nextPlugin},
xfrs: []*xfr{
{
Zones: []string{"example.org."},
to: []string{"1.2.3.4"},
},
},
Next: nextPlugin,
Next: &nextPlugin,
}
ctx := context.TODO()
w := dnstest.NewMultiRecorder(&test.ResponseWriter{})
dnsmsg := &dns.Msg{}
dnsmsg.SetAxfr(transfer.xfrs[0].Zones[0])
w := dnstest.NewRecorder(&test.ResponseWriter{})
m := &dns.Msg{}
m.SetAxfr(transfer.xfrs[0].Zones[0])
rcode, err := transfer.ServeDNS(ctx, w, dnsmsg)
_, err := transfer.ServeDNS(ctx, w, m)
if err != nil {
t.Error(err)
}
if rcode != dns.RcodeRefused {
t.Errorf("Expected REFUSED response code, got %s", dns.RcodeToString[rcode])
if w.Msg.Rcode != dns.RcodeRefused {
t.Errorf("Expected REFUSED response code, got %s", dns.RcodeToString[w.Msg.Rcode])
}
}