]> git.ipfire.org Git - thirdparty/bind9.git/commitdiff
QNAME minimization
authorWitold Kręcicki <wpk@isc.org>
Wed, 9 May 2018 10:02:11 +0000 (12:02 +0200)
committerWitold Kręcicki <wpk@isc.org>
Tue, 12 Jun 2018 07:18:46 +0000 (09:18 +0200)
20 files changed:
bin/named/config.c
bin/named/server.c
bin/tests/system/qname-minimization/ans2/ans.py [new file with mode: 0755]
bin/tests/system/qname-minimization/clean.sh [new file with mode: 0644]
bin/tests/system/qname-minimization/ns1/named.conf.in [new file with mode: 0644]
bin/tests/system/qname-minimization/ns1/root.db [new file with mode: 0644]
bin/tests/system/qname-minimization/ns3/named.conf.in [new file with mode: 0644]
bin/tests/system/qname-minimization/ns4/named.conf.in [new file with mode: 0644]
bin/tests/system/qname-minimization/ns5/named.conf.in [new file with mode: 0644]
bin/tests/system/qname-minimization/setup.sh [new file with mode: 0644]
bin/tests/system/qname-minimization/tests.sh [new file with mode: 0755]
bin/tests/system/testsock.pl [changed mode: 0644->0755]
doc/misc/options
lib/dns/include/dns/resolver.h
lib/dns/include/dns/view.h
lib/dns/resolver.c
lib/dns/view.c
lib/isccfg/namedconf.c
lib/ns/query.c
util/copyrights

index eb78c2cddce3adbcc396e224d6ffb292c654bb26..7dd9d9e61ab04e7c914ce011d80406faa8adb695 100644 (file)
@@ -185,6 +185,8 @@ options {\n\
        provide-ixfr true;\n\
        query-source address *;\n\
        query-source-v6 address *;\n\
+       qname-minimization no;\n\
+       qname-minimization-strict no;\n\
        recursion true;\n\
        request-expire true;\n\
        request-ixfr true;\n\
index 1f827a8700c612d257f3031a8994deaa7bec5007..c5dc11cd4202b53f9c95171284daed7e0e179a8c 100644 (file)
@@ -4639,6 +4639,16 @@ configure_view(dns_view_t *view, dns_viewlist_t *viewlist,
        INSIST(result == ISC_R_SUCCESS);
        view->recursion = cfg_obj_asboolean(obj);
 
+       obj = NULL;
+       result = named_config_get(maps, "qname-minimization", &obj);
+       INSIST(result == ISC_R_SUCCESS);
+       view->qminimization = cfg_obj_asboolean(obj);
+
+       obj = NULL;
+       result = named_config_get(maps, "qname-minimization-strict", &obj);
+       INSIST(result == ISC_R_SUCCESS);
+       view->qmin_strict = cfg_obj_asboolean(obj);
+
        obj = NULL;
        result = named_config_get(maps, "auth-nxdomain", &obj);
        INSIST(result == ISC_R_SUCCESS);
diff --git a/bin/tests/system/qname-minimization/ans2/ans.py b/bin/tests/system/qname-minimization/ans2/ans.py
new file mode 100755 (executable)
index 0000000..036158e
--- /dev/null
@@ -0,0 +1,206 @@
+############################################################################
+# 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.
+############################################################################
+
+from __future__ import print_function
+import os
+import sys
+import signal
+import socket
+import select
+from datetime import datetime, timedelta
+import time
+import functools
+
+import dns, dns.message, dns.query
+from dns.rdatatype import *
+from dns.rdataclass import *
+from dns.rcode import *
+from dns.name import *
+
+
+# Log query to file
+def logquery(type, qname):
+    with open("qlog", "a") as f:
+        f.write("%s %s\n", type, qname)
+
+############################################################################
+# Respond to a DNS query.
+# For good. it serves:
+# ns2.good. IN A 10.53.0.2
+# icky.icky.icky.ptang.zoop.boing.good. A 192.0.2.1
+# it responds properly (with NODATA empty response) to non-empty terminals
+#
+# For slow. it works the same as for good., but each response is delayed by 400 miliseconds
+#
+# For bad. it works the same as for good., but returns NXDOMAIN to non-empty terminals
+#
+# For 1.0.0.2.ip6.arpa it serves
+# 1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.8.f.4.0.1.0.0.2.ip6.arpa. IN PTR nee.com.
+# 1.0.0.2.ip6.arpa. IN NS ns2.good
+# ip6.arpa. IN NS ns2.good
+############################################################################
+def create_response(msg):
+    m = dns.message.from_wire(msg)
+    qname = m.question[0].name.to_text()
+    lqname = qname.lower()
+    labels = lqname.split('.')
+
+    # get qtype
+    rrtype = m.question[0].rdtype
+    typename = dns.rdatatype.to_text(rrtype)
+    bad = False
+    slow = False
+
+    # log this query
+    with open("query.log", "a") as f:
+        f.write("%s %s\n" % (typename, lqname))
+        print("%s %s" % (typename, lqname), end=" ")
+
+    r = dns.message.make_response(m)
+    r.set_rcode(NOERROR)
+
+    if lqname.endswith("1.0.0.2.ip6.arpa."):
+        # Direct query - give direct answer
+        if lqname == "1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.8.f.4.0.1.0.0.2.ip6.arpa." and rrtype == PTR:
+            # Direct query - give direct answer
+            r.answer.append(dns.rrset.from_text("1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.8.f.4.0.1.0.0.2.ip6.arpa.", 1, IN, PTR, "nee.com."))
+        elif lqname == "1.0.0.2.ip6.arpa." and rrtype == NS:
+            # NS query at the apex
+            r.answer.append(dns.rrset.from_text("1.0.0.2.ip6.arpa.", 1, IN, NS, "ns2.good."))
+        elif "1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.8.f.4.0.1.0.0.2.ip6.arpa.".endswith(lqname):
+            # NODATA answer
+            r.authority.append(dns.rrset.from_text("1.0.0.2.ip6.arpa.", 1, IN, SOA, "ns2.good. hostmaster.arpa. 2018050100 1 1 1 1"))
+        else:
+            # NXDOMAIN
+            r.authority.append(dns.rrset.from_text("1.0.0.2.ip6.arpa.", 1, IN, SOA, "ns2.good. hostmaster.arpa. 2018050100 1 1 1 1"))
+            r.set_rcode(NXDOMAIN)
+        return r
+    elif lqname.endswith("ip6.arpa."):
+        if lqname == "ip6.arpa." and rrtype == NS:
+            # NS query at the apex
+            r.answer.append(dns.rrset.from_text("ip6.arpa.", 1, IN, NS, "ns2.good."))
+        elif "1.0.0.2.ip6.arpa.".endswith(lqname):
+            # NODATA answer
+            r.authority.append(dns.rrset.from_text("ip6.arpa.", 1, IN, SOA, "ns2.good. hostmaster.arpa. 2018050100 1 1 1 1"))
+        else:
+            # NXDOMAIN
+            r.authority.append(dns.rrset.from_text("ip6.arpa.", 1, IN, SOA, "ns2.good. hostmaster.arpa. 2018050100 1 1 1 1"))
+            r.set_rcode(NXDOMAIN)
+        return r
+    elif lqname.endswith("bad."):
+        bad = True
+        suffix = "bad."
+        lqname = lqname[:-4]
+    elif lqname.endswith("good."):
+        suffix = "good."
+        lqname = lqname[:-5]
+    elif lqname.endswith("slow."):
+        slow = True
+        suffix = "slow."
+        lqname = lqname[:-5]
+    else:
+        r.set_rcode(REFUSED)
+        return r
+
+    # Good/bad differs only in how we treat non-empty terminals
+    if lqname == "icky.icky.icky.ptang.zoop.boing." and rrtype == A:
+        r.answer.append(dns.rrset.from_text(lqname + suffix, 1, IN, A, "192.0.2.1"))
+    elif lqname == "" and rrtype == NS:
+        r.answer.append(dns.rrset.from_text(suffix, 1, IN, NS, "ns2." + suffix))
+    elif lqname == "ns2." and rrtype == A:
+        r.answer.append(dns.rrset.from_text("ns2."+suffix, 1, IN, A, "10.53.0.2"))
+    elif lqname == "ns2." and rrtype == AAAA:
+        r.answer.append(dns.rrset.from_text("ns2."+suffix, 1, IN, AAAA, "fd92:7065:b8e:ffff::2"))
+    else:
+        r.authority.append(dns.rrset.from_text(suffix, 1, IN, SOA, "ns2." + suffix + " hostmaster.arpa. 2018050100 1 1 1 1"))
+        if bad or not "icky.icky.icky.ptang.zoop.boing.".endswith(lqname):
+            r.set_rcode(NXDOMAIN)
+    if slow:
+        time.sleep(0.4)
+    return r
+
+
+def sigterm(signum, frame):
+    print ("Shutting down now...")
+    os.remove('ans.pid')
+    running = False
+    sys.exit(0)
+
+############################################################################
+# Main
+#
+# Set up responder and control channel, open the pid file, and start
+# the main loop, listening for queries on the query channel or commands
+# on the control channel and acting on them.
+############################################################################
+ip4 = "10.53.0.2"
+ip6 = "fd92:7065:b8e:ffff::2"
+
+try: port=int(os.environ['PORT'])
+except: port=5300
+
+query4_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
+query4_socket.bind((ip4, port))
+
+havev6 = True
+try:
+    query6_socket = socket.socket(socket.AF_INET6, socket.SOCK_DGRAM)
+    try:
+        query6_socket.bind((ip6, port))
+    except:
+        query6_socket.close()
+        havev6 = False
+except:
+    havev6 = False
+
+signal.signal(signal.SIGTERM, sigterm)
+
+f = open('ans.pid', 'w')
+pid = os.getpid()
+print (pid, file=f)
+f.close()
+
+running = True
+
+print ("Listening on %s port %d" % (ip4, port))
+if havev6:
+    print ("Listening on %s port %d" % (ip6, port))
+print ("Ctrl-c to quit")
+
+if havev6:
+    input = [query4_socket, query6_socket]
+else:
+    input = [query4_socket]
+
+while running:
+    try:
+        inputready, outputready, exceptready = select.select(input, [], [])
+    except select.error as e:
+        break
+    except socket.error as e:
+        break
+    except KeyboardInterrupt:
+        break
+
+    for s in inputready:
+        if s == query4_socket or s == query6_socket:
+            print ("Query received on %s" %
+                    (ip4 if s == query4_socket else ip6), end=" ")
+            # Handle incoming queries
+            msg = s.recvfrom(65535)
+            rsp = create_response(msg[0])
+            if rsp:
+                print(dns.rcode.to_text(rsp.rcode()))
+                s.sendto(rsp.to_wire(), msg[1])
+            else:
+                print("NO RESPONSE")
+    if not running:
+        break
diff --git a/bin/tests/system/qname-minimization/clean.sh b/bin/tests/system/qname-minimization/clean.sh
new file mode 100644 (file)
index 0000000..eaff421
--- /dev/null
@@ -0,0 +1,17 @@
+#!/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 http://mozilla.org/MPL/2.0/.
+#
+# See the COPYRIGHT file distributed with this work for additional
+# information regarding copyright ownership.
+
+rm -f ns*/named.conf
+rm -f */named.memstats
+rm -f */named.run
+rm -f dig.out.*
+rm -f ns*/named.lock
+rm -f ans2/query.log
diff --git a/bin/tests/system/qname-minimization/ns1/named.conf.in b/bin/tests/system/qname-minimization/ns1/named.conf.in
new file mode 100644 (file)
index 0000000..7188e3c
--- /dev/null
@@ -0,0 +1,29 @@
+/*
+ * 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.
+ */
+
+// NS1
+
+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 yes;
+};
+
+zone "." {
+       type master;
+       file "root.db";
+};
diff --git a/bin/tests/system/qname-minimization/ns1/root.db b/bin/tests/system/qname-minimization/ns1/root.db
new file mode 100644 (file)
index 0000000..83f157f
--- /dev/null
@@ -0,0 +1,32 @@
+; 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 20
+.                      IN SOA  wpk.isc.org. a.root.servers.nil. (
+                               2000042100      ; serial
+                               600             ; refresh
+                               600             ; retry
+                               1200            ; expire
+                               2               ; minimum
+                               )
+.                      NS      a.root-servers.nil.
+a.root-servers.nil.    A       10.53.0.1
+ip6.arpa.              NS      ns2.good.
+
+good.                  NS      ns2.good.
+ns2.good.              A       10.53.0.2
+
+bad.                   NS      ns2.bad.
+ns2.bad.               A       10.53.0.2
+
+slow                   NS      ns2.slow.
+ns2.slow.              A       10.53.0.2
+
+horrible.              NS      ns2.horrible.
+ns2.horrible.          A       10.53.0.2
diff --git a/bin/tests/system/qname-minimization/ns3/named.conf.in b/bin/tests/system/qname-minimization/ns3/named.conf.in
new file mode 100644 (file)
index 0000000..2a6dce9
--- /dev/null
@@ -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.
+ */
+
+// NS3
+
+options {
+       query-source address 10.53.0.3;
+       notify-source 10.53.0.3;
+       transfer-source 10.53.0.3;
+       port @PORT@;
+       pid-file "named.pid";
+       listen-on { 10.53.0.3; };
+       listen-on-v6 { none; };
+       recursion yes;
+       qname-minimization no;
+       qname-minimization-strict no;
+       querylog yes;
+       resolver-query-timeout 30;
+};
+
+key rndc_key {
+       secret "1234abcd8765";
+       algorithm hmac-sha256;
+};
+
+controls {
+       inet 10.53.0.3 port @CONTROLPORT@ allow { any; } keys { rndc_key; };
+};
+
+zone "." {
+       type hint;
+       file "../../common/root.hint";
+};
diff --git a/bin/tests/system/qname-minimization/ns4/named.conf.in b/bin/tests/system/qname-minimization/ns4/named.conf.in
new file mode 100644 (file)
index 0000000..403fc0f
--- /dev/null
@@ -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.
+ */
+
+// NS4
+
+options {
+       query-source address 10.53.0.4;
+       notify-source 10.53.0.4;
+       transfer-source 10.53.0.4;
+       port @PORT@;
+       pid-file "named.pid";
+       listen-on { 10.53.0.4; };
+       listen-on-v6 { none; };
+       recursion yes;
+       qname-minimization yes;
+       qname-minimization-strict yes;
+       querylog yes;
+       resolver-query-timeout 30;
+};
+
+key rndc_key {
+       secret "1234abcd8765";
+       algorithm hmac-sha256;
+};
+
+controls {
+       inet 10.53.0.4 port @CONTROLPORT@ allow { any; } keys { rndc_key; };
+};
+
+zone "." {
+       type hint;
+       file "../../common/root.hint";
+};
diff --git a/bin/tests/system/qname-minimization/ns5/named.conf.in b/bin/tests/system/qname-minimization/ns5/named.conf.in
new file mode 100644 (file)
index 0000000..f570b88
--- /dev/null
@@ -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.
+ */
+
+// NS5
+
+options {
+       query-source address 10.53.0.5;
+       notify-source 10.53.0.5;
+       transfer-source 10.53.0.5;
+       port @PORT@;
+       pid-file "named.pid";
+       listen-on { 10.53.0.5; };
+       listen-on-v6 { none; };
+       recursion yes;
+       qname-minimization yes;
+       qname-minimization-strict no;
+       querylog yes;
+       resolver-query-timeout 30;
+};
+
+key rndc_key {
+       secret "1234abcd8765";
+       algorithm hmac-sha256;
+};
+
+controls {
+       inet 10.53.0.5 port @CONTROLPORT@ allow { any; } keys { rndc_key; };
+};
+
+zone "." {
+       type hint;
+       file "../../common/root.hint";
+};
diff --git a/bin/tests/system/qname-minimization/setup.sh b/bin/tests/system/qname-minimization/setup.sh
new file mode 100644 (file)
index 0000000..0294ed7
--- /dev/null
@@ -0,0 +1,20 @@
+#!/bin/sh -e
+#
+# 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.
+
+SYSTEMTESTTOP=..
+. $SYSTEMTESTTOP/conf.sh
+
+$SHELL clean.sh
+
+copy_setports ns1/named.conf.in ns1/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
diff --git a/bin/tests/system/qname-minimization/tests.sh b/bin/tests/system/qname-minimization/tests.sh
new file mode 100755 (executable)
index 0000000..84ab0a4
--- /dev/null
@@ -0,0 +1,138 @@
+#!/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 http://mozilla.org/MPL/2.0/.
+#
+# See the COPYRIGHT file distributed with this work for additional
+# information regarding copyright ownership.
+
+SYSTEMTESTTOP=..
+. $SYSTEMTESTTOP/conf.sh
+
+DIGOPTS="-p ${PORT}"
+RNDCCMD="$RNDC -c $SYSTEMTESTTOP/common/rndc.conf -p ${CONTROLPORT} -s"
+CLEANQL="rm -f ans2/query.log"
+status=0
+n=0
+
+n=`expr $n + 1`
+echo_i "query for .good is not minimized when qname-minimization is off ($n)"
+ret=0
+$CLEANQL
+$RNDCCMD 10.53.0.3 flush
+$DIG $DIGOPTS icky.icky.icky.ptang.zoop.boing.good. @10.53.0.3 > dig.out.test$n
+grep "status: NOERROR" dig.out.test$n > /dev/null || ret=1
+echo "A icky.icky.icky.ptang.zoop.boing.good." | diff ans2/query.log - > /dev/null || ret=1
+if [ $ret != 0 ]; then echo_i "failed"; fi
+status=`expr $status + $ret`
+
+n=`expr $n + 1`
+echo_i "query for .bad is not minimized when qname-minimization is off ($n)"
+ret=0
+$CLEANQL
+$RNDCCMD 10.53.0.3 flush
+$DIG $DIGOPTS icky.icky.icky.ptang.zoop.boing.bad. @10.53.0.3 > dig.out.test$n
+grep "status: NOERROR" dig.out.test$n > /dev/null || ret=1
+echo "A icky.icky.icky.ptang.zoop.boing.bad." | diff ans2/query.log - > /dev/null || ret=1
+if [ $ret != 0 ]; then echo_i "failed"; fi
+status=`expr $status + $ret`
+
+n=`expr $n + 1`
+echo_i "query for .slow is not minimized when qname-minimization is off ($n)"
+ret=0
+$CLEANQL
+$RNDCCMD 10.53.0.3 flush
+$DIG $DIGOPTS icky.icky.icky.ptang.zoop.boing.slow. @10.53.0.3 > dig.out.test$n
+grep "status: NOERROR" dig.out.test$n > /dev/null || ret=1
+echo "A icky.icky.icky.ptang.zoop.boing.slow." | diff ans2/query.log - > /dev/null || ret=1
+if [ $ret != 0 ]; then echo_i "failed"; fi
+status=`expr $status + $ret`
+
+n=`expr $n + 1`
+echo_i "query for .good is properly minimized when qname-minimization is on ($n)"
+ret=0
+$CLEANQL
+$RNDCCMD 10.53.0.4 flush
+$DIG $DIGOPTS icky.icky.icky.ptang.zoop.boing.good. @10.53.0.4 > dig.out.test$n
+grep "status: NOERROR" dig.out.test$n > /dev/null || ret=1
+cat << __EOF | diff ans2/query.log - > /dev/null || ret=1
+NS boing.good.
+NS zoop.boing.good.
+NS ptang.zoop.boing.good.
+NS icky.ptang.zoop.boing.good.
+NS icky.icky.ptang.zoop.boing.good.
+A icky.icky.icky.ptang.zoop.boing.good.
+__EOF
+if [ $ret != 0 ]; then echo_i "failed"; fi
+status=`expr $status + $ret`
+
+n=`expr $n + 1`
+echo_i "query for .bad fails when qname-minimization is on and in strict mode ($n)"
+ret=0
+$CLEANQL
+$RNDCCMD 10.53.0.4 flush
+$DIG $DIGOPTS icky.icky.icky.ptang.zoop.boing.bad. @10.53.0.4 > dig.out.test$n
+grep "status: NXDOMAIN" dig.out.test$n > /dev/null || ret=1
+echo "NS boing.bad." | diff ans2/query.log - > /dev/null || ret=1
+if [ $ret != 0 ]; then echo_i "failed"; fi
+status=`expr $status + $ret`
+
+n=`expr $n + 1`
+echo_i "query for .bad succeds when qname-minimization is on and not in strict mode ($n)"
+ret=0
+$CLEANQL
+$RNDCCMD 10.53.0.5 flush
+$DIG $DIGOPTS icky.icky.icky.ptang.zoop.boing.bad. @10.53.0.5 > dig.out.test$n
+grep "status: NOERROR" dig.out.test$n > /dev/null || ret=1
+cat << __EOF | diff ans2/query.log - > /dev/null || ret=1
+NS boing.bad.
+NS zoop.boing.bad.
+NS ptang.zoop.boing.bad.
+NS icky.ptang.zoop.boing.bad.
+NS icky.icky.ptang.zoop.boing.bad.
+A icky.icky.icky.ptang.zoop.boing.bad.
+__EOF
+if [ $ret != 0 ]; then echo_i "failed"; fi
+status=`expr $status + $ret`
+
+n=`expr $n + 1`
+echo_i "query for .slow is properly minimized when qname-minimization is on ($n)"
+ret=0
+$CLEANQL
+$RNDCCMD 10.53.0.4 flush
+$DIG $DIGOPTS icky.icky.icky.ptang.zoop.boing.slow. @10.53.0.4 > dig.out.test$n
+grep "status: NOERROR" dig.out.test$n > /dev/null || ret=1
+cat << __EOF | diff ans2/query.log - > /dev/null || ret=1
+NS boing.slow.
+NS zoop.boing.slow.
+NS ptang.zoop.boing.slow.
+NS icky.ptang.zoop.boing.slow.
+NS icky.icky.ptang.zoop.boing.slow.
+A icky.icky.icky.ptang.zoop.boing.slow.
+__EOF
+if [ $ret != 0 ]; then echo_i "failed"; fi
+status=`expr $status + $ret`
+
+n=`expr $n + 1`
+echo_i "query for .ip6.arpa succeds and skips on boundaries when qname-minimization is on ($n)"
+ret=0
+$CLEANQL
+$RNDCCMD 10.53.0.4 flush
+$DIG $DIGOPTS -x 2001:4f8::1 @10.53.0.4 > dig.out.test$n
+grep "status: NOERROR" dig.out.test$n > /dev/null || ret=1
+cat << __EOF | diff ans2/query.log - > /dev/null || ret=1
+NS 1.0.0.2.ip6.arpa.
+NS 8.f.4.0.1.0.0.2.ip6.arpa.
+NS 0.0.0.0.8.f.4.0.1.0.0.2.ip6.arpa.
+NS 0.0.0.0.0.0.8.f.4.0.1.0.0.2.ip6.arpa.
+NS 0.0.0.0.0.0.0.0.8.f.4.0.1.0.0.2.ip6.arpa.
+PTR 1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.8.f.4.0.1.0.0.2.ip6.arpa.
+__EOF
+if [ $ret != 0 ]; then echo_i "failed"; fi
+status=`expr $status + $ret`
+
+echo_i "exit status: $status"
+[ $status -eq 0 ] || exit 1
old mode 100644 (file)
new mode 100755 (executable)
index e2bcd1eb9e0e7f5ce937c7c5efc6470a6d63a2ff..51e97d563e43ecb5707dc074ce2f4e9c8d8e91f5 100644 (file)
@@ -264,6 +264,8 @@ options {
         preferred-glue <string>;
         prefetch <integer> [ <integer> ];
         provide-ixfr <boolean>;
+       qname-minimization <boolean>;
+       qname-minimization-strict <boolean>;
         query-source ( ( [ address ] ( <ipv4_address> | * ) [ port (
             <integer> | * ) ] ) | ( [ [ address ] ( <ipv4_address> | * ) ]
             port ( <integer> | * ) ) ) [ dscp <integer> ];
index 17418424ec2e4efbfe840b6adbe4c3def3092dcf..fbd90a6689befa32be4bf0241694856d32e85a63 100644 (file)
@@ -105,6 +105,13 @@ typedef enum {
 /* RESERVED ECS                                0x2000 */
 /* RESERVED TCPCLIENT                  0x4000 */
 #define DNS_FETCHOPT_NOCACHED          0x8000       /*%< Force cache update. */
+#define DNS_FETCHOPT_QMINIMIZE         0x00010000   /*%< Use qname minimization. */
+#define DNS_FETCHOPT_QMIN_STRICT       0x00020000   /*%< Do not work around servers that
+                                                         return errors on non-empty
+                                                         terminals. */
+#define DNS_FETCHOPT_QMIN_SKIP_ON_IP6A 0x00040000   /*%< Skip some labels when
+                                                         doing qname minimization on
+                                                         ip6.arpa. */
 
 /* Reserved in use by adb.c            0x00400000 */
 #define        DNS_FETCHOPT_EDNSVERSIONSET     0x00800000
@@ -267,7 +274,7 @@ dns_resolver_createfetch(dns_resolver_t *res, const dns_name_t *name,
                         dns_rdatatype_t type,
                         const dns_name_t *domain, dns_rdataset_t *nameservers,
                         dns_forwarders_t *forwarders,
-                        const isc_sockaddr_t *client, isc_uint16_t id,
+                        const isc_sockaddr_t *client, dns_messageid_t id,
                         unsigned int options, unsigned int depth,
                         isc_counter_t *qc, isc_task_t *task,
                         isc_taskaction_t action, void *arg,
index 3fd4959506f1aaf4af12c88eeb3d7e72fcb66214..3ec8aba42fc46864678ec48dba4f0ba3002c03c4 100644 (file)
@@ -115,6 +115,8 @@ struct dns_view {
        dns_order_t *                   order;
        dns_fwdtable_t *                fwdtable;
        isc_boolean_t                   recursion;
+       isc_boolean_t                   qminimization;
+       isc_boolean_t                   qmin_strict;
        isc_boolean_t                   auth_nxdomain;
        isc_boolean_t                   use_glue_cache;
        isc_boolean_t                   minimal_any;
index 840f99ac97334fa4626921728057a0abea74861b..1898021672863e7ec5306e43a2ff57f77482e7d3 100644 (file)
@@ -258,8 +258,8 @@ struct fetchctx {
        /*% Not locked. */
        unsigned int                    magic;
        dns_resolver_t *                res;
-       dns_name_t                      name;
-       dns_rdatatype_t                 type;
+       dns_name_t                      fullname;
+       dns_rdatatype_t                 fulltype;
        unsigned int                    options;
        unsigned int                    bucketnum;
        unsigned int                    dbucketnum;
@@ -275,6 +275,7 @@ struct fetchctx {
        isc_event_t                     control_event;
        ISC_LINK(struct fetchctx)       link;
        ISC_LIST(dns_fetchevent_t)      events;
+
        /*% Locked by task event serialization. */
        dns_name_t                      domain;
        dns_rdataset_t                  nameservers;
@@ -304,6 +305,11 @@ struct fetchctx {
        isc_boolean_t                   ns_ttl_ok;
        isc_uint32_t                    ns_ttl;
        isc_counter_t *                 qc;
+       isc_boolean_t                   minimized;
+       unsigned int                    qmin_labels;
+       isc_boolean_t                   ip6arpaskip;
+       dns_name_t                      name;
+       dns_rdatatype_t                 type;
 
        /*%
         * The number of events we're waiting for.
@@ -554,6 +560,11 @@ void dns_resolver_setfuzzing() {
 }
 #endif
 
+static unsigned char ip6_arpa_data[]  = "\003IP6\004ARPA";
+static unsigned char ip6_arpa_offsets[] = { 0, 4, 9 };
+static const dns_name_t ip6_arpa =
+       DNS_NAME_INITABSOLUTE(ip6_arpa_data, ip6_arpa_offsets);
+
 static void destroy(dns_resolver_t *res);
 static void empty_bucket(dns_resolver_t *res);
 static isc_result_t resquery_send(resquery_t *query);
@@ -561,6 +572,7 @@ static void resquery_response(isc_task_t *task, isc_event_t *event);
 static void resquery_connected(isc_task_t *task, isc_event_t *event);
 static void fctx_try(fetchctx_t *fctx, isc_boolean_t retrying,
                     isc_boolean_t badcache);
+void fctx_minimize_qname(fetchctx_t *fctx);
 static void fctx_destroy(fetchctx_t *fctx);
 static isc_boolean_t fctx_unlink(fetchctx_t *fctx);
 static isc_result_t ncache_adderesult(dns_message_t *message,
@@ -749,6 +761,12 @@ rctx_referral(respctx_t *rctx);
 static isc_result_t
 rctx_answer_none(respctx_t *rctx);
 
+static void
+rctx_follow_referral(respctx_t *rctx);
+
+static isc_result_t
+rctx_answer_minimized(respctx_t *rctx);
+
 static void
 rctx_nextserver(respctx_t *rctx, dns_adbaddrinfo_t *addrinfo,
                isc_result_t result);
@@ -4136,6 +4154,7 @@ fctx_destroy(fetchctx_t *fctx) {
                dns_name_free(&fctx->domain, fctx->mctx);
        if (dns_rdataset_isassociated(&fctx->nameservers))
                dns_rdataset_disassociate(&fctx->nameservers);
+       dns_name_free(&fctx->fullname, fctx->mctx);
        dns_name_free(&fctx->name, fctx->mctx);
        dns_db_detach(&fctx->cache);
        dns_adb_detach(&fctx->adb);
@@ -4514,9 +4533,14 @@ fctx_create(dns_resolver_t *res, const dns_name_t *name, dns_rdatatype_t type,
        result = dns_name_dup(name, mctx, &fctx->name);
        if (result != ISC_R_SUCCESS)
                goto cleanup_info;
+       dns_name_init(&fctx->fullname, NULL);
+       result = dns_name_dup(name, mctx, &fctx->fullname);
+       if (result != ISC_R_SUCCESS)
+               goto cleanup_name;
        dns_name_init(&fctx->domain, NULL);
        dns_rdataset_init(&fctx->nameservers);
 
+       fctx->fulltype = type;
        fctx->type = type;
        fctx->options = options;
        /*
@@ -4532,6 +4556,9 @@ fctx_create(dns_resolver_t *res, const dns_name_t *name, dns_rdatatype_t type,
        fctx->want_shutdown = ISC_FALSE;
        fctx->cloned = ISC_FALSE;
        fctx->depth = depth;
+       fctx->minimized = isc_boolean_false;
+       fctx->ip6arpaskip = isc_boolean_false;
+       fctx->qmin_labels = 1;
        ISC_LIST_INIT(fctx->queries);
        ISC_LIST_INIT(fctx->finds);
        ISC_LIST_INIT(fctx->altfinds);
@@ -4635,12 +4662,12 @@ fctx_create(dns_resolver_t *res, const dns_name_t *name, dns_rdatatype_t type,
                         */
                        result = dns_name_dup(fname, mctx, &fctx->domain);
                        if (result != ISC_R_SUCCESS)
-                               goto cleanup_name;
+                               goto cleanup_fullname;
                }
        } else {
                result = dns_name_dup(domain, mctx, &fctx->domain);
                if (result != ISC_R_SUCCESS)
-                       goto cleanup_name;
+                       goto cleanup_fullname;
                dns_rdataset_clone(nameservers, &fctx->nameservers);
                fctx->ns_ttl = fctx->nameservers.ttl;
                fctx->ns_ttl_ok = ISC_TRUE;
@@ -4726,6 +4753,15 @@ fctx_create(dns_resolver_t *res, const dns_name_t *name, dns_rdatatype_t type,
        ISC_LINK_INIT(fctx, link);
        fctx->magic = FCTX_MAGIC;
 
+       /*
+        * If qname minimization is enabled we need to trim
+        * the name in fctx to proper length.
+        */
+       if (options & DNS_FETCHOPT_QMINIMIZE) {
+               fctx->ip6arpaskip = (options & DNS_FETCHOPT_QMIN_SKIP_ON_IP6A) && dns_name_issubdomain(&fctx->name, &ip6_arpa);
+               fctx_minimize_qname(fctx);
+       }
+
        ISC_LIST_APPEND(res->buckets[bucketnum].fctxs, fctx, link);
 
        LOCK(&res->nlock);
@@ -4754,6 +4790,9 @@ fctx_create(dns_resolver_t *res, const dns_name_t *name, dns_rdatatype_t type,
        if (dns_rdataset_isassociated(&fctx->nameservers))
                dns_rdataset_disassociate(&fctx->nameservers);
 
+ cleanup_fullname:
+       dns_name_free(&fctx->fullname, mctx);
+
  cleanup_name:
        dns_name_free(&fctx->name, mctx);
 
@@ -5636,6 +5675,8 @@ cache_name(fetchctx_t *fctx, dns_name_t *name, dns_adbaddrinfo_t *addrinfo,
        unsigned int valoptions = 0;
        isc_boolean_t checknta = ISC_TRUE;
 
+       FCTXTRACE("cache_name");
+
        /*
         * The appropriate bucket lock must be held.
         */
@@ -6459,12 +6500,10 @@ check_related(void *arg, const dns_name_t *addname, dns_rdatatype_t type) {
 #ifndef CHECK_FOR_GLUE_IN_ANSWER
 #define CHECK_FOR_GLUE_IN_ANSWER 0
 #endif
-#if CHECK_FOR_GLUE_IN_ANSWER
 static isc_result_t
 check_answer(void *arg, const dns_name_t *addname, dns_rdatatype_t type) {
        return (check_section(arg, addname, type, DNS_SECTION_ANSWER));
 }
-#endif
 
 static isc_boolean_t
 is_answeraddress_allowed(dns_view_t *view, dns_name_t *name,
@@ -7752,7 +7791,12 @@ rctx_answer(respctx_t *rctx) {
        fetchctx_t *fctx = rctx->fctx;
        resquery_t *query = rctx->query;
 
-       if ((fctx->rmessage->flags & DNS_MESSAGEFLAG_AA) != 0 ||
+       if (fctx->minimized) {
+                       result = rctx_answer_minimized(rctx);
+                       if (result != ISC_R_SUCCESS) {
+                               FCTXTRACE3("rctx_answer_minimized", result);
+                       }
+       } else if ((fctx->rmessage->flags & DNS_MESSAGEFLAG_AA) != 0 ||
            ISFORWARDER(query->addrinfo))
        {
                result = rctx_answer_positive(rctx);
@@ -8475,6 +8519,16 @@ rctx_answer_none(respctx_t *rctx) {
                return (rctx->result);
        }
 
+       /*
+        * If we're doing qname minimization this is an empty non-terminal, add
+        * the next label to query and restart it.
+        */
+       if (fctx->minimized &&
+           (fctx->rmessage->rcode == dns_rcode_noerror ||
+            !(fctx->options & DNS_FETCHOPT_QMIN_STRICT))) {
+               return rctx_answer_minimized(rctx);
+       }
+
        /*
         * Since we're not doing a referral, we don't want to cache any
         * NS RRs we may have found.
@@ -8842,6 +8896,10 @@ rctx_referral(respctx_t *rctx) {
                                                  check_answer, fctx);
        }
 #endif
+       if (fctx->minimized) {
+               (void)dns_rdataset_additionaldata(rctx->ns_rdataset,
+                                                 check_answer, fctx);
+       }
        fctx->attributes &= ~FCTX_ATTR_GLUING;
 
        /*
@@ -8887,6 +8945,39 @@ rctx_referral(respctx_t *rctx) {
        log_ns_ttl(fctx, "DELEGATION");
        rctx->result = DNS_R_DELEGATION;
 
+       rctx_follow_referral(rctx);
+       if (rctx->fctx->minimized) {
+               /*
+                * Reset the value
+                */
+               fctx->qmin_labels = 1;
+               fctx_minimize_qname(rctx->fctx);
+       }
+       return (ISC_R_COMPLETE);
+}
+
+/*
+ * rctx_answer_minimized():
+ * Handles a positive response to a qname-minimized query.
+ */
+static isc_result_t
+rctx_answer_minimized(respctx_t *rctx) {
+       fetchctx_t *fctx = rctx->fctx;
+
+       FCTXTRACE("rctx_answer_minimized");
+       if (dns_rdataset_isassociated(&fctx->nameservers)) {
+               dns_rdataset_disassociate(&fctx->nameservers);
+       }
+       rctx->result = DNS_R_DELEGATION;
+       rctx_follow_referral(rctx);
+       fctx->qmin_labels++;
+       fctx_minimize_qname(rctx->fctx);
+
+       return (ISC_R_SUCCESS);
+}
+
+static void
+rctx_follow_referral(respctx_t *rctx) {
        /*
         * Reinitialize 'rctx' to prepare for following the delegation:
         * set the get_nameservers and next_server flags appropriately and
@@ -8903,8 +8994,6 @@ rctx_referral(respctx_t *rctx) {
        rctx->fctx->neterr = 0;
        rctx->fctx->badresp = 0;
        rctx->fctx->adberr = 0;
-
-       return (ISC_R_COMPLETE);
 }
 
 /*
@@ -10138,9 +10227,9 @@ fctx_match(fetchctx_t *fctx, const dns_name_t *name, dns_rdatatype_t type,
            ISC_LIST_EMPTY(fctx->events))
                return (ISC_FALSE);
 
-       if (fctx->type != type || fctx->options != options)
+       if (fctx->fulltype != type || fctx->options != options)
                return (ISC_FALSE);
-       return (dns_name_equal(&fctx->name, name));
+       return (dns_name_equal(&fctx->fullname, name));
 }
 
 static inline void
@@ -10164,6 +10253,57 @@ log_fetch(const dns_name_t *name, dns_rdatatype_t type) {
                      "fetch: %s/%s", namebuf, typebuf);
 }
 
+void
+fctx_minimize_qname(fetchctx_t *fctx) {
+       /* XXXWPK TODO we should update info to show that this query is minimized */
+       unsigned int dlabels, nlabels;
+
+       dlabels = dns_name_countlabels(&fctx->domain);
+       nlabels = dns_name_countlabels(&fctx->fullname);
+       dns_name_free(&fctx->name, fctx->mctx);
+       dns_name_init(&fctx->name, NULL);
+       if (fctx->ip6arpaskip) {
+               /*
+                * For ip6.arpa we want to skip some of the labels, with boundaries
+                * at /16, /32, /48, /56, /64 and /128
+                * in 'label count' terms that's equal to
+                *    7    11   15   17   19      35
+                * We fix fctx->qmin_labels to point to the nearest boundary
+                */
+               if (dlabels + fctx->qmin_labels < 7) {
+                       fctx->qmin_labels = 7 - dlabels;
+               } else if (dlabels + fctx->qmin_labels < 11) {
+                       fctx->qmin_labels = 11 - dlabels;
+               } else if (dlabels + fctx->qmin_labels < 15) {
+                       fctx->qmin_labels = 15 - dlabels;
+               } else if (dlabels + fctx->qmin_labels < 17) {
+                       fctx->qmin_labels = 17 - dlabels;
+               } else if (dlabels + fctx->qmin_labels < 19) {
+                       fctx->qmin_labels = 19 - dlabels;
+               } else if (dlabels + fctx->qmin_labels > 19) {
+                       fctx->qmin_labels = 35 - dlabels;
+               }
+       }
+       if (dlabels + fctx->qmin_labels < nlabels) {
+               /*
+                * We want to query for [qmin_labels from fctx->fullname] + fctx->domain
+                */
+               dns_fixedname_t fname;
+               dns_fixedname_init(&fname);
+               dns_name_split(&fctx->fullname,
+                              dlabels + fctx->qmin_labels,
+                              NULL, dns_fixedname_name(&fname));
+               dns_name_dup(dns_fixedname_name(&fname), fctx->mctx, &fctx->name);
+               fctx->type = dns_rdatatype_ns;
+               fctx->minimized = isc_boolean_true;
+       } else {
+               /* Minimization is done, we'll ask for whole qname */
+               fctx->type = fctx->fulltype;
+               dns_name_dup(&fctx->fullname, fctx->mctx, &fctx->name);
+               fctx->minimized = isc_boolean_false;
+       }
+}
+
 isc_result_t
 dns_resolver_createfetch(dns_resolver_t *res, const dns_name_t *name,
                         dns_rdatatype_t type,
index 8fbdd7bf85a41d6aab71aa56732aba34be86d593..5dafdb5a1e24e9b9d3862ee958884f92008386f7 100644 (file)
@@ -182,6 +182,8 @@ dns_view_create(isc_mem_t *mctx, dns_rdataclass_t rdclass,
         * Initialize configuration data with default values.
         */
        view->recursion = ISC_TRUE;
+       view->qminimization = ISC_FALSE;
+       view->qmin_strict = ISC_FALSE;
        view->auth_nxdomain = ISC_FALSE; /* Was true in BIND 8 */
        view->enablednssec = ISC_TRUE;
        view->enablevalidation = ISC_TRUE;
index d69051ca1e04fca514c7df1d2ad4edd374eddfaa..2ac7146794d632fb5fbaeb303667100f4ddad3be 100644 (file)
@@ -1937,6 +1937,8 @@ view_clauses[] = {
        { "preferred-glue", &cfg_type_astring, 0 },
        { "prefetch", &cfg_type_prefetch, 0 },
        { "provide-ixfr", &cfg_type_boolean, 0 },
+       { "qname-minimization", &cfg_type_boolean, 0 },
+       { "qname-minimization-strict", &cfg_type_boolean, 0 },
        /*
         * Note that the query-source option syntax is different
         * from the other -source options.
index 27705c433c41e086d6cf6be19a203ef2e18f8f5e..037bc0653df96930accc5cdf60800bfca3e14925 100644 (file)
@@ -11106,6 +11106,15 @@ ns_query_start(ns_client_t *client) {
        } else if (!client->view->enablevalidation)
                client->query.fetchoptions |= DNS_FETCHOPT_NOVALIDATE;
 
+       if (client->view->qminimization) {
+               client->query.fetchoptions |= DNS_FETCHOPT_QMINIMIZE |
+                               DNS_FETCHOPT_QMIN_SKIP_ON_IP6A;
+               if (client->view->qmin_strict) {
+                       client->query.fetchoptions |=
+                               DNS_FETCHOPT_QMIN_STRICT;
+               }
+       }
+
        /*
         * Allow glue NS records to be added to the authority section
         * if the answer is secure.
index a5ca23f99a2fecab55d5a8d31f5387d3a7494eb2..2e1f6ba4d51c543fa7c6bb9763718851b1ed2c34 100644 (file)
 ./bin/tests/system/pkcs11ssl/setup.sh          SH      2014,2016,2018
 ./bin/tests/system/pkcs11ssl/tests.sh          SH      2014,2016,2018
 ./bin/tests/system/pkcs11ssl/usepkcs11         X       2014,2018
+./bin/tests/system/qname-minimization/ans2/ans.py      PYTHON  2018
+./bin/tests/system/qname-minimization/clean.sh SH      2018
+./bin/tests/system/qname-minimization/ns1/named.conf.in        CONF-C  2018
+./bin/tests/system/qname-minimization/ns1/root.db      ZONE    2018
+./bin/tests/system/qname-minimization/ns3/named.conf.in        CONF-C  2018
+./bin/tests/system/qname-minimization/ns4/named.conf.in        CONF-C  2018
+./bin/tests/system/qname-minimization/ns5/named.conf.in        CONF-C  2018
+./bin/tests/system/qname-minimization/setup.sh SH      2018
+./bin/tests/system/qname-minimization/tests.sh SH      2018
 ./bin/tests/system/reclimit/README             TXT.BRIEF       2014,2016,2017,2018
 ./bin/tests/system/reclimit/ans2/ans.pl                PERL    2014,2015,2016,2017,2018
 ./bin/tests/system/reclimit/ans7/ans.pl                PERL    2014,2016,2018