xref: /openbsd-src/usr.sbin/nsd/nsec3.c (revision b71395ea3d4830c6fd338870b804059761b8292d)
162ac0c33Sjakob /*
262ac0c33Sjakob  * nsec3.c -- nsec3 handling.
362ac0c33Sjakob  *
4dd5b221eSsthen  * Copyright (c) 2001-2006, NLnet Labs. All rights reserved.
562ac0c33Sjakob  *
662ac0c33Sjakob  * See LICENSE for the license.
762ac0c33Sjakob  *
862ac0c33Sjakob  */
9dd5b221eSsthen #include "config.h"
1062ac0c33Sjakob #ifdef NSEC3
1162ac0c33Sjakob #include <stdio.h>
1262ac0c33Sjakob #include <stdlib.h>
1362ac0c33Sjakob 
1462ac0c33Sjakob #include "nsec3.h"
1562ac0c33Sjakob #include "iterated_hash.h"
1662ac0c33Sjakob #include "namedb.h"
1762ac0c33Sjakob #include "nsd.h"
1862ac0c33Sjakob #include "answer.h"
19dd5b221eSsthen #include "options.h"
2062ac0c33Sjakob 
21dd5b221eSsthen #define NSEC3_RDATA_BITMAP 5
2262ac0c33Sjakob 
23dd5b221eSsthen /* compare nsec3 hashes in nsec3 tree */
24dd5b221eSsthen static int
cmp_hash_tree(const void * x,const void * y)25dd5b221eSsthen cmp_hash_tree(const void* x, const void* y)
26dd5b221eSsthen {
27dd5b221eSsthen 	const domain_type* a = (const domain_type*)x;
28dd5b221eSsthen 	const domain_type* b = (const domain_type*)y;
29c1e73312Sflorian 	if(!a->nsec3) return (b->nsec3?-1:0);
30c1e73312Sflorian 	if(!b->nsec3) return 1;
31ee5153b7Sflorian 	if(!a->nsec3->hash_wc) return (b->nsec3->hash_wc?-1:0);
32ee5153b7Sflorian 	if(!b->nsec3->hash_wc) return 1;
33ee5153b7Sflorian 	return memcmp(a->nsec3->hash_wc->hash.hash,
34ee5153b7Sflorian 		b->nsec3->hash_wc->hash.hash, NSEC3_HASH_LEN);
35dd5b221eSsthen }
36dd5b221eSsthen 
37dd5b221eSsthen /* compare nsec3 hashes in nsec3 wc tree */
38dd5b221eSsthen static int
cmp_wchash_tree(const void * x,const void * y)39dd5b221eSsthen cmp_wchash_tree(const void* x, const void* y)
40dd5b221eSsthen {
41dd5b221eSsthen 	const domain_type* a = (const domain_type*)x;
42dd5b221eSsthen 	const domain_type* b = (const domain_type*)y;
43c1e73312Sflorian 	if(!a->nsec3) return (b->nsec3?-1:0);
44c1e73312Sflorian 	if(!b->nsec3) return 1;
45ee5153b7Sflorian 	if(!a->nsec3->hash_wc) return (b->nsec3->hash_wc?-1:0);
46ee5153b7Sflorian 	if(!b->nsec3->hash_wc) return 1;
47ee5153b7Sflorian 	return memcmp(a->nsec3->hash_wc->wc.hash,
48ee5153b7Sflorian 		b->nsec3->hash_wc->wc.hash, NSEC3_HASH_LEN);
49dd5b221eSsthen }
50dd5b221eSsthen 
51dd5b221eSsthen /* compare nsec3 hashes in nsec3 ds tree */
52dd5b221eSsthen static int
cmp_dshash_tree(const void * x,const void * y)53dd5b221eSsthen cmp_dshash_tree(const void* x, const void* y)
54dd5b221eSsthen {
55dd5b221eSsthen 	const domain_type* a = (const domain_type*)x;
56dd5b221eSsthen 	const domain_type* b = (const domain_type*)y;
57c1e73312Sflorian 	if(!a->nsec3) return (b->nsec3?-1:0);
58c1e73312Sflorian 	if(!b->nsec3) return 1;
59ee5153b7Sflorian 	if(!a->nsec3->ds_parent_hash) return (b->nsec3->ds_parent_hash?-1:0);
60ee5153b7Sflorian 	if(!b->nsec3->ds_parent_hash) return 1;
61ee5153b7Sflorian 	return memcmp(a->nsec3->ds_parent_hash->hash,
62ee5153b7Sflorian 		b->nsec3->ds_parent_hash->hash, NSEC3_HASH_LEN);
63dd5b221eSsthen }
64dd5b221eSsthen 
65dd5b221eSsthen /* compare base32-encoded nsec3 hashes in nsec3 rr tree, they are
66dd5b221eSsthen  * stored in the domain name of the node */
67dd5b221eSsthen static int
cmp_nsec3_tree(const void * x,const void * y)68dd5b221eSsthen cmp_nsec3_tree(const void* x, const void* y)
69dd5b221eSsthen {
70dd5b221eSsthen 	const domain_type* a = (const domain_type*)x;
71dd5b221eSsthen 	const domain_type* b = (const domain_type*)y;
72dd5b221eSsthen 	/* labelcount + 32long label */
73c1e73312Sflorian 	assert(dname_name(domain_dname_const(a))[0] == 32);
74c1e73312Sflorian 	assert(dname_name(domain_dname_const(b))[0] == 32);
75c1e73312Sflorian 	return memcmp(dname_name(domain_dname_const(a)), dname_name(domain_dname_const(b)), 33);
76dd5b221eSsthen }
77dd5b221eSsthen 
nsec3_zone_trees_create(struct region * region,zone_type * zone)78dd5b221eSsthen void nsec3_zone_trees_create(struct region* region, zone_type* zone)
79dd5b221eSsthen {
80dd5b221eSsthen 	if(!zone->nsec3tree)
81dd5b221eSsthen 		zone->nsec3tree = rbtree_create(region, cmp_nsec3_tree);
82dd5b221eSsthen 	if(!zone->hashtree)
83dd5b221eSsthen 		zone->hashtree = rbtree_create(region, cmp_hash_tree);
84dd5b221eSsthen 	if(!zone->wchashtree)
85dd5b221eSsthen 		zone->wchashtree = rbtree_create(region, cmp_wchash_tree);
86dd5b221eSsthen 	if(!zone->dshashtree)
87dd5b221eSsthen 		zone->dshashtree = rbtree_create(region, cmp_dshash_tree);
88dd5b221eSsthen }
89dd5b221eSsthen 
9062ac0c33Sjakob static void
detect_nsec3_params(rr_type * nsec3_apex,const unsigned char ** salt,int * salt_len,int * iter)9162ac0c33Sjakob detect_nsec3_params(rr_type* nsec3_apex,
9262ac0c33Sjakob 	const unsigned char** salt, int* salt_len, int* iter)
9362ac0c33Sjakob {
9462ac0c33Sjakob 	assert(salt && salt_len && iter);
9562ac0c33Sjakob 	assert(nsec3_apex);
9662ac0c33Sjakob 	*salt_len = rdata_atom_data(nsec3_apex->rdatas[3])[0];
9762ac0c33Sjakob 	*salt = (unsigned char*)(rdata_atom_data(nsec3_apex->rdatas[3])+1);
9862ac0c33Sjakob 	*iter = read_uint16(rdata_atom_data(nsec3_apex->rdatas[2]));
9962ac0c33Sjakob }
10062ac0c33Sjakob 
101dd5b221eSsthen const dname_type *
nsec3_b32_create(region_type * region,zone_type * zone,unsigned char * hash)102dd5b221eSsthen nsec3_b32_create(region_type* region, zone_type* zone, unsigned char* hash)
10362ac0c33Sjakob {
104dd5b221eSsthen 	const dname_type* dname;
10562ac0c33Sjakob 	char b32[SHA_DIGEST_LENGTH*2+1];
106dd5b221eSsthen 	b32_ntop(hash, SHA_DIGEST_LENGTH, b32, sizeof(b32));
10762ac0c33Sjakob 	dname=dname_parse(region, b32);
10862ac0c33Sjakob 	dname=dname_concatenate(region, dname, domain_dname(zone->apex));
10962ac0c33Sjakob 	return dname;
11062ac0c33Sjakob }
11162ac0c33Sjakob 
112dd5b221eSsthen void
nsec3_hash_and_store(zone_type * zone,const dname_type * dname,uint8_t * store)113dd5b221eSsthen nsec3_hash_and_store(zone_type* zone, const dname_type* dname, uint8_t* store)
11462ac0c33Sjakob {
115dd5b221eSsthen 	const unsigned char* nsec3_salt = NULL;
116dd5b221eSsthen 	int nsec3_saltlength = 0;
117dd5b221eSsthen 	int nsec3_iterations = 0;
118dd5b221eSsthen 
119dd5b221eSsthen 	detect_nsec3_params(zone->nsec3_param, &nsec3_salt,
120dd5b221eSsthen 		&nsec3_saltlength, &nsec3_iterations);
121308d2509Sflorian 	assert(nsec3_iterations >= 0 && nsec3_iterations <= 65536);
122dd5b221eSsthen 	iterated_hash((unsigned char*)store, nsec3_salt, nsec3_saltlength,
123dd5b221eSsthen 		dname_name(dname), dname->name_size, nsec3_iterations);
124dd5b221eSsthen }
125dd5b221eSsthen 
126dd5b221eSsthen #define STORE_HASH(x,y) memmove(domain->nsec3->x,y,NSEC3_HASH_LEN); domain->nsec3->have_##x =1;
127dd5b221eSsthen 
128dd5b221eSsthen /** find hash or create it and store it */
129dd5b221eSsthen static void
nsec3_lookup_hash_and_wc(region_type * region,zone_type * zone,const dname_type * dname,domain_type * domain,region_type * tmpregion)130ee5153b7Sflorian nsec3_lookup_hash_and_wc(region_type* region, zone_type* zone,
131ee5153b7Sflorian 	const dname_type* dname, domain_type* domain, region_type* tmpregion)
132dd5b221eSsthen {
133dd5b221eSsthen 	const dname_type* wcard;
134ee5153b7Sflorian 	if(domain->nsec3->hash_wc) {
135dd5b221eSsthen 		return;
136dd5b221eSsthen 	}
137dd5b221eSsthen 	/* lookup failed; disk failure or so */
138ee5153b7Sflorian 	domain->nsec3->hash_wc = (nsec3_hash_wc_node_type *)
139ee5153b7Sflorian 		region_alloc(region, sizeof(nsec3_hash_wc_node_type));
140ee5153b7Sflorian 	domain->nsec3->hash_wc->hash.node.key = NULL;
141ee5153b7Sflorian 	domain->nsec3->hash_wc->wc.node.key = NULL;
142ee5153b7Sflorian 	nsec3_hash_and_store(zone, dname, domain->nsec3->hash_wc->hash.hash);
143dd5b221eSsthen 	wcard = dname_parse(tmpregion, "*");
144dd5b221eSsthen 	wcard = dname_concatenate(tmpregion, wcard, dname);
145ee5153b7Sflorian 	nsec3_hash_and_store(zone, wcard, domain->nsec3->hash_wc->wc.hash);
146dd5b221eSsthen }
147dd5b221eSsthen 
148dd5b221eSsthen static void
nsec3_lookup_hash_ds(region_type * region,zone_type * zone,const dname_type * dname,domain_type * domain)149ee5153b7Sflorian nsec3_lookup_hash_ds(region_type* region, zone_type* zone,
150ee5153b7Sflorian 	const dname_type* dname, domain_type* domain)
151dd5b221eSsthen {
152ee5153b7Sflorian 	if(domain->nsec3->ds_parent_hash) {
153dd5b221eSsthen 		return;
154dd5b221eSsthen 	}
155dd5b221eSsthen 	/* lookup failed; disk failure or so */
156ee5153b7Sflorian 	domain->nsec3->ds_parent_hash = (nsec3_hash_node_type *)
157ee5153b7Sflorian 		region_alloc(region, sizeof(nsec3_hash_node_type));
158ee5153b7Sflorian 	domain->nsec3->ds_parent_hash->node.key = NULL;
159ee5153b7Sflorian 	nsec3_hash_and_store(zone, dname, domain->nsec3->ds_parent_hash->hash);
16062ac0c33Sjakob }
16162ac0c33Sjakob 
16262ac0c33Sjakob static int
nsec3_has_soa(rr_type * rr)16362ac0c33Sjakob nsec3_has_soa(rr_type* rr)
16462ac0c33Sjakob {
165dd5b221eSsthen 	if(rdata_atom_size(rr->rdatas[NSEC3_RDATA_BITMAP]) >= 3 && /* has types in bitmap */
166dd5b221eSsthen 		rdata_atom_data(rr->rdatas[NSEC3_RDATA_BITMAP])[0] == 0 && /* first window = 0, */
167dd5b221eSsthen 		/* [1]: bitmap length must be >= 1 */
168dd5b221eSsthen 		/* [2]: bit[6] = SOA, thus mask first bitmap octet with 0x02 */
169dd5b221eSsthen 		rdata_atom_data(rr->rdatas[NSEC3_RDATA_BITMAP])[2]&0x02) { /* SOA bit set */
17062ac0c33Sjakob 		return 1;
171dd5b221eSsthen 	}
17262ac0c33Sjakob 	return 0;
17362ac0c33Sjakob }
17462ac0c33Sjakob 
17562ac0c33Sjakob static rr_type*
check_apex_soa(namedb_type * namedb,zone_type * zone,int nolog)17618e77612Sflorian check_apex_soa(namedb_type* namedb, zone_type *zone, int nolog)
17762ac0c33Sjakob {
178dd5b221eSsthen 	uint8_t h[NSEC3_HASH_LEN];
17962ac0c33Sjakob 	domain_type* domain;
180dd5b221eSsthen 	const dname_type* hashed_apex, *dname = domain_dname(zone->apex);
181dd5b221eSsthen 	unsigned j;
18262ac0c33Sjakob 	rrset_type* nsec3_rrset;
183dd5b221eSsthen 	region_type* tmpregion;
18462ac0c33Sjakob 
185dd5b221eSsthen 	nsec3_hash_and_store(zone, dname, h);
186dd5b221eSsthen 	tmpregion = region_create(xalloc, free);
187dd5b221eSsthen 	hashed_apex = nsec3_b32_create(tmpregion, zone, h);
18862ac0c33Sjakob 	domain = domain_table_find(namedb->domains, hashed_apex);
18962ac0c33Sjakob 	if(!domain) {
19018e77612Sflorian 		if(!nolog) {
191dd5b221eSsthen 			log_msg(LOG_ERR, "%s NSEC3PARAM entry has no hash(apex).",
192dd5b221eSsthen 				domain_to_string(zone->apex));
19362ac0c33Sjakob 			log_msg(LOG_ERR, "hash(apex)= %s",
19462ac0c33Sjakob 				dname_to_string(hashed_apex, NULL));
19518e77612Sflorian 		}
196dd5b221eSsthen 		region_destroy(tmpregion);
197dd5b221eSsthen 		return NULL;
19862ac0c33Sjakob 	}
19962ac0c33Sjakob 	nsec3_rrset = domain_find_rrset(domain, zone, TYPE_NSEC3);
20062ac0c33Sjakob 	if(!nsec3_rrset) {
20118e77612Sflorian 		if(!nolog) {
202dd5b221eSsthen 			log_msg(LOG_ERR, "%s NSEC3PARAM entry: hash(apex) has no NSEC3 RRset.",
203dd5b221eSsthen 				domain_to_string(zone->apex));
204dd5b221eSsthen 			log_msg(LOG_ERR, "hash(apex)= %s",
205dd5b221eSsthen 				dname_to_string(hashed_apex, NULL));
20618e77612Sflorian 		}
207dd5b221eSsthen 		region_destroy(tmpregion);
208dd5b221eSsthen 		return NULL;
20962ac0c33Sjakob 	}
2105bcb494bSjakob 	for(j=0; j<nsec3_rrset->rr_count; j++) {
211dd5b221eSsthen 		if(nsec3_has_soa(&nsec3_rrset->rrs[j])) {
21262ac0c33Sjakob 			region_destroy(tmpregion);
21362ac0c33Sjakob 			return &nsec3_rrset->rrs[j];
21462ac0c33Sjakob 		}
21562ac0c33Sjakob 	}
21618e77612Sflorian 	if(!nolog) {
217dd5b221eSsthen 		log_msg(LOG_ERR, "%s NSEC3PARAM entry: hash(apex) NSEC3 has no SOA flag.",
218dd5b221eSsthen 			domain_to_string(zone->apex));
219dd5b221eSsthen 		log_msg(LOG_ERR, "hash(apex)= %s",
220dd5b221eSsthen 			dname_to_string(hashed_apex, NULL));
22118e77612Sflorian 	}
22262ac0c33Sjakob 	region_destroy(tmpregion);
2235bcb494bSjakob 	return NULL;
22462ac0c33Sjakob }
22562ac0c33Sjakob 
22618e77612Sflorian static void
nsec3param_to_str(struct rr * rr,char * str,size_t buflen)22718e77612Sflorian nsec3param_to_str(struct rr* rr, char* str, size_t buflen)
22818e77612Sflorian {
22918e77612Sflorian 	rdata_atom_type* rd = rr->rdatas;
23018e77612Sflorian 	size_t len;
23118e77612Sflorian 	len = snprintf(str, buflen, "%u %u %u ",
23218e77612Sflorian 		(unsigned)rdata_atom_data(rd[0])[0],
23318e77612Sflorian 		(unsigned)rdata_atom_data(rd[1])[0],
23418e77612Sflorian 		(unsigned)read_uint16(rdata_atom_data(rd[2])));
23518e77612Sflorian 	if(rdata_atom_data(rd[3])[0] == 0) {
23618e77612Sflorian 		if(buflen > len + 2)
23718e77612Sflorian 			str[len++] = '-';
23818e77612Sflorian 	} else {
23918e77612Sflorian 		len += hex_ntop(rdata_atom_data(rd[3])+1,
24018e77612Sflorian 			rdata_atom_data(rd[3])[0], str+len, buflen-len-1);
24118e77612Sflorian 	}
24218e77612Sflorian 	if(buflen > len + 1)
24318e77612Sflorian 		str[len] = 0;
24418e77612Sflorian }
24518e77612Sflorian 
246dd5b221eSsthen static struct rr*
db_find_nsec3param(struct namedb * db,struct zone * z,struct rr * avoid_rr,int checkchain)24718e77612Sflorian db_find_nsec3param(struct namedb* db, struct zone* z, struct rr* avoid_rr,
24818e77612Sflorian 	int checkchain)
24918e77612Sflorian {
25018e77612Sflorian 	unsigned i;
25118e77612Sflorian 	rrset_type* rrset = domain_find_rrset(z->apex, z, TYPE_NSEC3PARAM);
25218e77612Sflorian 	if(!rrset) /* no NSEC3PARAM in mem */
25318e77612Sflorian 		return NULL;
25418e77612Sflorian 	/* find first nsec3param we can support (SHA1, no flags) */
25518e77612Sflorian 	for(i=0; i<rrset->rr_count; i++) {
25618e77612Sflorian 		rdata_atom_type* rd = rrset->rrs[i].rdatas;
25718e77612Sflorian 		/* do not use the RR that is going to be deleted (in IXFR) */
25818e77612Sflorian 		if(&rrset->rrs[i] == avoid_rr) continue;
25918e77612Sflorian 		if(rrset->rrs[i].rdata_count < 4) continue;
26018e77612Sflorian 		if(rdata_atom_data(rd[0])[0] == NSEC3_SHA1_HASH &&
26118e77612Sflorian 			rdata_atom_data(rd[1])[0] == 0) {
26218e77612Sflorian 			if(checkchain) {
26318e77612Sflorian 				z->nsec3_param = &rrset->rrs[i];
26418e77612Sflorian 				if(!check_apex_soa(db, z, 1)) {
26518e77612Sflorian 					char str[MAX_RDLENGTH*2+16];
26618e77612Sflorian 					nsec3param_to_str(z->nsec3_param,
26718e77612Sflorian 						str, sizeof(str));
26818e77612Sflorian 					VERBOSITY(1, (LOG_WARNING, "zone %s NSEC3PARAM %s has broken chain, ignoring", domain_to_string(z->apex), str));
26918e77612Sflorian 					continue; /* don't use broken chain */
27018e77612Sflorian 				}
27118e77612Sflorian 			}
27218e77612Sflorian 			if(2 <= verbosity) {
27318e77612Sflorian 				char str[MAX_RDLENGTH*2+16];
27418e77612Sflorian 				nsec3param_to_str(&rrset->rrs[i], str,
27518e77612Sflorian 					sizeof(str));
27618e77612Sflorian 				VERBOSITY(2, (LOG_INFO, "rehash of zone %s with parameters %s",
27718e77612Sflorian 					domain_to_string(z->apex), str));
27818e77612Sflorian 			}
27918e77612Sflorian 			return &rrset->rrs[i];
28018e77612Sflorian 		}
28118e77612Sflorian 	}
28218e77612Sflorian 	return NULL;
28318e77612Sflorian }
28418e77612Sflorian 
285dd5b221eSsthen void
nsec3_find_zone_param(struct namedb * db,struct zone * zone,struct rr * avoid_rr,int checkchain)286*b71395eaSflorian nsec3_find_zone_param(struct namedb* db, struct zone* zone,
28718e77612Sflorian 	struct rr* avoid_rr, int checkchain)
288dd5b221eSsthen {
289*b71395eaSflorian 	/* avoid using the rr that is going to be deleted, avoid_rr */
290*b71395eaSflorian 	zone->nsec3_param = db_find_nsec3param(db, zone, avoid_rr, checkchain);
291dd5b221eSsthen }
292dd5b221eSsthen 
293dd5b221eSsthen /* check params ok for one RR */
294dd5b221eSsthen static int
nsec3_rdata_params_ok(rdata_atom_type * prd,rdata_atom_type * rd)295dd5b221eSsthen nsec3_rdata_params_ok(rdata_atom_type* prd, rdata_atom_type* rd)
296dd5b221eSsthen {
297dd5b221eSsthen 	return (rdata_atom_data(rd[0])[0] ==
29862ac0c33Sjakob 		rdata_atom_data(prd[0])[0] && /* hash algo */
29962ac0c33Sjakob 	   rdata_atom_data(rd[2])[0] ==
30062ac0c33Sjakob 		rdata_atom_data(prd[2])[0] && /* iterations 0 */
30162ac0c33Sjakob 	   rdata_atom_data(rd[2])[1] ==
30262ac0c33Sjakob 		rdata_atom_data(prd[2])[1] && /* iterations 1 */
30362ac0c33Sjakob 	   rdata_atom_data(rd[3])[0] ==
30462ac0c33Sjakob 		rdata_atom_data(prd[3])[0] && /* salt length */
30562ac0c33Sjakob 	   memcmp(rdata_atom_data(rd[3])+1,
30662ac0c33Sjakob 		rdata_atom_data(prd[3])+1, rdata_atom_data(rd[3])[0])
307dd5b221eSsthen 		== 0 );
30862ac0c33Sjakob }
3095bcb494bSjakob 
31062ac0c33Sjakob int
nsec3_rr_uses_params(rr_type * rr,zone_type * zone)311dd5b221eSsthen nsec3_rr_uses_params(rr_type* rr, zone_type* zone)
31262ac0c33Sjakob {
313dd5b221eSsthen 	if(!rr || rr->rdata_count < 4)
31462ac0c33Sjakob 		return 0;
315dd5b221eSsthen 	return nsec3_rdata_params_ok(zone->nsec3_param->rdatas, rr->rdatas);
31662ac0c33Sjakob }
31762ac0c33Sjakob 
3185bcb494bSjakob int
nsec3_in_chain_count(domain_type * domain,zone_type * zone)319dd5b221eSsthen nsec3_in_chain_count(domain_type* domain, zone_type* zone)
32062ac0c33Sjakob {
321dd5b221eSsthen 	rrset_type* rrset = domain_find_rrset(domain, zone, TYPE_NSEC3);
322dd5b221eSsthen 	unsigned i;
32362ac0c33Sjakob 	int count = 0;
324dd5b221eSsthen 	if(!rrset || !zone->nsec3_param)
325dd5b221eSsthen 		return 0; /* no NSEC3s, none in the chain */
326dd5b221eSsthen 	for(i=0; i<rrset->rr_count; i++) {
327dd5b221eSsthen 		if(nsec3_rr_uses_params(&rrset->rrs[i], zone))
32862ac0c33Sjakob 			count++;
32962ac0c33Sjakob 	}
330dd5b221eSsthen 	return count;
33162ac0c33Sjakob }
33262ac0c33Sjakob 
333dd5b221eSsthen struct domain*
nsec3_chain_find_prev(struct zone * zone,struct domain * domain)334dd5b221eSsthen nsec3_chain_find_prev(struct zone* zone, struct domain* domain)
3355bcb494bSjakob {
336dd5b221eSsthen 	if(domain->nsec3 && domain->nsec3->nsec3_node.key) {
337dd5b221eSsthen 		/* see if there is a prev */
338fe5fe5f6Sflorian 		rbnode_type* r = rbtree_previous(&domain->nsec3->nsec3_node);
339dd5b221eSsthen 		if(r != RBTREE_NULL) {
340dd5b221eSsthen 			/* found a previous, which is not the root-node in
341dd5b221eSsthen 			 * the prehash tree (and thus points to the tree) */
342dd5b221eSsthen 			return (domain_type*)r->key;
3435bcb494bSjakob 		}
344dd5b221eSsthen 	}
345063644e9Sflorian 	if(zone->nsec3_last && zone->nsec3_last != domain)
346dd5b221eSsthen 		return zone->nsec3_last;
347dd5b221eSsthen 	return NULL;
348dd5b221eSsthen }
349dd5b221eSsthen 
350ac5517e4Sflorian 
351ac5517e4Sflorian /** clear hash tree. Called from nsec3_clear_precompile() only. */
352ac5517e4Sflorian static void
hash_tree_clear(rbtree_type * tree)353ac5517e4Sflorian hash_tree_clear(rbtree_type* tree)
354ac5517e4Sflorian {
355ac5517e4Sflorian 	if(!tree) return;
356ac5517e4Sflorian 
357ac5517e4Sflorian 	/* Previously (before commit 4ca61188b3f7a0e077476875810d18a5d439871f
358ac5517e4Sflorian 	 * and/or svn commit 4776) prehashes and corresponding rbtree nodes
359ac5517e4Sflorian 	 * were part of struct nsec3_domain_data. Clearing the hash_tree would
360ac5517e4Sflorian 	 * then mean setting the key value of the nodes to NULL to indicate
361ac5517e4Sflorian 	 * absence of the prehash.
362ac5517e4Sflorian 	 * But since prehash structs are separatly allocated, this is no longer
363ac5517e4Sflorian 	 * necessary as currently the prehash structs are simply recycled and
364ac5517e4Sflorian 	 * NULLed.
365ac5517e4Sflorian 	 *
366ac5517e4Sflorian 	 * rbnode_type* n;
367ac5517e4Sflorian 	 * for(n=rbtree_first(tree); n!=RBTREE_NULL; n=rbtree_next(n)) {
368ac5517e4Sflorian 	 *	n->key = NULL;
369ac5517e4Sflorian 	 * }
370ac5517e4Sflorian 	 */
371ac5517e4Sflorian 	tree->count = 0;
372ac5517e4Sflorian 	tree->root = RBTREE_NULL;
373ac5517e4Sflorian }
374ac5517e4Sflorian 
375dd5b221eSsthen void
nsec3_clear_precompile(struct namedb * db,zone_type * zone)376dd5b221eSsthen nsec3_clear_precompile(struct namedb* db, zone_type* zone)
377dd5b221eSsthen {
378dd5b221eSsthen 	domain_type* walk;
379dd5b221eSsthen 	/* clear prehash items (there must not be items for other zones) */
380dd5b221eSsthen 	prehash_clear(db->domains);
381dd5b221eSsthen 	/* clear trees */
382dd5b221eSsthen 	hash_tree_clear(zone->nsec3tree);
383dd5b221eSsthen 	hash_tree_clear(zone->hashtree);
384dd5b221eSsthen 	hash_tree_clear(zone->wchashtree);
385dd5b221eSsthen 	hash_tree_clear(zone->dshashtree);
386dd5b221eSsthen 	/* wipe hashes */
387dd5b221eSsthen 
388dd5b221eSsthen 	/* wipe precompile */
389dd5b221eSsthen 	walk = zone->apex;
390dd5b221eSsthen 	while(walk && domain_is_subdomain(walk, zone->apex)) {
391dd5b221eSsthen 		if(walk->nsec3) {
392b8cbfafcSflorian 			if(nsec3_condition_hash(walk, zone)) {
393dd5b221eSsthen 				walk->nsec3->nsec3_node.key = NULL;
394dd5b221eSsthen 				walk->nsec3->nsec3_cover = NULL;
395dd5b221eSsthen 				walk->nsec3->nsec3_wcard_child_cover = NULL;
396dd5b221eSsthen 				walk->nsec3->nsec3_is_exact = 0;
397ee5153b7Sflorian 				if (walk->nsec3->hash_wc) {
398ee5153b7Sflorian 					region_recycle(db->domains->region,
399ee5153b7Sflorian 						walk->nsec3->hash_wc,
400ee5153b7Sflorian 						sizeof(nsec3_hash_wc_node_type));
401ee5153b7Sflorian 					walk->nsec3->hash_wc = NULL;
402ee5153b7Sflorian 				}
403dd5b221eSsthen 			}
404b8cbfafcSflorian 			if(nsec3_condition_dshash(walk, zone)) {
405dd5b221eSsthen 				walk->nsec3->nsec3_ds_parent_cover = NULL;
406dd5b221eSsthen 				walk->nsec3->nsec3_ds_parent_is_exact = 0;
407ee5153b7Sflorian 				if (walk->nsec3->ds_parent_hash) {
408ee5153b7Sflorian 					region_recycle(db->domains->region,
409ee5153b7Sflorian 						walk->nsec3->ds_parent_hash,
410ee5153b7Sflorian 						sizeof(nsec3_hash_node_type));
411ee5153b7Sflorian 					walk->nsec3->ds_parent_hash = NULL;
412ee5153b7Sflorian 				}
413dd5b221eSsthen 			}
414dd5b221eSsthen 		}
415dd5b221eSsthen 		walk = domain_next(walk);
416dd5b221eSsthen 	}
417dd5b221eSsthen 	zone->nsec3_last = NULL;
418dd5b221eSsthen }
419dd5b221eSsthen 
420dd5b221eSsthen /* see if domain name is part of (existing names in) the nsec3 zone */
421dd5b221eSsthen int
nsec3_domain_part_of_zone(domain_type * d,zone_type * z)422dd5b221eSsthen nsec3_domain_part_of_zone(domain_type* d, zone_type* z)
423dd5b221eSsthen {
424dd5b221eSsthen 	while(d) {
425dd5b221eSsthen 		if(d->is_apex)
426dd5b221eSsthen 			return (z->apex == d); /* zonecut, if right zone*/
427dd5b221eSsthen 		d = d->parent;
428dd5b221eSsthen 	}
429dd5b221eSsthen 	return 0;
430dd5b221eSsthen }
431dd5b221eSsthen 
432dd5b221eSsthen /* condition when a domain is precompiled */
433dd5b221eSsthen int
nsec3_condition_hash(domain_type * d,zone_type * z)434dd5b221eSsthen nsec3_condition_hash(domain_type* d, zone_type* z)
435dd5b221eSsthen {
436dd5b221eSsthen 	return d->is_existing && !domain_has_only_NSEC3(d, z) &&
437dd5b221eSsthen 		nsec3_domain_part_of_zone(d, z) && !domain_is_glue(d, z);
438dd5b221eSsthen }
439dd5b221eSsthen 
440dd5b221eSsthen /* condition when a domain is ds precompiled */
441dd5b221eSsthen int
nsec3_condition_dshash(domain_type * d,zone_type * z)442dd5b221eSsthen nsec3_condition_dshash(domain_type* d, zone_type* z)
443dd5b221eSsthen {
444dd5b221eSsthen 	return d->is_existing && !domain_has_only_NSEC3(d, z) &&
445dd5b221eSsthen 		(domain_find_rrset(d, z, TYPE_DS) ||
446b8cbfafcSflorian 		domain_find_rrset(d, z, TYPE_NS)) && d != z->apex
447b8cbfafcSflorian 		&& nsec3_domain_part_of_zone(d->parent, z);
448dd5b221eSsthen }
449dd5b221eSsthen 
450dd5b221eSsthen zone_type*
nsec3_tree_zone(namedb_type * db,domain_type * d)451dd5b221eSsthen nsec3_tree_zone(namedb_type* db, domain_type* d)
452dd5b221eSsthen {
453dd5b221eSsthen 	/* see nsec3_domain_part_of_zone; domains part of zone that has
454dd5b221eSsthen 	 * apex above them */
455dd5b221eSsthen 	/* this does not use the rrset->zone pointer because there may be
456dd5b221eSsthen 	 * no rrsets left at apex (no SOA), e.g. during IXFR */
457dd5b221eSsthen 	while(d) {
458dd5b221eSsthen 		if(d->is_apex) {
459dd5b221eSsthen 			/* we can try a SOA if its present (faster than tree)*/
460dd5b221eSsthen 			/* DNSKEY and NSEC3PARAM are also good indicators */
461dd5b221eSsthen 			rrset_type *rrset;
462dd5b221eSsthen 			for (rrset = d->rrsets; rrset; rrset = rrset->next)
463dd5b221eSsthen 				if (rrset_rrtype(rrset) == TYPE_SOA ||
464dd5b221eSsthen 					rrset_rrtype(rrset) == TYPE_DNSKEY ||
465dd5b221eSsthen 					rrset_rrtype(rrset) == TYPE_NSEC3PARAM)
466dd5b221eSsthen 					return rrset->zone;
467c1e73312Sflorian 			return namedb_find_zone(db, domain_dname(d));
468dd5b221eSsthen 		}
469dd5b221eSsthen 		d = d->parent;
470dd5b221eSsthen 	}
471dd5b221eSsthen 	return NULL;
472dd5b221eSsthen }
473dd5b221eSsthen 
474dd5b221eSsthen zone_type*
nsec3_tree_dszone(namedb_type * db,domain_type * d)475dd5b221eSsthen nsec3_tree_dszone(namedb_type* db, domain_type* d)
476dd5b221eSsthen {
477dd5b221eSsthen 	/* the DStree does not contain nodes with d==z->apex */
478dd5b221eSsthen 	if(d->is_apex)
479dd5b221eSsthen 		d = d->parent;
480dd5b221eSsthen 	return nsec3_tree_zone(db, d);
481dd5b221eSsthen }
482dd5b221eSsthen 
483dd5b221eSsthen int
nsec3_find_cover(zone_type * zone,uint8_t * hash,size_t hashlen,domain_type ** result)484dd5b221eSsthen nsec3_find_cover(zone_type* zone, uint8_t* hash, size_t hashlen,
485dd5b221eSsthen 	domain_type** result)
486dd5b221eSsthen {
487fe5fe5f6Sflorian 	rbnode_type* r = NULL;
488dd5b221eSsthen 	int exact;
489dd5b221eSsthen 	domain_type d;
490dd5b221eSsthen 	uint8_t n[48];
491dd5b221eSsthen 
492dd5b221eSsthen 	/* nsec3tree is sorted by b32 encoded domain name of the NSEC3 */
493dd5b221eSsthen 	b32_ntop(hash, hashlen, (char*)(n+5), sizeof(n)-5);
494c1e73312Sflorian #ifdef USE_RADIX_TREE
495dd5b221eSsthen 	d.dname = (dname_type*)n;
496c1e73312Sflorian #else
497c1e73312Sflorian 	d.node.key = n;
498c1e73312Sflorian #endif
499dd5b221eSsthen 	n[0] = 34; /* name_size */
500dd5b221eSsthen 	n[1] = 2; /* label_count */
501dd5b221eSsthen 	n[2] = 0; /* label_offset[0] */
502dd5b221eSsthen 	n[3] = 0; /* label_offset[1] */
503dd5b221eSsthen 	n[4] = 32; /* label-size[0] */
504dd5b221eSsthen 
505dd5b221eSsthen 	assert(result);
506dd5b221eSsthen 	assert(zone->nsec3_param && zone->nsec3tree);
507dd5b221eSsthen 
508dd5b221eSsthen 	exact = rbtree_find_less_equal(zone->nsec3tree, &d, &r);
509dd5b221eSsthen 	if(r) {
510dd5b221eSsthen 		*result = (domain_type*)r->key;
511dd5b221eSsthen 	} else {
512dd5b221eSsthen 		*result = zone->nsec3_last;
513dd5b221eSsthen 	}
514dd5b221eSsthen 	return exact;
515dd5b221eSsthen }
516dd5b221eSsthen 
517dd5b221eSsthen void
nsec3_precompile_domain(struct namedb * db,struct domain * domain,struct zone * zone,region_type * tmpregion)518dd5b221eSsthen nsec3_precompile_domain(struct namedb* db, struct domain* domain,
519dd5b221eSsthen 	struct zone* zone, region_type* tmpregion)
520dd5b221eSsthen {
521dd5b221eSsthen 	domain_type* result = 0;
522dd5b221eSsthen 	int exact;
523dd5b221eSsthen 	allocate_domain_nsec3(db->domains, domain);
524dd5b221eSsthen 
525dd5b221eSsthen 	/* hash it */
526ee5153b7Sflorian 	nsec3_lookup_hash_and_wc(db->region,
527ee5153b7Sflorian 		zone, domain_dname(domain), domain, tmpregion);
528dd5b221eSsthen 
529dd5b221eSsthen 	/* add into tree */
530dd5b221eSsthen 	zone_add_domain_in_hash_tree(db->region, &zone->hashtree,
531ee5153b7Sflorian 		cmp_hash_tree, domain, &domain->nsec3->hash_wc->hash.node);
532dd5b221eSsthen 	zone_add_domain_in_hash_tree(db->region, &zone->wchashtree,
533ee5153b7Sflorian 		cmp_wchash_tree, domain, &domain->nsec3->hash_wc->wc.node);
534dd5b221eSsthen 
535dd5b221eSsthen 	/* lookup in tree cover ptr (or exact) */
536ee5153b7Sflorian 	exact = nsec3_find_cover(zone, domain->nsec3->hash_wc->hash.hash,
537ee5153b7Sflorian 		sizeof(domain->nsec3->hash_wc->hash.hash), &result);
538dd5b221eSsthen 	domain->nsec3->nsec3_cover = result;
539dd5b221eSsthen 	if(exact)
540dd5b221eSsthen 		domain->nsec3->nsec3_is_exact = 1;
541dd5b221eSsthen 	else	domain->nsec3->nsec3_is_exact = 0;
542dd5b221eSsthen 
543dd5b221eSsthen 	/* find cover for *.domain for wildcard denial */
544b90bb40eSsthen 	(void)nsec3_find_cover(zone, domain->nsec3->hash_wc->wc.hash,
545ee5153b7Sflorian 		sizeof(domain->nsec3->hash_wc->wc.hash), &result);
546dd5b221eSsthen 	domain->nsec3->nsec3_wcard_child_cover = result;
547dd5b221eSsthen }
548dd5b221eSsthen 
549dd5b221eSsthen void
nsec3_precompile_domain_ds(struct namedb * db,struct domain * domain,struct zone * zone)550dd5b221eSsthen nsec3_precompile_domain_ds(struct namedb* db, struct domain* domain,
551dd5b221eSsthen 	struct zone* zone)
552dd5b221eSsthen {
553dd5b221eSsthen 	domain_type* result = 0;
554dd5b221eSsthen 	int exact;
555dd5b221eSsthen 	allocate_domain_nsec3(db->domains, domain);
556dd5b221eSsthen 
557dd5b221eSsthen 	/* hash it : it could have different hash parameters then the
558dd5b221eSsthen 	   other hash for this domain name */
559ee5153b7Sflorian 	nsec3_lookup_hash_ds(db->region, zone, domain_dname(domain), domain);
560dd5b221eSsthen 	/* lookup in tree cover ptr (or exact) */
561ee5153b7Sflorian 	exact = nsec3_find_cover(zone, domain->nsec3->ds_parent_hash->hash,
562ee5153b7Sflorian 		sizeof(domain->nsec3->ds_parent_hash->hash), &result);
563dd5b221eSsthen 	if(exact)
564dd5b221eSsthen 		domain->nsec3->nsec3_ds_parent_is_exact = 1;
565dd5b221eSsthen 	else 	domain->nsec3->nsec3_ds_parent_is_exact = 0;
566dd5b221eSsthen 	domain->nsec3->nsec3_ds_parent_cover = result;
567dd5b221eSsthen 	/* add into tree */
568dd5b221eSsthen 	zone_add_domain_in_hash_tree(db->region, &zone->dshashtree,
569ee5153b7Sflorian 		cmp_dshash_tree, domain, &domain->nsec3->ds_parent_hash->node);
570dd5b221eSsthen }
571dd5b221eSsthen 
572dd5b221eSsthen static void
parse_nsec3_name(const dname_type * dname,uint8_t * hash,size_t buflen)573dd5b221eSsthen parse_nsec3_name(const dname_type* dname, uint8_t* hash, size_t buflen)
574dd5b221eSsthen {
575dd5b221eSsthen 	/* first label must be the match, */
576dd5b221eSsthen 	size_t lablen = (buflen-1) * 8 / 5;
577dd5b221eSsthen 	const uint8_t* wire = dname_name(dname);
578dd5b221eSsthen 	assert(lablen == 32 && buflen == NSEC3_HASH_LEN+1);
579dd5b221eSsthen 	/* labels of length 32 for SHA1, and must have space+1 for convert */
580dd5b221eSsthen 	if(wire[0] != lablen) {
581dd5b221eSsthen 		/* not NSEC3 */
582dd5b221eSsthen 		memset(hash, 0, buflen);
5835bcb494bSjakob 		return;
5845bcb494bSjakob 	}
585dd5b221eSsthen 	(void)b32_pton((char*)wire+1, hash, buflen);
586dd5b221eSsthen }
587dd5b221eSsthen 
588dd5b221eSsthen void
nsec3_precompile_nsec3rr(namedb_type * db,struct domain * domain,struct zone * zone)589dd5b221eSsthen nsec3_precompile_nsec3rr(namedb_type* db, struct domain* domain,
590dd5b221eSsthen 	struct zone* zone)
591dd5b221eSsthen {
592dd5b221eSsthen 	allocate_domain_nsec3(db->domains, domain);
593dd5b221eSsthen 	/* add into nsec3tree */
594dd5b221eSsthen 	zone_add_domain_in_hash_tree(db->region, &zone->nsec3tree,
595dd5b221eSsthen 		cmp_nsec3_tree, domain, &domain->nsec3->nsec3_node);
596dd5b221eSsthen 	/* fixup the last in the zone */
597dd5b221eSsthen 	if(rbtree_last(zone->nsec3tree)->key == domain) {
598dd5b221eSsthen 		zone->nsec3_last = domain;
599dd5b221eSsthen 	}
600dd5b221eSsthen }
601dd5b221eSsthen 
602dd5b221eSsthen void
nsec3_precompile_newparam(namedb_type * db,zone_type * zone)603dd5b221eSsthen nsec3_precompile_newparam(namedb_type* db, zone_type* zone)
604dd5b221eSsthen {
605dd5b221eSsthen 	region_type* tmpregion = region_create(xalloc, free);
606dd5b221eSsthen 	domain_type* walk;
607dd5b221eSsthen 	time_t s = time(NULL);
608533110e2Sbrad 	unsigned long n = 0, c = 0;
609dd5b221eSsthen 
610dd5b221eSsthen 	/* add nsec3s of chain to nsec3tree */
611dd5b221eSsthen 	for(walk=zone->apex; walk && domain_is_subdomain(walk, zone->apex);
612dd5b221eSsthen 		walk = domain_next(walk)) {
613dd5b221eSsthen 		n++;
614dd5b221eSsthen 		if(nsec3_in_chain_count(walk, zone) != 0) {
615dd5b221eSsthen 			nsec3_precompile_nsec3rr(db, walk, zone);
616dd5b221eSsthen 		}
617dd5b221eSsthen 	}
618dd5b221eSsthen 	/* hash and precompile zone */
619dd5b221eSsthen 	for(walk=zone->apex; walk && domain_is_subdomain(walk, zone->apex);
620dd5b221eSsthen 		walk = domain_next(walk)) {
621dd5b221eSsthen 		if(nsec3_condition_hash(walk, zone)) {
622dd5b221eSsthen 			nsec3_precompile_domain(db, walk, zone, tmpregion);
623dd5b221eSsthen 			region_free_all(tmpregion);
624dd5b221eSsthen 		}
625dd5b221eSsthen 		if(nsec3_condition_dshash(walk, zone))
626dd5b221eSsthen 			nsec3_precompile_domain_ds(db, walk, zone);
627dd5b221eSsthen 		if(++c % ZONEC_PCT_COUNT == 0 && time(NULL) > s + ZONEC_PCT_TIME) {
628dd5b221eSsthen 			s = time(NULL);
629dd5b221eSsthen 			VERBOSITY(1, (LOG_INFO, "nsec3 %s %d %%",
630533110e2Sbrad 				zone->opts->name,
6314564029fSflorian 				(n==0)?0:(int)(c*((unsigned long)100)/n)));
632dd5b221eSsthen 		}
633dd5b221eSsthen 	}
634dd5b221eSsthen 	region_destroy(tmpregion);
635dd5b221eSsthen }
636dd5b221eSsthen 
637dd5b221eSsthen void
prehash_zone_complete(struct namedb * db,struct zone * zone)638dd5b221eSsthen prehash_zone_complete(struct namedb* db, struct zone* zone)
639dd5b221eSsthen {
640dd5b221eSsthen 	/* robust clear it */
641dd5b221eSsthen 	nsec3_clear_precompile(db, zone);
642dd5b221eSsthen 	/* find zone settings */
643dd5b221eSsthen 
644dd5b221eSsthen 	assert(db && zone);
645*b71395eaSflorian 	nsec3_find_zone_param(db, zone, NULL, 1);
64618e77612Sflorian 	if(!zone->nsec3_param || !check_apex_soa(db, zone, 0)) {
647dd5b221eSsthen 		zone->nsec3_param = NULL;
648dd5b221eSsthen 		zone->nsec3_last = NULL;
649dd5b221eSsthen 		return;
650dd5b221eSsthen 	}
651dd5b221eSsthen 	nsec3_precompile_newparam(db, zone);
652dd5b221eSsthen }
6535bcb494bSjakob 
6545bcb494bSjakob static void
init_lookup_key_hash_tree(domain_type * d,uint8_t * hash)655dd5b221eSsthen init_lookup_key_hash_tree(domain_type* d, uint8_t* hash)
656ee5153b7Sflorian { memcpy(d->nsec3->hash_wc->hash.hash, hash, NSEC3_HASH_LEN); }
6575bcb494bSjakob 
658dd5b221eSsthen static void
init_lookup_key_wc_tree(domain_type * d,uint8_t * hash)659dd5b221eSsthen init_lookup_key_wc_tree(domain_type* d, uint8_t* hash)
660ee5153b7Sflorian { memcpy(d->nsec3->hash_wc->wc.hash, hash, NSEC3_HASH_LEN); }
661dd5b221eSsthen 
662dd5b221eSsthen static void
init_lookup_key_ds_tree(domain_type * d,uint8_t * hash)663dd5b221eSsthen init_lookup_key_ds_tree(domain_type* d, uint8_t* hash)
664ee5153b7Sflorian { memcpy(d->nsec3->ds_parent_hash->hash, hash, NSEC3_HASH_LEN); }
665dd5b221eSsthen 
666dd5b221eSsthen /* find first in the tree and true if the first to process it */
667dd5b221eSsthen static int
process_first(rbtree_type * tree,uint8_t * hash,rbnode_type ** p,void (* init)(domain_type *,uint8_t *))668fe5fe5f6Sflorian process_first(rbtree_type* tree, uint8_t* hash, rbnode_type** p,
669dd5b221eSsthen 	void (*init)(domain_type*, uint8_t*))
670dd5b221eSsthen {
671dd5b221eSsthen 	domain_type d;
672dd5b221eSsthen 	struct nsec3_domain_data n;
673ee5153b7Sflorian 	nsec3_hash_wc_node_type hash_wc;
674ee5153b7Sflorian 	nsec3_hash_node_type ds_parent_hash;
675ee5153b7Sflorian 
676dd5b221eSsthen 	if(!tree) {
677dd5b221eSsthen 		*p = RBTREE_NULL;
678dd5b221eSsthen 		return 0;
679dd5b221eSsthen 	}
680ee5153b7Sflorian 	hash_wc.hash.node.key = NULL;
681ee5153b7Sflorian 	hash_wc.wc.node.key = NULL;
682ee5153b7Sflorian 	n.hash_wc = &hash_wc;
683ee5153b7Sflorian 	ds_parent_hash.node.key = NULL;
684ee5153b7Sflorian 	n.ds_parent_hash = &ds_parent_hash;
685dd5b221eSsthen 	d.nsec3 = &n;
686dd5b221eSsthen 	init(&d, hash);
687dd5b221eSsthen 	if(rbtree_find_less_equal(tree, &d, p)) {
688dd5b221eSsthen 		/* found an exact match */
689dd5b221eSsthen 		return 1;
690dd5b221eSsthen 	}
691dd5b221eSsthen 	if(!*p) /* before first, go from first */
692dd5b221eSsthen 		*p = rbtree_first(tree);
693dd5b221eSsthen 	/* the inexact, smaller, match we found, does not itself need to
694dd5b221eSsthen 	 * be edited */
695dd5b221eSsthen 	else
696dd5b221eSsthen 		*p = rbtree_next(*p); /* if this becomes NULL, nothing to do */
697dd5b221eSsthen 	return 0;
698dd5b221eSsthen }
699dd5b221eSsthen 
700dd5b221eSsthen /* set end pointer if possible */
701dd5b221eSsthen static void
process_end(rbtree_type * tree,uint8_t * hash,rbnode_type ** p,void (* init)(domain_type *,uint8_t *))702fe5fe5f6Sflorian process_end(rbtree_type* tree, uint8_t* hash, rbnode_type** p,
703dd5b221eSsthen 	void (*init)(domain_type*, uint8_t*))
704dd5b221eSsthen {
705dd5b221eSsthen 	domain_type d;
706dd5b221eSsthen 	struct nsec3_domain_data n;
707ee5153b7Sflorian 	nsec3_hash_wc_node_type hash_wc;
708ee5153b7Sflorian 	nsec3_hash_node_type ds_parent_hash;
709ee5153b7Sflorian 
710dd5b221eSsthen 	if(!tree) {
711dd5b221eSsthen 		*p = RBTREE_NULL;
712dd5b221eSsthen 		return;
713dd5b221eSsthen 	}
714ee5153b7Sflorian 	hash_wc.hash.node.key = NULL;
715ee5153b7Sflorian 	hash_wc.wc.node.key = NULL;
716ee5153b7Sflorian 	n.hash_wc = &hash_wc;
717ee5153b7Sflorian 	ds_parent_hash.node.key = NULL;
718ee5153b7Sflorian 	n.ds_parent_hash = &ds_parent_hash;
719dd5b221eSsthen 	d.nsec3 = &n;
720dd5b221eSsthen 	init(&d, hash);
721dd5b221eSsthen 	if(rbtree_find_less_equal(tree, &d, p)) {
722dd5b221eSsthen 		/* an exact match, fine, because this one does not get
723dd5b221eSsthen 		 * processed */
724dd5b221eSsthen 		return;
725dd5b221eSsthen 	}
726dd5b221eSsthen 	/* inexact element, but if NULL, until first element in tree */
727dd5b221eSsthen 	if(!*p) {
728dd5b221eSsthen 		*p = rbtree_first(tree);
729dd5b221eSsthen 		return;
730dd5b221eSsthen 	}
731dd5b221eSsthen 	/* inexact match, use next element, if possible, the smaller
732dd5b221eSsthen 	 * element is part of the range */
733dd5b221eSsthen 	*p = rbtree_next(*p);
734dd5b221eSsthen 	/* if next returns null, we go until the end of the tree */
735dd5b221eSsthen }
736dd5b221eSsthen 
737dd5b221eSsthen /* prehash domains in hash range start to end */
738dd5b221eSsthen static void
process_range(zone_type * zone,domain_type * start,domain_type * end,domain_type * nsec3)739dd5b221eSsthen process_range(zone_type* zone, domain_type* start,
740dd5b221eSsthen 	domain_type* end, domain_type* nsec3)
741dd5b221eSsthen {
742dd5b221eSsthen 	/* start NULL means from first in tree */
743dd5b221eSsthen 	/* end NULL means to last in tree */
744fe5fe5f6Sflorian 	rbnode_type *p = RBTREE_NULL, *pwc = RBTREE_NULL, *pds = RBTREE_NULL;
745fe5fe5f6Sflorian 	rbnode_type *p_end = RBTREE_NULL, *pwc_end = RBTREE_NULL, *pds_end = RBTREE_NULL;
746dd5b221eSsthen 	/* because the nodes are on the prehashlist, the domain->nsec3 is
747dd5b221eSsthen 	 * already allocated, and we need not allocate it here */
748dd5b221eSsthen 	/* set start */
749dd5b221eSsthen 	if(start) {
750dd5b221eSsthen 		uint8_t hash[NSEC3_HASH_LEN+1];
751dd5b221eSsthen 		parse_nsec3_name(domain_dname(start), hash, sizeof(hash));
752dd5b221eSsthen 		/* if exact match on first, set is_exact */
753dd5b221eSsthen 		if(process_first(zone->hashtree, hash, &p, init_lookup_key_hash_tree)) {
754dd5b221eSsthen 			((domain_type*)(p->key))->nsec3->nsec3_cover = nsec3;
755dd5b221eSsthen 			((domain_type*)(p->key))->nsec3->nsec3_is_exact = 1;
756dd5b221eSsthen 			p = rbtree_next(p);
757dd5b221eSsthen 		}
758dd5b221eSsthen 		(void)process_first(zone->wchashtree, hash, &pwc, init_lookup_key_wc_tree);
759dd5b221eSsthen 		if(process_first(zone->dshashtree, hash, &pds, init_lookup_key_ds_tree)){
760dd5b221eSsthen 			((domain_type*)(pds->key))->nsec3->
761dd5b221eSsthen 				nsec3_ds_parent_cover = nsec3;
762dd5b221eSsthen 			((domain_type*)(pds->key))->nsec3->
763dd5b221eSsthen 				nsec3_ds_parent_is_exact = 1;
764dd5b221eSsthen 			pds = rbtree_next(pds);
765dd5b221eSsthen 		}
766dd5b221eSsthen 	} else {
767dd5b221eSsthen 		if(zone->hashtree)
768dd5b221eSsthen 			p = rbtree_first(zone->hashtree);
769dd5b221eSsthen 		if(zone->wchashtree)
770dd5b221eSsthen 			pwc = rbtree_first(zone->wchashtree);
771dd5b221eSsthen 		if(zone->dshashtree)
772dd5b221eSsthen 			pds = rbtree_first(zone->dshashtree);
773dd5b221eSsthen 	}
774dd5b221eSsthen 	/* set end */
775dd5b221eSsthen 	if(end) {
776dd5b221eSsthen 		uint8_t hash[NSEC3_HASH_LEN+1];
777dd5b221eSsthen 		parse_nsec3_name(domain_dname(end), hash, sizeof(hash));
778dd5b221eSsthen 		process_end(zone->hashtree, hash, &p_end, init_lookup_key_hash_tree);
779dd5b221eSsthen 		process_end(zone->wchashtree, hash, &pwc_end, init_lookup_key_wc_tree);
780dd5b221eSsthen 		process_end(zone->dshashtree, hash, &pds_end, init_lookup_key_ds_tree);
781dd5b221eSsthen 	}
782dd5b221eSsthen 
783dd5b221eSsthen 	/* precompile */
784dd5b221eSsthen 	while(p != RBTREE_NULL && p != p_end) {
785dd5b221eSsthen 		((domain_type*)(p->key))->nsec3->nsec3_cover = nsec3;
786dd5b221eSsthen 		((domain_type*)(p->key))->nsec3->nsec3_is_exact = 0;
787dd5b221eSsthen 		p = rbtree_next(p);
788dd5b221eSsthen 	}
789dd5b221eSsthen 	while(pwc != RBTREE_NULL && pwc != pwc_end) {
790dd5b221eSsthen 		((domain_type*)(pwc->key))->nsec3->
791dd5b221eSsthen 			nsec3_wcard_child_cover = nsec3;
792dd5b221eSsthen 		pwc = rbtree_next(pwc);
793dd5b221eSsthen 	}
794dd5b221eSsthen 	while(pds != RBTREE_NULL && pds != pds_end) {
795dd5b221eSsthen 		((domain_type*)(pds->key))->nsec3->
796dd5b221eSsthen 			nsec3_ds_parent_cover = nsec3;
797dd5b221eSsthen 		((domain_type*)(pds->key))->nsec3->
798dd5b221eSsthen 			nsec3_ds_parent_is_exact = 0;
799dd5b221eSsthen 		pds = rbtree_next(pds);
800dd5b221eSsthen 	}
801dd5b221eSsthen }
802dd5b221eSsthen 
803dd5b221eSsthen /* prehash a domain from the prehash list */
804dd5b221eSsthen static void
process_prehash_domain(domain_type * domain,zone_type * zone)805dd5b221eSsthen process_prehash_domain(domain_type* domain, zone_type* zone)
806dd5b221eSsthen {
807dd5b221eSsthen 	/* in the hashtree, wchashtree, dshashtree walk through to next NSEC3
808dd5b221eSsthen 	 * and set precompile pointers to point to this domain (or is_exact),
809dd5b221eSsthen 	 * the first domain can be is_exact. If it is the last NSEC3, also
810dd5b221eSsthen 	 * process the initial part (before the first) */
811fe5fe5f6Sflorian 	rbnode_type* nx;
812dd5b221eSsthen 
813dd5b221eSsthen 	/* this domain is part of the prehash list and therefore the
814dd5b221eSsthen 	 * domain->nsec3 is allocated and need not be allocated here */
815dd5b221eSsthen 	assert(domain->nsec3 && domain->nsec3->nsec3_node.key);
816dd5b221eSsthen 	nx = rbtree_next(&domain->nsec3->nsec3_node);
817dd5b221eSsthen 	if(nx != RBTREE_NULL) {
818dd5b221eSsthen 		/* process until next nsec3 */
819dd5b221eSsthen 		domain_type* end = (domain_type*)nx->key;
820dd5b221eSsthen 		process_range(zone, domain, end, domain);
821dd5b221eSsthen 	} else {
822dd5b221eSsthen 		/* first is root, but then comes the first nsec3 */
823dd5b221eSsthen 		domain_type* first = (domain_type*)(rbtree_first(
824dd5b221eSsthen 			zone->nsec3tree)->key);
825dd5b221eSsthen 		/* last in zone */
826dd5b221eSsthen 		process_range(zone, domain, NULL, domain);
827dd5b221eSsthen 		/* also process before first in zone */
828dd5b221eSsthen 		process_range(zone, NULL, first, domain);
829dd5b221eSsthen 	}
830dd5b221eSsthen }
831dd5b221eSsthen 
prehash_zone(struct namedb * db,struct zone * zone)832dd5b221eSsthen void prehash_zone(struct namedb* db, struct zone* zone)
833dd5b221eSsthen {
834dd5b221eSsthen 	domain_type* d;
835dd5b221eSsthen 	if(!zone->nsec3_param) {
836dd5b221eSsthen 		prehash_clear(db->domains);
837dd5b221eSsthen 		return;
838dd5b221eSsthen 	}
83918e77612Sflorian 	if(!check_apex_soa(db, zone, 1)) {
84018e77612Sflorian 		/* the zone fails apex soa check, prehash complete may
84118e77612Sflorian 		 * detect other valid chains */
84218e77612Sflorian 		prehash_clear(db->domains);
84318e77612Sflorian 		prehash_zone_complete(db, zone);
84418e77612Sflorian 		return;
84518e77612Sflorian 	}
846dd5b221eSsthen 	/* process prehash list */
847dd5b221eSsthen 	for(d = db->domains->prehash_list; d; d = d->nsec3->prehash_next) {
848dd5b221eSsthen 		process_prehash_domain(d, zone);
849dd5b221eSsthen 	}
850dd5b221eSsthen 	/* clear prehash list */
851dd5b221eSsthen 	prehash_clear(db->domains);
852dd5b221eSsthen 
85318e77612Sflorian 	if(!check_apex_soa(db, zone, 0)) {
854dd5b221eSsthen 		zone->nsec3_param = NULL;
855dd5b221eSsthen 		zone->nsec3_last = NULL;
856dd5b221eSsthen 	}
857dd5b221eSsthen }
8585bcb494bSjakob 
85962ac0c33Sjakob /* add the NSEC3 rrset to the query answer at the given domain */
86062ac0c33Sjakob static void
nsec3_add_rrset(struct query * query,struct answer * answer,rr_section_type section,struct domain * domain)86162ac0c33Sjakob nsec3_add_rrset(struct query* query, struct answer* answer,
86262ac0c33Sjakob 	rr_section_type section, struct domain* domain)
86362ac0c33Sjakob {
86462ac0c33Sjakob 	if(domain) {
86562ac0c33Sjakob 		rrset_type* rrset = domain_find_rrset(domain, query->zone, TYPE_NSEC3);
86662ac0c33Sjakob 		if(rrset)
86762ac0c33Sjakob 			answer_add_rrset(answer, section, domain, rrset);
86862ac0c33Sjakob 	}
86962ac0c33Sjakob }
87062ac0c33Sjakob 
87162ac0c33Sjakob /* this routine does hashing at query-time. slow. */
87262ac0c33Sjakob static void
nsec3_add_nonexist_proof(struct query * query,struct answer * answer,struct domain * encloser,const dname_type * qname)87362ac0c33Sjakob nsec3_add_nonexist_proof(struct query* query, struct answer* answer,
874dd5b221eSsthen         struct domain* encloser, const dname_type* qname)
87562ac0c33Sjakob {
876dd5b221eSsthen 	uint8_t hash[NSEC3_HASH_LEN];
8775bcb494bSjakob 	const dname_type* to_prove;
878dd5b221eSsthen 	domain_type* cover=0;
87962ac0c33Sjakob 	assert(encloser);
88062ac0c33Sjakob 	/* if query=a.b.c.d encloser=c.d. then proof needed for b.c.d. */
88162ac0c33Sjakob 	/* if query=a.b.c.d encloser=*.c.d. then proof needed for b.c.d. */
88262ac0c33Sjakob 	to_prove = dname_partial_copy(query->region, qname,
88362ac0c33Sjakob 		dname_label_match_count(qname, domain_dname(encloser))+1);
88462ac0c33Sjakob 	/* generate proof that one label below closest encloser does not exist */
885dd5b221eSsthen 	nsec3_hash_and_store(query->zone, to_prove, hash);
886dd5b221eSsthen 	if(nsec3_find_cover(query->zone, hash, sizeof(hash), &cover))
88762ac0c33Sjakob 	{
88862ac0c33Sjakob 		/* exact match, hash collision */
889ee5153b7Sflorian 		domain_type* walk;
890ee5153b7Sflorian 		char hashbuf[512];
891ee5153b7Sflorian 		char reversebuf[512];
892ee5153b7Sflorian 		(void)b32_ntop(hash, sizeof(hash), hashbuf, sizeof(hashbuf));
893ee5153b7Sflorian 		snprintf(reversebuf, sizeof(reversebuf), "(no name in the zone hashes to this nsec3 record)");
894ee5153b7Sflorian 		walk = query->zone->apex;
895ee5153b7Sflorian 		while(walk) {
896ee5153b7Sflorian 			if(walk->nsec3 && walk->nsec3->nsec3_cover == cover) {
897ee5153b7Sflorian 				snprintf(reversebuf, sizeof(reversebuf),
898ee5153b7Sflorian 					"%s %s", domain_to_string(walk),
899ee5153b7Sflorian 					walk->nsec3->nsec3_is_exact?"exact":"no_exact_hash_match");
900ee5153b7Sflorian 				if(walk->nsec3->nsec3_is_exact)
901ee5153b7Sflorian 					break;
902ee5153b7Sflorian 			}
903ee5153b7Sflorian 			if(walk->nsec3 && walk->nsec3->nsec3_ds_parent_cover == cover) {
904ee5153b7Sflorian 				snprintf(reversebuf, sizeof(reversebuf),
905ee5153b7Sflorian 					"%s %s", domain_to_string(walk),
906ee5153b7Sflorian 					walk->nsec3->nsec3_ds_parent_is_exact?"exact":"no_exact_hash_match");
907ee5153b7Sflorian 				if(walk->nsec3->nsec3_ds_parent_is_exact)
908ee5153b7Sflorian 					break;
909ee5153b7Sflorian 			}
910ee5153b7Sflorian 			walk = domain_next(walk);
911ee5153b7Sflorian 		}
912ee5153b7Sflorian 
913ee5153b7Sflorian 
91462ac0c33Sjakob 		/* the hashed name of the query corresponds to an existing name. */
915ee5153b7Sflorian 		VERBOSITY(3, (LOG_ERR, "nsec3 hash collision for name=%s hash=%s reverse=%s",
916ee5153b7Sflorian 			dname_to_string(to_prove, NULL), hashbuf, reversebuf));
91762ac0c33Sjakob 		RCODE_SET(query->packet, RCODE_SERVFAIL);
9188d298c9fSsthen 		/* RFC 8914 - Extended DNS Errors
9198d298c9fSsthen 		 * 4.21. Extended DNS Error Code 0 - Other */
9208d298c9fSsthen 		ASSIGN_EDE_CODE_AND_STRING_LITERAL(query->edns.ede,
9218d298c9fSsthen 			EDE_OTHER, "NSEC3 hash collision");
92262ac0c33Sjakob 		return;
92362ac0c33Sjakob 	}
924dd5b221eSsthen 	else
925dd5b221eSsthen 	{
92662ac0c33Sjakob 		/* cover proves the qname does not exist */
92762ac0c33Sjakob 		nsec3_add_rrset(query, answer, AUTHORITY_SECTION, cover);
92862ac0c33Sjakob 	}
92962ac0c33Sjakob }
93062ac0c33Sjakob 
93162ac0c33Sjakob static void
nsec3_add_closest_encloser_proof(struct query * query,struct answer * answer,struct domain * closest_encloser,const dname_type * qname)93262ac0c33Sjakob nsec3_add_closest_encloser_proof(
93362ac0c33Sjakob 	struct query* query, struct answer* answer,
934dd5b221eSsthen 	struct domain* closest_encloser, const dname_type* qname)
93562ac0c33Sjakob {
93662ac0c33Sjakob 	if(!closest_encloser)
93762ac0c33Sjakob 		return;
93862ac0c33Sjakob 	/* prove that below closest encloser nothing exists */
939dd5b221eSsthen 	nsec3_add_nonexist_proof(query, answer, closest_encloser, qname);
94062ac0c33Sjakob 	/* proof that closest encloser exists */
941dd5b221eSsthen 	if(closest_encloser->nsec3 && closest_encloser->nsec3->nsec3_is_exact)
94262ac0c33Sjakob 		nsec3_add_rrset(query, answer, AUTHORITY_SECTION,
943dd5b221eSsthen 			closest_encloser->nsec3->nsec3_cover);
94462ac0c33Sjakob }
94562ac0c33Sjakob 
94662ac0c33Sjakob void
nsec3_answer_wildcard(struct query * query,struct answer * answer,struct domain * wildcard,const dname_type * qname)94762ac0c33Sjakob nsec3_answer_wildcard(struct query *query, struct answer *answer,
948dd5b221eSsthen         struct domain *wildcard, const dname_type* qname)
94962ac0c33Sjakob {
95062ac0c33Sjakob 	if(!wildcard)
95162ac0c33Sjakob 		return;
952dd5b221eSsthen 	if(!query->zone->nsec3_param)
95362ac0c33Sjakob 		return;
954dd5b221eSsthen 	nsec3_add_nonexist_proof(query, answer, wildcard, qname);
95562ac0c33Sjakob }
95662ac0c33Sjakob 
95762ac0c33Sjakob static void
nsec3_add_ds_proof(struct query * query,struct answer * answer,struct domain * domain,int delegpt)95862ac0c33Sjakob nsec3_add_ds_proof(struct query *query, struct answer *answer,
959f72b2965Sjakob 	struct domain *domain, int delegpt)
96062ac0c33Sjakob {
96162ac0c33Sjakob 	/* assert we are above the zone cut */
96262ac0c33Sjakob 	assert(domain != query->zone->apex);
963dd5b221eSsthen 	if(domain->nsec3 && domain->nsec3->nsec3_ds_parent_is_exact) {
96462ac0c33Sjakob 		/* use NSEC3 record from above the zone cut. */
96562ac0c33Sjakob 		nsec3_add_rrset(query, answer, AUTHORITY_SECTION,
966dd5b221eSsthen 			domain->nsec3->nsec3_ds_parent_cover);
9676e9bf1eeSflorian 	} else if (!delegpt && domain->nsec3 && domain->nsec3->nsec3_is_exact
9686e9bf1eeSflorian 		&& nsec3_domain_part_of_zone(domain->nsec3->nsec3_cover,
9696e9bf1eeSflorian 		query->zone)) {
9705bcb494bSjakob 		nsec3_add_rrset(query, answer, AUTHORITY_SECTION,
971dd5b221eSsthen 			domain->nsec3->nsec3_cover);
97262ac0c33Sjakob 	} else {
97362ac0c33Sjakob 		/* prove closest provable encloser */
97462ac0c33Sjakob 		domain_type* par = domain->parent;
975dd5b221eSsthen 		domain_type* prev_par = 0;
976f72b2965Sjakob 
977dd5b221eSsthen 		while(par && (!par->nsec3 || !par->nsec3->nsec3_is_exact))
97862ac0c33Sjakob 		{
97962ac0c33Sjakob 			prev_par = par;
98062ac0c33Sjakob 			par = par->parent;
98172f0a8e9Ssthen 		}
982dd5b221eSsthen 		assert(par); /* parent zone apex must be provable, thus this ends */
983dd5b221eSsthen 		if(!par->nsec3) return;
98462ac0c33Sjakob 		nsec3_add_rrset(query, answer, AUTHORITY_SECTION,
985dd5b221eSsthen 			par->nsec3->nsec3_cover);
98662ac0c33Sjakob 		/* we took several steps to go to the provable parent, so
98762ac0c33Sjakob 		   the one below it has no exact nsec3, disprove it.
98862ac0c33Sjakob 		   disprove is easy, it has a prehashed cover ptr. */
989dd5b221eSsthen 		if(prev_par && prev_par->nsec3) {
990dd5b221eSsthen 			assert(prev_par != domain &&
991dd5b221eSsthen 				!prev_par->nsec3->nsec3_is_exact);
99262ac0c33Sjakob 			nsec3_add_rrset(query, answer, AUTHORITY_SECTION,
993dd5b221eSsthen 				prev_par->nsec3->nsec3_cover);
994a904e103Sflorian 		} else {
995a904e103Sflorian 			/* the exact case was handled earlier, so this is
996a904e103Sflorian 			 * with a closest-encloser proof, if in the part
997a904e103Sflorian 			 * before the else the closest encloser proof is done,
998a904e103Sflorian 			 * then we do not need to add a DS here because
999a904e103Sflorian 			 * the optout proof is already complete. If not,
1000a904e103Sflorian 			 * we add the nsec3 here to complete the closest
1001a904e103Sflorian 			 * encloser proof with a next closer */
100262ac0c33Sjakob 			/* add optout range from parent zone */
100362ac0c33Sjakob 			/* note: no check of optout bit, resolver checks it */
1004a904e103Sflorian 			if(domain->nsec3) {
100562ac0c33Sjakob 				nsec3_add_rrset(query, answer, AUTHORITY_SECTION,
1006dd5b221eSsthen 					domain->nsec3->nsec3_ds_parent_cover);
100762ac0c33Sjakob 			}
100862ac0c33Sjakob 		}
1009a904e103Sflorian 	}
1010a904e103Sflorian }
101162ac0c33Sjakob 
101262ac0c33Sjakob void
nsec3_answer_nodata(struct query * query,struct answer * answer,struct domain * original)101362ac0c33Sjakob nsec3_answer_nodata(struct query* query, struct answer* answer,
101462ac0c33Sjakob 	struct domain* original)
101562ac0c33Sjakob {
1016dd5b221eSsthen 	if(!query->zone->nsec3_param)
101762ac0c33Sjakob 		return;
101862ac0c33Sjakob 	/* nodata when asking for secure delegation */
101962ac0c33Sjakob 	if(query->qtype == TYPE_DS)
102062ac0c33Sjakob 	{
102162ac0c33Sjakob 		if(original == query->zone->apex) {
102262ac0c33Sjakob 			/* DS at zone apex, but server not authoritative for parent zone */
102362ac0c33Sjakob 			/* so answer at the child zone level */
1024dd5b221eSsthen 			if(original->nsec3 && original->nsec3->nsec3_is_exact)
102562ac0c33Sjakob 				nsec3_add_rrset(query, answer, AUTHORITY_SECTION,
1026dd5b221eSsthen 					original->nsec3->nsec3_cover);
102762ac0c33Sjakob 			return;
102862ac0c33Sjakob 		}
102962ac0c33Sjakob 		/* query->zone must be the parent zone */
1030f72b2965Sjakob 		nsec3_add_ds_proof(query, answer, original, 0);
1031977db6e5Sflorian 		/* if the DS is from a wildcard match */
1032977db6e5Sflorian 		if (original==original->wildcard_child_closest_match
1033977db6e5Sflorian 			&& label_is_wildcard(dname_name(domain_dname(original)))) {
1034977db6e5Sflorian 			/* denial for wildcard is already there */
1035977db6e5Sflorian 			/* add parent proof to have a closest encloser proof for wildcard parent */
1036977db6e5Sflorian 			/* in other words: nsec3 matching closest encloser */
1037977db6e5Sflorian 			if(original->parent && original->parent->nsec3 &&
1038977db6e5Sflorian 				original->parent->nsec3->nsec3_is_exact)
1039977db6e5Sflorian 				nsec3_add_rrset(query, answer, AUTHORITY_SECTION,
1040977db6e5Sflorian 					original->parent->nsec3->nsec3_cover);
1041977db6e5Sflorian 		}
104262ac0c33Sjakob 	}
104362ac0c33Sjakob 	/* the nodata is result from a wildcard match */
104462ac0c33Sjakob 	else if (original==original->wildcard_child_closest_match
104562ac0c33Sjakob 		&& label_is_wildcard(dname_name(domain_dname(original)))) {
104662ac0c33Sjakob 		/* denial for wildcard is already there */
10475bcb494bSjakob 
104862ac0c33Sjakob 		/* add parent proof to have a closest encloser proof for wildcard parent */
10495bcb494bSjakob 		/* in other words: nsec3 matching closest encloser */
1050dd5b221eSsthen 		if(original->parent && original->parent->nsec3 &&
1051dd5b221eSsthen 			original->parent->nsec3->nsec3_is_exact)
105262ac0c33Sjakob 			nsec3_add_rrset(query, answer, AUTHORITY_SECTION,
1053dd5b221eSsthen 				original->parent->nsec3->nsec3_cover);
105462ac0c33Sjakob 		/* proof for wildcard itself */
10555bcb494bSjakob 		/* in other words: nsec3 matching source of synthesis */
1056dd5b221eSsthen 		if(original->nsec3)
105762ac0c33Sjakob 			nsec3_add_rrset(query, answer, AUTHORITY_SECTION,
1058dd5b221eSsthen 				original->nsec3->nsec3_cover);
105962ac0c33Sjakob 	}
106062ac0c33Sjakob 	else {	/* add nsec3 to prove rrset does not exist */
1061275a8d89Sflorian 		if(original->nsec3) {
1062275a8d89Sflorian 			if(!original->nsec3->nsec3_is_exact) {
1063275a8d89Sflorian 				/* go up to an existing parent */
1064275a8d89Sflorian 				while(original->parent && original->parent->nsec3 && !original->parent->nsec3->nsec3_is_exact)
1065275a8d89Sflorian 					original = original->parent;
1066275a8d89Sflorian 			}
106762ac0c33Sjakob 			nsec3_add_rrset(query, answer, AUTHORITY_SECTION,
1068dd5b221eSsthen 				original->nsec3->nsec3_cover);
1069275a8d89Sflorian 			if(!original->nsec3->nsec3_is_exact) {
1070275a8d89Sflorian 				if(original->parent && original->parent->nsec3 && original->parent->nsec3->nsec3_is_exact)
1071275a8d89Sflorian 				    nsec3_add_rrset(query, answer, AUTHORITY_SECTION,
1072275a8d89Sflorian 					original->parent->nsec3->nsec3_cover);
1073275a8d89Sflorian 
1074275a8d89Sflorian 			}
1075dd5b221eSsthen 		}
107662ac0c33Sjakob 	}
107762ac0c33Sjakob }
107862ac0c33Sjakob 
107962ac0c33Sjakob void
nsec3_answer_delegation(struct query * query,struct answer * answer)108062ac0c33Sjakob nsec3_answer_delegation(struct query *query, struct answer *answer)
108162ac0c33Sjakob {
1082dd5b221eSsthen 	if(!query->zone->nsec3_param)
108362ac0c33Sjakob 		return;
1084f72b2965Sjakob 	nsec3_add_ds_proof(query, answer, query->delegation_domain, 1);
108562ac0c33Sjakob }
108662ac0c33Sjakob 
108762ac0c33Sjakob int
domain_has_only_NSEC3(struct domain * domain,struct zone * zone)108862ac0c33Sjakob domain_has_only_NSEC3(struct domain* domain, struct zone* zone)
108962ac0c33Sjakob {
109062ac0c33Sjakob 	/* check for only NSEC3/RRSIG */
109162ac0c33Sjakob 	rrset_type* rrset = domain->rrsets;
109261eb1089Ssthen 	int nsec3_seen = 0;
109362ac0c33Sjakob 	while(rrset)
109462ac0c33Sjakob 	{
109562ac0c33Sjakob 		if(!zone || rrset->zone == zone)
109662ac0c33Sjakob 		{
109762ac0c33Sjakob 			if(rrset->rrs[0].type == TYPE_NSEC3)
109862ac0c33Sjakob 				nsec3_seen = 1;
109961eb1089Ssthen 			else if(rrset->rrs[0].type != TYPE_RRSIG)
110062ac0c33Sjakob 				return 0;
110162ac0c33Sjakob 		}
110262ac0c33Sjakob 		rrset = rrset->next;
110362ac0c33Sjakob 	}
110462ac0c33Sjakob 	return nsec3_seen;
110562ac0c33Sjakob }
110662ac0c33Sjakob 
110762ac0c33Sjakob void
nsec3_answer_authoritative(struct domain ** match,struct query * query,struct answer * answer,struct domain * closest_encloser,const dname_type * qname)110862ac0c33Sjakob nsec3_answer_authoritative(struct domain** match, struct query *query,
110962ac0c33Sjakob 	struct answer *answer, struct domain* closest_encloser,
1110dd5b221eSsthen 	const dname_type* qname)
111162ac0c33Sjakob {
1112dd5b221eSsthen 	if(!query->zone->nsec3_param)
111362ac0c33Sjakob 		return;
111462ac0c33Sjakob 	assert(match);
111562ac0c33Sjakob 	/* there is a match, this has 1 RRset, which is NSEC3, but qtype is not. */
11165bcb494bSjakob         /* !is_existing: no RR types exist at the QNAME, nor at any descendant of QNAME */
11175bcb494bSjakob 	if(*match && !(*match)->is_existing &&
111862ac0c33Sjakob #if 0
111962ac0c33Sjakob 		query->qtype != TYPE_NSEC3 &&
112062ac0c33Sjakob #endif
112162ac0c33Sjakob 		domain_has_only_NSEC3(*match, query->zone))
112262ac0c33Sjakob 	{
112362ac0c33Sjakob 		/* act as if the NSEC3 domain did not exist, name error */
112462ac0c33Sjakob 		*match = 0;
112562ac0c33Sjakob 		/* all nsec3s are directly below the apex, that is closest encloser */
1126dd5b221eSsthen 		if(query->zone->apex->nsec3 &&
1127dd5b221eSsthen 			query->zone->apex->nsec3->nsec3_is_exact)
112862ac0c33Sjakob 			nsec3_add_rrset(query, answer, AUTHORITY_SECTION,
1129dd5b221eSsthen 				query->zone->apex->nsec3->nsec3_cover);
113062ac0c33Sjakob 		/* disprove the nsec3 record. */
1131dd5b221eSsthen 		if(closest_encloser->nsec3)
1132dd5b221eSsthen 			nsec3_add_rrset(query, answer, AUTHORITY_SECTION, closest_encloser->nsec3->nsec3_cover);
113362ac0c33Sjakob 		/* disprove a wildcard */
1134dd5b221eSsthen 		if(query->zone->apex->nsec3)
11355bcb494bSjakob 			nsec3_add_rrset(query, answer, AUTHORITY_SECTION,
1136dd5b221eSsthen 				query->zone->apex->nsec3->nsec3_wcard_child_cover);
113762ac0c33Sjakob 		if (domain_wildcard_child(query->zone->apex)) {
113862ac0c33Sjakob 			/* wildcard exists below the domain */
113962ac0c33Sjakob 			/* wildcard and nsec3 domain clash. server failure. */
114062ac0c33Sjakob 			RCODE_SET(query->packet, RCODE_SERVFAIL);
11418d298c9fSsthen 			/* RFC 8914 - Extended DNS Errors
11428d298c9fSsthen 			 * 4.21. Extended DNS Error Code 0 - Other */
11438d298c9fSsthen 			ASSIGN_EDE_CODE_AND_STRING_LITERAL(query->edns.ede,
11448d298c9fSsthen 				EDE_OTHER, "Wildcard and NSEC3 domain clash");
114562ac0c33Sjakob 		}
114662ac0c33Sjakob 		return;
114762ac0c33Sjakob 	}
11485bcb494bSjakob 	else if(*match && (*match)->is_existing &&
11495bcb494bSjakob #if 0
11505bcb494bSjakob 		query->qtype != TYPE_NSEC3 &&
11515bcb494bSjakob #endif
1152275a8d89Sflorian 		(domain_has_only_NSEC3(*match, query->zone) ||
1153275a8d89Sflorian 		!domain_find_any_rrset(*match, query->zone)))
11545bcb494bSjakob 	{
11555bcb494bSjakob 		/* this looks like a NSEC3 domain, but is actually an empty non-terminal. */
11565bcb494bSjakob 		nsec3_answer_nodata(query, answer, *match);
11575bcb494bSjakob 		return;
11585bcb494bSjakob 	}
115962ac0c33Sjakob 	if(!*match) {
116062ac0c33Sjakob 		/* name error, domain does not exist */
1161dd5b221eSsthen 		nsec3_add_closest_encloser_proof(query, answer, closest_encloser,
1162dd5b221eSsthen 			qname);
1163dd5b221eSsthen 		if(closest_encloser->nsec3)
116462ac0c33Sjakob 			nsec3_add_rrset(query, answer, AUTHORITY_SECTION,
1165dd5b221eSsthen 				closest_encloser->nsec3->nsec3_wcard_child_cover);
116662ac0c33Sjakob 	}
116762ac0c33Sjakob }
116862ac0c33Sjakob 
116962ac0c33Sjakob #endif /* NSEC3 */
1170