Skip to content

Commit

Permalink
Update the rfc2136 mutlizone fix to support the default root zone
Browse files Browse the repository at this point in the history
Add some tests for rfc2136 multizones

Signed-off-by: Greg Sidelinger <[email protected]>
  • Loading branch information
gregsidelinger committed Dec 20, 2023
1 parent e3b8e54 commit 1418a3a
Show file tree
Hide file tree
Showing 2 changed files with 182 additions and 13 deletions.
24 changes: 12 additions & 12 deletions provider/rfc2136/rfc2136.go
Original file line number Diff line number Diff line change
Expand Up @@ -257,6 +257,7 @@ func (r rfc2136Provider) ApplyChanges(ctx context.Context, changes *plan.Changes
log.Debugf("Processing batch %d of create changes", c)

m := make(map[string]*dns.Msg)
m["."] = new(dns.Msg) // Add the root zone
for _, z := range r.zoneNames {
z = dns.Fqdn(z)
m[z] = new(dns.Msg)
Expand All @@ -275,10 +276,9 @@ func (r rfc2136Provider) ApplyChanges(ctx context.Context, changes *plan.Changes
}

// only send if there are records available
for _, z := range r.zoneNames {
z = dns.Fqdn(z)
if len(m[z].Ns) > 0 {
err := r.actions.SendMessage(m[z])
for _, z := range m {
if len(z.Ns) > 0 {
err := r.actions.SendMessage(z)
if err != nil {
log.Errorf("RFC2136 update failed: %v", err)
errors = append(errors, err)
Expand All @@ -292,6 +292,7 @@ func (r rfc2136Provider) ApplyChanges(ctx context.Context, changes *plan.Changes
log.Debugf("Processing batch %d of update changes", c)

m := make(map[string]*dns.Msg)
m["."] = new(dns.Msg) // Add the root zone
for _, z := range r.zoneNames {
z = dns.Fqdn(z)
m[z] = new(dns.Msg)
Expand All @@ -311,10 +312,9 @@ func (r rfc2136Provider) ApplyChanges(ctx context.Context, changes *plan.Changes
}

// only send if there are records available
for _, z := range r.zoneNames {
z = dns.Fqdn(z)
if len(m[z].Ns) > 0 {
err := r.actions.SendMessage(m[z])
for _, z := range m {
if len(z.Ns) > 0 {
err := r.actions.SendMessage(z)
if err != nil {
log.Errorf("RFC2136 update failed: %v", err)
errors = append(errors, err)
Expand All @@ -328,6 +328,7 @@ func (r rfc2136Provider) ApplyChanges(ctx context.Context, changes *plan.Changes
log.Debugf("Processing batch %d of delete changes", c)

m := make(map[string]*dns.Msg)
m["."] = new(dns.Msg) // Add the root zone
for _, z := range r.zoneNames {
z = dns.Fqdn(z)
m[z] = new(dns.Msg)
Expand All @@ -346,10 +347,9 @@ func (r rfc2136Provider) ApplyChanges(ctx context.Context, changes *plan.Changes
}

// only send if there are records available
for _, z := range r.zoneNames {
z = dns.Fqdn(z)
if len(m[z].Ns) > 0 {
err := r.actions.SendMessage(m[z])
for _, z := range m {
if len(z.Ns) > 0 {
err := r.actions.SendMessage(z)
if err != nil {
log.Errorf("RFC2136 update failed: %v", err)
errors = append(errors, err)
Expand Down
171 changes: 170 additions & 1 deletion provider/rfc2136/rfc2136_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ package rfc2136
import (
"context"
"fmt"
"regexp"
"strings"
"testing"
"time"
Expand Down Expand Up @@ -47,7 +48,7 @@ func newStub() *rfc2136Stub {
}

func (r *rfc2136Stub) SendMessage(msg *dns.Msg) error {
log.Info(msg.String())
zone := extractZoneFromMessage(msg.String())
lines := extractUpdateSectionFromMessage(msg)
for _, line := range lines {
// break at first empty line
Expand All @@ -57,6 +58,11 @@ func (r *rfc2136Stub) SendMessage(msg *dns.Msg) error {

line = strings.Replace(line, "\t", " ", -1)
log.Info(line)
log.Infof("zone=%s", zone)
record := strings.Split(line, " ")[0]
if !strings.HasSuffix(record, zone) {
return fmt.Errorf("Message contains updates outside of it's zone. zone=%v record=%v", zone, record)
}

if strings.Contains(line, " NONE ") {
r.updateMsgs = append(r.updateMsgs, msg)
Expand Down Expand Up @@ -98,12 +104,28 @@ func createRfc2136StubProvider(stub *rfc2136Stub) (provider.Provider, error) {
return NewRfc2136Provider("", 0, nil, false, "key", "secret", "hmac-sha512", true, endpoint.DomainFilter{}, false, 300*time.Second, false, "", "", "", 50, stub)
}

func createRfc2136StubProviderWithZones(stub *rfc2136Stub) (provider.Provider, error) {
zones := []string{"foo.com", "foobar.com"}
return NewRfc2136Provider("", 0, zones, false, "key", "secret", "hmac-sha512", true, endpoint.DomainFilter{}, false, 300*time.Second, false, "", "", "", 50, stub)
}

func createRfc2136StubProviderWithZonesFilters(stub *rfc2136Stub) (provider.Provider, error) {
zones := []string{"foo.com", "foobar.com"}
return NewRfc2136Provider("", 0, zones, false, "key", "secret", "hmac-sha512", true, endpoint.DomainFilter{Filters: zones}, false, 300*time.Second, false, "", "", "", 50, stub)
}

func extractUpdateSectionFromMessage(msg fmt.Stringer) []string {
const searchPattern = "UPDATE SECTION:"
updateSectionOffset := strings.Index(msg.String(), searchPattern)
return strings.Split(strings.TrimSpace(msg.String()[updateSectionOffset+len(searchPattern):]), "\n")
}

func extractZoneFromMessage(msg string) string {
re := regexp.MustCompile(`ZONE SECTION:\n;(?P<ZONE>[\.,\-,\w,\d]+)\t`)
matches := re.FindStringSubmatch(msg)
return matches[re.SubexpIndex("ZONE")]
}

// TestRfc2136GetRecordsMultipleTargets simulates a single record with multiple targets.
func TestRfc2136GetRecordsMultipleTargets(t *testing.T) {
stub := newStub()
Expand Down Expand Up @@ -154,6 +176,32 @@ func TestRfc2136GetRecords(t *testing.T) {
assert.True(t, contains(recs, "v2.foo.com"))
}

// Make sure the test version of SendMessage raises an error
// if a zone update ever contains records outside of it's zone
// as the TestRfc2136ApplyChanges tests all assume this
func TestRfc2136SendMessage(t *testing.T) {
stub := newStub()

m := new(dns.Msg)
m.SetUpdate("foo.com.")
rr, err := dns.NewRR(fmt.Sprintf("%s %d %s %s", "v1.foo.com.", 0, "A", "1.2.3.4"))
m.Insert([]dns.RR{rr})

err = stub.SendMessage(m)
assert.NoError(t, err)

rr, err = dns.NewRR(fmt.Sprintf("%s %d %s %s", "v1.bar.com.", 0, "A", "1.2.3.4"))
m.Insert([]dns.RR{rr})

err = stub.SendMessage(m)
assert.Error(t, err)

m.SetUpdate(".")
err = stub.SendMessage(m)
assert.NoError(t, err)
}

// These tests are use the . root zone with no filters
func TestRfc2136ApplyChanges(t *testing.T) {
stub := newStub()
provider, err := createRfc2136StubProvider(stub)
Expand Down Expand Up @@ -210,6 +258,127 @@ func TestRfc2136ApplyChanges(t *testing.T) {
assert.True(t, strings.Contains(stub.updateMsgs[1].String(), "v2.foobar.com"))
}

// These tests all use the foo.com and foobar.com zones with no filters
func TestRfc2136ApplyChangesWithZones(t *testing.T) {
stub := newStub()
provider, err := createRfc2136StubProviderWithZones(stub)
assert.NoError(t, err)

p := &plan.Changes{
Create: []*endpoint.Endpoint{
{
DNSName: "v1.foo.com",
RecordType: "A",
Targets: []string{"1.2.3.4"},
RecordTTL: endpoint.TTL(400),
},
{
DNSName: "v1.foobar.com",
RecordType: "TXT",
Targets: []string{"boom"},
},
{
DNSName: "ns.foobar.com",
RecordType: "NS",
Targets: []string{"boom"},
},
},
Delete: []*endpoint.Endpoint{
{
DNSName: "v2.foo.com",
RecordType: "A",
Targets: []string{"1.2.3.4"},
},
{
DNSName: "v2.foobar.com",
RecordType: "TXT",
Targets: []string{"boom2"},
},
},
}

err = provider.ApplyChanges(context.Background(), p)
assert.NoError(t, err)

assert.Equal(t, 3, len(stub.createMsgs))
assert.True(t, strings.Contains(stub.createMsgs[0].String(), "v1.foo.com"))
assert.True(t, strings.Contains(stub.createMsgs[0].String(), "1.2.3.4"))

assert.True(t, strings.Contains(stub.createMsgs[1].String(), "v1.foobar.com"))
assert.True(t, strings.Contains(stub.createMsgs[1].String(), "boom"))

assert.True(t, strings.Contains(stub.createMsgs[2].String(), "ns.foobar.com"))
assert.True(t, strings.Contains(stub.createMsgs[2].String(), "boom"))

assert.Equal(t, 2, len(stub.updateMsgs))
assert.True(t, strings.Contains(stub.updateMsgs[0].String(), "v2.foo.com"))
assert.True(t, strings.Contains(stub.updateMsgs[1].String(), "v2.foobar.com"))
}

// These tests use the foo.com and foobar.com zones and with filters set to both zones
func TestRfc2136ApplyChangesWithZonesFilters(t *testing.T) {
stub := newStub()
provider, err := createRfc2136StubProviderWithZonesFilters(stub)
assert.NoError(t, err)

p := &plan.Changes{
Create: []*endpoint.Endpoint{
{
DNSName: "v1.foo.com",
RecordType: "A",
Targets: []string{"1.2.3.4"},
RecordTTL: endpoint.TTL(400),
},
{
DNSName: "v1.foobar.com",
RecordType: "TXT",
Targets: []string{"boom"},
},
{
DNSName: "ns.foobar.com",
RecordType: "NS",
Targets: []string{"boom"},
},
{
DNSName: "filtered-out.foo.bar",
RecordType: "A",
Targets: []string{"1.2.3.4"},
RecordTTL: endpoint.TTL(400),
},
},
Delete: []*endpoint.Endpoint{
{
DNSName: "v2.foo.com",
RecordType: "A",
Targets: []string{"1.2.3.4"},
},
{
DNSName: "v2.foobar.com",
RecordType: "TXT",
Targets: []string{"boom2"},
},
},
}

err = provider.ApplyChanges(context.Background(), p)
assert.NoError(t, err)

assert.Equal(t, 3, len(stub.createMsgs))
assert.True(t, strings.Contains(stub.createMsgs[0].String(), "v1.foo.com"))
assert.True(t, strings.Contains(stub.createMsgs[0].String(), "1.2.3.4"))

assert.True(t, strings.Contains(stub.createMsgs[1].String(), "v1.foobar.com"))
assert.True(t, strings.Contains(stub.createMsgs[1].String(), "boom"))

assert.True(t, strings.Contains(stub.createMsgs[2].String(), "ns.foobar.com"))
assert.True(t, strings.Contains(stub.createMsgs[2].String(), "boom"))

assert.Equal(t, 2, len(stub.updateMsgs))
assert.True(t, strings.Contains(stub.updateMsgs[0].String(), "v2.foo.com"))
assert.True(t, strings.Contains(stub.updateMsgs[1].String(), "v2.foobar.com"))

}

func TestRfc2136ApplyChangesWithDifferentTTLs(t *testing.T) {
stub := newStub()

Expand Down

0 comments on commit 1418a3a

Please sign in to comment.