summaryrefslogtreecommitdiff
path: root/lib/dns/adb.c
diff options
context:
space:
mode:
Diffstat (limited to 'lib/dns/adb.c')
-rw-r--r--lib/dns/adb.c168
1 files changed, 126 insertions, 42 deletions
diff --git a/lib/dns/adb.c b/lib/dns/adb.c
index 10d51bc44143..c75ea59f751f 100644
--- a/lib/dns/adb.c
+++ b/lib/dns/adb.c
@@ -15,8 +15,6 @@
* PERFORMANCE OF THIS SOFTWARE.
*/
-/* $Id: adb.c,v 1.264 2011/12/05 17:10:51 each Exp $ */
-
/*! \file
*
* \note
@@ -157,7 +155,7 @@ struct dns_adb {
unsigned int *entry_refcnt;
isc_event_t cevent;
- isc_boolean_t cevent_sent;
+ isc_boolean_t cevent_out;
isc_boolean_t shutting_down;
isc_eventlist_t whenshutdown;
isc_event_t growentries;
@@ -201,6 +199,7 @@ struct dns_adbfetch {
unsigned int magic;
dns_fetch_t *fetch;
dns_rdataset_t rdataset;
+ unsigned int depth;
};
/*%
@@ -245,6 +244,7 @@ struct dns_adbentry {
isc_sockaddr_t sockaddr;
isc_stdtime_t expires;
+ isc_stdtime_t lastage;
/*%<
* A nonzero 'expires' field indicates that the entry should
* persist until that time. This allows entries found
@@ -300,8 +300,7 @@ static inline isc_boolean_t dec_entry_refcnt(dns_adb_t *, isc_boolean_t,
static inline void violate_locking_hierarchy(isc_mutex_t *, isc_mutex_t *);
static isc_boolean_t clean_namehooks(dns_adb_t *, dns_adbnamehooklist_t *);
static void clean_target(dns_adb_t *, dns_name_t *);
-static void clean_finds_at_name(dns_adbname_t *, isc_eventtype_t,
- unsigned int);
+static void clean_finds_at_name(dns_adbname_t *, isc_eventtype_t, unsigned int);
static isc_boolean_t check_expire_namehooks(dns_adbname_t *, isc_stdtime_t);
static isc_boolean_t check_expire_entry(dns_adb_t *, dns_adbentry_t **,
isc_stdtime_t);
@@ -309,6 +308,7 @@ static void cancel_fetches_at_name(dns_adbname_t *);
static isc_result_t dbfind_name(dns_adbname_t *, isc_stdtime_t,
dns_rdatatype_t);
static isc_result_t fetch_name(dns_adbname_t *, isc_boolean_t,
+ unsigned int, isc_counter_t *qc,
dns_rdatatype_t);
static inline void check_exit(dns_adb_t *);
static void destroy(dns_adb_t *);
@@ -321,6 +321,9 @@ static inline isc_boolean_t unlink_entry(dns_adb_t *, dns_adbentry_t *);
static isc_boolean_t kill_name(dns_adbname_t **, isc_eventtype_t);
static void water(void *, int);
static void dump_entry(FILE *, dns_adbentry_t *, isc_boolean_t, isc_stdtime_t);
+static void adjustsrtt(dns_adbaddrinfo_t *addr, unsigned int rtt,
+ unsigned int factor, isc_stdtime_t now);
+static void shutdown_task(isc_task_t *task, isc_event_t *ev);
/*
* MUST NOT overlap DNS_ADBFIND_* flags!
@@ -344,7 +347,7 @@ static void dump_entry(FILE *, dns_adbentry_t *, isc_boolean_t, isc_stdtime_t);
* Private flag(s) for entries.
* MUST NOT overlap FCTX_ADDRINFO_xxx and DNS_FETCHOPT_NOEDNS0.
*/
-#define ENTRY_IS_DEAD 0x80000000
+#define ENTRY_IS_DEAD 0x00400000
/*
* To the name, address classes are all that really exist. If it has a
@@ -1498,10 +1501,13 @@ check_exit(dns_adb_t *adb) {
* If there aren't any external references either, we're
* done. Send the control event to initiate shutdown.
*/
- INSIST(!adb->cevent_sent); /* Sanity check. */
+ INSIST(!adb->cevent_out); /* Sanity check. */
+ ISC_EVENT_INIT(&adb->cevent, sizeof(adb->cevent), 0, NULL,
+ DNS_EVENT_ADBCONTROL, shutdown_task, adb,
+ adb, NULL, NULL);
event = &adb->cevent;
isc_task_send(adb->task, &event);
- adb->cevent_sent = ISC_TRUE;
+ adb->cevent_out = ISC_TRUE;
}
}
@@ -1756,6 +1762,7 @@ new_adbentry(dns_adb_t *adb) {
e->flags = 0;
isc_random_get(&r);
e->srtt = (r & 0x1f) + 1;
+ e->lastage = 0;
e->expires = 0;
ISC_LIST_INIT(e->lameinfo);
ISC_LINK_INIT(e, plink);
@@ -2430,10 +2437,9 @@ dns_adb_create(isc_mem_t *mem, dns_view_t *view, isc_timermgr_t *timermgr,
adb->view = view;
adb->taskmgr = taskmgr;
adb->next_cleanbucket = 0;
- ISC_EVENT_INIT(&adb->cevent, sizeof(adb->cevent), 0, NULL,
- DNS_EVENT_ADBCONTROL, shutdown_task, adb,
- adb, NULL, NULL);
- adb->cevent_sent = ISC_FALSE;
+ ISC_EVENT_INIT(&adb->cevent, sizeof(adb->cevent),
+ 0, NULL, 0, NULL, NULL, NULL, NULL, NULL);
+ adb->cevent_out = ISC_FALSE;
adb->shutting_down = ISC_FALSE;
ISC_LIST_INIT(adb->whenshutdown);
@@ -2467,7 +2473,7 @@ dns_adb_create(isc_mem_t *mem, dns_view_t *view, isc_timermgr_t *timermgr,
"intializing table sizes to %u\n",
nbuckets[11]);
adb->nentries = nbuckets[11];
- adb->nnames= nbuckets[11];
+ adb->nnames = nbuckets[11];
}
@@ -2740,9 +2746,28 @@ dns_adb_whenshutdown(dns_adb_t *adb, isc_task_t *task, isc_event_t **eventp) {
UNLOCK(&adb->lock);
}
+static void
+shutdown_stage2(isc_task_t *task, isc_event_t *event) {
+ dns_adb_t *adb;
+
+ UNUSED(task);
+
+ adb = event->ev_arg;
+ INSIST(DNS_ADB_VALID(adb));
+
+ LOCK(&adb->lock);
+ INSIST(adb->shutting_down);
+ adb->cevent_out = ISC_FALSE;
+ (void)shutdown_names(adb);
+ (void)shutdown_entries(adb);
+ if (dec_adb_irefcnt(adb))
+ check_exit(adb);
+ UNLOCK(&adb->lock);
+}
+
void
dns_adb_shutdown(dns_adb_t *adb) {
- isc_boolean_t need_check_exit;
+ isc_event_t *event;
/*
* Shutdown 'adb'.
@@ -2753,11 +2778,16 @@ dns_adb_shutdown(dns_adb_t *adb) {
if (!adb->shutting_down) {
adb->shutting_down = ISC_TRUE;
isc_mem_setwater(adb->mctx, water, adb, 0, 0);
- need_check_exit = shutdown_names(adb);
- if (!need_check_exit)
- need_check_exit = shutdown_entries(adb);
- if (need_check_exit)
- check_exit(adb);
+ /*
+ * Isolate shutdown_names and shutdown_entries calls.
+ */
+ inc_adb_irefcnt(adb);
+ ISC_EVENT_INIT(&adb->cevent, sizeof(adb->cevent), 0, NULL,
+ DNS_EVENT_ADBCONTROL, shutdown_stage2, adb,
+ adb, NULL, NULL);
+ adb->cevent_out = ISC_TRUE;
+ event = &adb->cevent;
+ isc_task_send(adb->task, &event);
}
UNLOCK(&adb->lock);
@@ -2770,6 +2800,19 @@ dns_adb_createfind(dns_adb_t *adb, isc_task_t *task, isc_taskaction_t action,
isc_stdtime_t now, dns_name_t *target,
in_port_t port, dns_adbfind_t **findp)
{
+ return (dns_adb_createfind2(adb, task, action, arg, name,
+ qname, qtype, options, now,
+ target, port, 0, NULL, findp));
+}
+
+isc_result_t
+dns_adb_createfind2(dns_adb_t *adb, isc_task_t *task, isc_taskaction_t action,
+ void *arg, dns_name_t *name, dns_name_t *qname,
+ dns_rdatatype_t qtype, unsigned int options,
+ isc_stdtime_t now, dns_name_t *target,
+ in_port_t port, unsigned int depth, isc_counter_t *qc,
+ dns_adbfind_t **findp)
+{
dns_adbfind_t *find;
dns_adbname_t *adbname;
int bucket;
@@ -3000,7 +3043,7 @@ dns_adb_createfind(dns_adb_t *adb, isc_task_t *task, isc_taskaction_t action,
* Start V4.
*/
if (WANT_INET(wanted_fetches) &&
- fetch_name(adbname, start_at_zone,
+ fetch_name(adbname, start_at_zone, depth, qc,
dns_rdatatype_a) == ISC_R_SUCCESS) {
DP(DEF_LEVEL,
"dns_adb_createfind: started A fetch for name %p",
@@ -3011,7 +3054,7 @@ dns_adb_createfind(dns_adb_t *adb, isc_task_t *task, isc_taskaction_t action,
* Start V6.
*/
if (WANT_INET6(wanted_fetches) &&
- fetch_name(adbname, start_at_zone,
+ fetch_name(adbname, start_at_zone, depth, qc,
dns_rdatatype_aaaa) == ISC_R_SUCCESS) {
DP(DEF_LEVEL,
"dns_adb_createfind: "
@@ -3754,6 +3797,12 @@ fetch_callback(isc_task_t *task, isc_event_t *ev) {
DP(DEF_LEVEL, "adb: fetch of '%s' %s failed: %s",
buf, address_type == DNS_ADBFIND_INET ? "A" : "AAAA",
dns_result_totext(dev->result));
+ /*
+ * Don't record a failure unless this is the initial
+ * fetch of a chain.
+ */
+ if (fetch->depth > 1)
+ goto out;
/* XXXMLG Don't pound on bad servers. */
if (address_type == DNS_ADBFIND_INET) {
name->expire_v4 = ISC_MIN(name->expire_v4, now + 300);
@@ -3791,9 +3840,8 @@ fetch_callback(isc_task_t *task, isc_event_t *ev) {
}
static isc_result_t
-fetch_name(dns_adbname_t *adbname,
- isc_boolean_t start_at_zone,
- dns_rdatatype_t type)
+fetch_name(dns_adbname_t *adbname, isc_boolean_t start_at_zone,
+ unsigned int depth, isc_counter_t *qc, dns_rdatatype_t type)
{
isc_result_t result;
dns_adbfetch_t *fetch = NULL;
@@ -3838,12 +3886,14 @@ fetch_name(dns_adbname_t *adbname,
result = ISC_R_NOMEMORY;
goto cleanup;
}
-
- result = dns_resolver_createfetch(adb->view->resolver, &adbname->name,
- type, name, nameservers, NULL,
- options, adb->task, fetch_callback,
- adbname, &fetch->rdataset, NULL,
- &fetch->fetch);
+ fetch->depth = depth;
+
+ result = dns_resolver_createfetch3(adb->view->resolver, &adbname->name,
+ type, name, nameservers, NULL,
+ NULL, 0, options, depth, qc,
+ adb->task, fetch_callback, adbname,
+ &fetch->rdataset, NULL,
+ &fetch->fetch);
if (result != ISC_R_SUCCESS)
goto cleanup;
@@ -3912,8 +3962,7 @@ dns_adb_adjustsrtt(dns_adb_t *adb, dns_adbaddrinfo_t *addr,
unsigned int rtt, unsigned int factor)
{
int bucket;
- unsigned int new_srtt;
- isc_stdtime_t now;
+ isc_stdtime_t now = 0;
REQUIRE(DNS_ADB_VALID(adb));
REQUIRE(DNS_ADBADDRINFO_VALID(addr));
@@ -3922,21 +3971,53 @@ dns_adb_adjustsrtt(dns_adb_t *adb, dns_adbaddrinfo_t *addr,
bucket = addr->entry->lock_bucket;
LOCK(&adb->entrylocks[bucket]);
- if (factor == DNS_ADB_RTTADJAGE)
- new_srtt = addr->entry->srtt * 98 / 100;
- else
+ if (addr->entry->expires == 0 || factor == DNS_ADB_RTTADJAGE)
+ isc_stdtime_get(&now);
+ adjustsrtt(addr, rtt, factor, now);
+
+ UNLOCK(&adb->entrylocks[bucket]);
+}
+
+void
+dns_adb_agesrtt(dns_adb_t *adb, dns_adbaddrinfo_t *addr, isc_stdtime_t now) {
+ int bucket;
+
+ REQUIRE(DNS_ADB_VALID(adb));
+ REQUIRE(DNS_ADBADDRINFO_VALID(addr));
+
+ bucket = addr->entry->lock_bucket;
+ LOCK(&adb->entrylocks[bucket]);
+
+ adjustsrtt(addr, 0, DNS_ADB_RTTADJAGE, now);
+
+ UNLOCK(&adb->entrylocks[bucket]);
+}
+
+static void
+adjustsrtt(dns_adbaddrinfo_t *addr, unsigned int rtt, unsigned int factor,
+ isc_stdtime_t now)
+{
+ isc_uint64_t new_srtt;
+
+ if (factor == DNS_ADB_RTTADJAGE) {
+ if (addr->entry->lastage != now) {
+ new_srtt = addr->entry->srtt;
+ new_srtt <<= 9;
+ new_srtt -= addr->entry->srtt;
+ new_srtt >>= 9;
+ addr->entry->lastage = now;
+ } else
+ new_srtt = addr->entry->srtt;
+ } else
new_srtt = (addr->entry->srtt / 10 * factor)
+ (rtt / 10 * (10 - factor));
- addr->entry->srtt = new_srtt;
- addr->srtt = new_srtt;
+ new_srtt &= 0xffffffff;
+ addr->entry->srtt = (unsigned int) new_srtt;
+ addr->srtt = (unsigned int) new_srtt;
- if (addr->entry->expires == 0) {
- isc_stdtime_get(&now);
+ if (addr->entry->expires == 0)
addr->entry->expires = now + ADB_ENTRY_WINDOW;
- }
-
- UNLOCK(&adb->entrylocks[bucket]);
}
void
@@ -3949,6 +4030,9 @@ dns_adb_changeflags(dns_adb_t *adb, dns_adbaddrinfo_t *addr,
REQUIRE(DNS_ADB_VALID(adb));
REQUIRE(DNS_ADBADDRINFO_VALID(addr));
+ REQUIRE((bits & ENTRY_IS_DEAD) == 0);
+ REQUIRE((mask & ENTRY_IS_DEAD) == 0);
+
bucket = addr->entry->lock_bucket;
LOCK(&adb->entrylocks[bucket]);