+3882. [func] By default, negative trust anchors will be tested
+ periodically to see whether data below them can be
+ validated, and if so, they will be allowed to
+ expire early. The "rndc nta -force" option
+ overrides this behvaior. The default NTA lifetime
+ and the recheck frequency can be configured by the
+ "nta-lifetime" and "nta-recheck" options. [RT #36146]
+
3881. [bug] Address memory leak with UPDATE error handling.
[RT #36303]
static char defaultconf[] = "\
options {\n\
automatic-interface-scan yes;\n\
+ bindkeys-file \"" NS_SYSCONFDIR "/bind.keys\";\n\
# blackhole {none;};\n"
#ifndef WIN32
" coresize default;\n\
memstatistics-file \"named.memstats\";\n\
multiple-cnames no;\n\
# named-xfer <obsolete>;\n\
+ nta-lifetime 3600;\n\
+ nta-recheck 300;\n\
# 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\
* PERFORMANCE OF THIS SOFTWARE.
*/
-/* $Id$ */
-
/*! \file */
#include <config.h>
return (ISC_R_UNEXPECTED);
}
- result = dns_view_initntatable(view, mctx);
+ result = dns_view_initntatable(view, ns_g_taskmgr, ns_g_timermgr);
if (result != ISC_R_SUCCESS) {
isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
NS_LOGMODULE_SERVER, ISC_LOG_ERROR,
if (result == ISC_R_SUCCESS)
CHECK(mustbesecure(obj, view->resolver));
+ obj = NULL;
+ result = ns_config_get(maps, "nta-recheck", &obj);
+ INSIST(result == ISC_R_SUCCESS);
+ view->nta_recheck = cfg_obj_asuint32(obj);
+
+ obj = NULL;
+ result = ns_config_get(maps, "nta-lifetime", &obj);
+ INSIST(result == ISC_R_SUCCESS);
+ view->nta_lifetime = cfg_obj_asuint32(obj);
+
obj = NULL;
result = ns_config_get(maps, "preferred-glue", &obj);
if (result == ISC_R_SUCCESS) {
} else {
empty_zones_enable = ISC_FALSE;
}
+
if (empty_zones_enable && !lwresd_g_useresolvconf) {
const char *empty;
int empty_zone = 0;
ns_server_dumpsecroots(ns_server_t *server, char *args) {
dns_view_t *view;
dns_keytable_t *secroots = NULL;
+ dns_ntatable_t *ntatable = NULL;
isc_result_t result;
char *ptr;
FILE *fp = NULL;
result = ISC_R_SUCCESS;
continue;
}
- fprintf(fp, "\n Start view %s\n\n", view->name);
+ fprintf(fp, "\n Start view %s\n", view->name);
+ fprintf(fp, " Secure roots:\n\n");
result = dns_keytable_dump(secroots, fp);
if (result != ISC_R_SUCCESS)
fprintf(fp, " dumpsecroots failed: %s\n",
isc_result_totext(result));
+
+ if (ntatable != NULL)
+ dns_ntatable_detach(&ntatable);
+ result = dns_view_getntatable(view, &ntatable);
+ if (result == ISC_R_NOTFOUND) {
+ result = ISC_R_SUCCESS;
+ continue;
+ }
+ fprintf(fp, "\n Negative trust anchors:\n\n");
+ result = dns_ntatable_dump(ntatable, fp);
+ if (result != ISC_R_SUCCESS)
+ fprintf(fp, " dumpntatable failed: %s\n",
+ isc_result_totext(result));
}
if (ptr != NULL)
ptr = next_token(&args, " \t");
cleanup:
if (secroots != NULL)
dns_keytable_detach(&secroots);
+ if (ntatable != NULL)
+ dns_ntatable_detach(&ntatable);
if (fp != NULL)
(void)isc_stdio_close(fp);
if (result == ISC_R_SUCCESS)
dns_view_t *view;
dns_ntatable_t *ntatable = NULL;
isc_result_t result;
- char *ptr, *nametext, *viewname;
+ char *ptr, *nametext = NULL, *viewname;
isc_stdtime_t now, when;
isc_time_t t;
char tbuf[64];
const char *msg = NULL;
+ isc_boolean_t dump = ISC_FALSE, force = ISC_FALSE;
dns_fixedname_t fn;
dns_name_t *ntaname;
dns_ttl_t ntattl;
- isc_textregion_t tr;
+ isc_boolean_t ttlset = ISC_FALSE;
+
+ UNUSED(force);
dns_fixedname_init(&fn);
ntaname = dns_fixedname_name(&fn);
if (ptr == NULL)
return (ISC_R_UNEXPECTEDEND);
+ for (;;) {
+ size_t len;
+
+ /* Check for options */
+ ptr = next_token(&args, " \t");
+ if (ptr == NULL)
+ return (ISC_R_UNEXPECTEDEND);
+
+ len = strlen(ptr);
+
+ if (strncasecmp(ptr, "-dump", len) == 0)
+ dump = ISC_TRUE;
+ else if (strncasecmp(ptr, "-remove", len) == 0) {
+ ntattl = 0;
+ ttlset = ISC_TRUE;
+ } else if (strncasecmp(ptr, "-force", len) == 0) {
+ force = ISC_TRUE;
+ continue;
+ } else if (strncasecmp(ptr, "-lifetime", len) == 0) {
+ isc_textregion_t tr;
+
+ ptr = next_token(&args, " \t");
+ if (ptr == NULL) {
+ msg = "No lifetime specified";
+ CHECK(ISC_R_UNEXPECTEDEND);
+ }
+
+ tr.base = ptr;
+ tr.length = strlen(ptr);
+ result = dns_ttl_fromtext(&tr, &ntattl);
+ if (result != ISC_R_SUCCESS) {
+ msg = "could not parse NTA lifetime";
+ CHECK(result);
+ }
+
+ if (ntattl > 86400) {
+ msg = "NTA lifetime cannot exceed one day";
+ CHECK(ISC_R_RANGE);
+ }
+
+ ttlset = ISC_TRUE;
+ continue;
+ } else
+ nametext = ptr;
+
+ break;
+ }
+
+ /*
+ * If -dump was specified, list NTA's and return
+ */
+ if (dump) {
+ for (view = ISC_LIST_HEAD(server->viewlist);
+ view != NULL;
+ view = ISC_LIST_NEXT(view, link))
+ {
+ if (ntatable != NULL)
+ dns_ntatable_detach(&ntatable);
+ result = dns_view_getntatable(view, &ntatable);
+ if (result == ISC_R_NOTFOUND) {
+ result = ISC_R_SUCCESS;
+ continue;
+ }
+ CHECK(dns_ntatable_totext(ntatable, text));
+ }
+
+ goto cleanup;
+ }
+
/* Get the NTA name. */
- nametext = next_token(&args, " \t");
+ if (nametext == NULL)
+ nametext = next_token(&args, " \t");
if (nametext == NULL)
return (ISC_R_UNEXPECTEDEND);
CHECK(dns_name_fromtext(ntaname, &b, dns_rootname, 0, NULL));
}
- /* Get the NTA duration. */
- ptr = next_token(&args, " \t");
- if (ptr == NULL)
- return (ISC_R_UNEXPECTEDEND);
-
- tr.base = ptr;
- tr.length = strlen(ptr);
- CHECK(dns_ttl_fromtext(&tr, &ntattl));
-
- if (ntattl > 86400) {
- msg = "NTA cannot exceed one day";
- CHECK(ISC_R_RANGE);
- }
-
/* Look for the view name. */
viewname = next_token(&args, " \t");
- /* Set up the NTA */
isc_stdtime_get(&now);
- when = now + ntattl;
result = isc_task_beginexclusive(server->task);
RUNTIME_CHECK(result == ISC_R_SUCCESS);
strcmp(view->name, viewname) != 0)
continue;
+ if (view->nta_lifetime == 0 || view->nta_recheck == 0)
+ continue;
+
+ if (!ttlset)
+ ntattl = view->nta_lifetime;
+
if (ntatable != NULL)
dns_ntatable_detach(&ntatable);
nametext, view->name,
isc_result_totext(result));
- isc_time_set(&t, when, 0);
- isc_time_formattimestamp(&t, tbuf, sizeof(tbuf));
+ if (ntattl != 0) {
+ CHECK(dns_ntatable_add(ntatable, ntaname,
+ force, now, ntattl));
- if (ntattl > 0) {
- CHECK(dns_ntatable_add(ntatable, ntaname, when));
+ when = now + ntattl;
+ isc_time_set(&t, when, 0);
+ isc_time_formattimestamp(&t, tbuf, sizeof(tbuf));
CHECK(putstr(text, "Negative trust anchor added: "));
CHECK(putstr(text, nametext));
<term><userinput>secroots <optional><replaceable>view ...</replaceable></optional></userinput></term>
<listitem>
<para>
- Dump the server's security roots to the secroots
- file for the specified views. If no view is
- specified, security roots for all
- views are dumped.
+ Dump the server's security roots and negative trust anchors
+ to the secroots file for the specified views. If no view is
+ specified, all views are dumped.
</para>
</listitem>
</varlistentry>
</varlistentry>
<varlistentry>
- <term><userinput>nta <replaceable>domain</replaceable> <replaceable>duration</replaceable> </userinput></term>
+ <term><userinput>nta
+ <optional>( -d | -f | -r | -l <replaceable>duration</replaceable>)</optional>
+ <replaceable>domain</replaceable>
+ <optional><replaceable>view</replaceable></optional>
+ </userinput></term>
<listitem>
<para>
Sets a DNSSEC negative trust anchor (NTA)
for <option>domain</option>, with a lifetime of
- <option>duration</option> (up to a limit of one day).
+ <option>lifetime</option>. The default lifetime is
+ configured in <file>named.conf</file> via the
+ <option>nta-lifetime</option>, and defaults to
+ one hour. The lifetime cannot exceed one day.
</para>
<para>
A negative trust anchor selectively disables
restarted (NTA's do not persist across restarts).
</para>
<para>
- TTL-style suffixes can be used to specify
- <option>duration</option> in seconds, minutes, or hours.
+ An existing NTA can be removed by using the
+ <option>-remove</option> option.
+ </para>
+ <para>
+ An NTA's lifetime can be specified with the
+ <option>-lifetime</option> option. TTL-style
+ suffixes can be used to specify the lifetime in
+ seconds, minutes, or hours. If the specified NTA
+ already exists, its lifetime will be updated to the
+ new value. Setting <option>lifetime</option> to zero
+ is equivalent to <option>-remove</option>.
+ </para>
+ <para>
+ If <option>-dump</option> is used, any other arguments
+ are ignored, and a list of existing NTAs is printed
+ (note that this may include NTAs that are expired but
+ have not yet been cleaned up).
+ </para>
+ <para>
+ Normally, <command>named</command> will periodically
+ test to see whether data below an NTA can now be
+ validated (see the <option>nta-recheck</option> option
+ in the Administrator Reference Manual for details).
+ If data can be validated, then the NTA is regarded as
+ no longer necessary, and will be allowed to expire
+ early. The <option>-force</option> overrides this
+ behavior and forces an NTA to persist for its entire
+ lifetime, regardless of whether data could be
+ validated if the NTA were not present.
</para>
<para>
- If the specified domain already has an NTA, its duration
- will be updated to the new value. Setting
- <option>duration</option> to zero will delete the NTA.
+ All of these options can be shortened, i.e., to
+ <option>-l</option>, <option>-r</option>, <option>-d</option>,
+ and <option>-f</option>.
</para>
</listitem>
</varlistentry>
linecount=`grep "dlv.isc.org/RSAMD5/.* ; managed" ns2/named.secroots | wc -l`
[ "$linecount" -eq 2 ] || ret=1
linecount=`cat ns2/named.secroots | wc -l`
-[ "$linecount" -eq 13 ] || ret=1
+[ "$linecount" -eq 28 ] || ret=1
n=`expr $n + 1`
if [ $ret != 0 ]; then echo "I:failed"; fi
status=`expr $status + $ret`
bogus NS ns.bogus
ns.bogus A 10.53.0.3
+; A subdomain with a corrupt DS
+badds NS ns.badds
+ns.badds A 10.53.0.3
+
; A dynamic secure subdomain
dynamic NS dynamic
dynamic A 10.53.0.3
( cd ../ns3 && $SHELL sign.sh )
-for subdomain in secure bogus dynamic keyless nsec3 optout nsec3-unknown \
- optout-unknown multiple rsasha256 rsasha512 kskonly update-nsec3 \
- auto-nsec auto-nsec3 secure.below-cname ttlpatch split-dnssec \
- split-smart expired expiring upper lower
-
+for subdomain in secure badds bogus dynamic keyless nsec3 optout \
+ nsec3-unknown optout-unknown multiple rsasha256 rsasha512 \
+ kskonly update-nsec3 auto-nsec auto-nsec3 secure.below-cname \
+ ttlpatch split-dnssec split-smart expired expiring upper lower
do
cp ../ns3/dsset-$subdomain.example. .
done
a A 10.0.0.1
b A 10.0.0.2
+c A 10.0.0.3
d A 10.0.0.4
z A 10.0.0.26
allow-update { any; };
};
+zone "badds.example" {
+ type master;
+ file "badds.example.db.signed";
+ allow-update { any; };
+};
+
zone "dynamic.example" {
type master;
file "dynamic.example.db.signed";
a A 10.0.0.1
b A 10.0.0.2
+c A 10.0.0.3
d A 10.0.0.4
+e A 10.0.0.5
+f A 10.0.0.6
+g A 10.0.0.7
z A 10.0.0.26
a.a.a.a.a.a.a.a.a.a.e A 10.0.0.27
x CNAME a
kskname=`$KEYGEN -q -3 -r $RANDFILE -fk $zone`
zskname=`$KEYGEN -q -3 -r $RANDFILE $zone`
cp $infile $zonefile
+
+#
+# A zone with a bad DS in the parent
+# (sourced from bogus.example.db.in)
+#
+zone=badds.example.
+infile=bogus.example.db.in
+zonefile=badds.example.db
+
+keyname=`$KEYGEN -q -r $RANDFILE -a RSAMD5 -b 768 -n zone $zone`
+
+cat $infile $keyname.key >$zonefile
+
+$SIGNER -P -r $RANDFILE -o $zone $zonefile > /dev/null 2>&1
+sed -e 's/bogus/badds/g' < dsset-bogus.example. > dsset-badds.example.
dnssec-validation yes;
dnssec-must-be-secure mustbesecure.example yes;
+ nta-lifetime 10s;
+ nta-recheck 7s;
+
# Note: We only reference the bind.keys file here to confirm that it
# is *not* being used. It contains the real root key, and we're
# using a local toy root zone for the tests, so it wouldn't work.
echo "a.bogus.example. A 10.0.0.22" >>../ns3/bogus.example.db.signed
echo "b.bogus.example. A 10.0.0.23" >>../ns3/bogus.example.db.signed
+echo "c.bogus.example. A 10.0.0.23" >>../ns3/bogus.example.db.signed
cd ../ns3 && cp -f siginterval1.conf siginterval.conf
cd ../ns4 && cp -f named1.conf named.conf
linecount=`grep "./RSAMD5/$keyid ; trusted" ns4/named.secroots | wc -l`
[ "$linecount" -eq 1 ] || ret=1
linecount=`cat ns4/named.secroots | wc -l`
-[ "$linecount" -eq 5 ] || ret=1
+[ "$linecount" -eq 10 ] || ret=1
n=`expr $n + 1`
if [ $ret != 0 ]; then echo "I:failed"; fi
status=`expr $status + $ret`
echo "I:checking positive and negative validation with negative trust anchors ($n)"
ret=0
+#
# check correct initial behavior
+#
$DIG $DIGOPTS a.bogus.example. a @10.53.0.4 > dig.out.ns4.test$n.1 || ret=1
grep "status: SERVFAIL" dig.out.ns4.test$n.1 > /dev/null || ret=1
-$DIG $DIGOPTS a.secure.example. a @10.53.0.4 > dig.out.ns4.test$n.2 || ret=1
-grep "flags:[^;]* ad[^;]*;" dig.out.ns4.test$n.2 > /dev/null || ret=1
+$DIG $DIGOPTS badds.example. soa @10.53.0.4 > dig.out.ns4.test$n.2 || ret=1
+grep "status: SERVFAIL" dig.out.ns4.test$n.2 > /dev/null || ret=1
+$DIG $DIGOPTS a.secure.example. a @10.53.0.4 > dig.out.ns4.test$n.3 || ret=1
+grep "flags:[^;]* ad[^;]*;" dig.out.ns4.test$n.3 > /dev/null || ret=1
+
+#
# add negative trust anchors
-$RNDC -c ../common/rndc.conf -s 10.53.0.4 -p 9953 nta bogus.example 15s 2>&1 | sed 's/^/I:ns4 /'
-$RNDC -c ../common/rndc.conf -s 10.53.0.4 -p 9953 nta secure.example 15s 2>&1 | sed 's/^/I:ns4 /'
+#
+$RNDC -c ../common/rndc.conf -s 10.53.0.4 -p 9953 nta -f -l 15s bogus.example 2>&1 | sed 's/^/I:ns4 /'
+$RNDC -c ../common/rndc.conf -s 10.53.0.4 -p 9953 nta badds.example 2>&1 | sed 's/^/I:ns4 /'
+lines=`$RNDC -c ../common/rndc.conf -s 10.53.0.4 -p 9953 nta -d | wc -l`
+[ "$lines" -eq 2 ] || ret=1
+$RNDC -c ../common/rndc.conf -s 10.53.0.4 -p 9953 nta secure.example 2>&1 | sed 's/^/I:ns4 /'
+$RNDC -c ../common/rndc.conf -s 10.53.0.4 -p 9953 nta fakenode.secure.example 2>&1 | sed 's/^/I:ns4 /'
+lines=`$RNDC -c ../common/rndc.conf -s 10.53.0.4 -p 9953 nta -d | wc -l`
+[ "$lines" -eq 4 ] || ret=1
+
+#
# check behavior with NTA's in place
-$DIG $DIGOPTS a.bogus.example. a @10.53.0.4 > dig.out.ns4.test$n.3 || ret=1
-grep "status: SERVFAIL" dig.out.ns4.test$n.3 > /dev/null && ret=1
-$DIG $DIGOPTS a.secure.example. a @10.53.0.4 > dig.out.ns4.test$n.4 || ret=1
-grep "flags:[^;]* ad[^;]*;" dig.out.ns4.test$n.4 > /dev/null && ret=1
-echo "I: waiting for NTA expiration"
-sleep 15
-# check correct behavior after expiry
-$DIG $DIGOPTS b.bogus.example. a @10.53.0.4 > dig.out.ns4.test$n.5 || ret=1
-grep "status: SERVFAIL" dig.out.ns4.test$n.5 > /dev/null || ret=1
-$DIG $DIGOPTS b.secure.example. a @10.53.0.4 > dig.out.ns4.test$n.6 || ret=1
-grep "flags:[^;]* ad[^;]*;" dig.out.ns4.test$n.6 > /dev/null || ret=1
+#
+$DIG $DIGOPTS a.bogus.example. a @10.53.0.4 > dig.out.ns4.test$n.4 || ret=1
+grep "status: SERVFAIL" dig.out.ns4.test$n.4 > /dev/null && ret=1
+$DIG $DIGOPTS badds.example. soa @10.53.0.4 > dig.out.ns4.test$n.5 || ret=1
+grep "status: SERVFAIL" dig.out.ns4.test$n.5 > /dev/null && ret=1
+$DIG $DIGOPTS a.secure.example. a @10.53.0.4 > dig.out.ns4.test$n.6 || ret=1
+grep "flags:[^;]* ad[^;]*;" dig.out.ns4.test$n.6 > /dev/null && ret=1
+$DIG $DIGOPTS a.fakenode.secure.example. a @10.53.0.4 > dig.out.ns4.test$n.7 || ret=1
+grep "flags:[^;]* ad[^;]*;" dig.out.ns4.test$n.7 > /dev/null && ret=1
+echo "I: dumping secroots"
+$RNDC -c ../common/rndc.conf -s 10.53.0.4 -p 9953 secroots | sed 's/^/I:ns4 /'
+grep "bogus.example: expiry" ns4/named.secroots > /dev/null || ret=1
+grep "badds.example: expiry" ns4/named.secroots > /dev/null || ret=1
+grep "secure.example: expiry" ns4/named.secroots > /dev/null || ret=1
+grep "fakenode.secure.example: expiry" ns4/named.secroots > /dev/null || ret=1
+echo "I: waiting for NTA rechecks/expirations"
+
+#
+# secure.example and badds.example used default nta-duration
+# (configured as 10s in ns4/named1.conf), but nta recheck interval
+# is configured to 7s, so at t=8 the NTAs for secure.example and
+# fakenode.secure.example should both be lifted, but badds.example
+# should still be going.
+#
+sleep 8
+$DIG $DIGOPTS b.secure.example. a @10.53.0.4 > dig.out.ns4.test$n.8 || ret=1
+grep "flags:[^;]* ad[^;]*;" dig.out.ns4.test$n.8 > /dev/null || ret=1
+$DIG $DIGOPTS b.fakenode.secure.example. a @10.53.0.4 > dig.out.ns4.test$n.9 || ret=1
+grep "flags:[^;]* ad[^;]*;" dig.out.ns4.test$n.9 > /dev/null || ret=1
+grep "status: NXDOMAIN" dig.out.ns4.test$n.9 > /dev/null || ret=1
+$DIG $DIGOPTS badds.example. soa @10.53.0.4 > dig.out.ns4.test$n.10 || ret=1
+grep "status: SERVFAIL" dig.out.ns4.test$n.10 > /dev/null && ret=1
+
+#
+# bogus.example was set to expire in 15s, so at t=11
+# it should still be NTA'd, but badds.example used the default
+# lifetime of 10s, so it should revert to SERVFAIL now.
+#
+sleep 3
+$DIG $DIGOPTS b.bogus.example. a @10.53.0.4 > dig.out.ns4.test$n.11 || ret=1
+grep "status: SERVFAIL" dig.out.ns4.test$n.11 > /dev/null && ret=1
+$DIG $DIGOPTS a.badds.example. a @10.53.0.4 > dig.out.ns4.test$n.12 || ret=1
+grep "status: SERVFAIL" dig.out.ns4.test$n.12 > /dev/null || ret=1
+$DIG $DIGOPTS c.secure.example. a @10.53.0.4 > dig.out.ns4.test$n.13 || ret=1
+grep "flags:[^;]* ad[^;]*;" dig.out.ns4.test$n.13 > /dev/null || ret=1
+
+#
+# at t=16, all the NTAs should have expired.
+#
+sleep 5
+# check correct behavior after bogus.example expiry
+$DIG $DIGOPTS d.secure.example. a @10.53.0.4 > dig.out.ns4.test$n.14 || ret=1
+grep "flags:[^;]* ad[^;]*;" dig.out.ns4.test$n.14 > /dev/null || ret=1
+$DIG $DIGOPTS c.bogus.example. a @10.53.0.4 > dig.out.ns4.test$n.15 || ret=1
+grep "status: SERVFAIL" dig.out.ns4.test$n.15 > /dev/null || ret=1
+# check nta table has been cleaned up now
+lines=`$RNDC -c ../common/rndc.conf -s 10.53.0.4 -p 9953 nta -d | wc -l`
+[ "$lines" -eq 0 ] || ret=1
n=`expr $n + 1`
if [ $ret != 0 ]; then echo "I:failed"; fi
status=`expr $status + $ret`
<optional> max-refresh-time <replaceable>number</replaceable> ; </optional>
<optional> min-retry-time <replaceable>number</replaceable> ; </optional>
<optional> max-retry-time <replaceable>number</replaceable> ; </optional>
+ <optional> nta-lifetime <replaceable>duration</replaceable> ; </optional>
+ <optional> nta-recheck <replaceable>duration</replaceable> ; </optional>
<optional> port <replaceable>ip_port</replaceable>; </optional>
<optional> dscp <replaceable>ip_dscp</replaceable></optional> ;
<optional> additional-from-auth <replaceable>yes_or_no</replaceable> ; </optional>
</listitem>
</varlistentry>
+ <varlistentry>
+ <term><command>nta-lifetime</command></term>
+ <listitem>
+ <para>
+ Species the default lifetime, in seconds,
+ that will be used for negative trust anchors added
+ via <command>rndc nta</command>.
+ </para>
+ <para>
+ A negative trust anchor selectively disables
+ DNSSEC validation for zones that known to be
+ failing because of misconfiguration rather than
+ an attack. When data to be validated is
+ at or below an active NTA (and above any other
+ configured trust anchors), <command>named</command> will
+ abort the DNSSEC validation process and treat the data as
+ insecure rather than bogus. This continues until the
+ NTA's lifetime is elapsed, or until the server is
+ restarted (NTA's do not persist across restarts).
+ </para>
+ <para>
+ For convienience, TTL-style time unit suffixes can be
+ used to specify the NTA lifetime in seconds, minutes
+ or hours. <option>nta-lifetime</option> defaults to
+ one hour. It cannot exceed one day.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><command>nta-recheck</command></term>
+ <listitem>
+ <para>
+ Species how often to check whether negative
+ trust anchors added via <command>rndc nta</command>
+ are still necessary.
+ </para>
+ <para>
+ A negative trust anchor is normally used when a
+ domain has stopped validating due to operator error;
+ it temporarily disables DNSSEC validation for that
+ domain. In the interest of ensuring that DNSSEC
+ validation is turned back on as soon as possible,
+ <command>named</command> will periodically send a
+ query to the domain, ignoring negative trust anchors,
+ to find out whether it can now be validated. If so,
+ the negative trust anchor is allowed to expire early.
+ </para>
+ <para>
+ Validity checks can be disabled for an indivdiual
+ NTA by using <command>rndc nta -f</command>, or
+ for all NTA's by setting <option>nta-recheck</option>
+ to zero.
+ </para>
+ <para>
+ For convienience, TTL-style time unit suffixes can be
+ used to specify the NTA recheck interval in seconds,
+ minutes or hours. The default is five minutes.
+ </para>
+ </listitem>
+ </varlistentry>
+
+
<varlistentry>
<term><command>max-zone-ttl</command></term>
<listitem>
const char *str;
dns_name_t *name;
isc_buffer_t b;
+ isc_uint32_t lifetime;
static intervaltable intervals[] = {
{ "cleaning-interval", 60, 28 * 24 * 60 }, /* 28 days */
if (tresult != ISC_R_SUCCESS)
result = tresult;
+ obj = NULL;
+ (void)cfg_map_get(options, "nta-lifetime", &obj);
+ if (obj != NULL) {
+ lifetime = cfg_obj_asuint32(obj);
+ if (lifetime > 86400) {
+ cfg_obj_log(obj, logctx, ISC_LOG_ERROR,
+ "'nta-lifetime' cannot exceed one day");
+ result = ISC_R_RANGE;
+ } else if (lifetime == 0) {
+ cfg_obj_log(obj, logctx, ISC_LOG_ERROR,
+ "'nta-lifetime' may not be zero");
+ result = ISC_R_RANGE;
+ }
+ }
+
+ obj = NULL;
+ (void)cfg_map_get(options, "nta-recheck", &obj);
+ if (obj != NULL) {
+ isc_uint32_t recheck = cfg_obj_asuint32(obj);
+ if (recheck > 86400) {
+ cfg_obj_log(obj, logctx, ISC_LOG_ERROR,
+ "'nta-recheck' cannot exceed one day");
+ result = ISC_R_RANGE;
+ }
+
+ if (recheck > lifetime)
+ cfg_obj_log(obj, logctx, ISC_LOG_WARNING,
+ "'nta-recheck' (%d seconds) is "
+ "greater than 'nta-lifetime' "
+ "(%d seconds)", recheck, lifetime);
+ }
+
return (result);
}
result = isc_task_create(taskmgr, 1, &dbtask);
if (result != ISC_R_SUCCESS)
goto cleanup_db;
+
+ isc_task_setname(dbtask, "cache_dbtask", NULL);
dns_db_settask(cache->db, dbtask);
isc_task_detach(&dbtask);
}
* DNSSEC validation.
*/
+#include <isc/buffer.h>
#include <isc/lang.h>
#include <isc/magic.h>
#include <isc/refcount.h>
#include <isc/rwlock.h>
#include <isc/stdtime.h>
+#include <isc/task.h>
+#include <isc/timer.h>
#include <dns/types.h>
+#include <dns/rdataset.h>
+#include <dns/resolver.h>
+#include <dns/view.h>
ISC_LANG_BEGINDECLS
struct dns_ntatable {
/* Unlocked. */
unsigned int magic;
- isc_mem_t *mctx;
+ dns_view_t *view;
isc_rwlock_t rwlock;
+ isc_uint32_t recheck;
+ isc_taskmgr_t *taskmgr;
+ isc_timermgr_t *timermgr;
+ isc_task_t *task;
/* Locked by rwlock. */
isc_uint32_t references;
dns_rbt_t *table;
#define NTATABLE_MAGIC ISC_MAGIC('N', 'T', 'A', 't')
#define VALID_NTATABLE(nt) ISC_MAGIC_VALID(nt, NTATABLE_MAGIC)
-struct dns_nta {
- unsigned int magic;
- isc_refcount_t refcount;
- isc_stdtime_t expiry;
-};
-
-#define NTA_MAGIC ISC_MAGIC('N', 'T', 'A', 'n')
-#define VALID_NTA(nn) ISC_MAGIC_VALID(nn, NTA_MAGIC)
-
isc_result_t
-dns_ntatable_create(isc_mem_t *mctx, dns_ntatable_t **ntatablep);
+dns_ntatable_create(dns_view_t *view,
+ isc_taskmgr_t *taskmgr, isc_timermgr_t *timermgr,
+ dns_ntatable_t **ntatablep);
/*%<
- * Create an NTA table.
+ * Create an NTA table in view 'view'.
*
* Requires:
*
- *\li 'mctx' is a valid memory context.
+ *\li 'view' is a valid view.
+ *
+ *\li 'tmgr' is a valid timer manager.
*
*\li ntatablep != NULL && *ntatablep == NULL
*
isc_result_t
dns_ntatable_add(dns_ntatable_t *ntatable, dns_name_t *name,
- isc_uint32_t expiry);
+ isc_boolean_t force, isc_stdtime_t now,
+ isc_uint32_t lifetime);
/*%<
* Add a negative trust anchor to 'ntatable' for name 'name',
- * which will expire at time 'expiry'.
+ * which will expire at time 'now' + 'lifetime'. If 'force' is ISC_FALSE,
+ * then the name will be checked periodically to see if it's bogus;
+ * if not, then the NTA will be allowed to expire early.
*
* Notes:
*
*/
isc_result_t
-dns_ntatable_dump(dns_ntatable_t *ntatable, FILE *fp);
+dns_ntatable_totext(dns_ntatable_t *ntatable, isc_buffer_t *buf);
/*%<
- * Dump the NTA table on fp.
+ * Dump the NTA table to buffer 'buf'
*/
isc_result_t
-dns_nta_create(isc_mem_t *mctx, dns_nta_t **target);
+dns_ntatable_dump(dns_ntatable_t *ntatable, FILE *fp);
/*%<
- * Allocate space for an NTA
+ * Dump the NTA table to the file opened as 'fp'.
*/
ISC_LANG_ENDDECLS
#define DNS_FETCHOPT_WANTNSID 0x080 /*%< Request NSID */
#define DNS_FETCHOPT_PREFETCH 0x100 /*%< Do prefetch */
#define DNS_FETCHOPT_NOCDFLAG 0x200 /*%< Don't set CD flag. */
+#define DNS_FETCHOPT_NONTA 0x400 /*%< Ignore NTA table. */
/* Reserved in use by adb.c 0x00400000 */
#define DNS_FETCHOPT_EDNSVERSIONSET 0x00800000
#define DNS_VALIDATOR_DLV 0x0001U
#define DNS_VALIDATOR_DEFER 0x0002U
#define DNS_VALIDATOR_NOCDFLAG 0x0004U
+#define DNS_VALIDATOR_NONTA 0x0008U /*% Ignore NTA table */
ISC_LANG_BEGINDECLS
isc_boolean_t requestsit;
dns_ttl_t maxcachettl;
dns_ttl_t maxncachettl;
+ isc_uint32_t nta_lifetime;
+ isc_uint32_t nta_recheck;
dns_ttl_t prefetch_trigger;
dns_ttl_t prefetch_eligible;
in_port_t dstport;
*/
isc_result_t
-dns_view_initntatable(dns_view_t *view, isc_mem_t *mctx);
+dns_view_initntatable(dns_view_t *view,
+ isc_taskmgr_t *taskmgr, isc_timermgr_t *timermgr);
/*%<
* Initialize the negative trust anchor table for the view.
*
isc_result_t
dns_view_issecuredomain(dns_view_t *view, dns_name_t *name,
- isc_stdtime_t now, isc_boolean_t *secure_domain);
+ isc_stdtime_t now, isc_boolean_t checknta,
+ isc_boolean_t *secure_domain);
/*%<
* Is 'name' at or beneath a trusted key, and not covered by a valid
* negative trust anchor? Put answer in '*secure_domain'.
*
+ * If 'checknta' is ISC_FALSE, ignore the NTA table in determining
+ * whether this is a secure domain.
+ *
* Requires:
* \li 'view' is valid.
*
#include <config.h>
+#include <isc/buffer.h>
#include <isc/log.h>
#include <isc/mem.h>
#include <isc/rwlock.h>
#include <isc/string.h>
+#include <isc/task.h>
#include <isc/time.h>
+#include <isc/timer.h>
#include <isc/util.h>
+#include <dns/db.h>
#include <dns/log.h>
#include <dns/nta.h>
#include <dns/fixedname.h>
#include <dns/name.h>
#include <dns/rbt.h>
+#include <dns/rdataset.h>
+#include <dns/resolver.h>
#include <dns/result.h>
+struct dns_nta {
+ unsigned int magic;
+ isc_refcount_t refcount;
+ dns_ntatable_t *ntatable;
+ isc_timer_t *timer;
+ dns_fetch_t *fetch;
+ dns_rdataset_t rdataset;
+ dns_rdataset_t sigrdataset;
+ dns_fixedname_t fn;
+ dns_name_t *name;
+ isc_stdtime_t expiry;
+};
+
+#define NTA_MAGIC ISC_MAGIC('N', 'T', 'A', 'n')
+#define VALID_NTA(nn) ISC_MAGIC_VALID(nn, NTA_MAGIC)
+
static void
nta_detach(isc_mem_t *mctx, dns_nta_t **ntap) {
unsigned int refs;
*ntap = NULL;
isc_refcount_decrement(&nta->refcount, &refs);
if (refs == 0) {
+ nta->magic = 0;
+ if (nta->timer != NULL) {
+ (void) isc_timer_reset(nta->timer,
+ isc_timertype_inactive,
+ NULL, NULL, ISC_TRUE);
+ isc_timer_detach(&nta->timer);
+ }
isc_refcount_destroy(&nta->refcount);
+ if (dns_rdataset_isassociated(&nta->rdataset))
+ dns_rdataset_disassociate(&nta->rdataset);
+ if (dns_rdataset_isassociated(&nta->sigrdataset))
+ dns_rdataset_disassociate(&nta->sigrdataset);
+ if (nta->fetch != NULL) {
+ dns_resolver_cancelfetch(nta->fetch);
+ dns_resolver_destroyfetch(&nta->fetch);
+ }
isc_mem_put(mctx, nta, sizeof(dns_nta_t));
}
}
}
isc_result_t
-dns_ntatable_create(isc_mem_t *mctx, dns_ntatable_t **ntatablep) {
+dns_ntatable_create(dns_view_t *view,
+ isc_taskmgr_t *taskmgr, isc_timermgr_t *timermgr,
+ dns_ntatable_t **ntatablep)
+{
dns_ntatable_t *ntatable;
isc_result_t result;
- /*
- * Create an NTA table.
- */
-
REQUIRE(ntatablep != NULL && *ntatablep == NULL);
- ntatable = isc_mem_get(mctx, sizeof(*ntatable));
+ ntatable = isc_mem_get(view->mctx, sizeof(*ntatable));
if (ntatable == NULL)
return (ISC_R_NOMEMORY);
- ntatable->table = NULL;
- result = dns_rbt_create(mctx, free_nta, mctx, &ntatable->table);
+ ntatable->task = NULL;
+ result = isc_task_create(taskmgr, 0, &ntatable->task);
if (result != ISC_R_SUCCESS)
goto cleanup_ntatable;
+ isc_task_setname(ntatable->task, "ntatable", ntatable);
+
+ ntatable->table = NULL;
+ result = dns_rbt_create(view->mctx, free_nta, view->mctx,
+ &ntatable->table);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup_task;
result = isc_rwlock_init(&ntatable->rwlock, 0, 0);
if (result != ISC_R_SUCCESS)
goto cleanup_rbt;
- ntatable->mctx = NULL;
- isc_mem_attach(mctx, &ntatable->mctx);
+ ntatable->timermgr = timermgr;
+ ntatable->taskmgr = taskmgr;
+
+ ntatable->view = view;
ntatable->references = 1;
+
ntatable->magic = NTATABLE_MAGIC;
*ntatablep = ntatable;
cleanup_rbt:
dns_rbt_destroy(&ntatable->table);
+ cleanup_task:
+ isc_task_detach(&ntatable->task);
+
cleanup_ntatable:
- isc_mem_put(mctx, ntatable, sizeof(*ntatable));
+ isc_mem_put(ntatable->view->mctx, ntatable, sizeof(*ntatable));
return (result);
}
if (destroy) {
dns_rbt_destroy(&ntatable->table);
isc_rwlock_destroy(&ntatable->rwlock);
+ if (ntatable->task != NULL)
+ isc_task_detach(&ntatable->task);
+ ntatable->timermgr = NULL;
+ ntatable->taskmgr = NULL;
ntatable->magic = 0;
- isc_mem_putanddetach(&ntatable->mctx,
- ntatable, sizeof(*ntatable));
+ isc_mem_put(ntatable->view->mctx, ntatable, sizeof(*ntatable));
+ }
+}
+
+static void
+fetch_done(isc_task_t *task, isc_event_t *event) {
+ dns_fetchevent_t *devent = (dns_fetchevent_t *)event;
+ dns_nta_t *nta = devent->ev_arg;
+ isc_result_t eresult = devent->result;
+ dns_ntatable_t *ntatable = nta->ntatable;
+ isc_stdtime_t now;
+
+ UNUSED(task);
+
+ if (dns_rdataset_isassociated(&nta->rdataset))
+ dns_rdataset_disassociate(&nta->rdataset);
+ if (dns_rdataset_isassociated(&nta->sigrdataset))
+ dns_rdataset_disassociate(&nta->sigrdataset);
+ dns_resolver_destroyfetch(&nta->fetch);
+
+ if (devent->node != NULL)
+ dns_db_detachnode(devent->db, &devent->node);
+ if (devent->db != NULL)
+ dns_db_detach(&devent->db);
+
+ isc_event_free(&event);
+
+ switch (eresult) {
+ case ISC_R_SUCCESS:
+ case DNS_R_NCACHENXDOMAIN:
+ case DNS_R_NXDOMAIN:
+ case DNS_R_NCACHENXRRSET:
+ case DNS_R_NXRRSET:
+ isc_stdtime_get(&now);
+ nta->expiry = now;
+ break;
+ default:
+ break;
+ }
+
+ /*
+ * If we're expiring before the next recheck, we might
+ * as well stop the timer now.
+ */
+ if (nta->timer != NULL && nta->expiry - now < ntatable->recheck)
+ (void) isc_timer_reset(nta->timer, isc_timertype_inactive,
+ NULL, NULL, ISC_TRUE);
+}
+
+static void
+checkbogus(isc_task_t *task, isc_event_t *event) {
+ dns_nta_t *nta = event->ev_arg;
+ dns_ntatable_t *ntatable = nta->ntatable;
+ dns_view_t *view = ntatable->view;
+
+ if (nta->fetch != NULL) {
+ dns_resolver_cancelfetch(nta->fetch);
+ dns_resolver_destroyfetch(&nta->fetch);
+ }
+ if (dns_rdataset_isassociated(&nta->rdataset))
+ dns_rdataset_disassociate(&nta->rdataset);
+ if (dns_rdataset_isassociated(&nta->sigrdataset))
+ dns_rdataset_disassociate(&nta->sigrdataset);
+
+ isc_event_free(&event);
+
+ (void)dns_resolver_createfetch(view->resolver, nta->name,
+ dns_rdatatype_nsec,
+ NULL, NULL, NULL,
+ DNS_FETCHOPT_NONTA,
+ task, fetch_done, nta,
+ &nta->rdataset,
+ &nta->sigrdataset,
+ &nta->fetch);
+}
+
+static isc_result_t
+settimer(dns_ntatable_t *ntatable, dns_nta_t *nta, isc_uint32_t lifetime) {
+ isc_result_t result;
+ isc_interval_t interval;
+ dns_view_t *view;
+
+ REQUIRE(VALID_NTATABLE(ntatable));
+ REQUIRE(VALID_NTA(nta));
+
+ if (ntatable->timermgr == NULL)
+ return (ISC_R_SUCCESS);
+
+ view = ntatable->view;
+ if (view->nta_recheck == 0 || lifetime <= view->nta_recheck)
+ return (ISC_R_SUCCESS);
+
+ isc_interval_set(&interval, view->nta_recheck, 0);
+ result = isc_timer_create(ntatable->timermgr, isc_timertype_ticker,
+ NULL, &interval, ntatable->task,
+ checkbogus, nta, &nta->timer);
+ return (result);
+}
+
+static isc_result_t
+nta_create(dns_ntatable_t *ntatable, dns_name_t *name, dns_nta_t **target) {
+ isc_result_t result;
+ dns_nta_t *nta = NULL;
+ dns_view_t *view;
+
+ REQUIRE(VALID_NTATABLE(ntatable));
+ REQUIRE(target != NULL && *target == NULL);
+
+ view = ntatable->view;
+
+ nta = isc_mem_get(view->mctx, sizeof(dns_nta_t));
+ if (nta == NULL)
+ return (ISC_R_NOMEMORY);
+
+ nta->ntatable = ntatable;
+ nta->expiry = 0;
+ nta->timer = NULL;
+ nta->fetch = NULL;
+ dns_rdataset_init(&nta->rdataset);
+ dns_rdataset_init(&nta->sigrdataset);
+
+ result = isc_refcount_init(&nta->refcount, 1);
+ if (result != ISC_R_SUCCESS) {
+ isc_mem_put(view->mctx, nta, sizeof(dns_nta_t));
+ return (result);
}
+
+ dns_fixedname_init(&nta->fn);
+ nta->name = dns_fixedname_name(&nta->fn);
+ dns_name_copy(name, nta->name, NULL);
+
+ nta->magic = NTA_MAGIC;
+
+ *target = nta;
+ return (ISC_R_SUCCESS);
}
isc_result_t
dns_ntatable_add(dns_ntatable_t *ntatable, dns_name_t *name,
- isc_uint32_t expiry)
+ isc_boolean_t force, isc_stdtime_t now,
+ isc_uint32_t lifetime)
{
isc_result_t result;
dns_nta_t *nta = NULL;
dns_rbtnode_t *node;
+ dns_view_t *view;
REQUIRE(VALID_NTATABLE(ntatable));
- result = dns_nta_create(ntatable->mctx, &nta);
+ view = ntatable->view;
+
+ result = nta_create(ntatable, name, &nta);
if (result != ISC_R_SUCCESS)
return (result);
- nta->expiry = expiry;
+ nta->expiry = now + lifetime;
RWLOCK(&ntatable->rwlock, isc_rwlocktype_write);
node = NULL;
result = dns_rbt_addnode(ntatable->table, name, &node);
if (result == ISC_R_SUCCESS) {
+ if (!force)
+ (void)settimer(ntatable, nta, lifetime);
node->data = nta;
nta = NULL;
} else if (result == ISC_R_EXISTS) {
dns_nta_t *n = node->data;
if (n == NULL) {
+ if (!force)
+ (void)settimer(ntatable, nta, lifetime);
node->data = nta;
nta = NULL;
} else {
n->expiry = nta->expiry;
- nta_detach(ntatable->mctx, &nta);
+ nta_detach(view->mctx, &nta);
}
result = ISC_R_SUCCESS;
}
RWUNLOCK(&ntatable->rwlock, isc_rwlocktype_write);
if (nta != NULL)
- nta_detach(ntatable->mctx, &nta);
+ nta_detach(view->mctx, &nta);
return (result);
}
DNS_LOGMODULE_NTA, ISC_LOG_INFO,
"deleting expired NTA at %s", nb);
+ if (nta->timer != NULL) {
+ (void) isc_timer_reset(nta->timer,
+ isc_timertype_inactive,
+ NULL, NULL, ISC_TRUE);
+ isc_timer_detach(&nta->timer);
+ }
+
result = delete(ntatable, foundname);
if (result != ISC_R_SUCCESS) {
isc_log_write(dns_lctx, DNS_LOGCATEGORY_DNSSEC,
}
isc_result_t
-dns_ntatable_dump(dns_ntatable_t *ntatable, FILE *fp) {
+dns_ntatable_totext(dns_ntatable_t *ntatable, isc_buffer_t *buf) {
isc_result_t result;
dns_rbtnode_t *node;
dns_rbtnodechain_t chain;
+ isc_boolean_t first = ISC_TRUE;
+ isc_stdtime_t now;
REQUIRE(VALID_NTATABLE(ntatable));
+ isc_stdtime_get(&now);
+
RWLOCK(&ntatable->rwlock, isc_rwlocktype_read);
- dns_rbtnodechain_init(&chain, ntatable->mctx);
+ dns_rbtnodechain_init(&chain, ntatable->view->mctx);
result = dns_rbtnodechain_first(&chain, ntatable->table, NULL, NULL);
if (result != ISC_R_SUCCESS && result != DNS_R_NEWORIGIN)
goto cleanup;
if (node->data != NULL) {
dns_nta_t *n = (dns_nta_t *) node->data;
char nbuf[DNS_NAME_FORMATSIZE], tbuf[80];
- dns_name_t name;
+ char obuf[DNS_NAME_FORMATSIZE + 200];
+ dns_fixedname_t fn;
+ dns_name_t *name;
isc_time_t t;
- dns_name_init(&name, NULL);
- dns_rbt_namefromnode(node, &name);
- dns_name_format(&name, nbuf, sizeof(nbuf));
+ dns_fixedname_init(&fn);
+ name = dns_fixedname_name(&fn);
+ dns_rbt_fullnamefromnode(node, name);
+ dns_name_format(name, nbuf, sizeof(nbuf));
isc_time_set(&t, n->expiry, 0);
isc_time_formattimestamp(&t, tbuf, sizeof(tbuf));
- fprintf(fp, "%s : expiry %s\n", nbuf, tbuf);
+
+ snprintf(obuf, sizeof(obuf), "%s%s: %s %s",
+ first ? "" : "\n", nbuf,
+ n->expiry < now ? "expired" : "expiry",
+ tbuf);
+ first = ISC_FALSE;
+ if (strlen(obuf) >= isc_buffer_availablelength(buf)) {
+ result = ISC_R_NOSPACE;
+ goto cleanup;
+ } else
+ isc_buffer_putstr(buf, obuf);
}
result = dns_rbtnodechain_next(&chain, NULL, NULL);
if (result != ISC_R_SUCCESS && result != DNS_R_NEWORIGIN) {
}
}
+ if (isc_buffer_availablelength(buf) != 0)
+ isc_buffer_putuint8(buf, 0);
+
cleanup:
dns_rbtnodechain_invalidate(&chain);
RWUNLOCK(&ntatable->rwlock, isc_rwlocktype_read);
}
isc_result_t
-dns_nta_create(isc_mem_t *mctx, dns_nta_t **target) {
+dns_ntatable_dump(dns_ntatable_t *ntatable, FILE *fp) {
isc_result_t result;
- dns_nta_t *nta = NULL;
+ dns_rbtnode_t *node;
+ dns_rbtnodechain_t chain;
+ isc_stdtime_t now;
- REQUIRE(target != NULL && *target == NULL);
+ REQUIRE(VALID_NTATABLE(ntatable));
- nta = isc_mem_get(mctx, sizeof(dns_nta_t));
- if (nta == NULL)
- return (ISC_R_NOMEMORY);
+ isc_stdtime_get(&now);
- nta->expiry = 0;
+ RWLOCK(&ntatable->rwlock, isc_rwlocktype_read);
+ dns_rbtnodechain_init(&chain, ntatable->view->mctx);
+ result = dns_rbtnodechain_first(&chain, ntatable->table, NULL, NULL);
+ if (result != ISC_R_SUCCESS && result != DNS_R_NEWORIGIN)
+ goto cleanup;
+ for (;;) {
+ dns_rbtnodechain_current(&chain, NULL, NULL, &node);
+ if (node->data != NULL) {
+ dns_nta_t *n = (dns_nta_t *) node->data;
+ char nbuf[DNS_NAME_FORMATSIZE], tbuf[80];
+ dns_fixedname_t fn;
+ dns_name_t *name;
+ isc_time_t t;
- result = isc_refcount_init(&nta->refcount, 1);
- if (result != ISC_R_SUCCESS) {
- isc_mem_put(mctx, nta, sizeof(dns_nta_t));
- return (result);
+ dns_fixedname_init(&fn);
+ name = dns_fixedname_name(&fn);
+ dns_rbt_fullnamefromnode(node, name);
+ dns_name_format(name, nbuf, sizeof(nbuf));
+ isc_time_set(&t, n->expiry, 0);
+ isc_time_formattimestamp(&t, tbuf, sizeof(tbuf));
+ fprintf(fp, "%s: %s %s\n", nbuf,
+ n->expiry < now ? "expired" : "expiry",
+ tbuf);
+ }
+ result = dns_rbtnodechain_next(&chain, NULL, NULL);
+ if (result != ISC_R_SUCCESS && result != DNS_R_NEWORIGIN) {
+ if (result == ISC_R_NOMORE)
+ result = ISC_R_SUCCESS;
+ break;
+ }
}
- nta->magic = NTA_MAGIC;
-
- *target = nta;
- return (ISC_R_SUCCESS);
+ cleanup:
+ dns_rbtnodechain_invalidate(&chain);
+ RWUNLOCK(&ntatable->rwlock, isc_rwlocktype_read);
+ return (result);
}
static isc_result_t
issecuredomain(dns_view_t *view, dns_name_t *name, dns_rdatatype_t type,
- isc_stdtime_t now, isc_boolean_t *issecure)
+ isc_stdtime_t now, isc_boolean_t checknta,
+ isc_boolean_t *issecure)
{
dns_name_t suffix;
unsigned int labels;
name = &suffix;
}
- return (dns_view_issecuredomain(view, name, now, issecure));
+ return (dns_view_issecuredomain(view, name, now, checknta, issecure));
}
static isc_result_t
else if ((query->options & DNS_FETCHOPT_NOVALIDATE) != 0)
fctx->qmessage->flags |= DNS_MESSAGEFLAG_CD;
else if (res->view->enablevalidation &&
- ((fctx->qmessage->flags & DNS_MESSAGEFLAG_RD) != 0)) {
+ ((fctx->qmessage->flags & DNS_MESSAGEFLAG_RD) != 0))
+ {
+ isc_boolean_t checknta =
+ ISC_TF((query->options & DNS_FETCHOPT_NONTA) == 0);
result = issecuredomain(res->view, &fctx->name,
fctx->type, query->start.seconds,
- &secure_domain);
+ checknta, &secure_domain);
if (result != ISC_R_SUCCESS)
secure_domain = ISC_FALSE;
if (res->view->dlv != NULL)
isc_task_t *task;
isc_boolean_t fail;
unsigned int valoptions = 0;
+ isc_boolean_t checknta = ISC_TRUE;
/*
* The appropriate bucket lock must be held.
/*
* Is DNSSEC validation required for this name?
*/
+ if ((fctx->options & DNS_FETCHOPT_NONTA) != 0) {
+ valoptions |= DNS_VALIDATOR_NONTA;
+ checknta = ISC_FALSE;
+ }
+
if (res->view->enablevalidation) {
result = issecuredomain(res->view, name, fctx->type,
- now, &secure_domain);
+ now, checknta, &secure_domain);
if (result != ISC_R_SUCCESS)
return (result);
if (!secure_domain && res->view->dlv != NULL) {
- valoptions = DNS_VALIDATOR_DLV;
+ valoptions |= DNS_VALIDATOR_DLV;
secure_domain = ISC_TRUE;
}
}
dns_fetchevent_t *event;
isc_uint32_t ttl;
unsigned int valoptions = 0;
+ isc_boolean_t checknta = ISC_TRUE;
FCTXTRACE("ncache_message");
/*
* Is DNSSEC validation required for this name?
*/
+ if ((fctx->options & DNS_FETCHOPT_NONTA) != 0) {
+ valoptions |= DNS_VALIDATOR_NONTA;
+ checknta = ISC_FALSE;
+ }
+
if (fctx->res->view->enablevalidation) {
result = issecuredomain(res->view, name, fctx->type,
- now, &secure_domain);
+ now, checknta, &secure_domain);
if (result != ISC_R_SUCCESS)
return (result);
if (!secure_domain && res->view->dlv != NULL) {
- valoptions = DNS_VALIDATOR_DLV;
+ valoptions |= DNS_VALIDATOR_DLV;
secure_domain = ISC_TRUE;
}
}
result = isc_task_create(taskmgr, 0, &task);
if (result != ISC_R_SUCCESS)
goto cleanup_primelock;
+ isc_task_setname(task, "resolver_task", NULL);
result = isc_timer_create(timermgr, isc_timertype_inactive, NULL, NULL,
task, spillattimer_countdown, res,
static const char *keystr2 = "BQEAAAABwuHz9Cem0BJ0JQTO7C/a3McR6hMaufljs1dfG/inaJpYv7vH XTrAOm/MeKp+/x6eT4QLru0KoZkvZJnqTI8JyaFTw2OM/ItBfh/hL2lm Cft2O7n3MfeqYtvjPnY7dWghYW4sVfH7VVEGm958o9nfi79532Qeklxh x8pXWdeAaRU=";
+static dns_view_t *view = NULL;
+
/*
* Test utilities. In general, these assume input parameters are valid
* (checking with ATF_REQUIRE_EQ, thus aborting if not) and unlikely run time
/* Common setup: create a keytable and ntatable to test with a few keys */
static void
create_tables() {
+ isc_result_t result;
dst_key_t *key = NULL;
isc_stdtime_t now;
+ result = dns_test_makeview("view", &view);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+
ATF_REQUIRE_EQ(dns_keytable_create(mctx, &keytable), ISC_R_SUCCESS);
- ATF_REQUIRE_EQ(dns_ntatable_create(mctx, &ntatable), ISC_R_SUCCESS);
+ ATF_REQUIRE_EQ(dns_ntatable_create(view, taskmgr, timermgr,
+ &ntatable), ISC_R_SUCCESS);
/* Add a normal key */
create_key(257, 3, 5, "example.com", keystr1, &key);
isc_stdtime_get(&now);
ATF_REQUIRE_EQ(dns_ntatable_add(ntatable,
str2name("insecure.example"),
- now + 3600),
+ ISC_FALSE, now, 3600),
ISC_R_SUCCESS);
}
dns_ntatable_detach(&ntatable);
if (keytable != NULL)
dns_keytable_detach(&keytable);
+
+ dns_view_detach(&view);
}
/*
UNUSED(tc);
- ATF_REQUIRE_EQ(dns_test_begin(NULL, ISC_FALSE), ISC_R_SUCCESS);
+ ATF_REQUIRE_EQ(dns_test_begin(NULL, ISC_TRUE), ISC_R_SUCCESS);
create_tables();
/*
ATF_TC_BODY(delete, tc) {
UNUSED(tc);
- ATF_REQUIRE_EQ(dns_test_begin(NULL, ISC_FALSE), ISC_R_SUCCESS);
+ ATF_REQUIRE_EQ(dns_test_begin(NULL, ISC_TRUE), ISC_R_SUCCESS);
create_tables();
/* dns_keytable_delete requires exact match */
UNUSED(tc);
- ATF_REQUIRE_EQ(dns_test_begin(NULL, ISC_FALSE), ISC_R_SUCCESS);
+ ATF_REQUIRE_EQ(dns_test_begin(NULL, ISC_TRUE), ISC_R_SUCCESS);
create_tables();
/* key name doesn't match */
UNUSED(tc);
- ATF_REQUIRE_EQ(dns_test_begin(NULL, ISC_FALSE), ISC_R_SUCCESS);
+ ATF_REQUIRE_EQ(dns_test_begin(NULL, ISC_TRUE), ISC_R_SUCCESS);
create_tables();
/*
"null.example", "sub.null.example", NULL};
UNUSED(tc);
- ATF_REQUIRE_EQ(dns_test_begin(NULL, ISC_FALSE), ISC_R_SUCCESS);
+ ATF_REQUIRE_EQ(dns_test_begin(NULL, ISC_TRUE), ISC_R_SUCCESS);
create_tables();
/*
ATF_TC_BODY(dump, tc) {
UNUSED(tc);
- ATF_REQUIRE_EQ(dns_test_begin(NULL, ISC_FALSE), ISC_R_SUCCESS);
+ ATF_REQUIRE_EQ(dns_test_begin(NULL, ISC_TRUE), ISC_R_SUCCESS);
create_tables();
/*
result = dns_test_makeview("view", &view);
ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+ result = isc_task_create(taskmgr, 0, &view->task);
+ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+
result = dns_view_initsecroots(view, mctx);
ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
result = dns_view_getsecroots(view, &keytable);
ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
- result = dns_view_initntatable(view, mctx);
+ result = dns_view_initntatable(view, taskmgr, timermgr);
ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
result = dns_view_getntatable(view, &ntatable);
ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
isc_stdtime_get(&now);
result = dns_ntatable_add(ntatable,
- str2name("insecure.example"), now + 1);
+ str2name("insecure.example"),
+ ISC_FALSE, now, 1);
ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
/* Should be secure */
result = dns_view_issecuredomain(view,
str2name("test.secure.example"),
- now, &issecure);
+ now, ISC_TRUE, &issecure);
ATF_CHECK_EQ(result, ISC_R_SUCCESS);
ATF_CHECK(issecure);
/* Should not be secure */
result = dns_view_issecuredomain(view,
str2name("test.insecure.example"),
- now, &issecure);
+ now, ISC_TRUE, &issecure);
ATF_CHECK_EQ(result, ISC_R_SUCCESS);
ATF_CHECK(!issecure);
/* As of now + 2, the NTA should be clear */
result = dns_view_issecuredomain(view,
str2name("test.insecure.example"),
- now + 2, &issecure);
+ now + 2, ISC_TRUE, &issecure);
ATF_CHECK_EQ(result, ISC_R_SUCCESS);
ATF_CHECK(issecure);
/* Now check deletion */
- result = dns_ntatable_add(ntatable,
- str2name("new.example"), now + 3600);
+ result = dns_ntatable_add(ntatable, str2name("new.example"),
+ ISC_FALSE, now, 3600);
ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
result = dns_view_issecuredomain(view, str2name("test.new.example"),
- now, &issecure);
+ now, ISC_TRUE, &issecure);
ATF_CHECK_EQ(result, ISC_R_SUCCESS);
ATF_CHECK(!issecure);
ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
result = dns_view_issecuredomain(view, str2name("test.new.example"),
- now, &issecure);
+ now, ISC_TRUE, &issecure);
ATF_CHECK_EQ(result, ISC_R_SUCCESS);
ATF_CHECK(issecure);
if ((val->options & DNS_VALIDATOR_NOCDFLAG) != 0)
fopts |= DNS_FETCHOPT_NOCDFLAG;
+ if ((val->options & DNS_VALIDATOR_NONTA) != 0)
+ fopts |= DNS_FETCHOPT_NONTA;
+
validator_logcreate(val, name, type, caller, "fetch");
return (dns_resolver_createfetch(val->view->resolver, name, type,
NULL, NULL, NULL, fopts,
return (DNS_R_NOVALIDSIG);
}
- if (dns_view_ntacovers(val->view, val->start, dlvname, val->view->dlv))
+ if (((val->options & DNS_VALIDATOR_NONTA) == 0) &&
+ dns_view_ntacovers(val->view, val->start, dlvname, val->view->dlv))
return (DNS_R_NTACOVERED);
while (dns_name_countlabels(dlvname) >=
view->provideixfr = ISC_TRUE;
view->maxcachettl = 7 * 24 * 3600;
view->maxncachettl = 3 * 3600;
+ view->nta_lifetime = 0;
+ view->nta_recheck = 0;
view->prefetch_eligible = 0;
view->prefetch_trigger = 0;
view->dstport = 53;
}
isc_result_t
-dns_view_initntatable(dns_view_t *view, isc_mem_t *mctx) {
+dns_view_initntatable(dns_view_t *view,
+ isc_taskmgr_t *taskmgr, isc_timermgr_t *timermgr)
+{
REQUIRE(DNS_VIEW_VALID(view));
if (view->ntatable_priv != NULL)
dns_ntatable_detach(&view->ntatable_priv);
- return (dns_ntatable_create(mctx, &view->ntatable_priv));
+ return (dns_ntatable_create(view, taskmgr, timermgr,
+ &view->ntatable_priv));
}
isc_result_t
isc_result_t
dns_view_issecuredomain(dns_view_t *view, dns_name_t *name,
- isc_stdtime_t now, isc_boolean_t *secure_domain)
+ isc_stdtime_t now, isc_boolean_t checknta,
+ isc_boolean_t *secure_domain)
{
isc_result_t result;
isc_boolean_t secure = ISC_FALSE;
if (result != ISC_R_SUCCESS)
return (result);
- if (secure && view->ntatable_priv != NULL &&
+ if (checknta && secure && view->ntatable_priv != NULL &&
dns_ntatable_covered(view->ntatable_priv, now, name, anchor))
secure = ISC_FALSE;
dns_nsec_nseconly
dns_nsec_setbit
dns_nsec_typepresent
-dns_nta_create
dns_ntatable_add
dns_ntatable_attach
dns_ntatable_covered
static cfg_type_t cfg_type_sockaddr4wild;
static cfg_type_t cfg_type_sockaddr6wild;
static cfg_type_t cfg_type_statschannels;
+static cfg_type_t cfg_type_ttlval;
static cfg_type_t cfg_type_view;
static cfg_type_t cfg_type_viewopts;
static cfg_type_t cfg_type_zone;
{ "max-udp-size", &cfg_type_uint32, 0 },
{ "min-roots", &cfg_type_uint32, CFG_CLAUSEFLAG_NOTIMP },
{ "minimal-responses", &cfg_type_boolean, 0 },
+ { "nta-recheck", &cfg_type_ttlval, 0 },
+ { "nta-lifetime", &cfg_type_ttlval, 0 },
{ "prefetch", &cfg_type_prefetch, 0 },
{ "preferred-glue", &cfg_type_astring, 0 },
{ "no-case-compress", &cfg_type_bracketed_aml, 0 },
};
static isc_result_t
-parse_maxttlval(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
+parse_ttlval(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
isc_result_t result;
cfg_obj_t *obj = NULL;
isc_uint32_t ttl;
}
/*%
- * A size value (number + optional unit).
+ * A TTL value (number + optional unit).
*/
-static cfg_type_t cfg_type_maxttlval = {
- "maxttlval", parse_maxttlval, cfg_print_uint64, cfg_doc_terminal,
- &cfg_rep_uint64, NULL };
+static cfg_type_t cfg_type_ttlval = {
+ "ttlval", parse_ttlval, cfg_print_uint64, cfg_doc_terminal,
+ &cfg_rep_uint64, NULL
+};
static isc_result_t
parse_maxttl(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
- return (parse_enum_or_other(pctx, type, &cfg_type_maxttlval, ret));
+ return (parse_enum_or_other(pctx, type, &cfg_type_ttlval, ret));
}
/*%