xref: /freebsd/contrib/unbound/validator/val_neg.c (revision 8f76bb7d)
1b7579f77SDag-Erling Smørgrav /*
2b7579f77SDag-Erling Smørgrav  * validator/val_neg.c - validator aggressive negative caching functions.
3b7579f77SDag-Erling Smørgrav  *
4b7579f77SDag-Erling Smørgrav  * Copyright (c) 2008, NLnet Labs. All rights reserved.
5b7579f77SDag-Erling Smørgrav  *
6b7579f77SDag-Erling Smørgrav  * This software is open source.
7b7579f77SDag-Erling Smørgrav  *
8b7579f77SDag-Erling Smørgrav  * Redistribution and use in source and binary forms, with or without
9b7579f77SDag-Erling Smørgrav  * modification, are permitted provided that the following conditions
10b7579f77SDag-Erling Smørgrav  * are met:
11b7579f77SDag-Erling Smørgrav  *
12b7579f77SDag-Erling Smørgrav  * Redistributions of source code must retain the above copyright notice,
13b7579f77SDag-Erling Smørgrav  * this list of conditions and the following disclaimer.
14b7579f77SDag-Erling Smørgrav  *
15b7579f77SDag-Erling Smørgrav  * Redistributions in binary form must reproduce the above copyright notice,
16b7579f77SDag-Erling Smørgrav  * this list of conditions and the following disclaimer in the documentation
17b7579f77SDag-Erling Smørgrav  * and/or other materials provided with the distribution.
18b7579f77SDag-Erling Smørgrav  *
19b7579f77SDag-Erling Smørgrav  * Neither the name of the NLNET LABS nor the names of its contributors may
20b7579f77SDag-Erling Smørgrav  * be used to endorse or promote products derived from this software without
21b7579f77SDag-Erling Smørgrav  * specific prior written permission.
22b7579f77SDag-Erling Smørgrav  *
23b7579f77SDag-Erling Smørgrav  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
2417d15b25SDag-Erling Smørgrav  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
2517d15b25SDag-Erling Smørgrav  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
2617d15b25SDag-Erling Smørgrav  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
2717d15b25SDag-Erling Smørgrav  * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
2817d15b25SDag-Erling Smørgrav  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
2917d15b25SDag-Erling Smørgrav  * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
3017d15b25SDag-Erling Smørgrav  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
3117d15b25SDag-Erling Smørgrav  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
3217d15b25SDag-Erling Smørgrav  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
3317d15b25SDag-Erling Smørgrav  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
34b7579f77SDag-Erling Smørgrav  */
35b7579f77SDag-Erling Smørgrav 
36b7579f77SDag-Erling Smørgrav /**
37b7579f77SDag-Erling Smørgrav  * \file
38b7579f77SDag-Erling Smørgrav  *
39b7579f77SDag-Erling Smørgrav  * This file contains helper functions for the validator module.
40b7579f77SDag-Erling Smørgrav  * The functions help with aggressive negative caching.
4105ab2901SDag-Erling Smørgrav  * This creates new denials of existence, and proofs for absence of types
42b7579f77SDag-Erling Smørgrav  * from cached NSEC records.
43b7579f77SDag-Erling Smørgrav  */
44b7579f77SDag-Erling Smørgrav #include "config.h"
45b7579f77SDag-Erling Smørgrav #ifdef HAVE_OPENSSL_SSL_H
46*8f76bb7dSCy Schubert #include <openssl/ssl.h>
478ed2b524SDag-Erling Smørgrav #define NSEC3_SHA_LEN SHA_DIGEST_LENGTH
488ed2b524SDag-Erling Smørgrav #else
498ed2b524SDag-Erling Smørgrav #define NSEC3_SHA_LEN 20
50b7579f77SDag-Erling Smørgrav #endif
51b7579f77SDag-Erling Smørgrav #include "validator/val_neg.h"
52b7579f77SDag-Erling Smørgrav #include "validator/val_nsec.h"
53b7579f77SDag-Erling Smørgrav #include "validator/val_nsec3.h"
54b7579f77SDag-Erling Smørgrav #include "validator/val_utils.h"
55b7579f77SDag-Erling Smørgrav #include "util/data/dname.h"
56b7579f77SDag-Erling Smørgrav #include "util/data/msgreply.h"
57b7579f77SDag-Erling Smørgrav #include "util/log.h"
58b7579f77SDag-Erling Smørgrav #include "util/net_help.h"
59b7579f77SDag-Erling Smørgrav #include "util/config_file.h"
60b7579f77SDag-Erling Smørgrav #include "services/cache/rrset.h"
61b7579f77SDag-Erling Smørgrav #include "services/cache/dns.h"
6209a3aaf3SDag-Erling Smørgrav #include "sldns/rrdef.h"
6309a3aaf3SDag-Erling Smørgrav #include "sldns/sbuffer.h"
64b7579f77SDag-Erling Smørgrav 
val_neg_data_compare(const void * a,const void * b)65b7579f77SDag-Erling Smørgrav int val_neg_data_compare(const void* a, const void* b)
66b7579f77SDag-Erling Smørgrav {
67b7579f77SDag-Erling Smørgrav 	struct val_neg_data* x = (struct val_neg_data*)a;
68b7579f77SDag-Erling Smørgrav 	struct val_neg_data* y = (struct val_neg_data*)b;
69b7579f77SDag-Erling Smørgrav 	int m;
70b7579f77SDag-Erling Smørgrav 	return dname_canon_lab_cmp(x->name, x->labs, y->name, y->labs, &m);
71b7579f77SDag-Erling Smørgrav }
72b7579f77SDag-Erling Smørgrav 
val_neg_zone_compare(const void * a,const void * b)73b7579f77SDag-Erling Smørgrav int val_neg_zone_compare(const void* a, const void* b)
74b7579f77SDag-Erling Smørgrav {
75b7579f77SDag-Erling Smørgrav 	struct val_neg_zone* x = (struct val_neg_zone*)a;
76b7579f77SDag-Erling Smørgrav 	struct val_neg_zone* y = (struct val_neg_zone*)b;
77b7579f77SDag-Erling Smørgrav 	int m;
78b7579f77SDag-Erling Smørgrav 	if(x->dclass != y->dclass) {
79b7579f77SDag-Erling Smørgrav 		if(x->dclass < y->dclass)
80b7579f77SDag-Erling Smørgrav 			return -1;
81b7579f77SDag-Erling Smørgrav 		return 1;
82b7579f77SDag-Erling Smørgrav 	}
83b7579f77SDag-Erling Smørgrav 	return dname_canon_lab_cmp(x->name, x->labs, y->name, y->labs, &m);
84b7579f77SDag-Erling Smørgrav }
85b7579f77SDag-Erling Smørgrav 
val_neg_create(struct config_file * cfg,size_t maxiter)86b7579f77SDag-Erling Smørgrav struct val_neg_cache* val_neg_create(struct config_file* cfg, size_t maxiter)
87b7579f77SDag-Erling Smørgrav {
88b7579f77SDag-Erling Smørgrav 	struct val_neg_cache* neg = (struct val_neg_cache*)calloc(1,
89b7579f77SDag-Erling Smørgrav 		sizeof(*neg));
90b7579f77SDag-Erling Smørgrav 	if(!neg) {
91b7579f77SDag-Erling Smørgrav 		log_err("Could not create neg cache: out of memory");
92b7579f77SDag-Erling Smørgrav 		return NULL;
93b7579f77SDag-Erling Smørgrav 	}
94b7579f77SDag-Erling Smørgrav 	neg->nsec3_max_iter = maxiter;
95b7579f77SDag-Erling Smørgrav 	neg->max = 1024*1024; /* 1 M is thousands of entries */
96b7579f77SDag-Erling Smørgrav 	if(cfg) neg->max = cfg->neg_cache_size;
97b7579f77SDag-Erling Smørgrav 	rbtree_init(&neg->tree, &val_neg_zone_compare);
98b7579f77SDag-Erling Smørgrav 	lock_basic_init(&neg->lock);
99b7579f77SDag-Erling Smørgrav 	lock_protect(&neg->lock, neg, sizeof(*neg));
100b7579f77SDag-Erling Smørgrav 	return neg;
101b7579f77SDag-Erling Smørgrav }
102b7579f77SDag-Erling Smørgrav 
val_neg_get_mem(struct val_neg_cache * neg)103b7579f77SDag-Erling Smørgrav size_t val_neg_get_mem(struct val_neg_cache* neg)
104b7579f77SDag-Erling Smørgrav {
105b7579f77SDag-Erling Smørgrav 	size_t result;
106b7579f77SDag-Erling Smørgrav 	lock_basic_lock(&neg->lock);
107b7579f77SDag-Erling Smørgrav 	result = sizeof(*neg) + neg->use;
108b7579f77SDag-Erling Smørgrav 	lock_basic_unlock(&neg->lock);
109b7579f77SDag-Erling Smørgrav 	return result;
110b7579f77SDag-Erling Smørgrav }
111b7579f77SDag-Erling Smørgrav 
112b7579f77SDag-Erling Smørgrav /** clear datas on cache deletion */
113b7579f77SDag-Erling Smørgrav static void
neg_clear_datas(rbnode_type * n,void * ATTR_UNUSED (arg))1143005e0a3SDag-Erling Smørgrav neg_clear_datas(rbnode_type* n, void* ATTR_UNUSED(arg))
115b7579f77SDag-Erling Smørgrav {
116b7579f77SDag-Erling Smørgrav 	struct val_neg_data* d = (struct val_neg_data*)n;
117b7579f77SDag-Erling Smørgrav 	free(d->name);
118b7579f77SDag-Erling Smørgrav 	free(d);
119b7579f77SDag-Erling Smørgrav }
120b7579f77SDag-Erling Smørgrav 
121b7579f77SDag-Erling Smørgrav /** clear zones on cache deletion */
122b7579f77SDag-Erling Smørgrav static void
neg_clear_zones(rbnode_type * n,void * ATTR_UNUSED (arg))1233005e0a3SDag-Erling Smørgrav neg_clear_zones(rbnode_type* n, void* ATTR_UNUSED(arg))
124b7579f77SDag-Erling Smørgrav {
125b7579f77SDag-Erling Smørgrav 	struct val_neg_zone* z = (struct val_neg_zone*)n;
126b7579f77SDag-Erling Smørgrav 	/* delete all the rrset entries in the tree */
127b7579f77SDag-Erling Smørgrav 	traverse_postorder(&z->tree, &neg_clear_datas, NULL);
128b7579f77SDag-Erling Smørgrav 	free(z->nsec3_salt);
129b7579f77SDag-Erling Smørgrav 	free(z->name);
130b7579f77SDag-Erling Smørgrav 	free(z);
131b7579f77SDag-Erling Smørgrav }
132b7579f77SDag-Erling Smørgrav 
neg_cache_delete(struct val_neg_cache * neg)133b7579f77SDag-Erling Smørgrav void neg_cache_delete(struct val_neg_cache* neg)
134b7579f77SDag-Erling Smørgrav {
135b7579f77SDag-Erling Smørgrav 	if(!neg) return;
136b7579f77SDag-Erling Smørgrav 	lock_basic_destroy(&neg->lock);
137b7579f77SDag-Erling Smørgrav 	/* delete all the zones in the tree */
138b7579f77SDag-Erling Smørgrav 	traverse_postorder(&neg->tree, &neg_clear_zones, NULL);
139b7579f77SDag-Erling Smørgrav 	free(neg);
140b7579f77SDag-Erling Smørgrav }
141b7579f77SDag-Erling Smørgrav 
142b7579f77SDag-Erling Smørgrav /**
143b7579f77SDag-Erling Smørgrav  * Put data element at the front of the LRU list.
144b7579f77SDag-Erling Smørgrav  * @param neg: negative cache with LRU start and end.
145b7579f77SDag-Erling Smørgrav  * @param data: this data is fronted.
146b7579f77SDag-Erling Smørgrav  */
neg_lru_front(struct val_neg_cache * neg,struct val_neg_data * data)147b7579f77SDag-Erling Smørgrav static void neg_lru_front(struct val_neg_cache* neg,
148b7579f77SDag-Erling Smørgrav 	struct val_neg_data* data)
149b7579f77SDag-Erling Smørgrav {
150b7579f77SDag-Erling Smørgrav 	data->prev = NULL;
151b7579f77SDag-Erling Smørgrav 	data->next = neg->first;
152b7579f77SDag-Erling Smørgrav 	if(!neg->first)
153b7579f77SDag-Erling Smørgrav 		neg->last = data;
154b7579f77SDag-Erling Smørgrav 	else	neg->first->prev = data;
155b7579f77SDag-Erling Smørgrav 	neg->first = data;
156b7579f77SDag-Erling Smørgrav }
157b7579f77SDag-Erling Smørgrav 
158b7579f77SDag-Erling Smørgrav /**
159b7579f77SDag-Erling Smørgrav  * Remove data element from LRU list.
160b7579f77SDag-Erling Smørgrav  * @param neg: negative cache with LRU start and end.
161b7579f77SDag-Erling Smørgrav  * @param data: this data is removed from the list.
162b7579f77SDag-Erling Smørgrav  */
neg_lru_remove(struct val_neg_cache * neg,struct val_neg_data * data)163b7579f77SDag-Erling Smørgrav static void neg_lru_remove(struct val_neg_cache* neg,
164b7579f77SDag-Erling Smørgrav 	struct val_neg_data* data)
165b7579f77SDag-Erling Smørgrav {
166b7579f77SDag-Erling Smørgrav 	if(data->prev)
167b7579f77SDag-Erling Smørgrav 		data->prev->next = data->next;
168b7579f77SDag-Erling Smørgrav 	else	neg->first = data->next;
169b7579f77SDag-Erling Smørgrav 	if(data->next)
170b7579f77SDag-Erling Smørgrav 		data->next->prev = data->prev;
171b7579f77SDag-Erling Smørgrav 	else	neg->last = data->prev;
172b7579f77SDag-Erling Smørgrav }
173b7579f77SDag-Erling Smørgrav 
174b7579f77SDag-Erling Smørgrav /**
175b7579f77SDag-Erling Smørgrav  * Touch LRU for data element, put it at the start of the LRU list.
176b7579f77SDag-Erling Smørgrav  * @param neg: negative cache with LRU start and end.
177b7579f77SDag-Erling Smørgrav  * @param data: this data is used.
178b7579f77SDag-Erling Smørgrav  */
neg_lru_touch(struct val_neg_cache * neg,struct val_neg_data * data)179b7579f77SDag-Erling Smørgrav static void neg_lru_touch(struct val_neg_cache* neg,
180b7579f77SDag-Erling Smørgrav 	struct val_neg_data* data)
181b7579f77SDag-Erling Smørgrav {
182b7579f77SDag-Erling Smørgrav 	if(data == neg->first)
183b7579f77SDag-Erling Smørgrav 		return; /* nothing to do */
184b7579f77SDag-Erling Smørgrav 	/* remove from current lru position */
185b7579f77SDag-Erling Smørgrav 	neg_lru_remove(neg, data);
186b7579f77SDag-Erling Smørgrav 	/* add at front */
187b7579f77SDag-Erling Smørgrav 	neg_lru_front(neg, data);
188b7579f77SDag-Erling Smørgrav }
189b7579f77SDag-Erling Smørgrav 
190b7579f77SDag-Erling Smørgrav /**
191b7579f77SDag-Erling Smørgrav  * Delete a zone element from the negative cache.
192b7579f77SDag-Erling Smørgrav  * May delete other zone elements to keep tree coherent, or
193b7579f77SDag-Erling Smørgrav  * only mark the element as 'not in use'.
194b7579f77SDag-Erling Smørgrav  * @param neg: negative cache.
195b7579f77SDag-Erling Smørgrav  * @param z: zone element to delete.
196b7579f77SDag-Erling Smørgrav  */
neg_delete_zone(struct val_neg_cache * neg,struct val_neg_zone * z)197b7579f77SDag-Erling Smørgrav static void neg_delete_zone(struct val_neg_cache* neg, struct val_neg_zone* z)
198b7579f77SDag-Erling Smørgrav {
199b7579f77SDag-Erling Smørgrav 	struct val_neg_zone* p, *np;
200b7579f77SDag-Erling Smørgrav 	if(!z) return;
201b7579f77SDag-Erling Smørgrav 	log_assert(z->in_use);
202b7579f77SDag-Erling Smørgrav 	log_assert(z->count > 0);
203b7579f77SDag-Erling Smørgrav 	z->in_use = 0;
204b7579f77SDag-Erling Smørgrav 
205b7579f77SDag-Erling Smørgrav 	/* go up the tree and reduce counts */
206b7579f77SDag-Erling Smørgrav 	p = z;
207b7579f77SDag-Erling Smørgrav 	while(p) {
208b7579f77SDag-Erling Smørgrav 		log_assert(p->count > 0);
209b7579f77SDag-Erling Smørgrav 		p->count --;
210b7579f77SDag-Erling Smørgrav 		p = p->parent;
211b7579f77SDag-Erling Smørgrav 	}
212b7579f77SDag-Erling Smørgrav 
213b7579f77SDag-Erling Smørgrav 	/* remove zones with zero count */
214b7579f77SDag-Erling Smørgrav 	p = z;
215b7579f77SDag-Erling Smørgrav 	while(p && p->count == 0) {
216b7579f77SDag-Erling Smørgrav 		np = p->parent;
217b7579f77SDag-Erling Smørgrav 		(void)rbtree_delete(&neg->tree, &p->node);
218b7579f77SDag-Erling Smørgrav 		neg->use -= p->len + sizeof(*p);
219b7579f77SDag-Erling Smørgrav 		free(p->nsec3_salt);
220b7579f77SDag-Erling Smørgrav 		free(p->name);
221b7579f77SDag-Erling Smørgrav 		free(p);
222b7579f77SDag-Erling Smørgrav 		p = np;
223b7579f77SDag-Erling Smørgrav 	}
224b7579f77SDag-Erling Smørgrav }
225b7579f77SDag-Erling Smørgrav 
neg_delete_data(struct val_neg_cache * neg,struct val_neg_data * el)226b7579f77SDag-Erling Smørgrav void neg_delete_data(struct val_neg_cache* neg, struct val_neg_data* el)
227b7579f77SDag-Erling Smørgrav {
228b7579f77SDag-Erling Smørgrav 	struct val_neg_zone* z;
229b7579f77SDag-Erling Smørgrav 	struct val_neg_data* p, *np;
230b7579f77SDag-Erling Smørgrav 	if(!el) return;
231b7579f77SDag-Erling Smørgrav 	z = el->zone;
232b7579f77SDag-Erling Smørgrav 	log_assert(el->in_use);
233b7579f77SDag-Erling Smørgrav 	log_assert(el->count > 0);
234b7579f77SDag-Erling Smørgrav 	el->in_use = 0;
235b7579f77SDag-Erling Smørgrav 
236b7579f77SDag-Erling Smørgrav 	/* remove it from the lru list */
237b7579f77SDag-Erling Smørgrav 	neg_lru_remove(neg, el);
238e86b9096SDag-Erling Smørgrav 	log_assert(neg->first != el && neg->last != el);
239b7579f77SDag-Erling Smørgrav 
240b7579f77SDag-Erling Smørgrav 	/* go up the tree and reduce counts */
241b7579f77SDag-Erling Smørgrav 	p = el;
242b7579f77SDag-Erling Smørgrav 	while(p) {
243b7579f77SDag-Erling Smørgrav 		log_assert(p->count > 0);
244b7579f77SDag-Erling Smørgrav 		p->count --;
245b7579f77SDag-Erling Smørgrav 		p = p->parent;
246b7579f77SDag-Erling Smørgrav 	}
247b7579f77SDag-Erling Smørgrav 
248b7579f77SDag-Erling Smørgrav 	/* delete 0 count items from tree */
249b7579f77SDag-Erling Smørgrav 	p = el;
250b7579f77SDag-Erling Smørgrav 	while(p && p->count == 0) {
251b7579f77SDag-Erling Smørgrav 		np = p->parent;
252b7579f77SDag-Erling Smørgrav 		(void)rbtree_delete(&z->tree, &p->node);
253b7579f77SDag-Erling Smørgrav 		neg->use -= p->len + sizeof(*p);
254b7579f77SDag-Erling Smørgrav 		free(p->name);
255b7579f77SDag-Erling Smørgrav 		free(p);
256b7579f77SDag-Erling Smørgrav 		p = np;
257b7579f77SDag-Erling Smørgrav 	}
258b7579f77SDag-Erling Smørgrav 
259b7579f77SDag-Erling Smørgrav 	/* check if the zone is now unused */
260b7579f77SDag-Erling Smørgrav 	if(z->tree.count == 0) {
261b7579f77SDag-Erling Smørgrav 		neg_delete_zone(neg, z);
262b7579f77SDag-Erling Smørgrav 	}
263b7579f77SDag-Erling Smørgrav }
264b7579f77SDag-Erling Smørgrav 
265b7579f77SDag-Erling Smørgrav /**
266b7579f77SDag-Erling Smørgrav  * Create more space in negative cache
267b7579f77SDag-Erling Smørgrav  * The oldest elements are deleted until enough space is present.
268b7579f77SDag-Erling Smørgrav  * Empty zones are deleted.
269b7579f77SDag-Erling Smørgrav  * @param neg: negative cache.
270b7579f77SDag-Erling Smørgrav  * @param need: how many bytes are needed.
271b7579f77SDag-Erling Smørgrav  */
neg_make_space(struct val_neg_cache * neg,size_t need)272b7579f77SDag-Erling Smørgrav static void neg_make_space(struct val_neg_cache* neg, size_t need)
273b7579f77SDag-Erling Smørgrav {
274b7579f77SDag-Erling Smørgrav 	/* delete elements until enough space or its empty */
275b7579f77SDag-Erling Smørgrav 	while(neg->last && neg->max < neg->use + need) {
276b7579f77SDag-Erling Smørgrav 		neg_delete_data(neg, neg->last);
277b7579f77SDag-Erling Smørgrav 	}
278b7579f77SDag-Erling Smørgrav }
279b7579f77SDag-Erling Smørgrav 
neg_find_zone(struct val_neg_cache * neg,uint8_t * nm,size_t len,uint16_t dclass)280b7579f77SDag-Erling Smørgrav struct val_neg_zone* neg_find_zone(struct val_neg_cache* neg,
281b7579f77SDag-Erling Smørgrav 	uint8_t* nm, size_t len, uint16_t dclass)
282b7579f77SDag-Erling Smørgrav {
283b7579f77SDag-Erling Smørgrav 	struct val_neg_zone lookfor;
284b7579f77SDag-Erling Smørgrav 	struct val_neg_zone* result;
285b7579f77SDag-Erling Smørgrav 	lookfor.node.key = &lookfor;
286b7579f77SDag-Erling Smørgrav 	lookfor.name = nm;
287b7579f77SDag-Erling Smørgrav 	lookfor.len = len;
288b7579f77SDag-Erling Smørgrav 	lookfor.labs = dname_count_labels(lookfor.name);
289b7579f77SDag-Erling Smørgrav 	lookfor.dclass = dclass;
290b7579f77SDag-Erling Smørgrav 
291b7579f77SDag-Erling Smørgrav 	result = (struct val_neg_zone*)
292b7579f77SDag-Erling Smørgrav 		rbtree_search(&neg->tree, lookfor.node.key);
293b7579f77SDag-Erling Smørgrav 	return result;
294b7579f77SDag-Erling Smørgrav }
295b7579f77SDag-Erling Smørgrav 
296b7579f77SDag-Erling Smørgrav /**
297b7579f77SDag-Erling Smørgrav  * Find the given data
298b7579f77SDag-Erling Smørgrav  * @param zone: negative zone
299b7579f77SDag-Erling Smørgrav  * @param nm: what to look for.
300b7579f77SDag-Erling Smørgrav  * @param len: length of nm
301b7579f77SDag-Erling Smørgrav  * @param labs: labels in nm
302b7579f77SDag-Erling Smørgrav  * @return data or NULL if not found.
303b7579f77SDag-Erling Smørgrav  */
neg_find_data(struct val_neg_zone * zone,uint8_t * nm,size_t len,int labs)304b7579f77SDag-Erling Smørgrav static struct val_neg_data* neg_find_data(struct val_neg_zone* zone,
305b7579f77SDag-Erling Smørgrav 	uint8_t* nm, size_t len, int labs)
306b7579f77SDag-Erling Smørgrav {
307b7579f77SDag-Erling Smørgrav 	struct val_neg_data lookfor;
308b7579f77SDag-Erling Smørgrav 	struct val_neg_data* result;
309b7579f77SDag-Erling Smørgrav 	lookfor.node.key = &lookfor;
310b7579f77SDag-Erling Smørgrav 	lookfor.name = nm;
311b7579f77SDag-Erling Smørgrav 	lookfor.len = len;
312b7579f77SDag-Erling Smørgrav 	lookfor.labs = labs;
313b7579f77SDag-Erling Smørgrav 
314b7579f77SDag-Erling Smørgrav 	result = (struct val_neg_data*)
315b7579f77SDag-Erling Smørgrav 		rbtree_search(&zone->tree, lookfor.node.key);
316b7579f77SDag-Erling Smørgrav 	return result;
317b7579f77SDag-Erling Smørgrav }
318b7579f77SDag-Erling Smørgrav 
319b7579f77SDag-Erling Smørgrav /**
320b7579f77SDag-Erling Smørgrav  * Calculate space needed for the data and all its parents
321b7579f77SDag-Erling Smørgrav  * @param rep: NSEC entries.
322b7579f77SDag-Erling Smørgrav  * @return size.
323b7579f77SDag-Erling Smørgrav  */
calc_data_need(struct reply_info * rep)324b7579f77SDag-Erling Smørgrav static size_t calc_data_need(struct reply_info* rep)
325b7579f77SDag-Erling Smørgrav {
326b7579f77SDag-Erling Smørgrav 	uint8_t* d;
327b7579f77SDag-Erling Smørgrav 	size_t i, len, res = 0;
328b7579f77SDag-Erling Smørgrav 
329b7579f77SDag-Erling Smørgrav 	for(i=rep->an_numrrsets; i<rep->an_numrrsets+rep->ns_numrrsets; i++) {
330b7579f77SDag-Erling Smørgrav 		if(ntohs(rep->rrsets[i]->rk.type) == LDNS_RR_TYPE_NSEC) {
331b7579f77SDag-Erling Smørgrav 			d = rep->rrsets[i]->rk.dname;
332b7579f77SDag-Erling Smørgrav 			len = rep->rrsets[i]->rk.dname_len;
333b7579f77SDag-Erling Smørgrav 			res = sizeof(struct val_neg_data) + len;
334b7579f77SDag-Erling Smørgrav 			while(!dname_is_root(d)) {
335b7579f77SDag-Erling Smørgrav 				log_assert(len > 1); /* not root label */
336b7579f77SDag-Erling Smørgrav 				dname_remove_label(&d, &len);
337b7579f77SDag-Erling Smørgrav 				res += sizeof(struct val_neg_data) + len;
338b7579f77SDag-Erling Smørgrav 			}
339b7579f77SDag-Erling Smørgrav 		}
340b7579f77SDag-Erling Smørgrav 	}
341b7579f77SDag-Erling Smørgrav 	return res;
342b7579f77SDag-Erling Smørgrav }
343b7579f77SDag-Erling Smørgrav 
344b7579f77SDag-Erling Smørgrav /**
345b7579f77SDag-Erling Smørgrav  * Calculate space needed for zone and all its parents
346b7579f77SDag-Erling Smørgrav  * @param d: name of zone
347b7579f77SDag-Erling Smørgrav  * @param len: length of name
348b7579f77SDag-Erling Smørgrav  * @return size.
349b7579f77SDag-Erling Smørgrav  */
calc_zone_need(uint8_t * d,size_t len)350b7579f77SDag-Erling Smørgrav static size_t calc_zone_need(uint8_t* d, size_t len)
351b7579f77SDag-Erling Smørgrav {
352b7579f77SDag-Erling Smørgrav 	size_t res = sizeof(struct val_neg_zone) + len;
353b7579f77SDag-Erling Smørgrav 	while(!dname_is_root(d)) {
354b7579f77SDag-Erling Smørgrav 		log_assert(len > 1); /* not root label */
355b7579f77SDag-Erling Smørgrav 		dname_remove_label(&d, &len);
356b7579f77SDag-Erling Smørgrav 		res += sizeof(struct val_neg_zone) + len;
357b7579f77SDag-Erling Smørgrav 	}
358b7579f77SDag-Erling Smørgrav 	return res;
359b7579f77SDag-Erling Smørgrav }
360b7579f77SDag-Erling Smørgrav 
361b7579f77SDag-Erling Smørgrav /**
362b7579f77SDag-Erling Smørgrav  * Find closest existing parent zone of the given name.
363b7579f77SDag-Erling Smørgrav  * @param neg: negative cache.
364b7579f77SDag-Erling Smørgrav  * @param nm: name to look for
365b7579f77SDag-Erling Smørgrav  * @param nm_len: length of nm
366b7579f77SDag-Erling Smørgrav  * @param labs: labelcount of nm.
367b7579f77SDag-Erling Smørgrav  * @param qclass: class.
368b7579f77SDag-Erling Smørgrav  * @return the zone or NULL if none found.
369b7579f77SDag-Erling Smørgrav  */
neg_closest_zone_parent(struct val_neg_cache * neg,uint8_t * nm,size_t nm_len,int labs,uint16_t qclass)370b7579f77SDag-Erling Smørgrav static struct val_neg_zone* neg_closest_zone_parent(struct val_neg_cache* neg,
371b7579f77SDag-Erling Smørgrav 	uint8_t* nm, size_t nm_len, int labs, uint16_t qclass)
372b7579f77SDag-Erling Smørgrav {
373b7579f77SDag-Erling Smørgrav 	struct val_neg_zone key;
374b7579f77SDag-Erling Smørgrav 	struct val_neg_zone* result;
3753005e0a3SDag-Erling Smørgrav 	rbnode_type* res = NULL;
376b7579f77SDag-Erling Smørgrav 	key.node.key = &key;
377b7579f77SDag-Erling Smørgrav 	key.name = nm;
378b7579f77SDag-Erling Smørgrav 	key.len = nm_len;
379b7579f77SDag-Erling Smørgrav 	key.labs = labs;
380b7579f77SDag-Erling Smørgrav 	key.dclass = qclass;
381b7579f77SDag-Erling Smørgrav 	if(rbtree_find_less_equal(&neg->tree, &key, &res)) {
382b7579f77SDag-Erling Smørgrav 		/* exact match */
383b7579f77SDag-Erling Smørgrav 		result = (struct val_neg_zone*)res;
384b7579f77SDag-Erling Smørgrav 	} else {
385b7579f77SDag-Erling Smørgrav 		/* smaller element (or no element) */
386b7579f77SDag-Erling Smørgrav 		int m;
387b7579f77SDag-Erling Smørgrav 		result = (struct val_neg_zone*)res;
388b7579f77SDag-Erling Smørgrav 		if(!result || result->dclass != qclass)
389b7579f77SDag-Erling Smørgrav 			return NULL;
390b7579f77SDag-Erling Smørgrav 		/* count number of labels matched */
391b7579f77SDag-Erling Smørgrav 		(void)dname_lab_cmp(result->name, result->labs, key.name,
392b7579f77SDag-Erling Smørgrav 			key.labs, &m);
393b7579f77SDag-Erling Smørgrav 		while(result) { /* go up until qname is subdomain of stub */
394b7579f77SDag-Erling Smørgrav 			if(result->labs <= m)
395b7579f77SDag-Erling Smørgrav 				break;
396b7579f77SDag-Erling Smørgrav 			result = result->parent;
397b7579f77SDag-Erling Smørgrav 		}
398b7579f77SDag-Erling Smørgrav 	}
399b7579f77SDag-Erling Smørgrav 	return result;
400b7579f77SDag-Erling Smørgrav }
401b7579f77SDag-Erling Smørgrav 
402b7579f77SDag-Erling Smørgrav /**
403b7579f77SDag-Erling Smørgrav  * Find closest existing parent data for the given name.
404b7579f77SDag-Erling Smørgrav  * @param zone: to look in.
405b7579f77SDag-Erling Smørgrav  * @param nm: name to look for
406b7579f77SDag-Erling Smørgrav  * @param nm_len: length of nm
407b7579f77SDag-Erling Smørgrav  * @param labs: labelcount of nm.
408b7579f77SDag-Erling Smørgrav  * @return the data or NULL if none found.
409b7579f77SDag-Erling Smørgrav  */
neg_closest_data_parent(struct val_neg_zone * zone,uint8_t * nm,size_t nm_len,int labs)410b7579f77SDag-Erling Smørgrav static struct val_neg_data* neg_closest_data_parent(
411b7579f77SDag-Erling Smørgrav 	struct val_neg_zone* zone, uint8_t* nm, size_t nm_len, int labs)
412b7579f77SDag-Erling Smørgrav {
413b7579f77SDag-Erling Smørgrav 	struct val_neg_data key;
414b7579f77SDag-Erling Smørgrav 	struct val_neg_data* result;
4153005e0a3SDag-Erling Smørgrav 	rbnode_type* res = NULL;
416b7579f77SDag-Erling Smørgrav 	key.node.key = &key;
417b7579f77SDag-Erling Smørgrav 	key.name = nm;
418b7579f77SDag-Erling Smørgrav 	key.len = nm_len;
419b7579f77SDag-Erling Smørgrav 	key.labs = labs;
420b7579f77SDag-Erling Smørgrav 	if(rbtree_find_less_equal(&zone->tree, &key, &res)) {
421b7579f77SDag-Erling Smørgrav 		/* exact match */
422b7579f77SDag-Erling Smørgrav 		result = (struct val_neg_data*)res;
423b7579f77SDag-Erling Smørgrav 	} else {
424b7579f77SDag-Erling Smørgrav 		/* smaller element (or no element) */
425b7579f77SDag-Erling Smørgrav 		int m;
426b7579f77SDag-Erling Smørgrav 		result = (struct val_neg_data*)res;
427b7579f77SDag-Erling Smørgrav 		if(!result)
428b7579f77SDag-Erling Smørgrav 			return NULL;
429b7579f77SDag-Erling Smørgrav 		/* count number of labels matched */
430b7579f77SDag-Erling Smørgrav 		(void)dname_lab_cmp(result->name, result->labs, key.name,
431b7579f77SDag-Erling Smørgrav 			key.labs, &m);
432b7579f77SDag-Erling Smørgrav 		while(result) { /* go up until qname is subdomain of stub */
433b7579f77SDag-Erling Smørgrav 			if(result->labs <= m)
434b7579f77SDag-Erling Smørgrav 				break;
435b7579f77SDag-Erling Smørgrav 			result = result->parent;
436b7579f77SDag-Erling Smørgrav 		}
437b7579f77SDag-Erling Smørgrav 	}
438b7579f77SDag-Erling Smørgrav 	return result;
439b7579f77SDag-Erling Smørgrav }
440b7579f77SDag-Erling Smørgrav 
441b7579f77SDag-Erling Smørgrav /**
442b7579f77SDag-Erling Smørgrav  * Create a single zone node
443b7579f77SDag-Erling Smørgrav  * @param nm: name for zone (copied)
444b7579f77SDag-Erling Smørgrav  * @param nm_len: length of name
445b7579f77SDag-Erling Smørgrav  * @param labs: labels in name.
446b7579f77SDag-Erling Smørgrav  * @param dclass: class of zone, host order.
447b7579f77SDag-Erling Smørgrav  * @return new zone or NULL on failure
448b7579f77SDag-Erling Smørgrav  */
neg_setup_zone_node(uint8_t * nm,size_t nm_len,int labs,uint16_t dclass)449b7579f77SDag-Erling Smørgrav static struct val_neg_zone* neg_setup_zone_node(
450b7579f77SDag-Erling Smørgrav 	uint8_t* nm, size_t nm_len, int labs, uint16_t dclass)
451b7579f77SDag-Erling Smørgrav {
452b7579f77SDag-Erling Smørgrav 	struct val_neg_zone* zone =
453b7579f77SDag-Erling Smørgrav 		(struct val_neg_zone*)calloc(1, sizeof(*zone));
454b7579f77SDag-Erling Smørgrav 	if(!zone) {
455b7579f77SDag-Erling Smørgrav 		return NULL;
456b7579f77SDag-Erling Smørgrav 	}
457b7579f77SDag-Erling Smørgrav 	zone->node.key = zone;
458b7579f77SDag-Erling Smørgrav 	zone->name = memdup(nm, nm_len);
459b7579f77SDag-Erling Smørgrav 	if(!zone->name) {
460b7579f77SDag-Erling Smørgrav 		free(zone);
461b7579f77SDag-Erling Smørgrav 		return NULL;
462b7579f77SDag-Erling Smørgrav 	}
463b7579f77SDag-Erling Smørgrav 	zone->len = nm_len;
464b7579f77SDag-Erling Smørgrav 	zone->labs = labs;
465b7579f77SDag-Erling Smørgrav 	zone->dclass = dclass;
466b7579f77SDag-Erling Smørgrav 
467b7579f77SDag-Erling Smørgrav 	rbtree_init(&zone->tree, &val_neg_data_compare);
468b7579f77SDag-Erling Smørgrav 	return zone;
469b7579f77SDag-Erling Smørgrav }
470b7579f77SDag-Erling Smørgrav 
471b7579f77SDag-Erling Smørgrav /**
472b7579f77SDag-Erling Smørgrav  * Create a linked list of parent zones, starting at longname ending on
473b7579f77SDag-Erling Smørgrav  * the parent (can be NULL, creates to the root).
474b7579f77SDag-Erling Smørgrav  * @param nm: name for lowest in chain
475b7579f77SDag-Erling Smørgrav  * @param nm_len: length of name
476b7579f77SDag-Erling Smørgrav  * @param labs: labels in name.
477b7579f77SDag-Erling Smørgrav  * @param dclass: class of zone.
478b7579f77SDag-Erling Smørgrav  * @param parent: NULL for to root, else so it fits under here.
479b7579f77SDag-Erling Smørgrav  * @return zone; a chain of zones and their parents up to the parent.
480b7579f77SDag-Erling Smørgrav  *  	or NULL on malloc failure
481b7579f77SDag-Erling Smørgrav  */
neg_zone_chain(uint8_t * nm,size_t nm_len,int labs,uint16_t dclass,struct val_neg_zone * parent)482b7579f77SDag-Erling Smørgrav static struct val_neg_zone* neg_zone_chain(
483b7579f77SDag-Erling Smørgrav 	uint8_t* nm, size_t nm_len, int labs, uint16_t dclass,
484b7579f77SDag-Erling Smørgrav 	struct val_neg_zone* parent)
485b7579f77SDag-Erling Smørgrav {
486b7579f77SDag-Erling Smørgrav 	int i;
487b7579f77SDag-Erling Smørgrav 	int tolabs = parent?parent->labs:0;
488b7579f77SDag-Erling Smørgrav 	struct val_neg_zone* zone, *prev = NULL, *first = NULL;
489b7579f77SDag-Erling Smørgrav 
490b7579f77SDag-Erling Smørgrav 	/* create the new subtree, i is labelcount of current creation */
491b7579f77SDag-Erling Smørgrav 	/* this creates a 'first' to z->parent=NULL list of zones */
492b7579f77SDag-Erling Smørgrav 	for(i=labs; i!=tolabs; i--) {
493b7579f77SDag-Erling Smørgrav 		/* create new item */
494b7579f77SDag-Erling Smørgrav 		zone = neg_setup_zone_node(nm, nm_len, i, dclass);
495b7579f77SDag-Erling Smørgrav 		if(!zone) {
496b7579f77SDag-Erling Smørgrav 			/* need to delete other allocations in this routine!*/
497b7579f77SDag-Erling Smørgrav 			struct val_neg_zone* p=first, *np;
498b7579f77SDag-Erling Smørgrav 			while(p) {
499b7579f77SDag-Erling Smørgrav 				np = p->parent;
500b7579f77SDag-Erling Smørgrav 				free(p->name);
50117d15b25SDag-Erling Smørgrav 				free(p);
502b7579f77SDag-Erling Smørgrav 				p = np;
503b7579f77SDag-Erling Smørgrav 			}
504b7579f77SDag-Erling Smørgrav 			return NULL;
505b7579f77SDag-Erling Smørgrav 		}
506b7579f77SDag-Erling Smørgrav 		if(i == labs) {
507b7579f77SDag-Erling Smørgrav 			first = zone;
508b7579f77SDag-Erling Smørgrav 		} else {
509b7579f77SDag-Erling Smørgrav 			prev->parent = zone;
510b7579f77SDag-Erling Smørgrav 		}
511b7579f77SDag-Erling Smørgrav 		/* prepare for next name */
512b7579f77SDag-Erling Smørgrav 		prev = zone;
513b7579f77SDag-Erling Smørgrav 		dname_remove_label(&nm, &nm_len);
514b7579f77SDag-Erling Smørgrav 	}
515b7579f77SDag-Erling Smørgrav 	return first;
516b7579f77SDag-Erling Smørgrav }
517b7579f77SDag-Erling Smørgrav 
val_neg_zone_take_inuse(struct val_neg_zone * zone)518b7579f77SDag-Erling Smørgrav void val_neg_zone_take_inuse(struct val_neg_zone* zone)
519b7579f77SDag-Erling Smørgrav {
520b7579f77SDag-Erling Smørgrav 	if(!zone->in_use) {
521b7579f77SDag-Erling Smørgrav 		struct val_neg_zone* p;
522b7579f77SDag-Erling Smørgrav 		zone->in_use = 1;
523b7579f77SDag-Erling Smørgrav 		/* increase usage count of all parents */
524b7579f77SDag-Erling Smørgrav 		for(p=zone; p; p = p->parent) {
525b7579f77SDag-Erling Smørgrav 			p->count++;
526b7579f77SDag-Erling Smørgrav 		}
527b7579f77SDag-Erling Smørgrav 	}
528b7579f77SDag-Erling Smørgrav }
529b7579f77SDag-Erling Smørgrav 
neg_create_zone(struct val_neg_cache * neg,uint8_t * nm,size_t nm_len,uint16_t dclass)530b7579f77SDag-Erling Smørgrav struct val_neg_zone* neg_create_zone(struct val_neg_cache* neg,
531b7579f77SDag-Erling Smørgrav 	uint8_t* nm, size_t nm_len, uint16_t dclass)
532b7579f77SDag-Erling Smørgrav {
533b7579f77SDag-Erling Smørgrav 	struct val_neg_zone* zone;
534b7579f77SDag-Erling Smørgrav 	struct val_neg_zone* parent;
535b7579f77SDag-Erling Smørgrav 	struct val_neg_zone* p, *np;
536b7579f77SDag-Erling Smørgrav 	int labs = dname_count_labels(nm);
537b7579f77SDag-Erling Smørgrav 
538b7579f77SDag-Erling Smørgrav 	/* find closest enclosing parent zone that (still) exists */
539b7579f77SDag-Erling Smørgrav 	parent = neg_closest_zone_parent(neg, nm, nm_len, labs, dclass);
540b7579f77SDag-Erling Smørgrav 	if(parent && query_dname_compare(parent->name, nm) == 0)
541b7579f77SDag-Erling Smørgrav 		return parent; /* already exists, weird */
542b7579f77SDag-Erling Smørgrav 	/* if parent exists, it is in use */
543b7579f77SDag-Erling Smørgrav 	log_assert(!parent || parent->count > 0);
544b7579f77SDag-Erling Smørgrav 	zone = neg_zone_chain(nm, nm_len, labs, dclass, parent);
545b7579f77SDag-Erling Smørgrav 	if(!zone) {
546b7579f77SDag-Erling Smørgrav 		return NULL;
547b7579f77SDag-Erling Smørgrav 	}
548b7579f77SDag-Erling Smørgrav 
549b7579f77SDag-Erling Smørgrav 	/* insert the list of zones into the tree */
550b7579f77SDag-Erling Smørgrav 	p = zone;
551b7579f77SDag-Erling Smørgrav 	while(p) {
552b7579f77SDag-Erling Smørgrav 		np = p->parent;
553b7579f77SDag-Erling Smørgrav 		/* mem use */
554b7579f77SDag-Erling Smørgrav 		neg->use += sizeof(struct val_neg_zone) + p->len;
555b7579f77SDag-Erling Smørgrav 		/* insert in tree */
556b7579f77SDag-Erling Smørgrav 		(void)rbtree_insert(&neg->tree, &p->node);
557b7579f77SDag-Erling Smørgrav 		/* last one needs proper parent pointer */
558b7579f77SDag-Erling Smørgrav 		if(np == NULL)
559b7579f77SDag-Erling Smørgrav 			p->parent = parent;
560b7579f77SDag-Erling Smørgrav 		p = np;
561b7579f77SDag-Erling Smørgrav 	}
562b7579f77SDag-Erling Smørgrav 	return zone;
563b7579f77SDag-Erling Smørgrav }
564b7579f77SDag-Erling Smørgrav 
565b7579f77SDag-Erling Smørgrav /** find zone name of message, returns the SOA record */
reply_find_soa(struct reply_info * rep)566b7579f77SDag-Erling Smørgrav static struct ub_packed_rrset_key* reply_find_soa(struct reply_info* rep)
567b7579f77SDag-Erling Smørgrav {
568b7579f77SDag-Erling Smørgrav 	size_t i;
569b7579f77SDag-Erling Smørgrav 	for(i=rep->an_numrrsets; i< rep->an_numrrsets+rep->ns_numrrsets; i++){
570b7579f77SDag-Erling Smørgrav 		if(ntohs(rep->rrsets[i]->rk.type) == LDNS_RR_TYPE_SOA)
571b7579f77SDag-Erling Smørgrav 			return rep->rrsets[i];
572b7579f77SDag-Erling Smørgrav 	}
573b7579f77SDag-Erling Smørgrav 	return NULL;
574b7579f77SDag-Erling Smørgrav }
575b7579f77SDag-Erling Smørgrav 
576b7579f77SDag-Erling Smørgrav /** see if the reply has NSEC records worthy of caching */
reply_has_nsec(struct reply_info * rep)577b7579f77SDag-Erling Smørgrav static int reply_has_nsec(struct reply_info* rep)
578b7579f77SDag-Erling Smørgrav {
579b7579f77SDag-Erling Smørgrav 	size_t i;
580b7579f77SDag-Erling Smørgrav 	struct packed_rrset_data* d;
581b7579f77SDag-Erling Smørgrav 	if(rep->security != sec_status_secure)
582b7579f77SDag-Erling Smørgrav 		return 0;
583b7579f77SDag-Erling Smørgrav 	for(i=rep->an_numrrsets; i< rep->an_numrrsets+rep->ns_numrrsets; i++){
584b7579f77SDag-Erling Smørgrav 		if(ntohs(rep->rrsets[i]->rk.type) == LDNS_RR_TYPE_NSEC) {
585b7579f77SDag-Erling Smørgrav 			d = (struct packed_rrset_data*)rep->rrsets[i]->
586b7579f77SDag-Erling Smørgrav 				entry.data;
587b7579f77SDag-Erling Smørgrav 			if(d->security == sec_status_secure)
588b7579f77SDag-Erling Smørgrav 				return 1;
589b7579f77SDag-Erling Smørgrav 		}
590b7579f77SDag-Erling Smørgrav 	}
591b7579f77SDag-Erling Smørgrav 	return 0;
592b7579f77SDag-Erling Smørgrav }
593b7579f77SDag-Erling Smørgrav 
594b7579f77SDag-Erling Smørgrav 
595b7579f77SDag-Erling Smørgrav /**
596b7579f77SDag-Erling Smørgrav  * Create single node of data element.
597b7579f77SDag-Erling Smørgrav  * @param nm: name (copied)
598b7579f77SDag-Erling Smørgrav  * @param nm_len: length of name
599b7579f77SDag-Erling Smørgrav  * @param labs: labels in name.
600b7579f77SDag-Erling Smørgrav  * @return element with name nm, or NULL malloc failure.
601b7579f77SDag-Erling Smørgrav  */
neg_setup_data_node(uint8_t * nm,size_t nm_len,int labs)602b7579f77SDag-Erling Smørgrav static struct val_neg_data* neg_setup_data_node(
603b7579f77SDag-Erling Smørgrav 	uint8_t* nm, size_t nm_len, int labs)
604b7579f77SDag-Erling Smørgrav {
605b7579f77SDag-Erling Smørgrav 	struct val_neg_data* el;
606b7579f77SDag-Erling Smørgrav 	el = (struct val_neg_data*)calloc(1, sizeof(*el));
607b7579f77SDag-Erling Smørgrav 	if(!el) {
608b7579f77SDag-Erling Smørgrav 		return NULL;
609b7579f77SDag-Erling Smørgrav 	}
610b7579f77SDag-Erling Smørgrav 	el->node.key = el;
611b7579f77SDag-Erling Smørgrav 	el->name = memdup(nm, nm_len);
612b7579f77SDag-Erling Smørgrav 	if(!el->name) {
613b7579f77SDag-Erling Smørgrav 		free(el);
614b7579f77SDag-Erling Smørgrav 		return NULL;
615b7579f77SDag-Erling Smørgrav 	}
616b7579f77SDag-Erling Smørgrav 	el->len = nm_len;
617b7579f77SDag-Erling Smørgrav 	el->labs = labs;
618b7579f77SDag-Erling Smørgrav 	return el;
619b7579f77SDag-Erling Smørgrav }
620b7579f77SDag-Erling Smørgrav 
621b7579f77SDag-Erling Smørgrav /**
622b7579f77SDag-Erling Smørgrav  * Create chain of data element and parents
623b7579f77SDag-Erling Smørgrav  * @param nm: name
624b7579f77SDag-Erling Smørgrav  * @param nm_len: length of name
625b7579f77SDag-Erling Smørgrav  * @param labs: labels in name.
626b7579f77SDag-Erling Smørgrav  * @param parent: up to where to make, if NULL up to root label.
627b7579f77SDag-Erling Smørgrav  * @return lowest element with name nm, or NULL malloc failure.
628b7579f77SDag-Erling Smørgrav  */
neg_data_chain(uint8_t * nm,size_t nm_len,int labs,struct val_neg_data * parent)629b7579f77SDag-Erling Smørgrav static struct val_neg_data* neg_data_chain(
630b7579f77SDag-Erling Smørgrav 	uint8_t* nm, size_t nm_len, int labs, struct val_neg_data* parent)
631b7579f77SDag-Erling Smørgrav {
632b7579f77SDag-Erling Smørgrav 	int i;
633b7579f77SDag-Erling Smørgrav 	int tolabs = parent?parent->labs:0;
634b7579f77SDag-Erling Smørgrav 	struct val_neg_data* el, *first = NULL, *prev = NULL;
635b7579f77SDag-Erling Smørgrav 
636b7579f77SDag-Erling Smørgrav 	/* create the new subtree, i is labelcount of current creation */
637b7579f77SDag-Erling Smørgrav 	/* this creates a 'first' to z->parent=NULL list of zones */
638b7579f77SDag-Erling Smørgrav 	for(i=labs; i!=tolabs; i--) {
639b7579f77SDag-Erling Smørgrav 		/* create new item */
640b7579f77SDag-Erling Smørgrav 		el = neg_setup_data_node(nm, nm_len, i);
641b7579f77SDag-Erling Smørgrav 		if(!el) {
642b7579f77SDag-Erling Smørgrav 			/* need to delete other allocations in this routine!*/
643b7579f77SDag-Erling Smørgrav 			struct val_neg_data* p = first, *np;
644b7579f77SDag-Erling Smørgrav 			while(p) {
645b7579f77SDag-Erling Smørgrav 				np = p->parent;
646b7579f77SDag-Erling Smørgrav 				free(p->name);
64717d15b25SDag-Erling Smørgrav 				free(p);
648b7579f77SDag-Erling Smørgrav 				p = np;
649b7579f77SDag-Erling Smørgrav 			}
650b7579f77SDag-Erling Smørgrav 			return NULL;
651b7579f77SDag-Erling Smørgrav 		}
652b7579f77SDag-Erling Smørgrav 		if(i == labs) {
653b7579f77SDag-Erling Smørgrav 			first = el;
654b7579f77SDag-Erling Smørgrav 		} else {
655b7579f77SDag-Erling Smørgrav 			prev->parent = el;
656b7579f77SDag-Erling Smørgrav 		}
657b7579f77SDag-Erling Smørgrav 
658b7579f77SDag-Erling Smørgrav 		/* prepare for next name */
659b7579f77SDag-Erling Smørgrav 		prev = el;
660b7579f77SDag-Erling Smørgrav 		dname_remove_label(&nm, &nm_len);
661b7579f77SDag-Erling Smørgrav 	}
662b7579f77SDag-Erling Smørgrav 	return first;
663b7579f77SDag-Erling Smørgrav }
664b7579f77SDag-Erling Smørgrav 
665b7579f77SDag-Erling Smørgrav /**
666b7579f77SDag-Erling Smørgrav  * Remove NSEC records between start and end points.
667b7579f77SDag-Erling Smørgrav  * By walking the tree, the tree is sorted canonically.
668b7579f77SDag-Erling Smørgrav  * @param neg: negative cache.
669b7579f77SDag-Erling Smørgrav  * @param zone: the zone
670b7579f77SDag-Erling Smørgrav  * @param el: element to start walking at.
671b7579f77SDag-Erling Smørgrav  * @param nsec: the nsec record with the end point
672b7579f77SDag-Erling Smørgrav  */
wipeout(struct val_neg_cache * neg,struct val_neg_zone * zone,struct val_neg_data * el,struct ub_packed_rrset_key * nsec)673b7579f77SDag-Erling Smørgrav static void wipeout(struct val_neg_cache* neg, struct val_neg_zone* zone,
674b7579f77SDag-Erling Smørgrav 	struct val_neg_data* el, struct ub_packed_rrset_key* nsec)
675b7579f77SDag-Erling Smørgrav {
676b7579f77SDag-Erling Smørgrav 	struct packed_rrset_data* d = (struct packed_rrset_data*)nsec->
677b7579f77SDag-Erling Smørgrav 		entry.data;
678b7579f77SDag-Erling Smørgrav 	uint8_t* end;
679b7579f77SDag-Erling Smørgrav 	size_t end_len;
680b7579f77SDag-Erling Smørgrav 	int end_labs, m;
6813005e0a3SDag-Erling Smørgrav 	rbnode_type* walk, *next;
682b7579f77SDag-Erling Smørgrav 	struct val_neg_data* cur;
683b7579f77SDag-Erling Smørgrav 	uint8_t buf[257];
684b7579f77SDag-Erling Smørgrav 	/* get endpoint */
685b7579f77SDag-Erling Smørgrav 	if(!d || d->count == 0 || d->rr_len[0] < 2+1)
686b7579f77SDag-Erling Smørgrav 		return;
687b7579f77SDag-Erling Smørgrav 	if(ntohs(nsec->rk.type) == LDNS_RR_TYPE_NSEC) {
688b7579f77SDag-Erling Smørgrav 		end = d->rr_data[0]+2;
689b7579f77SDag-Erling Smørgrav 		end_len = dname_valid(end, d->rr_len[0]-2);
690b7579f77SDag-Erling Smørgrav 		end_labs = dname_count_labels(end);
691b7579f77SDag-Erling Smørgrav 	} else {
692b7579f77SDag-Erling Smørgrav 		/* NSEC3 */
693b7579f77SDag-Erling Smørgrav 		if(!nsec3_get_nextowner_b32(nsec, 0, buf, sizeof(buf)))
694b7579f77SDag-Erling Smørgrav 			return;
695b7579f77SDag-Erling Smørgrav 		end = buf;
696b7579f77SDag-Erling Smørgrav 		end_labs = dname_count_size_labels(end, &end_len);
697b7579f77SDag-Erling Smørgrav 	}
698b7579f77SDag-Erling Smørgrav 
699b7579f77SDag-Erling Smørgrav 	/* sanity check, both owner and end must be below the zone apex */
700b7579f77SDag-Erling Smørgrav 	if(!dname_subdomain_c(el->name, zone->name) ||
701b7579f77SDag-Erling Smørgrav 		!dname_subdomain_c(end, zone->name))
702b7579f77SDag-Erling Smørgrav 		return;
703b7579f77SDag-Erling Smørgrav 
704b7579f77SDag-Erling Smørgrav 	/* detect end of zone NSEC ; wipe until the end of zone */
705b7579f77SDag-Erling Smørgrav 	if(query_dname_compare(end, zone->name) == 0) {
706b7579f77SDag-Erling Smørgrav 		end = NULL;
707b7579f77SDag-Erling Smørgrav 	}
708b7579f77SDag-Erling Smørgrav 
709b7579f77SDag-Erling Smørgrav 	walk = rbtree_next(&el->node);
710b7579f77SDag-Erling Smørgrav 	while(walk && walk != RBTREE_NULL) {
711b7579f77SDag-Erling Smørgrav 		cur = (struct val_neg_data*)walk;
712b7579f77SDag-Erling Smørgrav 		/* sanity check: must be larger than start */
713b7579f77SDag-Erling Smørgrav 		if(dname_canon_lab_cmp(cur->name, cur->labs,
714b7579f77SDag-Erling Smørgrav 			el->name, el->labs, &m) <= 0) {
715b7579f77SDag-Erling Smørgrav 			/* r == 0 skip original record. */
716b7579f77SDag-Erling Smørgrav 			/* r < 0  too small! */
717b7579f77SDag-Erling Smørgrav 			walk = rbtree_next(walk);
718b7579f77SDag-Erling Smørgrav 			continue;
719b7579f77SDag-Erling Smørgrav 		}
720b7579f77SDag-Erling Smørgrav 		/* stop at endpoint, also data at empty nonterminals must be
721b7579f77SDag-Erling Smørgrav 		 * removed (no NSECs there) so everything between
722b7579f77SDag-Erling Smørgrav 		 * start and end */
723b7579f77SDag-Erling Smørgrav 		if(end && dname_canon_lab_cmp(cur->name, cur->labs,
724b7579f77SDag-Erling Smørgrav 			end, end_labs, &m) >= 0) {
725b7579f77SDag-Erling Smørgrav 			break;
726b7579f77SDag-Erling Smørgrav 		}
727b7579f77SDag-Erling Smørgrav 		/* this element has to be deleted, but we cannot do it
728b7579f77SDag-Erling Smørgrav 		 * now, because we are walking the tree still ... */
729b7579f77SDag-Erling Smørgrav 		/* get the next element: */
730b7579f77SDag-Erling Smørgrav 		next = rbtree_next(walk);
731b7579f77SDag-Erling Smørgrav 		/* now delete the original element, this may trigger
732b7579f77SDag-Erling Smørgrav 		 * rbtree rebalances, but really, the next element is
733b7579f77SDag-Erling Smørgrav 		 * the one we need.
734b7579f77SDag-Erling Smørgrav 		 * But it may trigger delete of other data and the
735b7579f77SDag-Erling Smørgrav 		 * entire zone. However, if that happens, this is done
736b7579f77SDag-Erling Smørgrav 		 * by deleting the *parents* of the element for deletion,
737b7579f77SDag-Erling Smørgrav 		 * and maybe also the entire zone if it is empty.
738b7579f77SDag-Erling Smørgrav 		 * But parents are smaller in canonical compare, thus,
739b7579f77SDag-Erling Smørgrav 		 * if a larger element exists, then it is not a parent,
740b7579f77SDag-Erling Smørgrav 		 * it cannot get deleted, the zone cannot get empty.
741b7579f77SDag-Erling Smørgrav 		 * If the next==NULL, then zone can be empty. */
742b7579f77SDag-Erling Smørgrav 		if(cur->in_use)
743b7579f77SDag-Erling Smørgrav 			neg_delete_data(neg, cur);
744b7579f77SDag-Erling Smørgrav 		walk = next;
745b7579f77SDag-Erling Smørgrav 	}
746b7579f77SDag-Erling Smørgrav }
747b7579f77SDag-Erling Smørgrav 
neg_insert_data(struct val_neg_cache * neg,struct val_neg_zone * zone,struct ub_packed_rrset_key * nsec)748b7579f77SDag-Erling Smørgrav void neg_insert_data(struct val_neg_cache* neg,
749b7579f77SDag-Erling Smørgrav 	struct val_neg_zone* zone, struct ub_packed_rrset_key* nsec)
750b7579f77SDag-Erling Smørgrav {
751b7579f77SDag-Erling Smørgrav 	struct packed_rrset_data* d;
752b7579f77SDag-Erling Smørgrav 	struct val_neg_data* parent;
753b7579f77SDag-Erling Smørgrav 	struct val_neg_data* el;
754b7579f77SDag-Erling Smørgrav 	uint8_t* nm = nsec->rk.dname;
755b7579f77SDag-Erling Smørgrav 	size_t nm_len = nsec->rk.dname_len;
756b7579f77SDag-Erling Smørgrav 	int labs = dname_count_labels(nsec->rk.dname);
757b7579f77SDag-Erling Smørgrav 
758b7579f77SDag-Erling Smørgrav 	d = (struct packed_rrset_data*)nsec->entry.data;
759b7579f77SDag-Erling Smørgrav 	if( !(d->security == sec_status_secure ||
760b7579f77SDag-Erling Smørgrav 		(d->security == sec_status_unchecked && d->rrsig_count > 0)))
761b7579f77SDag-Erling Smørgrav 		return;
762b7579f77SDag-Erling Smørgrav 	log_nametypeclass(VERB_ALGO, "negcache rr",
763b7579f77SDag-Erling Smørgrav 		nsec->rk.dname, ntohs(nsec->rk.type),
764b7579f77SDag-Erling Smørgrav 		ntohs(nsec->rk.rrset_class));
765b7579f77SDag-Erling Smørgrav 
766b7579f77SDag-Erling Smørgrav 	/* find closest enclosing parent data that (still) exists */
767b7579f77SDag-Erling Smørgrav 	parent = neg_closest_data_parent(zone, nm, nm_len, labs);
768b7579f77SDag-Erling Smørgrav 	if(parent && query_dname_compare(parent->name, nm) == 0) {
769b7579f77SDag-Erling Smørgrav 		/* perfect match already exists */
770b7579f77SDag-Erling Smørgrav 		log_assert(parent->count > 0);
771b7579f77SDag-Erling Smørgrav 		el = parent;
772b7579f77SDag-Erling Smørgrav 	} else {
773b7579f77SDag-Erling Smørgrav 		struct val_neg_data* p, *np;
774b7579f77SDag-Erling Smørgrav 
775b7579f77SDag-Erling Smørgrav 		/* create subtree for perfect match */
776b7579f77SDag-Erling Smørgrav 		/* if parent exists, it is in use */
777b7579f77SDag-Erling Smørgrav 		log_assert(!parent || parent->count > 0);
778b7579f77SDag-Erling Smørgrav 
779b7579f77SDag-Erling Smørgrav 		el = neg_data_chain(nm, nm_len, labs, parent);
780b7579f77SDag-Erling Smørgrav 		if(!el) {
781b7579f77SDag-Erling Smørgrav 			log_err("out of memory inserting NSEC negative cache");
782b7579f77SDag-Erling Smørgrav 			return;
783b7579f77SDag-Erling Smørgrav 		}
784b7579f77SDag-Erling Smørgrav 		el->in_use = 0; /* set on below */
785b7579f77SDag-Erling Smørgrav 
786b7579f77SDag-Erling Smørgrav 		/* insert the list of zones into the tree */
787b7579f77SDag-Erling Smørgrav 		p = el;
788b7579f77SDag-Erling Smørgrav 		while(p) {
789b7579f77SDag-Erling Smørgrav 			np = p->parent;
790b7579f77SDag-Erling Smørgrav 			/* mem use */
791b7579f77SDag-Erling Smørgrav 			neg->use += sizeof(struct val_neg_data) + p->len;
792b7579f77SDag-Erling Smørgrav 			/* insert in tree */
793b7579f77SDag-Erling Smørgrav 			p->zone = zone;
794b7579f77SDag-Erling Smørgrav 			(void)rbtree_insert(&zone->tree, &p->node);
795b7579f77SDag-Erling Smørgrav 			/* last one needs proper parent pointer */
796b7579f77SDag-Erling Smørgrav 			if(np == NULL)
797b7579f77SDag-Erling Smørgrav 				p->parent = parent;
798b7579f77SDag-Erling Smørgrav 			p = np;
799b7579f77SDag-Erling Smørgrav 		}
800b7579f77SDag-Erling Smørgrav 	}
801b7579f77SDag-Erling Smørgrav 
802b7579f77SDag-Erling Smørgrav 	if(!el->in_use) {
803b7579f77SDag-Erling Smørgrav 		struct val_neg_data* p;
804b7579f77SDag-Erling Smørgrav 
805b7579f77SDag-Erling Smørgrav 		el->in_use = 1;
806b7579f77SDag-Erling Smørgrav 		/* increase usage count of all parents */
807b7579f77SDag-Erling Smørgrav 		for(p=el; p; p = p->parent) {
808b7579f77SDag-Erling Smørgrav 			p->count++;
809b7579f77SDag-Erling Smørgrav 		}
810b7579f77SDag-Erling Smørgrav 
811b7579f77SDag-Erling Smørgrav 		neg_lru_front(neg, el);
812b7579f77SDag-Erling Smørgrav 	} else {
813b7579f77SDag-Erling Smørgrav 		/* in use, bring to front, lru */
814b7579f77SDag-Erling Smørgrav 		neg_lru_touch(neg, el);
815b7579f77SDag-Erling Smørgrav 	}
816b7579f77SDag-Erling Smørgrav 
817b7579f77SDag-Erling Smørgrav 	/* if nsec3 store last used parameters */
818b7579f77SDag-Erling Smørgrav 	if(ntohs(nsec->rk.type) == LDNS_RR_TYPE_NSEC3) {
819b7579f77SDag-Erling Smørgrav 		int h;
820b7579f77SDag-Erling Smørgrav 		uint8_t* s;
821b7579f77SDag-Erling Smørgrav 		size_t slen, it;
822b7579f77SDag-Erling Smørgrav 		if(nsec3_get_params(nsec, 0, &h, &it, &s, &slen) &&
823b7579f77SDag-Erling Smørgrav 			it <= neg->nsec3_max_iter &&
824b7579f77SDag-Erling Smørgrav 			(h != zone->nsec3_hash || it != zone->nsec3_iter ||
825b7579f77SDag-Erling Smørgrav 			slen != zone->nsec3_saltlen ||
826b7579f77SDag-Erling Smørgrav 			memcmp(zone->nsec3_salt, s, slen) != 0)) {
827f61ef7f6SDag-Erling Smørgrav 
828f61ef7f6SDag-Erling Smørgrav 			if(slen > 0) {
829b7579f77SDag-Erling Smørgrav 				uint8_t* sa = memdup(s, slen);
830b7579f77SDag-Erling Smørgrav 				if(sa) {
831b7579f77SDag-Erling Smørgrav 					free(zone->nsec3_salt);
832b7579f77SDag-Erling Smørgrav 					zone->nsec3_salt = sa;
833b7579f77SDag-Erling Smørgrav 					zone->nsec3_saltlen = slen;
834b7579f77SDag-Erling Smørgrav 					zone->nsec3_iter = it;
835f61ef7f6SDag-Erling Smørgrav 					zone->nsec3_hash = h;
836f61ef7f6SDag-Erling Smørgrav 				}
837f61ef7f6SDag-Erling Smørgrav 			} else {
838f61ef7f6SDag-Erling Smørgrav 				free(zone->nsec3_salt);
839f61ef7f6SDag-Erling Smørgrav 				zone->nsec3_salt = NULL;
840f61ef7f6SDag-Erling Smørgrav 				zone->nsec3_saltlen = 0;
841f61ef7f6SDag-Erling Smørgrav 				zone->nsec3_iter = it;
842f61ef7f6SDag-Erling Smørgrav 				zone->nsec3_hash = h;
843b7579f77SDag-Erling Smørgrav 			}
844b7579f77SDag-Erling Smørgrav 		}
845b7579f77SDag-Erling Smørgrav 	}
846b7579f77SDag-Erling Smørgrav 
847b7579f77SDag-Erling Smørgrav 	/* wipe out the cache items between NSEC start and end */
848b7579f77SDag-Erling Smørgrav 	wipeout(neg, zone, el, nsec);
849b7579f77SDag-Erling Smørgrav }
850b7579f77SDag-Erling Smørgrav 
85157bddd21SDag-Erling Smørgrav /** see if the reply has signed NSEC records and return the signer */
reply_nsec_signer(struct reply_info * rep,size_t * signer_len,uint16_t * dclass)85257bddd21SDag-Erling Smørgrav static uint8_t* reply_nsec_signer(struct reply_info* rep, size_t* signer_len,
85357bddd21SDag-Erling Smørgrav 	uint16_t* dclass)
85457bddd21SDag-Erling Smørgrav {
85557bddd21SDag-Erling Smørgrav 	size_t i;
85657bddd21SDag-Erling Smørgrav 	struct packed_rrset_data* d;
85757bddd21SDag-Erling Smørgrav 	uint8_t* s;
85857bddd21SDag-Erling Smørgrav 	for(i=rep->an_numrrsets; i< rep->an_numrrsets+rep->ns_numrrsets; i++){
85957bddd21SDag-Erling Smørgrav 		if(ntohs(rep->rrsets[i]->rk.type) == LDNS_RR_TYPE_NSEC ||
86057bddd21SDag-Erling Smørgrav 			ntohs(rep->rrsets[i]->rk.type) == LDNS_RR_TYPE_NSEC3) {
86157bddd21SDag-Erling Smørgrav 			d = (struct packed_rrset_data*)rep->rrsets[i]->
86257bddd21SDag-Erling Smørgrav 				entry.data;
86357bddd21SDag-Erling Smørgrav 			/* return first signer name of first NSEC */
86457bddd21SDag-Erling Smørgrav 			if(d->rrsig_count != 0) {
86557bddd21SDag-Erling Smørgrav 				val_find_rrset_signer(rep->rrsets[i],
86657bddd21SDag-Erling Smørgrav 					&s, signer_len);
86757bddd21SDag-Erling Smørgrav 				if(s && *signer_len) {
86857bddd21SDag-Erling Smørgrav 					*dclass = ntohs(rep->rrsets[i]->
86957bddd21SDag-Erling Smørgrav 						rk.rrset_class);
87057bddd21SDag-Erling Smørgrav 					return s;
87157bddd21SDag-Erling Smørgrav 				}
87257bddd21SDag-Erling Smørgrav 			}
87357bddd21SDag-Erling Smørgrav 		}
87457bddd21SDag-Erling Smørgrav 	}
87557bddd21SDag-Erling Smørgrav 	return 0;
87657bddd21SDag-Erling Smørgrav }
87757bddd21SDag-Erling Smørgrav 
val_neg_addreply(struct val_neg_cache * neg,struct reply_info * rep)878b7579f77SDag-Erling Smørgrav void val_neg_addreply(struct val_neg_cache* neg, struct reply_info* rep)
879b7579f77SDag-Erling Smørgrav {
880b7579f77SDag-Erling Smørgrav 	size_t i, need;
881b7579f77SDag-Erling Smørgrav 	struct ub_packed_rrset_key* soa;
88257bddd21SDag-Erling Smørgrav 	uint8_t* dname = NULL;
88357bddd21SDag-Erling Smørgrav 	size_t dname_len;
88457bddd21SDag-Erling Smørgrav 	uint16_t rrset_class;
885b7579f77SDag-Erling Smørgrav 	struct val_neg_zone* zone;
886b7579f77SDag-Erling Smørgrav 	/* see if secure nsecs inside */
887b7579f77SDag-Erling Smørgrav 	if(!reply_has_nsec(rep))
888b7579f77SDag-Erling Smørgrav 		return;
889b7579f77SDag-Erling Smørgrav 	/* find the zone name in message */
89057bddd21SDag-Erling Smørgrav 	if((soa = reply_find_soa(rep))) {
89157bddd21SDag-Erling Smørgrav 		dname = soa->rk.dname;
89257bddd21SDag-Erling Smørgrav 		dname_len = soa->rk.dname_len;
89357bddd21SDag-Erling Smørgrav 		rrset_class = ntohs(soa->rk.rrset_class);
89457bddd21SDag-Erling Smørgrav 	}
89557bddd21SDag-Erling Smørgrav 	else {
89657bddd21SDag-Erling Smørgrav 		/* No SOA in positive (wildcard) answer. Use signer from the
89757bddd21SDag-Erling Smørgrav 		 * validated answer RRsets' signature. */
89857bddd21SDag-Erling Smørgrav 		if(!(dname = reply_nsec_signer(rep, &dname_len, &rrset_class)))
899b7579f77SDag-Erling Smørgrav 			return;
90057bddd21SDag-Erling Smørgrav 	}
901b7579f77SDag-Erling Smørgrav 
902b7579f77SDag-Erling Smørgrav 	log_nametypeclass(VERB_ALGO, "negcache insert for zone",
90357bddd21SDag-Erling Smørgrav 		dname, LDNS_RR_TYPE_SOA, rrset_class);
904b7579f77SDag-Erling Smørgrav 
905b7579f77SDag-Erling Smørgrav 	/* ask for enough space to store all of it */
906b7579f77SDag-Erling Smørgrav 	need = calc_data_need(rep) +
90757bddd21SDag-Erling Smørgrav 		calc_zone_need(dname, dname_len);
908b7579f77SDag-Erling Smørgrav 	lock_basic_lock(&neg->lock);
909b7579f77SDag-Erling Smørgrav 	neg_make_space(neg, need);
910b7579f77SDag-Erling Smørgrav 
911b7579f77SDag-Erling Smørgrav 	/* find or create the zone entry */
91257bddd21SDag-Erling Smørgrav 	zone = neg_find_zone(neg, dname, dname_len, rrset_class);
913b7579f77SDag-Erling Smørgrav 	if(!zone) {
91457bddd21SDag-Erling Smørgrav 		if(!(zone = neg_create_zone(neg, dname, dname_len,
91557bddd21SDag-Erling Smørgrav 			rrset_class))) {
916b7579f77SDag-Erling Smørgrav 			lock_basic_unlock(&neg->lock);
917b7579f77SDag-Erling Smørgrav 			log_err("out of memory adding negative zone");
918b7579f77SDag-Erling Smørgrav 			return;
919b7579f77SDag-Erling Smørgrav 		}
920b7579f77SDag-Erling Smørgrav 	}
921b7579f77SDag-Erling Smørgrav 	val_neg_zone_take_inuse(zone);
922b7579f77SDag-Erling Smørgrav 
923b7579f77SDag-Erling Smørgrav 	/* insert the NSECs */
924b7579f77SDag-Erling Smørgrav 	for(i=rep->an_numrrsets; i< rep->an_numrrsets+rep->ns_numrrsets; i++){
925b7579f77SDag-Erling Smørgrav 		if(ntohs(rep->rrsets[i]->rk.type) != LDNS_RR_TYPE_NSEC)
926b7579f77SDag-Erling Smørgrav 			continue;
927b7579f77SDag-Erling Smørgrav 		if(!dname_subdomain_c(rep->rrsets[i]->rk.dname,
928b7579f77SDag-Erling Smørgrav 			zone->name)) continue;
929b7579f77SDag-Erling Smørgrav 		/* insert NSEC into this zone's tree */
930b7579f77SDag-Erling Smørgrav 		neg_insert_data(neg, zone, rep->rrsets[i]);
931b7579f77SDag-Erling Smørgrav 	}
932b7579f77SDag-Erling Smørgrav 	if(zone->tree.count == 0) {
933b7579f77SDag-Erling Smørgrav 		/* remove empty zone if inserts failed */
934b7579f77SDag-Erling Smørgrav 		neg_delete_zone(neg, zone);
935b7579f77SDag-Erling Smørgrav 	}
936b7579f77SDag-Erling Smørgrav 	lock_basic_unlock(&neg->lock);
937b7579f77SDag-Erling Smørgrav }
938b7579f77SDag-Erling Smørgrav 
939b7579f77SDag-Erling Smørgrav /**
940b7579f77SDag-Erling Smørgrav  * Lookup closest data record. For NSEC denial.
941b7579f77SDag-Erling Smørgrav  * @param zone: zone to look in
942b7579f77SDag-Erling Smørgrav  * @param qname: name to look for.
943b7579f77SDag-Erling Smørgrav  * @param len: length of name
944b7579f77SDag-Erling Smørgrav  * @param labs: labels in name
945b7579f77SDag-Erling Smørgrav  * @param data: data element, exact or smaller or NULL
946b7579f77SDag-Erling Smørgrav  * @return true if exact match.
947b7579f77SDag-Erling Smørgrav  */
neg_closest_data(struct val_neg_zone * zone,uint8_t * qname,size_t len,int labs,struct val_neg_data ** data)948b7579f77SDag-Erling Smørgrav static int neg_closest_data(struct val_neg_zone* zone,
949b7579f77SDag-Erling Smørgrav 	uint8_t* qname, size_t len, int labs, struct val_neg_data** data)
950b7579f77SDag-Erling Smørgrav {
951b7579f77SDag-Erling Smørgrav 	struct val_neg_data key;
9523005e0a3SDag-Erling Smørgrav 	rbnode_type* r;
953b7579f77SDag-Erling Smørgrav 	key.node.key = &key;
954b7579f77SDag-Erling Smørgrav 	key.name = qname;
955b7579f77SDag-Erling Smørgrav 	key.len = len;
956b7579f77SDag-Erling Smørgrav 	key.labs = labs;
957b7579f77SDag-Erling Smørgrav 	if(rbtree_find_less_equal(&zone->tree, &key, &r)) {
958b7579f77SDag-Erling Smørgrav 		/* exact match */
959b7579f77SDag-Erling Smørgrav 		*data = (struct val_neg_data*)r;
960b7579f77SDag-Erling Smørgrav 		return 1;
961b7579f77SDag-Erling Smørgrav 	} else {
962b7579f77SDag-Erling Smørgrav 		/* smaller match */
963b7579f77SDag-Erling Smørgrav 		*data = (struct val_neg_data*)r;
964b7579f77SDag-Erling Smørgrav 		return 0;
965b7579f77SDag-Erling Smørgrav 	}
966b7579f77SDag-Erling Smørgrav }
967b7579f77SDag-Erling Smørgrav 
val_neg_addreferral(struct val_neg_cache * neg,struct reply_info * rep,uint8_t * zone_name)968b7579f77SDag-Erling Smørgrav void val_neg_addreferral(struct val_neg_cache* neg, struct reply_info* rep,
969b7579f77SDag-Erling Smørgrav 	uint8_t* zone_name)
970b7579f77SDag-Erling Smørgrav {
971b7579f77SDag-Erling Smørgrav 	size_t i, need;
972b7579f77SDag-Erling Smørgrav 	uint8_t* signer;
973b7579f77SDag-Erling Smørgrav 	size_t signer_len;
974b7579f77SDag-Erling Smørgrav 	uint16_t dclass;
975b7579f77SDag-Erling Smørgrav 	struct val_neg_zone* zone;
976b7579f77SDag-Erling Smørgrav 	/* no SOA in this message, find RRSIG over NSEC's signer name.
977b7579f77SDag-Erling Smørgrav 	 * note the NSEC records are maybe not validated yet */
978b7579f77SDag-Erling Smørgrav 	signer = reply_nsec_signer(rep, &signer_len, &dclass);
979b7579f77SDag-Erling Smørgrav 	if(!signer)
980b7579f77SDag-Erling Smørgrav 		return;
981b7579f77SDag-Erling Smørgrav 	if(!dname_subdomain_c(signer, zone_name)) {
982b7579f77SDag-Erling Smørgrav 		/* the signer is not in the bailiwick, throw it out */
983b7579f77SDag-Erling Smørgrav 		return;
984b7579f77SDag-Erling Smørgrav 	}
985b7579f77SDag-Erling Smørgrav 
986b7579f77SDag-Erling Smørgrav 	log_nametypeclass(VERB_ALGO, "negcache insert referral ",
987b7579f77SDag-Erling Smørgrav 		signer, LDNS_RR_TYPE_NS, dclass);
988b7579f77SDag-Erling Smørgrav 
989b7579f77SDag-Erling Smørgrav 	/* ask for enough space to store all of it */
990b7579f77SDag-Erling Smørgrav 	need = calc_data_need(rep) + calc_zone_need(signer, signer_len);
991b7579f77SDag-Erling Smørgrav 	lock_basic_lock(&neg->lock);
992b7579f77SDag-Erling Smørgrav 	neg_make_space(neg, need);
993b7579f77SDag-Erling Smørgrav 
994b7579f77SDag-Erling Smørgrav 	/* find or create the zone entry */
995b7579f77SDag-Erling Smørgrav 	zone = neg_find_zone(neg, signer, signer_len, dclass);
996b7579f77SDag-Erling Smørgrav 	if(!zone) {
997b7579f77SDag-Erling Smørgrav 		if(!(zone = neg_create_zone(neg, signer, signer_len,
998b7579f77SDag-Erling Smørgrav 			dclass))) {
999b7579f77SDag-Erling Smørgrav 			lock_basic_unlock(&neg->lock);
1000b7579f77SDag-Erling Smørgrav 			log_err("out of memory adding negative zone");
1001b7579f77SDag-Erling Smørgrav 			return;
1002b7579f77SDag-Erling Smørgrav 		}
1003b7579f77SDag-Erling Smørgrav 	}
1004b7579f77SDag-Erling Smørgrav 	val_neg_zone_take_inuse(zone);
1005b7579f77SDag-Erling Smørgrav 
1006b7579f77SDag-Erling Smørgrav 	/* insert the NSECs */
1007b7579f77SDag-Erling Smørgrav 	for(i=rep->an_numrrsets; i< rep->an_numrrsets+rep->ns_numrrsets; i++){
1008b7579f77SDag-Erling Smørgrav 		if(ntohs(rep->rrsets[i]->rk.type) != LDNS_RR_TYPE_NSEC &&
1009b7579f77SDag-Erling Smørgrav 			ntohs(rep->rrsets[i]->rk.type) != LDNS_RR_TYPE_NSEC3)
1010b7579f77SDag-Erling Smørgrav 			continue;
1011b7579f77SDag-Erling Smørgrav 		if(!dname_subdomain_c(rep->rrsets[i]->rk.dname,
1012b7579f77SDag-Erling Smørgrav 			zone->name)) continue;
1013b7579f77SDag-Erling Smørgrav 		/* insert NSEC into this zone's tree */
1014b7579f77SDag-Erling Smørgrav 		neg_insert_data(neg, zone, rep->rrsets[i]);
1015b7579f77SDag-Erling Smørgrav 	}
1016b7579f77SDag-Erling Smørgrav 	if(zone->tree.count == 0) {
1017b7579f77SDag-Erling Smørgrav 		/* remove empty zone if inserts failed */
1018b7579f77SDag-Erling Smørgrav 		neg_delete_zone(neg, zone);
1019b7579f77SDag-Erling Smørgrav 	}
1020b7579f77SDag-Erling Smørgrav 	lock_basic_unlock(&neg->lock);
1021b7579f77SDag-Erling Smørgrav }
1022b7579f77SDag-Erling Smørgrav 
1023b7579f77SDag-Erling Smørgrav /**
1024b7579f77SDag-Erling Smørgrav  * Check that an NSEC3 rrset does not have a type set.
1025b7579f77SDag-Erling Smørgrav  * None of the nsec3s in a hash-collision are allowed to have the type.
1026b7579f77SDag-Erling Smørgrav  * (since we do not know which one is the nsec3 looked at, flags, ..., we
1027b7579f77SDag-Erling Smørgrav  * ignore the cached item and let it bypass negative caching).
1028b7579f77SDag-Erling Smørgrav  * @param k: the nsec3 rrset to check.
1029b7579f77SDag-Erling Smørgrav  * @param t: type to check
1030b7579f77SDag-Erling Smørgrav  * @return true if no RRs have the type.
1031b7579f77SDag-Erling Smørgrav  */
nsec3_no_type(struct ub_packed_rrset_key * k,uint16_t t)1032b7579f77SDag-Erling Smørgrav static int nsec3_no_type(struct ub_packed_rrset_key* k, uint16_t t)
1033b7579f77SDag-Erling Smørgrav {
1034b7579f77SDag-Erling Smørgrav 	int count = (int)((struct packed_rrset_data*)k->entry.data)->count;
1035b7579f77SDag-Erling Smørgrav 	int i;
1036b7579f77SDag-Erling Smørgrav 	for(i=0; i<count; i++)
1037b7579f77SDag-Erling Smørgrav 		if(nsec3_has_type(k, i, t))
1038b7579f77SDag-Erling Smørgrav 			return 0;
1039b7579f77SDag-Erling Smørgrav 	return 1;
1040b7579f77SDag-Erling Smørgrav }
1041b7579f77SDag-Erling Smørgrav 
1042b7579f77SDag-Erling Smørgrav /**
1043b7579f77SDag-Erling Smørgrav  * See if rrset exists in rrset cache.
1044b7579f77SDag-Erling Smørgrav  * If it does, the bit is checked, and if not expired, it is returned
1045b7579f77SDag-Erling Smørgrav  * allocated in region.
1046b7579f77SDag-Erling Smørgrav  * @param rrset_cache: rrset cache
1047b7579f77SDag-Erling Smørgrav  * @param qname: to lookup rrset name
1048b7579f77SDag-Erling Smørgrav  * @param qname_len: length of qname.
1049b7579f77SDag-Erling Smørgrav  * @param qtype: type of rrset to lookup, host order
1050b7579f77SDag-Erling Smørgrav  * @param qclass: class of rrset to lookup, host order
1051b7579f77SDag-Erling Smørgrav  * @param flags: flags for rrset to lookup
1052b7579f77SDag-Erling Smørgrav  * @param region: where to alloc result
1053b7579f77SDag-Erling Smørgrav  * @param checkbit: if true, a bit in the nsec typemap is checked for absence.
1054b7579f77SDag-Erling Smørgrav  * @param checktype: which bit to check
1055b7579f77SDag-Erling Smørgrav  * @param now: to check ttl against
1056b7579f77SDag-Erling Smørgrav  * @return rrset or NULL
1057b7579f77SDag-Erling Smørgrav  */
1058b7579f77SDag-Erling Smørgrav static struct ub_packed_rrset_key*
grab_nsec(struct rrset_cache * rrset_cache,uint8_t * qname,size_t qname_len,uint16_t qtype,uint16_t qclass,uint32_t flags,struct regional * region,int checkbit,uint16_t checktype,time_t now)1059b7579f77SDag-Erling Smørgrav grab_nsec(struct rrset_cache* rrset_cache, uint8_t* qname, size_t qname_len,
1060b7579f77SDag-Erling Smørgrav 	uint16_t qtype, uint16_t qclass, uint32_t flags,
1061b7579f77SDag-Erling Smørgrav 	struct regional* region, int checkbit, uint16_t checktype,
106217d15b25SDag-Erling Smørgrav 	time_t now)
1063b7579f77SDag-Erling Smørgrav {
1064b7579f77SDag-Erling Smørgrav 	struct ub_packed_rrset_key* r, *k = rrset_cache_lookup(rrset_cache,
1065b7579f77SDag-Erling Smørgrav 		qname, qname_len, qtype, qclass, flags, now, 0);
1066b7579f77SDag-Erling Smørgrav 	struct packed_rrset_data* d;
1067b7579f77SDag-Erling Smørgrav 	if(!k) return NULL;
1068b7579f77SDag-Erling Smørgrav 	d = (struct packed_rrset_data*)k->entry.data;
1069b7579f77SDag-Erling Smørgrav 	if(d->ttl < now) {
1070b7579f77SDag-Erling Smørgrav 		lock_rw_unlock(&k->entry.lock);
1071b7579f77SDag-Erling Smørgrav 		return NULL;
1072b7579f77SDag-Erling Smørgrav 	}
1073b7579f77SDag-Erling Smørgrav 	/* only secure or unchecked records that have signatures. */
1074b7579f77SDag-Erling Smørgrav 	if( ! ( d->security == sec_status_secure ||
1075b7579f77SDag-Erling Smørgrav 		(d->security == sec_status_unchecked &&
1076b7579f77SDag-Erling Smørgrav 		d->rrsig_count > 0) ) ) {
1077b7579f77SDag-Erling Smørgrav 		lock_rw_unlock(&k->entry.lock);
1078b7579f77SDag-Erling Smørgrav 		return NULL;
1079b7579f77SDag-Erling Smørgrav 	}
1080b7579f77SDag-Erling Smørgrav 	/* check if checktype is absent */
1081b7579f77SDag-Erling Smørgrav 	if(checkbit && (
1082b7579f77SDag-Erling Smørgrav 		(qtype == LDNS_RR_TYPE_NSEC && nsec_has_type(k, checktype)) ||
1083b7579f77SDag-Erling Smørgrav 		(qtype == LDNS_RR_TYPE_NSEC3 && !nsec3_no_type(k, checktype))
1084b7579f77SDag-Erling Smørgrav 		)) {
1085b7579f77SDag-Erling Smørgrav 		lock_rw_unlock(&k->entry.lock);
1086b7579f77SDag-Erling Smørgrav 		return NULL;
1087b7579f77SDag-Erling Smørgrav 	}
1088b7579f77SDag-Erling Smørgrav 	/* looks OK! copy to region and return it */
1089b7579f77SDag-Erling Smørgrav 	r = packed_rrset_copy_region(k, region, now);
1090b7579f77SDag-Erling Smørgrav 	/* if it failed, we return the NULL */
1091b7579f77SDag-Erling Smørgrav 	lock_rw_unlock(&k->entry.lock);
1092b7579f77SDag-Erling Smørgrav 	return r;
1093b7579f77SDag-Erling Smørgrav }
1094b7579f77SDag-Erling Smørgrav 
109557bddd21SDag-Erling Smørgrav /**
109657bddd21SDag-Erling Smørgrav  * Get best NSEC record for qname. Might be matching, covering or totally
109757bddd21SDag-Erling Smørgrav  * useless.
109857bddd21SDag-Erling Smørgrav  * @param neg_cache: neg cache
109957bddd21SDag-Erling Smørgrav  * @param qname: to lookup rrset name
110057bddd21SDag-Erling Smørgrav  * @param qname_len: length of qname.
110157bddd21SDag-Erling Smørgrav  * @param qclass: class of rrset to lookup, host order
110257bddd21SDag-Erling Smørgrav  * @param rrset_cache: rrset cache
110357bddd21SDag-Erling Smørgrav  * @param now: to check ttl against
110457bddd21SDag-Erling Smørgrav  * @param region: where to alloc result
110557bddd21SDag-Erling Smørgrav  * @return rrset or NULL
110657bddd21SDag-Erling Smørgrav  */
110757bddd21SDag-Erling Smørgrav static struct ub_packed_rrset_key*
neg_find_nsec(struct val_neg_cache * neg_cache,uint8_t * qname,size_t qname_len,uint16_t qclass,struct rrset_cache * rrset_cache,time_t now,struct regional * region)110857bddd21SDag-Erling Smørgrav neg_find_nsec(struct val_neg_cache* neg_cache, uint8_t* qname, size_t qname_len,
110957bddd21SDag-Erling Smørgrav 	uint16_t qclass, struct rrset_cache* rrset_cache, time_t now,
111057bddd21SDag-Erling Smørgrav 	struct regional* region)
111157bddd21SDag-Erling Smørgrav {
111257bddd21SDag-Erling Smørgrav 	int labs;
111357bddd21SDag-Erling Smørgrav 	uint32_t flags;
111457bddd21SDag-Erling Smørgrav 	struct val_neg_zone* zone;
111557bddd21SDag-Erling Smørgrav 	struct val_neg_data* data;
111657bddd21SDag-Erling Smørgrav 	struct ub_packed_rrset_key* nsec;
111757bddd21SDag-Erling Smørgrav 
111857bddd21SDag-Erling Smørgrav 	labs = dname_count_labels(qname);
111957bddd21SDag-Erling Smørgrav 	lock_basic_lock(&neg_cache->lock);
112057bddd21SDag-Erling Smørgrav 	zone = neg_closest_zone_parent(neg_cache, qname, qname_len, labs,
112157bddd21SDag-Erling Smørgrav 		qclass);
112257bddd21SDag-Erling Smørgrav 	while(zone && !zone->in_use)
112357bddd21SDag-Erling Smørgrav 		zone = zone->parent;
112457bddd21SDag-Erling Smørgrav 	if(!zone) {
112557bddd21SDag-Erling Smørgrav 		lock_basic_unlock(&neg_cache->lock);
112657bddd21SDag-Erling Smørgrav 		return NULL;
112757bddd21SDag-Erling Smørgrav 	}
112857bddd21SDag-Erling Smørgrav 
112957bddd21SDag-Erling Smørgrav 	/* NSEC only for now */
113057bddd21SDag-Erling Smørgrav 	if(zone->nsec3_hash) {
113157bddd21SDag-Erling Smørgrav 		lock_basic_unlock(&neg_cache->lock);
113257bddd21SDag-Erling Smørgrav 		return NULL;
113357bddd21SDag-Erling Smørgrav 	}
113457bddd21SDag-Erling Smørgrav 
113557bddd21SDag-Erling Smørgrav 	/* ignore return value, don't care if it is an exact or smaller match */
113657bddd21SDag-Erling Smørgrav 	(void)neg_closest_data(zone, qname, qname_len, labs, &data);
113757bddd21SDag-Erling Smørgrav 	if(!data) {
113857bddd21SDag-Erling Smørgrav 		lock_basic_unlock(&neg_cache->lock);
113957bddd21SDag-Erling Smørgrav 		return NULL;
114057bddd21SDag-Erling Smørgrav 	}
114157bddd21SDag-Erling Smørgrav 
114257bddd21SDag-Erling Smørgrav 	/* ENT nodes are not in use, try the previous node. If the previous node
114357bddd21SDag-Erling Smørgrav 	 * is not in use, we don't have an useful NSEC and give up. */
114457bddd21SDag-Erling Smørgrav 	if(!data->in_use) {
114557bddd21SDag-Erling Smørgrav 		data = (struct val_neg_data*)rbtree_previous((rbnode_type*)data);
114657bddd21SDag-Erling Smørgrav 		if((rbnode_type*)data == RBTREE_NULL || !data->in_use) {
114757bddd21SDag-Erling Smørgrav 			lock_basic_unlock(&neg_cache->lock);
114857bddd21SDag-Erling Smørgrav 			return NULL;
114957bddd21SDag-Erling Smørgrav 		}
115057bddd21SDag-Erling Smørgrav 	}
115157bddd21SDag-Erling Smørgrav 
115257bddd21SDag-Erling Smørgrav 	flags = 0;
115357bddd21SDag-Erling Smørgrav 	if(query_dname_compare(data->name, zone->name) == 0)
115457bddd21SDag-Erling Smørgrav 		flags = PACKED_RRSET_NSEC_AT_APEX;
115557bddd21SDag-Erling Smørgrav 
115657bddd21SDag-Erling Smørgrav 	nsec = grab_nsec(rrset_cache, data->name, data->len, LDNS_RR_TYPE_NSEC,
115757bddd21SDag-Erling Smørgrav 		zone->dclass, flags, region, 0, 0, now);
115857bddd21SDag-Erling Smørgrav 	lock_basic_unlock(&neg_cache->lock);
115957bddd21SDag-Erling Smørgrav 	return nsec;
116057bddd21SDag-Erling Smørgrav }
116157bddd21SDag-Erling Smørgrav 
1162b7579f77SDag-Erling Smørgrav /** find nsec3 closest encloser in neg cache */
1163b7579f77SDag-Erling Smørgrav static struct val_neg_data*
neg_find_nsec3_ce(struct val_neg_zone * zone,uint8_t * qname,size_t qname_len,int qlabs,sldns_buffer * buf,uint8_t * hashnc,size_t * nclen)1164b7579f77SDag-Erling Smørgrav neg_find_nsec3_ce(struct val_neg_zone* zone, uint8_t* qname, size_t qname_len,
116517d15b25SDag-Erling Smørgrav 		int qlabs, sldns_buffer* buf, uint8_t* hashnc, size_t* nclen)
1166b7579f77SDag-Erling Smørgrav {
1167b7579f77SDag-Erling Smørgrav 	struct val_neg_data* data;
11688ed2b524SDag-Erling Smørgrav 	uint8_t hashce[NSEC3_SHA_LEN];
1169b7579f77SDag-Erling Smørgrav 	uint8_t b32[257];
1170b7579f77SDag-Erling Smørgrav 	size_t celen, b32len;
1171b7579f77SDag-Erling Smørgrav 
1172b7579f77SDag-Erling Smørgrav 	*nclen = 0;
1173b7579f77SDag-Erling Smørgrav 	while(qlabs > 0) {
1174b7579f77SDag-Erling Smørgrav 		/* hash */
1175b7579f77SDag-Erling Smørgrav 		if(!(celen=nsec3_get_hashed(buf, qname, qname_len,
1176b7579f77SDag-Erling Smørgrav 			zone->nsec3_hash, zone->nsec3_iter, zone->nsec3_salt,
1177b7579f77SDag-Erling Smørgrav 			zone->nsec3_saltlen, hashce, sizeof(hashce))))
1178b7579f77SDag-Erling Smørgrav 			return NULL;
1179b7579f77SDag-Erling Smørgrav 		if(!(b32len=nsec3_hash_to_b32(hashce, celen, zone->name,
1180b7579f77SDag-Erling Smørgrav 			zone->len, b32, sizeof(b32))))
1181b7579f77SDag-Erling Smørgrav 			return NULL;
1182b7579f77SDag-Erling Smørgrav 
1183b7579f77SDag-Erling Smørgrav 		/* lookup (exact match only) */
1184b7579f77SDag-Erling Smørgrav 		data = neg_find_data(zone, b32, b32len, zone->labs+1);
1185b7579f77SDag-Erling Smørgrav 		if(data && data->in_use) {
1186b7579f77SDag-Erling Smørgrav 			/* found ce match! */
1187b7579f77SDag-Erling Smørgrav 			return data;
1188b7579f77SDag-Erling Smørgrav 		}
1189b7579f77SDag-Erling Smørgrav 
1190b7579f77SDag-Erling Smørgrav 		*nclen = celen;
1191b7579f77SDag-Erling Smørgrav 		memmove(hashnc, hashce, celen);
1192b7579f77SDag-Erling Smørgrav 		dname_remove_label(&qname, &qname_len);
1193b7579f77SDag-Erling Smørgrav 		qlabs --;
1194b7579f77SDag-Erling Smørgrav 	}
1195b7579f77SDag-Erling Smørgrav 	return NULL;
1196b7579f77SDag-Erling Smørgrav }
1197b7579f77SDag-Erling Smørgrav 
1198b7579f77SDag-Erling Smørgrav /** check nsec3 parameters on nsec3 rrset with current zone values */
1199b7579f77SDag-Erling Smørgrav static int
neg_params_ok(struct val_neg_zone * zone,struct ub_packed_rrset_key * rrset)1200b7579f77SDag-Erling Smørgrav neg_params_ok(struct val_neg_zone* zone, struct ub_packed_rrset_key* rrset)
1201b7579f77SDag-Erling Smørgrav {
1202b7579f77SDag-Erling Smørgrav 	int h;
1203b7579f77SDag-Erling Smørgrav 	uint8_t* s;
1204b7579f77SDag-Erling Smørgrav 	size_t slen, it;
1205b7579f77SDag-Erling Smørgrav 	if(!nsec3_get_params(rrset, 0, &h, &it, &s, &slen))
1206b7579f77SDag-Erling Smørgrav 		return 0;
1207b7579f77SDag-Erling Smørgrav 	return (h == zone->nsec3_hash && it == zone->nsec3_iter &&
1208b7579f77SDag-Erling Smørgrav 		slen == zone->nsec3_saltlen &&
1209b7579f77SDag-Erling Smørgrav 		memcmp(zone->nsec3_salt, s, slen) == 0);
1210b7579f77SDag-Erling Smørgrav }
1211b7579f77SDag-Erling Smørgrav 
1212b7579f77SDag-Erling Smørgrav /** get next closer for nsec3 proof */
1213b7579f77SDag-Erling Smørgrav static struct ub_packed_rrset_key*
neg_nsec3_getnc(struct val_neg_zone * zone,uint8_t * hashnc,size_t nclen,struct rrset_cache * rrset_cache,struct regional * region,time_t now,uint8_t * b32,size_t maxb32)1214b7579f77SDag-Erling Smørgrav neg_nsec3_getnc(struct val_neg_zone* zone, uint8_t* hashnc, size_t nclen,
1215b7579f77SDag-Erling Smørgrav 	struct rrset_cache* rrset_cache, struct regional* region,
121617d15b25SDag-Erling Smørgrav 	time_t now, uint8_t* b32, size_t maxb32)
1217b7579f77SDag-Erling Smørgrav {
1218b7579f77SDag-Erling Smørgrav 	struct ub_packed_rrset_key* nc_rrset;
1219b7579f77SDag-Erling Smørgrav 	struct val_neg_data* data;
1220b7579f77SDag-Erling Smørgrav 	size_t b32len;
1221b7579f77SDag-Erling Smørgrav 
1222b7579f77SDag-Erling Smørgrav 	if(!(b32len=nsec3_hash_to_b32(hashnc, nclen, zone->name,
1223b7579f77SDag-Erling Smørgrav 		zone->len, b32, maxb32)))
1224b7579f77SDag-Erling Smørgrav 		return NULL;
1225b7579f77SDag-Erling Smørgrav 	(void)neg_closest_data(zone, b32, b32len, zone->labs+1, &data);
1226b7579f77SDag-Erling Smørgrav 	if(!data && zone->tree.count != 0) {
1227b7579f77SDag-Erling Smørgrav 		/* could be before the first entry ; return the last
1228b7579f77SDag-Erling Smørgrav 		 * entry (possibly the rollover nsec3 at end) */
1229b7579f77SDag-Erling Smørgrav 		data = (struct val_neg_data*)rbtree_last(&zone->tree);
1230b7579f77SDag-Erling Smørgrav 	}
1231b7579f77SDag-Erling Smørgrav 	while(data && !data->in_use)
1232b7579f77SDag-Erling Smørgrav 		data = data->parent;
1233b7579f77SDag-Erling Smørgrav 	if(!data)
1234b7579f77SDag-Erling Smørgrav 		return NULL;
1235b7579f77SDag-Erling Smørgrav 	/* got a data element in tree, grab it */
1236b7579f77SDag-Erling Smørgrav 	nc_rrset = grab_nsec(rrset_cache, data->name, data->len,
1237b7579f77SDag-Erling Smørgrav 		LDNS_RR_TYPE_NSEC3, zone->dclass, 0, region, 0, 0, now);
1238b7579f77SDag-Erling Smørgrav 	if(!nc_rrset)
1239b7579f77SDag-Erling Smørgrav 		return NULL;
1240b7579f77SDag-Erling Smørgrav 	if(!neg_params_ok(zone, nc_rrset))
1241b7579f77SDag-Erling Smørgrav 		return NULL;
1242b7579f77SDag-Erling Smørgrav 	return nc_rrset;
1243b7579f77SDag-Erling Smørgrav }
1244b7579f77SDag-Erling Smørgrav 
1245b7579f77SDag-Erling Smørgrav /** neg cache nsec3 proof procedure*/
1246b7579f77SDag-Erling Smørgrav static struct dns_msg*
neg_nsec3_proof_ds(struct val_neg_zone * zone,uint8_t * qname,size_t qname_len,int qlabs,sldns_buffer * buf,struct rrset_cache * rrset_cache,struct regional * region,time_t now,uint8_t * topname)1247b7579f77SDag-Erling Smørgrav neg_nsec3_proof_ds(struct val_neg_zone* zone, uint8_t* qname, size_t qname_len,
124817d15b25SDag-Erling Smørgrav 		int qlabs, sldns_buffer* buf, struct rrset_cache* rrset_cache,
124917d15b25SDag-Erling Smørgrav 		struct regional* region, time_t now, uint8_t* topname)
1250b7579f77SDag-Erling Smørgrav {
1251b7579f77SDag-Erling Smørgrav 	struct dns_msg* msg;
1252b7579f77SDag-Erling Smørgrav 	struct val_neg_data* data;
12538ed2b524SDag-Erling Smørgrav 	uint8_t hashnc[NSEC3_SHA_LEN];
1254b7579f77SDag-Erling Smørgrav 	size_t nclen;
1255b7579f77SDag-Erling Smørgrav 	struct ub_packed_rrset_key* ce_rrset, *nc_rrset;
1256b7579f77SDag-Erling Smørgrav 	struct nsec3_cached_hash c;
1257b7579f77SDag-Erling Smørgrav 	uint8_t nc_b32[257];
1258b7579f77SDag-Erling Smørgrav 
1259b7579f77SDag-Erling Smørgrav 	/* for NSEC3 ; determine the closest encloser for which we
1260b7579f77SDag-Erling Smørgrav 	 * can find an exact match. Remember the hashed lower name,
1261b7579f77SDag-Erling Smørgrav 	 * since that is the one we need a closest match for.
1262b7579f77SDag-Erling Smørgrav 	 * If we find a match straight away, then it becomes NODATA.
1263b7579f77SDag-Erling Smørgrav 	 * Otherwise, NXDOMAIN or if OPTOUT, an insecure delegation.
1264b7579f77SDag-Erling Smørgrav 	 * Also check that parameters are the same on closest encloser
1265b7579f77SDag-Erling Smørgrav 	 * and on closest match.
1266b7579f77SDag-Erling Smørgrav 	 */
1267b7579f77SDag-Erling Smørgrav 	if(!zone->nsec3_hash)
1268b7579f77SDag-Erling Smørgrav 		return NULL; /* not nsec3 zone */
1269b7579f77SDag-Erling Smørgrav 
1270b7579f77SDag-Erling Smørgrav 	if(!(data=neg_find_nsec3_ce(zone, qname, qname_len, qlabs, buf,
1271b7579f77SDag-Erling Smørgrav 		hashnc, &nclen))) {
1272b7579f77SDag-Erling Smørgrav 		return NULL;
1273b7579f77SDag-Erling Smørgrav 	}
1274b7579f77SDag-Erling Smørgrav 
1275b7579f77SDag-Erling Smørgrav 	/* grab the ce rrset */
1276b7579f77SDag-Erling Smørgrav 	ce_rrset = grab_nsec(rrset_cache, data->name, data->len,
1277b7579f77SDag-Erling Smørgrav 		LDNS_RR_TYPE_NSEC3, zone->dclass, 0, region, 1,
1278b7579f77SDag-Erling Smørgrav 		LDNS_RR_TYPE_DS, now);
1279b7579f77SDag-Erling Smørgrav 	if(!ce_rrset)
1280b7579f77SDag-Erling Smørgrav 		return NULL;
1281b7579f77SDag-Erling Smørgrav 	if(!neg_params_ok(zone, ce_rrset))
1282b7579f77SDag-Erling Smørgrav 		return NULL;
1283b7579f77SDag-Erling Smørgrav 
1284b7579f77SDag-Erling Smørgrav 	if(nclen == 0) {
1285b7579f77SDag-Erling Smørgrav 		/* exact match, just check the type bits */
1286b7579f77SDag-Erling Smørgrav 		/* need: -SOA, -DS, +NS */
1287b7579f77SDag-Erling Smørgrav 		if(nsec3_has_type(ce_rrset, 0, LDNS_RR_TYPE_SOA) ||
1288b7579f77SDag-Erling Smørgrav 			nsec3_has_type(ce_rrset, 0, LDNS_RR_TYPE_DS) ||
1289b7579f77SDag-Erling Smørgrav 			!nsec3_has_type(ce_rrset, 0, LDNS_RR_TYPE_NS))
1290b7579f77SDag-Erling Smørgrav 			return NULL;
1291b7579f77SDag-Erling Smørgrav 		if(!(msg = dns_msg_create(qname, qname_len,
1292b7579f77SDag-Erling Smørgrav 			LDNS_RR_TYPE_DS, zone->dclass, region, 1)))
1293b7579f77SDag-Erling Smørgrav 			return NULL;
1294b7579f77SDag-Erling Smørgrav 		/* TTL reduced in grab_nsec */
1295b7579f77SDag-Erling Smørgrav 		if(!dns_msg_authadd(msg, region, ce_rrset, 0))
1296b7579f77SDag-Erling Smørgrav 			return NULL;
1297b7579f77SDag-Erling Smørgrav 		return msg;
1298b7579f77SDag-Erling Smørgrav 	}
1299b7579f77SDag-Erling Smørgrav 
1300b7579f77SDag-Erling Smørgrav 	/* optout is not allowed without knowing the trust-anchor in use,
1301b7579f77SDag-Erling Smørgrav 	 * otherwise the optout could spoof away that anchor */
1302b7579f77SDag-Erling Smørgrav 	if(!topname)
1303b7579f77SDag-Erling Smørgrav 		return NULL;
1304b7579f77SDag-Erling Smørgrav 
1305b7579f77SDag-Erling Smørgrav 	/* if there is no exact match, it must be in an optout span
1306b7579f77SDag-Erling Smørgrav 	 * (an existing DS implies an NSEC3 must exist) */
1307b7579f77SDag-Erling Smørgrav 	nc_rrset = neg_nsec3_getnc(zone, hashnc, nclen, rrset_cache,
1308b7579f77SDag-Erling Smørgrav 		region, now, nc_b32, sizeof(nc_b32));
1309b7579f77SDag-Erling Smørgrav 	if(!nc_rrset)
1310b7579f77SDag-Erling Smørgrav 		return NULL;
1311b7579f77SDag-Erling Smørgrav 	if(!neg_params_ok(zone, nc_rrset))
1312b7579f77SDag-Erling Smørgrav 		return NULL;
1313b7579f77SDag-Erling Smørgrav 	if(!nsec3_has_optout(nc_rrset, 0))
1314b7579f77SDag-Erling Smørgrav 		return NULL;
1315b7579f77SDag-Erling Smørgrav 	c.hash = hashnc;
1316b7579f77SDag-Erling Smørgrav 	c.hash_len = nclen;
1317b7579f77SDag-Erling Smørgrav 	c.b32 = nc_b32+1;
1318b7579f77SDag-Erling Smørgrav 	c.b32_len = (size_t)nc_b32[0];
1319b7579f77SDag-Erling Smørgrav 	if(nsec3_covers(zone->name, &c, nc_rrset, 0, buf)) {
1320b7579f77SDag-Erling Smørgrav 		/* nc_rrset covers the next closer name.
1321b7579f77SDag-Erling Smørgrav 		 * ce_rrset equals a closer encloser.
1322b7579f77SDag-Erling Smørgrav 		 * nc_rrset is optout.
1323b7579f77SDag-Erling Smørgrav 		 * No need to check wildcard for type DS */
1324b7579f77SDag-Erling Smørgrav 		/* capacity=3: ce + nc + soa(if needed) */
1325b7579f77SDag-Erling Smørgrav 		if(!(msg = dns_msg_create(qname, qname_len,
1326b7579f77SDag-Erling Smørgrav 			LDNS_RR_TYPE_DS, zone->dclass, region, 3)))
1327b7579f77SDag-Erling Smørgrav 			return NULL;
1328b7579f77SDag-Erling Smørgrav 		/* now=0 because TTL was reduced in grab_nsec */
1329b7579f77SDag-Erling Smørgrav 		if(!dns_msg_authadd(msg, region, ce_rrset, 0))
1330b7579f77SDag-Erling Smørgrav 			return NULL;
1331b7579f77SDag-Erling Smørgrav 		if(!dns_msg_authadd(msg, region, nc_rrset, 0))
1332b7579f77SDag-Erling Smørgrav 			return NULL;
1333b7579f77SDag-Erling Smørgrav 		return msg;
1334b7579f77SDag-Erling Smørgrav 	}
1335b7579f77SDag-Erling Smørgrav 	return NULL;
1336b7579f77SDag-Erling Smørgrav }
1337b7579f77SDag-Erling Smørgrav 
1338b7579f77SDag-Erling Smørgrav /**
1339b7579f77SDag-Erling Smørgrav  * Add SOA record for external responses.
1340b7579f77SDag-Erling Smørgrav  * @param rrset_cache: to look into.
1341b7579f77SDag-Erling Smørgrav  * @param now: current time.
1342b7579f77SDag-Erling Smørgrav  * @param region: where to perform the allocation
1343b7579f77SDag-Erling Smørgrav  * @param msg: current msg with NSEC.
1344b7579f77SDag-Erling Smørgrav  * @param zone: val_neg_zone if we have one.
1345b7579f77SDag-Erling Smørgrav  * @return false on lookup or alloc failure.
1346b7579f77SDag-Erling Smørgrav  */
add_soa(struct rrset_cache * rrset_cache,time_t now,struct regional * region,struct dns_msg * msg,struct val_neg_zone * zone)134717d15b25SDag-Erling Smørgrav static int add_soa(struct rrset_cache* rrset_cache, time_t now,
1348b7579f77SDag-Erling Smørgrav 	struct regional* region, struct dns_msg* msg, struct val_neg_zone* zone)
1349b7579f77SDag-Erling Smørgrav {
1350b7579f77SDag-Erling Smørgrav 	struct ub_packed_rrset_key* soa;
1351b7579f77SDag-Erling Smørgrav 	uint8_t* nm;
1352b7579f77SDag-Erling Smørgrav 	size_t nmlen;
1353b7579f77SDag-Erling Smørgrav 	uint16_t dclass;
1354b7579f77SDag-Erling Smørgrav 	if(zone) {
1355b7579f77SDag-Erling Smørgrav 		nm = zone->name;
1356b7579f77SDag-Erling Smørgrav 		nmlen = zone->len;
1357b7579f77SDag-Erling Smørgrav 		dclass = zone->dclass;
1358b7579f77SDag-Erling Smørgrav 	} else {
1359b7579f77SDag-Erling Smørgrav 		/* Assumes the signer is the zone SOA to add */
1360b7579f77SDag-Erling Smørgrav 		nm = reply_nsec_signer(msg->rep, &nmlen, &dclass);
1361b7579f77SDag-Erling Smørgrav 		if(!nm)
1362b7579f77SDag-Erling Smørgrav 			return 0;
1363b7579f77SDag-Erling Smørgrav 	}
1364b7579f77SDag-Erling Smørgrav 	soa = rrset_cache_lookup(rrset_cache, nm, nmlen, LDNS_RR_TYPE_SOA,
1365b7579f77SDag-Erling Smørgrav 		dclass, PACKED_RRSET_SOA_NEG, now, 0);
1366b7579f77SDag-Erling Smørgrav 	if(!soa)
1367b7579f77SDag-Erling Smørgrav 		return 0;
1368b7579f77SDag-Erling Smørgrav 	if(!dns_msg_authadd(msg, region, soa, now)) {
1369b7579f77SDag-Erling Smørgrav 		lock_rw_unlock(&soa->entry.lock);
1370b7579f77SDag-Erling Smørgrav 		return 0;
1371b7579f77SDag-Erling Smørgrav 	}
1372b7579f77SDag-Erling Smørgrav 	lock_rw_unlock(&soa->entry.lock);
1373b7579f77SDag-Erling Smørgrav 	return 1;
1374b7579f77SDag-Erling Smørgrav }
1375b7579f77SDag-Erling Smørgrav 
1376b7579f77SDag-Erling Smørgrav struct dns_msg*
val_neg_getmsg(struct val_neg_cache * neg,struct query_info * qinfo,struct regional * region,struct rrset_cache * rrset_cache,sldns_buffer * buf,time_t now,int addsoa,uint8_t * topname,struct config_file * cfg)1377b7579f77SDag-Erling Smørgrav val_neg_getmsg(struct val_neg_cache* neg, struct query_info* qinfo,
1378b7579f77SDag-Erling Smørgrav 	struct regional* region, struct rrset_cache* rrset_cache,
137957bddd21SDag-Erling Smørgrav 	sldns_buffer* buf, time_t now, int addsoa, uint8_t* topname,
138057bddd21SDag-Erling Smørgrav 	struct config_file* cfg)
1381b7579f77SDag-Erling Smørgrav {
1382b7579f77SDag-Erling Smørgrav 	struct dns_msg* msg;
138357bddd21SDag-Erling Smørgrav 	struct ub_packed_rrset_key* nsec; /* qname matching/covering nsec */
138457bddd21SDag-Erling Smørgrav 	struct ub_packed_rrset_key* wcrr; /* wildcard record or nsec */
138557bddd21SDag-Erling Smørgrav 	uint8_t* nodata_wc = NULL;
138657bddd21SDag-Erling Smørgrav 	uint8_t* ce = NULL;
138757bddd21SDag-Erling Smørgrav 	size_t ce_len;
138857bddd21SDag-Erling Smørgrav 	uint8_t wc_ce[LDNS_MAX_DOMAINLEN+3];
138957bddd21SDag-Erling Smørgrav 	struct query_info wc_qinfo;
139057bddd21SDag-Erling Smørgrav 	struct ub_packed_rrset_key* cache_wc;
139157bddd21SDag-Erling Smørgrav 	struct packed_rrset_data* wcrr_data;
139257bddd21SDag-Erling Smørgrav 	int rcode = LDNS_RCODE_NOERROR;
1393b7579f77SDag-Erling Smørgrav 	uint8_t* zname;
1394b7579f77SDag-Erling Smørgrav 	size_t zname_len;
1395b7579f77SDag-Erling Smørgrav 	int zname_labs;
1396b7579f77SDag-Erling Smørgrav 	struct val_neg_zone* zone;
1397b7579f77SDag-Erling Smørgrav 
139857bddd21SDag-Erling Smørgrav 	/* only for DS queries when aggressive use of NSEC is disabled */
139957bddd21SDag-Erling Smørgrav 	if(qinfo->qtype != LDNS_RR_TYPE_DS && !cfg->aggressive_nsec)
1400b7579f77SDag-Erling Smørgrav 		return NULL;
1401b7579f77SDag-Erling Smørgrav 	log_assert(!topname || dname_subdomain_c(qinfo->qname, topname));
1402b7579f77SDag-Erling Smørgrav 
140357bddd21SDag-Erling Smørgrav 	/* Get best available NSEC for qname */
140457bddd21SDag-Erling Smørgrav 	nsec = neg_find_nsec(neg, qinfo->qname, qinfo->qname_len, qinfo->qclass,
140557bddd21SDag-Erling Smørgrav 		rrset_cache, now, region);
140657bddd21SDag-Erling Smørgrav 
140757bddd21SDag-Erling Smørgrav 	/* Matching NSEC, use to generate No Data answer. Not creating answers
140857bddd21SDag-Erling Smørgrav 	 * yet for No Data proven using wildcard. */
140957bddd21SDag-Erling Smørgrav 	if(nsec && nsec_proves_nodata(nsec, qinfo, &nodata_wc) && !nodata_wc) {
1410*8f76bb7dSCy Schubert 		/* do not create nodata answers for qtype ANY, it is a query
1411*8f76bb7dSCy Schubert 		 * type, not an rrtype to disprove. Nameerrors are useful for
1412*8f76bb7dSCy Schubert 		 * qtype ANY, in the else branch. */
1413*8f76bb7dSCy Schubert 		if(qinfo->qtype == LDNS_RR_TYPE_ANY)
1414*8f76bb7dSCy Schubert 			return NULL;
1415b7579f77SDag-Erling Smørgrav 		if(!(msg = dns_msg_create(qinfo->qname, qinfo->qname_len,
1416b7579f77SDag-Erling Smørgrav 			qinfo->qtype, qinfo->qclass, region, 2)))
1417b7579f77SDag-Erling Smørgrav 			return NULL;
141857bddd21SDag-Erling Smørgrav 		if(!dns_msg_authadd(msg, region, nsec, 0))
1419b7579f77SDag-Erling Smørgrav 			return NULL;
1420b7579f77SDag-Erling Smørgrav 		if(addsoa && !add_soa(rrset_cache, now, region, msg, NULL))
1421b7579f77SDag-Erling Smørgrav 			return NULL;
14220fb34990SDag-Erling Smørgrav 
14230fb34990SDag-Erling Smørgrav 		lock_basic_lock(&neg->lock);
14240fb34990SDag-Erling Smørgrav 		neg->num_neg_cache_noerror++;
14250fb34990SDag-Erling Smørgrav 		lock_basic_unlock(&neg->lock);
1426b7579f77SDag-Erling Smørgrav 		return msg;
142757bddd21SDag-Erling Smørgrav 	} else if(nsec && val_nsec_proves_name_error(nsec, qinfo->qname)) {
142857bddd21SDag-Erling Smørgrav 		if(!(msg = dns_msg_create(qinfo->qname, qinfo->qname_len,
142957bddd21SDag-Erling Smørgrav 			qinfo->qtype, qinfo->qclass, region, 3)))
143057bddd21SDag-Erling Smørgrav 			return NULL;
143157bddd21SDag-Erling Smørgrav 		if(!(ce = nsec_closest_encloser(qinfo->qname, nsec)))
143257bddd21SDag-Erling Smørgrav 			return NULL;
143357bddd21SDag-Erling Smørgrav 		dname_count_size_labels(ce, &ce_len);
143457bddd21SDag-Erling Smørgrav 
143557bddd21SDag-Erling Smørgrav 		/* No extra extra NSEC required if both nameerror qname and
143657bddd21SDag-Erling Smørgrav 		 * nodata *.ce. are proven already. */
143757bddd21SDag-Erling Smørgrav 		if(!nodata_wc || query_dname_compare(nodata_wc, ce) != 0) {
143857bddd21SDag-Erling Smørgrav 			/* Qname proven non existing, get wildcard record for
143957bddd21SDag-Erling Smørgrav 			 * QTYPE or NSEC covering or matching wildcard. */
144057bddd21SDag-Erling Smørgrav 
144157bddd21SDag-Erling Smørgrav 			/* Num labels in ce is always smaller than in qname,
144257bddd21SDag-Erling Smørgrav 			 * therefore adding the wildcard label cannot overflow
144357bddd21SDag-Erling Smørgrav 			 * buffer. */
144457bddd21SDag-Erling Smørgrav 			wc_ce[0] = 1;
144557bddd21SDag-Erling Smørgrav 			wc_ce[1] = (uint8_t)'*';
144657bddd21SDag-Erling Smørgrav 			memmove(wc_ce+2, ce, ce_len);
144757bddd21SDag-Erling Smørgrav 			wc_qinfo.qname = wc_ce;
144857bddd21SDag-Erling Smørgrav 			wc_qinfo.qname_len = ce_len + 2;
144957bddd21SDag-Erling Smørgrav 			wc_qinfo.qtype = qinfo->qtype;
145057bddd21SDag-Erling Smørgrav 
145157bddd21SDag-Erling Smørgrav 
145257bddd21SDag-Erling Smørgrav 			if((cache_wc = rrset_cache_lookup(rrset_cache, wc_qinfo.qname,
145357bddd21SDag-Erling Smørgrav 				wc_qinfo.qname_len, wc_qinfo.qtype,
145457bddd21SDag-Erling Smørgrav 				qinfo->qclass, 0/*flags*/, now, 0/*read only*/))) {
145557bddd21SDag-Erling Smørgrav 				/* Synthesize wildcard answer */
145657bddd21SDag-Erling Smørgrav 				wcrr_data = (struct packed_rrset_data*)cache_wc->entry.data;
145757bddd21SDag-Erling Smørgrav 				if(!(wcrr_data->security == sec_status_secure ||
145857bddd21SDag-Erling Smørgrav 					(wcrr_data->security == sec_status_unchecked &&
145957bddd21SDag-Erling Smørgrav 					wcrr_data->rrsig_count > 0))) {
146057bddd21SDag-Erling Smørgrav 					lock_rw_unlock(&cache_wc->entry.lock);
146157bddd21SDag-Erling Smørgrav 					return NULL;
146257bddd21SDag-Erling Smørgrav 				}
146357bddd21SDag-Erling Smørgrav 				if(!(wcrr = packed_rrset_copy_region(cache_wc,
146457bddd21SDag-Erling Smørgrav 					region, now))) {
146557bddd21SDag-Erling Smørgrav 					lock_rw_unlock(&cache_wc->entry.lock);
146657bddd21SDag-Erling Smørgrav 					return NULL;
146757bddd21SDag-Erling Smørgrav 				};
146857bddd21SDag-Erling Smørgrav 				lock_rw_unlock(&cache_wc->entry.lock);
146957bddd21SDag-Erling Smørgrav 				wcrr->rk.dname = qinfo->qname;
147057bddd21SDag-Erling Smørgrav 				wcrr->rk.dname_len = qinfo->qname_len;
147157bddd21SDag-Erling Smørgrav 				if(!dns_msg_ansadd(msg, region, wcrr, 0))
147257bddd21SDag-Erling Smørgrav 					return NULL;
147357bddd21SDag-Erling Smørgrav 				/* No SOA needed for wildcard synthesised
147457bddd21SDag-Erling Smørgrav 				 * answer. */
147557bddd21SDag-Erling Smørgrav 				addsoa = 0;
147657bddd21SDag-Erling Smørgrav 			} else {
147757bddd21SDag-Erling Smørgrav 				/* Get wildcard NSEC for possible non existence
147857bddd21SDag-Erling Smørgrav 				 * proof */
147957bddd21SDag-Erling Smørgrav 				if(!(wcrr = neg_find_nsec(neg, wc_qinfo.qname,
148057bddd21SDag-Erling Smørgrav 					wc_qinfo.qname_len, qinfo->qclass,
148157bddd21SDag-Erling Smørgrav 					rrset_cache, now, region)))
148257bddd21SDag-Erling Smørgrav 					return NULL;
148357bddd21SDag-Erling Smørgrav 
148457bddd21SDag-Erling Smørgrav 				nodata_wc = NULL;
148557bddd21SDag-Erling Smørgrav 				if(val_nsec_proves_name_error(wcrr, wc_ce))
148657bddd21SDag-Erling Smørgrav 					rcode = LDNS_RCODE_NXDOMAIN;
148757bddd21SDag-Erling Smørgrav 				else if(!nsec_proves_nodata(wcrr, &wc_qinfo,
148857bddd21SDag-Erling Smørgrav 					&nodata_wc) || nodata_wc)
14890fb34990SDag-Erling Smørgrav 					/* &nodata_wc shouldn't be set, wc_qinfo
149057bddd21SDag-Erling Smørgrav 					 * already contains wildcard domain. */
149157bddd21SDag-Erling Smørgrav 					/* NSEC doesn't prove anything for
149257bddd21SDag-Erling Smørgrav 					 * wildcard. */
149357bddd21SDag-Erling Smørgrav 					return NULL;
149457bddd21SDag-Erling Smørgrav 				if(query_dname_compare(wcrr->rk.dname,
149557bddd21SDag-Erling Smørgrav 					nsec->rk.dname) != 0)
149657bddd21SDag-Erling Smørgrav 					if(!dns_msg_authadd(msg, region, wcrr, 0))
149757bddd21SDag-Erling Smørgrav 						return NULL;
149857bddd21SDag-Erling Smørgrav 			}
1499b7579f77SDag-Erling Smørgrav 		}
1500b7579f77SDag-Erling Smørgrav 
150157bddd21SDag-Erling Smørgrav 		if(!dns_msg_authadd(msg, region, nsec, 0))
150257bddd21SDag-Erling Smørgrav 			return NULL;
150357bddd21SDag-Erling Smørgrav 		if(addsoa && !add_soa(rrset_cache, now, region, msg, NULL))
150457bddd21SDag-Erling Smørgrav 			return NULL;
150557bddd21SDag-Erling Smørgrav 
15060fb34990SDag-Erling Smørgrav 		/* Increment statistic counters */
15070fb34990SDag-Erling Smørgrav 		lock_basic_lock(&neg->lock);
15080fb34990SDag-Erling Smørgrav 		if(rcode == LDNS_RCODE_NOERROR)
15090fb34990SDag-Erling Smørgrav 			neg->num_neg_cache_noerror++;
15100fb34990SDag-Erling Smørgrav 		else if(rcode == LDNS_RCODE_NXDOMAIN)
15110fb34990SDag-Erling Smørgrav 			neg->num_neg_cache_nxdomain++;
15120fb34990SDag-Erling Smørgrav 		lock_basic_unlock(&neg->lock);
15130fb34990SDag-Erling Smørgrav 
151457bddd21SDag-Erling Smørgrav 		FLAGS_SET_RCODE(msg->rep->flags, rcode);
151557bddd21SDag-Erling Smørgrav 		return msg;
151657bddd21SDag-Erling Smørgrav 	}
151757bddd21SDag-Erling Smørgrav 
151857bddd21SDag-Erling Smørgrav 	/* No aggressive use of NSEC3 for now, only proceed for DS types. */
151957bddd21SDag-Erling Smørgrav 	if(qinfo->qtype != LDNS_RR_TYPE_DS){
152057bddd21SDag-Erling Smørgrav 		return NULL;
152157bddd21SDag-Erling Smørgrav 	}
1522b7579f77SDag-Erling Smørgrav 	/* check NSEC3 neg cache for type DS */
1523b7579f77SDag-Erling Smørgrav 	/* need to look one zone higher for DS type */
1524b7579f77SDag-Erling Smørgrav 	zname = qinfo->qname;
1525b7579f77SDag-Erling Smørgrav 	zname_len = qinfo->qname_len;
1526b7579f77SDag-Erling Smørgrav 	dname_remove_label(&zname, &zname_len);
1527b7579f77SDag-Erling Smørgrav 	zname_labs = dname_count_labels(zname);
1528b7579f77SDag-Erling Smørgrav 
1529b7579f77SDag-Erling Smørgrav 	/* lookup closest zone */
1530b7579f77SDag-Erling Smørgrav 	lock_basic_lock(&neg->lock);
1531b7579f77SDag-Erling Smørgrav 	zone = neg_closest_zone_parent(neg, zname, zname_len, zname_labs,
1532b7579f77SDag-Erling Smørgrav 		qinfo->qclass);
1533b7579f77SDag-Erling Smørgrav 	while(zone && !zone->in_use)
1534b7579f77SDag-Erling Smørgrav 		zone = zone->parent;
1535b7579f77SDag-Erling Smørgrav 	/* check that the zone is not too high up so that we do not pick data
1536b7579f77SDag-Erling Smørgrav 	 * out of a zone that is above the last-seen key (or trust-anchor). */
1537b7579f77SDag-Erling Smørgrav 	if(zone && topname) {
1538b7579f77SDag-Erling Smørgrav 		if(!dname_subdomain_c(zone->name, topname))
1539b7579f77SDag-Erling Smørgrav 			zone = NULL;
1540b7579f77SDag-Erling Smørgrav 	}
1541b7579f77SDag-Erling Smørgrav 	if(!zone) {
1542b7579f77SDag-Erling Smørgrav 		lock_basic_unlock(&neg->lock);
1543b7579f77SDag-Erling Smørgrav 		return NULL;
1544b7579f77SDag-Erling Smørgrav 	}
1545b7579f77SDag-Erling Smørgrav 
1546b7579f77SDag-Erling Smørgrav 	msg = neg_nsec3_proof_ds(zone, qinfo->qname, qinfo->qname_len,
1547b7579f77SDag-Erling Smørgrav 		zname_labs+1, buf, rrset_cache, region, now, topname);
1548b7579f77SDag-Erling Smørgrav 	if(msg && addsoa && !add_soa(rrset_cache, now, region, msg, zone)) {
1549b7579f77SDag-Erling Smørgrav 		lock_basic_unlock(&neg->lock);
1550b7579f77SDag-Erling Smørgrav 		return NULL;
1551b7579f77SDag-Erling Smørgrav 	}
1552b7579f77SDag-Erling Smørgrav 	lock_basic_unlock(&neg->lock);
1553b7579f77SDag-Erling Smørgrav 	return msg;
1554b7579f77SDag-Erling Smørgrav }
1555