mirror of
https://github.com/isc-projects/bind9.git
synced 2026-06-03 22:08:25 -04:00
Add TCP timeouts system test
The system tests were missing a test that would test tcp-initial-timeout
and tcp-idle-timeout.
This commit adds new "timeouts" system test that adds:
* Test that waits longer than tcp-initial-timeout and then checks
whether the socket was closed
* Test that sends and receives DNS message then waits longer than
tcp-initial-timeout but shorter time than tcp-idle-timeout than
sends DNS message again than waits longer than tcp-idle-timeout
and checks whether the socket was closed
* Similar test, but bursting 25 DNS messages than waiting longer than
tcp-initial-timeout and shorter than tcp-idle-timeout than do second
25 DNS message burst
* Check whether transfer longer than tcp-initial-timeout succeeds
This commit is contained in:
parent
2edba8777f
commit
64cff61c02
11 changed files with 383 additions and 1 deletions
|
|
@ -5,3 +5,4 @@ disable=
|
|||
C0116, # missing-function-docstring
|
||||
R0801, # duplicate-code
|
||||
C0103, # invalid-name
|
||||
C0415,# import-outside-toplevel
|
||||
|
|
|
|||
|
|
@ -201,7 +201,7 @@ if HAVE_PYTHON
|
|||
TESTS += kasp tcp pipelined
|
||||
|
||||
if HAVE_PYMOD_DNS
|
||||
TESTS += qmin cookie
|
||||
TESTS += qmin cookie timeouts
|
||||
|
||||
if HAVE_PERLMOD_NET_DNS
|
||||
TESTS += dnssec
|
||||
|
|
|
|||
19
bin/tests/system/timeouts/clean.sh
Normal file
19
bin/tests/system/timeouts/clean.sh
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
#!/bin/sh
|
||||
#
|
||||
# Copyright (C) Internet Systems Consortium, Inc. ("ISC")
|
||||
#
|
||||
# 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 ./.cache ./__pycache__
|
||||
rm -f ./ns*/large.db
|
||||
59
bin/tests/system/timeouts/conftest.py
Normal file
59
bin/tests/system/timeouts/conftest.py
Normal file
|
|
@ -0,0 +1,59 @@
|
|||
############################################################################
|
||||
# Copyright (C) Internet Systems Consortium, Inc. ("ISC")
|
||||
#
|
||||
# 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.
|
||||
############################################################################
|
||||
|
||||
import os
|
||||
import pytest
|
||||
|
||||
|
||||
def pytest_configure(config):
|
||||
config.addinivalue_line(
|
||||
"markers", "dnspython: mark tests that need dnspython to function"
|
||||
)
|
||||
config.addinivalue_line(
|
||||
"markers", "dnspython2: mark tests that need dnspython >= 2.0.0"
|
||||
)
|
||||
|
||||
|
||||
def pytest_collection_modifyitems(config, items):
|
||||
# pylint: disable=unused-argument,unused-import,too-many-branches
|
||||
# pylint: disable=import-outside-toplevel
|
||||
|
||||
# Test for dnspython module
|
||||
skip_dnspython = pytest.mark.skip(
|
||||
reason="need dnspython module to run")
|
||||
try:
|
||||
import dns.query # noqa: F401
|
||||
except ModuleNotFoundError:
|
||||
for item in items:
|
||||
if "dnspython" in item.keywords:
|
||||
item.add_marker(skip_dnspython)
|
||||
|
||||
# Test for dnspython >= 2.0.0 module
|
||||
skip_dnspython2 = pytest.mark.skip(
|
||||
reason="need dnspython >= 2.0.0 module to run")
|
||||
try:
|
||||
from dns.query import send_tcp # noqa: F401
|
||||
except ImportError:
|
||||
for item in items:
|
||||
if "dnspython2" in item.keywords:
|
||||
item.add_marker(skip_dnspython2)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def port(request):
|
||||
# pylint: disable=unused-argument
|
||||
env_port = os.getenv("PORT")
|
||||
if port is None:
|
||||
env_port = 5300
|
||||
else:
|
||||
env_port = int(env_port)
|
||||
|
||||
return env_port
|
||||
23
bin/tests/system/timeouts/ns1/example.db
Normal file
23
bin/tests/system/timeouts/ns1/example.db
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
; Copyright (C) Internet Systems Consortium, Inc. ("ISC")
|
||||
;
|
||||
; 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 http://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
|
||||
41
bin/tests/system/timeouts/ns1/named.conf.in
Normal file
41
bin/tests/system/timeouts/ns1/named.conf.in
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
/*
|
||||
* Copyright (C) Internet Systems Consortium, Inc. ("ISC")
|
||||
*
|
||||
* 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 http://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;
|
||||
};
|
||||
|
||||
zone "." {
|
||||
type primary;
|
||||
file "root.db";
|
||||
};
|
||||
|
||||
zone "example." {
|
||||
type primary;
|
||||
file "example.db";
|
||||
check-integrity no;
|
||||
};
|
||||
22
bin/tests/system/timeouts/ns1/root.db
Normal file
22
bin/tests/system/timeouts/ns1/root.db
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
; Copyright (C) Internet Systems Consortium, Inc. ("ISC")
|
||||
;
|
||||
; 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 http://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
|
||||
28
bin/tests/system/timeouts/prereq.sh
Normal file
28
bin/tests/system/timeouts/prereq.sh
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
#!/bin/sh
|
||||
#
|
||||
# Copyright (C) Internet Systems Consortium, Inc. ("ISC")
|
||||
#
|
||||
# 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
|
||||
|
||||
if test -n "$PYTHON"
|
||||
then
|
||||
if $PYTHON -c "from dns.query import send_tcp" 2> /dev/null
|
||||
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
|
||||
23
bin/tests/system/timeouts/setup.sh
Normal file
23
bin/tests/system/timeouts/setup.sh
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
#!/bin/sh
|
||||
#
|
||||
# Copyright (C) Internet Systems Consortium, Inc. ("ISC")
|
||||
#
|
||||
# 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 "
|
||||
for a in range(150000):
|
||||
print('%s IN NS a' % (a))
|
||||
print('%s IN NS b' % (a))" > ns1/large.db
|
||||
161
bin/tests/system/timeouts/tests-tcp.py
Normal file
161
bin/tests/system/timeouts/tests-tcp.py
Normal file
|
|
@ -0,0 +1,161 @@
|
|||
#!/usr/bin/python3
|
||||
############################################################################
|
||||
# Copyright (C) Internet Systems Consortium, Inc. ("ISC")
|
||||
#
|
||||
# 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
|
||||
|
||||
TIMEOUT = 10
|
||||
|
||||
|
||||
def create_msg(qname, qtype):
|
||||
import dns.message
|
||||
msg = dns.message.make_query(qname, qtype, want_dnssec=True,
|
||||
use_edns=0, payload=4096)
|
||||
|
||||
return msg
|
||||
|
||||
|
||||
def timeout():
|
||||
return time.time() + TIMEOUT
|
||||
|
||||
|
||||
@pytest.mark.dnspython
|
||||
@pytest.mark.dnspython2
|
||||
def test_initial_timeout(port):
|
||||
#
|
||||
# The initial timeout is 2.5 seconds, so this should timeout
|
||||
#
|
||||
import dns.query
|
||||
|
||||
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock:
|
||||
sock.connect(("10.53.0.1", 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 ConnectionResetError as e:
|
||||
raise EOFError from e
|
||||
|
||||
|
||||
@pytest.mark.dnspython
|
||||
@pytest.mark.dnspython2
|
||||
def test_idle_timeout(port):
|
||||
#
|
||||
# The idle timeout is 5 second, so sending the second message must fail
|
||||
#
|
||||
import dns.rcode
|
||||
|
||||
msg = create_msg("example.", "A")
|
||||
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock:
|
||||
sock.connect(("10.53.0.1", port))
|
||||
|
||||
time.sleep(1)
|
||||
|
||||
(sbytes, stime) = dns.query.send_tcp(sock, msg, timeout())
|
||||
(response, rtime) = dns.query.receive_tcp(sock, timeout())
|
||||
|
||||
time.sleep(3)
|
||||
|
||||
(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 ConnectionResetError as e:
|
||||
raise EOFError from e
|
||||
|
||||
|
||||
@pytest.mark.dnspython
|
||||
@pytest.mark.dnspython2
|
||||
def test_pipelining_timeout(port):
|
||||
#
|
||||
# The pipelining should only timeout after the last message is received
|
||||
#
|
||||
import dns.query
|
||||
|
||||
msg = create_msg("example.", "A")
|
||||
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock:
|
||||
sock.connect(("10.53.0.1", 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 ConnectionResetError as e:
|
||||
raise EOFError from e
|
||||
|
||||
|
||||
@pytest.mark.dnspython
|
||||
@pytest.mark.dnspython2
|
||||
def test_long_axfr(port):
|
||||
#
|
||||
# The timers should not fire during AXFR, thus the connection should not
|
||||
# close abruptly
|
||||
#
|
||||
import dns.query
|
||||
import dns.rdataclass
|
||||
import dns.rdatatype
|
||||
|
||||
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock:
|
||||
sock.connect(("10.53.0.1", 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
|
||||
|
|
@ -892,6 +892,11 @@
|
|||
./bin/tests/system/testsock.pl PERL 2000,2001,2004,2007,2010,2011,2012,2013,2016,2018,2019,2020,2021
|
||||
./bin/tests/system/testsock6.pl PERL 2010,2012,2014,2016,2018,2019,2020,2021
|
||||
./bin/tests/system/testsummary.sh SH 2018,2019,2020,2021
|
||||
./bin/tests/system/timeouts/clean.sh SH 2021
|
||||
./bin/tests/system/timeouts/conftest.py PYTHON 2021
|
||||
./bin/tests/system/timeouts/prereq.sh SH 2021
|
||||
./bin/tests/system/timeouts/setup.sh SH 2021
|
||||
./bin/tests/system/timeouts/tests-tcp.py PYTHON-BIN 2021
|
||||
./bin/tests/system/tkey/clean.sh SH 2001,2004,2007,2011,2012,2013,2014,2015,2016,2018,2019,2020,2021
|
||||
./bin/tests/system/tkey/keycreate.c C 2001,2004,2005,2007,2009,2011,2012,2014,2015,2016,2017,2018,2019,2020,2021
|
||||
./bin/tests/system/tkey/keydelete.c C 2001,2004,2005,2007,2009,2010,2011,2014,2015,2016,2017,2018,2019,2020,2021
|
||||
|
|
|
|||
Loading…
Reference in a new issue