mirror of
https://github.com/coredns/coredns.git
synced 2026-10-17 15:55:47 -04:00
plugin/file: test that out-of-zone CNAME targets keep NOERROR (#8569)
This commit is contained in:
@@ -469,6 +469,9 @@ func delegationFromElem(tr *tree.Tree, elem *tree.Elem, qname string, qtype uint
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (z *Zone) doLookup(ctx context.Context, state request.Request, target string, qtype uint16) ([]dns.RR, Result) {
|
func (z *Zone) doLookup(ctx context.Context, state request.Request, target string, qtype uint16) ([]dns.RR, Result) {
|
||||||
|
if z.Upstream == nil {
|
||||||
|
return nil, ServerFailure
|
||||||
|
}
|
||||||
m, e := z.Upstream.Lookup(ctx, state, target, qtype)
|
m, e := z.Upstream.Lookup(ctx, state, target, qtype)
|
||||||
if e != nil {
|
if e != nil {
|
||||||
return nil, ServerFailure
|
return nil, ServerFailure
|
||||||
@@ -485,6 +488,9 @@ func (z *Zone) doLookup(ctx context.Context, state request.Request, target strin
|
|||||||
if m.Rcode == dns.RcodeSuccess && len(m.Answer) == 0 {
|
if m.Rcode == dns.RcodeSuccess && len(m.Answer) == 0 {
|
||||||
return m.Answer, NoData
|
return m.Answer, NoData
|
||||||
}
|
}
|
||||||
|
// Anything else, including the REFUSED returned for a target outside every
|
||||||
|
// zone served here, keeps NOERROR: the CNAME is the answer and the resolver
|
||||||
|
// chases the target itself. See #7346.
|
||||||
return m.Answer, Success
|
return m.Answer, Success
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -557,3 +557,55 @@ cname-srv IN CNAME srv.example.org.
|
|||||||
dname IN DNAME dsub.example.org.
|
dname IN DNAME dsub.example.org.
|
||||||
srv.dsub IN SRV 10 50 8080 host.example.org.
|
srv.dsub IN SRV 10 50 8080 host.example.org.
|
||||||
`
|
`
|
||||||
|
|
||||||
|
const dbExampleOrgOutOfZone = `$ORIGIN example.org.
|
||||||
|
@ 3600 IN SOA sns.dns.icann.org. noc.dns.icann.org. ( 2017042745 7200 3600 1209600 3600 )
|
||||||
|
3600 IN NS a.iana-servers.net.
|
||||||
|
www 3600 IN CNAME www.example.net.
|
||||||
|
`
|
||||||
|
|
||||||
|
// refusedUpstream answers every lookup with REFUSED, which is what the server
|
||||||
|
// returns for a name that matches none of the zones it serves.
|
||||||
|
type refusedUpstream struct{}
|
||||||
|
|
||||||
|
func (refusedUpstream) Lookup(_ context.Context, _ request.Request, name string, typ uint16) (*dns.Msg, error) {
|
||||||
|
m := new(dns.Msg)
|
||||||
|
m.SetQuestion(name, typ)
|
||||||
|
m.Rcode = dns.RcodeRefused
|
||||||
|
return m, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestLookupRefusedUpstream checks that a CNAME whose target lies outside every
|
||||||
|
// served zone is answered with NOERROR and the CNAME, so the resolver can chase
|
||||||
|
// the target itself. The self-lookup for such a target comes back REFUSED, which
|
||||||
|
// is not an error for an authoritative answer. See #7346 and #7381.
|
||||||
|
func TestLookupRefusedUpstream(t *testing.T) {
|
||||||
|
const origin = "example.org."
|
||||||
|
zone, err := Parse(strings.NewReader(dbExampleOrgOutOfZone), origin, "stdin", 0)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Expected no error when reading zone, got %q", err)
|
||||||
|
}
|
||||||
|
zone.Upstream = refusedUpstream{}
|
||||||
|
|
||||||
|
fm := File{Next: test.ErrorHandler(), Zones: Zones{Z: map[string]*Zone{origin: zone}, Names: []string{origin}}}
|
||||||
|
|
||||||
|
m := new(dns.Msg)
|
||||||
|
m.SetQuestion("www.example.org.", dns.TypeA)
|
||||||
|
|
||||||
|
rec := dnstest.NewRecorder(&test.ResponseWriter{})
|
||||||
|
if _, err := fm.ServeDNS(context.TODO(), rec, m); err != nil {
|
||||||
|
t.Fatalf("Expected no error, got %v", err)
|
||||||
|
}
|
||||||
|
if rec.Msg == nil {
|
||||||
|
t.Fatal("Expected a response to be written, got none")
|
||||||
|
}
|
||||||
|
if rec.Msg.Rcode != dns.RcodeSuccess {
|
||||||
|
t.Errorf("rcode is %q, expected %q", dns.RcodeToString[rec.Msg.Rcode], dns.RcodeToString[dns.RcodeSuccess])
|
||||||
|
}
|
||||||
|
if len(rec.Msg.Answer) != 1 {
|
||||||
|
t.Fatalf("Expected the CNAME in the answer, got %d records", len(rec.Msg.Answer))
|
||||||
|
}
|
||||||
|
if _, ok := rec.Msg.Answer[0].(*dns.CNAME); !ok {
|
||||||
|
t.Errorf("Expected a CNAME in the answer, got %s", rec.Msg.Answer[0])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package file
|
package file
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
@@ -8,11 +9,17 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/coredns/coredns/plugin/file/tree"
|
"github.com/coredns/coredns/plugin/file/tree"
|
||||||
"github.com/coredns/coredns/plugin/pkg/upstream"
|
"github.com/coredns/coredns/request"
|
||||||
|
|
||||||
"github.com/miekg/dns"
|
"github.com/miekg/dns"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Upstreamer is used to resolve CNAME or other external targets during the
|
||||||
|
// resolution process.
|
||||||
|
type Upstreamer interface {
|
||||||
|
Lookup(ctx context.Context, state request.Request, name string, typ uint16) (*dns.Msg, error)
|
||||||
|
}
|
||||||
|
|
||||||
// Zone is a structure that contains all data related to a DNS zone.
|
// Zone is a structure that contains all data related to a DNS zone.
|
||||||
type Zone struct {
|
type Zone struct {
|
||||||
origin string
|
origin string
|
||||||
@@ -32,7 +39,7 @@ type Zone struct {
|
|||||||
ReloadByMtime bool
|
ReloadByMtime bool
|
||||||
reloadShutdown chan bool
|
reloadShutdown chan bool
|
||||||
|
|
||||||
Upstream *upstream.Upstream // Upstream for looking up external names during the resolution process.
|
Upstream Upstreamer // Upstream for looking up external names during the resolution process.
|
||||||
}
|
}
|
||||||
|
|
||||||
// Apex contains the apex records of a zone: SOA, NS and their potential signatures.
|
// Apex contains the apex records of a zone: SOA, NS and their potential signatures.
|
||||||
|
|||||||
Reference in New Issue
Block a user