diff --git a/CHANGES b/CHANGES
index cb15c9b632..a903fa6b87 100644
--- a/CHANGES
+++ b/CHANGES
@@ -1,3 +1,6 @@
+4000. [bug] NXDOMAIN redirection incorrectly handled NXRRSET
+ from the redirect zone. [RT #37722]
+
3999. [func] "mkeys" and "nzf" files are now named after
their corresponding views, unless the view name
contains characters that would be incompatible
diff --git a/bin/named/query.c b/bin/named/query.c
index 6f90c0fc41..87f0033256 100644
--- a/bin/named/query.c
+++ b/bin/named/query.c
@@ -6010,7 +6010,7 @@ dns64_aaaaok(ns_client_t *client, dns_rdataset_t *rdataset,
* Only perform the update if the client is in the allow query acl and
* returning the update would not cause a DNSSEC validation failure.
*/
-static isc_boolean_t
+static isc_result_t
redirect(ns_client_t *client, dns_name_t *name, dns_rdataset_t *rdataset,
dns_dbnode_t **nodep, dns_db_t **dbp, dns_dbversion_t **versionp,
dns_rdatatype_t qtype)
@@ -6029,7 +6029,7 @@ redirect(ns_client_t *client, dns_name_t *name, dns_rdataset_t *rdataset,
CTRACE(ISC_LOG_DEBUG(3), "redirect");
if (client->view->redirect == NULL)
- return (ISC_FALSE);
+ return (ISC_R_NOTFOUND);
dns_fixedname_init(&fixed);
found = dns_fixedname_name(&fixed);
@@ -6039,15 +6039,15 @@ redirect(ns_client_t *client, dns_name_t *name, dns_rdataset_t *rdataset,
dns_clientinfo_init(&ci, client, NULL);
if (WANTDNSSEC(client) && dns_db_iszone(*dbp) && dns_db_issecure(*dbp))
- return (ISC_FALSE);
+ return (ISC_R_NOTFOUND);
if (WANTDNSSEC(client) && dns_rdataset_isassociated(rdataset)) {
if (rdataset->trust == dns_trust_secure)
- return (ISC_FALSE);
+ return (ISC_R_NOTFOUND);
if (rdataset->trust == dns_trust_ultimate &&
(rdataset->type == dns_rdatatype_nsec ||
rdataset->type == dns_rdatatype_nsec3))
- return (ISC_FALSE);
+ return (ISC_R_NOTFOUND);
if ((rdataset->attributes & DNS_RDATASETATTR_NEGATIVE) != 0) {
for (result = dns_rdataset_first(rdataset);
result == ISC_R_SUCCESS;
@@ -6058,7 +6058,7 @@ redirect(ns_client_t *client, dns_name_t *name, dns_rdataset_t *rdataset,
if (type == dns_rdatatype_nsec ||
type == dns_rdatatype_nsec3 ||
type == dns_rdatatype_rrsig)
- return (ISC_FALSE);
+ return (ISC_R_NOTFOUND);
}
}
}
@@ -6067,16 +6067,16 @@ redirect(ns_client_t *client, dns_name_t *name, dns_rdataset_t *rdataset,
dns_zone_getqueryacl(client->view->redirect),
ISC_TRUE);
if (result != ISC_R_SUCCESS)
- return (ISC_FALSE);
+ return (ISC_R_NOTFOUND);
result = dns_zone_getdb(client->view->redirect, &db);
if (result != ISC_R_SUCCESS)
- return (ISC_FALSE);
+ return (ISC_R_NOTFOUND);
dbversion = query_findversion(client, db);
if (dbversion == NULL) {
dns_db_detach(&db);
- return (ISC_FALSE);
+ return (ISC_R_NOTFOUND);
}
/*
@@ -6085,16 +6085,22 @@ redirect(ns_client_t *client, dns_name_t *name, dns_rdataset_t *rdataset,
result = dns_db_findext(db, client->query.qname, dbversion->version,
qtype, DNS_DBFIND_NOZONECUT, client->now,
&node, found, &cm, &ci, &trdataset, NULL);
- if (result != ISC_R_SUCCESS) {
+ if (result == DNS_R_NXRRSET || result == DNS_R_NCACHENXRRSET) {
+ if (dns_rdataset_isassociated(rdataset))
+ dns_rdataset_disassociate(rdataset);
+ if (dns_rdataset_isassociated(&trdataset))
+ dns_rdataset_disassociate(&trdataset);
+ goto nxrrset;
+ } else if (result != ISC_R_SUCCESS) {
if (dns_rdataset_isassociated(&trdataset))
dns_rdataset_disassociate(&trdataset);
if (node != NULL)
dns_db_detachnode(db, &node);
dns_db_detach(&db);
- return (ISC_FALSE);
+ return (ISC_R_NOTFOUND);
}
- CTRACE(ISC_LOG_DEBUG(3), "redirect: found data: done");
+ CTRACE(ISC_LOG_DEBUG(3), "redirect: found data: done");
dns_name_copy(found, name, NULL);
if (dns_rdataset_isassociated(rdataset))
dns_rdataset_disassociate(rdataset);
@@ -6102,6 +6108,7 @@ redirect(ns_client_t *client, dns_name_t *name, dns_rdataset_t *rdataset,
dns_rdataset_clone(&trdataset, rdataset);
dns_rdataset_disassociate(&trdataset);
}
+ nxrrset:
if (*nodep != NULL)
dns_db_detachnode(*dbp, nodep);
dns_db_detach(dbp);
@@ -6114,7 +6121,7 @@ redirect(ns_client_t *client, dns_name_t *name, dns_rdataset_t *rdataset,
client->query.attributes |= (NS_QUERYATTR_NOAUTHORITY |
NS_QUERYATTR_NOADDITIONAL);
- return (ISC_TRUE);
+ return (result);
}
/*
@@ -6142,7 +6149,7 @@ query_find(ns_client_t *client, dns_fetchevent_t *event, dns_rdatatype_t qtype)
int order;
isc_buffer_t *dbuf;
isc_buffer_t b;
- isc_result_t result, eresult;
+ isc_result_t result, eresult, tresult;
dns_fixedname_t fixed;
dns_fixedname_t wildcardname;
dns_dbversion_t *version, *zversion;
@@ -6157,6 +6164,7 @@ query_find(ns_client_t *client, dns_fetchevent_t *event, dns_rdatatype_t qtype)
int line = -1;
isc_boolean_t dns64_exclude, dns64;
isc_boolean_t nxrewrite = ISC_FALSE;
+ isc_boolean_t redirected = ISC_FALSE;
dns_clientinfomethods_t cm;
dns_clientinfo_t ci;
char errmsg[256];
@@ -6388,7 +6396,6 @@ query_find(ns_client_t *client, dns_fetchevent_t *event, dns_rdatatype_t qtype)
dns_db_t *tdb = NULL;
dns_zone_t *tzone = NULL;
dns_dbversion_t *tversion = NULL;
- isc_result_t tresult;
tresult = query_getzonedb(client, client->query.qname, qtype,
DNS_GETDB_PARTIAL, &tzone, &tdb,
@@ -7191,6 +7198,8 @@ query_find(ns_client_t *client, dns_fetchevent_t *event, dns_rdatatype_t qtype)
* Look for a NSEC3 record if we don't have a NSEC record.
*/
nxrrset_rrsig:
+ if (redirected)
+ goto cleanup;
if (!dns_rdataset_isassociated(rdataset) &&
WANTDNSSEC(client)) {
if ((fname->attributes & DNS_NAMEATTR_WILDCARD) == 0) {
@@ -7315,10 +7324,21 @@ query_find(ns_client_t *client, dns_fetchevent_t *event, dns_rdatatype_t qtype)
case DNS_R_NXDOMAIN:
INSIST(is_zone);
- if (!empty_wild &&
- redirect(client, fname, rdataset, &node, &db, &version,
- type))
- break;
+ if (!empty_wild) {
+ tresult = redirect(client, fname, rdataset, &node,
+ &db, &version, type);
+ if (tresult == ISC_R_SUCCESS)
+ break;
+ if (tresult == DNS_R_NXRRSET) {
+ redirected = ISC_TRUE;
+ goto iszone_nxrrset;
+ }
+ if (tresult == DNS_R_NCACHENXRRSET) {
+ redirected = ISC_TRUE;
+ is_zone = ISC_FALSE;
+ goto ncache_nxrrset;
+ }
+ }
if (dns_rdataset_isassociated(rdataset)) {
/*
* If we've got a NSEC record, we need to save the
@@ -7381,9 +7401,22 @@ query_find(ns_client_t *client, dns_fetchevent_t *event, dns_rdatatype_t qtype)
goto cleanup;
case DNS_R_NCACHENXDOMAIN:
- if (redirect(client, fname, rdataset, &node, &db, &version,
- type))
+ tresult = redirect(client, fname, rdataset, &node,
+ &db, &version, type);
+ if (tresult == ISC_R_SUCCESS)
break;
+ if (tresult == DNS_R_NXRRSET) {
+ redirected = ISC_TRUE;
+ is_zone = ISC_TRUE;
+ goto iszone_nxrrset;
+ }
+ if (tresult == DNS_R_NCACHENXRRSET) {
+ redirected = ISC_TRUE;
+ result = tresult;
+ goto ncache_nxrrset;
+ }
+ /* FALLTHROUGH */
+
case DNS_R_NCACHENXRRSET:
ncache_nxrrset:
INSIST(!is_zone);
diff --git a/bin/tests/system/redirect/tests.sh b/bin/tests/system/redirect/tests.sh
index a9de4130fd..1b802aad3d 100644
--- a/bin/tests/system/redirect/tests.sh
+++ b/bin/tests/system/redirect/tests.sh
@@ -332,6 +332,14 @@ n=`expr $n + 1`
if [ $ret != 0 ]; then echo "I:failed"; fi
status=`expr $status + $ret`
+echo "I:checking redirect works (with noerror) when qtype is not found ($n)"
+ret=0
+$DIG $DIGOPTS nonexist. @10.53.0.2 -b 10.53.0.2 txt > dig.out.ns2.test$n || ret=1
+grep "status: NOERROR" dig.out.ns2.test$n > /dev/null || ret=1
+n=`expr $n + 1`
+if [ $ret != 0 ]; then echo "I:failed"; fi
+status=`expr $status + $ret`
+
echo "I:checking that redirect zones reload correctly"
ret=0
sleep 1 # ensure file mtime will have changed
diff --git a/doc/arm/notes.xml b/doc/arm/notes.xml
index ed00f28a41..db721d7f9a 100644
--- a/doc/arm/notes.xml
+++ b/doc/arm/notes.xml
@@ -334,6 +334,13 @@
corrected at run time. [RT #37187]
+
+
+ When NXDOMAIN redirection is in use, queries for a name
+ that is present in the redirection zone but a type that
+ is not present will now return NOERROR instead of NXDOMAIN.
+
+