diff -up bind-9.3.6-P1/bin/named/server.c.rh572848 bind-9.3.6-P1/bin/named/server.c --- bind-9.3.6-P1/bin/named/server.c.rh572848 2010-08-10 12:17:31.699313304 +0200 +++ bind-9.3.6-P1/bin/named/server.c 2010-08-10 12:17:31.699313304 +0200 @@ -4045,6 +4045,8 @@ dumpdone(void *arg, isc_result_t result) } 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) { diff -up bind-9.3.6-P1/lib/dns/include/dns/resolver.h.rh572848 bind-9.3.6-P1/lib/dns/include/dns/resolver.h --- bind-9.3.6-P1/lib/dns/include/dns/resolver.h.rh572848 2006-02-02 00:48:51.000000000 +0100 +++ bind-9.3.6-P1/lib/dns/include/dns/resolver.h 2010-08-10 12:17:31.699313304 +0200 @@ -426,6 +426,48 @@ dns_resolver_setmustbesecure(dns_resolve isc_boolean_t dns_resolver_getmustbesecure(dns_resolver_t *resolver, dns_name_t *name); +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: + * resolver to be valid. + * 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: + * resolver to be valid. + * 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: + * 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: + * resolver to be valid. + */ + ISC_LANG_ENDDECLS #endif /* DNS_RESOLVER_H */ diff -up bind-9.3.6-P1/lib/dns/include/dns/result.h.rh572848 bind-9.3.6-P1/lib/dns/include/dns/result.h --- bind-9.3.6-P1/lib/dns/include/dns/result.h.rh572848 2004-05-14 07:06:41.000000000 +0200 +++ bind-9.3.6-P1/lib/dns/include/dns/result.h 2010-08-10 12:17:31.699313304 +0200 @@ -143,8 +143,9 @@ #define DNS_R_UNKNOWNCOMMAND (ISC_RESULTCLASS_DNS + 99) #define DNS_R_MUSTBESECURE (ISC_RESULTCLASS_DNS + 100) #define DNS_R_COVERINGNSEC (ISC_RESULTCLASS_DNS + 101) +#define DNS_R_BROKENCHAIN (ISC_RESULTCLASS_DNS + 102) -#define DNS_R_NRESULTS 102 /* Number of results */ +#define DNS_R_NRESULTS 103 /* Number of results */ /* * DNS wire format rcodes. diff -up bind-9.3.6-P1/lib/dns/include/dns/validator.h.rh572848 bind-9.3.6-P1/lib/dns/include/dns/validator.h --- bind-9.3.6-P1/lib/dns/include/dns/validator.h.rh572848 2007-09-19 05:41:33.000000000 +0200 +++ bind-9.3.6-P1/lib/dns/include/dns/validator.h 2010-08-10 12:17:31.699313304 +0200 @@ -151,6 +151,8 @@ struct dns_validator { isc_boolean_t mustbesecure; unsigned int dlvlabels; unsigned int depth; + unsigned int authcount; + unsigned int authfail; }; /*% diff -up bind-9.3.6-P1/lib/dns/resolver.c.rh572848 bind-9.3.6-P1/lib/dns/resolver.c --- bind-9.3.6-P1/lib/dns/resolver.c.rh572848 2010-08-10 12:17:31.679309993 +0200 +++ bind-9.3.6-P1/lib/dns/resolver.c 2010-08-10 12:17:31.709314955 +0200 @@ -286,6 +286,18 @@ typedef struct alternate { 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; @@ -324,6 +336,13 @@ struct dns_resolver { isc_eventlist_t whenshutdown; unsigned int activebuckets; isc_boolean_t priming; + + /* 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. */ @@ -353,7 +372,7 @@ static void empty_bucket(dns_resolver_t 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); +static void fctx_try(fetchctx_t *fctx, 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, @@ -898,7 +917,7 @@ process_sendevent(resquery_t *query, isc if (result != ISC_R_SUCCESS) fctx_done(fctx, result); else - fctx_try(fctx); + fctx_try(fctx, ISC_FALSE); } } @@ -1591,7 +1610,7 @@ resquery_connected(isc_task_t *task, isc if (result != ISC_R_SUCCESS) fctx_done(fctx, result); else - fctx_try(fctx); + fctx_try(fctx, ISC_FALSE); } } @@ -1650,7 +1669,7 @@ fctx_finddone(isc_task_t *task, isc_even dns_adb_destroyfind(&find); if (want_try) - fctx_try(fctx); + fctx_try(fctx, ISC_FALSE); else if (want_done) fctx_done(fctx, ISC_R_FAILURE); else if (bucket_empty) @@ -1990,7 +2009,7 @@ findname(fetchctx_t *fctx, dns_name_t *n } 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; @@ -2206,12 +2225,24 @@ fctx_getaddresses(fetchctx_t *fctx) { fctx_cleanupfinds(fctx); goto restart; } 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 { @@ -2433,7 +2464,7 @@ fctx_nextaddress(fetchctx_t *fctx) { } static void -fctx_try(fetchctx_t *fctx) { +fctx_try(fetchctx_t *fctx, isc_boolean_t badcache) { isc_result_t result; dns_adbaddrinfo_t *addrinfo; @@ -2451,7 +2482,7 @@ fctx_try(fetchctx_t *fctx) { 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. @@ -2594,7 +2625,7 @@ fctx_timeout(isc_task_t *task, isc_event /* * Keep trying. */ - fctx_try(fctx); + fctx_try(fctx, ISC_FALSE); } isc_event_free(&event); @@ -2764,7 +2795,7 @@ fctx_start(isc_task_t *task, isc_event_t if (result != ISC_R_SUCCESS) fctx_done(fctx, result); else - fctx_try(fctx); + fctx_try(fctx, ISC_FALSE); } else if (bucket_empty) empty_bucket(res); } @@ -3310,6 +3341,8 @@ validated(isc_task_t *task, isc_event_t 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. @@ -3352,37 +3385,83 @@ validated(isc_task_t *task, isc_event_t } if (vevent->result != ISC_R_SUCCESS) { + isc_result_t vresult; FCTXTRACE("validation failed"); - 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; + vresult = vevent->result; + if (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 (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 = vresult; add_bad(fctx, addrinfo, result); 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); /* Locks bucket. */ - else - fctx_try(fctx); /* 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); /* Locks bucket. */ + } else + fctx_try(fctx, ISC_TRUE); /* Locks bucket. */ return; } - isc_stdtime_get(&now); - if (negative) { dns_rdatatype_t covers; FCTXTRACE("nonexistence validation OK"); @@ -5146,7 +5225,7 @@ resume_dslookup(isc_task_t *task, isc_ev /* * Try again. */ - fctx_try(fctx); + fctx_try(fctx, ISC_FALSE); } else { unsigned int n; dns_rdataset_t *nsrdataset = NULL; @@ -5847,7 +5926,7 @@ resquery_response(isc_task_t *task, isc_ /* * Try again. */ - fctx_try(fctx); + fctx_try(fctx, ISC_FALSE); } else if (resend) { /* * Resend (probably with changed options). @@ -5908,6 +5987,28 @@ resquery_response(isc_task_t *task, isc_ /*** *** 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) { @@ -5944,6 +6045,7 @@ 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); @@ -6032,6 +6134,10 @@ dns_resolver_create(dns_view_t *view, 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->nbuckets = ntasks; @@ -6730,6 +6836,255 @@ dns_resolver_getudpsize(dns_resolver_t * 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; diff -up bind-9.3.6-P1/lib/dns/result.c.rh572848 bind-9.3.6-P1/lib/dns/result.c --- bind-9.3.6-P1/lib/dns/result.c.rh572848 2004-05-14 07:06:39.000000000 +0200 +++ bind-9.3.6-P1/lib/dns/result.c 2010-08-10 12:17:50.772459957 +0200 @@ -151,7 +151,8 @@ static const char *text[DNS_R_NRESULTS] "unknown command", /* 99 DNS_R_UNKNOWNCOMMAND */ "must-be-secure", /* 100 DNS_R_MUSTBESECURE */ - "covering NSEC record returned" /* 101 DNS_R_COVERINGNSEC */ + "covering NSEC record returned", /* 101 DNS_R_COVERINGNSEC */ + "broken trust chain", /* 102 DNS_R_BROKENCHAIN */ }; static const char *rcode_text[DNS_R_NRCODERESULTS] = { diff -up bind-9.3.6-P1/lib/dns/validator.c.rh572848 bind-9.3.6-P1/lib/dns/validator.c --- bind-9.3.6-P1/lib/dns/validator.c.rh572848 2010-08-10 12:17:31.689311653 +0200 +++ bind-9.3.6-P1/lib/dns/validator.c 2010-08-10 12:17:31.709314955 +0200 @@ -339,7 +339,7 @@ fetch_callback_validator(isc_task_t *tas 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); @@ -409,7 +409,7 @@ dsfetched(isc_task_t *task, isc_event_t 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); @@ -552,7 +552,7 @@ keyvalidated(isc_task_t *task, isc_event 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); @@ -602,7 +602,7 @@ dsvalidated(isc_task_t *task, isc_event_ 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); @@ -804,6 +804,8 @@ authvalidated(isc_task_t *task, isc_even 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 { @@ -869,6 +871,7 @@ authvalidated(isc_task_t *task, isc_even * \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) { @@ -878,9 +881,12 @@ view_find(dns_validator_t *val, dns_name 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); @@ -890,6 +896,16 @@ view_find(dns_validator_t *val, dns_name 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; @@ -1246,7 +1262,8 @@ get_key(dns_validator_t *val, dns_rdata_ * 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) @@ -1754,11 +1771,14 @@ validatezonekey(dns_validator_t *val) { */ 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); } @@ -1819,7 +1839,8 @@ validatezonekey(dns_validator_t *val) { 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); } /* @@ -2143,6 +2164,7 @@ nsecvalidate(dns_validator_t *val, isc_b "nsecvalidate"); if (result != ISC_R_SUCCESS) return (result); + val->authcount++; return (DNS_R_WAIT); } @@ -2196,6 +2218,8 @@ nsecvalidate(dns_validator_t *val, isc_b return (ISC_R_SUCCESS); } + if (val->authcount == val->authfail) + return (DNS_R_BROKENCHAIN); validator_log(val, ISC_LOG_DEBUG(3), "nonexistence proof(s) not found"); val->attributes |= VALATTR_AUTHNONPENDING; @@ -2227,6 +2251,52 @@ check_ds(dns_validator_t *val, dns_name_ 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 { + 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. * @@ -2422,6 +2492,24 @@ finddlvsep(dns_validator_t *val, isc_boo 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) { validator_log(val, ISC_LOG_DEBUG(3), "DLV not validated"); @@ -2475,6 +2563,7 @@ finddlvsep(dns_validator_t *val, isc_boo * \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) @@ -2701,7 +2790,8 @@ proveunsecure(dns_validator_t *val, isc_ if (result != ISC_R_SUCCESS) goto out; return (DNS_R_WAIT); - } + } else if (result == DNS_R_BROKENCHAIN) + return (result); } validator_log(val, ISC_LOG_DEBUG(3), "insecurity proof failed"); return (DNS_R_NOTINSECURE); /* Couldn't complete insecurity proof */ @@ -2907,6 +2997,8 @@ dns_validator_create(dns_view_t *view, d 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); diff -up bind-9.3.6-P1/lib/dns/view.c.rh572848 bind-9.3.6-P1/lib/dns/view.c --- bind-9.3.6-P1/lib/dns/view.c.rh572848 2010-08-10 12:17:31.659306690 +0200 +++ bind-9.3.6-P1/lib/dns/view.c 2010-08-10 12:17:31.709314955 +0200 @@ -1175,6 +1175,7 @@ dns_view_dumpdbtostream(dns_view_t *view if (result != ISC_R_SUCCESS) return (result); dns_adb_dump(view->adb, fp); + dns_resolver_printbadcache(view->resolver, fp); return (ISC_R_SUCCESS); } @@ -1191,6 +1192,8 @@ dns_view_flushcache(dns_view_t *view) { return (result); dns_db_detach(&view->cachedb); dns_cache_attachdb(view->cache, &view->cachedb); + if (view->resolver != NULL) + dns_resolver_flushbadcache(view->resolver, NULL); dns_adb_flush(view->adb); return (ISC_R_SUCCESS); @@ -1205,6 +1208,8 @@ dns_view_flushname(dns_view_t *view, dns 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)); }