summaryrefslogtreecommitdiff
path: root/lib/dns/resolver.c
diff options
context:
space:
mode:
Diffstat (limited to 'lib/dns/resolver.c')
-rw-r--r--lib/dns/resolver.c133
1 files changed, 120 insertions, 13 deletions
diff --git a/lib/dns/resolver.c b/lib/dns/resolver.c
index a1c263e12d4b..a5d7c2500f8f 100644
--- a/lib/dns/resolver.c
+++ b/lib/dns/resolver.c
@@ -15,7 +15,7 @@
* PERFORMANCE OF THIS SOFTWARE.
*/
-/* $Id: resolver.c,v 1.384.14.12 2009/05/11 02:38:03 tbox Exp $ */
+/* $Id: resolver.c,v 1.384.14.14 2009/06/02 23:47:13 tbox Exp $ */
/*! \file */
@@ -472,6 +472,30 @@ valcreate(fetchctx_t *fctx, dns_adbaddrinfo_t *addrinfo, dns_name_t *name,
}
static isc_boolean_t
+rrsig_fromchildzone(fetchctx_t *fctx, dns_rdataset_t *rdataset) {
+ dns_namereln_t namereln;
+ dns_rdata_rrsig_t rrsig;
+ dns_rdata_t rdata = DNS_RDATA_INIT;
+ int order;
+ isc_result_t result;
+ unsigned int labels;
+
+ for (result = dns_rdataset_first(rdataset);
+ result == ISC_R_SUCCESS;
+ result = dns_rdataset_next(rdataset)) {
+ dns_rdataset_current(rdataset, &rdata);
+ result = dns_rdata_tostruct(&rdata, &rrsig, NULL);
+ RUNTIME_CHECK(result == ISC_R_SUCCESS);
+ namereln = dns_name_fullcompare(&rrsig.signer, &fctx->domain,
+ &order, &labels);
+ if (namereln == dns_namereln_subdomain)
+ return (ISC_TRUE);
+ dns_rdata_reset(&rdata);
+ }
+ return (ISC_FALSE);
+}
+
+static isc_boolean_t
fix_mustbedelegationornxdomain(dns_message_t *message, fetchctx_t *fctx) {
dns_name_t *name;
dns_name_t *domain = &fctx->domain;
@@ -484,13 +508,43 @@ fix_mustbedelegationornxdomain(dns_message_t *message, fetchctx_t *fctx) {
return (ISC_FALSE);
/*
- * Look for BIND 8 style delegations.
- * Also look for answers to ANY queries where the duplicate NS RRset
- * may have been stripped from the authority section.
+ * A DS RRset can appear anywhere in a zone, even for a delegation-only
+ * zone. So a response to an explicit query for this type should be
+ * excluded from delegation-only fixup.
+ *
+ * SOA, NS, and DNSKEY can only exist at a zone apex, so a postive
+ * response to a query for these types can never violate the
+ * delegation-only assumption: if the query name is below a
+ * zone cut, the response should normally be a referral, which should
+ * be accepted; if the query name is below a zone cut but the server
+ * happens to have authority for the zone of the query name, the
+ * response is a (non-referral) answer. But this does not violate
+ * delegation-only because the query name must be in a different zone
+ * due to the "apex-only" nature of these types. Note that if the
+ * remote server happens to have authority for a child zone of a
+ * delegation-only zone, we may still incorrectly "fix" the response
+ * with NXDOMAIN for queries for other types. Unfortunately it's
+ * generally impossible to differentiate this case from violation of
+ * the delegation-only assumption. Once the resolver learns the
+ * correct zone cut, possibly via a separate query for an "apex-only"
+ * type, queries for other types will be resolved correctly.
+ *
+ * A query for type ANY will be accepted if it hits an exceptional
+ * type above in the answer section as it should be from a child
+ * zone.
+ *
+ * Also accept answers with RRSIG records from the child zone.
+ * Direct queries for RRSIG records should not be answered from
+ * the parent zone.
*/
+
if (message->counts[DNS_SECTION_ANSWER] != 0 &&
(fctx->type == dns_rdatatype_ns ||
- fctx->type == dns_rdatatype_any)) {
+ fctx->type == dns_rdatatype_ds ||
+ fctx->type == dns_rdatatype_soa ||
+ fctx->type == dns_rdatatype_any ||
+ fctx->type == dns_rdatatype_rrsig ||
+ fctx->type == dns_rdatatype_dnskey)) {
result = dns_message_firstname(message, DNS_SECTION_ANSWER);
while (result == ISC_R_SUCCESS) {
name = NULL;
@@ -499,10 +553,32 @@ fix_mustbedelegationornxdomain(dns_message_t *message, fetchctx_t *fctx) {
for (rdataset = ISC_LIST_HEAD(name->list);
rdataset != NULL;
rdataset = ISC_LIST_NEXT(rdataset, link)) {
- type = rdataset->type;
- if (type != dns_rdatatype_ns)
+ if (!dns_name_equal(name, &fctx->name))
continue;
- if (dns_name_issubdomain(name, domain))
+ type = rdataset->type;
+ /*
+ * RRsig from child?
+ */
+ if (type == dns_rdatatype_rrsig &&
+ rrsig_fromchildzone(fctx, rdataset))
+ return (ISC_FALSE);
+ /*
+ * Direct query for apex records or DS.
+ */
+ if (fctx->type == type &&
+ (type == dns_rdatatype_ds ||
+ type == dns_rdatatype_ns ||
+ type == dns_rdatatype_soa ||
+ type == dns_rdatatype_dnskey))
+ return (ISC_FALSE);
+ /*
+ * Indirect query for apex records or DS.
+ */
+ if (fctx->type == dns_rdatatype_any &&
+ (type == dns_rdatatype_ns ||
+ type == dns_rdatatype_ds ||
+ type == dns_rdatatype_soa ||
+ type == dns_rdatatype_dnskey))
return (ISC_FALSE);
}
result = dns_message_nextname(message,
@@ -510,7 +586,14 @@ fix_mustbedelegationornxdomain(dns_message_t *message, fetchctx_t *fctx) {
}
}
- /* Look for referral. */
+ /*
+ * A NODATA response to a DS query?
+ */
+ if (fctx->type == dns_rdatatype_ds &&
+ message->counts[DNS_SECTION_ANSWER] == 0)
+ return (ISC_FALSE);
+
+ /* Look for referral or indication of answer from child zone? */
if (message->counts[DNS_SECTION_AUTHORITY] == 0)
goto munge;
@@ -525,13 +608,37 @@ fix_mustbedelegationornxdomain(dns_message_t *message, fetchctx_t *fctx) {
if (type == dns_rdatatype_soa &&
dns_name_equal(name, domain))
keep_auth = ISC_TRUE;
+
if (type != dns_rdatatype_ns &&
- type != dns_rdatatype_soa)
+ type != dns_rdatatype_soa &&
+ type != dns_rdatatype_rrsig)
continue;
- if (dns_name_equal(name, domain))
- goto munge;
- if (dns_name_issubdomain(name, domain))
+
+ if (type == dns_rdatatype_rrsig) {
+ if (rrsig_fromchildzone(fctx, rdataset))
+ return (ISC_FALSE);
+ else
+ continue;
+ }
+
+ /* NS or SOA records. */
+ if (dns_name_equal(name, domain)) {
+ /*
+ * If a query for ANY causes a negative
+ * response, we can be sure that this is
+ * an empty node. For other type of queries
+ * we cannot differentiate an empty node
+ * from a node that just doesn't have that
+ * type of record. We only accept the former
+ * case.
+ */
+ if (message->counts[DNS_SECTION_ANSWER] == 0 &&
+ fctx->type == dns_rdatatype_any)
+ return (ISC_FALSE);
+ } else if (dns_name_issubdomain(name, domain)) {
+ /* Referral or answer from child zone. */
return (ISC_FALSE);
+ }
}
result = dns_message_nextname(message, DNS_SECTION_AUTHORITY);
}