Home | History | Annotate | Line # | Download | only in forward
      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 set -e
     15 
     16 #shellcheck source=conf.sh
     17 . ../conf.sh
     18 
     19 dig_with_opts() (
     20   "$DIG" -p "$PORT" "$@"
     21 )
     22 
     23 sendcmd() (
     24   SERVER="${1}"
     25   COMMAND="${2}"
     26   COMMAND_ARGS="${3}"
     27   dig_with_opts "@${SERVER}" "${COMMAND_ARGS}.${COMMAND}._control." TXT +time=5 +tries=1 +tcp >/dev/null 2>&1
     28 )
     29 
     30 rndccmd() {
     31   "$RNDC" -c ../_common/rndc.conf -p "$CONTROLPORT" -s "$@"
     32 }
     33 
     34 root=10.53.0.1
     35 hidden=10.53.0.2
     36 f1=10.53.0.3
     37 f2=10.53.0.4
     38 
     39 status=0
     40 n=0
     41 
     42 n=$((n + 1))
     43 echo_i "checking that a forward zone overrides global forwarders ($n)"
     44 ret=0
     45 dig_with_opts +noadd +noauth txt.example1. txt @$hidden >dig.out.$n.hidden || ret=1
     46 dig_with_opts +noadd +noauth txt.example1. txt @$f1 >dig.out.$n.f1 || ret=1
     47 digcomp dig.out.$n.hidden dig.out.$n.f1 || ret=1
     48 if [ $ret != 0 ]; then echo_i "failed"; fi
     49 status=$((status + ret))
     50 
     51 n=$((n + 1))
     52 echo_i "checking that a forward first zone no forwarders recurses ($n)"
     53 ret=0
     54 dig_with_opts +noadd +noauth txt.example2. txt @$root >dig.out.$n.root || ret=1
     55 dig_with_opts +noadd +noauth txt.example2. txt @$f1 >dig.out.$n.f1 || ret=1
     56 digcomp dig.out.$n.root dig.out.$n.f1 || ret=1
     57 if [ $ret != 0 ]; then echo_i "failed"; fi
     58 status=$((status + ret))
     59 
     60 n=$((n + 1))
     61 echo_i "checking that a forward only zone no forwarders fails ($n)"
     62 ret=0
     63 dig_with_opts +noadd +noauth txt.example2. txt @$root >dig.out.$n.root || ret=1
     64 dig_with_opts +noadd +noauth txt.example2. txt @$f1 >dig.out.$n.f1 || ret=1
     65 digcomp dig.out.$n.root dig.out.$n.f1 || ret=1
     66 if [ $ret != 0 ]; then echo_i "failed"; fi
     67 status=$((status + ret))
     68 
     69 n=$((n + 1))
     70 echo_i "checking that global forwarders work ($n)"
     71 ret=0
     72 dig_with_opts +noadd +noauth txt.example4. txt @$hidden >dig.out.$n.hidden || ret=1
     73 dig_with_opts +noadd +noauth txt.example4. txt @$f1 >dig.out.$n.f1 || ret=1
     74 digcomp dig.out.$n.hidden dig.out.$n.f1 || ret=1
     75 if [ $ret != 0 ]; then echo_i "failed"; fi
     76 status=$((status + ret))
     77 
     78 n=$((n + 1))
     79 echo_i "checking that DoT expired certificate does not work ($n)"
     80 if $FEATURETEST --have-fips-dh; then
     81   ret=0
     82   nextpart ns4/named.run >/dev/null
     83   dig_with_opts +noadd +noauth txt.example4. txt @$hidden >dig.out.$n.hidden || ret=1
     84   dig_with_opts +noadd +noauth txt.example4. txt @$f2 >dig.out.$n.f2 || ret=1
     85   digcomp dig.out.$n.hidden dig.out.$n.f2 >/dev/null 2>&1 && ret=1
     86   wait_for_log 1 "TLS peer certificate verification failed" ns4/named.run || ret=1
     87   if [ $ret != 0 ]; then echo_i "failed"; fi
     88   status=$((status + ret))
     89 else
     90   echo_i "skipped."
     91 fi
     92 
     93 n=$((n + 1))
     94 echo_i "checking that a forward zone works (DoT insecure) ($n)"
     95 if $FEATURETEST --have-fips-dh; then
     96   ret=0
     97   nextpart ns4/named.run >/dev/null
     98   dig_with_opts +noadd +noauth txt.example1. txt @$hidden >dig.out.$n.hidden || ret=1
     99   dig_with_opts +noadd +noauth txt.example1. txt @$f2 >dig.out.$n.f2 || ret=1
    100   digcomp dig.out.$n.hidden dig.out.$n.f2 || ret=1
    101   wait_for_log 1 "TLS client session created for 10.53.0.2" ns4/named.run || ret=1
    102   if [ $ret != 0 ]; then echo_i "failed"; fi
    103   status=$((status + ret))
    104 else
    105   echo_i "skipped."
    106 fi
    107 
    108 n=$((n + 1))
    109 echo_i "checking that forwarding doesn't spontaneously happen ($n)"
    110 ret=0
    111 dig_with_opts +noadd +noauth txt.example2. txt @$root >dig.out.$n.root || ret=1
    112 dig_with_opts +noadd +noauth txt.example2. txt @$f2 >dig.out.$n.f2 || ret=1
    113 digcomp dig.out.$n.root dig.out.$n.f2 || ret=1
    114 if [ $ret != 0 ]; then echo_i "failed"; fi
    115 status=$((status + ret))
    116 
    117 n=$((n + 1))
    118 echo_i "checking that a forward zone with no specified policy works (DoT forward-secrecy) ($n)"
    119 if $FEATURETEST --have-fips-dh; then
    120   ret=0
    121   nextpart ns4/named.run >/dev/null
    122   dig_with_opts +noadd +noauth txt.example3. txt @$hidden >dig.out.$n.hidden || ret=1
    123   dig_with_opts +noadd +noauth txt.example3. txt @$f2 >dig.out.$n.f2 || ret=1
    124   digcomp dig.out.$n.hidden dig.out.$n.f2 || ret=1
    125   wait_for_log 1 "TLS client session created for 10.53.0.2" ns4/named.run || ret=1
    126   if [ $ret != 0 ]; then echo_i "failed"; fi
    127   status=$((status + ret))
    128 else
    129   echo_i "skipped."
    130 fi
    131 
    132 n=$((n + 1))
    133 echo_i "checking that DoT remote-hostname works ($n)"
    134 if $FEATURETEST --have-fips-dh; then
    135   ret=0
    136   nextpart ns4/named.run >/dev/null
    137   dig_with_opts +noadd +noauth txt.example8. txt @$hidden >dig.out.$n.hidden || ret=1
    138   dig_with_opts +noadd +noauth txt.example8. txt @$f2 >dig.out.$n.f2 || ret=1
    139   digcomp dig.out.$n.hidden dig.out.$n.f2 >/dev/null 2>&1 || ret=1
    140   wait_for_log 1 "TLS client session created for 10.53.0.2" ns4/named.run || ret=1
    141   if [ $ret != 0 ]; then echo_i "failed"; fi
    142   status=$((status + ret))
    143 else
    144   echo_i "skipped."
    145 fi
    146 
    147 n=$((n + 1))
    148 echo_i "checking that DoT bad remote-hostname does not work ($n)"
    149 if $FEATURETEST --have-fips-dh; then
    150   ret=0
    151   nextpart ns4/named.run >/dev/null
    152   dig_with_opts +noadd +noauth txt.example9. txt @$hidden >dig.out.$n.hidden || ret=1
    153   dig_with_opts +noadd +noauth txt.example9. txt @$f2 >dig.out.$n.f2 || ret=1
    154   digcomp dig.out.$n.hidden dig.out.$n.f2 >/dev/null 2>&1 && ret=1
    155   wait_for_log 1 "TLS peer certificate verification failed" ns4/named.run || ret=1
    156   if [ $ret != 0 ]; then echo_i "failed"; fi
    157   status=$((status + ret))
    158 else
    159   echo_i "skipped."
    160 fi
    161 
    162 n=$((n + 1))
    163 echo_i "checking that a forward only doesn't recurse ($n)"
    164 ret=0
    165 dig_with_opts txt.example5. txt @$f2 >dig.out.$n.f2 || ret=1
    166 grep "SERVFAIL" dig.out.$n.f2 >/dev/null || ret=1
    167 if [ $ret != 0 ]; then echo_i "failed"; fi
    168 status=$((status + ret))
    169 
    170 # GL#1793
    171 n=$((n + 1))
    172 echo_i "checking that the 'serverquota' counter isn't increased because of the SERVFAIL in the previous check ($n)"
    173 ret=0
    174 "${CURL}" "http://10.53.0.4:${EXTRAPORT1}/json/v1" 2>/dev/null >statschannel.out.$n
    175 grep -F "ServerQuota" statschannel.out.$n >/dev/null && ret=1
    176 if [ $ret != 0 ]; then echo_i "failed"; fi
    177 status=$((status + ret))
    178 
    179 n=$((n + 1))
    180 echo_i "checking for negative caching of forwarder response ($n)"
    181 # prime the cache, shutdown the forwarder then check that we can
    182 # get the answer from the cache.  restart forwarder.
    183 ret=0
    184 dig_with_opts nonexist. txt @10.53.0.5 >dig.out.$n.f2 || ret=1
    185 grep "status: NXDOMAIN" dig.out.$n.f2 >/dev/null || ret=1
    186 stop_server ns4 || ret=1
    187 dig_with_opts nonexist. txt @10.53.0.5 >dig.out.$n.f2 || ret=1
    188 grep "status: NXDOMAIN" dig.out.$n.f2 >/dev/null || ret=1
    189 start_server --restart --noclean --port "${PORT}" ns4 || ret=1
    190 if [ $ret != 0 ]; then echo_i "failed"; fi
    191 status=$((status + ret))
    192 
    193 check_override() (
    194   dig_with_opts 1.0.10.in-addr.arpa TXT @$f2 >dig.out.$n.f2 \
    195     && grep "status: NOERROR" dig.out.$n.f2 >/dev/null \
    196     && dig_with_opts 2.0.10.in-addr.arpa TXT @$f2 >dig.out.$n.f2 \
    197     && grep "status: NXDOMAIN" dig.out.$n.f2 >/dev/null
    198 )
    199 
    200 n=$((n + 1))
    201 echo_i "checking that forward only zone overrides empty zone (DoT forward-secrecy-mutual-tls) ($n)"
    202 if $FEATURETEST --have-fips-dh; then
    203   ret=0
    204   # retry loop in case the server restart above causes transient failure
    205   retry_quiet 10 check_override || ret=1
    206   if [ $ret != 0 ]; then echo_i "failed"; fi
    207   status=$((status + ret))
    208 else
    209   echo_i "skipped."
    210 fi
    211 
    212 n=$((n + 1))
    213 echo_i "checking that DS lookups for grafting forward zones are isolated ($n)"
    214 ret=0
    215 dig_with_opts grafted A @10.53.0.4 >dig.out.$n.q1 || ret=1
    216 dig_with_opts grafted DS @10.53.0.4 >dig.out.$n.q2 || ret=1
    217 dig_with_opts grafted A @10.53.0.4 >dig.out.$n.q3 || ret=1
    218 dig_with_opts grafted AAAA @10.53.0.4 >dig.out.$n.q4 || ret=1
    219 grep "status: NOERROR" dig.out.$n.q1 >/dev/null || ret=1
    220 grep "status: NXDOMAIN" dig.out.$n.q2 >/dev/null || ret=1
    221 grep "status: NOERROR" dig.out.$n.q3 >/dev/null || ret=1
    222 grep "status: NOERROR" dig.out.$n.q4 >/dev/null || ret=1
    223 if [ $ret != 0 ]; then echo_i "failed"; fi
    224 status=$((status + ret))
    225 
    226 n=$((n + 1))
    227 echo_i "checking that rfc1918 inherited 'forward first;' zones are warned about ($n)"
    228 ret=0
    229 $CHECKCONF rfc1918-inherited.conf | grep "forward first;" >/dev/null || ret=1
    230 $CHECKCONF rfc1918-notinherited.conf | grep "forward first;" >/dev/null && ret=1
    231 if [ $ret != 0 ]; then echo_i "failed"; fi
    232 status=$((status + ret))
    233 
    234 n=$((n + 1))
    235 echo_i "checking that ULA inherited 'forward first;' zones are warned about ($n)"
    236 ret=0
    237 $CHECKCONF ula-inherited.conf | grep "forward first;" >/dev/null || ret=1
    238 $CHECKCONF ula-notinherited.conf | grep "forward first;" >/dev/null && ret=1
    239 if [ $ret != 0 ]; then echo_i "failed"; fi
    240 status=$((status + ret))
    241 
    242 count_sent() (
    243   logfile="$1"
    244   start_pattern="$2"
    245   pattern="$3"
    246   nextpartpeek "$logfile" | sed -n "/$start_pattern/,/^\$/p" | grep -c "$pattern"
    247 )
    248 
    249 check_sent() (
    250   expected="$1"
    251   shift
    252   count=$(count_sent "$@")
    253   [ "$expected" = "$count" ]
    254 )
    255 
    256 wait_for_log() (
    257   nextpartpeek "$1" | grep "$2" >/dev/null
    258 
    259 )
    260 
    261 n=$((n + 1))
    262 echo_i "checking that a forwarder timeout prevents it from being reused in the same fetch context ($n)"
    263 ret=0
    264 # Make ans6 receive queries without responding to them.
    265 sendcmd 10.53.0.6 send-responses "disable"
    266 # Query for a record in a zone which is forwarded to a non-responding forwarder
    267 # and is delegated from the root to check whether the forwarder will be retried
    268 # when a delegation is encountered after falling back to full recursive
    269 # resolution.
    270 nextpart ns3/named.run >/dev/null
    271 dig_with_opts txt.example7. txt @$f1 >dig.out.$n.f1 || ret=1
    272 # The forwarder for the "example7" zone should only be queried once.
    273 start_pattern="sending packet to 10\.53\.0\.6"
    274 retry_quiet 5 wait_for_log ns3/named.run "$start_pattern"
    275 check_sent 1 ns3/named.run "$start_pattern" ";txt\.example7\.[[:space:]]*IN[[:space:]]*TXT$" || ret=1
    276 sendcmd 10.53.0.6 send-responses "enable"
    277 if [ $ret != 0 ]; then echo_i "failed"; fi
    278 status=$((status + ret))
    279 
    280 n=$((n + 1))
    281 echo_i "checking that priming queries are not forwarded ($n)"
    282 ret=0
    283 nextpart ns7/named.run >/dev/null
    284 dig_with_opts +noadd +noauth txt.example1. txt @10.53.0.7 >dig.out.$n.f7 || ret=1
    285 received_pattern="received packet from 10\.53\.0\.1"
    286 start_pattern="sending packet to 10\.53\.0\.1"
    287 retry_quiet 5 wait_for_log ns7/named.run "$received_pattern" || ret=1
    288 check_sent 1 ns7/named.run "$start_pattern" ";\.[[:space:]]*IN[[:space:]]*NS$" || ret=1
    289 sent=$(grep -c "10.53.0.7#.* (.): query '\./NS/IN' approved" ns4/named.run || true)
    290 [ "$sent" -eq 0 ] || ret=1
    291 sent=$(grep -c "10.53.0.7#.* (.): query '\./NS/IN' approved" ns1/named.run || true)
    292 [ "$sent" -eq 1 ] || ret=1
    293 if [ $ret != 0 ]; then echo_i "failed"; fi
    294 status=$((status + ret))
    295 
    296 n=$((n + 1))
    297 echo_i "checking recovery from forwarding to a non-recursive server ($n)"
    298 ret=0
    299 dig_with_opts xxx.sld.tld txt @10.53.0.8 >dig.out.$n.f8 || ret=1
    300 grep "status: NOERROR" dig.out.$n.f8 >/dev/null || ret=1
    301 if [ $ret != 0 ]; then echo_i "failed"; fi
    302 status=$((status + ret))
    303 
    304 n=$((n + 1))
    305 echo_i "checking that rebinding protection works in forward only mode ($n)"
    306 ret=0
    307 # 10.53.0.5 will forward target.malicious. query to 10.53.0.4
    308 # which in turn will return a CNAME for subdomain.rebind.
    309 # to honor the option deny-answer-aliases { "rebind"; };
    310 # ns5 should return a SERVFAIL to avoid potential rebinding attacks
    311 dig_with_opts +noadd +noauth @10.53.0.5 target.malicious. >dig.out.$n || ret=1
    312 grep "status: SERVFAIL" dig.out.$n >/dev/null || ret=1
    313 if [ $ret != 0 ]; then echo_i "failed"; fi
    314 status=$((status + ret))
    315 
    316 n=$((n + 1))
    317 echo_i "checking switch from forwarding to normal resolution while chasing DS ($n)"
    318 ret=0
    319 cp ns3/named2.conf ns3/named.conf
    320 rndccmd 10.53.0.3 reconfig 2>&1 | sed 's/^/ns3 /' | cat_i
    321 sleep 1
    322 nextpart ns3/named.run >/dev/null
    323 dig_with_opts @$f1 xxx.yyy.sld.tld ds >dig.out.$n.f1 || ret=1
    324 grep "status: SERVFAIL" dig.out.$n.f1 >/dev/null || ret=1
    325 if [ $ret != 0 ]; then echo_i "failed"; fi
    326 status=$((status + ret))
    327 
    328 # See [GL #3129].
    329 # Enable silent mode for ans11.
    330 sendcmd 10.53.0.11 send-responses "disable"
    331 n=$((n + 1))
    332 echo_i "checking the handling of hung DS fetch while chasing DS ($n)"
    333 ret=0
    334 cp ns3/named2.conf ns3/named.conf
    335 rndccmd 10.53.0.3 reconfig 2>&1 | sed 's/^/ns3 /' | cat_i
    336 rndccmd 10.53.0.3 flush 2>&1 | sed 's/^/ns3 /' | cat_i
    337 sleep 1
    338 nextpart ns3/named.run >/dev/null
    339 dig_with_opts @$f1 xxx.yyy.sld.tld ds >dig.out.$n.f1 || ret=1
    340 grep "status: SERVFAIL" dig.out.$n.f1 >/dev/null || ret=1
    341 # Disable silent mode for ans11.
    342 sendcmd 10.53.0.11 send-responses "enable"
    343 if [ $ret != 0 ]; then echo_i "failed"; fi
    344 status=$((status + ret))
    345 
    346 #
    347 # Check various spoofed response scenarios. The same tests will be
    348 # run twice, with "forward first" and "forward only" configurations.
    349 #
    350 run_spooftests() {
    351   n=$((n + 1))
    352   echo_i "checking spoofed response scenario 1 - out of bailiwick NS ($n)"
    353   ret=0
    354   # prime
    355   dig_with_opts @10.53.0.9 attackSecureDomain.net >dig.out.$n.prime || ret=1
    356   # check 'net' is not poisoned.
    357   dig_with_opts @10.53.0.9 diditwork.net. TXT >dig.out.$n.net || ret=1
    358   grep '^diditwork\.net\..*TXT.*"recursed"' dig.out.$n.net >/dev/null || ret=1
    359   # check 'sub.local.net' is not poisoned.
    360   dig_with_opts @10.53.0.9 sub.local.net TXT >dig.out.$n.sub || ret=1
    361   grep '^sub\.local\.net\..*TXT.*"recursed"' dig.out.$n.sub >/dev/null || ret=1
    362   if [ $ret != 0 ]; then echo_i "failed"; fi
    363   status=$((status + ret))
    364 
    365   n=$((n + 1))
    366   echo_i "checking spoofed response scenario 2 - inject DNAME/net2. ($n)"
    367   ret=0
    368   # prime
    369   dig_with_opts @10.53.0.9 attackSecureDomain.net2 >dig.out.$n.prime || ret=1
    370   # check that net2/DNAME is not cached
    371   dig_with_opts @10.53.0.9 net2. DNAME >dig.out.$n.net2 || ret=1
    372   grep "ANSWER: 0," dig.out.$n.net2 >/dev/null || ret=1
    373   grep "status: NXDOMAIN" dig.out.$n.net2 >/dev/null || ret=1
    374   if [ $ret != 0 ]; then echo_i "failed"; fi
    375   status=$((status + ret))
    376 
    377   n=$((n + 1))
    378   echo_i "checking spoofed response scenario 3 - extra answer ($n)"
    379   ret=0
    380   # prime
    381   dig_with_opts @10.53.0.9 attackSecureDomain.net3 >dig.out.$n.prime || ret=1
    382   # check extra net3 records are not cached
    383   rndccmd 10.53.0.9 dumpdb -cache 2>&1 | sed 's/^/ns9 /' | cat_i
    384   for try in 1 2 3 4 5; do
    385     lines=$(grep "net3" ns9/named_dump.db | wc -l)
    386     if [ ${lines} -eq 0 ]; then
    387       sleep 1
    388       continue
    389     fi
    390     [ ${lines} -eq 1 ] || ret=1
    391     grep -q '^attackSecureDomain.net3' ns9/named_dump.db || ret=1
    392     grep -q '^local.net3' ns9/named_dump.db && ret=1
    393   done
    394   if [ $ret != 0 ]; then echo_i "failed"; fi
    395   status=$((status + ret))
    396 }
    397 
    398 echo_i "checking spoofed response scenarios with forward first zones"
    399 run_spooftests
    400 
    401 cp ns9/named2.conf ns9/named.conf
    402 rndccmd 10.53.0.9 reconfig 2>&1 | sed 's/^/ns3 /' | cat_i
    403 rndccmd 10.53.0.9 flush 2>&1 | sed 's/^/ns3 /' | cat_i
    404 sleep 1
    405 
    406 echo_i "rechecking spoofed response scenarios with forward only zones"
    407 run_spooftests
    408 
    409 #
    410 # This scenario expects the spoofed response to succeed. The tests are
    411 # similar to the ones above, but not identical.
    412 #
    413 echo_i "rechecking spoofed response scenarios with 'forward only' set globally"
    414 cp ns9/named3.conf ns9/named.conf
    415 rndccmd 10.53.0.9 reconfig 2>&1 | sed 's/^/ns3 /' | cat_i
    416 rndccmd 10.53.0.9 flush 2>&1 | sed 's/^/ns3 /' | cat_i
    417 sleep 1
    418 
    419 n=$((n + 1))
    420 echo_i "checking spoofed response scenario 1 - out of bailiwick NS ($n)"
    421 ret=0
    422 # prime
    423 dig_with_opts @10.53.0.9 attackSecureDomain.net >dig.out.$n.prime || ret=1
    424 # check 'net' is poisoned.
    425 dig_with_opts @10.53.0.9 diditwork.net. TXT >dig.out.$n.net || ret=1
    426 grep '^didItWork\.net\..*TXT.*"if you can see this record the attack worked"' dig.out.$n.net >/dev/null || ret=1
    427 # check 'sub.local.net' is poisoned.
    428 dig_with_opts @10.53.0.9 sub.local.net TXT >dig.out.$n.sub || ret=1
    429 grep '^sub\.local\.net\..*TXT.*"if you see this attacker overrode local delegation"' dig.out.$n.sub >/dev/null || ret=1
    430 if [ $ret != 0 ]; then echo_i "failed"; fi
    431 status=$((status + ret))
    432 
    433 n=$((n + 1))
    434 echo_i "checking spoofed response scenario 2 - inject DNAME/net2. ($n)"
    435 ret=0
    436 # prime
    437 dig_with_opts @10.53.0.9 attackSecureDomain.net2 >dig.out.$n.prime || ret=1
    438 # check that net2/DNAME is cached
    439 dig_with_opts @10.53.0.9 net2. DNAME >dig.out.$n.net2 || ret=1
    440 grep "ANSWER: 1," dig.out.$n.net2 >/dev/null || ret=1
    441 grep "net2\..*IN.DNAME.net\.example\.lll\." dig.out.$n.net2 >/dev/null || ret=1
    442 if [ $ret != 0 ]; then echo_i "failed"; fi
    443 status=$((status + ret))
    444 
    445 #
    446 # This test doesn't use any forwarder clauses but is here because it
    447 # is similar to forwarders, as the set of servers that can populate
    448 # the namespace is defined by the zone content.
    449 #
    450 echo_i "rechecking spoofed response scenarios glue below local zone"
    451 cp ns9/named4.conf ns9/named.conf
    452 rndccmd 10.53.0.9 reconfig 2>&1 | sed 's/^/ns3 /' | cat_i
    453 rndccmd 10.53.0.9 flush 2>&1 | sed 's/^/ns3 /' | cat_i
    454 sleep 1
    455 
    456 n=$((n + 1))
    457 echo_i "checking sibling glue below zone ($n)"
    458 ret=0
    459 # prime
    460 dig_with_opts @10.53.0.9 sibling.tld >dig.out.$n.prime || ret=1
    461 # check for glue A record for sub.local.tld is not used
    462 dig_with_opts @10.53.0.9 sub.local.tld TXT >dig.out.$n.sub || ret=1
    463 grep "ANSWER: 1," dig.out.$n.sub >/dev/null || ret=1
    464 grep 'sub\.local\.tld\..*IN.TXT."good"$' dig.out.$n.sub >/dev/null || ret=1
    465 if [ $ret != 0 ]; then echo_i "failed"; fi
    466 status=$((status + ret))
    467 
    468 echo_i "exit status: $status"
    469 [ $status -eq 0 ] || exit 1
    470