adb.c revision 5fca48054b5e791a2fa0c5015bc3b6fef4fcdce1
/*
* Copyright (C) 1999, 2000 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.
*/
/* $Id: adb.c,v 1.160 2000/12/18 20:03:33 gson Exp $ */
/*
* 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.
*/
#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/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 CLEAN_BUCKETS buckets every CLEAN_SECONDS.
*/
#if CLEAN_BUCKET < 1
#define CLEAN_BUCKETS 1
#endif
#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;
unsigned int fetch_err;
unsigned int fetch6_err;
};
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_a6context_t *);
unsigned int, 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
#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 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_FIRST_A6 0x80000000
/*
* 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.
*/
#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)
#define NXDOMAIN_RESULT(r) ((r) == DNS_R_NXDOMAIN || \
(r) == DNS_R_NCACHENXDOMAIN)
#define NXRRSET_RESULT(r) ((r) == DNS_R_NCACHENXRRSET || \
(r) == DNS_R_NXRRSET)
/*
* 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
ISC_R_NOTFOUND /* not YET found */
};
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 {
foundentry->refcnt++;
}
if (rdtype == dns_rdatatype_a)
else
}
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;
}
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;
/*
* 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.
*/
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)
{
"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.
*/
else
/*
* We're now shutdown. Send any whenshutdown events.
*/
}
/*
* 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->edns_level = -1;
e->goodness = 0;
isc_random_get(&r);
e->expires = 0;
e->avoid_bitstring = 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;
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) {
"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);
ISC_LINK_INIT(f, plink);
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;
/*
* Check for avoid bitstring timeout.
*/
if (entry->avoid_bitstring > 0
entry->avoid_bitstring = 0;
if (!FIND_RETURNLAME(find)
goto nextv4;
}
goto out;
}
/*
* Found a valid entry. Add it to the find's list.
*/
}
}
/*
* Check for avoid bitstring timeout.
*/
if (entry->avoid_bitstring > 0
entry->avoid_bitstring = 0;
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.
*/
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
int freq;
}
entry = next_entry;
}
}
static void
unsigned int i;
for (i = 0 ; i < CLEAN_BUCKETS ; i++) {
/*
* 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.
* 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);
}
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 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;
DP(5,
"Cleaning interval for adb: "
"%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
}
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;
}
if (FIND_HINTOK(find))
if (FIND_GLUEOK(find))
}
/*
* 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 a6.
*
* 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;
}
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?
*/
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, jump to the fetch
* code.
*
* If the name exists but the A6 doesn't, try starting
* an aaaa database search.
*
* If neither of these are true, say we want an A6 fetch
* and perhaps we'll get lucky.
*/
if (NXDOMAIN_RESULT(result))
goto fetch;
else if (NXRRSET_RESULT(result))
goto aaaa;
else {
goto fetch;
}
aaaa:
if (result == ISC_R_SUCCESS) {
"dns_adb_createfind: found AAAA for name %p",
adbname);
goto fetch;
}
/*
* Did we get a CNAME or DNAME? This should have hit
* during the A6 query, but we'll reproduce it here Just
* In Case.
*/
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 (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
/*
* Copy out error flags from the name structure into the find.
*/
out:
if (want_event) {
}
}
if (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 leisure.
*/
}
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, " [err4 %u] [err6 %u]\n",
if (debug)
print_fetch_list(f, name);
if (debug)
print_find_list(f, name);
}
}
/*
* Unlock everything
*/
for (i = 0 ; i < NBUCKETS ; i++)
for (i = 0 ; i < NBUCKETS ; i++)
}
static void
{
char addrbuf[ISC_NETADDR_FORMATSIZE];
if (debug)
fprintf(f, ";\t%p: refcnt %u flags %08x goodness %d\n",
if (entry->avoid_bitstring != 0)
fprintf(f, " [avoid_bitstring %d]",
fprintf(f, "\n");
}
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 goodness %d"
" srtt %u addr %s avoid_bitstring %u\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 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
{
if (rdtype == dns_rdatatype_a)
else
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 isc_result_t
switch (result) {
case DNS_R_GLUE:
case DNS_R_HINT:
case ISC_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 A6",
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.
*/
"adb name %p: Caching negative entry for A6 (ttl %u)",
if (result == DNS_R_NCACHENXDOMAIN)
else
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;
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
} 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.
*/
/* 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 void
int bucket;
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)",
else
}
/*
*/
if (result == ISC_R_SUCCESS) {
"adb A6 fetch name %p: caching alias target",
name);
if (FETCH_FIRSTA6(fetch)) {
/*
* Make this name 'pokeable', since
* we've learned that this name is an
* alias.
*/
}
}
goto out;
}
"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.
*/
if (NXDOMAIN_RESULT(result))
else
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
nameservers = NULL;
if (start_at_root) {
adbname);
name = dns_rootname;
goto cleanup;
nameservers = &rdataset;
}
goto cleanup;
}
if (result != ISC_R_SUCCESS)
goto cleanup;
return (result);
}
/* XXXMLG Why doesn't this look a lot like fetch_name_a and fetch_name_a6? */
static isc_result_t
goto cleanup;
}
if (result != ISC_R_SUCCESS)
goto cleanup;
return (result);
}
static isc_result_t
nameservers = NULL;
if (start_at_root) {
adbname);
name = dns_rootname;
goto cleanup;
nameservers = &rdataset;
}
goto cleanup;
}
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.
*/
}
void
{
int bucket;
}
{
int bucket;
if (now == 0)
goto unlock;
}
/*
* We don't know anything about this address.
*/
goto unlock;
}
} else
/*
* Check for avoid bitstring timeout.
*/
entry->avoid_bitstring = 0;
}
return (result);
}
void
int bucket;
if (want_check_exit) {
}
}