]> git.ipfire.org Git - thirdparty/bind9.git/commitdiff
- qname minimization:
authorWitold Kręcicki <wpk@isc.org>
Fri, 11 May 2018 10:27:56 +0000 (12:27 +0200)
committerWitold Kręcicki <wpk@isc.org>
Tue, 12 Jun 2018 07:18:46 +0000 (09:18 +0200)
 - make qname-minimization option tristate {strict,relaxed,disabled}
 - go straight for the record if we hit NXDOMAIN in relaxed mode
 - go straight for the record after 3 labels without new delegation or 7 labels total

- use start of fetch (and not time of response) as 'now' time for querying cache for
  zonecut when following delegation.

16 files changed:
bin/named/config.c
bin/named/server.c
bin/tests/system/qname-minimization/ans2/ans.py
bin/tests/system/qname-minimization/ans3/ans.py [new file with mode: 0755]
bin/tests/system/qname-minimization/ans4/ans.py [new file with mode: 0755]
bin/tests/system/qname-minimization/clean.sh
bin/tests/system/qname-minimization/ns5/named.conf.in
bin/tests/system/qname-minimization/ns6/named.conf.in [moved from bin/tests/system/qname-minimization/ns3/named.conf.in with 73% similarity]
bin/tests/system/qname-minimization/ns7/named.conf.in [moved from bin/tests/system/qname-minimization/ns4/named.conf.in with 73% similarity]
bin/tests/system/qname-minimization/setup.sh
bin/tests/system/qname-minimization/tests.sh
lib/dns/adb.c
lib/dns/include/dns/resolver.h
lib/dns/resolver.c
lib/isccfg/namedconf.c
util/copyrights

index 7dd9d9e61ab04e7c914ce011d80406faa8adb695..3e4ceaea53d081ae675879f630acb64996f86850 100644 (file)
@@ -185,8 +185,7 @@ options {\n\
        provide-ixfr true;\n\
        query-source address *;\n\
        query-source-v6 address *;\n\
-       qname-minimization no;\n\
-       qname-minimization-strict no;\n\
+       qname-minimization relaxed;\n\
        recursion true;\n\
        request-expire true;\n\
        request-ixfr true;\n\
index c5dc11cd4202b53f9c95171284daed7e0e179a8c..38f9df1a0f7b86d8ea194d29bf39afceb433ea66 100644 (file)
@@ -4642,12 +4642,18 @@ configure_view(dns_view_t *view, dns_viewlist_t *viewlist,
        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);
+       const char * qminmode = cfg_obj_asstring(obj);
+       INSIST(qminmode != NULL);
+       if (!strcmp(qminmode, "strict")) {
+               view->qminimization = ISC_TRUE;
+               view->qmin_strict = ISC_TRUE;
+       } else if (!strcmp(qminmode, "relaxed")) {
+               view->qminimization = ISC_TRUE;
+               view->qmin_strict = ISC_FALSE;
+       } else {
+               view->qminimization = ISC_FALSE;
+               view->qmin_strict = ISC_FALSE;
+       }
 
        obj = NULL;
        result = named_config_get(maps, "auth-nxdomain", &obj);
index 036158e5f69a1b99e070c466ca223e90f4a43446..1f44062d807507b26723c8eb0aec33cda5e151d1 100755 (executable)
@@ -35,7 +35,9 @@ def logquery(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
+# zoop.boing.good. NS ns3.good.
+# ns3.good. IN A 10.53.0.3
+# too.many.labels.a.b.c.d.e.f.g.h.i.j.k.l.m.n.o.p.q.r.s.t.u.v.w.x.y.z.good. A 192.0.2.2
 # 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
@@ -111,20 +113,33 @@ def create_response(msg):
         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"))
+    if lqname.endswith("zoop.boing."):
+        r.authority.append(dns.rrset.from_text("zoop.boing." + suffix, 1, IN, NS, "ns3." + suffix))
+    elif lqname == "many.labels.a.b.c.d.e.f.g.h.i.j.k.l.m.n.o.p.q.r.s.t.u.v.w.x.y.z." and rrtype == A:
+        r.answer.append(dns.rrset.from_text(lqname + suffix, 1, IN, A, "192.0.2.2"))
     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"))
+    elif lqname == "ns3." and rrtype == A:
+        r.answer.append(dns.rrset.from_text("ns3."+suffix, 1, IN, A, "10.53.0.3"))
+    elif lqname == "ns3." and rrtype == AAAA:
+        r.answer.append(dns.rrset.from_text("ns3."+suffix, 1, IN, AAAA, "fd92:7065:b8e:ffff::3"))
+    elif lqname == "a.bit.longer.ns.name." and rrtype == A:
+        r.answer.append(dns.rrset.from_text("a.bit.longer.ns.name."+suffix, 1, IN, A, "10.53.0.4"))
+    elif lqname == "a.bit.longer.ns.name." and rrtype == AAAA:
+        r.answer.append(dns.rrset.from_text("a.bit.longer.ns.name."+suffix, 1, IN, AAAA, "fd92:7065:b8e:ffff::4"))
     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):
+        if bad or not \
+            ("icky.icky.icky.ptang.zoop.boing.".endswith(lqname) or \
+             "many.labels.a.b.c.d.e.f.g.h.i.j.k.l.m.n.o.p.q.r.s.t.u.v.w.x.y.z.".endswith(lqname) or \
+             "a.bit.longer.ns.name.".endswith(lqname)):
             r.set_rcode(NXDOMAIN)
     if slow:
-        time.sleep(0.4)
+        time.sleep(0.2)
     return r
 
 
diff --git a/bin/tests/system/qname-minimization/ans3/ans.py b/bin/tests/system/qname-minimization/ans3/ans.py
new file mode 100755 (executable)
index 0000000..d767403
--- /dev/null
@@ -0,0 +1,175 @@
+############################################################################
+# 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:
+# zoop.boing.good. NS ns3.good. 
+# icky.ptang.zoop.boing.good. NS a.bit.longer.ns.name.good.
+# 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
+############################################################################
+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("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 == "zoop.boing." and rrtype == NS:
+        r.answer.append(dns.rrset.from_text(lqname + suffix, 1, IN, NS, "ns3."+suffix))
+    elif lqname.endswith("icky.ptang.zoop.boing."):
+        r.authority.append(dns.rrset.from_text("icky.ptang.zoop.boing." + suffix, 1, IN, NS, "a.bit.longer.ns.name." + suffix))
+    elif "icky.ptang.zoop.boing.".endswith(lqname):
+        r.authority.append(dns.rrset.from_text("zoop.boing." + suffix, 1, IN, SOA, "ns3." + suffix + " hostmaster.arpa. 2018050100 1 1 1 1"))
+        if bad:
+            r.set_rcode(NXDOMAIN)
+    elif "zoop.boing.".endswith(lqname):
+        r.authority.append(dns.rrset.from_text("zoop.boing." + suffix, 1, IN, SOA, "ns3." + suffix + " hostmaster.arpa. 2018050100 1 1 1 1"))
+        r.set_rcode(NXDOMAIN)
+    else:
+        r.set_rcode(REFUSED)
+
+    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.3"
+ip6 = "fd92:7065:b8e:ffff::3"
+
+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/ans4/ans.py b/bin/tests/system/qname-minimization/ans4/ans.py
new file mode 100755 (executable)
index 0000000..aab281a
--- /dev/null
@@ -0,0 +1,175 @@
+############################################################################
+# 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:
+# icky.ptang.zoop.boing.good. NS a.bit.longer.ns.name.
+# icky.icky.icky.ptang.zoop.boing.good. A 192.0.2.1
+# more.icky.icky.icky.ptang.zoop.boing.good. A 192.0.2.2
+# 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
+############################################################################
+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("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 == "more.icky.icky.icky.ptang.zoop.boing." and rrtype == A:
+        r.answer.append(dns.rrset.from_text(lqname + suffix, 1, IN, A, "192.0.2.2"))
+    elif lqname == "icky.ptang.zoop.boing." and rrtype == NS:
+        r.answer.append(dns.rrset.from_text(lqname + suffix, 1, IN, NS, "a.bit.longer.ns.name."+suffix))
+    elif lqname.endswith("icky.ptang.zoop.boing."):
+        r.authority.append(dns.rrset.from_text("icky.ptang.zoop.boing." + suffix, 1, IN, SOA, "ns2." + suffix + " hostmaster.arpa. 2018050100 1 1 1 1"))
+        if bad or not "more.icky.icky.icky.ptang.zoop.boing.".endswith(lqname):
+            r.set_rcode(NXDOMAIN)
+    else:
+        r.set_rcode(REFUSED)
+
+    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.4"
+ip6 = "fd92:7065:b8e:ffff::4"
+
+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
index eaff4210bbeb250f036aaf46d397bbfb5dab864a..bd5af224a54292919fe7ba8b2bccc2cfa8ac061e 100644 (file)
@@ -14,4 +14,4 @@ rm -f */named.memstats
 rm -f */named.run
 rm -f dig.out.*
 rm -f ns*/named.lock
-rm -f ans2/query.log
+rm -f ans*/query.log
index f570b888359bc174b062f6e0a4ec2e016d89a441..4d045a58ad8be5376f3a6f3a1539ea37649bad90 100644 (file)
@@ -20,8 +20,7 @@ options {
        listen-on { 10.53.0.5; };
        listen-on-v6 { none; };
        recursion yes;
-       qname-minimization yes;
-       qname-minimization-strict no;
+       qname-minimization disabled;
        querylog yes;
        resolver-query-timeout 30;
 };
similarity index 73%
rename from bin/tests/system/qname-minimization/ns3/named.conf.in
rename to bin/tests/system/qname-minimization/ns6/named.conf.in
index 2a6dce9c13c37c6440c99fa6e761d088bd435539..661549b40709a2b6a70c6d2104b31ee84ae8bda1 100644 (file)
@@ -9,19 +9,18 @@
  * information regarding copyright ownership.
  */
 
-// NS3
+// NS6
 
 options {
-       query-source address 10.53.0.3;
-       notify-source 10.53.0.3;
-       transfer-source 10.53.0.3;
+       query-source address 10.53.0.6;
+       notify-source 10.53.0.6;
+       transfer-source 10.53.0.6;
        port @PORT@;
        pid-file "named.pid";
-       listen-on { 10.53.0.3; };
+       listen-on { 10.53.0.6; };
        listen-on-v6 { none; };
        recursion yes;
-       qname-minimization no;
-       qname-minimization-strict no;
+       qname-minimization strict;
        querylog yes;
        resolver-query-timeout 30;
 };
@@ -32,7 +31,7 @@ key rndc_key {
 };
 
 controls {
-       inet 10.53.0.3 port @CONTROLPORT@ allow { any; } keys { rndc_key; };
+       inet 10.53.0.6 port @CONTROLPORT@ allow { any; } keys { rndc_key; };
 };
 
 zone "." {
similarity index 73%
rename from bin/tests/system/qname-minimization/ns4/named.conf.in
rename to bin/tests/system/qname-minimization/ns7/named.conf.in
index 403fc0fd157b59aa1637285502e2e46cf037c930..48def89b2587bacfd39c06f59e972bfc8f990e0a 100644 (file)
@@ -9,19 +9,18 @@
  * information regarding copyright ownership.
  */
 
-// NS4
+// NS7
 
 options {
-       query-source address 10.53.0.4;
-       notify-source 10.53.0.4;
-       transfer-source 10.53.0.4;
+       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.4; };
+       listen-on { 10.53.0.7; };
        listen-on-v6 { none; };
        recursion yes;
-       qname-minimization yes;
-       qname-minimization-strict yes;
+       qname-minimization relaxed;
        querylog yes;
        resolver-query-timeout 30;
 };
@@ -32,7 +31,7 @@ key rndc_key {
 };
 
 controls {
-       inet 10.53.0.4 port @CONTROLPORT@ allow { any; } keys { rndc_key; };
+       inet 10.53.0.7 port @CONTROLPORT@ allow { any; } keys { rndc_key; };
 };
 
 zone "." {
index 0294ed74879d64afe4ca1adbe52aec0fff8138e8..e9c67c7cb937b0fbb97d6d1df026ef04bad912f7 100644 (file)
@@ -15,6 +15,6 @@ SYSTEMTESTTOP=..
 $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
+copy_setports ns6/named.conf.in ns6/named.conf
+copy_setports ns7/named.conf.in ns7/named.conf
index 84ab0a48346a48febfcc0b4eccb8dac4a10ebcdf..bd06cdd123f61b89e2a5b09f616985a337138718 100755 (executable)
@@ -14,7 +14,7 @@ SYSTEMTESTTOP=..
 
 DIGOPTS="-p ${PORT}"
 RNDCCMD="$RNDC -c $SYSTEMTESTTOP/common/rndc.conf -p ${CONTROLPORT} -s"
-CLEANQL="rm -f ans2/query.log"
+CLEANQL="rm -f ans*/query.log"
 status=0
 n=0
 
@@ -22,10 +22,19 @@ 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
+$RNDCCMD 10.53.0.5 flush
+$DIG $DIGOPTS icky.icky.icky.ptang.zoop.boing.good. @10.53.0.5 > 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
+grep "icky.icky.icky.ptang.zoop.boing.good. 1  IN A    192.0.2.1" dig.out.test$n > /dev/null || ret=1
+cat << __EOF | diff ans2/query.log - > /dev/null || ret=1
+A icky.icky.icky.ptang.zoop.boing.good.
+A ns3.good.
+AAAA ns3.good.
+A a.bit.longer.ns.name.good.
+AAAA a.bit.longer.ns.name.good.
+__EOF
+echo "A icky.icky.icky.ptang.zoop.boing.good." | diff ans3/query.log - > /dev/null || ret=1
+echo "A icky.icky.icky.ptang.zoop.boing.good." | diff ans4/query.log - > /dev/null || ret=1
 if [ $ret != 0 ]; then echo_i "failed"; fi
 status=`expr $status + $ret`
 
@@ -33,10 +42,19 @@ 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
+$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
-echo "A icky.icky.icky.ptang.zoop.boing.bad." | diff ans2/query.log - > /dev/null || ret=1
+grep "icky.icky.icky.ptang.zoop.boing.bad. 1 IN A      192.0.2.1" dig.out.test$n > /dev/null || ret=1
+cat << __EOF | diff ans2/query.log - > /dev/null || ret=1
+A icky.icky.icky.ptang.zoop.boing.bad.
+A ns3.bad.
+AAAA ns3.bad.
+A a.bit.longer.ns.name.bad.
+AAAA a.bit.longer.ns.name.bad.
+__EOF
+echo "A icky.icky.icky.ptang.zoop.boing.bad." | diff ans3/query.log - > /dev/null || ret=1
+echo "A icky.icky.icky.ptang.zoop.boing.bad." | diff ans4/query.log - > /dev/null || ret=1
 if [ $ret != 0 ]; then echo_i "failed"; fi
 status=`expr $status + $ret`
 
@@ -44,10 +62,20 @@ 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
+$RNDCCMD 10.53.0.5 flush
+$DIG $DIGOPTS icky.icky.icky.ptang.zoop.boing.slow. @10.53.0.5 > dig.out.test$n
+sleep 5
 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
+grep "icky.icky.icky.ptang.zoop.boing.slow. 1  IN A    192.0.2.1" dig.out.test$n > /dev/null || ret=1
+cat << __EOF | diff ans2/query.log - > /dev/null || ret=1
+A icky.icky.icky.ptang.zoop.boing.slow.
+A ns3.slow.
+AAAA ns3.slow.
+A a.bit.longer.ns.name.slow.
+AAAA a.bit.longer.ns.name.slow.
+__EOF
+echo "A icky.icky.icky.ptang.zoop.boing.slow." | diff ans3/query.log - > /dev/null || ret=1
+echo "A icky.icky.icky.ptang.zoop.boing.slow." | diff ans4/query.log - > /dev/null || ret=1
 if [ $ret != 0 ]; then echo_i "failed"; fi
 status=`expr $status + $ret`
 
@@ -55,14 +83,35 @@ 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
+$RNDCCMD 10.53.0.6 flush
+$DIG $DIGOPTS icky.icky.icky.ptang.zoop.boing.good. @10.53.0.6 > dig.out.test$n
 grep "status: NOERROR" dig.out.test$n > /dev/null || ret=1
+grep "icky.icky.icky.ptang.zoop.boing.good. 1  IN A    192.0.2.1" dig.out.test$n > /dev/null || ret=1
+# Duplicated NS queries are there because we're not creating
+# a separate fetch when doing qname minimization - so two
+# queries running for the same name but different RRTYPE 
+# (A and AAAA in this case) will create separate queries
+# for NSes on the way. Those will be cached though, so it
+# should not be a problem
 cat << __EOF | diff ans2/query.log - > /dev/null || ret=1
 NS boing.good.
 NS zoop.boing.good.
+A ns3.good.
+AAAA ns3.good.
+NS name.good.
+NS name.good.
+NS ns.name.good.
+NS ns.name.good.
+NS longer.ns.name.good.
+NS longer.ns.name.good.
+A a.bit.longer.ns.name.good.
+AAAA a.bit.longer.ns.name.good.
+__EOF
+cat << __EOF | diff ans3/query.log - > /dev/null || ret=1
 NS ptang.zoop.boing.good.
 NS icky.ptang.zoop.boing.good.
+__EOF
+cat << __EOF | diff ans4/query.log - > /dev/null || ret=1
 NS icky.icky.ptang.zoop.boing.good.
 A icky.icky.icky.ptang.zoop.boing.good.
 __EOF
@@ -70,31 +119,36 @@ 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)"
+echo_i "query for .bad fails when qname-minimization is 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
+$RNDCCMD 10.53.0.6 flush
+$DIG $DIGOPTS icky.icky.icky.ptang.zoop.boing.bad. @10.53.0.6 > 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)"
+echo_i "query for .bad succeds when qname-minimization is in relaxed 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
+$RNDCCMD 10.53.0.7 flush
+$DIG $DIGOPTS icky.icky.icky.ptang.zoop.boing.bad. @10.53.0.7 > dig.out.test$n
 grep "status: NOERROR" dig.out.test$n > /dev/null || ret=1
+grep "icky.icky.icky.ptang.zoop.boing.bad. 1 IN A      192.0.2.1" 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.
+A ns3.bad.
+AAAA ns3.bad.
+NS name.bad.
+NS name.bad.
+A a.bit.longer.ns.name.bad.
+AAAA a.bit.longer.ns.name.bad.
 __EOF
+echo "A icky.icky.icky.ptang.zoop.boing.bad." | diff ans3/query.log - > /dev/null || ret=1
+echo "A icky.icky.icky.ptang.zoop.boing.bad." | diff ans4/query.log - > /dev/null || ret=1
 if [ $ret != 0 ]; then echo_i "failed"; fi
 status=`expr $status + $ret`
 
@@ -102,14 +156,30 @@ 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
+$RNDCCMD 10.53.0.6 flush
+$DIG $DIGOPTS icky.icky.icky.ptang.zoop.boing.slow. @10.53.0.6 > dig.out.test$n
+sleep 5
 grep "status: NOERROR" dig.out.test$n > /dev/null || ret=1
+grep "icky.icky.icky.ptang.zoop.boing.slow. 1  IN A    192.0.2.1" 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.
+A ns3.slow.
+AAAA ns3.slow.
+NS name.slow.
+NS name.slow.
+NS ns.name.slow.
+NS ns.name.slow.
+NS longer.ns.name.slow.
+NS longer.ns.name.slow.
+A a.bit.longer.ns.name.slow.
+AAAA a.bit.longer.ns.name.slow.
+__EOF
+cat << __EOF | diff ans3/query.log - > /dev/null || ret=1
 NS ptang.zoop.boing.slow.
 NS icky.ptang.zoop.boing.slow.
+__EOF
+cat << __EOF | diff ans4/query.log - > /dev/null || ret=1
 NS icky.icky.ptang.zoop.boing.slow.
 A icky.icky.icky.ptang.zoop.boing.slow.
 __EOF
@@ -117,12 +187,13 @@ 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)"
+echo_i "query for .ip6.arpa succeds and skips on proper 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
+$RNDCCMD 10.53.0.6 flush
+$DIG $DIGOPTS -x 2001:4f8::1 @10.53.0.6 > dig.out.test$n
 grep "status: NOERROR" dig.out.test$n > /dev/null || ret=1
+grep "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." 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.
@@ -134,5 +205,57 @@ __EOF
 if [ $ret != 0 ]; then echo_i "failed"; fi
 status=`expr $status + $ret`
 
+n=`expr $n + 1`
+echo_i "query for multiple label name skips after 3rd no-delegation response ($n)"
+ret=0
+$CLEANQL
+$RNDCCMD 10.53.0.6 flush
+$DIG $DIGOPTS many.labels.a.b.c.d.e.f.g.h.i.j.k.l.m.n.o.p.q.r.s.t.u.v.w.x.y.z.good. @10.53.0.6 > dig.out.test$n
+grep "status: NOERROR" dig.out.test$n > /dev/null || ret=1
+grep "many.labels.a.b.c.d.e.f.g.h.i.j.k.l.m.n.o.p.q.r.s.t.u.v.w.x.y.z.good. 1  IN A 192.0.2.2" dig.out.test$n > /dev/null || ret=1
+# We skipped after third no-delegation.
+cat << __EOF | diff ans2/query.log - > /dev/null || ret=1
+NS z.good.
+NS y.z.good.
+NS x.y.z.good.
+A many.labels.a.b.c.d.e.f.g.h.i.j.k.l.m.n.o.p.q.r.s.t.u.v.w.x.y.z.good.
+__EOF
+if [ $ret != 0 ]; then echo_i "failed"; fi
+status=`expr $status + $ret`
+
+n=`expr $n + 1`
+echo_i "query for multiple label name skips after 7th label ($n)"
+ret=0
+$CLEANQL
+$RNDCCMD 10.53.0.6 flush
+$DIG $DIGOPTS more.icky.icky.icky.ptang.zoop.boing.good. @10.53.0.6 > dig.out.test$n
+grep "status: NOERROR" dig.out.test$n > /dev/null || ret=1
+grep "more.icky.icky.icky.ptang.zoop.boing.good. 1 IN  A 192.0.2.2" 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.
+A ns3.good.
+AAAA ns3.good.
+NS name.good.
+NS name.good.
+NS ns.name.good.
+NS ns.name.good.
+NS longer.ns.name.good.
+NS longer.ns.name.good.
+A a.bit.longer.ns.name.good.
+AAAA a.bit.longer.ns.name.good.
+__EOF
+cat << __EOF | diff ans3/query.log - > /dev/null || ret=1
+NS ptang.zoop.boing.good.
+NS icky.ptang.zoop.boing.good.
+__EOF
+# There's no NS icky.icky.ptang.zoop.boing.good. query - we skipped it.
+cat << __EOF | diff ans4/query.log - > /dev/null || ret=1
+NS icky.icky.ptang.zoop.boing.good.
+A more.icky.icky.icky.ptang.zoop.boing.good.
+__EOF
+if [ $ret != 0 ]; then echo_i "failed"; fi
+status=`expr $status + $ret`
+
 echo_i "exit status: $status"
 [ $status -eq 0 ] || exit 1
index 044c9a90929a8d22a7fd84d3d95fa2beb14e39e5..c163053db5fccd3bbd7c937cdb134f88794e1592 100644 (file)
@@ -4029,6 +4029,14 @@ fetch_name(dns_adbname_t *adbname, isc_boolean_t start_at_zone,
                nameservers = &rdataset;
                options |= DNS_FETCHOPT_UNSHARED;
        }
+       
+       if (adb->view->qminimization) {
+               options |= DNS_FETCHOPT_QMINIMIZE;
+               options |= DNS_FETCHOPT_QMIN_SKIP_ON_IP6A;
+               if (adb->view->qmin_strict) {
+                       options |= DNS_FETCHOPT_QMIN_STRICT;
+               }
+       }
 
        fetch = new_adbfetch(adb);
        if (fetch == NULL) {
index fbd90a6689befa32be4bf0241694856d32e85a63..4be589fceb36f815e09cff6386876a5870102f06 100644 (file)
@@ -141,6 +141,10 @@ typedef enum {
 #define DNS_RESOLVER_CHECKNAMES                0x01
 #define DNS_RESOLVER_CHECKNAMESFAIL    0x02
 
+#define DNS_QMIN_MAXLABELS             7
+#define DNS_QMIN_MAX_NO_DELEGATION     3
+#define DNS_MAX_LABELS                 127
+
 isc_result_t
 dns_resolver_create(dns_view_t *view,
                    isc_taskmgr_t *taskmgr,
index 1898021672863e7ec5306e43a2ff57f77482e7d3..312d8efa16e8d3701c8323c20d08baedac395af0 100644 (file)
@@ -265,6 +265,7 @@ struct fetchctx {
        unsigned int                    dbucketnum;
        char *                          info;
        isc_mem_t *                     mctx;
+       isc_stdtime_t                   now;
 
        /*% Locked by appropriate bucket lock. */
        fetchstate                      state;
@@ -689,7 +690,7 @@ typedef struct respctx {
        isc_boolean_t ns_in_answer;     /* NS may be in the answer section */
        isc_boolean_t negative;         /* is this a negative response? */
 
-       isc_stdtime_t now;              /* time info */
+       isc_stdtime_t now;              /* time info */
        isc_time_t tnow;
        isc_time_t *finish;
 
@@ -2542,8 +2543,8 @@ resquery_send(resquery_t *query) {
        fctx->timeout = ISC_FALSE;
 
        /*
-        * Use EDNS0, unless the caller doesn't want it, or we know that
-        * the remote server doesn't like it.
+        * Use EDNS0, unless the caller doesn't want it, or we know that the
+        * remote server doesn't like it.
         */
        if ((query->options & DNS_FETCHOPT_NOEDNS0) == 0) {
                if ((query->addrinfo->flags & DNS_FETCHOPT_NOEDNS0) == 0) {
@@ -4437,7 +4438,7 @@ fctx_join(fetchctx_t *fctx, isc_task_t *task, const isc_sockaddr_t *client,
                return (ISC_R_NOMEMORY);
        }
        event->result = DNS_R_SERVFAIL;
-       event->qtype = fctx->type;
+       event->qtype = fctx->fulltype;
        event->db = NULL;
        event->node = NULL;
        event->rdataset = rdataset;
@@ -4559,6 +4560,7 @@ fctx_create(dns_resolver_t *res, const dns_name_t *name, dns_rdatatype_t type,
        fctx->minimized = isc_boolean_false;
        fctx->ip6arpaskip = isc_boolean_false;
        fctx->qmin_labels = 1;
+       isc_stdtime_get(&fctx->now);
        ISC_LIST_INIT(fctx->queries);
        ISC_LIST_INIT(fctx->finds);
        ISC_LIST_INIT(fctx->altfinds);
@@ -7423,7 +7425,6 @@ rctx_respinit(isc_task_t *task, dns_dispatchevent_t *devent,
        TIME_NOW(&rctx->tnow);
        rctx->finish = &rctx->tnow;
        isc_stdtime_get(&rctx->now);
-
 }
 
 /*
@@ -8497,7 +8498,20 @@ rctx_answer_none(respctx_t *rctx) {
                } else {
                        log_formerr(fctx, "invalid response");
                }
-
+               /*
+                * If we're minimizing in relaxed mode, retry with full name,
+                * just to be safe. The error will be logged.
+                */
+               isc_log_write(dns_lctx, DNS_LOGCATEGORY_RESOLVER, DNS_LOGMODULE_RESOLVER, ISC_LOG_INFO, "XXX %d %d", fctx->minimized, fctx->options & DNS_FETCHOPT_QMIN_STRICT);
+               if (fctx->minimized &&
+                   !(fctx->options & DNS_FETCHOPT_QMIN_STRICT)) {
+                       isc_log_write(dns_lctx, DNS_LOGCATEGORY_RESOLVER,
+                                     DNS_LOGMODULE_RESOLVER, ISC_LOG_INFO,
+                                     "disabling qname minimization for '%s'"
+                                     " due to formerr", fctx->info);
+                       fctx->qmin_labels = DNS_MAX_LABELS+1;
+                       return rctx_answer_minimized(rctx);
+               }
                return (DNS_R_FORMERR);
        }
 
@@ -8523,12 +8537,17 @@ rctx_answer_none(respctx_t *rctx) {
         * 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))) {
+       if (fctx->minimized && fctx->rmessage->rcode == dns_rcode_noerror) {
+               return rctx_answer_minimized(rctx);
+       }
+       /*
+        * Workaround for broken servers in relaxed mode - if we hit an
+        * NXDOMAIN we go straight to the full query.
+        */
+       if (fctx->minimized && !(fctx->options & DNS_FETCHOPT_QMIN_STRICT)) {
+               fctx->qmin_labels = DNS_MAX_LABELS+1;
                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.
@@ -9079,7 +9098,7 @@ rctx_nextserver(respctx_t *rctx, dns_adbaddrinfo_t *addrinfo,
                }
                result = dns_view_findzonecut(fctx->res->view,
                                              name, fname,
-                                             rctx->now, findoptions,
+                                             fctx->now, findoptions,
                                              ISC_TRUE, ISC_TRUE,
                                              &fctx->nameservers,
                                              NULL);
@@ -10283,6 +10302,10 @@ fctx_minimize_qname(fetchctx_t *fctx) {
                } else if (dlabels + fctx->qmin_labels > 19) {
                        fctx->qmin_labels = 35 - dlabels;
                }
+       } else if (dlabels + fctx->qmin_labels > DNS_QMIN_MAXLABELS) {
+                       fctx->qmin_labels = DNS_MAX_LABELS + 1;
+       } else if (fctx->qmin_labels > DNS_QMIN_MAX_NO_DELEGATION) {
+                       fctx->qmin_labels = DNS_MAX_LABELS + 1;
        }
        if (dlabels + fctx->qmin_labels < nlabels) {
                /*
index 2ac7146794d632fb5fbaeb303667100f4ddad3be..8c0b68e52ad17de91343cb0ed02583d7f5edc015 100644 (file)
@@ -130,6 +130,7 @@ static cfg_type_t cfg_type_optional_uint32;
 static cfg_type_t cfg_type_options;
 static cfg_type_t cfg_type_portiplist;
 static cfg_type_t cfg_type_printtime;
+static cfg_type_t cfg_type_qminmethod;
 static cfg_type_t cfg_type_querysource4;
 static cfg_type_t cfg_type_querysource6;
 static cfg_type_t cfg_type_querysource;
@@ -1937,8 +1938,7 @@ 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 },
+       { "qname-minimization", &cfg_type_qminmethod, 0 },
        /*
         * Note that the query-source option syntax is different
         * from the other -source options.
@@ -2951,6 +2951,15 @@ static cfg_type_t cfg_type_optional_keyref = {
        doc_optional_keyvalue, &cfg_rep_string, &key_kw
 };
 
+static const char *qminmethod_enums[] = {
+       "strict", "relaxed", "disabled", NULL
+};
+
+static cfg_type_t cfg_type_qminmethod = {
+       "qminmethod", cfg_parse_enum, cfg_print_ustring, cfg_doc_enum,
+       &cfg_rep_string, qminmethod_enums
+};
+
 #ifdef HAVE_GEOIP
 /*
  * "geoip" ACL element:
index 2e1f6ba4d51c543fa7c6bb9763718851b1ed2c34..ddeb8eafa22e383500dc78b34ee0794da15927c1 100644 (file)
 ./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/ans3/ans.py      PYTHON  2018
+./bin/tests/system/qname-minimization/ans4/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/ns6/named.conf.in        CONF-C  2018
+./bin/tests/system/qname-minimization/ns7/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