adb.c revision 0c27b3fe77ac1d5094ba3521e8142d9e7973133f
/*
* Copyright (C) 1999-2016 Internet Systems Consortium, Inc. ("ISC")
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
/*! \file
*
* \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.
*
*/
#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>
/*!
* 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.
*/
/*%
* 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;
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 nnames;
unsigned int namescnt;
unsigned int *name_refcnt;
/*!
* Bucketized locks and lists for entries.
*
* XXXRTH Have a per-bucket structure that contains all of these?
*/
unsigned int nentries;
unsigned int entriescnt;
unsigned int *entry_refcnt;
double atr_low;
double atr_high;
double atr_discount;
};
/*
* 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;
unsigned int depth;
};
/*%
* 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 nh;
unsigned int flags;
unsigned int srtt;
unsigned int completed;
unsigned int timeouts;
unsigned char plain;
unsigned char plainto;
unsigned char edns;
unsigned char to4096; /* Our max. */
double atr;
/*
* Ethernet 1500 - IP(20) - IP6(40) - UDP(8) = 1432.
*/
unsigned char to1432; /* Ethernet */
unsigned char to1232; /* IPv6 nofrag */
unsigned char to512; /* plain DNS */
unsigned char * cookie;
/*%<
* 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 *);
static void cancel_fetches_at_name(dns_adbname_t *);
unsigned int, isc_counter_t *qc,
static inline void check_exit(dns_adb_t *);
static void water(void *, int);
/*
* 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
/*
* Private flag(s) for entries.
* MUST NOT overlap FCTX_ADDRINFO_xxx and DNS_FETCHOPT_NOEDNS0.
*/
#define ENTRY_IS_DEAD 0x00400000
/*
* 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
}
/*%
* Increment resolver-related statistics counters.
*/
static inline void
}
/*%
* Set adb-related statistics counters.
*/
static inline void
}
static inline void
}
static inline void
}
static inline dns_ttl_t
if (ttl < ADB_CACHE_MINIMUM)
if (ttl > ADB_CACHE_MAXIMUM)
return (ttl);
}
/*
* Hashing is most efficient if the number of buckets is prime.
* The sequence below is the closest previous primes to 2^n and
* 1.5 * 2^n, for values of n from 10 to 28. (The tables will
* no longer grow beyond 2^28 entries.)
*/
8191, 12281, 16381, 24571, 32749,
49193, 65521, 98299, 131071, 199603,
262139, 393209, 524287, 768431, 1048573,
1572853, 2097143, 3145721, 4194301,
6291449, 8388593, 12582893, 16777213,
25165813, 33554393, 50331599, 67108859,
100663291, 134217689, 201326557,
268535431, 0 };
static void
dns_adbentry_t *e;
unsigned int *newentry_refcnt = NULL;
unsigned int i, n, bucket;
isc_event_free(&ev);
if (result != ISC_R_SUCCESS)
goto check_exit;
i = 0;
i++;
if (nbuckets[i] != 0)
n = nbuckets[i];
else
goto done;
/*
* Are we shutting down?
*/
goto cleanup;
/*
* Grab all the resources we need.
*/
newentry_refcnt == NULL)
goto cleanup;
/*
* Initialise the new resources.
*/
if (result != ISC_R_SUCCESS)
goto cleanup;
for (i = 0; i < n; i++) {
ISC_LIST_INIT(newentries[i]);
newentry_sd[i] = ISC_FALSE;
newentry_refcnt[i] = 0;
}
/*
* Move entries to new arrays.
*/
while (e != NULL) {
e->lock_bucket = bucket;
adb->entry_refcnt[i]--;
}
while (e != NULL) {
e->lock_bucket = bucket;
adb->entry_refcnt[i]--;
}
}
/*
* Cleanup old resources.
*/
/*
* Install new resources.
*/
/*
* Only on success do we set adb->growentries_sent to ISC_FALSE.
* This will prevent us being continuously being called on error.
*/
goto done;
if (newentries != NULL)
sizeof(*newentries) * n);
if (newdeadentries != NULL)
sizeof(*newdeadentries) * n);
if (newentrylocks != NULL)
sizeof(*newentrylocks) * n);
if (newentry_sd != NULL)
sizeof(*newentry_sd) * n);
if (newentry_refcnt != NULL)
sizeof(*newentry_refcnt) * n);
done:
if (dec_adb_irefcnt(adb))
}
static void
unsigned int *newname_refcnt = NULL;
unsigned int i, n, bucket;
isc_event_free(&ev);
if (result != ISC_R_SUCCESS)
goto check_exit;
i = 0;
i++;
if (nbuckets[i] != 0)
n = nbuckets[i];
else
goto done;
/*
* Are we shutting down?
*/
goto cleanup;
/*
* Grab all the resources we need.
*/
newname_refcnt == NULL)
goto cleanup;
/*
* Initialise the new resources.
*/
if (result != ISC_R_SUCCESS)
goto cleanup;
for (i = 0; i < n; i++) {
ISC_LIST_INIT(newnames[i]);
newname_sd[i] = ISC_FALSE;
newname_refcnt[i] = 0;
}
/*
* Move names to new arrays.
*/
adb->name_refcnt[i]--;
newname_refcnt[bucket]++;
}
adb->name_refcnt[i]--;
newname_refcnt[bucket]++;
}
}
/*
* Cleanup old resources.
*/
/*
* Install new resources.
*/
/*
* Only on success do we set adb->grownames_sent to ISC_FALSE.
* This will prevent us being continuously being called on error.
*/
goto done;
if (newdeadnames != NULL)
if (newnamelocks != NULL)
if (newname_sd != NULL)
if (newname_refcnt != NULL)
sizeof(*newname_refcnt) * n);
done:
if (dec_adb_irefcnt(adb))
}
/*
* 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;
}
now);
if (foundentry == NULL) {
goto fail;
}
} else {
break;
foundentry->refcnt++;
foundentry->nh++;
} 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
int bucket;
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;
else
return (result);
}
/*
* Requires the entry's bucket be locked.
*/
static inline void
int i;
dns_adbentry_t *e;
for (i = 0; i < 2; i++) {
if (e == NULL)
break;
if (e->refcnt == 0) {
unlink_entry(adb, e);
free_adbentry(adb, &e);
continue;
}
e->flags |= ENTRY_IS_DEAD;
}
}
}
/*
* Requires the entry's bucket be locked.
*/
static inline isc_boolean_t
int bucket;
else
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
unsigned 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
unsigned 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)
}
}
/*
* 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));
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->nh = 0;
e->flags = 0;
e->udpsize = 0;
e->edns = 0;
e->completed = 0;
e->timeouts = 0;
e->plain = 0;
e->plainto = 0;
e->to4096 = 0;
e->to1432 = 0;
e->to1232 = 0;
e->to512 = 0;
e->cookielen = 0;
isc_random_get(&r);
e->lastage = 0;
e->expires = 0;
e->active = 0;
e->mode = 0;
e->atr = 0.0;
ISC_LIST_INIT(e->lameinfo);
ISC_LINK_INIT(e, plink);
adb->entriescnt++;
{
}
return (e);
}
static inline void
dns_adbentry_t *e;
e = *entry;
e->magic = 0;
}
adb->entriescnt--;
}
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;
dns_rdataset_init(&f->rdataset);
f->magic = DNS_ADBFETCH_MAGIC;
return (f);
}
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);
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) {
}
/* Search the list, while cleaning up expired entries. */
entry = entry_next) {
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
char msgbuf[2048];
char addrbuf[ISC_NETADDR_FORMATSIZE];
ISC_LOG_INFO, "adb: quota %s (%d/%d): %s",
}
static void
{
int bucket;
{
goto nextv4;
}
if (!FIND_RETURNLAME(find)
goto nextv4;
}
goto out;
}
/*
* Found a valid entry. Add it to the find's list.
*/
}
}
{
goto nextv6;
}
if (!FIND_RETURNLAME(find)
goto nextv6;
}
goto out;
}
/*
* Found a valid entry. Add it to the find's list.
*/
}
}
out:
if (bucket != DNS_ADB_INVALIDBUCKET)
}
static void
isc_event_free(&ev);
/*
* Wait for lock around check_exit() call to be released.
*/
}
/*
* 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++;
victims++;
goto next;
}
if (!NAME_FETCH(victim) &&
victims++;
}
next:
if (!overmem)
break;
}
}
/*
* 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);
}
static void
}
/*
* Public functions.
*/
{
unsigned 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;
adb->entriescnt = 0;
if (result != ISC_R_SUCCESS) {
"intializing table sizes to %u\n",
nbuckets[11]);
}
if (result != ISC_R_SUCCESS)
goto fail0b;
if (result != ISC_R_SUCCESS)
goto fail0c;
if (result != ISC_R_SUCCESS)
goto fail0d;
if (result != ISC_R_SUCCESS)
goto fail0e;
if (result != ISC_R_SUCCESS)
goto fail0f;
if (result != ISC_R_SUCCESS)
goto fail0g;
do { \
result = ISC_R_NOMEMORY; \
goto fail1; \
}\
} while (0)
do { \
result = ISC_R_NOMEMORY; \
goto fail1; \
}\
} while (0)
/*
* Initialize the bucket locks for names and elements.
* May as well initialize the list heads, too.
*/
if (result != ISC_R_SUCCESS)
goto fail1;
adb->name_refcnt[i] = 0;
}
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 an internal task.
*/
if (result != ISC_R_SUCCESS)
goto fail3;
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
if (need_exit_check) {
}
}
void
/*
* Send '*eventp' to 'task' when 'adb' has shutdown.
*/
/*
* We're already shutdown. Send the event.
*/
} else {
}
}
static void
(void)shutdown_names(adb);
(void)shutdown_entries(adb);
if (dec_adb_irefcnt(adb))
}
void
/*
* Shutdown 'adb'.
*/
if (!adb->shutting_down) {
/*
* Isolate shutdown_names and shutdown_entries calls.
*/
}
}
{
}
{
int bucket;
unsigned int wanted_addresses;
unsigned int wanted_fetches;
unsigned int query_pending;
char namebuf[DNS_NAME_FORMATSIZE];
}
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)) {
}
else
namebuf[0] = 0;
/*
* 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))
} else {
/* Move this name forward in the LRU list */
}
/*
* Expire old entries, etc.
*/
/*
* Do we know that the name is an alias?
*/
/*
* Yes, it is.
*/
"dns_adb_createfind: name %s (%p) is an alias (cached)",
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 %s (%p) in db",
goto v6;
}
/*
* Did we get a CNAME or DNAME?
*/
if (result == DNS_R_ALIAS) {
"dns_adb_createfind: name %s (%p) is an alias",
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 %s (%p)",
goto fetch;
}
/*
* Did we get a CNAME or DNAME?
*/
if (result == DNS_R_ALIAS) {
"dns_adb_createfind: name %s (%p) is an alias",
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) {
"started A fetch for name %s (%p)",
}
/*
* Start V6.
*/
if (WANT_INET6(wanted_fetches) &&
dns_rdatatype_aaaa) == ISC_R_SUCCESS) {
"started AAAA fetch for name %s (%p)",
}
}
/*
* 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
unsigned 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.
*/
}
static void
return;
}
static void
unsigned int i;
fprintf(f, ";\n; Address database dump\n;\n");
fprintf(f, "; [edns success/4096 timeout/1432 timeout/1232 timeout/"
"512 timeout]\n");
if (debug)
fprintf(f, "; addr %p, erefcnt %u, irefcnt %u, finds out %u\n",
/*
* Dump the names
*/
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");
}
}
/*
* Unlock everything
*/
}
static void
{
char addrbuf[ISC_NETADDR_FORMATSIZE];
char typebuf[DNS_RDATATYPE_FORMATSIZE];
if (debug)
fprintf(f, ";\t%s [srtt %u] [flags %08x] [edns %u/%u/%u/%u/%u] "
unsigned int i;
fprintf(f, " [cookie=");
fprintf(f, "]");
}
fprintf(f, " [atr %0.2f] [quota %d]",
}
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 -> { 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
/*
* We need to specify whether to search static-stub zones (if
* configured) depending on whether this is a "start at zone" lookup,
* i.e., whether it's a "bailiwick" glue. If it's bailiwick (in which
* case NAME_STARTATZONE is set) we need to stop the search at any
* matching static-stub zone without looking into the cache to honor
* the configuration on which server we should send queries to.
*/
/* 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)
} else
/*
* 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];
/*
* Don't record a failure unless this is the initial
* fetch of a chain.
*/
goto out;
/* 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;
isc_stdtime_t now = 0;
}
void
int bucket;
}
static void
{
if (factor == DNS_ADB_RTTADJAGE) {
new_srtt <<= 9;
new_srtt >>= 9;
} else
} 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.
*/
}
/*
* (10000 / ((10 + n) / 10)^(3/2)) for n in 0..99.
* These will be used to make quota adjustments.
*/
static int quota_adj[] = {
10000, 8668, 7607, 6747, 6037, 5443, 4941, 4512, 4141,
3818, 3536, 3286, 3065, 2867, 2690, 2530, 2385, 2254,
2134, 2025, 1925, 1832, 1747, 1668, 1595, 1527, 1464,
1405, 1350, 1298, 1250, 1205, 1162, 1121, 1083, 1048,
1014, 981, 922, 894, 868, 843, 820, 797, 775, 755,
735, 716, 698, 680, 664, 648, 632, 618, 603, 590, 577,
564, 552, 540, 529, 518, 507, 497, 487, 477, 468, 459,
450, 442, 434, 426, 418, 411, 404, 397, 390, 383, 377,
370, 364, 358, 353, 347, 342, 336, 331, 326, 321, 316,
312, 307, 303, 298, 294, 290, 286, 282, 278
};
/*
* Caller must hold adbentry lock
*/
static void
{
double tr;
return;
if (timeout)
return;
/*
* Calculate an exponential rolling average of the timeout ratio
*
* XXX: Integer arithmetic might be better than floating point
*/
}
/* Ensure we don't drop to zero */
}
#define EDNSTOS 3U
int bucket;
} else {
/*
* Increment plain so we don't get stuck.
*/
}
}
}
return (noedns);
}
void
int bucket;
}
}
void
int bucket;
/*
* If we have not had a successful query then clear all
* edns timeout information.
*/
} else {
}
}
}
void
int bucket;
if (size <= 512U) {
}
} else if (size <= 1232U) {
}
} else if (size <= 1432U) {
}
} else {
}
}
}
void
int bucket;
if (size < 512U)
size = 512U;
}
}
unsigned int
int bucket;
unsigned int size;
return (size);
}
unsigned int
}
unsigned int
int bucket;
unsigned int size;
size = 512;
size = 1232;
size = 1432;
else
size = 4096;
/*
* Don't shrink probe size below what we have seen due to multiple
* lookups.
*/
if (lookups > 0 &&
return (size);
}
void
{
int bucket;
}
}
}
{
int bucket;
{
} else
len = 0;
return (len);
}
{
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.
*/
#ifdef DUMP_ADB_AFTER_CLEANING
#endif
}
void
int bucket;
}
}
}
void
unsigned int i;
{
}
}
}
}
static void
/*
* We're going to change the way to handle overmem condition: use
* isc_mem_isovermem() instead of storing the state via this callback,
* since the latter way tends to cause race conditions.
* To minimize the change, and in case we re-enable the callback
* approach, however, keep this function at the moment.
*/
}
void
else
}
void
{
}
return (block);
}
void
int bucket;
}
void
int bucket;
}