If transferring or loading a mirror zone fails, resolution should still succeed by means of falling back to regular recursive queries. Currently, though, if a slave zone is present in the zone table and not loaded, a SERVFAIL response is generated. Thus, mirror zones need special handling in this regard. Add a new dns_zt_find() flag, DNS_ZTFIND_MIRROR, and set it every time a domain name is looked up rather than a zone itself. Handle that flag in dns_zt_find() in such a way that a mirror zone which is expired or not yet loaded is ignored when looking up domain names, but still possible to find when the caller wants to know whether the zone is configured. This causes a fallback to recursion when mirror zone data is unavailable without making unloaded mirror zones invisible to code checking a zone's existence.
334 lines
15 KiB
Bash
334 lines
15 KiB
Bash
#!/bin/sh
|
|
#
|
|
# 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.
|
|
|
|
SYSTEMTESTTOP=..
|
|
. $SYSTEMTESTTOP/conf.sh
|
|
|
|
DIGOPTS="-p ${PORT} +dnssec +time=1 +tries=1 +multi"
|
|
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 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|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"
|
|
ret=1
|
|
}
|
|
|
|
# Wait until loading the given zone on the given server either completes
|
|
# successfully for the specified serial number or fails.
|
|
wait_for_load() {
|
|
zone=$1
|
|
serial=$2
|
|
log=$3
|
|
for i in 1 2 3 4 5 6 7 8 9 10; do
|
|
nextpartpeek $log | egrep "$zone.*(loaded serial $serial|unable to load)" > /dev/null && return
|
|
sleep 1
|
|
done
|
|
echo_i "exceeded time limit waiting for proof of '$zone' being loaded to appear in $log"
|
|
ret=1
|
|
}
|
|
|
|
# Trigger a reload of ns2 and wait until loading the given zone completes.
|
|
reload_zone() {
|
|
zone=$1
|
|
serial=$2
|
|
$RNDCCMD 10.53.0.2 reload > /dev/null 2>&1
|
|
wait_for_load $zone $serial ns2/named.run
|
|
}
|
|
|
|
status=0
|
|
n=0
|
|
|
|
ORIGINAL_SERIAL=`awk '$2 == "SOA" {print $5}' ns2/verify.db.in`
|
|
UPDATED_SERIAL_BAD=`expr ${ORIGINAL_SERIAL} + 1`
|
|
UPDATED_SERIAL_GOOD=`expr ${ORIGINAL_SERIAL} + 2`
|
|
|
|
n=`expr $n + 1`
|
|
echo_i "checking that an unsigned mirror zone is rejected ($n)"
|
|
ret=0
|
|
wait_for_transfer verify-unsigned
|
|
$DIG $DIGOPTS @10.53.0.3 +norec verify-unsigned SOA > dig.out.ns3.test$n 2>&1 || ret=1
|
|
grep "${UPDATED_SERIAL_BAD}.*; serial" dig.out.ns3.test$n > /dev/null && ret=1
|
|
nextpart ns3/named.run | grep "verify-unsigned.*Zone contains no DNSSEC keys" > /dev/null || ret=1
|
|
if [ $ret != 0 ]; then echo_i "failed"; fi
|
|
status=`expr $status + $ret`
|
|
|
|
n=`expr $n + 1`
|
|
echo_i "checking that a mirror zone signed using an untrusted key is rejected ($n)"
|
|
ret=0
|
|
nextpartreset ns3/named.run
|
|
wait_for_transfer verify-untrusted
|
|
$DIG $DIGOPTS @10.53.0.3 +norec verify-untrusted SOA > dig.out.ns3.test$n 2>&1 || ret=1
|
|
grep "${UPDATED_SERIAL_BAD}.*; serial" dig.out.ns3.test$n > /dev/null && ret=1
|
|
nextpart ns3/named.run | grep "verify-untrusted.*No trusted KSK DNSKEY found" > /dev/null || ret=1
|
|
if [ $ret != 0 ]; then echo_i "failed"; fi
|
|
status=`expr $status + $ret`
|
|
|
|
n=`expr $n + 1`
|
|
echo_i "checking that an AXFR of an incorrectly signed mirror zone is rejected ($n)"
|
|
ret=0
|
|
nextpartreset ns3/named.run
|
|
wait_for_transfer verify-axfr
|
|
$DIG $DIGOPTS @10.53.0.3 +norec verify-axfr SOA > dig.out.ns3.test$n 2>&1 || ret=1
|
|
grep "${UPDATED_SERIAL_BAD}.*; serial" dig.out.ns3.test$n > /dev/null && ret=1
|
|
nextpart ns3/named.run | grep "No correct RSASHA256 signature for verify-axfr SOA" > /dev/null || ret=1
|
|
if [ $ret != 0 ]; then echo_i "failed"; fi
|
|
status=`expr $status + $ret`
|
|
|
|
n=`expr $n + 1`
|
|
echo_i "checking that an AXFR of an updated, correctly signed mirror zone is accepted ($n)"
|
|
ret=0
|
|
nextpart ns3/named.run > /dev/null
|
|
cat ns2/verify-axfr.db.good.signed > ns2/verify-axfr.db.signed
|
|
reload_zone verify-axfr ${UPDATED_SERIAL_GOOD}
|
|
$RNDCCMD 10.53.0.3 retransfer verify-axfr > /dev/null 2>&1
|
|
wait_for_transfer verify-axfr
|
|
$DIG $DIGOPTS @10.53.0.3 +norec verify-axfr SOA > dig.out.ns3.test$n 2>&1 || ret=1
|
|
grep "${UPDATED_SERIAL_GOOD}.*; serial" dig.out.ns3.test$n > /dev/null || ret=1
|
|
if [ $ret != 0 ]; then echo_i "failed"; fi
|
|
status=`expr $status + $ret`
|
|
|
|
n=`expr $n + 1`
|
|
echo_i "checking that an IXFR of an incorrectly signed mirror zone is rejected ($n)"
|
|
nextpartreset ns3/named.run
|
|
ret=0
|
|
wait_for_transfer verify-ixfr
|
|
nextpart ns3/named.run > /dev/null
|
|
# Make a copy of the original zone file for reuse in journal tests below.
|
|
cp ns2/verify-ixfr.db.signed ns3/verify-journal.db.mirror
|
|
# Wait 1 second so that the zone file timestamp changes and the subsequent
|
|
# invocation of "rndc reload" triggers a zone reload.
|
|
sleep 1
|
|
cat ns2/verify-ixfr.db.bad.signed > ns2/verify-ixfr.db.signed
|
|
reload_zone verify-ixfr ${UPDATED_SERIAL_BAD}
|
|
# Make a copy of the bad zone journal for reuse in journal tests below.
|
|
cp ns2/verify-ixfr.db.signed.jnl ns3/verify-journal.db.bad.mirror.jnl
|
|
# Trigger IXFR.
|
|
$RNDCCMD 10.53.0.3 refresh verify-ixfr > /dev/null 2>&1
|
|
wait_for_transfer verify-ixfr
|
|
# Ensure the transfer was incremental as expected.
|
|
if [ `nextpartpeek ns3/named.run | grep "verify-ixfr.*got incremental response" | wc -l` -eq 0 ]; then
|
|
echo_i "failed: did not get an incremental response"
|
|
ret=1
|
|
fi
|
|
# Ensure the new, bad version of the zone was not accepted.
|
|
$DIG $DIGOPTS @10.53.0.3 +norec verify-ixfr SOA > dig.out.ns3.test$n 2>&1 || ret=1
|
|
grep "${UPDATED_SERIAL_BAD}.*; serial" dig.out.ns3.test$n > /dev/null && ret=1
|
|
nextpart ns3/named.run | grep "No correct RSASHA256 signature for verify-ixfr SOA" > /dev/null || ret=1
|
|
if [ $ret != 0 ]; then echo_i "failed"; fi
|
|
status=`expr $status + $ret`
|
|
|
|
n=`expr $n + 1`
|
|
echo_i "checking that an IXFR of an updated, correctly signed mirror zone is accepted after AXFR failover ($n)"
|
|
ret=0
|
|
nextpart ns3/named.run > /dev/null
|
|
# Wait 1 second so that the zone file timestamp changes and the subsequent
|
|
# invocation of "rndc reload" triggers a zone reload.
|
|
sleep 1
|
|
cat ns2/verify-ixfr.db.good.signed > ns2/verify-ixfr.db.signed
|
|
reload_zone verify-ixfr ${UPDATED_SERIAL_GOOD}
|
|
# Make a copy of the good zone journal for reuse in journal tests below.
|
|
cp ns2/verify-ixfr.db.signed.jnl ns3/verify-journal.db.good.mirror.jnl
|
|
# Trigger IXFR.
|
|
$RNDCCMD 10.53.0.3 refresh verify-ixfr > /dev/null 2>&1
|
|
wait_for_transfer verify-ixfr
|
|
# Ensure the new, good version of the zone was accepted.
|
|
$DIG $DIGOPTS @10.53.0.3 +norec verify-ixfr SOA > dig.out.ns3.test$n 2>&1 || ret=1
|
|
grep "${UPDATED_SERIAL_GOOD}.*; serial" dig.out.ns3.test$n > /dev/null || ret=1
|
|
if [ $ret != 0 ]; then echo_i "failed"; fi
|
|
status=`expr $status + $ret`
|
|
|
|
n=`expr $n + 1`
|
|
echo_i "checking that loading an incorrectly signed mirror zone from disk fails ($n)"
|
|
ret=0
|
|
nextpartreset ns3/named.run
|
|
wait_for_load verify-load ${UPDATED_SERIAL_BAD} ns3/named.run
|
|
$DIG $DIGOPTS @10.53.0.3 +norec verify-load SOA > dig.out.ns3.test$n 2>&1 || ret=1
|
|
grep "${UPDATED_SERIAL_BAD}.*; serial" dig.out.ns3.test$n > /dev/null && ret=1
|
|
nextpart ns3/named.run | grep "No correct RSASHA256 signature for verify-load SOA" > /dev/null || ret=1
|
|
if [ $ret != 0 ]; then echo_i "failed"; fi
|
|
status=`expr $status + $ret`
|
|
|
|
n=`expr $n + 1`
|
|
echo_i "checking that loading a correctly signed mirror zone from disk succeeds ($n)"
|
|
ret=0
|
|
$PERL $SYSTEMTESTTOP/stop.pl --use-rndc --port ${CONTROLPORT} . ns3
|
|
cat ns2/verify-load.db.good.signed > ns3/verify-load.db.mirror
|
|
nextpart ns3/named.run > /dev/null
|
|
$PERL $SYSTEMTESTTOP/start.pl --noclean --restart --port ${PORT} . ns3
|
|
wait_for_load verify-load ${UPDATED_SERIAL_GOOD} ns3/named.run
|
|
$DIG $DIGOPTS @10.53.0.3 +norec verify-load SOA > dig.out.ns3.test$n 2>&1 || ret=1
|
|
grep "${UPDATED_SERIAL_GOOD}.*; serial" dig.out.ns3.test$n > /dev/null || ret=1
|
|
if [ $ret != 0 ]; then echo_i "failed"; fi
|
|
status=`expr $status + $ret`
|
|
|
|
n=`expr $n + 1`
|
|
echo_i "checking that loading a journal for an incorrectly signed mirror zone fails ($n)"
|
|
ret=0
|
|
$PERL $SYSTEMTESTTOP/stop.pl --use-rndc --port ${CONTROLPORT} . ns3
|
|
cp ns3/verify-journal.db.mirror ns3/verify-ixfr.db.mirror
|
|
cp ns3/verify-journal.db.bad.mirror.jnl ns3/verify-ixfr.db.mirror.jnl
|
|
nextpart ns3/named.run > /dev/null
|
|
$PERL $SYSTEMTESTTOP/start.pl --noclean --restart --port ${PORT} . ns3
|
|
wait_for_load verify-ixfr ${UPDATED_SERIAL_BAD} ns3/named.run
|
|
$DIG $DIGOPTS @10.53.0.3 +norec verify-ixfr SOA > dig.out.ns3.test$n 2>&1 || ret=1
|
|
grep "${UPDATED_SERIAL_BAD}.*; serial" dig.out.ns3.test$n > /dev/null && ret=1
|
|
nextpart ns3/named.run | grep "No correct RSASHA256 signature for verify-ixfr SOA" > /dev/null || ret=1
|
|
if [ $ret != 0 ]; then echo_i "failed"; fi
|
|
status=`expr $status + $ret`
|
|
|
|
n=`expr $n + 1`
|
|
echo_i "checking that loading a journal for a correctly signed mirror zone succeeds ($n)"
|
|
ret=0
|
|
$PERL $SYSTEMTESTTOP/stop.pl --use-rndc --port ${CONTROLPORT} . ns3
|
|
cp ns3/verify-journal.db.mirror ns3/verify-ixfr.db.mirror
|
|
cp ns3/verify-journal.db.good.mirror.jnl ns3/verify-ixfr.db.mirror.jnl
|
|
nextpart ns3/named.run > /dev/null
|
|
$PERL $SYSTEMTESTTOP/start.pl --noclean --restart --port ${PORT} . ns3
|
|
wait_for_load verify-ixfr ${UPDATED_SERIAL_GOOD} ns3/named.run
|
|
$DIG $DIGOPTS @10.53.0.3 +norec verify-ixfr SOA > dig.out.ns3.test$n 2>&1 || ret=1
|
|
grep "${UPDATED_SERIAL_GOOD}.*; serial" dig.out.ns3.test$n > /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 involving a mirror zone works as expected ($n)"
|
|
ret=0
|
|
$DIG $DIGOPTS @10.53.0.3 foo.example 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
|
|
# Ensure ns1 was not queried.
|
|
grep "query 'foo.example/A/IN'" ns1/named.run > /dev/null && ret=1
|
|
if [ $ret != 0 ]; then echo_i "failed"; fi
|
|
status=`expr $status + $ret`
|
|
|
|
n=`expr $n + 1`
|
|
echo_i "checking that non-recursive queries for names below mirror zone get responded from cache ($n)"
|
|
ret=0
|
|
# Issue a non-recursive query for an RRset which is expected to be in cache.
|
|
$DIG $DIGOPTS @10.53.0.3 +norec foo.example. 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
|
|
# Ensure the response is not a delegation.
|
|
grep "ANSWER: 0" dig.out.ns3.test$n > /dev/null && ret=1
|
|
grep "foo.example.*IN.*A.*127.0.0.1" dig.out.ns3.test$n > /dev/null || ret=1
|
|
if [ $ret != 0 ]; then echo_i "failed"; fi
|
|
status=`expr $status + $ret`
|
|
|
|
n=`expr $n + 1`
|
|
echo_i "checking that delegations from cache which improve mirror zone delegations are properly handled ($n)"
|
|
ret=0
|
|
# First, issue a recursive query in order to cache an RRset which is not within
|
|
# the mirror zone's bailiwick.
|
|
$DIG $DIGOPTS @10.53.0.3 sub.example. NS > dig.out.ns3.test$n.1 2>&1 || ret=1
|
|
# Ensure the child-side NS RRset is returned.
|
|
grep "NOERROR" dig.out.ns3.test$n.1 > /dev/null || ret=1
|
|
grep "ANSWER: 1" dig.out.ns3.test$n.1 > /dev/null || ret=1
|
|
grep "sub.example.*IN.*NS" dig.out.ns3.test$n.1 > /dev/null || ret=1
|
|
# Issue a non-recursive query for something below the cached zone cut.
|
|
$DIG $DIGOPTS @10.53.0.3 +norec foo.sub.example. A > dig.out.ns3.test$n.2 2>&1 || ret=1
|
|
# Ensure the cached NS RRset is returned in a delegation.
|
|
grep "NOERROR" dig.out.ns3.test$n.2 > /dev/null || ret=1
|
|
grep "ANSWER: 0" dig.out.ns3.test$n.2 > /dev/null || ret=1
|
|
grep "sub.example.*IN.*NS" dig.out.ns3.test$n.2 > /dev/null || ret=1
|
|
if [ $ret != 0 ]; then echo_i "failed"; fi
|
|
status=`expr $status + $ret`
|
|
|
|
n=`expr $n + 1`
|
|
echo_i "checking flags set in a DNSKEY response sourced from a mirror zone ($n)"
|
|
ret=0
|
|
$DIG $DIGOPTS @10.53.0.3 . DNSKEY > 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:.* aa" dig.out.ns3.test$n > /dev/null && ret=1
|
|
grep "flags:.* ad" dig.out.ns3.test$n > /dev/null || ret=1
|
|
if [ $ret != 0 ]; then echo_i "failed"; fi
|
|
status=`expr $status + $ret`
|
|
|
|
n=`expr $n + 1`
|
|
echo_i "checking flags set in a SOA response sourced from a mirror zone ($n)"
|
|
ret=0
|
|
$DIG $DIGOPTS @10.53.0.3 . SOA > 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:.* aa" dig.out.ns3.test$n > /dev/null && ret=1
|
|
grep "flags:.* ad" dig.out.ns3.test$n > /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 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
|