7e32a4
From e92ac3b83209ddc46ca9a3facd7edf1f14052edf Mon Sep 17 00:00:00 2001
7e32a4
From: rpm-build <rpm-build>
7e32a4
Date: Wed, 8 Feb 2017 13:49:47 +0100
7e32a4
Subject: [PATCH] 4558.   [bug]       Synthesised CNAME before matching DNAME
7e32a4
 was still                     being cached when it should not have been.  [RT
7e32a4
 #44318]
7e32a4
7e32a4
Fixes and tests last case fixed by CVE-2016-9147
7e32a4
---
7e32a4
 bin/tests/system/dname/ans3/ans.pl |  95 +++++++++++++++++++++++
7e32a4
 bin/tests/system/dname/ns1/root.db |   5 +-
7e32a4
 bin/tests/system/dname/tests.sh    |  25 ++++++-
7e32a4
 lib/dns/resolver.c                 | 150 +++++++++++++++++++++++++------------
7e32a4
 4 files changed, 225 insertions(+), 50 deletions(-)
7e32a4
 create mode 100644 bin/tests/system/dname/ans3/ans.pl
7e32a4
7e32a4
diff --git a/bin/tests/system/dname/ans3/ans.pl b/bin/tests/system/dname/ans3/ans.pl
7e32a4
new file mode 100644
7e32a4
index 0000000..271fc7d
7e32a4
--- /dev/null
7e32a4
+++ b/bin/tests/system/dname/ans3/ans.pl
7e32a4
@@ -0,0 +1,95 @@
7e32a4
+#!/usr/bin/env perl
7e32a4
+#
7e32a4
+# Copyright (C) 2014-2016  Internet Systems Consortium, Inc. ("ISC")
7e32a4
+#
7e32a4
+# This Source Code Form is subject to the terms of the Mozilla Public
7e32a4
+# License, v. 2.0. If a copy of the MPL was not distributed with this
7e32a4
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
7e32a4
+
7e32a4
+use strict;
7e32a4
+use warnings;
7e32a4
+
7e32a4
+use IO::File;
7e32a4
+use Getopt::Long;
7e32a4
+use Net::DNS::Nameserver;
7e32a4
+
7e32a4
+my $pidf = new IO::File "ans.pid", "w" or die "cannot open pid file: $!";
7e32a4
+print $pidf "$$\n" or die "cannot write pid file: $!";
7e32a4
+$pidf->close or die "cannot close pid file: $!";
7e32a4
+sub rmpid { unlink "ans.pid"; exit 1; };
7e32a4
+
7e32a4
+$SIG{INT} = \&rmpid;
7e32a4
+$SIG{TERM} = \&rmpid;
7e32a4
+
7e32a4
+my $localaddr = "10.53.0.3";
7e32a4
+my $localport = 5300;
7e32a4
+my $verbose = 0;
7e32a4
+my $ttl = 60;
7e32a4
+my $zone = "example.broken";
7e32a4
+my $nsname = "ns3.$zone";
7e32a4
+my $synth = "synth-then-dname.$zone";
7e32a4
+my $synth2 = "synth2-then-dname.$zone";
7e32a4
+
7e32a4
+sub reply_handler {
7e32a4
+    my ($qname, $qclass, $qtype, $peerhost, $query, $conn) = @_;
7e32a4
+    my ($rcode, @ans, @auth, @add);
7e32a4
+
7e32a4
+    print ("request: $qname/$qtype\n");
7e32a4
+    STDOUT->flush();
7e32a4
+
7e32a4
+    if ($qname eq "example.broken") {
7e32a4
+        if ($qtype eq "SOA") {
7e32a4
+	    my $rr = new Net::DNS::RR("$qname $ttl $qclass SOA . . 0 0 0 0 0");
7e32a4
+	    push @ans, $rr;
7e32a4
+        } elsif ($qtype eq "NS") {
7e32a4
+	    my $rr = new Net::DNS::RR("$qname $ttl $qclass NS $nsname");
7e32a4
+	    push @ans, $rr;
7e32a4
+	    $rr = new Net::DNS::RR("$nsname $ttl $qclass A $localaddr");
7e32a4
+	    push @add, $rr;
7e32a4
+        }
7e32a4
+        $rcode = "NOERROR";
7e32a4
+    } elsif ($qname eq "cname-to-$synth2") {
7e32a4
+        my $rr = new Net::DNS::RR("$qname $ttl $qclass CNAME name.$synth2");
7e32a4
+	push @ans, $rr;
7e32a4
+        $rr = new Net::DNS::RR("name.$synth2 $ttl $qclass CNAME name");
7e32a4
+	push @ans, $rr;
7e32a4
+        $rr = new Net::DNS::RR("$synth2 $ttl $qclass DNAME .");
7e32a4
+	push @ans, $rr;
7e32a4
+	$rcode = "NOERROR";
7e32a4
+    } elsif ($qname eq "$synth" || $qname eq "$synth2") {
7e32a4
+	if ($qtype eq "DNAME") {
7e32a4
+	    my $rr = new Net::DNS::RR("$qname $ttl $qclass DNAME .");
7e32a4
+	    push @ans, $rr;
7e32a4
+	}
7e32a4
+	$rcode = "NOERROR";
7e32a4
+    } elsif ($qname eq "name.$synth") {
7e32a4
+	my $rr = new Net::DNS::RR("$qname $ttl $qclass CNAME name.");
7e32a4
+	push @ans, $rr;
7e32a4
+	$rr = new Net::DNS::RR("$synth $ttl $qclass DNAME .");
7e32a4
+	push @ans, $rr;
7e32a4
+	$rcode = "NOERROR";
7e32a4
+    } elsif ($qname eq "name.$synth2") {
7e32a4
+	my $rr = new Net::DNS::RR("$qname $ttl $qclass CNAME name.");
7e32a4
+	push @ans, $rr;
7e32a4
+	$rr = new Net::DNS::RR("$synth2 $ttl $qclass DNAME .");
7e32a4
+	push @ans, $rr;
7e32a4
+	$rcode = "NOERROR";
7e32a4
+    } else {
7e32a4
+	$rcode = "REFUSED";
7e32a4
+    }
7e32a4
+    return ($rcode, \@ans, \@auth, \@add, { aa => 1 });
7e32a4
+}
7e32a4
+
7e32a4
+GetOptions(
7e32a4
+    'port=i' => \$localport,
7e32a4
+    'verbose!' => \$verbose,
7e32a4
+);
7e32a4
+
7e32a4
+my $ns = Net::DNS::Nameserver->new(
7e32a4
+    LocalAddr => $localaddr,
7e32a4
+    LocalPort => $localport,
7e32a4
+    ReplyHandler => \&reply_handler,
7e32a4
+    Verbose => $verbose,
7e32a4
+);
7e32a4
+
7e32a4
+$ns->main_loop;
7e32a4
diff --git a/bin/tests/system/dname/ns1/root.db b/bin/tests/system/dname/ns1/root.db
7e32a4
index 7049e77..2e84ae0 100644
7e32a4
--- a/bin/tests/system/dname/ns1/root.db
7e32a4
+++ b/bin/tests/system/dname/ns1/root.db
7e32a4
@@ -12,8 +12,6 @@
7e32a4
 ; OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
7e32a4
 ; PERFORMANCE OF THIS SOFTWARE.
7e32a4
 
7e32a4
-; $Id: root.db,v 1.2 2011/03/18 21:14:19 fdupont Exp $
7e32a4
-
7e32a4
 $TTL 300
7e32a4
 . 			IN SOA	gson.nominum.com. a.root.servers.nil. (
7e32a4
 				2000042100   	; serial
7e32a4
@@ -27,3 +25,6 @@ a.root-servers.nil.	A	10.53.0.1
7e32a4
 
7e32a4
 example.		NS	ns2.example.
7e32a4
 ns2.example.		A	10.53.0.2
7e32a4
+
7e32a4
+example.broken.		NS	ns3.example.broken.
7e32a4
+ns3.example.broken.	A	10.53.0.3
7e32a4
diff --git a/bin/tests/system/dname/tests.sh b/bin/tests/system/dname/tests.sh
7e32a4
index 04bfcb2..6dc9e88 100644
7e32a4
--- a/bin/tests/system/dname/tests.sh
7e32a4
+++ b/bin/tests/system/dname/tests.sh
7e32a4
@@ -20,6 +20,7 @@ SYSTEMTESTTOP=..
7e32a4
 . $SYSTEMTESTTOP/conf.sh
7e32a4
 
7e32a4
 status=0
7e32a4
+n=0
7e32a4
 
7e32a4
 echo "I:checking short dname from authoritative"
7e32a4
 ret=0
7e32a4
@@ -81,6 +82,26 @@ grep '^a.target.example.' dig.out.ns4.cname > /dev/null || ret=1
7e32a4
 if [ $ret != 0 ]; then echo "I:failed"; fi
7e32a4
 status=`expr $status + $ret`
7e32a4
 
7e32a4
-echo "I:exit status: $status"
7e32a4
+n=`expr $n + 1`
7e32a4
+echo "I:checking dname is returned with synthesized cname before dname ($n)"
7e32a4
+ret=0
7e32a4
+$DIG @10.53.0.4 -p 5300 name.synth-then-dname.example.broken A > dig.out.test$n
7e32a4
+grep "status: NXDOMAIN" dig.out.test$n > /dev/null || ret=1
7e32a4
+grep '^name.synth-then-dname\.example\.broken\..*CNAME.*name.$' dig.out.test$n > /dev/null || ret=1
7e32a4
+grep '^synth-then-dname\.example\.broken\..*DNAME.*\.$' dig.out.test$n > /dev/null || ret=1
7e32a4
+if [ $ret != 0 ]; then echo "I:failed"; fi
7e32a4
+status=`expr $status + $ret`
7e32a4
 
7e32a4
-exit $status
7e32a4
+n=`expr $n + 1`
7e32a4
+echo "I:checking dname is returned with cname to synthesized cname before dname ($n)"
7e32a4
+ret=0
7e32a4
+$DIG @10.53.0.4 -p 5300 cname-to-synth2-then-dname.example.broken A > dig.out.test$n
7e32a4
+grep "status: NXDOMAIN" dig.out.test$n > /dev/null || ret=1
7e32a4
+grep '^cname-to-synth2-then-dname\.example\.broken\..*CNAME.*name\.synth2-then-dname\.example\.broken.$' dig.out.test$n > /dev/null || ret=1
7e32a4
+grep '^name\.synth2-then-dname\.example\.broken\..*CNAME.*name.$' dig.out.test$n > /dev/null || ret=1
7e32a4
+grep '^synth2-then-dname\.example\.broken\..*DNAME.*\.$' dig.out.test$n > /dev/null || ret=1
7e32a4
+if [ $ret != 0 ]; then echo "I:failed"; fi
7e32a4
+status=`expr $status + $ret`
7e32a4
+
7e32a4
+echo "I:exit status: $status"
7e32a4
+[ $status -eq 0 ] || exit 1
7e32a4
diff --git a/lib/dns/resolver.c b/lib/dns/resolver.c
7e32a4
index bfd4dcb..c3607fa 100644
7e32a4
--- a/lib/dns/resolver.c
7e32a4
+++ b/lib/dns/resolver.c
7e32a4
@@ -5406,9 +5406,13 @@ cname_target(dns_rdataset_t *rdataset, dns_name_t *tname) {
7e32a4
 	return (ISC_R_SUCCESS);
7e32a4
 }
7e32a4
 
7e32a4
+/*%
7e32a4
+ * Construct the synthesised CNAME from the existing QNAME and
7e32a4
+ * the DNAME RR and store it in 'target'.
7e32a4
+ */
7e32a4
 static inline isc_result_t
7e32a4
 dname_target(dns_rdataset_t *rdataset, dns_name_t *qname,
7e32a4
-	     unsigned int nlabels, dns_fixedname_t *fixeddname)
7e32a4
+	     unsigned int nlabels, dns_name_t *target)
7e32a4
 {
7e32a4
 	isc_result_t result;
7e32a4
 	dns_rdata_t rdata = DNS_RDATA_INIT;
7e32a4
@@ -5428,14 +5432,33 @@ dname_target(dns_rdataset_t *rdataset, dns_name_t *qname,
7e32a4
 
7e32a4
 	dns_fixedname_init(&prefix);
7e32a4
 	dns_name_split(qname, nlabels, dns_fixedname_name(&prefix), NULL);
7e32a4
-	dns_fixedname_init(fixeddname);
7e32a4
 	result = dns_name_concatenate(dns_fixedname_name(&prefix),
7e32a4
-				      &dname.dname,
7e32a4
-				      dns_fixedname_name(fixeddname), NULL);
7e32a4
+				      &dname.dname, target, NULL);
7e32a4
 	dns_rdata_freestruct(&dname);
7e32a4
 	return (result);
7e32a4
 }
7e32a4
 
7e32a4
+/*%
7e32a4
+ * Check if it was possible to construct 'qname' from 'lastcname'
7e32a4
+ * and 'rdataset'.
7e32a4
+ */
7e32a4
+static inline isc_result_t
7e32a4
+fromdname(dns_rdataset_t *rdataset, dns_name_t *lastcname,
7e32a4
+	  unsigned int nlabels, const dns_name_t *qname)
7e32a4
+{
7e32a4
+	dns_fixedname_t fixed;
7e32a4
+	isc_result_t result;
7e32a4
+	dns_name_t *target;
7e32a4
+
7e32a4
+	dns_fixedname_init(&fixed);
7e32a4
+	target = dns_fixedname_name(&fixed);
7e32a4
+	result = dname_target(rdataset, lastcname, nlabels, target);
7e32a4
+	if (result != ISC_R_SUCCESS || !dns_name_equal(qname, target))
7e32a4
+		return (ISC_R_NOTFOUND);
7e32a4
+
7e32a4
+	return (ISC_R_SUCCESS);
7e32a4
+}
7e32a4
+
7e32a4
 static isc_boolean_t
7e32a4
 is_answeraddress_allowed(dns_view_t *view, dns_name_t *name,
7e32a4
 			 dns_rdataset_t *rdataset)
7e32a4
@@ -6039,12 +6062,12 @@ answer_response(fetchctx_t *fctx) {
7e32a4
 	isc_result_t result;
7e32a4
 	dns_message_t *message;
7e32a4
 	dns_name_t *name, *dname = NULL, *qname, tname, *ns_name;
7e32a4
-	dns_name_t *cname = NULL;
7e32a4
+	dns_name_t *cname = NULL, *lastcname = NULL;
7e32a4
 	dns_rdataset_t *rdataset, *ns_rdataset;
7e32a4
-	isc_boolean_t done, external, chaining, aa, found, want_chaining;
7e32a4
+	isc_boolean_t done, external, aa, found, want_chaining;
7e32a4
 	isc_boolean_t have_answer, found_cname, found_dname, found_type;
7e32a4
 	isc_boolean_t wanted_chaining;
7e32a4
-	unsigned int aflag;
7e32a4
+	unsigned int aflag, chaining;
7e32a4
 	dns_rdatatype_t type;
7e32a4
 	dns_fixedname_t fdname, fqname;
7e32a4
 	dns_view_t *view;
7e32a4
@@ -6062,9 +6085,9 @@ answer_response(fetchctx_t *fctx) {
7e32a4
 	found_cname = ISC_FALSE;
7e32a4
 	found_dname = ISC_FALSE;
7e32a4
 	found_type = ISC_FALSE;
7e32a4
-	chaining = ISC_FALSE;
7e32a4
 	have_answer = ISC_FALSE;
7e32a4
 	want_chaining = ISC_FALSE;
7e32a4
+	chaining = 0;
7e32a4
 	POST(want_chaining);
7e32a4
 	if ((message->flags & DNS_MESSAGEFLAG_AA) != 0)
7e32a4
 		aa = ISC_TRUE;
7e32a4
@@ -6075,14 +6098,15 @@ answer_response(fetchctx_t *fctx) {
7e32a4
 	view = fctx->res->view;
7e32a4
 	result = dns_message_firstname(message, DNS_SECTION_ANSWER);
7e32a4
 	while (!done && result == ISC_R_SUCCESS) {
7e32a4
-		dns_namereln_t namereln;
7e32a4
-		int order;
7e32a4
-		unsigned int nlabels;
7e32a4
+		dns_namereln_t namereln, lastreln;
7e32a4
+		int order, lastorder;
7e32a4
+		unsigned int nlabels, lastnlabels;
7e32a4
 
7e32a4
 		name = NULL;
7e32a4
 		dns_message_currentname(message, DNS_SECTION_ANSWER, &name);
7e32a4
 		external = ISC_TF(!dns_name_issubdomain(name, &fctx->domain));
7e32a4
 		namereln = dns_name_fullcompare(qname, name, &order, &nlabels);
7e32a4
+
7e32a4
 		if (namereln == dns_namereln_equal) {
7e32a4
 			wanted_chaining = ISC_FALSE;
7e32a4
 			for (rdataset = ISC_LIST_HEAD(name->list);
7e32a4
@@ -6188,6 +6212,7 @@ answer_response(fetchctx_t *fctx) {
7e32a4
 							&fctx->domain)) {
7e32a4
 						return (DNS_R_SERVFAIL);
7e32a4
 					}
7e32a4
+					lastcname = name;
7e32a4
 				} else if (rdataset->type == dns_rdatatype_rrsig
7e32a4
 					   && rdataset->covers ==
7e32a4
 					   dns_rdatatype_cname
7e32a4
@@ -6211,7 +6236,7 @@ answer_response(fetchctx_t *fctx) {
7e32a4
 					rdataset->attributes |=
7e32a4
 						DNS_RDATASETATTR_CACHE;
7e32a4
 					rdataset->trust = dns_trust_answer;
7e32a4
-					if (!chaining) {
7e32a4
+					if (chaining == 0) {
7e32a4
 						/*
7e32a4
 						 * This data is "the" answer
7e32a4
 						 * to our question only if
7e32a4
@@ -6288,10 +6313,21 @@ answer_response(fetchctx_t *fctx) {
7e32a4
 			 * cause us to ignore the signatures of
7e32a4
 			 * CNAMEs.
7e32a4
 			 */
7e32a4
-			if (wanted_chaining)
7e32a4
-				chaining = ISC_TRUE;
7e32a4
+			if (wanted_chaining && chaining < 2U)
7e32a4
+				chaining++;
7e32a4
 		} else {
7e32a4
 			dns_rdataset_t *dnameset = NULL;
7e32a4
+			isc_boolean_t synthcname = ISC_FALSE;
7e32a4
+
7e32a4
+			if (lastcname != NULL) {
7e32a4
+				lastreln = dns_name_fullcompare(lastcname,
7e32a4
+								name,
7e32a4
+								&lastorder,
7e32a4
+								&lastnlabels);
7e32a4
+				if (lastreln == dns_namereln_subdomain &&
7e32a4
+				    lastnlabels == dns_name_countlabels(name))
7e32a4
+					synthcname = ISC_TRUE;
7e32a4
+			}
7e32a4
 
7e32a4
 			/*
7e32a4
 			 * Look for a DNAME (or its SIG).  Anything else is
7e32a4
@@ -6320,7 +6356,7 @@ answer_response(fetchctx_t *fctx) {
7e32a4
 				 * If we're not chaining, then the DNAME and
7e32a4
 				 * its signature should not be external.
7e32a4
 				 */
7e32a4
-				if (!chaining && external) {
7e32a4
+				if (chaining == 0 && external) {
7e32a4
 					char qbuf[DNS_NAME_FORMATSIZE];
7e32a4
 					char obuf[DNS_NAME_FORMATSIZE];
7e32a4
 
7e32a4
@@ -6338,16 +6374,9 @@ answer_response(fetchctx_t *fctx) {
7e32a4
 				/*
7e32a4
 				 * If DNAME + synthetic CNAME then the
7e32a4
 				 * namereln is dns_namereln_subdomain.
7e32a4
-				 *
7e32a4
-				 * If synthetic CNAME + DNAME then the
7e32a4
-				 * namereln is dns_namereln_commonancestor
7e32a4
-				 * and the number of label must match the
7e32a4
-				 * DNAME.  This order is not RFC compliant.
7e32a4
 				 */
7e32a4
-
7e32a4
 				if (namereln != dns_namereln_subdomain &&
7e32a4
-				    (namereln != dns_namereln_commonancestor ||
7e32a4
-				     nlabels != dns_name_countlabels(name)))
7e32a4
+				    !synthcname)
7e32a4
 				{
7e32a4
 					char qbuf[DNS_NAME_FORMATSIZE];
7e32a4
 					char obuf[DNS_NAME_FORMATSIZE];
7e32a4
@@ -6367,8 +6396,19 @@ answer_response(fetchctx_t *fctx) {
7e32a4
 					want_chaining = ISC_TRUE;
7e32a4
 					POST(want_chaining);
7e32a4
 					aflag = DNS_RDATASETATTR_ANSWER;
7e32a4
-					result = dname_target(rdataset, qname,
7e32a4
-							      nlabels, &fdname);
7e32a4
+					dns_fixedname_init(&fdname);
7e32a4
+					dname = dns_fixedname_name(&fdname);
7e32a4
+					if (synthcname) {
7e32a4
+						result = fromdname(rdataset,
7e32a4
+								   lastcname,
7e32a4
+								   lastnlabels,
7e32a4
+								   qname);
7e32a4
+					} else {
7e32a4
+						result = dname_target(rdataset,
7e32a4
+								      qname,
7e32a4
+								      nlabels,
7e32a4
+								      dname);
7e32a4
+					}
7e32a4
 					if (result == ISC_R_NOSPACE) {
7e32a4
 						/*
7e32a4
 						 * We can't construct the
7e32a4
@@ -6382,8 +6422,8 @@ answer_response(fetchctx_t *fctx) {
7e32a4
 					else
7e32a4
 						dnameset = rdataset;
7e32a4
 
7e32a4
-					dname = dns_fixedname_name(&fdname);
7e32a4
-					if (!is_answertarget_allowed(view,
7e32a4
+					if (!synthcname &&
7e32a4
+					    !is_answertarget_allowed(view,
7e32a4
 						     qname, rdataset->type,
7e32a4
 						     dname, &fctx->domain))
7e32a4
 					{
7e32a4
@@ -6404,7 +6444,13 @@ answer_response(fetchctx_t *fctx) {
7e32a4
 				name->attributes |= DNS_NAMEATTR_CACHE;
7e32a4
 				rdataset->attributes |= DNS_RDATASETATTR_CACHE;
7e32a4
 				rdataset->trust = dns_trust_answer;
7e32a4
-				if (!chaining) {
7e32a4
+				/*
7e32a4
+				 * If we are not chaining or the first CNAME
7e32a4
+				 * is a synthesised CNAME before the DNAME.
7e32a4
+				 */
7e32a4
+				if ((chaining == 0) ||
7e32a4
+				    (chaining == 1U && synthcname))
7e32a4
+				{
7e32a4
 					/*
7e32a4
 					 * This data is "the" answer to
7e32a4
 					 * our question only if we're
7e32a4
@@ -6414,9 +6460,12 @@ answer_response(fetchctx_t *fctx) {
7e32a4
 					if (aflag == DNS_RDATASETATTR_ANSWER) {
7e32a4
 						have_answer = ISC_TRUE;
7e32a4
 						found_dname = ISC_TRUE;
7e32a4
-						if (cname != NULL)
7e32a4
+						if (cname != NULL &&
7e32a4
+						    synthcname)
7e32a4
+						{
7e32a4
 							cname->attributes &=
7e32a4
 							   ~DNS_NAMEATTR_ANSWER;
7e32a4
+						}
7e32a4
 						name->attributes |=
7e32a4
 							DNS_NAMEATTR_ANSWER;
7e32a4
 					}
7e32a4
@@ -6434,26 +6483,35 @@ answer_response(fetchctx_t *fctx) {
7e32a4
 			 * DNAME chaining.
7e32a4
 			 */
7e32a4
 			if (dnameset != NULL) {
7e32a4
-				/*
7e32a4
-				 * Copy the dname into the qname fixed name.
7e32a4
-				 *
7e32a4
-				 * Although we check for failure of the copy
7e32a4
-				 * operation, in practice it should never fail
7e32a4
-				 * since we already know that the  result fits
7e32a4
-				 * in a fixedname.
7e32a4
-				 */
7e32a4
-				dns_fixedname_init(&fqname);
7e32a4
-				qname = dns_fixedname_name(&fqname);
7e32a4
-				result = dns_name_copy(dname, qname, NULL);
7e32a4
-				if (result != ISC_R_SUCCESS)
7e32a4
-					return (result);
7e32a4
+				if (!synthcname) {
7e32a4
+					/*
7e32a4
+					 * Copy the dname into the qname fixed
7e32a4
+					 * name.
7e32a4
+					 *
7e32a4
+					 * Although we check for failure of the
7e32a4
+					 * copy operation, in practice it
7e32a4
+					 * should never fail since we already
7e32a4
+					 * know that the result fits in a
7e32a4
+					 * fixedname.
7e32a4
+					 */
7e32a4
+					dns_fixedname_init(&fqname);
7e32a4
+					qname = dns_fixedname_name(&fqname);
7e32a4
+					result = dns_name_copy(dname, qname,
7e32a4
+							       NULL);
7e32a4
+					if (result != ISC_R_SUCCESS)
7e32a4
+						return (result);
7e32a4
+				}
7e32a4
 				wanted_chaining = ISC_TRUE;
7e32a4
 				name->attributes |= DNS_NAMEATTR_CHAINING;
7e32a4
 				dnameset->attributes |=
7e32a4
 					    DNS_RDATASETATTR_CHAINING;
7e32a4
 			}
7e32a4
-			if (wanted_chaining)
7e32a4
-				chaining = ISC_TRUE;
7e32a4
+			/*
7e32a4
+			 * Ensure that we can't ever get chaining == 1
7e32a4
+			 * above if we have processed a DNAME.
7e32a4
+			 */
7e32a4
+			if (wanted_chaining && chaining < 2U)
7e32a4
+				chaining += 2;
7e32a4
 		}
7e32a4
 		result = dns_message_nextname(message, DNS_SECTION_ANSWER);
7e32a4
 	}
7e32a4
@@ -6478,7 +6536,7 @@ answer_response(fetchctx_t *fctx) {
7e32a4
 	/*
7e32a4
 	 * Did chaining end before we got the final answer?
7e32a4
 	 */
7e32a4
-	if (chaining) {
7e32a4
+	if (chaining != 0) {
7e32a4
 		/*
7e32a4
 		 * Yes.  This may be a negative reply, so hand off
7e32a4
 		 * authority section processing to the noanswer code.
7e32a4
@@ -6527,7 +6585,7 @@ answer_response(fetchctx_t *fctx) {
7e32a4
 						DNS_NAMEATTR_CACHE;
7e32a4
 					rdataset->attributes |=
7e32a4
 						DNS_RDATASETATTR_CACHE;
7e32a4
-					if (aa && !chaining)
7e32a4
+					if (aa && chaining == 0)
7e32a4
 						rdataset->trust =
7e32a4
 						    dns_trust_authauthority;
7e32a4
 					else
7e32a4
-- 
7e32a4
2.9.3
7e32a4