1 1.1.1.5 christos #!/bin/sh 2 1.1.1.5 christos 3 1.1 christos # Copyright (C) Internet Systems Consortium, Inc. ("ISC") 4 1.1 christos # 5 1.1.1.4 christos # SPDX-License-Identifier: MPL-2.0 6 1.1.1.4 christos # 7 1.1 christos # This Source Code Form is subject to the terms of the Mozilla Public 8 1.1.1.4 christos # License, v. 2.0. If a copy of the MPL was not distributed with this 9 1.1.1.3 christos # file, you can obtain one at https://mozilla.org/MPL/2.0/. 10 1.1 christos # 11 1.1 christos # See the COPYRIGHT file distributed with this work for additional 12 1.1 christos # information regarding copyright ownership. 13 1.1 christos 14 1.1.1.6 christos set -e 15 1.1.1.6 christos 16 1.1 christos # test response rate limiting 17 1.1 christos 18 1.1.1.6 christos . ../conf.sh 19 1.1 christos 20 1.1.1.6 christos RNDCCMD="$RNDC -c ../_common/rndc.conf -p ${CONTROLPORT} -s" 21 1.1 christos 22 1.1 christos #set -x 23 1.1 christos 24 1.1.1.6 christos ns1=10.53.0.1 # root, defining the others 25 1.1.1.6 christos ns2=10.53.0.2 # test server 26 1.1.1.6 christos ns3=10.53.0.3 # secondary test server 27 1.1.1.6 christos ns4=10.53.0.4 # log-only test server 28 1.1.1.6 christos ns7=10.53.0.7 # whitelisted client 29 1.1 christos 30 1.1 christos USAGE="$0: [-x]" 31 1.1 christos while getopts "x" c; do 32 1.1.1.6 christos case $c in 33 1.1.1.6 christos x) set -x ;; 34 1.1.1.6 christos *) 35 1.1.1.6 christos echo "$USAGE" 1>&2 36 1.1.1.6 christos exit 1 37 1.1.1.6 christos ;; 38 1.1.1.6 christos esac 39 1.1 christos done 40 1.1.1.6 christos shift $((OPTIND - 1)) 41 1.1 christos if test "$#" -ne 0; then 42 1.1.1.6 christos echo "$USAGE" 1>&2 43 1.1.1.6 christos exit 1 44 1.1 christos fi 45 1.1 christos # really quit on control-C 46 1.1 christos trap 'exit 1' 1 2 15 47 1.1 christos 48 1.1 christos ret=0 49 1.1.1.6 christos setret() { 50 1.1.1.6 christos ret=1 51 1.1.1.6 christos echo_i "$*" 52 1.1 christos } 53 1.1 christos 54 1.1 christos # Wait until soon after the start of a second to make results consistent. 55 1.1 christos # The start of a second credits a rate limit. 56 1.1 christos # This would be far easier in C or by assuming a modern version of perl. 57 1.1.1.6 christos sec_start() { 58 1.1.1.6 christos START=$(date) 59 1.1.1.6 christos while true; do 60 1.1.1.6 christos NOW=$(date) 61 1.1.1.6 christos if test "$START" != "$NOW"; then 62 1.1.1.6 christos return 63 1.1.1.6 christos fi 64 1.1.1.6 christos $PERL -e 'select(undef, undef, undef, 0.05)' || true 65 1.1.1.6 christos done 66 1.1 christos } 67 1.1 christos 68 1.1 christos # turn off ${HOME}/.digrc 69 1.1.1.6 christos HOME=/dev/null 70 1.1.1.6 christos export HOME 71 1.1 christos 72 1.1 christos # $1=number of tests $2=target domain $3=dig options 73 1.1 christos QNUM=1 74 1.1.1.6 christos burst() { 75 1.1.1.6 christos BURST_LIMIT=$1 76 1.1.1.6 christos shift 77 1.1.1.6 christos BURST_DOM_BASE="$1" 78 1.1.1.6 christos shift 79 1.1.1.6 christos 80 1.1.1.6 christos XCNT=$CNT 81 1.1.1.6 christos CNT='XXX' 82 1.1.1.6 christos eval FILENAME="mdig.out-$BURST_DOM_BASE" 83 1.1.1.6 christos CNT=$XCNT 84 1.1.1.6 christos 85 1.1.1.6 christos DOMS="" 86 1.1.1.6 christos CNTS=$($PERL -e 'for ( $i = 0; $i < '$BURST_LIMIT'; $i++) { printf "%03d\n", '$QNUM' + $i; }') 87 1.1.1.6 christos for CNT in $CNTS; do 88 1.1.1.6 christos eval BURST_DOM="$BURST_DOM_BASE" 89 1.1.1.6 christos DOMS="$DOMS $BURST_DOM" 90 1.1.1.6 christos done 91 1.1.1.6 christos ARGS="+burst +nocookie +continue +time=1 +tries=1 -p ${PORT} $* @$ns2 $DOMS" 92 1.1.1.6 christos $MDIG $ARGS 2>&1 \ 93 1.1.1.6 christos | tee -a full-$FILENAME \ 94 1.1.1.6 christos | sed -n -e '/^;; AUTHORITY/,/^$/d' \ 95 1.1.1.6 christos -e '/^;; ADDITIONAL/,/^$/d' \ 96 1.1.1.6 christos -e 's/^[^;].* \([^ ]\{1,\}\)$/\1/p' \ 97 1.1.1.6 christos -e 's/;; flags.* tc .*/TC/p' \ 98 1.1.1.6 christos -e 's/;; .* status: NXDOMAIN.*/NXDOMAIN/p' \ 99 1.1.1.6 christos -e 's/;; .* status: NOERROR.*/NOERROR/p' \ 100 1.1.1.6 christos -e 's/;; .* status: SERVFAIL.*/SERVFAIL/p' \ 101 1.1.1.6 christos -e 's/response failed with timed out.*/drop/p' \ 102 1.1.1.6 christos -e 's/;; communications error to.*/drop/p' >>$FILENAME & 103 1.1.1.6 christos QNUM=$((QNUM + BURST_LIMIT)) 104 1.1 christos } 105 1.1 christos 106 1.1 christos # compare integers $1 and $2; ensure the difference is no more than $3 107 1.1.1.6 christos range() { 108 1.1.1.6 christos $PERL -e 'if (abs(int($ARGV[0]) - int($ARGV[1])) > int($ARGV[2])) { exit(1) }' $1 $2 $3 109 1.1 christos } 110 1.1 christos 111 1.1 christos # $1=domain $2=IP address $3=# of IP addresses $4=TC $5=drop 112 1.1 christos # $6=NXDOMAIN $7=SERVFAIL or other errors 113 1.1 christos ck_result() { 114 1.1.1.6 christos # wait to the background mdig calls to complete. 115 1.1.1.6 christos wait 116 1.1.1.6 christos BAD=no 117 1.1.1.6 christos ADDRS=$(grep -E "^$2$" mdig.out-$1 2>/dev/null | wc -l) 118 1.1.1.6 christos # count simple truncated and truncated NXDOMAIN as TC 119 1.1.1.6 christos TC=$(grep -E "^TC|NXDOMAINTC$" mdig.out-$1 2>/dev/null | wc -l) 120 1.1.1.6 christos DROP=$(grep -E "^drop$" mdig.out-$1 2>/dev/null | wc -l) 121 1.1.1.6 christos # count NXDOMAIN and truncated NXDOMAIN as NXDOMAIN 122 1.1.1.6 christos NXDOMAIN=$(grep -E "^NXDOMAIN|NXDOMAINTC$" mdig.out-$1 2>/dev/null | wc -l) 123 1.1.1.6 christos SERVFAIL=$(grep -E "^SERVFAIL$" mdig.out-$1 2>/dev/null | wc -l) 124 1.1.1.6 christos NOERROR=$(grep -E "^NOERROR$" mdig.out-$1 2>/dev/null | wc -l) 125 1.1.1.6 christos 126 1.1.1.6 christos range $ADDRS "$3" 1 \ 127 1.1.1.6 christos || setret "$ADDRS instead of $3 '$2' responses for $1" \ 128 1.1.1.6 christos && BAD=yes 129 1.1.1.6 christos 130 1.1.1.6 christos range $TC "$4" 1 \ 131 1.1.1.6 christos || setret "$TC instead of $4 truncation responses for $1" \ 132 1.1.1.6 christos && BAD=yes 133 1.1.1.6 christos 134 1.1.1.6 christos range $DROP "$5" 1 \ 135 1.1.1.6 christos || setret "$DROP instead of $5 dropped responses for $1" \ 136 1.1.1.6 christos && BAD=yes 137 1.1.1.6 christos 138 1.1.1.6 christos range $NXDOMAIN "$6" 1 \ 139 1.1.1.6 christos || setret "$NXDOMAIN instead of $6 NXDOMAIN responses for $1" \ 140 1.1.1.6 christos && BAD=yes 141 1.1.1.6 christos 142 1.1.1.6 christos range $SERVFAIL "$7" 1 \ 143 1.1.1.6 christos || setret "$SERVFAIL instead of $7 error responses for $1" \ 144 1.1.1.6 christos && BAD=yes 145 1.1.1.6 christos 146 1.1.1.6 christos range $NOERROR "$8" 1 \ 147 1.1.1.6 christos || setret "$NOERROR instead of $8 NOERROR responses for $1" \ 148 1.1.1.6 christos && BAD=yes 149 1.1.1.6 christos 150 1.1.1.6 christos if test -z "$BAD"; then 151 1.1.1.6 christos rm -f mdig.out-$1 152 1.1.1.6 christos fi 153 1.1 christos } 154 1.1 christos 155 1.1.1.6 christos ckstats() { 156 1.1.1.6 christos LABEL="$1" 157 1.1.1.6 christos shift 158 1.1.1.6 christos TYPE="$1" 159 1.1.1.6 christos shift 160 1.1.1.6 christos EXPECTED="$1" 161 1.1.1.6 christos shift 162 1.1.1.6 christos C=$(cat ns2/named.stats \ 163 1.1.1.6 christos | sed -n -e "s/[ ]*\([0-9]*\).responses $TYPE for rate limits.*/\1/p" \ 164 1.1.1.6 christos | tail -1) 165 1.1.1.6 christos C=$((C)) 166 1.1 christos 167 1.1.1.6 christos range "$C" $EXPECTED 1 \ 168 1.1.1.6 christos || setret "wrong $LABEL $TYPE statistics of $C instead of $EXPECTED" 169 1.1 christos } 170 1.1 christos 171 1.1 christos ######### 172 1.1 christos sec_start 173 1.1 christos 174 1.1.1.6 christos # Tests of referrals to "." must be done before the hints are loaded. 175 1.1 christos burst 5 a1.tld3 +norec 176 1.1 christos # basic rate limiting 177 1.1 christos burst 3 a1.tld2 178 1.1 christos # delay allows an additional response. 179 1.1 christos sleep 1 180 1.1 christos burst 10 a1.tld2 181 1.1 christos # Request 30 different qnames to try a wildcard. 182 1.1.1.4 christos burst 30 'y.x$CNT.a2.tld2' 183 1.1 christos 184 1.1 christos # IP TC drop NXDOMAIN SERVFAIL NOERROR 185 1.1 christos # referrals to "." 186 1.1.1.6 christos ck_result a1.tld3 x 0 1 2 0 0 2 187 1.1 christos # check 13 results including 1 second delay that allows an additional response 188 1.1.1.6 christos ck_result a1.tld2 192.0.2.1 3 4 6 0 0 8 189 1.1 christos 190 1.1.1.4 christos # Check the wildcard answers. 191 1.1.1.4 christos # The zone origin name of the 30 requests is counted. 192 1.1.1.6 christos ck_result 'y.x*.a2.tld2' 192.0.2.2 2 10 18 0 0 12 193 1.1 christos 194 1.1 christos ######### 195 1.1 christos sec_start 196 1.1 christos 197 1.1 christos burst 10 'x.a3.tld3' 198 1.1 christos burst 10 'y$CNT.a3.tld3' 199 1.1 christos burst 10 'z$CNT.a4.tld2' 200 1.1 christos 201 1.1 christos # 10 identical recursive responses are limited 202 1.1.1.6 christos ck_result 'x.a3.tld3' 192.0.3.3 2 3 5 0 0 5 203 1.1 christos 204 1.1 christos # 10 different recursive responses are not limited 205 1.1.1.6 christos ck_result 'y*.a3.tld3' 192.0.3.3 10 0 0 0 0 10 206 1.1 christos 207 1.1 christos # 10 different NXDOMAIN responses are limited based on the parent name. 208 1.1 christos # We count 13 responses because we count truncated NXDOMAIN responses 209 1.1 christos # as both truncated and NXDOMAIN. 210 1.1.1.6 christos ck_result 'z*.a4.tld2' x 0 3 5 5 0 0 211 1.1 christos 212 1.1 christos $RNDCCMD $ns2 stats 213 1.1 christos ckstats first dropped 36 214 1.1 christos ckstats first truncated 21 215 1.1 christos 216 1.1 christos ######### 217 1.1 christos sec_start 218 1.1 christos 219 1.1 christos burst 10 a5.tld2 +tcp 220 1.1 christos burst 10 a6.tld2 -b $ns7 221 1.1 christos burst 10 a7.tld4 222 1.1 christos burst 2 a8.tld2 -t AAAA 223 1.1 christos burst 2 a8.tld2 -t TXT 224 1.1 christos burst 2 a8.tld2 -t SPF 225 1.1 christos 226 1.1 christos # IP TC drop NXDOMAIN SERVFAIL NOERROR 227 1.1 christos # TCP responses are not rate limited 228 1.1.1.6 christos ck_result a5.tld2 192.0.2.5 10 0 0 0 0 10 229 1.1 christos 230 1.1 christos # whitelisted client is not rate limited 231 1.1.1.6 christos ck_result a6.tld2 192.0.2.6 10 0 0 0 0 10 232 1.1 christos 233 1.1 christos # Errors such as SERVFAIL are rate limited. 234 1.1.1.6 christos ck_result a7.tld4 x 0 0 8 0 2 0 235 1.1 christos 236 1.1 christos # NODATA responses are counted as the same regardless of qtype. 237 1.1.1.6 christos ck_result a8.tld2 x 0 2 2 0 0 4 238 1.1 christos 239 1.1 christos $RNDCCMD $ns2 stats 240 1.1 christos ckstats second dropped 46 241 1.1 christos ckstats second truncated 23 242 1.1 christos 243 1.1 christos ######### 244 1.1 christos sec_start 245 1.1 christos 246 1.1 christos # IP TC drop NXDOMAIN SERVFAIL NOERROR 247 1.1 christos # all-per-second 248 1.1 christos # The qnames are all unique but the client IP address is constant. 249 1.1 christos QNUM=101 250 1.1 christos burst 60 'all$CNT.a9.tld2' 251 1.1 christos 252 1.1.1.6 christos ck_result 'a*.a9.tld2' 192.0.2.8 50 0 10 0 0 50 253 1.1 christos 254 1.1 christos $RNDCCMD $ns2 stats 255 1.1 christos ckstats final dropped 56 256 1.1 christos ckstats final truncated 23 257 1.1 christos 258 1.1 christos ######### 259 1.1 christos sec_start 260 1.1 christos 261 1.1.1.8 christos # check that "would limit" is emitted for "log-only yes;" 262 1.1 christos DIGOPTS="+nocookie +nosearch +time=1 +tries=1 +ignore -p ${PORT}" 263 1.1.1.8 christos $DIG $DIGOPTS @$ns4 A a7.tld4 >dig.out.a7.tld 2>/dev/null 264 1.1.1.8 christos # skip this check if query takes over 500ms 265 1.1.1.8 christos if grep -E ';; Query time: [1-4]?[0-9]?[0-9] msec' dig.out.a7.tld >/dev/null 2>&1; then 266 1.1.1.8 christos for i in 1 2 3 4 5; do 267 1.1.1.8 christos $DIG $DIGOPTS @$ns4 A a7.tld4 >/dev/null 2>&1 & 268 1.1.1.8 christos done 269 1.1.1.8 christos wait 270 1.1.1.8 christos grep "would limit" ns4/named.run >/dev/null 2>&1 || setret "\"would limit\" not found in log file." 271 1.1.1.8 christos fi 272 1.1 christos 273 1.1.1.4 christos # regression test for GL #2839 274 1.1.1.4 christos DIGOPTS="+bufsize=4096 +ignore -p ${PORT}" 275 1.1.1.6 christos $DIG $DIGOPTS @$ns4 TXT big.tld4 >/dev/null 2>&1 276 1.1.1.4 christos 277 1.1 christos echo_i "exit status: $ret" 278 1.1 christos [ $ret -eq 0 ] || exit 1 279