Commit df507515 authored by Mark Andrews's avatar Mark Andrews
Browse files

4700. [func] Serving of stale answers is now supported. This

                        allows named to provide stale cached answers when
                        the authoritative server is under attack.
                        See max-stale-ttl, stale-answer-enable,
                        stale-answer-ttl. [RT #44790]
parent e8a4edf0
4700. [func] Serving of stale answers is now supported. This
allows named to provide stale cached answers when
the authoritative server is under attack.
See max-stale-ttl, stale-answer-enable,
stale-answer-ttl. [RT #44790]
4699. [func] Multiple cookie-secret clauses can now be specified.
The first one specified is used to generate new
server cookies. [RT #45672]
......
......@@ -53,17 +53,18 @@ options {\n\
#endif
#ifndef WIN32
" coresize default;\n\
datasize default;\n\
files unlimited;\n\
stacksize default;\n"
datasize default;\n"
#endif
"# session-keyfile \"" NS_LOCALSTATEDIR "/run/named/session.key\";\n\
session-keyname local-ddns;\n\
session-keyalg hmac-sha256;\n\
"\
# deallocate-on-exit <obsolete>;\n\
# directory <none>\n\
dump-file \"named_dump.db\";\n\
# fake-iquery <obsolete>;\n\
edns-udp-size 4096;\n\
# fake-iquery <obsolete>;\n"
#ifndef WIN32
" files unlimited;\n"
#endif
"\
# has-old-clients <obsolete>;\n\
heartbeat-interval 60;\n\
# host-statistics <obsolete>;\n\
......@@ -71,33 +72,40 @@ options {\n\
# keep-response-order {none;};\n\
listen-on {any;};\n\
listen-on-v6 {any;};\n\
# lock-file \"" NS_LOCALSTATEDIR "/run/named/named.lock\";\n\
match-mapped-addresses no;\n\
max-rsa-exponent-size 0; /* no limit */\n\
max-udp-size 4096;\n\
memstatistics-file \"named.memstats\";\n\
# multiple-cnames <obsolete>;\n\
# named-xfer <obsolete>;\n\
nocookie-udp-size 4096;\n\
notify-rate 20;\n\
nta-lifetime 3600;\n\
nta-recheck 300;\n\
notify-rate 20;\n\
# pid-file \"" NS_LOCALSTATEDIR "/run/named/named.pid\"; /* or /lwresd.pid */\n\
# lock-file \"" NS_LOCALSTATEDIR "/run/named/named.lock\";\n\
port 53;\n\
prefetch 2 9;\n\
recursing-file \"named.recursing\";\n\
secroots-file \"named.secroots\";\n\
"
prefetch 2 9;\n"
#ifdef PATH_RANDOMDEV
"\
random-device \"" PATH_RANDOMDEV "\";\n\
"
" random-device \"" PATH_RANDOMDEV "\";\n"
#endif
"\
" recursing-file \"named.recursing\";\n\
recursive-clients 1000;\n\
request-nsid false;\n\
reserved-sockets 512;\n\
resolver-query-timeout 10;\n\
secroots-file \"named.secroots\";\n\
send-cookie true;\n\
# serial-queries <obsolete>;\n\
serial-query-rate 20;\n\
server-id none;\n\
startup-notify-rate 20;\n\
session-keyalg hmac-sha256;\n\
# session-keyfile \"" NS_LOCALSTATEDIR "/run/named/session.key\";\n\
session-keyname local-ddns;\n"
#ifndef WIN32
" stacksize default;\n"
#endif
" startup-notify-rate 20;\n\
statistics-file \"named.stats\";\n\
# statistics-interval <obsolete>;\n\
tcp-advertised-timeout 300;\n\
......@@ -107,22 +115,16 @@ options {\n\
tcp-keepalive-timeout 300;\n\
tcp-listen-queue 10;\n\
# tkey-dhkey <none>\n\
# tkey-gssapi-credential <none>\n\
# tkey-domain <none>\n\
# tkey-gssapi-credential <none>\n\
transfer-message-size 20480;\n\
transfers-per-ns 2;\n\
transfers-in 10;\n\
transfers-out 10;\n\
transfers-per-ns 2;\n\
# treat-cr-as-space <obsolete>;\n\
trust-anchor-telemetry yes;\n\
# use-id-pool <obsolete>;\n\
# use-ixfr <obsolete>;\n\
edns-udp-size 4096;\n\
max-udp-size 4096;\n\
nocookie-udp-size 4096;\n\
send-cookie true;\n\
request-nsid false;\n\
reserved-sockets 512;\n\
\n\
/* DLV */\n\
dnssec-lookaside . trust-anchor dlv.isc.org;\n\
......@@ -147,34 +149,35 @@ options {\n\
clients-per-query 10;\n\
dnssec-accept-expired no;\n\
dnssec-enable yes;\n\
dnssec-validation yes; \n\
dnssec-validation yes; \n"
#ifdef HAVE_DNSTAP
" dnstap-identity hostname;\n"
#endif
"\
# fetch-glue <obsolete>;\n\
fetch-quota-params 100 0.1 0.3 0.7;\n\
fetches-per-server 0;\n\
fetches-per-zone 0;\n\
"
fetches-per-zone 0;\n"
#ifdef ALLOW_FILTER_AAAA
" filter-aaaa-on-v4 no;\n\
filter-aaaa-on-v6 no;\n\
filter-aaaa { any; };\n\
"
filter-aaaa { any; };\n"
#endif
"\
glue-cache yes;\n\
lame-ttl 600;\n\
"
#ifdef HAVE_GEOIP
" geoip-use-ecs yes;\n"
#endif
" glue-cache yes;\n\
lame-ttl 600;\n"
#ifdef HAVE_LMDB
"\
lmdb-mapsize 32M;\n\
"
" lmdb-mapsize 32M;\n"
#endif
"\
max-cache-size 90%;\n\
" max-cache-size 90%;\n\
max-cache-ttl 604800; /* 1 week */\n\
max-clients-per-query 100;\n\
max-ncache-ttl 10800; /* 3 hours */\n\
max-recursion-depth 7;\n\
max-recursion-queries 75;\n\
max-stale-ttl 604800; /* 1 week */\n\
message-compression yes;\n\
# min-roots <obsolete>;\n\
minimal-any false;\n\
......@@ -189,74 +192,67 @@ options {\n\
request-expire true;\n\
request-ixfr true;\n\
require-server-cookie no;\n\
resolver-nonbackoff-tries 3;\n\
resolver-retry-interval 800; /* in milliseconds */\n\
# rfc2308-type1 <obsolete>;\n\
servfail-ttl 1;\n\
# sortlist <none>\n\
stale-answer-enable false;\n\
stale-answer-ttl 1; /* 1 second */\n\
synth-from-dnssec yes;\n\
# topology <none>\n\
transfer-format many-answers;\n\
v6-bias 50;\n\
zero-no-soa-ttl-cache no;\n\
"
#ifdef HAVE_DNSTAP
"\
dnstap-identity hostname;\n\
"
#endif
#ifdef HAVE_GEOIP
"\
geoip-use-ecs yes;\n\
"
#endif
" /* zone */\n\
\n\
/* zone */\n\
allow-query {any;};\n\
allow-query-on {any;};\n\
allow-transfer {any;};\n\
notify yes;\n\
# also-notify <none>\n\
notify-delay 5;\n\
notify-to-soa no;\n\
alt-transfer-source *;\n\
alt-transfer-source-v6 *;\n\
check-integrity yes;\n\
check-mx-cname warn;\n\
check-sibling yes;\n\
check-srv-cname warn;\n\
check-wildcard yes;\n\
dialup no;\n\
dnssec-dnskey-kskonly no;\n\
dnssec-loadkeys-interval 60;\n\
dnssec-secure-to-insecure no;\n\
dnssec-update-mode maintain;\n\
# forward <none>\n\
# forwarders <none>\n\
inline-signing no;\n\
ixfr-from-differences false;\n\
# maintain-ixfr-base <obsolete>;\n\
# max-ixfr-log-size <obsolete>\n\
transfer-source *;\n\
transfer-source-v6 *;\n\
alt-transfer-source *;\n\
alt-transfer-source-v6 *;\n\
max-transfer-time-in 120;\n\
max-transfer-time-out 120;\n\
max-transfer-idle-in 60;\n\
max-transfer-idle-out 60;\n\
max-journal-size default;\n\
max-records 0;\n\
max-retry-time 1209600; /* 2 weeks */\n\
min-retry-time 500;\n\
max-refresh-time 2419200; /* 4 weeks */\n\
max-retry-time 1209600; /* 2 weeks */\n\
max-transfer-idle-in 60;\n\
max-transfer-idle-out 60;\n\
max-transfer-time-in 120;\n\
max-transfer-time-out 120;\n\
min-refresh-time 300;\n\
min-retry-time 500;\n\
multi-master no;\n\
dnssec-secure-to-insecure no;\n\
sig-validity-interval 30; /* days */\n\
notify yes;\n\
notify-delay 5;\n\
notify-to-soa no;\n\
serial-update-method increment;\n\
sig-signing-nodes 100;\n\
sig-signing-signatures 10;\n\
sig-signing-type 65534;\n\
inline-signing no;\n\
zone-statistics terse;\n\
max-journal-size default;\n\
ixfr-from-differences false;\n\
check-wildcard yes;\n\
check-sibling yes;\n\
check-integrity yes;\n\
check-mx-cname warn;\n\
check-srv-cname warn;\n\
zero-no-soa-ttl yes;\n\
update-check-ksk yes;\n\
serial-update-method increment;\n\
dnssec-update-mode maintain;\n\
dnssec-dnskey-kskonly no;\n\
dnssec-loadkeys-interval 60;\n\
sig-validity-interval 30; /* days */\n\
transfer-source *;\n\
transfer-source-v6 *;\n\
try-tcp-refresh yes; /* BIND 8 compat */\n\
update-check-ksk yes;\n\
zero-no-soa-ttl yes;\n\
zone-statistics terse;\n\
};\n\
"
......
......@@ -277,6 +277,8 @@ ns_control_docommand(isccc_sexpr_t *message, isc_boolean_t readonly,
result = ns_server_dnstap(ns_g_server, lex, text);
} else if (command_compare(command, NS_COMMAND_TCPTIMEOUTS)) {
result = ns_server_tcptimeouts(lex, text);
} else if (command_compare(command, NS_COMMAND_SERVESTALE)) {
result = ns_server_servestale(ns_g_server, lex, text);
} else {
isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
NS_LOGMODULE_CONTROL, ISC_LOG_WARNING,
......
......@@ -66,6 +66,7 @@
#define NS_COMMAND_DNSTAPREOPEN "dnstap-reopen"
#define NS_COMMAND_DNSTAP "dnstap"
#define NS_COMMAND_TCPTIMEOUTS "tcp-timeouts"
#define NS_COMMAND_SERVESTALE "serve-stale"
isc_result_t
ns_controls_create(ns_server_t *server, ns_controls_t **ctrlsp);
......
......@@ -215,7 +215,10 @@ enum {
dns_nsstatscounter_nodatasynth = 59,
dns_nsstatscounter_wildcardsynth = 60,
dns_nsstatscounter_max = 61
dns_nsstatscounter_trystale = 61,
dns_nsstatscounter_usedstale = 62,
dns_nsstatscounter_max = 63
};
/*%
......@@ -760,4 +763,12 @@ ns_server_dnstap(ns_server_t *server, isc_lex_t *lex, isc_buffer_t **text);
isc_result_t
ns_server_tcptimeouts(isc_lex_t *lex, isc_buffer_t **text);
/*%
* Control whether stale answers are served or not when configured in
* named.conf.
*/
isc_result_t
ns_server_servestale(ns_server_t *server, isc_lex_t *lex,
isc_buffer_t **text);
#endif /* NAMED_SERVER_H */
......@@ -146,10 +146,14 @@ do { \
#define REDIRECT(c) (((c)->query.attributes & \
NS_QUERYATTR_REDIRECT) != 0)
/*% No QNAME Proof? */
/*% Does the rdataset 'r' have an attached 'No QNAME Proof'? */
#define NOQNAME(r) (((r)->attributes & \
DNS_RDATASETATTR_NOQNAME) != 0)
/*% Does the rdataset 'r' contains a stale answer? */
#define STALE(r) (((r)->attributes & \
DNS_RDATASETATTR_STALE) != 0)
#ifdef WANT_QUERYTRACE
static inline void
client_trace(ns_client_t *client, int level, const char *message) {
......@@ -326,6 +330,7 @@ typedef struct query_ctx {
isc_boolean_t need_wildcardproof; /* wilcard proof needed */
isc_boolean_t nxrewrite; /* negative answer from RPZ */
isc_boolean_t findcoveringnsec; /* lookup covering NSEC */
isc_boolean_t want_stale; /* want stale records? */
dns_fixedname_t wildcardname; /* name needing wcard proof */
dns_fixedname_t dsname; /* name needing DS */
......@@ -4634,6 +4639,7 @@ qctx_init(ns_client_t *client, dns_fetchevent_t *event,
qctx->findcoveringnsec = client->view->synthfromdnssec;
qctx->is_staticstub_zone = ISC_FALSE;
qctx->nxrewrite = ISC_FALSE;
qctx->want_stale = ISC_FALSE;
qctx->authoritative = ISC_FALSE;
}
......@@ -5009,6 +5015,35 @@ query_lookup(query_ctx_t *qctx) {
dns_cache_updatestats(qctx->client->view->cache, result);
}
if (qctx->want_stale) {
char namebuf[DNS_NAME_FORMATSIZE];
isc_boolean_t success;
qctx->client->query.dboptions &= ~DNS_DBFIND_STALEOK;
qctx->want_stale = ISC_FALSE;
if (dns_rdataset_isassociated(qctx->rdataset) &&
dns_rdataset_count(qctx->rdataset) > 0 &&
STALE(qctx->rdataset)) {
qctx->rdataset->ttl =
qctx->client->view->staleanswerttl;
success = ISC_TRUE;
} else {
success = ISC_FALSE;
}
dns_name_format(qctx->client->query.qname,
namebuf, sizeof(namebuf));
isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
NS_LOGMODULE_QUERY, ISC_LOG_INFO,
"%s resolver failure, stale answer %s",
namebuf, success ? "used" : "unavailable");
if (!success) {
QUERY_ERROR(qctx, DNS_R_SERVFAIL);
return (query_done(qctx));
}
}
return (query_gotanswer(qctx, result));
}
......@@ -5126,6 +5161,8 @@ query_recurse(ns_client_t *client, dns_rdatatype_t qtype, dns_name_t *qname,
dns_rdataset_t *rdataset, *sigrdataset;
isc_sockaddr_t *peeraddr = NULL;
CTRACE(ISC_LOG_DEBUG(3), "query_recurse");
if (!resuming)
inc_stats(client, dns_nsstatscounter_recursion);
......@@ -5995,7 +6032,11 @@ query_gotanswer(query_ctx_t *qctx, isc_result_t result) {
"query_gotanswer: unexpected error: %s",
isc_result_totext(result));
CCTRACE(ISC_LOG_ERROR, errmsg);
QUERY_ERROR(qctx, DNS_R_SERVFAIL);
if (qctx->resuming) {
qctx->want_stale = ISC_TRUE;
} else {
QUERY_ERROR(qctx, DNS_R_SERVFAIL);
}
return (query_done(qctx));
}
}
......@@ -6469,7 +6510,7 @@ query_respond(query_ctx_t *qctx) {
/*
* If we have a zero ttl from the cache, refetch.
*/
if (!qctx->is_zone && !qctx->resuming &&
if (!qctx->is_zone && !qctx->resuming && !STALE(qctx->rdataset) &&
qctx->rdataset->ttl == 0 && RECURSIONOK(qctx->client))
{
qctx_clean(qctx);
......@@ -8274,7 +8315,7 @@ query_cname(query_ctx_t *qctx) {
/*
* If we have a zero ttl from the cache refetch it.
*/
if (!qctx->is_zone && !qctx->resuming &&
if (!qctx->is_zone && !qctx->resuming && !STALE(qctx->rdataset) &&
qctx->rdataset->ttl == 0 && RECURSIONOK(qctx->client))
{
qctx_clean(qctx);
......@@ -9561,6 +9602,49 @@ query_done(query_ctx_t *qctx) {
return (query_start(qctx));
}
if (qctx->want_stale) {
dns_ttl_t stale_ttl = 0;
isc_result_t result;
isc_boolean_t staleanswersok = ISC_FALSE;
/*
* Stale answers only make sense if stale_ttl > 0 but
* we want rndc to be able to control returning stale
* answers if they are configured.
*/
dns_db_attach(qctx->client->view->cachedb, &qctx->db);
result = dns_db_getservestalettl(qctx->db, &stale_ttl);
if (result == ISC_R_SUCCESS && stale_ttl > 0) {
switch (qctx->client->view->staleanswersok) {
case dns_stale_answer_yes:
staleanswersok = ISC_TRUE;
break;
case dns_stale_answer_conf:
staleanswersok =
qctx->client->view->staleanswersenable;
break;
case dns_stale_answer_no:
staleanswersok = ISC_FALSE;
break;
}
} else {
staleanswersok = ISC_FALSE;
}
if (staleanswersok) {
qctx->client->query.dboptions |= DNS_DBFIND_STALEOK;
inc_stats(qctx->client, dns_nsstatscounter_trystale);
if (qctx->client->query.fetch != NULL)
dns_resolver_destroyfetch(
&qctx->client->query.fetch);
return (query_lookup(qctx));
}
dns_db_detach(&qctx->db);
qctx->want_stale = ISC_FALSE;
QUERY_ERROR(qctx, DNS_R_SERVFAIL);
return (query_done(qctx));
}
if (qctx->result != ISC_R_SUCCESS &&
(!PARTIALANSWER(qctx->client) || WANTRECURSION(qctx->client) ||
qctx->result == DNS_R_DROP))
......
......@@ -1737,7 +1737,8 @@ static isc_boolean_t
cache_sharable(dns_view_t *originview, dns_view_t *view,
isc_boolean_t new_zero_no_soattl,
unsigned int new_cleaning_interval,
isc_uint64_t new_max_cache_size)
isc_uint64_t new_max_cache_size,
isc_uint32_t new_stale_ttl)
{
/*
* If the cache cannot even reused for the same view, it cannot be
......@@ -1752,6 +1753,7 @@ cache_sharable(dns_view_t *originview, dns_view_t *view,
*/
if (dns_cache_getcleaninginterval(originview->cache) !=
new_cleaning_interval ||
dns_cache_getservestalettl(originview->cache) != new_stale_ttl ||
dns_cache_getcachesize(originview->cache) != new_max_cache_size) {
return (ISC_FALSE);
}
......@@ -3331,6 +3333,7 @@ configure_view(dns_view_t *view, dns_viewlist_t *viewlist,
isc_uint32_t max_cache_size_percent = 0;
size_t max_adb_size;
isc_uint32_t lame_ttl, fail_ttl;
isc_uint32_t max_stale_ttl;
dns_tsig_keyring_t *ring = NULL;
dns_view_t *pview = NULL; /* Production view */
isc_mem_t *cmctx = NULL, *hmctx = NULL;
......@@ -3360,6 +3363,7 @@ configure_view(dns_view_t *view, dns_viewlist_t *viewlist,
isc_boolean_t old_rpz_ok = ISC_FALSE;
isc_dscp_t dscp4 = -1, dscp6 = -1;
dns_dyndbctx_t *dctx = NULL;
unsigned int resolver_param;
REQUIRE(DNS_VIEW_VALID(view));
......@@ -3738,6 +3742,23 @@ configure_view(dns_view_t *view, dns_viewlist_t *viewlist,
INSIST(result == ISC_R_SUCCESS);
view->synthfromdnssec = cfg_obj_asboolean(obj);
result = ns_config_get(maps, "max-stale-ttl", &obj);
INSIST(result == ISC_R_SUCCESS);
max_stale_ttl = cfg_obj_asuint32(obj);
obj = NULL;
result = ns_config_get(maps, "stale-answer-enable", &obj);
INSIST(result == ISC_R_SUCCESS);
view->staleanswersenable = cfg_obj_asboolean(obj);
result = dns_viewlist_find(&ns_g_server->viewlist, view->name,
view->rdclass, &pview);
if (result == ISC_R_SUCCESS) {
view->staleanswersok = pview->staleanswersok;
dns_view_detach(&pview);
} else
view->staleanswersok = dns_stale_answer_conf;
/*
* Configure the view's cache.
*
......@@ -3771,7 +3792,8 @@ configure_view(dns_view_t *view, dns_viewlist_t *viewlist,
nsc = cachelist_find(cachelist, cachename, view->rdclass);
if (nsc != NULL) {
if (!cache_sharable(nsc->primaryview, view, zero_no_soattl,
cleaning_interval, max_cache_size)) {
cleaning_interval, max_cache_size,
max_stale_ttl)) {
isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
NS_LOGMODULE_SERVER, ISC_LOG_ERROR,
"views %s and %s can't share the cache "
......@@ -3870,9 +3892,15 @@ configure_view(dns_view_t *view, dns_viewlist_t *viewlist,
dns_cache_setcleaninginterval(cache, cleaning_interval);
dns_cache_setcachesize(cache, max_cache_size);
dns_cache_setservestalettl(cache, max_stale_ttl);
dns_cache_detach(&cache);
obj = NULL;
result = ns_config_get(maps, "stale-answer-ttl", &obj);
INSIST(result == ISC_R_SUCCESS);
view->staleanswerttl = ISC_MAX(cfg_obj_asuint32(obj), 1);
/*
* Resolver.
*
......@@ -4059,6 +4087,21 @@ configure_view(dns_view_t *view, dns_viewlist_t *viewlist,
maxbits = 4096;
view->maxbits = maxbits;
/*
* Set resolver retry parameters.
*/
obj = NULL;
CHECK(ns_config_get(maps, "resolver-retry-interval", &obj));
resolver_param = cfg_obj_asuint32(obj);
if (resolver_param > 0)
dns_resolver_setretryinterval(view->resolver, resolver_param);
obj = NULL;
CHECK(ns_config_get(maps, "resolver-nonbackoff-tries", &obj));
resolver_param = cfg_obj_asuint32(obj);
if (resolver_param > 0)
dns_resolver_setnonbackofftries(view->resolver, resolver_param);
/*
* Set supported DNSSEC algorithms.
*/
......@@ -14090,3 +14133,132 @@ ns_server_tcptimeouts(isc_lex_t *lex, isc_buffer_t **text) {
return (result);
}
isc_result_t
ns_server_servestale(ns_server_t *server, isc_lex_t *lex,
isc_buffer_t **text)
{
char *ptr, *classtxt, *viewtxt = NULL;
char msg[128];
dns_rdataclass_t rdclass = dns_rdataclass_in;
dns_view_t *view;
isc_boolean_t found = ISC_FALSE;
dns_stale_answer_t staleanswersok;
isc_boolean_t wantstatus = ISC_FALSE;
isc_result_t result = ISC_R_SUCCESS;
/* Skip the command name. */
ptr = next_token(lex, text);
if (ptr == NULL)
return (ISC_R_UNEXPECTEDEND);
ptr = next_token(lex, NULL);
if (ptr == NULL)
return (ISC_R_UNEXPECTEDEND);
if (strcasecmp(ptr, "on") == 0 || strcasecmp(ptr, "yes") == 0) {
staleanswersok = dns_stale_answer_yes;
} else if (strcasecmp(ptr, "off") == 0 || strcasecmp(ptr, "no") == 0) {
staleanswersok = dns_stale_answer_no;
} else if (strcasecmp(ptr, "reset") == 0) {
staleanswersok = dns_stale_answer_conf;
} else if (strcasecmp(ptr, "status") == 0) {
wantstatus = ISC_TRUE;
} else
return (DNS_R_SYNTAX);
/* Look for the optional class name. */
classtxt = next_token(lex, text);
if (classtxt != NULL) {
/* Look for the optional view name. */
viewtxt = next_token(lex, text);
}
if (classtxt != NULL) {
isc_textregion_t r;
r.base = classtxt;
r.length = strlen(classtxt);
result = dns_rdataclass_fromtext(&rdclass, &r);
if (result != ISC_R_SUCCESS) {
if (viewtxt == NULL) {
viewtxt = classtxt;
classtxt = NULL;