fix(auto/file): return REFUSED when no next plugin is available (#7381)

This commit is contained in:
Cameron Steel 2025-07-04 19:39:19 +10:00 committed by GitHub
parent 1449cb660e
commit 0aee758833
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 50 additions and 33 deletions

View File

@ -51,6 +51,10 @@ func (a Auto) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Msg) (i
// Now the real zone. // Now the real zone.
zone = plugin.Zones(a.Zones.Names()).Matches(qname) zone = plugin.Zones(a.Zones.Names()).Matches(qname)
if zone == "" { 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) return plugin.NextOrFailure(a.Name(), a.Next, ctx, w, r)
} }

View File

@ -104,39 +104,44 @@ func TestAutoServeDNSZoneMatching(t *testing.T) {
t.Parallel() t.Parallel()
tests := []struct { tests := []struct {
name string name string
origins []string origins []string
names []string names []string
qname string qname string
hasZone bool hasZone bool
shouldRefuse bool
}{ }{
{ {
name: "exact zone match", name: "exact zone match",
origins: []string{"example.org."}, origins: []string{"example.org."},
names: []string{"example.org."}, names: []string{"example.org."},
qname: "test.example.org.", qname: "test.example.org.",
hasZone: true, hasZone: true,
shouldRefuse: false,
}, },
{ {
name: "subdomain zone match", name: "subdomain zone match",
origins: []string{"example.org."}, origins: []string{"example.org."},
names: []string{"example.org."}, names: []string{"example.org."},
qname: "sub.test.example.org.", qname: "sub.test.example.org.",
hasZone: true, hasZone: true,
shouldRefuse: false,
}, },
{ {
name: "no origin match", name: "no origin match",
origins: []string{"other.org."}, origins: []string{"other.org."},
names: []string{"example.org."}, names: []string{"example.org."},
qname: "test.example.org.", qname: "test.example.org.",
hasZone: false, hasZone: false,
shouldRefuse: false,
}, },
{ {
name: "origin match but no name match", name: "origin match but no name match",
origins: []string{"example.org."}, origins: []string{"example.org."},
names: []string{"other.org."}, names: []string{"other.org."},
qname: "test.example.org.", qname: "test.example.org.",
hasZone: false, hasZone: false,
shouldRefuse: true,
}, },
} }
@ -163,14 +168,18 @@ func TestAutoServeDNSZoneMatching(t *testing.T) {
rec := dnstest.NewRecorder(&test.ResponseWriter{}) rec := dnstest.NewRecorder(&test.ResponseWriter{})
ctx := context.Background() ctx := context.Background()
_, err := a.ServeDNS(ctx, rec, m) code, err := a.ServeDNS(ctx, rec, m)
if tt.hasZone { if tt.hasZone {
if err != nil { if err != nil {
t.Errorf("Expected no error for zone match, got: %v", err) t.Errorf("Expected no error for zone match, got: %v", err)
} }
} else { } 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") t.Errorf("Expected error for no zone match, got nil")
} }
} }

View File

@ -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 // TODO(miek): match the qname better in the map
zone := plugin.Zones(f.Zones.Names).Matches(qname) zone := plugin.Zones(f.Zones.Names).Matches(qname)
if zone == "" { 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) return plugin.NextOrFailure(f.Name(), f.Next, ctx, w, r)
} }

View File

@ -32,8 +32,8 @@ func TestAuto(t *testing.T) {
if err != nil { if err != nil {
t.Fatal("Expected to receive reply, but didn't") t.Fatal("Expected to receive reply, but didn't")
} }
if resp.Rcode != dns.RcodeServerFailure { if resp.Rcode != dns.RcodeRefused {
t.Fatalf("Expected reply to be a SERVFAIL, got %d", resp.Rcode) t.Fatalf("Expected reply to be REFUSED, got %d", resp.Rcode)
} }
// Write db.example.org to get example.org. // Write db.example.org to get example.org.
@ -59,8 +59,8 @@ func TestAuto(t *testing.T) {
if err != nil { if err != nil {
t.Fatal("Expected to receive reply, but didn't") t.Fatal("Expected to receive reply, but didn't")
} }
if resp.Rcode != dns.RcodeServerFailure { if resp.Rcode != dns.RcodeRefused {
t.Fatalf("Expected reply to be a SERVFAIL, got %d", resp.Rcode) t.Fatalf("Expected reply to be REFUSED, got %d", resp.Rcode)
} }
} }
@ -93,8 +93,8 @@ func TestAutoNonExistentZone(t *testing.T) {
if err != nil { if err != nil {
t.Fatal("Expected to receive reply, but didn't") t.Fatal("Expected to receive reply, but didn't")
} }
if resp.Rcode != dns.RcodeServerFailure { if resp.Rcode != dns.RcodeRefused {
t.Fatalf("Expected reply to be a SERVFAIL, got %d", resp.Rcode) t.Fatalf("Expected reply to be REFUSED, got %d", resp.Rcode)
} }
} }