File bind-CVE-2017-3135.patch of Package bind

Index: bind-9.9.9-P1/bin/tests/system/dname/ans3/ans.pl
===================================================================
--- /dev/null
+++ bind-9.9.9-P1/bin/tests/system/dname/ans3/ans.pl
@@ -0,0 +1,95 @@
+#!/usr/bin/env perl
+#
+# Copyright (C) 2014-2016  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/.
+
+use strict;
+use warnings;
+
+use IO::File;
+use Getopt::Long;
+use Net::DNS::Nameserver;
+
+my $pidf = new IO::File "ans.pid", "w" or die "cannot open pid file: $!";
+print $pidf "$$\n" or die "cannot write pid file: $!";
+$pidf->close or die "cannot close pid file: $!";
+sub rmpid { unlink "ans.pid"; exit 1; };
+
+$SIG{INT} = \&rmpid;
+$SIG{TERM} = \&rmpid;
+
+my $localaddr = "10.53.0.3";
+my $localport = 5300;
+my $verbose = 0;
+my $ttl = 60;
+my $zone = "example.broken";
+my $nsname = "ns3.$zone";
+my $synth = "synth-then-dname.$zone";
+my $synth2 = "synth2-then-dname.$zone";
+
+sub reply_handler {
+    my ($qname, $qclass, $qtype, $peerhost, $query, $conn) = @_;
+    my ($rcode, @ans, @auth, @add);
+
+    print ("request: $qname/$qtype\n");
+    STDOUT->flush();
+
+    if ($qname eq "example.broken") {
+        if ($qtype eq "SOA") {
+	    my $rr = new Net::DNS::RR("$qname $ttl $qclass SOA . . 0 0 0 0 0");
+	    push @ans, $rr;
+        } elsif ($qtype eq "NS") {
+	    my $rr = new Net::DNS::RR("$qname $ttl $qclass NS $nsname");
+	    push @ans, $rr;
+	    $rr = new Net::DNS::RR("$nsname $ttl $qclass A $localaddr");
+	    push @add, $rr;
+        }
+        $rcode = "NOERROR";
+    } elsif ($qname eq "cname-to-$synth2") {
+        my $rr = new Net::DNS::RR("$qname $ttl $qclass CNAME name.$synth2");
+	push @ans, $rr;
+        $rr = new Net::DNS::RR("name.$synth2 $ttl $qclass CNAME name");
+	push @ans, $rr;
+        $rr = new Net::DNS::RR("$synth2 $ttl $qclass DNAME .");
+	push @ans, $rr;
+	$rcode = "NOERROR";
+    } elsif ($qname eq "$synth" || $qname eq "$synth2") {
+	if ($qtype eq "DNAME") {
+	    my $rr = new Net::DNS::RR("$qname $ttl $qclass DNAME .");
+	    push @ans, $rr;
+	}
+	$rcode = "NOERROR";
+    } elsif ($qname eq "name.$synth") {
+	my $rr = new Net::DNS::RR("$qname $ttl $qclass CNAME name.");
+	push @ans, $rr;
+	$rr = new Net::DNS::RR("$synth $ttl $qclass DNAME .");
+	push @ans, $rr;
+	$rcode = "NOERROR";
+    } elsif ($qname eq "name.$synth2") {
+	my $rr = new Net::DNS::RR("$qname $ttl $qclass CNAME name.");
+	push @ans, $rr;
+	$rr = new Net::DNS::RR("$synth2 $ttl $qclass DNAME .");
+	push @ans, $rr;
+	$rcode = "NOERROR";
+    } else {
+	$rcode = "REFUSED";
+    }
+    return ($rcode, \@ans, \@auth, \@add, { aa => 1 });
+}
+
+GetOptions(
+    'port=i' => \$localport,
+    'verbose!' => \$verbose,
+);
+
+my $ns = Net::DNS::Nameserver->new(
+    LocalAddr => $localaddr,
+    LocalPort => $localport,
+    ReplyHandler => \&reply_handler,
+    Verbose => $verbose,
+);
+
+$ns->main_loop;
Index: bind-9.9.9-P1/bin/tests/system/dname/ns1/root.db
===================================================================
--- bind-9.9.9-P1.orig/bin/tests/system/dname/ns1/root.db
+++ bind-9.9.9-P1/bin/tests/system/dname/ns1/root.db
@@ -12,8 +12,6 @@
 ; OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 ; PERFORMANCE OF THIS SOFTWARE.
 
-; $Id: root.db,v 1.2 2011/03/18 21:14:19 fdupont Exp $
-
 $TTL 300
 . 			IN SOA	gson.nominum.com. a.root.servers.nil. (
 				2000042100   	; serial
@@ -27,3 +25,6 @@ a.root-servers.nil.	A	10.53.0.1
 
 example.		NS	ns2.example.
 ns2.example.		A	10.53.0.2
+
+example.broken.		NS	ns3.example.broken.
+ns3.example.broken.	A	10.53.0.3
Index: bind-9.9.9-P1/bin/tests/system/dname/tests.sh
===================================================================
--- bind-9.9.9-P1.orig/bin/tests/system/dname/tests.sh
+++ bind-9.9.9-P1/bin/tests/system/dname/tests.sh
@@ -20,6 +20,7 @@ SYSTEMTESTTOP=..
 . $SYSTEMTESTTOP/conf.sh
 
 status=0
+n=0
 
 echo "I:checking short dname from authoritative"
 ret=0
@@ -63,6 +64,26 @@ grep "status: YXDOMAIN" dig.out.ns4.tool
 if [ $ret != 0 ]; then echo "I:failed"; fi
 status=`expr $status + $ret`
 
-echo "I:exit status: $status"
+n=`expr $n + 1`
+echo "I:checking dname is returned with synthesized cname before dname ($n)"
+ret=0
+$DIG @10.53.0.4 -p 5300 name.synth-then-dname.example.broken A > dig.out.test$n
+grep "status: NXDOMAIN" dig.out.test$n > /dev/null || ret=1
+grep '^name.synth-then-dname\.example\.broken\..*CNAME.*name.$' dig.out.test$n > /dev/null || ret=1
+grep '^synth-then-dname\.example\.broken\..*DNAME.*\.$' dig.out.test$n > /dev/null || ret=1
+if [ $ret != 0 ]; then echo "I:failed"; fi
+status=`expr $status + $ret`
 
-exit $status
+n=`expr $n + 1`
+echo "I:checking dname is returned with cname to synthesized cname before dname ($n)"
+ret=0
+$DIG @10.53.0.4 -p 5300 cname-to-synth2-then-dname.example.broken A > dig.out.test$n
+grep "status: NXDOMAIN" dig.out.test$n > /dev/null || ret=1
+grep '^cname-to-synth2-then-dname\.example\.broken\..*CNAME.*name\.synth2-then-dname\.example\.broken.$' dig.out.test$n > /dev/null || ret=1
+grep '^name\.synth2-then-dname\.example\.broken\..*CNAME.*name.$' dig.out.test$n > /dev/null || ret=1
+grep '^synth2-then-dname\.example\.broken\..*DNAME.*\.$' dig.out.test$n > /dev/null || ret=1
+if [ $ret != 0 ]; then echo "I:failed"; fi
+status=`expr $status + $ret`
+
+echo "I:exit status: $status"
+[ $status -eq 0 ] || exit 1
Index: bind-9.9.9-P1/bin/named/query.c
===================================================================
--- bind-9.9.9-P1.orig/bin/named/query.c
+++ bind-9.9.9-P1/bin/named/query.c
@@ -5674,7 +5674,7 @@ query_find(ns_client_t *client, dns_fetc
 	dns_rpz_st_t *rpz_st;
 	isc_boolean_t resuming;
 	int line = -1;
-	isc_boolean_t dns64_exclude, dns64;
+	isc_boolean_t dns64_exclude, dns64, rpz;
 	isc_boolean_t nxrewrite = ISC_FALSE;
 	isc_boolean_t redirected = ISC_FALSE;
 	dns_clientinfomethods_t cm;
@@ -5687,6 +5687,7 @@ query_find(ns_client_t *client, dns_fetc
 	char mbuf[BUFSIZ];
 	char qbuf[DNS_NAME_FORMATSIZE];
 #endif
+	dns_name_t *rpzqname;
 
 	CTRACE(ISC_LOG_DEBUG(3), "query_find");
 
@@ -5712,7 +5713,7 @@ query_find(ns_client_t *client, dns_fetc
 	zone = NULL;
 	need_wildcardproof = ISC_FALSE;
 	empty_wild = ISC_FALSE;
-	dns64_exclude = dns64 = ISC_FALSE;
+	dns64_exclude = dns64 = rpz = ISC_FALSE;
 	options = 0;
 	resuming = ISC_FALSE;
 	is_zone = ISC_FALSE;
@@ -5881,6 +5882,7 @@ query_find(ns_client_t *client, dns_fetc
 	authoritative = ISC_FALSE;
 	version = NULL;
 	need_wildcardproof = ISC_FALSE;
+	rpz = ISC_FALSE;
 
 	if (client->view->checknames &&
 	    !dns_rdata_checkowner(client->query.qname,
@@ -6016,11 +6018,29 @@ query_find(ns_client_t *client, dns_fetc
 	}
 
 	/*
-	 * Now look for an answer in the database.
+	 * Now look for an answer in the database.  If this is a dns64
+	 * AAAA lookup on a rpz database adjust the qname.
 	 */
-	result = dns_db_findext(db, client->query.qname, version, type,
+	if (dns64 && rpz)
+		rpzqname = client->query.rpz_st->qname;
+	else
+		rpzqname = client->query.qname;
+
+	result = dns_db_findext(db, rpzqname, version, type,
 				client->query.dboptions, client->now,
 				&node, fname, &cm, &ci, rdataset, sigrdataset);
+	/*
+	 * Fixup fname and sigrdataset.
+	 */
+	if (dns64 && rpz) {
+		isc_result_t rresult;
+
+		rresult = dns_name_copy(client->query.qname, fname, NULL);
+		RUNTIME_CHECK(rresult == ISC_R_SUCCESS);
+		if (sigrdataset != NULL &&
+		    dns_rdataset_isassociated(sigrdataset))
+			dns_rdataset_disassociate(sigrdataset);
+	}
 
  resume:
 	CTRACE(ISC_LOG_DEBUG(3), "query_find: resume");
@@ -6223,10 +6243,12 @@ query_find(ns_client_t *client, dns_fetc
 			case DNS_RPZ_POLICY_NXDOMAIN:
 				result = DNS_R_NXDOMAIN;
 				nxrewrite = ISC_TRUE;
+				rpz = ISC_TRUE;
 				break;
 			case DNS_RPZ_POLICY_NODATA:
 				result = DNS_R_NXRRSET;
 				nxrewrite = ISC_TRUE;
+				rpz = ISC_TRUE;
 				break;
 			case DNS_RPZ_POLICY_RECORD:
 				result = rpz_st->m.result;
@@ -6246,6 +6268,7 @@ query_find(ns_client_t *client, dns_fetc
 					rdataset->ttl = ISC_MIN(rdataset->ttl,
 							    rpz_st->m.ttl);
 				}
+				rpz = ISC_TRUE;
 				break;
 			case DNS_RPZ_POLICY_WILDCNAME:
 				result = dns_rdataset_first(rdataset);
@@ -6288,7 +6311,6 @@ query_find(ns_client_t *client, dns_fetc
 						NS_CLIENTATTR_WANTAD);
 			client->message->flags &= ~DNS_MESSAGEFLAG_AD;
 			query_putrdataset(client, &sigrdataset);
-			rpz_st->q.is_zone = is_zone;
 			is_zone = ISC_TRUE;
 			rpz_log_rewrite(client, ISC_FALSE, rpz_st->m.policy,
 					rpz_st->m.type, zone, rpz_st->qname);
@@ -6671,15 +6693,6 @@ query_find(ns_client_t *client, dns_fetc
 			rdataset = NULL;
 			sigrdataset = NULL;
 			type = qtype = dns_rdatatype_a;
-			rpz_st = client->query.rpz_st;
-			if (rpz_st != NULL) {
-				/*
-				 * Arrange for RPZ rewriting of any A records.
-				 */
-				if ((rpz_st->state & DNS_RPZ_REWRITTEN) != 0)
-					is_zone = rpz_st->q.is_zone;
-				rpz_st_clear(client);
-			}
 			dns64 = ISC_TRUE;
 			goto db_find;
 		}
@@ -6994,15 +7007,6 @@ query_find(ns_client_t *client, dns_fetc
 			sigrdataset = NULL;
 			fname = NULL;
 			type = qtype = dns_rdatatype_a;
-			rpz_st = client->query.rpz_st;
-			if (rpz_st != NULL) {
-				/*
-				 * Arrange for RPZ rewriting of any A records.
-				 */
-				if ((rpz_st->state & DNS_RPZ_REWRITTEN) != 0)
-					is_zone = rpz_st->q.is_zone;
-				rpz_st_clear(client);
-			}
 			dns64 = ISC_TRUE;
 			goto db_find;
 		}
@@ -7516,15 +7520,6 @@ query_find(ns_client_t *client, dns_fetc
 			rdataset = NULL;
 			sigrdataset = NULL;
 			type = qtype = dns_rdatatype_a;
-			rpz_st = client->query.rpz_st;
-			if (rpz_st != NULL) {
-				/*
-				 * Arrange for RPZ rewriting of any A records.
-				 */
-				if ((rpz_st->state & DNS_RPZ_REWRITTEN) != 0)
-					is_zone = rpz_st->q.is_zone;
-				rpz_st_clear(client);
-			}
 			dns64_exclude = dns64 = ISC_TRUE;
 			goto db_find;
 		}
Index: bind-9.9.9-P1/lib/dns/message.c
===================================================================
--- bind-9.9.9-P1.orig/lib/dns/message.c
+++ bind-9.9.9-P1/lib/dns/message.c
@@ -1213,8 +1213,8 @@ getsection(isc_buffer_t *source, dns_mes
 {
 	isc_region_t r;
 	unsigned int count, rdatalen;
-	dns_name_t *name;
-	dns_name_t *name2;
+	dns_name_t *name = NULL;
+	dns_name_t *name2 = NULL;
 	dns_offsets_t *offsets;
 	dns_rdataset_t *rdataset;
 	dns_rdatalist_t *rdatalist;
@@ -1224,7 +1224,7 @@ getsection(isc_buffer_t *source, dns_mes
 	dns_rdata_t *rdata;
 	dns_ttl_t ttl;
 	dns_namelist_t *section;
-	isc_boolean_t free_name, free_rdataset;
+	isc_boolean_t free_name = ISC_FALSE, free_rdataset = ISC_FALSE;
 	isc_boolean_t preserve_order, best_effort, seen_problem;
 	isc_boolean_t issigzero;
 
Index: bind-9.9.9-P1/lib/dns/rdataset.c
===================================================================
--- bind-9.9.9-P1.orig/lib/dns/rdataset.c
+++ bind-9.9.9-P1/lib/dns/rdataset.c
@@ -337,6 +337,7 @@ towiresorted(dns_rdataset_t *rdataset, c
 	 */
 
 	REQUIRE(DNS_RDATASET_VALID(rdataset));
+	REQUIRE(rdataset->methods != NULL);
 	REQUIRE(countp != NULL);
 	REQUIRE((order == NULL) == (order_arg == NULL));
 	REQUIRE(cctx != NULL && cctx->mctx != NULL);
Index: bind-9.9.9-P1/lib/dns/resolver.c
===================================================================
--- bind-9.9.9-P1.orig/lib/dns/resolver.c
+++ bind-9.9.9-P1/lib/dns/resolver.c
@@ -5818,9 +5818,13 @@ cname_target(dns_rdataset_t *rdataset, d
 	return (ISC_R_SUCCESS);
 }
 
+/*%
+ * Construct the synthesised CNAME from the existing QNAME and
+ * the DNAME RR and store it in 'target'.
+ */
 static inline isc_result_t
 dname_target(dns_rdataset_t *rdataset, dns_name_t *qname,
-	     unsigned int nlabels, dns_fixedname_t *fixeddname)
+	    unsigned int nlabels, dns_name_t *target)
 {
 	isc_result_t result;
 	dns_rdata_t rdata = DNS_RDATA_INIT;
@@ -5840,14 +5844,33 @@ dname_target(dns_rdataset_t *rdataset, d
 
 	dns_fixedname_init(&prefix);
 	dns_name_split(qname, nlabels, dns_fixedname_name(&prefix), NULL);
-	dns_fixedname_init(fixeddname);
 	result = dns_name_concatenate(dns_fixedname_name(&prefix),
-				      &dname.dname,
-				      dns_fixedname_name(fixeddname), NULL);
+				      &dname.dname, target, NULL);	
 	dns_rdata_freestruct(&dname);
 	return (result);
 }
 
+/*%
+ * Check if it was possible to construct 'qname' from 'lastcname'
+ * and 'rdataset'.
+ */
+static inline isc_result_t
+fromdname(dns_rdataset_t *rdataset, dns_name_t *lastcname,
+         unsigned int nlabels, const dns_name_t *qname)
+{
+       dns_fixedname_t fixed;
+       isc_result_t result;
+       dns_name_t *target;
+
+       dns_fixedname_init(&fixed);
+       target = dns_fixedname_name(&fixed);
+       result = dname_target(rdataset, lastcname, nlabels, target);
+       if (result != ISC_R_SUCCESS || !dns_name_equal(qname, target))
+               return (ISC_R_NOTFOUND);
+
+       return (ISC_R_SUCCESS);
+}
+
 static isc_boolean_t
 is_answeraddress_allowed(dns_view_t *view, dns_name_t *name,
 			 dns_rdataset_t *rdataset)
@@ -6464,12 +6487,12 @@ answer_response(fetchctx_t *fctx) {
 	isc_result_t result;
 	dns_message_t *message;
 	dns_name_t *name, *dname = NULL, *qname, *dqname, tname, *ns_name;
-	dns_name_t *cname = NULL;
+	dns_name_t *cname = NULL, *lastcname = NULL;
 	dns_rdataset_t *rdataset, *ns_rdataset;
-	isc_boolean_t done, external, chaining, aa, found, want_chaining;
+	isc_boolean_t done, external, aa, found, want_chaining;
 	isc_boolean_t have_answer, found_cname, found_dname, found_type;
 	isc_boolean_t wanted_chaining;
-	unsigned int aflag;
+	unsigned int aflag, chaining;
 	dns_rdatatype_t type;
 	dns_fixedname_t fdname, fqname, fqdname;
 	dns_view_t *view;
@@ -6487,7 +6510,7 @@ answer_response(fetchctx_t *fctx) {
 	found_cname = ISC_FALSE;
 	found_dname = ISC_FALSE;
 	found_type = ISC_FALSE;
-	chaining = ISC_FALSE;
+	chaining = 0;
 	have_answer = ISC_FALSE;
 	want_chaining = ISC_FALSE;
 	POST(want_chaining);
@@ -6501,16 +6524,14 @@ answer_response(fetchctx_t *fctx) {
 	dns_fixedname_init(&fqdname);
 	result = dns_message_firstname(message, DNS_SECTION_ANSWER);
 	while (!done && result == ISC_R_SUCCESS) {
-		dns_namereln_t namereln, dnamereln;
-		int order;
-		unsigned int nlabels;
+		dns_namereln_t namereln, lastreln;
+		int order, lastorder;
+		unsigned int nlabels, lastnlabels;
 
 		name = NULL;
 		dns_message_currentname(message, DNS_SECTION_ANSWER, &name);
 		external = ISC_TF(!dns_name_issubdomain(name, &fctx->domain));
 		namereln = dns_name_fullcompare(qname, name, &order, &nlabels);
-		dnamereln = dns_name_fullcompare(dqname, name, &order,
-						 &nlabels);
 		if (namereln == dns_namereln_equal) {
 			wanted_chaining = ISC_FALSE;
 			for (rdataset = ISC_LIST_HEAD(name->list);
@@ -6616,6 +6637,7 @@ answer_response(fetchctx_t *fctx) {
 							&fctx->domain)) {
 						return (DNS_R_SERVFAIL);
 					}
+					lastcname = name;
 				} else if (rdataset->type == dns_rdatatype_rrsig
 					   && rdataset->covers ==
 					      dns_rdatatype_cname
@@ -6639,7 +6661,7 @@ answer_response(fetchctx_t *fctx) {
 					rdataset->attributes |=
 						DNS_RDATASETATTR_CACHE;
 					rdataset->trust = dns_trust_answer;
-					if (!chaining) {
+					if (chaining == 0) {
 						/*
 						 * This data is "the" answer
 						 * to our question only if
@@ -6716,10 +6738,22 @@ answer_response(fetchctx_t *fctx) {
 			 * cause us to ignore the signatures of
 			 * CNAMEs.
 			 */
-			if (wanted_chaining)
-				chaining = ISC_TRUE;
+			if (wanted_chaining && chaining < 2U)
+				chaining++;
+
 		} else {
 			dns_rdataset_t *dnameset = NULL;
+			isc_boolean_t synthcname = ISC_FALSE;
+
+			if (lastcname != NULL) {
+				lastreln = dns_name_fullcompare(lastcname,
+								name,
+								&lastorder,
+								&lastnlabels);
+				if (lastreln == dns_namereln_subdomain &&
+				    lastnlabels == dns_name_countlabels(name))
+					synthcname = ISC_TRUE;
+			}
 
 			/*
 			 * Look for a DNAME (or its SIG).  Anything else is
@@ -6748,7 +6782,7 @@ answer_response(fetchctx_t *fctx) {
 				 * If we're not chaining, then the DNAME and
 				 * its signature should not be external.
 				 */
-				if (!chaining && external) {
+				if (chaining == 0 && external) {
 					char qbuf[DNS_NAME_FORMATSIZE];
 					char obuf[DNS_NAME_FORMATSIZE];
 
@@ -6763,7 +6797,7 @@ answer_response(fetchctx_t *fctx) {
 					return (DNS_R_FORMERR);
 				}
 
-				if (dnamereln != dns_namereln_subdomain) {
+				if (namereln != dns_namereln_subdomain && !synthcname) {
 					char qbuf[DNS_NAME_FORMATSIZE];
 					char obuf[DNS_NAME_FORMATSIZE];
 
@@ -6782,8 +6816,19 @@ answer_response(fetchctx_t *fctx) {
 					want_chaining = ISC_TRUE;
 					POST(want_chaining);
 					aflag = DNS_RDATASETATTR_ANSWER;
-					result = dname_target(rdataset, dqname,
-							      nlabels, &fdname);
+					dns_fixedname_init(&fdname);
+					dname = dns_fixedname_name(&fdname);
+					if (synthcname) {
+						result = fromdname(rdataset,
+								   lastcname,
+								   lastnlabels,
+								   qname);
+					} else {
+						result = dname_target(rdataset,
+								      qname,
+								      nlabels,
+								      dname);
+					}
 					if (result == ISC_R_NOSPACE) {
 						/*
 						 * We can't construct the
@@ -6796,9 +6841,8 @@ answer_response(fetchctx_t *fctx) {
 						return (result);
 					else
 						dnameset = rdataset;
-
-					dname = dns_fixedname_name(&fdname);
-					if (!is_answertarget_allowed(view,
+					if (!synthcname &&
+					    !is_answertarget_allowed(view,
 						     dqname, rdataset->type,
 						     dname, &fctx->domain))
 					{
@@ -6821,7 +6865,13 @@ answer_response(fetchctx_t *fctx) {
 				name->attributes |= DNS_NAMEATTR_CACHE;
 				rdataset->attributes |= DNS_RDATASETATTR_CACHE;
 				rdataset->trust = dns_trust_answer;
-				if (!chaining) {
+				/*
+				 * If we are not chaining or the first CNAME
+				 * is a synthesised CNAME before the DNAME.
+				 */
+				if ((chaining == 0) ||
+				    (chaining == 1U && synthcname))
+				{
 					/*
 					 * This data is "the" answer to
 					 * our question only if we're
@@ -6831,9 +6881,11 @@ answer_response(fetchctx_t *fctx) {
 					if (aflag == DNS_RDATASETATTR_ANSWER) {
 						have_answer = ISC_TRUE;
 						found_dname = ISC_TRUE;
-						if (cname != NULL)
+						if (cname != NULL &&  synthcname)
+						{
 							cname->attributes &=
 							   ~DNS_NAMEATTR_ANSWER;
+						}
 						name->attributes |=
 							DNS_NAMEATTR_ANSWER;
 					}
@@ -6851,26 +6903,35 @@ answer_response(fetchctx_t *fctx) {
 			 * DNAME chaining.
 			 */
 			if (dnameset != NULL) {
-				/*
-				 * Copy the dname into the qname fixed name.
-				 *
-				 * Although we check for failure of the copy
-				 * operation, in practice it should never fail
-				 * since we already know that the  result fits
-				 * in a fixedname.
-				 */
-				dns_fixedname_init(&fqname);
-				qname = dns_fixedname_name(&fqname);
-				result = dns_name_copy(dname, qname, NULL);
-				if (result != ISC_R_SUCCESS)
-					return (result);
+				if (!synthcname) {
+					/*
+					 * Copy the dname into the qname fixed
+					 * name.
+					 *
+					 * Although we check for failure of the
+					 * copy operation, in practice it
+					 * should never fail since we already
+					 * know that the result fits in a
+					 * fixedname.
+					 */
+					dns_fixedname_init(&fqname);
+					qname = dns_fixedname_name(&fqname);
+					result = dns_name_copy(dname, qname,
+							       NULL);
+					if (result != ISC_R_SUCCESS)
+						return (result);
+				}
 				wanted_chaining = ISC_TRUE;
 				name->attributes |= DNS_NAMEATTR_CHAINING;
 				dnameset->attributes |=
 					    DNS_RDATASETATTR_CHAINING;
 			}
-			if (wanted_chaining)
-				chaining = ISC_TRUE;
+			/*
+			 * Ensure that we can't ever get chaining == 1
+			 * above if we have processed a DNAME.
+			 */
+			if (wanted_chaining && chaining < 2U)
+				chaining += 2;
 		}
 		result = dns_message_nextname(message, DNS_SECTION_ANSWER);
 	}
@@ -6895,7 +6956,7 @@ answer_response(fetchctx_t *fctx) {
 	/*
 	 * Did chaining end before we got the final answer?
 	 */
-	if (chaining) {
+	if (chaining != 0) {
 		/*
 		 * Yes.  This may be a negative reply, so hand off
 		 * authority section processing to the noanswer code.
@@ -6944,7 +7005,7 @@ answer_response(fetchctx_t *fctx) {
 						DNS_NAMEATTR_CACHE;
 					rdataset->attributes |=
 						DNS_RDATASETATTR_CACHE;
-					if (aa && !chaining)
+					if (aa && chaining == 0)
 						rdataset->trust =
 						    dns_trust_authauthority;
 					else