Home | History | Annotate | Line # | Download | only in doth
      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 disable=SC1091
     17 . ../conf.sh
     18 
     19 common_dig_options="+noadd +nosea +nostat +noquest +nocmd"
     20 msg_xfrs_not_allowed=";; zone transfers over the established TLS connection are not allowed"
     21 msg_peer_verification_failed=";; TLS peer certificate verification"
     22 
     23 ca_file="./CA/CA.pem"
     24 
     25 OPENSSL_VERSION=$("$PYTHON" "$TOP_SRCDIR/bin/tests/system/doth/get_openssl_version.py")
     26 OPENSSL_VERSION_MAJOR=$(echo "$OPENSSL_VERSION" | cut -d ' ' -f 1)
     27 OPENSSL_VERSION_MINOR=$(echo "$OPENSSL_VERSION" | cut -d ' ' -f 2)
     28 
     29 # According to the RFC 8310, Section 8.1, Subject field MUST
     30 # NOT be inspected when verifying a hostname when using
     31 # DoT. Only SubjectAltName must be checked instead. That is
     32 # not the case for HTTPS, though.
     33 
     34 # Unfortunately, some quite old versions of OpenSSL (< 1.1.1)
     35 # might lack the functionality to implement that. It should
     36 # have very little real-world consequences, as most of the
     37 # production-ready certificates issued by real CAs will have
     38 # SubjectAltName set. In such a case, the Subject field is
     39 # ignored.
     40 #
     41 # On the platforms with too old TLS versions, e.g. RedHat 7, we should
     42 # ignore the tests checking the correct handling of absence of
     43 # SubjectAltName.
     44 if [ $OPENSSL_VERSION_MAJOR -gt 1 ]; then
     45   run_san_tests=1
     46 elif [ $OPENSSL_VERSION_MAJOR -eq 1 ] && [ $OPENSSL_VERSION_MINOR -ge 1 ]; then
     47   run_san_tests=1
     48 fi
     49 
     50 dig_with_tls_opts() {
     51   # shellcheck disable=SC2086
     52   "$DIG" +tls $common_dig_options -p "${TLSPORT}" "$@"
     53 }
     54 
     55 dig_with_https_opts() {
     56   # shellcheck disable=SC2086
     57   "$DIG" +https $common_dig_options -p "${HTTPSPORT}" "$@"
     58 }
     59 
     60 dig_with_http_opts() {
     61   # shellcheck disable=SC2086
     62   "$DIG" +http-plain $common_dig_options -p "${HTTPPORT}" "$@"
     63 }
     64 
     65 dig_with_opts() {
     66   # shellcheck disable=SC2086
     67   "$DIG" $common_dig_options -p "${PORT}" "$@"
     68 }
     69 
     70 wait_for_tls_xfer() (
     71   srv_number="$1"
     72   shift
     73   zone_name="$1"
     74   shift
     75   # Let's bind to .10 to make it possible to easily distinguish dig from NSs in packet traces
     76   dig_with_tls_opts -b 10.53.0.10 "@10.53.0.$srv_number" "${zone_name}." AXFR >"dig.out.ns$srv_number.${zone_name}.test$n" || return 1
     77   grep "^;" "dig.out.ns$srv_number.${zone_name}.test$n" >/dev/null && return 1
     78   return 0
     79 )
     80 
     81 status=0
     82 n=0
     83 
     84 n=$((n + 1))
     85 echo_i "testing XoT server functionality (using dig) ($n)"
     86 ret=0
     87 dig_with_tls_opts example. -b 10.53.0.10 @10.53.0.1 axfr >dig.out.ns1.test$n || ret=1
     88 grep "^;" dig.out.ns1.test$n | cat_i
     89 digcomp example.axfr.good dig.out.ns1.test$n || ret=1
     90 if test $ret != 0; then echo_i "failed"; fi
     91 status=$((status + ret))
     92 
     93 n=$((n + 1))
     94 echo_i "testing incoming XoT functionality (from the first secondary) ($n)"
     95 ret=0
     96 if retry_quiet 10 wait_for_tls_xfer 2 example; then
     97   digcomp example.axfr.good "dig.out.ns2.example.test$n" || ret=1
     98 else
     99   echo_i "timed out waiting for zone transfer"
    100   grep "^;" "dig.out.ns2.example.test$n" | cat_i
    101   ret=1
    102 fi
    103 if test $ret != 0; then echo_i "failed"; fi
    104 status=$((status + ret))
    105 
    106 if [ -n "$run_san_tests" ]; then
    107   n=$((n + 1))
    108   echo_i "testing incoming XoT functionality (from the first secondary, no SubjectAltName, failure expected) ($n)"
    109   ret=0
    110   if retry_quiet 10 wait_for_tls_xfer 2 example3; then
    111     ret=1
    112   else
    113     echo_i "timed out waiting for zone transfer"
    114   fi
    115   if [ $ret != 0 ]; then echo_i "failed"; fi
    116   status=$((status + ret))
    117 fi
    118 
    119 n=$((n + 1))
    120 echo_i "testing incoming XoT functionality (from the first secondary, StrictTLS via implicit IP) ($n)"
    121 ret=0
    122 if retry_quiet 10 wait_for_tls_xfer 2 example4; then
    123   retry_quiet 5 test -f "ns2/example4.db" || ret=1
    124 else
    125   echo_i "timed out waiting for zone transfer"
    126   grep "^;" "dig.out.ns2.example4.test$n" | cat_i
    127   ret=1
    128 fi
    129 if [ $ret != 0 ]; then echo_i "failed"; fi
    130 status=$((status + ret))
    131 
    132 n=$((n + 1))
    133 echo_i "testing incoming XoT functionality (from the first secondary, StrictTLS via specified IPv4) ($n)"
    134 ret=0
    135 if retry_quiet 10 wait_for_tls_xfer 2 example5; then
    136   retry_quiet 5 test -f "ns2/example5.db" || ret=1
    137 else
    138   echo_i "timed out waiting for zone transfer"
    139   grep "^;" "dig.out.ns2.example5.test$n" | cat_i
    140   ret=1
    141 fi
    142 if [ $ret != 0 ]; then echo_i "failed"; fi
    143 status=$((status + ret))
    144 
    145 n=$((n + 1))
    146 echo_i "testing incoming XoT functionality (from the first secondary, StrictTLS via specified IPv6) ($n)"
    147 ret=0
    148 if retry_quiet 10 wait_for_tls_xfer 2 example6; then
    149   retry_quiet 5 test -f "ns2/example6.db" || ret=1
    150 else
    151   echo_i "timed out waiting for zone transfer"
    152   grep "^;" "dig.out.ns2.example6.test$n" | cat_i
    153   ret=1
    154 fi
    155 if [ $ret != 0 ]; then echo_i "failed"; fi
    156 status=$((status + ret))
    157 
    158 n=$((n + 1))
    159 echo_i "testing incoming XoT functionality (from the first secondary, wrong hostname, failure expected) ($n)"
    160 ret=0
    161 if retry_quiet 10 wait_for_tls_xfer 2 example7; then
    162   ret=1
    163 else
    164   echo_i "timed out waiting for zone transfer"
    165 fi
    166 if [ $ret != 0 ]; then echo_i "failed"; fi
    167 status=$((status + ret))
    168 
    169 n=$((n + 1))
    170 echo_i "testing incoming XoT functionality (from the first secondary, expired certificate, failure expected) ($n)"
    171 ret=0
    172 if retry_quiet 10 wait_for_tls_xfer 2 example8; then
    173   ret=1
    174 else
    175   echo_i "timed out waiting for zone transfer"
    176 fi
    177 if [ $ret != 0 ]; then echo_i "failed"; fi
    178 status=$((status + ret))
    179 
    180 n=$((n + 1))
    181 echo_i "testing incoming XoT functionality (from the first secondary, MutualTLS) ($n)"
    182 ret=0
    183 if retry_quiet 10 wait_for_tls_xfer 2 example9; then
    184   retry_quiet 5 test -f "ns2/example9.db" || ret=1
    185 else
    186   echo_i "timed out waiting for zone transfer"
    187   grep "^;" "dig.out.ns2.example9.test$n" | cat_i
    188   ret=1
    189 fi
    190 if [ $ret != 0 ]; then echo_i "failed"; fi
    191 status=$((status + ret))
    192 
    193 n=$((n + 1))
    194 echo_i "testing incoming XoT functionality (from the first secondary, MutualTLS, no client cert, failure expected) ($n)"
    195 ret=0
    196 if retry_quiet 10 wait_for_tls_xfer 2 example10; then
    197   ret=1
    198 else
    199   echo_i "timed out waiting for zone transfer"
    200 fi
    201 if [ $ret != 0 ]; then echo_i "failed"; fi
    202 status=$((status + ret))
    203 
    204 n=$((n + 1))
    205 echo_i "testing incoming XoT functionality (from the first secondary, MutualTLS, expired client cert, failure expected) ($n)"
    206 ret=0
    207 if retry_quiet 10 wait_for_tls_xfer 2 example11; then
    208   ret=1
    209 else
    210   echo_i "timed out waiting for zone transfer"
    211 fi
    212 if [ $ret != 0 ]; then echo_i "failed"; fi
    213 status=$((status + ret))
    214 
    215 n=$((n + 1))
    216 echo_i "testing incoming XoT functionality (from the second secondary) ($n)"
    217 ret=0
    218 if retry_quiet 10 wait_for_tls_xfer 3 example; then
    219   digcomp example.axfr.good "dig.out.ns3.example.test$n" || ret=1
    220 else
    221   echo_i "timed out waiting for zone transfer"
    222   grep "^;" "dig.out.ns3.example.test$n" | cat_i
    223   ret=1
    224 fi
    225 if test $ret != 0; then echo_i "failed"; fi
    226 status=$((status + ret))
    227 
    228 n=$((n + 1))
    229 echo_i "testing incoming XoT functionality (from the second secondary, mismatching ciphers, failure expected) ($n)"
    230 ret=0
    231 if retry_quiet 10 wait_for_tls_xfer 3 example2; then
    232   ret=1
    233 else
    234   echo_i "timed out waiting for zone transfer"
    235 fi
    236 if test $ret != 0; then echo_i "failed"; fi
    237 status=$((status + ret))
    238 
    239 n=$((n + 1))
    240 echo_i "testing incoming XoT functionality (from the third secondary) ($n)"
    241 ret=0
    242 if retry_quiet 10 wait_for_tls_xfer 4 example; then
    243   digcomp example.axfr.good "dig.out.ns4.example.test$n" || ret=1
    244 else
    245   echo_i "timed out waiting for zone transfer"
    246   grep "^;" "dig.out.ns4.example.test$n" | cat_i
    247   ret=1
    248 fi
    249 if test $ret != 0; then echo_i "failed"; fi
    250 status=$((status + ret))
    251 
    252 n=$((n + 1))
    253 echo_i "checking DoT query (ephemeral key) ($n)"
    254 ret=0
    255 dig_with_tls_opts @10.53.0.1 . SOA >dig.out.test$n || ret=1
    256 grep "status: NOERROR" dig.out.test$n >/dev/null || ret=1
    257 if [ $ret != 0 ]; then echo_i "failed"; fi
    258 status=$((status + ret))
    259 
    260 n=$((n + 1))
    261 echo_i "checking DoT query via IPv6 (ephemeral key) ($n)"
    262 ret=0
    263 dig_with_tls_opts -6 @fd92:7065:b8e:ffff::1 . SOA >dig.out.test$n || ret=1
    264 grep "status: NOERROR" dig.out.test$n >/dev/null || ret=1
    265 if [ $ret != 0 ]; then echo_i "failed"; fi
    266 status=$((status + ret))
    267 
    268 n=$((n + 1))
    269 echo_i "checking DoT query (static key) ($n)"
    270 ret=0
    271 dig_with_tls_opts @10.53.0.2 example SOA >dig.out.test$n || ret=1
    272 grep "status: NOERROR" dig.out.test$n >/dev/null || ret=1
    273 if [ $ret != 0 ]; then echo_i "failed"; fi
    274 status=$((status + ret))
    275 
    276 n=$((n + 1))
    277 echo_i "checking DoT query via IPv6 (static key) ($n)"
    278 ret=0
    279 dig_with_tls_opts -6 @fd92:7065:b8e:ffff::2 example SOA >dig.out.test$n || ret=1
    280 grep "status: NOERROR" dig.out.test$n >/dev/null || ret=1
    281 if [ $ret != 0 ]; then echo_i "failed"; fi
    282 status=$((status + ret))
    283 
    284 n=$((n + 1))
    285 echo_i "checking DoT XFR ($n)"
    286 ret=0
    287 dig_with_tls_opts +comm @10.53.0.1 . AXFR >dig.out.test$n || ret=1
    288 grep "status: NOERROR" dig.out.test$n >/dev/null || ret=1
    289 if [ $ret != 0 ]; then echo_i "failed"; fi
    290 status=$((status + ret))
    291 
    292 # zone transfers are allowed only via TLS
    293 n=$((n + 1))
    294 echo_i "testing zone transfer over Do53 server functionality (using dig, failure expected) ($n)"
    295 ret=0
    296 dig_with_opts example. -b 10.53.0.10 @10.53.0.1 axfr >dig.out.ns1.test$n || ret=1
    297 grep "; Transfer failed." dig.out.ns1.test$n >/dev/null || ret=1
    298 if [ $ret != 0 ]; then echo_i "failed"; fi
    299 status=$((status + ret))
    300 
    301 # querying zones is still allowed via UDP/TCP
    302 n=$((n + 1))
    303 echo_i "checking Do53 query ($n)"
    304 ret=0
    305 dig_with_opts @10.53.0.1 example SOA >dig.out.test$n || ret=1
    306 grep "status: NOERROR" dig.out.test$n >/dev/null || ret=1
    307 if [ $ret != 0 ]; then echo_i "failed"; fi
    308 status=$((status + ret))
    309 
    310 # In this test we are trying to establish a DoT connection over the
    311 # DoH port. That is intentional, as dig should fail right after
    312 # handshake has happened and before sending any queries, as XFRs, per
    313 # the RFC, could happen only over a connection where "dot" ALPN token
    314 # was negotiated.  over DoH it cannot happen, as only "h2" token could
    315 # be selected for a DoH connection.
    316 n=$((n + 1))
    317 echo_i "checking DoT XFR with wrong ALPN token (h2, failure expected) ($n)"
    318 ret=0
    319 # shellcheck disable=SC2086
    320 "$DIG" +tls $common_dig_options -p "${HTTPSPORT}" +comm @10.53.0.1 . AXFR >dig.out.test$n
    321 grep "$msg_xfrs_not_allowed" dig.out.test$n >/dev/null || ret=1
    322 if [ $ret != 0 ]; then echo_i "failed"; fi
    323 status=$((status + ret))
    324 
    325 # Let's try to issue an HTTP/2 query over TLS port to check if dig
    326 # will detect ALPN token negotiation problem.
    327 n=$((n + 1))
    328 echo_i "checking DoH query when ALPN is expected to fail (dot, failure expected) ($n)"
    329 ret=0
    330 # shellcheck disable=SC2086
    331 "$DIG" +https $common_dig_options -p "${TLSPORT}" "$@" @10.53.0.1 . SOA >dig.out.test$n && ret=1
    332 grep "ALPN for HTTP/2 failed." dig.out.test$n >/dev/null || ret=1
    333 if [ $ret != 0 ]; then echo_i "failed"; fi
    334 status=$((status + ret))
    335 
    336 n=$((n + 1))
    337 echo_i "checking DoH query (POST) ($n)"
    338 ret=0
    339 dig_with_https_opts +stat @10.53.0.1 . SOA >dig.out.test$n || ret=1
    340 grep "status: NOERROR" dig.out.test$n >/dev/null || ret=1
    341 grep -F "(HTTPS)" dig.out.test$n >/dev/null || ret=1
    342 if [ $ret != 0 ]; then echo_i "failed"; fi
    343 status=$((status + ret))
    344 
    345 n=$((n + 1))
    346 echo_i "checking DoH query via IPv6 (POST) ($n)"
    347 ret=0
    348 dig_with_https_opts +stat -6 @fd92:7065:b8e:ffff::1 . SOA >dig.out.test$n || ret=1
    349 grep "status: NOERROR" dig.out.test$n >/dev/null || ret=1
    350 grep -F "(HTTPS)" dig.out.test$n >/dev/null || ret=1
    351 if [ $ret != 0 ]; then echo_i "failed"; fi
    352 status=$((status + ret))
    353 
    354 n=$((n + 1))
    355 echo_i "checking DoH query (POST, static key) ($n)"
    356 ret=0
    357 dig_with_https_opts @10.53.0.2 example SOA >dig.out.test$n || ret=1
    358 grep "status: NOERROR" dig.out.test$n >/dev/null || ret=1
    359 if [ $ret != 0 ]; then echo_i "failed"; fi
    360 status=$((status + ret))
    361 
    362 n=$((n + 1))
    363 echo_i "checking DoH query via IPv6 (POST, static key) ($n)"
    364 ret=0
    365 dig_with_https_opts -6 @fd92:7065:b8e:ffff::2 example SOA >dig.out.test$n || ret=1
    366 grep "status: NOERROR" dig.out.test$n >/dev/null || ret=1
    367 if [ $ret != 0 ]; then echo_i "failed"; fi
    368 status=$((status + ret))
    369 
    370 n=$((n + 1))
    371 echo_i "checking DoH query (POST, nonstandard endpoint) ($n)"
    372 ret=0
    373 dig_with_https_opts +https=/alter @10.53.0.1 . SOA >dig.out.test$n || ret=1
    374 grep "status: NOERROR" dig.out.test$n >/dev/null || ret=1
    375 if [ $ret != 0 ]; then echo_i "failed"; fi
    376 status=$((status + ret))
    377 
    378 n=$((n + 1))
    379 echo_i "checking DoH query via IPv6 (POST, nonstandard endpoint) ($n)"
    380 ret=0
    381 dig_with_https_opts -6 +https=/alter @fd92:7065:b8e:ffff::1 . SOA >dig.out.test$n || ret=1
    382 grep "status: NOERROR" dig.out.test$n >/dev/null || ret=1
    383 if [ $ret != 0 ]; then echo_i "failed"; fi
    384 status=$((status + ret))
    385 
    386 n=$((n + 1))
    387 echo_i "checking DoH query (POST, undefined endpoint, failure expected) ($n)"
    388 ret=0
    389 dig_with_https_opts +tries=1 +time=1 +https=/fake @10.53.0.1 . SOA >dig.out.test$n && ret=1
    390 grep "communications error" dig.out.test$n >/dev/null || ret=1
    391 if [ $ret != 0 ]; then echo_i "failed"; fi
    392 status=$((status + ret))
    393 
    394 n=$((n + 1))
    395 echo_i "checking DoH query via IPv6 (POST, undefined endpoint, failure expected) ($n)"
    396 ret=0
    397 dig_with_https_opts -6 +tries=1 +time=1 +https=/fake @fd92:7065:b8e:ffff::1 . SOA >dig.out.test$n && ret=1
    398 grep "communications error" dig.out.test$n >/dev/null || ret=1
    399 if [ $ret != 0 ]; then echo_i "failed"; fi
    400 status=$((status + ret))
    401 
    402 n=$((n + 1))
    403 echo_i "checking DoH XFR (POST) (failure expected) ($n)"
    404 ret=0
    405 dig_with_https_opts +comm @10.53.0.1 . AXFR >dig.out.test$n || ret=1
    406 grep "; Transfer failed." dig.out.test$n >/dev/null || ret=1
    407 if [ $ret != 0 ]; then echo_i "failed"; fi
    408 status=$((status + ret))
    409 
    410 n=$((n + 1))
    411 echo_i "checking DoH query (GET) ($n)"
    412 ret=0
    413 dig_with_https_opts +stat +https-get @10.53.0.1 . SOA >dig.out.test$n || ret=1
    414 grep "status: NOERROR" dig.out.test$n >/dev/null || ret=1
    415 grep -F "(HTTPS-GET)" dig.out.test$n >/dev/null || ret=1
    416 if [ $ret != 0 ]; then echo_i "failed"; fi
    417 status=$((status + ret))
    418 
    419 n=$((n + 1))
    420 echo_i "checking DoH query via IPv6 (GET) ($n)"
    421 ret=0
    422 dig_with_https_opts -6 +stat +https-get @fd92:7065:b8e:ffff::1 . SOA >dig.out.test$n || ret=1
    423 grep "status: NOERROR" dig.out.test$n >/dev/null || ret=1
    424 grep -F "(HTTPS-GET)" dig.out.test$n >/dev/null || ret=1
    425 if [ $ret != 0 ]; then echo_i "failed"; fi
    426 status=$((status + ret))
    427 
    428 n=$((n + 1))
    429 echo_i "checking DoH query (GET, static key) ($n)"
    430 ret=0
    431 dig_with_https_opts +https-get @10.53.0.2 example SOA >dig.out.test$n || ret=1
    432 grep "status: NOERROR" dig.out.test$n >/dev/null || ret=1
    433 if [ $ret != 0 ]; then echo_i "failed"; fi
    434 status=$((status + ret))
    435 
    436 n=$((n + 1))
    437 echo_i "checking DoH query via IPv6 (GET, static key) ($n)"
    438 ret=0
    439 dig_with_https_opts -6 +https-get @fd92:7065:b8e:ffff::2 example SOA >dig.out.test$n || ret=1
    440 grep "status: NOERROR" dig.out.test$n >/dev/null || ret=1
    441 if [ $ret != 0 ]; then echo_i "failed"; fi
    442 status=$((status + ret))
    443 
    444 n=$((n + 1))
    445 echo_i "checking DoH query (GET, nonstandard endpoint) ($n)"
    446 ret=0
    447 dig_with_https_opts +https-get=/alter @10.53.0.1 . SOA >dig.out.test$n || ret=1
    448 grep "status: NOERROR" dig.out.test$n >/dev/null || ret=1
    449 if [ $ret != 0 ]; then echo_i "failed"; fi
    450 status=$((status + ret))
    451 
    452 n=$((n + 1))
    453 echo_i "checking DoH query via IPv6 (GET, nonstandard endpoint) ($n)"
    454 ret=0
    455 dig_with_https_opts -6 +https-get=/alter @fd92:7065:b8e:ffff::1 . SOA >dig.out.test$n || ret=1
    456 grep "status: NOERROR" dig.out.test$n >/dev/null || ret=1
    457 if [ $ret != 0 ]; then echo_i "failed"; fi
    458 status=$((status + ret))
    459 
    460 n=$((n + 1))
    461 echo_i "checking DoH query (GET, undefined endpoint, failure expected) ($n)"
    462 ret=0
    463 dig_with_https_opts +tries=1 +time=1 +https-get=/fake @10.53.0.1 . SOA >dig.out.test$n && ret=1
    464 grep "communications error" dig.out.test$n >/dev/null || ret=1
    465 if [ $ret != 0 ]; then echo_i "failed"; fi
    466 status=$((status + ret))
    467 
    468 n=$((n + 1))
    469 echo_i "checking DoH query via IPv6 (GET, undefined endpoint, failure expected) ($n)"
    470 ret=0
    471 dig_with_https_opts -6 +tries=1 +time=1 +https-get=/fake @fd92:7065:b8e:ffff::1 . SOA >dig.out.test$n && ret=1
    472 grep "communications error" dig.out.test$n >/dev/null || ret=1
    473 if [ $ret != 0 ]; then echo_i "failed"; fi
    474 status=$((status + ret))
    475 
    476 n=$((n + 1))
    477 echo_i "checking DoH XFR (GET) (failure expected) ($n)"
    478 ret=0
    479 dig_with_https_opts +https-get +comm @10.53.0.1 . AXFR >dig.out.test$n || ret=1
    480 grep "; Transfer failed." dig.out.test$n >/dev/null || ret=1
    481 if [ $ret != 0 ]; then echo_i "failed"; fi
    482 status=$((status + ret))
    483 
    484 n=$((n + 1))
    485 echo_i "checking unencrypted DoH query (POST) ($n)"
    486 ret=0
    487 dig_with_http_opts +stat @10.53.0.1 . SOA >dig.out.test$n || ret=1
    488 grep "status: NOERROR" dig.out.test$n >/dev/null || ret=1
    489 grep -F "(HTTP)" dig.out.test$n >/dev/null || ret=1
    490 if [ $ret != 0 ]; then echo_i "failed"; fi
    491 status=$((status + ret))
    492 
    493 n=$((n + 1))
    494 echo_i "checking unencrypted DoH query via IPv6 (POST) ($n)"
    495 ret=0
    496 dig_with_http_opts -6 +stat @fd92:7065:b8e:ffff::1 . SOA >dig.out.test$n || ret=1
    497 grep "status: NOERROR" dig.out.test$n >/dev/null || ret=1
    498 grep -F "(HTTP)" dig.out.test$n >/dev/null || ret=1
    499 if [ $ret != 0 ]; then echo_i "failed"; fi
    500 status=$((status + ret))
    501 
    502 n=$((n + 1))
    503 echo_i "checking unencrypted DoH query (GET) ($n)"
    504 ret=0
    505 dig_with_http_opts +stat +http-plain-get @10.53.0.1 . SOA >dig.out.test$n || ret=1
    506 grep "status: NOERROR" dig.out.test$n >/dev/null || ret=1
    507 grep -F "(HTTP-GET)" dig.out.test$n >/dev/null || ret=1
    508 if [ $ret != 0 ]; then echo_i "failed"; fi
    509 status=$((status + ret))
    510 
    511 n=$((n + 1))
    512 echo_i "checking unencrypted DoH query via IPv6 (GET) ($n)"
    513 ret=0
    514 dig_with_http_opts -6 +stat +http-plain-get @fd92:7065:b8e:ffff::1 . SOA >dig.out.test$n || ret=1
    515 grep "status: NOERROR" dig.out.test$n >/dev/null || ret=1
    516 grep -F "(HTTP-GET)" dig.out.test$n >/dev/null || ret=1
    517 if [ $ret != 0 ]; then echo_i "failed"; fi
    518 status=$((status + ret))
    519 
    520 n=$((n + 1))
    521 echo_i "checking unencrypted DoH XFR (failure expected) ($n)"
    522 ret=0
    523 dig_with_http_opts +comm @10.53.0.1 . AXFR >dig.out.test$n || ret=1
    524 grep "; Transfer failed." dig.out.test$n >/dev/null || ret=1
    525 if [ $ret != 0 ]; then echo_i "failed"; fi
    526 status=$((status + ret))
    527 
    528 n=$((n + 1))
    529 echo_i "checking DoH query for a large answer (POST) ($n)"
    530 ret=0
    531 dig_with_https_opts @10.53.0.1 biganswer.example A >dig.out.test$n || ret=1
    532 grep "status: NOERROR" dig.out.test$n >/dev/null || ret=1
    533 grep "ANSWER: 2500" dig.out.test$n >/dev/null || ret=1
    534 if [ $ret != 0 ]; then echo_i "failed"; fi
    535 status=$((status + ret))
    536 
    537 n=$((n + 1))
    538 echo_i "checking DoH query via IPv6 for a large answer (POST) ($n)"
    539 ret=0
    540 dig_with_https_opts -6 @fd92:7065:b8e:ffff::1 biganswer.example A >dig.out.test$n || ret=1
    541 grep "status: NOERROR" dig.out.test$n >/dev/null || ret=1
    542 grep "ANSWER: 2500" dig.out.test$n >/dev/null || ret=1
    543 if [ $ret != 0 ]; then echo_i "failed"; fi
    544 status=$((status + ret))
    545 
    546 n=$((n + 1))
    547 echo_i "checking DoH query for a large answer (GET) ($n)"
    548 ret=0
    549 dig_with_https_opts +https-get @10.53.0.1 biganswer.example A >dig.out.test$n || ret=1
    550 grep "status: NOERROR" dig.out.test$n >/dev/null || ret=1
    551 grep "ANSWER: 2500" dig.out.test$n >/dev/null || ret=1
    552 if [ $ret != 0 ]; then echo_i "failed"; fi
    553 status=$((status + ret))
    554 
    555 n=$((n + 1))
    556 echo_i "checking DoH query via IPv6 for a large answer (GET) ($n)"
    557 ret=0
    558 dig_with_https_opts -6 +https-get @fd92:7065:b8e:ffff::1 biganswer.example A >dig.out.test$n || ret=1
    559 grep "status: NOERROR" dig.out.test$n >/dev/null || ret=1
    560 grep "ANSWER: 2500" dig.out.test$n >/dev/null || ret=1
    561 if [ $ret != 0 ]; then echo_i "failed"; fi
    562 status=$((status + ret))
    563 
    564 n=$((n + 1))
    565 echo_i "checking unencrypted DoH query for a large answer (POST) ($n)"
    566 ret=0
    567 dig_with_http_opts @10.53.0.1 biganswer.example A >dig.out.test$n || ret=1
    568 grep "status: NOERROR" dig.out.test$n >/dev/null || ret=1
    569 grep "ANSWER: 2500" dig.out.test$n >/dev/null || ret=1
    570 if [ $ret != 0 ]; then echo_i "failed"; fi
    571 status=$((status + ret))
    572 
    573 n=$((n + 1))
    574 echo_i "checking unencrypted DoH query via IPv6 for a large answer (POST) ($n)"
    575 ret=0
    576 dig_with_http_opts -6 @fd92:7065:b8e:ffff::1 biganswer.example A >dig.out.test$n || ret=1
    577 grep "status: NOERROR" dig.out.test$n >/dev/null || ret=1
    578 grep "ANSWER: 2500" dig.out.test$n >/dev/null || ret=1
    579 if [ $ret != 0 ]; then echo_i "failed"; fi
    580 status=$((status + ret))
    581 
    582 n=$((n + 1))
    583 echo_i "checking unencrypted DoH query for a large answer (GET) ($n)"
    584 ret=0
    585 dig_with_http_opts +http-plain-get @10.53.0.1 biganswer.example A >dig.out.test$n || ret=1
    586 grep "status: NOERROR" dig.out.test$n >/dev/null || ret=1
    587 grep "ANSWER: 2500" dig.out.test$n >/dev/null || ret=1
    588 if [ $ret != 0 ]; then echo_i "failed"; fi
    589 status=$((status + ret))
    590 
    591 n=$((n + 1))
    592 echo_i "checking unencrypted DoH query via IPv6 for a large answer (GET) ($n)"
    593 ret=0
    594 dig_with_http_opts -6 +http-plain-get @fd92:7065:b8e:ffff::1 biganswer.example A >dig.out.test$n || ret=1
    595 grep "status: NOERROR" dig.out.test$n >/dev/null || ret=1
    596 grep "ANSWER: 2500" dig.out.test$n >/dev/null || ret=1
    597 if [ $ret != 0 ]; then echo_i "failed"; fi
    598 status=$((status + ret))
    599 
    600 wait_for_tlsctx_update_ns4() {
    601   grep "updating TLS context on 10.53.0.4#${HTTPSPORT}" ns4/named.run >/dev/null || return 1
    602   grep "updating TLS context on 10.53.0.4#${TLSPORT}" ns4/named.run >/dev/null || return 1
    603   return 0
    604 }
    605 
    606 n=$((n + 1))
    607 echo_i "doing rndc reconfig to see that queries keep being served after that ($n)"
    608 ret=0
    609 rndc_reconfig ns4 10.53.0.4 60
    610 retry_quiet 15 wait_for_tlsctx_update_ns4 || ret=1
    611 if [ $ret != 0 ]; then echo_i "failed"; fi
    612 status=$((status + ret))
    613 
    614 n=$((n + 1))
    615 echo_i "checking DoT query after a reconfiguration ($n)"
    616 ret=0
    617 dig_with_tls_opts @10.53.0.4 example SOA >dig.out.test$n || ret=1
    618 grep "status: NOERROR" dig.out.test$n >/dev/null || ret=1
    619 if [ $ret != 0 ]; then echo_i "failed"; fi
    620 status=$((status + ret))
    621 
    622 n=$((n + 1))
    623 echo_i "checking DoH query (POST) after a reconfiguration ($n)"
    624 ret=0
    625 dig_with_https_opts @10.53.0.4 example SOA >dig.out.test$n || ret=1
    626 grep "status: NOERROR" dig.out.test$n >/dev/null || ret=1
    627 if [ $ret != 0 ]; then echo_i "failed"; fi
    628 status=$((status + ret))
    629 
    630 n=$((n + 1))
    631 echo_i "doing rndc reconfig to see if HTTP endpoints have gotten reconfigured ($n)"
    632 ret=0
    633 # 'sed -i ...' is not portable. Sigh...
    634 sed 's/\/dns-query/\/dns-query-test/g' "ns4/named.conf" >"ns4/named.conf.sed"
    635 mv -f "ns4/named.conf.sed" "ns4/named.conf"
    636 rndc_reconfig ns4 10.53.0.4 60
    637 retry_quiet 15 wait_for_tlsctx_update_ns4 || ret=1
    638 if [ $ret != 0 ]; then echo_i "failed"; fi
    639 status=$((status + ret))
    640 
    641 n=$((n + 1))
    642 echo_i "checking DoH query (POST) to verify HTTP endpoint reconfiguration ($n)"
    643 ret=0
    644 dig_with_https_opts +https='/dns-query-test' @10.53.0.4 example SOA >dig.out.test$n || ret=1
    645 grep "status: NOERROR" dig.out.test$n >/dev/null || ret=1
    646 if [ $ret != 0 ]; then echo_i "failed"; fi
    647 status=$((status + ret))
    648 
    649 n=$((n + 1))
    650 echo_i "checking DoT query (with TLS verification enabled) ($n)"
    651 ret=0
    652 dig_with_tls_opts +tls-ca="$ca_file" +tls-hostname="srv01.crt01.example.com" @10.53.0.1 . SOA >dig.out.test$n || ret=1
    653 grep "status: NOERROR" dig.out.test$n >/dev/null || ret=1
    654 if [ $ret != 0 ]; then echo_i "failed"; fi
    655 status=$((status + ret))
    656 
    657 n=$((n + 1))
    658 echo_i "checking DoH query (with TLS verification enabled, self-signed cert, failure expected) ($n)"
    659 ret=0
    660 dig_with_https_opts +tls-ca="$ca_file" +tls-hostname="srv01.crt01.example.com" @10.53.0.1 . SOA >dig.out.test$n || ret=1
    661 grep "$msg_peer_verification_failed" dig.out.test$n >/dev/null || ret=1
    662 if [ $ret != 0 ]; then echo_i "failed"; fi
    663 status=$((status + ret))
    664 
    665 n=$((n + 1))
    666 echo_i "checking DoT query (with TLS verification using the system's CA store, failure expected) ($n)"
    667 ret=0
    668 dig_with_tls_opts +tls-ca +tls-hostname="srv01.crt01.example.com" @10.53.0.1 . SOA >dig.out.test$n || ret=1
    669 grep "$msg_peer_verification_failed" dig.out.test$n >/dev/null || ret=1
    670 if [ $ret != 0 ]; then echo_i "failed"; fi
    671 status=$((status + ret))
    672 
    673 n=$((n + 1))
    674 echo_i "checking DoH query (with TLS verification using the system's CA store, failure expected) ($n)"
    675 ret=0
    676 dig_with_https_opts +tls-ca +tls-hostname="srv01.crt01.example.com" @10.53.0.1 . SOA >dig.out.test$n || ret=1
    677 grep "$msg_peer_verification_failed" dig.out.test$n >/dev/null || ret=1
    678 if [ $ret != 0 ]; then echo_i "failed"; fi
    679 status=$((status + ret))
    680 
    681 # the primary server's certificate contains the IP address in the
    682 # SubjectAltName section
    683 n=$((n + 1))
    684 echo_i "checking DoT query (with TLS verification, hostname is not specified, IP address is used instead) ($n)"
    685 ret=0
    686 dig_with_tls_opts +tls-ca="$ca_file" @10.53.0.1 . SOA >dig.out.test$n || ret=1
    687 grep "$msg_peer_verification_failed" dig.out.test$n >/dev/null && ret=1
    688 if [ $ret != 0 ]; then echo_i "failed"; fi
    689 status=$((status + ret))
    690 
    691 if [ -n "$run_san_tests" ]; then
    692   # SubjectAltName is required for DoT as according to RFC 8310, Subject
    693   # field MUST NOT be inspected when verifying hostname for DoT.
    694   n=$((n + 1))
    695   echo_i "checking DoT query (with TLS verification enabled when SubjectAltName is not set, failure expected) ($n)"
    696   ret=0
    697   dig_with_tls_opts +tls-ca="$ca_file" +tls-hostname="srv01.crt02-no-san.example.com" @10.53.0.1 . SOA >dig.out.test$n || ret=1
    698   grep "$msg_peer_verification_failed" dig.out.test$n >/dev/null || ret=1
    699   if [ $ret != 0 ]; then echo_i "failed"; fi
    700   status=$((status + ret))
    701 
    702   n=$((n + 1))
    703   echo_i "checking DoT XFR over a TLS port where SubjectAltName is not set (failure expected) ($n)"
    704   ret=0
    705   # shellcheck disable=SC2086
    706   dig_with_tls_opts +tls-ca="$ca_file" +tls-hostname="srv01.crt02-no-san.example.com" -p "${EXTRAPORT2}" +comm @10.53.0.1 . AXFR >dig.out.test$n || ret=1
    707   grep "$msg_peer_verification_failed" dig.out.test$n >/dev/null || ret=1
    708   if [ $ret != 0 ]; then echo_i "failed"; fi
    709   status=$((status + ret))
    710 fi
    711 
    712 # SubjectAltName is not required for HTTPS. Having a properly set
    713 # Common Name in the Subject field is enough.
    714 n=$((n + 1))
    715 echo_i "checking DoH query (when SubjectAltName is not set) ($n)"
    716 ret=0
    717 dig_with_https_opts +tls-ca="$ca_file" +tls-hostname="srv01.crt02-no-san.example.com" -p "${EXTRAPORT3}" +comm @10.53.0.1 . SOA >dig.out.test$n || ret=1
    718 grep "status: NOERROR" dig.out.test$n >/dev/null || ret=1
    719 if [ $ret != 0 ]; then echo_i "failed"; fi
    720 status=$((status + ret))
    721 
    722 n=$((n + 1))
    723 echo_i "checking DoT query (expired certificate, Opportunistic TLS) ($n)"
    724 ret=0
    725 dig_with_tls_opts +tls -p "${EXTRAPORT4}" +comm @10.53.0.1 . SOA >dig.out.test$n || ret=1
    726 grep "status: NOERROR" dig.out.test$n >/dev/null || ret=1
    727 if [ $ret != 0 ]; then echo_i "failed"; fi
    728 status=$((status + ret))
    729 
    730 n=$((n + 1))
    731 echo_i "checking DoT query (expired certificate, Strict TLS, failure expected) ($n)"
    732 ret=0
    733 dig_with_tls_opts +tls-ca="$ca_file" -p "${EXTRAPORT4}" +comm @10.53.0.1 . SOA >dig.out.test$n || ret=1
    734 grep "$msg_peer_verification_failed" dig.out.test$n >/dev/null || ret=1
    735 if [ $ret != 0 ]; then echo_i "failed"; fi
    736 status=$((status + ret))
    737 
    738 n=$((n + 1))
    739 echo_i "testing XoT server functionality (using dig, client certificate required, failure expected) ($n)"
    740 ret=0
    741 dig_with_tls_opts +tls-ca="$ca_file" -p "${EXTRAPORT5}" example8. -b 10.53.0.10 @10.53.0.1 axfr >dig.out.ns1.test$n || ret=1
    742 grep "; Transfer failed." dig.out.ns1.test$n >/dev/null || ret=1
    743 if test $ret != 0; then echo_i "failed"; fi
    744 status=$((status + ret))
    745 
    746 n=$((n + 1))
    747 echo_i "testing XoT server functionality (using dig, client certificate used) ($n)"
    748 ret=0
    749 dig_with_tls_opts +tls-ca="$ca_file" +tls-certfile="./CA/certs/srv01.client01.example.com.pem" +tls-keyfile="./CA/certs/srv01.client01.example.com.key" -p "${EXTRAPORT5}" example8. -b 10.53.0.10 @10.53.0.1 axfr >dig.out.ns1.test$n || ret=1
    750 digcomp dig.out.ns1.test$n example8.axfr.good >/dev/null || ret=1
    751 if test $ret != 0; then echo_i "failed"; fi
    752 status=$((status + ret))
    753 
    754 n=$((n + 1))
    755 echo_i "checking DoH query (client certificate required, failure expected) ($n)"
    756 ret=0
    757 dig_with_https_opts +tls-ca="$ca_file" -p "${EXTRAPORT6}" +comm @10.53.0.1 . SOA >dig.out.test$n && ret=1
    758 grep "status: NOERROR" dig.out.test$n >/dev/null && ret=1
    759 if [ $ret != 0 ]; then echo_i "failed"; fi
    760 status=$((status + ret))
    761 
    762 n=$((n + 1))
    763 echo_i "checking DoH query (client certificate used) ($n)"
    764 ret=0
    765 # shellcheck disable=SC2086
    766 dig_with_https_opts +https +tls-ca="$ca_file" +tls-certfile="./CA/certs/srv01.client01.example.com.pem" +tls-keyfile="./CA/certs/srv01.client01.example.com.key" -p "${EXTRAPORT6}" +comm @10.53.0.1 . SOA >dig.out.test$n || ret=1
    767 grep "status: NOERROR" dig.out.test$n >/dev/null || ret=1
    768 if [ $ret != 0 ]; then echo_i "failed"; fi
    769 status=$((status + ret))
    770 
    771 # send two requests one after another so that session resumption will happen
    772 n=$((n + 1))
    773 echo_i "checking DoH query (client certificate used - session resumption when using Mutual TLS) ($n)"
    774 ret=0
    775 # shellcheck disable=SC2086
    776 dig_with_https_opts +https +tls-ca="$ca_file" +tls-certfile="./CA/certs/srv01.client01.example.com.pem" +tls-keyfile="./CA/certs/srv01.client01.example.com.key" -p "${EXTRAPORT6}" +comm @10.53.0.1 . SOA . SOA >dig.out.test$n || ret=1
    777 grep "TLS error" dig.out.test$n >/dev/null && ret=1
    778 if [ $ret != 0 ]; then echo_i "failed"; fi
    779 status=$((status + ret))
    780 
    781 test_opcodes() {
    782   EXPECT_STATUS="$1"
    783   shift
    784   for op in "$@"; do
    785     n=$((n + 1))
    786     echo_i "checking unexpected opcode query over DoH for opcode $op ($n)"
    787     ret=0
    788     dig_with_https_opts +https @10.53.0.1 +opcode="$op" >dig.out.test$n || ret=1
    789     grep "status: $EXPECT_STATUS" dig.out.test$n >/dev/null || ret=1
    790     if [ $ret != 0 ]; then echo_i "failed"; fi
    791     status=$((status + ret))
    792 
    793     n=$((n + 1))
    794     echo_i "checking unexpected opcode query over DoH via IPv6 for opcode $op ($n)"
    795     ret=0
    796     dig_with_https_opts -6 +https @fd92:7065:b8e:ffff::1 +opcode="$op" >dig.out.test$n || ret=1
    797     grep "status: $EXPECT_STATUS" dig.out.test$n >/dev/null || ret=1
    798     if [ $ret != 0 ]; then echo_i "failed"; fi
    799     status=$((status + ret))
    800 
    801     n=$((n + 1))
    802     echo_i "checking unexpected opcode query over DoH without encryption for opcode $op ($n)"
    803     ret=0
    804     dig_with_http_opts +http-plain @10.53.0.1 +opcode="$op" >dig.out.test$n || ret=1
    805     grep "status: $EXPECT_STATUS" dig.out.test$n >/dev/null || ret=1
    806     if [ $ret != 0 ]; then echo_i "failed"; fi
    807     status=$((status + ret))
    808 
    809     n=$((n + 1))
    810     echo_i "checking unexpected opcode query over DoH via IPv6 without encryption for opcode $op ($n)"
    811     ret=0
    812     dig_with_http_opts -6 +http-plain @fd92:7065:b8e:ffff::1 +opcode="$op" >dig.out.test$n || ret=1
    813     grep "status: $EXPECT_STATUS" dig.out.test$n >/dev/null || ret=1
    814     if [ $ret != 0 ]; then echo_i "failed"; fi
    815     status=$((status + ret))
    816 
    817     n=$((n + 1))
    818     echo_i "checking unexpected opcode query over DoT for opcode $op ($n)"
    819     ret=0
    820     dig_with_tls_opts +tls @10.53.0.1 +opcode="$op" >dig.out.test$n || ret=1
    821     grep "status: $EXPECT_STATUS" dig.out.test$n >/dev/null || ret=1
    822     if [ $ret != 0 ]; then echo_i "failed"; fi
    823     status=$((status + ret))
    824 
    825     n=$((n + 1))
    826     echo_i "checking unexpected opcode query over DoT via IPv6 for opcode $op ($n)"
    827     ret=0
    828     dig_with_tls_opts -6 +tls @fd92:7065:b8e:ffff::1 +opcode="$op" >dig.out.test$n || ret=1
    829     grep "status: $EXPECT_STATUS" dig.out.test$n >/dev/null || ret=1
    830     if [ $ret != 0 ]; then echo_i "failed"; fi
    831     status=$((status + ret))
    832   done
    833 }
    834 
    835 test_opcodes NOERROR 0
    836 test_opcodes NOTIMP 1 2 3 6 7 8 9 10 11 12 13 14 15
    837 test_opcodes FORMERR 4 5
    838 
    839 n=$((n + 1))
    840 echo_i "checking server quotas for both encrypted and unencrypted HTTP ($n)"
    841 ret=0
    842 BINDHOST="10.53.0.1" "$PYTHON" "$TOP_SRCDIR/bin/tests/system/doth/stress_http_quota.py" || ret=$?
    843 if [ $ret != 0 ]; then echo_i "failed"; fi
    844 status=$((status + ret))
    845 
    846 # check whether we can use curl for sending test queries.
    847 if [ -x "${CURL}" ]; then
    848   CURL_HTTP2="$(${CURL} --version | grep -E '^Features:.* HTTP2( |$)' || true)"
    849 
    850   if [ -n "$CURL_HTTP2" ]; then
    851     testcurl=1
    852   else
    853     echo_i "The available version of CURL does not have HTTP/2 support"
    854   fi
    855 fi
    856 
    857 # Note: see README.curl for information on how to generate curl
    858 # queries.
    859 if [ -n "$testcurl" ]; then
    860   n=$((n + 1))
    861   echo_i "checking max-age for positive answer ($n)"
    862   ret=0
    863   # use curl to query for 'example/SOA'
    864   $CURL -kD headers.$n "https://10.53.0.1:${HTTPSPORT}/dns-query?dns=AAEAAAABAAAAAAAAB2V4YW1wbGUAAAYAAQ" >/dev/null 2>&1 || ret=1
    865   grep "cache-control: max-age=86400" headers.$n >/dev/null || ret=1
    866   if [ $ret != 0 ]; then echo_i "failed"; fi
    867   status=$((status + ret))
    868 
    869   n=$((n + 1))
    870   echo_i "checking max-age for negative answer ($n)"
    871   ret=0
    872   # use curl to query for 'fake.example/TXT'
    873   $CURL -kD headers.$n "https://10.53.0.1:${HTTPSPORT}/dns-query?dns=AAEAAAABAAAAAAAABGZha2UHZXhhbXBsZQAAEAAB" >/dev/null 2>&1 || ret=1
    874   grep "cache-control: max-age=3600" headers.$n >/dev/null || ret=1
    875   if [ $ret != 0 ]; then echo_i "failed"; fi
    876   status=$((status + ret))
    877 fi
    878 
    879 n=$((n + 1))
    880 echo_i "checking Do53 query to NS5 for zone \"example12\" (verifying successful client TLS context reuse by the NS5 server instance during XoT) ($n)"
    881 ret=0
    882 dig_with_opts +comm @10.53.0.5 example12 SOA >dig.out.test$n || ret=1
    883 grep "status: NOERROR" dig.out.test$n >/dev/null || ret=1
    884 if [ $ret != 0 ]; then echo_i "failed"; fi
    885 status=$((status + ret))
    886 
    887 n=$((n + 1))
    888 echo_i "checking Do53 query to NS5 for zone \"example13\" (verifying successful client TLS context reuse by the NS5 server instance during XoT) ($n)"
    889 ret=0
    890 dig_with_opts +comm @10.53.0.5 example13 SOA >dig.out.test$n || ret=1
    891 grep "status: NOERROR" dig.out.test$n >/dev/null || ret=1
    892 if [ $ret != 0 ]; then echo_i "failed"; fi
    893 status=$((status + ret))
    894 
    895 n=$((n + 1))
    896 echo_i "checking Do53 query to NS5 for zone \"example14\" (verifying successful client TLS context reuse by the NS5 server instance during XoT) ($n)"
    897 ret=0
    898 dig_with_opts +comm @10.53.0.5 example14 SOA >dig.out.test$n || ret=1
    899 grep "status: NOERROR" dig.out.test$n >/dev/null || ret=1
    900 if [ $ret != 0 ]; then echo_i "failed"; fi
    901 status=$((status + ret))
    902 
    903 n=$((n + 1))
    904 echo_i "checking Do53 query to NS5 for zone \"example15\" (verifying successful client TLS context reuse by the NS5 server instance during XoT) ($n)"
    905 ret=0
    906 dig_with_opts +comm @10.53.0.5 example15 SOA >dig.out.test$n || ret=1
    907 grep "status: NOERROR" dig.out.test$n >/dev/null || ret=1
    908 if [ $ret != 0 ]; then echo_i "failed"; fi
    909 status=$((status + ret))
    910 
    911 # see GL #4572
    912 n=$((n + 1))
    913 echo_i "testing that zone forwarding fails when using a wrong TLS configuration on the server without aborting it (a condition for bug #4572, failure expected) ($n)"
    914 ret=0
    915 dig_with_opts test.example.com. -b 10.53.0.10 @10.53.0.2 >dig.out.test$n || ret=1
    916 grep "status: SERVFAIL" dig.out.test$n >/dev/null || ret=1
    917 if test $ret != 0; then echo_i "failed"; fi
    918 status=$((status + ret))
    919 
    920 echo_i "exit status: $status"
    921 [ $status -eq 0 ] || exit 1
    922