| 
									
										
										
										
											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
										 |  |  | } |