]> git.ipfire.org Git - thirdparty/bind9.git/commitdiff
Add system test lingering CLOSE_WAIT TCP sockets
authorMatthijs Mekking <matthijs@isc.org>
Thu, 3 Feb 2022 09:49:25 +0000 (10:49 +0100)
committerMichal Nowak <mnowak@isc.org>
Fri, 8 Apr 2022 07:36:08 +0000 (09:36 +0200)
Add a test case to check for lingering TCP sockets stuck in the
CLOSE_WAIT state. This can happen if a client sends some garbage after
its first query.

The system test runs the reproducer script and then sends another TCP
query to the resolver. The resolver is configured to allow one TCP
client only. If BIND has its TCP socket stuck in CLOSE_WAIT, it does
not have the resources available to answer the second query.

Note: A better test would be to check if the named daemon does not
have a TCP socket stuck in CLOSE_WAIT for example with netstat. When
running this test locally you can examine named with netstat manually.
But since netstat is platform specific it is not a good candidate to do
this as a system test.

If you, if you could return, don't let it burn.
Do you have to let it linger?
- Cranberries

(cherry picked from commit b9ebde705bfcc63e4de04e86cffc67467f9bcfed)

bin/tests/system/tcp/clean.sh
bin/tests/system/tcp/ns7/named.conf.in [new file with mode: 0644]
bin/tests/system/tcp/ns7/named.dropedns [new file with mode: 0644]
bin/tests/system/tcp/ns7/root.db [new file with mode: 0644]
bin/tests/system/tcp/setup.sh
bin/tests/system/tcp/tests-tcp.py [new file with mode: 0644]

index 850f5f9b1c58f940bb648f9dc77216e62ddbf478..ae4cb25c777e7284fd174753b603f01a4deeb8df 100644 (file)
 # 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*
+rm -f ns*/named.memstats
+rm -f ns*/named.run
+rm -f ns*/named.conf
+rm -f ns*/named.stats*
diff --git a/bin/tests/system/tcp/ns7/named.conf.in b/bin/tests/system/tcp/ns7/named.conf.in
new file mode 100644 (file)
index 0000000..bf434d9
--- /dev/null
@@ -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/named.dropedns b/bin/tests/system/tcp/ns7/named.dropedns
new file mode 100644 (file)
index 0000000..37dd9cf
--- /dev/null
@@ -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 (file)
index 0000000..bb31741
--- /dev/null
@@ -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
index bf5f4399da8b16763644e17e8b73ed5c3033f263..70aee8c793b8ab9d3ecea02bccb25265ceb21f0a 100644 (file)
@@ -21,3 +21,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 (file)
index 0000000..41659f7
--- /dev/null
@@ -0,0 +1,73 @@
+#!/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.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=1)
+    sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, True)
+    return sock
+
+
+# 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())