a.root-servers.nil. A 10.53.0.1
example NS ns2.example.
ns2.example. A 10.53.0.2
+initially-unavailable. NS ns2.initially-unavailable.
+ns2.initially-unavailable. A 10.53.0.2
--- /dev/null
+; Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+;
+; This Source Code Form is subject to the terms of the Mozilla Public
+; License, v. 2.0. If a copy of the MPL was not distributed with this
+; file, You can obtain one at http://mozilla.org/MPL/2.0/.
+;
+; See the COPYRIGHT file distributed with this work for additional
+; information regarding copyright ownership.
+
+$TTL 3600
+@ SOA a.root-servers.nil. hostmaster 2000010100 3600 1200 604800 3600
+@ NS ns2
+ns2 A 10.53.0.2
+foo CNAME foo.example.
file "sub.example.db.in";
};
+zone "initially-unavailable" {
+ type master;
+ file "initially-unavailable.db.signed";
+ allow-transfer { 10.53.0.254; };
+};
+
zone "verify-axfr" {
type master;
file "verify-axfr.db.signed";
keys_to_trust=""
-for zonename in example; do
+for zonename in example initially-unavailable; do
zone=$zonename
infile=$zonename.db.in
zonefile=$zonename.db
$SIGNER -P -o $zone $zonefile > /dev/null
done
+# Only add the key for "initially-unavailable" to the list of keys trusted by
+# ns3. "example" is expected to be validated using a chain of trust starting in
+# the "root" zone on ns1.
+keys_to_trust="$keys_to_trust $keyname1"
+
ORIGINAL_SERIAL=`awk '$2 == "SOA" {print $5}' verify.db.in`
UPDATED_SERIAL_BAD=`expr ${ORIGINAL_SERIAL} + 1`
UPDATED_SERIAL_GOOD=`expr ${ORIGINAL_SERIAL} + 2`
file "root.db.mirror";
};
+zone "initially-unavailable" {
+ type slave;
+ masters { 10.53.0.2; };
+ mirror yes;
+ file "initially-unavailable.db.mirror";
+};
+
zone "verify-axfr" {
type slave;
masters { 10.53.0.2; };
RNDCCMD="$RNDC -c $SYSTEMTESTTOP/common/rndc.conf -p ${CONTROLPORT} -s"
# Wait until the transfer of the given zone to ns3 either completes successfully
-# or is aborted by a verification failure.
+# or is aborted by a verification failure or a REFUSED response from the master.
wait_for_transfer() {
zone=$1
for i in 1 2 3 4 5 6 7 8 9 10; do
- nextpartpeek ns3/named.run | egrep "'$zone/IN'.*Transfer status: (success|verify failure)" > /dev/null && return
+ nextpartpeek ns3/named.run | egrep "'$zone/IN'.*Transfer status: (success|verify failure|REFUSED)" > /dev/null && return
sleep 1
done
echo_i "exceeded time limit waiting for proof of '$zone' being transferred to appear in ns3/named.run"
if [ $ret != 0 ]; then echo_i "failed"; fi
status=`expr $status + $ret`
+n=`expr $n + 1`
+echo_i "checking that resolution succeeds with unavailable mirror zone data ($n)"
+ret=0
+wait_for_transfer initially-unavailable
+# Query for a record in a zone that is set up to be mirrored, but
+# untransferrable from the configured master. Resolution should still succeed.
+$DIG $DIGOPTS @10.53.0.3 foo.initially-unavailable. A > dig.out.ns3.test$n.1 2>&1 || ret=1
+# Check response code and flags in the answer.
+grep "NOERROR" dig.out.ns3.test$n.1 > /dev/null || ret=1
+grep "flags:.* ad" dig.out.ns3.test$n.1 > /dev/null || ret=1
+# Sanity check: the authoritative server should have been queried.
+nextpart ns2/named.run | grep "query 'foo.initially-unavailable/A/IN'" > /dev/null || ret=1
+# Reconfigure ns2 so that the zone can be mirrored on ns3.
+sed "s/10.53.0.254/10.53.0.3/;" ns2/named.conf > ns2/named.conf.modified
+mv ns2/named.conf.modified ns2/named.conf
+$RNDCCMD 10.53.0.2 reconfig > /dev/null 2>&1
+# Flush the cache on ns3 and retransfer the mirror zone.
+$RNDCCMD 10.53.0.3 flush > /dev/null 2>&1
+nextpart ns3/named.run > /dev/null
+$RNDCCMD 10.53.0.3 retransfer initially-unavailable > /dev/null 2>&1
+wait_for_transfer initially-unavailable
+# Query for the same record again. Resolution should still succeed.
+$DIG $DIGOPTS @10.53.0.3 foo.initially-unavailable. A > dig.out.ns3.test$n.2 2>&1 || ret=1
+# Check response code and flags in the answer.
+grep "NOERROR" dig.out.ns3.test$n.2 > /dev/null || ret=1
+grep "flags:.* ad" dig.out.ns3.test$n.2 > /dev/null || ret=1
+# Ensure the authoritative server was not queried.
+nextpart ns2/named.run | grep "query 'foo.initially-unavailable/A/IN'" > /dev/null && ret=1
+if [ $ret != 0 ]; then echo_i "failed"; fi
+status=`expr $status + $ret`
+
+n=`expr $n + 1`
+echo_i "checking that resolution succeeds with expired mirror zone data ($n)"
+ret=0
+# Reconfigure ns2 so that the zone from the previous test can no longer be
+# mirrored on ns3.
+sed "s/10.53.0.3/10.53.0.254/;" ns2/named.conf > ns2/named.conf.modified
+mv ns2/named.conf.modified ns2/named.conf
+$RNDCCMD 10.53.0.2 reconfig > /dev/null 2>&1
+# Stop ns3, update the timestamp of the zone file to one far in the past, then
+# restart ns3.
+$PERL $SYSTEMTESTTOP/stop.pl --use-rndc --port ${CONTROLPORT} . ns3
+touch -t 200001010000 ns3/initially-unavailable.db.mirror
+nextpart ns3/named.run > /dev/null
+$PERL $SYSTEMTESTTOP/start.pl --noclean --restart --port ${PORT} . ns3
+# Ensure named attempts to retransfer the zone due to its expiry.
+wait_for_transfer initially-unavailable
+nextpart ns3/named.run | grep "initially-unavailable.*expired" > /dev/null || ret=1
+# Query for a record in the expired zone. Resolution should still succeed.
+$DIG $DIGOPTS @10.53.0.3 foo.initially-unavailable. A > dig.out.ns3.test$n 2>&1 || ret=1
+# Check response code and flags in the answer.
+grep "NOERROR" dig.out.ns3.test$n > /dev/null || ret=1
+grep "flags:.* ad" dig.out.ns3.test$n > /dev/null || ret=1
+# Sanity check: the authoritative server should have been queried.
+nextpart ns2/named.run | grep "query 'foo.initially-unavailable/A/IN'" > /dev/null || ret=1
+if [ $ret != 0 ]; then echo_i "failed"; fi
+status=`expr $status + $ret`
+
echo_i "exit status: $status"
[ $status -eq 0 ] || exit 1
* otherwise NULL.
*/
+isc_boolean_t
+dns_zone_isloaded(const dns_zone_t *zone);
+/*%<
+ * Return ISC_TRUE if 'zone' was loaded and has not expired yet, return
+ * ISC_FALSE otherwise.
+ */
+
isc_boolean_t
dns_zone_ismirror(const dns_zone_t *zone);
/*%<
#include <dns/types.h>
#define DNS_ZTFIND_NOEXACT 0x01
+#define DNS_ZTFIND_MIRROR 0x02
ISC_LANG_BEGINDECLS
zone = NULL;
LOCK(&view->lock);
if (view->zonetable != NULL)
- result = dns_zt_find(view->zonetable, name, 0, NULL, &zone);
+ result = dns_zt_find(view->zonetable, name, DNS_ZTFIND_MIRROR,
+ NULL, &zone);
else
result = ISC_R_NOTFOUND;
UNLOCK(&view->lock);
dns_name_t *zfname;
dns_rdataset_t zrdataset, zsigrdataset;
dns_fixedname_t zfixedname;
- unsigned int ztoptions = 0;
+ unsigned int ztoptions = DNS_ZTFIND_MIRROR;
REQUIRE(DNS_VIEW_VALID(view));
REQUIRE(view->frozen);
return (zone->gluecachestats);
}
+isc_boolean_t
+dns_zone_isloaded(const dns_zone_t *zone) {
+ REQUIRE(DNS_ZONE_VALID(zone));
+
+ return (DNS_ZONE_FLAG(zone, DNS_ZONEFLG_LOADED));
+}
+
isc_boolean_t
dns_zone_ismirror(const dns_zone_t *zone) {
REQUIRE(DNS_ZONE_VALID(zone));
result = dns_rbt_findname(zt->table, name, rbtoptions, foundname,
(void **) (void*)&dummy);
- if (result == ISC_R_SUCCESS || result == DNS_R_PARTIALMATCH)
- dns_zone_attach(dummy, zonep);
+ if (result == ISC_R_SUCCESS || result == DNS_R_PARTIALMATCH) {
+ /*
+ * If DNS_ZTFIND_MIRROR is set and the zone which was
+ * determined to be the deepest match for the supplied name is
+ * a mirror zone which is expired or not yet loaded, treat it
+ * as non-existent. This will trigger a fallback to recursion
+ * instead of returning a SERVFAIL.
+ */
+ if ((options & DNS_ZTFIND_MIRROR) != 0 &&
+ dns_zone_ismirror(dummy) && !dns_zone_isloaded(dummy))
+ {
+ result = ISC_R_NOTFOUND;
+ } else {
+ dns_zone_attach(dummy, zonep);
+ }
+ }
RWUNLOCK(&zt->rwlock, isc_rwlocktype_read);
/*%
* Find a zone database to answer the query.
*/
- ztoptions = ((options & DNS_GETDB_NOEXACT) != 0) ?
- DNS_ZTFIND_NOEXACT : 0;
+ ztoptions = DNS_ZTFIND_MIRROR;
+ if ((options & DNS_GETDB_NOEXACT) != 0) {
+ ztoptions |= DNS_ZTFIND_NOEXACT;
+ }
result = dns_zt_find(client->view->zonetable, name, ztoptions, NULL,
&zone);
./bin/tests/system/mirror/ns1/root.db.in ZONE 2018
./bin/tests/system/mirror/ns1/sign.sh SH 2018
./bin/tests/system/mirror/ns2/example.db.in ZONE 2018
+./bin/tests/system/mirror/ns2/initially-unavailable.db.in ZONE 2018
./bin/tests/system/mirror/ns2/named.conf.in CONF-C 2018
./bin/tests/system/mirror/ns2/sign.sh SH 2018
./bin/tests/system/mirror/ns2/sub.example.db.in ZONE 2018