Commit 099d6d7ee7 for bind
commit 099d6d7ee7cc1935ffc39451cdfe49f69e1d24cb
Author: Evan Hunt <each@isc.org>
Date: Thu Sep 18 20:49:42 2025 -0700
Authoritative DELEG support
Support for the DELEG type has been added to the load-time zone
integrity checks, DNSSEC code that identifies zone cuts and closest
enclosers, dnssec-signzone, and the qpzone database.
If a DELEG RRset is present in a zone, then a query with DE=1 for
data below its owner name will be answered with a DELEG referral.
If an NS referral is present and DELEG is not, an NS referral is
returned, along with a proof of DELEG nonexistence if the zone is
signed.
If both NS and DELEG are present, NS is used if DE=0, and DELEG if
DE=1.
Several new special cases have been added to the handling of queries
for data *at* a zone cut; the correct response depends on whether the
delegation record is NS, DELEG, or both, and whether the DE flag is
set. qpzone_find() and qpzone_check_zonecut() have been refactored
and revised to handle all of these cases correctly:
- For query type KEY, NSEC, or DS, or query type DELEG if DE=1, or
if the GLUEOK flag is set, we treat the node as if it were not a
zone cut and search for a normal answer.
- For query type DELEG if DE=0, if NS is present, we return an NS
referral. If NS is not present, we treat the node as i it were
not a zone cut and search for a normal answer.
- For any other query type:
- If DELEG is present and DE=1: return DELEG referral
- If NS is not present, but DELEG is, and DE=0: return NXRRSET
- If NS is present, and either DELEG is not present *or* DE=0:
return NS referral.
diff --git a/bin/dnssec/dnssec-signzone.c b/bin/dnssec/dnssec-signzone.c
index da3a2fb848..98eb28daf7 100644
--- a/bin/dnssec/dnssec-signzone.c
+++ b/bin/dnssec/dnssec-signzone.c
@@ -96,6 +96,14 @@
typedef struct hashlist hashlist_t;
+/* Delegation types: NS, DELEG, or both */
+typedef enum {
+ DEL_NONE = 0,
+ DEL_NS = 1,
+ DEL_DELEG = 2,
+ DEL_BOTH = 3,
+} deltype_t;
+
static int nsec_datatype = dns_rdatatype_nsec;
#define check_dns_dbiterator_current(result) \
@@ -1183,25 +1191,33 @@ secure(dns_name_t *name, dns_dbnode_t *node) {
return result == ISC_R_SUCCESS;
}
-static bool
-is_delegation(dns_db_t *db, dns_dbversion_t *ver, dns_name_t *origin,
- dns_name_t *name, dns_dbnode_t *node, uint32_t *ttlp) {
- dns_rdataset_t nsset;
- isc_result_t result;
+static deltype_t
+delegation_type(dns_db_t *db, dns_dbversion_t *ver, dns_name_t *origin,
+ dns_name_t *name, dns_dbnode_t *node, uint32_t *ttlp) {
+ dns_rdataset_t rdataset = DNS_RDATASET_INIT;
+ deltype_t deltype = DEL_NONE;
if (dns_name_equal(name, origin)) {
- return false;
+ return DEL_NONE;
}
- dns_rdataset_init(&nsset);
- result = dns_db_findrdataset(db, node, ver, dns_rdatatype_ns, 0, 0,
- &nsset, NULL);
- if (dns_rdataset_isassociated(&nsset)) {
- SET_IF_NOT_NULL(ttlp, nsset.ttl);
- dns_rdataset_disassociate(&nsset);
+ (void)dns_db_findrdataset(db, node, ver, dns_rdatatype_ns, 0, 0,
+ &rdataset, NULL);
+ if (dns_rdataset_isassociated(&rdataset)) {
+ deltype |= DEL_NS;
+ SET_IF_NOT_NULL(ttlp, rdataset.ttl);
+ dns_rdataset_disassociate(&rdataset);
}
- return result == ISC_R_SUCCESS;
+ (void)dns_db_findrdataset(db, node, ver, dns_rdatatype_deleg, 0, 0,
+ &rdataset, NULL);
+ if (dns_rdataset_isassociated(&rdataset)) {
+ deltype |= DEL_DELEG;
+ SET_IF_NOT_NULL(ttlp, rdataset.ttl);
+ dns_rdataset_disassociate(&rdataset);
+ }
+
+ return deltype;
}
/*%
@@ -1229,9 +1245,9 @@ signname(dns_dbnode_t *node, bool apex, dns_name_t *name) {
isc_result_t result;
dns_rdataset_t rdataset;
dns_rdatasetiter_t *rdsiter;
- bool isdelegation = false;
dns_diff_t del, add;
char namestr[DNS_NAME_FORMATSIZE];
+ deltype_t deltype;
dns_rdataset_init(&rdataset);
dns_name_format(name, namestr, sizeof(namestr));
@@ -1239,9 +1255,7 @@ signname(dns_dbnode_t *node, bool apex, dns_name_t *name) {
/*
* Determine if this is a delegation point.
*/
- if (is_delegation(gdb, gversion, gorigin, name, node, NULL)) {
- isdelegation = true;
- }
+ deltype = delegation_type(gdb, gversion, gorigin, name, node, NULL);
/*
* Now iterate through the rdatasets.
@@ -1262,11 +1276,11 @@ signname(dns_dbnode_t *node, bool apex, dns_name_t *name) {
/*
* If this name is a delegation point, skip all records
* except NSEC and DS sets. Otherwise check that there
- * isn't a DS record.
+ * isn't a DS or DELEG record.
*/
- if (isdelegation) {
+ if (deltype != DEL_NONE) {
if (rdataset.type != nsec_datatype &&
- rdataset.type != dns_rdatatype_ds)
+ !dns_rdatatype_atparent(rdataset.type))
{
goto skip;
}
@@ -1641,11 +1655,13 @@ assignwork(void *arg) {
(zonecut == NULL ||
!dns_name_issubdomain(name, zonecut)))
{
- if (is_delegation(gdb, gversion, gorigin, name,
- node, NULL))
- {
+ deltype_t deltype =
+ delegation_type(gdb, gversion, gorigin,
+ name, node, NULL);
+ if (deltype != DEL_NONE) {
zonecut = savezonecut(&fzonecut, name);
if (!OPTOUT(nsec3flags) ||
+ (deltype & DEL_DELEG) != 0 ||
secure(name, node))
{
found = true;
@@ -1867,6 +1883,8 @@ nsecify(void) {
check_result(result, "dns_dbiterator_first()");
while (!done) {
+ deltype_t deltype;
+
result = dns_dbiterator_current(dbiter, &node, name);
check_dns_dbiterator_current(result);
/*
@@ -1889,7 +1907,9 @@ nsecify(void) {
(void)active_node(node);
}
- if (is_delegation(gdb, gversion, gorigin, name, node, &nsttl)) {
+ deltype = delegation_type(gdb, gversion, gorigin, name, node,
+ &nsttl);
+ if (deltype != DEL_NONE) {
zonecut = savezonecut(&fzonecut, name);
remove_sigs(node, true, 0);
if (generateds) {
@@ -2316,6 +2336,7 @@ nsec3ify(unsigned int hashalg, dns_iterations_t iterations,
result = dns_dbiterator_next(dbiter);
nextnode = NULL;
while (result == ISC_R_SUCCESS) {
+ deltype_t deltype;
result = dns_dbiterator_current(dbiter, &nextnode,
nextname);
check_dns_dbiterator_current(result);
@@ -2334,15 +2355,16 @@ nsec3ify(unsigned int hashalg, dns_iterations_t iterations,
result = dns_dbiterator_next(dbiter);
continue;
}
- if (is_delegation(gdb, gversion, gorigin, nextname,
- nextnode, &nsttl))
- {
+ deltype = delegation_type(gdb, gversion, gorigin,
+ nextname, nextnode, &nsttl);
+ if (deltype != DEL_NONE) {
zonecut = savezonecut(&fzonecut, nextname);
remove_sigs(nextnode, true, 0);
if (generateds) {
add_ds(nextname, nextnode, nsttl);
}
if (OPTOUT(nsec3flags) &&
+ (deltype & DEL_DELEG) == 0 &&
!secure(nextname, nextnode))
{
dns_db_detachnode(&nextnode);
@@ -2458,6 +2480,7 @@ nsec3ify(unsigned int hashalg, dns_iterations_t iterations,
result = dns_dbiterator_next(dbiter);
nextnode = NULL;
while (result == ISC_R_SUCCESS) {
+ deltype_t deltype;
result = dns_dbiterator_current(dbiter, &nextnode,
nextname);
check_dns_dbiterator_current(result);
@@ -2475,11 +2498,12 @@ nsec3ify(unsigned int hashalg, dns_iterations_t iterations,
result = dns_dbiterator_next(dbiter);
continue;
}
- if (is_delegation(gdb, gversion, gorigin, nextname,
- nextnode, NULL))
- {
+ deltype = delegation_type(gdb, gversion, gorigin,
+ nextname, nextnode, NULL);
+ if (deltype != DEL_NONE) {
zonecut = savezonecut(&fzonecut, nextname);
if (OPTOUT(nsec3flags) &&
+ (deltype & DEL_DELEG) == 0 &&
!secure(nextname, nextnode))
{
dns_db_detachnode(&nextnode);
diff --git a/bin/tests/system/dnssectools/signer/general/test14.zone b/bin/tests/system/dnssectools/signer/general/test14.zone
new file mode 100644
index 0000000000..4ec0cbfdb3
--- /dev/null
+++ b/bin/tests/system/dnssectools/signer/general/test14.zone
@@ -0,0 +1,27 @@
+; Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+;
+; SPDX-License-Identifier: MPL-2.0
+;
+; 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 https://mozilla.org/MPL/2.0/.
+;
+; See the COPYRIGHT file distributed with this work for additional
+; information regarding copyright ownership.
+
+; This is a zone which has two DNSKEY records, both of which have
+; existing private key files available. They should be loaded automatically
+; and the zone correctly signed.
+;
+$TTL 3600
+example.com. IN SOA ns hostmaster 00090000 1200 3600 604800 300
+$include Kexample.com.+010+18240.key
+$include Kexample.com.+010+28633.key
+
+deleg-only IN DELEG server-ipv4=1.2.3.4
+
+ns-only IN NS ns.ns-only
+ns.ns-only IN A 1.2.3.4
+
+mixed IN NS ns.ns-only
+mixed IN DELEG server-ipv4=1.2.3.4
diff --git a/bin/tests/system/dnssectools/tests.sh b/bin/tests/system/dnssectools/tests.sh
index c23cd3604d..4e9ae18084 100644
--- a/bin/tests/system/dnssectools/tests.sh
+++ b/bin/tests/system/dnssectools/tests.sh
@@ -237,6 +237,22 @@ n=$((n + 1))
test "$ret" -eq 0 || echo_i "failed"
status=$((status + ret))
+echo_ic "check that dnssec-signzone correctly handles DELEG ($n)"
+ret=0
+(
+ cd signer/general || exit 1
+ rm -f signed.zone
+ $SIGNER -O full -f signed.zone -o example.com. test14.zone >signer.out.$n
+ grep -q "^mixed.*IN RRSIG.*DELEG" signed.zone || exit 1
+ grep -q "^mixed.*IN NSEC.*NS .*DELEG" signed.zone || exit 1
+ grep -q "^deleg-only.*IN NSEC.*DELEG" signed.zone || exit 1
+ grep -q "^deleg-only.*IN NSEC.*NS " signed.zone && exit 1
+ test -f signed.zone || exit 1
+) || ret=1
+n=$((n + 1))
+test "$ret" -eq 0 || echo_i "failed"
+status=$((status + ret))
+
get_default_algorithm_key_ids_from_sigs() {
zone=$1
diff --git a/doc/arm/reference.rst b/doc/arm/reference.rst
index d51a64a843..115685a3d9 100644
--- a/doc/arm/reference.rst
+++ b/doc/arm/reference.rst
@@ -2625,8 +2625,8 @@ Boolean Options
:rfc:`952` and :rfc:`821` as modified by :rfc:`1123`.
:any:`check-names` applies to the owner names of A, AAAA, and MX records.
- It also applies to the domain names in the RDATA of NS, SOA, MX, and
- SRV records. It further applies to the RDATA of PTR records where the
+ It also applies to the domain names in the RDATA of NS, SOA, MX, SRV,
+ and DELEG records. It further applies to the RDATA of PTR records where the
owner name indicates that it is a reverse lookup of a hostname (the
owner name ends in IN-ADDR.ARPA, IP6.ARPA, or IP6.INT).
@@ -2675,6 +2675,8 @@ Boolean Options
use :iscman:`named-checkzone`). For NS records, only names below top-of-zone
are checked (for out-of-zone names and glue consistency checks, use
:iscman:`named-checkzone`). DS records not at delegations are rejected.
+ DELEG records containing "server-name" or "include-delegparam"
+ parameters which point to names below the zone cut are rejected.
The default is ``yes``.
The use of the SPF record to publish Sender Policy Framework is
diff --git a/lib/dns/clientinfo.c b/lib/dns/clientinfo.c
index 1ea5e7d69d..17913fc758 100644
--- a/lib/dns/clientinfo.c
+++ b/lib/dns/clientinfo.c
@@ -25,10 +25,12 @@ dns_clientinfomethods_init(dns_clientinfomethods_t *methods,
}
void
-dns_clientinfo_init(dns_clientinfo_t *ci, void *data, void *versionp) {
+dns_clientinfo_init(dns_clientinfo_t *ci, void *data, void *versionp,
+ dns_edectx_t *edectx) {
ci->version = DNS_CLIENTINFO_VERSION;
ci->data = data;
ci->dbversion = versionp;
+ ci->edectx = edectx;
dns_ecs_init(&ci->ecs);
}
diff --git a/lib/dns/dnssec.c b/lib/dns/dnssec.c
index 4117145bf5..c8dbdf6ce8 100644
--- a/lib/dns/dnssec.c
+++ b/lib/dns/dnssec.c
@@ -448,7 +448,7 @@ dns_dnssec_verify(const dns_name_t *name, dns_rdataset_t *set, dst_key_t *key,
* NS, SOA and DNSKEY records are signed by their owners.
* NSEC3 records are signed by the apex, exactly one level up
* from their owner names.
- * DS records are signed by the parent zone.
+ * DS and DELEG records are signed by the parent.
*/
switch (set->type) {
case dns_rdatatype_nsec3: {
@@ -473,6 +473,7 @@ dns_dnssec_verify(const dns_name_t *name, dns_rdataset_t *set, dst_key_t *key,
}
break;
case dns_rdatatype_ds:
+ case dns_rdatatype_deleg:
if (dns_name_equal(name, &sig.signer)) {
inc_stat(dns_dnssecstats_fail);
return DNS_R_SIGINVALID;
diff --git a/lib/dns/include/dns/clientinfo.h b/lib/dns/include/dns/clientinfo.h
index f25c4925ce..f2f7fcff0c 100644
--- a/lib/dns/include/dns/clientinfo.h
+++ b/lib/dns/include/dns/clientinfo.h
@@ -43,21 +43,23 @@
#include <isc/types.h>
#include <dns/ecs.h>
+#include <dns/ede.h>
/*****
***** Types
*****/
-#define DNS_CLIENTINFO_VERSION 3
+#define DNS_CLIENTINFO_VERSION 4
/*
* Any updates to this structure should also be applied in
* https://gitlab.isc.org/isc-projects/dlz-modules/-/raw/main/modules/include/dlz_minimal.h
*/
typedef struct dns_clientinfo {
- uint16_t version;
- void *data;
- void *dbversion;
- dns_ecs_t ecs;
+ uint16_t version;
+ void *data;
+ void *dbversion;
+ dns_ecs_t ecs;
+ dns_edectx_t *edectx;
} dns_clientinfo_t;
typedef isc_result_t (*dns_clientinfo_sourceip_t)(dns_clientinfo_t *client,
@@ -84,10 +86,12 @@ dns_clientinfomethods_init(dns_clientinfomethods_t *methods,
dns_clientinfo_sourceip_t sourceip);
void
-dns_clientinfo_init(dns_clientinfo_t *ci, void *data, void *versionp);
+dns_clientinfo_init(dns_clientinfo_t *ci, void *data, void *versionp,
+ dns_edectx_t *edectx);
/*%<
- * Initialize a clientinfo object, setting the data to 'data' and the
- * database version to 'versionp'. ECS data is initialized to 0/0/0.
+ * Initialize a clientinfo object, setting the data to 'data', the
+ * database version to 'versionp', and the EDE context to 'edectx'.
+ * ECS data is initialized to 0/0/0.
*/
void
diff --git a/lib/dns/include/dns/db.h b/lib/dns/include/dns/db.h
index ba9a7b2d7b..0eb0d2fbe8 100644
--- a/lib/dns/include/dns/db.h
+++ b/lib/dns/include/dns/db.h
@@ -291,6 +291,7 @@ enum {
* stale-refresh-time window.
*/
DNS_DBFIND_STALESTART = 1 << 12,
+ DNS_DBFIND_DELEGOK = 1 << 13,
};
/*@}*/
@@ -856,6 +857,11 @@ dns__db_find(dns_db_t *db, const dns_name_t *name, dns_dbversion_t *version,
* pending data will be returned. This option is only meaningful for
* cache databases.
*
+ * \li If 'options' does not have #DNS_DBFIND_DELEGOK set, then only
+ * NS referrals will be returned. This allows different delegation
+ * answers to be sent depending on whether a client has set the
+ * DE (DELEG-OK) bit.
+ *
* \li If the #DNS_DBFIND_NOWILD option is set, then wildcard matching will
* be disabled. This option is only meaningful for zone databases.
*
diff --git a/lib/dns/include/dns/rdata.h b/lib/dns/include/dns/rdata.h
index 71fbac4ab9..0187197ca4 100644
--- a/lib/dns/include/dns/rdata.h
+++ b/lib/dns/include/dns/rdata.h
@@ -143,6 +143,8 @@ enum {
DNS_RDATATYPEATTR_ATCNAME = 1 << 10,
/*% Follow additional */
DNS_RDATATYPEATTR_FOLLOWADDITIONAL = 1 << 11,
+ /*% Delegation type */
+ DNS_RDATATYPEATTR_DELEGATION = 1 << 12,
};
#define DNS_RDATA_INIT \
@@ -642,6 +644,22 @@ dns_rdatatype_atparent(dns_rdatatype_t type) {
0;
}
+/*%
+ * Special case of dns_rdatatype_atparent(), to be used when DELEG might or
+ * might not count as an at-parent type, depending on whether DE=1.
+ *
+ * Requires:
+ * \li 'type' is a valid rdata type.
+ *
+ */
+static inline bool
+dns_rdatatype_atparent_deleg(dns_rdatatype_t type, bool de) {
+ if (type == dns_rdatatype_deleg) {
+ return de;
+ }
+ return dns_rdatatype_atparent(type);
+}
+
/*%
* Return true if adding a record of type 'type' to the ADDITIONAL section
* of a message can itself trigger the addition of still more data to the
@@ -751,6 +769,20 @@ dns_rdatatype_isalias(dns_rdatatype_t type) {
return type == dns_rdatatype_cname || type == dns_rdatatype_dname;
}
+/*%
+ * Return true iff rdata of type 'type' is a delegating type: either
+ * NS or DELEG.
+ *
+ * Requires:
+ * \li 'type' is a valid rdata type.
+ *
+ */
+static inline bool
+dns_rdatatype_isdelegation(dns_rdatatype_t type) {
+ return (dns_rdatatype_attributes(type) &
+ DNS_RDATATYPEATTR_DELEGATION) != 0;
+}
+
/*%
* Return true iff the rdata class 'rdclass' is a meta-class
* like ANY or NONE.
diff --git a/lib/dns/nsec.c b/lib/dns/nsec.c
index 4e211abe20..337f2694e7 100644
--- a/lib/dns/nsec.c
+++ b/lib/dns/nsec.c
@@ -143,8 +143,9 @@ dns_nsec_buildrdata(dns_db_t *db, dns_dbversion_t *version, dns_dbnode_t *node,
/*
* At zone cuts, deny the existence of glue in the parent zone.
*/
- if (dns_nsec_isset(bm, dns_rdatatype_ns) &&
- !dns_nsec_isset(bm, dns_rdatatype_soa))
+ if (dns_nsec_isset(bm, dns_rdatatype_deleg) ||
+ (dns_nsec_isset(bm, dns_rdatatype_ns) &&
+ !dns_nsec_isset(bm, dns_rdatatype_soa)))
{
for (i = 0; i <= max_type; i++) {
if (dns_nsec_isset(bm, i) &&
diff --git a/lib/dns/nsec3.c b/lib/dns/nsec3.c
index 77cfb09c8e..31f8c01080 100644
--- a/lib/dns/nsec3.c
+++ b/lib/dns/nsec3.c
@@ -127,18 +127,20 @@ dns_nsec3_buildrdata(dns_db_t *db, dns_dbversion_t *version, dns_dbnode_t *node,
* Work out if we need to set the RRSIG bit for
* this node. We set the RRSIG bit if either of
* the following conditions are met:
- * 1) We have a SOA or DS then we need to set
- * the RRSIG bit as both always will be signed.
- * 2) We set the RRSIG bit if we don't have
- * a NS record but do have other data.
+ * 1) We have an SOA, DS or DELEG; these will
+ * always be signed.
+ * 2) We don't have an NS, but do have other data.
*/
- if (rdataset.type == dns_rdatatype_soa ||
- rdataset.type == dns_rdatatype_ds)
- {
+ switch (rdataset.type) {
+ case dns_rdatatype_soa:
+ case dns_rdatatype_ds:
+ case dns_rdatatype_deleg:
need_rrsig = true;
- } else if (rdataset.type == dns_rdatatype_ns) {
+ break;
+ case dns_rdatatype_ns:
found_ns = true;
- } else {
+ break;
+ default:
found = true;
}
}
@@ -156,8 +158,9 @@ dns_nsec3_buildrdata(dns_db_t *db, dns_dbversion_t *version, dns_dbnode_t *node,
/*
* At zone cuts, deny the existence of glue in the parent zone.
*/
- if (dns_nsec_isset(bm, dns_rdatatype_ns) &&
- !dns_nsec_isset(bm, dns_rdatatype_soa))
+ if (dns_nsec_isset(bm, dns_rdatatype_deleg) ||
+ (dns_nsec_isset(bm, dns_rdatatype_ns) &&
+ !dns_nsec_isset(bm, dns_rdatatype_soa)))
{
for (i = 0; i <= max_type; i++) {
if (dns_nsec_isset(bm, i) &&
@@ -1934,6 +1937,8 @@ dns__nsec3_noexistnodata(dns_rdatatype_t type, const dns_name_t *name,
(dns_name_empty(closest) ||
dns_name_issubdomain(qname, closest)) &&
!dns_nsec3_typepresent(&rdata, dns_rdatatype_ds) &&
+ !dns_nsec3_typepresent(&rdata,
+ dns_rdatatype_deleg) &&
!dns_nsec3_typepresent(&rdata,
dns_rdatatype_dname) &&
(dns_nsec3_typepresent(&rdata, dns_rdatatype_soa) ||
diff --git a/lib/dns/qpzone.c b/lib/dns/qpzone.c
index 69078fb7a3..b0d6b32f7b 100644
--- a/lib/dns/qpzone.c
+++ b/lib/dns/qpzone.c
@@ -1767,6 +1767,7 @@ qpzone_findrdataset(dns_db_t *db, dns_dbnode_t *dbnode,
static bool
delegating_type(qpzonedb_t *qpdb, qpznode_t *node, dns_typepair_t typepair) {
return typepair == DNS_TYPEPAIR(dns_rdatatype_dname) ||
+ typepair == DNS_TYPEPAIR(dns_rdatatype_deleg) ||
(typepair == DNS_TYPEPAIR(dns_rdatatype_ns) &&
(node != qpdb->origin || IS_STUB(qpdb)));
}
@@ -3394,43 +3395,69 @@ again:
}
static isc_result_t
-qpzone_check_zonecut(qpznode_t *node, void *arg DNS__DB_FLARG) {
+qpzone_check_zonecut(qpznode_t *node, void *arg, bool skipdname DNS__DB_FLARG) {
qpz_search_t *search = arg;
dns_vecheader_t *dname_header = NULL, *sigdname_header = NULL;
+ dns_vecheader_t *deleg_header = NULL, *sigdeleg_header = NULL;
dns_vecheader_t *ns_header = NULL;
- dns_vecheader_t *found = NULL;
+ dns_vecheader_t *found = NULL, *foundsig = NULL;
isc_result_t result = DNS_R_CONTINUE;
isc_rwlocktype_t nlocktype = isc_rwlocktype_none;
isc_rwlock_t *nlock = qpzone_get_lock(node);
+ bool de = (search->options & DNS_DBFIND_DELEGOK) != 0;
NODE_RDLOCK(nlock, &nlocktype);
/*
- * Look for an NS or DNAME rdataset active in our version.
+ * The 'delegating' flag will be set for any node below
+ * the origin that has an NS, DNAME or DELEG record.
+ * If it's not set and not the origin, we don't need to
+ * search. We might need to set the 'wild' flag, though.
+ */
+ if (node != search->qpdb->origin && !node->delegating) {
+ if (node->wild && (search->options & DNS_DBFIND_NOWILD) == 0) {
+ search->wild = true;
+ }
+ CLEANUP(result);
+ }
+
+ /*
+ * Look for an NS, DELEG or DNAME rdataset active in our version.
*/
ISC_SLIST_FOREACH(top, node->next_type, next_type) {
- if (top->typepair == DNS_TYPEPAIR(dns_rdatatype_ns) ||
- top->typepair == DNS_TYPEPAIR(dns_rdatatype_dname) ||
- top->typepair == DNS_SIGTYPEPAIR(dns_rdatatype_dname))
- {
+ bool rrsig = (DNS_TYPEPAIR_TYPE(top->typepair) ==
+ dns_rdatatype_rrsig);
+ bool ns = (top->typepair == DNS_TYPEPAIR(dns_rdatatype_ns));
+ bool deleg =
+ (top->typepair == DNS_TYPEPAIR(dns_rdatatype_deleg)) ||
+ (top->typepair == DNS_SIGTYPEPAIR(dns_rdatatype_deleg));
+ bool dname =
+ (top->typepair == DNS_TYPEPAIR(dns_rdatatype_dname)) ||
+ (top->typepair == DNS_SIGTYPEPAIR(dns_rdatatype_dname));
+
+ if (ns || deleg || (!skipdname && dname)) {
dns_vecheader_t *header =
first_existing_header(top, search->serial);
if (header != NULL) {
- if (top->typepair ==
- DNS_TYPEPAIR(dns_rdatatype_dname))
- {
- dname_header = header;
- } else if (top->typepair ==
- DNS_SIGTYPEPAIR(dns_rdatatype_dname))
- {
+ if (dname && rrsig) {
sigdname_header = header;
- } else if (node != search->qpdb->origin ||
- IS_STUB(search->qpdb))
+ } else if (dname) {
+ dname_header = header;
+ } else if (node == search->qpdb->origin &&
+ !IS_STUB(search->qpdb))
{
/*
- * We've found an NS rdataset that
- * isn't at the origin node.
+ * If we're at the origin node
+ * then we're not interested in
+ * the NS, and DELEG isn't allowed,
+ * so we can skip those.
*/
+ continue;
+ } else if (deleg && rrsig) {
+ sigdeleg_header = header;
+ } else if (deleg) {
+ deleg_header = header;
+ } else if (ns) {
ns_header = header;
}
}
@@ -3439,20 +3466,38 @@ qpzone_check_zonecut(qpznode_t *node, void *arg DNS__DB_FLARG) {
/*
* Did we find anything?
+ *
+ * Note: delegations have precedence over DNAME in normal zones,
+ * but the reverse is true in stub zones. DELEG has
+ * precedence over NS if both exist and DE=1; NS has
+ * precedence over DELEG if both exist and DE=0.
*/
- if (!IS_STUB(search->qpdb) && ns_header != NULL) {
- /*
- * Note that NS has precedence over DNAME if both exist
- * in a zone. Otherwise DNAME take precedence over NS.
- */
- found = ns_header;
- search->zonecut_sigheader = NULL;
- } else if (dname_header != NULL) {
- found = dname_header;
- search->zonecut_sigheader = sigdname_header;
- } else if (ns_header != NULL) {
- found = ns_header;
- search->zonecut_sigheader = NULL;
+ if (!IS_STUB(search->qpdb)) {
+ if (de && deleg_header != NULL) {
+ found = deleg_header;
+ foundsig = sigdeleg_header;
+ } else if (ns_header != NULL) {
+ found = ns_header;
+ } else if (deleg_header != NULL) {
+ found = deleg_header;
+ foundsig = sigdeleg_header;
+ } else if (dname_header != NULL) {
+ found = dname_header;
+ foundsig = sigdname_header;
+ }
+ } else {
+ if (dname_header != NULL) {
+ found = dname_header;
+ foundsig = sigdname_header;
+ } else if (de && deleg_header != NULL) {
+ found = deleg_header;
+ foundsig = sigdeleg_header;
+ } else if (ns_header != NULL) {
+ found = ns_header;
+ } else if (deleg_header != NULL) {
+ found = deleg_header;
+ foundsig = sigdeleg_header;
+ }
}
if (found != NULL) {
@@ -3462,7 +3507,16 @@ qpzone_check_zonecut(qpznode_t *node, void *arg DNS__DB_FLARG) {
*/
qpznode_acquire(node DNS__DB_FLARG_PASS);
search->zonecut = node;
- search->zonecut_header = found;
+
+ /*
+ * We set the zonecut node but not the header if this
+ * is a DELEG-only delegation and DE=0.
+ */
+ if (de || found != deleg_header) {
+ search->zonecut_header = found;
+ search->zonecut_sigheader = foundsig;
+ }
+
/*
* Since we've found a zonecut, anything beneath it is
* glue and is not subject to wildcard matching, so we
@@ -3503,6 +3557,7 @@ qpzone_check_zonecut(qpznode_t *node, void *arg DNS__DB_FLARG) {
}
}
+cleanup:
NODE_UNLOCK(nlock, &nlocktype);
return result;
@@ -3571,14 +3626,13 @@ qpzone_find(dns_db_t *db, const dns_name_t *name, dns_dbversion_t *version,
dns_rdatatype_t type, unsigned int options,
isc_stdtime_t now ISC_ATTR_UNUSED, dns_name_t *foundname,
dns_clientinfomethods_t *methods ISC_ATTR_UNUSED,
- dns_clientinfo_t *clientinfo ISC_ATTR_UNUSED,
- dns_rdataset_t *rdataset,
+ dns_clientinfo_t *clientinfo, dns_rdataset_t *rdataset,
dns_rdataset_t *sigrdataset DNS__DB_FLARG) {
isc_result_t result;
qpzonedb_t *qpdb = (qpzonedb_t *)db;
qpznode_t *node = NULL;
bool cname_ok = true, close_version = false;
- bool maybe_zonecut = false;
+ bool at_zonecut = false, deleg_only = false;
bool wild = false, empty_node = false;
bool nsec3 = false;
dns_vecheader_t *found = NULL, *nsecheader = NULL;
@@ -3587,6 +3641,8 @@ qpzone_find(dns_db_t *db, const dns_name_t *name, dns_dbversion_t *version,
bool active;
isc_rwlock_t *nlock = NULL;
isc_rwlocktype_t nlocktype = isc_rwlocktype_none;
+ bool de = (options & DNS_DBFIND_DELEGOK) != 0;
+ bool atparent = dns_rdatatype_atparent_deleg(type, de);
REQUIRE(VALID_QPZONE((qpzonedb_t *)db));
INSIST(version == NULL ||
@@ -3632,8 +3688,9 @@ qpzone_find(dns_db_t *db, const dns_name_t *name, dns_dbversion_t *version,
* Check the QP chain to see if there's a node above us with a
* active DNAME or NS rdatasets.
*
- * We're only interested in nodes above QNAME, so if the result
- * was success, then we skip the last item in the chain.
+ * At the moment we're only interested in nodes above QNAME, so if
+ * the result from dns_qp_lookup() was success, then we skip the
+ * last item in the chain.
*/
unsigned int clen = dns_qpchain_length(&search.chain);
if (result == ISC_R_SUCCESS) {
@@ -3646,7 +3703,8 @@ qpzone_find(dns_db_t *db, const dns_name_t *name, dns_dbversion_t *version,
isc_result_t tresult;
dns_qpchain_node(&search.chain, i, (void **)&n, NULL);
- tresult = qpzone_check_zonecut(n, &search DNS__DB_FLARG_PASS);
+ tresult = qpzone_check_zonecut(n, &search,
+ false DNS__DB_FLARG_PASS);
if (tresult != DNS_R_CONTINUE) {
result = tresult;
dns_name_copy(&n->name, foundname);
@@ -3654,9 +3712,32 @@ qpzone_find(dns_db_t *db, const dns_name_t *name, dns_dbversion_t *version,
}
}
- if (result == DNS_R_PARTIALMATCH) {
+ /*
+ * If we're beneath a zone cut, we don't want to look for
+ * CNAMEs because they're not legitimate zone glue. And
+ * some DNSSEC types (KEY and NSEC) are not subject to CNAME
+ * matching (see RFC4035, section 2.5, and RFC3007).
+ */
+ if (search.zonecut != NULL || type == dns_rdatatype_key ||
+ type == dns_rdatatype_nsec)
+ {
+ cname_ok = false;
+ }
+
+ if (result == ISC_R_SUCCESS && search.zonecut == NULL) {
+ /*
+ * We found the node we were looking for, and there aren't
+ * any zone cuts higher in the tree, but we need to call
+ * qpzone_check_zonecut() on the QNAME node too, to
+ * populate search.zonecut, .zonecut_header and
+ * .zonecut_sigheader in case we need to return a
+ * referral.
+ */
+ qpzone_check_zonecut(node, &search, true DNS__DB_FLARG_PASS);
+ } else if (result == DNS_R_PARTIALMATCH) {
+ /* No such name, or it exists but has a zone cut above it. */
partial_match:
- if (search.zonecut != NULL) {
+ if (search.zonecut_header != NULL) {
result = qpzone_setup_delegation(
&search, foundname, rdataset,
sigrdataset DNS__DB_FLARG_PASS);
@@ -3718,32 +3799,25 @@ found:
* We have found a node whose name is the desired name, or we
* have matched a wildcard.
*/
-
nlock = qpzone_get_lock(node);
NODE_RDLOCK(nlock, &nlocktype);
- if (search.zonecut != NULL) {
- /*
- * If we're beneath a zone cut, we don't want to look for
- * CNAMEs because they're not legitimate zone glue.
- */
- cname_ok = false;
- } else {
+ if (search.zonecut == node) {
/*
- * The node may be a zone cut itself. If it might be one,
- * make sure we check for it later.
- *
- * DS records live above the zone cut in ordinary zone so
- * we want to ignore any referral.
- *
- * Stub zones don't have anything "above" the delegation so
- * we always return a referral.
+ * This node contains a delegation type (NS or DELEG),
+ * so it might be a zone cut. If this isn't the zone origin
+ * (where NS is authoritative, and DELEG is forbidden), or
+ * if this is a stub zone, then it *is* a zone cut.
*/
- if (node->delegating && ((node != search.qpdb->origin &&
- !dns_rdatatype_atparent(type)) ||
- IS_STUB(search.qpdb)))
- {
- maybe_zonecut = true;
+ if (node != search.qpdb->origin || IS_STUB(search.qpdb)) {
+ at_zonecut = true;
+ /*
+ * If the delegation record is DELEG-only, with no
+ * NS, we might need to handle it specially later.
+ */
+ if (search.zonecut_header == NULL) {
+ deleg_only = true;
+ }
}
}
@@ -3761,147 +3835,179 @@ found:
/*
* We now go looking for rdata...
*/
-
sigpair = DNS_SIGTYPEPAIR(type);
empty_node = true;
ISC_SLIST_FOREACH(top, node->next_type, next_type) {
+ bool keep_looking;
+
/*
* Look for an active, extant rdataset.
*/
dns_vecheader_t *header = first_existing_header(top,
search.serial);
- if (header != NULL) {
+ if (header == NULL) {
+ continue;
+ }
+
+ /*
+ * We now know that there is at least one active rdataset
+ * at this node.
+ */
+ empty_node = false;
+
+ if (!at_zonecut || atparent) {
/*
- * We now know that there is at least one active
- * rdataset at this node.
+ * Either this isn't a zone cut or we're looking
+ * for a parent-side type. Keep looking for an
+ * answer.
*/
- empty_node = false;
-
+ keep_looking = true;
+ } else {
/*
- * Do special zone cut handling, if requested.
+ * Zone cut. There are several special cases we
+ * have to handle, depending on query type and
+ * whether the zone is delegated with NS, DELEG, or
+ * both:
+ *
+ * QTYPE NSEC, or the GLUEOK flag is set:
+ * - look for the answer as if this were not
+ * a zone cut.
+ *
+ * QTYPE DELEG:
+ * - if NS is present and DE=0: return NS referral
+ * - otherwise, look for the answer
+ *
+ * any other QTYPE:
+ * - if NS is not present but DELEG is, and
+ * DE=0: return NXRRSET
+ * - if NS is present, and either DELEG is not
+ * present *or* DE=0: return NS referral
+ * - if DELEG is present and DE=1: return
+ * DELEG referral
*/
- if (maybe_zonecut &&
- top->typepair == DNS_TYPEPAIR(dns_rdatatype_ns))
+ if (type == dns_rdatatype_nsec ||
+ (options & DNS_DBFIND_GLUEOK) != 0)
{
- /*
- * We increment the reference count on node to
- * ensure that search->zonecut_header will
- * still be valid later.
- */
- qpznode_acquire(node DNS__DB_FLARG_PASS);
- search.zonecut = node;
- search.zonecut_header = header;
- search.zonecut_sigheader = NULL;
- maybe_zonecut = false;
-
- if ((search.options & DNS_DBFIND_GLUEOK) == 0 &&
- type != dns_rdatatype_nsec)
- {
- /*
- * Glue is not OK, but any answer we
- * could return would be glue. Return
- * the delegation.
- */
- found = NULL;
+ keep_looking = true;
+ } else if (type == dns_rdatatype_deleg) {
+ if (!de && !deleg_only) {
+ /* triggers a referral */
break;
}
- if (found != NULL && foundsig != NULL) {
- break;
+ keep_looking = true;
+ } else if (deleg_only && !de) {
+ if (clientinfo != NULL &&
+ clientinfo->edectx != NULL)
+ {
+ dns_ede_add(clientinfo->edectx,
+ DNS_EDE_DELEGONLY, NULL);
}
+ keep_looking = false;
+ } else {
+ /* triggers a referral */
+ break;
}
+ }
+ /*
+ * If the NSEC3 record doesn't match the chain
+ * we are using, behave as if it isn't here.
+ */
+ if (top->typepair == DNS_TYPEPAIR(dns_rdatatype_nsec3) &&
+ !matchparams(header, &search))
+ {
+ NODE_UNLOCK(nlock, &nlocktype);
+ goto partial_match;
+ }
+
+ /*
+ * For an ANY query, the caller will be iterating the
+ * node, so it doesn't matter which header we find, we
+ * just need one.
+ */
+ if (type == dns_rdatatype_any) {
+ found = header;
+ break;
+ }
+
+ /*
+ * If we found a type we were looking for, remember it.
+ */
+ if (keep_looking &&
+ (top->typepair == type ||
+ (top->typepair == DNS_TYPEPAIR(dns_rdatatype_cname) &&
+ cname_ok)))
+ {
/*
- * If the NSEC3 record doesn't match the chain
- * we are using behave as if it isn't here.
+ * We've found the answer!
*/
+ found = header;
if (top->typepair ==
- DNS_TYPEPAIR(dns_rdatatype_nsec3) &&
- !matchparams(header, &search))
- {
- NODE_UNLOCK(nlock, &nlocktype);
- goto partial_match;
- }
- /*
- * If we found a type we were looking for,
- * remember it.
- */
- if (top->typepair == type ||
- type == dns_rdatatype_any ||
- (top->typepair ==
- DNS_TYPEPAIR(dns_rdatatype_cname) &&
- cname_ok))
+ DNS_TYPEPAIR(dns_rdatatype_cname) &&
+ cname_ok)
{
/*
- * We've found the answer!
- */
- found = header;
- if (top->typepair ==
- DNS_TYPEPAIR(dns_rdatatype_cname) &&
- cname_ok)
- {
- /*
- * We may be finding a CNAME instead
- * of the desired type.
- *
- * If we've already got the CNAME RRSIG,
- * use it, otherwise change sigtype
- * so that we find it.
- */
- if (cnamesig != NULL) {
- foundsig = cnamesig;
- } else {
- sigpair = DNS_SIGTYPEPAIR(
- dns_rdatatype_cname);
- }
- }
- /*
- * If we've got all we need, end the search.
+ * We may be finding a CNAME instead
+ * of the desired type.
+ *
+ * If we've already got the CNAME RRSIG,
+ * use it, otherwise change sigtype
+ * so that we find it.
*/
- if (!maybe_zonecut && foundsig != NULL) {
- break;
+ if (cnamesig != NULL) {
+ foundsig = cnamesig;
+ } else {
+ sigpair = DNS_SIGTYPEPAIR(
+ dns_rdatatype_cname);
}
- } else if (top->typepair == sigpair) {
- /*
- * We've found the RRSIG rdataset for our
- * target type. Remember it.
- */
- foundsig = header;
- /*
- * If we've got all we need, end the search.
- */
- if (!maybe_zonecut && found != NULL) {
- break;
- }
- } else if (top->typepair ==
- DNS_TYPEPAIR(dns_rdatatype_nsec) &&
- !search.version->havensec3)
- {
- /*
- * Remember a NSEC rdataset even if we're
- * not specifically looking for it, because
- * we might need it later.
- */
- nsecheader = header;
- } else if (top->typepair ==
- DNS_SIGTYPEPAIR(
- dns_rdatatype_nsec) &&
- !search.version->havensec3)
- {
- /*
- * If we need the NSEC rdataset, we'll also
- * need its signature.
- */
- nsecsig = header;
- } else if (cname_ok &&
- top->typepair ==
- DNS_SIGTYPEPAIR(dns_rdatatype_cname))
- {
- /*
- * If we get a CNAME match, we'll also need
- * its signature.
- */
- cnamesig = header;
}
+
+ /*
+ * If we've got all we need, end the search.
+ */
+ if (foundsig != NULL) {
+ break;
+ }
+ } else if (keep_looking && top->typepair == sigpair) {
+ /*
+ * We've found the RRSIG rdataset for our
+ * target type. Remember it.
+ */
+ foundsig = header;
+
+ /*
+ * If we've got all we need, end the search.
+ */
+ if (found != NULL) {
+ break;
+ }
+ } else if (top->typepair == DNS_TYPEPAIR(dns_rdatatype_nsec) &&
+ !search.version->havensec3)
+ {
+ /*
+ * Remember a NSEC rdataset even if we're
+ * not specifically looking for it, because
+ * we might need it later.
+ */
+ nsecheader = header;
+ } else if (top->typepair ==
+ DNS_SIGTYPEPAIR(dns_rdatatype_nsec) &&
+ !search.version->havensec3)
+ {
+ /*
+ * If we need the NSEC rdataset, we'll also
+ * need its signature.
+ */
+ nsecsig = header;
+ } else if (keep_looking && cname_ok &&
+ top->typepair ==
+ DNS_SIGTYPEPAIR(dns_rdatatype_cname))
+ {
+ /*
+ * If we get a CNAME match, we'll also need
+ * its signature.
+ */
+ cnamesig = header;
}
}
@@ -3930,9 +4036,11 @@ found:
* If we didn't find what we were looking for...
*/
if (found == NULL) {
- if (search.zonecut != NULL) {
+ if (search.zonecut_header != NULL &&
+ ((at_zonecut && !atparent) || search.zonecut != node))
+ {
/*
- * We were trying to find glue at a node beneath a
+ * We were trying to find glue at or below a
* zone cut, but didn't.
*
* Return the delegation.
@@ -3946,6 +4054,7 @@ found:
sigrdataset DNS__DB_FLARG_PASS);
goto tree_exit;
}
+
/*
* The desired type doesn't exist.
*/
@@ -3998,6 +4107,12 @@ found:
* that result to the caller.
*/
result = DNS_R_CNAME;
+ } else if (search.zonecut != NULL && search.zonecut != node) {
+ /* If we're below a zone cut, the result is glue. */
+ result = DNS_R_GLUE;
+ } else if (search.zonecut != NULL && at_zonecut && atparent) {
+ /* We're at a zone cut, but looking for a parent-side type */
+ result = ISC_R_SUCCESS;
} else if (search.zonecut != NULL) {
/*
* If we're beneath a zone cut, we must indicate that the
@@ -4005,7 +4120,9 @@ found:
* and the type is NSEC.
*/
if (search.zonecut == node) {
- if (dns_rdatatype_isnsec(type)) {
+ if (dns_rdatatype_isnsec(type) ||
+ type == dns_rdatatype_deleg)
+ {
result = ISC_R_SUCCESS;
} else if (type == dns_rdatatype_any) {
result = DNS_R_ZONECUT;
@@ -5018,7 +5135,7 @@ qpzone_addrdataset_inner(qpzonedb_t *qpdb, qpznode_t *node,
}
/*
- * If we're adding a delegation type (e.g. NS or DNAME),
+ * If we're adding a delegation type (e.g. NS, DNAME or DELEG),
* then we need to set the callback bit on the node.
*/
if (result == ISC_R_SUCCESS &&
diff --git a/lib/dns/rdata/generic/ns_2.c b/lib/dns/rdata/generic/ns_2.c
index 856a6bbe39..28262ad3e1 100644
--- a/lib/dns/rdata/generic/ns_2.c
+++ b/lib/dns/rdata/generic/ns_2.c
@@ -14,7 +14,8 @@
#ifndef RDATA_GENERIC_NS_2_C
#define RDATA_GENERIC_NS_2_C
-#define RRTYPE_NS_ATTRIBUTES (DNS_RDATATYPEATTR_ZONECUTAUTH)
+#define RRTYPE_NS_ATTRIBUTES \
+ (DNS_RDATATYPEATTR_ZONECUTAUTH | DNS_RDATATYPEATTR_DELEGATION)
static isc_result_t
fromtext_ns(ARGS_FROMTEXT) {
diff --git a/lib/dns/rdata/in_1/deleg_61440.c b/lib/dns/rdata/in_1/deleg_61440.c
index 14d6d1c03b..e079c847fb 100644
--- a/lib/dns/rdata/in_1/deleg_61440.c
+++ b/lib/dns/rdata/in_1/deleg_61440.c
@@ -19,7 +19,7 @@
#define RRTYPE_DELEG_ATTRIBUTES \
(DNS_RDATATYPEATTR_FOLLOWADDITIONAL | DNS_RDATATYPEATTR_ATPARENT | \
- DNS_RDATATYPEATTR_ZONECUTAUTH)
+ DNS_RDATATYPEATTR_ZONECUTAUTH | DNS_RDATATYPEATTR_DELEGATION)
/*
* Delegation Information Registry
@@ -210,9 +210,9 @@ finish:
}
static isc_result_t
-servname_fromtxt(isc_textregion_t *region, const dns_name_t *origin,
- unsigned int options, isc_lex_t *lexer,
- dns_rdatacallbacks_t *callbacks, isc_buffer_t *target) {
+servname_fromtext(isc_textregion_t *region, const dns_name_t *origin,
+ unsigned int options, isc_lex_t *lexer,
+ dns_rdatacallbacks_t *callbacks, isc_buffer_t *target) {
isc_textregion_t src = *region;
if (origin == NULL) {
@@ -319,8 +319,8 @@ deleg_fromtext(isc_textregion_t *region, const dns_name_t *origin,
} while (e != NULL);
break;
case deleg_namelist:
- RETERR(servname_fromtxt(region, origin, options, lexer,
- callbacks, target));
+ RETERR(servname_fromtext(region, origin, options, lexer,
+ callbacks, target));
break;
case deleg_keylist:
if (region->length == 0) {
@@ -918,7 +918,9 @@ generic_checknames_in_deleg(ARGS_CHECKNAMES) {
if (!dns_name_ishostname(&name, false)) {
return false;
}
- if (dns_name_issubdomain(&name, owner)) {
+ if (rdata->type == dns_rdatatype_deleg &&
+ dns_name_issubdomain(&name, owner))
+ {
return false;
}
}
diff --git a/lib/dns/resolver.c b/lib/dns/resolver.c
index 89070b0586..5f91c23153 100644
--- a/lib/dns/resolver.c
+++ b/lib/dns/resolver.c
@@ -8061,7 +8061,7 @@ resquery_response_continue(void *arg, isc_result_t result) {
result = rctx_answer_none(rctx);
switch (result) {
case ISC_R_SUCCESS:
- case DNS_R_CHASEDSSERVERS:
+ case DNS_R_CHASEPARENTSERVERS:
break;
case DNS_R_DELEGATION:
/*
@@ -9167,15 +9167,16 @@ rctx_answer_none(respctx_t *rctx) {
}
/*
- * Trigger lookups for DNS nameservers.
+ * Trigger lookups for DNS nameservers for types for
+ * which the parent zone is authoritative (DS, DELEG).
*/
if (rctx->negative &&
rctx->query->rmessage->rcode == dns_rcode_noerror &&
- fctx->type == dns_rdatatype_ds && rctx->soa_name != NULL &&
+ dns_rdatatype_atparent(fctx->type) && rctx->soa_name != NULL &&
dns_name_equal(rctx->soa_name, fctx->name) &&
!dns_name_isroot(fctx->name))
{
- return DNS_R_CHASEDSSERVERS;
+ return DNS_R_CHASEPARENTSERVERS;
}
/*
@@ -9870,7 +9871,7 @@ rctx_done(respctx_t *rctx, isc_result_t result) {
} else if (rctx->resend) {
UNLOCK(&fctx->lock);
rctx_resend(rctx, addrinfo);
- } else if (result == DNS_R_CHASEDSSERVERS) {
+ } else if (result == DNS_R_CHASEPARENTSERVERS) {
UNLOCK(&fctx->lock);
rctx_chaseds(rctx, message, addrinfo, result);
} else if (result == ISC_R_SUCCESS && !HAVE_ANSWER(fctx)) {
diff --git a/lib/dns/update.c b/lib/dns/update.c
index 96617ede86..9e17819ae1 100644
--- a/lib/dns/update.c
+++ b/lib/dns/update.c
@@ -376,7 +376,8 @@ rrset_visible(dns_db_t *db, dns_dbversion_t *ver, dns_name_t *name,
dns_fixedname_t fixed;
dns_fixedname_init(&fixed);
- result = dns_db_find(db, name, ver, type, DNS_DBFIND_NOWILD,
+ result = dns_db_find(db, name, ver, type,
+ DNS_DBFIND_NOWILD | DNS_DBFIND_DELEGOK,
(isc_stdtime_t)0, dns_name(&fixed), NULL, NULL);
switch (result) {
case ISC_R_SUCCESS:
@@ -658,10 +659,10 @@ is_active(dns_db_t *db, dns_dbversion_t *ver, dns_name_t *name, bool *flag,
isc_result_t result;
dns_fixedname_t foundname;
dns_fixedname_init(&foundname);
- result = dns_db_find(db, name, ver, dns_rdatatype_any,
- DNS_DBFIND_GLUEOK | DNS_DBFIND_NOWILD,
- (isc_stdtime_t)0, dns_name(&foundname), NULL,
- NULL);
+ result = dns_db_find(
+ db, name, ver, dns_rdatatype_any,
+ DNS_DBFIND_GLUEOK | DNS_DBFIND_DELEGOK | DNS_DBFIND_NOWILD,
+ (isc_stdtime_t)0, dns_name(&foundname), NULL, NULL);
if (result == ISC_R_SUCCESS || result == DNS_R_EMPTYNAME) {
*flag = true;
*cut = false;
@@ -1215,8 +1216,8 @@ add_exposed_sigs(dns_update_log_t *log, dns_zone_t *zone, dns_db_t *db,
dns_rdataset_disassociate(&rdataset);
/*
- * We don't need to sign unsigned NSEC records at the cut
- * as they are handled elsewhere.
+ * We don't need to sign unsigned DS records at the
+ * zone cut as they are handled elsewhere.
*/
if ((type == dns_rdatatype_rrsig) ||
(cut && type != dns_rdatatype_ds))
@@ -1594,12 +1595,12 @@ next_state:
/*
* Find names potentially affected by delegation changes
- * (obscured by adding an NS or DNAME, or unobscured by
- * removing one).
+ * (obscured by adding an NS, DNAME or DELEG, or
+ * unobscured by removing one).
*/
ISC_LIST_FOREACH(state->diffnames.tuples, t, link) {
- bool ns_existed, dname_existed;
- bool ns_exists, dname_exists;
+ bool ns_existed, dname_existed, deleg_existed;
+ bool ns_exists, dname_exists, deleg_exists;
if (oldver != NULL) {
CHECK(rrset_exists(db, oldver, &t->name,
@@ -1615,13 +1616,23 @@ next_state:
} else {
dname_existed = false;
}
+ if (oldver != NULL) {
+ CHECK(rrset_exists(db, oldver, &t->name,
+ dns_rdatatype_deleg, 0,
+ &deleg_existed));
+ } else {
+ deleg_existed = false;
+ }
CHECK(rrset_exists(db, newver, &t->name,
dns_rdatatype_ns, 0, &ns_exists));
CHECK(rrset_exists(db, newver, &t->name,
dns_rdatatype_dname, 0,
&dname_exists));
- if ((ns_exists || dname_exists) ==
- (ns_existed || dname_existed))
+ CHECK(rrset_exists(db, newver, &t->name,
+ dns_rdatatype_deleg, 0,
+ &deleg_exists));
+ if ((ns_exists || dname_exists || deleg_exists) ==
+ (ns_existed || dname_existed || deleg_existed))
{
continue;
}
@@ -1807,15 +1818,15 @@ next_state:
/*
* Find names potentially affected by delegation changes
- * (obscured by adding an NS or DNAME, or unobscured by
- * removing one).
+ * (obscured by adding an NS, DNAME or DELEG, or
+ * unobscured by removing one).
*/
tuple = ISC_LIST_HEAD(diff->tuples);
while (tuple != NULL) {
dns_name_t *name = &tuple->name;
- bool ns_existed, dname_existed;
- bool ns_exists, dname_exists;
+ bool ns_existed, dname_existed, deleg_existed;
+ bool ns_exists, dname_exists, deleg_exists;
bool exists, existed;
if (tuple->rdata.type == dns_rdatatype_nsec ||
@@ -1841,14 +1852,24 @@ next_state:
} else {
dname_existed = false;
}
+ if (oldver != NULL) {
+ CHECK(rrset_exists(db, oldver, name,
+ dns_rdatatype_deleg, 0,
+ &deleg_existed));
+ } else {
+ deleg_existed = false;
+ }
CHECK(rrset_exists(db, newver, name, dns_rdatatype_ns,
0, &ns_exists));
CHECK(rrset_exists(db, newver, name,
dns_rdatatype_dname, 0,
&dname_exists));
+ CHECK(rrset_exists(db, newver, name,
+ dns_rdatatype_deleg, 0,
+ &deleg_exists));
- exists = ns_exists || dname_exists;
- existed = ns_existed || dname_existed;
+ exists = ns_exists || dname_exists || deleg_exists;
+ existed = ns_existed || dname_existed || deleg_existed;
if (exists == existed) {
goto nextname;
}
diff --git a/lib/dns/zone.c b/lib/dns/zone.c
index e88cbca5f3..f24df3642f 100644
--- a/lib/dns/zone.c
+++ b/lib/dns/zone.c
@@ -2286,33 +2286,30 @@ static bool
integrity_checks(dns_zone_t *zone, dns_db_t *db) {
dns_dbiterator_t *dbiterator = NULL;
dns_dbnode_t *node = NULL;
- dns_rdataset_t rdataset;
+ dns_rdataset_t rdataset = DNS_RDATASET_INIT;
dns_fixedname_t fixed;
dns_fixedname_t fixedbottom;
+ dns_name_t *name = dns_fixedname_initname(&fixed);
+ dns_name_t *bottom = dns_fixedname_initname(&fixedbottom);
dns_rdata_mx_t mx;
dns_rdata_ns_t ns;
dns_rdata_in_srv_t srv;
- dns_name_t *name;
- dns_name_t *bottom;
isc_result_t result;
- bool ok = true, have_spf, have_txt;
- bool has_a = false;
- bool has_aaaa = false;
- int level;
+ bool ok = true;
+ bool has_a = false, has_aaaa = false;
char namebuf[DNS_NAME_FORMATSIZE];
bool logged_algorithm[DST_MAX_ALGS];
bool logged_digest_type[DNS_DSDIGEST_MAX + 1];
- name = dns_fixedname_initname(&fixed);
- bottom = dns_fixedname_initname(&fixedbottom);
- dns_rdataset_init(&rdataset);
-
result = dns_db_createiterator(db, 0, &dbiterator);
if (result != ISC_R_SUCCESS) {
return true;
}
DNS_DBITERATOR_FOREACH(dbiterator) {
+ bool have_ns = false, have_deleg = false;
+ bool have_spf = false, have_txt = false;
+
CHECK(dns_dbiterator_current(dbiterator, &node, name));
/*
@@ -2374,23 +2371,25 @@ integrity_checks(dns_zone_t *zone, dns_db_t *db) {
checkforns:
/*
- * Don't check the NS records at the origin.
+ * Skip checking NS records at the apex.
*/
if (dns_name_equal(name, &zone->origin)) {
- goto checkfords;
+ goto checkfordeleg;
}
result = dns_db_findrdataset(db, node, NULL, dns_rdatatype_ns,
0, 0, &rdataset, NULL);
if (result != ISC_R_SUCCESS) {
- goto checkfords;
+ goto checkfordeleg;
}
/*
* Remember bottom of zone due to NS.
*/
dns_name_copy(name, bottom);
+ have_ns = true;
+ /* Check glue correctness */
DNS_RDATASET_FOREACH(&rdataset) {
dns_rdata_t rdata = DNS_RDATA_INIT;
dns_rdataset_current(&rdataset, &rdata);
@@ -2405,15 +2404,85 @@ integrity_checks(dns_zone_t *zone, dns_db_t *db) {
}
dns_rdataset_disassociate(&rdataset);
+ checkfordeleg:
+ result = dns_db_findrdataset(db, node, NULL,
+ dns_rdatatype_deleg, 0, 0,
+ &rdataset, NULL);
+ if (result != ISC_R_SUCCESS) {
+ goto checkfords;
+ }
+
+ /* DELEG is not allowed at the apex and is class-IN only. */
+ if (dns_name_equal(name, &zone->origin)) {
+ dns_name_format(name, namebuf, sizeof(namebuf));
+ dns_zone_log(zone, ISC_LOG_ERROR,
+ "DELEG at zone apex (%s)", namebuf);
+ dns_rdataset_disassociate(&rdataset);
+ ok = false;
+ goto checkfords;
+ } else if (zone->rdclass != dns_rdataclass_in) {
+ dns_zone_log(zone, ISC_LOG_ERROR, "DELEG: bad class");
+ ok = false;
+ goto checkfords;
+ }
+
+ /*
+ * check the DELEG rrset for server-name and
+ * include-delegparam paramters, and reject if they're
+ * below the owner name.
+ */
+ DNS_RDATASET_FOREACH(&rdataset) {
+ dns_rdata_t rdata = DNS_RDATA_INIT;
+ dns_rdataset_current(&rdataset, &rdata);
+ dns_fixedname_t fbad;
+ dns_name_t *bad = dns_fixedname_initname(&fbad);
+
+ if (!dns_rdata_checknames(&rdata, name, bad)) {
+ dns_name_format(bad, namebuf, sizeof(namebuf));
+ dns_zone_log(zone, ISC_LOG_ERROR,
+ "DELEG: invalid target name '%s'",
+ namebuf);
+ ok = false;
+ dns_rdataset_disassociate(&rdataset);
+ goto checkfords;
+ }
+ }
+
+ /*
+ * Remember bottom of zone due to DELEG.
+ */
+ dns_name_copy(name, bottom);
+ have_deleg = true;
+ dns_rdataset_disassociate(&rdataset);
+
+ checkfords:
/*
- * Check for deprecated DS digest types.
+ * DS is only allowed at a delegation point (i.e.,
+ * coexisting with NS or DELEG and not at the apex).
*/
result = dns_db_findrdataset(db, node, NULL, dns_rdatatype_ds,
0, 0, &rdataset, NULL);
if (result != ISC_R_SUCCESS) {
- goto next;
+ goto checkfordname;
+ }
+
+ if (!have_ns && !have_deleg) {
+ int level = ISC_LOG_WARNING;
+ if (zone->type == dns_zone_primary) {
+ level = ISC_LOG_ERROR;
+ ok = false;
+ }
+ dns_name_format(name, namebuf, sizeof(namebuf));
+ dns_zone_log(zone, level,
+ "DS not at delegation point (%s)",
+ namebuf);
+ dns_rdataset_disassociate(&rdataset);
+ goto checkfordname;
}
+ /*
+ * Check for deprecated DS digest types.
+ */
memset(logged_algorithm, 0, sizeof(logged_algorithm));
memset(logged_digest_type, 0, sizeof(logged_digest_type));
DNS_RDATASET_FOREACH(&rdataset) {
@@ -2469,38 +2538,21 @@ integrity_checks(dns_zone_t *zone, dns_db_t *db) {
}
dns_rdataset_disassociate(&rdataset);
- goto next;
-
- checkfords:
- result = dns_db_findrdataset(db, node, NULL, dns_rdatatype_ds,
- 0, 0, &rdataset, NULL);
- if (result != ISC_R_SUCCESS) {
- goto checkfordname;
- }
- dns_rdataset_disassociate(&rdataset);
-
- if (zone->type == dns_zone_primary) {
- level = ISC_LOG_ERROR;
- ok = false;
- } else {
- level = ISC_LOG_WARNING;
- }
- dns_name_format(name, namebuf, sizeof(namebuf));
- dns_zone_log(zone, level, "DS not at delegation point (%s)",
- namebuf);
-
checkfordname:
result = dns_db_findrdataset(db, node, NULL,
dns_rdatatype_dname, 0, 0,
&rdataset, NULL);
- if (result == ISC_R_SUCCESS) {
- /*
- * Remember bottom of zone due to DNAME.
- */
- dns_name_copy(name, bottom);
- dns_rdataset_disassociate(&rdataset);
+ if (result != ISC_R_SUCCESS) {
+ goto checkformx;
}
+ /*
+ * Remember bottom of zone due to DNAME.
+ */
+ dns_name_copy(name, bottom);
+ dns_rdataset_disassociate(&rdataset);
+
+ checkformx:
result = dns_db_findrdataset(db, node, NULL, dns_rdatatype_mx,
0, 0, &rdataset, NULL);
if (result != ISC_R_SUCCESS) {
@@ -2520,6 +2572,7 @@ integrity_checks(dns_zone_t *zone, dns_db_t *db) {
checksrv:
if (zone->rdclass != dns_rdataclass_in) {
+ /* All the remaining tests are for class IN only */
goto next;
}
result = dns_db_findrdataset(db, node, NULL, dns_rdatatype_srv,
@@ -2541,7 +2594,8 @@ integrity_checks(dns_zone_t *zone, dns_db_t *db) {
checkforaaaa:
/*
- * Check if there is an A or AAAA RRset in the zone.
+ * Check if there is an A or AAAA RRset anywhere in
+ * the zone.
*/
if (!has_a) {
result = dns_db_findrdataset(db, node, NULL,
@@ -2572,7 +2626,7 @@ integrity_checks(dns_zone_t *zone, dns_db_t *db) {
if (zone->rdclass != dns_rdataclass_in) {
goto next;
}
- have_spf = have_txt = false;
+
result = dns_db_findrdataset(db, node, NULL, dns_rdatatype_spf,
0, 0, &rdataset, NULL);
if (result == ISC_R_SUCCESS) {
@@ -4142,7 +4196,6 @@ zone_postload(dns_zone_t *zone, dns_db_t *db, isc_time_t loadtime,
dns_journal_destroy(&journal);
} else {
jserial = serial;
- result = ISC_R_SUCCESS;
}
if (jserial != serial) {
@@ -4189,7 +4242,7 @@ zone_postload(dns_zone_t *zone, dns_db_t *db, isc_time_t loadtime,
if (nscount == 0 && DNS_ZONE_OPTION(zone, DNS_ZONEOPT_CHECKNS))
{
dns_zone_logc(zone, DNS_LOGCATEGORY_ZONELOAD,
- ISC_LOG_ERROR, "has no NS records");
+ ISC_LOG_WARNING, "has no NS records");
if (DNS_ZONE_OPTION(zone, DNS_ZONEOPT_FATALNS)) {
RETERR(DNS_R_BADZONE);
}
@@ -6310,7 +6363,8 @@ check_if_bottom_of_zone(dns_db_t *db, dns_dbnode_t *node,
dns_dbversion_t *version, bool *is_bottom_of_zone) {
isc_result_t result;
dns_rdatasetiter_t *iterator = NULL;
- bool seen_soa = false, seen_ns = false, seen_dname = false;
+ bool seen_soa = false, seen_ns = false;
+ bool seen_dname = false, seen_deleg = false;
REQUIRE(is_bottom_of_zone != NULL);
@@ -6332,6 +6386,9 @@ check_if_bottom_of_zone(dns_db_t *db, dns_dbnode_t *node,
case dns_rdatatype_ns:
seen_ns = true;
break;
+ case dns_rdatatype_deleg:
+ seen_deleg = true;
+ break;
case dns_rdatatype_dname:
seen_dname = true;
break;
@@ -6339,7 +6396,7 @@ check_if_bottom_of_zone(dns_db_t *db, dns_dbnode_t *node,
dns_rdataset_disassociate(&rdataset);
}
- if ((seen_ns && !seen_soa) || seen_dname) {
+ if ((seen_ns && !seen_soa) || seen_dname || seen_deleg) {
*is_bottom_of_zone = true;
}
@@ -6355,6 +6412,7 @@ typedef struct seen {
bool nsec3;
bool ds;
bool dname;
+ bool deleg;
} seen_t;
static isc_result_t
@@ -6388,6 +6446,8 @@ allrdatasets(dns_db_t *db, dns_dbnode_t *node, dns_dbversion_t *version,
(*seen).nsec = true;
} else if (rdataset.type == dns_rdatatype_nsec3) {
(*seen).nsec3 = true;
+ } else if (rdataset.type == dns_rdatatype_deleg) {
+ (*seen).deleg = true;
}
dns_rdataset_disassociate(&rdataset);
@@ -6487,6 +6547,7 @@ sign_a_node(dns_db_t *db, dns_zone_t *zone, dns_name_t *name,
}
if (seen.ns && !seen.soa && rdataset.type != dns_rdatatype_ds &&
+ rdataset.type != dns_rdatatype_deleg &&
rdataset.type != dns_rdatatype_nsec)
{
continue;
@@ -15271,7 +15332,8 @@ zone_replacedb(dns_zone_t *zone, dns_db_t *db, bool dump) {
}
if (nscount == 0 && DNS_ZONE_OPTION(zone, DNS_ZONEOPT_CHECKNS))
{
- dns_zone_log(zone, ISC_LOG_ERROR, "has no NS records");
+ dns_zone_log(zone, ISC_LOG_WARNING,
+ "has no NS records");
if (DNS_ZONE_OPTION(zone, DNS_ZONEOPT_FATALNS)) {
RETERR(DNS_R_BADZONE);
}
@@ -15541,7 +15603,7 @@ again:
DNS_ZONE_OPTION(zone, DNS_ZONEOPT_CHECKNS))
{
dns_zone_logc(zone, DNS_LOGCATEGORY_XFER_IN,
- ISC_LOG_ERROR,
+ ISC_LOG_WARNING,
"transferred zone "
"has no NS records");
if (DNS_ZONE_OPTION(zone, DNS_ZONEOPT_FATALNS))
@@ -17183,7 +17245,9 @@ checkds_done(void *arg) {
}
ISC_LIST_FOREACH(name->list, rdataset, link) {
- if (rdataset->type != dns_rdatatype_ds) {
+ if (rdataset->type != dns_rdatatype_ds &&
+ rdataset->type != dns_rdatatype_deleg)
+ {
goto next;
}
diff --git a/lib/dns/zoneverify.c b/lib/dns/zoneverify.c
index 925a5059dc..576a7eec82 100644
--- a/lib/dns/zoneverify.c
+++ b/lib/dns/zoneverify.c
@@ -131,22 +131,31 @@ zoneverify_log_error(const vctx_t *vctx, const char *fmt, ...) {
static bool
is_delegation(const vctx_t *vctx, const dns_name_t *name, dns_dbnode_t *node,
uint32_t *ttlp) {
- dns_rdataset_t nsset;
+ dns_rdataset_t rdataset = DNS_RDATASET_INIT;
isc_result_t result;
if (dns_name_equal(name, vctx->origin)) {
return false;
}
- dns_rdataset_init(&nsset);
result = dns_db_findrdataset(vctx->db, node, vctx->ver,
- dns_rdatatype_ns, 0, 0, &nsset, NULL);
- if (dns_rdataset_isassociated(&nsset)) {
- SET_IF_NOT_NULL(ttlp, nsset.ttl);
- dns_rdataset_disassociate(&nsset);
+ dns_rdatatype_ns, 0, 0, &rdataset, NULL);
+ if (result == ISC_R_SUCCESS && dns_rdataset_isassociated(&rdataset)) {
+ SET_IF_NOT_NULL(ttlp, rdataset.ttl);
+ dns_rdataset_disassociate(&rdataset);
+ return true;
}
- return result == ISC_R_SUCCESS;
+ result = dns_db_findrdataset(vctx->db, node, vctx->ver,
+ dns_rdatatype_deleg, 0, 0, &rdataset,
+ NULL);
+ if (result == ISC_R_SUCCESS && dns_rdataset_isassociated(&rdataset)) {
+ SET_IF_NOT_NULL(ttlp, rdataset.ttl);
+ dns_rdataset_disassociate(&rdataset);
+ return true;
+ }
+
+ return false;
}
/*%
@@ -705,6 +714,11 @@ verifynsec3(const vctx_t *vctx, const dns_name_t *name,
dns_rdatatype_nsec3, 0, 0,
&rdataset, NULL);
}
+
+ /*
+ * An OPT-OUT range may cover unsigned delegation points and
+ * empty nodes, but any other node must have a matching NSEC3.
+ */
if (result != ISC_R_SUCCESS &&
(!delegation || !optout ||
(!empty && dns_nsec_isset(types, dns_rdatatype_ds))))
@@ -918,6 +932,7 @@ verifynode(vctx_t *vctx, const dns_name_t *name, dns_dbnode_t *node,
*/
if (rdataset.type != dns_rdatatype_rrsig &&
(!delegation || rdataset.type == dns_rdatatype_ds ||
+ rdataset.type == dns_rdatatype_deleg ||
rdataset.type == dns_rdatatype_nsec))
{
result = verifyset(vctx, &rdataset, name, node, dstkeys,
diff --git a/lib/isc/include/isc/result.h b/lib/isc/include/isc/result.h
index 477956595e..2668a4e7e2 100644
--- a/lib/isc/include/isc/result.h
+++ b/lib/isc/include/isc/result.h
@@ -169,7 +169,7 @@ typedef enum isc_result {
DNS_R_LAME,
DNS_R_UNEXPECTEDRCODE,
DNS_R_UNEXPECTEDOPCODE,
- DNS_R_CHASEDSSERVERS,
+ DNS_R_CHASEPARENTSERVERS,
DNS_R_EMPTYNAME,
DNS_R_EMPTYWILD,
DNS_R_BADBITMAP,
diff --git a/lib/isc/result.c b/lib/isc/result.c
index 65c9fa884a..4e876382ad 100644
--- a/lib/isc/result.c
+++ b/lib/isc/result.c
@@ -172,7 +172,7 @@ static const char *description[ISC_R_NRESULTS] = {
[DNS_R_LAME] = "lame server detected",
[DNS_R_UNEXPECTEDRCODE] = "unexpected RCODE",
[DNS_R_UNEXPECTEDOPCODE] = "unexpected OPCODE",
- [DNS_R_CHASEDSSERVERS] = "chase DS servers",
+ [DNS_R_CHASEPARENTSERVERS] = "chase parent servers",
[DNS_R_EMPTYNAME] = "empty name",
[DNS_R_EMPTYWILD] = "empty wild",
[DNS_R_BADBITMAP] = "bad bitmap",
@@ -408,7 +408,7 @@ static const char *identifier[ISC_R_NRESULTS] = {
[DNS_R_LAME] = "DNS_R_LAME",
[DNS_R_UNEXPECTEDRCODE] = "DNS_R_UNEXPECTEDRCODE",
[DNS_R_UNEXPECTEDOPCODE] = "DNS_R_UNEXPECTEDOPCODE",
- [DNS_R_CHASEDSSERVERS] = "DNS_R_CHASEDSSERVERS",
+ [DNS_R_CHASEPARENTSERVERS] = "DNS_R_CHASEPARENTSERVERS",
[DNS_R_EMPTYNAME] = "DNS_R_EMPTYNAME",
[DNS_R_EMPTYWILD] = "DNS_R_EMPTYWILD",
[DNS_R_BADBITMAP] = "DNS_R_BADBITMAP",
diff --git a/lib/ns/include/ns/client.h b/lib/ns/include/ns/client.h
index d84a60117c..def34e60e3 100644
--- a/lib/ns/include/ns/client.h
+++ b/lib/ns/include/ns/client.h
@@ -179,6 +179,7 @@ struct ns_client {
*/
bool wantdnssec : 1; /*%< include dnssec records
*/
+ bool wantdeleg : 1; /*%< DELEG ok */
bool wantnsid : 1; /*%< include nameserver ID */
bool badcookie : 1; /*%< bad/out-of-date cookie
*/
diff --git a/lib/ns/query.c b/lib/ns/query.c
index 649bfb0ae8..96b2769fd1 100644
--- a/lib/ns/query.c
+++ b/lib/ns/query.c
@@ -1468,7 +1468,7 @@ query_getdb(ns_client_t *client, dns_name_t *name, dns_rdatatype_t qtype,
isc_result_t tresult;
dns_clientinfomethods_init(&cm, ns_client_sourceip);
- dns_clientinfo_init(&ci, client, NULL);
+ dns_clientinfo_init(&ci, client, NULL, &client->edectx);
dns_clientinfo_setecs(&ci, &client->inner.ecs);
tdbp = NULL;
@@ -1605,7 +1605,7 @@ query_additionalauthfind(dns_db_t *db, dns_dbversion_t *version,
isc_result_t result;
dns_clientinfomethods_init(&cm, ns_client_sourceip);
- dns_clientinfo_init(&ci, client, NULL);
+ dns_clientinfo_init(&ci, client, NULL, &client->edectx);
/*
* Since we are looking for authoritative data, we do not set
@@ -1745,7 +1745,7 @@ query_additional_cb(void *arg, const dns_name_t *name, dns_rdatatype_t qtype,
}
dns_clientinfomethods_init(&cm, ns_client_sourceip);
- dns_clientinfo_init(&ci, client, NULL);
+ dns_clientinfo_init(&ci, client, NULL, &client->edectx);
/*
* We treat type A additional section processing as if it
@@ -2165,7 +2165,7 @@ query_additional(query_ctx_t *qctx, dns_name_t *name,
}
dns_clientinfomethods_init(&cm, ns_client_sourceip);
- dns_clientinfo_init(&ci, client, NULL);
+ dns_clientinfo_init(&ci, client, NULL, &client->edectx);
dns_db_addglue(qctx->db, dbversion->version, name, rdataset,
client->message, &cm, &ci);
@@ -2328,10 +2328,12 @@ query_zone_delegation_rrset(query_ctx_t *qctx, dns_linkedname_t **namep,
return;
}
- dns_clientinfomethods_init(&cm, ns_client_sourceip);
- dns_clientinfo_init(&ci, client, NULL);
- dns_db_addglue(qctx->db, qctx->version, dns_name(mname), rdataset,
- client->message, &cm, &ci);
+ if (rdataset->type == dns_rdatatype_ns) {
+ dns_clientinfomethods_init(&cm, ns_client_sourceip);
+ dns_clientinfo_init(&ci, client, NULL, &client->edectx);
+ dns_db_addglue(qctx->db, qctx->version, dns_name(mname),
+ rdataset, client->message, &cm, &ci);
+ }
CTRACE(ISC_LOG_DEBUG(3), "query_zone_delegation_rrset: done");
}
@@ -2483,7 +2485,8 @@ stale_refresh_aftermath(ns_client_t *client, isc_result_t result) {
qctx_init(client, NULL, 0, &qctx);
dns_clientinfomethods_init(&cm, ns_client_sourceip);
- dns_clientinfo_init(&ci, qctx.client, NULL);
+ dns_clientinfo_init(&ci, qctx.client, NULL,
+ &qctx.client->edectx);
if (qctx.client->inner.haveecs) {
dns_clientinfo_setecs(&ci, &qctx.client->inner.ecs);
}
@@ -2913,7 +2916,7 @@ rpz_rrset_find(ns_client_t *client, dns_name_t *name, dns_rdatatype_t type,
}
dns_clientinfomethods_init(&cm, ns_client_sourceip);
- dns_clientinfo_init(&ci, client, NULL);
+ dns_clientinfo_init(&ci, client, NULL, &client->edectx);
result = dns_db_findext(*dbp, name, version, type, options,
client->inner.now, found, &cm, &ci, *rdatasetp,
NULL);
@@ -3069,7 +3072,7 @@ rpz_find_p(ns_client_t *client, dns_name_t *self_name, dns_rdatatype_t qtype,
REQUIRE(foundname != NULL);
dns_clientinfomethods_init(&cm, ns_client_sourceip);
- dns_clientinfo_init(&ci, client, NULL);
+ dns_clientinfo_init(&ci, client, NULL, &client->edectx);
/*
* Try to find either a CNAME or the type of record demanded by the
@@ -4254,7 +4257,7 @@ query_findclosestnsec3(dns_name_t *qname, dns_db_t *db,
dns_name_clone(qname, &name);
labels = dns_name_countlabels(&name);
dns_clientinfomethods_init(&cm, ns_client_sourceip);
- dns_clientinfo_init(&ci, client, NULL);
+ dns_clientinfo_init(&ci, client, NULL, &client->edectx);
/*
* Map unknown algorithm to known value.
@@ -4443,7 +4446,7 @@ redirect(ns_client_t *client, dns_name_t *name, dns_rdataset_t *rdataset,
found = dns_fixedname_initname(&fixed);
dns_clientinfomethods_init(&cm, ns_client_sourceip);
- dns_clientinfo_init(&ci, client, NULL);
+ dns_clientinfo_init(&ci, client, NULL, &client->edectx);
dns_clientinfo_setecs(&ci, &client->inner.ecs);
if (client->inner.wantdnssec && *dbp != NULL && dns_db_iszone(*dbp) &&
@@ -4580,7 +4583,7 @@ redirect2(ns_client_t *client, dns_name_t *name, dns_rdataset_t *rdataset,
found = dns_fixedname_initname(&fixed);
dns_clientinfomethods_init(&cm, ns_client_sourceip);
- dns_clientinfo_init(&ci, client, NULL);
+ dns_clientinfo_init(&ci, client, NULL, &client->edectx);
dns_clientinfo_setecs(&ci, &client->inner.ecs);
if (client->inner.wantdnssec && *dbp != NULL && dns_db_iszone(*dbp) &&
@@ -5236,7 +5239,7 @@ ns__query_start(query_ctx_t *qctx) {
qctx->qtype, qctx->options, &qctx->zone, &qctx->db,
&qctx->version, &qctx->is_zone);
if ((result != ISC_R_SUCCESS || !qctx->is_zone) &&
- qctx->qtype == dns_rdatatype_ds &&
+ dns_rdatatype_atparent(qctx->qtype) &&
!qctx->client->query.recursionok && qctx->options.noexact)
{
/*
@@ -5565,7 +5568,7 @@ query_lookup(query_ctx_t *qctx) {
CALL_HOOK(NS_QUERY_LOOKUP_BEGIN, qctx);
dns_clientinfomethods_init(&cm, ns_client_sourceip);
- dns_clientinfo_init(&ci, qctx->client, NULL);
+ dns_clientinfo_init(&ci, qctx->client, NULL, &qctx->client->edectx);
if (qctx->client->inner.haveecs) {
dns_clientinfo_setecs(&ci, &qctx->client->inner.ecs);
}
@@ -5615,6 +5618,13 @@ query_lookup(query_ctx_t *qctx) {
dboptions |= DNS_DBFIND_COVERINGNSEC;
}
+ /*
+ * Client used the DE flag.
+ */
+ if (qctx->client->inner.wantdeleg) {
+ dboptions |= DNS_DBFIND_DELEGOK;
+ }
+
foundname = dns_fixedname_initname(&qctx->foundname);
result = dns_db_findext(qctx->db, rpzqname, qctx->version, qctx->type,
@@ -7352,7 +7362,7 @@ query_respond_any(query_ctx_t *qctx) {
}
dns_clientinfomethods_init(&cm, ns_client_sourceip);
- dns_clientinfo_init(&ci, qctx->client, NULL);
+ dns_clientinfo_init(&ci, qctx->client, NULL, &qctx->client->edectx);
if (qctx->client->inner.haveecs) {
dns_clientinfo_setecs(&ci, &qctx->client->inner.ecs);
}
@@ -8475,7 +8485,7 @@ query_addds(query_ctx_t *qctx) {
lookupname = qctx_has_foundname(qctx) ? qctx_get_foundname(qctx) : name;
foundname = dns_fixedname_initname(&foundfixed);
dns_clientinfomethods_init(&cm, ns_client_sourceip);
- dns_clientinfo_init(&ci, client, NULL);
+ dns_clientinfo_init(&ci, client, NULL, &client->edectx);
if (client->inner.haveecs) {
dns_clientinfo_setecs(&ci, &client->inner.ecs);
}
@@ -8515,6 +8525,10 @@ query_addds(query_ctx_t *qctx) {
*/
MSG_SECTION_FOREACH(client->message, DNS_SECTION_AUTHORITY, rname) {
result = dns_message_findtype(rname, dns_rdatatype_ns, 0, NULL);
+ if (result != ISC_R_SUCCESS) {
+ result = dns_message_findtype(
+ rname, dns_rdatatype_deleg, 0, NULL);
+ }
if (result == ISC_R_SUCCESS) {
/*
* Add the relevant RRset (DS or NSEC) to the
@@ -9473,7 +9487,7 @@ query_coveringnsec(query_ctx_t *qctx) {
nowild = dns_fixedname_initname(&fnowild);
dns_clientinfomethods_init(&cm, ns_client_sourceip);
- dns_clientinfo_init(&ci, qctx->client, NULL);
+ dns_clientinfo_init(&ci, qctx->client, NULL, &qctx->client->edectx);
/*
* All signer names must be the same to accept.
@@ -10186,7 +10200,7 @@ query_addsoa(query_ctx_t *qctx, unsigned int override_ttl,
CTRACE(ISC_LOG_DEBUG(3), "query_addsoa");
dns_clientinfomethods_init(&cm, ns_client_sourceip);
- dns_clientinfo_init(&ci, client, NULL);
+ dns_clientinfo_init(&ci, client, NULL, &client->edectx);
/*
* Don't add the SOA record for test which set "-T nosoa".
@@ -10323,7 +10337,7 @@ query_addns(query_ctx_t *qctx) {
fname = dns_fixedname_initname(&foundname);
dns_clientinfomethods_init(&cm, ns_client_sourceip);
- dns_clientinfo_init(&ci, client, NULL);
+ dns_clientinfo_init(&ci, client, NULL, &client->edectx);
/*
* Get resources and make 'name' be the database origin.
@@ -10407,7 +10421,7 @@ query_addbestns(query_ctx_t *qctx) {
CTRACE(ISC_LOG_DEBUG(3), "query_addbestns");
dns_clientinfomethods_init(&cm, ns_client_sourceip);
- dns_clientinfo_init(&ci, client, NULL);
+ dns_clientinfo_init(&ci, client, NULL, &client->edectx);
dns_name_init(&qname);
dns_name_clone(client->query.qname, &qname);
@@ -10593,7 +10607,7 @@ query_addwildcardproof(query_ctx_t *qctx, dns_name_t *name, bool ispositive,
REQUIRE(name != NULL);
dns_clientinfomethods_init(&cm, ns_client_sourceip);
- dns_clientinfo_init(&ci, client, NULL);
+ dns_clientinfo_init(&ci, client, NULL, &client->edectx);
/*
* Get the NOQNAME proof then if !ispositive
@@ -11253,6 +11267,10 @@ ns_query_start(ns_client_t *client, isc_nmhandle_t *handle) {
client->inner.wantdnssec = true;
}
+ if ((client->inner.extflags & DNS_MESSAGEEXTFLAG_DE) != 0) {
+ client->inner.wantdeleg = true;
+ }
+
switch (client->inner.view->minimalresponses) {
case dns_minimal_no:
break;
diff --git a/lib/ns/update.c b/lib/ns/update.c
index 53c1525f9d..c9d6889dd9 100644
--- a/lib/ns/update.c
+++ b/lib/ns/update.c
@@ -530,7 +530,7 @@ foreach_rrset(dns_db_t *db, dns_dbversion_t *ver, const dns_name_t *name,
* different from the current version
*/
dns_db_currentversion(db, &oldver);
- dns_clientinfo_init(&ci, NULL, (ver != oldver) ? ver : NULL);
+ dns_clientinfo_init(&ci, NULL, (ver != oldver) ? ver : NULL, NULL);
dns_db_closeversion(db, &oldver, false);
node = NULL;
@@ -612,7 +612,7 @@ foreach_rr(dns_db_t *db, dns_dbversion_t *ver, const dns_name_t *name,
* different from the current version
*/
dns_db_currentversion(db, &oldver);
- dns_clientinfo_init(&ci, NULL, (ver != oldver) ? ver : NULL);
+ dns_clientinfo_init(&ci, NULL, (ver != oldver) ? ver : NULL, NULL);
dns_db_closeversion(db, &oldver, false);
if (type == dns_rdatatype_any) {
diff --git a/tests/dns/qpzone_test.c b/tests/dns/qpzone_test.c
index 58f94ddaee..a079680576 100644
--- a/tests/dns/qpzone_test.c
+++ b/tests/dns/qpzone_test.c
@@ -594,7 +594,7 @@ ISC_RUN_TEST_IMPL(wildcard_foundname) {
assert_null(db);
}
-ISC_RUN_TEST_IMPL(wildcard_delegation_foundname) {
+ISC_RUN_TEST_IMPL(wildcard_no_delegation) {
isc_result_t result;
dns_db_t *db = NULL;
dns_dbversion_t *version = NULL;
@@ -610,6 +610,12 @@ ISC_RUN_TEST_IMPL(wildcard_delegation_foundname) {
assert_int_equal(result, ISC_R_SUCCESS);
assert_non_null(db);
+ /*
+ * Create an NS record at a wildcard node, *.example.org, then
+ * query for host.example.org/A. It should *not* return
+ * DNS_R_DELEGATION. Delegation types at wildcard nodes
+ * were previously undefined, but are now outright forbidden.
+ */
dns_test_namefromstring("host.example.org.", &fqname);
dns_test_namefromstring("*.example.org.", &fwild);
qname = dns_fixedname_name(&fqname);
@@ -633,12 +639,8 @@ ISC_RUN_TEST_IMPL(wildcard_delegation_foundname) {
dns_db_currentversion(db, &version);
result = dns_db_find(db, qname, version, dns_rdatatype_a, 0, 0, found,
&rdataset, NULL);
- assert_int_equal(result, DNS_R_DELEGATION);
- assert_true(dns_name_equal(found, wild));
- assert_true(found->attributes.wildcard);
- assert_true(dns_rdataset_isassociated(&rdataset));
+ assert_int_equal(result, DNS_R_NXRRSET);
- dns_rdataset_disassociate(&rdataset);
dns_db_closeversion(db, &version, false);
dns_db_detach(&db);
assert_null(db);
@@ -909,7 +911,7 @@ ISC_TEST_ENTRY(resign_sooner_values)
ISC_TEST_ENTRY(unscheduled_resign)
ISC_TEST_ENTRY(diffop_add_sub)
ISC_TEST_ENTRY(wildcard_foundname)
-ISC_TEST_ENTRY(wildcard_delegation_foundname)
+ISC_TEST_ENTRY(wildcard_no_delegation)
ISC_TEST_ENTRY(nodes_outside_zone)
ISC_TEST_ENTRY(diffop_addresign)
ISC_TEST_LIST_END