+2852. [bug] Handle broken DNSSEC trust chains better. [RT #15619]
+
2851. [doc] nslookup.1, removed <informalexample> from the docbook
source as it produced bad nroff. [RT #21007]
* PERFORMANCE OF THIS SOFTWARE.
*/
-/* $Id: server.c,v 1.520.12.11 2009/12/24 00:17:47 each Exp $ */
+/* $Id: server.c,v 1.520.12.12 2010/02/25 05:35:09 marka Exp $ */
/*! \file */
}
if (dctx->cache != NULL) {
dns_adb_dump(dctx->view->view->adb, dctx->fp);
+ dns_resolver_printbadcache(dctx->view->view->resolver,
+ dctx->fp);
dns_db_detach(&dctx->cache);
}
if (dctx->dumpzones) {
- PERFORMANCE OF THIS SOFTWARE.
-->
-<!-- File: $Id: Bv9ARM-book.xml,v 1.380.14.24 2010/01/23 23:47:52 tbox Exp $ -->
+<!-- File: $Id: Bv9ARM-book.xml,v 1.380.14.25 2010/02/25 05:35:09 marka Exp $ -->
<book xmlns:xi="http://www.w3.org/2001/XInclude">
<title>BIND 9 Administrator Reference Manual</title>
<literal>1800</literal> (30 minutes).
</para>
+ <para>
+ Lame-ttl also controls the amount of time DNSSEC
+ validation failures are cached. There is a minimum
+ of 30 seconds applied to bad cache entries if the
+ lame-ttl is set to less than 30 seconds.
+ </para>
+
</listitem>
</varlistentry>
* PERFORMANCE OF THIS SOFTWARE.
*/
-/* $Id: rdataset.h,v 1.65.50.2 2009/01/18 23:47:41 tbox Exp $ */
+/* $Id: rdataset.h,v 1.65.50.3 2010/02/25 05:35:11 marka Exp $ */
#ifndef DNS_RDATASET_H
#define DNS_RDATASET_H 1
dns_rdataset_t *rdataset,
dns_rdatasetadditional_t type,
dns_rdatatype_t qtype);
+ void (*settrust)(dns_rdataset_t *rdataset,
+ dns_trust_t trust);
+ void (*expire)(dns_rdataset_t *rdataset);
} dns_rdatasetmethods_t;
#define DNS_RDATASET_MAGIC ISC_MAGIC('D','N','S','R')
* information for 'rdataset.'
*/
+void
+dns_rdataset_settrust(dns_rdataset_t *rdataset, dns_trust_t trust);
+/*%<
+ * Set the trust of the 'rdataset' to trust in any in the backing database.
+ * The local trust level of 'rdataset' is also set.
+ */
+
+void
+dns_rdataset_expire(dns_rdataset_t *rdataset);
+/*%<
+ * Mark the rdataset to be expired in the backing database.
+ */
+
ISC_LANG_ENDDECLS
#endif /* DNS_RDATASET_H */
* PERFORMANCE OF THIS SOFTWARE.
*/
-/* $Id: resolver.h,v 1.60.56.3 2009/01/29 22:40:35 jinmei Exp $ */
+/* $Id: resolver.h,v 1.60.56.4 2010/02/25 05:35:11 marka Exp $ */
#ifndef DNS_RESOLVER_H
#define DNS_RESOLVER_H 1
unsigned int
dns_resolver_getoptions(dns_resolver_t *resolver);
+void
+dns_resolver_addbadcache(dns_resolver_t *resolver, dns_name_t *name,
+ dns_rdatatype_t type, isc_time_t *expire);
+/*%<
+ * Add a entry to the bad cache for <name,type> that will expire at 'expire'.
+ *
+ * Requires:
+ * \li resolver to be valid.
+ * \li name to be valid.
+ */
+
+isc_boolean_t
+dns_resolver_getbadcache(dns_resolver_t *resolver, dns_name_t *name,
+ dns_rdatatype_t type, isc_time_t *now);
+/*%<
+ * Check to see if there is a unexpired entry in the bad cache for
+ * <name,type>.
+ *
+ * Requires:
+ * \li resolver to be valid.
+ * \li name to be valid.
+ */
+
+void
+dns_resolver_flushbadcache(dns_resolver_t *resolver, dns_name_t *name);
+/*%<
+ * Flush the bad cache of all entries at 'name' if 'name' is non NULL.
+ * Flush the entire bad cache if 'name' is NULL.
+ *
+ * Requires:
+ * \li resolver to be valid.
+ */
+
+void
+dns_resolver_printbadcache(dns_resolver_t *resolver, FILE *fp);
+/*%
+ * Print out the contents of the bad cache to 'fp'.
+ *
+ * Requires:
+ * \li resolver to be valid.
+ */
+
ISC_LANG_ENDDECLS
#endif /* DNS_RESOLVER_H */
* PERFORMANCE OF THIS SOFTWARE.
*/
-/* $Id: result.h,v 1.116 2008/09/25 04:02:39 tbox Exp $ */
+/* $Id: result.h,v 1.116.48.1 2010/02/25 05:35:11 marka Exp $ */
#ifndef DNS_RESULT_H
#define DNS_RESULT_H 1
#define DNS_R_MXISADDRESS (ISC_RESULTCLASS_DNS + 102)
#define DNS_R_DUPLICATE (ISC_RESULTCLASS_DNS + 103)
#define DNS_R_INVALIDNSEC3 (ISC_RESULTCLASS_DNS + 104)
+#define DNS_R_NOTMASTER (ISC_RESULTCLASS_DNS + 105)
+#define DNS_R_BROKENCHAIN (ISC_RESULTCLASS_DNS + 106)
-#define DNS_R_NRESULTS 105 /*%< Number of results */
+#define DNS_R_NRESULTS 107 /*%< Number of results */
/*
* DNS wire format rcodes.
* PERFORMANCE OF THIS SOFTWARE.
*/
-/* $Id: validator.h,v 1.41.48.3 2009/01/18 23:25:17 marka Exp $ */
+/* $Id: validator.h,v 1.41.48.4 2010/02/25 05:35:11 marka Exp $ */
#ifndef DNS_VALIDATOR_H
#define DNS_VALIDATOR_H 1
isc_boolean_t mustbesecure;
unsigned int dlvlabels;
unsigned int depth;
+ unsigned int authcount;
+ unsigned int authfail;
};
/*%
* PERFORMANCE OF THIS SOFTWARE.
*/
-/* $Id: ncache.c,v 1.43 2008/09/25 04:02:38 tbox Exp $ */
+/* $Id: ncache.c,v 1.43.48.1 2010/02/25 05:35:09 marka Exp $ */
/*! \file */
NULL,
NULL,
NULL,
+ NULL,
+ NULL,
NULL
};
* PERFORMANCE OF THIS SOFTWARE.
*/
-/* $Id: rbtdb.c,v 1.270.12.16 2009/12/30 08:34:30 jinmei Exp $ */
+/* $Id: rbtdb.c,v 1.270.12.17 2010/02/25 05:35:09 marka Exp $ */
/*! \file */
static isc_result_t resign_insert(dns_rbtdb_t *rbtdb, int idx,
rdatasetheader_t *newheader);
static void prune_tree(isc_task_t *task, isc_event_t *event);
+static void rdataset_settrust(dns_rdataset_t *rdataset, dns_trust_t trust);
+static void rdataset_expire(dns_rdataset_t *rdataset);
static dns_rdatasetmethods_t rdataset_methods = {
rdataset_disassociate,
rdataset_getclosest,
rdataset_getadditional,
rdataset_setadditional,
- rdataset_putadditional
+ rdataset_putadditional,
+ rdataset_settrust,
+ rdataset_expire
};
static void rdatasetiter_destroy(dns_rdatasetiter_t **iteratorp);
return (ISC_R_SUCCESS);
}
+static void
+rdataset_settrust(dns_rdataset_t *rdataset, dns_trust_t trust) {
+ rdatasetheader_t *header = rdataset->private3;
+
+ header--;
+ header->trust = rdataset->trust = trust;
+}
+
+static void
+rdataset_expire(dns_rdataset_t *rdataset) {
+ dns_rbtdb_t *rbtdb = rdataset->private1;
+ dns_rbtnode_t *rbtnode = rdataset->private2;
+ rdatasetheader_t *header = rdataset->private3;
+
+ header--;
+ NODE_LOCK(&rbtdb->node_locks[rbtnode->locknum].lock,
+ isc_rwlocktype_write);
+ expire_header(rbtdb, header, ISC_FALSE);
+ NODE_UNLOCK(&rbtdb->node_locks[rbtnode->locknum].lock,
+ isc_rwlocktype_write);
+}
+
/*
* Rdataset Iterator Methods
*/
* PERFORMANCE OF THIS SOFTWARE.
*/
-/* $Id: rdatalist.c,v 1.36 2008/09/24 02:46:22 marka Exp $ */
+/* $Id: rdatalist.c,v 1.36.50.1 2010/02/25 05:35:10 marka Exp $ */
/*! \file */
isc__rdatalist_getclosest,
NULL,
NULL,
+ NULL,
+ NULL,
NULL
};
* PERFORMANCE OF THIS SOFTWARE.
*/
-/* $Id: rdataset.c,v 1.82.50.2 2009/01/18 23:47:40 tbox Exp $ */
+/* $Id: rdataset.c,v 1.82.50.3 2010/02/25 05:35:10 marka Exp $ */
/*! \file */
NULL,
NULL,
NULL,
+ NULL,
+ NULL,
NULL
};
return (ISC_R_FAILURE);
}
+void
+dns_rdataset_settrust(dns_rdataset_t *rdataset, dns_trust_t trust) {
+ REQUIRE(DNS_RDATASET_VALID(rdataset));
+ REQUIRE(rdataset->methods != NULL);
+
+ if (rdataset->methods->settrust != NULL)
+ (rdataset->methods->settrust)(rdataset, trust);
+ else
+ rdataset->trust = trust;
+}
+
+void
+dns_rdataset_expire(dns_rdataset_t *rdataset) {
+ REQUIRE(DNS_RDATASET_VALID(rdataset));
+ REQUIRE(rdataset->methods != NULL);
+
+ if (rdataset->methods->expire != NULL)
+ (rdataset->methods->expire)(rdataset);
+}
* PERFORMANCE OF THIS SOFTWARE.
*/
-/* $Id: rdataslab.c,v 1.48.50.2 2009/01/18 23:47:40 tbox Exp $ */
+/* $Id: rdataslab.c,v 1.48.50.3 2010/02/25 05:35:10 marka Exp $ */
/*! \file */
NULL,
NULL,
NULL,
+ NULL,
+ NULL,
NULL
};
* PERFORMANCE OF THIS SOFTWARE.
*/
-/* $Id: resolver.c,v 1.384.14.20 2010/01/07 23:47:36 tbox Exp $ */
+/* $Id: resolver.c,v 1.384.14.21 2010/02/25 05:35:10 marka Exp $ */
/*! \file */
ISC_LINK(struct alternate) link;
} alternate_t;
+typedef struct dns_badcache dns_badcache_t;
+struct dns_badcache {
+ dns_badcache_t * next;
+ dns_rdatatype_t type;
+ isc_time_t expire;
+ unsigned int hashval;
+ dns_name_t name;
+};
+#define DNS_BADCACHE_SIZE 1021
+#define DNS_BADCACHE_TTL(fctx) \
+ (((fctx)->res->lame_ttl > 30 ) ? (fctx)->res->lame_ttl : 30)
+
struct dns_resolver {
/* Unlocked. */
unsigned int magic;
isc_boolean_t priming;
unsigned int spillat; /* clients-per-query */
unsigned int nextdisp;
+
+ /* Bad cache. */
+ dns_badcache_t ** badcache;
+ unsigned int badcount;
+ unsigned int badhash;
+ unsigned int badsweep;
+
/* Locked by primelock. */
dns_fetch_t * primefetch;
/* Locked by nlock. */
static isc_result_t resquery_send(resquery_t *query);
static void resquery_response(isc_task_t *task, isc_event_t *event);
static void resquery_connected(isc_task_t *task, isc_event_t *event);
-static void fctx_try(fetchctx_t *fctx, isc_boolean_t retrying);
+static void fctx_try(fetchctx_t *fctx, isc_boolean_t retrying,
+ isc_boolean_t badcache);
static isc_boolean_t fctx_destroy(fetchctx_t *fctx);
static isc_result_t ncache_adderesult(dns_message_t *message,
dns_db_t *cache, dns_dbnode_t *node,
if (result != ISC_R_SUCCESS)
fctx_done(fctx, result, __LINE__);
else
- fctx_try(fctx, ISC_TRUE);
+ fctx_try(fctx, ISC_TRUE, ISC_FALSE);
}
}
if (result != ISC_R_SUCCESS)
fctx_done(fctx, result, __LINE__);
else
- fctx_try(fctx, ISC_TRUE);
+ fctx_try(fctx, ISC_TRUE, ISC_FALSE);
}
}
dns_adb_destroyfind(&find);
if (want_try)
- fctx_try(fctx, ISC_TRUE);
+ fctx_try(fctx, ISC_TRUE, ISC_FALSE);
else if (want_done)
fctx_done(fctx, ISC_R_FAILURE, __LINE__);
else if (bucket_empty)
}
static isc_result_t
-fctx_getaddresses(fetchctx_t *fctx) {
+fctx_getaddresses(fetchctx_t *fctx, isc_boolean_t badcache) {
dns_rdata_t rdata = DNS_RDATA_INIT;
isc_result_t result;
dns_resolver_t *res;
*/
result = DNS_R_WAIT;
} else {
+ isc_time_t expire;
+ isc_interval_t i;
/*
* We've lost completely. We don't know any
* addresses, and the ADB has told us it can't get
* them.
*/
FCTXTRACE("no addresses");
+ isc_interval_set(&i, DNS_BADCACHE_TTL(fctx), 0);
+ result = isc_time_nowplusinterval(&expire, &i);
+ if (badcache &&
+ (fctx->type == dns_rdatatype_dnskey ||
+ fctx->type == dns_rdatatype_dlv ||
+ fctx->type == dns_rdatatype_ds) &&
+ result == ISC_R_SUCCESS)
+ dns_resolver_addbadcache(fctx->res,
+ &fctx->name,
+ fctx->type, &expire);
result = ISC_R_FAILURE;
}
} else {
}
static void
-fctx_try(fetchctx_t *fctx, isc_boolean_t retrying) {
+fctx_try(fetchctx_t *fctx, isc_boolean_t retrying, isc_boolean_t badcache) {
isc_result_t result;
dns_adbaddrinfo_t *addrinfo;
fctx_cleanupaltfinds(fctx);
fctx_cleanupforwaddrs(fctx);
fctx_cleanupaltaddrs(fctx);
- result = fctx_getaddresses(fctx);
+ result = fctx_getaddresses(fctx, badcache);
if (result == DNS_R_WAIT) {
/*
* Sleep waiting for addresses.
/*
* Keep trying.
*/
- fctx_try(fctx, ISC_TRUE);
+ fctx_try(fctx, ISC_TRUE, ISC_FALSE);
}
isc_event_free(&event);
if (result != ISC_R_SUCCESS)
fctx_done(fctx, result, __LINE__);
else
- fctx_try(fctx, ISC_FALSE);
+ fctx_try(fctx, ISC_FALSE, ISC_FALSE);
} else if (bucket_empty)
empty_bucket(res);
}
LOCK(&fctx->res->buckets[fctx->bucketnum].lock);
+ isc_stdtime_get(&now);
+
/*
* If chaining, we need to make sure that the right result code is
* returned, and that the rdatasets are bound.
inc_stats(fctx->res, dns_resstatscounter_valfail);
fctx->valfail++;
fctx->vresult = vevent->result;
- result = ISC_R_NOTFOUND;
- if (vevent->rdataset != NULL)
- result = dns_db_findnode(fctx->cache, vevent->name,
- ISC_TRUE, &node);
- if (result == ISC_R_SUCCESS)
- (void)dns_db_deleterdataset(fctx->cache, node, NULL,
- vevent->type, 0);
- if (result == ISC_R_SUCCESS && vevent->sigrdataset != NULL)
- (void)dns_db_deleterdataset(fctx->cache, node, NULL,
- dns_rdatatype_rrsig,
- vevent->type);
- if (result == ISC_R_SUCCESS)
- dns_db_detachnode(fctx->cache, &node);
- result = vevent->result;
+ if (fctx->vresult != DNS_R_BROKENCHAIN) {
+ result = ISC_R_NOTFOUND;
+ if (vevent->rdataset != NULL)
+ result = dns_db_findnode(fctx->cache,
+ vevent->name,
+ ISC_TRUE, &node);
+ if (result == ISC_R_SUCCESS)
+ (void)dns_db_deleterdataset(fctx->cache, node,
+ NULL,
+ vevent->type, 0);
+ if (result == ISC_R_SUCCESS &&
+ vevent->sigrdataset != NULL)
+ (void)dns_db_deleterdataset(fctx->cache, node,
+ NULL,
+ dns_rdatatype_rrsig,
+ vevent->type);
+ if (result == ISC_R_SUCCESS)
+ dns_db_detachnode(fctx->cache, &node);
+ }
+ if (fctx->vresult == DNS_R_BROKENCHAIN && !negative) {
+ /*
+ * Cache the data as pending for later validation.
+ */
+ result = ISC_R_NOTFOUND;
+ if (vevent->rdataset != NULL)
+ result = dns_db_findnode(fctx->cache,
+ vevent->name,
+ ISC_TRUE, &node);
+ if (result == ISC_R_SUCCESS) {
+ (void)dns_db_addrdataset(fctx->cache, node,
+ NULL, now,
+ vevent->rdataset, 0,
+ NULL);
+ }
+ if (result == ISC_R_SUCCESS &&
+ vevent->sigrdataset != NULL)
+ (void)dns_db_addrdataset(fctx->cache, node,
+ NULL, now,
+ vevent->sigrdataset,
+ 0, NULL);
+ if (result == ISC_R_SUCCESS)
+ dns_db_detachnode(fctx->cache, &node);
+ }
+ result = fctx->vresult;
add_bad(fctx, addrinfo, result, badns_validation);
isc_event_free(&event);
UNLOCK(&fctx->res->buckets[fctx->bucketnum].lock);
INSIST(fctx->validator == NULL);
fctx->validator = ISC_LIST_HEAD(fctx->validators);
- if (fctx->validator != NULL) {
+ if (fctx->validator != NULL)
dns_validator_send(fctx->validator);
- } else if (sentresponse)
+ else if (sentresponse)
fctx_done(fctx, result, __LINE__); /* Locks bucket. */
- else
- fctx_try(fctx, ISC_TRUE); /* Locks bucket. */
+ else if (result == DNS_R_BROKENCHAIN) {
+ isc_result_t tresult;
+ isc_time_t expire;
+ isc_interval_t i;
+
+ isc_interval_set(&i, DNS_BADCACHE_TTL(fctx), 0);
+ tresult = isc_time_nowplusinterval(&expire, &i);
+ if (negative &&
+ (fctx->type == dns_rdatatype_dnskey ||
+ fctx->type == dns_rdatatype_dlv ||
+ fctx->type == dns_rdatatype_ds) &&
+ tresult == ISC_R_SUCCESS)
+ dns_resolver_addbadcache(fctx->res,
+ &fctx->name,
+ fctx->type, &expire);
+ fctx_done(fctx, result, __LINE__); /* Locks bucket. */
+ } else
+ fctx_try(fctx, ISC_TRUE, ISC_TRUE); /* Locks bucket. */
return;
}
- isc_stdtime_get(&now);
if (negative) {
dns_rdatatype_t covers;
/*
* Try again.
*/
- fctx_try(fctx, ISC_TRUE);
+ fctx_try(fctx, ISC_TRUE, ISC_FALSE);
} else {
unsigned int n;
dns_rdataset_t *nsrdataset = NULL;
/*
* Try again.
*/
- fctx_try(fctx, !get_nameservers);
+ fctx_try(fctx, !get_nameservers, ISC_FALSE);
} else if (resend) {
/*
* Resend (probably with changed options).
/***
*** Resolver Methods
***/
+static void
+destroy_badcache(dns_resolver_t *res) {
+ dns_badcache_t *bad, *next;
+ unsigned int i;
+
+ if (res->badcache != NULL) {
+ for (i = 0; i < res->badhash; i++)
+ for (bad = res->badcache[i]; bad != NULL;
+ bad = next) {
+ next = bad->next;
+ isc_mem_put(res->mctx, bad, sizeof(*bad) +
+ bad->name.length);
+ res->badcount--;
+ }
+ isc_mem_put(res->mctx, res->badcache,
+ sizeof(*res->badcache) * res->badhash);
+ res->badcache = NULL;
+ res->badhash = 0;
+ INSIST(res->badcount == 0);
+ }
+}
static void
destroy(dns_resolver_t *res) {
isc_mem_put(res->mctx, a, sizeof(*a));
}
dns_resolver_reset_algorithms(res);
+ destroy_badcache(res);
dns_resolver_resetmustbesecure(res);
#if USE_ALGLOCK
isc_rwlock_destroy(&res->alglock);
ISC_LIST_INIT(res->alternates);
res->udpsize = RECV_BUFFER_SIZE;
res->algorithms = NULL;
+ res->badcache = NULL;
+ res->badcount = 0;
+ res->badhash = 0;
+ res->badsweep = 0;
res->mustbesecure = NULL;
res->spillatmin = res->spillat = 10;
res->spillatmax = 100;
return (resolver->udpsize);
}
+void
+dns_resolver_flushbadcache(dns_resolver_t *resolver, dns_name_t *name) {
+ unsigned int i;
+ dns_badcache_t *bad, *prev, *next;
+
+ REQUIRE(VALID_RESOLVER(resolver));
+
+ LOCK(&resolver->lock);
+ if (resolver->badcache == NULL)
+ goto unlock;
+
+ if (name != NULL) {
+ isc_time_t now;
+ isc_result_t result;
+ result = isc_time_now(&now);
+ if (result != ISC_R_SUCCESS)
+ isc_time_settoepoch(&now);
+ i = dns_name_hash(name, ISC_FALSE) % resolver->badhash;
+ prev = NULL;
+ for (bad = resolver->badcache[i]; bad != NULL; bad = next) {
+ int n;
+ next = bad->next;
+ n = isc_time_compare(&bad->expire, &now);
+ if (n < 0 || dns_name_equal(name, &bad->name)) {
+ if (prev == NULL)
+ resolver->badcache[i] = bad->next;
+ else
+ prev->next = bad->next;
+ isc_mem_put(resolver->mctx, bad, sizeof(*bad) +
+ bad->name.length);
+ resolver->badcount--;
+ } else
+ prev = bad;
+ }
+ } else
+ destroy_badcache(resolver);
+
+ unlock:
+ UNLOCK(&resolver->lock);
+
+}
+
+static void
+resizehash(dns_resolver_t *resolver, isc_time_t *now, isc_boolean_t grow) {
+ unsigned int newsize;
+ dns_badcache_t **new, *bad, *next;
+ unsigned int i;
+
+ if (grow)
+ newsize = resolver->badhash * 2 + 1;
+ else
+ newsize = (resolver->badhash - 1) / 2;
+
+ new = isc_mem_get(resolver->mctx,
+ sizeof(*resolver->badcache) * newsize);
+ if (new == NULL)
+ return;
+ memset(new, 0, sizeof(*resolver->badcache) * newsize);
+ for (i = 0; i < resolver->badhash; i++) {
+ for (bad = resolver->badcache[i]; bad != NULL; bad = next) {
+ next = bad->next;
+ if (isc_time_compare(&bad->expire, now) < 0) {
+ isc_mem_put(resolver->mctx, bad, sizeof(*bad) +
+ bad->name.length);
+ resolver->badcount--;
+ } else {
+ bad->next = new[bad->hashval % newsize];
+ new[bad->hashval % newsize] = bad;
+ }
+ }
+ }
+ isc_mem_put(resolver->mctx, resolver->badcache,
+ sizeof(*resolver->badcache) * resolver->badhash);
+ resolver->badhash = newsize;
+ resolver->badcache = new;
+}
+
+void
+dns_resolver_addbadcache(dns_resolver_t *resolver, dns_name_t *name,
+ dns_rdatatype_t type, isc_time_t *expire)
+{
+ isc_time_t now;
+ isc_result_t result = ISC_R_SUCCESS;
+ unsigned int i, hashval;
+ dns_badcache_t *bad, *prev, *next;
+
+ REQUIRE(VALID_RESOLVER(resolver));
+
+ LOCK(&resolver->lock);
+ if (resolver->badcache == NULL) {
+ resolver->badcache = isc_mem_get(resolver->mctx,
+ sizeof(*resolver->badcache) *
+ DNS_BADCACHE_SIZE);
+ if (resolver->badcache == NULL) {
+ result = ISC_R_NOMEMORY;
+ goto cleanup;
+ }
+ resolver->badhash = DNS_BADCACHE_SIZE;
+ memset(resolver->badcache, 0, sizeof(*resolver->badcache) *
+ resolver->badhash);
+ }
+
+ result = isc_time_now(&now);
+ if (result != ISC_R_SUCCESS)
+ isc_time_settoepoch(&now);
+ hashval = dns_name_hash(name, ISC_FALSE);
+ i = hashval % resolver->badhash;
+ prev = NULL;
+ for (bad = resolver->badcache[i]; bad != NULL; bad = next) {
+ next = bad->next;
+ if (bad->type == type && dns_name_equal(name, &bad->name))
+ break;
+ if (isc_time_compare(&bad->expire, &now) < 0) {
+ if (prev == NULL)
+ resolver->badcache[i] = bad->next;
+ else
+ prev->next = bad->next;
+ isc_mem_put(resolver->mctx, bad, sizeof(*bad) +
+ bad->name.length);
+ resolver->badcount--;
+ } else
+ prev = bad;
+ }
+ if (bad == NULL) {
+ isc_buffer_t buffer;
+ bad = isc_mem_get(resolver->mctx, sizeof(*bad) + name->length);
+ if (bad == NULL) {
+ result = ISC_R_NOMEMORY;
+ goto cleanup;
+ }
+ bad->type = type;
+ bad->hashval = hashval;
+ isc_buffer_init(&buffer, bad + 1, name->length);
+ dns_name_init(&bad->name, NULL);
+ dns_name_copy(name, &bad->name, &buffer);
+ bad->next = resolver->badcache[i];
+ resolver->badcache[i] = bad;
+ resolver->badcount++;
+ if (resolver->badcount > resolver->badhash * 8)
+ resizehash(resolver, &now, ISC_TRUE);
+ if (resolver->badcount < resolver->badhash * 2 &&
+ resolver->badhash > DNS_BADCACHE_SIZE)
+ resizehash(resolver, &now, ISC_FALSE);
+ }
+ bad->expire = *expire;
+ cleanup:
+ UNLOCK(&resolver->lock);
+}
+
+isc_boolean_t
+dns_resolver_getbadcache(dns_resolver_t *resolver, dns_name_t *name,
+ dns_rdatatype_t type, isc_time_t *now)
+{
+ dns_badcache_t *bad, *prev, *next;
+ isc_boolean_t answer = ISC_FALSE;
+ unsigned int i;
+
+ REQUIRE(VALID_RESOLVER(resolver));
+
+ LOCK(&resolver->lock);
+ if (resolver->badcache == NULL)
+ goto unlock;
+
+ i = dns_name_hash(name, ISC_FALSE) % resolver->badhash;
+ prev = NULL;
+ for (bad = resolver->badcache[i]; bad != NULL; bad = next) {
+ next = bad->next;
+ /*
+ * Search the hash list. Clean out expired records as we go.
+ */
+ if (isc_time_compare(&bad->expire, now) < 0) {
+ if (prev != NULL)
+ prev->next = bad->next;
+ else
+ resolver->badcache[i] = bad->next;
+ isc_mem_put(resolver->mctx, bad, sizeof(*bad) +
+ bad->name.length);
+ resolver->badcount--;
+ continue;
+ }
+ if (bad->type == type && dns_name_equal(name, &bad->name)) {
+ answer = ISC_TRUE;
+ break;
+ }
+ prev = bad;
+ }
+
+ /*
+ * Slow sweep to clean out stale records.
+ */
+ i = resolver->badsweep++ % resolver->badhash;
+ bad = resolver->badcache[i];
+ if (bad != NULL && isc_time_compare(&bad->expire, now) < 0) {
+ resolver->badcache[i] = bad->next;
+ isc_mem_put(resolver->mctx, bad, sizeof(*bad) +
+ bad->name.length);
+ resolver->badcount--;
+ }
+
+ unlock:
+ UNLOCK(&resolver->lock);
+ return (answer);
+}
+
+void
+dns_resolver_printbadcache(dns_resolver_t *resolver, FILE *fp) {
+ char namebuf[DNS_NAME_FORMATSIZE];
+ char typebuf[DNS_RDATATYPE_FORMATSIZE];
+ dns_badcache_t *bad, *next, *prev;
+ isc_time_t now;
+ unsigned int i;
+ isc_uint64_t t;
+
+ LOCK(&resolver->lock);
+ fprintf(fp, ";\n; Bad cache\n;\n");
+
+ if (resolver->badcache == NULL)
+ goto unlock;
+
+ TIME_NOW(&now);
+ for (i = 0; i < resolver->badhash; i++) {
+ prev = NULL;
+ for (bad = resolver->badcache[i]; bad != NULL; bad = next) {
+ next = bad->next;
+ if (isc_time_compare(&bad->expire, &now) < 0) {
+ if (prev != NULL)
+ prev->next = bad->next;
+ else
+ resolver->badcache[i] = bad->next;
+ isc_mem_put(resolver->mctx, bad, sizeof(*bad) +
+ bad->name.length);
+ resolver->badcount--;
+ continue;
+ }
+ prev = bad;
+ dns_name_format(&bad->name, namebuf, sizeof(namebuf));
+ dns_rdatatype_format(bad->type, typebuf,
+ sizeof(typebuf));
+ t = isc_time_microdiff(&bad->expire, &now);
+ t /= 1000;
+ fprintf(fp, "; %s/%s [ttl "
+ "%" ISC_PLATFORM_QUADFORMAT "u]\n",
+ namebuf, typebuf, t);
+ }
+ }
+
+ unlock:
+ UNLOCK(&resolver->lock);
+}
+
static void
free_algorithm(void *node, void *arg) {
unsigned char *algorithms = node;
* PERFORMANCE OF THIS SOFTWARE.
*/
-/* $Id: result.c,v 1.125 2008/09/25 04:02:38 tbox Exp $ */
+/* $Id: result.c,v 1.125.48.1 2010/02/25 05:35:11 marka Exp $ */
/*! \file */
"MX is an address", /*%< 102 DNS_R_MXISADDRESS */
"duplicate query", /*%< 103 DNS_R_DUPLICATE */
"invalid NSEC3 owner name (wildcard)", /*%< 104 DNS_R_INVALIDNSEC3 */
+
+ "not master", /*%< 105 DNS_R_NOTMASTER */
+ "broken trust chain", /*%< 106 DNS_R_BROKENCHAIN */
};
static const char *rcode_text[DNS_R_NRCODERESULTS] = {
* PERFORMANCE OF THIS SOFTWARE.
*/
-/* $Id: sdb.c,v 1.66.48.3 2009/06/26 06:25:44 marka Exp $ */
+/* $Id: sdb.c,v 1.66.48.4 2010/02/25 05:35:11 marka Exp $ */
/*! \file */
NULL,
NULL,
NULL,
+ NULL,
+ NULL,
NULL
};
* USE OR PERFORMANCE OF THIS SOFTWARE.
*/
-/* $Id: sdlz.c,v 1.18.50.3 2009/06/26 06:25:44 marka Exp $ */
+/* $Id: sdlz.c,v 1.18.50.4 2010/02/25 05:35:11 marka Exp $ */
/*! \file */
NULL,
NULL,
NULL,
+ NULL,
+ NULL,
NULL
};
* PERFORMANCE OF THIS SOFTWARE.
*/
-/* $Id: validator.c,v 1.164.12.11 2009/12/30 06:44:44 each Exp $ */
+/* $Id: validator.c,v 1.164.12.12 2010/02/25 05:35:11 marka Exp $ */
#include <config.h>
markanswer(dns_validator_t *val) {
validator_log(val, ISC_LOG_DEBUG(3), "marking as answer");
if (val->event->rdataset != NULL)
- val->event->rdataset->trust = dns_trust_answer;
+ dns_rdataset_settrust(val->event->rdataset, dns_trust_answer);
if (val->event->sigrdataset != NULL)
- val->event->sigrdataset->trust = dns_trust_answer;
+ dns_rdataset_settrust(val->event->sigrdataset,
+ dns_trust_answer);
+}
+
+static inline void
+marksecure(dns_validatorevent_t *event) {
+ dns_rdataset_settrust(event->rdataset, dns_trust_secure);
+ dns_rdataset_settrust(event->sigrdataset, dns_trust_secure);
}
static void
if (eresult == ISC_R_CANCELED)
validator_done(val, eresult);
else
- validator_done(val, DNS_R_NOVALIDKEY);
+ validator_done(val, DNS_R_BROKENCHAIN);
}
want_destroy = exit_check(val);
UNLOCK(&val->lock);
if (eresult == ISC_R_CANCELED)
validator_done(val, eresult);
else
- validator_done(val, DNS_R_NOVALIDDS);
+ validator_done(val, DNS_R_BROKENCHAIN);
}
want_destroy = exit_check(val);
UNLOCK(&val->lock);
if (result != DNS_R_WAIT)
validator_done(val, result);
} else {
+ if (eresult != DNS_R_BROKENCHAIN) {
+ if (dns_rdataset_isassociated(&val->frdataset))
+ dns_rdataset_expire(&val->frdataset);
+ if (dns_rdataset_isassociated(&val->fsigrdataset))
+ dns_rdataset_expire(&val->fsigrdataset);
+ }
validator_log(val, ISC_LOG_DEBUG(3),
"keyvalidated: got %s",
isc_result_totext(eresult));
- validator_done(val, eresult);
+ validator_done(val, DNS_R_BROKENCHAIN);
}
want_destroy = exit_check(val);
UNLOCK(&val->lock);
if (result != DNS_R_WAIT)
validator_done(val, result);
} else {
+ if (eresult != DNS_R_BROKENCHAIN) {
+ if (dns_rdataset_isassociated(&val->frdataset))
+ dns_rdataset_expire(&val->frdataset);
+ if (dns_rdataset_isassociated(&val->fsigrdataset))
+ dns_rdataset_expire(&val->fsigrdataset);
+ }
validator_log(val, ISC_LOG_DEBUG(3),
"dsvalidated: got %s",
isc_result_totext(eresult));
- validator_done(val, eresult);
+ validator_done(val, DNS_R_BROKENCHAIN);
}
want_destroy = exit_check(val);
UNLOCK(&val->lock);
validator_log(val, ISC_LOG_DEBUG(3),
"authvalidated: got %s",
isc_result_totext(result));
+ if (result == DNS_R_BROKENCHAIN)
+ val->authfail++;
if (result == ISC_R_CANCELED)
validator_done(val, result);
else {
* \li DNS_R_NCACHENXRRSET
* \li DNS_R_NXRRSET
* \li DNS_R_NXDOMAIN
+ * \li DNS_R_BROKENCHAIN
*/
static inline isc_result_t
view_find(dns_validator_t *val, dns_name_t *name, dns_rdatatype_t type) {
dns_rdata_t rdata = DNS_RDATA_INIT;
isc_result_t result;
unsigned int options;
+ isc_time_t now;
char buf1[DNS_NAME_FORMATSIZE];
char buf2[DNS_NAME_FORMATSIZE];
char buf3[DNS_NAME_FORMATSIZE];
+ char namebuf[DNS_NAME_FORMATSIZE];
+ char typebuf[DNS_RDATATYPE_FORMATSIZE];
if (dns_rdataset_isassociated(&val->frdataset))
dns_rdataset_disassociate(&val->frdataset);
if (val->view->zonetable == NULL)
return (ISC_R_CANCELED);
+ if (isc_time_now(&now) == ISC_R_SUCCESS &&
+ dns_resolver_getbadcache(val->view->resolver, name, type, &now)) {
+
+ dns_name_format(name, namebuf, sizeof(namebuf));
+ dns_rdatatype_format(type, typebuf, sizeof(typebuf));
+ validator_log(val, ISC_LOG_INFO, "bad cache hit (%s/%s)",
+ namebuf, typebuf);
+ return (DNS_R_BROKENCHAIN);
+ }
+
options = DNS_DBFIND_PENDINGOK;
if (type == dns_rdatatype_dlv)
options |= DNS_DBFIND_COVERINGNSEC;
result = dns_view_find(val->view, name, type, 0, options,
ISC_FALSE, NULL, NULL, foundname,
&val->frdataset, &val->fsigrdataset);
+
if (result == DNS_R_NXDOMAIN) {
if (dns_rdataset_isassociated(&val->frdataset))
dns_rdataset_disassociate(&val->frdataset);
/*
* We don't know anything about this key.
*/
- result = create_fetch(val, &siginfo->signer, dns_rdatatype_dnskey,
+ result = create_fetch(val, &siginfo->signer,
+ dns_rdatatype_dnskey,
fetch_callback_validator, "get_key");
if (result != ISC_R_SUCCESS)
return (result);
* This key doesn't exist.
*/
result = DNS_R_CONTINUE;
- }
+ } else if (result == DNS_R_BROKENCHAIN)
+ return (result);
if (dns_rdataset_isassociated(&val->frdataset) &&
val->keyset != &val->frdataset)
"looking for noqname proof");
return (nsecvalidate(val, ISC_FALSE));
} else if (result == ISC_R_SUCCESS) {
- event->rdataset->trust = dns_trust_secure;
- event->sigrdataset->trust = dns_trust_secure;
+ marksecure(event);
validator_log(val, ISC_LOG_DEBUG(3),
"marking as secure");
return (result);
"no RRSIG matching DLV key");
}
if (result == ISC_R_SUCCESS) {
- val->event->rdataset->trust = dns_trust_secure;
- val->event->sigrdataset->trust = dns_trust_secure;
+ marksecure(val->event);
validator_log(val, ISC_LOG_DEBUG(3), "marking as secure");
return (result);
} else if (result == ISC_R_NOMORE && !supported_algorithm) {
keynode = nextnode;
}
if (result == ISC_R_SUCCESS) {
- event->rdataset->trust = dns_trust_secure;
- event->sigrdataset->trust = dns_trust_secure;
+ marksecure(event);
validator_log(val, ISC_LOG_DEBUG(3),
"signed by trusted key; "
"marking as secure");
*/
dns_name_format(val->event->name, namebuf,
sizeof(namebuf));
- validator_log(val, ISC_LOG_DEBUG(2),
+ validator_log(val, ISC_LOG_NOTICE,
"unable to find a DNSKEY which verifies "
"the DNSKEY RRset and also matches one "
"of specified trusted-keys for '%s'",
namebuf);
+ validator_log(val, ISC_LOG_NOTICE,
+ "please check the 'trusted-keys' for "
+ "'%s' in named.conf.", namebuf);
return (DNS_R_NOVALIDKEY);
}
dns_rdataset_disassociate(&val->fsigrdataset);
validator_log(val, ISC_LOG_DEBUG(2), "no DS record");
return (DNS_R_NOVALIDSIG);
- }
+ } else if (result == DNS_R_BROKENCHAIN)
+ return (result);
}
/*
"no RRSIG matching DS key");
}
if (result == ISC_R_SUCCESS) {
- event->rdataset->trust = dns_trust_secure;
- event->sigrdataset->trust = dns_trust_secure;
+ marksecure(event);
validator_log(val, ISC_LOG_DEBUG(3), "marking as secure");
return (result);
} else if (result == ISC_R_NOMORE && !supported_algorithm) {
"nsecvalidate");
if (result != ISC_R_SUCCESS)
return (result);
+ val->authcount++;
return (DNS_R_WAIT);
}
"noqname proof found");
validator_log(val, ISC_LOG_DEBUG(3),
"marking as secure");
- val->event->rdataset->trust = dns_trust_secure;
- val->event->sigrdataset->trust = dns_trust_secure;
+ marksecure(val->event);
return (ISC_R_SUCCESS);
} else if ((val->attributes & VALATTR_FOUNDOPTOUT) != 0 &&
dns_name_countlabels(dns_fixedname_name(&val->wild))
}
findnsec3proofs(val);
+ if (val->authcount == val->authfail)
+ return (DNS_R_BROKENCHAIN);
validator_log(val, ISC_LOG_DEBUG(3),
"nonexistence proof(s) not found");
val->attributes |= VALATTR_INSECURITY;
return (ISC_FALSE);
}
+static void
+dlvvalidated(isc_task_t *task, isc_event_t *event) {
+ dns_validatorevent_t *devent;
+ dns_validator_t *val;
+ isc_result_t eresult;
+ isc_boolean_t want_destroy;
+
+ UNUSED(task);
+ INSIST(event->ev_type == DNS_EVENT_VALIDATORDONE);
+
+ devent = (dns_validatorevent_t *)event;
+ val = devent->ev_arg;
+ eresult = devent->result;
+
+ isc_event_free(&event);
+ dns_validator_destroy(&val->subvalidator);
+
+ INSIST(val->event != NULL);
+
+ validator_log(val, ISC_LOG_DEBUG(3), "in dlvvalidated");
+ LOCK(&val->lock);
+ if (CANCELED(val)) {
+ validator_done(val, ISC_R_CANCELED);
+ } else if (eresult == ISC_R_SUCCESS) {
+ validator_log(val, ISC_LOG_DEBUG(3),
+ "dlvset with trust %d", val->frdataset.trust);
+ dns_rdataset_clone(&val->frdataset, &val->dlv);
+ val->havedlvsep = ISC_TRUE;
+ if (dlv_algorithm_supported(val))
+ dlv_validator_start(val);
+ else {
+ markanswer(val);
+ validator_done(val, ISC_R_SUCCESS);
+ }
+ } else {
+ if (eresult != DNS_R_BROKENCHAIN) {
+ if (dns_rdataset_isassociated(&val->frdataset))
+ dns_rdataset_expire(&val->frdataset);
+ if (dns_rdataset_isassociated(&val->fsigrdataset))
+ dns_rdataset_expire(&val->fsigrdataset);
+ }
+ validator_log(val, ISC_LOG_DEBUG(3),
+ "dlvvalidated: got %s",
+ isc_result_totext(eresult));
+ validator_done(val, DNS_R_BROKENCHAIN);
+ }
+ want_destroy = exit_check(val);
+ UNLOCK(&val->lock);
+ if (want_destroy)
+ destroy(val);
+}
+
/*%
* Callback from fetching a DLV record.
*
namebuf);
result = view_find(val, dlvname, dns_rdatatype_dlv);
if (result == ISC_R_SUCCESS) {
+ if (DNS_TRUST_PENDING(val->frdataset.trust) &&
+ dns_rdataset_isassociated(&val->fsigrdataset))
+ {
+ dns_fixedname_init(&val->fname);
+ dns_name_copy(dlvname,
+ dns_fixedname_name(&val->fname),
+ NULL);
+ result = create_validator(val,
+ dns_fixedname_name(&val->fname),
+ dns_rdatatype_dlv,
+ &val->frdataset,
+ &val->fsigrdataset,
+ dlvvalidated,
+ "finddlvsep");
+ if (result != ISC_R_SUCCESS)
+ return (result);
+ return (DNS_R_WAIT);
+ }
if (val->frdataset.trust < dns_trust_secure)
return (DNS_R_NOVALIDSIG);
val->havedlvsep = ISC_TRUE;
* \li DNS_R_NOVALIDSIG
* \li DNS_R_NOVALIDNSEC
* \li DNS_R_NOTINSECURE
+ * \li DNS_R_BROKENCHAIN
*/
static isc_result_t
proveunsecure(dns_validator_t *val, isc_boolean_t have_ds, isc_boolean_t resume)
if (result != ISC_R_SUCCESS)
goto out;
return (DNS_R_WAIT);
- }
+ } else if (result == DNS_R_BROKENCHAIN)
+ return (result);
}
/*
val->seensig = ISC_FALSE;
val->havedlvsep = ISC_FALSE;
val->depth = 0;
+ val->authcount = 0;
+ val->authfail = 0;
val->mustbesecure = dns_resolver_getmustbesecure(view->resolver, name);
dns_rdataset_init(&val->frdataset);
dns_rdataset_init(&val->fsigrdataset);
* PERFORMANCE OF THIS SOFTWARE.
*/
-/* $Id: view.c,v 1.150.84.3 2009/11/12 23:39:23 marka Exp $ */
+/* $Id: view.c,v 1.150.84.4 2010/02/25 05:35:11 marka Exp $ */
/*! \file */
(void)fprintf(fp, ";\n; Cache dump of view '%s'\n;\n", view->name);
result = dns_master_dumptostream(view->mctx, view->cachedb, NULL,
- &dns_master_style_cache, fp);
+ &dns_master_style_cache, fp);
if (result != ISC_R_SUCCESS)
return (result);
dns_adb_dump(view->adb, fp);
+ dns_resolver_printbadcache(view->resolver, fp);
return (ISC_R_SUCCESS);
}
dns_cache_attachdb(view->cache, &view->cachedb);
if (view->acache != NULL)
dns_acache_setdb(view->acache, view->cachedb);
+ if (view->resolver != NULL)
+ dns_resolver_flushbadcache(view->resolver, NULL);
dns_adb_flush(view->adb);
return (ISC_R_SUCCESS);
dns_adb_flushname(view->adb, name);
if (view->cache == NULL)
return (ISC_R_SUCCESS);
+ if (view->resolver != NULL)
+ dns_resolver_flushbadcache(view->resolver, name);
return (dns_cache_flushname(view->cache, name));
}