Commit 83adb8fa authored by Chris O'Haver's avatar Chris O'Haver Committed by GitHub

plugin/file/cache: Add metadata for wildcard record responses (#5308)

For responses synthesized by known wildcard records, publish metadata containing the wildcard record name
Signed-off-by: default avatarChris O'Haver <cohaver@infoblox.com>
parent e80d6965
...@@ -114,6 +114,9 @@ type ResponseWriter struct { ...@@ -114,6 +114,9 @@ type ResponseWriter struct {
ad bool // When true the original request had the AD bit set. ad bool // When true the original request had the AD bit set.
prefetch bool // When true write nothing back to the client. prefetch bool // When true write nothing back to the client.
remoteAddr net.Addr remoteAddr net.Addr
wildcardFunc func() string // function to retrieve wildcard name that synthesized the result.
} }
// newPrefetchResponseWriter returns a Cache ResponseWriter to be used in // newPrefetchResponseWriter returns a Cache ResponseWriter to be used in
...@@ -202,6 +205,9 @@ func (w *ResponseWriter) set(m *dns.Msg, key uint64, mt response.Type, duration ...@@ -202,6 +205,9 @@ func (w *ResponseWriter) set(m *dns.Msg, key uint64, mt response.Type, duration
switch mt { switch mt {
case response.NoError, response.Delegation: case response.NoError, response.Delegation:
i := newItem(m, w.now(), duration) i := newItem(m, w.now(), duration)
if w.wildcardFunc != nil {
i.wildcard = w.wildcardFunc()
}
if w.pcache.Add(key, i) { if w.pcache.Add(key, i) {
evictions.WithLabelValues(w.server, Success, w.zonesMetricLabel).Inc() evictions.WithLabelValues(w.server, Success, w.zonesMetricLabel).Inc()
} }
...@@ -212,6 +218,9 @@ func (w *ResponseWriter) set(m *dns.Msg, key uint64, mt response.Type, duration ...@@ -212,6 +218,9 @@ func (w *ResponseWriter) set(m *dns.Msg, key uint64, mt response.Type, duration
case response.NameError, response.NoData, response.ServerError: case response.NameError, response.NoData, response.ServerError:
i := newItem(m, w.now(), duration) i := newItem(m, w.now(), duration)
if w.wildcardFunc != nil {
i.wildcard = w.wildcardFunc()
}
if w.ncache.Add(key, i) { if w.ncache.Add(key, i) {
evictions.WithLabelValues(w.server, Denial, w.zonesMetricLabel).Inc() evictions.WithLabelValues(w.server, Denial, w.zonesMetricLabel).Inc()
} }
......
...@@ -7,6 +7,7 @@ import ( ...@@ -7,6 +7,7 @@ import (
"time" "time"
"github.com/coredns/coredns/plugin" "github.com/coredns/coredns/plugin"
"github.com/coredns/coredns/plugin/metadata"
"github.com/coredns/coredns/plugin/pkg/dnstest" "github.com/coredns/coredns/plugin/pkg/dnstest"
"github.com/coredns/coredns/plugin/pkg/response" "github.com/coredns/coredns/plugin/pkg/response"
"github.com/coredns/coredns/plugin/test" "github.com/coredns/coredns/plugin/test"
...@@ -578,3 +579,59 @@ func TestComputeTTL(t *testing.T) { ...@@ -578,3 +579,59 @@ func TestComputeTTL(t *testing.T) {
} }
} }
} }
func TestCacheWildcardMetadata(t *testing.T) {
c := New()
qname := "foo.bar.example.org."
wildcard := "*.bar.example.org."
c.Next = wildcardMetadataBackend(qname, wildcard)
req := new(dns.Msg)
req.SetQuestion(qname, dns.TypeA)
// 1. Test writing wildcard metadata retrieved from backend to the cache
ctx := metadata.ContextWithMetadata(context.TODO())
w := dnstest.NewRecorder(&test.ResponseWriter{})
c.ServeDNS(ctx, w, req)
if c.pcache.Len() != 1 {
t.Errorf("Msg should have been cached")
}
_, k := key(qname, w.Msg, response.NoError)
i, _ := c.pcache.Get(k)
if i.(*item).wildcard != wildcard {
t.Errorf("expected wildcard reponse to enter cache with cache item's wildcard = %q, got %q", wildcard, i.(*item).wildcard)
}
// 2. Test retrieving the cached item from cache and writing its wildcard value to metadata
// reset context and response writer
ctx = metadata.ContextWithMetadata(context.TODO())
w = dnstest.NewRecorder(&test.ResponseWriter{})
c.ServeDNS(ctx, &test.ResponseWriter{}, req)
f := metadata.ValueFunc(ctx, "zone/wildcard")
if f == nil {
t.Fatal("expected metadata func for wildcard response retrieved from cache, got nil")
}
if f() != wildcard {
t.Errorf("after retrieving wildcard item from cache, expected \"zone/wildcard\" metadata value to be %q, got %q", wildcard, i.(*item).wildcard)
}
}
// wildcardMetadataBackend mocks a backend that reponds with a response for qname synthesized by wildcard
// and sets the zone/wildcard metadata value
func wildcardMetadataBackend(qname, wildcard string) plugin.Handler {
return plugin.HandlerFunc(func(ctx context.Context, w dns.ResponseWriter, r *dns.Msg) (int, error) {
m := new(dns.Msg)
m.SetReply(r)
m.Response, m.RecursionAvailable = true, true
m.Answer = []dns.RR{test.A(qname + " 300 IN A 127.0.0.1")}
metadata.SetValueFunc(ctx, "zone/wildcard", func() string {
return wildcard
})
w.WriteMsg(m)
return dns.RcodeSuccess, nil
})
}
...@@ -6,6 +6,7 @@ import ( ...@@ -6,6 +6,7 @@ import (
"time" "time"
"github.com/coredns/coredns/plugin" "github.com/coredns/coredns/plugin"
"github.com/coredns/coredns/plugin/metadata"
"github.com/coredns/coredns/plugin/metrics" "github.com/coredns/coredns/plugin/metrics"
"github.com/coredns/coredns/request" "github.com/coredns/coredns/request"
...@@ -37,7 +38,7 @@ func (c *Cache) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Msg) ...@@ -37,7 +38,7 @@ func (c *Cache) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Msg)
ttl := 0 ttl := 0
i := c.getIgnoreTTL(now, state, server) i := c.getIgnoreTTL(now, state, server)
if i == nil { if i == nil {
crr := &ResponseWriter{ResponseWriter: w, Cache: c, state: state, server: server, do: do, ad: ad} crr := &ResponseWriter{ResponseWriter: w, Cache: c, state: state, server: server, do: do, ad: ad, wildcardFunc: wildcardFunc(ctx)}
return c.doRefresh(ctx, state, crr) return c.doRefresh(ctx, state, crr)
} }
ttl = i.ttl(now) ttl = i.ttl(now)
...@@ -63,12 +64,29 @@ func (c *Cache) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Msg) ...@@ -63,12 +64,29 @@ func (c *Cache) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Msg)
cw := newPrefetchResponseWriter(server, state, c) cw := newPrefetchResponseWriter(server, state, c)
go c.doPrefetch(ctx, state, cw, i, now) go c.doPrefetch(ctx, state, cw, i, now)
} }
if i.wildcard != "" {
// Set wildcard source record name to metadata
metadata.SetValueFunc(ctx, "zone/wildcard", func() string {
return i.wildcard
})
}
resp := i.toMsg(r, now, do, ad) resp := i.toMsg(r, now, do, ad)
w.WriteMsg(resp) w.WriteMsg(resp)
return dns.RcodeSuccess, nil return dns.RcodeSuccess, nil
} }
func wildcardFunc(ctx context.Context) func() string {
return func() string {
// Get wildcard source record name from metadata
if f := metadata.ValueFunc(ctx, "zone/wildcard"); f != nil {
return f()
}
return ""
}
}
func (c *Cache) doPrefetch(ctx context.Context, state request.Request, cw *ResponseWriter, i *item, now time.Time) { func (c *Cache) doPrefetch(ctx context.Context, state request.Request, cw *ResponseWriter, i *item, now time.Time) {
cachePrefetches.WithLabelValues(cw.server, c.zonesMetricLabel).Inc() cachePrefetches.WithLabelValues(cw.server, c.zonesMetricLabel).Inc()
c.doRefresh(ctx, state, cw) c.doRefresh(ctx, state, cw)
......
...@@ -19,6 +19,7 @@ type item struct { ...@@ -19,6 +19,7 @@ type item struct {
Answer []dns.RR Answer []dns.RR
Ns []dns.RR Ns []dns.RR
Extra []dns.RR Extra []dns.RR
wildcard string
origTTL uint32 origTTL uint32
stored time.Time stored time.Time
......
...@@ -6,6 +6,7 @@ import ( ...@@ -6,6 +6,7 @@ import (
"github.com/coredns/coredns/core/dnsserver" "github.com/coredns/coredns/core/dnsserver"
"github.com/coredns/coredns/plugin/file/rrutil" "github.com/coredns/coredns/plugin/file/rrutil"
"github.com/coredns/coredns/plugin/file/tree" "github.com/coredns/coredns/plugin/file/tree"
"github.com/coredns/coredns/plugin/metadata"
"github.com/coredns/coredns/request" "github.com/coredns/coredns/request"
"github.com/miekg/dns" "github.com/miekg/dns"
...@@ -214,7 +215,10 @@ func (z *Zone) Lookup(ctx context.Context, state request.Request, qname string) ...@@ -214,7 +215,10 @@ func (z *Zone) Lookup(ctx context.Context, state request.Request, qname string)
// Found wildcard. // Found wildcard.
if wildElem != nil { if wildElem != nil {
auth := ap.ns(do) // set metadata value for the wildcard record that synthesized the result
metadata.SetValueFunc(ctx, "zone/wildcard", func() string {
return wildElem.Name()
})
if rrs := wildElem.TypeForWildcard(dns.TypeCNAME, qname); len(rrs) > 0 && qtype != dns.TypeCNAME { if rrs := wildElem.TypeForWildcard(dns.TypeCNAME, qname); len(rrs) > 0 && qtype != dns.TypeCNAME {
ctx = context.WithValue(ctx, dnsserver.LoopKey{}, loop+1) ctx = context.WithValue(ctx, dnsserver.LoopKey{}, loop+1)
...@@ -233,6 +237,7 @@ func (z *Zone) Lookup(ctx context.Context, state request.Request, qname string) ...@@ -233,6 +237,7 @@ func (z *Zone) Lookup(ctx context.Context, state request.Request, qname string)
return nil, ret, nil, NoData return nil, ret, nil, NoData
} }
auth := ap.ns(do)
if do { if do {
// An NSEC is needed to say no longer name exists under this wildcard. // An NSEC is needed to say no longer name exists under this wildcard.
if deny, found := tr.Prev(qname); found { if deny, found := tr.Prev(qname); found {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment