| 
									
										
										
										
											2016-08-19 17:14:17 -07:00
										 |  |  | package cache
 | 
					
						
							| 
									
										
										
										
											2016-04-19 11:13:24 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | import (
 | 
					
						
							| 
									
										
										
										
											2019-11-29 11:17:50 -04:00
										 |  |  | 	"errors"
 | 
					
						
							| 
									
										
										
										
											2016-11-09 10:01:26 +00:00
										 |  |  | 	"fmt"
 | 
					
						
							| 
									
										
										
										
											2016-04-19 11:13:24 +01:00
										 |  |  | 	"strconv"
 | 
					
						
							| 
									
										
										
										
											2022-02-14 12:10:30 -05:00
										 |  |  | 	"strings"
 | 
					
						
							| 
									
										
										
										
											2016-10-02 08:31:44 +01:00
										 |  |  | 	"time"
 | 
					
						
							| 
									
										
										
										
											2016-04-19 11:13:24 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-09-24 18:14:41 +02:00
										 |  |  | 	"github.com/coredns/caddy"
 | 
					
						
							| 
									
										
										
										
											2017-02-21 22:51:47 -08:00
										 |  |  | 	"github.com/coredns/coredns/core/dnsserver"
 | 
					
						
							| 
									
										
										
										
											2017-09-14 09:36:06 +01:00
										 |  |  | 	"github.com/coredns/coredns/plugin"
 | 
					
						
							|  |  |  | 	"github.com/coredns/coredns/plugin/pkg/cache"
 | 
					
						
							| 
									
										
										
										
											2018-04-22 21:40:33 +01:00
										 |  |  | 	clog "github.com/coredns/coredns/plugin/pkg/log"
 | 
					
						
							| 
									
										
										
										
											2016-04-19 11:13:24 +01:00
										 |  |  | )
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-04-22 21:40:33 +01:00
										 |  |  | var log = clog.NewWithPlugin("cache")
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-09-20 08:02:30 +01:00
										 |  |  | func init() { plugin.Register("cache", setup) }
 | 
					
						
							| 
									
										
										
										
											2016-08-19 17:14:17 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | func setup(c *caddy.Controller) error {
 | 
					
						
							| 
									
										
										
										
											2016-10-02 08:31:44 +01:00
										 |  |  | 	ca, err := cacheParse(c)
 | 
					
						
							| 
									
										
										
										
											2016-04-19 11:13:24 +01:00
										 |  |  | 	if err != nil {
 | 
					
						
							| 
									
										
										
										
											2017-09-14 09:36:06 +01:00
										 |  |  | 		return plugin.Error("cache", err)
 | 
					
						
							| 
									
										
										
										
											2016-04-19 11:13:24 +01:00
										 |  |  | 	}
 | 
					
						
							| 
									
										
										
										
											2022-09-08 14:56:27 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	c.OnStartup(func() error {
 | 
					
						
							|  |  |  | 		ca.viewMetricLabel = dnsserver.GetConfig(c).ViewName
 | 
					
						
							|  |  |  | 		return nil
 | 
					
						
							|  |  |  | 	})
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-09-14 09:36:06 +01:00
										 |  |  | 	dnsserver.GetConfig(c).AddPlugin(func(next plugin.Handler) plugin.Handler {
 | 
					
						
							| 
									
										
										
										
											2016-10-02 08:31:44 +01:00
										 |  |  | 		ca.Next = next
 | 
					
						
							|  |  |  | 		return ca
 | 
					
						
							| 
									
										
										
										
											2016-08-19 17:14:17 -07:00
										 |  |  | 	})
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return nil
 | 
					
						
							| 
									
										
										
										
											2016-04-19 11:13:24 +01:00
										 |  |  | }
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-10-02 08:31:44 +01:00
										 |  |  | func cacheParse(c *caddy.Controller) (*Cache, error) {
 | 
					
						
							| 
									
										
										
										
											2018-01-17 08:35:22 +01:00
										 |  |  | 	ca := New()
 | 
					
						
							| 
									
										
										
										
											2016-04-19 11:13:24 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-02-28 18:16:05 -08:00
										 |  |  | 	j := 0
 | 
					
						
							| 
									
										
										
										
											2016-04-19 11:13:24 +01:00
										 |  |  | 	for c.Next() {
 | 
					
						
							| 
									
										
										
										
											2018-02-28 18:16:05 -08:00
										 |  |  | 		if j > 0 {
 | 
					
						
							|  |  |  | 			return nil, plugin.ErrOnce
 | 
					
						
							|  |  |  | 		}
 | 
					
						
							|  |  |  | 		j++
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-10-02 08:31:44 +01:00
										 |  |  | 		// cache [ttl] [zones..]
 | 
					
						
							|  |  |  | 		args := c.RemainingArgs()
 | 
					
						
							|  |  |  | 		if len(args) > 0 {
 | 
					
						
							|  |  |  | 			// first args may be just a number, then it is the ttl, if not it is a zone
 | 
					
						
							|  |  |  | 			ttl, err := strconv.Atoi(args[0])
 | 
					
						
							|  |  |  | 			if err == nil {
 | 
					
						
							| 
									
										
										
										
											2016-11-09 10:01:26 +00:00
										 |  |  | 				// Reserve 0 (and smaller for future things)
 | 
					
						
							|  |  |  | 				if ttl <= 0 {
 | 
					
						
							|  |  |  | 					return nil, fmt.Errorf("cache TTL can not be zero or negative: %d", ttl)
 | 
					
						
							|  |  |  | 				}
 | 
					
						
							| 
									
										
										
										
											2016-10-02 08:31:44 +01:00
										 |  |  | 				ca.pttl = time.Duration(ttl) * time.Second
 | 
					
						
							|  |  |  | 				ca.nttl = time.Duration(ttl) * time.Second
 | 
					
						
							|  |  |  | 				args = args[1:]
 | 
					
						
							|  |  |  | 			}
 | 
					
						
							|  |  |  | 		}
 | 
					
						
							| 
									
										
										
										
											2021-05-17 22:19:54 +02:00
										 |  |  | 		origins := plugin.OriginsFromArgsOrServerBlock(args, c.ServerBlockKeys)
 | 
					
						
							| 
									
										
										
										
											2016-10-02 08:31:44 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | 		// Refinements? In an extra block.
 | 
					
						
							|  |  |  | 		for c.NextBlock() {
 | 
					
						
							|  |  |  | 			switch c.Val() {
 | 
					
						
							|  |  |  | 			// first number is cap, second is an new ttl
 | 
					
						
							| 
									
										
										
										
											2016-10-26 10:01:52 +01:00
										 |  |  | 			case Success:
 | 
					
						
							| 
									
										
										
										
											2016-10-02 08:31:44 +01:00
										 |  |  | 				args := c.RemainingArgs()
 | 
					
						
							|  |  |  | 				if len(args) == 0 {
 | 
					
						
							|  |  |  | 					return nil, c.ArgErr()
 | 
					
						
							|  |  |  | 				}
 | 
					
						
							|  |  |  | 				pcap, err := strconv.Atoi(args[0])
 | 
					
						
							|  |  |  | 				if err != nil {
 | 
					
						
							|  |  |  | 					return nil, err
 | 
					
						
							|  |  |  | 				}
 | 
					
						
							|  |  |  | 				ca.pcap = pcap
 | 
					
						
							|  |  |  | 				if len(args) > 1 {
 | 
					
						
							|  |  |  | 					pttl, err := strconv.Atoi(args[1])
 | 
					
						
							|  |  |  | 					if err != nil {
 | 
					
						
							|  |  |  | 						return nil, err
 | 
					
						
							|  |  |  | 					}
 | 
					
						
							| 
									
										
										
										
											2016-11-09 10:01:26 +00:00
										 |  |  | 					// Reserve 0 (and smaller for future things)
 | 
					
						
							|  |  |  | 					if pttl <= 0 {
 | 
					
						
							|  |  |  | 						return nil, fmt.Errorf("cache TTL can not be zero or negative: %d", pttl)
 | 
					
						
							|  |  |  | 					}
 | 
					
						
							| 
									
										
										
										
											2016-10-02 08:31:44 +01:00
										 |  |  | 					ca.pttl = time.Duration(pttl) * time.Second
 | 
					
						
							| 
									
										
										
										
											2018-09-03 14:26:02 -05:00
										 |  |  | 					if len(args) > 2 {
 | 
					
						
							|  |  |  | 						minpttl, err := strconv.Atoi(args[2])
 | 
					
						
							|  |  |  | 						if err != nil {
 | 
					
						
							|  |  |  | 							return nil, err
 | 
					
						
							|  |  |  | 						}
 | 
					
						
							|  |  |  | 						// Reserve < 0
 | 
					
						
							|  |  |  | 						if minpttl < 0 {
 | 
					
						
							|  |  |  | 							return nil, fmt.Errorf("cache min TTL can not be negative: %d", minpttl)
 | 
					
						
							|  |  |  | 						}
 | 
					
						
							|  |  |  | 						ca.minpttl = time.Duration(minpttl) * time.Second
 | 
					
						
							|  |  |  | 					}
 | 
					
						
							| 
									
										
										
										
											2016-10-02 08:31:44 +01:00
										 |  |  | 				}
 | 
					
						
							| 
									
										
										
										
											2016-10-26 10:01:52 +01:00
										 |  |  | 			case Denial:
 | 
					
						
							| 
									
										
										
										
											2016-10-02 08:31:44 +01:00
										 |  |  | 				args := c.RemainingArgs()
 | 
					
						
							|  |  |  | 				if len(args) == 0 {
 | 
					
						
							|  |  |  | 					return nil, c.ArgErr()
 | 
					
						
							|  |  |  | 				}
 | 
					
						
							|  |  |  | 				ncap, err := strconv.Atoi(args[0])
 | 
					
						
							|  |  |  | 				if err != nil {
 | 
					
						
							|  |  |  | 					return nil, err
 | 
					
						
							|  |  |  | 				}
 | 
					
						
							|  |  |  | 				ca.ncap = ncap
 | 
					
						
							|  |  |  | 				if len(args) > 1 {
 | 
					
						
							|  |  |  | 					nttl, err := strconv.Atoi(args[1])
 | 
					
						
							|  |  |  | 					if err != nil {
 | 
					
						
							|  |  |  | 						return nil, err
 | 
					
						
							| 
									
										
										
										
											2016-04-19 11:13:24 +01:00
										 |  |  | 					}
 | 
					
						
							| 
									
										
										
										
											2016-11-09 10:01:26 +00:00
										 |  |  | 					// Reserve 0 (and smaller for future things)
 | 
					
						
							|  |  |  | 					if nttl <= 0 {
 | 
					
						
							|  |  |  | 						return nil, fmt.Errorf("cache TTL can not be zero or negative: %d", nttl)
 | 
					
						
							|  |  |  | 					}
 | 
					
						
							| 
									
										
										
										
											2016-10-02 08:31:44 +01:00
										 |  |  | 					ca.nttl = time.Duration(nttl) * time.Second
 | 
					
						
							| 
									
										
										
										
											2018-09-03 14:26:02 -05:00
										 |  |  | 					if len(args) > 2 {
 | 
					
						
							|  |  |  | 						minnttl, err := strconv.Atoi(args[2])
 | 
					
						
							|  |  |  | 						if err != nil {
 | 
					
						
							|  |  |  | 							return nil, err
 | 
					
						
							|  |  |  | 						}
 | 
					
						
							|  |  |  | 						// Reserve < 0
 | 
					
						
							|  |  |  | 						if minnttl < 0 {
 | 
					
						
							|  |  |  | 							return nil, fmt.Errorf("cache min TTL can not be negative: %d", minnttl)
 | 
					
						
							|  |  |  | 						}
 | 
					
						
							|  |  |  | 						ca.minnttl = time.Duration(minnttl) * time.Second
 | 
					
						
							|  |  |  | 					}
 | 
					
						
							| 
									
										
										
										
											2016-04-19 11:13:24 +01:00
										 |  |  | 				}
 | 
					
						
							| 
									
										
										
										
											2017-06-13 12:39:10 -07:00
										 |  |  | 			case "prefetch":
 | 
					
						
							|  |  |  | 				args := c.RemainingArgs()
 | 
					
						
							|  |  |  | 				if len(args) == 0 || len(args) > 3 {
 | 
					
						
							|  |  |  | 					return nil, c.ArgErr()
 | 
					
						
							|  |  |  | 				}
 | 
					
						
							|  |  |  | 				amount, err := strconv.Atoi(args[0])
 | 
					
						
							|  |  |  | 				if err != nil {
 | 
					
						
							|  |  |  | 					return nil, err
 | 
					
						
							|  |  |  | 				}
 | 
					
						
							|  |  |  | 				if amount < 0 {
 | 
					
						
							|  |  |  | 					return nil, fmt.Errorf("prefetch amount should be positive: %d", amount)
 | 
					
						
							|  |  |  | 				}
 | 
					
						
							|  |  |  | 				ca.prefetch = amount
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 				if len(args) > 1 {
 | 
					
						
							|  |  |  | 					dur, err := time.ParseDuration(args[1])
 | 
					
						
							|  |  |  | 					if err != nil {
 | 
					
						
							|  |  |  | 						return nil, err
 | 
					
						
							|  |  |  | 					}
 | 
					
						
							|  |  |  | 					ca.duration = dur
 | 
					
						
							|  |  |  | 				}
 | 
					
						
							|  |  |  | 				if len(args) > 2 {
 | 
					
						
							|  |  |  | 					pct := args[2]
 | 
					
						
							|  |  |  | 					if x := pct[len(pct)-1]; x != '%' {
 | 
					
						
							|  |  |  | 						return nil, fmt.Errorf("last character of percentage should be `%%`, but is: %q", x)
 | 
					
						
							|  |  |  | 					}
 | 
					
						
							|  |  |  | 					pct = pct[:len(pct)-1]
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 					num, err := strconv.Atoi(pct)
 | 
					
						
							|  |  |  | 					if err != nil {
 | 
					
						
							|  |  |  | 						return nil, err
 | 
					
						
							|  |  |  | 					}
 | 
					
						
							|  |  |  | 					if num < 10 || num > 90 {
 | 
					
						
							|  |  |  | 						return nil, fmt.Errorf("percentage should fall in range [10, 90]: %d", num)
 | 
					
						
							|  |  |  | 					}
 | 
					
						
							|  |  |  | 					ca.percentage = num
 | 
					
						
							|  |  |  | 				}
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-11-29 11:17:50 -04:00
										 |  |  | 			case "serve_stale":
 | 
					
						
							|  |  |  | 				args := c.RemainingArgs()
 | 
					
						
							| 
									
										
										
										
											2022-05-02 19:16:33 +02:00
										 |  |  | 				if len(args) > 2 {
 | 
					
						
							| 
									
										
										
										
											2019-11-29 11:17:50 -04:00
										 |  |  | 					return nil, c.ArgErr()
 | 
					
						
							|  |  |  | 				}
 | 
					
						
							|  |  |  | 				ca.staleUpTo = 1 * time.Hour
 | 
					
						
							| 
									
										
										
										
											2022-05-02 19:16:33 +02:00
										 |  |  | 				if len(args) > 0 {
 | 
					
						
							| 
									
										
										
										
											2019-11-29 11:17:50 -04:00
										 |  |  | 					d, err := time.ParseDuration(args[0])
 | 
					
						
							|  |  |  | 					if err != nil {
 | 
					
						
							|  |  |  | 						return nil, err
 | 
					
						
							|  |  |  | 					}
 | 
					
						
							|  |  |  | 					if d < 0 {
 | 
					
						
							|  |  |  | 						return nil, errors.New("invalid negative duration for serve_stale")
 | 
					
						
							|  |  |  | 					}
 | 
					
						
							|  |  |  | 					ca.staleUpTo = d
 | 
					
						
							|  |  |  | 				}
 | 
					
						
							| 
									
										
										
										
											2022-05-02 19:16:33 +02:00
										 |  |  | 				ca.verifyStale = false
 | 
					
						
							|  |  |  | 				if len(args) > 1 {
 | 
					
						
							|  |  |  | 					mode := strings.ToLower(args[1])
 | 
					
						
							|  |  |  | 					if mode != "immediate" && mode != "verify" {
 | 
					
						
							|  |  |  | 						return nil, fmt.Errorf("invalid value for serve_stale refresh mode: %s", mode)
 | 
					
						
							|  |  |  | 					}
 | 
					
						
							|  |  |  | 					ca.verifyStale = mode == "verify"
 | 
					
						
							|  |  |  | 				}
 | 
					
						
							| 
									
										
										
										
											2022-06-17 15:48:57 -04:00
										 |  |  | 			case "servfail":
 | 
					
						
							|  |  |  | 				args := c.RemainingArgs()
 | 
					
						
							|  |  |  | 				if len(args) != 1 {
 | 
					
						
							|  |  |  | 					return nil, c.ArgErr()
 | 
					
						
							|  |  |  | 				}
 | 
					
						
							|  |  |  | 				d, err := time.ParseDuration(args[0])
 | 
					
						
							|  |  |  | 				if err != nil {
 | 
					
						
							|  |  |  | 					return nil, err
 | 
					
						
							|  |  |  | 				}
 | 
					
						
							|  |  |  | 				if d < 0 {
 | 
					
						
							|  |  |  | 					return nil, errors.New("invalid negative ttl for servfail")
 | 
					
						
							|  |  |  | 				}
 | 
					
						
							|  |  |  | 				if d > 5*time.Minute {
 | 
					
						
							|  |  |  | 					// RFC 2308 prohibits caching SERVFAIL longer than 5 minutes
 | 
					
						
							|  |  |  | 					return nil, errors.New("caching SERVFAIL responses over 5 minutes is not permitted")
 | 
					
						
							|  |  |  | 				}
 | 
					
						
							|  |  |  | 				ca.failttl = d
 | 
					
						
							| 
									
										
										
										
											2022-07-28 10:51:08 -04:00
										 |  |  | 			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)
 | 
					
						
							|  |  |  | 				}
 | 
					
						
							| 
									
										
										
										
											2023-01-27 17:35:24 +01:00
										 |  |  | 			case "keepttl":
 | 
					
						
							| 
									
										
										
										
											2023-08-09 08:32:50 -04:00
										 |  |  | 				args := c.RemainingArgs()
 | 
					
						
							| 
									
										
										
										
											2023-01-27 17:35:24 +01:00
										 |  |  | 				if len(args) != 0 {
 | 
					
						
							|  |  |  | 					return nil, c.ArgErr()
 | 
					
						
							|  |  |  | 				}
 | 
					
						
							|  |  |  | 				ca.keepttl = true
 | 
					
						
							| 
									
										
										
										
											2016-10-02 08:31:44 +01:00
										 |  |  | 			default:
 | 
					
						
							|  |  |  | 				return nil, c.ArgErr()
 | 
					
						
							| 
									
										
										
										
											2016-04-19 11:13:24 +01:00
										 |  |  | 			}
 | 
					
						
							| 
									
										
										
										
											2016-10-02 08:31:44 +01:00
										 |  |  | 		}
 | 
					
						
							| 
									
										
										
										
											2016-04-19 11:13:24 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-10-02 08:31:44 +01:00
										 |  |  | 		ca.Zones = origins
 | 
					
						
							| 
									
										
										
										
											2022-02-14 12:10:30 -05:00
										 |  |  | 		ca.zonesMetricLabel = strings.Join(origins, ",")
 | 
					
						
							| 
									
										
										
										
											2017-06-13 12:39:10 -07:00
										 |  |  | 		ca.pcache = cache.New(ca.pcap)
 | 
					
						
							|  |  |  | 		ca.ncache = cache.New(ca.ncap)
 | 
					
						
							| 
									
										
										
										
											2016-04-19 11:13:24 +01:00
										 |  |  | 	}
 | 
					
						
							| 
									
										
										
										
											2016-10-02 08:31:44 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-02-28 18:16:05 -08:00
										 |  |  | 	return ca, nil
 | 
					
						
							| 
									
										
										
										
											2016-04-19 11:13:24 +01:00
										 |  |  | }
 |