diff options
Diffstat (limited to 'bin/tests/system/tcp')
-rw-r--r-- | bin/tests/system/tcp/1996-alloc_dnsbuf-crash-test.pkt | 12 | ||||
-rw-r--r-- | bin/tests/system/tcp/ans6/ans.py | 157 | ||||
-rw-r--r-- | bin/tests/system/tcp/clean.sh | 22 | ||||
-rw-r--r-- | bin/tests/system/tcp/ns1/named.conf.in | 40 | ||||
-rw-r--r-- | bin/tests/system/tcp/ns1/root.db | 24 | ||||
-rw-r--r-- | bin/tests/system/tcp/ns2/example.db | 28 | ||||
-rw-r--r-- | bin/tests/system/tcp/ns2/named.conf.in | 47 | ||||
-rw-r--r-- | bin/tests/system/tcp/ns3/named.conf.in | 42 | ||||
-rw-r--r-- | bin/tests/system/tcp/ns4/named.conf.in | 44 | ||||
-rw-r--r-- | bin/tests/system/tcp/ns5/named.conf.in | 45 | ||||
-rw-r--r-- | bin/tests/system/tcp/ns7/named.conf.in | 42 | ||||
-rw-r--r-- | bin/tests/system/tcp/ns7/named.dropedns | 1 | ||||
-rw-r--r-- | bin/tests/system/tcp/ns7/root.db | 24 | ||||
-rw-r--r-- | bin/tests/system/tcp/setup.sh | 23 | ||||
-rw-r--r-- | bin/tests/system/tcp/tests.sh | 203 | ||||
-rw-r--r-- | bin/tests/system/tcp/tests_sh_tcp.py | 14 | ||||
-rw-r--r-- | bin/tests/system/tcp/tests_tcp.py | 116 |
17 files changed, 884 insertions, 0 deletions
diff --git a/bin/tests/system/tcp/1996-alloc_dnsbuf-crash-test.pkt b/bin/tests/system/tcp/1996-alloc_dnsbuf-crash-test.pkt new file mode 100644 index 0000000..7520c3a --- /dev/null +++ b/bin/tests/system/tcp/1996-alloc_dnsbuf-crash-test.pkt @@ -0,0 +1,12 @@ +# Transaction ID +0001 +# Standard query +0000 +# Questions: 1, Additional: 1 +0001 0000 0000 0000 +# QNAME: www.isc.org +03 697363 03 6F7267 00 +# Type: AXFR +00fc +# Class: IN +0001 diff --git a/bin/tests/system/tcp/ans6/ans.py b/bin/tests/system/tcp/ans6/ans.py new file mode 100644 index 0000000..4595ddc --- /dev/null +++ b/bin/tests/system/tcp/ans6/ans.py @@ -0,0 +1,157 @@ +# Copyright (C) Internet Systems Consortium, Inc. ("ISC") +# +# SPDX-License-Identifier: MPL-2.0 +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, you can obtain one at https://mozilla.org/MPL/2.0/. +# +# See the COPYRIGHT file distributed with this work for additional +# information regarding copyright ownership. + +############################################################################ +# +# This tool allows an arbitrary number of TCP connections to be made to the +# specified service and to keep them open until told otherwise. It is +# controlled by writing text commands to a TCP socket (default port: 5309). +# +# Currently supported commands: +# +# - open <COUNT> <HOST> <PORT> +# +# Opens <COUNT> TCP connections to <HOST>:<PORT> and keeps them open. +# <HOST> must be an IP address (IPv4 or IPv6). +# +# - close <COUNT> +# +# Close the oldest <COUNT> previously established connections. +# +############################################################################ + +from __future__ import print_function + +import datetime +import errno +import os +import select +import signal +import socket +import sys +import time + + +# Timeout for establishing all connections requested by a single 'open' command. +OPEN_TIMEOUT = 2 +VERSION_QUERY = b"\x00\x1e\xaf\xb8\x01\x00\x00\x01\x00\x00\x00\x00\x00\x00\x07version\x04bind\x00\x00\x10\x00\x03" + + +def log(msg): + print(datetime.datetime.now().strftime("%d-%b-%Y %H:%M:%S.%f ") + msg) + + +def open_connections(active_conns, count, host, port): + queued = [] + errors = [] + + try: + socket.inet_aton(host) + family = socket.AF_INET + except socket.error: + family = socket.AF_INET6 + + log("Opening %d connections..." % count) + + for _ in range(count): + sock = socket.socket(family, socket.SOCK_STREAM) + sock.setblocking(0) + err = sock.connect_ex((host, port)) + if err not in (0, errno.EINPROGRESS): + log("%s on connect for socket %s" % (errno.errorcode[err], sock)) + errors.append(sock) + else: + queued.append(sock) + + start = time.time() + while queued: + now = time.time() + time_left = OPEN_TIMEOUT - (now - start) + if time_left <= 0: + break + _, wsocks, _ = select.select([], queued, [], time_left) + for sock in wsocks: + queued.remove(sock) + err = sock.getsockopt(socket.SOL_SOCKET, socket.SO_ERROR) + if err: + log("%s for socket %s" % (errno.errorcode[err], sock)) + errors.append(sock) + else: + sock.send(VERSION_QUERY) + active_conns.append(sock) + + if errors: + log("result=FAIL: %d connection(s) failed" % len(errors)) + elif queued: + log("result=FAIL: Timed out, aborting %d pending connections" % len(queued)) + for sock in queued: + sock.close() + else: + log("result=OK: Successfully opened %d connections" % count) + + +def close_connections(active_conns, count): + log("Closing %s connections..." % "all" if count == 0 else str(count)) + if count == 0: + count = len(active_conns) + for _ in range(count): + sock = active_conns.pop(0) + sock.close() + log("result=OK: Successfully closed %d connections" % count) + + +def sigterm(*_): + log("SIGTERM received, shutting down") + os.remove("ans.pid") + sys.exit(0) + + +def main(): + active_conns = [] + + signal.signal(signal.SIGTERM, sigterm) + + with open("ans.pid", "w") as pidfile: + print(os.getpid(), file=pidfile) + + listenip = "10.53.0.6" + try: + port = int(os.environ["CONTROLPORT"]) + except KeyError: + port = 5309 + + log("Listening on %s:%d" % (listenip, port)) + + ctlsock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + ctlsock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) + ctlsock.bind((listenip, port)) + ctlsock.listen(1) + + while True: + (clientsock, _) = ctlsock.accept() + log("Accepted control connection from %s" % clientsock) + cmdline = clientsock.recv(512).decode("ascii").strip() + if cmdline: + log("Received command: %s" % cmdline) + cmd = cmdline.split() + if cmd[0] == "open": + count, host, port = cmd[1:] + open_connections(active_conns, int(count), host, int(port)) + elif cmd[0] == "close": + (count,) = cmd[1:] + close_connections(active_conns, int(count)) + else: + log("result=FAIL: Unknown command") + clientsock.close() + + +if __name__ == "__main__": + main() diff --git a/bin/tests/system/tcp/clean.sh b/bin/tests/system/tcp/clean.sh new file mode 100644 index 0000000..1ea5b60 --- /dev/null +++ b/bin/tests/system/tcp/clean.sh @@ -0,0 +1,22 @@ +#!/bin/sh + +# Copyright (C) Internet Systems Consortium, Inc. ("ISC") +# +# SPDX-License-Identifier: MPL-2.0 +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, you can obtain one at https://mozilla.org/MPL/2.0/. +# +# See the COPYRIGHT file distributed with this work for additional +# information regarding copyright ownership. + +rm -f ./*/named.memstats +rm -f ./*/named.run +rm -f ./*/named.conf +rm -f ./*/named.stats* +rm -f ans6/ans.run* +rm -f dig.out* +rm -f rndc.out* +rm -f ns*/named.lock +rm -f ns*/managed-keys.bind* diff --git a/bin/tests/system/tcp/ns1/named.conf.in b/bin/tests/system/tcp/ns1/named.conf.in new file mode 100644 index 0000000..010e754 --- /dev/null +++ b/bin/tests/system/tcp/ns1/named.conf.in @@ -0,0 +1,40 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * SPDX-License-Identifier: MPL-2.0 + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, you can obtain one at https://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +options { + query-source address 10.53.0.1; + notify-source 10.53.0.1; + transfer-source 10.53.0.1; + port @PORT@; + pid-file "named.pid"; + listen-on { 10.53.0.1; }; + listen-on-v6 { none; }; + recursion no; + dnssec-validation no; + notify yes; + statistics-file "named.stats"; +}; + +key rndc_key { + secret "1234abcd8765"; + algorithm @DEFAULT_HMAC@; +}; + +controls { + inet 10.53.0.1 port @CONTROLPORT@ allow { any; } keys { rndc_key; }; +}; + +zone "." { + type primary; + file "root.db"; +}; diff --git a/bin/tests/system/tcp/ns1/root.db b/bin/tests/system/tcp/ns1/root.db new file mode 100644 index 0000000..17780d1 --- /dev/null +++ b/bin/tests/system/tcp/ns1/root.db @@ -0,0 +1,24 @@ +; Copyright (C) Internet Systems Consortium, Inc. ("ISC") +; +; SPDX-License-Identifier: MPL-2.0 +; +; This Source Code Form is subject to the terms of the Mozilla Public +; License, v. 2.0. If a copy of the MPL was not distributed with this +; file, you can obtain one at https://mozilla.org/MPL/2.0/. +; +; See the COPYRIGHT file distributed with this work for additional +; information regarding copyright ownership. + +$TTL 300 +. IN SOA gson.nominum.com. a.root.servers.nil. ( + 2000042100 ; serial + 600 ; refresh + 600 ; retry + 1200 ; expire + 600 ; minimum + ) +. NS a.root-servers.nil. +a.root-servers.nil. A 10.53.0.1 + +example. NS ns2.example. +ns2.example. A 10.53.0.2 diff --git a/bin/tests/system/tcp/ns2/example.db b/bin/tests/system/tcp/ns2/example.db new file mode 100644 index 0000000..4d60ce3 --- /dev/null +++ b/bin/tests/system/tcp/ns2/example.db @@ -0,0 +1,28 @@ +; Copyright (C) Internet Systems Consortium, Inc. ("ISC") +; +; SPDX-License-Identifier: MPL-2.0 +; +; This Source Code Form is subject to the terms of the Mozilla Public +; License, v. 2.0. If a copy of the MPL was not distributed with this +; file, you can obtain one at https://mozilla.org/MPL/2.0/. +; +; See the COPYRIGHT file distributed with this work for additional +; information regarding copyright ownership. + +$ORIGIN . +$TTL 300 ; 5 minutes +example IN SOA mname1. . ( + 1 ; serial + 20 ; refresh (20 seconds) + 20 ; retry (20 seconds) + 1814400 ; expire (3 weeks) + 3600 ; minimum (1 hour) + ) +example. NS ns2.example. +ns2.example. A 10.53.0.2 + +$ORIGIN example. +a A 10.0.0.1 + MX 10 mail.example. + +mail A 10.0.0.2 diff --git a/bin/tests/system/tcp/ns2/named.conf.in b/bin/tests/system/tcp/ns2/named.conf.in new file mode 100644 index 0000000..0a97093 --- /dev/null +++ b/bin/tests/system/tcp/ns2/named.conf.in @@ -0,0 +1,47 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * SPDX-License-Identifier: MPL-2.0 + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, you can obtain one at https://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +options { + query-source address 10.53.0.2; + notify-source 10.53.0.2; + transfer-source 10.53.0.2; + port @PORT@; + pid-file "named.pid"; + listen-on { 10.53.0.2; }; + listen-on-v6 { none; }; + recursion yes; + dnssec-validation no; + notify yes; + statistics-file "named.stats"; +}; + +key rndc_key { + secret "1234abcd8765"; + algorithm @DEFAULT_HMAC@; +}; + +controls { + inet 10.53.0.2 port @CONTROLPORT@ allow { any; } keys { rndc_key; }; +}; + + +zone "." { + type hint; + file "../../common/root.hint"; +}; + +zone "example" { + type primary; + file "example.db"; + allow-update { any; }; +}; diff --git a/bin/tests/system/tcp/ns3/named.conf.in b/bin/tests/system/tcp/ns3/named.conf.in new file mode 100644 index 0000000..8516d72 --- /dev/null +++ b/bin/tests/system/tcp/ns3/named.conf.in @@ -0,0 +1,42 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * SPDX-License-Identifier: MPL-2.0 + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, you can obtain one at https://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +options { + query-source address 10.53.0.3; + notify-source 10.53.0.3; + transfer-source 10.53.0.3; + port @PORT@; + directory "."; + pid-file "named.pid"; + listen-on { 10.53.0.3; }; + listen-on-v6 { none; }; + recursion yes; + dnssec-validation no; + notify yes; +}; + +server 10.53.0.1 { tcp-only yes; }; + +key rndc_key { + secret "1234abcd8765"; + algorithm @DEFAULT_HMAC@; +}; + +controls { + inet 10.53.0.3 port @CONTROLPORT@ allow { any; } keys { rndc_key; }; +}; + +zone "." { + type hint; + file "../../common/root.hint"; +}; diff --git a/bin/tests/system/tcp/ns4/named.conf.in b/bin/tests/system/tcp/ns4/named.conf.in new file mode 100644 index 0000000..a7758cc --- /dev/null +++ b/bin/tests/system/tcp/ns4/named.conf.in @@ -0,0 +1,44 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * SPDX-License-Identifier: MPL-2.0 + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, you can obtain one at https://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +options { + query-source address 10.53.0.4; + notify-source 10.53.0.4; + transfer-source 10.53.0.4; + port @PORT@; + directory "."; + pid-file "named.pid"; + listen-on { 10.53.0.4; }; + listen-on-v6 { none; }; + recursion yes; + dnssec-validation no; + notify yes; + forwarders { 10.53.0.2; }; + forward only; +}; + +server 10.53.0.2 { tcp-only yes; }; + +key rndc_key { + secret "1234abcd8765"; + algorithm @DEFAULT_HMAC@; +}; + +controls { + inet 10.53.0.4 port @CONTROLPORT@ allow { any; } keys { rndc_key; }; +}; + +zone "." { + type hint; + file "../../common/root.hint"; +}; diff --git a/bin/tests/system/tcp/ns5/named.conf.in b/bin/tests/system/tcp/ns5/named.conf.in new file mode 100644 index 0000000..bd754f7 --- /dev/null +++ b/bin/tests/system/tcp/ns5/named.conf.in @@ -0,0 +1,45 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * SPDX-License-Identifier: MPL-2.0 + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, you can obtain one at https://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +// NS5 + +key rndc_key { + secret "1234abcd8765"; + algorithm @DEFAULT_HMAC@; +}; + +controls { + inet 10.53.0.5 port @CONTROLPORT@ allow { any; } keys { rndc_key; }; +}; + + +options { + query-source address 10.53.0.5; + notify-source 10.53.0.5; + transfer-source 10.53.0.5; + port @PORT@; + directory "."; + pid-file "named.pid"; + listen-on { 10.53.0.5; }; + listen-on-v6 { none; }; + tcp-listen-queue 32; + recursion yes; + notify yes; + tcp-clients 17; + dnssec-validation no; +}; + +zone "." { + type hint; + file "../../common/root.hint"; +}; diff --git a/bin/tests/system/tcp/ns7/named.conf.in b/bin/tests/system/tcp/ns7/named.conf.in new file mode 100644 index 0000000..5441519 --- /dev/null +++ b/bin/tests/system/tcp/ns7/named.conf.in @@ -0,0 +1,42 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * SPDX-License-Identifier: MPL-2.0 + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, you can obtain one at https://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +options { + query-source address 10.53.0.7; + notify-source 10.53.0.7; + transfer-source 10.53.0.7; + port @PORT@; + pid-file "named.pid"; + listen-on { 10.53.0.7; }; + listen-on-v6 { none; }; + recursion no; + dnssec-validation no; + notify yes; + statistics-file "named.stats"; + tcp-clients 1; + keep-response-order { any; }; +}; + +key rndc_key { + secret "1234abcd8765"; + algorithm @DEFAULT_HMAC@; +}; + +controls { + inet 10.53.0.7 port @CONTROLPORT@ allow { any; } keys { rndc_key; }; +}; + +zone "." { + type primary; + file "root.db"; +}; diff --git a/bin/tests/system/tcp/ns7/named.dropedns b/bin/tests/system/tcp/ns7/named.dropedns new file mode 100644 index 0000000..37dd9cf --- /dev/null +++ b/bin/tests/system/tcp/ns7/named.dropedns @@ -0,0 +1 @@ +dropedns diff --git a/bin/tests/system/tcp/ns7/root.db b/bin/tests/system/tcp/ns7/root.db new file mode 100644 index 0000000..bb31741 --- /dev/null +++ b/bin/tests/system/tcp/ns7/root.db @@ -0,0 +1,24 @@ +; Copyright (C) Internet Systems Consortium, Inc. ("ISC") +; +; SPDX-License-Identifier: MPL-2.0 +; +; This Source Code Form is subject to the terms of the Mozilla Public +; License, v. 2.0. If a copy of the MPL was not distributed with this +; file, you can obtain one at https://mozilla.org/MPL/2.0/. +; +; See the COPYRIGHT file distributed with this work for additional +; information regarding copyright ownership. + +$TTL 300 +. IN SOA gson.nominum.com. a.root.servers.nil. ( + 2000042100 ; serial + 600 ; refresh + 600 ; retry + 1200 ; expire + 600 ; minimum + ) +. NS a.root-servers.nil. +a.root-servers.nil. A 10.53.0.7 + +example. NS ns2.example. +ns2.example. A 10.53.0.2 diff --git a/bin/tests/system/tcp/setup.sh b/bin/tests/system/tcp/setup.sh new file mode 100644 index 0000000..475f399 --- /dev/null +++ b/bin/tests/system/tcp/setup.sh @@ -0,0 +1,23 @@ +#!/bin/sh + +# Copyright (C) Internet Systems Consortium, Inc. ("ISC") +# +# SPDX-License-Identifier: MPL-2.0 +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, you can obtain one at https://mozilla.org/MPL/2.0/. +# +# See the COPYRIGHT file distributed with this work for additional +# information regarding copyright ownership. + +. ../conf.sh + +$SHELL clean.sh + +copy_setports ns1/named.conf.in ns1/named.conf +copy_setports ns2/named.conf.in ns2/named.conf +copy_setports ns3/named.conf.in ns3/named.conf +copy_setports ns4/named.conf.in ns4/named.conf +copy_setports ns5/named.conf.in ns5/named.conf +copy_setports ns7/named.conf.in ns7/named.conf diff --git a/bin/tests/system/tcp/tests.sh b/bin/tests/system/tcp/tests.sh new file mode 100644 index 0000000..12ca679 --- /dev/null +++ b/bin/tests/system/tcp/tests.sh @@ -0,0 +1,203 @@ +#!/bin/sh + +# Copyright (C) Internet Systems Consortium, Inc. ("ISC") +# +# SPDX-License-Identifier: MPL-2.0 +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, you can obtain one at https://mozilla.org/MPL/2.0/. +# +# See the COPYRIGHT file distributed with this work for additional +# information regarding copyright ownership. + +set -e + +# shellcheck source=../conf.sh +. ../conf.sh + +dig_with_opts() { + "${DIG}" -p "${PORT}" "$@" +} + +rndccmd() { + "${RNDC}" -p "${CONTROLPORT}" -c ../common/rndc.conf -s "$@" +} + +status=0 +n=0 + +n=$((n + 1)) +echo_i "initializing TCP statistics ($n)" +ret=0 +rndccmd 10.53.0.1 stats || ret=1 +rndccmd 10.53.0.2 stats || ret=1 +mv ns1/named.stats ns1/named.stats.test$n +mv ns2/named.stats ns2/named.stats.test$n +ntcp10="$(grep "TCP requests received" ns1/named.stats.test$n | tail -1 | awk '{print $1}')" +ntcp20="$(grep "TCP requests received" ns2/named.stats.test$n | tail -1 | awk '{print $1}')" +if [ $ret != 0 ]; then echo_i "failed"; fi +status=$((status + ret)) + +n=$((n + 1)) +echo_i "checking TCP request statistics (resolver) ($n)" +ret=0 +dig_with_opts @10.53.0.3 txt.example. > dig.out.test$n +sleep 1 +rndccmd 10.53.0.1 stats || ret=1 +rndccmd 10.53.0.2 stats || ret=1 +mv ns1/named.stats ns1/named.stats.test$n +mv ns2/named.stats ns2/named.stats.test$n +ntcp11="$(grep "TCP requests received" ns1/named.stats.test$n | tail -1 | awk '{print $1}')" +ntcp21="$(grep "TCP requests received" ns2/named.stats.test$n | tail -1 | awk '{print $1}')" +if [ "$ntcp10" -ge "$ntcp11" ]; then ret=1; fi +if [ "$ntcp20" -ne "$ntcp21" ]; then ret=1; fi +if [ $ret != 0 ]; then echo_i "failed"; fi +status=$((status + ret)) + +n=$((n + 1)) +echo_i "checking TCP request statistics (forwarder) ($n)" +ret=0 +dig_with_opts @10.53.0.4 txt.example. > dig.out.test$n +sleep 1 +rndccmd 10.53.0.1 stats || ret=1 +rndccmd 10.53.0.2 stats || ret=1 +mv ns1/named.stats ns1/named.stats.test$n +mv ns2/named.stats ns2/named.stats.test$n +ntcp12="$(grep "TCP requests received" ns1/named.stats.test$n | tail -1 | awk '{print $1}')" +ntcp22="$(grep "TCP requests received" ns2/named.stats.test$n | tail -1 | awk '{print $1}')" +if [ "$ntcp11" -ne "$ntcp12" ]; then ret=1; fi +if [ "$ntcp21" -ge "$ntcp22" ];then ret=1; fi +if [ $ret != 0 ]; then echo_i "failed"; fi +status=$((status + ret)) + +# -------- TCP high-water tests ---------- +refresh_tcp_stats() { + rndccmd 10.53.0.5 status > rndc.out.$n || ret=1 + TCP_CUR="$(sed -n "s/^tcp clients: \([0-9][0-9]*\).*/\1/p" rndc.out.$n)" + TCP_LIMIT="$(sed -n "s/^tcp clients: .*\/\([0-9][0-9]*\)/\1/p" rndc.out.$n)" + TCP_HIGH="$(sed -n "s/^TCP high-water: \([0-9][0-9]*\)/\1/p" rndc.out.$n)" +} + +# Send a command to the tool script listening on 10.53.0.6. +send_command() { + nextpart ans6/ans.run > /dev/null + echo "$*" | send 10.53.0.6 "${CONTROLPORT}" + wait_for_log_peek 10 "result=" ans6/ans.run || ret=1 + if ! nextpartpeek ans6/ans.run | grep -qF "result=OK"; then + return 1 + fi +} + +# Instructs ans6 to open $1 TCP connections to 10.53.0.5. +open_connections() { + send_command "open" "${1}" 10.53.0.5 "${PORT}" || return 1 +} + +# Instructs ans6 to close $1 TCP connections to 10.53.0.5. +close_connections() { + send_command "close" "${1}" || return 1 +} + +# Check TCP connections are working normally before opening +# multiple connections +n=$((n + 1)) +echo_i "checking TCP query repsonse ($n)" +ret=0 +dig_with_opts +tcp @10.53.0.5 txt.example > dig.out.test$n +grep "status: NXDOMAIN" dig.out.test$n > /dev/null || ret=1 +if [ $ret != 0 ]; then echo_i "failed"; fi +status=$((status + ret)) + +# Check TCP statistics after server startup before using them as a baseline for +# subsequent checks. +n=$((n + 1)) +echo_i "TCP high-water: check initial statistics ($n)" +ret=0 +refresh_tcp_stats +assert_int_equal "${TCP_CUR}" 0 "current TCP clients count" || ret=1 +# We compare initial tcp-highwater value with 1 because as part of the +# system test startup, the script start.pl executes dig to check if target +# named is running, and that increments tcp-quota by one. +assert_int_equal "${TCP_HIGH}" 1 "tcp-highwater count" || ret=1 +if [ $ret != 0 ]; then echo_i "failed"; fi +status=$((status + ret)) + +# Ensure the TCP high-water statistic gets updated after some TCP connections +# are established. +n=$((n + 1)) +echo_i "TCP high-water: check value after some TCP connections are established ($n)" +ret=0 +OLD_TCP_CUR="${TCP_CUR}" +TCP_ADDED=9 +open_connections "${TCP_ADDED}" || ret=1 +check_stats_added() { + refresh_tcp_stats + assert_int_equal "${TCP_CUR}" $((OLD_TCP_CUR + TCP_ADDED)) "current TCP clients count" || return 1 + assert_int_equal "${TCP_HIGH}" $((OLD_TCP_CUR + TCP_ADDED)) "TCP high-water value" || return 1 +} +retry 2 check_stats_added || ret=1 +if [ $ret != 0 ]; then echo_i "failed"; fi +status=$((status + ret)) + +# Ensure the TCP high-water statistic remains unchanged after some TCP +# connections are closed. +n=$((n + 1)) +echo_i "TCP high-water: check value after some TCP connections are closed ($n)" +ret=0 +OLD_TCP_CUR="${TCP_CUR}" +OLD_TCP_HIGH="${TCP_HIGH}" +TCP_REMOVED=5 +close_connections "${TCP_REMOVED}" || ret=1 +check_stats_removed() { + refresh_tcp_stats + assert_int_equal "${TCP_CUR}" $((OLD_TCP_CUR - TCP_REMOVED)) "current TCP clients count" || return 1 + assert_int_equal "${TCP_HIGH}" "${OLD_TCP_HIGH}" "TCP high-water value" || return 1 +} +retry 2 check_stats_removed || ret=1 +if [ $ret != 0 ]; then echo_i "failed"; fi +status=$((status + ret)) + +# Ensure the TCP high-water statistic never exceeds the configured TCP clients +# limit. +n=$((n + 1)) +echo_i "TCP high-water: ensure tcp-clients is an upper bound ($n)" +ret=0 +open_connections $((TCP_LIMIT + 1)) || ret=1 +check_stats_limit() { + refresh_tcp_stats + assert_int_equal "${TCP_CUR}" "${TCP_LIMIT}" "current TCP clients count" || return 1 + assert_int_equal "${TCP_HIGH}" "${TCP_LIMIT}" "TCP high-water value" || return 1 +} +retry 2 check_stats_limit || ret=1 +if [ $ret != 0 ]; then echo_i "failed"; fi +status=$((status + ret)) + +# Check TCP connections are working normally before opening +# multiple connections +n=$((n + 1)) +echo_i "checking TCP response recovery ($n)" +ret=0 +# "0" closes all connections +close_connections 0 || ret=1 +dig_with_opts +tcp @10.53.0.5 txt.example > dig.out.test$n || ret=1 +grep "status: NXDOMAIN" dig.out.test$n > /dev/null || ret=1 +if [ $ret != 0 ]; then echo_i "failed"; fi +status=$((status + ret)) + +#################################################### +# NOTE: The next test resets the debug level to 1. # +#################################################### + +n=$((n + 1)) +echo_i "checking that BIND 9 doesn't crash on long TCP messages ($n)" +ret=0 +# Avoid logging useless information. +rndccmd 10.53.0.1 trace 1 || ret=1 +{ $PERL ../packet.pl -a "10.53.0.1" -p "${PORT}" -t tcp -r 300000 1996-alloc_dnsbuf-crash-test.pkt || ret=1 ; } | cat_i +dig_with_opts +tcp @10.53.0.1 txt.example > dig.out.test$n || ret=1 +if [ $ret != 0 ]; then echo_i "failed"; fi +status=$((status + ret)) + +echo_i "exit status: $status" +[ $status -eq 0 ] || exit 1 diff --git a/bin/tests/system/tcp/tests_sh_tcp.py b/bin/tests/system/tcp/tests_sh_tcp.py new file mode 100644 index 0000000..b1d797c --- /dev/null +++ b/bin/tests/system/tcp/tests_sh_tcp.py @@ -0,0 +1,14 @@ +# Copyright (C) Internet Systems Consortium, Inc. ("ISC") +# +# SPDX-License-Identifier: MPL-2.0 +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, you can obtain one at https://mozilla.org/MPL/2.0/. +# +# See the COPYRIGHT file distributed with this work for additional +# information regarding copyright ownership. + + +def test_tcp(run_tests_sh): + run_tests_sh() diff --git a/bin/tests/system/tcp/tests_tcp.py b/bin/tests/system/tcp/tests_tcp.py new file mode 100644 index 0000000..532b47b --- /dev/null +++ b/bin/tests/system/tcp/tests_tcp.py @@ -0,0 +1,116 @@ +#!/usr/bin/python3 + +# Copyright (C) Internet Systems Consortium, Inc. ("ISC") +# +# SPDX-License-Identifier: MPL-2.0 +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, you can obtain one at https://mozilla.org/MPL/2.0/. +# +# See the COPYRIGHT file distributed with this work for additional +# information regarding copyright ownership. + +# pylint: disable=unused-variable + +import socket +import struct +import time + +import pytest + +pytest.importorskip("dns", minversion="2.0.0") +import dns.message +import dns.query + + +TIMEOUT = 10 + + +def create_msg(qname, qtype, edns=-1): + msg = dns.message.make_query(qname, qtype, use_edns=edns) + return msg + + +def timeout(): + return time.time() + TIMEOUT + + +def create_socket(host, port): + sock = socket.create_connection((host, port), timeout=10) + sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, True) + return sock + + +def test_tcp_garbage(named_port): + with create_socket("10.53.0.7", named_port) as sock: + msg = create_msg("a.example.", "A") + (sbytes, stime) = dns.query.send_tcp(sock, msg, timeout()) + (response, rtime) = dns.query.receive_tcp(sock, timeout()) + + wire = msg.to_wire() + assert len(wire) > 0 + + # Send DNS message shorter than DNS message header (12), + # this should cause the connection to be terminated + sock.send(struct.pack("!H", 11)) + sock.send(struct.pack("!s", b"0123456789a")) + + with pytest.raises(EOFError): + try: + (sbytes, stime) = dns.query.send_tcp(sock, msg, timeout()) + (response, rtime) = dns.query.receive_tcp(sock, timeout()) + except ConnectionError as e: + raise EOFError from e + + +def test_tcp_garbage_response(named_port): + with create_socket("10.53.0.7", named_port) as sock: + msg = create_msg("a.example.", "A") + (sbytes, stime) = dns.query.send_tcp(sock, msg, timeout()) + (response, rtime) = dns.query.receive_tcp(sock, timeout()) + + wire = msg.to_wire() + assert len(wire) > 0 + + # Send DNS response instead of DNS query, this should cause + # the connection to be terminated + + rmsg = dns.message.make_response(msg) + (sbytes, stime) = dns.query.send_tcp(sock, rmsg, timeout()) + + with pytest.raises(EOFError): + try: + (response, rtime) = dns.query.receive_tcp(sock, timeout()) + except ConnectionError as e: + raise EOFError from e + + +# Regression test for CVE-2022-0396 +def test_close_wait(named_port): + with create_socket("10.53.0.7", named_port) as sock: + msg = create_msg("a.example.", "A") + (sbytes, stime) = dns.query.send_tcp(sock, msg, timeout()) + (response, rtime) = dns.query.receive_tcp(sock, timeout()) + + msg = dns.message.make_query("a.example.", "A", use_edns=0, payload=1232) + (sbytes, stime) = dns.query.send_tcp(sock, msg, timeout()) + + # Shutdown the socket, but ignore the other side closing the socket + # first because we sent DNS message with EDNS0 + try: + sock.shutdown(socket.SHUT_RDWR) + except ConnectionError: + pass + except OSError: + pass + + # BIND allows one TCP client, the part above sends DNS messaage with EDNS0 + # after the first query. BIND should react adequately because of + # ns7/named.dropedns and close the socket, making room for the next + # request. If it gets stuck in CLOSE_WAIT state, there is no connection + # available for the query below and it will time out. + with create_socket("10.53.0.7", named_port) as sock: + msg = create_msg("a.example.", "A") + (sbytes, stime) = dns.query.send_tcp(sock, msg, timeout()) + (response, rtime) = dns.query.receive_tcp(sock, timeout()) |