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 19dig_with_opts() { 20 "${DIG}" -p "${PORT}" "$@" 21} 22 23rndccmd() { 24 "${RNDC}" -p "${CONTROLPORT}" -c ../_common/rndc.conf -s "$@" 25} 26 27status=0 28n=0 29 30n=$((n + 1)) 31echo_i "initializing TCP statistics ($n)" 32ret=0 33rndccmd 10.53.0.1 stats || ret=1 34rndccmd 10.53.0.2 stats || ret=1 35mv ns1/named.stats ns1/named.stats.test$n 36mv ns2/named.stats ns2/named.stats.test$n 37ntcp10="$(grep "TCP requests received" ns1/named.stats.test$n | tail -1 | awk '{print $1}')" 38ntcp20="$(grep "TCP requests received" ns2/named.stats.test$n | tail -1 | awk '{print $1}')" 39if [ $ret != 0 ]; then echo_i "failed"; fi 40status=$((status + ret)) 41 42n=$((n + 1)) 43echo_i "checking TCP request statistics (resolver) ($n)" 44ret=0 45dig_with_opts @10.53.0.3 txt.example. >dig.out.test$n 46sleep 1 47rndccmd 10.53.0.1 stats || ret=1 48rndccmd 10.53.0.2 stats || ret=1 49mv ns1/named.stats ns1/named.stats.test$n 50mv ns2/named.stats ns2/named.stats.test$n 51ntcp11="$(grep "TCP requests received" ns1/named.stats.test$n | tail -1 | awk '{print $1}')" 52ntcp21="$(grep "TCP requests received" ns2/named.stats.test$n | tail -1 | awk '{print $1}')" 53if [ "$ntcp10" -ge "$ntcp11" ]; then ret=1; fi 54if [ "$ntcp20" -ne "$ntcp21" ]; then ret=1; fi 55if [ $ret != 0 ]; then echo_i "failed"; fi 56status=$((status + ret)) 57 58n=$((n + 1)) 59echo_i "checking TCP request statistics (forwarder) ($n)" 60ret=0 61dig_with_opts @10.53.0.4 txt.example. >dig.out.test$n 62sleep 1 63rndccmd 10.53.0.1 stats || ret=1 64rndccmd 10.53.0.2 stats || ret=1 65mv ns1/named.stats ns1/named.stats.test$n 66mv ns2/named.stats ns2/named.stats.test$n 67ntcp12="$(grep "TCP requests received" ns1/named.stats.test$n | tail -1 | awk '{print $1}')" 68ntcp22="$(grep "TCP requests received" ns2/named.stats.test$n | tail -1 | awk '{print $1}')" 69if [ "$ntcp11" -ne "$ntcp12" ]; then ret=1; fi 70if [ "$ntcp21" -ge "$ntcp22" ]; then ret=1; fi 71if [ $ret != 0 ]; then echo_i "failed"; fi 72status=$((status + ret)) 73 74# -------- TCP high-water tests ---------- 75refresh_tcp_stats() { 76 rndccmd 10.53.0.5 status >rndc.out.$n || ret=1 77 TCP_CUR="$(sed -n "s/^tcp clients: \([0-9][0-9]*\).*/\1/p" rndc.out.$n)" 78 TCP_LIMIT="$(sed -n "s/^tcp clients: .*\/\([0-9][0-9]*\)/\1/p" rndc.out.$n)" 79 TCP_HIGH="$(sed -n "s/^TCP high-water: \([0-9][0-9]*\)/\1/p" rndc.out.$n)" 80} 81 82# Send a command to the tool script listening on 10.53.0.6. 83send_command() { 84 nextpart ans6/ans.run >/dev/null 85 echo "$*" | send 10.53.0.6 "${CONTROLPORT}" 86 wait_for_log_peek 10 "result=" ans6/ans.run || ret=1 87 if ! nextpartpeek ans6/ans.run | grep -qF "result=OK"; then 88 return 1 89 fi 90} 91 92# Instructs ans6 to open $1 TCP connections to 10.53.0.5. 93open_connections() { 94 send_command "open" "${1}" 10.53.0.5 "${PORT}" || return 1 95} 96 97# Instructs ans6 to close $1 TCP connections to 10.53.0.5. 98close_connections() { 99 send_command "close" "${1}" || return 1 100} 101 102# Check TCP connections are working normally before opening 103# multiple connections 104n=$((n + 1)) 105echo_i "checking TCP query repsonse ($n)" 106ret=0 107dig_with_opts +tcp @10.53.0.5 txt.example >dig.out.test$n 108grep "status: NXDOMAIN" dig.out.test$n >/dev/null || ret=1 109if [ $ret != 0 ]; then echo_i "failed"; fi 110status=$((status + ret)) 111 112# Check TCP statistics after server startup before using them as a baseline for 113# subsequent checks. 114n=$((n + 1)) 115echo_i "TCP high-water: check initial statistics ($n)" 116ret=0 117refresh_tcp_stats 118assert_int_equal "${TCP_CUR}" 0 "current TCP clients count" || ret=1 119# We compare initial tcp-highwater value with 1 because as part of the 120# system test startup, the script start.pl executes dig to check if target 121# named is running, and that increments tcp-quota by one. 122assert_int_equal "${TCP_HIGH}" 1 "tcp-highwater count" || ret=1 123if [ $ret != 0 ]; then echo_i "failed"; fi 124status=$((status + ret)) 125 126# Ensure the TCP high-water statistic gets updated after some TCP connections 127# are established. 128n=$((n + 1)) 129echo_i "TCP high-water: check value after some TCP connections are established ($n)" 130ret=0 131OLD_TCP_CUR="${TCP_CUR}" 132TCP_ADDED=9 133open_connections "${TCP_ADDED}" || ret=1 134check_stats_added() { 135 refresh_tcp_stats 136 assert_int_equal "${TCP_CUR}" $((OLD_TCP_CUR + TCP_ADDED)) "current TCP clients count" || return 1 137 assert_int_equal "${TCP_HIGH}" $((OLD_TCP_CUR + TCP_ADDED)) "TCP high-water value" || return 1 138} 139retry 2 check_stats_added || ret=1 140if [ $ret != 0 ]; then echo_i "failed"; fi 141status=$((status + ret)) 142 143# Ensure the TCP high-water statistic remains unchanged after some TCP 144# connections are closed. 145n=$((n + 1)) 146echo_i "TCP high-water: check value after some TCP connections are closed ($n)" 147ret=0 148OLD_TCP_CUR="${TCP_CUR}" 149OLD_TCP_HIGH="${TCP_HIGH}" 150TCP_REMOVED=5 151close_connections "${TCP_REMOVED}" || ret=1 152check_stats_removed() { 153 refresh_tcp_stats 154 assert_int_equal "${TCP_CUR}" $((OLD_TCP_CUR - TCP_REMOVED)) "current TCP clients count" || return 1 155 assert_int_equal "${TCP_HIGH}" "${OLD_TCP_HIGH}" "TCP high-water value" || return 1 156} 157retry 2 check_stats_removed || ret=1 158if [ $ret != 0 ]; then echo_i "failed"; fi 159status=$((status + ret)) 160 161# Ensure the TCP high-water statistic never exceeds the configured TCP clients 162# limit. 163n=$((n + 1)) 164echo_i "TCP high-water: ensure tcp-clients is an upper bound ($n)" 165ret=0 166open_connections $((TCP_LIMIT + 1)) || ret=1 167check_stats_limit() { 168 refresh_tcp_stats 169 assert_int_equal "${TCP_CUR}" "${TCP_LIMIT}" "current TCP clients count" || return 1 170 assert_int_equal "${TCP_HIGH}" "${TCP_LIMIT}" "TCP high-water value" || return 1 171} 172retry 2 check_stats_limit || ret=1 173if [ $ret != 0 ]; then echo_i "failed"; fi 174status=$((status + ret)) 175 176# Check TCP connections are working normally before opening 177# multiple connections 178n=$((n + 1)) 179echo_i "checking TCP response recovery ($n)" 180ret=0 181# "0" closes all connections 182close_connections 0 || ret=1 183dig_with_opts +tcp @10.53.0.5 txt.example >dig.out.test$n || ret=1 184grep "status: NXDOMAIN" dig.out.test$n >/dev/null || ret=1 185if [ $ret != 0 ]; then echo_i "failed"; fi 186status=$((status + ret)) 187 188#################################################### 189# NOTE: The next test resets the debug level to 1. # 190#################################################### 191 192n=$((n + 1)) 193echo_i "checking that BIND 9 doesn't crash on long TCP messages ($n)" 194ret=0 195# Avoid logging useless information. 196rndccmd 10.53.0.1 trace 1 || ret=1 197{ $PERL ../packet.pl -a "10.53.0.1" -p "${PORT}" -t tcp -r 300000 1996-alloc_dnsbuf-crash-test.pkt || ret=1; } | cat_i 198dig_with_opts +tcp @10.53.0.1 txt.example >dig.out.test$n || ret=1 199if [ $ret != 0 ]; then echo_i "failed"; fi 200status=$((status + ret)) 201 202echo_i "exit status: $status" 203[ $status -eq 0 ] || exit 1 204