From ebfdb50ac78c40323b2adbd0a9cbbf256e199b8b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Sur=C3=BD?= Date: Tue, 15 Feb 2022 21:01:25 +0100 Subject: [PATCH] Add TCP garbage system test Test if the TCP connection gets reset when garbage instead of DNS message is sent. I'm only happy when it rains Pour some misery down on me - Garbage --- bin/tests/system/tcp/clean.sh | 8 +-- bin/tests/system/tcp/conftest.py | 59 ++++++++++++++++ bin/tests/system/tcp/ns7/named.conf.in | 41 +++++++++++ bin/tests/system/tcp/ns7/root.db | 24 +++++++ bin/tests/system/tcp/setup.sh | 1 + bin/tests/system/tcp/tests-tcp.py | 94 ++++++++++++++++++++++++++ 6 files changed, 223 insertions(+), 4 deletions(-) create mode 100644 bin/tests/system/tcp/conftest.py create mode 100644 bin/tests/system/tcp/ns7/named.conf.in create mode 100644 bin/tests/system/tcp/ns7/root.db create mode 100644 bin/tests/system/tcp/tests-tcp.py 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