summaryrefslogtreecommitdiff
path: root/usr.sbin/nsd/nsec3.c
diff options
context:
space:
mode:
Diffstat (limited to 'usr.sbin/nsd/nsec3.c')
-rw-r--r--usr.sbin/nsd/nsec3.c1397
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 = &paramset->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), &paramset->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);
}
}