diff --git a/bin/tests/system/tcp/clean.sh b/bin/tests/system/tcp/clean.sh index 850f5f9b1c..1ea5b60d0f 100644 --- a/bin/tests/system/tcp/clean.sh +++ b/bin/tests/system/tcp/clean.sh @@ -11,10 +11,10 @@ # 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 ./*/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* diff --git a/bin/tests/system/tcp/conftest.py b/bin/tests/system/tcp/conftest.py new file mode 100644 index 0000000000..0ce749b3fd --- /dev/null +++ b/bin/tests/system/tcp/conftest.py @@ -0,0 +1,59 @@ +# 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. + +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 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 0000000000..bf434d9913 --- /dev/null +++ b/bin/tests/system/tcp/ns7/named.conf.in @@ -0,0 +1,41 @@ +/* + * 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; + notify yes; + statistics-file "named.stats"; + tcp-clients 1; + keep-response-order { any; }; +}; + +key rndc_key { + secret "1234abcd8765"; + algorithm hmac-sha256; +}; + +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/root.db b/bin/tests/system/tcp/ns7/root.db new file mode 100644 index 0000000000..bb31741496 --- /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 index 124326f882..475f399048 100644 --- a/bin/tests/system/tcp/setup.sh +++ b/bin/tests/system/tcp/setup.sh @@ -20,3 +20,4 @@ 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-tcp.py b/bin/tests/system/tcp/tests-tcp.py new file mode 100644 index 0000000000..7ff84f264b --- /dev/null +++ b/bin/tests/system/tcp/tests-tcp.py @@ -0,0 +1,94 @@ +#!/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 + +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 + + +def create_socket(host, port): + sock = socket.create_connection((host, port), timeout=1) + sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, True) + return sock + + +@pytest.mark.dnspython +@pytest.mark.dnspython2 +def test_tcp_garbage(port): + import dns.query + + with create_socket("10.53.0.7", 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 + + +@pytest.mark.dnspython +@pytest.mark.dnspython2 +def test_tcp_garbage_response(port): + import dns.query + import dns.message + + with create_socket("10.53.0.7", 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