diff options
Diffstat (limited to 'usr.sbin/nsd/nsec3.c')
-rw-r--r-- | usr.sbin/nsd/nsec3.c | 1397 |
1 files changed, 676 insertions, 721 deletions
diff --git a/usr.sbin/nsd/nsec3.c b/usr.sbin/nsd/nsec3.c index 0220845113f..f84a4cedb6e 100644 --- a/usr.sbin/nsd/nsec3.c +++ b/usr.sbin/nsd/nsec3.c @@ -1,31 +1,93 @@ /* * nsec3.c -- nsec3 handling. * - * Copyright (c) 2001-2011, NLnet Labs. All rights reserved. + * Copyright (c) 2001-2006, NLnet Labs. All rights reserved. * * See LICENSE for the license. * */ -#include <config.h> +#include "config.h" #ifdef NSEC3 #include <stdio.h> #include <stdlib.h> -#include <errno.h> #include "nsec3.h" #include "iterated_hash.h" #include "namedb.h" #include "nsd.h" #include "answer.h" +#include "udbzone.h" +#include "options.h" -#define NSEC3_SHA1_HASH 1 /* same type code as DS hash */ +#define NSEC3_RDATA_BITMAP 5 +/* compare nsec3 hashes in nsec3 tree */ +static int +cmp_hash_tree(const void* x, const void* y) +{ + const domain_type* a = (const domain_type*)x; + const domain_type* b = (const domain_type*)y; + return memcmp(a->nsec3->nsec3_hash, b->nsec3->nsec3_hash, + NSEC3_HASH_LEN); +} + +/* compare nsec3 hashes in nsec3 wc tree */ +static int +cmp_wchash_tree(const void* x, const void* y) +{ + const domain_type* a = (const domain_type*)x; + const domain_type* b = (const domain_type*)y; + return memcmp(a->nsec3->nsec3_wc_hash, b->nsec3->nsec3_wc_hash, + NSEC3_HASH_LEN); +} + +/* compare nsec3 hashes in nsec3 ds tree */ +static int +cmp_dshash_tree(const void* x, const void* y) +{ + const domain_type* a = (const domain_type*)x; + const domain_type* b = (const domain_type*)y; + return memcmp(a->nsec3->nsec3_ds_parent_hash, + b->nsec3->nsec3_ds_parent_hash, NSEC3_HASH_LEN); +} + +/* compare base32-encoded nsec3 hashes in nsec3 rr tree, they are + * stored in the domain name of the node */ +static int +cmp_nsec3_tree(const void* x, const void* y) +{ + const domain_type* a = (const domain_type*)x; + const domain_type* b = (const domain_type*)y; + /* labelcount + 32long label */ + assert(dname_name(a->dname)[0] == 32); + assert(dname_name(b->dname)[0] == 32); + return memcmp(dname_name(a->dname), dname_name(b->dname), 33); +} + +void nsec3_zone_trees_create(struct region* region, zone_type* zone) +{ + if(!zone->nsec3tree) + zone->nsec3tree = rbtree_create(region, cmp_nsec3_tree); + if(!zone->hashtree) + zone->hashtree = rbtree_create(region, cmp_hash_tree); + if(!zone->wchashtree) + zone->wchashtree = rbtree_create(region, cmp_wchash_tree); + if(!zone->dshashtree) + zone->dshashtree = rbtree_create(region, cmp_dshash_tree); +} + +void nsec3_hash_tree_clear(struct zone* zone) +{ + hash_tree_clear(zone->nsec3tree); + hash_tree_clear(zone->hashtree); + hash_tree_clear(zone->wchashtree); + hash_tree_clear(zone->dshashtree); +} static void detect_nsec3_params(rr_type* nsec3_apex, const unsigned char** salt, int* salt_len, int* iter) { - /* always uses first NSEC3 record with SOA bit set */ assert(salt && salt_len && iter); assert(nsec3_apex); *salt_len = rdata_atom_data(nsec3_apex->rdatas[3])[0]; @@ -33,676 +95,678 @@ detect_nsec3_params(rr_type* nsec3_apex, *iter = read_uint16(rdata_atom_data(nsec3_apex->rdatas[2])); } -static const dname_type * -nsec3_hash_dname_param(region_type *region, zone_type *zone, - const dname_type *dname, rr_type* param_rr) +const dname_type * +nsec3_b32_create(region_type* region, zone_type* zone, unsigned char* hash) { - unsigned char hash[SHA_DIGEST_LENGTH]; + const dname_type* dname; char b32[SHA_DIGEST_LENGTH*2+1]; + b32_ntop(hash, SHA_DIGEST_LENGTH, b32, sizeof(b32)); + dname=dname_parse(region, b32); + dname=dname_concatenate(region, dname, domain_dname(zone->apex)); + return dname; +} + +void +nsec3_hash_and_store(zone_type* zone, const dname_type* dname, uint8_t* store) +{ const unsigned char* nsec3_salt = NULL; int nsec3_saltlength = 0; int nsec3_iterations = 0; - detect_nsec3_params(param_rr, &nsec3_salt, + detect_nsec3_params(zone->nsec3_param, &nsec3_salt, &nsec3_saltlength, &nsec3_iterations); - iterated_hash(hash, nsec3_salt, nsec3_saltlength, dname_name(dname), - dname->name_size, nsec3_iterations); - b32_ntop(hash, sizeof(hash), b32, sizeof(b32)); - dname=dname_parse(region, b32); - dname=dname_concatenate(region, dname, domain_dname(zone->apex)); - return dname; + iterated_hash((unsigned char*)store, nsec3_salt, nsec3_saltlength, + dname_name(dname), dname->name_size, nsec3_iterations); } -const dname_type * -nsec3_hash_dname(region_type *region, zone_type *zone, - const dname_type *dname) +#define STORE_HASH(x,y) memmove(domain->nsec3->x,y,NSEC3_HASH_LEN); domain->nsec3->have_##x =1; + +/** find hash or create it and store it */ +static void +nsec3_lookup_hash_and_wc(zone_type* zone, const dname_type* dname, + domain_type* domain, region_type* tmpregion) { - return nsec3_hash_dname_param(region, zone, - dname, zone->nsec3_soa_rr); + const dname_type* wcard; + if(domain->nsec3->have_nsec3_hash && domain->nsec3->have_nsec3_wc_hash) { + return; + } + /* lookup failed; disk failure or so */ + nsec3_hash_and_store(zone, dname, domain->nsec3->nsec3_hash); + domain->nsec3->have_nsec3_hash = 1; + wcard = dname_parse(tmpregion, "*"); + wcard = dname_concatenate(tmpregion, wcard, dname); + nsec3_hash_and_store(zone, wcard, domain->nsec3->nsec3_wc_hash); + domain->nsec3->have_nsec3_wc_hash = 1; +} + +static void +nsec3_lookup_hash_ds(zone_type* zone, const dname_type* dname, + domain_type* domain) +{ + if(domain->nsec3->have_nsec3_ds_parent_hash) { + return; + } + /* lookup failed; disk failure or so */ + nsec3_hash_and_store(zone, dname, domain->nsec3->nsec3_ds_parent_hash); + domain->nsec3->have_nsec3_ds_parent_hash = 1; } static int nsec3_has_soa(rr_type* rr) { - if(rdata_atom_size(rr->rdatas[5]) >= 3 && /* has types in bitmap */ - rdata_atom_data(rr->rdatas[5])[0] == 0 && /* first window = 0, */ - /* [1]: windowlen must be >= 1 */ - rdata_atom_data(rr->rdatas[5])[2]&0x02) /* SOA bit set */ + if(rdata_atom_size(rr->rdatas[NSEC3_RDATA_BITMAP]) >= 3 && /* has types in bitmap */ + rdata_atom_data(rr->rdatas[NSEC3_RDATA_BITMAP])[0] == 0 && /* first window = 0, */ + /* [1]: bitmap length must be >= 1 */ + /* [2]: bit[6] = SOA, thus mask first bitmap octet with 0x02 */ + rdata_atom_data(rr->rdatas[NSEC3_RDATA_BITMAP])[2]&0x02) { /* SOA bit set */ return 1; + } return 0; } static rr_type* -find_zone_nsec3(namedb_type* namedb, zone_type *zone) +check_apex_soa(namedb_type* namedb, zone_type *zone) { - size_t i; + uint8_t h[NSEC3_HASH_LEN]; domain_type* domain; + const dname_type* hashed_apex, *dname = domain_dname(zone->apex); + unsigned j; + rrset_type* nsec3_rrset; region_type* tmpregion; - /* Check settings in NSEC3PARAM. - Hash algorithm must be OK. And a NSEC3 with soa bit - must map to the zone apex. */ - rrset_type* paramset = domain_find_rrset(zone->apex, zone, TYPE_NSEC3PARAM); - if(!paramset || !paramset->rrs || !paramset->rr_count) - return NULL; + + nsec3_hash_and_store(zone, dname, h); tmpregion = region_create(xalloc, free); - for(i=0; i < paramset->rr_count; i++) { - rr_type* rr = ¶mset->rrs[i]; - const dname_type* hashed_apex; - rrset_type* nsec3_rrset; - size_t j; - const unsigned char *salt1; - int saltlen1, iter1; - - if(rdata_atom_data(rr->rdatas[0])[0] != NSEC3_SHA1_HASH) { - log_msg(LOG_ERR, "%s NSEC3PARAM entry %d has unknown hash algo %d", - dname_to_string(domain_dname(zone->apex), NULL), (int)i, - rdata_atom_data(rr->rdatas[0])[0]); - continue; - } - if(rdata_atom_data(rr->rdatas[1])[0] != 0) { - /* draft-nsec3-09: NSEC3PARAM records with flags - field value other than zero MUST be ignored. */ - continue; - } - /* check hash of apex -> NSEC3 with soa bit on */ - hashed_apex = nsec3_hash_dname_param(tmpregion, - zone, domain_dname(zone->apex), ¶mset->rrs[i]); - domain = domain_table_find(namedb->domains, hashed_apex); - if(!domain) { - log_msg(LOG_ERR, "%s NSEC3PARAM entry %d has no hash(apex).", - dname_to_string(domain_dname(zone->apex), NULL), (int)i); - log_msg(LOG_ERR, "hash(apex)= %s", - dname_to_string(hashed_apex, NULL)); - continue; - } - nsec3_rrset = domain_find_rrset(domain, zone, TYPE_NSEC3); - if(!nsec3_rrset) { - log_msg(LOG_ERR, "%s NSEC3PARAM entry %d: hash(apex) has no NSEC3 RRset", - dname_to_string(domain_dname(zone->apex), NULL), (int)i); - continue; - } - detect_nsec3_params(rr, &salt1, &saltlen1, &iter1); - /* find SOA bit enabled nsec3, with the same settings */ - for(j=0; j < nsec3_rrset->rr_count; j++) { - const unsigned char *salt2; - int saltlen2, iter2; - if(!nsec3_has_soa(&nsec3_rrset->rrs[j])) - continue; - /* check params OK. Ignores the optout bit. */ - detect_nsec3_params(&nsec3_rrset->rrs[j], - &salt2, &saltlen2, &iter2); - if(saltlen1 == saltlen2 && iter1 == iter2 && - rdata_atom_data(rr->rdatas[0])[0] == /* algo */ - rdata_atom_data(nsec3_rrset->rrs[j].rdatas[0])[0] - && memcmp(salt1, salt2, saltlen1) == 0) { - /* found it */ - DEBUG(DEBUG_QUERY, 1, (LOG_INFO, - "detected NSEC3 for zone %s saltlen=%d iter=%d", - dname_to_string(domain_dname( - zone->apex),0), saltlen2, iter2)); - region_destroy(tmpregion); - return &nsec3_rrset->rrs[j]; - } + hashed_apex = nsec3_b32_create(tmpregion, zone, h); + domain = domain_table_find(namedb->domains, hashed_apex); + if(!domain) { + log_msg(LOG_ERR, "%s NSEC3PARAM entry has no hash(apex).", + domain_to_string(zone->apex)); + log_msg(LOG_ERR, "hash(apex)= %s", + dname_to_string(hashed_apex, NULL)); + region_destroy(tmpregion); + return NULL; + } + nsec3_rrset = domain_find_rrset(domain, zone, TYPE_NSEC3); + if(!nsec3_rrset) { + log_msg(LOG_ERR, "%s NSEC3PARAM entry: hash(apex) has no NSEC3 RRset.", + domain_to_string(zone->apex)); + log_msg(LOG_ERR, "hash(apex)= %s", + dname_to_string(hashed_apex, NULL)); + region_destroy(tmpregion); + return NULL; + } + for(j=0; j<nsec3_rrset->rr_count; j++) { + if(nsec3_has_soa(&nsec3_rrset->rrs[j])) { + region_destroy(tmpregion); + return &nsec3_rrset->rrs[j]; } - log_msg(LOG_ERR, "%s NSEC3PARAM entry %d: hash(apex) no NSEC3 with SOAbit", - dname_to_string(domain_dname(zone->apex), NULL), (int)i); } + log_msg(LOG_ERR, "%s NSEC3PARAM entry: hash(apex) NSEC3 has no SOA flag.", + domain_to_string(zone->apex)); + log_msg(LOG_ERR, "hash(apex)= %s", + dname_to_string(hashed_apex, NULL)); region_destroy(tmpregion); return NULL; } -/* check that the rrset has an NSEC3 that uses the same parameters as the - zone is using. Pass NSEC3 rrset, and zone must have nsec3_rrset set. - if you pass NULL then 0 is returned. */ -static int -nsec3_rrset_params_ok(rr_type* base, rrset_type* rrset) +static struct rr* +udb_zone_find_nsec3param(udb_base* udb, udb_ptr* uz, struct zone* z) { - rdata_atom_type* prd; - size_t i; - if(!rrset) - return 0; /* without rrset, no matching params either */ - assert(rrset && rrset->zone && (base || rrset->zone->nsec3_soa_rr)); - if(!base) - base = rrset->zone->nsec3_soa_rr; - prd = base->rdatas; - for(i=0; i < rrset->rr_count; ++i) { + udb_ptr urr; + unsigned i; + rrset_type* rrset = domain_find_rrset(z->apex, z, TYPE_NSEC3PARAM); + if(!rrset) /* no NSEC3PARAM in mem */ + return NULL; + udb_ptr_new(&urr, udb, &ZONE(uz)->nsec3param); + if(!urr.data || RR(&urr)->len < 5) { + /* no NSEC3PARAM in udb */ + udb_ptr_unlink(&urr, udb); + return NULL; + } + /* find matching NSEC3PARAM RR in memory */ + for(i=0; i<rrset->rr_count; i++) { + /* if this RR matches the udb RR then we are done */ rdata_atom_type* rd = rrset->rrs[i].rdatas; - assert(rrset->rrs[i].type == TYPE_NSEC3); - if(rdata_atom_data(rd[0])[0] == - rdata_atom_data(prd[0])[0] && /* hash algo */ - rdata_atom_data(rd[2])[0] == - rdata_atom_data(prd[2])[0] && /* iterations 0 */ - rdata_atom_data(rd[2])[1] == - rdata_atom_data(prd[2])[1] && /* iterations 1 */ - rdata_atom_data(rd[3])[0] == - rdata_atom_data(prd[3])[0] && /* salt length */ - memcmp(rdata_atom_data(rd[3])+1, - rdata_atom_data(prd[3])+1, rdata_atom_data(rd[3])[0]) - == 0 ) - { - /* this NSEC3 matches nsec3 parameters from zone */ - return 1; + if(RR(&urr)->wire[0] == rdata_atom_data(rd[0])[0] && /*alg*/ + RR(&urr)->wire[1] == rdata_atom_data(rd[1])[0] && /*flg*/ + RR(&urr)->wire[2] == rdata_atom_data(rd[2])[0] && /*iter*/ + RR(&urr)->wire[3] == rdata_atom_data(rd[2])[1] && + RR(&urr)->wire[4] == rdata_atom_data(rd[3])[0] && /*slen*/ + RR(&urr)->len >= 5 + RR(&urr)->wire[4] && + memcmp(RR(&urr)->wire+5, rdata_atom_data(rd[3])+1, + rdata_atom_data(rd[3])[0]) == 0) { + udb_ptr_unlink(&urr, udb); + return &rrset->rrs[i]; } } - return 0; + udb_ptr_unlink(&urr, udb); + return NULL; +} + +void +nsec3_find_zone_param(struct namedb* db, struct zone* zone, udb_ptr* z) +{ + /* get nsec3param RR from udb */ + zone->nsec3_param = udb_zone_find_nsec3param(db->udb, z, zone); } -#ifdef FULL_PREHASH +/* check params ok for one RR */ +static int +nsec3_rdata_params_ok(rdata_atom_type* prd, rdata_atom_type* rd) +{ + return (rdata_atom_data(rd[0])[0] == + rdata_atom_data(prd[0])[0] && /* hash algo */ + rdata_atom_data(rd[2])[0] == + rdata_atom_data(prd[2])[0] && /* iterations 0 */ + rdata_atom_data(rd[2])[1] == + rdata_atom_data(prd[2])[1] && /* iterations 1 */ + rdata_atom_data(rd[3])[0] == + rdata_atom_data(prd[3])[0] && /* salt length */ + memcmp(rdata_atom_data(rd[3])+1, + rdata_atom_data(prd[3])+1, rdata_atom_data(rd[3])[0]) + == 0 ); +} int -nsec3_find_cover(namedb_type* db, zone_type* zone, - const dname_type* hashname, domain_type** result) +nsec3_rr_uses_params(rr_type* rr, zone_type* zone) { - rrset_type *rrset; - domain_type *walk; - domain_type *closest_match; - domain_type *closest_encloser; - int exact; + if(!rr || rr->rdata_count < 4) + return 0; + return nsec3_rdata_params_ok(zone->nsec3_param->rdatas, rr->rdatas); +} - assert(result); - assert(zone->nsec3_soa_rr); - - exact = domain_table_search( - db->domains, hashname, &closest_match, &closest_encloser); - /* exact match of hashed domain name + it has an NSEC3? */ - if(exact && - nsec3_rrset_params_ok(NULL, - domain_find_rrset(closest_encloser, zone, TYPE_NSEC3))) { - *result = closest_encloser; - assert(*result != 0); - return 1; +int +nsec3_in_chain_count(domain_type* domain, zone_type* zone) +{ + rrset_type* rrset = domain_find_rrset(domain, zone, TYPE_NSEC3); + unsigned i; + int count = 0; + if(!rrset || !zone->nsec3_param) + return 0; /* no NSEC3s, none in the chain */ + for(i=0; i<rrset->rr_count; i++) { + if(nsec3_rr_uses_params(&rrset->rrs[i], zone)) + count++; } + return count; +} - /* find covering NSEC3 record, lexicographically before the closest match */ - /* use nsec3_lookup to jumpstart the search */ - walk = closest_match->nsec3_lookup; - rrset = 0; - while(walk && dname_is_subdomain(domain_dname(walk), domain_dname(zone->apex))) - { - if(nsec3_rrset_params_ok(NULL, - domain_find_rrset(walk, zone, TYPE_NSEC3))) { - /* this rrset is OK NSEC3, exit while */ - rrset = domain_find_rrset(walk, zone, TYPE_NSEC3); - break; +struct domain* +nsec3_chain_find_prev(struct zone* zone, struct domain* domain) +{ + if(domain->nsec3 && domain->nsec3->nsec3_node.key) { + /* see if there is a prev */ + rbnode_t* r = rbtree_previous(&domain->nsec3->nsec3_node); + if(r != RBTREE_NULL) { + /* found a previous, which is not the root-node in + * the prehash tree (and thus points to the tree) */ + return (domain_type*)r->key; } - walk = domain_previous(walk); } - if(rrset) - *result = walk; - else { - /* - * There are no NSEC3s before the closest match. - * so the hash name is before the first NSEC3 record in the zone. - * use last NSEC3, which covers the wraparound in hash space - * - * Since the zone has an NSEC3 with the SOA bit set for NSEC3 to turn on, - * there is also a last nsec3, so find_cover always assigns *result!=0. - */ - *result = zone->nsec3_last; + if(zone->nsec3_last) + return zone->nsec3_last; + return NULL; +} + +void +nsec3_clear_precompile(struct namedb* db, zone_type* zone) +{ + domain_type* walk; + /* clear prehash items (there must not be items for other zones) */ + prehash_clear(db->domains); + /* clear trees */ + hash_tree_clear(zone->nsec3tree); + hash_tree_clear(zone->hashtree); + hash_tree_clear(zone->wchashtree); + hash_tree_clear(zone->dshashtree); + /* wipe hashes */ + + /* wipe precompile */ + walk = zone->apex; + while(walk && domain_is_subdomain(walk, zone->apex)) { + if(walk->nsec3) { + if(nsec3_domain_part_of_zone(walk, zone)) { + walk->nsec3->nsec3_node.key = NULL; + walk->nsec3->nsec3_cover = NULL; + walk->nsec3->nsec3_wcard_child_cover = NULL; + walk->nsec3->nsec3_is_exact = 0; + walk->nsec3->have_nsec3_hash = 0; + walk->nsec3->have_nsec3_wc_hash = 0; + walk->nsec3->hash_node.key = NULL; + walk->nsec3->wchash_node.key = NULL; + } + if(!walk->parent || + nsec3_domain_part_of_zone(walk->parent, zone)) { + walk->nsec3->nsec3_ds_parent_cover = NULL; + walk->nsec3->nsec3_ds_parent_is_exact = 0; + walk->nsec3->have_nsec3_ds_parent_hash = 0; + walk->nsec3->dshash_node.key = NULL; + } + } + walk = domain_next(walk); + } + zone->nsec3_last = NULL; +} + +/* see if domain name is part of (existing names in) the nsec3 zone */ +int +nsec3_domain_part_of_zone(domain_type* d, zone_type* z) +{ + while(d) { + if(d->is_apex) + return (z->apex == d); /* zonecut, if right zone*/ + d = d->parent; } - assert(*result != 0); return 0; } -#else +/* condition when a domain is precompiled */ +int +nsec3_condition_hash(domain_type* d, zone_type* z) +{ + return d->is_existing && !domain_has_only_NSEC3(d, z) && + nsec3_domain_part_of_zone(d, z) && !domain_is_glue(d, z); +} + +/* condition when a domain is ds precompiled */ +int +nsec3_condition_dshash(domain_type* d, zone_type* z) +{ + return d->is_existing && !domain_has_only_NSEC3(d, z) && + (domain_find_rrset(d, z, TYPE_DS) || + domain_find_rrset(d, z, TYPE_NS)) && d != z->apex; +} + +zone_type* +nsec3_tree_zone(namedb_type* db, domain_type* d) +{ + /* see nsec3_domain_part_of_zone; domains part of zone that has + * apex above them */ + /* this does not use the rrset->zone pointer because there may be + * no rrsets left at apex (no SOA), e.g. during IXFR */ + while(d) { + if(d->is_apex) { + /* we can try a SOA if its present (faster than tree)*/ + /* DNSKEY and NSEC3PARAM are also good indicators */ + rrset_type *rrset; + for (rrset = d->rrsets; rrset; rrset = rrset->next) + if (rrset_rrtype(rrset) == TYPE_SOA || + rrset_rrtype(rrset) == TYPE_DNSKEY || + rrset_rrtype(rrset) == TYPE_NSEC3PARAM) + return rrset->zone; + return namedb_find_zone(db, d->dname); + } + d = d->parent; + } + return NULL; +} + +zone_type* +nsec3_tree_dszone(namedb_type* db, domain_type* d) +{ + /* the DStree does not contain nodes with d==z->apex */ + if(d->is_apex) + d = d->parent; + return nsec3_tree_zone(db, d); +} int -nsec3_find_cover(namedb_type* ATTR_UNUSED(db), zone_type* zone, - const dname_type* hashname, struct nsec3_domain **result) +nsec3_find_cover(zone_type* zone, uint8_t* hash, size_t hashlen, + domain_type** result) { - rbnode_t *node; + rbnode_t* r = NULL; int exact; + domain_type d; + uint8_t n[48]; + + /* nsec3tree is sorted by b32 encoded domain name of the NSEC3 */ + b32_ntop(hash, hashlen, (char*)(n+5), sizeof(n)-5); + d.dname = (dname_type*)n; + n[0] = 34; /* name_size */ + n[1] = 2; /* label_count */ + n[2] = 0; /* label_offset[0] */ + n[3] = 0; /* label_offset[1] */ + n[4] = 32; /* label-size[0] */ assert(result); - if (!zone->nsec3_domains) - return 0; - - exact = rbtree_find_less_equal(zone->nsec3_domains, hashname, &node); - if (!node) { - exact = 0; - node = rbtree_last(zone->nsec3_domains); - } + assert(zone->nsec3_param && zone->nsec3tree); - while (node != RBTREE_NULL) { - struct rrset *nsec3_rrset; - struct nsec3_domain *nsec3_domain = - (struct nsec3_domain *) node; - nsec3_rrset = domain_find_rrset(nsec3_domain->nsec3_domain, - zone, TYPE_NSEC3); - if (!nsec3_rrset) { - /* - * RRset in zone->nsec3_domains whose type != NSEC3 - * If we get here, something is seriously wrong! - */ - return 0; - } - if (nsec3_rrset_params_ok(NULL, nsec3_rrset) != 0) { - *result = nsec3_domain; - return exact; - } - exact = 0; /* No match, so we're looking for closest match */ - node = rbtree_previous(node); + exact = rbtree_find_less_equal(zone->nsec3tree, &d, &r); + if(r) { + *result = (domain_type*)r->key; + } else { + *result = zone->nsec3_last; } - /* - * If we reach this point, *result == NULL. This should - * never happen since the zone should have one NSEC3 record with - * the SOA bit set, which matches a NSEC3PARAM RR in the zone. - */ return exact; } -#endif -#ifdef FULL_PREHASH -static void -prehash_domain_r(namedb_type* db, zone_type* zone, - domain_type* domain, region_type* region) +void +nsec3_precompile_domain(struct namedb* db, struct domain* domain, + struct zone* zone, region_type* tmpregion) { - int exact; - const dname_type *wcard, *wcard_child, *hashname; domain_type* result = 0; - if(!zone->nsec3_soa_rr) - { - /* set to 0 (in case NSEC3 removed after an update) */ - domain->nsec3_is_exact = 0; - domain->nsec3_cover = NULL; - domain->nsec3_wcard_child_cover = NULL; - return; - } + int exact; + allocate_domain_nsec3(db->domains, domain); + + /* hash it */ + nsec3_lookup_hash_and_wc(zone, domain_dname(domain), domain, tmpregion); - hashname = nsec3_hash_dname(region, zone, domain_dname(domain)); - exact = nsec3_find_cover(db, zone, hashname, &result); - domain->nsec3_cover = result; + /* add into tree */ + zone_add_domain_in_hash_tree(db->region, &zone->hashtree, + cmp_hash_tree, domain, &domain->nsec3->hash_node); + zone_add_domain_in_hash_tree(db->region, &zone->wchashtree, + cmp_wchash_tree, domain, &domain->nsec3->wchash_node); + + /* lookup in tree cover ptr (or exact) */ + exact = nsec3_find_cover(zone, domain->nsec3->nsec3_hash, + sizeof(domain->nsec3->nsec3_hash), &result); + domain->nsec3->nsec3_cover = result; if(exact) - domain->nsec3_is_exact = 1; - else domain->nsec3_is_exact = 0; + domain->nsec3->nsec3_is_exact = 1; + else domain->nsec3->nsec3_is_exact = 0; /* find cover for *.domain for wildcard denial */ - wcard = dname_parse(region, "*"); - wcard_child = dname_concatenate(region, wcard, domain_dname(domain)); - hashname = nsec3_hash_dname(region, zone, wcard_child); - exact = nsec3_find_cover(db, zone, hashname, &result); - domain->nsec3_wcard_child_cover = result; - - if(exact && !domain_wildcard_child(domain)) - { - /* We found an exact match for the *.domain NSEC3 hash, - * but the domain wildcard child (*.domain) does not exist. - * Thus there is a hash collision. It will cause servfail - * for NXdomain queries below this domain. - */ - log_msg(LOG_WARNING, "prehash: collision of wildcard " - "denial for %s. Sign zone with different salt " - "to remove collision.", - dname_to_string(domain_dname(domain),0)); - } + exact = nsec3_find_cover(zone, domain->nsec3->nsec3_wc_hash, + sizeof(domain->nsec3->nsec3_wc_hash), &result); + domain->nsec3->nsec3_wcard_child_cover = result; } -#else - -static void -prehash_domain_r(namedb_type* db, zone_type* zone, - domain_type* domain, region_type* region) +void +nsec3_precompile_domain_ds(struct namedb* db, struct domain* domain, + struct zone* zone) { + domain_type* result = 0; int exact; - const dname_type *hashname; - struct nsec3_domain* result = NULL; - - domain->nsec3_cover = NULL; - hashname = nsec3_hash_dname(region, zone, domain_dname(domain)); - exact = nsec3_find_cover(db, zone, hashname, &result); - if (result && exact) - { - result->covers = domain; - domain->nsec3_cover = result->nsec3_domain; - } - return; -} -#endif - -static void -prehash_domain(namedb_type* db, zone_type* zone, - domain_type* domain, region_type* region) -{ - prehash_domain_r(db, zone, domain, region); + allocate_domain_nsec3(db->domains, domain); + + /* hash it : it could have different hash parameters then the + other hash for this domain name */ + nsec3_lookup_hash_ds(zone, domain_dname(domain), domain); + /* lookup in tree cover ptr (or exact) */ + exact = nsec3_find_cover(zone, domain->nsec3->nsec3_ds_parent_hash, + sizeof(domain->nsec3->nsec3_ds_parent_hash), &result); + if(exact) + domain->nsec3->nsec3_ds_parent_is_exact = 1; + else domain->nsec3->nsec3_ds_parent_is_exact = 0; + domain->nsec3->nsec3_ds_parent_cover = result; + /* add into tree */ + zone_add_domain_in_hash_tree(db->region, &zone->dshashtree, + cmp_dshash_tree, domain, &domain->nsec3->dshash_node); } -#ifdef FULL_PREHASH static void -prehash_ds(namedb_type* db, zone_type* zone, - domain_type* domain, region_type* region) +parse_nsec3_name(const dname_type* dname, uint8_t* hash, size_t buflen) { - domain_type* result = 0; - const dname_type* hashname; - int exact; - - if(!zone->nsec3_soa_rr) { - domain->nsec3_ds_parent_is_exact = 0; - domain->nsec3_ds_parent_cover = NULL; + /* first label must be the match, */ + size_t lablen = (buflen-1) * 8 / 5; + const uint8_t* wire = dname_name(dname); + assert(lablen == 32 && buflen == NSEC3_HASH_LEN+1); + /* labels of length 32 for SHA1, and must have space+1 for convert */ + if(wire[0] != lablen) { + /* not NSEC3 */ + memset(hash, 0, buflen); return; } - - /* hash again, other zone could have different hash parameters */ - hashname = nsec3_hash_dname(region, zone, domain_dname(domain)); - exact = nsec3_find_cover(db, zone, hashname, &result); - if(exact) - domain->nsec3_ds_parent_is_exact = 1; - else domain->nsec3_ds_parent_is_exact = 0; - domain->nsec3_ds_parent_cover = result; + (void)b32_pton((char*)wire+1, hash, buflen); } -#endif -#ifndef FULL_PREHASH -struct domain * -find_last_nsec3_domain(struct zone *zone) +void +nsec3_precompile_nsec3rr(namedb_type* db, struct domain* domain, + struct zone* zone) { - rbnode_t *node; - if (zone->nsec3_domains == NULL) { - return NULL; + allocate_domain_nsec3(db->domains, domain); + /* add into nsec3tree */ + zone_add_domain_in_hash_tree(db->region, &zone->nsec3tree, + cmp_nsec3_tree, domain, &domain->nsec3->nsec3_node); + /* fixup the last in the zone */ + if(rbtree_last(zone->nsec3tree)->key == domain) { + zone->nsec3_last = domain; } - node = rbtree_last(zone->nsec3_domains); - if (node == RBTREE_NULL) { - return NULL; - } - return ((struct nsec3_domain *) node)->nsec3_domain; } void -prehash_zone_incremental(struct namedb *db, struct zone *zone) +nsec3_precompile_newparam(namedb_type* db, zone_type* zone) { - region_type *temp_region; - rbnode_t *node; - /* find zone NSEC3PARAM settings */ - zone->nsec3_soa_rr = find_zone_nsec3(db, zone); - if (zone->nsec3_soa_rr == NULL) { - zone->nsec3_last = NULL; - return; - } - if (db->nsec3_mod_domains == NULL) { - return; - } - zone->nsec3_last = find_last_nsec3_domain(zone); - temp_region = region_create(xalloc, free); - node = rbtree_first(db->nsec3_mod_domains); - while (node != RBTREE_NULL) { - struct nsec3_mod_domain *nsec3_mod_domain = - (struct nsec3_mod_domain *) node; - struct domain *walk = nsec3_mod_domain->domain; - struct domain *domain_zone_apex; - - if (!walk || - (dname_is_subdomain(domain_dname(walk), - domain_dname(zone->apex)) == 0)) { - node = rbtree_next(node); - continue; + region_type* tmpregion = region_create(xalloc, free); + domain_type* walk; + time_t s = time(NULL); + unsigned n = 0, c = 0; + + /* add nsec3s of chain to nsec3tree */ + for(walk=zone->apex; walk && domain_is_subdomain(walk, zone->apex); + walk = domain_next(walk)) { + n++; + if(nsec3_in_chain_count(walk, zone) != 0) { + nsec3_precompile_nsec3rr(db, walk, zone); } - if (walk->nsec3_cover != NULL) { - node = rbtree_next(node); - continue; - } - /* Empty Terminal */ - if (!walk->is_existing) { - walk->nsec3_cover = NULL; - node = rbtree_next(node); - continue; - } - - /* - * Don't hash NSEC3 only nodes, unless possibly - * part of a weird case where node is empty nonterminal - * requiring NSEC3 but node name also is the hashed - * node name of another node requiring NSEC3. - * NSEC3 Empty Nonterminal with NSEC3 RRset present. - */ - if (domain_has_only_NSEC3(walk, zone) != 0) { - struct domain *next_domain = domain_next(walk); - if ((next_domain == NULL) || - (next_domain->parent != walk)) { - walk->nsec3_cover = NULL; - node = rbtree_next(node); - continue; - } + } + /* hash and precompile zone */ + for(walk=zone->apex; walk && domain_is_subdomain(walk, zone->apex); + walk = domain_next(walk)) { + if(nsec3_condition_hash(walk, zone)) { + nsec3_precompile_domain(db, walk, zone, tmpregion); + region_free_all(tmpregion); } - - /* - * Identify domain nodes that belong to the zone - * which are not glue records. What if you hit a - * record that's in two zones but which has no - * cut point between the zones. Not valid but - * someone is gonna try it sometime. - * This implementation doesn't link an NSEC3 - * record to the domain. - */ - domain_zone_apex = domain_find_zone_apex(walk); - if ((domain_zone_apex != NULL) && - (domain_zone_apex == zone->apex) && - (domain_is_glue(walk, zone) == 0)) { - - prehash_domain(db, zone, walk, temp_region); - region_free_all(temp_region); + if(nsec3_condition_dshash(walk, zone)) + nsec3_precompile_domain_ds(db, walk, zone); + if(++c % ZONEC_PCT_COUNT == 0 && time(NULL) > s + ZONEC_PCT_TIME) { + s = time(NULL); + VERBOSITY(1, (LOG_INFO, "nsec3 %s %d %%", + zone->opts->name, c*100/n)); } - - node = rbtree_next(node); } - namedb_nsec3_mod_domains_destroy(db); - region_destroy(temp_region); + region_destroy(tmpregion); } void -prehash_zone(struct namedb* db, struct zone* zone) +prehash_zone_complete(struct namedb* db, struct zone* zone) { - domain_type *walk; - domain_type *last_nsec3_node; - region_type *temp_region; - assert(db && zone); + udb_ptr udbz; + /* robust clear it */ + nsec3_clear_precompile(db, zone); /* find zone settings */ - zone->nsec3_soa_rr = find_zone_nsec3(db, zone); - if(!zone->nsec3_soa_rr) { + + assert(db && zone); + if(!udb_zone_search(db->udb, &udbz, dname_name(domain_dname( + zone->apex)), domain_dname(zone->apex)->name_size)) { + udb_ptr_init(&udbz, db->udb); /* zero the ptr */ + } + nsec3_find_zone_param(db, zone, &udbz); + if(!zone->nsec3_param || !check_apex_soa(db, zone)) { + zone->nsec3_param = NULL; zone->nsec3_last = NULL; + udb_ptr_unlink(&udbz, db->udb); return; } - temp_region = region_create(xalloc, free); + udb_ptr_unlink(&udbz, db->udb); + nsec3_precompile_newparam(db, zone); +} - /* go through entire zone and setup nsec3_lookup speedup */ - walk = zone->apex; - last_nsec3_node = NULL; - /* since we walk in sorted order, we pass all NSEC3s in sorted - order and we can set the lookup ptrs */ - while(walk && dname_is_subdomain( - domain_dname(walk), domain_dname(zone->apex))) - { - struct domain *domain_zone_apex; +static void +init_lookup_key_hash_tree(domain_type* d, uint8_t* hash) +{ memcpy(d->nsec3->nsec3_hash, hash, NSEC3_HASH_LEN); } - if (walk->nsec3_cover != NULL) { - walk = domain_next(walk); - continue; - } - /* Empty Terminal */ - if (walk->is_existing == 0) { - walk->nsec3_cover = NULL; - walk = domain_next(walk); - continue; - } +static void +init_lookup_key_wc_tree(domain_type* d, uint8_t* hash) +{ memcpy(d->nsec3->nsec3_wc_hash, hash, NSEC3_HASH_LEN); } - /* - * Don't hash NSEC3 only nodes, unless possibly - * part of a weird case where node is empty nonterminal - * requiring NSEC3 but node name also is the hashed - * node name of another node requiring NSEC3. - * NSEC3 Empty Nonterminal with NSEC3 RRset present. - */ - if (domain_has_only_NSEC3(walk, zone)) { - struct domain *next_domain = domain_next(walk); - if ((next_domain == NULL) || - (next_domain->parent != walk)) { - walk->nsec3_cover = NULL; - walk = domain_next(walk); - continue; - } - } +static void +init_lookup_key_ds_tree(domain_type* d, uint8_t* hash) +{ memcpy(d->nsec3->nsec3_ds_parent_hash, hash, NSEC3_HASH_LEN); } - /* - * Identify domain nodes that belong to the zone - * which are not glue records. What if you hit a - * record that's in two zones but which has no - * cut point between the zones. Not valid but - * someone is gonna try it sometime. - * This implementation doesn't link an NSEC3 - * record to the domain. - */ - domain_zone_apex = domain_find_zone_apex(walk); - if ((domain_zone_apex != NULL) && - (domain_zone_apex == zone->apex) && - (domain_is_glue(walk, zone) == 0)) - { - prehash_domain(db, zone, walk, temp_region); - region_free_all(temp_region); - } - walk = domain_next(walk); +/* find first in the tree and true if the first to process it */ +static int +process_first(rbtree_t* tree, uint8_t* hash, rbnode_t** p, + void (*init)(domain_type*, uint8_t*)) +{ + domain_type d; + struct nsec3_domain_data n; + if(!tree) { + *p = RBTREE_NULL; + return 0; + } + d.nsec3 = &n; + init(&d, hash); + if(rbtree_find_less_equal(tree, &d, p)) { + /* found an exact match */ + return 1; } - region_destroy(temp_region); + if(!*p) /* before first, go from first */ + *p = rbtree_first(tree); + /* the inexact, smaller, match we found, does not itself need to + * be edited */ + else + *p = rbtree_next(*p); /* if this becomes NULL, nothing to do */ + return 0; } -#else - +/* set end pointer if possible */ static void -prehash_zone(struct namedb* db, struct zone* zone) +process_end(rbtree_t* tree, uint8_t* hash, rbnode_t** p, + void (*init)(domain_type*, uint8_t*)) { - domain_type *walk; - domain_type *last_nsec3_node; - region_type *temp_region; - assert(db && zone); - - /* find zone settings */ - zone->nsec3_soa_rr = find_zone_nsec3(db, zone); - if(!zone->nsec3_soa_rr) { - zone->nsec3_last = NULL; + domain_type d; + struct nsec3_domain_data n; + if(!tree) { + *p = RBTREE_NULL; return; } - temp_region = region_create(xalloc, free); - - /* go through entire zone and setup nsec3_lookup speedup */ - walk = zone->apex; - last_nsec3_node = NULL; - /* since we walk in sorted order, we pass all NSEC3s in sorted - order and we can set the lookup ptrs */ - while(walk && dname_is_subdomain( - domain_dname(walk), domain_dname(zone->apex))) - { - zone_type* z = domain_find_zone(walk); - if(z && z==zone) - { - if(domain_find_rrset(walk, zone, TYPE_NSEC3)) - last_nsec3_node = walk; - walk->nsec3_lookup = last_nsec3_node; - } - walk = domain_next(walk); + d.nsec3 = &n; + init(&d, hash); + if(rbtree_find_less_equal(tree, &d, p)) { + /* an exact match, fine, because this one does not get + * processed */ + return; } - zone->nsec3_last = last_nsec3_node; - - /* go through entire zone */ - walk = zone->apex; - while(walk && dname_is_subdomain( - domain_dname(walk), domain_dname(zone->apex))) - { - zone_type* z; - if(!walk->is_existing && domain_has_only_NSEC3(walk, zone)) { - walk->nsec3_cover = NULL; - walk->nsec3_wcard_child_cover = NULL; - walk = domain_next(walk); - continue; - } - z = domain_find_zone(walk); - if(z && z==zone && !domain_is_glue(walk, zone)) - { - prehash_domain(db, zone, walk, temp_region); - region_free_all(temp_region); - } - /* prehash the DS (parent zone) */ - if(domain_find_rrset(walk, zone, TYPE_DS) || - (domain_find_rrset(walk, zone, TYPE_NS) && - walk != zone->apex)) - { - assert(walk != zone->apex /* DS must be above zone cut */); - prehash_ds(db, zone, walk, temp_region); - region_free_all(temp_region); - } - walk = domain_next(walk); + /* inexact element, but if NULL, until first element in tree */ + if(!*p) { + *p = rbtree_first(tree); + return; } - region_destroy(temp_region); + /* inexact match, use next element, if possible, the smaller + * element is part of the range */ + *p = rbtree_next(*p); + /* if next returns null, we go until the end of the tree */ } -#endif -void -prehash(struct namedb* db, int updated_only) +/* prehash domains in hash range start to end */ +static void +process_range(zone_type* zone, domain_type* start, + domain_type* end, domain_type* nsec3) { - zone_type *z; - time_t end, start = time(NULL); - int count = 0; - for(z = db->zones; z; z = z->next) - { - if(!updated_only || z->updated) { - prehash_zone(db, z); - if(z->nsec3_soa_rr) - count++; + /* start NULL means from first in tree */ + /* end NULL means to last in tree */ + rbnode_t *p = RBTREE_NULL, *pwc = RBTREE_NULL, *pds = RBTREE_NULL; + rbnode_t *p_end = RBTREE_NULL, *pwc_end = RBTREE_NULL, *pds_end = RBTREE_NULL; + /* because the nodes are on the prehashlist, the domain->nsec3 is + * already allocated, and we need not allocate it here */ + /* set start */ + if(start) { + uint8_t hash[NSEC3_HASH_LEN+1]; + parse_nsec3_name(domain_dname(start), hash, sizeof(hash)); + /* if exact match on first, set is_exact */ + if(process_first(zone->hashtree, hash, &p, init_lookup_key_hash_tree)) { + ((domain_type*)(p->key))->nsec3->nsec3_cover = nsec3; + ((domain_type*)(p->key))->nsec3->nsec3_is_exact = 1; + p = rbtree_next(p); } + (void)process_first(zone->wchashtree, hash, &pwc, init_lookup_key_wc_tree); + if(process_first(zone->dshashtree, hash, &pds, init_lookup_key_ds_tree)){ + ((domain_type*)(pds->key))->nsec3-> + nsec3_ds_parent_cover = nsec3; + ((domain_type*)(pds->key))->nsec3-> + nsec3_ds_parent_is_exact = 1; + pds = rbtree_next(pds); + } + } else { + if(zone->hashtree) + p = rbtree_first(zone->hashtree); + if(zone->wchashtree) + pwc = rbtree_first(zone->wchashtree); + if(zone->dshashtree) + pds = rbtree_first(zone->dshashtree); + } + /* set end */ + if(end) { + uint8_t hash[NSEC3_HASH_LEN+1]; + parse_nsec3_name(domain_dname(end), hash, sizeof(hash)); + process_end(zone->hashtree, hash, &p_end, init_lookup_key_hash_tree); + process_end(zone->wchashtree, hash, &pwc_end, init_lookup_key_wc_tree); + process_end(zone->dshashtree, hash, &pds_end, init_lookup_key_ds_tree); } - end = time(NULL); - if(count > 0) - VERBOSITY(1, (LOG_INFO, "nsec3-prepare took %lld " - "seconds for %d zones.", (long long)(end-start), count)); -} + /* precompile */ + while(p != RBTREE_NULL && p != p_end) { + ((domain_type*)(p->key))->nsec3->nsec3_cover = nsec3; + ((domain_type*)(p->key))->nsec3->nsec3_is_exact = 0; + p = rbtree_next(p); + } + while(pwc != RBTREE_NULL && pwc != pwc_end) { + ((domain_type*)(pwc->key))->nsec3-> + nsec3_wcard_child_cover = nsec3; + pwc = rbtree_next(pwc); + } + while(pds != RBTREE_NULL && pds != pds_end) { + ((domain_type*)(pds->key))->nsec3-> + nsec3_ds_parent_cover = nsec3; + ((domain_type*)(pds->key))->nsec3-> + nsec3_ds_parent_is_exact = 0; + pds = rbtree_next(pds); + } +} -#ifndef FULL_PREHASH +/* prehash a domain from the prehash list */ static void -nsec3_hash_and_find_cover(struct region *region, - struct namedb *db, const struct dname *domain_dname, - struct zone *zone, int *exact, struct domain **result) +process_prehash_domain(domain_type* domain, zone_type* zone) { - dname_type const *hash_dname; - struct nsec3_domain *nsec3_domain; - - *result = NULL; - *exact = 0; - hash_dname = nsec3_hash_dname(region, zone, domain_dname); - *exact = nsec3_find_cover(db, zone, hash_dname, &nsec3_domain); - if (nsec3_domain != NULL) { - *result = nsec3_domain->nsec3_domain; - } - return; + /* in the hashtree, wchashtree, dshashtree walk through to next NSEC3 + * and set precompile pointers to point to this domain (or is_exact), + * the first domain can be is_exact. If it is the last NSEC3, also + * process the initial part (before the first) */ + rbnode_t* nx; + + /* this domain is part of the prehash list and therefore the + * domain->nsec3 is allocated and need not be allocated here */ + assert(domain->nsec3 && domain->nsec3->nsec3_node.key); + nx = rbtree_next(&domain->nsec3->nsec3_node); + if(nx != RBTREE_NULL) { + /* process until next nsec3 */ + domain_type* end = (domain_type*)nx->key; + process_range(zone, domain, end, domain); + } else { + /* first is root, but then comes the first nsec3 */ + domain_type* first = (domain_type*)(rbtree_first( + zone->nsec3tree)->key); + /* last in zone */ + process_range(zone, domain, NULL, domain); + /* also process before first in zone */ + process_range(zone, NULL, first, domain); + } } -static void -nsec3_hash_and_find_wild_cover(struct region *region, - struct namedb *db, struct domain *domain, - struct zone *zone, int *exact, struct domain **result) +void prehash_zone(struct namedb* db, struct zone* zone) { - struct dname const *wcard_child; - /* find cover for *.domain for wildcard denial */ - (void) dname_make_wildcard(region, domain_dname(domain), - &wcard_child); - nsec3_hash_and_find_cover(region, db, wcard_child, zone, exact, - result); - if ((*exact != 0) && - (domain_wildcard_child(domain) == NULL)) { - /* We found an exact match for the *.domain NSEC3 hash, - * but the domain wildcard child (*.domain) does not exist. - * Thus there is a hash collision. It will cause servfail - * for NXdomain queries below this domain. - */ - log_msg(LOG_WARNING, - "collision of wildcard denial for %s. " - "Sign zone with different salt to remove collision.", - dname_to_string(domain_dname(domain), NULL)); + domain_type* d; + if(!zone->nsec3_param) { + prehash_clear(db->domains); + return; } -} -#endif + /* process prehash list */ + for(d = db->domains->prehash_list; d; d = d->nsec3->prehash_next) { + process_prehash_domain(d, zone); + } + /* clear prehash list */ + prehash_clear(db->domains); + if(!check_apex_soa(db, zone)) { + zone->nsec3_param = NULL; + zone->nsec3_last = NULL; + } +} /* add the NSEC3 rrset to the query answer at the given domain */ static void -nsec3_add_rrset(struct query *query, struct answer *answer, +nsec3_add_rrset(struct query* query, struct answer* answer, rr_section_type section, struct domain* domain) { if(domain) { @@ -714,30 +778,20 @@ nsec3_add_rrset(struct query *query, struct answer *answer, /* this routine does hashing at query-time. slow. */ static void -nsec3_add_nonexist_proof(struct query *query, struct answer *answer, - struct domain *encloser, struct namedb* db, const dname_type* qname) +nsec3_add_nonexist_proof(struct query* query, struct answer* answer, + struct domain* encloser, const dname_type* qname) { - const dname_type *to_prove; -#ifdef FULL_PREHASH - const dname_type *hashed; -#else - int exact = 0; -#endif - domain_type *cover = NULL; + uint8_t hash[NSEC3_HASH_LEN]; + const dname_type* to_prove; + domain_type* cover=0; assert(encloser); /* if query=a.b.c.d encloser=c.d. then proof needed for b.c.d. */ /* if query=a.b.c.d encloser=*.c.d. then proof needed for b.c.d. */ to_prove = dname_partial_copy(query->region, qname, dname_label_match_count(qname, domain_dname(encloser))+1); /* generate proof that one label below closest encloser does not exist */ -#ifdef FULL_PREHASH - hashed = nsec3_hash_dname(query->region, query->zone, to_prove); - if(nsec3_find_cover(db, query->zone, hashed, &cover)) -#else - nsec3_hash_and_find_cover(query->region, db, to_prove, query->zone, - &exact, &cover); - if (exact) -#endif + nsec3_hash_and_store(query->zone, to_prove, hash); + if(nsec3_find_cover(query->zone, hash, sizeof(hash), &cover)) { /* exact match, hash collision */ /* the hashed name of the query corresponds to an existing name. */ @@ -746,7 +800,8 @@ nsec3_add_nonexist_proof(struct query *query, struct answer *answer, RCODE_SET(query->packet, RCODE_SERVFAIL); return; } - else if (cover) { + else + { /* cover proves the qname does not exist */ nsec3_add_rrset(query, answer, AUTHORITY_SECTION, cover); } @@ -754,139 +809,89 @@ nsec3_add_nonexist_proof(struct query *query, struct answer *answer, static void nsec3_add_closest_encloser_proof( - struct query *query, struct answer *answer, - struct domain *closest_encloser, struct namedb* db, - const dname_type* qname) + struct query* query, struct answer* answer, + struct domain* closest_encloser, const dname_type* qname) { if(!closest_encloser) return; /* prove that below closest encloser nothing exists */ - nsec3_add_nonexist_proof(query, answer, closest_encloser, db, qname); + nsec3_add_nonexist_proof(query, answer, closest_encloser, qname); /* proof that closest encloser exists */ -#ifdef FULL_PREHASH - if(closest_encloser->nsec3_is_exact) -#else - if(closest_encloser->nsec3_cover) -#endif + if(closest_encloser->nsec3 && closest_encloser->nsec3->nsec3_is_exact) nsec3_add_rrset(query, answer, AUTHORITY_SECTION, - closest_encloser->nsec3_cover); + closest_encloser->nsec3->nsec3_cover); } - void nsec3_answer_wildcard(struct query *query, struct answer *answer, - struct domain *wildcard, struct namedb* db, const dname_type* qname) + struct domain *wildcard, const dname_type* qname) { if(!wildcard) return; - if(!query->zone->nsec3_soa_rr) + if(!query->zone->nsec3_param) return; - nsec3_add_nonexist_proof(query, answer, wildcard, db, qname); + nsec3_add_nonexist_proof(query, answer, wildcard, qname); } - static void nsec3_add_ds_proof(struct query *query, struct answer *answer, struct domain *domain, int delegpt) { -#ifndef FULL_PREHASH - struct domain * ds_parent_cover = NULL; - int exact = 0; -#endif /* assert we are above the zone cut */ assert(domain != query->zone->apex); - -#ifdef FULL_PREHASH - if(domain->nsec3_ds_parent_is_exact) { + if(domain->nsec3 && domain->nsec3->nsec3_ds_parent_is_exact) { /* use NSEC3 record from above the zone cut. */ nsec3_add_rrset(query, answer, AUTHORITY_SECTION, - domain->nsec3_ds_parent_cover); - } else if (!delegpt && domain->nsec3_is_exact) { -#else - nsec3_hash_and_find_cover(query->region, NULL, domain_dname(domain), - query->zone, &exact, &ds_parent_cover); - if (exact) { - /* use NSEC3 record from above the zone cut. */ - if (ds_parent_cover) { - nsec3_add_rrset(query, answer, AUTHORITY_SECTION, - ds_parent_cover); - } - } else if (!delegpt && domain->nsec3_cover) { -#endif + domain->nsec3->nsec3_ds_parent_cover); + } else if (!delegpt && domain->nsec3 && domain->nsec3->nsec3_is_exact) { nsec3_add_rrset(query, answer, AUTHORITY_SECTION, - domain->nsec3_cover); + domain->nsec3->nsec3_cover); } else { /* prove closest provable encloser */ domain_type* par = domain->parent; - domain_type* prev_par = NULL; - assert(par); /* must be provable, thus there must be a parent */ + domain_type* prev_par = 0; -#ifdef FULL_PREHASH - while(!par->nsec3_is_exact) -#else - while(!par->nsec3_cover) -#endif + while(par && (!par->nsec3 || !par->nsec3->nsec3_is_exact)) { prev_par = par; par = par->parent; - assert(par); /* parent zone apex must be provable, thus this ends */ } + assert(par); /* parent zone apex must be provable, thus this ends */ + if(!par->nsec3) return; nsec3_add_rrset(query, answer, AUTHORITY_SECTION, - par->nsec3_cover); + par->nsec3->nsec3_cover); /* we took several steps to go to the provable parent, so the one below it has no exact nsec3, disprove it. disprove is easy, it has a prehashed cover ptr. */ - if(prev_par) { -#ifdef FULL_PREHASH - assert(prev_par != domain && !prev_par->nsec3_is_exact); + if(prev_par && prev_par->nsec3) { + assert(prev_par != domain && + !prev_par->nsec3->nsec3_is_exact); nsec3_add_rrset(query, answer, AUTHORITY_SECTION, - prev_par->nsec3_cover); -#else - struct domain *prev_parent_cover = NULL; - nsec3_hash_and_find_cover(query->region, NULL, - domain_dname(prev_par), query->zone, - &exact, &prev_parent_cover); - if (prev_parent_cover) { - nsec3_add_rrset(query, answer, - AUTHORITY_SECTION, prev_parent_cover); - } -#endif + prev_par->nsec3->nsec3_cover); } - - /* use NSEC3 record from above the zone cut. */ /* add optout range from parent zone */ /* note: no check of optout bit, resolver checks it */ -#ifdef FULL_PREHASH - nsec3_add_rrset(query, answer, AUTHORITY_SECTION, - domain->nsec3_ds_parent_cover); -#else - nsec3_add_rrset(query, answer, AUTHORITY_SECTION, - ds_parent_cover); -#endif + if(domain->nsec3) + nsec3_add_rrset(query, answer, AUTHORITY_SECTION, + domain->nsec3->nsec3_ds_parent_cover); } } - void -nsec3_answer_nodata(struct query *query, struct answer *answer, - struct domain *original) +nsec3_answer_nodata(struct query* query, struct answer* answer, + struct domain* original) { - if(!query->zone->nsec3_soa_rr) + if(!query->zone->nsec3_param) return; - /* nodata when asking for secure delegation */ if(query->qtype == TYPE_DS) { if(original == query->zone->apex) { /* DS at zone apex, but server not authoritative for parent zone */ /* so answer at the child zone level */ -#ifdef FULL_PREHASH - if(original->nsec3_is_exact) -#else - if(original->nsec3_cover) -#endif + if(original->nsec3 && original->nsec3->nsec3_is_exact) nsec3_add_rrset(query, answer, AUTHORITY_SECTION, - original->nsec3_cover); + original->nsec3->nsec3_cover); return; } /* query->zone must be the parent zone */ @@ -895,56 +900,32 @@ nsec3_answer_nodata(struct query *query, struct answer *answer, /* the nodata is result from a wildcard match */ else if (original==original->wildcard_child_closest_match && label_is_wildcard(dname_name(domain_dname(original)))) { -#ifndef FULL_PREHASH - struct domain* original_cover; - int exact; -#endif /* denial for wildcard is already there */ /* add parent proof to have a closest encloser proof for wildcard parent */ /* in other words: nsec3 matching closest encloser */ -#ifdef FULL_PREHASH - if(original->parent && original->parent->nsec3_is_exact) -#else - if(original->parent && original->parent->nsec3_cover) -#endif + if(original->parent && original->parent->nsec3 && + original->parent->nsec3->nsec3_is_exact) nsec3_add_rrset(query, answer, AUTHORITY_SECTION, - original->parent->nsec3_cover); - + original->parent->nsec3->nsec3_cover); /* proof for wildcard itself */ /* in other words: nsec3 matching source of synthesis */ -#ifdef FULL_PREHASH - nsec3_add_rrset(query, answer, AUTHORITY_SECTION, - original->nsec3_cover); -#else - original_cover = original->nsec3_cover; - if (!original_cover) { /* not exact */ - nsec3_hash_and_find_cover(query->region, NULL, - domain_dname(original), query->zone, - &exact, &original_cover); - } - if (original_cover) { - nsec3_add_rrset(query, answer, AUTHORITY_SECTION, - original_cover); - } -#endif - + if(original->nsec3) + nsec3_add_rrset(query, answer, AUTHORITY_SECTION, + original->nsec3->nsec3_cover); } - else { /* add nsec3 to prove rrset does not exist */ -#ifdef FULL_PREHASH - if(original->nsec3_is_exact) -#else - if (original->nsec3_cover != NULL) -#endif + else { /* add nsec3 to prove rrset does not exist */ + if(original->nsec3 && original->nsec3->nsec3_is_exact) { nsec3_add_rrset(query, answer, AUTHORITY_SECTION, - original->nsec3_cover); + original->nsec3->nsec3_cover); + } } } void nsec3_answer_delegation(struct query *query, struct answer *answer) { - if(!query->zone->nsec3_soa_rr) + if(!query->zone->nsec3_param) return; nsec3_add_ds_proof(query, answer, query->delegation_domain, 1); } @@ -972,14 +953,9 @@ domain_has_only_NSEC3(struct domain* domain, struct zone* zone) void nsec3_answer_authoritative(struct domain** match, struct query *query, struct answer *answer, struct domain* closest_encloser, - struct namedb* db, const dname_type* qname) + const dname_type* qname) { -#ifndef FULL_PREHASH - struct domain *cover_domain = NULL; - int exact = 0; -#endif - - if(!query->zone->nsec3_soa_rr) + if(!query->zone->nsec3_param) return; assert(match); /* there is a match, this has 1 RRset, which is NSEC3, but qtype is not. */ @@ -993,30 +969,17 @@ nsec3_answer_authoritative(struct domain** match, struct query *query, /* act as if the NSEC3 domain did not exist, name error */ *match = 0; /* all nsec3s are directly below the apex, that is closest encloser */ -#ifdef FULL_PREHASH - if(query->zone->apex->nsec3_is_exact) -#else - if(query->zone->apex->nsec3_cover) -#endif + if(query->zone->apex->nsec3 && + query->zone->apex->nsec3->nsec3_is_exact) nsec3_add_rrset(query, answer, AUTHORITY_SECTION, - query->zone->apex->nsec3_cover); - + query->zone->apex->nsec3->nsec3_cover); /* disprove the nsec3 record. */ - nsec3_add_rrset(query, answer, AUTHORITY_SECTION, - closest_encloser->nsec3_cover); + if(closest_encloser->nsec3) + nsec3_add_rrset(query, answer, AUTHORITY_SECTION, closest_encloser->nsec3->nsec3_cover); /* disprove a wildcard */ -#ifdef FULL_PREHASH - nsec3_add_rrset(query, answer, AUTHORITY_SECTION, - query->zone->apex->nsec3_wcard_child_cover); -#else - cover_domain = NULL; - nsec3_hash_and_find_cover(query->region, db, - domain_dname(closest_encloser), - query->zone, &exact, &cover_domain); - if (cover_domain) - nsec3_add_rrset(query, answer, AUTHORITY_SECTION, - cover_domain); -#endif + if(query->zone->apex->nsec3) + nsec3_add_rrset(query, answer, AUTHORITY_SECTION, + query->zone->apex->nsec3->nsec3_wcard_child_cover); if (domain_wildcard_child(query->zone->apex)) { /* wildcard exists below the domain */ /* wildcard and nsec3 domain clash. server failure. */ @@ -1036,19 +999,11 @@ nsec3_answer_authoritative(struct domain** match, struct query *query, } if(!*match) { /* name error, domain does not exist */ - nsec3_add_closest_encloser_proof(query, answer, - closest_encloser, db, qname); -#ifdef FULL_PREHASH - nsec3_add_rrset(query, answer, AUTHORITY_SECTION, - closest_encloser->nsec3_wcard_child_cover); -#else - cover_domain = NULL; - nsec3_hash_and_find_wild_cover(query->region, db, - closest_encloser, query->zone, &exact, &cover_domain); - if (cover_domain) + nsec3_add_closest_encloser_proof(query, answer, closest_encloser, + qname); + if(closest_encloser->nsec3) nsec3_add_rrset(query, answer, AUTHORITY_SECTION, - cover_domain); -#endif + closest_encloser->nsec3->nsec3_wcard_child_cover); } } |