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