/*
* Copyright (C) 1999-2018 Internet Systems Consortium, Inc. ("ISC")
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
/*! \file */
#include <config.h>
#include <string.h>
#include <dns/badcache.h>
#include <dns/rdataclass.h>
#include <dns/rdatalist.h>
#include <dns/rdataset.h>
#include <dns/rdatasetiter.h>
#include <dns/rdatastruct.h>
#include <dns/rdatatype.h>
#include <dns/resolver.h>
#include <named/sortlist.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? */
NS_QUERYATTR_PARTIALANSWER) != 0)
/*% Use Cache? */
NS_QUERYATTR_CACHEOK) != 0)
/*% Recursion OK? */
NS_QUERYATTR_RECURSIONOK) != 0)
/*% Recursing? */
NS_QUERYATTR_RECURSING) != 0)
/*% Cache glue ok? */
NS_QUERYATTR_CACHEGLUEOK) != 0)
/*% Want Recursion? */
NS_QUERYATTR_WANTRECURSION) != 0)
/*% Is TCP? */
/*% Want DNSSEC? */
NS_CLIENTATTR_WANTDNSSEC) != 0)
/*% Want WANTAD? */
NS_CLIENTATTR_WANTAD) != 0)
/*% Client presented a valid COOKIE. */
NS_CLIENTATTR_HAVECOOKIE) != 0)
/*% Client presented a COOKIE. */
NS_CLIENTATTR_WANTCOOKIE) != 0)
/*% No authority? */
NS_QUERYATTR_NOAUTHORITY) != 0)
/*% No additional? */
NS_QUERYATTR_NOADDITIONAL) != 0)
/*% Secure? */
NS_QUERYATTR_SECURE) != 0)
/*% DNS64 A lookup? */
NS_QUERYATTR_DNS64) != 0)
NS_QUERYATTR_DNS64EXCLUDE) != 0)
NS_QUERYATTR_REDIRECT) != 0)
/*% No QNAME Proof? */
DNS_RDATASETATTR_NOQNAME) != 0)
#ifdef WANT_QUERYTRACE
static inline void
"query client=%p thread=0x%lx "
"(%s/%s): %s",
(unsigned long) isc_thread_self(),
}
} else {
"query client=%p thread=0x%lx "
"(<unknown-query>): %s",
(unsigned long) isc_thread_self(),
message);
}
}
#else
#define CTRACE(l,m) ((void)m)
#endif /* WANT_QUERYTRACE */
/*
* These have the same semantics as:
*
* foo_attach(b, a);
* foo_detach(&a);
*
* without the locking and magic testing.
*
* We use SAVE and RESTORE as that shows the operation being performed.
*/
typedef struct client_additionalctx {
static isc_result_t
static isc_boolean_t
static void
dns_name_t *found);
static inline void
static void
static isc_boolean_t
/*%
* Increment query statistics counters.
*/
static inline void
return;
/* Do regular response type stats */
/* Do query type statistics
*
* We only increment per-type if we're using the authoritative
* answer counter, preventing double-counting.
*/
if (counter == dns_nsstatscounter_authans) {
if (querystats != NULL) {
}
}
}
}
static void
else
else
} else
else /* We end up here in case of YXDOMAIN, and maybe others */
}
static void
switch (result) {
case DNS_R_SERVFAIL:
break;
case DNS_R_FORMERR:
break;
default:
break;
}
if (ns_g_server->log_queries)
}
static void
if (result == DNS_R_DUPLICATE)
else if (result == DNS_R_DROP)
else
}
static inline void
unsigned int i;
dbversion = dbversion_next, i++)
{
/*
* If we're not freeing everything, we keep the first three
* dbversions structures around.
*/
if (i > 3 || everything) {
link);
sizeof(*dbversion));
}
}
}
void
}
}
static inline void
}
}
static inline void
/*%
* Reset the query state of a client to its default state.
*/
/*
* Cancel the fetch if it's running.
*/
/*
* Cleanup any active versions.
*/
dbversion = dbversion_next) {
}
sizeof(isc_boolean_t));
}
}
}
}
/*
* client->query.qname was dynamically allocated.
*/
}
if (everything) {
}
}
}
static void
}
void
}
static inline isc_result_t
/*%
* Allocate a name buffer.
*/
if (result != ISC_R_SUCCESS) {
"query_newnamebuf: isc_buffer_allocate failed: done");
return (result);
}
return (ISC_R_SUCCESS);
}
static inline isc_buffer_t *
isc_region_t r;
/*%
* Return a name buffer with space for a maximal name, allocating
* a new one if necessary.
*/
if (result != ISC_R_SUCCESS) {
"query_getnamebuf: query_newnamebuf failed: done");
return (NULL);
}
}
if (r.length < DNS_NAME_MAXWIRE) {
if (result != ISC_R_SUCCESS) {
"query_getnamebuf: query_newnamebuf failed: done");
return (NULL);
}
}
return (dbuf);
}
static inline void
isc_region_t r;
/*%
* 'name' is using space in 'dbuf', but 'dbuf' has not yet been
* adjusted to take account of that. We do the adjustment.
*/
dns_name_toregion(name, &r);
}
static inline void
/*%
* 'name' is no longer needed. Return it to our pool of temporary
* names. If it is using a name buffer, relinquish its exclusive
* rights on the buffer.
*/
if (dns_name_hasbuffer(name)) {
!= 0);
}
}
static inline dns_name_t *
{
isc_region_t r;
if (result != ISC_R_SUCCESS) {
"query_newname: dns_message_gettempname failed: done");
return (NULL);
}
return (name);
}
static inline dns_rdataset_t *
if (result != ISC_R_SUCCESS) {
"query_newrdataset: "
"dns_message_gettemprdataset failed: done");
return (NULL);
}
return (rdataset);
}
static inline isc_result_t
unsigned int i;
for (i = 0; i < n; i++) {
} else {
/*
* We only return ISC_R_NOMEMORY if we couldn't
* allocate anything.
*/
if (i == 0)
return (ISC_R_NOMEMORY);
else
return (ISC_R_SUCCESS);
}
}
return (ISC_R_SUCCESS);
}
static inline ns_dbversion_t *
if (result != ISC_R_SUCCESS)
return (NULL);
}
return (dbversion);
}
/*
* This mutex is destroyed when the client is destroyed in
* exit_check().
*/
if (result != ISC_R_SUCCESS)
return (result);
if (result != ISC_R_SUCCESS) {
return (result);
}
if (result != ISC_R_SUCCESS) {
}
return (result);
}
static ns_dbversion_t *
/*%
* We may already have done a query related to this
* database. If so, we must be sure to make subsequent
* queries from the same version.
*/
break;
}
/*
* This is a new zone for this query. Add it to
* the active list.
*/
return (NULL);
}
return (dbversion);
}
static inline isc_result_t
{
/*
* This limits our searching to the zone where the first name
* (the query target) was looked for. This prevents following
* CNAMES or DNAMES into other zones and prevents returning
* additional data from other zones.
*/
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.
*/
!RECURSIONOK(client)) {
return (DNS_R_REFUSED);
}
/*
* If the zone has an ACL, we'll check it, otherwise
* we use the view's "allow-query" ACL. Each ACL is only checked
* once per query.
*
* Also, get the database version to use.
*/
/*
* Get the current version of this database.
*/
return (DNS_R_SERVFAIL);
}
if ((options & DNS_GETDB_IGNOREACL) != 0)
goto approved;
if (dbversion->acl_checked) {
return (DNS_R_REFUSED);
goto approved;
}
NS_QUERYATTR_QUERYOKVALID) != 0) {
/*
* We've evaluated the view's queryacl already. If
* NS_QUERYATTR_QUERYOK is set, then the client is
* allowed to make queries, otherwise the query should
* be refused.
*/
NS_QUERYATTR_QUERYOK) == 0) {
return (DNS_R_REFUSED);
}
goto approved;
}
}
if ((options & DNS_GETDB_NOLOG) == 0) {
if (result == ISC_R_SUCCESS) {
ISC_LOG_DEBUG(3),
"%s approved", msg);
}
} else {
"%s denied", msg);
}
}
if (result == ISC_R_SUCCESS) {
/*
* We were allowed by the default
* "allow-query" ACL. Remember this so we
* don't have to check again.
*/
}
/*
* We've now evaluated the view's query ACL, and
* the NS_QUERYATTR_QUERYOK attribute is now valid.
*/
}
/* If and only if we've gotten this far, check allow-query-on too */
if (result == ISC_R_SUCCESS) {
if (queryonacl == NULL)
if ((options & DNS_GETDB_NOLOG) == 0 &&
result != ISC_R_SUCCESS)
"query-on denied");
}
if (result != ISC_R_SUCCESS) {
return (DNS_R_REFUSED);
}
/* Transfer ownership, if necessary. */
return (ISC_R_SUCCESS);
}
static inline isc_result_t
{
unsigned int ztoptions;
/*%
* Find a zone database to answer the query.
*/
DNS_ZTFIND_NOEXACT : 0;
&zone);
if (result == DNS_R_PARTIALMATCH)
if (result != ISC_R_SUCCESS)
goto fail;
versionp);
if (result != ISC_R_SUCCESS)
goto fail;
/* Transfer ownership. */
return (DNS_R_PARTIALMATCH);
return (ISC_R_SUCCESS);
fail:
dns_db_detach(&db);
return (result);
}
static void
{
/*
* Count enabled rewrites in the global counter.
* Count both enabled and disabled rewrites for each zone.
*/
}
}
return;
return;
s1 = " (CNAME to: ";
s2 = ")";
}
DNS_RPZ_INFO_LEVEL, "%srpz %s %s rewrite %s via %s%s%s%s",
}
static void
{
const char *failed;
const char *slash;
const char *via;
const char *str_blank;
const char *rpztypestr1;
const char *rpztypestr2;
return;
/*
*/
if (level <= DNS_RPZ_DEBUG_LEVEL1)
failed = "failed: ";
else
failed = ": ";
if (rpz_type2 != DNS_RPZ_TYPE_BAD) {
slash = "/";
} else {
slash = "";
rpztypestr2 = "";
}
via = " via ";
} else {
via = "";
p_namebuf[0] = '\0';
}
"rpz %s%s%s rewrite %s%s%s%s%s%s : %s",
}
static void
{
}
/*
* Get a policy rewrite zone database.
*/
static isc_result_t
{
if (result == ISC_R_SUCCESS) {
/*
* It isn't meaningful to log this message when
* logging is disabled for some policy zones.
*/
{
sizeof(qnamebuf));
"try rpz %s rewrite %s via %s",
}
*versionp = rpz_version;
return (ISC_R_SUCCESS);
}
" query_getzonedb()", result);
return (result);
}
static inline isc_result_t
{
/*%
* Find a cache database to answer the query.
* This may fail with DNS_R_REFUSED if the client
* is not allowed to use the cache.
*/
return (DNS_R_REFUSED);
/*
* We've evaluated the view's cacheacl already. If
* NS_QUERYATTR_CACHEACLOK is set, then the client is
* allowed to make queries, otherwise the query should
* be refused.
*/
goto refuse;
} else {
/*
* We haven't evaluated the view's queryacl yet.
*/
}
if (check_acl) {
ISC_TRUE);
if (result == ISC_R_SUCCESS) {
/*
* We were allowed by the "allow-query-cache" ACL.
* Remember this so we don't have to check again.
*/
ISC_LOG_DEBUG(3)))
{
ISC_LOG_DEBUG(3),
"%s approved", msg);
}
} else if (log) {
sizeof(msg));
"%s denied", msg);
}
/*
* We've now evaluated the view's query ACL, and
* the NS_QUERYATTR_CACHEACLOKVALID attribute is now valid.
*/
if (result != ISC_R_SUCCESS)
goto refuse;
}
/* Approved. */
/* Transfer ownership. */
return (ISC_R_SUCCESS);
dns_db_detach(&db);
return (result);
}
static inline isc_result_t
{
unsigned int namelabels;
unsigned int zonelabels;
/* Calculate how many labels are in name. */
zonelabels = 0;
/* Try to find name in bind's standard database. */
/* See how many labels are in the zone's name. */
/*
* If # zone labels < # name labels, try to find an even better match
* Only try if DLZ drivers are loaded for this view
*/
{
/* If we successful, we found a better match. */
if (tresult == ISC_R_SUCCESS) {
/*
* If the previous search returned a zone, detach it.
*/
/*
* If the previous search returned a database,
* detach it.
*/
/*
* If the previous search returned a version, clear it.
*/
} else {
/*
* Be sure to return our database.
*/
}
/*
* We return a null zone, No stats for DLZ zones.
*/
}
}
/* If successful, Transfer ownership of zone. */
if (result == ISC_R_SUCCESS) {
/*
* If neither attempt above succeeded, return the cache instead
*/
} else if (result == ISC_R_NOTFOUND) {
}
return (result);
}
static inline isc_boolean_t
{
for (section = DNS_SECTION_ANSWER;
section++) {
if (result == ISC_R_SUCCESS) {
/*
* We've already got this RRset in the response.
*/
"query_isduplicate: true: done");
return (ISC_TRUE);
} else if (result == DNS_R_NXRRSET) {
/*
* The name exists, but the rdataset does not.
*/
if (section == DNS_SECTION_ADDITIONAL)
break;
} else
}
return (ISC_FALSE);
}
static isc_result_t
isc_buffer_t b;
return (ISC_R_SUCCESS);
/*
* Initialization.
*/
sigrdataset = NULL;
/*
* We treat type A additional section processing as if it
* were "any address type" additional section processing.
* To avoid multiple lookups, we do an 'any' database
* lookup and iterate over the node.
*/
if (qtype == dns_rdatatype_a)
else
/*
* Get some resources.
*/
goto cleanup;
goto cleanup;
if (WANTDNSSEC(client)) {
if (sigrdataset == NULL)
goto cleanup;
}
/*
* Look for a zone database that might contain authoritative
* additional data.
*/
if (result != ISC_R_SUCCESS)
goto try_cache;
/*
* Since we are looking for authoritative data, we do not set
* the GLUEOK flag. Glue will be looked for later, but not
* necessarily in the same database.
*/
if (result == ISC_R_SUCCESS) {
goto found;
}
dns_db_detach(&db);
/*
* No authoritative data was found. The cache is our next best bet.
*/
if (result != ISC_R_SUCCESS)
/*
* Most likely the client isn't allowed to query the cache.
*/
goto try_glue;
/*
* Attempt to validate glue.
*/
if (sigrdataset == NULL) {
if (sigrdataset == NULL)
goto cleanup;
}
if (!WANTDNSSEC(client))
if (result == ISC_R_SUCCESS)
goto found;
dns_db_detach(&db);
/*
* No cached data was found. Glue is our last chance.
* RFC1035 sayeth:
*
* NS records cause both the usual additional section
* processing to locate a type A record, and, when used
* in a referral, a special search of the zone in which
* they reside for glue information.
*
* This is the "special search". Note that we must search
* the zone where the NS record resides, not the zone it
* points to, and that we only do the search in the delegation
* case (identified by client->query.gluedb being set).
*/
goto cleanup;
/*
* Don't poison caches using the bailiwick protection model.
*/
goto cleanup;
if (!(result == ISC_R_SUCCESS ||
result == DNS_R_ZONECUT ||
result == DNS_R_GLUE))
goto cleanup;
/*
* We have found a potential additional data rdataset, or
* at least a node to iterate over.
*/
/*
* If we have an rdataset, add it to the additional data
* section.
*/
if (dns_rdataset_isassociated(rdataset) &&
} else
/*
* Note: we only add SIGs if we've added the type they cover,
* so we do not need to check if the SIG rdataset is already
* in the response.
*/
if (sigrdataset != NULL &&
{
sigrdataset = NULL;
}
}
if (qtype == dns_rdatatype_a) {
#ifdef ALLOW_FILTER_AAAA
#endif
/*
* We now go looking for A and AAAA records, along with
* their signatures.
*
* XXXRTH This code could be more efficient.
*/
} else {
goto addname;
}
if (sigrdataset != NULL) {
} else if (WANTDNSSEC(client)) {
if (sigrdataset == NULL)
goto addname;
}
goto aaaa_lookup;
dns_rdatatype_a, 0,
if (result == DNS_R_NCACHENXDOMAIN)
goto addname;
if (result == DNS_R_NCACHENXRRSET) {
if (sigrdataset != NULL &&
}
if (result == ISC_R_SUCCESS) {
#ifdef ALLOW_FILTER_AAAA
#endif
{
if (sigrdataset != NULL &&
/* treat as if not found */
dns_rdatatype_a, &mname))
{
&fname);
} else
}
if (sigrdataset != NULL &&
{
sigrdataset, link);
}
goto addname;
goto addname;
} else {
if (sigrdataset != NULL &&
}
}
goto addname;
if (result == DNS_R_NCACHENXDOMAIN)
goto addname;
if (result == DNS_R_NCACHENXRRSET) {
if (sigrdataset != NULL &&
}
if (result == ISC_R_SUCCESS) {
/*
* There's an A; check whether we're filtering AAAA
*/
#ifdef ALLOW_FILTER_AAAA
if (have_a &&
goto addname;
#endif
{
if (sigrdataset != NULL &&
/* treat as if not found */
{
&fname);
} else
}
if (sigrdataset != NULL &&
{
sigrdataset, link);
sigrdataset = NULL;
}
}
}
}
/*
* If we haven't added anything, then we're done.
*/
if (!added_something)
goto cleanup;
/*
* We may have added our rdatasets to an existing name, if so, then
* need_addname will be ISC_FALSE. Whether we used an existing name
* or a new one, we must set fname to NULL to prevent cleanup.
*/
if (need_addname)
/*
* In a few cases, we want to add additional data for additional
* data. It's simpler to just deal with special cases here than
* to try to create a general purpose mechanism and allow the
* rdata implementations to do it themselves.
*
* This involves recursion, but the depth is limited. The
* most complex case is adding a SRV rdataset, which involves
* recursing to add address records, which in turn can cause
* recursion to add KEYs.
*/
/*
* If we're adding SRV records to the additional data
* section, it's helpful if we add the SRV additional data
* as well.
*/
client);
}
if (sigrdataset != NULL)
dns_db_detach(&db);
return (eresult);
}
static inline void
{
}
}
static inline isc_result_t
{
if (db_current == NULL) {
if (result != ISC_R_SUCCESS)
return (result);
}
goto cleanup;
}
return (result);
}
static isc_result_t
isc_buffer_t b;
/*
* If we don't have an additional cache call query_addadditional.
*/
/*
* This function is optimized for "address" types. For other
* types, use a generic routine.
* XXX: ideally, this function should be generic enough.
*/
}
/*
* Initialization.
*/
sigrdataset = NULL;
/*
* We treat type A additional section processing as if it
* were "any address type" additional section processing.
* To avoid multiple lookups, we do an 'any' database
* lookup and iterate over the node.
* XXXJT: this approach can cause a suboptimal result when the cache
* DB only has partial address types and the glue DB has remaining
* ones.
*/
/*
* Get some resources.
*/
goto cleanup;
goto cleanup;
/* Check additional cache */
if (result != ISC_R_SUCCESS)
goto findauthdb;
"query_addadditional2: auth zone not found");
goto try_cache;
}
/* Is the cached DB up-to-date? */
if (result != ISC_R_SUCCESS) {
"query_addadditional2: old auth additional cache");
&cfname);
goto findauthdb;
}
/*
* We have a negative cache. We don't have to check the zone
* ACL, since the result (not using this zone) would be same
* regardless of the result.
*/
"query_addadditional2: negative auth additional cache");
dns_db_detach(&cdb);
goto try_cache;
}
if (result != ISC_R_SUCCESS) {
&cfname);
goto try_cache;
}
/* We've got an active cache. */
"query_addadditional2: auth additional cache");
goto foundcache;
/*
* Look for a zone database that might contain authoritative
* additional data.
*/
if (result != ISC_R_SUCCESS) {
/* Cache the negative result */
NULL);
goto try_cache;
}
/*
* Since we are looking for authoritative data, we do not set
* the GLUEOK flag. Glue will be looked for later, but not
* necessarily in the same database.
*/
if (result == ISC_R_SUCCESS)
goto found;
/* Cache the negative result */
dns_db_detach(&db);
/*
* No authoritative data was found. The cache is our next best bet.
*/
if (result != ISC_R_SUCCESS)
/*
* Most likely the client isn't allowed to query the cache.
*/
goto try_glue;
if (result == ISC_R_SUCCESS)
goto found;
dns_db_detach(&db);
/*
* No cached data was found. Glue is our last chance.
* RFC1035 sayeth:
*
* NS records cause both the usual additional section
* processing to locate a type A record, and, when used
* in a referral, a special search of the zone in which
* they reside for glue information.
*
* This is the "special search". Note that we must search
* the zone where the NS record resides, not the zone it
* points to, and that we only do the search in the delegation
* case (identified by client->query.gluedb being set).
*/
goto cleanup;
/*
* Don't poison caches using the bailiwick protection model.
*/
goto cleanup;
/* Check additional cache */
if (result != ISC_R_SUCCESS)
goto findglue;
if (result != ISC_R_SUCCESS) {
"query_addadditional2: old glue additional cache");
&cfname);
goto findglue;
}
/* We have a negative cache. */
"query_addadditional2: negative glue additional cache");
dns_db_detach(&cdb);
goto cleanup;
}
/* Cache hit. */
goto foundcache;
if (!(result == ISC_R_SUCCESS ||
result == DNS_R_ZONECUT ||
result == DNS_R_GLUE)) {
/* cache the negative result */
fname);
goto cleanup;
}
/*
* We have found a DB node to iterate over from a DB.
* We are going to look for address RRsets (i.e., A and AAAA) in the DB
* node we've just found. We'll then store the complete information
* in the additional data cache.
*/
goto cleanup;
if (sigrdataset == NULL)
goto cleanup;
goto aaaa_lookup;
/*
* Find A RRset with sig RRset. Even if we don't find a sig RRset
* for a client using DNSSEC, we'll continue the process to make a
* complete list to be cached. However, we need to cancel the
* caching when something unexpected happens, in order to avoid
* caching incomplete information.
*/
/*
* then drop it.
*/
if (result == ISC_R_SUCCESS &&
}
if (result == DNS_R_NCACHENXDOMAIN)
goto setcache;
if (result == DNS_R_NCACHENXRRSET) {
}
if (result == ISC_R_SUCCESS) {
/* Remember the result as a cache */
if (dns_rdataset_isassociated(sigrdataset)) {
}
/* do not cache incomplete information */
goto foundcache;
}
}
goto foundcache;
/* Find AAAA RRset with sig RRset */
/*
* then drop it.
*/
if (result == ISC_R_SUCCESS &&
}
if (result == ISC_R_SUCCESS) {
if (dns_rdataset_isassociated(sigrdataset)) {
sigrdataset = NULL;
}
}
/*
* Set the new result in the cache if required. We do not support
* caching additional data from a cache DB.
*/
if (needadditionalcache == ISC_TRUE &&
&cfname);
}
crdataset = crdataset_next) {
&mname)) {
/*
* A different type of this name is
* already stored in the additional
* section. We'll reuse the name.
* Note that this should happen at most
* once. Otherwise, fname->link could
* leak below.
*/
} else
}
} else
}
}
/*
* If we haven't added anything, then we're done.
*/
if (!added_something)
goto cleanup;
/*
* We may have added our rdatasets to an existing name, if so, then
* need_addname will be ISC_FALSE. Whether we used an existing name
* or a new one, we must set fname to NULL to prevent cleanup.
*/
if (need_addname)
if (sigrdataset != NULL)
}
dns_db_detach(&db);
return (eresult);
}
static inline void
{
/*
* Add 'rdataset' and any pertinent additional data to
* 'fname', a name in the response message for 'client'.
*/
if (NOADDITIONAL(client))
return;
/*
* Add additional data.
*
* We don't care if dns_rdataset_additionaldata() fails.
*/
}
static isc_result_t
{
isc_region_t r;
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.
*/
dns64_rdata = NULL;
if (result == ISC_R_SUCCESS) {
/*
* We've already got an RRset of the given name and type.
* There's nothing else to do;
*/
"query_dns64: dns_message_findname succeeded: done");
return (ISC_R_SUCCESS);
} else if (result == DNS_R_NXDOMAIN) {
/*
* The name doesn't exist.
*/
} else {
}
(section == DNS_SECTION_ANSWER ||
if (result != ISC_R_SUCCESS)
goto cleanup;
if (result != ISC_R_SUCCESS)
goto cleanup;
if (result != ISC_R_SUCCESS)
goto cleanup;
else
if (RECURSIONOK(client))
/*
* 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.
*/
result == ISC_R_SUCCESS;
if (result != ISC_R_SUCCESS) {
continue;
}
&dns64_rdata);
if (result != ISC_R_SUCCESS)
goto cleanup;
dns_rdatatype_aaaa, &r);
link);
dns64_rdata = NULL;
}
}
if (result != ISC_R_NOMORE)
goto cleanup;
goto cleanup;
if (result != ISC_R_SUCCESS)
goto cleanup;
if (dns64_rdata != NULL)
if (dns64_rdataset != NULL)
if (dns64_rdatalist != NULL) {
dns64_rdata != NULL;
{
dns64_rdata, link);
}
}
return (result);
}
static void
{
isc_region_t r;
unsigned int i;
myrdataset = NULL;
myrdatalist = NULL;
&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;
*/
"query_filter64: dns_message_findname succeeded: done");
return;
} else if (result == DNS_R_NXDOMAIN) {
} else {
}
(section == DNS_SECTION_ANSWER ||
if (result != ISC_R_SUCCESS)
goto cleanup;
if (result != ISC_R_SUCCESS)
goto cleanup;
if (result != ISC_R_SUCCESS)
goto cleanup;
i = 0;
result == ISC_R_SUCCESS;
continue;
if (result != ISC_R_SUCCESS)
goto cleanup;
dns_rdatatype_aaaa, &r);
}
if (result != ISC_R_NOMORE)
goto cleanup;
if (result != ISC_R_SUCCESS)
goto cleanup;
}
myrdataset = NULL;
myrdatalist = NULL;
if (myrdataset != NULL)
if (myrdatalist != NULL) {
{
}
}
}
static void
{
/*%
* 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.
*/
if (sigrdatasetp != NULL)
else
sigrdataset = NULL;
if (result == ISC_R_SUCCESS) {
/*
* We've already got an RRset of the given name and type.
*/
"query_addrrset: dns_message_findname succeeded: done");
return;
} else if (result == DNS_R_NXDOMAIN) {
/*
* The name doesn't exist.
*/
} else {
}
(section == DNS_SECTION_ANSWER ||
/*
* Note: we only add SIGs if we've added the type they cover, so
* we do not need to check if the SIG rdataset is already in the
* response.
*/
/*
* We have a signature. Add it to the response.
*/
*sigrdatasetp = NULL;
}
}
static inline isc_result_t
{
/*
* Initialization.
*/
/*
* Don't add the SOA record for test which set "-T nosoa".
*/
return (ISC_R_SUCCESS);
/*
* Get resources and make 'name' be the database origin.
*/
if (result != ISC_R_SUCCESS)
return (result);
goto cleanup;
}
if (sigrdataset == NULL) {
goto cleanup;
}
}
/*
* Find the SOA.
*/
if (result == ISC_R_SUCCESS) {
} else {
}
if (result != ISC_R_SUCCESS) {
/*
* This is bad. We tried to get the SOA RR at the zone top
* and it didn't work!
*/
} else {
/*
* Extract the SOA MINIMUM.
*/
if (result != ISC_R_SUCCESS)
goto cleanup;
if (override_ttl != ISC_UINT32_MAX &&
if (sigrdataset != NULL)
}
/*
* Add the SOA and its SIG to the response, with the
* TTLs adjusted per RFC2308 section 3.
*/
if (sigrdataset != NULL)
else
sigrdatasetp = NULL;
if (section == DNS_SECTION_ADDITIONAL)
section);
}
if (sigrdataset != NULL)
return (eresult);
}
static inline isc_result_t
/*
* Initialization.
*/
/*
* Get resources and make 'name' be the database origin.
*/
if (result != ISC_R_SUCCESS) {
"query_addns: dns_message_gettempname failed: done");
return (result);
}
"query_addns: query_newrdataset failed");
goto cleanup;
}
if (sigrdataset == NULL) {
"query_addns: query_newrdataset failed");
goto cleanup;
}
}
/*
* Find the NS rdataset.
*/
if (result == ISC_R_SUCCESS) {
} else {
}
if (result != ISC_R_SUCCESS) {
"query_addns: "
"dns_db_findrdataset or dns_db_find failed");
/*
* This is bad. We tried to get the NS rdataset at the zone
* top and it didn't work!
*/
} else {
if (sigrdataset != NULL)
else
sigrdatasetp = NULL;
}
if (sigrdataset != NULL)
return (eresult);
}
static isc_result_t
{
isc_region_t r;
/*
* We assume the name data referred to by tname won't go away.
*/
if (result != ISC_R_SUCCESS)
return (result);
if (result != ISC_R_SUCCESS) {
return (result);
}
if (result != ISC_R_SUCCESS) {
return (result);
}
if (result != ISC_R_SUCCESS) {
return (result);
}
if (result != ISC_R_SUCCESS) {
return (result);
}
dns_name_toregion(tname, &r);
== ISC_R_SUCCESS);
}
return (ISC_R_SUCCESS);
}
/*
* Mark the RRsets as secure. Update the cache (db) to reflect the
* change in trust level.
*/
static void
{
/*
* Save the updated secure state. Ignore failures.
*/
if (result != ISC_R_SUCCESS)
return;
0, NULL);
0, NULL);
}
/*
* Find the secure key that corresponds to rrsig.
* Note: 'keyrdataset' maintains state between successive calls,
* there may be multiple keys with the same keyid.
* Return ISC_FALSE if we have exhausted all the possible keys.
*/
static isc_boolean_t
{
if (!dns_rdataset_isassociated(keyrdataset)) {
if (result != ISC_R_SUCCESS)
return (ISC_FALSE);
if (result != ISC_R_SUCCESS)
return (ISC_FALSE);
return (ISC_FALSE);
} else
for ( ; result == ISC_R_SUCCESS;
isc_buffer_t b;
if (result != ISC_R_SUCCESS)
continue;
dst_key_iszonekey(*keyp)) {
break;
}
}
return (secure);
}
static isc_boolean_t
{
goto again;
}
return (ISC_TRUE);
return (ISC_FALSE);
}
/*
* Validate the rdataset if possible with available records.
*/
static isc_boolean_t
{
return (ISC_FALSE);
result == ISC_R_SUCCESS;
if (result != ISC_R_SUCCESS)
return (ISC_FALSE);
continue;
continue;
do {
break;
dst_key_free(&key);
return (ISC_TRUE);
}
dst_key_free(&key);
} while (1);
}
return (ISC_FALSE);
}
static void
isc_buffer_t b;
sigrdataset = NULL;
zsigrdataset = NULL;
/*
* Find the right database.
*/
if (result != ISC_R_SUCCESS)
goto cleanup;
/*
* We'll need some resources...
*/
goto cleanup;
goto cleanup;
/*
* Get the RRSIGs if the client requested them or if we may
* need to validate answers from the cache.
*/
if (sigrdataset == NULL)
goto cleanup;
}
/*
* Now look for the zonecut.
*/
if (is_zone) {
if (result != DNS_R_DELEGATION)
goto cleanup;
goto db_find;
}
} else {
if (result == ISC_R_SUCCESS) {
/*
* We found a zonecut in the cache, but our
* zone delegation is better.
*/
}
/*
* We didn't find anything in the cache, but we
* have a zone delegation, so use it.
*/
} else
goto cleanup;
}
if (use_zone) {
/*
* We've already done query_keepname() on
* zfname, so we must set dbuf to NULL to
* prevent query_addrrset() from trying to
* call query_keepname() again.
*/
if (sigrdataset != NULL)
dns_db_detach(&db);
}
/*
* Attempt to validate RRsets that are pending or that are glue.
*/
goto cleanup;
goto cleanup;
/*
* If the answer is secure only add NS records if they are secure
* when the client may be looking for AD in the response.
*/
goto cleanup;
/*
* If the client doesn't want DNSSEC we can discard the sigrdataset
* now.
*/
if (!WANTDNSSEC(client))
if (sigrdataset != NULL)
dns_db_detach(&db);
if (zsigrdataset != NULL)
dns_db_detach(&zdb);
}
}
static void
else if (dns_rdataset_isassociated(*rdataset))
}
static void
{
return;
}
}
static void
{
unsigned int count;
sigrdataset = NULL;
/*
* We'll need some resources...
*/
goto cleanup;
/*
* Look for the DS record, which may or may not be present.
*/
/*
* If we didn't find it, look for an NSEC.
*/
if (result == ISC_R_NOTFOUND)
goto addnsec3;
if (!dns_rdataset_isassociated(rdataset) ||
goto addnsec3;
/*
* We've already added the NS record, so if the name's not there,
* we have other problems. Use this name rather than calling
* query_addrrset().
*/
if (result != ISC_R_SUCCESS)
goto cleanup;
&rname);
if (result != ISC_R_SUCCESS)
goto cleanup;
sigrdataset = NULL;
return;
if (!dns_db_iszone(db))
goto cleanup;
/*
* Add the NSEC3 which proves the DS does not exist.
*/
goto cleanup;
goto cleanup;
/*
* Did we find the closest provable encloser instead?
* If so add the nearest to the closest provable encloser.
*/
goto cleanup;
goto cleanup;
}
if (sigrdataset != NULL)
}
static void
{
unsigned int options;
int order;
sigrdataset = NULL;
/*
* Get the NOQNAME proof then if !ispositive
* get the NOWILDCARD proof.
*
* DNS_DBFIND_NOWILD finds the NSEC records that covers the
* name ignoring any wildcard. From the owner and next names
* of this record you can compute which wildcard (if it exists)
* will match by finding the longest common suffix of the
* owner name and next names with the qname and prefixing that
* with the wildcard label.
*
* e.g.
* Given:
* example SOA
* example NSEC b.example
* b.example A
* b.example NSEC a.d.example
* a.d.example A
* a.d.example NSEC g.f.example
* g.f.example A
* g.f.example NSEC z.i.example
* z.i.example A
* z.i.example NSEC example
*
* QNAME:
* a.example -> example NSEC b.example
* owner common example
* next common example
* wild *.example
* d.b.example -> b.example NSEC a.d.example
* owner common b.example
* next common example
* wild *.b.example
* a.f.example -> a.d.example NSEC g.f.example
* owner common example
* next common f.example
* wild *.f.example
* j.example -> z.i.example NSEC example
* owner common example
* next common example
* wild *.example
*/
/*
* We'll need some resources...
*/
goto cleanup;
goto cleanup;
if (!dns_rdataset_isassociated(rdataset)) {
/*
* No NSEC proof available, return NSEC3 proofs instead.
*/
/*
* Find the closest encloser.
*/
while (result == DNS_R_NXDOMAIN) {
/*
* Sanity check.
*/
if (labels == 0U)
goto cleanup;
}
/*
* Add closest (provable) encloser NSEC3.
*/
goto cleanup;
if (!ispositive)
/*
* Replace resources which were consumed by query_addrrset.
*/
goto cleanup;
}
else if (dns_rdataset_isassociated(rdataset))
if (sigrdataset == NULL)
else if (dns_rdataset_isassociated(sigrdataset))
goto cleanup;
/*
* Add no qname proof.
*/
else
goto cleanup;
if (ispositive)
goto cleanup;
/*
* Replace resources which were consumed by query_addrrset.
*/
goto cleanup;
}
else if (dns_rdataset_isassociated(rdataset))
if (sigrdataset == NULL)
else if (dns_rdataset_isassociated(sigrdataset))
goto cleanup;
/*
* Add the no wildcard proof.
*/
if (result != ISC_R_SUCCESS)
goto cleanup;
goto cleanup;
goto cleanup;
} else if (result == DNS_R_NXDOMAIN) {
if (!ispositive)
if (result == ISC_R_SUCCESS) {
}
if (result == ISC_R_SUCCESS) {
&olabels);
&nlabels);
/*
* Check for a pathological condition created when
* serving some malformed signed zones and bail out.
*/
goto cleanup;
else
if (result == ISC_R_SUCCESS)
}
}
if (sigrdataset != NULL)
if (have_wname) {
goto again;
}
}
if (sigrdataset != NULL)
}
static void
{
unsigned int labels;
return;
}
if (sigrdatasetp == NULL)
return;
return;
if (result != ISC_R_SUCCESS)
return;
if (result != ISC_R_SUCCESS)
return;
return;
/* XXX */
/*
* We'll need some resources...
*/
return;
return;
/* This will succeed, since we've stripped labels. */
NULL) == ISC_R_SUCCESS);
}
static void
{
/*
* If the two pointers are the same then leave the setting of
* (*deventp) to NULL to isc_event_free.
*/
}
static void
int errorloglevel;
/*
* Resume a query after recursion.
*/
/*
* This is the fetch we've been waiting for.
*/
/*
* Update client->now.
*/
} else {
/*
* This is a fetch completion event for a canceled fetch.
* Clean up and don't resume the find.
*/
}
/*
* If this client is shutting down, or this transaction
* has timed out, do not resume the find.
*/
if (fetch_canceled || client_shuttingdown) {
if (fetch_canceled) {
} else
/*
* This may destroy the client.
*/
} else {
if (result != ISC_R_SUCCESS) {
if (result == DNS_R_SERVFAIL)
else
}
}
}
}
static void
}
}
static void
{
unsigned int options;
return;
&client->recursionquota);
if (result != ISC_R_SUCCESS)
return;
}
if (tmprdataset == NULL)
return;
else
if (result != ISC_R_SUCCESS) {
}
}
static isc_result_t
{
if (!resuming)
/*
* We are about to recurse, which means that this client will
* be unavailable for serving new requests for an indeterminate
* amount of time. If this client is currently responsible
* for handling incoming queries, set up a new client
* object to handle them while we are waiting for a
* response. There is no need to replace TCP clients
* because those have already been replaced when the
* connection was accepted (if allowed by the TCP quota).
*/
&client->recursionquota);
if (result == ISC_R_SOFTQUOTA) {
"recursive-clients soft limit "
"exceeded (%d/%d/%d), "
"aborting oldest query",
}
} else if (result == ISC_R_QUOTA) {
"no more recursive clients "
"(%d/%d/%d): %s",
}
}
if (result != ISC_R_SUCCESS) {
"ns_client_replace() failed: %s",
}
}
if (result != ISC_R_SUCCESS)
return (result);
}
/*
* Invoke the resolver.
*/
return (ISC_R_NOMEMORY);
if (WANTDNSSEC(client)) {
if (sigrdataset == NULL) {
return (ISC_R_NOMEMORY);
}
} else
sigrdataset = NULL;
else
if (result == ISC_R_SUCCESS) {
/*
* Record that we're waiting for an event. A client which
* is shutting down will not be destroyed until all the
* events have been received.
*/
} else {
if (sigrdataset != NULL)
}
return (result);
}
static inline void
{
}
}
static inline void
}
static inline isc_result_t
"rpz_ready: query_newrdataset failed");
return (DNS_R_SERVFAIL);
}
} else if (dns_rdataset_isassociated(*rdatasetp)) {
}
return (ISC_R_SUCCESS);
}
static void
}
static dns_rpz_zbits_t
{
switch (rpz_type) {
case DNS_RPZ_TYPE_CLIENT_IP:
break;
case DNS_RPZ_TYPE_QNAME:
break;
case DNS_RPZ_TYPE_IP:
if (ip_type == dns_rdatatype_a) {
} else if (ip_type == dns_rdatatype_aaaa) {
} else {
}
break;
case DNS_RPZ_TYPE_NSDNAME:
break;
case DNS_RPZ_TYPE_NSIP:
if (ip_type == dns_rdatatype_a) {
} else if (ip_type == dns_rdatatype_aaaa) {
} else {
}
break;
default:
INSIST(0);
break;
}
/*
* Choose
* the earliest configured policy zone (rpz->num)
* QNAME over IP over NSDNAME over NSIP (rpz_type)
* the smallest name,
* the longest IP address prefix,
* the lexically smallest address.
*/
} else{
}
}
/*
* If the client wants recursion, allow only compatible policies.
*/
if (!RECURSIONOK(client))
return (zbits);
}
static void
unsigned int options;
return;
&client->recursionquota);
if (result != ISC_R_SUCCESS)
return;
}
if (tmprdataset == NULL)
return;
else
if (result != ISC_R_SUCCESS) {
}
}
/*
* Get an NS, A, or AAAA rrset related to the response for the client
* to check the contents of that rrset for hits by eligible policy zones.
*/
static isc_result_t
{
if (result == DNS_R_DELEGATION) {
}
return (result);
}
if (result != ISC_R_SUCCESS) {
return (result);
}
} else {
if (result != ISC_R_SUCCESS) {
return (result);
}
}
/*
* Try the cache if we're authoritative for an
* ancestor but not the domain itself.
*/
}
if (result == DNS_R_DELEGATION) {
/*
* Recurse for NS rrset or A or AAAA rrset for an NS.
* Do not recurse for addresses for the query name.
*/
if (rpz_type == DNS_RPZ_TYPE_IP) {
} else {
if (result == ISC_R_SUCCESS) {
}
}
}
return (result);
}
/*
* Compute a policy owner name, p_name, in a policy zone given the needed
* policy type and the trigger name.
*/
static isc_result_t
{
/*
* The policy owner name consists of a suffix depending on the type
* and policy zone and a prefix that is the longest possible string
* from the trigger name that keesp the resulting policy owner name
* from being too long.
*/
switch (rpz_type) {
case DNS_RPZ_TYPE_CLIENT_IP:
break;
case DNS_RPZ_TYPE_QNAME:
break;
case DNS_RPZ_TYPE_IP:
break;
case DNS_RPZ_TYPE_NSDNAME:
break;
case DNS_RPZ_TYPE_NSIP:
break;
default:
INSIST(0);
}
/*
* Start with relative version of the full trigger name,
* and trim enough allow the addition of the suffix.
*/
first = 0;
for (;;) {
&prefix);
if (result == ISC_R_SUCCESS)
break;
/*
* Trim the trigger name until the combination is not too long.
*/
return (ISC_R_FAILURE);
}
/*
* Complain once about trimming the trigger name.
*/
if (first == 0) {
}
++first;
}
return (ISC_R_SUCCESS);
}
/*
* Look in policy zone rpz for a policy of rpz_type by p_name.
* The self-name (usually the client qname or an NS name) is compared with
* the target of a CNAME policy for the old style passthru encoding.
* If found, the policy is recorded in *zonep, *dbp, *versionp, *nodep,
* *rdatasetp, and *policyp.
* The target DNS type, qtype, chooses the best rdataset for *rdatasetp.
* The caller must decide if the found policy is most suitable, including
* better than a previously found policy.
* If it is best, the caller records it in client->query.rpz_st->m.
*/
static isc_result_t
{
/*
* Try to find either a CNAME or the type of record demanded by the
* request from the policy zone.
*/
if (result != ISC_R_SUCCESS) {
return (DNS_R_SERVFAIL);
}
if (result != ISC_R_SUCCESS)
return (DNS_R_NXDOMAIN);
/*
* Choose the best rdataset if we found something.
*/
if (result == ISC_R_SUCCESS) {
&rdsiter);
if (result != ISC_R_SUCCESS) {
"rpz_find_p: allrdatasets failed");
return (DNS_R_SERVFAIL);
}
result == ISC_R_SUCCESS;
break;
}
if (result != ISC_R_SUCCESS) {
if (result != ISC_R_NOMORE) {
" rdatasetiter", result);
"rpz_find_p: rdatasetiter failed");
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 (qtype == dns_rdatatype_rrsig ||
else
}
}
switch (result) {
case ISC_R_SUCCESS:
} else {
if ((*policyp == DNS_RPZ_POLICY_RECORD ||
*policyp == DNS_RPZ_POLICY_WILDCNAME) &&
qtype != dns_rdatatype_cname &&
return (DNS_R_CNAME);
}
return (ISC_R_SUCCESS);
case DNS_R_NXRRSET:
return (result);
case DNS_R_DNAME:
/*
* DNAME policy RRs have very few if any uses that are not
* better served with simple wildcards. Making them work would
* require complications to get the number of labels matched
* in the name or the found name to the main DNS_R_DNAME case
* in query_find(). The domain also does not appear in the
* summary database at the right level, so this happens only
* with a single policy zone when we have no summary database.
* Treat it as a miss.
*/
case DNS_R_NXDOMAIN:
case DNS_R_EMPTYNAME:
return (DNS_R_NXDOMAIN);
default:
"", result);
"rpz_find_p: unexpected result");
return (DNS_R_SERVFAIL);
}
}
static void
{
/*
* Save the replacement rdataset from the policy
* and make the previous replacement rdataset scratch.
*/
} else {
}
}
/*
* Check this address in every eligible policy zone.
*/
static isc_result_t
{
while (zbits != 0) {
if (rpz_num == DNS_RPZ_INVALID_NUM)
break;
/*
* Do not try applying policy zones that cannot replace a
* previously found policy zone.
* Stop looking if the next best choice cannot
* replace what we already have.
*/
break;
break;
}
/*
* Get the policy for a prefix at least as long
* as the prefix of the entry we had before.
*/
if (result != ISC_R_SUCCESS)
continue;
p_rdatasetp, &policy);
switch (result) {
case DNS_R_NXDOMAIN:
/*
* Continue after a policy record that is missing
* contrary to the summary data. The summary
* data can out of date during races with and among
* policy zone updates.
*/
"rpz_rewrite_ip: mismatched summary data; "
"continuing");
continue;
case DNS_R_SERVFAIL:
return (DNS_R_SERVFAIL);
default:
/*
* Forget this policy if it is not preferable
* to the previously found policy.
* If this policy is not good, then stop looking
* because none of the later policy zones would work.
*
* With more than one applicable policy, prefer
* the earliest configured policy,
* client-IP over QNAME over IP over NSDNAME over NSIP,
* the longest prefix
* the lexically smallest address.
* dns_rpz_find_ip() ensures st->m.rpz->num >= rpz->num.
* We can compare new and current p_name because
* both are of the same type and in the same zone.
* The tests above eliminate other reasons to
* reject this policy. If this policy can't work,
* then neither can later zones.
*/
break;
/*
* Stop checking after saving an enabled hit in this
* policy zone. The radix tree in the policy zone
* ensures that we found the longest match.
*/
"rpz_rewrite_ip: rpz_save_p");
break;
}
/*
* Log DNS_RPZ_POLICY_DISABLED zones
* and try the next eligible policy zone.
*/
}
}
return (ISC_R_SUCCESS);
}
/*
* Check the IP addresses in the A or AAAA rrsets for name against
* all eligible rpz_type (IP or NSIP) response policy rewrite rules.
*/
static isc_result_t
{
if (zbits == 0)
return (ISC_R_SUCCESS);
/*
* Get the A or AAAA rdataset.
*/
switch (result) {
case ISC_R_SUCCESS:
case DNS_R_GLUE:
case DNS_R_ZONECUT:
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:
case ISC_R_NOTFOUND:
return (ISC_R_SUCCESS);
case DNS_R_DELEGATION:
case DNS_R_DUPLICATE:
case DNS_R_DROP:
return (result);
case DNS_R_CNAME:
case DNS_R_DNAME:
" NS address rewrite rrset", result);
return (ISC_R_SUCCESS);
default:
rpz_type, " NS address rewrite rrset",
result);
}
"rpz_rewrite_ip_rrset: unexpected result");
return (DNS_R_SERVFAIL);
}
/*
* Check all of the IP addresses in the rdataset.
*/
result == ISC_R_SUCCESS;
case dns_rdatatype_a:
break;
case dns_rdatatype_aaaa:
break;
default:
continue;
}
zbits, p_rdatasetp);
if (result != ISC_R_SUCCESS)
return (result);
}
return (ISC_R_SUCCESS);
}
/*
* Look for IP addresses in A and AAAA rdatasets
* that trigger all eligible IP or NSIP policy rules.
*/
static isc_result_t
{
ip_version = NULL;
p_rdataset = NULL;
(qtype == dns_rdatatype_a ||
qtype == dns_rdatatype_any ||
rpz_type == DNS_RPZ_TYPE_NSIP)) {
/*
* Rewrite based on an IPv4 address that will appear
* in the ANSWER section or if we are checking IP addresses.
*/
&p_rdataset, resuming);
if (result == ISC_R_SUCCESS)
} else {
}
if (result == ISC_R_SUCCESS &&
(qtype == dns_rdatatype_aaaa ||
qtype == dns_rdatatype_any ||
rpz_type == DNS_RPZ_TYPE_NSIP)) {
/*
* Rewrite based on IPv6 addresses that will appear
* in the ANSWER section or if we are checking IP addresses.
*/
&p_rdataset, resuming);
}
return (result);
}
/*
* Try to rewrite a request for a qtype rdataset based on the trigger name
* trig_name and rpz_type (DNS_RPZ_TYPE_QNAME or DNS_RPZ_TYPE_NSDNAME).
* Record the results including the replacement rdataset if any
* in client->query.rpz_st.
* *rdatasetp is a scratch rdataset.
*/
static isc_result_t
{
zbits &= allowed_zbits;
if (zbits == 0)
return (ISC_R_SUCCESS);
/*
* Use the summary database to find the bit mask of policy zones
* with policies for this trigger name. We do this even if there
* is only one eligible policy zone so that wildcard triggers
* are matched correctly, and not into their parent.
*/
if (zbits == 0)
return (ISC_R_SUCCESS);
/*
* Check the trigger name in every policy zone that the summary data
* says has a hit for the trigger name.
* Most of the time there are no eligible zones and the summary data
* keeps us from getting this far.
* We check the most eligible zone first and so usually check only
* one policy zone.
*/
if ((zbits & 1) == 0)
continue;
/*
* Do not check policy zones that cannot replace a previously
* found policy.
*/
break;
break;
}
/*
* Get the next policy zone's record for this trigger name.
*/
if (result != ISC_R_SUCCESS)
continue;
switch (result) {
case DNS_R_NXDOMAIN:
/*
* Continue after a missing policy record
* contrary to the summary data. The summary
* data can out of date during races with and among
* policy zone updates.
*/
"rpz_rewrite_name: mismatched summary data; "
"continuing");
continue;
case DNS_R_SERVFAIL:
return (DNS_R_SERVFAIL);
default:
/*
* With more than one applicable policy, prefer
* the earliest configured policy,
* client-IP over QNAME over IP over NSDNAME over NSIP,
* and the smallest name.
* We known st->m.rpz->num >= rpz->num and either
* st->m.rpz->num > rpz->num or st->m.type >= rpz_type
*/
continue;
#if 0
/*
* This code would block a customer reported information
* leak of rpz rules by rewriting requests in the
* rpz-ip, rpz-nsip, rpz-nsdname,and rpz-passthru TLDs.
* Without this code, a bad guy could request
* 24.0.3.2.10.rpz-ip. to find the policy rule for
* 10.2.3.0/14. It is an insignificant leak and this
* code is not worth its cost, because the bad guy
* could publish "evil.com A 10.2.3.4" and request
* evil.com to get the same information.
* Keep code with "#if 0" in case customer demand
* is irresistible.
*
* We have the less frequent case of a triggered
* policy. Check that we have not trigger on one
* of the pretend RPZ TLDs.
* This test would make it impossible to rewrite
* names in TLDs that start with "rpz-" should
* ICANN ever allow such TLDs.
*/
unsigned int labels;
if (labels >= 2) {
sizeof(DNS_RPZ_PREFIX)-1) == 0)
continue;
}
#endif
"rpz_rewrite_name: rpz_save_p");
/*
* After a hit, higher numbered policy zones
* are irrelevant
*/
return (ISC_R_SUCCESS);
}
/*
* Log DNS_RPZ_POLICY_DISABLED zones
* and try the next eligible policy zone.
*/
break;
}
}
return (ISC_R_SUCCESS);
}
static void
{
}
/*
* RPZ query result types
*/
typedef enum {
/*
* Look for response policy zone QNAME, NSIP, and NSDNAME rewriting.
*/
static isc_result_t
{
int rpz_ver;
return (DNS_R_DISALLOWED);
{
return (DNS_R_DISALLOWED);
}
return (ISC_R_NOMEMORY);
}
}
/*
* There is nothing to rewrite if the main query failed.
*/
switch (qresult) {
case ISC_R_SUCCESS:
case DNS_R_GLUE:
case DNS_R_ZONECUT:
break;
case DNS_R_EMPTYNAME:
case DNS_R_NXRRSET:
case DNS_R_NXDOMAIN:
case DNS_R_EMPTYWILD:
case DNS_R_NCACHENXDOMAIN:
case DNS_R_NCACHENXRRSET:
case DNS_R_CNAME:
case DNS_R_DNAME:
break;
case DNS_R_DELEGATION:
case ISC_R_NOTFOUND:
/*
* If recursion is on, do only tentative rewriting.
* If recursion is off, this the normal and only time we
* can rewrite.
*/
if (RECURSIONOK(client))
else
break;
case ISC_R_FAILURE:
case ISC_R_TIMEDOUT:
case DNS_R_BROKENCHAIN:
" stop on qresult in rpz_rewrite()", qresult);
return (ISC_R_SUCCESS);
default:
" stop on unrecognized qresult in rpz_rewrite()",
qresult);
return (ISC_R_SUCCESS);
}
if (qresult_type == RPZ_QRESULT_TYPE_RECURSE) {
/*
* This request needs recursion that has not been done.
* Get bits for the policy zones that do not need
* to wait for the results of recursion.
*/
if (allowed == 0)
return (ISC_R_SUCCESS);
} else {
}
/*
* Check once for triggers for the client IP address.
*/
if (zbits != 0) {
if (result != ISC_R_SUCCESS)
goto cleanup;
}
}
/*
* Check triggers for the query name if this is the first time
* for the current qname.
* There is a first time for each name in a CNAME chain
*/
if (result != ISC_R_SUCCESS)
goto cleanup;
/*
* Check IPv4 addresses in A RRs next.
* Reset to the start of the NS names.
*/
}
/*
* Quit if this was an attempt to find a qname or
* client-IP trigger before recursion.
* We will be back if no pre-recursion triggers hit.
* For example, consider 2 policy zones, both with qname and
* IP address triggers. If the qname misses the 1st zone,
* then we cannot know whether a hit for the qname in the
* 2nd zone matters until after recursing to get the A RRs and
* testing them in the first zone.
* Do not bother saving the work from this attempt,
* because recusion is so slow.
*/
if (qresult_type == RPZ_QRESULT_TYPE_RECURSE)
goto cleanup;
/*
* DNS_RPZ_DONE_QNAME but not DNS_RPZ_DONE_CLIENT_IP
* is reset at the end of dealing with each CNAME.
*/
}
/*
* Check known IP addresses for the query name if the database
* lookup resulted in some addresses (qresult_type ==
* RPZ_QRESULT_TYPE_DONE) and if we have not already checked them.
* Any recursion required for the query has already happened.
* Do not check addresses that will not be in the ANSWER section.
*/
if (result != ISC_R_SUCCESS)
goto cleanup;
/*
* We are finished checking the IP addresses for the qname.
* Start with IPv4 if we will check NS IP addesses.
*/
}
/*
* Stop looking for rules if there are none of the other kinds
* that could override what we already have.
*/
DNS_RPZ_TYPE_NSDNAME) == 0 &&
DNS_RPZ_TYPE_NSIP) == 0) {
goto cleanup;
}
/*
* Get NS rrset for each domain in the current qname.
*/
} else {
}
{
resuming);
dns_db_detach(&db);
goto cleanup;
switch (result) {
case ISC_R_SUCCESS:
if (result != ISC_R_SUCCESS)
goto cleanup;
break;
case DNS_R_DELEGATION:
case DNS_R_DUPLICATE:
case DNS_R_DROP:
goto cleanup;
case DNS_R_EMPTYNAME:
case DNS_R_NXRRSET:
case DNS_R_EMPTYWILD:
case DNS_R_NXDOMAIN:
case DNS_R_NCACHENXDOMAIN:
case DNS_R_NCACHENXRRSET:
case ISC_R_NOTFOUND:
case DNS_R_CNAME:
case DNS_R_DNAME:
0, NULL);
continue;
case ISC_R_TIMEDOUT:
case DNS_R_BROKENCHAIN:
case ISC_R_FAILURE:
" NS rpz_rrset_find()");
continue;
default:
" unrecognized NS"
" rpz_rrset_find()");
continue;
}
}
/*
* Check all NS names.
*/
do {
if (result != ISC_R_SUCCESS) {
" rdata_tostruct()", result);
goto cleanup;
}
/*
* Do nothing about "NS ."
*/
continue;
}
/*
* Check this NS name if we did not handle it
* during a previous recursion.
*/
&rdataset);
if (result != ISC_R_SUCCESS) {
goto cleanup;
}
}
/*
* Check all IP addresses for this NS name.
*/
if (result != ISC_R_SUCCESS)
goto cleanup;
} while (result == ISC_R_SUCCESS);
DNS_RPZ_TYPE_NSDNAME) == 0 &&
DNS_RPZ_TYPE_NSIP) == 0)
break;
}
/*
* Use the best hit, if any.
*/
}
}
return (result);
}
/*
* See if response policy zone rewriting is allowed by a lack of interest
* by the client in DNSSEC or a lack of signatures.
*/
static isc_boolean_t
{
return (ISC_TRUE);
/*
* We do not know if there are signatures if we have not recursed
* for them.
*/
return (ISC_FALSE);
if (sigrdataset == NULL)
return (ISC_TRUE);
return (ISC_FALSE);
/*
* We are happy to rewrite nothing.
*/
return (ISC_TRUE);
/*
* Do not rewrite if there is any sign of signatures.
*/
return (ISC_FALSE);
/*
* Look for a signature in a negative cache rdataset.
*/
return (ISC_TRUE);
result == ISC_R_SUCCESS;
if (type == dns_rdatatype_nsec ||
type == dns_rdatatype_nsec3 ||
return (ISC_FALSE);
}
return (ISC_TRUE);
}
/*
* Add a CNAME to the query response, including translating foo.evil.com and
* to
* foo.evil.com CNAME foo.evil.com.example.com
*/
static isc_result_t
{
unsigned int labels;
if (result == DNS_R_NAMETOOLONG)
} else {
}
if (result != ISC_R_SUCCESS)
return (result);
if (result != ISC_R_SUCCESS)
return (result);
/*
* Turn off DNSSEC because the results of a
* response policy zone cannot verify.
*/
return (ISC_R_SUCCESS);
}
#define QUERY_ERROR(r) \
do { \
eresult = r; \
want_restart = ISC_FALSE; \
} while (0)
#define RECURSE_ERROR(r) \
do { \
if ((r) == DNS_R_DUPLICATE || (r) == DNS_R_DROP) \
QUERY_ERROR(r); \
else \
} while (0)
/*
* Extract a network address from the RDATA of an A or AAAA
* record.
*
* Returns:
* ISC_R_SUCCESS
* ISC_R_NOTIMPLEMENTED The rdata is not a known address type.
*/
static isc_result_t
case dns_rdatatype_a:
return (ISC_R_SUCCESS);
case dns_rdatatype_aaaa:
return (ISC_R_SUCCESS);
default:
return (ISC_R_NOTIMPLEMENTED);
}
}
/*
* Find the sort order of 'rdata' in the topology-like
* ACL forming the second element in a 2-element top-level
* sortlist statement.
*/
static int
return (INT_MAX);
}
/*
* Find the sort order of 'rdata' in the matching element
* of a 1-element top-level sortlist statement.
*/
static int
return (INT_MAX);
}
/*
* Find the sortlist statement that applies to 'client' and set up
* the sortlist info in in client->message appropriately.
*/
static void
case NS_SORTLISTTYPE_1ELEMENT:
break;
case NS_SORTLISTTYPE_2ELEMENT:
break;
case NS_SORTLISTTYPE_NONE:
break;
default:
INSIST(0);
break;
}
}
static void
goto cleanup;
goto cleanup;
goto cleanup;
goto cleanup;
}
else if (dns_rdataset_isassociated(neg))
else if (dns_rdataset_isassociated(negsig))
goto cleanup;
}
static inline void
break;
break;
}
}
}
};
static void
unsigned int i;
for (i = 0; i < (sizeof(rfc1918names)/sizeof(*rfc1918names)); i++) {
&rfc1918names[i],
&found);
if (result != ISC_R_SUCCESS)
return;
"RFC 1918 response from "
"Internet for %s", buf);
}
return;
}
}
}
static void
{
unsigned int dboptions;
salt_length = sizeof(salt);
if (result != ISC_R_SUCCESS)
return;
/*
* Map unknown algorithm to known value.
*/
if (hash == DNS_NSEC3_UNKNOWNALG)
hash = 1;
if (result != ISC_R_SUCCESS)
return;
if (result == DNS_R_NXDOMAIN) {
if (!dns_rdataset_isassociated(rdataset)) {
return;
}
{
skip++;
&name);
"looking for closest provable encloser");
goto again;
}
if (exact)
"expected a exact match NSEC3, got "
"a covering record");
} else if (result != ISC_R_SUCCESS) {
return;
} else if (!exact)
"expected covering NSEC3, got an exact match");
if (skip != 0U)
found);
return;
}
#ifdef ALLOW_FILTER_AAAA
static isc_boolean_t
return (ISC_TRUE);
return (ISC_TRUE);
return (ISC_FALSE);
}
static isc_boolean_t
return (ISC_TRUE);
return (ISC_FALSE);
}
#endif
static isc_uint32_t
if (result != ISC_R_SUCCESS)
goto cleanup;
if (result != ISC_R_SUCCESS)
goto cleanup;
if (result != ISC_R_SUCCESS)
goto cleanup;
return (ttl);
}
static isc_boolean_t
{
unsigned int flags = 0;
unsigned int i, count;
return (ISC_TRUE);
if (RECURSIONOK(client))
for (i = 0; i < count; i++) {
break;
}
}
sizeof(isc_boolean_t) * count);
return (ISC_TRUE);
}
sizeof(isc_boolean_t) * count);
return (ISC_FALSE);
}
/*
* Look for the name and type in the redirection zone. If found update
* the arguments as appropriate. Return ISC_TRUE if a update was
* performed.
*
* Only perform the update if the client is in the allow query acl and
* returning the update would not cause a DNSSEC validation failure.
*/
static isc_result_t
{
return (ISC_R_NOTFOUND);
return (ISC_R_NOTFOUND);
return (ISC_R_NOTFOUND);
return (ISC_R_NOTFOUND);
result == ISC_R_SUCCESS;
if (type == dns_rdatatype_nsec ||
type == dns_rdatatype_nsec3 ||
return (ISC_R_NOTFOUND);
}
}
}
ISC_TRUE);
if (result != ISC_R_SUCCESS)
return (ISC_R_NOTFOUND);
if (result != ISC_R_SUCCESS)
return (ISC_R_NOTFOUND);
dns_db_detach(&db);
return (ISC_R_NOTFOUND);
}
/*
* Lookup the requested data in the redirect zone.
*/
goto nxrrset;
} else if (result != ISC_R_SUCCESS) {
dns_db_detach(&db);
return (ISC_R_NOTFOUND);
}
if (dns_rdataset_isassociated(&trdataset)) {
}
dns_db_detach(&db);
return (result);
}
static isc_result_t
{
unsigned int options;
return (ISC_R_NOTFOUND);
return (ISC_R_NOTFOUND);
return (ISC_R_NOTFOUND);
return (ISC_R_NOTFOUND);
return (ISC_R_NOTFOUND);
result == ISC_R_SUCCESS;
if (type == dns_rdatatype_nsec ||
type == dns_rdatatype_nsec3 ||
return (ISC_R_NOTFOUND);
}
}
}
redirectname, NULL);
if (result != ISC_R_SUCCESS)
return (ISC_R_NOTFOUND);
} else
options = 0;
if (result != ISC_R_SUCCESS)
return (ISC_R_NOTFOUND);
/*
* Lookup the requested data in the redirect zone.
*/
goto nxrrset;
/*
* Cleanup.
*/
dns_db_detach(&db);
/*
* Don't loop forever if the lookup failed last time.
*/
if (result == ISC_R_SUCCESS) {
return (DNS_R_CONTINUE);
}
}
return (ISC_R_NOTFOUND);
} else if (result != ISC_R_SUCCESS) {
dns_db_detach(&db);
return (ISC_R_NOTFOUND);
}
/*
* Adjust the found name to not include the redirectzone suffix.
*/
/*
* Make the name absolute.
*/
if (dns_rdataset_isassociated(&trdataset)) {
}
dns_db_detach(&db);
return (result);
}
/*
* Do the bulk of query processing for the current query of 'client'.
* If 'event' is non-NULL, we are returning from recursion and 'qtype'
* is ignored. Otherwise, 'qtype' is the query type.
*/
static isc_result_t
{
unsigned int n, nlabels;
int order;
isc_buffer_t b;
unsigned int options;
#ifdef WANT_QUERYTRACE
#endif
/*
* One-time initialization.
*
* It's especially important to initialize anything that the cleanup
* code might cleanup.
*/
sigrdataset = NULL;
zsigrdataset = NULL;
options = 0;
#ifdef WANT_QUERYTRACE
sizeof(qbuf));
else
"client attr:0x%x, query attr:0x%X, restarts:%d, "
"origqname:%s, timer:%d, authdb:%d, referral:%d",
#endif
/*
* We're returning from recursion. Restore the query context
* and resume.
*/
{
#ifdef WANT_QUERYTRACE
{
else
"<unset>");
"rpz rname:%s, pname:%s, fname:%s",
}
#endif
/*
* Restore saved state.
*/
"resume from redirect recursion");
#ifdef WANT_QUERYTRACE
"redirect fname:%s, qtype:%s, auth:%d",
#endif
/*
* Free resources used while recursing.
*/
} else {
"resume from normal recursion");
}
else
}
if (DNS64EXCLUDE(client)) {
}
{
/*
* Has response policy changed out from under us?
*/
"query_find: RPZ settings "
"out of date "
"(rpz_ver %d, expected %d)",
goto cleanup;
}
}
/*
* We'll need some resources...
*/
"query_find: query_getnamebuf failed (1)");
goto cleanup;
}
"query_find: query_newname failed (1)");
goto cleanup;
}
} else {
}
if (result != ISC_R_SUCCESS) {
"query_find: dns_name_copy failed");
goto cleanup;
}
} else {
}
goto resume;
}
/*
* Not returning from recursion.
*
* First, check for a recent match in the view's SERVFAIL cache.
* If we find one, and it was from a query with CD=1, *or*
* if the current query has CD=0, then we can just return
* SERVFAIL now.
*/
if (RECURSIONOK(client)) {
flags = 0;
#ifdef ENABLE_AFL
if (ns_g_fuzz_type == ns_fuzz_resolver) {
} else {
}
#else
#endif
if (failcache &&
(((flags & NS_FAILCACHE_CD) != 0) ||
{
sizeof(typename));
ISC_LOG_DEBUG(1),
"servfail cache hit %s/%s (%s)",
((flags & NS_FAILCACHE_CD) != 0)
? "CD=1"
: "CD=0");
}
goto cleanup;
}
}
/*
* If it's a SIG query, we'll iterate the node.
*/
else
sizeof(classname));
"check-names failure %s/%s/%s", namebuf,
goto cleanup;
}
/*
* First we must find the right database.
*/
if (dns_rdatatype_atparent(qtype) &&
qtype == dns_rdatatype_ds &&
!RECURSIONOK(client) &&
(options & DNS_GETDB_NOEXACT) != 0))
{
/*
* If the query type is DS, look to see if we are
* authoritative for the child zone.
*/
&tversion);
if (tresult == ISC_R_SUCCESS) {
options &= ~DNS_GETDB_NOEXACT;
dns_db_detach(&db);
} else {
dns_db_detach(&tdb);
}
}
if (result != ISC_R_SUCCESS) {
if (result == DNS_R_REFUSED) {
if (WANTRECURSION(client)) {
} else
if (!PARTIALANSWER(client))
} else {
"query_find: query_getdb failed");
}
goto cleanup;
}
if (is_zone) {
}
if (is_zone) {
/*
* if is_zone = true, zone = NULL then this is
* a DLZ zone. Don't attempt to attach zone.
*/
}
}
/* Track TCP vs UDP stats per zone */
else
}
/*
* We'll need some resources...
*/
"query_find: query_getnamebuf failed (2)");
goto cleanup;
}
"query_find: query_newname failed (2)");
goto cleanup;
}
if (sigrdataset == NULL) {
"query_find: query_newrdataset failed (2)");
goto cleanup;
}
}
/*
* Now look for an answer in the database. If this is a dns64
* AAAA lookup on a rpz database adjust the qname.
*/
else
/*
* Fixup fname and sigrdataset.
*/
if (sigrdataset != NULL &&
}
if (!is_zone)
/*
* Rate limit these responses to this client.
* Do not delay counting and handling obvious referrals,
* since those won't come here again.
* Delay handling delegations for which we are certain to recurse and
* return here (DNS_R_DELEGATION, not a child of one of our
* own zones, and recursion enabled)
* Don't mess with responses rewritten by RPZ
* Count each response at most once.
*/
{
if (result == DNS_R_NXDOMAIN) {
/*
* Use the database origin name to rate limit NXDOMAIN
*/
} else if (result == DNS_R_NCACHENXDOMAIN &&
(rdataset->attributes &
DNS_RDATASETATTR_NEGATIVE) != 0) {
/*
* Try to use owner name in the negative cache SOA.
*/
&nc_rdataset);
break;
}
}
} else if (result == DNS_R_NXRRSET ||
result == DNS_R_EMPTYNAME) {
} else if (result == DNS_R_DELEGATION) {
} else if (result == ISC_R_NOTFOUND) {
/*
* Handle referral to ".", including when recursion
* is off or not requested and the hints have not
* been loaded or we have "additional-from-cache no".
*/
} else {
}
if (rrl_result != DNS_RRL_RESULT_OK) {
/*
* Log dropped or slipped responses in the query
* category so that requests are not silently lost.
* Starts of rate-limited bursts are logged in
* DNS_LOGCATEGORY_RRL.
*
* Dropped responses are counted with dropped queries
* in QryDropped while slipped responses are counted
* with other truncated responses in RespTruncated.
*/
if (wouldlog) {
"%s", log_buf);
}
if (rrl_result == DNS_RRL_RESULT_DROP) {
/*
* These will also be counted in
* dns_nsstatscounter_dropped
*/
} else {
/*
* These will also be counted in
* dns_nsstatscounter_truncatedresp
*/
if (WANTCOOKIE(client)) {
} else {
if (resp_result ==
}
}
goto cleanup;
}
}
goto cleanup;
}
{
switch (rresult) {
case ISC_R_SUCCESS:
break;
case DNS_R_DISALLOWED:
goto norpz;
case DNS_R_DELEGATION:
/*
* recursing for NS names or addresses,
* so save the main query state
*/
goto cleanup;
default:
goto cleanup;
}
{
/*
* We got a hit and are going to answer with our
* fiction. Ensure that we answer with the name
* we looked up even if we were stopped short
* in recursion or for a deferral.
*/
}
case DNS_RPZ_POLICY_TCP_ONLY:
if (result == DNS_R_NXDOMAIN ||
goto cleanup;
case DNS_RPZ_POLICY_DROP:
goto cleanup;
case DNS_RPZ_POLICY_NXDOMAIN:
break;
case DNS_RPZ_POLICY_NODATA:
break;
case DNS_RPZ_POLICY_RECORD:
if (qtype == dns_rdatatype_any &&
result != DNS_R_CNAME) {
/*
* We will add all of the rdatasets of
* the node by iterating later,
* and set the TTL then.
*/
} else {
/*
* We will add this rdataset.
*/
}
break;
case DNS_RPZ_POLICY_WILDCNAME:
NULL);
if (result != ISC_R_SUCCESS)
goto cleanup;
goto cleanup;
case DNS_RPZ_POLICY_CNAME:
/*
* Add overridding CNAME from a named.conf
* response-policy statement
*/
if (result != ISC_R_SUCCESS)
goto cleanup;
goto cleanup;
default:
INSIST(0);
}
/*
* Turn off DNSSEC because the results of a
* response policy zone cannot verify.
*/
}
}
switch (result) {
case ISC_R_SUCCESS:
/*
* This case is handled in the main line below.
*/
break;
case DNS_R_GLUE:
case DNS_R_ZONECUT:
/*
* These cases are handled in the main line below.
*/
break;
case ISC_R_NOTFOUND:
/*
* The cache doesn't even have the root NS. Get them from
* the hints DB.
*/
dns_db_detach(&db);
/* We have no hints. */
} else {
}
if (result != ISC_R_SUCCESS) {
/*
* Nonsensical root hints may require cleanup.
*/
if (sigrdataset != NULL &&
/*
* We don't have any root server hints, but
* we may have working forwarders, so try to
* recurse anyway.
*/
if (RECURSIONOK(client)) {
if (result == ISC_R_SUCCESS) {
if (dns64)
if (dns64_exclude)
} else
goto cleanup;
} else {
/* Unable to give root server referral. */
"unable to give root server referral");
goto cleanup;
}
}
/*
* XXXRTH We should trigger root server priming here.
*/
/* FALLTHROUGH */
case DNS_R_DELEGATION:
if (is_zone) {
/*
* Look to see if we are authoritative for the
* child zone if the query type is DS.
*/
if (!RECURSIONOK(client) &&
(options & DNS_GETDB_NOEXACT) != 0 &&
qtype == dns_rdatatype_ds) {
&tversion);
if (result == ISC_R_SUCCESS) {
options &= ~DNS_GETDB_NOEXACT;
if (sigrdataset != NULL)
&sigrdataset);
&fname);
dns_db_detach(&db);
goto db_find;
}
dns_db_detach(&tdb);
}
/*
* We're authoritative for an ancestor of QNAME.
*/
/*
* If we don't have a cache, this is the best
* answer.
*
* If the client is making a nonrecursive
* query we always give out the authoritative
* delegation. This way even if we get
* junk in our cache, we won't fail in our
* role as the delegating authority if another
* nameserver asks us about a delegated
* subzone.
*
* We enable the retrieval of glue for this
* database by setting client->query.gluedb.
*/
}
/*
* We must ensure NOADDITIONAL is off,
* because the generation of
* additional data is required in
* delegations.
*/
if (sigrdataset != NULL)
else
sigrdatasetp = NULL;
if (detach) {
}
if (WANTDNSSEC(client))
} else {
/*
* We might have a better answer or delegation
* in the cache. We'll remember the current
* values of fname, rdataset, and sigrdataset.
* We'll then go looking for QNAME in the
* cache. If we find something better, we'll
* use it instead.
*/
goto db_find;
}
} else {
/*
* 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.
*/
/*
* We've already done query_keepname() on
* zfname, so we must set dbuf to NULL to
* prevent query_addrrset() from trying to
* call query_keepname() again.
*/
if (sigrdataset != NULL)
&sigrdataset);
/*
* We don't clean up zdb here because we
* may still need it. It will get cleaned
* up by the main cleanup code.
*/
}
if (RECURSIONOK(client)) {
/*
* Recurse!
*/
if (dns_rdatatype_atparent(type))
else if (dns64)
else
resuming);
if (result == ISC_R_SUCCESS) {
if (dns64)
if (dns64_exclude)
} else if (result == DNS_R_DUPLICATE ||
result == DNS_R_DROP)
else
} else {
/*
* This is the best answer.
*/
}
/*
* We must ensure NOADDITIONAL is off,
* because the generation of
* additional data is required in
* delegations.
*/
if (sigrdataset != NULL)
else
sigrdatasetp = NULL;
if (detach) {
}
if (WANTDNSSEC(client))
}
}
goto cleanup;
case DNS_R_EMPTYNAME:
case DNS_R_NXRRSET:
if (dns64)
#else
if (dns64 && !dns64_exclude)
#endif
{
/*
* Restore the answers from the previous AAAA lookup.
*/
if (sigrdataset != NULL)
"query_find: "
"query_getnamebuf failed (3)");
goto cleanup;
}
"query_find: "
"query_newname failed (3)");
goto cleanup;
}
}
/*
* Resume the diverted processing of the AAAA response?
*/
if (dns64_excluded)
break;
#endif
} else if (result == DNS_R_NXRRSET &&
{
/*
* Look to see if there are A records for this
* name.
*/
goto db_find;
}
/*
* Look for a NSEC3 record if we don't have a NSEC record.
*/
if (redirected)
goto cleanup;
if (!dns_rdataset_isassociated(rdataset) &&
WANTDNSSEC(client)) {
/*
* Did we find the closest provable encloser
* instead? If so add the nearest to the
* closest provable encloser.
*/
if (dns_rdataset_isassociated(rdataset) &&
!(ns_g_nonearest &&
qtype != dns_rdatatype_ds))
{
unsigned int count;
unsigned int skip;
/*
* Add the closest provable encloser.
*/
&rdataset, &sigrdataset,
dbuf,
+ 1;
found);
sigrdataset == NULL) {
"query_find: "
"failure getting "
"closest encloser");
goto cleanup;
}
/*
* 'nearest' doesn't exist so
* 'exist' is set to ISC_FALSE.
*/
NULL);
}
} else {
}
}
if (dns_rdataset_isassociated(rdataset)) {
/*
* If we've got a NSEC record, we need to save the
* name now because we're going call query_addsoa()
* below, and it needs to use the name buffer.
*/
/*
* We're not going to use fname, and need to release
* our hold on the name buffer so query_addsoa()
* may use it.
*/
}
/*
* Add SOA to the additional section if generated by a RPZ
* rewrite.
*/
if (result != ISC_R_SUCCESS) {
goto cleanup;
}
/*
* Add NSEC record if we found one.
*/
if (WANTDNSSEC(client)) {
&sigrdataset);
}
goto cleanup;
case DNS_R_EMPTYWILD:
/* FALLTHROUGH */
case DNS_R_NXDOMAIN:
if (!empty_wild) {
if (tresult == ISC_R_SUCCESS) {
break;
}
if (tresult == DNS_R_NXRRSET) {
goto iszone_nxrrset;
}
if (tresult == DNS_R_NCACHENXRRSET) {
goto ncache_nxrrset;
}
if (tresult == DNS_R_CONTINUE) {
NULL);
goto cleanup;
}
if (tresult == ISC_R_SUCCESS) {
break;
}
if (tresult == DNS_R_NXRRSET) {
goto iszone_nxrrset;
}
if (tresult == DNS_R_NCACHENXRRSET) {
goto ncache_nxrrset;
}
}
if (dns_rdataset_isassociated(rdataset)) {
/*
* If we've got a NSEC record, we need to save the
* name now because we're going call query_addsoa()
* below, and it needs to use the name buffer.
*/
/*
* We're not going to use fname, and need to release
* our hold on the name buffer so query_addsoa()
* may use it.
*/
}
/*
* Add SOA to the additional section if generated by a
* RPZ rewrite.
*
* If the query was for a SOA record force the
* ttl to zero so that it is possible for clients to find
* the containing zone of an arbitrary name with a stub
* resolver and not have it cached.
*/
ttl = 0;
section);
if (result != ISC_R_SUCCESS) {
goto cleanup;
}
if (WANTDNSSEC(client)) {
/*
* Add NSEC record if we found one.
*/
}
/*
* Set message rcode.
*/
if (empty_wild)
else
goto cleanup;
case DNS_R_NCACHENXDOMAIN:
if (tresult == ISC_R_SUCCESS) {
break;
}
if (tresult == DNS_R_NXRRSET) {
goto iszone_nxrrset;
}
if (tresult == DNS_R_NCACHENXRRSET) {
goto ncache_nxrrset;
}
if (tresult == DNS_R_CONTINUE) {
NULL);
goto cleanup;
}
if (tresult == ISC_R_SUCCESS) {
break;
}
if (tresult == DNS_R_NXRRSET) {
goto iszone_nxrrset;
}
if (tresult == DNS_R_NCACHENXRRSET) {
goto ncache_nxrrset;
}
/* FALLTHROUGH */
case DNS_R_NCACHENXRRSET:
/*
* Set message rcode, if required.
*/
if (result == DNS_R_NCACHENXDOMAIN)
/*
* Look for RFC 1918 leakage from Internet.
*/
if (result == DNS_R_NCACHENXDOMAIN &&
qtype == dns_rdatatype_ptr &&
if (dns64)
#else
if (dns64 && !dns64_exclude)
#endif
{
/*
* Restore the answers from the previous AAAA lookup.
*/
if (sigrdataset != NULL)
"query_find: "
"query_getnamebuf failed (4)");
goto cleanup;
}
"query_find: "
"query_newname failed (4)");
goto cleanup;
}
}
if (dns64_excluded)
break;
#endif
} else if (result == DNS_R_NCACHENXRRSET &&
{
/*
* Look to see if there are A records for this
* name.
*/
/*
* 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.
*/
goto db_find;
}
/*
* We don't call query_addrrset() because we don't need any
* of its extra features (and things would probably break!).
*/
if (dns_rdataset_isassociated(rdataset)) {
}
goto cleanup;
case DNS_R_CNAME:
/*
* If we have a zero ttl from the cache refetch it.
*/
{
if (sigrdataset != NULL &&
if (result == ISC_R_SUCCESS) {
if (dns64)
if (dns64_exclude)
} else
goto cleanup;
}
/*
* Keep a copy of the rdataset. We have to do this because
* query_addrrset may clear 'rdataset' (to prevent the
* cleanup code from cleaning it up).
*/
/*
* Add the CNAME to the answer section.
*/
if (sigrdataset != NULL)
else
sigrdatasetp = NULL;
if (WANTDNSSEC(client) &&
{
NULL);
}
else
/*
* We set the PARTIALANSWER attribute so that if anything goes
* wrong later on, we'll return what we've got so far.
*/
/*
* Reset qname to be the target name of the CNAME and restart
* the query.
*/
if (result != ISC_R_SUCCESS)
goto cleanup;
if (result != ISC_R_SUCCESS) {
goto cleanup;
}
if (result != ISC_R_SUCCESS) {
goto cleanup;
}
if (result != ISC_R_SUCCESS) {
goto cleanup;
}
if (!WANTRECURSION(client))
goto addauth;
case DNS_R_DNAME:
/*
* Compare the current qname to the found name. We need
* to know how many labels and bits are in common because
* we're going to have to split qname later on.
*/
/*
* Keep a copy of the rdataset. We have to do this because
* query_addrrset may clear 'rdataset' (to prevent the
* cleanup code from cleaning it up).
*/
/*
* Add the DNAME to the answer section.
*/
if (sigrdataset != NULL)
else
sigrdatasetp = NULL;
if (WANTDNSSEC(client) &&
{
NULL);
}
/*
* We set the PARTIALANSWER attribute so that if anything goes
* wrong later on, we'll return what we've got so far.
*/
/*
* Get the target name of the DNAME.
*/
if (result != ISC_R_SUCCESS)
goto cleanup;
if (result != ISC_R_SUCCESS) {
goto cleanup;
}
if (result != ISC_R_SUCCESS) {
goto cleanup;
}
/*
* Construct the new qname consisting of
* <found name prefix>.<dname target>
*/
goto cleanup;
}
goto cleanup;
}
/*
* RFC2672, section 4.1, subsection 3c says
* we should return YXDOMAIN if the constructed
* name would be too long.
*/
if (result == DNS_R_NAMETOOLONG)
if (result != ISC_R_SUCCESS)
goto cleanup;
/*
* Synthesize a CNAME consisting of
* <old qname> <dname ttl> CNAME <new qname>
* with <dname trust value>
*
* 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,
* even if we had an online key, making a signature
* on-the-fly is costly, and not really legitimate anyway
* since the synthesized CNAME is NOT in the zone.
*/
if (result != ISC_R_SUCCESS)
goto cleanup;
/*
* Switch to the new qname and restart.
*/
if (!WANTRECURSION(client))
goto addauth;
default:
/*
* Something has gone wrong.
*/
"query_find: unexpected error after resuming: %s",
goto cleanup;
}
if (WANTDNSSEC(client) &&
{
}
#ifdef ALLOW_FILTER_AAAA
/*
* The filter-aaaa-on-v4 option should suppress AAAAs for IPv4
* clients if there is an A; filter-aaaa-on-v6 option does the same
* for IPv6 clients.
*/
{
ISC_TRUE);
if (result == ISC_R_SUCCESS &&
else if (result == ISC_R_SUCCESS &&
}
#endif
if (type == dns_rdatatype_any) {
/*
* For minimal-any, we only add records that
* match this type or cover this type.
*/
#ifdef ALLOW_FILTER_AAAA
/*
* 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_a = !authoritative;
#endif
/*
* XXXRTH Need to handle zonecuts with special case
* code.
*/
n = 0;
if (result != ISC_R_SUCCESS) {
"query_find: type any; allrdatasets failed");
goto cleanup;
}
/*
* Calling query_addrrset() with a non-NULL dbuf is going
* to either keep or release the name. We don't want it to
* release fname, since we may have to call query_addrrset()
* more than once. That means we have to call query_keepname()
* now, and pass a NULL dbuf to query_addrrset().
*
* If we do a query_addrrset() below, we must set fname to
* NULL before leaving this block, otherwise we might try to
* cleanup fname even though we're using it!
*/
while (result == ISC_R_SUCCESS) {
#ifdef ALLOW_FILTER_AAAA
/*
* Notice the presence of A and AAAAs so
* that AAAAs can be hidden from IPv4 clients.
*/
}
#endif
/*
* We found an NS RRset; no need to add one later.
*/
if (qtype == dns_rdatatype_any &&
{
}
!dns_db_issecure(db) &&
/*
* The zone is transitioning from insecure
* to secure. Hide the dnssec records from
* ANY queries.
*/
qtype == dns_rdatatype_any &&
"minimal-any skip signature");
"minimal-any skip rdataset");
} else if ((qtype == dns_rdatatype_any ||
#ifdef ALLOW_FILTER_AAAA
#endif
else
}
/*
* Remember the first RRtype we find so we
* can skip others with minimal-any.
*/
else
n++;
/*
* rdataset is non-NULL only in certain
* pathological cases involving DNAMEs.
*/
break;
} else {
/*
* We're not interested in this rdataset.
*/
}
}
#ifdef ALLOW_FILTER_AAAA
/*
* Filter AAAAs if there is an A and there is no signature
* or we are supposed to break DNSSEC.
*/
#endif
if (n == 0) {
/*
* No matching rdatasets found in cache. If we were
* otherwise this is an error condition.
*/
if ((qtype == dns_rdatatype_rrsig ||
qtype == dns_rdatatype_sig) &&
result == ISC_R_NOMORE) {
if (!is_zone) {
goto addauth;
}
if (qtype == dns_rdatatype_rrsig &&
dns_db_issecure(db)) {
sizeof(namebuf));
"missing signature "
"for %s", namebuf);
}
goto nxrrset_rrsig;
} else {
"query_find: no matching rdatasets "
"in cache");
}
}
if (result != ISC_R_NOMORE) {
"query_find: dns_rdatasetiter_destroy failed");
goto cleanup;
}
} else {
/*
* This is the "normal" case -- an ordinary question to which
* we know the answer.
*/
/*
* If we have a zero ttl from the cache refetch it.
*/
{
if (sigrdataset != NULL &&
if (result == ISC_R_SUCCESS) {
if (dns64)
if (dns64_exclude)
} else
goto cleanup;
}
#ifdef ALLOW_FILTER_AAAA
/*
* 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 (qtype == dns_rdatatype_aaaa) {
dns_rdatatype_a, 0,
/*
* 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 |=
} else if (authoritative ||
!RECURSIONOK(client) ||
(result != DNS_R_DELEGATION &&
result != ISC_R_NOTFOUND)) {
client->attributes &=
} 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.
*/
if (result == ISC_R_SUCCESS) {
client->attributes |=
}
}
} else if (qtype == dns_rdatatype_a &&
(client->attributes &
NS_CLIENTATTR_FILTER_AAAA_RC) != 0) {
client->attributes &=
client->attributes |=
if (sigrdataset != NULL &&
goto cleanup;
}
}
#endif
/*
* Check to see if the AAAA RRset has non-excluded addresses
* in it. If not look for a A RRset.
*/
/*
* Look to see if there are A records for this
* name.
*/
goto db_find;
}
if (sigrdataset != NULL)
else
sigrdatasetp = NULL;
else
/*
* Special case NS handling
*/
/*
* We've already got an NS, no need to add one in
* the authority section
*/
dns_db_origin(db)))
{
}
/*
* BIND 8 priming queries need the additional section.
*/
}
}
/*
* Return the time to expire for slave and master zones.
*/
result == ISC_R_SUCCESS) {
client->attributes |=
}
}
}
}
if (dns64) {
if (result == ISC_R_NOMORE) {
if (dns64_exclude) {
if (!is_zone)
goto cleanup;
/*
* Add a fake SOA record.
*/
600, ISC_FALSE,
goto cleanup;
}
#endif
if (is_zone)
goto iszone_nxrrset;
else
goto ncache_nxrrset;
} else if (result != ISC_R_SUCCESS) {
goto cleanup;
}
} else {
}
/*
* We shouldn't ever fail to add 'rdataset'
* because it's already in the answer.
*/
}
/*
* Add NS records to the authority section (if we haven't already
* added them to the answer section).
*/
if (is_zone) {
if (!answer_has_ns) {
}
}
}
}
/*
* Add NSEC records to the authority section if they're needed for
* DNSSEC wildcard proofs.
*/
/*
* General cleanup.
*/
}
if (sigrdataset != NULL)
dns_db_detach(&db);
if (zsigrdataset != NULL)
dns_db_detach(&zdb);
}
}
/*
* AA bit.
*/
/*
* We're not authoritative, so we must ensure the AA bit
* isn't set.
*/
}
/*
* Restart the query?
*/
goto restart;
}
if (eresult != ISC_R_SUCCESS &&
|| eresult == DNS_R_DROP)) {
/*
* This was a duplicate query that we are
* recursing on or the result of rate limiting.
* Don't send a response now for a duplicate query,
* because the original will still cause a response.
*/
} else {
/*
* If we don't have any answer to give the client,
* or if the client requested recursion and thus wanted
* the complete answer, send an error response.
*/
}
/*
* We are done. Set up sortlist data for the message
* rendering code, make a final tweak to the AA bit if the
* auth-nxdomain config option says so, then render and
* send the response.
*/
/*
* If this is a referral and the answer to the question
* is in the glue sort it to the start of the additional
* section.
*/
/*
* If the response is somehow unexpected for the client and this
* is a result of recursion, return an error to the caller
* to indicate it may need to be logged.
*/
if (resuming &&
}
return (eresult);
}
static inline void
return;
}
{
return;
}
sizeof(classname));
int i = 0;
while (keytags-- > 0U) {
int n;
cp += n;
len -= n;
i++;
} else {
break;
}
}
}
}
ISC_LOG_INFO, "trust-anchor-telemetry '%s/%s' from %s%s",
}
}
static inline void
return;
if (client->ednsversion >= 0)
onbuf);
}
static inline void
return;
/*
* Query errors can happen for various reasons. In some cases we cannot
* even assume the query contains a valid question section, so we should
* expect exceptional cases.
*/
sizeof(namebuf));
sep1 = " for ";
sizeof(classname));
sizeof(typename));
sep2 = "/";
}
}
level, "query failed (%s)%s%s%s%s%s%s at %s:%d",
}
void
/*
* Test only.
*/
if (result == ISC_R_SHUTTINGDOWN) {
return;
} else if (result != ISC_R_SUCCESS) {
return;
}
}
/*
* Ensure that appropriate cleanups occur.
*/
/*
* Behave as if we don't support DNSSEC if not enabled.
*/
}
case dns_minimal_no:
break;
case dns_minimal_yes:
break;
case dns_minimal_noauth:
break;
case dns_minimal_noauthrec:
break;
}
/*
* We don't have a cache. Turn off cache support and
* recursion.
*/
/*
* If the client isn't allowed to recurse (due to
* "recursion no", the allow-recursion ACL, or the
* lack of a resolver in this view), or if it
* doesn't want recursion, turn recursion off.
*/
}
/*
* Check for multiple question queries, since edns1 is dead.
*/
return;
}
/*
* Get the question name.
*/
if (result != ISC_R_SUCCESS) {
return;
}
if (result != ISC_R_NOMORE) {
if (result == ISC_R_SUCCESS) {
/*
* There's more than one QNAME in the question
* section.
*/
} else
return;
}
if (ns_g_server->log_queries)
/*
* Check for meta-queries like IXFR and AXFR.
*/
if (dns_rdatatype_ismeta(qtype)) {
switch (qtype) {
case dns_rdatatype_any:
break; /* Let query_find handle it. */
case dns_rdatatype_ixfr:
case dns_rdatatype_axfr:
return;
case dns_rdatatype_maila:
case dns_rdatatype_mailb:
return;
case dns_rdatatype_tkey:
if (result == ISC_R_SUCCESS)
else
return;
default: /* TSIG, etc. */
return;
}
}
/*
* Turn on minimal response for (C)DNSKEY and (C)DS queries.
*/
{
}
/*
* Maybe turn on minimal responses for ANY queries.
*/
if (qtype == dns_rdatatype_any &&
/*
*/
/*
* If the client has requested that DNSSEC checking be disabled,
* allow lookups to return pending data and instruct the resolver
* to return data before validation has completed.
*
* We don't need to set DNS_DBFIND_PENDINGOK when validation is
* disabled as there will be no pending data.
*/
{
/*
* Allow glue NS records to be added to the authority section
* if the answer is secure.
*/
/*
* Set NS_CLIENTATTR_WANTDNSSEC if the client has set AD in the query.
* This allows AD to be returned on queries without DO set.
*/
/*
* This is an ordinary query.
*/
if (result != ISC_R_SUCCESS) {
return;
}
/*
* Assume authoritative response until it is known to be
* otherwise.
*
* If "-T noaa" has been set on the command line don't set
* AA on authoritative answers.
*/
if (!ns_g_noaa)
/*
* Set AD. We must clear it if we add non-validated data to a
* response.
*/
}