summaryrefslogtreecommitdiffstats
path: root/contrib/bind9/bin/named/query.c
diff options
context:
space:
mode:
Diffstat (limited to 'contrib/bind9/bin/named/query.c')
-rw-r--r--contrib/bind9/bin/named/query.c2245
1 files changed, 2059 insertions, 186 deletions
diff --git a/contrib/bind9/bin/named/query.c b/contrib/bind9/bin/named/query.c
index fa34da6..1950257 100644
--- a/contrib/bind9/bin/named/query.c
+++ b/contrib/bind9/bin/named/query.c
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2004-2010 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 2004-2011 Internet Systems Consortium, Inc. ("ISC")
* Copyright (C) 1999-2003 Internet Software Consortium.
*
* Permission to use, copy, modify, and/or distribute this software for any
@@ -15,7 +15,7 @@
* PERFORMANCE OF THIS SOFTWARE.
*/
-/* $Id: query.c,v 1.313.20.24 2010-09-24 08:09:07 marka Exp $ */
+/* $Id: query.c,v 1.353.8.2.2.5 2011-06-09 03:17:10 marka Exp $ */
/*! \file */
@@ -34,6 +34,7 @@
#ifdef DLZ
#include <dns/dlz.h>
#endif
+#include <dns/dns64.h>
#include <dns/dnssec.h>
#include <dns/events.h>
#include <dns/message.h>
@@ -62,6 +63,17 @@
#include <named/sortlist.h>
#include <named/xfrout.h>
+#if 0
+/*
+ * It has been recommended that DNS64 be changed to return excluded
+ * AAAA addresses if DNS64 synthesis does not occur. This minimises
+ * the impact on the lookup results. While most DNS AAAA lookups are
+ * done to send IP packets to a host, not all of them are and filtering
+ * excluded addresses has a negative impact on those uses.
+ */
+#define dns64_bis_return_excluded_addresses 1
+#endif
+
/*% Partial answer? */
#define PARTIALANSWER(c) (((c)->query.attributes & \
NS_QUERYATTR_PARTIALANSWER) != 0)
@@ -92,6 +104,12 @@
/*% Secure? */
#define SECURE(c) (((c)->query.attributes & \
NS_QUERYATTR_SECURE) != 0)
+/*% DNS64 A lookup? */
+#define DNS64(c) (((c)->query.attributes & \
+ NS_QUERYATTR_DNS64) != 0)
+
+#define DNS64EXCLUDE(c) (((c)->query.attributes & \
+ NS_QUERYATTR_DNS64EXCLUDE) != 0)
/*% No QNAME Proof? */
#define NOQNAME(r) (((r)->attributes & \
@@ -116,6 +134,7 @@
#define DNS_GETDB_NOEXACT 0x01U
#define DNS_GETDB_NOLOG 0x02U
#define DNS_GETDB_PARTIAL 0x04U
+#define DNS_GETDB_IGNOREACL 0x08U
#define PENDINGOK(x) (((x) & DNS_DBFIND_PENDINGOK) != 0)
@@ -141,6 +160,9 @@ query_findclosestnsec3(dns_name_t *qname, dns_db_t *db,
static inline void
log_queryerror(ns_client_t *client, isc_result_t result, int line, int level);
+static void
+rpz_st_clear(ns_client_t *client);
+
/*%
* Increment query statistics counters.
*/
@@ -252,6 +274,19 @@ ns_query_cancel(ns_client_t *client) {
}
static inline void
+query_putrdataset(ns_client_t *client, dns_rdataset_t **rdatasetp) {
+ dns_rdataset_t *rdataset = *rdatasetp;
+
+ CTRACE("query_putrdataset");
+ if (rdataset != NULL) {
+ if (dns_rdataset_isassociated(rdataset))
+ dns_rdataset_disassociate(rdataset);
+ dns_message_puttemprdataset(client->message, rdatasetp);
+ }
+ CTRACE("query_putrdataset: done");
+}
+
+static inline void
query_reset(ns_client_t *client, isc_boolean_t everything) {
isc_buffer_t *dbuf, *dbuf_next;
ns_dbversion_t *dbversion, *dbversion_next;
@@ -285,6 +320,18 @@ query_reset(ns_client_t *client, isc_boolean_t everything) {
if (client->query.authzone != NULL)
dns_zone_detach(&client->query.authzone);
+ if (client->query.dns64_aaaa != NULL)
+ query_putrdataset(client, &client->query.dns64_aaaa);
+ if (client->query.dns64_sigaaaa != NULL)
+ query_putrdataset(client, &client->query.dns64_sigaaaa);
+ if (client->query.dns64_aaaaok != NULL) {
+ isc_mem_put(client->mctx, client->query.dns64_aaaaok,
+ client->query.dns64_aaaaoklen *
+ sizeof(isc_boolean_t));
+ client->query.dns64_aaaaok = NULL;
+ client->query.dns64_aaaaoklen = 0;
+ }
+
query_freefreeversions(client, everything);
for (dbuf = ISC_LIST_HEAD(client->query.namebufs);
@@ -310,13 +357,22 @@ query_reset(ns_client_t *client, isc_boolean_t everything) {
NS_QUERYATTR_SECURE);
client->query.restarts = 0;
client->query.timerset = ISC_FALSE;
+ if (client->query.rpz_st != NULL) {
+ rpz_st_clear(client);
+ if (everything) {
+ isc_mem_put(client->mctx, client->query.rpz_st,
+ sizeof(*client->query.rpz_st));
+ client->query.rpz_st = NULL;
+ }
+ }
client->query.origqname = NULL;
- client->query.qname = NULL;
client->query.dboptions = 0;
client->query.fetchoptions = 0;
client->query.gluedb = NULL;
client->query.authdbset = ISC_FALSE;
client->query.isreferral = ISC_FALSE;
+ client->query.dns64_options = 0;
+ client->query.dns64_ttl = ISC_UINT32_MAX;
}
static void
@@ -473,20 +529,6 @@ query_newrdataset(ns_client_t *client) {
return (rdataset);
}
-static inline void
-query_putrdataset(ns_client_t *client, dns_rdataset_t **rdatasetp) {
- dns_rdataset_t *rdataset = *rdatasetp;
-
- CTRACE("query_putrdataset");
- if (rdataset != NULL) {
- if (dns_rdataset_isassociated(rdataset))
- dns_rdataset_disassociate(rdataset);
- dns_message_puttemprdataset(client->message, rdatasetp);
- }
- CTRACE("query_putrdataset: done");
-}
-
-
static inline isc_result_t
query_newdbversion(ns_client_t *client, unsigned int n) {
unsigned int i;
@@ -540,6 +582,7 @@ ns_query_init(ns_client_t *client) {
ISC_LIST_INIT(client->query.freeversions);
client->query.restarts = 0;
client->query.timerset = ISC_FALSE;
+ client->query.rpz_st = NULL;
client->query.qname = NULL;
result = isc_mutex_init(&client->query.fetchlock);
if (result != ISC_R_SUCCESS)
@@ -549,6 +592,10 @@ ns_query_init(ns_client_t *client) {
client->query.authzone = NULL;
client->query.authdbset = ISC_FALSE;
client->query.isreferral = ISC_FALSE;
+ client->query.dns64_aaaa = NULL;
+ client->query.dns64_sigaaaa = NULL;
+ client->query.dns64_aaaaok = NULL;
+ client->query.dns64_aaaaoklen = 0;
query_reset(client, ISC_FALSE);
result = query_newdbversion(client, 3);
if (result != ISC_R_SUCCESS) {
@@ -563,8 +610,7 @@ ns_query_init(ns_client_t *client) {
}
static inline ns_dbversion_t *
-query_findversion(ns_client_t *client, dns_db_t *db,
- isc_boolean_t *newzonep)
+query_findversion(ns_client_t *client, dns_db_t *db)
{
ns_dbversion_t *dbversion;
@@ -590,12 +636,11 @@ query_findversion(ns_client_t *client, dns_db_t *db,
return (NULL);
dns_db_attach(db, &dbversion->db);
dns_db_currentversion(db, &dbversion->version);
+ dbversion->acl_checked = ISC_FALSE;
dbversion->queryok = ISC_FALSE;
ISC_LIST_APPEND(client->query.activeversions,
dbversion, link);
- *newzonep = ISC_TRUE;
- } else
- *newzonep = ISC_FALSE;
+ }
return (dbversion);
}
@@ -607,7 +652,6 @@ query_validatezonedb(ns_client_t *client, dns_name_t *name,
dns_dbversion_t **versionp)
{
isc_result_t result;
- isc_boolean_t check_acl, new_zone;
dns_acl_t *queryacl;
ns_dbversion_t *dbversion;
@@ -623,7 +667,17 @@ query_validatezonedb(ns_client_t *client, dns_name_t *name,
if (!client->view->additionalfromauth &&
client->query.authdbset &&
db != client->query.authdb)
- goto refuse;
+ return (DNS_R_REFUSED);
+
+ /*
+ * Non recursive query to a static-stub zone is prohibited; its
+ * zone content is not public data, but a part of local configuration
+ * and should not be disclosed.
+ */
+ if (dns_zone_gettype(zone) == dns_zone_staticstub &&
+ !RECURSIONOK(client)) {
+ return (DNS_R_REFUSED);
+ }
/*
* If the zone has an ACL, we'll check it, otherwise
@@ -633,23 +687,19 @@ query_validatezonedb(ns_client_t *client, dns_name_t *name,
* Also, get the database version to use.
*/
- check_acl = ISC_TRUE; /* Keep compiler happy. */
- queryacl = NULL;
-
/*
* Get the current version of this database.
*/
- dbversion = query_findversion(client, db, &new_zone);
- if (dbversion == NULL) {
- result = DNS_R_SERVFAIL;
- goto fail;
- }
- if (new_zone) {
- check_acl = ISC_TRUE;
- } else if (!dbversion->queryok) {
- goto refuse;
- } else {
- check_acl = ISC_FALSE;
+ dbversion = query_findversion(client, db);
+ if (dbversion == NULL)
+ return (DNS_R_SERVFAIL);
+
+ if ((options & DNS_GETDB_IGNOREACL) != 0)
+ goto approved;
+ if (dbversion->acl_checked) {
+ if (!dbversion->queryok)
+ return (DNS_R_REFUSED);
+ goto approved;
}
queryacl = dns_zone_getqueryacl(zone);
@@ -663,88 +713,69 @@ query_validatezonedb(ns_client_t *client, dns_name_t *name,
* allowed to make queries, otherwise the query should
* be refused.
*/
- check_acl = ISC_FALSE;
+ dbversion->acl_checked = ISC_TRUE;
if ((client->query.attributes &
- NS_QUERYATTR_QUERYOK) == 0)
- goto refuse;
- } else {
- /*
- * We haven't evaluated the view's queryacl yet.
- */
- check_acl = ISC_TRUE;
+ NS_QUERYATTR_QUERYOK) == 0) {
+ dbversion->queryok = ISC_FALSE;
+ return (DNS_R_REFUSED);
+ }
+ dbversion->queryok = ISC_TRUE;
+ goto approved;
}
}
- if (check_acl) {
- isc_boolean_t log = ISC_TF((options & DNS_GETDB_NOLOG) == 0);
-
- result = ns_client_checkaclsilent(client, NULL, queryacl,
- ISC_TRUE);
- if (log) {
- char msg[NS_CLIENT_ACLMSGSIZE("query")];
- if (result == ISC_R_SUCCESS) {
- if (isc_log_wouldlog(ns_g_lctx,
- ISC_LOG_DEBUG(3)))
- {
- ns_client_aclmsg("query", name, qtype,
- client->view->rdclass,
- msg, sizeof(msg));
- ns_client_log(client,
- DNS_LOGCATEGORY_SECURITY,
- NS_LOGMODULE_QUERY,
- ISC_LOG_DEBUG(3),
- "%s approved", msg);
- }
- } else {
+ result = ns_client_checkaclsilent(client, NULL, queryacl, ISC_TRUE);
+ if ((options & DNS_GETDB_NOLOG) == 0) {
+ char msg[NS_CLIENT_ACLMSGSIZE("query")];
+ if (result == ISC_R_SUCCESS) {
+ if (isc_log_wouldlog(ns_g_lctx, ISC_LOG_DEBUG(3))) {
ns_client_aclmsg("query", name, qtype,
client->view->rdclass,
msg, sizeof(msg));
- ns_client_log(client, DNS_LOGCATEGORY_SECURITY,
- NS_LOGMODULE_QUERY, ISC_LOG_INFO,
- "%s denied", msg);
+ ns_client_log(client,
+ DNS_LOGCATEGORY_SECURITY,
+ NS_LOGMODULE_QUERY,
+ ISC_LOG_DEBUG(3),
+ "%s approved", msg);
}
+ } else {
+ ns_client_aclmsg("query", name, qtype,
+ client->view->rdclass,
+ msg, sizeof(msg));
+ ns_client_log(client, DNS_LOGCATEGORY_SECURITY,
+ NS_LOGMODULE_QUERY, ISC_LOG_INFO,
+ "%s denied", msg);
}
+ }
- if (queryacl == client->view->queryacl) {
- if (result == ISC_R_SUCCESS) {
- /*
- * We were allowed by the default
- * "allow-query" ACL. Remember this so we
- * don't have to check again.
- */
- client->query.attributes |=
- NS_QUERYATTR_QUERYOK;
- }
+ if (queryacl == client->view->queryacl) {
+ if (result == ISC_R_SUCCESS) {
/*
- * We've now evaluated the view's query ACL, and
- * the NS_QUERYATTR_QUERYOK attribute is now valid.
+ * We were allowed by the default
+ * "allow-query" ACL. Remember this so we
+ * don't have to check again.
*/
- client->query.attributes |= NS_QUERYATTR_QUERYOKVALID;
+ client->query.attributes |= NS_QUERYATTR_QUERYOK;
}
-
- if (result != ISC_R_SUCCESS)
- goto refuse;
+ /*
+ * We've now evaluated the view's query ACL, and
+ * the NS_QUERYATTR_QUERYOK attribute is now valid.
+ */
+ client->query.attributes |= NS_QUERYATTR_QUERYOKVALID;
}
- /* Approved. */
-
- /*
- * Remember the result of the ACL check so we
- * don't have to check again.
- */
+ dbversion->acl_checked = ISC_TRUE;
+ if (result != ISC_R_SUCCESS) {
+ dbversion->queryok = ISC_FALSE;
+ return (DNS_R_REFUSED);
+ }
dbversion->queryok = ISC_TRUE;
+ approved:
/* Transfer ownership, if necessary. */
if (versionp != NULL)
*versionp = dbversion->version;
-
return (ISC_R_SUCCESS);
-
- refuse:
- return (DNS_R_REFUSED);
-
- fail:
- return (result);
}
static inline isc_result_t
@@ -800,6 +831,97 @@ query_getzonedb(ns_client_t *client, dns_name_t *name, dns_rdatatype_t qtype,
return (result);
}
+static void
+rpz_log(ns_client_t *client) {
+ char namebuf1[DNS_NAME_FORMATSIZE];
+ char namebuf2[DNS_NAME_FORMATSIZE];
+ dns_rpz_st_t *st;
+ const char *pat;
+
+ if (!ns_g_server->log_queries ||
+ !isc_log_wouldlog(ns_g_lctx, DNS_RPZ_INFO_LEVEL))
+ return;
+
+ st = client->query.rpz_st;
+ dns_name_format(client->query.qname, namebuf1, sizeof(namebuf1));
+ dns_name_format(st->qname, namebuf2, sizeof(namebuf2));
+
+ switch (st->m.policy) {
+ case DNS_RPZ_POLICY_NO_OP:
+ pat ="response policy %s rewrite %s NO-OP using %s";
+ break;
+ case DNS_RPZ_POLICY_NXDOMAIN:
+ pat = "response policy %s rewrite %s to NXDOMAIN using %s";
+ break;
+ case DNS_RPZ_POLICY_NODATA:
+ pat = "response policy %s rewrite %s to NODATA using %s";
+ break;
+ case DNS_RPZ_POLICY_RECORD:
+ case DNS_RPZ_POLICY_CNAME:
+ pat = "response policy %s rewrite %s using %s";
+ break;
+ default:
+ INSIST(0);
+ }
+ ns_client_log(client, NS_LOGCATEGORY_QUERIES, NS_LOGMODULE_QUERY,
+ DNS_RPZ_INFO_LEVEL, pat, dns_rpz_type2str(st->m.type),
+ namebuf1, namebuf2);
+}
+
+static void
+rpz_fail_log(ns_client_t *client, int level, dns_rpz_type_t rpz_type,
+ dns_name_t *name, const char *str, isc_result_t result)
+{
+ char namebuf1[DNS_NAME_FORMATSIZE];
+ char namebuf2[DNS_NAME_FORMATSIZE];
+
+ if (!ns_g_server->log_queries || !isc_log_wouldlog(ns_g_lctx, level))
+ return;
+
+ dns_name_format(client->query.qname, namebuf1, sizeof(namebuf1));
+ dns_name_format(name, namebuf2, sizeof(namebuf2));
+ ns_client_log(client, NS_LOGCATEGORY_QUERY_EERRORS,
+ NS_LOGMODULE_QUERY, level,
+ "response policy %s rewrite %s via %s %sfailed: %s",
+ dns_rpz_type2str(rpz_type),
+ namebuf1, namebuf2, str, isc_result_totext(result));
+}
+
+/*
+ * Get a policy rewrite zone database.
+ */
+static isc_result_t
+rpz_getdb(ns_client_t *client, dns_rpz_type_t rpz_type,
+ dns_name_t *rpz_qname, dns_zone_t **zonep,
+ dns_db_t **dbp, dns_dbversion_t **versionp)
+{
+ char namebuf1[DNS_NAME_FORMATSIZE];
+ char namebuf2[DNS_NAME_FORMATSIZE];
+ dns_dbversion_t *rpz_version = NULL;
+ isc_result_t result;
+
+ result = query_getzonedb(client, rpz_qname, dns_rdatatype_any,
+ DNS_GETDB_IGNOREACL, zonep, dbp, &rpz_version);
+ if (result == ISC_R_SUCCESS) {
+ if (ns_g_server->log_queries &&
+ isc_log_wouldlog(ns_g_lctx, DNS_RPZ_DEBUG_LEVEL2)) {
+ dns_name_format(client->query.qname, namebuf1,
+ sizeof(namebuf1));
+ dns_name_format(rpz_qname, namebuf2, sizeof(namebuf2));
+ ns_client_log(client, NS_LOGCATEGORY_QUERIES,
+ NS_LOGMODULE_QUERY, DNS_RPZ_DEBUG_LEVEL2,
+ "try rpz %s rewrite %s via %s",
+ dns_rpz_type2str(rpz_type),
+ namebuf1, namebuf2);
+ }
+ *versionp = rpz_version;
+ return (ISC_R_SUCCESS);
+ }
+ rpz_fail_log(client, DNS_RPZ_ERROR_LEVEL, rpz_type, rpz_qname,
+ "query_getzonedb() ", result);
+ return (result);
+}
+
static inline isc_result_t
query_getcachedb(ns_client_t *client, dns_name_t *name, dns_rdatatype_t qtype,
dns_db_t **dbp, unsigned int options)
@@ -1958,6 +2080,323 @@ query_addrdataset(ns_client_t *client, dns_name_t *fname,
CTRACE("query_addrdataset: done");
}
+static isc_result_t
+query_dns64(ns_client_t *client, dns_name_t **namep, dns_rdataset_t *rdataset,
+ dns_rdataset_t *sigrdataset, isc_buffer_t *dbuf,
+ dns_section_t section)
+{
+ dns_name_t *name, *mname;
+ dns_rdata_t *dns64_rdata;
+ dns_rdata_t rdata = DNS_RDATA_INIT;
+ dns_rdatalist_t *dns64_rdatalist;
+ dns_rdataset_t *dns64_rdataset;
+ dns_rdataset_t *mrdataset;
+ isc_buffer_t *buffer;
+ isc_region_t r;
+ isc_result_t result;
+ dns_view_t *view = client->view;
+ isc_netaddr_t netaddr;
+ dns_dns64_t *dns64;
+ unsigned int flags = 0;
+
+ /*%
+ * To the current response for 'client', add the answer RRset
+ * '*rdatasetp' and an optional signature set '*sigrdatasetp', with
+ * owner name '*namep', to section 'section', unless they are
+ * already there. Also add any pertinent additional data.
+ *
+ * If 'dbuf' is not NULL, then '*namep' is the name whose data is
+ * stored in 'dbuf'. In this case, query_addrrset() guarantees that
+ * when it returns the name will either have been kept or released.
+ */
+ CTRACE("query_dns64");
+ name = *namep;
+ mname = NULL;
+ mrdataset = NULL;
+ buffer = NULL;
+ dns64_rdata = NULL;
+ dns64_rdataset = NULL;
+ dns64_rdatalist = NULL;
+ result = dns_message_findname(client->message, section,
+ name, dns_rdatatype_aaaa,
+ rdataset->covers,
+ &mname, &mrdataset);
+ if (result == ISC_R_SUCCESS) {
+ /*
+ * We've already got an RRset of the given name and type.
+ * There's nothing else to do;
+ */
+ CTRACE("query_dns64: dns_message_findname succeeded: done");
+ if (dbuf != NULL)
+ query_releasename(client, namep);
+ return (ISC_R_SUCCESS);
+ } else if (result == DNS_R_NXDOMAIN) {
+ /*
+ * The name doesn't exist.
+ */
+ if (dbuf != NULL)
+ query_keepname(client, name, dbuf);
+ dns_message_addname(client->message, name, section);
+ *namep = NULL;
+ mname = name;
+ } else {
+ RUNTIME_CHECK(result == DNS_R_NXRRSET);
+ if (dbuf != NULL)
+ query_releasename(client, namep);
+ }
+
+ if (rdataset->trust != dns_trust_secure &&
+ (section == DNS_SECTION_ANSWER ||
+ section == DNS_SECTION_AUTHORITY))
+ client->query.attributes &= ~NS_QUERYATTR_SECURE;
+
+ isc_netaddr_fromsockaddr(&netaddr, &client->peeraddr);
+
+ result = isc_buffer_allocate(client->mctx, &buffer, view->dns64cnt *
+ 16 * dns_rdataset_count(rdataset));
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+ result = dns_message_gettemprdataset(client->message, &dns64_rdataset);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+ result = dns_message_gettemprdatalist(client->message,
+ &dns64_rdatalist);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+
+ dns_rdataset_init(dns64_rdataset);
+ dns_rdatalist_init(dns64_rdatalist);
+ dns64_rdatalist->rdclass = dns_rdataclass_in;
+ dns64_rdatalist->type = dns_rdatatype_aaaa;
+ if (client->query.dns64_ttl != ISC_UINT32_MAX)
+ dns64_rdatalist->ttl = ISC_MIN(rdataset->ttl,
+ client->query.dns64_ttl);
+ else
+ dns64_rdatalist->ttl = ISC_MIN(rdataset->ttl, 600);
+
+ if (RECURSIONOK(client))
+ flags |= DNS_DNS64_RECURSIVE;
+
+ /*
+ * We use the signatures from the A lookup to set DNS_DNS64_DNSSEC
+ * as this provides a easy way to see if the answer was signed.
+ */
+ if (sigrdataset != NULL && dns_rdataset_isassociated(sigrdataset))
+ flags |= DNS_DNS64_DNSSEC;
+
+ for (result = dns_rdataset_first(rdataset);
+ result == ISC_R_SUCCESS;
+ result = dns_rdataset_next(rdataset)) {
+ for (dns64 = ISC_LIST_HEAD(client->view->dns64);
+ dns64 != NULL; dns64 = dns_dns64_next(dns64)) {
+
+ dns_rdataset_current(rdataset, &rdata);
+ isc__buffer_availableregion(buffer, &r);
+ INSIST(r.length >= 16);
+ result = dns_dns64_aaaafroma(dns64, &netaddr,
+ client->signer,
+ &ns_g_server->aclenv,
+ flags, rdata.data, r.base);
+ if (result != ISC_R_SUCCESS) {
+ dns_rdata_reset(&rdata);
+ continue;
+ }
+ isc_buffer_add(buffer, 16);
+ isc_buffer_remainingregion(buffer, &r);
+ isc_buffer_forward(buffer, 16);
+ result = dns_message_gettemprdata(client->message,
+ &dns64_rdata);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+ dns_rdata_init(dns64_rdata);
+ dns_rdata_fromregion(dns64_rdata, dns_rdataclass_in,
+ dns_rdatatype_aaaa, &r);
+ ISC_LIST_APPEND(dns64_rdatalist->rdata, dns64_rdata,
+ link);
+ dns64_rdata = NULL;
+ dns_rdata_reset(&rdata);
+ }
+ }
+ if (result != ISC_R_NOMORE)
+ goto cleanup;
+
+ if (ISC_LIST_EMPTY(dns64_rdatalist->rdata))
+ goto cleanup;
+
+ result = dns_rdatalist_tordataset(dns64_rdatalist, dns64_rdataset);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+ client->query.attributes |= NS_QUERYATTR_NOADDITIONAL;
+ dns64_rdataset->trust = rdataset->trust;
+ query_addrdataset(client, mname, dns64_rdataset);
+ dns64_rdataset = NULL;
+ dns64_rdatalist = NULL;
+ dns_message_takebuffer(client->message, &buffer);
+ result = ISC_R_SUCCESS;
+
+ cleanup:
+ if (buffer != NULL)
+ isc_buffer_free(&buffer);
+
+ if (dns64_rdata != NULL)
+ dns_message_puttemprdata(client->message, &dns64_rdata);
+
+ if (dns64_rdataset != NULL)
+ dns_message_puttemprdataset(client->message, &dns64_rdataset);
+
+ if (dns64_rdatalist != NULL) {
+ for (dns64_rdata = ISC_LIST_HEAD(dns64_rdatalist->rdata);
+ dns64_rdata != NULL;
+ dns64_rdata = ISC_LIST_HEAD(dns64_rdatalist->rdata))
+ {
+ ISC_LIST_UNLINK(dns64_rdatalist->rdata,
+ dns64_rdata, link);
+ dns_message_puttemprdata(client->message, &dns64_rdata);
+ }
+ dns_message_puttemprdatalist(client->message, &dns64_rdatalist);
+ }
+
+ CTRACE("query_dns64: done");
+ return (result);
+}
+
+static void
+query_filter64(ns_client_t *client, dns_name_t **namep,
+ dns_rdataset_t *rdataset, isc_buffer_t *dbuf,
+ dns_section_t section)
+{
+ dns_name_t *name, *mname;
+ dns_rdata_t *myrdata;
+ dns_rdata_t rdata = DNS_RDATA_INIT;
+ dns_rdatalist_t *myrdatalist;
+ dns_rdataset_t *myrdataset;
+ isc_buffer_t *buffer;
+ isc_region_t r;
+ isc_result_t result;
+ unsigned int i;
+
+ CTRACE("query_filter64");
+
+ INSIST(client->query.dns64_aaaaok != NULL);
+ INSIST(client->query.dns64_aaaaoklen == dns_rdataset_count(rdataset));
+
+ name = *namep;
+ mname = NULL;
+ buffer = NULL;
+ myrdata = NULL;
+ myrdataset = NULL;
+ myrdatalist = NULL;
+ result = dns_message_findname(client->message, section,
+ name, dns_rdatatype_aaaa,
+ rdataset->covers,
+ &mname, &myrdataset);
+ if (result == ISC_R_SUCCESS) {
+ /*
+ * We've already got an RRset of the given name and type.
+ * There's nothing else to do;
+ */
+ CTRACE("query_filter64: dns_message_findname succeeded: done");
+ if (dbuf != NULL)
+ query_releasename(client, namep);
+ return;
+ } else if (result == DNS_R_NXDOMAIN) {
+ mname = name;
+ *namep = NULL;
+ } else {
+ RUNTIME_CHECK(result == DNS_R_NXRRSET);
+ if (dbuf != NULL)
+ query_releasename(client, namep);
+ dbuf = NULL;
+ }
+
+ if (rdataset->trust != dns_trust_secure &&
+ (section == DNS_SECTION_ANSWER ||
+ section == DNS_SECTION_AUTHORITY))
+ client->query.attributes &= ~NS_QUERYATTR_SECURE;
+
+ result = isc_buffer_allocate(client->mctx, &buffer,
+ 16 * dns_rdataset_count(rdataset));
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+ result = dns_message_gettemprdataset(client->message, &myrdataset);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+ result = dns_message_gettemprdatalist(client->message, &myrdatalist);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+
+ dns_rdataset_init(myrdataset);
+ dns_rdatalist_init(myrdatalist);
+ myrdatalist->rdclass = dns_rdataclass_in;
+ myrdatalist->type = dns_rdatatype_aaaa;
+ myrdatalist->ttl = rdataset->ttl;
+
+ i = 0;
+ for (result = dns_rdataset_first(rdataset);
+ result == ISC_R_SUCCESS;
+ result = dns_rdataset_next(rdataset)) {
+ if (!client->query.dns64_aaaaok[i++])
+ continue;
+ dns_rdataset_current(rdataset, &rdata);
+ INSIST(rdata.length == 16);
+ isc_buffer_putmem(buffer, rdata.data, rdata.length);
+ isc_buffer_remainingregion(buffer, &r);
+ isc_buffer_forward(buffer, rdata.length);
+ result = dns_message_gettemprdata(client->message, &myrdata);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+ dns_rdata_init(myrdata);
+ dns_rdata_fromregion(myrdata, dns_rdataclass_in,
+ dns_rdatatype_aaaa, &r);
+ ISC_LIST_APPEND(myrdatalist->rdata, myrdata, link);
+ myrdata = NULL;
+ dns_rdata_reset(&rdata);
+ }
+ if (result != ISC_R_NOMORE)
+ goto cleanup;
+
+ result = dns_rdatalist_tordataset(myrdatalist, myrdataset);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+ client->query.attributes |= NS_QUERYATTR_NOADDITIONAL;
+ if (mname == name) {
+ if (dbuf != NULL)
+ query_keepname(client, name, dbuf);
+ dns_message_addname(client->message, name, section);
+ dbuf = NULL;
+ }
+ myrdataset->trust = rdataset->trust;
+ query_addrdataset(client, mname, myrdataset);
+ myrdataset = NULL;
+ myrdatalist = NULL;
+ dns_message_takebuffer(client->message, &buffer);
+
+ cleanup:
+ if (buffer != NULL)
+ isc_buffer_free(&buffer);
+
+ if (myrdata != NULL)
+ dns_message_puttemprdata(client->message, &myrdata);
+
+ if (myrdataset != NULL)
+ dns_message_puttemprdataset(client->message, &myrdataset);
+
+ if (myrdatalist != NULL) {
+ for (myrdata = ISC_LIST_HEAD(myrdatalist->rdata);
+ myrdata != NULL;
+ myrdata = ISC_LIST_HEAD(myrdatalist->rdata))
+ {
+ ISC_LIST_UNLINK(myrdatalist->rdata, myrdata, link);
+ dns_message_puttemprdata(client->message, &myrdata);
+ }
+ dns_message_puttemprdatalist(client->message, &myrdatalist);
+ }
+ if (dbuf != NULL)
+ query_releasename(client, &name);
+
+ CTRACE("query_filter64: done");
+}
+
static void
query_addrrset(ns_client_t *client, dns_name_t **namep,
dns_rdataset_t **rdatasetp, dns_rdataset_t **sigrdatasetp,
@@ -2036,7 +2475,7 @@ query_addrrset(ns_client_t *client, dns_name_t **namep,
static inline isc_result_t
query_addsoa(ns_client_t *client, dns_db_t *db, dns_dbversion_t *version,
- isc_boolean_t zero_ttl, isc_boolean_t isassociated)
+ unsigned int override_ttl, isc_boolean_t isassociated)
{
dns_name_t *name;
dns_dbnode_t *node;
@@ -2119,10 +2558,11 @@ query_addsoa(ns_client_t *client, dns_db_t *db, dns_dbversion_t *version,
if (result != ISC_R_SUCCESS)
goto cleanup;
- if (zero_ttl) {
- rdataset->ttl = 0;
+ if (override_ttl != ISC_UINT32_MAX &&
+ override_ttl < rdataset->ttl) {
+ rdataset->ttl = override_ttl;
if (sigrdataset != NULL)
- sigrdataset->ttl = 0;
+ sigrdataset->ttl = override_ttl;
}
/*
@@ -2246,67 +2686,79 @@ query_addns(ns_client_t *client, dns_db_t *db, dns_dbversion_t *version) {
return (eresult);
}
-static inline isc_result_t
-query_addcnamelike(ns_client_t *client, dns_name_t *qname, dns_name_t *tname,
- dns_rdataset_t *dname, dns_name_t **anamep,
- dns_rdatatype_t type)
+static isc_result_t
+query_add_cname(ns_client_t *client, dns_name_t *qname, dns_name_t *tname,
+ dns_trust_t trust, dns_ttl_t ttl)
{
dns_rdataset_t *rdataset;
dns_rdatalist_t *rdatalist;
dns_rdata_t *rdata;
- isc_result_t result;
isc_region_t r;
+ dns_name_t *aname;
+ isc_result_t result;
/*
* We assume the name data referred to by tname won't go away.
*/
- REQUIRE(anamep != NULL);
+ aname = NULL;
+ result = dns_message_gettempname(client->message, &aname);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+ result = dns_name_dup(qname, client->mctx, aname);
+ if (result != ISC_R_SUCCESS) {
+ dns_message_puttempname(client->message, &aname);
+ return (result);
+ }
rdatalist = NULL;
result = dns_message_gettemprdatalist(client->message, &rdatalist);
- if (result != ISC_R_SUCCESS)
+ if (result != ISC_R_SUCCESS) {
+ dns_message_puttempname(client->message, &aname);
return (result);
+ }
rdata = NULL;
result = dns_message_gettemprdata(client->message, &rdata);
- if (result != ISC_R_SUCCESS)
+ if (result != ISC_R_SUCCESS) {
+ dns_message_puttempname(client->message, &aname);
+ dns_message_puttemprdatalist(client->message, &rdatalist);
return (result);
+ }
rdataset = NULL;
result = dns_message_gettemprdataset(client->message, &rdataset);
- if (result != ISC_R_SUCCESS)
- return (result);
- dns_rdataset_init(rdataset);
- result = dns_name_dup(qname, client->mctx, *anamep);
if (result != ISC_R_SUCCESS) {
- dns_message_puttemprdataset(client->message, &rdataset);
+ dns_message_puttempname(client->message, &aname);
+ dns_message_puttemprdatalist(client->message, &rdatalist);
+ dns_message_puttemprdata(client->message, &rdata);
return (result);
}
-
- rdatalist->type = type;
+ dns_rdataset_init(rdataset);
+ rdatalist->type = dns_rdatatype_cname;
rdatalist->covers = 0;
rdatalist->rdclass = client->message->rdclass;
- rdatalist->ttl = dname->ttl;
+ rdatalist->ttl = ttl;
dns_name_toregion(tname, &r);
rdata->data = r.base;
rdata->length = r.length;
rdata->rdclass = client->message->rdclass;
- rdata->type = type;
+ rdata->type = dns_rdatatype_cname;
ISC_LIST_INIT(rdatalist->rdata);
ISC_LIST_APPEND(rdatalist->rdata, rdata, link);
RUNTIME_CHECK(dns_rdatalist_tordataset(rdatalist, rdataset)
== ISC_R_SUCCESS);
- rdataset->trust = dname->trust;
+ rdataset->trust = trust;
- query_addrrset(client, anamep, &rdataset, NULL, NULL,
+ query_addrrset(client, &aname, &rdataset, NULL, NULL,
DNS_SECTION_ANSWER);
-
if (rdataset != NULL) {
if (dns_rdataset_isassociated(rdataset))
dns_rdataset_disassociate(rdataset);
dns_message_puttemprdataset(client->message, &rdataset);
}
+ if (aname != NULL)
+ dns_message_puttempname(client->message, &aname);
return (ISC_R_SUCCESS);
}
@@ -2860,7 +3312,7 @@ query_addwildcardproof(ns_client_t *client, dns_db_t *db,
* j.example -> z.i.example NSEC example
* owner common example
* next common example
- * wild *.f.example
+ * wild *.example
*/
options = client->query.dboptions | DNS_DBFIND_NOWILD;
dns_fixedname_init(&wfixed);
@@ -3196,8 +3648,9 @@ query_resume(isc_task_t *task, isc_event_t *event) {
}
static isc_result_t
-query_recurse(ns_client_t *client, dns_rdatatype_t qtype, dns_name_t *qdomain,
- dns_rdataset_t *nameservers, isc_boolean_t resuming)
+query_recurse(ns_client_t *client, dns_rdatatype_t qtype, dns_name_t *qname,
+ dns_name_t *qdomain, dns_rdataset_t *nameservers,
+ isc_boolean_t resuming)
{
isc_result_t result;
dns_rdataset_t *rdataset, *sigrdataset;
@@ -3229,7 +3682,11 @@ query_recurse(ns_client_t *client, dns_rdatatype_t qtype, dns_name_t *qdomain,
NS_LOGMODULE_QUERY,
ISC_LOG_WARNING,
"recursive-clients soft limit "
- "exceeded, aborting oldest query");
+ "exceeded (%d/%d/%d), "
+ "aborting oldest query",
+ client->recursionquota->used,
+ client->recursionquota->soft,
+ client->recursionquota->max);
}
ns_client_killoldestquery(client);
result = ISC_R_SUCCESS;
@@ -3242,7 +3699,11 @@ query_recurse(ns_client_t *client, dns_rdatatype_t qtype, dns_name_t *qdomain,
ns_client_log(client, NS_LOGCATEGORY_CLIENT,
NS_LOGMODULE_QUERY,
ISC_LOG_WARNING,
- "no more recursive clients: %s",
+ "no more recursive clients "
+ "(%d/%d/%d): %s",
+ ns_g_server->recursionquota.used,
+ ns_g_server->recursionquota.soft,
+ ns_g_server->recursionquota.max,
isc_result_totext(result));
}
ns_client_killoldestquery(client);
@@ -3289,8 +3750,7 @@ query_recurse(ns_client_t *client, dns_rdatatype_t qtype, dns_name_t *qdomain,
else
peeraddr = NULL;
result = dns_resolver_createfetch2(client->view->resolver,
- client->query.qname,
- qtype, qdomain, nameservers,
+ qname, qtype, qdomain, nameservers,
NULL, peeraddr, client->message->id,
client->query.fetchoptions,
client->task,
@@ -3313,6 +3773,696 @@ query_recurse(ns_client_t *client, dns_rdatatype_t qtype, dns_name_t *qdomain,
return (result);
}
+static inline void
+rpz_clean(dns_zone_t **zonep, dns_db_t **dbp, dns_dbnode_t **nodep,
+ dns_rdataset_t **rdatasetp)
+{
+ if (nodep != NULL && *nodep != NULL) {
+ REQUIRE(dbp != NULL && *dbp != NULL);
+ dns_db_detachnode(*dbp, nodep);
+ }
+ if (dbp != NULL && *dbp != NULL)
+ dns_db_detach(dbp);
+ if (zonep != NULL && *zonep != NULL)
+ dns_zone_detach(zonep);
+ if (rdatasetp != NULL && *rdatasetp != NULL &&
+ dns_rdataset_isassociated(*rdatasetp))
+ dns_rdataset_disassociate(*rdatasetp);
+}
+
+static inline isc_result_t
+rpz_ready(ns_client_t *client, dns_zone_t **zonep, dns_db_t **dbp,
+ dns_dbnode_t **nodep, dns_rdataset_t **rdatasetp)
+{
+ REQUIRE(rdatasetp != NULL);
+
+ rpz_clean(zonep, dbp, nodep, rdatasetp);
+ if (*rdatasetp == NULL) {
+ *rdatasetp = query_newrdataset(client);
+ if (*rdatasetp == NULL)
+ return (DNS_R_SERVFAIL);
+ }
+ return (ISC_R_SUCCESS);
+}
+
+static void
+rpz_st_clear(ns_client_t *client) {
+ dns_rpz_st_t *st = client->query.rpz_st;
+
+ rpz_clean(&st->m.zone, &st->m.db, &st->m.node, NULL);
+ if (st->m.rdataset != NULL)
+ query_putrdataset(client, &st->m.rdataset);
+
+ rpz_clean(NULL, &st->ns.db, NULL, NULL);
+ if (st->ns.ns_rdataset != NULL)
+ query_putrdataset(client, &st->ns.ns_rdataset);
+ if (st->ns.r_rdataset != NULL)
+ query_putrdataset(client, &st->ns.r_rdataset);
+
+ rpz_clean(&st->q.zone, &st->q.db, &st->q.node, NULL);
+ if (st->q.rdataset != NULL)
+ query_putrdataset(client, &st->q.rdataset);
+ if (st->q.sigrdataset != NULL)
+ query_putrdataset(client, &st->q.sigrdataset);
+ st->state = 0;
+}
+
+/*
+ * Get NS, A, or AAAA rrset for rpz nsdname or nsip checking.
+ */
+static isc_result_t
+rpz_ns_find(ns_client_t *client, dns_name_t *name, dns_rdatatype_t type,
+ dns_db_t **dbp, dns_dbversion_t *version,
+ dns_rdataset_t **rdatasetp, isc_boolean_t resuming)
+{
+ dns_rpz_st_t *st;
+ isc_boolean_t is_zone;
+ dns_dbnode_t *node;
+ dns_fixedname_t fixed;
+ dns_name_t *found;
+ isc_result_t result;
+
+ st = client->query.rpz_st;
+ if ((st->state & DNS_RPZ_RECURSING) != 0) {
+ INSIST(st->ns.r_type == type);
+ INSIST(dns_name_equal(name, st->r_name));
+ INSIST(*rdatasetp == NULL ||
+ !dns_rdataset_isassociated(*rdatasetp));
+ st->state &= ~DNS_RPZ_RECURSING;
+ *dbp = st->ns.db;
+ st->ns.db = NULL;
+ if (*rdatasetp != NULL)
+ query_putrdataset(client, rdatasetp);
+ *rdatasetp = st->ns.r_rdataset;
+ st->ns.r_rdataset = NULL;
+ result = st->ns.r_result;
+ if (result == DNS_R_DELEGATION) {
+ rpz_fail_log(client, DNS_RPZ_ERROR_LEVEL,
+ DNS_RPZ_TYPE_NSIP, name,
+ "rpz_ns_find() ", result);
+ st->m.policy = DNS_RPZ_POLICY_ERROR;
+ result = DNS_R_SERVFAIL;
+ }
+ return (result);
+ }
+
+ result = rpz_ready(client, NULL, NULL, NULL, rdatasetp);
+ if (result != ISC_R_SUCCESS) {
+ st->m.policy = DNS_RPZ_POLICY_ERROR;
+ return (result);
+ }
+ if (*dbp != NULL) {
+ is_zone = ISC_FALSE;
+ } else {
+ dns_zone_t *zone;
+
+ version = NULL;
+ zone = NULL;
+ result = query_getdb(client, name, type, 0, &zone, dbp,
+ &version, &is_zone);
+ if (result != ISC_R_SUCCESS) {
+ rpz_fail_log(client, DNS_RPZ_ERROR_LEVEL,
+ DNS_RPZ_TYPE_NSIP, name, "NS getdb() ",
+ result);
+ st->m.policy = DNS_RPZ_POLICY_ERROR;
+ if (zone != NULL)
+ dns_zone_detach(&zone);
+ return (result);
+ }
+ if (zone != NULL)
+ dns_zone_detach(&zone);
+ }
+
+ node = NULL;
+ dns_fixedname_init(&fixed);
+ found = dns_fixedname_name(&fixed);
+ result = dns_db_find(*dbp, name, version, type, 0, client->now, &node,
+ found, *rdatasetp, NULL);
+ if (result == DNS_R_DELEGATION && is_zone && USECACHE(client)) {
+ /*
+ * Try the cache if we're authoritative for an
+ * ancestor but not the domain itself.
+ */
+ rpz_clean(NULL, dbp, &node, rdatasetp);
+ version = NULL;
+ dns_db_attach(client->view->cachedb, dbp);
+ result = dns_db_find(*dbp, name, version, dns_rdatatype_ns,
+ 0, client->now, &node, found,
+ *rdatasetp, NULL);
+ }
+ rpz_clean(NULL, dbp, &node, NULL);
+ if (result == DNS_R_DELEGATION) {
+ /*
+ * Recurse to get NS rrset or A or AAAA rrset for an NS name.
+ */
+ rpz_clean(NULL, NULL, NULL, rdatasetp);
+ dns_name_copy(name, st->r_name, NULL);
+ result = query_recurse(client, type, st->r_name, NULL, NULL,
+ resuming);
+ if (result == ISC_R_SUCCESS) {
+ st->state |= DNS_RPZ_RECURSING;
+ result = DNS_R_DELEGATION;
+ }
+ }
+ return (result);
+}
+
+/*
+ * Check the IP address in an A or AAAA rdataset against
+ * the IP or NSIP response policy rules of a view.
+ */
+static isc_result_t
+rpz_rewrite_ip(ns_client_t *client, dns_rdataset_t *rdataset,
+ dns_rpz_type_t rpz_type)
+{
+ dns_rpz_st_t *st;
+ dns_dbversion_t *version;
+ dns_zone_t *zone;
+ dns_db_t *db;
+ dns_rpz_zone_t *new_rpz;
+ isc_result_t result;
+
+ st = client->query.rpz_st;
+ if (st->m.rdataset == NULL) {
+ st->m.rdataset = query_newrdataset(client);
+ if (st->m.rdataset == NULL)
+ return (DNS_R_SERVFAIL);
+ }
+ zone = NULL;
+ db = NULL;
+ for (new_rpz = ISC_LIST_HEAD(client->view->rpz_zones);
+ new_rpz != NULL;
+ new_rpz = ISC_LIST_NEXT(new_rpz, link)) {
+ version = NULL;
+
+ /*
+ * Find the database for this policy zone to get its
+ * radix tree.
+ */
+ result = rpz_getdb(client, rpz_type, &new_rpz->origin,
+ &zone, &db, &version);
+ if (result != ISC_R_SUCCESS) {
+ rpz_clean(&zone, &db, NULL, NULL);
+ continue;
+ }
+ /*
+ * Look for a better (e.g. longer prefix) hit for an IP address
+ * in this rdataset in this radix tree than than the previous
+ * hit, if any. Note the domain name and quality of the
+ * best hit.
+ */
+ result = dns_db_rpz_findips(new_rpz, rpz_type, zone, db,
+ version, rdataset, st);
+ RUNTIME_CHECK(result == ISC_R_SUCCESS);
+ rpz_clean(&zone, &db, NULL, NULL);
+ }
+ return (ISC_R_SUCCESS);
+}
+
+static isc_result_t
+rpz_rewrite_nsip(ns_client_t *client, dns_rdatatype_t type, dns_name_t *name,
+ dns_db_t **dbp, dns_dbversion_t *version,
+ dns_rdataset_t **rdatasetp, isc_boolean_t resuming)
+{
+ isc_result_t result;
+
+ result = rpz_ns_find(client, name, type, dbp, version, rdatasetp,
+ resuming);
+ switch (result) {
+ case ISC_R_SUCCESS:
+ result = rpz_rewrite_ip(client, *rdatasetp, DNS_RPZ_TYPE_NSIP);
+ break;
+ case DNS_R_EMPTYNAME:
+ case DNS_R_EMPTYWILD:
+ case DNS_R_NXDOMAIN:
+ case DNS_R_NCACHENXDOMAIN:
+ case DNS_R_NXRRSET:
+ case DNS_R_NCACHENXRRSET:
+ result = ISC_R_SUCCESS;
+ break;
+ case DNS_R_DELEGATION:
+ case DNS_R_DUPLICATE:
+ case DNS_R_DROP:
+ break;
+ default:
+ if (client->query.rpz_st->m.policy != DNS_RPZ_POLICY_ERROR) {
+ client->query.rpz_st->m.policy = DNS_RPZ_POLICY_ERROR;
+ rpz_fail_log(client, ISC_LOG_WARNING, DNS_RPZ_TYPE_NSIP,
+ name, "NS address rewrite nsip ", result);
+ }
+ break;
+ }
+ return (result);
+}
+
+/*
+ * Get the rrset from a response policy zone.
+ */
+static isc_result_t
+rpz_find(ns_client_t *client, dns_rdatatype_t qtype, dns_name_t *qnamef,
+ dns_name_t *sname, dns_rpz_type_t rpz_type, dns_zone_t **zonep,
+ dns_db_t **dbp, dns_dbnode_t **nodep, dns_rdataset_t **rdatasetp,
+ dns_rpz_policy_t *policyp)
+{
+ dns_dbversion_t *version;
+ dns_rpz_policy_t policy;
+ dns_fixedname_t fixed;
+ dns_name_t *found;
+ isc_result_t result;
+
+ result = rpz_ready(client, zonep, dbp, nodep, rdatasetp);
+ if (result != ISC_R_SUCCESS) {
+ *policyp = DNS_RPZ_POLICY_ERROR;
+ return (result);
+ }
+
+ /*
+ * Try to get either a CNAME or the type of record demanded by the
+ * request from the policy zone.
+ */
+ version = NULL;
+ result = rpz_getdb(client, rpz_type, qnamef, zonep, dbp, &version);
+ if (result != ISC_R_SUCCESS) {
+ *policyp = DNS_RPZ_POLICY_MISS;
+ return (DNS_R_NXDOMAIN);
+ }
+
+ dns_fixedname_init(&fixed);
+ found = dns_fixedname_name(&fixed);
+ result = dns_db_find(*dbp, qnamef, version, dns_rdatatype_any, 0,
+ client->now, nodep, found, *rdatasetp, NULL);
+ if (result == ISC_R_SUCCESS) {
+ dns_rdatasetiter_t *rdsiter;
+
+ rdsiter = NULL;
+ result = dns_db_allrdatasets(*dbp, *nodep, version, 0,
+ &rdsiter);
+ if (result != ISC_R_SUCCESS) {
+ dns_db_detachnode(*dbp, nodep);
+ rpz_fail_log(client, DNS_RPZ_ERROR_LEVEL, rpz_type,
+ qnamef, "allrdatasets()", result);
+ *policyp = DNS_RPZ_POLICY_ERROR;
+ return (DNS_R_SERVFAIL);
+ }
+ for (result = dns_rdatasetiter_first(rdsiter);
+ result == ISC_R_SUCCESS;
+ result = dns_rdatasetiter_next(rdsiter)) {
+ dns_rdatasetiter_current(rdsiter, *rdatasetp);
+ if ((*rdatasetp)->type == dns_rdatatype_cname ||
+ (*rdatasetp)->type == qtype)
+ break;
+ dns_rdataset_disassociate(*rdatasetp);
+ }
+ dns_rdatasetiter_destroy(&rdsiter);
+ if (result != ISC_R_SUCCESS) {
+ if (result != ISC_R_NOMORE) {
+ rpz_fail_log(client, DNS_RPZ_ERROR_LEVEL,
+ rpz_type, qnamef, "rdatasetiter",
+ result);
+ *policyp = DNS_RPZ_POLICY_ERROR;
+ return (DNS_R_SERVFAIL);
+ }
+ /*
+ * Ask again to get the right DNS_R_DNAME/NXRRSET/...
+ * result if there is neither a CNAME nor target type.
+ */
+ if (dns_rdataset_isassociated(*rdatasetp))
+ dns_rdataset_disassociate(*rdatasetp);
+ dns_db_detachnode(*dbp, nodep);
+
+ if (qtype == dns_rdatatype_rrsig ||
+ qtype == dns_rdatatype_sig)
+ result = DNS_R_NXRRSET;
+ else
+ result = dns_db_find(*dbp, qnamef, version,
+ qtype, 0, client->now,
+ nodep, found, *rdatasetp,
+ NULL);
+ }
+ }
+ switch (result) {
+ case ISC_R_SUCCESS:
+ if ((*rdatasetp)->type != dns_rdatatype_cname) {
+ policy = DNS_RPZ_POLICY_RECORD;
+ } else {
+ policy = dns_rpz_decode_cname(*rdatasetp, sname);
+ if (policy == DNS_RPZ_POLICY_RECORD &&
+ qtype != dns_rdatatype_cname &&
+ qtype != dns_rdatatype_any)
+ result = DNS_R_CNAME;
+ }
+ break;
+ case DNS_R_DNAME:
+ /*
+ * DNAME policy RRs have very few if any uses that are not
+ * better served with simple wildcards. Making the work would
+ * require complications to get the number of labels matched
+ * in the name or the found name itself to the main DNS_R_DNAME
+ * case in query_find(). So fall through to treat them as NODATA.
+ */
+ case DNS_R_NXRRSET:
+ policy = DNS_RPZ_POLICY_NODATA;
+ break;
+ case DNS_R_NXDOMAIN:
+ case DNS_R_EMPTYNAME:
+ /*
+ * If we don't get a qname hit,
+ * see if it is worth looking for other types.
+ */
+ dns_db_rpz_enabled(*dbp, client->query.rpz_st);
+ dns_db_detach(dbp);
+ dns_zone_detach(zonep);
+ policy = DNS_RPZ_POLICY_MISS;
+ break;
+ default:
+ dns_db_detach(dbp);
+ dns_zone_detach(zonep);
+ rpz_fail_log(client, DNS_RPZ_ERROR_LEVEL, rpz_type, qnamef,
+ "", result);
+ policy = DNS_RPZ_POLICY_ERROR;
+ result = DNS_R_SERVFAIL;
+ break;
+ }
+
+ *policyp = policy;
+ return (result);
+}
+
+/*
+ * Build and look for a QNAME or NSDNAME owner name in a response policy zone.
+ */
+static isc_result_t
+rpz_rewrite_name(ns_client_t *client, dns_rdatatype_t qtype, dns_name_t *qname,
+ dns_rpz_type_t rpz_type, dns_rdataset_t **rdatasetp)
+{
+ dns_rpz_st_t *st;
+ dns_rpz_zone_t *rpz;
+ dns_fixedname_t prefixf, rpz_qnamef;
+ dns_name_t *prefix, *suffix, *rpz_qname;
+ dns_zone_t *zone;
+ dns_db_t *db;
+ dns_dbnode_t *node;
+ dns_rpz_policy_t policy;
+ unsigned int labels;
+ isc_result_t result;
+
+ st = client->query.rpz_st;
+ zone = NULL;
+ db = NULL;
+ node = NULL;
+
+ for (rpz = ISC_LIST_HEAD(client->view->rpz_zones);
+ rpz != NULL;
+ rpz = ISC_LIST_NEXT(rpz, link)) {
+ /*
+ * Construct the rule's owner name.
+ */
+ dns_fixedname_init(&prefixf);
+ prefix = dns_fixedname_name(&prefixf);
+ dns_name_split(qname, 1, prefix, NULL);
+ if (rpz_type == DNS_RPZ_TYPE_NSDNAME)
+ suffix = &rpz->nsdname;
+ else
+ suffix = &rpz->origin;
+ dns_fixedname_init(&rpz_qnamef);
+ rpz_qname = dns_fixedname_name(&rpz_qnamef);
+ for (;;) {
+ result = dns_name_concatenate(prefix, suffix,
+ rpz_qname, NULL);
+ if (result == ISC_R_SUCCESS)
+ break;
+ INSIST(result == DNS_R_NAMETOOLONG);
+ labels = dns_name_countlabels(prefix);
+ if (labels < 2) {
+ rpz_fail_log(client, DNS_RPZ_ERROR_LEVEL,
+ rpz_type, suffix,
+ "concatentate() ", result);
+ return (ISC_R_SUCCESS);
+ }
+ if (labels+1 == dns_name_countlabels(qname)) {
+ rpz_fail_log(client, DNS_RPZ_DEBUG_LEVEL1,
+ rpz_type, suffix,
+ "concatentate() ", result);
+ }
+ dns_name_split(prefix, labels - 1, NULL, prefix);
+ }
+
+ /*
+ * See if the qname rule (or RR) exists.
+ */
+ result = rpz_find(client, qtype, rpz_qname, qname, rpz_type,
+ &zone, &db, &node, rdatasetp, &policy);
+ switch (result) {
+ case DNS_R_NXDOMAIN:
+ case DNS_R_EMPTYNAME:
+ break;
+ case DNS_R_SERVFAIL:
+ rpz_clean(&zone, &db, &node, rdatasetp);
+ st->m.policy = DNS_RPZ_POLICY_ERROR;
+ return (DNS_R_SERVFAIL);
+ default:
+ /*
+ * when more than one name or address hits a rule,
+ * prefer the first set of names (qname or NS),
+ * the first policy zone, and the smallest name
+ */
+ if (st->m.type == rpz_type &&
+ rpz->num > st->m.rpz->num &&
+ 0 <= dns_name_compare(rpz_qname, st->qname))
+ continue;
+ rpz_clean(&st->m.zone, &st->m.db, &st->m.node,
+ &st->m.rdataset);
+ st->m.rpz = rpz;
+ st->m.type = rpz_type;
+ st->m.prefix = 0;
+ st->m.policy = policy;
+ st->m.result = result;
+ dns_name_copy(rpz_qname, st->qname, NULL);
+ if (dns_rdataset_isassociated(*rdatasetp)) {
+ dns_rdataset_t *trdataset;
+
+ trdataset = st->m.rdataset;
+ st->m.rdataset = *rdatasetp;
+ *rdatasetp = trdataset;
+ st->m.ttl = st->m.rdataset->ttl;
+ } else {
+ st->m.ttl = DNS_RPZ_TTL_DEFAULT;
+ }
+ st->m.node = node;
+ node = NULL;
+ st->m.db = db;
+ db = NULL;
+ st->m.zone = zone;
+ zone = NULL;
+ }
+ }
+
+ rpz_clean(&zone, &db, &node, rdatasetp);
+ return (ISC_R_SUCCESS);
+}
+
+/*
+ * Look for response policy zone NSIP and NSDNAME rewriting.
+ */
+static isc_result_t
+rpz_rewrite(ns_client_t *client, dns_rdatatype_t qtype,
+ isc_boolean_t resuming)
+{
+ dns_rpz_st_t *st;
+ dns_db_t *ipdb;
+ dns_rdataset_t *rdataset;
+ dns_fixedname_t nsnamef;
+ dns_name_t *nsname;
+ dns_dbversion_t *version;
+ isc_result_t result;
+
+ ipdb = NULL;
+ rdataset = NULL;
+
+ st = client->query.rpz_st;
+ if (st == NULL) {
+ st = isc_mem_get(client->mctx, sizeof(*st));
+ if (st == NULL)
+ return (ISC_R_NOMEMORY);
+ st->state = 0;
+ memset(&st->m, 0, sizeof(st->m));
+ memset(&st->ns, 0, sizeof(st->ns));
+ memset(&st->q, 0, sizeof(st->q));
+ dns_fixedname_init(&st->_qnamef);
+ dns_fixedname_init(&st->_r_namef);
+ dns_fixedname_init(&st->_fnamef);
+ st->qname = dns_fixedname_name(&st->_qnamef);
+ st->r_name = dns_fixedname_name(&st->_r_namef);
+ st->fname = dns_fixedname_name(&st->_fnamef);
+ client->query.rpz_st = st;
+ }
+ if ((st->state & DNS_RPZ_DONE_QNAME) == 0) {
+ st->state = DNS_RPZ_DONE_QNAME;
+ st->m.type = DNS_RPZ_TYPE_BAD;
+ st->m.policy = DNS_RPZ_POLICY_MISS;
+
+ /*
+ * Check rules for the name if this it the first time,
+ * i.e. we've not been recursing.
+ */
+ result = DNS_R_SERVFAIL;
+ st->state &= ~(DNS_RPZ_HAVE_IP | DNS_RPZ_HAVE_NSIPv4 |
+ DNS_RPZ_HAVE_NSIPv6 | DNS_RPZ_HAD_NSDNAME);
+ result = rpz_rewrite_name(client, qtype, client->query.qname,
+ DNS_RPZ_TYPE_QNAME, &rdataset);
+ if (st->m.policy != DNS_RPZ_POLICY_MISS)
+ goto cleanup;
+ if ((st->state & (DNS_RPZ_HAVE_NSIPv4 | DNS_RPZ_HAVE_NSIPv6 |
+ DNS_RPZ_HAD_NSDNAME)) == 0)
+ goto cleanup;
+ st->ns.label = dns_name_countlabels(client->query.qname);
+ }
+
+ dns_fixedname_init(&nsnamef);
+ dns_name_clone(client->query.qname, dns_fixedname_name(&nsnamef));
+ while (st->ns.label > 1 && st->m.policy == DNS_RPZ_POLICY_MISS) {
+ if (st->ns.label == dns_name_countlabels(client->query.qname)) {
+ nsname = client->query.qname;
+ } else {
+ nsname = dns_fixedname_name(&nsnamef);
+ dns_name_split(client->query.qname, st->ns.label,
+ NULL, nsname);
+ }
+ if (st->ns.ns_rdataset == NULL ||
+ !dns_rdataset_isassociated(st->ns.ns_rdataset)) {
+ dns_db_t *db = NULL;
+ result = rpz_ns_find(client, nsname, dns_rdatatype_ns,
+ &db, NULL, &st->ns.ns_rdataset,
+ resuming);
+ if (db != NULL)
+ dns_db_detach(&db);
+ if (result != ISC_R_SUCCESS) {
+ if (result == DNS_R_DELEGATION)
+ goto cleanup;
+ if (result == DNS_R_EMPTYNAME ||
+ result == DNS_R_NXRRSET ||
+ result == DNS_R_EMPTYWILD ||
+ result == DNS_R_NXDOMAIN ||
+ result == DNS_R_NCACHENXDOMAIN ||
+ result == DNS_R_NCACHENXRRSET ||
+ result == DNS_R_CNAME ||
+ result == DNS_R_DNAME) {
+ rpz_fail_log(client,
+ DNS_RPZ_DEBUG_LEVEL2,
+ DNS_RPZ_TYPE_NSIP, nsname,
+ "NS db_find() ", result);
+ dns_rdataset_disassociate(st->ns.
+ ns_rdataset);
+ st->ns.label--;
+ continue;
+ }
+ if (st->m.policy != DNS_RPZ_POLICY_ERROR) {
+ rpz_fail_log(client, DNS_RPZ_INFO_LEVEL,
+ DNS_RPZ_TYPE_NSIP, nsname,
+ "NS db_find() ", result);
+ st->m.policy = DNS_RPZ_POLICY_ERROR;
+ }
+ goto cleanup;
+ }
+ result = dns_rdataset_first(st->ns.ns_rdataset);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+ }
+ /*
+ * Check all NS names.
+ */
+ do {
+ dns_rdata_ns_t ns;
+ dns_rdata_t nsrdata = DNS_RDATA_INIT;
+
+ dns_rdataset_current(st->ns.ns_rdataset, &nsrdata);
+ result = dns_rdata_tostruct(&nsrdata, &ns, NULL);
+ dns_rdata_reset(&nsrdata);
+ if (result != ISC_R_SUCCESS) {
+ rpz_fail_log(client, DNS_RPZ_ERROR_LEVEL,
+ DNS_RPZ_TYPE_NSIP, nsname,
+ "rdata_tostruct() ", result);
+ st->m.policy = DNS_RPZ_POLICY_ERROR;
+ goto cleanup;
+ }
+ if ((st->state & DNS_RPZ_HAD_NSDNAME) != 0) {
+ result = rpz_rewrite_name(client, qtype,
+ &ns.name,
+ DNS_RPZ_TYPE_NSDNAME,
+ &rdataset);
+ if (result != ISC_R_SUCCESS) {
+ dns_rdata_freestruct(&ns);
+ goto cleanup;
+ }
+ }
+ /*
+ * Check all IP addresses for this NS name, but don't
+ * bother without NSIP rules or with a NSDNAME hit.
+ */
+ version = NULL;
+ if ((st->state & DNS_RPZ_HAVE_NSIPv4) != 0 &&
+ st->m.type != DNS_RPZ_TYPE_NSDNAME &&
+ (st->state & DNS_RPZ_DONE_A) == 0) {
+ result = rpz_rewrite_nsip(client,
+ dns_rdatatype_a,
+ &ns.name, &ipdb,
+ version, &rdataset,
+ resuming);
+ if (result == ISC_R_SUCCESS)
+ st->state |= DNS_RPZ_DONE_A;
+ }
+ if (result == ISC_R_SUCCESS &&
+ (st->state & DNS_RPZ_HAVE_NSIPv6) != 0 &&
+ st->m.type != DNS_RPZ_TYPE_NSDNAME) {
+ result = rpz_rewrite_nsip(client,
+ dns_rdatatype_aaaa,
+ &ns.name, &ipdb, version,
+ &rdataset, resuming);
+ }
+ dns_rdata_freestruct(&ns);
+ if (ipdb != NULL)
+ dns_db_detach(&ipdb);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+ st->state &= ~DNS_RPZ_DONE_A;
+ result = dns_rdataset_next(st->ns.ns_rdataset);
+ } while (result == ISC_R_SUCCESS);
+ dns_rdataset_disassociate(st->ns.ns_rdataset);
+ st->ns.label--;
+ }
+
+ /*
+ * Use the best, if any, hit.
+ */
+ result = ISC_R_SUCCESS;
+
+cleanup:
+ if (st->m.policy != DNS_RPZ_POLICY_MISS &&
+ st->m.policy != DNS_RPZ_POLICY_NO_OP &&
+ st->m.policy != DNS_RPZ_POLICY_ERROR &&
+ st->m.rpz->policy != DNS_RPZ_POLICY_GIVEN)
+ st->m.policy = st->m.rpz->policy;
+ if (st->m.policy == DNS_RPZ_POLICY_NO_OP)
+ rpz_log(client);
+ if (st->m.policy == DNS_RPZ_POLICY_MISS ||
+ st->m.policy == DNS_RPZ_POLICY_NO_OP ||
+ st->m.policy == DNS_RPZ_POLICY_ERROR)
+ rpz_clean(&st->m.zone, &st->m.db, &st->m.node, &st->m.rdataset);
+ if (st->m.policy != DNS_RPZ_POLICY_MISS)
+ st->state |= DNS_RPZ_REWRITTEN;
+ if (st->m.policy == DNS_RPZ_POLICY_ERROR) {
+ st->m.type = DNS_RPZ_TYPE_BAD;
+ result = DNS_R_SERVFAIL;
+ }
+ if (rdataset != NULL)
+ query_putrdataset(client, &rdataset);
+ if ((st->state & DNS_RPZ_RECURSING) == 0) {
+ rpz_clean(NULL, &st->ns.db, NULL, &st->ns.ns_rdataset);
+ }
+
+ return (result);
+}
+
#define MAX_RESTARTS 16
#define QUERY_ERROR(r) \
@@ -3698,6 +4848,99 @@ query_findclosestnsec3(dns_name_t *qname, dns_db_t *db,
return;
}
+#ifdef ALLOW_FILTER_AAAA_ON_V4
+static isc_boolean_t
+is_v4_client(ns_client_t *client) {
+ if (isc_sockaddr_pf(&client->peeraddr) == AF_INET)
+ return (ISC_TRUE);
+ if (isc_sockaddr_pf(&client->peeraddr) == AF_INET6 &&
+ IN6_IS_ADDR_V4MAPPED(&client->peeraddr.type.sin6.sin6_addr))
+ return (ISC_TRUE);
+ return (ISC_FALSE);
+}
+#endif
+
+static isc_uint32_t
+dns64_ttl(dns_db_t *db, dns_dbversion_t *version) {
+ dns_dbnode_t *node = NULL;
+ dns_rdata_soa_t soa;
+ dns_rdata_t rdata = DNS_RDATA_INIT;
+ dns_rdataset_t rdataset;
+ isc_result_t result;
+ isc_uint32_t ttl = ISC_UINT32_MAX;
+
+ result = dns_db_getoriginnode(db, &node);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+ dns_rdataset_init(&rdataset);
+ result = dns_db_findrdataset(db, node, version, dns_rdatatype_soa,
+ 0, 0, &rdataset, NULL);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+ result = dns_rdataset_first(&rdataset);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+
+ dns_rdataset_current(&rdataset, &rdata);
+ result = dns_rdata_tostruct(&rdata, &soa, NULL);
+ RUNTIME_CHECK(result == ISC_R_SUCCESS);
+ ttl = ISC_MIN(rdataset.ttl, soa.minimum);
+
+cleanup:
+ if (dns_rdataset_isassociated(&rdataset))
+ dns_rdataset_disassociate(&rdataset);
+ if (node != NULL)
+ dns_db_detachnode(db, &node);
+ return (ttl);
+}
+
+static isc_boolean_t
+dns64_aaaaok(ns_client_t *client, dns_rdataset_t *rdataset,
+ dns_rdataset_t *sigrdataset)
+{
+ isc_netaddr_t netaddr;
+ dns_dns64_t *dns64 = ISC_LIST_HEAD(client->view->dns64);
+ unsigned int flags = 0;
+ unsigned int i, count;
+ isc_boolean_t *aaaaok;
+
+ INSIST(client->query.dns64_aaaaok == NULL);
+ INSIST(client->query.dns64_aaaaoklen == 0);
+ INSIST(client->query.dns64_aaaa == NULL);
+ INSIST(client->query.dns64_sigaaaa == NULL);
+
+ if (dns64 == NULL)
+ return (ISC_TRUE);
+
+ if (RECURSIONOK(client))
+ flags |= DNS_DNS64_RECURSIVE;
+
+ if (sigrdataset != NULL && dns_rdataset_isassociated(sigrdataset))
+ flags |= DNS_DNS64_DNSSEC;
+
+ count = dns_rdataset_count(rdataset);
+ aaaaok = isc_mem_get(client->mctx, sizeof(isc_boolean_t) * count);
+
+ isc_netaddr_fromsockaddr(&netaddr, &client->peeraddr);
+ if (dns_dns64_aaaaok(dns64, &netaddr, client->signer,
+ &ns_g_server->aclenv, flags, rdataset,
+ aaaaok, count)) {
+ for (i = 0; i < count; i++) {
+ if (aaaaok != NULL && !aaaaok[i]) {
+ client->query.dns64_aaaaok = aaaaok;
+ client->query.dns64_aaaaoklen = count;
+ break;
+ }
+ }
+ if (i == count)
+ isc_mem_put(client->mctx, aaaaok,
+ sizeof(isc_boolean_t) * count);
+ return (ISC_TRUE);
+ }
+ isc_mem_put(client->mctx, aaaaok, sizeof(isc_boolean_t) * count);
+ return (ISC_FALSE);
+}
+
/*
* Do the bulk of query processing for the current query of 'client'.
* If 'event' is non-NULL, we are returning from recursion and 'qtype'
@@ -3716,6 +4959,7 @@ query_find(ns_client_t *client, dns_fetchevent_t *event, dns_rdatatype_t qtype)
dns_rdata_t rdata = DNS_RDATA_INIT;
dns_rdatasetiter_t *rdsiter;
isc_boolean_t want_restart, authoritative, is_zone, need_wildcardproof;
+ isc_boolean_t is_staticstub_zone;
unsigned int n, nlabels;
dns_namereln_t namereln;
int order;
@@ -3731,8 +4975,10 @@ query_find(ns_client_t *client, dns_fetchevent_t *event, dns_rdatatype_t qtype)
unsigned int options;
isc_boolean_t empty_wild;
dns_rdataset_t *noqname;
+ dns_rpz_st_t *rpz_st;
isc_boolean_t resuming;
int line = -1;
+ isc_boolean_t dns64_exclude, dns64;
CTRACE("query_find");
@@ -3758,28 +5004,67 @@ query_find(ns_client_t *client, dns_fetchevent_t *event, dns_rdatatype_t qtype)
zone = NULL;
need_wildcardproof = ISC_FALSE;
empty_wild = ISC_FALSE;
+ dns64_exclude = dns64 = ISC_FALSE;
options = 0;
resuming = ISC_FALSE;
is_zone = ISC_FALSE;
+ is_staticstub_zone = ISC_FALSE;
if (event != NULL) {
/*
* We're returning from recursion. Restore the query context
* and resume.
*/
-
want_restart = ISC_FALSE;
- authoritative = ISC_FALSE;
- qtype = event->qtype;
+ rpz_st = client->query.rpz_st;
+ if (rpz_st != NULL &&
+ (rpz_st->state & DNS_RPZ_RECURSING) != 0) {
+ is_zone = rpz_st->q.is_zone;
+ authoritative = rpz_st->q.authoritative;
+ zone = rpz_st->q.zone;
+ rpz_st->q.zone = NULL;
+ node = rpz_st->q.node;
+ rpz_st->q.node = NULL;
+ db = rpz_st->q.db;
+ rpz_st->q.db = NULL;
+ rdataset = rpz_st->q.rdataset;
+ rpz_st->q.rdataset = NULL;
+ sigrdataset = rpz_st->q.sigrdataset;
+ rpz_st->q.sigrdataset = NULL;
+ qtype = rpz_st->q.qtype;
+
+ if (event->node != NULL)
+ dns_db_detachnode(db, &event->node);
+ rpz_st->ns.db = event->db;
+ rpz_st->ns.r_type = event->qtype;
+ rpz_st->ns.r_rdataset = event->rdataset;
+ if (event->sigrdataset != NULL &&
+ dns_rdataset_isassociated(event->sigrdataset))
+ dns_rdataset_disassociate(event->sigrdataset);
+ } else {
+ authoritative = ISC_FALSE;
+
+ qtype = event->qtype;
+ db = event->db;
+ node = event->node;
+ rdataset = event->rdataset;
+ sigrdataset = event->sigrdataset;
+ }
+
if (qtype == dns_rdatatype_rrsig || qtype == dns_rdatatype_sig)
type = dns_rdatatype_any;
else
type = qtype;
- db = event->db;
- node = event->node;
- rdataset = event->rdataset;
- sigrdataset = event->sigrdataset;
+
+ if (DNS64(client)) {
+ client->query.attributes &= ~NS_QUERYATTR_DNS64;
+ dns64 = ISC_TRUE;
+ }
+ if (DNS64EXCLUDE(client)) {
+ client->query.attributes &= ~NS_QUERYATTR_DNS64EXCLUDE;
+ dns64_exclude = ISC_TRUE;
+ }
/*
* We'll need some resources...
@@ -3794,16 +5079,26 @@ query_find(ns_client_t *client, dns_fetchevent_t *event, dns_rdatatype_t qtype)
QUERY_ERROR(DNS_R_SERVFAIL);
goto cleanup;
}
- tname = dns_fixedname_name(&event->foundname);
+ if (rpz_st != NULL &&
+ (rpz_st->state & DNS_RPZ_RECURSING) != 0) {
+ tname = rpz_st->fname;
+ } else {
+ tname = dns_fixedname_name(&event->foundname);
+ }
result = dns_name_copy(tname, fname, NULL);
if (result != ISC_R_SUCCESS) {
QUERY_ERROR(DNS_R_SERVFAIL);
goto cleanup;
}
-
- result = event->result;
+ if (rpz_st != NULL &&
+ (rpz_st->state & DNS_RPZ_RECURSING) != 0) {
+ rpz_st->ns.r_result = event->result;
+ result = rpz_st->q.result;
+ isc_event_free(ISC_EVENT_PTR(&event));
+ } else {
+ result = event->result;
+ }
resuming = ISC_TRUE;
-
goto resume;
}
@@ -3902,8 +5197,12 @@ query_find(ns_client_t *client, dns_fetchevent_t *event, dns_rdatatype_t qtype)
goto cleanup;
}
- if (is_zone)
+ is_staticstub_zone = ISC_FALSE;
+ if (is_zone && zone != NULL) {
authoritative = ISC_TRUE;
+ if (dns_zone_gettype(zone) == dns_zone_staticstub)
+ is_staticstub_zone = ISC_TRUE;
+ }
if (event == NULL && client->query.restarts == 0) {
if (is_zone) {
@@ -3956,6 +5255,119 @@ query_find(ns_client_t *client, dns_fetchevent_t *event, dns_rdatatype_t qtype)
resume:
CTRACE("query_find: resume");
+
+ if (!ISC_LIST_EMPTY(client->view->rpz_zones) &&
+ RECURSIONOK(client) && !RECURSING(client) &&
+ result != DNS_R_DELEGATION && result != ISC_R_NOTFOUND &&
+ (client->query.rpz_st == NULL ||
+ (client->query.rpz_st->state & DNS_RPZ_REWRITTEN) == 0) &&
+ !dns_name_equal(client->query.qname, dns_rootname)) {
+ isc_result_t rresult;
+
+ rresult = rpz_rewrite(client, qtype, resuming);
+ rpz_st = client->query.rpz_st;
+ switch (rresult) {
+ case ISC_R_SUCCESS:
+ break;
+ case DNS_R_DELEGATION:
+ /*
+ * recursing for NS names or addresses,
+ * so save the main query state
+ */
+ rpz_st->q.qtype = qtype;
+ rpz_st->q.is_zone = is_zone;
+ rpz_st->q.authoritative = authoritative;
+ rpz_st->q.zone = zone;
+ zone = NULL;
+ rpz_st->q.db = db;
+ db = NULL;
+ rpz_st->q.node = node;
+ node = NULL;
+ rpz_st->q.rdataset = rdataset;
+ rdataset = NULL;
+ rpz_st->q.sigrdataset = sigrdataset;
+ sigrdataset = NULL;
+ dns_name_copy(fname, rpz_st->fname, NULL);
+ rpz_st->q.result = result;
+ client->query.attributes |= NS_QUERYATTR_RECURSING;
+ result = ISC_R_SUCCESS;
+ goto cleanup;
+ default:
+ RECURSE_ERROR(rresult);
+ goto cleanup;
+ }
+ if (rpz_st->m.policy != DNS_RPZ_POLICY_MISS &&
+ rpz_st->m.policy != DNS_RPZ_POLICY_NO_OP) {
+ result = dns_name_copy(client->query.qname, fname,
+ NULL);
+ RUNTIME_CHECK(result == ISC_R_SUCCESS);
+ finish_rewrite:
+ rpz_clean(&zone, &db, &node, NULL);
+ if (rpz_st->m.rdataset != NULL) {
+ if (rdataset != NULL)
+ query_putrdataset(client, &rdataset);
+ rdataset = rpz_st->m.rdataset;
+ rpz_st->m.rdataset = NULL;
+ } else if (rdataset != NULL &&
+ dns_rdataset_isassociated(rdataset)) {
+ dns_rdataset_disassociate(rdataset);
+ }
+ node = rpz_st->m.node;
+ rpz_st->m.node = NULL;
+ db = rpz_st->m.db;
+ rpz_st->m.db = NULL;
+ zone = rpz_st->m.zone;
+ rpz_st->m.zone = NULL;
+
+ result = rpz_st->m.result;
+ switch (rpz_st->m.policy) {
+ case DNS_RPZ_POLICY_NXDOMAIN:
+ result = DNS_R_NXDOMAIN;
+ break;
+ case DNS_RPZ_POLICY_NODATA:
+ result = DNS_R_NXRRSET;
+ break;
+ case DNS_RPZ_POLICY_RECORD:
+ if (type == dns_rdatatype_any &&
+ result != DNS_R_CNAME &&
+ dns_rdataset_isassociated(rdataset))
+ dns_rdataset_disassociate(rdataset);
+ break;
+ case DNS_RPZ_POLICY_CNAME:
+ result = dns_name_copy(&rpz_st->m.rpz->cname,
+ fname, NULL);
+ RUNTIME_CHECK(result == ISC_R_SUCCESS);
+ query_keepname(client, fname, dbuf);
+ result = query_add_cname(client,
+ client->query.qname,
+ fname,
+ dns_trust_authanswer,
+ rpz_st->m.ttl);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+ ns_client_qnamereplace(client, fname);
+ fname = NULL;
+ client->attributes &= ~NS_CLIENTATTR_WANTDNSSEC;
+ rpz_log(client);
+ want_restart = ISC_TRUE;
+ goto cleanup;
+ default:
+ INSIST(0);
+ }
+
+ /*
+ * Turn off DNSSEC because the results of a
+ * response policy zone cannot verify.
+ */
+ client->attributes &= ~NS_CLIENTATTR_WANTDNSSEC;
+ if (sigrdataset != NULL &&
+ dns_rdataset_isassociated(sigrdataset))
+ dns_rdataset_disassociate(sigrdataset);
+ is_zone = ISC_TRUE;
+ rpz_log(client);
+ }
+ }
+
switch (result) {
case ISC_R_SUCCESS:
/*
@@ -4008,11 +5420,18 @@ query_find(ns_client_t *client, dns_fetchevent_t *event, dns_rdatatype_t qtype)
*/
if (RECURSIONOK(client)) {
result = query_recurse(client, qtype,
+ client->query.qname,
NULL, NULL, resuming);
- if (result == ISC_R_SUCCESS)
+ if (result == ISC_R_SUCCESS) {
client->query.attributes |=
NS_QUERYATTR_RECURSING;
- else
+ if (dns64)
+ client->query.attributes |=
+ NS_QUERYATTR_DNS64;
+ if (dns64_exclude)
+ client->query.attributes |=
+ NS_QUERYATTR_DNS64EXCLUDE;
+ } else
RECURSE_ERROR(result);
goto cleanup;
} else {
@@ -4143,12 +5562,22 @@ query_find(ns_client_t *client, dns_fetchevent_t *event, dns_rdatatype_t qtype)
}
} else {
if (zfname != NULL &&
- !dns_name_issubdomain(fname, zfname)) {
+ (!dns_name_issubdomain(fname, zfname) ||
+ (is_staticstub_zone &&
+ dns_name_equal(fname, zfname)))) {
/*
- * We've already got a delegation from
- * authoritative data, and it is better
- * than what we found in the cache. Use
- * it instead of the cache delegation.
+ * In the following cases use "authoritative"
+ * data instead of the cache delegation:
+ * 1. We've already got a delegation from
+ * authoritative data, and it is better
+ * than what we found in the cache.
+ * 2. The query name matches the origin name
+ * of a static-stub zone. This needs to be
+ * considered for the case where the NS of
+ * the static-stub zone and the cached NS
+ * are different. We still need to contact
+ * the nameservers configured in the
+ * static-stub zone.
*/
query_releasename(client, &fname);
fname = zfname;
@@ -4183,15 +5612,31 @@ query_find(ns_client_t *client, dns_fetchevent_t *event, dns_rdatatype_t qtype)
*/
if (dns_rdatatype_atparent(type))
result = query_recurse(client, qtype,
- NULL, NULL,
- resuming);
+ client->query.qname,
+ NULL, NULL, resuming);
+ else if (dns64)
+ result = query_recurse(client,
+ dns_rdatatype_a,
+ client->query.qname,
+ NULL, NULL, resuming);
else
result = query_recurse(client, qtype,
- fname, rdataset,
- resuming);
- if (result == ISC_R_SUCCESS)
+ client->query.qname,
+ fname, rdataset,
+ resuming);
+
+ if (result == ISC_R_SUCCESS) {
client->query.attributes |=
NS_QUERYATTR_RECURSING;
+ if (dns64)
+ client->query.attributes |=
+ NS_QUERYATTR_DNS64;
+ if (dns64_exclude)
+ client->query.attributes |=
+ NS_QUERYATTR_DNS64EXCLUDE;
+ } else if (result == DNS_R_DUPLICATE ||
+ result == DNS_R_DROP)
+ QUERY_ERROR(result);
else
RECURSE_ERROR(result);
} else {
@@ -4231,11 +5676,75 @@ query_find(ns_client_t *client, dns_fetchevent_t *event, dns_rdatatype_t qtype)
}
}
goto cleanup;
+
case DNS_R_EMPTYNAME:
- result = DNS_R_NXRRSET;
- /* FALLTHROUGH */
case DNS_R_NXRRSET:
+ nxrrset:
INSIST(is_zone);
+
+#ifdef dns64_bis_return_excluded_addresses
+ if (dns64)
+#else
+ if (dns64 && !dns64_exclude)
+#endif
+ {
+ /*
+ * Restore the answers from the previous AAAA lookup.
+ */
+ if (rdataset != NULL)
+ query_putrdataset(client, &rdataset);
+ if (sigrdataset != NULL)
+ query_putrdataset(client, &sigrdataset);
+ rdataset = client->query.dns64_aaaa;
+ sigrdataset = client->query.dns64_sigaaaa;
+ if (fname == NULL) {
+ dbuf = query_getnamebuf(client);
+ if (dbuf == NULL) {
+ QUERY_ERROR(DNS_R_SERVFAIL);
+ goto cleanup;
+ }
+ fname = query_newname(client, dbuf, &b);
+ if (fname == NULL) {
+ QUERY_ERROR(DNS_R_SERVFAIL);
+ goto cleanup;
+ }
+ }
+ dns_name_copy(client->query.qname, fname, NULL);
+ client->query.dns64_aaaa = NULL;
+ client->query.dns64_sigaaaa = NULL;
+ dns64 = ISC_FALSE;
+#ifdef dns64_bis_return_excluded_addresses
+ /*
+ * Resume the diverted processing of the AAAA response?
+ */
+ if (dns64_excluded)
+ break;
+#endif
+ } else if (result == DNS_R_NXRRSET &&
+ !ISC_LIST_EMPTY(client->view->dns64) &&
+ client->message->rdclass == dns_rdataclass_in &&
+ qtype == dns_rdatatype_aaaa)
+ {
+ /*
+ * Look to see if there are A records for this
+ * name.
+ */
+ INSIST(client->query.dns64_aaaa == NULL);
+ INSIST(client->query.dns64_sigaaaa == NULL);
+ client->query.dns64_aaaa = rdataset;
+ client->query.dns64_sigaaaa = sigrdataset;
+ client->query.dns64_ttl = dns64_ttl(db, version);
+ query_releasename(client, &fname);
+ dns_db_detachnode(db, &node);
+ rdataset = NULL;
+ sigrdataset = NULL;
+ type = qtype = dns_rdatatype_a;
+ dns64 = ISC_TRUE;
+ goto db_find;
+ }
+
+ result = DNS_R_NXRRSET;
+
/*
* Look for a NSEC3 record if we don't have a NSEC record.
*/
@@ -4258,10 +5767,8 @@ query_find(ns_client_t *client, dns_fetchevent_t *event, dns_rdatatype_t qtype)
* instead? If so add the nearest to the
* closest provable encloser.
*/
- if (found &&
- dns_rdataset_isassociated(rdataset) &&
- !dns_name_equal(qname, found))
- {
+ if (dns_rdataset_isassociated(rdataset) &&
+ !dns_name_equal(qname, found)) {
unsigned int count;
unsigned int skip;
@@ -4328,7 +5835,7 @@ query_find(ns_client_t *client, dns_fetchevent_t *event, dns_rdatatype_t qtype)
/*
* Add SOA.
*/
- result = query_addsoa(client, db, version, ISC_FALSE,
+ result = query_addsoa(client, db, version, ISC_UINT32_MAX,
dns_rdataset_isassociated(rdataset));
if (result != ISC_R_SUCCESS) {
QUERY_ERROR(result);
@@ -4377,10 +5884,11 @@ query_find(ns_client_t *client, dns_fetchevent_t *event, dns_rdatatype_t qtype)
zone != NULL &&
#endif
dns_zone_getzeronosoattl(zone))
- result = query_addsoa(client, db, version, ISC_TRUE,
+ result = query_addsoa(client, db, version, 0,
dns_rdataset_isassociated(rdataset));
else
- result = query_addsoa(client, db, version, ISC_FALSE,
+ result = query_addsoa(client, db, version,
+ ISC_UINT32_MAX,
dns_rdataset_isassociated(rdataset));
if (result != ISC_R_SUCCESS) {
QUERY_ERROR(result);
@@ -4411,6 +5919,7 @@ query_find(ns_client_t *client, dns_fetchevent_t *event, dns_rdatatype_t qtype)
case DNS_R_NCACHENXDOMAIN:
case DNS_R_NCACHENXRRSET:
+ ncache_nxrrset:
INSIST(!is_zone);
authoritative = ISC_FALSE;
/*
@@ -4426,6 +5935,74 @@ query_find(ns_client_t *client, dns_fetchevent_t *event, dns_rdatatype_t qtype)
client->message->rdclass == dns_rdataclass_in &&
dns_name_countlabels(fname) == 7)
warn_rfc1918(client, fname, rdataset);
+
+#ifdef dns64_bis_return_excluded_addresses
+ if (dns64)
+#else
+ if (dns64 && !dns64_exclude)
+#endif
+ {
+ /*
+ * Restore the answers from the previous AAAA lookup.
+ */
+ if (rdataset != NULL)
+ query_putrdataset(client, &rdataset);
+ if (sigrdataset != NULL)
+ query_putrdataset(client, &sigrdataset);
+ rdataset = client->query.dns64_aaaa;
+ sigrdataset = client->query.dns64_sigaaaa;
+ if (fname == NULL) {
+ dbuf = query_getnamebuf(client);
+ if (dbuf == NULL) {
+ QUERY_ERROR(DNS_R_SERVFAIL);
+ goto cleanup;
+ }
+ fname = query_newname(client, dbuf, &b);
+ if (fname == NULL) {
+ QUERY_ERROR(DNS_R_SERVFAIL);
+ goto cleanup;
+ }
+ }
+ dns_name_copy(client->query.qname, fname, NULL);
+ client->query.dns64_aaaa = NULL;
+ client->query.dns64_sigaaaa = NULL;
+ dns64 = ISC_FALSE;
+#ifdef dns64_bis_return_excluded_addresses
+ if (dns64_excluded)
+ break;
+#endif
+ } else if (result == DNS_R_NCACHENXRRSET &&
+ !ISC_LIST_EMPTY(client->view->dns64) &&
+ client->message->rdclass == dns_rdataclass_in &&
+ qtype == dns_rdatatype_aaaa)
+ {
+ /*
+ * Look to see if there are A records for this
+ * name.
+ */
+ INSIST(client->query.dns64_aaaa == NULL);
+ INSIST(client->query.dns64_sigaaaa == NULL);
+ client->query.dns64_aaaa = rdataset;
+ client->query.dns64_sigaaaa = sigrdataset;
+ /*
+ * If the ttl is zero we need to workout if we have just
+ * decremented to zero or if there was no negative cache
+ * ttl in the answer.
+ */
+ if (rdataset->ttl != 0)
+ client->query.dns64_ttl = rdataset->ttl;
+ else if (dns_rdataset_first(rdataset) == ISC_R_SUCCESS)
+ client->query.dns64_ttl = 0;
+ query_releasename(client, &fname);
+ dns_db_detachnode(db, &node);
+ rdataset = NULL;
+ sigrdataset = NULL;
+ fname = NULL;
+ type = qtype = dns_rdatatype_a;
+ dns64 = ISC_TRUE;
+ goto db_find;
+ }
+
/*
* We don't call query_addrrset() because we don't need any
* of its extra features (and things would probably break!).
@@ -4562,11 +6139,11 @@ query_find(ns_client_t *client, dns_fetchevent_t *event, dns_rdatatype_t qtype)
dns_message_puttempname(client->message, &tname);
goto cleanup;
}
- dns_name_init(tname, NULL);
dns_name_clone(&dname.dname, tname);
dns_rdata_freestruct(&dname);
/*
- * Construct the new qname.
+ * Construct the new qname consisting of
+ * <found name prefix>.<dname target>
*/
dns_fixedname_init(&fixed);
prefix = dns_fixedname_name(&fixed);
@@ -4583,8 +6160,8 @@ query_find(ns_client_t *client, dns_fetchevent_t *event, dns_rdatatype_t qtype)
goto cleanup;
}
result = dns_name_concatenate(prefix, tname, fname, NULL);
+ dns_message_puttempname(client->message, &tname);
if (result != ISC_R_SUCCESS) {
- dns_message_puttempname(client->message, &tname);
if (result == ISC_R_NOSPACE) {
/*
* RFC2672, section 4.1, subsection 3c says
@@ -4597,11 +6174,12 @@ query_find(ns_client_t *client, dns_fetchevent_t *event, dns_rdatatype_t qtype)
}
query_keepname(client, fname, dbuf);
/*
- * Synthesize a CNAME for this DNAME.
+ * Synthesize a CNAME consisting of
+ * <old qname> <dname ttl> CNAME <new qname>
+ * with <dname trust value>
*
- * We want to synthesize a CNAME since if we don't
- * then older software that doesn't understand DNAME
- * will not chain like it should.
+ * Synthesize a CNAME so old old clients that don't understand
+ * DNAME can chain.
*
* We do not try to synthesize a signature because we hope
* that security aware servers will understand DNAME. Also,
@@ -4609,12 +6187,10 @@ query_find(ns_client_t *client, dns_fetchevent_t *event, dns_rdatatype_t qtype)
* on-the-fly is costly, and not really legitimate anyway
* since the synthesized CNAME is NOT in the zone.
*/
- dns_name_init(tname, NULL);
- (void)query_addcnamelike(client, client->query.qname, fname,
- trdataset, &tname,
- dns_rdatatype_cname);
- if (tname != NULL)
- dns_message_puttempname(client->message, &tname);
+ result = query_add_cname(client, client->query.qname, fname,
+ trdataset->trust, trdataset->ttl);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
/*
* Switch to the new qname and restart.
*/
@@ -4641,6 +6217,28 @@ query_find(ns_client_t *client, dns_fetchevent_t *event, dns_rdatatype_t qtype)
}
if (type == dns_rdatatype_any) {
+#ifdef ALLOW_FILTER_AAAA_ON_V4
+ isc_boolean_t have_aaaa, have_a, have_sig, filter_aaaa;
+
+ /*
+ * The filter-aaaa-on-v4 option should
+ * suppress AAAAs for IPv4 clients if there is an A.
+ * If we are not authoritative, assume there is a A
+ * even in if it is not in our cache. This assumption could
+ * be wrong but it is a good bet.
+ */
+ have_aaaa = ISC_FALSE;
+ have_a = !authoritative;
+ have_sig = ISC_FALSE;
+ if (client->view->v4_aaaa != dns_v4_aaaa_ok &&
+ is_v4_client(client) &&
+ ns_client_checkaclsilent(client, NULL,
+ client->view->v4_aaaa_acl,
+ ISC_TRUE) == ISC_R_SUCCESS)
+ filter_aaaa = ISC_TRUE;
+ else
+ filter_aaaa = ISC_FALSE;
+#endif
/*
* XXXRTH Need to handle zonecuts with special case
* code.
@@ -4652,6 +6250,54 @@ query_find(ns_client_t *client, dns_fetchevent_t *event, dns_rdatatype_t qtype)
QUERY_ERROR(DNS_R_SERVFAIL);
goto cleanup;
}
+
+ /*
+ * Check all A and AAAA records in all response policy
+ * IP address zones
+ */
+ rpz_st = client->query.rpz_st;
+ if (rpz_st != NULL &&
+ (rpz_st->state & DNS_RPZ_DONE_QNAME) != 0 &&
+ (rpz_st->state & DNS_RPZ_REWRITTEN) == 0 &&
+ RECURSIONOK(client) && !RECURSING(client) &&
+ (rpz_st->state & DNS_RPZ_HAVE_IP) != 0) {
+ for (result = dns_rdatasetiter_first(rdsiter);
+ result == ISC_R_SUCCESS;
+ result = dns_rdatasetiter_next(rdsiter)) {
+ dns_rdatasetiter_current(rdsiter, rdataset);
+ if (rdataset->type == dns_rdatatype_a ||
+ rdataset->type == dns_rdatatype_aaaa)
+ result = rpz_rewrite_ip(client,
+ rdataset,
+ DNS_RPZ_TYPE_IP);
+ dns_rdataset_disassociate(rdataset);
+ if (result != ISC_R_SUCCESS)
+ break;
+ }
+ if (result != ISC_R_NOMORE) {
+ dns_rdatasetiter_destroy(&rdsiter);
+ QUERY_ERROR(DNS_R_SERVFAIL);
+ goto cleanup;
+ }
+ switch (rpz_st->m.policy) {
+ case DNS_RPZ_POLICY_MISS:
+ break;
+ case DNS_RPZ_POLICY_NO_OP:
+ rpz_log(client);
+ rpz_st->state |= DNS_RPZ_REWRITTEN;
+ break;
+ case DNS_RPZ_POLICY_NXDOMAIN:
+ case DNS_RPZ_POLICY_NODATA:
+ case DNS_RPZ_POLICY_RECORD:
+ case DNS_RPZ_POLICY_CNAME:
+ dns_rdatasetiter_destroy(&rdsiter);
+ rpz_st->state |= DNS_RPZ_REWRITTEN;
+ goto finish_rewrite;
+ default:
+ INSIST(0);
+ }
+ }
+
/*
* Calling query_addrrset() with a non-NULL dbuf is going
* to either keep or release the name. We don't want it to
@@ -4668,6 +6314,18 @@ query_find(ns_client_t *client, dns_fetchevent_t *event, dns_rdatatype_t qtype)
result = dns_rdatasetiter_first(rdsiter);
while (result == ISC_R_SUCCESS) {
dns_rdatasetiter_current(rdsiter, rdataset);
+#ifdef ALLOW_FILTER_AAAA_ON_V4
+ /*
+ * Notice the presence of A and AAAAs so
+ * that AAAAs can be hidden from IPv4 clients.
+ */
+ if (filter_aaaa) {
+ if (rdataset->type == dns_rdatatype_aaaa)
+ have_aaaa = ISC_TRUE;
+ else if (rdataset->type == dns_rdatatype_a)
+ have_a = ISC_TRUE;
+ }
+#endif
if (is_zone && qtype == dns_rdatatype_any &&
!dns_db_issecure(db) &&
dns_rdatatype_isdnssec(rdataset->type)) {
@@ -4679,6 +6337,10 @@ query_find(ns_client_t *client, dns_fetchevent_t *event, dns_rdatatype_t qtype)
dns_rdataset_disassociate(rdataset);
} else if ((qtype == dns_rdatatype_any ||
rdataset->type == qtype) && rdataset->type != 0) {
+#ifdef ALLOW_FILTER_AAAA_ON_V4
+ if (dns_rdatatype_isdnssec(rdataset->type))
+ have_sig = ISC_TRUE;
+#endif
if (NOQNAME(rdataset) && WANTDNSSEC(client))
noqname = rdataset;
else
@@ -4709,6 +6371,16 @@ query_find(ns_client_t *client, dns_fetchevent_t *event, dns_rdatatype_t qtype)
result = dns_rdatasetiter_next(rdsiter);
}
+#ifdef ALLOW_FILTER_AAAA_ON_V4
+ /*
+ * Filter AAAAs if there is an A and there is no signature
+ * or we are supposed to break DNSSEC.
+ */
+ if (filter_aaaa && have_aaaa && have_a &&
+ (!have_sig || !WANTDNSSEC(client) ||
+ client->view->v4_aaaa == dns_v4_aaaa_break_dnssec))
+ client->attributes |= NS_CLIENTATTR_FILTER_AAAA;
+#endif
if (fname != NULL)
dns_message_puttempname(client->message, &fname);
@@ -4742,10 +6414,10 @@ query_find(ns_client_t *client, dns_fetchevent_t *event, dns_rdatatype_t qtype)
dns_rdatasetiter_destroy(&rdsiter);
if (RECURSIONOK(client)) {
result = query_recurse(client,
- qtype,
- NULL,
- NULL,
- resuming);
+ qtype,
+ client->query.qname,
+ NULL, NULL,
+ resuming);
if (result == ISC_R_SUCCESS)
client->query.attributes |=
NS_QUERYATTR_RECURSING;
@@ -4763,7 +6435,8 @@ query_find(ns_client_t *client, dns_fetchevent_t *event, dns_rdatatype_t qtype)
* Add SOA.
*/
result = query_addsoa(client, db, version,
- ISC_FALSE, ISC_FALSE);
+ ISC_UINT32_MAX,
+ ISC_FALSE);
if (result == ISC_R_SUCCESS)
result = ISC_R_NOMORE;
} else {
@@ -4783,6 +6456,162 @@ query_find(ns_client_t *client, dns_fetchevent_t *event, dns_rdatatype_t qtype)
* This is the "normal" case -- an ordinary question to which
* we know the answer.
*/
+
+ /*
+ * Check all A and AAAA records in all response policy
+ * IP address zones
+ */
+ rpz_st = client->query.rpz_st;
+ if (rpz_st != NULL &&
+ (rpz_st->state & DNS_RPZ_DONE_QNAME) != 0 &&
+ (rpz_st->state & DNS_RPZ_REWRITTEN) == 0 &&
+ RECURSIONOK(client) && !RECURSING(client) &&
+ (rpz_st->state & DNS_RPZ_HAVE_IP) != 0 &&
+ (qtype == dns_rdatatype_aaaa || qtype == dns_rdatatype_a)) {
+ result = rpz_rewrite_ip(client, rdataset,
+ DNS_RPZ_TYPE_IP);
+ if (result != ISC_R_SUCCESS) {
+ QUERY_ERROR(DNS_R_SERVFAIL);
+ goto cleanup;
+ }
+ /*
+ * After a hit in the radix tree for the policy domain,
+ * either stop trying to rewrite (DNS_RPZ_POLICY_NO_OP)
+ * or restart to ask the ordinary database of the
+ * policy zone for the DNS record corresponding to the
+ * record in the radix tree.
+ */
+ switch (rpz_st->m.policy) {
+ case DNS_RPZ_POLICY_MISS:
+ break;
+ case DNS_RPZ_POLICY_NO_OP:
+ rpz_log(client);
+ rpz_st->state |= DNS_RPZ_REWRITTEN;
+ break;
+ case DNS_RPZ_POLICY_NXDOMAIN:
+ case DNS_RPZ_POLICY_NODATA:
+ case DNS_RPZ_POLICY_RECORD:
+ case DNS_RPZ_POLICY_CNAME:
+ rpz_st->state |= DNS_RPZ_REWRITTEN;
+ goto finish_rewrite;
+ default:
+ INSIST(0);
+ }
+ }
+
+#ifdef ALLOW_FILTER_AAAA_ON_V4
+ /*
+ * Optionally hide AAAAs from IPv4 clients if there is an A.
+ * We add the AAAAs now, but might refuse to render them later
+ * after DNSSEC is figured out.
+ * This could be more efficient, but the whole idea is
+ * so fundamentally wrong, unavoidably inaccurate, and
+ * unneeded that it is best to keep it as short as possible.
+ */
+ if (client->view->v4_aaaa != dns_v4_aaaa_ok &&
+ is_v4_client(client) &&
+ ns_client_checkaclsilent(client, NULL,
+ client->view->v4_aaaa_acl,
+ ISC_TRUE) == ISC_R_SUCCESS &&
+ (!WANTDNSSEC(client) ||
+ sigrdataset == NULL ||
+ !dns_rdataset_isassociated(sigrdataset) ||
+ client->view->v4_aaaa == dns_v4_aaaa_break_dnssec)) {
+ if (qtype == dns_rdatatype_aaaa) {
+ trdataset = query_newrdataset(client);
+ result = dns_db_findrdataset(db, node, version,
+ dns_rdatatype_a, 0,
+ client->now,
+ trdataset, NULL);
+ if (dns_rdataset_isassociated(trdataset))
+ dns_rdataset_disassociate(trdataset);
+ query_putrdataset(client, &trdataset);
+
+ /*
+ * We have an AAAA but the A is not in our cache.
+ * Assume any result other than DNS_R_DELEGATION
+ * or ISC_R_NOTFOUND means there is no A and
+ * so AAAAs are ok.
+ * Assume there is no A if we can't recurse
+ * for this client, although that could be
+ * the wrong answer. What else can we do?
+ * Besides, that we have the AAAA and are using
+ * this mechanism suggests that we care more
+ * about As than AAAAs and would have cached
+ * the A if it existed.
+ */
+ if (result == ISC_R_SUCCESS) {
+ client->attributes |=
+ NS_CLIENTATTR_FILTER_AAAA;
+
+ } else if (authoritative ||
+ !RECURSIONOK(client) ||
+ (result != DNS_R_DELEGATION &&
+ result != ISC_R_NOTFOUND)) {
+ client->attributes &=
+ ~NS_CLIENTATTR_FILTER_AAAA;
+ } else {
+ /*
+ * This is an ugly kludge to recurse
+ * for the A and discard the result.
+ *
+ * Continue to add the AAAA now.
+ * We'll make a note to not render it
+ * if the recursion for the A succeeds.
+ */
+ result = query_recurse(client,
+ dns_rdatatype_a,
+ client->query.qname,
+ NULL, NULL, resuming);
+ if (result == ISC_R_SUCCESS) {
+ client->attributes |=
+ NS_CLIENTATTR_FILTER_AAAA_RC;
+ client->query.attributes |=
+ NS_QUERYATTR_RECURSING;
+ }
+ }
+
+ } else if (qtype == dns_rdatatype_a &&
+ (client->attributes &
+ NS_CLIENTATTR_FILTER_AAAA_RC) != 0) {
+ client->attributes &=
+ ~NS_CLIENTATTR_FILTER_AAAA_RC;
+ client->attributes |=
+ NS_CLIENTATTR_FILTER_AAAA;
+ dns_rdataset_disassociate(rdataset);
+ if (sigrdataset != NULL &&
+ dns_rdataset_isassociated(sigrdataset))
+ dns_rdataset_disassociate(sigrdataset);
+ goto cleanup;
+ }
+ }
+#endif
+ /*
+ * Check to see if the AAAA RRset has non-excluded addresses
+ * in it. If not look for a A RRset.
+ */
+ INSIST(client->query.dns64_aaaaok == NULL);
+
+ if (qtype == dns_rdatatype_aaaa && !dns64_exclude &&
+ !ISC_LIST_EMPTY(client->view->dns64) &&
+ client->message->rdclass == dns_rdataclass_in &&
+ !dns64_aaaaok(client, rdataset, sigrdataset)) {
+ /*
+ * Look to see if there are A records for this
+ * name.
+ */
+ client->query.dns64_aaaa = rdataset;
+ client->query.dns64_sigaaaa = sigrdataset;
+ client->query.dns64_ttl = rdataset->ttl;
+ query_releasename(client, &fname);
+ dns_db_detachnode(db, &node);
+ rdataset = NULL;
+ sigrdataset = NULL;
+ type = qtype = dns_rdatatype_a;
+ dns64_exclude = dns64 = ISC_TRUE;
+ goto db_find;
+ }
+
if (sigrdataset != NULL)
sigrdatasetp = &sigrdataset;
else
@@ -4798,8 +6627,43 @@ query_find(ns_client_t *client, dns_fetchevent_t *event, dns_rdatatype_t qtype)
dns_name_equal(client->query.qname, dns_rootname))
client->query.attributes &= ~NS_QUERYATTR_NOADDITIONAL;
- query_addrrset(client, &fname, &rdataset, sigrdatasetp, dbuf,
- DNS_SECTION_ANSWER);
+ if (dns64) {
+ qtype = type = dns_rdatatype_aaaa;
+ result = query_dns64(client, &fname, rdataset,
+ sigrdataset, dbuf,
+ DNS_SECTION_ANSWER);
+ dns_rdataset_disassociate(rdataset);
+ dns_message_puttemprdataset(client->message, &rdataset);
+ if (result == ISC_R_NOMORE) {
+#ifndef dns64_bis_return_excluded_addresses
+ if (dns64_exclude) {
+ if (!is_zone)
+ goto cleanup;
+ /*
+ * Add a fake SOA record.
+ */
+ result = query_addsoa(client, db,
+ version, 600,
+ ISC_FALSE);
+ goto cleanup;
+ }
+#endif
+ if (is_zone)
+ goto nxrrset;
+ else
+ goto ncache_nxrrset;
+ } else if (result != ISC_R_SUCCESS) {
+ eresult = result;
+ goto cleanup;
+ }
+ } else if (client->query.dns64_aaaaok != NULL) {
+ query_filter64(client, &fname, rdataset, dbuf,
+ DNS_SECTION_ANSWER);
+ query_putrdataset(client, &rdataset);
+ } else
+ query_addrrset(client, &fname, &rdataset,
+ sigrdatasetp, dbuf, DNS_SECTION_ANSWER);
+
if (noqname != NULL)
query_addnoqnameproof(client, noqname);
/*
@@ -4842,6 +6706,10 @@ query_find(ns_client_t *client, dns_fetchevent_t *event, dns_rdatatype_t qtype)
/*
* General cleanup.
*/
+ rpz_st = client->query.rpz_st;
+ if (rpz_st != NULL && (rpz_st->state & DNS_RPZ_RECURSING) == 0)
+ rpz_clean(&rpz_st->m.zone, &rpz_st->m.db, &rpz_st->m.node,
+ &rpz_st->m.rdataset);
if (rdataset != NULL)
query_putrdataset(client, &rdataset);
if (sigrdataset != NULL)
@@ -4949,6 +6817,7 @@ log_query(ns_client_t *client, unsigned int flags, unsigned int extflags) {
char namebuf[DNS_NAME_FORMATSIZE];
char typename[DNS_RDATATYPE_FORMATSIZE];
char classname[DNS_RDATACLASS_FORMATSIZE];
+ char onbuf[ISC_NETADDR_FORMATSIZE];
dns_rdataset_t *rdataset;
int level = ISC_LOG_INFO;
@@ -4960,14 +6829,18 @@ log_query(ns_client_t *client, unsigned int flags, unsigned int extflags) {
dns_name_format(client->query.qname, namebuf, sizeof(namebuf));
dns_rdataclass_format(rdataset->rdclass, classname, sizeof(classname));
dns_rdatatype_format(rdataset->type, typename, sizeof(typename));
+ isc_netaddr_format(&client->destaddr, onbuf, sizeof(onbuf));
ns_client_log(client, NS_LOGCATEGORY_QUERIES, NS_LOGMODULE_QUERY,
- level, "query: %s %s %s %s%s%s%s%s", namebuf, classname,
- typename, WANTRECURSION(client) ? "+" : "-",
+ level, "query: %s %s %s %s%s%s%s%s%s (%s)", namebuf,
+ classname, typename, WANTRECURSION(client) ? "+" : "-",
(client->signer != NULL) ? "S": "",
(client->opt != NULL) ? "E" : "",
+ ((client->attributes & NS_CLIENTATTR_TCP) != 0) ?
+ "T" : "",
((extflags & DNS_MESSAGEEXTFLAG_DO) != 0) ? "D" : "",
- ((flags & DNS_MESSAGEFLAG_CD) != 0) ? "C" : "");
+ ((flags & DNS_MESSAGEFLAG_CD) != 0) ? "C" : "",
+ onbuf);
}
static inline void
OpenPOWER on IntegriCloud