aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGravatar Chris O'Haver <cohaver@infoblox.com> 2022-07-28 10:51:08 -0400
committerGravatar GitHub <noreply@github.com> 2022-07-28 10:51:08 -0400
commit95fcf2c480b7ab6a291d97520d0d93296ddfdb6f (patch)
tree3f4b316b400ad41abea13e3d2d00dcffcf1a4fb0
parent2fe5273cd12562aca6939540f0a9e03b51d34aba (diff)
downloadcoredns-95fcf2c480b7ab6a291d97520d0d93296ddfdb6f.tar.gz
coredns-95fcf2c480b7ab6a291d97520d0d93296ddfdb6f.tar.zst
coredns-95fcf2c480b7ab6a291d97520d0d93296ddfdb6f.zip
plugin/cache: Add cache disable option (#5540)
* add cache disable options Signed-off-by: Chris O'Haver <cohaver@infoblox.com>
-rw-r--r--plugin/cache/README.md13
-rw-r--r--plugin/cache/cache.go14
-rw-r--r--plugin/cache/cache_test.go63
-rw-r--r--plugin/cache/handler.go3
-rw-r--r--plugin/cache/setup.go29
-rw-r--r--plugin/cache/setup_test.go39
6 files changed, 158 insertions, 3 deletions
diff --git a/plugin/cache/README.md b/plugin/cache/README.md
index 9322af16c..602072656 100644
--- a/plugin/cache/README.md
+++ b/plugin/cache/README.md
@@ -39,6 +39,7 @@ cache [TTL] [ZONES...] {
prefetch AMOUNT [[DURATION] [PERCENTAGE%]]
serve_stale [DURATION] [REFRESH_MODE]
servfail DURATION
+ disable success|denial [ZONES...]
}
~~~
@@ -67,6 +68,8 @@ cache [TTL] [ZONES...] {
* `servfail` cache SERVFAIL responses for **DURATION**. Setting **DURATION** to 0 will disable caching of SERVFAIL
responses. If this option is not set, SERVFAIL responses will be cached for 5 seconds. **DURATION** may not be
greater than 5 minutes.
+* `disable` disable the success or denial cache for the listed **ZONES**. If no **ZONES** are given, the specified
+ cache will be disabled for all zones.
## Capacity and Eviction
@@ -124,3 +127,13 @@ example.org {
}
}
~~~
+
+Enable caching for `example.org`, but do not cache denials in `sub.example.org`:
+
+~~~ corefile
+example.org {
+ cache {
+ disable denial sub.example.org
+ }
+}
+~~~ \ No newline at end of file
diff --git a/plugin/cache/cache.go b/plugin/cache/cache.go
index d1989f35b..bfd8c1576 100644
--- a/plugin/cache/cache.go
+++ b/plugin/cache/cache.go
@@ -43,6 +43,10 @@ type Cache struct {
staleUpTo time.Duration
verifyStale bool
+ // Positive/negative zone exceptions
+ pexcept []string
+ nexcept []string
+
// Testing.
now func() time.Time
}
@@ -117,6 +121,8 @@ type ResponseWriter struct {
wildcardFunc func() string // function to retrieve wildcard name that synthesized the result.
+ pexcept []string // positive zone exceptions
+ nexcept []string // negative zone exceptions
}
// newPrefetchResponseWriter returns a Cache ResponseWriter to be used in
@@ -204,6 +210,10 @@ func (w *ResponseWriter) set(m *dns.Msg, key uint64, mt response.Type, duration
// and key is valid
switch mt {
case response.NoError, response.Delegation:
+ if plugin.Zones(w.pexcept).Matches(m.Question[0].Name) != "" {
+ // zone is in exception list, do not cache
+ return
+ }
i := newItem(m, w.now(), duration)
if w.wildcardFunc != nil {
i.wildcard = w.wildcardFunc()
@@ -217,6 +227,10 @@ func (w *ResponseWriter) set(m *dns.Msg, key uint64, mt response.Type, duration
}
case response.NameError, response.NoData, response.ServerError:
+ if plugin.Zones(w.nexcept).Matches(m.Question[0].Name) != "" {
+ // zone is in exception list, do not cache
+ return
+ }
i := newItem(m, w.now(), duration)
if w.wildcardFunc != nil {
i.wildcard = w.wildcardFunc()
diff --git a/plugin/cache/cache_test.go b/plugin/cache/cache_test.go
index 851507a4d..0328c8411 100644
--- a/plugin/cache/cache_test.go
+++ b/plugin/cache/cache_test.go
@@ -17,8 +17,8 @@ import (
)
type cacheTestCase struct {
- test.Case
- in test.Case
+ test.Case // the expected message coming "out" of cache
+ in test.Case // the test message going "in" to cache
AuthenticatedData bool
RecursionAvailable bool
Truncated bool
@@ -163,6 +163,62 @@ var cacheTestCases = []cacheTestCase{
},
shouldCache: true,
},
+ {
+ in: test.Case{
+ Rcode: dns.RcodeNameError,
+ Qname: "neg-disabled.example.org.", Qtype: dns.TypeA,
+ Ns: []dns.RR{
+ test.SOA("example.org. 3600 IN SOA sns.dns.icann.org. noc.dns.icann.org. 2016082540 7200 3600 1209600 3600"),
+ },
+ },
+ Case: test.Case{},
+ shouldCache: false,
+ },
+ {
+ in: test.Case{
+ Rcode: dns.RcodeSuccess,
+ Qname: "pos-disabled.example.org.", Qtype: dns.TypeA,
+ Answer: []dns.RR{
+ test.A("pos-disabled.example.org. 3600 IN A 127.0.0.1"),
+ },
+ },
+ Case: test.Case{},
+ shouldCache: false,
+ },
+ {
+ in: test.Case{
+ Rcode: dns.RcodeNameError,
+ Qname: "pos-disabled.example.org.", Qtype: dns.TypeA,
+ Ns: []dns.RR{
+ test.SOA("example.org. 3600 IN SOA sns.dns.icann.org. noc.dns.icann.org. 2016082540 7200 3600 1209600 3600"),
+ },
+ },
+ Case: test.Case{
+ Rcode: dns.RcodeNameError,
+ Qname: "pos-disabled.example.org.", Qtype: dns.TypeA,
+ Ns: []dns.RR{
+ test.SOA("example.org. 3600 IN SOA sns.dns.icann.org. noc.dns.icann.org. 2016082540 7200 3600 1209600 3600"),
+ },
+ },
+ shouldCache: true,
+ },
+ {
+ in: test.Case{
+ Rcode: dns.RcodeSuccess,
+ Qname: "neg-disabled.example.org.", Qtype: dns.TypeA,
+ Answer: []dns.RR{
+ test.A("neg-disabled.example.org. 3600 IN A 127.0.0.1"),
+ },
+ },
+ Case: test.Case{
+ Rcode: dns.RcodeSuccess,
+ Qname: "neg-disabled.example.org.", Qtype: dns.TypeA,
+ Answer: []dns.RR{
+ test.A("neg-disabled.example.org. 3600 IN A 127.0.0.1"),
+ },
+ },
+ shouldCache: true,
+ },
}
func cacheMsg(m *dns.Msg, tc cacheTestCase) *dns.Msg {
@@ -183,6 +239,9 @@ func newTestCache(ttl time.Duration) (*Cache, *ResponseWriter) {
c.nttl = ttl
crr := &ResponseWriter{ResponseWriter: nil, Cache: c}
+ crr.nexcept = []string{"neg-disabled.example.org."}
+ crr.pexcept = []string{"pos-disabled.example.org."}
+
return c, crr
}
diff --git a/plugin/cache/handler.go b/plugin/cache/handler.go
index 69ec4928d..3d2f43904 100644
--- a/plugin/cache/handler.go
+++ b/plugin/cache/handler.go
@@ -38,7 +38,8 @@ func (c *Cache) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Msg)
ttl := 0
i := c.getIgnoreTTL(now, state, server)
if i == nil {
- crr := &ResponseWriter{ResponseWriter: w, Cache: c, state: state, server: server, do: do, ad: ad, wildcardFunc: wildcardFunc(ctx)}
+ crr := &ResponseWriter{ResponseWriter: w, Cache: c, state: state, server: server, do: do, ad: ad,
+ nexcept: c.nexcept, pexcept: c.pexcept, wildcardFunc: wildcardFunc(ctx)}
return c.doRefresh(ctx, state, crr)
}
ttl = i.ttl(now)
diff --git a/plugin/cache/setup.go b/plugin/cache/setup.go
index aa487105c..a1ce255a9 100644
--- a/plugin/cache/setup.go
+++ b/plugin/cache/setup.go
@@ -205,6 +205,35 @@ func cacheParse(c *caddy.Controller) (*Cache, error) {
return nil, errors.New("caching SERVFAIL responses over 5 minutes is not permitted")
}
ca.failttl = d
+ case "disable":
+ // disable [success|denial] [zones]...
+ args := c.RemainingArgs()
+ if len(args) < 1 {
+ return nil, c.ArgErr()
+ }
+
+ var zones []string
+ if len(args) > 1 {
+ for _, z := range args[1:] { // args[1:] define the list of zones to disable
+ nz := plugin.Name(z).Normalize()
+ if nz == "" {
+ return nil, fmt.Errorf("invalid disabled zone: %s", z)
+ }
+ zones = append(zones, nz)
+ }
+ } else {
+ // if no zones specified, default to root
+ zones = []string{"."}
+ }
+
+ switch args[0] { // args[0] defines which cache to disable
+ case Denial:
+ ca.nexcept = zones
+ case Success:
+ ca.pexcept = zones
+ default:
+ return nil, fmt.Errorf("cache type for disable must be %q or %q", Success, Denial)
+ }
default:
return nil, c.ArgErr()
}
diff --git a/plugin/cache/setup_test.go b/plugin/cache/setup_test.go
index 5e684c510..5d8b9653c 100644
--- a/plugin/cache/setup_test.go
+++ b/plugin/cache/setup_test.go
@@ -192,3 +192,42 @@ func TestServfail(t *testing.T) {
}
}
}
+
+func TestDisable(t *testing.T) {
+ tests := []struct {
+ input string
+ shouldErr bool
+ nexcept []string
+ pexcept []string
+ }{
+ // positive
+ {"disable denial example.com example.org", false, []string{"example.com.", "example.org."}, nil},
+ {"disable success example.com example.org", false, nil, []string{"example.com.", "example.org."}},
+ {"disable denial", false, []string{"."}, nil},
+ {"disable success", false, nil, []string{"."}},
+ {"disable denial example.com example.org\ndisable success example.com example.org", false,
+ []string{"example.com.", "example.org."}, []string{"example.com.", "example.org."}},
+ // negative
+ {"disable invalid example.com example.org", true, nil, nil},
+ }
+ for i, test := range tests {
+ c := caddy.NewTestController("dns", fmt.Sprintf("cache {\n%s\n}", test.input))
+ ca, err := cacheParse(c)
+ if test.shouldErr && err == nil {
+ t.Errorf("Test %v: Expected error but found nil", i)
+ continue
+ } else if !test.shouldErr && err != nil {
+ t.Errorf("Test %v: Expected no error but found error: %v", i, err)
+ continue
+ }
+ if test.shouldErr {
+ continue
+ }
+ if fmt.Sprintf("%v", test.nexcept) != fmt.Sprintf("%v", ca.nexcept) {
+ t.Errorf("Test %v: Expected %v but got: %v", i, test.nexcept, ca.nexcept)
+ }
+ if fmt.Sprintf("%v", test.pexcept) != fmt.Sprintf("%v", ca.pexcept) {
+ t.Errorf("Test %v: Expected %v but got: %v", i, test.pexcept, ca.pexcept)
+ }
+ }
+}