adb.c revision b239c8294a5653d21876d084e0c5b029f6b9fc5d
/*
* 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: adb.c,v 1.233 2007/10/19 17:15:53 explorer Exp $ */
/*! \file
*
* \note
* In finds, if task == NULL, no events will be generated, and no events
* have been sent. If task != NULL but taskaction == NULL, an event has been
* posted but not yet freed. If neither are NULL, no event was posted.
*
*/
/*%
* After we have cleaned all buckets, dump the database contents.
*/
#if 0
#define DUMP_ADB_AFTER_CLEANING
#endif
#include <config.h>
#include <limits.h>
#include <isc/mutexblock.h>
#include <dns/rdataset.h>
#include <dns/rdatastruct.h>
#include <dns/rdatatype.h>
#include <dns/resolver.h>
/*!
* The number of buckets needs to be a prime (for good hashing).
*
* XXXRTH How many buckets do we need?
*/
/*!
* For type 3 negative cache entries, we will remember that the address is
* broken for this long. XXXMLG This is also used for actual addresses, too.
* if the zone has extremely low TTLs.
*/
/*%
* Wake up every CLEAN_SECONDS and clean CLEAN_BUCKETS buckets, so that all
* buckets are cleaned in CLEAN_PERIOD seconds.
*/
#define CLEAN_PERIOD 3600
/*% See #CLEAN_PERIOD */
#define CLEAN_SECONDS 30
/*% See #CLEAN_PERIOD */
/*%
* The period in seconds after which an ADB name entry is regarded as stale
* and forced to be cleaned up.
* TODO: This should probably be configurable at run-time.
*/
#ifndef ADB_STALE_MARGIN
#define ADB_STALE_MARGIN 1800
#endif
typedef struct dns_adbnamehook dns_adbnamehook_t;
typedef struct dns_adblameinfo dns_adblameinfo_t;
typedef struct dns_adbfetch dns_adbfetch_t;
typedef struct dns_adbfetch6 dns_adbfetch6_t;
/*% dns adb structure */
struct dns_adb {
unsigned int magic;
#ifdef LRU_DEBUG
#endif
int next_cleanbucket;
unsigned int irefcnt;
unsigned int erefcnt;
/*!
* Bucketized locks and lists for names.
*
* XXXRTH Have a per-bucket structure that contains all of these?
*/
/*% See dns_adbnamelist_t */
/*% See dns_adbnamelist_t */
/*% See dns_adbnamelist_t */
unsigned int name_refcnt[NBUCKETS];
/*!
* Bucketized locks for entries.
*
* XXXRTH Have a per-bucket structure that contains all of these?
*/
unsigned int entry_refcnt[NBUCKETS];
#ifdef LRU_DEBUG
unsigned int stale_purge;
unsigned int stale_scan;
unsigned int stale_expire;
unsigned int stale_lru;
unsigned int nname, nname_total;
unsigned int nentry, nentry_total;
#endif
};
/*
* XXXMLG Document these structures.
*/
/*% dns_adbname structure */
struct dns_adbname {
unsigned int magic;
unsigned int partial_result;
unsigned int flags;
int lock_bucket;
unsigned int chains;
unsigned int fetch_err;
unsigned int fetch6_err;
/* for LRU-based management */
};
/*% The adbfetch structure */
struct dns_adbfetch {
unsigned int magic;
};
/*%
* This is a small widget that dangles off a dns_adbname_t. It contains a
* pointer to the address information about this host, and a link to the next
* namehook that will contain the next address this host has.
*/
struct dns_adbnamehook {
unsigned int magic;
};
/*%
* This is a small widget that holds qname-specific information about an
* address. Currently limited to lameness, but could just as easily be
* extended to other types of information about zones.
*/
struct dns_adblameinfo {
unsigned int magic;
};
/*%
* An address entry. It holds quite a bit of information about addresses,
* including edns state (in "flags"), rtt, and of course the address of
* the host.
*/
struct dns_adbentry {
unsigned int magic;
int lock_bucket;
unsigned int refcnt;
unsigned int flags;
unsigned int srtt;
/*%<
* A nonzero 'expires' field indicates that the entry should
* persist until that time. This allows entries found
* using dns_adb_findaddrinfo() to persist for a limited time
* even though they are not necessarily associated with a
* name.
*/
};
/*
* Internal functions (and prototypes).
*/
dns_adbentry_t *);
unsigned int, int *);
isc_sockaddr_t *, int *);
static inline void inc_adb_irefcnt(dns_adb_t *);
static inline void inc_adb_erefcnt(dns_adb_t *);
unsigned int);
static void cancel_fetches_at_name(dns_adbname_t *);
static inline void check_exit(dns_adb_t *);
static void water(void *, int);
#ifdef LRU_DEBUG
#endif
/*
* MUST NOT overlap DNS_ADBFIND_* flags!
*/
#define FIND_EVENT_SENT 0x40000000
#define FIND_EVENT_FREED 0x80000000
#define NAME_NEEDS_POKE 0x80000000
#define NAME_IS_DEAD 0x40000000
#define NAME_HINT_OK DNS_ADBFIND_HINTOK
#define NAME_GLUE_OK DNS_ADBFIND_GLUEOK
/*
* To the name, address classes are all that really exist. If it has a
* V6 address it doesn't care if it came from a AAAA query.
*/
/*
* Fetches are broken out into A and AAAA types. In some cases,
* however, it makes more sense to test for a particular class of fetches,
* like V4 or V6 above.
* Note: since we have removed the support of A6 in adb, FETCH_A and FETCH_AAAA
* are now equal to FETCH_V4 and FETCH_V6, respectively.
*/
#define NAME_FETCH_V4(n) (NAME_FETCH_A(n))
#define NAME_FETCH_V6(n) (NAME_FETCH_AAAA(n))
/*
* Find options and tests to see if there are addresses on the list.
*/
!= 0)
!= 0)
/*
* These are currently used on simple unsigned ints, so they are
* not really associated with any particular type.
*/
#define WANT_INET(x) (((x) & DNS_ADBFIND_INET) != 0)
#define WANT_INET6(x) (((x) & DNS_ADBFIND_INET6) != 0)
/*
* Find out if the flags on a name (nf) indicate if it is a hint or
* glue, and compare this to the appropriate bits set in o, to see if
* this is ok.
*/
((o) & DNS_ADBFIND_STARTATZONE))
#define EXIT_LEVEL ENTER_LEVEL
#define NCACHE_RESULT(r) ((r) == DNS_R_NCACHENXDOMAIN || \
(r) == DNS_R_NCACHENXRRSET)
#define AUTH_NX(r) ((r) == DNS_R_NXDOMAIN || \
(r) == DNS_R_NXRRSET)
#define NXDOMAIN_RESULT(r) ((r) == DNS_R_NXDOMAIN || \
(r) == DNS_R_NCACHENXDOMAIN)
#define NXRRSET_RESULT(r) ((r) == DNS_R_NCACHENXRRSET || \
(r) == DNS_R_NXRRSET || \
(r) == DNS_R_HINTNXRRSET)
/*
* Error state rankings.
*/
#define FIND_ERR_SUCCESS 0 /* highest rank */
#define FIND_ERR_CANCELED 1
#define FIND_ERR_FAILURE 2
#define FIND_ERR_NXDOMAIN 3
#define FIND_ERR_NXRRSET 4
#define FIND_ERR_UNEXPECTED 5
#define FIND_ERR_NOTFOUND 6
#define FIND_ERR_MAX 7
static const char *errnames[] = {
"success",
"canceled",
"failure",
"nxdomain",
"nxrrset",
"unexpected",
"not_found"
};
ISC_R_NOTFOUND /* not YET found */
};
static void
static void
}
static inline dns_ttl_t
if (ttl < ADB_CACHE_MINIMUM)
if (ttl > ADB_CACHE_MAXIMUM)
return (ttl);
}
/*
* Requires the adbname bucket be locked and that no entry buckets be locked.
*
* This code handles A and AAAA rdatasets only.
*/
static isc_result_t
{
int addr_bucket;
unsigned int findoptions;
if (rdtype == dns_rdatatype_a)
else
while (result == ISC_R_SUCCESS) {
if (rdtype == dns_rdatatype_a) {
} else {
}
goto fail;
}
if (foundentry == NULL) {
goto fail;
}
} else {
break;
foundentry->refcnt++;
} else
}
if (rdtype == dns_rdatatype_a)
else
}
}
fail:
if (addr_bucket != DNS_ADB_INVALIDBUCKET)
else
if (rdtype == dns_rdatatype_a) {
} else {
}
if (new_addresses_added) {
/*
* Lie a little here. This is more or less so code that cares
* can find out if any new information was added or not.
*/
return (ISC_R_SUCCESS);
}
return (result);
}
/*
* Requires the name's bucket be locked.
*/
static isc_boolean_t
name = *n;
*n = NULL;
/*
* If we're dead already, just check to see if we should go
* away now or not.
*/
if (result)
return (result);
}
/*
* Clean up the name's various lists. These two are destructive
* in that they will always empty the list.
*/
/*
* If fetches are running, cancel them. If none are running, we can
* just kill the name here.
*/
if (!NAME_FETCH(name)) {
if (result)
} else {
}
return (result);
}
/*
* Requires the name's bucket be locked and no entry buckets be locked.
*/
static isc_boolean_t
/*
* Check to see if we need to remove the v4 addresses
*/
if (NAME_HAS_V4(name)) {
}
}
/*
* Check to see if we need to remove the v6 addresses
*/
if (NAME_HAS_V6(name)) {
}
}
/*
* Check to see if we need to remove the alias target.
*/
}
}
/*
* Requires the name's bucket be locked.
*/
static inline void
}
/*
* Requires the name's bucket be locked.
*/
static inline isc_boolean_t
int bucket;
return (result);
}
/*
* Requires the entry's bucket be locked.
*/
static inline void
}
/*
* Requires the entry's bucket be locked.
*/
static inline isc_boolean_t
int bucket;
return (result);
}
static inline void
}
}
/*
* The ADB _MUST_ be locked before calling. Also, exit conditions must be
* checked after calling this function.
*/
static isc_boolean_t
int bucket;
/*
* This bucket has no names. We must decrement the
* irefcnt ourselves, since it will not be
* automatically triggered by a name being unlinked.
*/
} else {
/*
* Run through the list. For each name, clean up finds
* found there, and cancel any fetches running. When
* all the fetches are canceled, the name will destroy
* itself.
*/
}
}
}
return (result);
}
/*
* The ADB _MUST_ be locked before calling. Also, exit conditions must be
* checked after calling this function.
*/
static isc_boolean_t
int bucket;
/*
* This bucket has no entries. We must decrement the
* irefcnt ourselves, since it will not be
* automatically triggered by an entry being unlinked.
*/
} else {
/*
* Run through the list. Cleanup any entries not
* associated with names, and which are not in use.
*/
if (result)
}
entry = next_entry;
}
}
}
return (result);
}
/*
* Name bucket must be locked
*/
static void
if (NAME_FETCH_A(name))
if (NAME_FETCH_AAAA(name))
}
/*
* Assumes the name bucket is locked.
*/
static isc_boolean_t
{
int addr_bucket;
/*
* Clean up the entry if needed.
*/
if (addr_bucket != DNS_ADB_INVALIDBUCKET)
}
/*
* If we are in an overmem situation, force expiration
* so that # of names and # of entries are well
* balanced.
*/
if (is_purge)
}
/*
* Free the namehook
*/
}
if (addr_bucket != DNS_ADB_INVALIDBUCKET)
return (result);
}
static void
if (dns_name_countlabels(target) > 0) {
}
}
static isc_result_t
{
unsigned int nlabels;
int order;
/*
* Copy the CNAME's target into the target name.
*/
if (result != ISC_R_SUCCESS)
return (result);
if (result != ISC_R_SUCCESS)
return (result);
if (result != ISC_R_SUCCESS)
return (result);
} else {
/*
* Get the target name of the DNAME.
*/
if (result != ISC_R_SUCCESS)
return (result);
if (result != ISC_R_SUCCESS)
return (result);
/*
* Construct the new target name.
*/
NULL);
if (result != ISC_R_SUCCESS)
return (result);
if (result != ISC_R_SUCCESS)
return (result);
}
return (ISC_R_SUCCESS);
}
/*
* Assumes nothing is locked, since this is called by the client.
*/
static void
}
/*
* Assumes the name bucket is locked.
*/
static void
unsigned int addrs)
{
"ENTER clean_finds_at_name, name %p, evtype %08x, addrs %08x",
switch (evtype) {
if ((notify) != 0) {
}
break;
if (wanted == 0)
break;
default:
}
if (process) {
/*
* Unlink the find from the name, letting the caller
* call dns_adb_destroyfind() on it to clean it up
* later.
*/
"sending event %p to task %p for find %p",
} else {
}
}
}
static inline void
/*
* The caller must be holding the adb lock.
*/
if (adb->shutting_down) {
/*
* If there aren't any external references either, we're
* done. Send the control event to initiate shutdown.
*/
}
}
static inline isc_boolean_t
}
}
return (result);
}
static inline void
}
static inline void
}
static inline void
int bucket;
if (lock)
if (lock)
}
static inline isc_boolean_t
int bucket;
if (lock)
}
if (lock)
if (!destroy_entry)
return (result);
if (result)
return (result);
}
static inline dns_adbname_t *
return (NULL);
return (NULL);
}
name->partial_result = 0;
return (name);
}
static inline void
dns_adbname_t *n;
n = *name;
INSIST(!NAME_HAS_V4(n));
INSIST(!NAME_HAS_V6(n));
INSIST(!NAME_FETCH(n));
#ifdef LRU_DEBUG
#endif
n->magic = 0;
}
static inline dns_adbnamehook_t *
return (NULL);
return (nh);
}
static inline void
}
static inline dns_adblameinfo_t *
return (NULL);
return (NULL);
}
li->lame_timer = 0;
return (li);
}
static inline void
}
static inline dns_adbentry_t *
dns_adbentry_t *e;
isc_uint32_t r;
if (e == NULL)
return (NULL);
e->magic = DNS_ADBENTRY_MAGIC;
e->refcnt = 0;
e->flags = 0;
isc_random_get(&r);
e->expires = 0;
ISC_LIST_INIT(e->lameinfo);
ISC_LINK_INIT(e, plink);
#ifdef LRU_DEBUG
adb->nentry_total++;
#endif
return (e);
}
static inline void
dns_adbentry_t *e;
e = *entry;
e->magic = 0;
}
#ifdef LRU_DEBUG
#endif
}
static inline dns_adbfind_t *
dns_adbfind_t *h;
if (h == NULL)
return (NULL);
/*
* Public members.
*/
h->magic = 0;
h->partial_result = 0;
h->options = 0;
h->flags = 0;
h->result_v4 = ISC_R_UNEXPECTED;
h->result_v6 = ISC_R_UNEXPECTED;
ISC_LINK_INIT(h, publink);
ISC_LINK_INIT(h, plink);
ISC_LIST_INIT(h->list);
/*
* private members
*/
if (result != ISC_R_SUCCESS) {
return (NULL);
}
h->magic = DNS_ADBFIND_MAGIC;
return (h);
}
static inline dns_adbfetch_t *
dns_adbfetch_t *f;
if (f == NULL)
return (NULL);
f->magic = 0;
goto err;
goto err;
dns_rdataset_init(&f->rdataset);
f->magic = DNS_ADBFETCH_MAGIC;
return (f);
err:
return (NULL);
}
static inline void
dns_adbfetch_t *f;
f = *fetch;
f->magic = 0;
if (dns_rdataset_isassociated(&f->rdataset))
}
static inline isc_boolean_t
return (dec_adb_irefcnt(adb));
}
/*
* Copy bits from the entry into the newly allocated addrinfo. The entry
* must be locked, and the reference count must be bumped up by one
* if this function returns a valid pointer.
*/
static inline dns_adbaddrinfo_t *
return (NULL);
#ifdef LRU_DEBUG
#endif
return (ai);
}
static inline void
}
/*
* Search for the name. NOTE: The bucket is kept locked on both
* success and failure, so it must always be unlocked by the caller!
*
* On the first call to this function, *bucketp must be set to
* DNS_ADB_INVALIDBUCKET.
*/
static inline dns_adbname_t *
{
int bucket;
if (*bucketp == DNS_ADB_INVALIDBUCKET) {
}
return (adbname);
}
}
return (NULL);
}
/*
* Search for the address. NOTE: The bucket is kept locked on both
* success and failure, so it must always be unlocked by the caller.
*
* On the first call to this function, *bucketp must be set to
* DNS_ADB_INVALIDBUCKET. This will cause a lock to occur. On
* later calls (within the same "lock path") it can be left alone, so
* if this function is called multiple times locking is only done if
* the bucket changes.
*/
static inline dns_adbentry_t *
int bucket;
if (*bucketp == DNS_ADB_INVALIDBUCKET) {
}
return (entry);
}
return (NULL);
}
/*
* Entry bucket MUST be locked!
*/
static isc_boolean_t
{
return (ISC_FALSE);
/*
* Has the entry expired?
*/
}
/*
* Order tests from least to most expensive.
*
* We do not break out of the main loop here as
* we use the loop for house keeping.
*/
}
return (is_bad);
}
static void
{
int bucket;
if (!FIND_RETURNLAME(find)
goto nextv4;
}
goto out;
}
/*
* Found a valid entry. Add it to the find's list.
*/
}
}
goto nextv6;
goto out;
}
/*
* Found a valid entry. Add it to the find's list.
*/
}
}
out:
if (bucket != DNS_ADB_INVALIDBUCKET)
}
static void
/*
* Kill the timer, and then the ADB itself. Note that this implies
* that this task was the one scheduled to get timer events. If
* this is not true (and it is unfortunate there is no way to INSIST()
* this) badness will occur.
*/
#ifdef LRU_DEBUG
#endif
isc_event_free(&ev);
}
/*
* Name bucket must be locked; adb may be locked; no other locks held.
*/
static isc_boolean_t
return (result);
if (NAME_FETCH(name))
return (result);
return (result);
return (result);
return (result);
/*
* The name is empty. Delete it.
*/
/*
* Our caller, or one of its callers, will be calling check_exit() at
* some point, so we don't need to do it here.
*/
return (result);
}
/*%
* Examine the tail entry of the LRU list to see if it expires or is stale
* (unused for some period); if so, the name entry will be freed. If the ADB
* is in the overmem condition, the tail and the next to tail entries
* will be unconditionally removed (unless they have an outstanding fetch).
* We don't care about a race on 'overmem' at the risk of causing some
* collateral damage or a small delay in starting cleanup, so we don't bother
* to lock ADB (if it's not locked).
*
* Name bucket must be locked; adb may be locked; no other locks held.
*/
static void
int victims, max_victims;
int scans = 0;
/*
* We limit the number of scanned entries to 10 (arbitrary choice)
* in order to avoid examining too many entries when there are many
* tail entries that have fetches (this should be rare, but could
* happen).
*/
for (victims = 0;
victim = next_victim) {
scans++;
/*
* If the victim is already dead, it simply waits for some
* final events. Ignore it.
*/
goto next;
#ifdef LRU_DEBUG
adb->stale_expire++;
#endif
victims++;
goto next;
}
if (!NAME_FETCH(victim) &&
ISC_TRUE) ==
#ifdef LRU_DEBUG
#endif
victims++;
}
next:
if (!overmem)
break;
}
#ifdef LRU_DEBUG
/* XXX: omit lock for brevity */
#endif
}
/*
* Entry bucket must be locked; adb may be locked; no other locks held.
*/
static isc_boolean_t
{
return (result);
return (result);
/*
* The entry is not in use. Delete it.
*/
if (result)
return (result);
}
/*
* ADB must be locked, and no other locks held.
*/
static isc_boolean_t
return (result);
}
if (!result)
}
return (result);
}
/*
* ADB must be locked, and no other locks held.
*/
static isc_boolean_t
entry = next_entry;
}
return (result);
}
#if 1
static void
isc_event_free(&ev);
}
#else
static void
unsigned int i;
for (i = 0; i < CLEAN_BUCKETS; i++) {
/*
* Call our cleanup routines.
*/
== ISC_FALSE);
/*
* Set the next bucket to be cleaned.
*/
adb->next_cleanbucket++;
adb->next_cleanbucket = 0;
#ifdef DUMP_ADB_AFTER_CLEANING
#endif
}
}
/*
* Reset the timer.
* XXXDCL isc_timer_reset might return ISC_R_UNEXPECTED or
* ISC_R_NOMEMORY, but it isn't clear what could be done here
* if either one of those things happened.
*/
isc_event_free(&ev);
}
#endif
static void
#ifdef LRU_DEBUG
/* for debug: print statistics */
if (adb->nname_total > 0) {
"ADB %p name hit %.2f, entry hit %.2f", adb,
"ADB %p stale name purges: %u(%u,%u)/%u",
}
#endif
/*
* The timer is already dead, from the task's shutdown callback.
*/
}
/*
* Public functions.
*/
{
int i;
return (ISC_R_NOMEMORY);
/*
* Initialize things here that cannot fail, and especially things
* that must be NULL for the error return to work properly.
*/
#ifdef LRU_DEBUG
#endif
adb->next_cleanbucket = 0;
#ifdef LRU_DEBUG
/* for debug */
adb->nname_total = 0;
adb->nentry_total = 0;
adb->stale_purge = 0;
adb->stale_scan = 0;
adb->stale_expire = 0;
#endif
if (result != ISC_R_SUCCESS)
goto fail0b;
if (result != ISC_R_SUCCESS)
goto fail0c;
if (result != ISC_R_SUCCESS)
goto fail0d;
/*
* Initialize the bucket locks for names and elements.
* May as well initialize the list heads, too.
*/
if (result != ISC_R_SUCCESS)
goto fail1;
for (i = 0; i < NBUCKETS; i++) {
adb->name_refcnt[i] = 0;
}
for (i = 0; i < NBUCKETS; i++) {
adb->entry_refcnt[i] = 0;
}
if (result != ISC_R_SUCCESS)
goto fail2;
/*
* Memory pools
*/
#define MPINIT(t, p, n) do { \
if (result != ISC_R_SUCCESS) \
goto fail3; \
isc_mempool_setfreemax((p), FREE_ITEMS); \
isc_mempool_setfillcount((p), FILL_COUNT); \
isc_mempool_setname((p), n); \
} while (0)
/*
* Allocate a timer and a task for our periodic cleanup.
*/
if (result != ISC_R_SUCCESS)
goto fail3;
/*
* XXXMLG When this is changed to be a config file option,
*/
if (result != ISC_R_SUCCESS)
goto fail3;
#ifdef LRU_DEBUG
{
interval.nanoseconds = 0;
&interval) ==
}
#endif
"%u buckets every %u seconds, %u buckets in system, %u cl.interval",
/*
* Normal return.
*/
return (ISC_R_SUCCESS);
/* clean up entrylocks */
fail2: /* clean up namelocks */
fail1: /* clean up only allocated memory */
return (result);
}
void
}
void
if (need_exit_check) {
}
}
void
/*
* Send '*eventp' to 'task' when 'adb' has shutdown.
*/
/*
* We're already shutdown. Send the event.
*/
} else {
}
}
void
/*
* Shutdown 'adb'.
*/
if (!adb->shutting_down) {
if (!need_check_exit)
if (need_check_exit)
}
}
{
int bucket;
unsigned int wanted_addresses;
unsigned int wanted_fetches;
unsigned int query_pending;
}
wanted_fetches = 0;
query_pending = 0;
if (now == 0)
/*
* XXXMLG Move this comment somewhere else!
*
* Look up the name in our internal database.
*
* Possibilities: Note that these are not always exclusive.
*
* No name found. In this case, allocate a new name header and
* an initial namehook or two. If any of these allocations
* fail, clean up and return ISC_R_NOMEMORY.
*
* Name found, valid addresses present. Allocate one addrinfo
* structure for each found and append it to the linked list
* of addresses for this header.
*
* Name found, queries pending. In this case, if a task was
* passed in, allocate a job id, attach it to the name's job
* list and remember to tell the caller that there will be
* more info coming later.
*/
return (ISC_R_NOMEMORY);
/*
* Remember what types of addresses we are interested in.
*/
if (FIND_WANTEVENT(find)) {
}
/*
* Try to see if we know anything about this name at all.
*/
"dns_adb_createfind: returning ISC_R_SHUTTINGDOWN");
goto out;
}
/*
* Nothing found. Allocate a new adbname structure for this name.
*/
/*
* See if there is any stale name at the end of list, and purge
* it if so.
*/
goto out;
}
if (FIND_HINTOK(find))
if (FIND_GLUEOK(find))
if (FIND_STARTATZONE(find))
#ifdef LRU_DEBUG
adb->nname_total++;
#endif
} else {
/* Move this name forward in the LRU list */
#ifdef LRU_DEBUG
#endif
}
/*
* Expire old entries, etc.
*/
/*
* Do we know that the name is an alias?
*/
/*
* Yes, it is.
*/
"dns_adb_createfind: name %p is an alias (cached)",
adbname);
goto post_copy;
}
/*
* start fetches. First try looking for an A record
* in the database.
*/
&& WANT_INET(wanted_addresses)) {
if (result == ISC_R_SUCCESS) {
"dns_adb_createfind: found A for name %p in db",
adbname);
goto v6;
}
/*
* Did we get a CNAME or DNAME?
*/
if (result == DNS_R_ALIAS) {
"dns_adb_createfind: name %p is an alias",
adbname);
goto post_copy;
}
/*
* If the name doesn't exist at all, don't bother with
* v6 queries; they won't work.
*
* If the name does exist but we didn't get our data, go
* ahead and try AAAA.
*
* If the result is neither of these, try a fetch for A.
*/
if (NXDOMAIN_RESULT(result))
goto fetch;
else if (NXRRSET_RESULT(result))
goto v6;
if (!NAME_FETCH_V4(adbname))
}
v6:
&& WANT_INET6(wanted_addresses)) {
if (result == ISC_R_SUCCESS) {
"dns_adb_createfind: found AAAA for name %p",
adbname);
goto fetch;
}
/*
* Did we get a CNAME or DNAME?
*/
if (result == DNS_R_ALIAS) {
"dns_adb_createfind: name %p is an alias",
adbname);
goto post_copy;
}
/*
* Listen to negative cache hints, and don't start
* another query.
*/
goto fetch;
if (!NAME_FETCH_V6(adbname))
}
else
if (wanted_fetches != 0 &&
/*
* We're missing at least one address family. Either the
* caller hasn't instructed us to avoid fetches, or we don't
* know anything about any of the address families that would
* be acceptable so we have to launch fetches.
*/
if (FIND_STARTATZONE(find))
/*
* Start V4.
*/
if (WANT_INET(wanted_fetches) &&
dns_rdatatype_a) == ISC_R_SUCCESS) {
"dns_adb_createfind: started A fetch for name %p",
adbname);
}
/*
* Start V6.
*/
if (WANT_INET6(wanted_fetches) &&
dns_rdatatype_aaaa) == ISC_R_SUCCESS) {
"dns_adb_createfind: "
"started AAAA fetch for name %p",
adbname);
}
}
/*
* Run through the name and copy out the bits we are
* interested in.
*/
if (NAME_FETCH_V4(adbname))
if (NAME_FETCH_V6(adbname))
/*
* Attach to the name's query list if there are queries
* already running, and we have been asked to.
*/
if (!FIND_WANTEVENT(find))
if ((wanted_addresses & query_pending) == 0)
if (alias)
if (want_event) {
} else {
/*
* Remove the flag so the caller knows there will never
* be an event, and set internal flags to fake that
* the event was sent and freed, so dns_adb_destroyfind() will
* do the right thing.
*/
}
if (alias) {
if (result != ISC_R_SUCCESS)
goto out;
}
} else
/*
* Copy out error flags from the name structure into the find.
*/
out:
if (want_event) {
}
}
return (result);
}
void
int bucket;
/*
* The find doesn't exist on any list, and nothing is locked.
* Return the find to the memory pool, and decrement the adb's
* reference count.
*/
}
/*
* WARNING: The find is freed with the adb locked. This is done
* to avoid a race condition where we free the find, some other
* thread tests to see if it should be destroyed, detects it should
* be, destroys it, and then we try to lock it for our check, but the
* lock is destroyed.
*/
}
void
int bucket;
int unlock_bucket;
if (bucket == DNS_ADB_INVALIDBUCKET)
goto cleanup;
/*
* We need to get the adbname's lock to unlink the find.
*/
if (bucket != DNS_ADB_INVALIDBUCKET) {
}
if (!FIND_EVENTSENT(find)) {
}
}
void
int i;
/*
* Lock the adb itself, lock all the name buckets, then lock all
* the entry buckets. This should put the adb into a state where
* nothing can change, so we can iterate through everything and
* print at our leisure.
*/
for (i = 0; i < NBUCKETS; i++)
for (i = 0; i < NBUCKETS; i++)
}
static void
return;
}
static void
int i;
fprintf(f, ";\n; Address database dump\n;\n");
if (debug)
fprintf(f, "; addr %p, erefcnt %u, irefcnt %u, finds out %u\n",
for (i = 0; i < NBUCKETS; i++)
for (i = 0; i < NBUCKETS; i++)
/*
* Dump the names
*/
for (i = 0; i < NBUCKETS; i++) {
continue;
if (debug)
fprintf(f, "; bucket %d\n", i);
for (;
{
if (debug)
fprintf(f, "; name %p (flags %08x)\n",
fprintf(f, "; ");
fprintf(f, " alias ");
}
fprintf(f, " [v4 %s] [v6 %s]",
fprintf(f, "\n");
if (debug)
print_fetch_list(f, name);
if (debug)
print_find_list(f, name);
}
}
fprintf(f, ";\n; Unassociated entries\n;\n");
for (i = 0; i < NBUCKETS; i++) {
}
}
/*
* Unlock everything
*/
for (i = 0; i < NBUCKETS; i++)
for (i = 0; i < NBUCKETS; i++)
}
static void
{
char addrbuf[ISC_NETADDR_FORMATSIZE];
char typebuf[DNS_RDATATYPE_FORMATSIZE];
if (debug)
fprintf(f, ";\t%s [srtt %u] [flags %08x]",
fprintf(f, "\n");
fprintf(f, ";\t\t");
}
}
void
char tmp[512];
const char *tmpp;
/*
* Not used currently, in the API Just In Case we
*/
fprintf(f, ";\tqpending %08x partial %08x options %08x flags %08x\n",
fprintf(f, ";\tname_bucket %d, name %p, event sender %p\n",
fprintf(f, "\tAddresses:\n");
case AF_INET:
break;
case AF_INET6:
break;
default:
tmpp = "UnkFamily";
}
tmpp = "BadAddress";
fprintf(f, "\t\tentry %p, flags %08x"
" srtt %u addr %s\n",
}
}
static void
char buf[DNS_NAME_FORMATSIZE];
}
static void
{
{
if (debug)
}
}
static inline void
fprintf(f, "\t\tFetch(%s): %p -> { nh %p, entry %p, fetch %p }\n",
}
static void
if (NAME_FETCH_A(n))
if (NAME_FETCH_AAAA(n))
}
static void
dns_adb_dumpfind(find, f);
}
}
static isc_result_t
{
if (rdtype == dns_rdatatype_a)
else
/* XXXVIX this switch statement is too sparse to gen a jump table. */
switch (result) {
case DNS_R_GLUE:
case DNS_R_HINT:
case ISC_R_SUCCESS:
/*
* Found in the database. Even if we can't copy out
* any information, return success, or else a fetch
* will be made, which will only make things worse.
*/
if (rdtype == dns_rdatatype_a)
else
break;
case DNS_R_NXDOMAIN:
case DNS_R_NXRRSET:
/*
* We're authoritative and the data doesn't exist.
* Make up a negative cache entry so we don't ask again
* for a while.
*
* XXXRTH What time should we use? I'm putting in 30 seconds
* for now.
*/
if (rdtype == dns_rdatatype_a) {
"adb name %p: Caching auth negative entry for A",
adbname);
if (result == DNS_R_NXDOMAIN)
else
} else {
"adb name %p: Caching auth negative entry for AAAA",
adbname);
if (result == DNS_R_NXDOMAIN)
else
}
break;
case DNS_R_NCACHENXDOMAIN:
case DNS_R_NCACHENXRRSET:
/*
* We found a negative cache entry. Pull the TTL from it
* so we won't ask again for a while.
*/
if (rdtype == dns_rdatatype_a) {
if (result == DNS_R_NCACHENXDOMAIN)
else
"adb name %p: Caching negative entry for A (ttl %u)",
} else {
"adb name %p: Caching negative entry for AAAA (ttl %u)",
if (result == DNS_R_NCACHENXDOMAIN)
else
}
break;
case DNS_R_CNAME:
case DNS_R_DNAME:
/*
* Clear the hint and glue flags, so this will match
* more often.
*/
if (result == ISC_R_SUCCESS) {
"adb name %p: caching alias target",
adbname);
}
if (rdtype == dns_rdatatype_a)
else
break;
}
return (result);
}
static void
int bucket;
unsigned int address_type;
address_type = 0;
} else if (NAME_FETCH_AAAA(name)
}
INSIST(address_type != 0);
/*
* Cleanup things we don't care about.
*/
/*
* If this name is marked as dead, clean up, throwing away
* potentially good data.
*/
isc_event_free(&ev);
if (want_check_exit) {
}
return;
}
/*
* If we got a negative cache response, remember it.
*/
if (address_type == DNS_ADBFIND_INET) {
"caching negative entry for A (ttl %u)",
else
} else {
"caching negative entry for AAAA (ttl %u)",
else
}
goto out;
}
/*
*/
if (result == ISC_R_SUCCESS) {
"adb fetch name %p: caching alias target",
name);
}
goto check_result;
}
/*
* Did we get back junk? If so, and there are no more fetches
* sitting out there, tell all the finds about it.
*/
char buf[DNS_NAME_FORMATSIZE];
/* XXXMLG Don't pound on bad servers. */
if (address_type == DNS_ADBFIND_INET) {
} else {
}
goto out;
}
/*
* We got something potentially useful.
*/
if (result == ISC_R_SUCCESS) {
if (address_type == DNS_ADBFIND_INET)
else
}
out:
isc_event_free(&ev);
}
static isc_result_t
{
unsigned int options;
nameservers = NULL;
if (start_at_zone) {
"fetch_name: starting at zone for name %p",
adbname);
goto cleanup;
nameservers = &rdataset;
}
goto cleanup;
}
if (result != ISC_R_SUCCESS)
goto cleanup;
if (type == dns_rdatatype_a)
else
return (result);
}
/*
* XXXMLG Needs to take a find argument and an address info, no zone or adb,
* since these can be extracted from the find itself.
*/
{
int bucket;
goto unlock;
}
goto unlock;
}
return (result);
}
void
{
int bucket;
unsigned int new_srtt;
if (factor == DNS_ADB_RTTADJAGE)
else
}
void
{
int bucket;
/*
* Note that we do not update the other bits in addr->flags with
* the most recent values from addr->entry->flags.
*/
}
{
int bucket;
goto unlock;
}
/*
* We don't know anything about this address.
*/
goto unlock;
}
} else
} else {
}
return (result);
}
void
int bucket;
if (want_check_exit) {
}
}
void
unsigned int i;
/*
* Call our cleanup routines.
*/
for (i = 0; i < NBUCKETS; i++)
for (i = 0; i < NBUCKETS; i++)
#ifdef DUMP_ADB_AFTER_CLEANING
#endif
}
void
int bucket;
ISC_TRUE) ==
}
}
}
static void
#if 0 /* we don't need this timer for the new cleaning policy. */
if (overmem) {
}
#endif
}
void
else
}
#ifdef LRU_DEBUG
/*
* Periodic dumping of the internal state of the statistics.
* This will dump the cache contents, uses, record types, etc.
*/
static void
"ADB memory usage %p: mem inuse %lu, "
"%u/%u names, %u/%u entries, "
}
interval.nanoseconds = 0;
ISC_R_SUCCESS); /* XXX: this is not always true */
isc_event_free(&ev);
}
#endif