query.c revision 76db58eb818dc4839fa816df6a1a1ecb2c7a6bd0
/*
* Copyright (C) 2004-2011 Internet Systems Consortium, Inc. ("ISC")
* Copyright (C) 1999-2003 Internet Software Consortium.
*
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
* REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
* INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
* LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
* OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/
/* $Id: query.c,v 1.362 2011/04/27 17:46:46 each Exp $ */
/*! \file */
#include <config.h>
#include <string.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)
/*% Want DNSSEC? */
#define WANTDNSSEC(c) (((c)->attributes & \
NS_CLIENTATTR_WANTDNSSEC) != 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)
/*% No QNAME Proof? */
#define NOQNAME(r) (((r)->attributes & \
DNS_RDATASETATTR_NOQNAME) != 0)
#if 0
ISC_LOG_DEBUG(3), \
"client %p: %s", client, (m))
ISC_LOG_DEBUG(3), \
"query %p: %s", query, (m))
#else
#define CTRACE(m) ((void)m)
#define QTRACE(m) ((void)m)
#endif
#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)
typedef struct client_additionalctx {
static isc_result_t
static isc_boolean_t
static void
dns_name_t *found);
static inline void
static void
/*%
* Increment query statistics counters.
*/
static inline void
}
}
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;
}
}
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
CTRACE("query_putrdataset");
}
CTRACE("query_putrdataset: done");
}
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
CTRACE("query_newnamebuf");
/*%
* Allocate a name buffer.
*/
if (result != ISC_R_SUCCESS) {
CTRACE("query_newnamebuf: isc_buffer_allocate failed: done");
return (result);
}
CTRACE("query_newnamebuf: done");
return (ISC_R_SUCCESS);
}
static inline isc_buffer_t *
isc_region_t r;
CTRACE("query_getnamebuf");
/*%
* Return a name buffer with space for a maximal name, allocating
* a new one if necessary.
*/
if (result != ISC_R_SUCCESS) {
CTRACE("query_getnamebuf: query_newnamebuf failed: done");
return (NULL);
}
}
if (r.length < 255) {
if (result != ISC_R_SUCCESS) {
CTRACE("query_getnamebuf: query_newnamebuf failed: done");
return (NULL);
}
}
CTRACE("query_getnamebuf: done");
return (dbuf);
}
static inline void
isc_region_t r;
CTRACE("query_keepname");
/*%
* '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.
*/
CTRACE("query_releasename");
if (dns_name_hasbuffer(name)) {
!= 0);
}
CTRACE("query_releasename: done");
}
static inline dns_name_t *
{
isc_region_t r;
CTRACE("query_newname");
if (result != ISC_R_SUCCESS) {
CTRACE("query_newname: dns_message_gettempname failed: done");
return (NULL);
}
CTRACE("query_newname: done");
return (name);
}
static inline dns_rdataset_t *
CTRACE("query_newrdataset");
if (result != ISC_R_SUCCESS) {
CTRACE("query_newrdataset: "
"dns_message_gettemprdataset failed: done");
return (NULL);
}
CTRACE("query_newrdataset: done");
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);
}
if (result != ISC_R_SUCCESS)
return (result);
if (result != ISC_R_SUCCESS) {
return (result);
}
if (result != ISC_R_SUCCESS)
return (result);
}
static inline 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 (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
char namebuf1[DNS_NAME_FORMATSIZE];
char namebuf2[DNS_NAME_FORMATSIZE];
const char *pat;
if (!ns_g_server->log_queries ||
return;
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);
}
}
static void
{
char namebuf1[DNS_NAME_FORMATSIZE];
char namebuf2[DNS_NAME_FORMATSIZE];
return;
"response policy %s rewrite %s via %s %sfailed: %s",
}
/*
* Get a policy rewrite zone database.
*/
static isc_result_t
{
char namebuf1[DNS_NAME_FORMATSIZE];
char namebuf2[DNS_NAME_FORMATSIZE];
if (result == ISC_R_SUCCESS) {
if (ns_g_server->log_queries &&
sizeof(namebuf1));
"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 a DLZ driver is loaded for this view
*/
zonelabels, &tdbp);
/* 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.
*/
/*
* Get our database version.
*/
/*
* 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
{
CTRACE("query_isduplicate");
for (section = DNS_SECTION_ANSWER;
section++) {
if (result == ISC_R_SUCCESS) {
/*
* We've already got this RRset in the response.
*/
CTRACE("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
}
/*
* If the dns_name_t we're looking up is already in the message,
* we don't want to trigger the caller's name replacement logic.
*/
CTRACE("query_isduplicate: false: done");
return (ISC_FALSE);
}
static isc_result_t
isc_buffer_t b;
return (ISC_R_SUCCESS);
CTRACE("query_addadditional");
/*
* 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;
CTRACE("query_addadditional: db_find");
/*
* 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 (result == DNS_R_GLUE &&
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) {
/*
* 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;
}
dns_rdatatype_a, 0,
if (result == DNS_R_NCACHENXDOMAIN)
goto addname;
if (result == DNS_R_NCACHENXRRSET) {
/*
* Negative cache entries don't have sigrdatasets.
*/
}
if (result == ISC_R_SUCCESS) {
dns_rdatatype_a, &mname)) {
} else
if (sigrdataset != NULL &&
{
sigrdataset, link);
}
goto addname;
goto addname;
} else {
if (sigrdataset != NULL &&
}
}
if (result == DNS_R_NCACHENXDOMAIN)
goto addname;
if (result == DNS_R_NCACHENXRRSET) {
}
if (result == ISC_R_SUCCESS) {
dns_rdatatype_aaaa, &mname)) {
} else
if (sigrdataset != NULL &&
{
sigrdataset, link);
sigrdataset = NULL;
}
}
}
}
CTRACE("query_addadditional: addname");
/*
* 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);
}
CTRACE("query_addadditional: cleanup");
if (sigrdataset != NULL)
dns_db_detach(&db);
CTRACE("query_addadditional: done");
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 (qtype != dns_rdatatype_a) {
/*
* 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;
CTRACE("query_addadditional2");
/*
* 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;
CTRACE("query_addadditional2: auth zone not found");
goto try_cache;
}
/* Is the cached DB up-to-date? */
if (result != ISC_R_SUCCESS) {
CTRACE("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.
*/
CTRACE("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. */
CTRACE("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;
}
CTRACE("query_addadditional2: db_find");
/*
* 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) {
CTRACE("query_addadditional2: old glue additional cache");
&cfname);
goto findglue;
}
/* We have a negative cache. */
CTRACE("query_addadditional2: negative glue additional cache");
dns_db_detach(&cdb);
goto cleanup;
}
/* Cache hit. */
CTRACE("query_addadditional2: glue additional cache");
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;
/*
* 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) {
/*
* Negative cache entries don't have sigrdatasets.
*/
}
if (result == ISC_R_SUCCESS) {
/* Remember the result as a cache */
if (dns_rdataset_isassociated(sigrdataset)) {
}
/* do not cache incomplete information */
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
}
}
CTRACE("query_addadditional2: addname");
/*
* 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)
CTRACE("query_addadditional2: cleanup");
if (sigrdataset != NULL)
}
dns_db_detach(&db);
CTRACE("query_addadditional2: done");
return (eresult);
}
static inline void
{
/*
* Add 'rdataset' and any pertinent additional data to
* 'fname', a name in the response message for 'client'.
*/
CTRACE("query_addrdataset");
if (NOADDITIONAL(client))
return;
/*
* Add additional data.
*
* We don't care if dns_rdataset_additionaldata() fails.
*/
CTRACE("query_addrdataset: done");
}
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.
*/
CTRACE("query_dns64");
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;
*/
CTRACE("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);
}
}
CTRACE("query_dns64: done");
return (result);
}
static void
{
isc_region_t r;
unsigned int i;
CTRACE("query_filter64");
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;
*/
CTRACE("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) {
{
}
}
CTRACE("query_filter64: done");
}
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.
*/
CTRACE("query_addrrset");
if (sigrdatasetp != NULL)
else
sigrdataset = NULL;
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_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;
}
CTRACE("query_addrrset: done");
}
static inline isc_result_t
{
CTRACE("query_addsoa");
/*
* 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 (sigrdataset != NULL)
return (eresult);
}
static inline isc_result_t
CTRACE("query_addns");
/*
* Initialization.
*/
/*
* Get resources and make 'name' be the database origin.
*/
if (result != ISC_R_SUCCESS) {
CTRACE("query_addns: dns_message_gettempname failed: done");
return (result);
}
CTRACE("query_addns: query_newrdataset failed");
goto cleanup;
}
if (sigrdataset == NULL) {
CTRACE("query_addns: query_newrdataset failed");
goto cleanup;
}
}
/*
* Find the NS rdataset.
*/
if (result == ISC_R_SUCCESS) {
} else {
CTRACE("query_addns: calling dns_db_find");
CTRACE("query_addns: dns_db_find complete");
}
if (result != ISC_R_SUCCESS) {
CTRACE("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;
}
CTRACE("query_addns: cleanup");
if (sigrdataset != NULL)
CTRACE("query_addns: done");
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;
/*
* Bound the validated ttls then minimise.
*/
else
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;
CTRACE("query_addbestns");
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;
sigrdataset = NULL;
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)
zsigrdataset = NULL;
}
/*
* Attempt to validate RRsets that are pending or that are glue.
*/
goto cleanup;
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
{
isc_buffer_t *dbuf, b;
unsigned int count;
CTRACE("query_addds");
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
{
isc_buffer_t *dbuf, b;
unsigned int options;
int order;
CTRACE("query_addwildcardproof");
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) {
}
/*
* Add closest (provable) encloser NSEC3.
*/
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 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;
isc_buffer_t *dbuf, b;
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
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 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) {
static isc_stdtime_t last = 0;
"recursive-clients soft limit "
"exceeded (%d/%d/%d), "
"aborting oldest query",
}
} else if (result == ISC_R_QUOTA) {
static isc_stdtime_t last = 0;
"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 isc_result_t
{
return (DNS_R_SERVFAIL);
}
return (ISC_R_SUCCESS);
}
static void
}
/*
* Get NS, A, or AAAA rrset for rpz nsdname or nsip checking.
*/
static isc_result_t
{
if (result == DNS_R_DELEGATION) {
"rpz_ns_find() ", result);
}
return (result);
}
if (result != ISC_R_SUCCESS) {
return (result);
}
} else {
if (result != ISC_R_SUCCESS) {
result);
return (result);
}
}
/*
* Try the cache if we're authoritative for an
* ancestor but not the domain itself.
*/
}
if (result == DNS_R_DELEGATION) {
/*
* Recurse to get NS rrset or A or AAAA rrset for an NS name.
*/
resuming);
if (result == ISC_R_SUCCESS) {
}
}
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
{
return (DNS_R_SERVFAIL);
}
/*
* Find the database for this policy zone to get its
* radix tree.
*/
if (result != ISC_R_SUCCESS) {
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.
*/
}
return (ISC_R_SUCCESS);
}
static isc_result_t
{
resuming);
switch (result) {
case ISC_R_SUCCESS:
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:
break;
case DNS_R_DELEGATION:
case DNS_R_DUPLICATE:
case DNS_R_DROP:
break;
default:
}
break;
}
return (result);
}
/*
* Get the rrset from a response policy zone.
*/
static isc_result_t
{
if (result != ISC_R_SUCCESS) {
return (result);
}
/*
* Try to get either a CNAME or the type of record demanded by the
* request from the policy zone.
*/
if (result != ISC_R_SUCCESS) {
return (DNS_R_NXDOMAIN);
}
if (result == ISC_R_SUCCESS) {
&rdsiter);
if (result != ISC_R_SUCCESS) {
return (DNS_R_SERVFAIL);
}
result == ISC_R_SUCCESS;
break;
}
if (result != ISC_R_SUCCESS) {
if (result != ISC_R_NOMORE) {
result);
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
NULL);
}
}
switch (result) {
case ISC_R_SUCCESS:
} else {
if (policy == DNS_RPZ_POLICY_RECORD &&
qtype != dns_rdatatype_cname &&
}
break;
case DNS_R_DNAME:
break;
case DNS_R_NXRRSET:
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.
*/
break;
default:
"", result);
break;
}
return (result);
}
/*
* Build and look for a QNAME or NSDNAME owner name in a response policy zone.
*/
static isc_result_t
{
unsigned int labels;
/*
* Construct the rule's owner name.
*/
if (rpz_type == DNS_RPZ_TYPE_NSDNAME)
else
for (;;) {
if (result == ISC_R_SUCCESS)
break;
if (labels < 2) {
"concatentate() ", result);
return (ISC_R_SUCCESS);
}
"concatentate() ", result);
}
}
/*
* See if the qname rule (or RR) exists.
*/
switch (result) {
case DNS_R_NXDOMAIN:
case DNS_R_EMPTYNAME:
break;
case DNS_R_SERVFAIL:
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
*/
continue;
if (dns_rdataset_isassociated(*rdatasetp)) {
} else {
}
}
}
return (ISC_R_SUCCESS);
}
/*
* Look for response policy zone NSIP and NSDNAME rewriting.
*/
static isc_result_t
{
return (ISC_R_NOMEMORY);
}
/*
* Check rules for the name if this it the first time,
* i.e. we've not been recursing.
*/
if (result != ISC_R_SUCCESS)
goto cleanup;
goto cleanup;
DNS_RPZ_HAD_NSDNAME)) == 0)
goto cleanup;
}
} else {
}
resuming);
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) {
"NS db_find() ", result);
continue;
}
"NS db_find() ", result);
}
goto cleanup;
}
if (result != ISC_R_SUCCESS)
goto cleanup;
}
/*
* Check all NS names.
*/
do {
if (result != ISC_R_SUCCESS) {
"rdata_tostruct() ", result);
goto cleanup;
}
&rdataset);
if (result != ISC_R_SUCCESS) {
goto cleanup;
}
}
/*
* Check all IP addresses for this NS name, but don't
* bother without NSIP rules or with a NSDNAME hit.
*/
resuming);
if (result == ISC_R_SUCCESS)
}
if (result == ISC_R_SUCCESS &&
resuming);
}
if (result != ISC_R_SUCCESS)
goto cleanup;
} while (result == ISC_R_SUCCESS);
}
/*
* Use the best, if any, hit.
*/
}
}
return (result);
}
#define MAX_RESTARTS 16
#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
isc_buffer_t *dbuf, b;
CTRACE("query_addnoqnameproof");
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;
}
}
}
#define NS_NAME_INIT(A,B) \
{ \
A, sizeof(A), sizeof(B), \
}
static unsigned char inaddr10[] = "\00210\007IN-ADDR\004ARPA";
static unsigned char inaddr16172[] = "\00216\003172\007IN-ADDR\004ARPA";
static unsigned char inaddr17172[] = "\00217\003172\007IN-ADDR\004ARPA";
static unsigned char inaddr18172[] = "\00218\003172\007IN-ADDR\004ARPA";
static unsigned char inaddr19172[] = "\00219\003172\007IN-ADDR\004ARPA";
static unsigned char inaddr20172[] = "\00220\003172\007IN-ADDR\004ARPA";
static unsigned char inaddr21172[] = "\00221\003172\007IN-ADDR\004ARPA";
static unsigned char inaddr22172[] = "\00222\003172\007IN-ADDR\004ARPA";
static unsigned char inaddr23172[] = "\00223\003172\007IN-ADDR\004ARPA";
static unsigned char inaddr24172[] = "\00224\003172\007IN-ADDR\004ARPA";
static unsigned char inaddr25172[] = "\00225\003172\007IN-ADDR\004ARPA";
static unsigned char inaddr26172[] = "\00226\003172\007IN-ADDR\004ARPA";
static unsigned char inaddr27172[] = "\00227\003172\007IN-ADDR\004ARPA";
static unsigned char inaddr28172[] = "\00228\003172\007IN-ADDR\004ARPA";
static unsigned char inaddr29172[] = "\00229\003172\007IN-ADDR\004ARPA";
static unsigned char inaddr30172[] = "\00230\003172\007IN-ADDR\004ARPA";
static unsigned char inaddr31172[] = "\00231\003172\007IN-ADDR\004ARPA";
static unsigned char inaddr168192[] = "\003168\003192\007IN-ADDR\004ARPA";
static dns_name_t rfc1918names[] = {
};
static unsigned char prisoner_data[] = "\010prisoner\004iana\003org";
static unsigned char hostmaster_data[] = "\012hostmaster\014root-servers\003org";
static void
unsigned int i;
for (i = 0; i < (sizeof(rfc1918names)/sizeof(*rfc1918names)); i++) {
&rfc1918names[i],
&found);
if (result != ISC_R_SUCCESS)
return;
char buf[DNS_NAME_FORMATSIZE];
"RFC 1918 response from "
"Internet for %s", buf);
}
return;
}
}
}
static void
{
unsigned char salt[256];
unsigned int dboptions;
int order;
unsigned int count;
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;
}
&count) == dns_namereln_subdomain) {
"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");
return;
}
#ifdef ALLOW_FILTER_AAAA_ON_V4
static isc_boolean_t
return (ISC_TRUE);
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_boolean_t
{
CTRACE("redirect");
return (ISC_FALSE);
return (ISC_FALSE);
return (ISC_FALSE);
return (ISC_FALSE);
result == ISC_R_SUCCESS;
if (type == dns_rdatatype_nsec ||
type == dns_rdatatype_nsec3 ||
return (ISC_FALSE);
}
}
}
ISC_TRUE);
if (result != ISC_R_SUCCESS)
return (ISC_FALSE);
if (result != ISC_R_SUCCESS)
return (ISC_FALSE);
/*
* Lookup the requested data in the redirect zone.
*/
if (result != ISC_R_SUCCESS) {
dns_db_detach(&db);
return (ISC_FALSE);
}
CTRACE("redirect: found data: done");
if (dns_rdataset_isassociated(&trdataset)) {
}
dns_db_detach(&db);
return (ISC_TRUE);
}
/*
* 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;
int line = -1;
CTRACE("query_find");
/*
* One-time initialization.
*
* It's especially important to initialize anything that the cleanup
* code might cleanup.
*/
sigrdataset = NULL;
zsigrdataset = NULL;
options = 0;
/*
* We're returning from recursion. Restore the query context
* and resume.
*/
} else {
}
else
}
if (DNS64EXCLUDE(client)) {
}
/*
* We'll need some resources...
*/
goto cleanup;
}
goto cleanup;
}
} else {
}
if (result != ISC_R_SUCCESS) {
goto cleanup;
}
} else {
}
goto resume;
}
/*
* Not returning from recursion.
*/
/*
* If it's a SIG query, we'll iterate the node.
*/
else
CTRACE("query_find: restart");
char namebuf[DNS_NAME_FORMATSIZE];
char typename[DNS_RDATATYPE_FORMATSIZE];
sizeof(classname));
"check-names failure %s/%s/%s", namebuf,
goto cleanup;
}
/*
* First we must find the right database.
*/
if (dns_rdatatype_atparent(qtype) &&
/*
* Look to see if we are authoritative for the
* child zone if the query type is DS.
*/
&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
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.
*/
}
}
}
CTRACE("query_find: db_find");
/*
* We'll need some resources...
*/
goto cleanup;
}
goto cleanup;
}
if (sigrdataset == NULL) {
goto cleanup;
}
}
/*
* Now look for an answer in the database.
*/
CTRACE("query_find: resume");
switch (rresult) {
case ISC_R_SUCCESS:
break;
case DNS_R_DELEGATION:
/*
* recursing for NS names or addresses,
* so save the main query state
*/
sigrdataset = NULL;
goto cleanup;
default:
goto cleanup;
}
NULL);
}
case DNS_RPZ_POLICY_NXDOMAIN:
break;
case DNS_RPZ_POLICY_NODATA:
break;
case DNS_RPZ_POLICY_RECORD:
if (type == dns_rdatatype_any &&
break;
case DNS_RPZ_POLICY_CNAME:
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.
*/
if (sigrdataset != NULL &&
}
}
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. */
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 (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.
*/
sigrdataset = NULL;
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);
zsigrdataset = NULL;
/*
* 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 (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)
goto cleanup;
}
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.
*/
sigrdataset = NULL;
goto db_find;
}
/*
* Look for a NSEC3 record if we don't have a NSEC record.
*/
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) &&
unsigned int count;
unsigned int skip;
/*
* Add the closest provable encloser.
*/
&rdataset, &sigrdataset,
dbuf,
+ 1;
found);
sigrdataset == NULL) {
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.
*/
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 &&
break;
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. 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.
*/
if (qtype == dns_rdatatype_soa &&
else
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:
break;
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)
goto cleanup;
}
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.
*/
sigrdataset = NULL;
goto db_find;
}
/*
* We don't call query_addrrset() because we don't need any
* of its extra features (and things would probably break!).
*/
goto cleanup;
case DNS_R_CNAME:
/*
* 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.
*/
goto cleanup;
}
if (WANTDNSSEC(client) &&
{
}
if (type == dns_rdatatype_any) {
#ifdef ALLOW_FILTER_AAAA_ON_V4
/*
* 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_a = !authoritative;
is_v4_client(client) &&
ISC_TRUE) == ISC_R_SUCCESS)
else
#endif
/*
* XXXRTH Need to handle zonecuts with special case
* code.
*/
n = 0;
if (result != ISC_R_SUCCESS) {
goto cleanup;
}
/*
* Check all A and AAAA records in all response policy
* IP address zones
*/
result == ISC_R_SUCCESS;
if (result != ISC_R_SUCCESS)
break;
}
if (result != ISC_R_NOMORE) {
goto cleanup;
}
case DNS_RPZ_POLICY_MISS:
break;
case DNS_RPZ_POLICY_NO_OP:
break;
case DNS_RPZ_POLICY_NXDOMAIN:
case DNS_RPZ_POLICY_NODATA:
case DNS_RPZ_POLICY_RECORD:
case DNS_RPZ_POLICY_CNAME:
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
* 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_ON_V4
/*
* Notice the presence of A and AAAAs so
* that AAAAs can be hidden from IPv4 clients.
*/
if (filter_aaaa) {
}
#endif
!dns_db_issecure(db) &&
/*
* The zone is transitioning from insecure
* to secure. Hide the dnssec records from
* ANY queries.
*/
} else if ((qtype == dns_rdatatype_any ||
#ifdef ALLOW_FILTER_AAAA_ON_V4
#endif
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_ON_V4
/*
* Filter AAAAs if there is an A and there is no signature
* or we are supposed to break DNSSEC.
*/
#endif
if (n == 0 && is_zone) {
/*
* We didn't match any rdatasets.
*/
if ((qtype == dns_rdatatype_rrsig ||
qtype == dns_rdatatype_sig) &&
result == ISC_R_NOMORE) {
/*
* XXXRTH If this is a secure zone and we
* didn't find any SIGs, we should generate
* an error unless we were searching for
* glue. Ugh.
*/
if (!is_zone) {
/*
* Note: this is dead code because
* is_zone is always true due to the
* condition above. But naive
* recursion would cause infinite
* attempts of recursion because
* the answer to (RR)SIG queries
* won't be cached. Until we figure
* out what we should do and implement
* it we intentionally keep this code
* dead.
*/
if (RECURSIONOK(client)) {
resuming);
if (result == ISC_R_SUCCESS)
else
}
goto addauth;
}
/*
* We were searching for SIG records in
* a nonsecure zone. Send a "no error,
* no data" response.
*/
/*
* Add SOA.
*/
if (result == ISC_R_SUCCESS)
} else {
/*
* Something went wrong.
*/
}
}
if (result != ISC_R_NOMORE) {
goto cleanup;
}
} else {
/*
* 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
*/
if (result != ISC_R_SUCCESS) {
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.
*/
case DNS_RPZ_POLICY_MISS:
break;
case DNS_RPZ_POLICY_NO_OP:
break;
case DNS_RPZ_POLICY_NXDOMAIN:
case DNS_RPZ_POLICY_NODATA:
case DNS_RPZ_POLICY_RECORD:
case DNS_RPZ_POLICY_CNAME:
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.
*/
is_v4_client(client) &&
ISC_TRUE) == ISC_R_SUCCESS &&
(!WANTDNSSEC(client) ||
sigrdataset == NULL ||
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.
*/
sigrdataset = NULL;
goto db_find;
}
if (sigrdataset != NULL)
else
sigrdatasetp = NULL;
else
/*
* BIND 8 priming queries need the additional section.
*/
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 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.
*/
}
CTRACE("query_find: addauth");
/*
* Add NS records to the authority section (if we haven't already
* added them to the answer section).
*/
if (is_zone) {
if (!((qtype == dns_rdatatype_ns ||
qtype == dns_rdatatype_any) &&
dns_db_origin(db))))
} else if (qtype != dns_rdatatype_ns) {
}
}
/*
* Add NSEC records to the authority section if they're needed for
* DNSSEC wildcard proofs.
*/
CTRACE("query_find: cleanup");
/*
* 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 &&
/*
* This was a duplicate query that we are
* recursing on. Don't send a response now.
* The original query 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 &&
}
CTRACE("query_find: done");
return (eresult);
}
static inline void
char namebuf[DNS_NAME_FORMATSIZE];
char typename[DNS_RDATATYPE_FORMATSIZE];
char onbuf[ISC_NETADDR_FORMATSIZE];
int level = ISC_LOG_INFO;
return;
"T" : "",
onbuf);
}
static inline void
char namebuf[DNS_NAME_FORMATSIZE];
char typename[DNS_RDATATYPE_FORMATSIZE];
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
CTRACE("ns_query_start");
/*
* Test only.
*/
/*
* Ensure that appropriate cleanups occur.
*/
/*
* Behave as if we don't support DNSSEC if not enabled.
*/
}
/*
* 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.
*/
}
/*
* 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 multiple question queries, since edns1 is dead.
*/
return;
}
/*
* 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 DNSKEY and DS queries.
*/
/*
*/
/*
* 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 'want_ad' if the client has set AD in the query.
* This allows AD to be returned on queries without DO set.
*/
else
/*
* 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.
*/
}