From 0aee758833cabb1ec89756a698c52b83bbbdc587 Mon Sep 17 00:00:00 2001 From: Cameron Steel Date: Fri, 4 Jul 2025 19:39:19 +1000 Subject: [PATCH] fix(auto/file): return REFUSED when no next plugin is available (#7381) --- plugin/auto/auto.go | 4 +++ plugin/auto/auto_test.go | 63 +++++++++++++++++++++++----------------- plugin/file/file.go | 4 +++ test/auto_test.go | 12 ++++---- 4 files changed, 50 insertions(+), 33 deletions(-) diff --git a/plugin/auto/auto.go b/plugin/auto/auto.go index b2a7c76ed..fa62d8f14 100644 --- a/plugin/auto/auto.go +++ b/plugin/auto/auto.go @@ -51,6 +51,10 @@ func (a Auto) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Msg) (i // Now the real zone. zone = plugin.Zones(a.Zones.Names()).Matches(qname) if zone == "" { + // If no next plugin is configured, it's more correct to return REFUSED as auto acts as an authoritative server + if a.Next == nil { + return dns.RcodeRefused, nil + } return plugin.NextOrFailure(a.Name(), a.Next, ctx, w, r) } diff --git a/plugin/auto/auto_test.go b/plugin/auto/auto_test.go index 243c7ebc8..9871d342d 100644 --- a/plugin/auto/auto_test.go +++ b/plugin/auto/auto_test.go @@ -104,39 +104,44 @@ func TestAutoServeDNSZoneMatching(t *testing.T) { t.Parallel() tests := []struct { - name string - origins []string - names []string - qname string - hasZone bool + name string + origins []string + names []string + qname string + hasZone bool + shouldRefuse bool }{ { - name: "exact zone match", - origins: []string{"example.org."}, - names: []string{"example.org."}, - qname: "test.example.org.", - hasZone: true, + name: "exact zone match", + origins: []string{"example.org."}, + names: []string{"example.org."}, + qname: "test.example.org.", + hasZone: true, + shouldRefuse: false, }, { - name: "subdomain zone match", - origins: []string{"example.org."}, - names: []string{"example.org."}, - qname: "sub.test.example.org.", - hasZone: true, + name: "subdomain zone match", + origins: []string{"example.org."}, + names: []string{"example.org."}, + qname: "sub.test.example.org.", + hasZone: true, + shouldRefuse: false, }, { - name: "no origin match", - origins: []string{"other.org."}, - names: []string{"example.org."}, - qname: "test.example.org.", - hasZone: false, + name: "no origin match", + origins: []string{"other.org."}, + names: []string{"example.org."}, + qname: "test.example.org.", + hasZone: false, + shouldRefuse: false, }, { - name: "origin match but no name match", - origins: []string{"example.org."}, - names: []string{"other.org."}, - qname: "test.example.org.", - hasZone: false, + name: "origin match but no name match", + origins: []string{"example.org."}, + names: []string{"other.org."}, + qname: "test.example.org.", + hasZone: false, + shouldRefuse: true, }, } @@ -163,14 +168,18 @@ func TestAutoServeDNSZoneMatching(t *testing.T) { rec := dnstest.NewRecorder(&test.ResponseWriter{}) ctx := context.Background() - _, err := a.ServeDNS(ctx, rec, m) + code, err := a.ServeDNS(ctx, rec, m) if tt.hasZone { if err != nil { t.Errorf("Expected no error for zone match, got: %v", err) } } else { - if err == nil { + if tt.shouldRefuse { + if code != dns.RcodeRefused { + t.Errorf("Expected code %d, got %d", dns.RcodeRefused, code) + } + } else if err == nil { t.Errorf("Expected error for no zone match, got nil") } } diff --git a/plugin/file/file.go b/plugin/file/file.go index 8ff893ba8..67b81e9a5 100644 --- a/plugin/file/file.go +++ b/plugin/file/file.go @@ -42,6 +42,10 @@ func (f File) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Msg) (i // TODO(miek): match the qname better in the map zone := plugin.Zones(f.Zones.Names).Matches(qname) if zone == "" { + // If no next plugin is configured, it's more correct to return REFUSED as file acts as an authoritative server + if f.Next == nil { + return dns.RcodeRefused, nil + } return plugin.NextOrFailure(f.Name(), f.Next, ctx, w, r) } diff --git a/test/auto_test.go b/test/auto_test.go index a2aedeb2c..ea98b1c91 100644 --- a/test/auto_test.go +++ b/test/auto_test.go @@ -32,8 +32,8 @@ func TestAuto(t *testing.T) { if err != nil { t.Fatal("Expected to receive reply, but didn't") } - if resp.Rcode != dns.RcodeServerFailure { - t.Fatalf("Expected reply to be a SERVFAIL, got %d", resp.Rcode) + if resp.Rcode != dns.RcodeRefused { + t.Fatalf("Expected reply to be REFUSED, got %d", resp.Rcode) } // Write db.example.org to get example.org. @@ -59,8 +59,8 @@ func TestAuto(t *testing.T) { if err != nil { t.Fatal("Expected to receive reply, but didn't") } - if resp.Rcode != dns.RcodeServerFailure { - t.Fatalf("Expected reply to be a SERVFAIL, got %d", resp.Rcode) + if resp.Rcode != dns.RcodeRefused { + t.Fatalf("Expected reply to be REFUSED, got %d", resp.Rcode) } } @@ -93,8 +93,8 @@ func TestAutoNonExistentZone(t *testing.T) { if err != nil { t.Fatal("Expected to receive reply, but didn't") } - if resp.Rcode != dns.RcodeServerFailure { - t.Fatalf("Expected reply to be a SERVFAIL, got %d", resp.Rcode) + if resp.Rcode != dns.RcodeRefused { + t.Fatalf("Expected reply to be REFUSED, got %d", resp.Rcode) } }