adb.c revision 4abed3e3563c7ad346178433130e6d150d3ffeaf
/*
* Copyright (C) 1999 Internet Software Consortium.
*
* Permission to use, copy, modify, and distribute this software for any
* 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 INTERNET SOFTWARE CONSORTIUM DISCLAIMS
* ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE
* CONSORTIUM 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.
*/
/*
* Implementation notes
* --------------------
*
* 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.
*/
#define DUMP_ADB_AFTER_CLEANING
#include <config.h>
#include <limits.h>
#include <stdio.h>
#include <string.h>
#include <isc/assertions.h>
#include <isc/condition.h>
#include <isc/mutexblock.h>
#include <dns/fixedname.h>
#include <dns/rdataset.h>
#include <dns/resolver.h>
/*
* The number of buckets needs to be a prime (for good hashing).
*
* XXXRTH How many buckets do we need?
*
* This value must be coordinated with CLEAN_SECONDS (below).
*/
/*
* 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.
*/
/*
* Clean one bucket every CLEAN_SECONDS.
*/
#if CLEAN_SECONDS < 1
#define CLEAN_SECONDS 1
#endif
typedef struct dns_adbnamehook dns_adbnamehook_t;
typedef struct dns_adbzoneinfo dns_adbzoneinfo_t;
typedef struct dns_adbfetch dns_adbfetch_t;
typedef struct dns_adbfetch6 dns_adbfetch6_t;
struct dns_adb {
unsigned int magic;
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?
*/
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];
};
/*
* XXXMLG Document these structures.
*/
struct dns_adbname {
unsigned int magic;
unsigned int partial_result;
unsigned int flags;
int lock_bucket;
unsigned int chains;
};
struct dns_adbfetch {
unsigned int magic;
};
struct dns_adbfetch6 {
unsigned int magic;
unsigned int flags;
};
/*
* dns_adbnamehook_t
*
* 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;
};
/*
* dns_adbzoneinfo_t
*
* This is a small widget that holds zone-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_adbzoneinfo {
unsigned int magic;
};
/*
* An address entry. It holds quite a bit of information about addresses,
* including edns state, rtt, and of course the address of the host.
*/
struct dns_adbentry {
unsigned int magic;
int lock_bucket;
unsigned int refcnt;
unsigned int flags;
int edns_level; /* must be int! */
int goodness; /* bad < 0 <= good */
unsigned int srtt;
};
/*
* Internal functions (and prototypes).
*/
dns_adbentry_t *);
dns_adbentry_t *);
dns_a6context_t *);
int *);
isc_sockaddr_t *, int *);
static inline void dec_adb_irefcnt(dns_adb_t *);
unsigned int);
static void cancel_fetches_at_name(dns_adbname_t *);
static inline void check_exit(dns_adb_t *);
static void shutdown_names(dns_adb_t *);
static void shutdown_entries(dns_adb_t *);
/*
* 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
/*
* To the name, address classes are all that really exist. If it has a
* V6 address it doesn't care if it came from an A6 chain or an AAAA query.
*/
/*
* Fetches are broken out into A, AAAA, and A6 types. In some cases,
* however, it makes more sense to test for a particular class of fetches,
* like V4 or V6 above.
*/
#define NAME_FETCH_V4(n) (NAME_FETCH_A(n))
/*
* Was this fetch started using the hints database?
* Was this the initial fetch for the A6 record? If so, we might want to
* start AAAA queries if it fails.
*/
#define FETCH_USE_HINTS 0x80000000
#define FETCH_FIRST_A6 0x40000000
/*
* 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)
#define ENTER_LEVEL 50
#define EXIT_LEVEL ENTER_LEVEL
#define CLEAN_LEVEL 100
#define DEF_LEVEL 5
#define NCACHE_LEVEL 20
#define NCACHE_RESULT(r) ((r) == DNS_R_NCACHENXDOMAIN || \
(r) == DNS_R_NCACHENXRRSET)
#define AUTH_NX(r) ((r) == DNS_R_NXDOMAIN || \
(r) == DNS_R_NXRRSET)
static void
{
}
/*
* 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 next;
}
goto fail;
}
if (foundentry == NULL) {
goto fail;
}
} else {
foundentry->refcnt++;
}
if (rdtype == dns_rdatatype_a)
else
next:
}
fail:
if (addr_bucket != DNS_ADB_INVALIDBUCKET)
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);
}
static void
{
int addr_bucket;
goto fail;
}
goto fail;
}
if (foundentry == NULL) {
goto fail;
}
} else {
foundentry->refcnt++;
}
fail:
if (addr_bucket != DNS_ADB_INVALIDBUCKET)
}
/*
* Requires the name's bucket be locked.
*/
static void
{
name = *n;
*n = NULL;
/*
* If we're dead already, just check to see if we should go
* away now or not.
*/
return;
}
/*
* 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)) {
} else {
}
}
/*
* Requires the name's bucket be locked and no entry buckets be locked.
*/
static void
{
/*
* 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 void
{
int bucket;
}
/*
* Requires the entry's bucket be locked.
*/
static inline void
{
}
/*
* Requires the entry's bucket be locked.
*/
static inline void
{
int bucket;
}
static inline void
{
}
}
/*
* The ADB _MUST_ be locked before calling. Also, exit conditions must be
* checked after calling this function.
*/
static void
{
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.
*/
}
}
}
}
/*
* The ADB _MUST_ be locked before calling. Also, exit conditions must be
* checked after calling this function.
*/
static void
{
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.
*/
}
entry = next_entry;
}
}
}
}
/*
* Name bucket must be locked
*/
static void
{
if (NAME_FETCH_A(name))
if (NAME_FETCH_AAAA(name))
}
}
/*
* Assumes the name bucket is locked.
*/
static void
{
int addr_bucket;
/*
* Clean up the entry if needed.
*/
if (addr_bucket != DNS_ADB_INVALIDBUCKET)
}
}
/*
* Free the namehook
*/
}
if (addr_bucket != DNS_ADB_INVALIDBUCKET)
}
static void
if (dns_name_countlabels(target) > 0) {
}
}
static isc_result_t
{
int order;
isc_region_t r;
/*
* Copy the CNAME's target into the target name.
*/
if (result != ISC_R_SUCCESS)
return (result);
dns_name_fromregion(&tname, &r);
if (result != ISC_R_SUCCESS)
return (result);
} else {
/*
* Get the target name of the DNAME.
*/
if (result != ISC_R_SUCCESS)
return (result);
dns_name_fromregion(&tname, &r);
/*
* Construct the new target name.
*/
if (result != ISC_R_SUCCESS)
return (result);
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)
{
unsigned int wanted;
"ENTER clean_finds_at_name, name %p, evtype %08x, addrs %08x",
switch (evtype) {
}
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.
*/
else
/*
* We're now shutdown. Send any whenshutdown events.
*/
event = next_event) {
}
/*
* If there aren't any external references either, we're
* done. Send the control event to initiate shutdown.
*/
}
}
}
static inline void
{
}
static inline void
{
if (lock)
if (lock)
}
static inline void
{
if (lock)
if (lock)
}
static inline void
{
int bucket;
if (lock)
if (lock)
}
static inline void
{
int bucket;
if (lock)
}
if (lock)
if (!destroy_entry)
return;
}
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));
n->magic = 0;
}
static inline dns_adbnamehook_t *
{
return (NULL);
return (nh);
}
static inline void
{
}
static inline dns_adbzoneinfo_t *
{
return (NULL);
return (NULL);
}
zi->lame_timer = 0;
return (zi);
}
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;
e->goodness = 0;
e->expires = 0;
ISC_LIST_INIT(e->zoneinfo);
ISC_LINK_INIT(e, plink);
return (e);
}
static inline void
{
dns_adbentry_t *e;
e = *entry;
e->magic = 0;
}
}
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;
ISC_LINK_INIT(h, publink);
ISC_LINK_INIT(h, plink);
ISC_LIST_INIT(h->list);
/*
* private members
*/
if (result != ISC_R_SUCCESS) {
"isc_mutex_init failed in new_adbfind()");
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))
}
/*
* Caller must be holding the name lock.
*/
static isc_result_t
{
rdataset, sigrdataset));
}
/*
* Caller must be holding the name lock.
*/
static void
return;
}
if (result != ISC_R_SUCCESS)
goto cleanup;
if (result != ISC_R_SUCCESS) {
}
}
static inline dns_adbfetch6_t *
{
dns_adbfetch6_t *f;
if (f == NULL)
return (NULL);
f->magic = 0;
f->flags = 0;
goto err;
goto err;
dns_rdataset_init(&f->rdataset);
f->magic = DNS_ADBFETCH6_MAGIC;
return (f);
err:
return (NULL);
}
static inline void
{
dns_adbfetch6_t *f;
f = *fetch;
f->magic = 0;
if (dns_rdataset_isassociated(&f->rdataset))
}
static inline void
{
}
/*
* 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);
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.
*/
}
}
return (is_bad);
}
static void
{
int bucket;
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
{
(void)task; /* not used */
/*
* 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.
*/
isc_event_free(&ev);
}
/*
* name bucket must be locked; adb may be locked; no other locks held.
*/
static void
{
return;
if (NAME_FETCH(name))
return;
return;
return;
return;
/*
* 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.
*/
}
/*
* entry bucket must be locked; adb may be locked; no other locks held.
*/
static void
{
return;
return;
/*
* The entry is not in use. Delete it.
*/
}
/*
* ADB must be locked, and no other locks held.
*/
static void
{
return;
}
}
}
/*
* ADB must be locked, and no other locks held.
*/
static void
{
entry = next_entry;
}
}
static void
{
(void)task; /* not used */
/*
* Call our cleanup routines.
*/
/*
* Set the next bucket to be cleaned.
*/
adb->next_cleanbucket++;
adb->next_cleanbucket = 0;
#ifdef DUMP_ADB_AFTER_CLEANING
#endif
}
/*
* Reset the timer.
*/
isc_event_free(&ev);
}
static void
{
/*
* 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.
*/
adb->next_cleanbucket = 0;
if (result != ISC_R_SUCCESS)
goto fail0a;
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, l, 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); \
if (l) \
} 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;
/*
* Normal return.
*/
return (ISC_R_SUCCESS);
/* clean up entrylocks */
fail2: /* clean up namelocks */
fail1: /* clean up only allocated memory */
return (result);
}
void
{
}
void
{
/*
* Send '*eventp' to 'task' when 'adb' has shutdown.
*/
else
/*
* We're already shutdown. Send the event.
*/
} else {
}
}
void
/*
* Shutdown 'adb'.
*/
if (!adb->shutting_down) {
}
}
{
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.
*/
goto out;
}
}
/*
* 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.
*/
&& 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?
*/
"dns_adb_createfind: name %p is an alias",
adbname);
goto post_copy;
}
/*
* Listen to negative cache hints, and don't start
* another query.
*/
goto v6;
}
v6:
&& WANT_INET6(wanted_addresses)) {
if (result == ISC_R_SUCCESS) {
"dns_adb_createfind: Found A6 for name %p",
adbname);
goto fetch;
}
/*
* Did we get a CNAME or DNAME?
*/
"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 (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_STARTATROOT(find))
/*
* Start V4.
*/
if (WANT_INET(wanted_fetches) &&
"dns_adb_createfind: Started A fetch for name %p",
adbname);
}
/*
* Start V6.
*/
if (WANT_INET6(wanted_fetches) &&
"dns_adb_createfind: Started A6 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
out:
if (want_event) {
}
}
/* dns_adb_dumpfind(find, stderr); */
if (bucket != DNS_ADB_INVALIDBUCKET)
return (result);
}
{
int name_bucket;
/*
* Find the name.
*/
return (ISC_R_NOTFOUND);
}
/*
* If we're shutting down and this bucket is empty, we need to call
* check_exit() to see if we're done.
*/
if (name_bucket != DNS_ADB_INVALIDBUCKET)
if (want_check_exit) {
}
return (DNS_R_SUCCESS);
}
/* XXXMLG needs v6 support */
{
if (now == 0)
/*
* First, see if the host is already in the database. If it is,
* don't make a new host entry. If not, copy the name and name's
* contents into our structure and allocate what we'll need
* to attach things together.
*/
goto out;
}
}
/*
* Now, while keeping the name locked, search for the address.
* Three possibilities: One, the address doesn't exist.
* Two, the address exists, but we aren't linked to it.
* Three, the address exists and we are linked to it.
* (1) causes a new entry and namehook to be created.
* (2) causes only a new namehook.
* (3) is an error.
*/
/*
* Case (1): new entry and namehook.
*/
goto out;
}
}
/*
* Case (3): entry exists, we're linked.
*/
goto out;
}
}
/*
* Case (2): New namehook, link to entry from above.
*/
goto out;
}
/*
* If needed, string up the name and entry.
*/
return (ISC_R_SUCCESS);
out:
if (free_name)
if (free_entry)
if (free_namehook)
if (name_bucket != DNS_ADB_INVALIDBUCKET)
if (addr_bucket != DNS_ADB_INVALIDBUCKET)
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
{
/*
* 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 leasure.
*/
}
static void
{
int i;
char tmp[512];
const char *tmpp;
fprintf(f, "erefcnt %u, irefcnt %u, finds out %u\n",
for (i = 0 ; i < NBUCKETS ; i++)
for (i = 0 ; i < NBUCKETS ; i++)
/*
* Dump the names
*/
fprintf(f, "Names:\n");
for (i = 0 ; i < NBUCKETS ; i++) {
continue;
fprintf(f, "Name bucket %d:\n", i);
if (!DNS_ADBNAME_VALID(name))
fprintf(f, "\texpiry [");
fprintf(f, "inf ");
else
fprintf(f, "inf ");
else
fprintf(f, "inf] ");
else
fprintf(f, "\t\t alias for ");
}
fprintf(f, "\n");
print_namehook_list(f, name);
print_fetch_list(f, name);
print_find_list(f, name);
fprintf(f, "\n");
}
}
/*
* Dump the entries
*/
fprintf(f, "Entries:\n");
for (i = 0 ; i < NBUCKETS ; i++) {
continue;
fprintf(f, "Entry bucket %d:\n", i);
if (!DNS_ADBENTRY_VALID(entry))
if (entry->lock_bucket != i)
fprintf(f, "\tWRONG BUCKET! lock_bucket %d\n",
entry->lock_bucket);
case AF_INET:
break;
case AF_INET6:
break;
default:
tmpp = "UnkFamily";
}
tmpp = "CANNOT TRANSLATE ADDRESS!";
fprintf(f, "\t%p: refcnt %u flags %08x goodness %d"
" srtt %u addr %s\n",
}
}
/*
* Unlock everything
*/
for (i = 0 ; i < NBUCKETS ; i++)
for (i = 0 ; i < NBUCKETS ; i++)
}
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 = "CANNOT TRANSLATE ADDRESS!";
fprintf(f, "\t\tentry %p, flags %08x goodness %d"
" srtt %u addr %s\n",
}
}
static void
{
char buf[1024];
isc_buffer_t b;
isc_region_t r;
isc_buffer_used(&b, &r);
}
}
static void
{
}
}
}
static inline void
{
fprintf(f, "\t\tFetch(%s): %p -> { nh %p, entry %p, fetch %p }\n",
}
static inline void
{
fprintf(f, "\t\tFetch(A6): %p -> { nh %p, entry %p, fetch %p }\n",
}
static void
{
if (NAME_FETCH_A(n))
if (NAME_FETCH_AAAA(n))
print_fetch6(f, fetch6);
}
}
static void
{
dns_adb_dumpfind(find, f);
}
}
static isc_result_t
{
switch (result) {
case DNS_R_GLUE:
case DNS_R_HINT:
case DNS_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.
*/
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);
} else {
"adb name %p: Caching auth negative entry for AAAA",
adbname);
}
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) {
"adb name %p: Caching negative entry for A (ttl %u)",
} else {
"adb name %p: Caching negative entry for AAAA (ttl %u)",
}
break;
case DNS_R_CNAME:
case DNS_R_DNAME:
if (result == ISC_R_SUCCESS) {
"adb name %p: caching alias target",
adbname);
}
break;
}
return (result);
}
static isc_result_t
{
switch (result) {
case DNS_R_GLUE:
case DNS_R_HINT:
case DNS_R_SUCCESS:
/*
* Start a6 chain follower. There is no need to poke people
* who might be waiting, since this is call requires there
* are none.
*/
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.
*/
"adb name %p: Caching auth negative entry for AAAA",
adbname);
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.
*/
"adb name %p: Caching negative entry for A6 (ttl %u)",
break;
case DNS_R_CNAME:
case DNS_R_DNAME:
if (result == ISC_R_SUCCESS) {
"adb name %p: caching alias target",
adbname);
}
break;
}
return (result);
}
static void
{
int bucket;
unsigned int address_type;
(void)task;
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 we're shutting down and this bucket is empty, we need
* to call check_exit() to see if we're done.
*/
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 {
"Caching negative entry for AAAA (ttl %u)",
}
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.
*/
/* 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)
out:
isc_event_free(&ev);
}
static void
{
int bucket;
(void)task;
break;
/*
* 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 we're shutting down and this bucket is empty, we need
* to call check_exit() to see if we're done.
*/
if (want_check_exit) {
}
return;
}
/*
* If the A6 query didn't succeed, and this is the first query
* in the A6 chain, try AAAA records instead. For later failures,
* don't do this.
*/
/*
* If we got a negative cache response, remember it.
*/
"Caching negative entry for A6 (ttl %u)",
}
/*
*/
if (FETCH_USEHINTS(fetch))
else
"name %p: A6 query failed, starting AAAA", name);
/*
* Since this is the very first fetch, and it
* failed, we know there are no more running.
*/
if (result == ISC_R_SUCCESS) {
"name %p: callback_a6: Found AAAA for",
name);
goto out;
}
/*
* Listen to negative cache hints, and don't start
* another query.
*/
goto out;
/*
* Try to start fetches for AAAA.
*/
if (result == ISC_R_SUCCESS) {
"name %p: callback_a6: Started AAAA fetch",
name);
goto out;
}
}
goto out;
}
/*
* We got something potentially useful. Run the A6 chain
* follower on this A6 rdataset.
*/
out:
isc_event_free(&ev);
if (NAME_NEEDSPOKE(name))
else if (!NAME_FETCH_V6(name))
return;
}
static isc_result_t
{
if (start_at_root) {
adbname);
name = dns_rootname;
} else
goto cleanup;
}
if (result != ISC_R_SUCCESS)
goto cleanup;
return (result);
}
static isc_result_t
{
goto cleanup;
}
if (result != ISC_R_SUCCESS)
goto cleanup;
return (result);
}
static isc_result_t
{
if (start_at_root) {
adbname);
name = dns_rootname;
} else
goto cleanup;
}
if (use_hints)
if (result != ISC_R_SUCCESS)
goto cleanup;
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;
return (ISC_R_NOMEMORY);
return (ISC_R_SUCCESS);
}
void
int goodness_adjustment)
{
int bucket;
int old_goodness, new_goodness;
if (goodness_adjustment == 0)
return;
if (goodness_adjustment > 0) {
else
} else {
else
}
}
void
{
int bucket;
unsigned int new_srtt;
}
void
{
int bucket;
/*
* Note that we do not update the other bits in addr->flags with
* the most recent values from addr->entry->flags.
*
* XXXRTH I think this is what we want, because otherwise flags
* that the caller didn't ask to change could be updated.
*/
}
{
int bucket;
goto unlock;
}
/*
* We don't know anything about this address.
*/
goto unlock;
}
} else
}
return (result);
}
void
{
int bucket;
if (want_check_exit) {
}
}