diff options
Diffstat (limited to 'bin/tests/system/timeouts')
-rw-r--r-- | bin/tests/system/timeouts/clean.sh | 21 | ||||
-rw-r--r-- | bin/tests/system/timeouts/ns1/example.db | 25 | ||||
-rw-r--r-- | bin/tests/system/timeouts/ns1/named.args | 1 | ||||
-rw-r--r-- | bin/tests/system/timeouts/ns1/named.conf.in | 46 | ||||
-rw-r--r-- | bin/tests/system/timeouts/ns1/root.db | 24 | ||||
-rw-r--r-- | bin/tests/system/timeouts/prereq.sh | 31 | ||||
-rw-r--r-- | bin/tests/system/timeouts/setup.sh | 31 | ||||
-rw-r--r-- | bin/tests/system/timeouts/tests_tcp_timeouts.py | 284 |
8 files changed, 463 insertions, 0 deletions
diff --git a/bin/tests/system/timeouts/clean.sh b/bin/tests/system/timeouts/clean.sh new file mode 100644 index 0000000..0da8a9c --- /dev/null +++ b/bin/tests/system/timeouts/clean.sh @@ -0,0 +1,21 @@ +#!/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 ./ns*/managed-keys.bind* +rm -f ./ns*/named.conf +rm -f ./ns*/named.lock +rm -f ./ns*/named.memstats +rm -f ./ns*/named.run* +rm -f ./ns*/named.stats +rm -rf ./__pycache__ +rm -f ./ns*/large.db diff --git a/bin/tests/system/timeouts/ns1/example.db b/bin/tests/system/timeouts/ns1/example.db new file mode 100644 index 0000000..cb321ff --- /dev/null +++ b/bin/tests/system/timeouts/ns1/example.db @@ -0,0 +1,25 @@ +; 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 ; 5 minutes +@ SOA mname1. . ( + 2000062101 ; serial + 20 ; refresh (20 seconds) + 20 ; retry (20 seconds) + 1814400 ; expire (3 weeks) + 3600 ; minimum (1 hour) + ) + NS ns1 +ns1 A 10.53.0.1 +@ A 10.53.0.1 +a A 10.53.0.1 +b A 10.53.0.1 +$INCLUDE large.db diff --git a/bin/tests/system/timeouts/ns1/named.args b/bin/tests/system/timeouts/ns1/named.args new file mode 100644 index 0000000..2df2be2 --- /dev/null +++ b/bin/tests/system/timeouts/ns1/named.args @@ -0,0 +1 @@ +-m record,size,mctx -c named.conf -d 1 -D timeouts-ns1 -X named.lock -g -T maxcachesize=2097152 diff --git a/bin/tests/system/timeouts/ns1/named.conf.in b/bin/tests/system/timeouts/ns1/named.conf.in new file mode 100644 index 0000000..4b422e2 --- /dev/null +++ b/bin/tests/system/timeouts/ns1/named.conf.in @@ -0,0 +1,46 @@ +/* + * 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. + */ + +include "../../common/rndc.key"; + +controls { + inet 10.53.0.1 port @CONTROLPORT@ allow { any; } keys { rndc_key; }; +}; + +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; + notify no; + tcp-initial-timeout 20; + tcp-idle-timeout 50; + tcp-keepalive-timeout 70; + max-transfer-time-out 5; /* minutes */ + max-transfer-idle-out 1; /* minutes */ +}; + +zone "." { + type primary; + file "root.db"; +}; + +zone "example." { + type primary; + file "example.db"; + check-integrity no; +}; diff --git a/bin/tests/system/timeouts/ns1/root.db b/bin/tests/system/timeouts/ns1/root.db new file mode 100644 index 0000000..cb48acd --- /dev/null +++ b/bin/tests/system/timeouts/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.isc.org. 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 ns1.example. +ns1.example. A 10.53.0.1 diff --git a/bin/tests/system/timeouts/prereq.sh b/bin/tests/system/timeouts/prereq.sh new file mode 100644 index 0000000..2204695 --- /dev/null +++ b/bin/tests/system/timeouts/prereq.sh @@ -0,0 +1,31 @@ +#!/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. + +SYSTEMTESTTOP=.. +. $SYSTEMTESTTOP/conf.sh + +if test -n "$PYTHON" +then + if [ "$($PYTHON -c "import dns.version; print(dns.version.MAJOR)" 2> /dev/null)" -ge 2 ] + then + : + else + echo_i "This test requires the dnspython >= 2.0.0 module." >&2 + exit 1 + fi +else + echo_i "This test requires Python and the dnspython module." >&2 + exit 1 +fi + +exit 0 diff --git a/bin/tests/system/timeouts/setup.sh b/bin/tests/system/timeouts/setup.sh new file mode 100644 index 0000000..65bb057 --- /dev/null +++ b/bin/tests/system/timeouts/setup.sh @@ -0,0 +1,31 @@ +#!/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 + +copy_setports ns1/named.conf.in ns1/named.conf + +# +# Generate a large enough zone, so the transfer takes longer than +# tcp-initial-timeout interval +# +$PYTHON -c " +from __future__ import print_function +print('large IN TXT', end=' ') +for a in range(128): + print('\"%s\"' % ('A' * 240), end=' ') +print('') + +for a in range(150000): + print('%s IN NS a' % (a)) + print('%s IN NS b' % (a))" > ns1/large.db diff --git a/bin/tests/system/timeouts/tests_tcp_timeouts.py b/bin/tests/system/timeouts/tests_tcp_timeouts.py new file mode 100644 index 0000000..d3ee357 --- /dev/null +++ b/bin/tests/system/timeouts/tests_tcp_timeouts.py @@ -0,0 +1,284 @@ +#!/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 time + +import pytest + +pytest.importorskip("dns", minversion="2.0.0") +import dns.edns +import dns.message +import dns.name +import dns.query +import dns.rdataclass +import dns.rdatatype + +import pytest_custom_markers # pylint: disable=import-error + + +TIMEOUT = 10 + + +def create_msg(qname, qtype): + msg = dns.message.make_query( + qname, qtype, want_dnssec=True, use_edns=0, payload=4096 + ) + return msg + + +def timeout(): + return time.time() + TIMEOUT + + +def test_initial_timeout(named_port): + # + # The initial timeout is 2.5 seconds, so this should timeout + # + with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock: + sock.connect(("10.53.0.1", named_port)) + + time.sleep(3) + + msg = create_msg("example.", "A") + + 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_idle_timeout(named_port): + # + # The idle timeout is 5 seconds, so the third message should fail + # + msg = create_msg("example.", "A") + with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock: + sock.connect(("10.53.0.1", named_port)) + + time.sleep(1) + + (sbytes, stime) = dns.query.send_tcp(sock, msg, timeout()) + (response, rtime) = dns.query.receive_tcp(sock, timeout()) + + time.sleep(2) + + (sbytes, stime) = dns.query.send_tcp(sock, msg, timeout()) + (response, rtime) = dns.query.receive_tcp(sock, timeout()) + + time.sleep(6) + + 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_keepalive_timeout(named_port): + # + # Keepalive is 7 seconds, so the third message should succeed. + # + msg = create_msg("example.", "A") + kopt = dns.edns.GenericOption(11, b"\x00") + msg.use_edns(edns=True, payload=4096, options=[kopt]) + + with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock: + sock.connect(("10.53.0.1", named_port)) + + time.sleep(1) + + (sbytes, stime) = dns.query.send_tcp(sock, msg, timeout()) + (response, rtime) = dns.query.receive_tcp(sock, timeout()) + + time.sleep(2) + + (sbytes, stime) = dns.query.send_tcp(sock, msg, timeout()) + (response, rtime) = dns.query.receive_tcp(sock, timeout()) + + time.sleep(6) + + (sbytes, stime) = dns.query.send_tcp(sock, msg, timeout()) + (response, rtime) = dns.query.receive_tcp(sock, timeout()) + + +def test_pipelining_timeout(named_port): + # + # The pipelining should only timeout after the last message is received + # + msg = create_msg("example.", "A") + with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock: + sock.connect(("10.53.0.1", named_port)) + + time.sleep(1) + + # Send and receive 25 DNS queries + for n in range(25): + (sbytes, stime) = dns.query.send_tcp(sock, msg, timeout()) + for n in range(25): + (response, rtime) = dns.query.receive_tcp(sock, timeout()) + + time.sleep(3) + + # Send and receive 25 DNS queries + for n in range(25): + (sbytes, stime) = dns.query.send_tcp(sock, msg, timeout()) + for n in range(25): + (response, rtime) = dns.query.receive_tcp(sock, timeout()) + + time.sleep(6) + + 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_long_axfr(named_port): + # + # The timers should not fire during AXFR, thus the connection should not + # close abruptly + # + with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock: + sock.connect(("10.53.0.1", named_port)) + + name = dns.name.from_text("example.") + msg = create_msg("example.", "AXFR") + (sbytes, stime) = dns.query.send_tcp(sock, msg, timeout()) + + # Receive the initial DNS message with SOA + (response, rtime) = dns.query.receive_tcp( + sock, timeout(), one_rr_per_rrset=True + ) + soa = response.get_rrset( + dns.message.ANSWER, name, dns.rdataclass.IN, dns.rdatatype.SOA + ) + assert soa is not None + + # Pull DNS message from wire until the second SOA is received + while True: + (response, rtime) = dns.query.receive_tcp( + sock, timeout(), one_rr_per_rrset=True + ) + soa = response.get_rrset( + dns.message.ANSWER, name, dns.rdataclass.IN, dns.rdatatype.SOA + ) + if soa is not None: + break + assert soa is not None + + +# This test relies on the maximum socket send buffer size (wmem_max) being set +# to 212992 bytes (the typical default value on Linux systems). Environments +# that use a different value for this setting (for example, FreeBSD defaults to +# 32768 bytes) may need their system-level settings to be tweaked in order for +# this test to pass. +@pytest_custom_markers.flaky(max_runs=3) +def test_send_timeout(named_port): + with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock: + sock.connect(("10.53.0.1", named_port)) + + # Send and receive single large RDATA over TCP + msg = create_msg("large.example.", "TXT") + (sbytes, stime) = dns.query.send_tcp(sock, msg, timeout()) + (response, rtime) = dns.query.receive_tcp(sock, timeout()) + + # Send and receive 28 large (~32k) DNS queries that should + # fill the default maximum 208k TCP send buffer + for n in range(28): + (sbytes, stime) = dns.query.send_tcp(sock, msg, timeout()) + + # configure idle interval is 5 seconds, sleep 6 to make sure we are + # above the interval + time.sleep(6) + + 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 + + +@pytest_custom_markers.long_test +def test_max_transfer_idle_out(named_port): + with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock: + sock.connect(("10.53.0.1", named_port)) + + name = dns.name.from_text("example.") + msg = create_msg("example.", "AXFR") + (sbytes, stime) = dns.query.send_tcp(sock, msg, timeout()) + + # Receive the initial DNS message with SOA + (response, rtime) = dns.query.receive_tcp( + sock, timeout(), one_rr_per_rrset=True + ) + soa = response.get_rrset( + dns.message.ANSWER, name, dns.rdataclass.IN, dns.rdatatype.SOA + ) + assert soa is not None + + time.sleep(61) # max-transfer-idle-out is 1 minute + + with pytest.raises(ConnectionResetError): + # Process queued TCP messages + while True: + (response, rtime) = dns.query.receive_tcp( + sock, timeout(), one_rr_per_rrset=True + ) + soa = response.get_rrset( + dns.message.ANSWER, name, dns.rdataclass.IN, dns.rdatatype.SOA + ) + if soa is not None: + break + assert soa is None + + +@pytest_custom_markers.long_test +def test_max_transfer_time_out(named_port): + with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock: + sock.connect(("10.53.0.1", named_port)) + + name = dns.name.from_text("example.") + msg = create_msg("example.", "AXFR") + (sbytes, stime) = dns.query.send_tcp(sock, msg, timeout()) + + # Receive the initial DNS message with SOA + (response, rtime) = dns.query.receive_tcp( + sock, timeout(), one_rr_per_rrset=True + ) + soa = response.get_rrset( + dns.message.ANSWER, name, dns.rdataclass.IN, dns.rdatatype.SOA + ) + assert soa is not None + + # The loop should timeout at the 5 minutes (max-transfer-time-out) + with pytest.raises(EOFError): + while True: + time.sleep(1) + (response, rtime) = dns.query.receive_tcp( + sock, timeout(), one_rr_per_rrset=True + ) + soa = response.get_rrset( + dns.message.ANSWER, name, dns.rdataclass.IN, dns.rdatatype.SOA + ) + if soa is not None: + break + assert soa is None |