diff options
Diffstat (limited to 'bin/named/query.c')
-rw-r--r-- | bin/named/query.c | 2245 |
1 files changed, 2059 insertions, 186 deletions
diff --git a/bin/named/query.c b/bin/named/query.c index fa34da6..1950257 100644 --- a/bin/named/query.c +++ b/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 |