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 14set -e 15 16# shellcheck source=conf.sh 17. ../conf.sh 18 19DIGCMD="$DIG @10.53.0.2 -p ${PORT}" 20RNDCCMD="$RNDC -c ../_common/rndc.conf -p ${CONTROLPORT} -s" 21NS_PARAMS="-m record -c named.conf -d 99 -g -T maxcachesize=2097152" 22 23if ! $FEATURETEST --have-json-c; then 24 unset PERL_JSON 25 echo_i "JSON was not configured; skipping" >&2 26elif $PERL -e 'use JSON;' 2>/dev/null; then 27 PERL_JSON=1 28else 29 unset PERL_JSON 30 echo_i "JSON tests require JSON library; skipping" >&2 31fi 32 33if ! $FEATURETEST --have-libxml2; then 34 unset PERL_XML 35 echo_i "XML was not configured; skipping" >&2 36elif $PERL -e 'use XML::Simple;' 2>/dev/null; then 37 PERL_XML=1 38else 39 unset PERL_XML 40 echo_i "XML tests require XML::Simple; skipping" >&2 41fi 42 43if [ ! "$PERL_JSON" ] && [ ! "$PERL_XML" ]; then 44 echo_i "skipping all tests" 45 exit 0 46fi 47 48retry_quiet_fast() { 49 __retries="${1}" 50 shift 51 52 while :; do 53 if "$@"; then 54 return 0 55 fi 56 __retries=$((__retries - 1)) 57 if [ "${__retries}" -gt 0 ]; then 58 # sleep for 0.1 seconds 59 perl -e 'select(undef, undef, undef, .1)' 60 else 61 return 1 62 fi 63 done 64} 65 66wait_for_log_fast() ( 67 timeout="$1" 68 msg="$2" 69 file="$3" 70 retry_quiet_fast "$timeout" _search_log "$msg" "$file" && return 0 71 echo_i "exceeded time limit waiting for literal '$msg' in $file" 72 return 1 73) 74 75getzones() { 76 sleep 1 77 echo_i "... using $1" 78 case $1 in 79 xml) path='xml/v3/zones' ;; 80 json) path='json/v1/zones' ;; 81 *) return 1 ;; 82 esac 83 file=$($PERL fetch.pl -p ${EXTRAPORT1} $path) 84 cp $file $file.$1.$3 85 { 86 $PERL zones-${1}.pl $file $2 2>/dev/null | sort >zones.out.$3 87 result=$? 88 } || true 89 return $result 90} 91 92getxfrins() { 93 echo_i "... using $1" 94 case $1 in 95 xml) path='xml/v3/xfrins' ;; 96 json) path='json/v1/xfrins' ;; 97 *) return 1 ;; 98 esac 99 file=$($PERL fetch.pl -s 10.53.0.3 -p ${EXTRAPORT1} $path) 100 cp $file $file.$1.$2 101 result=$? 102 return $result 103} 104 105# TODO: Move loadkeys_on to conf.sh.common 106loadkeys_on() { 107 nsidx=$1 108 zone=$2 109 nextpart ns${nsidx}/named.run >/dev/null 110 $RNDCCMD 10.53.0.${nsidx} loadkeys ${zone} | sed "s/^/ns${nsidx} /" | cat_i 111 wait_for_log 20 "next key event" ns${nsidx}/named.run 112} 113 114# verify that the http server dropped the connection without replying 115check_http_dropped() { 116 if [ -x "${NC}" ]; then 117 "${NC}" 10.53.0.3 "${EXTRAPORT1}" >nc.out$n || ret=1 118 if test -s nc.out$n; then 119 ret=1 120 fi 121 else 122 echo_i "skipping test as nc not found" 123 fi 124} 125 126status=0 127n=1 128 129echo_i "check content-length parse error ($n)" 130ret=0 131check_http_dropped <<EOF 132POST /xml/v3/status HTTP/1.0 133Content-Length: nah 134 135EOF 136if [ $ret != 0 ]; then echo_i "failed"; fi 137status=$((status + ret)) 138n=$((n + 1)) 139 140echo_i "check negative content-length ($n)" 141ret=0 142check_http_dropped <<EOF 143POST /xml/v3/status HTTP/1.0 144Content-Length: -50 145 146EOF 147if [ $ret != 0 ]; then echo_i "failed"; fi 148status=$((status + ret)) 149n=$((n + 1)) 150 151echo_i "check content-length 32-bit overflow ($n)" 152check_http_dropped <<EOF 153POST /xml/v3/status HTTP/1.0 154Content-Length: 4294967239 155 156EOF 157if [ $ret != 0 ]; then echo_i "failed"; fi 158status=$((status + ret)) 159n=$((n + 1)) 160 161echo_i "check content-length 64-bit overflow ($n)" 162check_http_dropped <<EOF 163POST /xml/v3/status HTTP/1.0 164Content-Length: 18446744073709551549 165 166EOF 167 168if [ $ret != 0 ]; then echo_i "failed"; fi 169status=$((status + ret)) 170n=$((n + 1)) 171 172echo_i "Prepare for if-modified-since test ($n)" 173ret=0 174i=0 175if $FEATURETEST --have-libxml2 && [ -x "${CURL}" ]; then 176 URL="http://10.53.0.3:${EXTRAPORT1}/bind9.xsl" 177 ${CURL} --silent --show-error --fail --output bind9.xsl.1 $URL 178 ret=$? 179else 180 echo_i "skipping test: requires libxml2 and curl" 181fi 182if [ $ret != 0 ]; then echo_i "failed"; fi 183status=$((status + ret)) 184n=$((n + 1)) 185 186echo_i "checking consistency between named.stats and xml/json ($n)" 187ret=0 188rm -f ns2/named.stats 189$DIGCMD +tcp example ns >dig.out.$n || ret=1 190$RNDCCMD 10.53.0.2 stats 2>&1 | sed 's/^/I:ns1 /' 191query_count=$(awk '/QUERY/ {print $1}' ns2/named.stats) 192txt_count=$(awk '/TXT/ {print $1}' ns2/named.stats) 193noerror_count=$(awk '/NOERROR/ {print $1}' ns2/named.stats) 194if [ "$PERL_XML" ]; then 195 file=$($PERL fetch.pl -p ${EXTRAPORT1} xml/v3/server) 196 mv $file xml.stats 197 $PERL server-xml.pl >xml.fmtstats 2>/dev/null 198 xml_query_count=$(awk '/opcode QUERY/ { print $NF }' xml.fmtstats) 199 xml_query_count=${xml_query_count:-0} 200 [ "$query_count" -eq "$xml_query_count" ] || ret=1 201 xml_txt_count=$(awk '/qtype TXT/ { print $NF }' xml.fmtstats) 202 xml_txt_count=${xml_txt_count:-0} 203 [ "$txt_count" -eq "$xml_txt_count" ] || ret=1 204 xml_noerror_count=$(awk '/rcode NOERROR/ { print $NF }' xml.fmtstats) 205 xml_noerror_count=${xml_noerror_count:-0} 206 [ "$noerror_count" -eq "$xml_noerror_count" ] || ret=1 207fi 208if [ "$PERL_JSON" ]; then 209 file=$($PERL fetch.pl -p ${EXTRAPORT1} json/v1/server) 210 mv $file json.stats 211 $PERL server-json.pl >json.fmtstats 2>/dev/null 212 json_query_count=$(awk '/opcode QUERY/ { print $NF }' json.fmtstats) 213 json_query_count=${json_query_count:-0} 214 [ "$query_count" -eq "$json_query_count" ] || ret=1 215 json_txt_count=$(awk '/qtype TXT/ { print $NF }' json.fmtstats) 216 json_txt_count=${json_txt_count:-0} 217 [ "$txt_count" -eq "$json_txt_count" ] || ret=1 218 json_noerror_count=$(awk '/rcode NOERROR/ { print $NF }' json.fmtstats) 219 json_noerror_count=${json_noerror_count:-0} 220 [ "$noerror_count" -eq "$json_noerror_count" ] || ret=1 221fi 222if [ $ret != 0 ]; then echo_i "failed"; fi 223status=$((status + ret)) 224n=$((n + 1)) 225 226ret=0 227echo_i "checking malloced memory statistics xml/json ($n)" 228if [ "$PERL_XML" ]; then 229 file=$($PERL fetch.pl -p ${EXTRAPORT1} xml/v3/mem) 230 mv $file xml.mem 231 $PERL mem-xml.pl $file >xml.fmtmem 232 grep "'InUse' => '[0-9][0-9]*'" xml.fmtmem >/dev/null || ret=1 233 grep "'inuse' => '[0-9][0-9]*'" xml.fmtmem >/dev/null || ret=1 234fi 235if [ "$PERL_JSON" ]; then 236 file=$($PERL fetch.pl -p ${EXTRAPORT1} json/v1/mem) 237 mv $file json.mem 238 grep '"inuse":[0-9][0-9]*,' json.mem >/dev/null || ret=1 239 grep '"InUse":[0-9][0-9]*,' json.mem >/dev/null || ret=1 240fi 241if [ $ret != 0 ]; then echo_i "failed"; fi 242status=$((status + ret)) 243n=$((n + 1)) 244 245echo_i "checking consistency between regular and compressed output ($n)" 246ret=0 247if [ -x "${CURL}" ]; then 248 for i in 1 2 3 4 5; do 249 ret=0 250 if $FEATURETEST --have-libxml2; then 251 URL="http://10.53.0.2:${EXTRAPORT1}/xml/v3/server" 252 filter_str='s#<current-time>.*</current-time>##g' 253 else 254 URL="http://10.53.0.2:${EXTRAPORT1}/json/v1/server" 255 filter_str='s#"current-time.*",##g' 256 fi 257 "${CURL}" -D regular.headers "$URL" 2>/dev/null \ 258 | sed -e "$filter_str" >regular.out || ret=1 259 "${CURL}" -D compressed.headers --compressed "$URL" 2>/dev/null \ 260 | sed -e "$filter_str" >compressed.out || ret=1 261 diff regular.out compressed.out >/dev/null || ret=1 262 if [ $ret != 0 ]; then 263 echo_i "failed on try $i, probably a timing issue, trying again" 264 sleep 1 265 else 266 break 267 fi 268 done 269else 270 echo_i "skipping test as curl not found" 271fi 272status=$((status + ret)) 273n=$((n + 1)) 274 275ret=0 276echo_i "checking if compressed output is really compressed ($n)" 277if $FEATURETEST --with-zlib; then 278 REGSIZE=$(cat regular.headers \ 279 | grep -i Content-Length | sed -e "s/.*: \([0-9]*\).*/\1/") 280 COMPSIZE=$(cat compressed.headers \ 281 | grep -i Content-Length | sed -e "s/.*: \([0-9]*\).*/\1/") 282 if [ ! $((REGSIZE / COMPSIZE)) -gt 2 ]; then 283 ret=1 284 fi 285else 286 echo_i "skipped" 287fi 288if [ $ret != 0 ]; then echo_i "failed"; fi 289status=$((status + ret)) 290n=$((n + 1)) 291 292# Test dnssec sign statistics. 293zone="dnssec" 294sign_prefix="dnssec-sign operations" 295refresh_prefix="dnssec-refresh operations" 296ksk_id=$(cat ns2/$zone.ksk.id) 297zsk_id=$(cat ns2/$zone.zsk.id) 298 299# Test sign operations for scheduled resigning. 300ret=0 301# The dnssec zone has 10 RRsets to sign (including NSEC) with the ZSK and one 302# RRset (DNSKEY) with the KSK. So starting named with signatures that expire 303# almost right away, this should trigger 10 zsk and 1 ksk sign operations. 304echo "${refresh_prefix} ${zsk_id}: 10" >zones.expect 305echo "${refresh_prefix} ${ksk_id}: 1" >>zones.expect 306echo "${sign_prefix} ${zsk_id}: 10" >>zones.expect 307echo "${sign_prefix} ${ksk_id}: 1" >>zones.expect 308cat zones.expect | sort >zones.expect.$n 309rm -f zones.expect 310# Fetch and check the dnssec sign statistics. 311echo_i "fetching zone '$zone' stats data after zone maintenance at startup ($n)" 312if [ "$PERL_XML" ]; then 313 getzones xml $zone x$n || ret=1 314 cmp zones.out.x$n zones.expect.$n || ret=1 315fi 316if [ "$PERL_JSON" ]; then 317 getzones json 0 j$n || ret=1 318 cmp zones.out.j$n zones.expect.$n || ret=1 319fi 320if [ $ret != 0 ]; then echo_i "failed"; fi 321status=$((status + ret)) 322n=$((n + 1)) 323 324# Test sign operations after dynamic update. 325ret=0 326( 327 # Update dnssec zone to trigger signature creation. 328 echo zone $zone 329 echo server 10.53.0.2 "$PORT" 330 echo update add $zone. 300 in txt "nsupdate added me" 331 echo send 332) | $NSUPDATE 333# This should trigger the resign of SOA, TXT and NSEC (+3 zsk). 334echo "${refresh_prefix} ${zsk_id}: 10" >zones.expect 335echo "${refresh_prefix} ${ksk_id}: 1" >>zones.expect 336echo "${sign_prefix} ${zsk_id}: 13" >>zones.expect 337echo "${sign_prefix} ${ksk_id}: 1" >>zones.expect 338cat zones.expect | sort >zones.expect.$n 339rm -f zones.expect 340# Fetch and check the dnssec sign statistics. 341echo_i "fetching zone '$zone' stats data after dynamic update ($n)" 342if [ "$PERL_XML" ]; then 343 getzones xml $zone x$n || ret=1 344 cmp zones.out.x$n zones.expect.$n || ret=1 345fi 346if [ "$PERL_JSON" ]; then 347 getzones json 0 j$n || ret=1 348 cmp zones.out.j$n zones.expect.$n || ret=1 349fi 350if [ $ret != 0 ]; then echo_i "failed"; fi 351status=$((status + ret)) 352n=$((n + 1)) 353 354# Test sign operations of KSK. 355ret=0 356echo_i "fetch zone '$zone' stats data after updating DNSKEY RRset ($n)" 357id=$(echo "${zsk_id}" | cut -d+ -f2 -) 358# Add a DNSKEY, this triggers resigning the DNSKEY RRset. 359zsk=$("$KEYGEN" -L 3600 -q -a "$DEFAULT_ALGORITHM" -b "$DEFAULT_BITS" "$zone") 360( 361 echo zone $zone 362 echo server 10.53.0.2 "$PORT" 363 echo update add $(cat "${zsk}.key" | grep -v ";.*") 364 echo send 365) | $NSUPDATE 366# This should trigger the resign of SOA (+1 zsk) and DNSKEY (+1 ksk). 367echo "${refresh_prefix} ${zsk_id}: 10" >zones.expect 368echo "${refresh_prefix} ${ksk_id}: 1" >>zones.expect 369echo "${sign_prefix} ${zsk_id}: 14" >>zones.expect 370echo "${sign_prefix} ${ksk_id}: 2" >>zones.expect 371cat zones.expect | sort >zones.expect.$n 372rm -f zones.expect 373# Fetch and check the dnssec sign statistics. 374if [ "$PERL_XML" ]; then 375 getzones xml $zone x$n || ret=1 376 cmp zones.out.x$n zones.expect.$n || ret=1 377fi 378if [ "$PERL_JSON" ]; then 379 getzones json 0 j$n || ret=1 380 cmp zones.out.j$n zones.expect.$n || ret=1 381fi 382if [ $ret != 0 ]; then echo_i "failed"; fi 383status=$((status + ret)) 384n=$((n + 1)) 385 386# Test sign operations for scheduled resigning (many keys). 387ret=0 388zone="manykeys" 389ksk8_id=$(cat ns2/$zone.ksk8.id) 390zsk8_id=$(cat ns2/$zone.zsk8.id) 391ksk13_id=$(cat ns2/$zone.ksk13.id) 392zsk13_id=$(cat ns2/$zone.zsk13.id) 393ksk14_id=$(cat ns2/$zone.ksk14.id) 394zsk14_id=$(cat ns2/$zone.zsk14.id) 395# The dnssec zone has 10 RRsets to sign (including NSEC) with the ZSKs and one 396# RRset (DNSKEY) with the KSKs. So starting named with signatures that expire 397# almost right away, this should trigger 10 zsk and 1 ksk sign operations per 398# key. 399echo "${refresh_prefix} ${zsk8_id}: 10" >zones.expect 400echo "${refresh_prefix} ${zsk13_id}: 10" >>zones.expect 401echo "${refresh_prefix} ${zsk14_id}: 10" >>zones.expect 402echo "${refresh_prefix} ${ksk8_id}: 1" >>zones.expect 403echo "${refresh_prefix} ${ksk13_id}: 1" >>zones.expect 404echo "${refresh_prefix} ${ksk14_id}: 1" >>zones.expect 405echo "${sign_prefix} ${zsk8_id}: 10" >>zones.expect 406echo "${sign_prefix} ${zsk13_id}: 10" >>zones.expect 407echo "${sign_prefix} ${zsk14_id}: 10" >>zones.expect 408echo "${sign_prefix} ${ksk8_id}: 1" >>zones.expect 409echo "${sign_prefix} ${ksk13_id}: 1" >>zones.expect 410echo "${sign_prefix} ${ksk14_id}: 1" >>zones.expect 411cat zones.expect | sort >zones.expect.$n 412rm -f zones.expect 413# Fetch and check the dnssec sign statistics. 414echo_i "fetching zone '$zone' stats data after zone maintenance at startup ($n)" 415if [ "$PERL_XML" ]; then 416 getzones xml $zone x$n || ret=1 417 cmp zones.out.x$n zones.expect.$n || ret=1 418fi 419if [ "$PERL_JSON" ]; then 420 getzones json 2 j$n || ret=1 421 cmp zones.out.j$n zones.expect.$n || ret=1 422fi 423if [ $ret != 0 ]; then echo_i "failed"; fi 424status=$((status + ret)) 425n=$((n + 1)) 426 427# Test sign operations after dynamic update (many keys). 428ret=0 429( 430 # Update dnssec zone to trigger signature creation. 431 echo zone $zone 432 echo server 10.53.0.2 "$PORT" 433 echo update add $zone. 300 in txt "nsupdate added me" 434 echo send 435) | $NSUPDATE 436# This should trigger the resign of SOA, TXT and NSEC (+3 zsk). 437echo "${refresh_prefix} ${zsk8_id}: 10" >zones.expect 438echo "${refresh_prefix} ${zsk13_id}: 10" >>zones.expect 439echo "${refresh_prefix} ${zsk14_id}: 10" >>zones.expect 440echo "${refresh_prefix} ${ksk8_id}: 1" >>zones.expect 441echo "${refresh_prefix} ${ksk13_id}: 1" >>zones.expect 442echo "${refresh_prefix} ${ksk14_id}: 1" >>zones.expect 443echo "${sign_prefix} ${zsk8_id}: 13" >>zones.expect 444echo "${sign_prefix} ${zsk13_id}: 13" >>zones.expect 445echo "${sign_prefix} ${zsk14_id}: 13" >>zones.expect 446echo "${sign_prefix} ${ksk8_id}: 1" >>zones.expect 447echo "${sign_prefix} ${ksk13_id}: 1" >>zones.expect 448echo "${sign_prefix} ${ksk14_id}: 1" >>zones.expect 449cat zones.expect | sort >zones.expect.$n 450rm -f zones.expect 451# Fetch and check the dnssec sign statistics. 452echo_i "fetching zone '$zone' stats data after dynamic update ($n)" 453if [ "$PERL_XML" ]; then 454 getzones xml $zone x$n || ret=1 455 cmp zones.out.x$n zones.expect.$n || ret=1 456fi 457if [ "$PERL_JSON" ]; then 458 getzones json 2 j$n || ret=1 459 cmp zones.out.j$n zones.expect.$n || ret=1 460fi 461if [ $ret != 0 ]; then echo_i "failed"; fi 462status=$((status + ret)) 463n=$((n + 1)) 464 465# Test sign operations after dnssec-policy change (removing keys). 466ret=0 467copy_setports ns2/named2.conf.in ns2/named.conf 468$RNDCCMD 10.53.0.2 reload 2>&1 | sed 's/^/I:ns2 /' 469# This should trigger the resign of DNSKEY (+1 ksk), and SOA, NSEC, 470# TYPE65534 (+3 zsk). The dnssec-sign statistics for the removed keys should 471# be cleared and thus no longer visible. But NSEC and SOA are (mistakenly) 472# counted double, one time because of zone_resigninc and one time because of 473# zone_nsec3chain. So +5 zsk in total. 474echo "${refresh_prefix} ${zsk8_id}: 15" >zones.expect 475echo "${refresh_prefix} ${ksk8_id}: 2" >>zones.expect 476echo "${sign_prefix} ${zsk8_id}: 18" >>zones.expect 477echo "${sign_prefix} ${ksk8_id}: 2" >>zones.expect 478cat zones.expect | sort >zones.expect.$n 479rm -f zones.expect 480# Fetch and check the dnssec sign statistics. 481echo_i "fetching zone '$zone' stats data after dnssec-policy change ($n)" 482if [ "$PERL_XML" ]; then 483 getzones xml $zone x$n || ret=1 484 cmp zones.out.x$n zones.expect.$n || ret=1 485fi 486if [ "$PERL_JSON" ]; then 487 getzones json 2 j$n || ret=1 488 cmp zones.out.j$n zones.expect.$n || ret=1 489fi 490if [ $ret != 0 ]; then echo_i "failed"; fi 491status=$((status + ret)) 492n=$((n + 1)) 493 494echo_i "Check HTTP/1.1 client-side pipelined requests are handled (GET) ($n)" 495ret=0 496if [ -x "${NC}" ]; then 497 "${NC}" 10.53.0.3 "${EXTRAPORT1}" <<EOF >nc.out$n || ret=1 498GET /xml/v3/status HTTP/1.1 499Host: 10.53.0.3:${EXTRAPORT1} 500 501GET /xml/v3/status HTTP/1.1 502Host: 10.53.0.3:${EXTRAPORT1} 503Connection: close 504 505EOF 506 lines=$(grep -c "^<statistics version" nc.out$n) 507 test "$lines" = 2 || ret=1 508 # keep-alive not needed in HTTP/1.1, second response has close 509 lines=$(grep -c "^Connection: Keep-Alive" nc.out$n || true) 510 test "$lines" = 0 || ret=1 511 lines=$(grep -c "^Connection: close" nc.out$n) 512 test "$lines" = 1 || ret=1 513else 514 echo_i "skipping test as nc not found" 515fi 516if [ $ret != 0 ]; then echo_i "failed"; fi 517status=$((status + ret)) 518n=$((n + 1)) 519 520echo_i "Check HTTP/1.1 client-side pipelined requests are handled (POST) ($n)" 521ret=0 522if [ -x "${NC}" ]; then 523 "${NC}" 10.53.0.3 "${EXTRAPORT1}" <<EOF >nc.out$n || ret=1 524POST /xml/v3/status HTTP/1.1 525Host: 10.53.0.3:${EXTRAPORT1} 526Content-Type: application/json 527Content-Length: 3 528 529{} 530POST /xml/v3/status HTTP/1.1 531Host: 10.53.0.3:${EXTRAPORT1} 532Content-Type: application/json 533Content-Length: 3 534Connection: close 535 536{} 537EOF 538 lines=$(grep -c "^<statistics version" nc.out$n) 539 test "$lines" = 2 || ret=1 540 # keep-alive not needed in HTTP/1.1, second response has close 541 lines=$(grep -c "^Connection: Keep-Alive" nc.out$n || true) 542 test "$lines" = 0 || ret=1 543 lines=$(grep -c "^Connection: close" nc.out$n) 544 test "$lines" = 1 || ret=1 545else 546 echo_i "skipping test as nc not found" 547fi 548if [ $ret != 0 ]; then echo_i "failed"; fi 549status=$((status + ret)) 550n=$((n + 1)) 551 552echo_i "Check HTTP/1.0 keep-alive ($n)" 553ret=0 554if [ -x "${NC}" ]; then 555 "${NC}" 10.53.0.3 "${EXTRAPORT1}" <<EOF >nc.out$n || ret=1 556GET /xml/v3/status HTTP/1.0 557Connection: keep-alive 558 559GET /xml/v3/status HTTP/1.0 560 561EOF 562 # should be two responses 563 lines=$(grep -c "^<statistics version" nc.out$n) 564 test "$lines" = 2 || ret=1 565 # first response has keep-alive, second has close 566 lines=$(grep -c "^Connection: Keep-Alive" nc.out$n || true) 567 test "$lines" = 1 || ret=1 568 lines=$(grep -c "^Connection: close" nc.out$n) 569 test "$lines" = 1 || ret=1 570else 571 echo_i "skipping test as nc not found" 572fi 573if [ $ret != 0 ]; then echo_i "failed"; fi 574status=$((status + ret)) 575n=$((n + 1)) 576 577echo_i "Check inconsistent Connection: headers ($n)" 578ret=0 579if [ -x "${NC}" ]; then 580 "${NC}" 10.53.0.3 "${EXTRAPORT1}" <<EOF >nc.out$n || ret=1 581GET /xml/v3/status HTTP/1.0 582Connection: keep-alive 583Connection: close 584 585GET /xml/v3/status HTTP/1.0 586 587EOF 588 # should be one response (second is ignored) 589 lines=$(grep -c "^<statistics version" nc.out$n) 590 test "$lines" = 1 || ret=1 591 # no keep-alive, one close 592 lines=$(grep -c "^Connection: Keep-Alive" nc.out$n || true) 593 test "$lines" = 0 || ret=1 594 lines=$(grep -c "^Connection: close" nc.out$n) 595 test "$lines" = 1 || ret=1 596else 597 echo_i "skipping test as nc not found" 598fi 599if [ $ret != 0 ]; then echo_i "failed"; fi 600status=$((status + ret)) 601n=$((n + 1)) 602 603if [ -x "${CURL}" ] && ! ("${CURL}" --next 2>&1 | grep 'option --next: is unknown'); then 604 CURL_NEXT="${CURL}" 605fi 606 607echo_i "Check HTTP with more than 100 headers ($n)" 608ret=0 609i=0 610if [ -x "${CURL_NEXT}" ]; then 611 # build input stream. 612 : >header.in$n 613 while test $i -lt 101; do 614 printf 'X-Bloat%d: VGhlIG1vc3QgY29tbW9uIHJlYXNvbiBmb3IgYmxvYXRpbmcgaXMgaGF2aW5nIGEgbG90IG9mIGdhcyBpbiB5b3VyIGd1dC4gCg==\r\n' $i >>header.in$n 615 i=$((i + 1)) 616 done 617 printf '\r\n' >>header.in$n 618 619 # send the requests then wait for named to close the socket. 620 URL="http://10.53.0.3:${EXTRAPORT1}/xml/v3/status" 621 "${CURL}" --silent --include --get "$URL" --next --get --header @header.in$n "$URL" >curl.out$n && ret=1 622 # we expect 1 request to be processed. 623 lines=$(grep -c "^<statistics version" curl.out$n) 624 test "$lines" = 1 || ret=1 625else 626 echo_i "skipping test as curl with --next support not found" 627fi 628if [ $ret != 0 ]; then echo_i "failed"; fi 629status=$((status + ret)) 630n=$((n + 1)) 631 632echo_i "Check HTTP/1.1 keep-alive with truncated stream ($n)" 633ret=0 634i=0 635if [ -x "${CURL_NEXT}" ]; then 636 # build input stream. 637 printf 'X-Bloat: ' >header.in$n 638 while test $i -lt 5000; do 639 printf '%s' "VGhlIG1vc3QgY29tbW9uIHJlYXNvbiBmb3IgYmxvYXRpbmcgaXMgaGF2aW5nIGEgbG90IG9mIGdhcyBpbiB5b3VyIGd1dC4gCg==" >>header.in$n 640 i=$((i + 1)) 641 done 642 printf '\r\n' >>header.in$n 643 644 # send the requests then wait for named to close the socket. 645 URL="http://10.53.0.3:${EXTRAPORT1}/xml/v3/status" 646 "${CURL}" --silent --include --get "$URL" --next --get --header @header.in$n "$URL" >curl.out$n && ret=1 647 # we expect 1 request to be processed. 648 lines=$(grep -c "^<statistics version" curl.out$n) 649 test "$lines" = 1 || ret=1 650else 651 echo_i "skipping test as curl with --next support not found" 652fi 653if [ $ret != 0 ]; then echo_i "failed"; fi 654status=$((status + ret)) 655n=$((n + 1)) 656 657echo_i "Check that consequtive responses do not grow excessively ($n)" 658ret=0 659i=0 660if [ -x "${CURL}" ]; then 661 URL="http://10.53.0.3:${EXTRAPORT1}/json/v1" 662 "${CURL}" --silent --include --header "Accept-Encoding: deflate, gzip, br, zstd" "$URL" "$URL" "$URL" "$URL" "$URL" "$URL" "$URL" "$URL" "$URL" "$URL" >curl.out$n || ret=1 663 grep -a Content-Length curl.out$n | awk 'BEGIN { prev=0; } { if (prev != 0 && $2 - prev > 100) { exit(1); } prev = $2; }' || ret=1 664else 665 echo_i "skipping test as curl not found" 666fi 667if [ $ret != 0 ]; then echo_i "failed"; fi 668status=$((status + ret)) 669n=$((n + 1)) 670 671echo_i "Check if-modified-since works ($n)" 672ret=0 673if $FEATURETEST --have-libxml2 && [ -x "${CURL}" ]; then 674 URL="http://10.53.0.3:${EXTRAPORT1}/bind9.xsl" 675 # ensure over-long time stamps are ignored 676 ${CURL} --silent --show-error --fail --output bind9.xsl.2 $URL \ 677 --header 'If-Modified-Since: 0123456789 0123456789 0123456789 0123456789 0123456789 0123456789' 678 if ! [ bind9.xsl.2 -nt bind9.xsl.1 ] \ 679 || ! ${CURL} --silent --show-error --fail \ 680 --output bind9.xsl.3 $URL \ 681 --time-cond bind9.xsl.1 \ 682 || [ -f bind9.xsl.3 ]; then 683 ret=1 684 fi 685else 686 echo_i "skipping test: requires libxml2 and curl" 687fi 688if [ $ret != 0 ]; then echo_i "failed"; fi 689status=$((status + ret)) 690n=$((n + 1)) 691 692echo_i "Checking that there are no 'first refresh' zones in ns3 ($n)" 693ret=0 694$RNDCCMD 10.53.0.3 status | grep -E '^xfers first refresh: 0$' >/dev/null || ret=1 695if [ $ret != 0 ]; then echo_i "failed"; fi 696status=$((status + ret)) 697n=$((n + 1)) 698 699echo_i "Transfering zones from ns1 to ns3 in slow mode ($n)" 700ret=0 701i=0 702# Restart ns1 with '-T transferslowly' to see the xfrins information in ns3's statschannel while it's ongoing 703stop_server ns1 704start_server --noclean --restart --port ${PORT} ns1 -- "-D statschannel-ns1 $NS_PARAMS -T transferslowly" 705# Request a retransfer of the secondary zones 706nextpart ns3/named.run >/dev/null 707$RNDCCMD 10.53.0.3 retransfer example | sed "s/^/ns3 /" | cat_i 708$RNDCCMD 10.53.0.3 retransfer example-tcp | sed "s/^/ns3 /" | cat_i 709$RNDCCMD 10.53.0.3 retransfer example-tls | sed "s/^/ns3 /" | cat_i 710$RNDCCMD 10.53.0.3 addzone 'example-new { type secondary; primaries { 10.53.0.1; }; file "example-new.db"; };' 2>&1 | sed "s/^/ns3 /" | cat_i 711wait_for_log_fast 200 "zone example/IN: Transfer started" ns3/named.run || ret=1 712if [ $ret != 0 ]; then echo_i "failed"; fi 713status=$((status + ret)) 714n=$((n + 1)) 715 716_wait_for_transfers() { 717 if [ "$PERL_XML" ]; then 718 getxfrins xml x$n || return 1 719 720 # XML is encoded in one line, use awk to separate each transfer 721 # with a newline 722 723 # We expect 4 transfers 724 count=$(awk '{ gsub("<xfrin ", "\n<xfrin ") } 1' xfrins.xml.x$n | grep -c -E '<state>(Zone Transfer Request|First Data|Receiving AXFR Data)</state>') 725 if [ $count != 4 ]; then return 1; fi 726 727 # We expect 3 of 4 to be retransfers 728 count=$(awk '{ gsub("<xfrin ", "\n<xfrin ") } 1' xfrins.xml.x$n | grep -c -F '<firstrefresh>No</firstrefresh>') 729 if [ $count != 3 ]; then return 1; fi 730 731 # We expect 1 of 4 to be a new transfer 732 count=$(awk '{ gsub("<xfrin ", "\n<xfrin ") } 1' xfrins.xml.x$n | grep -c -F '<firstrefresh>Yes</firstrefresh>') 733 if [ $count != 1 ]; then return 1; fi 734 fi 735 736 if [ "$PERL_JSON" ]; then 737 getxfrins json j$n || return 1 738 739 # We expect 4 transfers 740 count=$(grep -c -E '"state":"(Zone Transfer Request|First Data|Receiving AXFR Data)"' xfrins.json.j$n) 741 if [ $count != 4 ]; then return 1; fi 742 743 # We expect 3 of 4 to be retransfers 744 count=$(grep -c -F '"firstrefresh":"No"' xfrins.json.j$n) 745 if [ $count != 3 ]; then return 1; fi 746 747 # We expect 1 of 4 to be a new transfer 748 count=$(grep -c -F '"firstrefresh":"Yes"' xfrins.json.j$n) 749 if [ $count != 1 ]; then return 1; fi 750 fi 751} 752 753# We have now less than one second to catch the zone transfers in progress 754echo_i "Checking zone transfer information in the statistics channel ($n)" 755ret=0 756retry_quiet_fast 200 _wait_for_transfers || ret=1 757if [ $ret != 0 ]; then echo_i "failed"; fi 758status=$((status + ret)) 759n=$((n + 1)) 760 761echo_i "Checking that there is one 'first refresh' zone in ns3 ($n)" 762ret=0 763$RNDCCMD 10.53.0.3 status | grep -E '^xfers first refresh: 1$' >/dev/null || ret=1 764if [ $ret != 0 ]; then echo_i "failed"; fi 765status=$((status + ret)) 766n=$((n + 1)) 767 768if [ "$PERL_JSON" ]; then 769 echo_i "Checking zone transfer transports ($n)" 770 ret=0 771 cp xfrins.json.j$((n - 2)) xfrins.json.j$n 772 $PERL xfrins-json.pl xfrins.json.j$n example >xfrins.example.format$n 773 echo "soatransport: UDP" >xfrins.example.expect$n 774 echo "transport: TCP" >>xfrins.example.expect$n 775 cmp xfrins.example.format$n xfrins.example.expect$n || ret=1 776 $PERL xfrins-json.pl xfrins.json.j$n example-tcp >xfrins.example-tcp.format$n 777 echo "soatransport: TCP" >xfrins.example-tcp.expect$n 778 echo "transport: TCP" >>xfrins.example-tcp.expect$n 779 cmp xfrins.example-tcp.format$n xfrins.example-tcp.expect$n || ret=1 780 $PERL xfrins-json.pl xfrins.json.j$n example-tls >xfrins.example-tls.format$n 781 echo "soatransport: TLS" >xfrins.example-tls.expect$n 782 echo "transport: TLS" >>xfrins.example-tls.expect$n 783 cmp xfrins.example-tls.format$n xfrins.example-tls.expect$n || ret=1 784 if [ $ret != 0 ]; then echo_i "failed"; fi 785 status=$((status + ret)) 786 n=$((n + 1)) 787fi 788 789echo_i "Wait for slow zone transfer to complete ($n)" 790ret=0 791wait_for_log 20 "zone example/IN: zone transfer finished: success" ns3/named.run || ret=1 792if [ $ret != 0 ]; then echo_i "failed"; fi 793status=$((status + ret)) 794n=$((n + 1)) 795 796echo_i "exit status: $status" 797[ $status -eq 0 ] || exit 1 798