+3703. [func] Prefetch about to expire records if they are queried
+ for, see prefetch option for details. [RT #35041]
+
3702. [func] 'dnssec-coverage -l' option specifies a length
of time to check for coverage; events further into
the future are ignored. 'dnssec-coverage -z'
# pid-file \"" NS_LOCALSTATEDIR "/run/named/named.pid\"; /* or /lwresd.pid */\n\
bindkeys-file \"" NS_SYSCONFDIR "/bind.keys\";\n\
port 53;\n\
+ prefetch 2 9;\n\
recursing-file \"named.recursing\";\n\
secroots-file \"named.secroots\";\n\
"
isc_boolean_t isreferral;
isc_mutex_t fetchlock;
dns_fetch_t * fetch;
+ dns_fetch_t * prefetch;
dns_rpz_st_t * rpz_st;
isc_bufferlist_t namebufs;
ISC_LIST(ns_dbversion_t) activeversions;
if (result != ISC_R_SUCCESS)
return (result);
client->query.fetch = NULL;
+ client->query.prefetch = NULL;
client->query.authdb = NULL;
client->query.authzone = NULL;
client->query.authdbset = ISC_FALSE;
dns_resolver_destroyfetch(&fetch);
}
+static void
+prefetch_done(isc_task_t *task, isc_event_t *event) {
+ dns_fetchevent_t *devent = (dns_fetchevent_t *)event;
+ ns_client_t *client;
+
+ UNUSED(task);
+
+ REQUIRE(event->ev_type == DNS_EVENT_FETCHDONE);
+ client = devent->ev_arg;
+ REQUIRE(NS_CLIENT_VALID(client));
+ REQUIRE(task == client->task);
+
+ LOCK(&client->query.fetchlock);
+ if (client->query.prefetch != NULL) {
+ INSIST(devent->fetch == client->query.prefetch);
+ client->query.prefetch = NULL;
+ }
+ UNLOCK(&client->query.fetchlock);
+ if (devent->fetch != NULL)
+ dns_resolver_destroyfetch(&devent->fetch);
+ if (devent->node != NULL)
+ dns_db_detachnode(devent->db, &devent->node);
+ if (devent->db != NULL)
+ dns_db_detach(&devent->db);
+ query_putrdataset(client, &devent->rdataset);
+ isc_event_free(&event);
+ ns_client_detach(&client);
+}
+
+static void
+query_prefetch(ns_client_t *client, dns_name_t *qname,
+ dns_rdataset_t *rdataset)
+{
+ isc_result_t result;
+ isc_sockaddr_t *peeraddr;
+ dns_rdataset_t *tmprdataset;
+ ns_client_t *dummy = NULL;
+
+ if (client->view->prefetch_trigger == 0U ||
+ rdataset->ttl > client->view->prefetch_trigger ||
+ (rdataset->attributes & DNS_RDATASETATTR_PREFETCH) == 0)
+ return;
+
+ if (client->recursionquota == NULL) {
+ result = isc_quota_attach(&ns_g_server->recursionquota,
+ &client->recursionquota);
+ if (result != ISC_R_SUCCESS)
+ return;
+ }
+ if (client->query.prefetch != NULL)
+ return;
+
+ tmprdataset = query_newrdataset(client);
+ if (tmprdataset == NULL)
+ return;
+ if ((client->attributes & NS_CLIENTATTR_TCP) == 0)
+ peeraddr = &client->peeraddr;
+ else
+ peeraddr = NULL;
+ ns_client_attach(client, &dummy);
+ unsigned options = client->query.fetchoptions | DNS_FETCHOPT_PREFETCH;
+ result = dns_resolver_createfetch2(client->view->resolver,
+ qname, rdataset->type, NULL, NULL,
+ NULL, peeraddr, client->message->id,
+ options, client->task,
+ prefetch_done, client, tmprdataset,
+ NULL, &client->query.prefetch);
+ if (result != ISC_R_SUCCESS) {
+ query_putrdataset(client, &tmprdataset);
+ ns_client_detach(&dummy);
+ }
+}
+
static isc_result_t
query_recurse(ns_client_t *client, dns_rdatatype_t qtype, dns_name_t *qname,
dns_name_t *qdomain, dns_rdataset_t *nameservers,
noqname = rdataset;
else
noqname = NULL;
+ if (!is_zone && RECURSIONOK(client))
+ query_prefetch(client, fname, rdataset);
query_addrrset(client, &fname, &rdataset, sigrdatasetp, dbuf,
DNS_SECTION_ANSWER);
if (noqname != NULL)
NULL);
need_wildcardproof = ISC_TRUE;
}
+ if (!is_zone && RECURSIONOK(client))
+ query_prefetch(client, fname, rdataset);
query_addrrset(client, &fname, &rdataset, sigrdatasetp, dbuf,
DNS_SECTION_ANSWER);
/*
if (rpz_st != NULL)
rdataset->ttl = ISC_MIN(rdataset->ttl,
rpz_st->m.ttl);
+ if (!is_zone && RECURSIONOK(client))
+ query_prefetch(client, fname, rdataset);
query_addrrset(client,
fname != NULL ? &fname : &tname,
&rdataset, NULL,
query_filter64(client, &fname, rdataset, dbuf,
DNS_SECTION_ANSWER);
query_putrdataset(client, &rdataset);
- } else
+ } else {
+ if (!is_zone && RECURSIONOK(client))
+ query_prefetch(client, fname, rdataset);
query_addrrset(client, &fname, &rdataset,
sigrdatasetp, dbuf, DNS_SECTION_ANSWER);
+ }
if (noqname != NULL)
query_addnoqnameproof(client, noqname);
CHECK(configure_view_acl(vconfig, config, "filter-aaaa", NULL,
actx, ns_g_mctx, &view->aaaa_acl));
#endif
+ obj = NULL;
+ result = ns_config_get(maps, "prefetch", &obj);
+ if (result == ISC_R_SUCCESS) {
+ const cfg_obj_t *trigger, *eligible;
+
+ trigger = cfg_tuple_get(obj, "trigger");
+ view->prefetch_trigger = cfg_obj_asuint32(trigger);
+ if (view->prefetch_trigger > 10)
+ view->prefetch_trigger = 10;
+ eligible = cfg_tuple_get(obj, "eligible");
+ if (cfg_obj_isvoid(eligible)) {
+ int i;
+ for (i = 1; maps[i] != NULL; i++) {
+ obj = NULL;
+ result = ns_config_get(&maps[i],
+ "prefetch", &obj);
+ INSIST(result == ISC_R_SUCCESS);
+ eligible = cfg_tuple_get(obj, "eligible");
+ if (cfg_obj_isuint32(eligible))
+ break;
+ }
+ INSIST(cfg_obj_isuint32(eligible));
+ }
+ view->prefetch_eligible = cfg_obj_asuint32(eligible);
+ if (view->prefetch_eligible < view->prefetch_trigger + 6)
+ view->prefetch_eligible = view->prefetch_trigger + 6;
+ }
obj = NULL;
result = ns_config_get(maps, "dnssec-enable", &obj);
# Clean up after resolver tests.
#
rm -f */named.memstats
-rm -f dig.out dig.*.out.*
+rm -f */*.jdb
+rm -f dig.out dig.out.*
+rm -f dig.*.out.*
rm -f dig.*.foo.*
rm -f dig.*.bar.*
rm -f dig.*.prime.*
$TTL 5
to-be-removed NS ns.to-be-removed
ns.to-be-removed A 10.53.0.6
+fetch 10 TXT A short ttl
)
@ NS ns.tld.
ns A 10.53.0.4
+fetch 10 TXT A short ttl
/* $Id: named.conf,v 1.4 2011/03/13 23:47:36 tbox Exp $ */
-// NS4
+// NS5
controls { /* empty */ };
@ IN MX 0 mail
ns IN A 10.53.0.6
mail IN A 10.53.0.6
+fetch 10 IN TXT A short ttl
/* $Id: named.conf,v 1.4 2012/02/09 23:47:18 tbox Exp $ */
-// NS4
+// NS6
controls { /* empty */ };
* PERFORMANCE OF THIS SOFTWARE.
*/
-// NS4
+// NS7
controls { /* empty */ };
recursion yes;
empty-zones-enable yes;
disable-empty-zone 20.172.in-addr.arpa;
+ prefetch 0;
};
key rndc_key {
* PERFORMANCE OF THIS SOFTWARE.
*/
-// NS4
+// NS7
controls { /* empty */ };
recursion yes;
empty-zones-enable yes;
disable-empty-zone 20.172.in-addr.arpa;
+ prefetch 0;
};
key rndc_key {
if [ $ret != 0 ]; then echo "I:failed"; fi
status=`expr $status + $ret`
-#HERE <<<
cp ns7/named2.conf ns7/named.conf
$RNDC -c ../common/rndc.conf -s 10.53.0.7 -p 9953 reconfig 2>&1 | sed 's/^/I:ns7 /'
grep "ANSWER: 1" dig.ns7.out.${n} > /dev/null || ret=1
if [ $ret != 0 ]; then echo "I:failed"; ret=1; fi
status=`expr $status + $ret`
-#HERE >>>
+
+n=`expr $n + 1`
+echo "I:check prefetch (${n})"
+ret=0
+$DIG @10.53.0.5 -p 5300 fetch.tld txt > dig.out.1.${n} || ret=1
+ttl1=`awk '/"A" "short" "ttl"/ { print $2 - 2 }' dig.out.1.${n}`
+# sleep so we are in prefetch range
+sleep ${ttl1:-0}
+# trigger prefetch
+$DIG @10.53.0.5 -p 5300 fetch.tld txt > dig.out.2.${n} || ret=1
+ttl2=`awk '/"A" "short" "ttl"/ { print $2 }' dig.out.2.${n}`
+sleep 1
+# check that prefetch occured
+$DIG @10.53.0.5 -p 5300 fetch.tld txt > dig.out.3.${n} || ret=1
+ttl=`awk '/"A" "short" "ttl"/ { print $2 }' dig.out.3.${n}`
+test ${ttl:-0} -gt ${ttl2:-1} || ret=1
+if [ $ret != 0 ]; then echo "I:failed"; fi
+status=`expr $status + $ret`
+
+n=`expr $n + 1`
+echo "I:check prefetch disabled (${n})"
+ret=0
+$DIG @10.53.0.7 -p 5300 fetch.example.net txt > dig.out.1.${n} || ret=1
+ttl1=`awk '/"A" "short" "ttl"/ { print $2 - 1 }' dig.out.1.${n}`
+# sleep so we are in expire range
+sleep ${ttl1:-0}
+# look for zero ttl, allow for one miss at getting zero ttl
+for i in 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9
+do
+ $DIG @10.53.0.7 -p 5300 fetch.example.net txt > dig.out.2.${n} || ret=1
+ ttl2=`awk '/"A" "short" "ttl"/ { print $2 }' dig.out.2.${n}`
+ test ${ttl2:-1} -eq 0 && break
+ sleep 1
+done
+test ${ttl2:-1} -eq 0 || ret=1
+# delay so that any prefetched record will have a lower ttl than expected
+sleep 3
+# check that prefetch has not occured
+$DIG @10.53.0.7 -p 5300 fetch.example.net txt > dig.out.3.${n} || ret=1
+ttl=`awk '/"A" "short" "ttl"/ { print $2 - 1 }' dig.out.3.${n}`
+test ${ttl:-0} -eq ${ttl1:-1} || ret=1
+if [ $ret != 0 ]; then echo "I:failed"; fi
+status=`expr $status + $ret`
echo "I:exit status: $status"
exit $status
</para>
</listitem>
</varlistentry>
+
+ <varlistentry>
+ <term><command>prefetch</command></term>
+ <listitem>
+ <para>
+ Specifies the trigger ttl value (range [1..10])
+ at which prefetch of the current query will be
+ made and optionally the minimum ttl value that will be
+ accepted for the records to be candidates for
+ prefetching. The defaults are 2 and 9 respectively.
+ Setting a trigger ttl value of 0 will disable
+ prefetching. A minimum delta of 6 is enforced.
+ </para>
+ </listitem>
+ </varlistentry>
</variablelist>
</sect3>
fake-iquery <boolean>; // obsolete
fetch-glue <boolean>; // obsolete
files <size>;
- filter-aaaa { <address_match_element>; ... }; // not configured
- filter-aaaa-on-v4 <filter_aaaa>; // not configured
- filter-aaaa-on-v6 <filter_aaaa>; // not configured
+ filter-aaaa { <address_match_element>; ... };
+ filter-aaaa-on-v4 <filter_aaaa>;
+ filter-aaaa-on-v6 <filter_aaaa>;
flush-zones-on-shutdown <boolean>;
forward ( first | only );
forwarders [ port <integer> ] [ dscp <integer> ] { ( <ipv4_address>
pid-file ( <quoted_string> | none );
port <integer>;
preferred-glue <string>;
+ prefetch <integer> [ <integer> ];
provide-ixfr <boolean>;
query-source <querysource4>;
query-source-v6 <querysource6>;
empty-server <string>;
empty-zones-enable <boolean>;
fetch-glue <boolean>; // obsolete
- filter-aaaa { <address_match_element>; ... }; // not configured
- filter-aaaa-on-v4 <filter_aaaa>; // not configured
- filter-aaaa-on-v6 <filter_aaaa>; // not configured
+ filter-aaaa { <address_match_element>; ... };
+ filter-aaaa-on-v4 <filter_aaaa>;
+ filter-aaaa-on-v6 <filter_aaaa>;
forward ( first | only );
forwarders [ port <integer> ] [ dscp <integer> ] { ( <ipv4_address>
| <ipv6_address> ) [ port <integer> ] [ dscp <integer> ]; ... };
notify-to-soa <boolean>;
nsec3-test-zone <boolean>; // test only
preferred-glue <string>;
+ prefetch <integer> [ <integer> ];
provide-ixfr <boolean>;
query-source <querysource4>;
query-source-v6 <querysource6>;
#define DNS_DBADD_FORCE 0x02
#define DNS_DBADD_EXACT 0x04
#define DNS_DBADD_EXACTTTL 0x08
+#define DNS_DBADD_PREFETCH 0x10
/*@}*/
/*%
#define DNS_RDATASETATTR_CLOSEST 0x00080000
#define DNS_RDATASETATTR_OPTOUT 0x00100000 /*%< OPTOUT proof */
#define DNS_RDATASETATTR_NEGATIVE 0x00200000
+#define DNS_RDATASETATTR_PREFETCH 0x00400000
/*%
* _OMITDNSSEC:
/*
* Options that modify how a 'fetch' is done.
*/
-#define DNS_FETCHOPT_TCP 0x01 /*%< Use TCP. */
-#define DNS_FETCHOPT_UNSHARED 0x02 /*%< See below. */
-#define DNS_FETCHOPT_RECURSIVE 0x04 /*%< Set RD? */
-#define DNS_FETCHOPT_NOEDNS0 0x08 /*%< Do not use EDNS. */
-#define DNS_FETCHOPT_FORWARDONLY 0x10 /*%< Only use forwarders. */
-#define DNS_FETCHOPT_NOVALIDATE 0x20 /*%< Disable validation. */
-#define DNS_FETCHOPT_EDNS512 0x40 /*%< Advertise a 512 byte
+#define DNS_FETCHOPT_TCP 0x001 /*%< Use TCP. */
+#define DNS_FETCHOPT_UNSHARED 0x002 /*%< See below. */
+#define DNS_FETCHOPT_RECURSIVE 0x004 /*%< Set RD? */
+#define DNS_FETCHOPT_NOEDNS0 0x008 /*%< Do not use EDNS. */
+#define DNS_FETCHOPT_FORWARDONLY 0x010 /*%< Only use forwarders. */
+#define DNS_FETCHOPT_NOVALIDATE 0x020 /*%< Disable validation. */
+#define DNS_FETCHOPT_EDNS512 0x040 /*%< Advertise a 512 byte
UDP buffer. */
-#define DNS_FETCHOPT_WANTNSID 0x80 /*%< Request NSID */
+#define DNS_FETCHOPT_WANTNSID 0x080 /*%< Request NSID */
+#define DNS_FETCHOPT_PREFETCH 0x100 /*%< Request NSID */
#define DNS_FETCHOPT_EDNSVERSIONSET 0x00800000
#define DNS_FETCHOPT_EDNSVERSIONMASK 0xff000000
isc_boolean_t requestnsid;
dns_ttl_t maxcachettl;
dns_ttl_t maxncachettl;
+ dns_ttl_t prefetch_trigger;
+ dns_ttl_t prefetch_eligible;
in_port_t dstport;
dns_aclenv_t aclenv;
dns_rdatatype_t preferred_glue;
#define RDATASET_ATTR_NXDOMAIN 0x0010
#define RDATASET_ATTR_RESIGN 0x0020
#define RDATASET_ATTR_STATCOUNT 0x0040
-#define RDATASET_ATTR_OPTOUT 0x0080
+#define RDATASET_ATTR_OPTOUT 0x0080
#define RDATASET_ATTR_NEGATIVE 0x0100
+#define RDATASET_ATTR_PREFETCH 0x0200
typedef struct acache_cbarg {
dns_rdatasetadditional_t type;
(((header)->attributes & RDATASET_ATTR_OPTOUT) != 0)
#define NEGATIVE(header) \
(((header)->attributes & RDATASET_ATTR_NEGATIVE) != 0)
+#define PREFETCH(header) \
+ (((header)->attributes & RDATASET_ATTR_PREFETCH) != 0)
#define DEFAULT_NODE_LOCK_COUNT 7 /*%< Should be prime. */
rdataset->attributes |= DNS_RDATASETATTR_NXDOMAIN;
if (OPTOUT(header))
rdataset->attributes |= DNS_RDATASETATTR_OPTOUT;
+ if (PREFETCH(header))
+ rdataset->attributes |= DNS_RDATASETATTR_PREFETCH;
rdataset->private1 = rbtdb;
rdataset->private2 = node;
raw = (unsigned char *)header + sizeof(*header);
}
}
if (IS_CACHE(rbtdb) && header->rdh_ttl >= now &&
+ (options & DNS_DBADD_PREFETCH) == 0 &&
(header->type == dns_rdatatype_a ||
header->type == dns_rdatatype_aaaa ||
header->type == dns_rdatatype_ds ||
} else {
newheader->serial = 1;
newheader->resign = 0;
+ if ((rdataset->attributes & DNS_RDATASETATTR_PREFETCH) != 0)
+ newheader->attributes |= RDATASET_ATTR_PREFETCH;
if ((rdataset->attributes & DNS_RDATASETATTR_NEGATIVE) != 0)
newheader->attributes |= RDATASET_ATTR_NEGATIVE;
if ((rdataset->attributes & DNS_RDATASETATTR_NXDOMAIN) != 0)
if (result != ISC_R_SUCCESS)
goto noanswer_response;
+ unsigned options = 0;
+ if ((fctx->options & DNS_FETCHOPT_PREFETCH) != 0)
+ options = DNS_DBADD_PREFETCH;
result = dns_db_addrdataset(fctx->cache, node, NULL, now,
- vevent->rdataset, 0, ardataset);
+ vevent->rdataset, options, ardataset);
if (result != ISC_R_SUCCESS &&
result != DNS_R_UNCHANGED)
goto noanswer_response;
if (rdataset->ttl > res->view->maxcachettl)
rdataset->ttl = res->view->maxcachettl;
+ /*
+ * Mark the rdataset as being prefetch eligible.
+ */
+ if (rdataset->ttl > fctx->res->view->prefetch_eligible)
+ rdataset->attributes |= DNS_RDATASETATTR_PREFETCH;
+
/*
* Find the SIG for this rdataset, if we have it.
*/
sigrdataset->ttl = rdataset->ttl;
}
+ /*
+ * Mark the rdataset as being prefetch eligible.
+ */
+ if (rdataset->ttl > fctx->res->view->prefetch_eligible)
+ rdataset->attributes |= DNS_RDATASETATTR_PREFETCH;
+
+
/*
* Cache this rdataset/sigrdataset pair as
* pending data. Track whether it was additional
if (sigrdataset != NULL)
sigrdataset->trust = trust;
if (!need_validation || !ANSWER(rdataset)) {
+ options = 0;
if (ANSWER(rdataset) &&
rdataset->type != dns_rdatatype_rrsig) {
isc_result_t tresult;
ISC_R_SUCCESS);
}
}
+ if ((fctx->options & DNS_FETCHOPT_PREFETCH) != 0)
+ options = DNS_DBADD_PREFETCH;
addedrdataset = ardataset;
result = dns_db_addrdataset(fctx->cache, node,
NULL, now, rdataset,
- 0, addedrdataset);
+ options,
+ addedrdataset);
if (result == DNS_R_UNCHANGED) {
result = ISC_R_SUCCESS;
if (!need_validation &&
addedrdataset = asigrdataset;
result = dns_db_addrdataset(fctx->cache,
node, NULL, now,
- sigrdataset, 0,
+ sigrdataset,
+ options,
addedrdataset);
if (result == DNS_R_UNCHANGED)
result = ISC_R_SUCCESS;
* over the existing cache contents.
*/
options = DNS_DBADD_FORCE;
- } else
+ } else if ((fctx->options & DNS_FETCHOPT_PREFETCH) != 0)
+ options = DNS_DBADD_PREFETCH;
+ else
options = 0;
if (ANSWER(rdataset) &&
&cfg_rep_tuple, lookaside_fields
};
+static isc_result_t
+parse_optional_uint32(cfg_parser_t *pctx, const cfg_type_t *type,
+ cfg_obj_t **ret)
+{
+ isc_result_t result;
+ UNUSED(type);
+
+ CHECK(cfg_peektoken(pctx, ISC_LEXOPT_NUMBER | ISC_LEXOPT_CNUMBER));
+ if (pctx->token.type == isc_tokentype_number) {
+ CHECK(cfg_parse_obj(pctx, &cfg_type_uint32, ret));
+ } else {
+ CHECK(cfg_parse_obj(pctx, &cfg_type_void, ret));
+ }
+ cleanup:
+ return (result);
+}
+
+static void
+doc_optional_uint32(cfg_printer_t *pctx, const cfg_type_t *type) {
+ UNUSED(type);
+ cfg_print_cstr(pctx, "[ <integer> ]");
+}
+
+static cfg_type_t cfg_type_optional_uint32 = {
+ "optional_uint32", parse_optional_uint32, NULL, doc_optional_uint32,
+ NULL, NULL };
+
+static cfg_tuplefielddef_t prefetch_fields[] = {
+ { "trigger", &cfg_type_uint32, 0 },
+ { "eligible", &cfg_type_optional_uint32, 0 },
+ { NULL, NULL, 0 }
+};
+
+static cfg_type_t cfg_type_prefetch = {
+ "prefetch", cfg_parse_tuple, cfg_print_tuple, cfg_doc_tuple,
+ &cfg_rep_tuple, prefetch_fields
+};
/*
* DNS64.
*/
{ "max-udp-size", &cfg_type_uint32, 0 },
{ "min-roots", &cfg_type_uint32, CFG_CLAUSEFLAG_NOTIMP },
{ "minimal-responses", &cfg_type_boolean, 0 },
+ { "prefetch", &cfg_type_prefetch, 0 },
{ "preferred-glue", &cfg_type_astring, 0 },
{ "provide-ixfr", &cfg_type_boolean, 0 },
/*
/*%
* Sig-validity-interval.
*/
-static isc_result_t
-parse_optional_uint32(cfg_parser_t *pctx, const cfg_type_t *type,
- cfg_obj_t **ret)
-{
- isc_result_t result;
- UNUSED(type);
-
- CHECK(cfg_peektoken(pctx, ISC_LEXOPT_NUMBER | ISC_LEXOPT_CNUMBER));
- if (pctx->token.type == isc_tokentype_number) {
- CHECK(cfg_parse_obj(pctx, &cfg_type_uint32, ret));
- } else {
- CHECK(cfg_parse_obj(pctx, &cfg_type_void, ret));
- }
- cleanup:
- return (result);
-}
-
-static void
-doc_optional_uint32(cfg_printer_t *pctx, const cfg_type_t *type) {
- UNUSED(type);
- cfg_print_cstr(pctx, "[ <integer> ]");
-}
-
-static cfg_type_t cfg_type_optional_uint32 = {
- "optional_uint32", parse_optional_uint32, NULL, doc_optional_uint32,
- NULL, NULL };
static cfg_tuplefielddef_t validityinterval_fields[] = {
{ "validity", &cfg_type_uint32, 0 },