query.c revision ec5347e2c775f027573ce5648b910361aa926c01
/*
* Copyright (C) 2004-2007 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.297 2007/06/18 23:47:19 tbox Exp $ */
/*! \file */
#include <config.h>
#include <string.h>
#ifdef DLZ
#endif
#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>
/*% 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)
#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
typedef struct client_additionalctx {
static void
static isc_boolean_t
/*%
* Increment query statistics counters.
*/
static inline void
}
}
static void
} else {
}
} else {
}
} else {
/* We end up here in case of YXDOMAIN, and maybe others */
}
}
static void
}
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
/*%
* 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) {
}
}
}
/*
* client->query.qname was dynamically allocated.
*/
}
}
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 void
CTRACE("query_putrdataset");
}
CTRACE("query_putrdataset: done");
}
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);
} else
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.
*/
goto refuse;
/*
* 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.
*/
goto fail;
}
if (new_zone) {
goto refuse;
} else {
}
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)
goto refuse;
} else {
/*
* We haven't evaluated the view's queryacl yet.
*/
}
}
if (check_acl) {
ISC_TRUE);
if (log) {
if (result == ISC_R_SUCCESS) {
ISC_LOG_DEBUG(3)))
{
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)
goto refuse;
}
/* Approved. */
/*
* Remember the result of the ACL check so we
* don't have to check again.
*/
/* Transfer ownership, if necessary. */
return (ISC_R_SUCCESS);
return (DNS_R_REFUSED);
fail:
return (result);
}
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 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);
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)
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 default
* "allow-query" 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_QUERYOK 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
{
#ifdef DLZ
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.
*/
}
}
#else
#endif
/* If successfull, Transfer ownership of zone. */
if (result == ISC_R_SUCCESS) {
#ifdef DLZ
#endif
/*
* 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 poision 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 poision 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 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.
*/
/*
* Get resources and make 'name' be the database origin.
*/
if (result != ISC_R_SUCCESS)
return (result);
goto cleanup;
}
if (WANTDNSSEC(client)) {
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 (zero_ttl) {
if (sigrdataset != NULL)
sigrdataset->ttl = 0;
}
/*
* 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 (WANTDNSSEC(client)) {
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 inline 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);
}
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 sucessive 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
{
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 cleanup;
if (!dns_rdataset_isassociated(rdataset) ||
goto cleanup;
/*
* 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;
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 !ispositve
* 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 *.f.example
*/
/*
* We'll need some resources...
*/
goto cleanup;
goto cleanup;
if (result == DNS_R_NXDOMAIN) {
if (!ispositive)
if (result == ISC_R_SUCCESS) {
}
if (result == ISC_R_SUCCESS) {
&olabels);
&nlabels);
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 */
ISC_TRUE);
/*
* We'll need some resources...
*/
return;
return;
/* This will succeed, since we've stripped labels. */
NULL) == ISC_R_SUCCESS);
}
static void
/*
* 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 cancelled 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_cancelled || client_shuttingdown) {
if (fetch_cancelled)
else
/*
* This may destroy the client.
*/
} else {
}
}
static isc_result_t
{
/*
* 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, aborting oldest query");
}
} else if (result == ISC_R_QUOTA) {
static isc_stdtime_t last = 0;
"no more recursive clients: %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);
}
#define MAX_RESTARTS 16
#define QUERY_ERROR(r) \
do { \
eresult = r; \
want_restart = ISC_FALSE; \
} 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;
}
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;
if (result != ISC_R_SUCCESS)
return;
char buf[DNS_NAME_FORMATSIZE];
"RFC 1918 response from "
"Internet for %s", buf);
}
return;
}
}
}
/*
* 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 void
{
unsigned int n, nlabels;
int order;
isc_buffer_t b;
unsigned int options;
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
/*
* We'll need some resources...
*/
goto cleanup;
}
goto cleanup;
}
if (result != ISC_R_SUCCESS) {
goto cleanup;
}
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 (!PARTIALANSWER(client))
} else
goto cleanup;
}
if (is_zone)
if (is_zone) {
#ifdef DLZ
/*
* if is_zone = true, zone = NULL then this is
* a DLZ zone. Don't attempt to attach zone.
*/
#endif
#ifdef DLZ
}
#endif
}
}
CTRACE("query_find: db_find");
/*
* We'll need some resources...
*/
goto cleanup;
}
goto cleanup;
}
if (WANTDNSSEC(client)) {
if (sigrdataset == NULL) {
goto cleanup;
}
}
/*
* Now look for an answer in the database.
*/
CTRACE("query_find: resume");
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)
else if (result == DNS_R_DUPLICATE ||
result == DNS_R_DROP) {
/* Duplicate query. */
} else {
/* Unable to recurse. */
}
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;
} 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 {
/*
* We've already got a delegation from
* authoritative data, and it is better
* than what we found in the cache. Use
* it instead of the cache delegation.
*/
/*
* 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 (result == ISC_R_SUCCESS)
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:
/* FALLTHROUGH */
case DNS_R_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.
*/
} else {
/*
* 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 (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.
*/
} else {
/*
* 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 &&
#ifdef DLZ
#endif
else
if (result != ISC_R_SUCCESS) {
goto cleanup;
}
/*
* Add NSEC record if we found one.
*/
if (dns_rdataset_isassociated(rdataset)) {
if (WANTDNSSEC(client)) {
}
}
/*
* Set message rcode.
*/
if (empty_wild)
else
goto cleanup;
case DNS_R_NCACHENXDOMAIN:
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 &&
/*
* 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.
*/
goto cleanup;
}
goto cleanup;
}
if (result != ISC_R_SUCCESS) {
if (result == ISC_R_NOSPACE) {
/*
* RFC2672, section 4.1, subsection 3c says
* we should return YXDOMAIN if the constructed
* name would be too long.
*/
}
goto cleanup;
}
/*
* Synthesize a CNAME for this DNAME.
*
* We want to synthesize a CNAME since if we don't
* then older software that doesn't understand DNAME
* will not chain like it should.
*
* 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.
*/
/*
* 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) {
/*
* XXXRTH Need to handle zonecuts with special case
* code.
*/
n = 0;
if (result != ISC_R_SUCCESS) {
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) {
if ((qtype == dns_rdatatype_any ||
n++;
/*
* rdataset is non-NULL only in certain pathological
* cases involving DNAMEs.
*/
break;
} else {
/*
* We're not interested in this rdataset.
*/
}
}
if (n == 0) {
/*
* We didn't match any rdatasets.
*/
if (qtype == dns_rdatatype_rrsig &&
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) {
if (RECURSIONOK(client)) {
NULL,
NULL);
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.
*/
if (sigrdataset != NULL)
else
sigrdatasetp = NULL;
else
/*
* BIND 8 priming queries need the additional section.
*/
/*
* 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.
*/
ISC_TRUE);
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.
*/
}
CTRACE("query_find: done");
}
static inline void
char namebuf[DNS_NAME_FORMATSIZE];
char typename[DNS_RDATATYPE_FORMATSIZE];
int level = ISC_LOG_INFO;
return;
}
void
CTRACE("ns_query_start");
/*
* 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;
}
}
/*
* 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.
*/
/*
* This is an ordinary query.
*/
if (result != ISC_R_SUCCESS) {
return;
}
/*
* Assume authoritative response until it is known to be
* otherwise.
*/
/*
* Set AD. We must clear it if we add non-validated data to a
* response.
*/
}