1#!/bin/sh 2 3# Copyright (C) Internet Systems Consortium, Inc. ("ISC") 4# 5# SPDX-License-Identifier: MPL-2.0 6# 7# This Source Code Form is subject to the terms of the Mozilla Public 8# License, v. 2.0. If a copy of the MPL was not distributed with this 9# file, you can obtain one at https://mozilla.org/MPL/2.0/. 10# 11# See the COPYRIGHT file distributed with this work for additional 12# information regarding copyright ownership. 13 14# WARNING: The test labelled "testing request-ixfr option in view vs zone" 15# is fragile because it depends upon counting instances of records 16# in the log file - need a better approach <sdm> - until then, 17# if you add any tests above that point, you will break the test. 18 19set -e 20 21. ../conf.sh 22 23wait_for_serial() ( 24 $DIG $DIGOPTS "@$1" "$2" SOA >"$4" 25 serial=$(awk '$4 == "SOA" { print $7 }' "$4") 26 [ "$3" -eq "${serial:--1}" ] 27) 28 29status=0 30n=0 31 32DIGOPTS="+tcp +noadd +nosea +nostat +noquest +nocomm +nocmd -p ${PORT}" 33RNDCCMD="$RNDC -p ${CONTROLPORT} -c ../_common/rndc.conf -s" 34 35sendcmd() { 36 send 10.53.0.2 "${EXTRAPORT1}" 37} 38 39n=$((n + 1)) 40echo_i "testing initial AXFR ($n)" 41ret=0 42 43sendcmd <<EOF 44/SOA/ 45nil. 300 SOA ns.nil. root.nil. 1 300 300 604800 300 46/AXFR/ 47nil. 300 SOA ns.nil. root.nil. 1 300 300 604800 300 48/AXFR/ 49nil. 300 NS ns.nil. 50nil. 300 TXT "initial AXFR" 51a.nil. 60 A 10.0.0.61 52b.nil. 60 A 10.0.0.62 53/AXFR/ 54nil. 300 SOA ns.nil. root.nil. 1 300 300 604800 300 55EOF 56 57sleep 1 58 59# Initially, ns1 is not authoritative for anything (see setup.sh). 60# Now that ans is up and running with the right data, we make it 61# a secondary for nil. 62 63cat <<EOF >>ns1/named.conf 64zone "nil" { 65 type secondary; 66 file "myftp.db"; 67 primaries { 10.53.0.2; }; 68}; 69EOF 70 71rndc_reload ns1 10.53.0.1 72 73retry_quiet 10 wait_for_serial 10.53.0.1 nil. 1 dig.out.test$n || ret=1 74 75$DIG $DIGOPTS @10.53.0.1 nil. TXT | grep 'initial AXFR' >/dev/null || ret=1 76if [ $ret != 0 ]; then echo_i "failed"; fi 77status=$((status + ret)) 78 79n=$((n + 1)) 80echo_i "testing successful IXFR ($n)" 81ret=0 82 83# We change the IP address of a.nil., and the TXT record at the apex. 84# Then we do a SOA-only update. 85 86sendcmd <<EOF 87/SOA/ 88nil. 300 SOA ns.nil. root.nil. 3 300 300 604800 300 89/IXFR/ 90nil. 300 SOA ns.nil. root.nil. 3 300 300 604800 300 91nil. 300 SOA ns.nil. root.nil. 1 300 300 604800 300 92a.nil. 60 A 10.0.0.61 93nil. 300 TXT "initial AXFR" 94nil. 300 SOA ns.nil. root.nil. 2 300 300 604800 300 95nil. 300 TXT "successful IXFR" 96a.nil. 60 A 10.0.1.61 97nil. 300 SOA ns.nil. root.nil. 2 300 300 604800 300 98nil. 300 SOA ns.nil. root.nil. 3 300 300 604800 300 99nil. 300 SOA ns.nil. root.nil. 3 300 300 604800 300 100EOF 101 102sleep 1 103 104$RNDCCMD 10.53.0.1 refresh nil | sed 's/^/ns1 /' | cat_i 105 106sleep 2 107 108$DIG $DIGOPTS @10.53.0.1 nil. TXT | grep 'successful IXFR' >/dev/null || ret=1 109if [ $ret != 0 ]; then echo_i "failed"; fi 110status=$((status + ret)) 111 112n=$((n + 1)) 113echo_i "testing AXFR fallback after IXFR failure (not exact error) ($n)" 114ret=0 115 116# Provide a broken IXFR response and a working fallback AXFR response 117 118sendcmd <<EOF 119/SOA/ 120nil. 300 SOA ns.nil. root.nil. 4 300 300 604800 300 121/IXFR/ 122nil. 300 SOA ns.nil. root.nil. 4 300 300 604800 300 123nil. 300 SOA ns.nil. root.nil. 3 300 300 604800 300 124nil. 300 TXT "delete-nonexistent-txt-record" 125nil. 300 SOA ns.nil. root.nil. 4 300 300 604800 300 126nil. 300 TXT "this-txt-record-would-be-added" 127nil. 300 SOA ns.nil. root.nil. 4 300 300 604800 300 128/AXFR/ 129nil. 300 SOA ns.nil. root.nil. 3 300 300 604800 300 130/AXFR/ 131nil. 300 NS ns.nil. 132nil. 300 TXT "fallback AXFR" 133/AXFR/ 134nil. 300 SOA ns.nil. root.nil. 3 300 300 604800 300 135EOF 136 137sleep 1 138 139$RNDCCMD 10.53.0.1 refresh nil | sed 's/^/ns1 /' | cat_i 140 141sleep 2 142 143$DIG $DIGOPTS @10.53.0.1 nil. TXT | grep 'fallback AXFR' >/dev/null || ret=1 144if [ $ret != 0 ]; then echo_i "failed"; fi 145status=$((status + ret)) 146 147n=$((n + 1)) 148echo_i "testing AXFR fallback after IXFR failure (bad SOA owner) ($n)" 149ret=0 150 151# Prepare for checking the logs later on. 152nextpart ns1/named.run >/dev/null 153 154# Provide a broken IXFR response and a working fallback AXFR response. 155sendcmd <<EOF 156/SOA/ 157nil. 300 SOA ns.nil. root.nil. 4 300 300 604800 300 158/IXFR/ 159nil. 300 SOA ns.nil. root.nil. 4 300 300 604800 300 160nil. 300 SOA ns.nil. root.nil. 3 300 300 604800 300 161bad-owner. 300 SOA ns.nil. root.nil. 4 300 300 604800 300 162test.nil. 300 TXT "serial 4, malformed IXFR" 163nil. 300 SOA ns.nil. root.nil. 4 300 300 604800 300 164/AXFR/ 165nil. 300 SOA ns.nil. root.nil. 4 300 300 604800 300 166/AXFR/ 167nil. 300 NS ns.nil. 168test.nil. 300 TXT "serial 4, fallback AXFR" 169/AXFR/ 170nil. 300 SOA ns.nil. root.nil. 4 300 300 604800 300 171EOF 172$RNDCCMD 10.53.0.1 refresh nil | sed 's/^/ns1 /' | cat_i 173 174# A broken server would accept the malformed IXFR and apply its contents to the 175# zone. A fixed one would reject the IXFR and fall back to AXFR. Both IXFR and 176# AXFR above bring the nil. zone up to serial 4, but we cannot reliably query 177# for the SOA record to check whether the transfer was finished because a broken 178# server would send back SERVFAIL responses to SOA queries after accepting the 179# malformed IXFR. Instead, check transfer progress by querying for a TXT record 180# at test.nil. which is present in both IXFR and AXFR (with different contents). 181_wait_until_transfer_is_finished() { 182 $DIG $DIGOPTS +tries=1 +time=1 @10.53.0.1 test.nil. TXT >dig.out.test$n.1 \ 183 && grep -q -F "serial 4" dig.out.test$n.1 184} 185if ! retry_quiet 10 _wait_until_transfer_is_finished; then 186 echo_i "timed out waiting for version 4 of zone nil. to be transferred" 187 ret=1 188fi 189 190# At this point a broken server would be serving a zone with no SOA records. 191# Try crashing it by triggering a SOA refresh query. 192$RNDCCMD 10.53.0.1 refresh nil | sed 's/^/ns1 /' | cat_i 193 194# Do not wait until the zone refresh completes - even if a crash has not 195# happened by now, a broken server would never serve the record which is only 196# present in the fallback AXFR, so checking for that is enough to verify if a 197# server is broken or not; if it is, it is bound to crash shortly anyway. 198$DIG $DIGOPTS test.nil. TXT @10.53.0.1 >dig.out.test$n.2 || ret=1 199grep -q -F "serial 4, fallback AXFR" dig.out.test$n.2 || ret=1 200 201# Ensure the expected error is logged. 202nextpart ns1/named.run | grep -q -F "SOA name mismatch" || ret=1 203 204if [ $ret != 0 ]; then echo_i "failed"; fi 205status=$((status + ret)) 206 207n=$((n + 1)) 208echo_i "testing ixfr-from-differences option ($n)" 209# ns3 is primary; ns4 is secondary 210{ 211 $CHECKZONE test. ns3/mytest.db >/dev/null 2>&1 212 rc=$? 213} || true 214if [ $rc -ne 0 ]; then 215 echo_i "named-checkzone returned failure on ns3/mytest.db" 216fi 217 218retry_quiet 10 wait_for_serial 10.53.0.4 test. 1 dig.out.test$n || ret=1 219 220nextpart ns4/named.run >/dev/null 221 222# modify the primary 223sleep 1 224cp ns3/mytest1.db ns3/mytest.db 225$RNDCCMD 10.53.0.3 reload | sed 's/^/ns3 /' | cat_i 226 227# wait for primary to reload 228retry_quiet 10 wait_for_serial 10.53.0.3 test. 2 dig.out.test$n || ret=1 229 230# wait for secondary to reload 231tret=0 232retry_quiet 5 wait_for_serial 10.53.0.4 test. 2 dig.out.test$n || tret=1 233if [ $tret -eq 1 ]; then 234 # re-noitfy after 5 seconds, then wait another 10 235 $RNDCCMD 10.53.0.3 notify test | set 's/^/ns3 /' | cat_i 236 retry_quiet 10 wait_for_serial 10.53.0.4 test. 2 dig.out.test$n || ret=1 237fi 238 239wait_for_log 10 'got incremental' ns4/named.run || ret=1 240if [ $ret != 0 ]; then echo_i "failed"; fi 241status=$((status + ret)) 242 243n=$((n + 1)) 244echo_i "testing 'request-ixfr no' option inheritance from view ($n)" 245ret=0 246# There's a view with 2 zones. In the view, "request-ixfr yes" 247# but in the zone "sub.test", request-ixfr no" 248# we want to make sure that a change to sub.test results in AXFR, while 249# changes to test. result in IXFR 250 251sleep 1 252cp ns3/subtest1.db ns3/subtest.db # change to sub.test zone, should be AXFR 253nextpart ns4/named.run >/dev/null 254$RNDCCMD 10.53.0.3 reload | sed 's/^/ns3 /' | cat_i 255 256# wait for primary to reload 257retry_quiet 10 wait_for_serial 10.53.0.3 sub.test. 3 dig.out.test$n || ret=1 258 259# wait for secondary to reload 260tret=0 261retry_quiet 5 wait_for_serial 10.53.0.4 sub.test. 3 dig.out.test$n || tret=1 262if [ $tret -eq 1 ]; then 263 # re-noitfy after 5 seconds, then wait another 10 264 $RNDCCMD 10.53.0.3 notify sub.test | set 's/^/ns3 /' | cat_i 265 retry_quiet 10 wait_for_serial 10.53.0.4 sub.test. 3 dig.out.test$n || ret=1 266fi 267 268wait_for_log 10 'got nonincremental response' ns4/named.run || ret=1 269if [ $ret != 0 ]; then echo_i "failed"; fi 270status=$((status + ret)) 271 272n=$((n + 1)) 273echo_i "testing 'request-ixfr yes' option inheritance from view ($n)" 274ret=0 275sleep 1 276cp ns3/mytest2.db ns3/mytest.db # change to test zone, should be IXFR 277nextpart ns4/named.run >/dev/null 278$RNDCCMD 10.53.0.3 reload | sed 's/^/ns3 /' | cat_i 279 280# wait for primary to reload 281retry_quiet 10 wait_for_serial 10.53.0.3 test. 3 dig.out.test$n || ret=1 282 283# wait for secondary to reload 284tret=0 285retry_quiet 5 wait_for_serial 10.53.0.4 test. 3 dig.out.test$n || tret=1 286if [ $tret -eq 1 ]; then 287 # re-noitfy after 5 seconds, then wait another 10 288 $RNDCCMD 10.53.0.3 notify test | set 's/^/ns3 /' | cat_i 289 retry_quiet 10 wait_for_serial 10.53.0.4 test. 3 dig.out.test$n || ret=1 290fi 291 292wait_for_log 10 'got incremental response' ns4/named.run || ret=1 293if [ $ret != 0 ]; then echo_i "failed"; fi 294status=$((status + ret)) 295 296n=$((n + 1)) 297ret=0 298echo_i "testing DiG's handling of a multi message AXFR style IXFR response ($n)" 299( 300 (sleep 10 && kill $$) 2>/dev/null & 301 sub=$! 302 $DIG -p ${PORT} ixfr=0 large @10.53.0.3 >dig.out.test$n 303 kill $sub 304) 305lines=$(grep hostmaster.large dig.out.test$n | wc -l) 306test ${lines:-0} -eq 2 || ret=1 307messages=$(sed -n 's/^;;.*messages \([0-9]*\),.*/\1/p' dig.out.test$n) 308test ${messages:-0} -gt 1 || ret=1 309if [ $ret != 0 ]; then echo_i "failed"; fi 310status=$((status + ret)) 311 312n=$((n + 1)) 313echo_i "test 'dig +notcp ixfr=<value>' vs 'dig ixfr=<value> +notcp' vs 'dig ixfr=<value>' ($n)" 314ret=0 315# Should be "switch to TCP" response 316$DIG $DIGOPTS +notcp ixfr=1 test @10.53.0.4 >dig.out1.test$n || ret=1 317$DIG $DIGOPTS ixfr=1 +notcp test @10.53.0.4 >dig.out2.test$n || ret=1 318digcomp dig.out1.test$n dig.out2.test$n || ret=1 319awk '$4 == "SOA" { soacnt++} END {if (soacnt == 1) exit(0); else exit(1);}' dig.out1.test$n || ret=1 320awk '$4 == "SOA" { if ($7 == 3) exit(0); else exit(1);}' dig.out1.test$n || ret=1 321# 322nextpart ns4/named.run >/dev/null 323# Should be incremental transfer. 324$DIG $DIGOPTS ixfr=1 test @10.53.0.4 >dig.out3.test$n || ret=1 325awk '$4 == "SOA" { soacnt++} END { if (soacnt == 6) exit(0); else exit(1);}' dig.out3.test$n || ret=1 326if [ $ret != 0 ]; then echo_i "failed"; fi 327status=$((status + ret)) 328 329n=$((n + 1)) 330echo_i "check estimated IXFR size ($n)" 331ret=0 332# note IXFR delta size will be slightly bigger with version 1 transaction 333# headers as there is no correction for the overall record length storage. 334# Ver1 = 4 * (6 + 10 + 10 + 17 + 5 * 4) + 2 * (13 + 10 + 4) + (6 * 4) = 330 335# Ver2 = 4 * (6 + 10 + 10 + 17 + 5 * 4) + 2 * (13 + 10 + 4) = 306 336nextpart ns4/named.run | grep "IXFR delta size (306 bytes)" >/dev/null || ret=1 337if [ $ret != 0 ]; then echo_i "failed"; fi 338status=$((status + ret)) 339 340# make sure ns5 has transfered the zone 341# wait for secondary to reload 342tret=0 343retry_quiet 5 wait_for_serial 10.53.0.5 test. 4 dig.out.test$n || tret=1 344if [ $tret -eq 1 ]; then 345 # re-noitfy after 5 seconds, then wait another 10 346 $RNDCCMD 10.53.0.3 notify test | set 's/^/ns3 /' | cat_i 347 retry_quiet 10 wait_for_serial 10.53.0.5 test. 3 dig.out.test$n || ret=1 348fi 349 350n=$((n + 1)) 351echo_i "test 'provide-ixfr no;' (serial < current) ($n)" 352ret=0 353nextpart ns5/named.run >/dev/null 354# Should be "AXFR style" response 355$DIG $DIGOPTS ixfr=1 test @10.53.0.5 >dig.out1.test$n || ret=1 356# Should be "switch to TCP" response 357$DIG $DIGOPTS ixfr=1 +notcp test @10.53.0.5 >dig.out2.test$n || ret=1 358awk '$4 == "SOA" { soacnt++} END {if (soacnt == 2) exit(0); else exit(1);}' dig.out1.test$n || ret=1 359awk '$4 == "SOA" { soacnt++} END {if (soacnt == 1) exit(0); else exit(1);}' dig.out2.test$n || ret=1 360msg="IXFR delta response disabled due to 'provide-ixfr no;' being set" 361nextpart ns5/named.run | grep "$msg" >/dev/null || ret=1 362if [ $ret != 0 ]; then echo_i "failed"; fi 363status=$((status + ret)) 364 365n=$((n + 1)) 366echo_i "test 'provide-ixfr no;' (serial = current) ($n)" 367ret=0 368# Should be "AXFR style" response 369$DIG $DIGOPTS ixfr=3 test @10.53.0.5 >dig.out1.test$n || ret=1 370# Should be "switch to TCP" response 371$DIG $DIGOPTS ixfr=3 +notcp test @10.53.0.5 >dig.out2.test$n || ret=1 372awk '$4 == "SOA" { soacnt++} END {if (soacnt == 1) exit(0); else exit(1);}' dig.out1.test$n || ret=1 373awk '$4 == "SOA" { soacnt++} END {if (soacnt == 1) exit(0); else exit(1);}' dig.out2.test$n || ret=1 374if [ $ret != 0 ]; then echo_i "failed"; fi 375status=$((status + ret)) 376 377n=$((n + 1)) 378echo_i "test 'provide-ixfr no;' (serial > current) ($n)" 379ret=0 380# Should be "AXFR style" response 381$DIG $DIGOPTS ixfr=4 test @10.53.0.5 >dig.out1.test$n || ret=1 382# Should be "switch to TCP" response 383$DIG $DIGOPTS ixfr=4 +notcp test @10.53.0.5 >dig.out2.test$n || ret=1 384awk '$4 == "SOA" { soacnt++} END {if (soacnt == 1) exit(0); else exit(1);}' dig.out1.test$n || ret=1 385awk '$4 == "SOA" { soacnt++} END {if (soacnt == 1) exit(0); else exit(1);}' dig.out2.test$n || ret=1 386if [ $ret != 0 ]; then echo_i "failed"; fi 387status=$((status + ret)) 388 389n=$((n + 1)) 390echo_i "checking whether dig calculates IXFR statistics correctly ($n)" 391ret=0 392$DIG $DIGOPTS +noedns +stat -b 10.53.0.4 @10.53.0.4 test. ixfr=2 >dig.out1.test$n 393get_dig_xfer_stats dig.out1.test$n >stats.dig 394diff ixfr-stats.good stats.dig >/dev/null || ret=1 395if [ $ret != 0 ]; then echo_i "failed"; fi 396status=$((status + ret)) 397 398# Note: in the next two tests, we use ns4 logs for checking both incoming and 399# outgoing transfer statistics as ns4 is both a secondary server (for ns3) and a 400# primary server (for dig queries from the previous test) for "test". 401 402_wait_for_stats() { 403 get_named_xfer_stats ns4/named.run "$1" test "$2" >"$3" 404 diff ixfr-stats.good "$3" >/dev/null || return 1 405 return 0 406} 407 408n=$((n + 1)) 409echo_i "checking whether named calculates incoming IXFR statistics correctly ($n)" 410ret=0 411retry_quiet 10 _wait_for_stats 10.53.0.3 "Transfer completed" stats.incoming || ret=1 412if [ $ret != 0 ]; then echo_i "failed"; fi 413status=$((status + ret)) 414 415n=$((n + 1)) 416echo_i "checking whether named calculates outgoing IXFR statistics correctly ($n)" 417retry_quiet 10 _wait_for_stats 10.53.0.4 "IXFR ended" stats.outgoing || ret=1 418if [ $ret != 0 ]; then echo_i "failed"; fi 419status=$((status + ret)) 420 421n=$((n + 1)) 422ret=0 423echo_i "testing fallback to AXFR when max-ixfr-ratio is exceeded ($n)" 424nextpart ns4/named.run >/dev/null 425 426sleep 1 427cp ns3/mytest3.db ns3/mytest.db # change to test zone, too big for IXFR 428$RNDCCMD 10.53.0.3 reload | sed 's/^/ns3 /' | cat_i 429 430# wait for secondary to reload 431tret=0 432retry_quiet 5 wait_for_serial 10.53.0.4 test. 4 dig.out.test$n || tret=1 433if [ $tret -eq 1 ]; then 434 # re-noitfy after 5 seconds, then wait another 10 435 $RNDCCMD 10.53.0.3 notify test | set 's/^/ns3 /' | cat_i 436 retry_quiet 10 wait_for_serial 10.53.0.4 test. 4 dig.out.test$n || ret=1 437fi 438 439wait_for_log 10 'got nonincremental response' ns4/named.run || ret=1 440if [ $ret != 0 ]; then echo_i "failed"; fi 441status=$((status + ret)) 442 443echo_i "exit status: $status" 444[ $status -eq 0 ] || exit 1 445