summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDominik Derigs <dl6er@dl6er.de>2022-11-26 21:18:34 +0000
committerSimon Kelley <simon@thekelleys.org.uk>2022-11-26 21:18:34 +0000
commitefbf80be5834e922b00784a00f27636c0f8e4034 (patch)
treeee345e42261f34714a28b4ff060b9e0df82218e2
parent022ad63f0c8cbb17ba37ee4128eae30ebb873ce4 (diff)
downloaddnsmasq-efbf80be5834e922b00784a00f27636c0f8e4034.tar.gz
Make max staleness of stale cache entries configurable and default to one day.
-rw-r--r--man/dnsmasq.85
-rw-r--r--src/cache.c16
-rw-r--r--src/config.h1
-rw-r--r--src/dnsmasq.h6
-rw-r--r--src/option.c22
5 files changed, 39 insertions, 11 deletions
diff --git a/man/dnsmasq.8 b/man/dnsmasq.8
index 53bccb8..2495ed1 100644
--- a/man/dnsmasq.8
+++ b/man/dnsmasq.8
@@ -828,11 +828,12 @@ name on successive queries, for load-balancing. This turns off that
behaviour, so that the records are always returned in the order
that they are received from upstream.
.TP
-.B --use-stale-cache
+.B --use-stale-cache[=<max TTL excess in s>]
When set, if a DNS name exists in the cache, but its time-to-live has expired, dnsmasq will return the data anyway. (It attempts to refresh the
data with an upstream query after returning the stale data.) This can improve speed and reliability. It comes at the expense
of sometimes returning out-of-date data and less efficient cache utilisation, since old data cannot be flushed when its TTL expires, so the cache becomes
-strictly least-recently-used.
+mostly least-recently-used. To mitigate issues caused by massively outdated DNS replies, the maximum overaging of cached records can be specified in seconds
+(defaulting to not serve anything older than one day). Setting the TTL excess time to zero will serve stale cache data regardless how long it has expired.
.TP
.B \-0, --dns-forward-max=<queries>
Set the maximum number of concurrent DNS queries. The default value is
diff --git a/src/cache.c b/src/cache.c
index 119cf9f..b3c38c0 100644
--- a/src/cache.c
+++ b/src/cache.c
@@ -380,9 +380,17 @@ static int is_outdated_cname_pointer(struct crec *crecp)
static int is_expired(time_t now, struct crec *crecp)
{
- /* Don't dump expired entries if we're using them, cache becomes strictly LRU in that case.
- Never use expired DS or DNSKEY entries. */
- if (option_bool(OPT_STALE_CACHE) && !(crecp->flags & (F_DS | F_DNSKEY)))
+ /* Don't dump expired entries if they are within the accepted timeout range.
+ The cache becomes approx. LRU. Never use expired DS or DNSKEY entries.
+ Possible values for daemon->cache_max_expiry:
+ -1 == serve cached content regardless how long ago it expired
+ 0 == the option is disabled, expired content isn't served
+ <n> == serve cached content only if it expire less than <n> seconds
+ ago (where n is a positive integer) */
+ if (daemon->cache_max_expiry != 0 &&
+ (daemon->cache_max_expiry == -1 ||
+ difftime(now, crecp->ttd) < daemon->cache_max_expiry) &&
+ !(crecp->flags & (F_DS | F_DNSKEY)))
return 0;
if (crecp->flags & F_IMMORTAL)
@@ -1762,7 +1770,7 @@ void dump_cache(time_t now)
daemon->cachesize, daemon->metrics[METRIC_DNS_CACHE_LIVE_FREED], daemon->metrics[METRIC_DNS_CACHE_INSERTED]);
my_syslog(LOG_INFO, _("queries forwarded %u, queries answered locally %u"),
daemon->metrics[METRIC_DNS_QUERIES_FORWARDED], daemon->metrics[METRIC_DNS_LOCAL_ANSWERED]);
- if (option_bool(OPT_STALE_CACHE))
+ if (daemon->cache_max_expiry != 0)
my_syslog(LOG_INFO, _("queries answered from stale cache %u"), daemon->metrics[METRIC_DNS_STALE_ANSWERED]);
#ifdef HAVE_AUTH
my_syslog(LOG_INFO, _("queries for authoritative zones %u"), daemon->metrics[METRIC_DNS_AUTH_ANSWERED]);
diff --git a/src/config.h b/src/config.h
index df1d985..1e7b30f 100644
--- a/src/config.h
+++ b/src/config.h
@@ -60,6 +60,7 @@
#define LOOP_TEST_DOMAIN "test" /* domain for loop testing, "test" is reserved by RFC 2606 and won't therefore clash */
#define LOOP_TEST_TYPE T_TXT
#define DEFAULT_FAST_RETRY 1000 /* ms, default delay before fast retry */
+#define STALE_CACHE_EXPIRY 86400 /* 1 day in secs, default maximum expiry time for stale cache data */
/* compile-time options: uncomment below to enable or do eg.
make COPTS=-DHAVE_BROKEN_RTC
diff --git a/src/dnsmasq.h b/src/dnsmasq.h
index 90dc986..aaa6d62 100644
--- a/src/dnsmasq.h
+++ b/src/dnsmasq.h
@@ -280,9 +280,8 @@ struct event_desc {
#define OPT_FILTER_AAAA 68
#define OPT_STRIP_ECS 69
#define OPT_STRIP_MAC 70
-#define OPT_STALE_CACHE 71
-#define OPT_NORR 72
-#define OPT_LAST 73
+#define OPT_NORR 71
+#define OPT_LAST 72
#define OPTION_BITS (sizeof(unsigned int)*8)
#define OPTION_SIZE ( (OPT_LAST/OPTION_BITS)+((OPT_LAST%OPTION_BITS)!=0) )
@@ -1201,6 +1200,7 @@ extern struct daemon {
unsigned long soa_sn, soa_refresh, soa_retry, soa_expiry;
u32 metrics[__METRIC_MAX];
int fast_retry_time, fast_retry_timeout;
+ int cache_max_expiry;
#ifdef HAVE_DNSSEC
struct ds_config *ds;
char *timestamp_file;
diff --git a/src/option.c b/src/option.c
index 6a63268..8e61a6b 100644
--- a/src/option.c
+++ b/src/option.c
@@ -373,7 +373,7 @@ static const struct myoption opts[] =
{ "quiet-tftp", 0, 0, LOPT_QUIET_TFTP },
{ "port-limit", 1, 0, LOPT_RANDPORT_LIM },
{ "fast-dns-retry", 2, 0, LOPT_FAST_RETRY },
- { "use-stale-cache", 0, 0 , LOPT_STALE_CACHE },
+ { "use-stale-cache", 2, 0 , LOPT_STALE_CACHE },
{ NULL, 0, 0, 0 }
};
@@ -431,7 +431,7 @@ static struct {
{ 'M', ARG_DUP, "<bootp opts>", gettext_noop("Specify BOOTP options to DHCP server."), NULL },
{ 'n', OPT_NO_POLL, NULL, gettext_noop("Do NOT poll %s file, reload only on SIGHUP."), RESOLVFILE },
{ 'N', OPT_NO_NEG, NULL, gettext_noop("Do NOT cache failed search results."), NULL },
- { LOPT_STALE_CACHE, OPT_STALE_CACHE, NULL, gettext_noop("Use expired cache data for faster reply."), NULL },
+ { LOPT_STALE_CACHE, ARG_ONE, "[=<max_expired>]", gettext_noop("Use expired cache data for faster reply."), NULL },
{ 'o', OPT_ORDER, NULL, gettext_noop("Use nameservers strictly in the order given in %s."), RESOLVFILE },
{ 'O', ARG_DUP, "<optspec>", gettext_noop("Specify options to be sent to DHCP clients."), NULL },
{ LOPT_FORCE, ARG_DUP, "<optspec>", gettext_noop("DHCP option sent even if the client does not request it."), NULL},
@@ -5151,6 +5151,24 @@ err:
break;
}
+ case LOPT_STALE_CACHE:
+ {
+ int max_expiry = STALE_CACHE_EXPIRY;
+ if (arg)
+ {
+ /* Don't accept negative TTLs here, they'd have the counter-intuitive
+ side-effect of evicting cache records before they expire */
+ if (!atoi_check(arg, &max_expiry) || max_expiry < 0)
+ ret_err(gen_err);
+ /* Store "serve expired forever" as -1 internally, the option isn't
+ active for daemon->cache_max_expiry == 0 */
+ if (max_expiry == 0)
+ max_expiry = -1;
+ }
+ daemon->cache_max_expiry = max_expiry;
+ break;
+ }
+
#ifdef HAVE_DNSSEC
case LOPT_DNSSEC_STAMP: /* --dnssec-timestamp */
daemon->timestamp_file = opt_string_alloc(arg);