diff --git a/bin/tests/system/qmin/tests.sh b/bin/tests/system/qmin/tests.sh index 26d3cb8946..12d74518fc 100755 --- a/bin/tests/system/qmin/tests.sh +++ b/bin/tests/system/qmin/tests.sh @@ -154,20 +154,20 @@ grep "icky.icky.icky.ptang.zoop.boing.good. 1 IN A 192.0.2.1" dig.out.test$n > / sleep 1 sort ans2/query.log > ans2/query.log.sorted cat << __EOF | diff ans2/query.log.sorted - > /dev/null || ret=1 -ADDR _.boing.good. -ADDR _.zoop.boing.good. ADDR a.bit.longer.ns.name.good. ADDR a.bit.longer.ns.name.good. ADDR ns2.good. ADDR ns3.good. ADDR ns3.good. +NS boing.good. +NS zoop.boing.good. __EOF cat << __EOF | diff ans3/query.log - > /dev/null || ret=1 -ADDR _.ptang.zoop.boing.good. -ADDR _.icky.ptang.zoop.boing.good. +NS ptang.zoop.boing.good. +NS icky.ptang.zoop.boing.good. __EOF cat << __EOF | diff ans4/query.log - > /dev/null || ret=1 -ADDR _.icky.icky.ptang.zoop.boing.good. +NS icky.icky.ptang.zoop.boing.good. ADDR icky.icky.icky.ptang.zoop.boing.good. __EOF for ans in ans2 ans3 ans4; do mv -f $ans/query.log query-$ans-$n.log 2>/dev/null || true; done @@ -203,20 +203,18 @@ grep "icky.icky.icky.ptang.zoop.boing.bad. 1 IN A 192.0.2.1" dig.out.test$n > /d sleep 1 sort ans2/query.log > ans2/query.log.sorted cat << __EOF | diff ans2/query.log.sorted - > /dev/null || ret=1 -ADDR _.boing.bad. -ADDR _.zoop.boing.bad. ADDR a.bit.longer.ns.name.bad. ADDR a.bit.longer.ns.name.bad. +ADDR icky.icky.icky.ptang.zoop.boing.bad. ADDR ns2.bad. ADDR ns3.bad. ADDR ns3.bad. +NS boing.bad. __EOF cat << __EOF | diff ans3/query.log - > /dev/null || ret=1 -ADDR _.ptang.zoop.boing.bad. -ADDR _.icky.ptang.zoop.boing.bad. +ADDR icky.icky.icky.ptang.zoop.boing.bad. __EOF cat << __EOF | diff ans4/query.log - > /dev/null || ret=1 -ADDR _.icky.icky.ptang.zoop.boing.bad. ADDR icky.icky.icky.ptang.zoop.boing.bad. __EOF for ans in ans2 ans3 ans4; do mv -f $ans/query.log query-$ans-$n.log 2>/dev/null || true; done @@ -251,16 +249,15 @@ $DIG $DIGOPTS icky.icky.icky.ptang.zoop.boing.ugly. @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.ugly. 1 IN A 192.0.2.1" dig.out.test$n > /dev/null || ret=1 sleep 1 - sort ans2/query.log > ans2/query.log.sorted cat << __EOF | diff ans2/query.log.sorted - > /dev/null || ret=1 -ADDR _.boing.ugly. ADDR a.bit.longer.ns.name.ugly. ADDR a.bit.longer.ns.name.ugly. ADDR icky.icky.icky.ptang.zoop.boing.ugly. ADDR ns2.ugly. ADDR ns3.ugly. ADDR ns3.ugly. +NS boing.ugly. __EOF echo "ADDR icky.icky.icky.ptang.zoop.boing.ugly." | diff ans3/query.log - > /dev/null || ret=1 echo "ADDR icky.icky.icky.ptang.zoop.boing.ugly." | diff ans4/query.log - > /dev/null || ret=1 @@ -270,7 +267,7 @@ status=$((status+ret)) $RNDCCMD 10.53.0.7 flush n=$((n+1)) -echo_i "information that minimization was unsuccessful for .ugly is logged ($n)" +echo_i "information that minimization was unsuccessful for .ugly is logged in relaxed mode ($n)" ret=0 wait_for_log 5 "success resolving 'icky.icky.icky.ptang.zoop.boing.ugly/A' after disabling qname minimization" ns7/named.run > /dev/null || ret=1 if [ $ret != 0 ]; then echo_i "failed"; fi @@ -458,9 +455,9 @@ grep "a\.b\.stale\..*1.*IN.*TXT.*hooray" dig.out.test$n > /dev/null || ret=1 sleep 1 sort ans2/query.log > ans2/query.log.sorted cat << __EOF | diff ans2/query.log.sorted - > /dev/null || ret=1 -ADDR _.b.stale. ADDR ns.b.stale. ADDR ns2.stale. +NS b.stale. __EOF test -f ans3/query.log && ret=1 sort ans4/query.log > ans4/query.log.sorted @@ -523,7 +520,7 @@ grep "a\.b\.stale\..*1.*IN.*TXT.*hooray" dig.out.test$n > /dev/null || ret=1 sleep 1 sort ans2/query.log > ans2/query.log.sorted cat << __EOF | diff ans2/query.log.sorted - > /dev/null || ret=1 -ADDR _.b.stale. +NS b.stale. __EOF test -f ans3/query.log && ret=1 sort ans4/query.log > ans4/query.log.sorted diff --git a/doc/arm/reference.rst b/doc/arm/reference.rst index 3de2effb72..bc294a676e 100644 --- a/doc/arm/reference.rst +++ b/doc/arm/reference.rst @@ -1399,21 +1399,22 @@ default is used. :tags: query :short: Controls QNAME minimization behavior in the BIND 9 resolver. - When this is set to ``strict``, BIND follows the QNAME - minimization algorithm to the letter, as specified in :rfc:`7816`. + When this is set to ``strict``, BIND follows the QNAME minimization + algorithm to the letter, as specified in :rfc:`7816`. Setting this option to ``relaxed`` causes BIND to fall back to - normal (non-minimized) query mode when it receives either NXDOMAIN or - other unexpected responses (e.g., SERVFAIL, improper zone cut, - REFUSED) to a minimized query. A resolver can use a leading - underscore, like ``_.example.com``, in an attempt to improve - interoperability. (See :rfc:`7816` section 3.) + normal (non-minimized) query mode when it receives either NXDOMAIN + or other unexpected responses (e.g., SERVFAIL, improper zone + cut, REFUSED) to a minimized query. + + In ``relaxed`` mode ``named`` makes NS queries for ```` as it + walks down the tree. ``disabled`` disables QNAME minimization completely. ``off`` is a synonym for ``disabled``. - The current default is ``relaxed``, but it - may be changed to ``strict`` in a future release. + The current default is ``relaxed``, but it may be changed to + ``strict`` in a future release. .. namedconf:statement:: tkey-gssapi-keytab :tags: security diff --git a/lib/dns/include/dns/resolver.h b/lib/dns/include/dns/resolver.h index bf4559f60f..8e55f30789 100644 --- a/lib/dns/include/dns/resolver.h +++ b/lib/dns/include/dns/resolver.h @@ -105,31 +105,21 @@ typedef enum { dns_quotatype_zone = 0, dns_quotatype_server } dns_quotatype_t; /* RESERVED ECS 0x00001000 */ /* RESERVED ECS 0x00002000 */ /* RESERVED TCPCLIENT 0x00004000 */ -#define DNS_FETCHOPT_NOCACHED 0x00008000 /*%< Force cache update. */ -#define DNS_FETCHOPT_QMINIMIZE \ - 0x00010000 /*%< Use qname \ - * minimization. */ -#define DNS_FETCHOPT_NOFOLLOW \ - 0x00020000 /*%< Don't follow \ - * delegations */ -#define DNS_FETCHOPT_QMIN_STRICT \ - 0x00040000 /*%< Do not work around \ - * servers that return \ - * errors on non-empty \ - * terminals. */ -#define DNS_FETCHOPT_QMIN_USE_A \ - 0x00080000 /*%< Use A type queries \ - * instead of NS when \ - * doing minimization */ -#define DNS_FETCHOPT_QMIN_SKIP_IP6A \ - 0x00100000 /*%< Skip some labels \ - * when doing qname \ - * minimization on \ +#define DNS_FETCHOPT_NOCACHED 0x00008000 /*%< Force cache update. */ +#define DNS_FETCHOPT_QMINIMIZE 0x00010000 /*%< Use qname minimization. */ +#define DNS_FETCHOPT_NOFOLLOW \ + 0x00020000 /*%< Don't retrieve the NS RRset from the child zone when a \ + * delegation is returned in response to a NS query. */ +#define DNS_FETCHOPT_QMIN_STRICT \ + 0x00040000 /*%< Do not work around servers that return errors on \ + * non-empty terminals. */ +#define DNS_FETCHOPT_QMIN_SKIP_IP6A \ + 0x00080000 /*%< Skip some labels when doing qname minimization on \ * ip6.arpa. */ -#define DNS_FETCHOPT_NOFORWARD \ - 0x00200000 /*%< Do not use forwarders \ - * if possible. */ +#define DNS_FETCHOPT_NOFORWARD \ + 0x00100000 /*%< Do not use forwarders if possible. */ +/* UNUSED 0x00200000 */ /* Reserved in use by adb.c 0x00400000 */ #define DNS_FETCHOPT_EDNSVERSIONSET 0x00800000 #define DNS_FETCHOPT_EDNSVERSIONMASK 0xff000000 diff --git a/lib/dns/resolver.c b/lib/dns/resolver.c index b5a78ab52b..cd8087f348 100644 --- a/lib/dns/resolver.c +++ b/lib/dns/resolver.c @@ -649,11 +649,6 @@ 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 unsigned char underscore_data[] = "\001_"; -static unsigned char underscore_offsets[] = { 0 }; -static const dns_name_t underscore_name = - DNS_NAME_INITNONABSOLUTE(underscore_data, underscore_offsets); - static void destroy(dns_resolver_t *res); static isc_result_t @@ -4225,11 +4220,14 @@ fctx_try(fetchctx_t *fctx, bool retrying, bool badcache) { } /* - * In "_ A" mode we're asking for _.domain - - * resolver by default will follow delegations - * then, we don't want that. + * Turn on NOFOLLOW in relaxed mode so that QNAME minimisation + * doesn't cause additional queries to resolve the target of the + * QNAME minimisation request when a referral is returned. This + * will also reduce the impact of mis-matched NS RRsets where + * the child's NS RRset is garbage. If a delegation is + * discovered DNS_R_DELEGATION will be returned to resume_qmin. */ - if ((options & DNS_FETCHOPT_QMIN_USE_A) != 0) { + if ((options & DNS_FETCHOPT_QMIN_STRICT) == 0) { options |= DNS_FETCHOPT_NOFOLLOW; } fctx_addref(fctx); @@ -4315,22 +4313,15 @@ resume_qmin(isc_task_t *task, isc_event_t *event) { } UNLOCK(&res->buckets[bucketnum].lock); - if (result == ISC_R_CANCELED) { + switch (result) { + case ISC_R_SHUTTINGDOWN: + case ISC_R_CANCELED: goto cleanup; - } - - /* - * If we're doing "_ A"-style minimization we can get - * NX answer to minimized query - we need to continue then. - * - * Otherwise - either disable minimization if we're - * in relaxed mode or fail if we're in strict mode. - */ - if ((NXDOMAIN_RESULT(result) && - (fctx->options & DNS_FETCHOPT_QMIN_USE_A) == 0) || - result == DNS_R_FORMERR || result == DNS_R_REMOTEFORMERR || - result == ISC_R_FAILURE) - { + case DNS_R_NXDOMAIN: + case DNS_R_NCACHENXDOMAIN: + case DNS_R_FORMERR: + case DNS_R_REMOTEFORMERR: + case ISC_R_FAILURE: if ((fctx->options & DNS_FETCHOPT_QMIN_STRICT) == 0) { fctx->qmin_labels = DNS_MAX_LABELS + 1; /* @@ -4342,6 +4333,14 @@ resume_qmin(isc_task_t *task, isc_event_t *event) { } else { goto cleanup; } + break; + default: + /* + * When DNS_FETCHOPT_NOFOLLOW is set and a delegation + * was discovered, DNS_R_DELEGATION is returned and is + * processed here. + */ + break; } if (dns_rdataset_isassociated(&fctx->nameservers)) { @@ -8017,7 +8016,8 @@ resquery_response(isc_result_t eresult, isc_region_t *region, void *arg) { case DNS_R_CHASEDSSERVERS: break; case DNS_R_DELEGATION: - /* With NOFOLLOW we want to pass the result code + /* + * With NOFOLLOW we want to pass the result code. */ if ((fctx->options & DNS_FETCHOPT_NOFOLLOW) == 0) { result = ISC_R_SUCCESS; @@ -10802,24 +10802,8 @@ fctx_minimize_qname(fetchctx_t *fctx) { dns_fixedname_t fname; dns_name_t *name = dns_fixedname_initname(&fname); dns_name_split(fctx->name, fctx->qmin_labels, NULL, name); - if ((fctx->options & DNS_FETCHOPT_QMIN_USE_A) != 0) { - isc_buffer_t dbuf; - dns_fixedname_t tmpname; - dns_name_t *tname = dns_fixedname_initname(&tmpname); - char ndata[DNS_NAME_MAXWIRE]; - - isc_buffer_init(&dbuf, ndata, DNS_NAME_MAXWIRE); - dns_fixedname_init(&tmpname); - result = dns_name_concatenate(&underscore_name, name, - tname, &dbuf); - if (result == ISC_R_SUCCESS) { - dns_name_copy(tname, fctx->qminname); - } - fctx->qmintype = dns_rdatatype_a; - } else { - dns_name_copy(name, fctx->qminname); - fctx->qmintype = dns_rdatatype_ns; - } + dns_name_copy(name, fctx->qminname); + fctx->qmintype = dns_rdatatype_ns; fctx->minimized = true; } else { /* Minimization is done, we'll ask for whole qname */ diff --git a/lib/ns/query.c b/lib/ns/query.c index 64e9278307..823edaafe8 100644 --- a/lib/ns/query.c +++ b/lib/ns/query.c @@ -12389,8 +12389,6 @@ ns_query_start(ns_client_t *client, isc_nmhandle_t *handle) { DNS_FETCHOPT_QMIN_SKIP_IP6A; if (client->view->qmin_strict) { client->query.fetchoptions |= DNS_FETCHOPT_QMIN_STRICT; - } else { - client->query.fetchoptions |= DNS_FETCHOPT_QMIN_USE_A; } }