From: Witold Kręcicki Date: Wed, 9 May 2018 10:02:11 +0000 (+0200) Subject: QNAME minimization X-Git-Tag: v9.13.2~42^2~14 X-Git-Url: http://git.ipfire.org/gitweb/index.cgi?a=commitdiff_plain;h=0698158eb004369e0afeeebda42e704a6ebed44b;p=thirdparty%2Fbind9.git QNAME minimization --- diff --git a/bin/named/config.c b/bin/named/config.c index eb78c2cddce..7dd9d9e61ab 100644 --- a/bin/named/config.c +++ b/bin/named/config.c @@ -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\ diff --git a/bin/named/server.c b/bin/named/server.c index 1f827a8700c..c5dc11cd420 100644 --- a/bin/named/server.c +++ b/bin/named/server.c @@ -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 index 00000000000..036158e5f69 --- /dev/null +++ b/bin/tests/system/qname-minimization/ans2/ans.py @@ -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 index 00000000000..eaff4210bbe --- /dev/null +++ b/bin/tests/system/qname-minimization/clean.sh @@ -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 index 00000000000..7188e3c3729 --- /dev/null +++ b/bin/tests/system/qname-minimization/ns1/named.conf.in @@ -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 index 00000000000..83f157f8f04 --- /dev/null +++ b/bin/tests/system/qname-minimization/ns1/root.db @@ -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 index 00000000000..2a6dce9c13c --- /dev/null +++ b/bin/tests/system/qname-minimization/ns3/named.conf.in @@ -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 index 00000000000..403fc0fd157 --- /dev/null +++ b/bin/tests/system/qname-minimization/ns4/named.conf.in @@ -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 index 00000000000..f570b888359 --- /dev/null +++ b/bin/tests/system/qname-minimization/ns5/named.conf.in @@ -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 index 00000000000..0294ed74879 --- /dev/null +++ b/bin/tests/system/qname-minimization/setup.sh @@ -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 index 00000000000..84ab0a48346 --- /dev/null +++ b/bin/tests/system/qname-minimization/tests.sh @@ -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 diff --git a/bin/tests/system/testsock.pl b/bin/tests/system/testsock.pl old mode 100644 new mode 100755 diff --git a/doc/misc/options b/doc/misc/options index e2bcd1eb9e0..51e97d563e4 100644 --- a/doc/misc/options +++ b/doc/misc/options @@ -264,6 +264,8 @@ options { preferred-glue ; prefetch [ ]; provide-ixfr ; + qname-minimization ; + qname-minimization-strict ; query-source ( ( [ address ] ( | * ) [ port ( | * ) ] ) | ( [ [ address ] ( | * ) ] port ( | * ) ) ) [ dscp ]; diff --git a/lib/dns/include/dns/resolver.h b/lib/dns/include/dns/resolver.h index 17418424ec2..fbd90a6689b 100644 --- a/lib/dns/include/dns/resolver.h +++ b/lib/dns/include/dns/resolver.h @@ -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, diff --git a/lib/dns/include/dns/view.h b/lib/dns/include/dns/view.h index 3fd4959506f..3ec8aba42fc 100644 --- a/lib/dns/include/dns/view.h +++ b/lib/dns/include/dns/view.h @@ -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; diff --git a/lib/dns/resolver.c b/lib/dns/resolver.c index 840f99ac973..18980216728 100644 --- a/lib/dns/resolver.c +++ b/lib/dns/resolver.c @@ -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, diff --git a/lib/dns/view.c b/lib/dns/view.c index 8fbdd7bf85a..5dafdb5a1e2 100644 --- a/lib/dns/view.c +++ b/lib/dns/view.c @@ -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; diff --git a/lib/isccfg/namedconf.c b/lib/isccfg/namedconf.c index d69051ca1e0..2ac7146794d 100644 --- a/lib/isccfg/namedconf.c +++ b/lib/isccfg/namedconf.c @@ -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. diff --git a/lib/ns/query.c b/lib/ns/query.c index 27705c433c4..037bc0653df 100644 --- a/lib/ns/query.c +++ b/lib/ns/query.c @@ -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. diff --git a/util/copyrights b/util/copyrights index a5ca23f99a2..2e1f6ba4d51 100644 --- a/util/copyrights +++ b/util/copyrights @@ -1729,6 +1729,15 @@ ./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