resolver.c revision 0c27b3fe77ac1d5094ba3521e8142d9e7973133f
/*
* Copyright (C) 1999-2016 Internet Systems Consortium, Inc. ("ISC")
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
/*! \file */
#include <config.h>
#include <ctype.h>
#include <isc/platform.h>
#ifdef AES_CC
#else
#endif
#include <dns/badcache.h>
#include <dns/dispatch.h>
#include <dns/keytable.h>
#include <dns/rdataclass.h>
#include <dns/rdatalist.h>
#include <dns/rdataset.h>
#include <dns/rdatastruct.h>
#include <dns/rdatatype.h>
#include <dns/resolver.h>
#include <dns/validator.h>
#ifdef WANT_QUERYTRACE
ISC_LOG_DEBUG(3), \
"res %p: %s", res, (m))
ISC_LOG_DEBUG(3), \
"res %p: %s", (r), (m))
#define FCTXTRACE(m) \
ISC_LOG_DEBUG(3), \
"fctx %p(%s): %s", \
ISC_LOG_DEBUG(3), \
"fctx %p(%s): %s %s", \
#define FCTXTRACE3(m, res) \
ISC_LOG_DEBUG(3), \
"fctx %p(%s): [result: %s] %s", \
isc_result_totext(res), (m))
ISC_LOG_DEBUG(3), \
"fctx %p(%s): [result: %s] %s %s", \
ISC_LOG_DEBUG(3), \
"fctx %p(%s): %s %s%u", \
ISC_LOG_DEBUG(3), \
"fetch %p (fctx %p(%s)): %s", \
ISC_LOG_DEBUG(3), \
"resquery %p (fctx %p(%s)): %s", \
#else
#endif /* WANT_QUERYTRACE */
#define US_PER_SEC 1000000U
/*
* The maximum time we will wait for a single query.
*/
#define MAX_SINGLE_QUERY_TIMEOUT 9U
/*
* We need to allow a individual query time to complete / timeout.
*/
/* The default time in seconds for the whole query to live. */
#ifndef DEFAULT_QUERY_TIMEOUT
#endif
/* The maximum time in seconds for the whole query to live. */
#ifndef MAXIMUM_QUERY_TIMEOUT
#define MAXIMUM_QUERY_TIMEOUT 30
#endif
/* The default maximum number of recursions to follow before giving up. */
#ifndef DEFAULT_RECURSION_DEPTH
#define DEFAULT_RECURSION_DEPTH 7
#endif
/* The default maximum number of iterative queries to allow before giving up. */
#ifndef DEFAULT_MAX_QUERIES
#define DEFAULT_MAX_QUERIES 75
#endif
/* Number of hash buckets for zone counters */
#ifndef RES_DOMAIN_BUCKETS
#define RES_DOMAIN_BUCKETS 523
#endif
#define RES_NOBUCKET 0xffffffff
/*%
* Maximum EDNS0 input packet size.
*/
/*%
* This defines the maximum number of timeouts we will permit before we
* disable EDNS0 on the query.
*/
#define MAX_EDNS0_TIMEOUTS 3
#define DNS_RESOLVER_BADCACHESIZE 1021
#define DNS_RESOLVER_BADCACHETTL(fctx) \
typedef struct fetchctx fetchctx_t;
typedef struct query {
/* Locked by task event serialization. */
unsigned int magic;
fetchctx_t * fctx;
int ednsversion;
unsigned int options;
unsigned int attributes;
unsigned int sends;
unsigned int connects;
unsigned int udpsize;
unsigned char data[512];
} resquery_t;
struct tried {
unsigned int count;
};
#define RESQUERY_ATTR_CANCELED 0x02
#define RESQUERY_CONNECTING(q) ((q)->connects > 0)
#define RESQUERY_CANCELED(q) (((q)->attributes & \
RESQUERY_ATTR_CANCELED) != 0)
#define RESQUERY_SENDING(q) ((q)->sends > 0)
typedef enum {
fetchstate_init = 0, /*%< Start event has not run yet. */
fetchstate_done /*%< FETCHDONE events posted. */
} fetchstate;
typedef enum {
badns_unreachable = 0,
} badnstype_t;
struct fetchctx {
/*% Not locked. */
unsigned int magic;
unsigned int options;
unsigned int bucketnum;
unsigned int dbucketnum;
char * info;
/*% Locked by appropriate bucket lock. */
unsigned int references;
/*% Locked by task event serialization. */
unsigned int attributes;
isc_timer_t * timer;
isc_counter_t * qc;
/*%
* The number of events we're waiting for.
*/
unsigned int pending;
/*%
* The number of times we've "restarted" the current
* nameserver set. This acts as a failsafe to prevent
* us from pounding constantly on a particular set of
* servers that, for whatever reason, are not giving
* us useful responses, but are responding in such a
* way that they are not marked "bad".
*/
unsigned int restarts;
/*%
* The number of timeouts that have occurred since we
* last successfully received a response packet. This
* is used for EDNS0 black hole detection.
*/
unsigned int timeouts;
/*%
* Look aside state for DS lookups.
*/
/*%
* Number of queries that reference this context.
*/
unsigned int nqueries;
/*%
* The reason to print when logging a successful
* response to a query.
*/
const char * reason;
/*%
* Random numbers to use for mixing up server addresses.
*/
/*%
* Fetch-local statistics for detailed logging.
*/
int exitline;
unsigned int querysent;
unsigned int referrals;
unsigned int lamecount;
unsigned int quotacount;
unsigned int neterr;
unsigned int badresp;
unsigned int adberr;
unsigned int findfail;
unsigned int valfail;
unsigned int depth;
};
#define FCTX_ATTR_HAVEANSWER 0x0001
#define FCTX_ATTR_GLUING 0x0002
#define FCTX_ATTR_ADDRWAIT 0x0004
#define FCTX_ATTR_SHUTTINGDOWN 0x0008
#define FCTX_ATTR_WANTCACHE 0x0010
#define FCTX_ATTR_WANTNCACHE 0x0020
#define FCTX_ATTR_NEEDEDNS0 0x0040
#define FCTX_ATTR_TRIEDFIND 0x0080
#define FCTX_ATTR_TRIEDALT 0x0100
0)
0)
0)
!= 0)
typedef struct {
fetchctx_t * fctx;
} dns_valarg_t;
struct dns_fetch {
unsigned int magic;
};
typedef struct fctxbucket {
isc_task_t * task;
} fctxbucket_t;
typedef struct fctxcount fctxcount_t;
struct fctxcount {
};
typedef struct zonebucket {
} zonebucket_t;
typedef struct alternate {
union {
struct {
} _n;
} _u;
} alternate_t;
struct dns_resolver {
/* Unlocked. */
unsigned int magic;
dns_view_t * view;
unsigned int options;
unsigned int nbuckets;
#if USE_ALGLOCK
#endif
#if USE_MBSLOCK
#endif
unsigned int spillatmax;
unsigned int spillatmin;
unsigned int query_timeout;
unsigned int maxdepth;
unsigned int maxqueries;
/* Locked by lock. */
unsigned int references;
unsigned int activebuckets;
unsigned int spillat; /* clients-per-query */
unsigned int zspill; /* fetches-per-zone */
/* Locked by primelock. */
/* Locked by nlock. */
unsigned int nfctx;
};
/*%
* Private addrinfo flags. These must not conflict with DNS_FETCHOPT_NOEDNS0
* (0x008) which we also use as an addrinfo flag.
*/
#define FCTX_ADDRINFO_MARK 0x00001
#define FCTX_ADDRINFO_FORWARDER 0x01000
#define FCTX_ADDRINFO_EDNSOK 0x04000
#define FCTX_ADDRINFO_NOCOOKIE 0x08000
#define FCTX_ADDRINFO_BADCOOKIE 0x10000
== 0)
#define ISFORWARDER(a) (((a)->flags & \
FCTX_ADDRINFO_FORWARDER) != 0)
FCTX_ADDRINFO_NOCOOKIE) != 0)
FCTX_ADDRINFO_EDNSOK) != 0)
FCTX_ADDRINFO_BADCOOKIE) != 0)
#ifdef ENABLE_AFL
void dns_resolver_setfuzzing() {
}
#endif
dns_name_t **noqname);
/*%
* Increment resolver-related statistics counters.
*/
static inline void
}
static inline void
}
static isc_result_t
{
return (ISC_R_NOMEMORY);
&validator);
if (result == ISC_R_SUCCESS) {
if ((valoptions & DNS_VALIDATOR_DEFER) == 0) {
}
} else
return (result);
}
static isc_boolean_t
int order;
unsigned int labels;
result == ISC_R_SUCCESS;
if (namereln == dns_namereln_subdomain)
return (ISC_TRUE);
}
return (ISC_FALSE);
}
static isc_boolean_t
return (ISC_FALSE);
/*
* A DS RRset can appear anywhere in a zone, even for a delegation-only
* zone. So a response to an explicit query for this type should be
* excluded from delegation-only fixup.
*
* SOA, NS, and DNSKEY can only exist at a zone apex, so a postive
* response to a query for these types can never violate the
* delegation-only assumption: if the query name is below a
* zone cut, the response should normally be a referral, which should
* be accepted; if the query name is below a zone cut but the server
* happens to have authority for the zone of the query name, the
* response is a (non-referral) answer. But this does not violate
* delegation-only because the query name must be in a different zone
* due to the "apex-only" nature of these types. Note that if the
* remote server happens to have authority for a child zone of a
* delegation-only zone, we may still incorrectly "fix" the response
* with NXDOMAIN for queries for other types. Unfortunately it's
* generally impossible to differentiate this case from violation of
* the delegation-only assumption. Once the resolver learns the
* correct zone cut, possibly via a separate query for an "apex-only"
* type, queries for other types will be resolved correctly.
*
* A query for type ANY will be accepted if it hits an exceptional
* type above in the answer section as it should be from a child
* zone.
*
* Also accept answers with RRSIG records from the child zone.
* Direct queries for RRSIG records should not be answered from
* the parent zone.
*/
while (result == ISC_R_SUCCESS) {
&name);
continue;
/*
* RRsig from child?
*/
if (type == dns_rdatatype_rrsig &&
return (ISC_FALSE);
/*
* Direct query for apex records or DS.
*/
(type == dns_rdatatype_ds ||
type == dns_rdatatype_ns ||
type == dns_rdatatype_soa ||
type == dns_rdatatype_dnskey))
return (ISC_FALSE);
/*
* Indirect query for apex records or DS.
*/
(type == dns_rdatatype_ns ||
type == dns_rdatatype_ds ||
type == dns_rdatatype_soa ||
type == dns_rdatatype_dnskey))
return (ISC_FALSE);
}
}
}
/*
* A NODATA response to a DS query?
*/
return (ISC_FALSE);
/* Look for referral or indication of answer from child zone? */
goto munge;
while (result == ISC_R_SUCCESS) {
if (type == dns_rdatatype_soa &&
if (type != dns_rdatatype_ns &&
type != dns_rdatatype_soa &&
continue;
if (type == dns_rdatatype_rrsig) {
return (ISC_FALSE);
else
continue;
}
/* NS or SOA records. */
/*
* If a query for ANY causes a negative
* response, we can be sure that this is
* an empty node. For other type of queries
* we cannot differentiate an empty node
* from a node that just doesn't have that
* type of record. We only accept the former
* case.
*/
return (ISC_FALSE);
/* Referral or answer from child zone. */
return (ISC_FALSE);
}
}
}
if (!keep_auth)
return (ISC_TRUE);
}
static inline isc_result_t
/*
* Start the lifetime timer for fctx.
*
* This is also used for stopping the idle timer; in that
* case we must purge events already posted to ensure that
* no further idle events are delivered.
*/
}
static inline void
/*
* We don't return a result if resetting the timer to inactive fails
* since there's nothing to be done about it. Resetting to inactive
* should never fail anyway, since the code as currently written
* cannot fail in that case.
*/
if (result != ISC_R_SUCCESS) {
"isc_timer_reset(): %s",
}
}
static inline isc_result_t
/*
* Start the idle timer for fctx. The lifetime timer continues
* to be in effect.
*/
}
/*
* Stopping the idle timer is equivalent to calling fctx_starttimer(), but
* we use fctx_stopidletimer for readability in the code below.
*/
#define fctx_stopidletimer fctx_starttimer
static inline void
unsigned int bucket;
if (empty)
}
static void
{
unsigned int factor;
FCTXTRACE("cancelquery");
/*
* Should we update the RTT?
*/
/*
* We have both the start and finish times for this
* packet, so we can compute a real RTT.
*/
if (rttms < DNS_RESOLVER_QRYRTTCLASS0) {
} else if (rttms < DNS_RESOLVER_QRYRTTCLASS1) {
} else if (rttms < DNS_RESOLVER_QRYRTTCLASS2) {
} else if (rttms < DNS_RESOLVER_QRYRTTCLASS3) {
} else if (rttms < DNS_RESOLVER_QRYRTTCLASS4) {
} else {
}
} else {
else
/*
* We don't have an RTT for this query. Maybe the
* packet was lost, or maybe this server is very
* slow. We don't know. Increase the RTT.
*/
mask = 0x3fff;
mask = 0x7fff;
mask = 0xffff;
mask = 0x1ffff;
mask = 0x3ffff;
mask = 0x7ffff;
else
mask = 0xfffff;
/*
* Don't adjust timeout on EDNS queries unless we have
* seen a EDNS response.
*/
mask >>= 2;
}
if (rtt > MAX_SINGLE_QUERY_TIMEOUT_US)
/*
* Replace the current RTT with our value.
*/
}
}
/*
* Age RTTs of servers not tried.
*/
now);
now);
}
/*
* Check for any outstanding socket events. If they exist, cancel
* them and let the event handlers finish the cleanup. The resolver
* only needs to worry about managing the connect and send events;
* the dispatcher manages the recv events.
*/
if (RESQUERY_CONNECTING(query)) {
/*
* Cancel the connect.
*/
}
} else if (RESQUERY_SENDING(query)) {
/*
* Cancel the pending send.
*/
else
}
/*
* It's safe to destroy the query now.
*/
}
static void
{
FCTXTRACE("cancelqueries");
query = next_query) {
}
}
static void
}
}
static void
}
}
static void
}
}
static void
}
}
static inline void
{
FCTXTRACE("stopeverything");
}
static void
char dbuf[DNS_NAME_FORMATSIZE];
return;
return;
"too many simultaneous fetches for %s "
"(allowed %d spilled %d)",
}
static isc_result_t
{
break;
}
else {
}
} else {
} else {
}
}
if (result == ISC_R_SUCCESS)
return (result);
}
static void
return;
{
break;
}
}
}
}
static inline void
unsigned int count = 0;
unsigned int old_spillat;
unsigned int new_spillat = 0; /* initialized to silence
compiler warnings */
/*
* Caller must be holding the appropriate bucket lock.
*/
FCTXTRACE("sendevents");
/*
* Keep some record of fetch result for logging later (if required).
*/
event = next_event) {
if (!HAVE_ANSWER(fctx))
/*
* Negative results must be indicated in event->result.
*/
}
count++;
}
if (new_spillat != old_spillat) {
}
&i, ISC_TRUE);
}
if (logit)
"clients-per-query increased to %u",
}
}
static inline void
char domainbuf[DNS_NAME_FORMATSIZE];
return;
/*
* We do not know if fctx->domain is the actual domain the record
* lives in or a parent domain so we have a '?' after it.
*/
"success resolving '%s' (in '%s'?) after %s",
}
static void
FCTXTRACE("done");
if (result == ISC_R_SUCCESS) {
/*%
* Log any deferred EDNS timeout messages.
*/
} else if (result == ISC_R_TIMEDOUT)
}
static void
if (RESQUERY_CANCELED(query)) {
/*
* This query was canceled while the
* isc_socket_sendto/connect() was in progress.
*/
}
} else {
case ISC_R_SUCCESS:
break;
case ISC_R_HOSTUNREACH:
case ISC_R_NETUNREACH:
case ISC_R_NOPERM:
case ISC_R_ADDRNOTAVAIL:
case ISC_R_CONNREFUSED:
FCTXTRACE3("query canceled in sendevent(): "
"no route to host; no response",
/*
* No route to remote.
*/
break;
default:
FCTXTRACE3("query canceled in sendevent() due to "
"unexpected event result; responding",
break;
}
}
if (retry) {
/*
* Behave as if the idle timer has expired. For TCP
* this may not actually reflect the latest timer.
*/
if (result != ISC_R_SUCCESS)
else
}
if (destroy_query)
}
static void
QTRACE("udpconnected");
}
static void
QTRACE("senddone");
/*
* XXXRTH
*
* Currently we don't wait for the senddone event before retrying
* a query. This means that if we get really behind, we may end
* up doing extra work!
*/
}
static inline isc_result_t
{
if (result != ISC_R_SUCCESS)
return (result);
}
static inline void
unsigned int seconds;
unsigned int us;
/*
* We retry every .8 seconds the first two times through the address
* list, and then we do exponential back-off.
*/
us = 800000;
else
/*
* Add a fudge factor to the expected rtt based on the current
* estimate.
*/
if (rtt < 50000)
rtt += 50000;
else if (rtt < 100000)
rtt += 100000;
else
rtt += 200000;
/*
* Always wait for at least the expected rtt.
*/
/*
* But don't ever wait for more than 10 seconds.
*/
if (us > MAX_SINGLE_QUERY_TIMEOUT_US)
}
static isc_result_t
unsigned int options)
{
unsigned int srtt;
FCTXTRACE("query");
/*
* A forwarder needs to make multiple queries. Give it at least
* a second to do these in.
*/
srtt = 1000000;
if (result != ISC_R_SUCCESS)
return (result);
goto stop_idle_timer;
}
query->attributes = 0;
/*
* Note that the caller MUST guarantee that 'addrinfo' will remain
* valid until this query is canceled.
*/
/*
* If this is a TCP query, then we need to make a socket and
* a dispatch for it here. Otherwise we use the resolver's
* shared dispatch.
*/
if (result == ISC_R_SUCCESS) {
if (result == ISC_R_SUCCESS)
if (result == ISC_R_SUCCESS)
}
}
dscp = -1;
int pf;
if (!have_addr) {
switch (pf) {
case PF_INET:
&addr);
break;
case PF_INET6:
&addr);
break;
default:
break;
}
if (result != ISC_R_SUCCESS)
goto cleanup_query;
}
isc_sockaddr_setport(&addr, 0);
if (result != ISC_R_SUCCESS)
goto cleanup_query;
#ifndef BROKEN_TCP_BIND_BEFORE_CONNECT
if (result != ISC_R_SUCCESS)
goto cleanup_socket;
#endif
/*
* A dispatch will be created once the connect succeeds.
*/
} else {
if (have_addr) {
switch (isc_sockaddr_pf(&addr)) {
case AF_INET:
break;
case AF_INET6:
break;
default:
goto cleanup_query;
}
4096, 20000, 32768, 16411,
if (result != ISC_R_SUCCESS)
goto cleanup_query;
} else {
case PF_INET:
break;
case PF_INET6:
break;
default:
goto cleanup_query;
}
}
/*
* We should always have a valid dispatcher here. If we
* don't support a protocol family, then its dispatcher
* will be NULL, but we shouldn't be finding addresses for
* protocol types we don't support, so the dispatcher
* we found should never be NULL.
*/
}
/*
* Connect to the remote server.
*
* XXXRTH Should we attach to the socket?
*/
if (result != ISC_R_SUCCESS)
goto cleanup_socket;
QTRACE("connecting via TCP");
} else {
goto cleanup_dispatch;
/* Inform the ADB that we're starting a fetch */
if (result != ISC_R_SUCCESS)
goto cleanup_dispatch;
}
else
return (ISC_R_SUCCESS);
}
return (result);
}
static isc_boolean_t
return (ISC_TRUE);
}
return (ISC_FALSE);
}
static void
#ifdef ENABLE_AFL
if (fuzzing_resolver)
return;
#endif
return;
return;
}
static struct tried *
return (tried);
}
return (NULL);
}
static void
return;
}
return;
}
static struct tried *
return (tried);
}
return (NULL);
}
static void
return;
}
return;
}
static void
#ifdef AES_CC
unsigned char digest[ISC_AES_BLOCK_LENGTH];
unsigned char input[16];
unsigned int i;
case AF_INET:
break;
case AF_INET6:
break;
}
for (i = 0; i < 8; i++)
#endif
#ifdef HMAC_SHA1_CC
unsigned char digest[ISC_SHA1_DIGESTLENGTH];
case AF_INET:
break;
case AF_INET6:
break;
}
#endif
#ifdef HMAC_SHA256_CC
unsigned char digest[ISC_SHA256_DIGESTLENGTH];
case AF_INET:
break;
case AF_INET6:
break;
}
#endif
}
static isc_result_t
{
unsigned int labels;
/*
* For DS variants we need to check fom the parent domain,
* since there may be a negative trust anchor for the name,
* while the enclosing domain where the DS record lives is
* under a secure entry point.
*/
}
}
static isc_boolean_t
return (ISC_FALSE);
return (ISC_TRUE);
if (result != ISC_R_SUCCESS)
return (ISC_FALSE);
return (secure_domain);
}
static isc_result_t
isc_region_t r;
unsigned ednsopt = 0;
#ifdef HAVE_DNSTAP
unsigned char zone[DNS_NAME_MAXWIRE];
#endif /* HAVE_DNSTAP */
QTRACE("send");
/*
* Reserve space for the TCP message length.
*/
} else {
}
if (result != ISC_R_SUCCESS)
goto cleanup_temps;
if (result != ISC_R_SUCCESS)
goto cleanup_temps;
/*
* Get a query id from the dispatch.
*/
task,
if (result != ISC_R_SUCCESS)
goto cleanup_temps;
/*
* Set up question.
*/
/*
* Set RD if the client has requested that we do a recursive query,
* or if we're sending to a forwarder.
*/
/*
* Set CD if the client says not to validate, or if the
* question is under a secure entry point and this is a
*/
/* Do nothing */
;
{
if (result != ISC_R_SUCCESS)
if (secure_domain)
}
/*
* We don't have to set opcode because it defaults to query.
*/
/*
* Convert the question to wire format.
*/
if (result != ISC_R_SUCCESS)
goto cleanup_message;
if (result != ISC_R_SUCCESS)
goto cleanup_message;
DNS_SECTION_QUESTION, 0);
if (result != ISC_R_SUCCESS)
goto cleanup_message;
/*
* The ADB does not know about servers with "edns no". Check this,
* and then inform the ADB for future use.
*/
!useedns)
{
}
/* Sync NOEDNS0 flag in addrinfo->flags and options now. */
/* See if response history indicates that EDNS is not supported. */
"UDP packet size to 512 octets";
}
}
}
/*
* Use EDNS0, unless the caller doesn't want it, or we know that
* the remote server doesn't like it.
*/
unsigned int version = DNS_EDNS_VERSION;
unsigned char cookie[64];
if ((flags & FCTX_ADDRINFO_EDNSOK) != 0 &&
}
udpsize = 512;
/*
* Was the size forced to 512 in the configuration?
*/
if (udpsize == 512U)
/*
* We have talked to this server before.
*/
if (hint != 0U)
/*
* We know nothing about the peer's capabilities
* so start with minimal EDNS UDP size.
*/
if (udpsize == 0U)
udpsize = 512;
if ((flags & DNS_FETCHOPT_EDNSVERSIONSET) != 0) {
}
(void) dns_peer_getsendcookie(peer,
&sendcookie);
&ednsversion);
if (result == ISC_R_SUCCESS &&
}
if (reqnsid) {
ednsopt++;
}
#if DNS_EDNS_VERSION > 0
/*
* Some EDNS(0) servers don't ignore unknown options
* as it was not a explict requirement of RFC 2671.
* Only send COOKIE to EDNS(1) servers.
*/
if (version < 1)
#endif
if (sendcookie) {
sizeof(cookie));
} else {
}
ednsopt++;
}
} else if (result != ISC_R_SUCCESS) {
/*
* We couldn't add the OPT, but we'll press on.
* We're not using EDNS0, so set the NOEDNS0
* bit.
*/
udpsize = 0;
}
} else {
/*
* We know this server doesn't like EDNS0, so we
* won't use it. Set the NOEDNS0 bit since we're
* not using EDNS0.
*/
}
} else
/*
* Record the UDP EDNS size choosen.
*/
/*
* If we need EDNS0 to do this query and aren't using it, we lose.
*/
goto cleanup_message;
}
if (udpsize > 512U)
if (udpsize == 512U)
/*
* Clear CD if EDNS is not in use.
*/
/*
* Add TSIG record tailored to the current recipient.
*/
goto cleanup_message;
if (result != ISC_R_SUCCESS)
goto cleanup_message;
}
if (result != ISC_R_SUCCESS)
goto cleanup_message;
if (result != ISC_R_SUCCESS)
goto cleanup_message;
#ifdef HAVE_DNSTAP
if (result == ISC_R_SUCCESS)
#endif /* HAVE_DNSTAP */
if (result != ISC_R_SUCCESS)
goto cleanup_message;
}
/*
* If using TCP, write the length of the message at the beginning
* of the buffer.
*/
}
/*
* Log the outgoing packet.
*/
ISC_LOG_DEBUG(11),
/*
* We're now done with the query message.
*/
if (query->exclusivesocket)
else
/*
* Send the query!
*/
if (query->exclusivesocket) {
query);
if (result != ISC_R_SUCCESS)
goto cleanup_message;
}
}
isc_buffer_usedregion(buffer, &r);
/*
* XXXRTH Make sure we don't send to ourselves! We should probably
* prune out these addresses when we get them from the ADB.
*/
} else {
}
if (result != ISC_R_SUCCESS) {
if (connecting) {
/*
* This query is still connecting.
* Mark it as canceled so that it will just be
* cleaned up when the connected event is received.
* Keep fctx around until the event is processed.
*/
}
goto cleanup_message;
}
QTRACE("sent");
#ifdef HAVE_DNSTAP
/*
* Log the outgoing query via dnstap.
*/
else
#endif /* HAVE_DNSTAP */
return (ISC_R_SUCCESS);
if (cleanup_cctx)
/*
* Stop the dispatcher from listening.
*/
return (result);
}
static void
unsigned int attrs;
QTRACE("connected");
/*
* XXXRTH
*
* Currently we don't wait for the connect event before retrying
* a query. This means that if we get really behind, we may end
* up doing extra work!
*/
if (RESQUERY_CANCELED(query)) {
/*
* This query was canceled while the connect() was in
* progress.
*/
} else {
case ISC_R_SUCCESS:
/*
* Extend the idle timer for TCP. 20 seconds
* should be long enough for a TCP connection to be
* established, a single DNS request to be sent,
* and the response received.
*/
if (result != ISC_R_SUCCESS) {
FCTXTRACE("query canceled: idle timer failed; "
"responding");
break;
}
/*
* We are connected. Create a dispatcher and
* send the query.
*/
attrs = 0;
else
/*
* Regardless of whether dns_dispatch_create()
* succeeded or not, we don't need our reference
* to the socket anymore.
*/
if (result == ISC_R_SUCCESS)
if (result != ISC_R_SUCCESS) {
FCTXTRACE("query canceled: "
"resquery_send() failed; responding");
}
break;
case ISC_R_NETUNREACH:
case ISC_R_HOSTUNREACH:
case ISC_R_CONNREFUSED:
case ISC_R_NOPERM:
case ISC_R_ADDRNOTAVAIL:
case ISC_R_CONNECTIONRESET:
FCTXTRACE3("query canceled in connected(): "
"no route to host; no response",
/*
* No route to remote.
*/
break;
default:
FCTXTRACE3("query canceled in connected() due to "
"unexpected event result; responding",
break;
}
}
if (retry) {
/*
* Behave as if the idle timer has expired. For TCP
* connections this may not actually reflect the latest timer.
*/
if (result != ISC_R_SUCCESS)
else
}
}
static void
unsigned int bucketnum;
FCTXTRACE("finddone");
/*
* The fetch is waiting for a name to be found.
*/
} else {
/*
* We've got nothing else to wait for and don't
* know the answer. There's nothing to do but
* fail the fctx.
*/
}
}
if (fctx->references == 0) {
}
}
if (want_try) {
} else if (want_done) {
FCTXTRACE("fetch failed in finddone(); return ISC_R_FAILURE");
} else if (dodestroy) {
if (bucket_empty)
}
}
static inline isc_boolean_t
return (ISC_TRUE);
}
return (ISC_FALSE);
}
static inline isc_boolean_t
#ifdef ENABLE_AFL
if (fuzzing_resolver)
return ISC_FALSE;
#endif
/*
* Mark all known bad servers, so we don't try to talk to them
* again.
*/
/*
* Mark any bad nameservers.
*/
else
}
}
/*
* Mark any bad forwarders.
*/
else
}
/*
* Mark any bad alternates.
*/
else
}
}
else
}
return (all_bad);
}
static void
{
char namebuf[DNS_NAME_FORMATSIZE];
char addrbuf[ISC_SOCKADDR_FORMATSIZE];
char classbuf[64];
char typebuf[64];
char code[64];
isc_buffer_t b;
const char *spc = "";
#ifdef ENABLE_AFL
if (fuzzing_resolver)
return;
#endif
if (reason == DNS_R_LAME)
else {
switch (badtype) {
case badns_unreachable:
break;
case badns_response:
break;
case badns_validation:
break; /* counted as 'valfail' */
}
}
/*
* We already know this server is bad.
*/
return;
}
FCTXTRACE("add_bad");
return;
return;
if (reason == DNS_R_UNEXPECTEDRCODE &&
return;
if (reason == DNS_R_UNEXPECTEDRCODE) {
spc = " ";
} else if (reason == DNS_R_UNEXPECTEDOPCODE) {
spc = " ";
} else {
code[0] = '\0';
}
"%s%s%s resolving '%s/%s/%s': %s",
}
/*
* Sort addrinfo list by RTT.
*/
static void
/* Lame N^2 bubble sort. */
}
}
}
}
/*
* Sort a list of finds by server RTT.
*/
static void
/* Sort each find's addrinfo list by SRTT. */
/* Lame N^2 bubble sort. */
while (!ISC_LIST_EMPTY(*findlist)) {
}
}
}
}
static void
{
/*
* If this name is a subdomain of the query domain, tell
* from getting stuck if the nameserver is beneath the zone cut
* and we don't know its address (e.g. because the A record has
* expired).
*/
/*
* See what we know about this address.
*/
if (result != ISC_R_SUCCESS) {
if (result == DNS_R_ALIAS) {
char namebuf[DNS_NAME_FORMATSIZE];
/*
*/
"skipping nameserver '%s' because it "
"is a CNAME, while resolving '%s'",
}
/*
* We have at least some of the addresses for the
* name.
*/
if (port != 0)
port);
}
}
if ((flags & FCTX_ADDRINFO_FORWARDER) != 0)
else
} else {
/*
* We don't know any of the addresses for this
* name.
*/
/*
* We're looking for them and will get an
* event about it later.
*/
/*
* Bootstrap.
*/
if (need_alternate != NULL &&
!*need_alternate && unshared &&
} else {
}
else
/*
* If we know there are no addresses for
* the family we are using then try to add
* an alternative server.
*/
}
}
}
static isc_boolean_t
int order;
unsigned int nlabels;
}
static isc_result_t
unsigned int stdoptions = 0;
/*
* Don't pound on remote servers. (Failsafe!)
*/
FCTXTRACE("too many restarts");
return (DNS_R_SERVFAIL);
}
"too much NS indirection resolving '%s' "
"(depth=%u, maxdepth=%u)",
return (DNS_R_SERVFAIL);
}
/*
* Forwarders.
*/
/*
* If this fctx has forwarders, use them; otherwise use any
* selective forwarders specified in the view; otherwise use the
* resolver's forwarders (if any).
*/
unsigned int labels;
/*
* DS records are found in the parent server.
* Strip label to get the correct forwarder (if any).
*/
}
domain, &forwarders);
if (result == ISC_R_SUCCESS) {
if (result != ISC_R_SUCCESS)
return (result);
if (result != ISC_R_SUCCESS)
return (result);
}
}
}
{
continue;
}
if (result == ISC_R_SUCCESS) {
else
}
}
/*
* If the forwarding policy is "only", we don't need the addresses
* of the nameservers.
*/
goto out;
/*
* Normal nameservers.
*/
/*
* To avoid sending out a flood of queries likely to
* result in NXRRSET, we suppress fetches for address
* families we don't have the first time through,
* provided that we have addresses in some family we
* can use.
*
* We don't want to set this option all the time, since
* if fctx->restarts > 1, we've clearly been having trouble
* with the addresses we had, so getting more could help.
*/
}
if ((stdoptions & DNS_ADBFIND_ADDRESSMASK) == 0)
return (DNS_R_SERVFAIL);
result == ISC_R_SUCCESS;
{
/*
* Extract the name from the NS record.
*/
if (result != ISC_R_SUCCESS)
continue;
&overquota, &need_alternate);
if (!overquota)
}
if (result != ISC_R_NOMORE)
return (result);
/*
* Do we need to use 6 to 4?
*/
if (need_alternate) {
int family;
alternate_t *a;
a != NULL;
a = ISC_LIST_NEXT(a, link)) {
if (!a->isaddress) {
continue;
}
continue;
&ai, 0);
if (result == ISC_R_SUCCESS) {
else
publink);
}
}
}
out:
/*
* Mark all known bad servers.
*/
/*
* How are we doing?
*/
if (all_bad) {
/*
* We've got no addresses.
*/
/*
* We're fetching the addresses, but don't have any
* yet. Tell the caller to wait for an answer.
*/
result = DNS_R_WAIT;
} else {
/*
* We've lost completely. We don't know any
* addresses, and the ADB has told us it can't get
* them.
*/
FCTXTRACE("no addresses");
if (badcache &&
result == ISC_R_SUCCESS)
/*
* If all of the addresses found were over the
* fetches-per-server quota, return the configured
* response.
*/
if (all_spilled) {
} else
}
} else {
/*
* We've found some addresses. We might still be looking
* for more addresses.
*/
}
return (result);
}
static inline void
char buf[ISC_NETADDR_FORMATSIZE];
int match;
match > 0)
}
if (aborted) {
msg = "ignoring blackholed / bogus server: ";
} else if (isc_sockaddr_isnetzero(sa)) {
msg = "ignoring net zero address: ";
} else if (isc_sockaddr_ismulticast(sa)) {
msg = "ignoring multicast address: ";
} else if (isc_sockaddr_isexperimental(sa)) {
msg = "ignoring experimental address: ";
return;
msg = "ignoring IPv6 mapped IPV4 address: ";
msg = "ignoring IPv6 compatibility IPV4 address: ";
} else
return;
}
}
static inline dns_adbaddrinfo_t *
/*
* Return the next untried address, if any.
*/
/*
* Find the first unmarked forwarder (if any).
*/
continue;
return (addrinfo);
}
}
/*
* No forwarders. Move to the next find.
*/
else {
}
/*
* Find the first unmarked addrinfo.
*/
do {
continue;
break;
}
}
break;
}
return (addrinfo);
/*
* No nameservers left. Try alternates.
*/
else {
}
/*
* Find the first unmarked addrinfo.
*/
do {
continue;
break;
}
}
break;
}
/*
* See if we have a better alternate server by address.
*/
continue;
break;
}
}
}
return (addrinfo);
}
static void
unsigned int bucketnum;
/* We've already exceeded maximum query count */
"exceeded max queries resolving '%s' "
"(querycount=%u, maxqueries=%u)",
return;
}
/* Try to find an address that isn't over quota */
break;
/* We have no more addresses. Start over. */
if (result == DNS_R_WAIT) {
/*
* Sleep waiting for addresses.
*/
FCTXTRACE("addrwait");
return;
} else if (result != ISC_R_SUCCESS) {
/*
* Something bad happened.
*/
return;
}
break;
}
/*
* While we may have addresses from the ADB, they
* might be bad ones. In this case, return SERVFAIL.
*/
return;
}
}
if (result != ISC_R_SUCCESS) {
"exceeded max queries resolving '%s'",
return;
}
}
if (result != ISC_R_SUCCESS) {
if (bucket_empty)
} else if (retrying)
}
static isc_boolean_t
unsigned int bucketnum;
/*
* Caller must be holding the bucket lock.
*/
FCTXTRACE("unlink");
return (ISC_TRUE);
return (ISC_FALSE);
}
static void
FCTXTRACE("destroy");
/*
* Free bad.
*/
}
}
}
}
}
/*
* Fetch event handlers.
*/
static void
FCTXTRACE("timeout");
} else {
/*
* We could cancel the running queries here, or we could let
* them keep going. Since we normally use separate sockets for
* different queries, we adopt the former approach to reduce
* the number of open sockets: cancel the oldest query if it
* expired after the query had started (this is usually the
* case but is not always so, depending on the task schedule
* timing).
*/
{
FCTXTRACE("query timed out; no response");
}
/*
* Our timer has triggered. Reestablish the fctx lifetime
* timer.
*/
if (result != ISC_R_SUCCESS)
else
/*
* Keep trying.
*/
}
}
static void
/*
* Start the shutdown process for fctx, if it isn't already underway.
*/
FCTXTRACE("shutdown");
/*
* The caller must be holding the appropriate bucket lock.
*/
if (fctx->want_shutdown)
return;
/*
* Unless we're still initializing (in which case the
* control event is still outstanding), we need to post
* the control event to tell the fetch we want it to
* exit.
*/
&cevent);
}
}
static void
unsigned int bucketnum;
FCTXTRACE("doshutdown");
/*
* An fctx that is shutting down is no longer in ADDRWAIT mode.
*/
/*
* Cancel all pending validators. Note that this must be done
* without the bucket lock held, since that could cause deadlock.
*/
}
/*
* Shut down anything that is still running on behalf of this
* fetch. To avoid deadlock with the ADB, we must do this
* before we lock the bucket lock.
*/
}
}
if (dodestroy) {
if (bucket_empty)
}
}
static void
unsigned int bucketnum;
FCTXTRACE("start");
if (fctx->want_shutdown) {
/*
* We haven't started this fctx yet, and we've been requested
* to shut it down.
*/
/*
* Since we haven't started, we INSIST that we have no
* pending ADB finds and no pending validations.
*/
if (fctx->references == 0) {
/*
* It's now safe to destroy this fctx.
*/
}
} else {
/*
* Normal fctx startup.
*/
/*
* Reset the control event for later use in shutting down
* the fctx.
*/
}
if (!done) {
/*
* All is well. Start working on the fetch.
*/
if (result != ISC_R_SUCCESS)
else
} else if (dodestroy) {
if (bucket_empty)
}
}
/*
* Fetch Creation, Joining, and Cancelation.
*/
static inline isc_result_t
{
FCTXTRACE("join");
/*
* We store the task we're going to send this event to in the
* sender field. We'll make the fetch the sender when we actually
* send the event.
*/
event = (dns_fetchevent_t *)
return (ISC_R_NOMEMORY);
}
/*
* Make sure that we can store the sigrdataset in the
* first event if it is needed by any of the events.
*/
else
fctx->references++;
return (ISC_R_SUCCESS);
}
static inline void
char namebuf[DNS_NAME_FORMATSIZE];
char domainbuf[DNS_NAME_FORMATSIZE];
"log_ns_ttl: fctx %p: %s: %s (in '%s'?): %u %u",
}
static isc_result_t
{
unsigned int findoptions = 0;
char typebuf[DNS_RDATATYPE_FORMATSIZE];
/*
* Caller must be holding the lock for bucket number 'bucketnum'.
*/
return (ISC_R_NOMEMORY);
} else {
if (result != ISC_R_SUCCESS)
goto cleanup_fetch;
}
/*
* Make fctx->info point to a copy of a formatted string
*/
goto cleanup_counter;
}
FCTXTRACE("create");
if (result != ISC_R_SUCCESS)
goto cleanup_info;
/*
* Note! We do not attach to the task. We are relying on the
* resolver to ensure that this task doesn't go away while we are
* using it.
*/
fctx->references = 0;
fctx->quotacount = 0;
fctx->attributes = 0;
unsigned int labels;
/*
* DS records are found in the parent server. Strip one
* leading label from the name (to be used in finding
* the forwarder).
*/
}
/* Find the forwarder for this name. */
domain, &forwarders);
if (result == ISC_R_SUCCESS)
/*
* The caller didn't supply a query domain and
* nameservers, and we're not in forward-only mode,
* so find the best nameservers to use.
*/
domain, 0, findoptions,
&fctx->nameservers,
NULL);
if (result != ISC_R_SUCCESS)
goto cleanup_nameservers;
if (result != ISC_R_SUCCESS)
goto cleanup_nameservers;
} else {
/*
* We're in forward-only mode. Set the query domain.
*/
if (result != ISC_R_SUCCESS)
goto cleanup_name;
}
} else {
if (result != ISC_R_SUCCESS)
goto cleanup_name;
}
/*
* Are there too many simultaneous queries for this domain?
*/
if (result != ISC_R_SUCCESS) {
goto cleanup_domain;
}
if (result != ISC_R_SUCCESS)
goto cleanup_fcount;
if (result != ISC_R_SUCCESS)
goto cleanup_qmessage;
/*
* Compute an expiration time for the entire fetch.
*/
if (iresult != ISC_R_SUCCESS) {
"isc_time_nowplusinterval: %s",
goto cleanup_rmessage;
}
/*
* Default retry interval initialization. We set the interval now
* mostly so it won't be uninitialized. It will be set to the
* correct value before a query is issued.
*/
/*
* Create an inactive timer. It will be made active when the fetch
* is actually started.
*/
if (iresult != ISC_R_SUCCESS) {
"isc_timer_create: %s",
goto cleanup_rmessage;
}
/*
* Attach to the view's cache and adb.
*/
return (ISC_R_SUCCESS);
return (result);
}
/*
* Handle Responses
*/
static inline isc_boolean_t
return (ISC_FALSE);
return (ISC_FALSE);
return (ISC_FALSE);
while (result == ISC_R_SUCCESS) {
int order;
unsigned int labels;
continue;
if (namereln == dns_namereln_equal &&
return (ISC_FALSE);
if (namereln == dns_namereln_subdomain)
return (ISC_FALSE);
return (ISC_TRUE);
}
}
return (ISC_FALSE);
}
static inline void
char namebuf[DNS_NAME_FORMATSIZE];
char domainbuf[DNS_NAME_FORMATSIZE];
char addrbuf[ISC_SOCKADDR_FORMATSIZE];
"lame server resolving '%s' (in '%s'?): %s",
}
static inline void
char nsbuf[ISC_SOCKADDR_FORMATSIZE];
char clbuf[ISC_SOCKADDR_FORMATSIZE];
const char *clmsg = "";
char msgbuf[2048];
clmsg = " for client ";
} else {
clbuf[0] = '\0';
}
"DNS format error from %s resolving %s%s%s: %s",
}
static inline isc_result_t
/*
* Caller must be holding the fctx lock.
*/
/*
* XXXRTH Currently we support only one question.
*/
return (DNS_R_FORMERR);
}
if (result != ISC_R_SUCCESS)
return (result);
char namebuf[DNS_NAME_FORMATSIZE];
char class[DNS_RDATACLASS_FORMATSIZE];
char type[DNS_RDATATYPE_FORMATSIZE];
return (DNS_R_FORMERR);
}
return (ISC_R_SUCCESS);
}
static void
FCTXTRACE("clone_results");
/*
* Set up any other events to have the same data as the first
* event.
*
* Caller must be holding the appropriate lock.
*/
return;
if (result != ISC_R_SUCCESS)
else
event->sigrdataset);
}
}
/*
* Destroy '*fctx' if it is ready to be destroyed (i.e., if it has
* no references and is no longer waiting for any events).
*
* Requires:
* '*fctx' is shutting down.
*
* Returns:
* true if the resolver is exiting and this is the last fctx in the bucket.
*/
static isc_boolean_t
unsigned int bucketnum;
if (!locked)
goto unlock;
}
}
if (!locked)
if (dodestroy)
return (bucket_empty);
}
/*
* The validator has finished.
*/
static void
unsigned options;
FCTXTRACE("received validation completion event");
/*
* Destroy the validator early so that we can
* destroy the fctx if necessary.
*/
/*
* If shutting down, ignore the results. Check to see if we're
* done waiting for validator completions and ADB pending events; if
* so, destroy the fctx.
*/
if (bucket_empty)
goto cleanup_event;
}
/*
* If chaining, we need to make sure that the right result code is
* returned, and that the rdatasets are bound.
*/
!negative &&
{
else {
}
} else
/*
* Either we're not shutting down, or we are shutting down but want
* to cache the result anyway (if this was a validation started by
* a query with cd set)
*/
/*
* Don't bind rdatasets; the caller
* will iterate the node.
*/
} else {
}
}
FCTXTRACE("validation failed");
if (result == ISC_R_SUCCESS)
NULL,
if (result == ISC_R_SUCCESS &&
NULL,
if (result == ISC_R_SUCCESS)
}
/*
* Cache the data as pending for later validation.
*/
if (result == ISC_R_SUCCESS) {
NULL);
}
if (result == ISC_R_SUCCESS &&
0, NULL);
if (result == ISC_R_SUCCESS)
}
else if (sentresponse)
else if (result == DNS_R_BROKENCHAIN) {
if (negative &&
tresult == ISC_R_SUCCESS)
} else
return;
}
if (negative) {
FCTXTRACE("nonexistence validation OK");
/*
* Cache DS NXDOMAIN seperately to other types.
*/
else
&node);
if (result != ISC_R_SUCCESS)
goto noanswer_response;
/*
* If we are asking for a SOA record set the cache time
* to zero to facilitate locating the containing zone of
* a arbitrary zone.
*/
ttl = 0;
if (result != ISC_R_SUCCESS)
goto noanswer_response;
goto answer_response;
} else
FCTXTRACE("validation OK");
}
{
noqname);
}
}
/*
* The data was already cached as pending data.
* Re-cache it as secure and bind the cached
* rdatasets to the first event on the fetch
* event list.
*/
if (result != ISC_R_SUCCESS)
goto noanswer_response;
options = 0;
if (result != ISC_R_SUCCESS &&
goto noanswer_response;
else
vevent->sigrdataset, 0,
if (result != ISC_R_SUCCESS &&
goto noanswer_response;
}
if (sentresponse) {
/*
* If we only deferred the destroy because we wanted to cache
* the data, destroy now.
*/
if (SHUTTINGDOWN(fctx))
if (bucket_empty)
goto cleanup_event;
}
/*
* Don't send a response yet - we have
* more rdatasets that still need to
* be validated.
*/
goto cleanup_event;
}
/*
*/
while (result == ISC_R_SUCCESS) {
&name);
continue;
sigrdataset != NULL;
continue;
break;
}
if (sigrdataset == NULL ||
continue;
&nsnode);
if (result != ISC_R_SUCCESS)
continue;
if (result == ISC_R_SUCCESS)
sigrdataset, 0,
NULL);
if (result != ISC_R_SUCCESS)
continue;
}
}
/*
* Respond with an answer, positive or negative,
* as opposed to an error. 'node' must be non-NULL.
*/
/*
* Negative results must be indicated in event->result.
*/
}
== ISC_R_SUCCESS);
}
}
static void
char msgbuf[2048];
}
static inline isc_result_t
{
unsigned int labels;
FCTXTRACE("findnoqname");
/*
* Find the SIG for this rdataset, if we have it.
*/
sigrdataset != NULL;
break;
}
if (sigrdataset == NULL)
return (ISC_R_NOTFOUND);
result == ISC_R_SUCCESS;
/* Wildcard has rrsig.labels < labels - 1. */
continue;
break;
}
if (result == ISC_R_NOMORE)
return (ISC_R_NOTFOUND);
if (result != ISC_R_SUCCESS)
return (result);
#define NXND(x) ((x) == ISC_R_SUCCESS)
result == ISC_R_SUCCESS;
continue;
fctx)))
{
if (!exists) {
}
}
{
if (!exists && setnearest) {
}
}
}
}
if (result == ISC_R_NOMORE)
sigrdataset != NULL;
break;
}
if (sigrdataset != NULL)
}
return (result);
}
static inline isc_result_t
{
unsigned int options;
unsigned int valoptions = 0;
/*
* The appropriate bucket lock must be held.
*/
/*
* Is DNSSEC validation required for this name?
*/
}
if (result != ISC_R_SUCCESS)
return (result);
}
}
else
asigrdataset = NULL;
!need_validation) {
if (result != ISC_R_SUCCESS)
return (result);
/*
* If this is an ANY, SIG or RRSIG query, we're not
* going to return any rdatasets, unless we encountered
* a CNAME or DNAME as "the answer". In this case,
* we're going to return DNS_R_CNAME or DNS_R_DNAME
* and we must set up the rdatasets.
*/
}
}
}
/*
* Find or create the cache node.
*/
if (result != ISC_R_SUCCESS)
return (result);
/*
* Cache or validate each cacheable rdataset.
*/
continue;
if (CHECKNAMES(rdataset)) {
char namebuf[DNS_NAME_FORMATSIZE];
char typebuf[DNS_RDATATYPE_FORMATSIZE];
char classbuf[DNS_RDATATYPE_FORMATSIZE];
sizeof(typebuf));
sizeof(classbuf));
"check-names %s %s/%s/%s",
if (fail) {
return (DNS_R_BADNAME);
}
continue;
}
}
/*
* Enforce the configure maximum cache TTL.
*/
/*
* Mark the rdataset as being prefetch eligible.
*/
/*
* Find the SIG for this rdataset, if we have it.
*/
sigrdataset != NULL;
break;
}
/*
* If this RRset is in a secure domain, is in bailiwick,
* and is not glue, attempt DNSSEC validation. (We do not
* attempt to validate glue or out-of-bailiwick data--even
* though there might be some performance benefit to doing
* so--because it makes it simpler and safer to ensure that
* records from a secure domain are only cached if validated
* within the context of a query to the domain that owns
* them.)
*/
/*
* RRSIGs are validated as part of validating the
* type they cover.
*/
continue;
if (sigrdataset == NULL) {
/*
* Ignore non-answer rdatasets that
* are missing signatures.
*/
continue;
}
}
/*
* Normalize the rdataset and sigrdataset TTLs.
*/
if (sigrdataset != NULL) {
sigrdataset->ttl);
}
/*
* Mark the rdataset as being prefetch eligible.
*/
/*
* Cache this rdataset/sigrdataset pair as
* pending data. Track whether it was additional
* or not.
*/
else
if (sigrdataset != NULL)
options = 0;
&noqname);
if (tresult == ISC_R_SUCCESS &&
tresult =
}
}
if (result == DNS_R_UNCHANGED) {
if (!need_validation &&
/*
* The answer in the cache is
* better than the answer we
* found, and is a negative
* cache entry, so we must set
* eresult appropriately.
*/
eresult =
else
eresult =
/*
* We have a negative response
* from the cache so don't
* attempt to add the RRSIG
* rrset.
*/
continue;
}
}
if (result != ISC_R_SUCCESS)
break;
if (sigrdataset != NULL) {
if (result == DNS_R_UNCHANGED)
if (result != ISC_R_SUCCESS)
break;
continue;
}
/*
* This is The Answer. We will
* validate it, but first we cache
* the rest of the response - it may
* contain useful keys.
*/
valsigrdataset == NULL);
} else {
/*
* This is one of (potentially)
* multiple answers to an ANY
* or SIG query. To keep things
* simple, we just start the
* validator right away rather
* than caching first and
* having to remember which
* rdatasets needed validation.
*/
valoptions, task);
/*
* Defer any further validations.
* This prevents multiple validators
* from manipulating fctx->rmessage
* simultaneously.
*/
}
else {
}
}
/*
* It's OK to cache this rdataset now.
*/
else
else {
}
}
/*
* If the trust level is 'dns_trust_glue'
* then we are adding data from a referral
* we got while executing the search algorithm.
* New referral data always takes precedence
* over the existing cache contents.
*/
else
options = 0;
if (tresult == ISC_R_SUCCESS &&
}
}
/*
* Now we can add the rdataset.
*/
if (result == DNS_R_UNCHANGED) {
/*
* The answer in the cache is better
* than the answer we found, and is
* a negative cache entry, so we
* must set eresult appropriately.
*/
else
}
} else if (result != ISC_R_SUCCESS)
break;
}
}
if (valrdataset != NULL) {
if (CHAINING(valrdataset)) {
else
}
}
/*
* Negative results must be indicated in event->result.
*/
}
}
}
return (result);
}
static inline isc_result_t
{
FCTXTRACE("cache_message");
for (section = DNS_SECTION_ANSWER;
section++) {
while (result == ISC_R_SUCCESS) {
&name);
if (result != ISC_R_SUCCESS)
break;
}
}
if (result != ISC_R_NOMORE)
break;
}
if (result == ISC_R_NOMORE)
return (result);
}
/*
* Do what dns_ncache_addoptout() does, and then compute an appropriate eresult.
*/
static isc_result_t
{
}
if (secure)
else
/*
* If the cache now contains a negative entry and we
* care about whether it is DNS_R_NCACHENXDOMAIN or
* DNS_R_NCACHENXRRSET then extract it.
*/
/*
* The cache data is a negative cache entry.
*/
else
} else {
/*
* Either we don't care about the nature of the
* cache rdataset (because no fetch is interested
* in the outcome), or the cache rdataset is not
* a negative cache entry. Whichever case it is,
* we can return success.
*
*/
}
}
return (result);
}
static inline isc_result_t
{
unsigned int valoptions = 0;
FCTXTRACE("ncache_message");
/*
* XXXMPA remove when we follow cnames and adjust the setting
* of FCTX_ATTR_WANTNCACHE in noanswer_response().
*/
/*
* Is DNSSEC validation required for this name?
*/
}
if (result != ISC_R_SUCCESS)
return (result);
}
}
else
if (secure_domain) {
/*
* Mark all rdatasets as pending.
*/
while (result == ISC_R_SUCCESS) {
&tname);
}
if (result != ISC_R_NOMORE)
return (result);
}
if (need_validation) {
/*
* Do negative response validation.
*/
/*
* If validation is necessary, return now. Otherwise continue
* to process the message, letting the validation complete
* in its own good time.
*/
return (result);
}
if (!HAVE_ANSWER(fctx)) {
if (result != ISC_R_SUCCESS)
goto unlock;
}
} else
if (result != ISC_R_SUCCESS)
goto unlock;
/*
* If we are asking for a SOA record set the cache time
* to zero to facilitate locating the containing zone of
* a arbitrary zone.
*/
covers == dns_rdatatype_any &&
ttl = 0;
if (result != ISC_R_SUCCESS)
goto unlock;
if (!HAVE_ANSWER(fctx)) {
}
}
return (result);
}
static inline void
{
if (gluing) {
/*
* Glue with 0 TTL causes problems. We force the TTL to
* 1 second to prevent this.
*/
} else
/*
* Avoid infinite loops by only marking new rdatasets.
*/
}
if (external)
}
static isc_result_t
{
return (ISC_R_SUCCESS);
#endif
else
if (result == ISC_R_SUCCESS) {
if (type == dns_rdatatype_a) {
else
if (rtype == dns_rdatatype_a ||
gluing);
}
} else {
&rdataset);
if (result == ISC_R_SUCCESS) {
/*
* Do we have its SIG too?
*/
if (result == ISC_R_SUCCESS)
gluing);
}
}
}
return (ISC_R_SUCCESS);
}
static isc_result_t
}
#ifndef CHECK_FOR_GLUE_IN_ANSWER
#define CHECK_FOR_GLUE_IN_ANSWER 0
#endif
static isc_result_t
}
#endif
static void
result == ISC_R_SUCCESS;
&name);
continue;
fctx);
}
}
}
if (rescan)
goto again;
}
static inline isc_result_t
if (result != ISC_R_SUCCESS)
return (result);
if (result != ISC_R_SUCCESS)
return (result);
return (ISC_R_SUCCESS);
}
static inline isc_result_t
{
/*
* Get the target name of the DNAME.
*/
if (result != ISC_R_SUCCESS)
return (result);
if (result != ISC_R_SUCCESS)
return (result);
return (result);
}
static isc_boolean_t
{
char addrbuf[ISC_NETADDR_FORMATSIZE];
char namebuf[DNS_NAME_FORMATSIZE];
char classbuf[64];
char typebuf[64];
int match;
/* By default, we allow any addresses. */
return (ISC_TRUE);
/*
* If the owner name matches one in the exclusion list, either exactly
* or partially, allow it.
*/
return (ISC_TRUE);
}
/*
* Otherwise, search the filter list for a match for each address
* record. If a match is found, the address should be filtered,
* so should the entire answer.
*/
result == ISC_R_SUCCESS;
} else {
}
sizeof(typebuf));
sizeof(classbuf));
"answer address %s denied for %s/%s/%s",
return (ISC_FALSE);
}
}
return (ISC_TRUE);
}
static isc_boolean_t
{
char qnamebuf[DNS_NAME_FORMATSIZE];
char tnamebuf[DNS_NAME_FORMATSIZE];
char classbuf[64];
char typebuf[64];
/* By default, we allow any target name. */
return (ISC_TRUE);
/*
* If the owner name matches one in the exclusion list, either exactly
* or partially, allow it.
*/
return (ISC_TRUE);
}
/*
* If the target name is a subdomain of the search domain, allow it.
*/
return (ISC_TRUE);
/*
* Otherwise, apply filters.
*/
sizeof(classbuf));
"%s target %s denied for %s/%s",
return (ISC_FALSE);
}
return (ISC_TRUE);
}
static void
char ns_namebuf[DNS_NAME_FORMATSIZE];
char namebuf[DNS_NAME_FORMATSIZE];
char tbuf[DNS_RDATATYPE_FORMATSIZE];
}
}
/*
* Handle a no-answer response (NXDOMAIN, NXRRSET, or referral).
* If look_in_options has LOOK_FOR_NS_IN_ANSWER then we look in the answer
* section for the NS RRset if the query type is NS; if it has
* LOOK_FOR_GLUE_IN_ANSWER we look for glue incorrectly returned in the answer
* section for A and AAAA queries.
*/
#define LOOK_FOR_NS_IN_ANSWER 0x1
#define LOOK_FOR_GLUE_IN_ANSWER 0x2
static isc_result_t
unsigned int look_in_options)
{
FCTXTRACE("noanswer_response");
if ((look_in_options & LOOK_FOR_NS_IN_ANSWER) != 0) {
} else
/*
* Setup qname.
*/
/*
* We have a normal, non-chained negative response or
* referral.
*/
else
} else {
/*
* We're being invoked by answer_response() after it has
*/
/*
* If the current qname is not a subdomain of the query
* domain, there's no point in looking at the authority
* section without doing DNSSEC validation.
*
* Until we do that validation, we'll just return success
* in this case.
*/
return (ISC_R_SUCCESS);
}
/*
* We have to figure out if this is a negative response, or a
* referral.
*/
/*
* Sometimes we can tell if its a negative response by looking at
* the message header.
*/
/*
* Process the authority section.
*/
ns_rdataset = NULL;
while (result == ISC_R_SUCCESS) {
/*
*/
if (type == dns_rdatatype_rrsig)
if (((type == dns_rdatatype_ns ||
type == dns_rdatatype_soa) &&
char qbuf[DNS_NAME_FORMATSIZE];
char nbuf[DNS_NAME_FORMATSIZE];
char tbuf[DNS_RDATATYPE_FORMATSIZE];
sizeof(tbuf));
sizeof(nbuf));
sizeof(qbuf));
"unrelated %s %s in "
"%s authority section",
goto nextname;
}
if (type == dns_rdatatype_ns) {
/*
* NS or RRSIG NS.
*
* Only one set of NS RRs is allowed.
*/
"multiple NS "
"RRsets in "
"authority "
"section");
return (DNS_R_FORMERR);
}
}
name->attributes |=
rdataset->attributes |=
}
if (type == dns_rdatatype_soa) {
/*
* SOA, or RRSIG SOA.
*
* Only one SOA is allowed.
*/
"multiple SOA "
"RRs in "
"authority "
"section");
return (DNS_R_FORMERR);
}
}
name->attributes |=
rdataset->attributes |=
if (aa)
else
}
}
}
if (result == ISC_R_NOMORE)
break;
else if (result != ISC_R_SUCCESS)
return (result);
}
/*
* A negative response has a SOA record (Type 2)
* and a optional NS RRset (Type 1) or it has neither
* a SOA or a NS RRset (Type 3, handled above) or
* rcode is NXDOMAIN (handled above) in which case
* the NS RRset is allowed (Type 4).
*/
while (result == ISC_R_SUCCESS) {
if (type == dns_rdatatype_rrsig)
if (type == dns_rdatatype_nsec ||
type == dns_rdatatype_nsec3) {
/*
* NSEC or RRSIG NSEC.
*/
if (negative_response) {
name->attributes |=
rdataset->attributes |=
} else if (type == dns_rdatatype_nsec) {
name->attributes |=
rdataset->attributes |=
}
if (aa)
else
/*
* No additional data needs to be
* marked.
*/
} else if (type == dns_rdatatype_ds) {
/*
* DS or SIG DS.
*
* These should only be here if
* this is a referral, and there
* should only be one DS RRset.
*/
"DS with no "
"referral");
return (DNS_R_FORMERR);
}
"DS doesn't "
"match "
"referral "
"(NS)");
return (DNS_R_FORMERR);
}
}
name->attributes |=
rdataset->attributes |=
if (aa)
else
}
}
} else {
}
if (result == ISC_R_NOMORE)
break;
else if (result != ISC_R_SUCCESS)
return (result);
}
/*
* Trigger lookups for DNS nameservers.
*/
return (DNS_R_CHASEDSSERVERS);
/*
* Did we find anything?
*/
/*
* Nope.
*/
/*
* and haven't found else anything useful here, but
* no error has occurred since we have an answer.
*/
return (ISC_R_SUCCESS);
} else {
/*
* The responder is insane.
*/
return (DNS_R_FORMERR);
}
char nbuf[DNS_NAME_FORMATSIZE];
char dbuf[DNS_NAME_FORMATSIZE];
char tbuf[DNS_RDATATYPE_FORMATSIZE];
sizeof(tbuf));
sizeof(dbuf));
" of zone %s -- invalid response",
} else {
}
return (DNS_R_FORMERR);
}
}
/*
* If we found both NS and SOA, they should be the same name.
*/
return (DNS_R_FORMERR);
}
/*
* Do we have a referral? (We only want to follow a referral if
* we're not following a chain.)
*/
/*
* We already know ns_name is a subdomain of fctx->domain.
* If ns_name is equal to fctx->domain, we're not making
* progress. We return DNS_R_FORMERR so that we'll keep
* trying other servers.
*/
return (DNS_R_FORMERR);
}
/*
* If the referral name is not a parent of the query
* name, consider the responder insane.
*/
/* Logged twice */
FCTXTRACE("referral to non-parent");
return (DNS_R_FORMERR);
}
/*
* Mark any additional data related to this rdataset.
* It's important that we do this before we change the
* query domain.
*/
fctx);
/*
* Look in the answer section for "glue" that is incorrectly
* returned as a answer. This is needed if the server also
* minimizes the response size by not adding records to the
* additional section that are in the answer section or if
* the record gets dropped due to message size constraints.
*/
if ((look_in_options & LOOK_FOR_GLUE_IN_ANSWER) != 0 &&
check_answer, fctx);
#endif
/*
* NS rdatasets with 0 TTL cause problems.
* dns_view_findzonecut() will not find them when we
* try to follow the referral, and we'll SERVFAIL
* because the best nameservers are now above QDOMAIN.
* We force the TTL to 1 second to prevent this.
*/
if (ns_rdataset->ttl == 0)
/*
* Set the current query domain to the referral name.
*
* XXXRTH We should check if we're in forward-only mode, and
* if so we should bail out.
*/
if (result != ISC_R_SUCCESS)
return (result);
if (result != ISC_R_SUCCESS)
return (result);
return (DNS_R_DELEGATION);
}
/*
* Since we're not doing a referral, we don't want to cache any
* NS RRs we may have found.
*/
return (ISC_R_SUCCESS);
}
static isc_result_t
unsigned int aflag;
FCTXTRACE("answer_response");
/*
* Examine the answer section, marking those rdatasets which are
* part of the answer and should be cached.
*/
else
int order;
unsigned int nlabels;
if (namereln == dns_namereln_equal) {
aflag = 0;
/*
* NSEC3 records are not allowed to
* appear in the answer section.
*/
return (DNS_R_FORMERR);
}
/*
* Apply filters, if given, on answers to reject
* a malicious attempt of rebinding.
*/
rdataset)) {
return (DNS_R_SERVFAIL);
}
/*
* We've found an ordinary answer.
*/
} else if (type == dns_rdatatype_any) {
/*
* We've found an answer matching
* an ANY query. There may be
* more.
*/
&& !found_cname) {
/*
* We've found a signature that
* covers the type we're looking for.
*/
&& !found_type) {
/*
* We're looking for something else,
* but we found a CNAME.
*
* Getting a CNAME response for some
* query types is an error, see
* RFC 4035, Section 2.5.
*/
if (type == dns_rdatatype_rrsig ||
type == dns_rdatatype_key ||
type == dns_rdatatype_nsec) {
char buf[DNS_RDATATYPE_FORMATSIZE];
"CNAME response "
"for %s RR", buf);
return (DNS_R_FORMERR);
}
&tname);
if (result != ISC_R_SUCCESS)
return (result);
/* Apply filters on the target name. */
if (!is_answertarget_allowed(view,
name,
&tname,
return (DNS_R_SERVFAIL);
}
&& !found_type) {
/*
* We're looking for something else,
* but we found a SIG CNAME.
*/
}
if (found) {
/*
* We've found an answer to our
* question.
*/
name->attributes |=
rdataset->attributes |=
if (!chaining) {
/*
* This data is "the" answer
* to our question only if
* we're not chaining (i.e.
* if we haven't followed
* a CNAME or DNAME).
*/
if (aflag ==
name->attributes |=
}
if (aa)
} else if (external) {
/*
* This data is outside of
* our query domain, and
* may not be cached.
*/
rdataset->attributes |=
}
/*
* Mark any additional data related
* to this rdataset.
*/
(void)dns_rdataset_additionaldata(
fctx);
/*
* CNAME chaining.
*/
if (want_chaining) {
name->attributes |=
rdataset->attributes |=
}
}
/*
* We could add an "else" clause here and
* log that we're ignoring this rdataset.
*/
}
/*
* If wanted_chaining is true, we've done
* some chaining as the result of processing
* this node, and thus we need to set
* chaining to true.
*
* We don't set chaining inside of the
* rdataset loop because doing that would
* cause us to ignore the signatures of
* CNAMEs.
*/
if (wanted_chaining)
} else {
/*
* Look for a DNAME (or its SIG). Anything else is
* ignored.
*/
{
/*
* Only pass DNAME or RRSIG(DNAME).
*/
continue;
/*
* If we're not chaining, then the DNAME and
* its signature should not be external.
*/
char qbuf[DNS_NAME_FORMATSIZE];
char obuf[DNS_NAME_FORMATSIZE];
sizeof(qbuf));
sizeof(obuf));
"RRSIG covering DNAME "
"in answer: %s is "
return (DNS_R_FORMERR);
}
if (namereln != dns_namereln_subdomain) {
char qbuf[DNS_NAME_FORMATSIZE];
char obuf[DNS_NAME_FORMATSIZE];
sizeof(qbuf));
sizeof(obuf));
"in answer: %s is "
return (DNS_R_FORMERR);
}
aflag = 0;
if (result == ISC_R_NOSPACE) {
/*
* We can't construct the
* DNAME target. Do not
* try to continue.
*/
} else if (result != ISC_R_SUCCESS)
return (result);
else
if (!is_answertarget_allowed(view,
return (DNS_R_SERVFAIL);
}
} else {
/*
* We've found a signature that
* covers the DNAME.
*/
}
/*
* We've found an answer to our
* question.
*/
if (!chaining) {
/*
* This data is "the" answer to
* our question only if we're
* not chaining.
*/
if (aflag == DNS_RDATASETATTR_ANSWER) {
name->attributes |=
}
if (aa)
} else if (external) {
rdataset->attributes |=
}
}
/*
* DNAME chaining.
*/
/*
* Copy the dname into the qname fixed name.
*
* Although we check for failure of the copy
* operation, in practice it should never fail
* since we already know that the result fits
* in a fixedname.
*/
if (result != ISC_R_SUCCESS)
return (result);
dnameset->attributes |=
}
if (wanted_chaining)
}
}
if (result == ISC_R_NOMORE)
if (result != ISC_R_SUCCESS)
return (result);
/*
* We should have found an answer.
*/
if (!have_answer) {
return (DNS_R_FORMERR);
}
/*
* This response is now potentially cacheable.
*/
/*
* Did chaining end before we got the final answer?
*/
if (chaining) {
/*
* Yes. This may be a negative reply, so hand off
* authority section processing to the noanswer code.
* If it isn't a noanswer response, no harm will be
* done.
*/
}
/*
* We didn't end with an incomplete chain, so the rcode should be
* "no error".
*/
"indicates error");
return (DNS_R_FORMERR);
}
/*
* Examine the authority section (if there is one).
*
* We expect there to be only one owner name for all the rdatasets
* in this section, and we expect that it is not external.
*/
ns_rdataset = NULL;
if (!external) {
/*
* We expect to find NS or SIG NS rdatasets, and
* nothing else.
*/
name->attributes |=
rdataset->attributes |=
else
}
/*
* Mark any additional data related
* to this rdataset.
*/
(void)dns_rdataset_additionaldata(
fctx);
}
}
}
}
if (result == ISC_R_NOMORE)
return (result);
}
static void
fctx->references++;
}
static isc_boolean_t
fctx->references--;
if (fctx->references == 0) {
/*
* No one cares about the result of this fetch anymore.
*/
/*
* This fctx is already shutdown; we were just
* waiting for the last reference to go away.
*/
} else {
/*
* Initiate shutdown.
*/
}
}
return (bucket_empty);
}
static void
unsigned int bucketnum;
FCTXTRACE("resume_dslookup");
FCTXTRACE("resuming DS lookup");
if (result != ISC_R_SUCCESS) {
goto cleanup;
}
if (result != ISC_R_SUCCESS) {
goto cleanup;
}
/*
* Try again.
*/
} else {
unsigned int n;
/*
* Retrieve state from fctx->nsfetch before we destroy it.
*/
goto cleanup;
}
&nameservers);
} else
FCTXTRACE("continuing to look for parent's NS records");
if (result != ISC_R_SUCCESS)
else {
fctx->references++;
}
}
if (!locked)
if (bucket_empty)
}
static inline void
result == ISC_R_SUCCESS;
{
result == ISC_R_SUCCESS;
ISC_FALSE) ||
{
rdataset->attributes |=
}
}
}
}
}
static void
}
/*
* Log server NSID at log level 'level'
*/
static void
{
char addrbuf[ISC_SOCKADDR_FORMATSIZE];
isc_uint16_t buflen, i;
unsigned char *p, *nsid;
/* Allocate buffer for storing hex version of the NSID */
goto cleanup;
goto cleanup;
/* Convert to hex */
p = buf;
for (i = 0; i < nsid_len; i++) {
}
*p = '\0';
/* Make printable version */
p = pbuf;
for (i = 0; i < nsid_len; i++) {
*p++ = nsid[i];
else
*p++ = '.';
}
*p = '\0';
sizeof(addrbuf));
}
static isc_boolean_t
}
static isc_boolean_t
result == ISC_R_SUCCESS;
continue;
return (ISC_TRUE);
}
return (ISC_FALSE);
}
static void
unsigned char *optvalue;
unsigned char cookie[8];
if (result == ISC_R_SUCCESS) {
switch (optcode) {
case DNS_OPT_NSID:
if (!seen_nsid &&
ISC_LOG_DEBUG(3),
break;
case DNS_OPT_COOKIE:
/*
* Only process the first cookie option.
*/
if (seen_cookie) {
break;
}
if (optlen >= 8U &&
optlen);
} else
break;
default:
break;
}
}
}
}
static void
unsigned int options;
unsigned int findoptions;
unsigned int bucketnum;
#ifdef HAVE_DNSTAP
unsigned char zone[DNS_NAME_MAXWIRE];
#endif /* HAVE_DNSTAP */
QTRACE("response");
else
FCTXTRACE("resolver shutting down");
goto done;
}
/*
* XXXRTH We should really get the current time just once. We
* need a routine to convert from an isc_time_t to an
* isc_stdtime_t.
*/
/*
* Did the dispatcher have a problem?
*/
/*
* The problem might be that they
* don't understand EDNS0. Turn it
* off and try again.
*/
} else {
/*
* There's no hope for this query.
*/
/*
* If this is a network error on an exclusive query
* socket, mark the server as bad so that we won't try
* it for this fetch again. Also adjust finish and
* no_response so that we penalize this address in SRTT
* adjustment later.
*/
if (query->exclusivesocket &&
}
}
goto done;
}
if (result != ISC_R_SUCCESS) {
goto done;
}
}
if (result != ISC_R_SUCCESS) {
goto done;
}
}
if ((options & DNS_FETCHOPT_TCP) == 0) {
if ((options & DNS_FETCHOPT_NOEDNS0) == 0)
else
}
if (result != ISC_R_SUCCESS) {
switch (result) {
case ISC_R_UNEXPECTEDEND:
if (!message->question_ok ||
(options & DNS_FETCHOPT_TCP) != 0) {
/*
* Either the message ended prematurely,
* sent over TCP. In all of these cases,
* something is wrong with the remote
* server and we don't want to retry using
* TCP.
*/
== 0) {
/*
* The problem might be that they
* don't understand EDNS0. Turn it
* off and try again.
*/
} else {
}
goto done;
}
/*
* We defer retrying via TCP for a bit so we can
* check out this message further.
*/
break;
case DNS_R_FORMERR:
/*
* The problem might be that they
* don't understand EDNS0. Turn it
* off and try again.
*/
} else {
}
goto done;
default:
/*
* Something bad has happened.
*/
goto done;
}
}
/*
* Log the incoming packet.
*/
#ifdef HAVE_DNSTAP
/*
* Log the response via dnstap.
*/
if (result == ISC_R_SUCCESS) {
if (result == ISC_R_SUCCESS)
}
else
#endif /* HAVE_DNSTAP */
FCTXTRACE("bad class");
goto done;
}
/*
* Process receive opt record.
*/
/*
* If the COOKIE is bad assume it is a attack and retry.
*/
/* XXXMPA log it */
FCTXTRACE("bad cookie");
goto done;
}
/*
* If the message is signed, check the signature. If not, this
* returns success anyway.
*/
if (result != ISC_R_SUCCESS) {
goto done;
}
/*
* The dispatcher should ensure we only get responses with QR set.
*/
/*
* INSIST() that the message comes from the place we sent it to,
* since the dispatch code should ensure this.
*
* INSIST() that the message id is correct (this should also be
* ensured by the dispatch code).
*/
/*
* We have an affirmative response to the query and we have
* previously got a response from this server which indicated
* EDNS may not be supported so we can now cache the lack of
* EDNS support.
*/
"received packet (bad edns) from",
ISC_LOG_DEBUG(3),
/*
* We didn't get a OPT record in response to a EDNS query.
*
* Old versions of named incorrectly drop the OPT record
* when there is a signed, truncated response so we check
* that TC is not set.
*
* Record that the server is not talking EDNS. While this
* should be safe to do for any rcode we limit it to NOERROR
* and NXDOMAIN.
*/
}
/*
* If we get a non error EDNS response record the fact so we
* won't fallback to plain DNS in the future for this server.
*/
}
/*
* Deal with truncated responses by retrying using TCP.
*/
if (truncated) {
if ((options & DNS_FETCHOPT_TCP) != 0) {
} else {
}
goto done;
}
/*
* Is it a query response?
*/
/* XXXRTH Log */
FCTXTRACE("invalid message opcode");
goto done;
}
/*
* Update statistics about erroneous responses.
*/
case dns_rcode_nxdomain:
break;
case dns_rcode_servfail:
break;
case dns_rcode_formerr:
break;
case dns_rcode_refused:
break;
case dns_rcode_badvers:
break;
case dns_rcode_badcookie:
break;
default:
break;
}
}
/*
* Is the remote server broken, or does it dislike us?
*/
isc_buffer_t b;
char code[64];
unsigned char cookie[64];
/*
* Some servers do not ignore unknown EDNS options.
*/
/*
* It's very likely they don't like EDNS0.
* If the response code is SERVFAIL, also check if the
* response contains an OPT RR and don't cache the
* failure since it can be returned for various other
* reasons.
*
* XXXRTH We should check if the question
* we're asking requires EDNS0, and
* if so, we should bail out.
*/
/*
* Remember that they may not like EDNS0.
*/
/*
* This forwarder doesn't understand us,
* but other forwarders might. Keep trying.
*/
} else {
/*
* The server doesn't understand us. Since
* all servers for a zone need similar
* capabilities, we assume that we will get
* FORMERR from all servers, and thus we
* cannot make any more progress with this
* fetch.
*/
}
/*
* DNAME mapping failed because the new name
* was too long. There's no chance of success
* for this fetch.
*/
unsigned int version;
#if DNS_EDNS_VERSION > 0
#endif
/*
* Some servers return BADVERS to unknown
* EDNS options. This cannot be long term
* strategy. Do not disable COOKIE if we have
* already have received a COOKIE from this
* server.
*/
}
#if DNS_EDNS_VERSION > 0
#endif
/*
* Record that we got a good EDNS response.
*/
}
/*
* RFC 2671 was not clear that unknown options should
* be ignored. RFC 6891 is clear that that they
* should be ignored. If we are supporting the
* experimental EDNS > 0 then perform strict
* version checking of badvers responses. We won't
* be sending COOKIE etc. in that case.
*/
#if DNS_EDNS_VERSION > 0
} else {
}
#else
if (version == 0U && setnocookie) {
} else {
}
#endif
/*
* We have recorded the new cookie.
*/
} else {
/*
* XXXRTH log.
*/
}
goto done;
}
/*
* Is the question the same as the one we asked?
*/
if (result != ISC_R_SUCCESS) {
/* XXXRTH Log */
if (result == DNS_R_FORMERR)
goto done;
}
/*
* Is the server lame?
*/
if (result != ISC_R_SUCCESS)
"could not mark server as lame: %s",
FCTXTRACE("lame server");
goto done;
}
/*
* Enforce delegations only zones like NET and COM.
*/
char namebuf[DNS_NAME_FORMATSIZE];
char domainbuf[DNS_NAME_FORMATSIZE];
char addrbuf[ISC_SOCKADDR_FORMATSIZE];
char classbuf[64];
char typebuf[64];
sizeof(classbuf));
sizeof(addrbuf));
"enforced delegation-only for '%s' (%s/%s/%s) "
"from %s",
}
/*
* Clear cache bits.
*/
/*
* Did we get any answers?
*/
/*
* [normal case]
* We've got answers. If it has an authoritative answer or an
* answer from a forwarder, we're done.
*/
{
if (result != ISC_R_SUCCESS)
{
/*
* A BIND8 server could return a non-authoritative
* answer when a CNAME is followed. We should treat
* it as a valid answer.
*/
if (result != ISC_R_SUCCESS)
FCTXTRACE3("answer_response (!ANY/!CNAME)",
result);
!betterreferral(fctx)) {
/*
* Lame response !!!.
*/
if (result != ISC_R_SUCCESS)
} else {
/*
* A BIND 8 server could incorrectly return a
* non-authoritative answer to an NS query
* instead of a referral. Since this answer
* lacks the SIGs necessary to do DNSSEC
* validation, we must invoke the following
* special kludge to treat it as a referral.
*/
if (result != ISC_R_SUCCESS)
FCTXTRACE3("noanswer_response (NS)",
result);
} else {
/*
* Some other servers may still somehow include
* an answer when it should return a referral
* with an empty answer. Check to see if we can
* treat this as a referral by ignoring the
* answer. Further more, there may be an
* to the answer section for that type of
* delegation when the query is for that glue
* record. LOOK_FOR_GLUE_IN_ANSWER will handle
* such a corner case.
*/
if (result != ISC_R_SUCCESS)
}
if (result != DNS_R_DELEGATION) {
/*
* At this point, AA is not set, the response
* is not a referral, and the server is not a
* forwarder. It is technically lame and it's
* easier to treat it as such than to figure out
* some more elaborate course of action.
*/
goto done;
}
goto force_referral;
}
if (result != ISC_R_SUCCESS) {
if (result == DNS_R_FORMERR)
goto done;
}
/*
* NXDOMAIN, NXRDATASET, or referral.
*/
switch (result) {
case ISC_R_SUCCESS:
case DNS_R_CHASEDSSERVERS:
break;
case DNS_R_DELEGATION:
/*
* We don't have the answer, but we know a better
* place to look.
*/
/*
* We have a new set of name servers, and it
* has not experienced any restarts yet.
*/
/*
* Update local statistics counters collected for each
* new zone.
*/
fctx->quotacount = 0;
break;
default:
/*
* Something has gone wrong.
*/
if (result == DNS_R_FORMERR)
goto done;
}
} else {
/*
* The server is insane.
*/
/* XXXRTH Log */
FCTXTRACE("broken server: unexpected rcode");
goto done;
}
/*
* Follow additional section data chains.
*/
/*
* Cache the cacheable parts of the message. This may also cause
* work to be queued to the DNSSEC validator.
*/
if (result != ISC_R_SUCCESS) {
goto done;
}
}
/*
* Ncache the negatively cacheable parts of the message. This may
* also cause work to be queued to the DNSSEC validator.
*/
if (WANTNCACHE(fctx)) {
/*
* Cache DS NXDOMAIN seperately to other types.
*/
else
/*
* Cache any negative cache entries in the message.
*/
if (result != ISC_R_SUCCESS)
}
done:
/*
* Remember the query's addrinfo, in case we need to mark the
* server as broken.
*/
FCTXTRACE4("query canceled in response(); ",
result);
/*
* Cancel the query.
*
* XXXRTH Don't cancel the query if waiting for validation?
*/
#ifdef ENABLE_AFL
return;
} else
#endif
if (keep_trying) {
if (result == DNS_R_FORMERR)
if (broken_server != ISC_R_SUCCESS) {
/*
* Add this server to the list of bad servers for
* this fctx.
*/
}
if (get_nameservers) {
if (result != ISC_R_SUCCESS) {
return;
}
findoptions = 0;
if ((options & DNS_FETCHOPT_UNSHARED) == 0)
else
&fctx->nameservers,
NULL);
if (result != ISC_R_SUCCESS) {
FCTXTRACE("couldn't find a zonecut");
return;
}
/*
* The best nameservers are now above our
* QDOMAIN.
*/
FCTXTRACE("nameservers now above QDOMAIN");
return;
}
if (result != ISC_R_SUCCESS) {
return;
}
if (result != ISC_R_SUCCESS) {
return;
}
}
/*
* Try again.
*/
} else if (resend) {
/*
* Resend (probably with changed options).
*/
FCTXTRACE("resend");
if (result != ISC_R_SUCCESS) {
if (bucket_empty)
}
/*
* All has gone well so far, but we are waiting for the
* DNSSEC validator to validate the answer.
*/
FCTXTRACE("wait for validator");
/*
* We must not retransmit while the validator is working;
* it has references to the current rmessage.
*/
if (result != ISC_R_SUCCESS)
} else if (result == DNS_R_CHASEDSSERVERS) {
unsigned int n;
FCTXTRACE("suspending DS lookup to find parent's NS records");
if (result != ISC_R_SUCCESS)
else {
if (result != ISC_R_SUCCESS)
}
} else {
/*
* We're done.
*/
}
}
/***
*** Resolver Methods
***/
static void
unsigned int i;
alternate_t *a;
RTRACE("destroy");
}
for (i = 0; i < RES_DOMAIN_BUCKETS; i++) {
}
RES_DOMAIN_BUCKETS * sizeof(zonebucket_t));
if (!a->isaddress)
}
#if USE_ALGLOCK
#endif
#if USE_MBSLOCK
#endif
}
static void
/*
* Caller must be holding the resolver lock.
*/
event = next_event) {
}
}
static void
RTRACE("empty_bucket");
res->activebuckets--;
if (res->activebuckets == 0)
}
static void
unsigned int count;
}
}
if (logit)
"clients-per-query decreased to %u", count);
}
unsigned int options,
{
unsigned int i, buckets_created = 0, dbuckets_created = 0;
char name[16];
unsigned dispattr;
/*
* Create a resolver.
*/
return (ISC_R_NOMEMORY);
RTRACE("create");
ntasks * sizeof(fctxbucket_t));
goto cleanup_res;
}
for (i = 0; i < ntasks; i++) {
if (result != ISC_R_SUCCESS)
goto cleanup_buckets;
if (result != ISC_R_SUCCESS) {
goto cleanup_buckets;
}
#ifdef ISC_PLATFORM_USETHREADS
/*
* Use a separate memory context for each bucket to reduce
* contention among multiple threads. Do this only when
* enabling threads because it will be require more memory.
*/
if (result != ISC_R_SUCCESS) {
goto cleanup_buckets;
}
#else
#endif
}
RES_DOMAIN_BUCKETS * sizeof(zonebucket_t));
goto cleanup_buckets;
}
for (i = 0; i < RES_DOMAIN_BUCKETS; i++) {
if (result != ISC_R_SUCCESS) {
goto cleanup_dbuckets;
}
}
if (dispatchv4 != NULL) {
res->exclusivev4 =
}
if (dispatchv6 != NULL) {
res->exclusivev6 =
}
if (result != ISC_R_SUCCESS)
goto cleanup_dispatches;
if (result != ISC_R_SUCCESS)
goto cleanup_lock;
if (result != ISC_R_SUCCESS)
goto cleanup_nlock;
if (result != ISC_R_SUCCESS)
goto cleanup_primelock;
&res->spillattimer);
if (result != ISC_R_SUCCESS)
goto cleanup_primelock;
#if USE_ALGLOCK
if (result != ISC_R_SUCCESS)
goto cleanup_spillattimer;
#endif
#if USE_MBSLOCK
if (result != ISC_R_SUCCESS)
goto cleanup_alglock;
#endif
return (ISC_R_SUCCESS);
#if USE_MBSLOCK
#if USE_ALGLOCK
#endif
#endif
#if USE_ALGLOCK || USE_MBSLOCK
#endif
for (i = 0; i < dbuckets_created; i++) {
}
RES_DOMAIN_BUCKETS * sizeof(zonebucket_t));
for (i = 0; i < buckets_created; i++) {
}
return (result);
}
static void
dns_db_detach(&db);
}
}
void
RTRACE("dns_resolver_prime");
}
if (want_priming) {
/*
* To avoid any possible recursive locking problems, we
* start the priming fetch like any other fetch, and holding
* no resolver locks. No one else will try to start it
* because we're the ones who set res->priming to true.
* Any other callers of dns_resolver_prime() while we're
* running will see that res->priming is already true and
* do nothing.
*/
RTRACE("priming");
return;
}
&res->primefetch);
if (result != ISC_R_SUCCESS) {
}
}
}
void
/*
* Freeze resolver.
*/
}
void
source->references++;
}
void
{
/*
* We're already shutdown. Send the event.
*/
} else {
}
}
void
unsigned int i;
RTRACE("shutdown");
RTRACE("exiting");
}
}
res->activebuckets--;
}
}
if (res->activebuckets == 0)
}
}
void
RTRACE("detach");
res->references--;
if (res->references == 0) {
}
if (need_destroy)
}
static inline isc_boolean_t
unsigned int options)
{
/*
* Don't match fetch contexts that are shutting down.
*/
return (ISC_FALSE);
return (ISC_FALSE);
}
static inline void
char namebuf[DNS_NAME_FORMATSIZE];
char typebuf[DNS_RDATATYPE_FORMATSIZE];
/*
* If there's no chance of logging it, don't render (format) the
* name and RDATA type (further below), and return early.
*/
return;
}
{
}
{
}
{
unsigned int bucketnum;
unsigned int count = 0;
unsigned int spillat;
unsigned int spillatmin;
/* XXXRTH Check for meta type */
} else
/*
* XXXRTH use a mempool?
*/
return (ISC_R_NOMEMORY);
goto unlock;
}
if ((options & DNS_FETCHOPT_UNSHARED) == 0) {
break;
}
}
/*
* Is this a duplicate?
*/
goto unlock;
}
count++;
}
}
result = DNS_R_DROP;
goto unlock;
}
}
if (result != ISC_R_SUCCESS)
goto unlock;
if (new_fctx) {
if (result == ISC_R_SUCCESS) {
/*
* Launch this fctx.
*/
} else {
/*
* We don't care about the result of fctx_unlink()
* since we know we're not exiting.
*/
(void)fctx_unlink(fctx);
}
}
if (dodestroy)
if (result == ISC_R_SUCCESS) {
FTRACE("created");
} else
return (result);
}
void
FTRACE("cancelfetch");
/*
* Find the completion event for this fetch (as opposed
* to those for other fetches that have joined the same
* fctx) and send it with result = ISC_R_CANCELED.
*/
event = next_event) {
break;
}
}
}
}
/*
* The fctx continues running even if no fetches remain;
* the answer is still cached.
*/
}
void
unsigned int bucketnum;
FTRACE("destroyfetch");
/*
* Sanity check: the caller should have gotten its event before
* trying to destroy the fetch.
*/
event = next_event) {
}
}
if (bucket_empty)
}
void
{
char domainbuf[DNS_NAME_FORMATSIZE];
"fetch completed at %s:%d for %s in "
"[domain:%s,referral:%u,restart:%u,qrysent:%u,"
"timeout:%u,lame:%u,quota:%u,neterr:%u,"
"badresp:%u,adberr:%u,findfail:%u,valfail:%u]",
}
}
return (resolver->dispatchmgr);
}
}
}
}
}
}
void
}
unsigned int
unsigned int n;
return (n);
}
alternate_t *a;
if (a == NULL)
return (ISC_R_NOMEMORY);
} else {
if (result != ISC_R_SUCCESS) {
return (result);
}
}
ISC_LINK_INIT(a, link);
return (ISC_R_SUCCESS);
}
void
}
}
void
else
}
void
}
void
{
#ifdef ENABLE_AFL
if (!fuzzing_resolver)
#endif
{
}
}
{
}
void
}
static void
unsigned char *algorithms = node;
}
void
#if USE_ALGLOCK
#endif
#if USE_ALGLOCK
#endif
}
unsigned int alg)
{
unsigned char *new;
unsigned char *algorithms;
/*
* Whether an algorithm is disabled (or not) is stored in a
* per-name bitfield that is stored as the node data of an
* RBT.
*/
if (alg > 255)
return (ISC_R_RANGE);
#if USE_ALGLOCK
#endif
if (result != ISC_R_SUCCESS)
goto cleanup;
}
/*
* If algorithms is set, algorithms[0] contains its
* length.
*/
/*
* If no bitfield exists in the node data, or if
* it is not long enough, allocate a new
* bitfield and copy the old (smaller) bitfield
* into it if one exists.
*/
goto cleanup;
}
if (algorithms != NULL)
/* new[0] should contain the length of new. */
/* Free the older bitfield. */
if (algorithms != NULL)
*algorithms);
} else
}
#if USE_ALGLOCK
#endif
return (result);
}
unsigned int alg)
{
unsigned char *algorithms;
/*
* DH is unsupported for DNSKEYs, see RFC 4034 sec. A.1.
*/
return (ISC_FALSE);
#if USE_ALGLOCK
#endif
goto unlock;
algorithms = data;
}
#if USE_ALGLOCK
#endif
if (found)
return (ISC_FALSE);
return (dst_algorithm_supported(alg));
}
static void
}
void
#if USE_ALGLOCK
#endif
#if USE_ALGLOCK
#endif
}
unsigned int digest_type)
{
unsigned char *new;
unsigned char *digests;
/*
* Whether a digest is disabled (or not) is stored in a per-name
* bitfield that is stored as the node data of an RBT.
*/
if (digest_type > 255)
return (ISC_R_RANGE);
#if USE_ALGLOCK
#endif
if (result != ISC_R_SUCCESS)
goto cleanup;
}
/* If digests is set, digests[0] contains its length. */
/*
* If no bitfield exists in the node data, or if
* it is not long enough, allocate a new
* bitfield and copy the old (smaller) bitfield
* into it if one exists.
*/
goto cleanup;
}
/* new[0] should contain the length of new. */
/* Free the older bitfield. */
*digests);
} else
}
#if USE_ALGLOCK
#endif
return (result);
}
unsigned int digest_type)
{
unsigned char *digests;
#if USE_ALGLOCK
#endif
goto unlock;
}
#if USE_ALGLOCK
#endif
if (found)
return (ISC_FALSE);
return (dst_ds_digest_supported(digest_type));
}
void
#if USE_MBSLOCK
#endif
#if USE_MBSLOCK
#endif
}
{
#if USE_MBSLOCK
#endif
&resolver->mustbesecure);
if (result != ISC_R_SUCCESS)
goto cleanup;
}
#if USE_MBSLOCK
#endif
return (result);
}
#if USE_MBSLOCK
#endif
goto unlock;
#if USE_MBSLOCK
#endif
return (value);
}
void
{
}
void
{
}
void
{
}
return (resolver->zero_no_soa_ttl);
}
void
}
unsigned int
}
unsigned int
return (resolver->query_timeout);
}
void
if (seconds == 0)
if (seconds > MAXIMUM_QUERY_TIMEOUT)
if (seconds < MINIMUM_QUERY_TIMEOUT)
}
void
}
return (resolver->querydscp4);
}
void
}
return (resolver->querydscp6);
}
void
}
unsigned int
}
void
}
unsigned int
return (resolver->maxqueries);
}
void
{
int i;
for (i = 0; i < RES_DOMAIN_BUCKETS; i++) {
{
}
}
}
void
{
}
{
}