File: | src/sbin/unwind/libunbound/validator/val_neg.c |
Warning: | line 276, column 3 Use of memory after it is freed |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
1 | /* | ||||
2 | * validator/val_neg.c - validator aggressive negative caching functions. | ||||
3 | * | ||||
4 | * Copyright (c) 2008, NLnet Labs. All rights reserved. | ||||
5 | * | ||||
6 | * This software is open source. | ||||
7 | * | ||||
8 | * Redistribution and use in source and binary forms, with or without | ||||
9 | * modification, are permitted provided that the following conditions | ||||
10 | * are met: | ||||
11 | * | ||||
12 | * Redistributions of source code must retain the above copyright notice, | ||||
13 | * this list of conditions and the following disclaimer. | ||||
14 | * | ||||
15 | * Redistributions in binary form must reproduce the above copyright notice, | ||||
16 | * this list of conditions and the following disclaimer in the documentation | ||||
17 | * and/or other materials provided with the distribution. | ||||
18 | * | ||||
19 | * Neither the name of the NLNET LABS nor the names of its contributors may | ||||
20 | * be used to endorse or promote products derived from this software without | ||||
21 | * specific prior written permission. | ||||
22 | * | ||||
23 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | ||||
24 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | ||||
25 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | ||||
26 | * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | ||||
27 | * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | ||||
28 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED | ||||
29 | * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR | ||||
30 | * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF | ||||
31 | * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING | ||||
32 | * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | ||||
33 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||||
34 | */ | ||||
35 | |||||
36 | /** | ||||
37 | * \file | ||||
38 | * | ||||
39 | * This file contains helper functions for the validator module. | ||||
40 | * The functions help with aggressive negative caching. | ||||
41 | * This creates new denials of existence, and proofs for absence of types | ||||
42 | * from cached NSEC records. | ||||
43 | */ | ||||
44 | #include "config.h" | ||||
45 | #ifdef HAVE_OPENSSL_SSL_H1 | ||||
46 | #include "openssl/ssl.h" | ||||
47 | #define NSEC3_SHA_LEN20 SHA_DIGEST_LENGTH20 | ||||
48 | #else | ||||
49 | #define NSEC3_SHA_LEN20 20 | ||||
50 | #endif | ||||
51 | #include "validator/val_neg.h" | ||||
52 | #include "validator/val_nsec.h" | ||||
53 | #include "validator/val_nsec3.h" | ||||
54 | #include "validator/val_utils.h" | ||||
55 | #include "util/data/dname.h" | ||||
56 | #include "util/data/msgreply.h" | ||||
57 | #include "util/log.h" | ||||
58 | #include "util/net_help.h" | ||||
59 | #include "util/config_file.h" | ||||
60 | #include "services/cache/rrset.h" | ||||
61 | #include "services/cache/dns.h" | ||||
62 | #include "sldns/rrdef.h" | ||||
63 | #include "sldns/sbuffer.h" | ||||
64 | |||||
65 | int val_neg_data_compare(const void* a, const void* b) | ||||
66 | { | ||||
67 | struct val_neg_data* x = (struct val_neg_data*)a; | ||||
68 | struct val_neg_data* y = (struct val_neg_data*)b; | ||||
69 | int m; | ||||
70 | return dname_canon_lab_cmp(x->name, x->labs, y->name, y->labs, &m); | ||||
71 | } | ||||
72 | |||||
73 | int val_neg_zone_compare(const void* a, const void* b) | ||||
74 | { | ||||
75 | struct val_neg_zone* x = (struct val_neg_zone*)a; | ||||
76 | struct val_neg_zone* y = (struct val_neg_zone*)b; | ||||
77 | int m; | ||||
78 | if(x->dclass != y->dclass) { | ||||
79 | if(x->dclass < y->dclass) | ||||
80 | return -1; | ||||
81 | return 1; | ||||
82 | } | ||||
83 | return dname_canon_lab_cmp(x->name, x->labs, y->name, y->labs, &m); | ||||
84 | } | ||||
85 | |||||
86 | struct val_neg_cache* val_neg_create(struct config_file* cfg, size_t maxiter) | ||||
87 | { | ||||
88 | struct val_neg_cache* neg = (struct val_neg_cache*)calloc(1, | ||||
89 | sizeof(*neg)); | ||||
90 | if(!neg) { | ||||
91 | log_err("Could not create neg cache: out of memory"); | ||||
92 | return NULL((void*)0); | ||||
93 | } | ||||
94 | neg->nsec3_max_iter = maxiter; | ||||
95 | neg->max = 1024*1024; /* 1 M is thousands of entries */ | ||||
96 | if(cfg) neg->max = cfg->neg_cache_size; | ||||
97 | rbtree_init(&neg->tree, &val_neg_zone_compare); | ||||
98 | lock_basic_init(&neg->lock); | ||||
99 | lock_protect(&neg->lock, neg, sizeof(*neg)); | ||||
100 | return neg; | ||||
101 | } | ||||
102 | |||||
103 | size_t val_neg_get_mem(struct val_neg_cache* neg) | ||||
104 | { | ||||
105 | size_t result; | ||||
106 | lock_basic_lock(&neg->lock); | ||||
107 | result = sizeof(*neg) + neg->use; | ||||
108 | lock_basic_unlock(&neg->lock); | ||||
109 | return result; | ||||
110 | } | ||||
111 | |||||
112 | /** clear datas on cache deletion */ | ||||
113 | static void | ||||
114 | neg_clear_datas(rbnode_type* n, void* ATTR_UNUSED(arg)arg __attribute__((unused))) | ||||
115 | { | ||||
116 | struct val_neg_data* d = (struct val_neg_data*)n; | ||||
117 | free(d->name); | ||||
118 | free(d); | ||||
119 | } | ||||
120 | |||||
121 | /** clear zones on cache deletion */ | ||||
122 | static void | ||||
123 | neg_clear_zones(rbnode_type* n, void* ATTR_UNUSED(arg)arg __attribute__((unused))) | ||||
124 | { | ||||
125 | struct val_neg_zone* z = (struct val_neg_zone*)n; | ||||
126 | /* delete all the rrset entries in the tree */ | ||||
127 | traverse_postorder(&z->tree, &neg_clear_datas, NULL((void*)0)); | ||||
128 | free(z->nsec3_salt); | ||||
129 | free(z->name); | ||||
130 | free(z); | ||||
131 | } | ||||
132 | |||||
133 | void neg_cache_delete(struct val_neg_cache* neg) | ||||
134 | { | ||||
135 | if(!neg) return; | ||||
136 | lock_basic_destroy(&neg->lock); | ||||
137 | /* delete all the zones in the tree */ | ||||
138 | traverse_postorder(&neg->tree, &neg_clear_zones, NULL((void*)0)); | ||||
139 | free(neg); | ||||
140 | } | ||||
141 | |||||
142 | /** | ||||
143 | * Put data element at the front of the LRU list. | ||||
144 | * @param neg: negative cache with LRU start and end. | ||||
145 | * @param data: this data is fronted. | ||||
146 | */ | ||||
147 | static void neg_lru_front(struct val_neg_cache* neg, | ||||
148 | struct val_neg_data* data) | ||||
149 | { | ||||
150 | data->prev = NULL((void*)0); | ||||
151 | data->next = neg->first; | ||||
152 | if(!neg->first) | ||||
153 | neg->last = data; | ||||
154 | else neg->first->prev = data; | ||||
155 | neg->first = data; | ||||
156 | } | ||||
157 | |||||
158 | /** | ||||
159 | * Remove data element from LRU list. | ||||
160 | * @param neg: negative cache with LRU start and end. | ||||
161 | * @param data: this data is removed from the list. | ||||
162 | */ | ||||
163 | static void neg_lru_remove(struct val_neg_cache* neg, | ||||
164 | struct val_neg_data* data) | ||||
165 | { | ||||
166 | if(data->prev) | ||||
167 | data->prev->next = data->next; | ||||
168 | else neg->first = data->next; | ||||
169 | if(data->next) | ||||
170 | data->next->prev = data->prev; | ||||
171 | else neg->last = data->prev; | ||||
172 | } | ||||
173 | |||||
174 | /** | ||||
175 | * Touch LRU for data element, put it at the start of the LRU list. | ||||
176 | * @param neg: negative cache with LRU start and end. | ||||
177 | * @param data: this data is used. | ||||
178 | */ | ||||
179 | static void neg_lru_touch(struct val_neg_cache* neg, | ||||
180 | struct val_neg_data* data) | ||||
181 | { | ||||
182 | if(data == neg->first) | ||||
183 | return; /* nothing to do */ | ||||
184 | /* remove from current lru position */ | ||||
185 | neg_lru_remove(neg, data); | ||||
186 | /* add at front */ | ||||
187 | neg_lru_front(neg, data); | ||||
188 | } | ||||
189 | |||||
190 | /** | ||||
191 | * Delete a zone element from the negative cache. | ||||
192 | * May delete other zone elements to keep tree coherent, or | ||||
193 | * only mark the element as 'not in use'. | ||||
194 | * @param neg: negative cache. | ||||
195 | * @param z: zone element to delete. | ||||
196 | */ | ||||
197 | static void neg_delete_zone(struct val_neg_cache* neg, struct val_neg_zone* z) | ||||
198 | { | ||||
199 | struct val_neg_zone* p, *np; | ||||
200 | if(!z) return; | ||||
201 | log_assert(z->in_use); | ||||
202 | log_assert(z->count > 0); | ||||
203 | z->in_use = 0; | ||||
204 | |||||
205 | /* go up the tree and reduce counts */ | ||||
206 | p = z; | ||||
207 | while(p) { | ||||
208 | log_assert(p->count > 0); | ||||
209 | p->count --; | ||||
210 | p = p->parent; | ||||
211 | } | ||||
212 | |||||
213 | /* remove zones with zero count */ | ||||
214 | p = z; | ||||
215 | while(p && p->count == 0) { | ||||
216 | np = p->parent; | ||||
217 | (void)rbtree_delete(&neg->tree, &p->node); | ||||
218 | neg->use -= p->len + sizeof(*p); | ||||
219 | free(p->nsec3_salt); | ||||
220 | free(p->name); | ||||
221 | free(p); | ||||
222 | p = np; | ||||
223 | } | ||||
224 | } | ||||
225 | |||||
226 | void neg_delete_data(struct val_neg_cache* neg, struct val_neg_data* el) | ||||
227 | { | ||||
228 | struct val_neg_zone* z; | ||||
229 | struct val_neg_data* p, *np; | ||||
230 | if(!el
| ||||
231 | z = el->zone; | ||||
232 | log_assert(el->in_use); | ||||
233 | log_assert(el->count > 0); | ||||
234 | el->in_use = 0; | ||||
235 | |||||
236 | /* remove it from the lru list */ | ||||
237 | neg_lru_remove(neg, el); | ||||
238 | log_assert(neg->first != el && neg->last != el); | ||||
239 | |||||
240 | /* go up the tree and reduce counts */ | ||||
241 | p = el; | ||||
242 | while(p) { | ||||
243 | log_assert(p->count > 0); | ||||
244 | p->count --; | ||||
245 | p = p->parent; | ||||
246 | } | ||||
247 | |||||
248 | /* delete 0 count items from tree */ | ||||
249 | p = el; | ||||
250 | while(p
| ||||
251 | np = p->parent; | ||||
252 | (void)rbtree_delete(&z->tree, &p->node); | ||||
253 | neg->use -= p->len + sizeof(*p); | ||||
254 | free(p->name); | ||||
255 | free(p); | ||||
256 | p = np; | ||||
257 | } | ||||
258 | |||||
259 | /* check if the zone is now unused */ | ||||
260 | if(z->tree.count == 0) { | ||||
261 | neg_delete_zone(neg, z); | ||||
262 | } | ||||
263 | } | ||||
264 | |||||
265 | /** | ||||
266 | * Create more space in negative cache | ||||
267 | * The oldest elements are deleted until enough space is present. | ||||
268 | * Empty zones are deleted. | ||||
269 | * @param neg: negative cache. | ||||
270 | * @param need: how many bytes are needed. | ||||
271 | */ | ||||
272 | static void neg_make_space(struct val_neg_cache* neg, size_t need) | ||||
273 | { | ||||
274 | /* delete elements until enough space or its empty */ | ||||
275 | while(neg->last
| ||||
276 | neg_delete_data(neg, neg->last); | ||||
| |||||
277 | } | ||||
278 | } | ||||
279 | |||||
280 | struct val_neg_zone* neg_find_zone(struct val_neg_cache* neg, | ||||
281 | uint8_t* nm, size_t len, uint16_t dclass) | ||||
282 | { | ||||
283 | struct val_neg_zone lookfor; | ||||
284 | struct val_neg_zone* result; | ||||
285 | lookfor.node.key = &lookfor; | ||||
286 | lookfor.name = nm; | ||||
287 | lookfor.len = len; | ||||
288 | lookfor.labs = dname_count_labels(lookfor.name); | ||||
289 | lookfor.dclass = dclass; | ||||
290 | |||||
291 | result = (struct val_neg_zone*) | ||||
292 | rbtree_search(&neg->tree, lookfor.node.key); | ||||
293 | return result; | ||||
294 | } | ||||
295 | |||||
296 | /** | ||||
297 | * Find the given data | ||||
298 | * @param zone: negative zone | ||||
299 | * @param nm: what to look for. | ||||
300 | * @param len: length of nm | ||||
301 | * @param labs: labels in nm | ||||
302 | * @return data or NULL if not found. | ||||
303 | */ | ||||
304 | static struct val_neg_data* neg_find_data(struct val_neg_zone* zone, | ||||
305 | uint8_t* nm, size_t len, int labs) | ||||
306 | { | ||||
307 | struct val_neg_data lookfor; | ||||
308 | struct val_neg_data* result; | ||||
309 | lookfor.node.key = &lookfor; | ||||
310 | lookfor.name = nm; | ||||
311 | lookfor.len = len; | ||||
312 | lookfor.labs = labs; | ||||
313 | |||||
314 | result = (struct val_neg_data*) | ||||
315 | rbtree_search(&zone->tree, lookfor.node.key); | ||||
316 | return result; | ||||
317 | } | ||||
318 | |||||
319 | /** | ||||
320 | * Calculate space needed for the data and all its parents | ||||
321 | * @param rep: NSEC entries. | ||||
322 | * @return size. | ||||
323 | */ | ||||
324 | static size_t calc_data_need(struct reply_info* rep) | ||||
325 | { | ||||
326 | uint8_t* d; | ||||
327 | size_t i, len, res = 0; | ||||
328 | |||||
329 | for(i=rep->an_numrrsets; i<rep->an_numrrsets+rep->ns_numrrsets; i++) { | ||||
330 | if(ntohs(rep->rrsets[i]->rk.type)(__uint16_t)(__builtin_constant_p(rep->rrsets[i]->rk.type ) ? (__uint16_t)(((__uint16_t)(rep->rrsets[i]->rk.type) & 0xffU) << 8 | ((__uint16_t)(rep->rrsets[i]-> rk.type) & 0xff00U) >> 8) : __swap16md(rep->rrsets [i]->rk.type)) == LDNS_RR_TYPE_NSEC) { | ||||
331 | d = rep->rrsets[i]->rk.dname; | ||||
332 | len = rep->rrsets[i]->rk.dname_len; | ||||
333 | res = sizeof(struct val_neg_data) + len; | ||||
334 | while(!dname_is_root(d)) { | ||||
335 | log_assert(len > 1); /* not root label */ | ||||
336 | dname_remove_label(&d, &len); | ||||
337 | res += sizeof(struct val_neg_data) + len; | ||||
338 | } | ||||
339 | } | ||||
340 | } | ||||
341 | return res; | ||||
342 | } | ||||
343 | |||||
344 | /** | ||||
345 | * Calculate space needed for zone and all its parents | ||||
346 | * @param d: name of zone | ||||
347 | * @param len: length of name | ||||
348 | * @return size. | ||||
349 | */ | ||||
350 | static size_t calc_zone_need(uint8_t* d, size_t len) | ||||
351 | { | ||||
352 | size_t res = sizeof(struct val_neg_zone) + len; | ||||
353 | while(!dname_is_root(d)) { | ||||
354 | log_assert(len > 1); /* not root label */ | ||||
355 | dname_remove_label(&d, &len); | ||||
356 | res += sizeof(struct val_neg_zone) + len; | ||||
357 | } | ||||
358 | return res; | ||||
359 | } | ||||
360 | |||||
361 | /** | ||||
362 | * Find closest existing parent zone of the given name. | ||||
363 | * @param neg: negative cache. | ||||
364 | * @param nm: name to look for | ||||
365 | * @param nm_len: length of nm | ||||
366 | * @param labs: labelcount of nm. | ||||
367 | * @param qclass: class. | ||||
368 | * @return the zone or NULL if none found. | ||||
369 | */ | ||||
370 | static struct val_neg_zone* neg_closest_zone_parent(struct val_neg_cache* neg, | ||||
371 | uint8_t* nm, size_t nm_len, int labs, uint16_t qclass) | ||||
372 | { | ||||
373 | struct val_neg_zone key; | ||||
374 | struct val_neg_zone* result; | ||||
375 | rbnode_type* res = NULL((void*)0); | ||||
376 | key.node.key = &key; | ||||
377 | key.name = nm; | ||||
378 | key.len = nm_len; | ||||
379 | key.labs = labs; | ||||
380 | key.dclass = qclass; | ||||
381 | if(rbtree_find_less_equal(&neg->tree, &key, &res)) { | ||||
382 | /* exact match */ | ||||
383 | result = (struct val_neg_zone*)res; | ||||
384 | } else { | ||||
385 | /* smaller element (or no element) */ | ||||
386 | int m; | ||||
387 | result = (struct val_neg_zone*)res; | ||||
388 | if(!result || result->dclass != qclass) | ||||
389 | return NULL((void*)0); | ||||
390 | /* count number of labels matched */ | ||||
391 | (void)dname_lab_cmp(result->name, result->labs, key.name, | ||||
392 | key.labs, &m); | ||||
393 | while(result) { /* go up until qname is subdomain of stub */ | ||||
394 | if(result->labs <= m) | ||||
395 | break; | ||||
396 | result = result->parent; | ||||
397 | } | ||||
398 | } | ||||
399 | return result; | ||||
400 | } | ||||
401 | |||||
402 | /** | ||||
403 | * Find closest existing parent data for the given name. | ||||
404 | * @param zone: to look in. | ||||
405 | * @param nm: name to look for | ||||
406 | * @param nm_len: length of nm | ||||
407 | * @param labs: labelcount of nm. | ||||
408 | * @return the data or NULL if none found. | ||||
409 | */ | ||||
410 | static struct val_neg_data* neg_closest_data_parent( | ||||
411 | struct val_neg_zone* zone, uint8_t* nm, size_t nm_len, int labs) | ||||
412 | { | ||||
413 | struct val_neg_data key; | ||||
414 | struct val_neg_data* result; | ||||
415 | rbnode_type* res = NULL((void*)0); | ||||
416 | key.node.key = &key; | ||||
417 | key.name = nm; | ||||
418 | key.len = nm_len; | ||||
419 | key.labs = labs; | ||||
420 | if(rbtree_find_less_equal(&zone->tree, &key, &res)) { | ||||
421 | /* exact match */ | ||||
422 | result = (struct val_neg_data*)res; | ||||
423 | } else { | ||||
424 | /* smaller element (or no element) */ | ||||
425 | int m; | ||||
426 | result = (struct val_neg_data*)res; | ||||
427 | if(!result) | ||||
428 | return NULL((void*)0); | ||||
429 | /* count number of labels matched */ | ||||
430 | (void)dname_lab_cmp(result->name, result->labs, key.name, | ||||
431 | key.labs, &m); | ||||
432 | while(result) { /* go up until qname is subdomain of stub */ | ||||
433 | if(result->labs <= m) | ||||
434 | break; | ||||
435 | result = result->parent; | ||||
436 | } | ||||
437 | } | ||||
438 | return result; | ||||
439 | } | ||||
440 | |||||
441 | /** | ||||
442 | * Create a single zone node | ||||
443 | * @param nm: name for zone (copied) | ||||
444 | * @param nm_len: length of name | ||||
445 | * @param labs: labels in name. | ||||
446 | * @param dclass: class of zone, host order. | ||||
447 | * @return new zone or NULL on failure | ||||
448 | */ | ||||
449 | static struct val_neg_zone* neg_setup_zone_node( | ||||
450 | uint8_t* nm, size_t nm_len, int labs, uint16_t dclass) | ||||
451 | { | ||||
452 | struct val_neg_zone* zone = | ||||
453 | (struct val_neg_zone*)calloc(1, sizeof(*zone)); | ||||
454 | if(!zone) { | ||||
455 | return NULL((void*)0); | ||||
456 | } | ||||
457 | zone->node.key = zone; | ||||
458 | zone->name = memdup(nm, nm_len); | ||||
459 | if(!zone->name) { | ||||
460 | free(zone); | ||||
461 | return NULL((void*)0); | ||||
462 | } | ||||
463 | zone->len = nm_len; | ||||
464 | zone->labs = labs; | ||||
465 | zone->dclass = dclass; | ||||
466 | |||||
467 | rbtree_init(&zone->tree, &val_neg_data_compare); | ||||
468 | return zone; | ||||
469 | } | ||||
470 | |||||
471 | /** | ||||
472 | * Create a linked list of parent zones, starting at longname ending on | ||||
473 | * the parent (can be NULL, creates to the root). | ||||
474 | * @param nm: name for lowest in chain | ||||
475 | * @param nm_len: length of name | ||||
476 | * @param labs: labels in name. | ||||
477 | * @param dclass: class of zone. | ||||
478 | * @param parent: NULL for to root, else so it fits under here. | ||||
479 | * @return zone; a chain of zones and their parents up to the parent. | ||||
480 | * or NULL on malloc failure | ||||
481 | */ | ||||
482 | static struct val_neg_zone* neg_zone_chain( | ||||
483 | uint8_t* nm, size_t nm_len, int labs, uint16_t dclass, | ||||
484 | struct val_neg_zone* parent) | ||||
485 | { | ||||
486 | int i; | ||||
487 | int tolabs = parent?parent->labs:0; | ||||
488 | struct val_neg_zone* zone, *prev = NULL((void*)0), *first = NULL((void*)0); | ||||
489 | |||||
490 | /* create the new subtree, i is labelcount of current creation */ | ||||
491 | /* this creates a 'first' to z->parent=NULL list of zones */ | ||||
492 | for(i=labs; i!=tolabs; i--) { | ||||
493 | /* create new item */ | ||||
494 | zone = neg_setup_zone_node(nm, nm_len, i, dclass); | ||||
495 | if(!zone) { | ||||
496 | /* need to delete other allocations in this routine!*/ | ||||
497 | struct val_neg_zone* p=first, *np; | ||||
498 | while(p) { | ||||
499 | np = p->parent; | ||||
500 | free(p->name); | ||||
501 | free(p); | ||||
502 | p = np; | ||||
503 | } | ||||
504 | return NULL((void*)0); | ||||
505 | } | ||||
506 | if(i == labs) { | ||||
507 | first = zone; | ||||
508 | } else { | ||||
509 | prev->parent = zone; | ||||
510 | } | ||||
511 | /* prepare for next name */ | ||||
512 | prev = zone; | ||||
513 | dname_remove_label(&nm, &nm_len); | ||||
514 | } | ||||
515 | return first; | ||||
516 | } | ||||
517 | |||||
518 | void val_neg_zone_take_inuse(struct val_neg_zone* zone) | ||||
519 | { | ||||
520 | if(!zone->in_use) { | ||||
521 | struct val_neg_zone* p; | ||||
522 | zone->in_use = 1; | ||||
523 | /* increase usage count of all parents */ | ||||
524 | for(p=zone; p; p = p->parent) { | ||||
525 | p->count++; | ||||
526 | } | ||||
527 | } | ||||
528 | } | ||||
529 | |||||
530 | struct val_neg_zone* neg_create_zone(struct val_neg_cache* neg, | ||||
531 | uint8_t* nm, size_t nm_len, uint16_t dclass) | ||||
532 | { | ||||
533 | struct val_neg_zone* zone; | ||||
534 | struct val_neg_zone* parent; | ||||
535 | struct val_neg_zone* p, *np; | ||||
536 | int labs = dname_count_labels(nm); | ||||
537 | |||||
538 | /* find closest enclosing parent zone that (still) exists */ | ||||
539 | parent = neg_closest_zone_parent(neg, nm, nm_len, labs, dclass); | ||||
540 | if(parent && query_dname_compare(parent->name, nm) == 0) | ||||
541 | return parent; /* already exists, weird */ | ||||
542 | /* if parent exists, it is in use */ | ||||
543 | log_assert(!parent || parent->count > 0); | ||||
544 | zone = neg_zone_chain(nm, nm_len, labs, dclass, parent); | ||||
545 | if(!zone) { | ||||
546 | return NULL((void*)0); | ||||
547 | } | ||||
548 | |||||
549 | /* insert the list of zones into the tree */ | ||||
550 | p = zone; | ||||
551 | while(p) { | ||||
552 | np = p->parent; | ||||
553 | /* mem use */ | ||||
554 | neg->use += sizeof(struct val_neg_zone) + p->len; | ||||
555 | /* insert in tree */ | ||||
556 | (void)rbtree_insert(&neg->tree, &p->node); | ||||
557 | /* last one needs proper parent pointer */ | ||||
558 | if(np == NULL((void*)0)) | ||||
559 | p->parent = parent; | ||||
560 | p = np; | ||||
561 | } | ||||
562 | return zone; | ||||
563 | } | ||||
564 | |||||
565 | /** find zone name of message, returns the SOA record */ | ||||
566 | static struct ub_packed_rrset_key* reply_find_soa(struct reply_info* rep) | ||||
567 | { | ||||
568 | size_t i; | ||||
569 | for(i=rep->an_numrrsets; i< rep->an_numrrsets+rep->ns_numrrsets; i++){ | ||||
570 | if(ntohs(rep->rrsets[i]->rk.type)(__uint16_t)(__builtin_constant_p(rep->rrsets[i]->rk.type ) ? (__uint16_t)(((__uint16_t)(rep->rrsets[i]->rk.type) & 0xffU) << 8 | ((__uint16_t)(rep->rrsets[i]-> rk.type) & 0xff00U) >> 8) : __swap16md(rep->rrsets [i]->rk.type)) == LDNS_RR_TYPE_SOA) | ||||
571 | return rep->rrsets[i]; | ||||
572 | } | ||||
573 | return NULL((void*)0); | ||||
574 | } | ||||
575 | |||||
576 | /** see if the reply has NSEC records worthy of caching */ | ||||
577 | static int reply_has_nsec(struct reply_info* rep) | ||||
578 | { | ||||
579 | size_t i; | ||||
580 | struct packed_rrset_data* d; | ||||
581 | if(rep->security != sec_status_secure) | ||||
582 | return 0; | ||||
583 | for(i=rep->an_numrrsets; i< rep->an_numrrsets+rep->ns_numrrsets; i++){ | ||||
584 | if(ntohs(rep->rrsets[i]->rk.type)(__uint16_t)(__builtin_constant_p(rep->rrsets[i]->rk.type ) ? (__uint16_t)(((__uint16_t)(rep->rrsets[i]->rk.type) & 0xffU) << 8 | ((__uint16_t)(rep->rrsets[i]-> rk.type) & 0xff00U) >> 8) : __swap16md(rep->rrsets [i]->rk.type)) == LDNS_RR_TYPE_NSEC) { | ||||
585 | d = (struct packed_rrset_data*)rep->rrsets[i]-> | ||||
586 | entry.data; | ||||
587 | if(d->security == sec_status_secure) | ||||
588 | return 1; | ||||
589 | } | ||||
590 | } | ||||
591 | return 0; | ||||
592 | } | ||||
593 | |||||
594 | |||||
595 | /** | ||||
596 | * Create single node of data element. | ||||
597 | * @param nm: name (copied) | ||||
598 | * @param nm_len: length of name | ||||
599 | * @param labs: labels in name. | ||||
600 | * @return element with name nm, or NULL malloc failure. | ||||
601 | */ | ||||
602 | static struct val_neg_data* neg_setup_data_node( | ||||
603 | uint8_t* nm, size_t nm_len, int labs) | ||||
604 | { | ||||
605 | struct val_neg_data* el; | ||||
606 | el = (struct val_neg_data*)calloc(1, sizeof(*el)); | ||||
607 | if(!el) { | ||||
608 | return NULL((void*)0); | ||||
609 | } | ||||
610 | el->node.key = el; | ||||
611 | el->name = memdup(nm, nm_len); | ||||
612 | if(!el->name) { | ||||
613 | free(el); | ||||
614 | return NULL((void*)0); | ||||
615 | } | ||||
616 | el->len = nm_len; | ||||
617 | el->labs = labs; | ||||
618 | return el; | ||||
619 | } | ||||
620 | |||||
621 | /** | ||||
622 | * Create chain of data element and parents | ||||
623 | * @param nm: name | ||||
624 | * @param nm_len: length of name | ||||
625 | * @param labs: labels in name. | ||||
626 | * @param parent: up to where to make, if NULL up to root label. | ||||
627 | * @return lowest element with name nm, or NULL malloc failure. | ||||
628 | */ | ||||
629 | static struct val_neg_data* neg_data_chain( | ||||
630 | uint8_t* nm, size_t nm_len, int labs, struct val_neg_data* parent) | ||||
631 | { | ||||
632 | int i; | ||||
633 | int tolabs = parent?parent->labs:0; | ||||
634 | struct val_neg_data* el, *first = NULL((void*)0), *prev = NULL((void*)0); | ||||
635 | |||||
636 | /* create the new subtree, i is labelcount of current creation */ | ||||
637 | /* this creates a 'first' to z->parent=NULL list of zones */ | ||||
638 | for(i=labs; i!=tolabs; i--) { | ||||
639 | /* create new item */ | ||||
640 | el = neg_setup_data_node(nm, nm_len, i); | ||||
641 | if(!el) { | ||||
642 | /* need to delete other allocations in this routine!*/ | ||||
643 | struct val_neg_data* p = first, *np; | ||||
644 | while(p) { | ||||
645 | np = p->parent; | ||||
646 | free(p->name); | ||||
647 | free(p); | ||||
648 | p = np; | ||||
649 | } | ||||
650 | return NULL((void*)0); | ||||
651 | } | ||||
652 | if(i == labs) { | ||||
653 | first = el; | ||||
654 | } else { | ||||
655 | prev->parent = el; | ||||
656 | } | ||||
657 | |||||
658 | /* prepare for next name */ | ||||
659 | prev = el; | ||||
660 | dname_remove_label(&nm, &nm_len); | ||||
661 | } | ||||
662 | return first; | ||||
663 | } | ||||
664 | |||||
665 | /** | ||||
666 | * Remove NSEC records between start and end points. | ||||
667 | * By walking the tree, the tree is sorted canonically. | ||||
668 | * @param neg: negative cache. | ||||
669 | * @param zone: the zone | ||||
670 | * @param el: element to start walking at. | ||||
671 | * @param nsec: the nsec record with the end point | ||||
672 | */ | ||||
673 | static void wipeout(struct val_neg_cache* neg, struct val_neg_zone* zone, | ||||
674 | struct val_neg_data* el, struct ub_packed_rrset_key* nsec) | ||||
675 | { | ||||
676 | struct packed_rrset_data* d = (struct packed_rrset_data*)nsec-> | ||||
677 | entry.data; | ||||
678 | uint8_t* end; | ||||
679 | size_t end_len; | ||||
680 | int end_labs, m; | ||||
681 | rbnode_type* walk, *next; | ||||
682 | struct val_neg_data* cur; | ||||
683 | uint8_t buf[257]; | ||||
684 | /* get endpoint */ | ||||
685 | if(!d || d->count == 0 || d->rr_len[0] < 2+1) | ||||
686 | return; | ||||
687 | if(ntohs(nsec->rk.type)(__uint16_t)(__builtin_constant_p(nsec->rk.type) ? (__uint16_t )(((__uint16_t)(nsec->rk.type) & 0xffU) << 8 | ( (__uint16_t)(nsec->rk.type) & 0xff00U) >> 8) : __swap16md (nsec->rk.type)) == LDNS_RR_TYPE_NSEC) { | ||||
688 | end = d->rr_data[0]+2; | ||||
689 | end_len = dname_valid(end, d->rr_len[0]-2); | ||||
690 | end_labs = dname_count_labels(end); | ||||
691 | } else { | ||||
692 | /* NSEC3 */ | ||||
693 | if(!nsec3_get_nextowner_b32(nsec, 0, buf, sizeof(buf))) | ||||
694 | return; | ||||
695 | end = buf; | ||||
696 | end_labs = dname_count_size_labels(end, &end_len); | ||||
697 | } | ||||
698 | |||||
699 | /* sanity check, both owner and end must be below the zone apex */ | ||||
700 | if(!dname_subdomain_c(el->name, zone->name) || | ||||
701 | !dname_subdomain_c(end, zone->name)) | ||||
702 | return; | ||||
703 | |||||
704 | /* detect end of zone NSEC ; wipe until the end of zone */ | ||||
705 | if(query_dname_compare(end, zone->name) == 0) { | ||||
706 | end = NULL((void*)0); | ||||
707 | } | ||||
708 | |||||
709 | walk = rbtree_next(&el->node); | ||||
710 | while(walk && walk != RBTREE_NULL&rbtree_null_node) { | ||||
711 | cur = (struct val_neg_data*)walk; | ||||
712 | /* sanity check: must be larger than start */ | ||||
713 | if(dname_canon_lab_cmp(cur->name, cur->labs, | ||||
714 | el->name, el->labs, &m) <= 0) { | ||||
715 | /* r == 0 skip original record. */ | ||||
716 | /* r < 0 too small! */ | ||||
717 | walk = rbtree_next(walk); | ||||
718 | continue; | ||||
719 | } | ||||
720 | /* stop at endpoint, also data at empty nonterminals must be | ||||
721 | * removed (no NSECs there) so everything between | ||||
722 | * start and end */ | ||||
723 | if(end && dname_canon_lab_cmp(cur->name, cur->labs, | ||||
724 | end, end_labs, &m) >= 0) { | ||||
725 | break; | ||||
726 | } | ||||
727 | /* this element has to be deleted, but we cannot do it | ||||
728 | * now, because we are walking the tree still ... */ | ||||
729 | /* get the next element: */ | ||||
730 | next = rbtree_next(walk); | ||||
731 | /* now delete the original element, this may trigger | ||||
732 | * rbtree rebalances, but really, the next element is | ||||
733 | * the one we need. | ||||
734 | * But it may trigger delete of other data and the | ||||
735 | * entire zone. However, if that happens, this is done | ||||
736 | * by deleting the *parents* of the element for deletion, | ||||
737 | * and maybe also the entire zone if it is empty. | ||||
738 | * But parents are smaller in canonical compare, thus, | ||||
739 | * if a larger element exists, then it is not a parent, | ||||
740 | * it cannot get deleted, the zone cannot get empty. | ||||
741 | * If the next==NULL, then zone can be empty. */ | ||||
742 | if(cur->in_use) | ||||
743 | neg_delete_data(neg, cur); | ||||
744 | walk = next; | ||||
745 | } | ||||
746 | } | ||||
747 | |||||
748 | void neg_insert_data(struct val_neg_cache* neg, | ||||
749 | struct val_neg_zone* zone, struct ub_packed_rrset_key* nsec) | ||||
750 | { | ||||
751 | struct packed_rrset_data* d; | ||||
752 | struct val_neg_data* parent; | ||||
753 | struct val_neg_data* el; | ||||
754 | uint8_t* nm = nsec->rk.dname; | ||||
755 | size_t nm_len = nsec->rk.dname_len; | ||||
756 | int labs = dname_count_labels(nsec->rk.dname); | ||||
757 | |||||
758 | d = (struct packed_rrset_data*)nsec->entry.data; | ||||
759 | if( !(d->security == sec_status_secure || | ||||
760 | (d->security == sec_status_unchecked && d->rrsig_count > 0))) | ||||
761 | return; | ||||
762 | log_nametypeclass(VERB_ALGO, "negcache rr", | ||||
763 | nsec->rk.dname, ntohs(nsec->rk.type)(__uint16_t)(__builtin_constant_p(nsec->rk.type) ? (__uint16_t )(((__uint16_t)(nsec->rk.type) & 0xffU) << 8 | ( (__uint16_t)(nsec->rk.type) & 0xff00U) >> 8) : __swap16md (nsec->rk.type)), | ||||
764 | ntohs(nsec->rk.rrset_class)(__uint16_t)(__builtin_constant_p(nsec->rk.rrset_class) ? ( __uint16_t)(((__uint16_t)(nsec->rk.rrset_class) & 0xffU ) << 8 | ((__uint16_t)(nsec->rk.rrset_class) & 0xff00U ) >> 8) : __swap16md(nsec->rk.rrset_class))); | ||||
765 | |||||
766 | /* find closest enclosing parent data that (still) exists */ | ||||
767 | parent = neg_closest_data_parent(zone, nm, nm_len, labs); | ||||
768 | if(parent && query_dname_compare(parent->name, nm) == 0) { | ||||
769 | /* perfect match already exists */ | ||||
770 | log_assert(parent->count > 0); | ||||
771 | el = parent; | ||||
772 | } else { | ||||
773 | struct val_neg_data* p, *np; | ||||
774 | |||||
775 | /* create subtree for perfect match */ | ||||
776 | /* if parent exists, it is in use */ | ||||
777 | log_assert(!parent || parent->count > 0); | ||||
778 | |||||
779 | el = neg_data_chain(nm, nm_len, labs, parent); | ||||
780 | if(!el) { | ||||
781 | log_err("out of memory inserting NSEC negative cache"); | ||||
782 | return; | ||||
783 | } | ||||
784 | el->in_use = 0; /* set on below */ | ||||
785 | |||||
786 | /* insert the list of zones into the tree */ | ||||
787 | p = el; | ||||
788 | while(p) { | ||||
789 | np = p->parent; | ||||
790 | /* mem use */ | ||||
791 | neg->use += sizeof(struct val_neg_data) + p->len; | ||||
792 | /* insert in tree */ | ||||
793 | p->zone = zone; | ||||
794 | (void)rbtree_insert(&zone->tree, &p->node); | ||||
795 | /* last one needs proper parent pointer */ | ||||
796 | if(np == NULL((void*)0)) | ||||
797 | p->parent = parent; | ||||
798 | p = np; | ||||
799 | } | ||||
800 | } | ||||
801 | |||||
802 | if(!el->in_use) { | ||||
803 | struct val_neg_data* p; | ||||
804 | |||||
805 | el->in_use = 1; | ||||
806 | /* increase usage count of all parents */ | ||||
807 | for(p=el; p; p = p->parent) { | ||||
808 | p->count++; | ||||
809 | } | ||||
810 | |||||
811 | neg_lru_front(neg, el); | ||||
812 | } else { | ||||
813 | /* in use, bring to front, lru */ | ||||
814 | neg_lru_touch(neg, el); | ||||
815 | } | ||||
816 | |||||
817 | /* if nsec3 store last used parameters */ | ||||
818 | if(ntohs(nsec->rk.type)(__uint16_t)(__builtin_constant_p(nsec->rk.type) ? (__uint16_t )(((__uint16_t)(nsec->rk.type) & 0xffU) << 8 | ( (__uint16_t)(nsec->rk.type) & 0xff00U) >> 8) : __swap16md (nsec->rk.type)) == LDNS_RR_TYPE_NSEC3) { | ||||
819 | int h; | ||||
820 | uint8_t* s; | ||||
821 | size_t slen, it; | ||||
822 | if(nsec3_get_params(nsec, 0, &h, &it, &s, &slen) && | ||||
823 | it <= neg->nsec3_max_iter && | ||||
824 | (h != zone->nsec3_hash || it != zone->nsec3_iter || | ||||
825 | slen != zone->nsec3_saltlen || | ||||
826 | memcmp(zone->nsec3_salt, s, slen) != 0)) { | ||||
827 | |||||
828 | if(slen > 0) { | ||||
829 | uint8_t* sa = memdup(s, slen); | ||||
830 | if(sa) { | ||||
831 | free(zone->nsec3_salt); | ||||
832 | zone->nsec3_salt = sa; | ||||
833 | zone->nsec3_saltlen = slen; | ||||
834 | zone->nsec3_iter = it; | ||||
835 | zone->nsec3_hash = h; | ||||
836 | } | ||||
837 | } else { | ||||
838 | free(zone->nsec3_salt); | ||||
839 | zone->nsec3_salt = NULL((void*)0); | ||||
840 | zone->nsec3_saltlen = 0; | ||||
841 | zone->nsec3_iter = it; | ||||
842 | zone->nsec3_hash = h; | ||||
843 | } | ||||
844 | } | ||||
845 | } | ||||
846 | |||||
847 | /* wipe out the cache items between NSEC start and end */ | ||||
848 | wipeout(neg, zone, el, nsec); | ||||
849 | } | ||||
850 | |||||
851 | /** see if the reply has signed NSEC records and return the signer */ | ||||
852 | static uint8_t* reply_nsec_signer(struct reply_info* rep, size_t* signer_len, | ||||
853 | uint16_t* dclass) | ||||
854 | { | ||||
855 | size_t i; | ||||
856 | struct packed_rrset_data* d; | ||||
857 | uint8_t* s; | ||||
858 | for(i=rep->an_numrrsets; i< rep->an_numrrsets+rep->ns_numrrsets; i++){ | ||||
859 | if(ntohs(rep->rrsets[i]->rk.type)(__uint16_t)(__builtin_constant_p(rep->rrsets[i]->rk.type ) ? (__uint16_t)(((__uint16_t)(rep->rrsets[i]->rk.type) & 0xffU) << 8 | ((__uint16_t)(rep->rrsets[i]-> rk.type) & 0xff00U) >> 8) : __swap16md(rep->rrsets [i]->rk.type)) == LDNS_RR_TYPE_NSEC || | ||||
860 | ntohs(rep->rrsets[i]->rk.type)(__uint16_t)(__builtin_constant_p(rep->rrsets[i]->rk.type ) ? (__uint16_t)(((__uint16_t)(rep->rrsets[i]->rk.type) & 0xffU) << 8 | ((__uint16_t)(rep->rrsets[i]-> rk.type) & 0xff00U) >> 8) : __swap16md(rep->rrsets [i]->rk.type)) == LDNS_RR_TYPE_NSEC3) { | ||||
861 | d = (struct packed_rrset_data*)rep->rrsets[i]-> | ||||
862 | entry.data; | ||||
863 | /* return first signer name of first NSEC */ | ||||
864 | if(d->rrsig_count != 0) { | ||||
865 | val_find_rrset_signer(rep->rrsets[i], | ||||
866 | &s, signer_len); | ||||
867 | if(s && *signer_len) { | ||||
868 | *dclass = ntohs(rep->rrsets[i]->(__uint16_t)(__builtin_constant_p(rep->rrsets[i]-> rk.rrset_class ) ? (__uint16_t)(((__uint16_t)(rep->rrsets[i]-> rk.rrset_class ) & 0xffU) << 8 | ((__uint16_t)(rep->rrsets[i]-> rk.rrset_class) & 0xff00U) >> 8) : __swap16md(rep-> rrsets[i]-> rk.rrset_class)) | ||||
869 | rk.rrset_class)(__uint16_t)(__builtin_constant_p(rep->rrsets[i]-> rk.rrset_class ) ? (__uint16_t)(((__uint16_t)(rep->rrsets[i]-> rk.rrset_class ) & 0xffU) << 8 | ((__uint16_t)(rep->rrsets[i]-> rk.rrset_class) & 0xff00U) >> 8) : __swap16md(rep-> rrsets[i]-> rk.rrset_class)); | ||||
870 | return s; | ||||
871 | } | ||||
872 | } | ||||
873 | } | ||||
874 | } | ||||
875 | return 0; | ||||
876 | } | ||||
877 | |||||
878 | void val_neg_addreply(struct val_neg_cache* neg, struct reply_info* rep) | ||||
879 | { | ||||
880 | size_t i, need; | ||||
881 | struct ub_packed_rrset_key* soa; | ||||
882 | uint8_t* dname = NULL((void*)0); | ||||
883 | size_t dname_len; | ||||
884 | uint16_t rrset_class; | ||||
885 | struct val_neg_zone* zone; | ||||
886 | /* see if secure nsecs inside */ | ||||
887 | if(!reply_has_nsec(rep)) | ||||
888 | return; | ||||
889 | /* find the zone name in message */ | ||||
890 | if((soa = reply_find_soa(rep))) { | ||||
891 | dname = soa->rk.dname; | ||||
892 | dname_len = soa->rk.dname_len; | ||||
893 | rrset_class = ntohs(soa->rk.rrset_class)(__uint16_t)(__builtin_constant_p(soa->rk.rrset_class) ? ( __uint16_t)(((__uint16_t)(soa->rk.rrset_class) & 0xffU ) << 8 | ((__uint16_t)(soa->rk.rrset_class) & 0xff00U ) >> 8) : __swap16md(soa->rk.rrset_class)); | ||||
894 | } | ||||
895 | else { | ||||
896 | /* No SOA in positive (wildcard) answer. Use signer from the | ||||
897 | * validated answer RRsets' signature. */ | ||||
898 | if(!(dname = reply_nsec_signer(rep, &dname_len, &rrset_class))) | ||||
899 | return; | ||||
900 | } | ||||
901 | |||||
902 | log_nametypeclass(VERB_ALGO, "negcache insert for zone", | ||||
903 | dname, LDNS_RR_TYPE_SOA, rrset_class); | ||||
904 | |||||
905 | /* ask for enough space to store all of it */ | ||||
906 | need = calc_data_need(rep) + | ||||
907 | calc_zone_need(dname, dname_len); | ||||
908 | lock_basic_lock(&neg->lock); | ||||
909 | neg_make_space(neg, need); | ||||
910 | |||||
911 | /* find or create the zone entry */ | ||||
912 | zone = neg_find_zone(neg, dname, dname_len, rrset_class); | ||||
913 | if(!zone) { | ||||
914 | if(!(zone = neg_create_zone(neg, dname, dname_len, | ||||
915 | rrset_class))) { | ||||
916 | lock_basic_unlock(&neg->lock); | ||||
917 | log_err("out of memory adding negative zone"); | ||||
918 | return; | ||||
919 | } | ||||
920 | } | ||||
921 | val_neg_zone_take_inuse(zone); | ||||
922 | |||||
923 | /* insert the NSECs */ | ||||
924 | for(i=rep->an_numrrsets; i< rep->an_numrrsets+rep->ns_numrrsets; i++){ | ||||
925 | if(ntohs(rep->rrsets[i]->rk.type)(__uint16_t)(__builtin_constant_p(rep->rrsets[i]->rk.type ) ? (__uint16_t)(((__uint16_t)(rep->rrsets[i]->rk.type) & 0xffU) << 8 | ((__uint16_t)(rep->rrsets[i]-> rk.type) & 0xff00U) >> 8) : __swap16md(rep->rrsets [i]->rk.type)) != LDNS_RR_TYPE_NSEC) | ||||
926 | continue; | ||||
927 | if(!dname_subdomain_c(rep->rrsets[i]->rk.dname, | ||||
928 | zone->name)) continue; | ||||
929 | /* insert NSEC into this zone's tree */ | ||||
930 | neg_insert_data(neg, zone, rep->rrsets[i]); | ||||
931 | } | ||||
932 | if(zone->tree.count == 0) { | ||||
933 | /* remove empty zone if inserts failed */ | ||||
934 | neg_delete_zone(neg, zone); | ||||
935 | } | ||||
936 | lock_basic_unlock(&neg->lock); | ||||
937 | } | ||||
938 | |||||
939 | /** | ||||
940 | * Lookup closest data record. For NSEC denial. | ||||
941 | * @param zone: zone to look in | ||||
942 | * @param qname: name to look for. | ||||
943 | * @param len: length of name | ||||
944 | * @param labs: labels in name | ||||
945 | * @param data: data element, exact or smaller or NULL | ||||
946 | * @return true if exact match. | ||||
947 | */ | ||||
948 | static int neg_closest_data(struct val_neg_zone* zone, | ||||
949 | uint8_t* qname, size_t len, int labs, struct val_neg_data** data) | ||||
950 | { | ||||
951 | struct val_neg_data key; | ||||
952 | rbnode_type* r; | ||||
953 | key.node.key = &key; | ||||
954 | key.name = qname; | ||||
955 | key.len = len; | ||||
956 | key.labs = labs; | ||||
957 | if(rbtree_find_less_equal(&zone->tree, &key, &r)) { | ||||
958 | /* exact match */ | ||||
959 | *data = (struct val_neg_data*)r; | ||||
960 | return 1; | ||||
961 | } else { | ||||
962 | /* smaller match */ | ||||
963 | *data = (struct val_neg_data*)r; | ||||
964 | return 0; | ||||
965 | } | ||||
966 | } | ||||
967 | |||||
968 | void val_neg_addreferral(struct val_neg_cache* neg, struct reply_info* rep, | ||||
969 | uint8_t* zone_name) | ||||
970 | { | ||||
971 | size_t i, need; | ||||
972 | uint8_t* signer; | ||||
973 | size_t signer_len; | ||||
974 | uint16_t dclass; | ||||
975 | struct val_neg_zone* zone; | ||||
976 | /* no SOA in this message, find RRSIG over NSEC's signer name. | ||||
977 | * note the NSEC records are maybe not validated yet */ | ||||
978 | signer = reply_nsec_signer(rep, &signer_len, &dclass); | ||||
979 | if(!signer) | ||||
| |||||
980 | return; | ||||
981 | if(!dname_subdomain_c(signer, zone_name)) { | ||||
982 | /* the signer is not in the bailiwick, throw it out */ | ||||
983 | return; | ||||
984 | } | ||||
985 | |||||
986 | log_nametypeclass(VERB_ALGO, "negcache insert referral ", | ||||
987 | signer, LDNS_RR_TYPE_NS, dclass); | ||||
988 | |||||
989 | /* ask for enough space to store all of it */ | ||||
990 | need = calc_data_need(rep) + calc_zone_need(signer, signer_len); | ||||
991 | lock_basic_lock(&neg->lock); | ||||
992 | neg_make_space(neg, need); | ||||
993 | |||||
994 | /* find or create the zone entry */ | ||||
995 | zone = neg_find_zone(neg, signer, signer_len, dclass); | ||||
996 | if(!zone) { | ||||
997 | if(!(zone = neg_create_zone(neg, signer, signer_len, | ||||
998 | dclass))) { | ||||
999 | lock_basic_unlock(&neg->lock); | ||||
1000 | log_err("out of memory adding negative zone"); | ||||
1001 | return; | ||||
1002 | } | ||||
1003 | } | ||||
1004 | val_neg_zone_take_inuse(zone); | ||||
1005 | |||||
1006 | /* insert the NSECs */ | ||||
1007 | for(i=rep->an_numrrsets; i< rep->an_numrrsets+rep->ns_numrrsets; i++){ | ||||
1008 | if(ntohs(rep->rrsets[i]->rk.type)(__uint16_t)(__builtin_constant_p(rep->rrsets[i]->rk.type ) ? (__uint16_t)(((__uint16_t)(rep->rrsets[i]->rk.type) & 0xffU) << 8 | ((__uint16_t)(rep->rrsets[i]-> rk.type) & 0xff00U) >> 8) : __swap16md(rep->rrsets [i]->rk.type)) != LDNS_RR_TYPE_NSEC && | ||||
1009 | ntohs(rep->rrsets[i]->rk.type)(__uint16_t)(__builtin_constant_p(rep->rrsets[i]->rk.type ) ? (__uint16_t)(((__uint16_t)(rep->rrsets[i]->rk.type) & 0xffU) << 8 | ((__uint16_t)(rep->rrsets[i]-> rk.type) & 0xff00U) >> 8) : __swap16md(rep->rrsets [i]->rk.type)) != LDNS_RR_TYPE_NSEC3) | ||||
1010 | continue; | ||||
1011 | if(!dname_subdomain_c(rep->rrsets[i]->rk.dname, | ||||
1012 | zone->name)) continue; | ||||
1013 | /* insert NSEC into this zone's tree */ | ||||
1014 | neg_insert_data(neg, zone, rep->rrsets[i]); | ||||
1015 | } | ||||
1016 | if(zone->tree.count == 0) { | ||||
1017 | /* remove empty zone if inserts failed */ | ||||
1018 | neg_delete_zone(neg, zone); | ||||
1019 | } | ||||
1020 | lock_basic_unlock(&neg->lock); | ||||
1021 | } | ||||
1022 | |||||
1023 | /** | ||||
1024 | * Check that an NSEC3 rrset does not have a type set. | ||||
1025 | * None of the nsec3s in a hash-collision are allowed to have the type. | ||||
1026 | * (since we do not know which one is the nsec3 looked at, flags, ..., we | ||||
1027 | * ignore the cached item and let it bypass negative caching). | ||||
1028 | * @param k: the nsec3 rrset to check. | ||||
1029 | * @param t: type to check | ||||
1030 | * @return true if no RRs have the type. | ||||
1031 | */ | ||||
1032 | static int nsec3_no_type(struct ub_packed_rrset_key* k, uint16_t t) | ||||
1033 | { | ||||
1034 | int count = (int)((struct packed_rrset_data*)k->entry.data)->count; | ||||
1035 | int i; | ||||
1036 | for(i=0; i<count; i++) | ||||
1037 | if(nsec3_has_type(k, i, t)) | ||||
1038 | return 0; | ||||
1039 | return 1; | ||||
1040 | } | ||||
1041 | |||||
1042 | /** | ||||
1043 | * See if rrset exists in rrset cache. | ||||
1044 | * If it does, the bit is checked, and if not expired, it is returned | ||||
1045 | * allocated in region. | ||||
1046 | * @param rrset_cache: rrset cache | ||||
1047 | * @param qname: to lookup rrset name | ||||
1048 | * @param qname_len: length of qname. | ||||
1049 | * @param qtype: type of rrset to lookup, host order | ||||
1050 | * @param qclass: class of rrset to lookup, host order | ||||
1051 | * @param flags: flags for rrset to lookup | ||||
1052 | * @param region: where to alloc result | ||||
1053 | * @param checkbit: if true, a bit in the nsec typemap is checked for absence. | ||||
1054 | * @param checktype: which bit to check | ||||
1055 | * @param now: to check ttl against | ||||
1056 | * @return rrset or NULL | ||||
1057 | */ | ||||
1058 | static struct ub_packed_rrset_key* | ||||
1059 | grab_nsec(struct rrset_cache* rrset_cache, uint8_t* qname, size_t qname_len, | ||||
1060 | uint16_t qtype, uint16_t qclass, uint32_t flags, | ||||
1061 | struct regional* region, int checkbit, uint16_t checktype, | ||||
1062 | time_t now) | ||||
1063 | { | ||||
1064 | struct ub_packed_rrset_key* r, *k = rrset_cache_lookup(rrset_cache, | ||||
1065 | qname, qname_len, qtype, qclass, flags, now, 0); | ||||
1066 | struct packed_rrset_data* d; | ||||
1067 | if(!k) return NULL((void*)0); | ||||
1068 | d = (struct packed_rrset_data*)k->entry.data; | ||||
1069 | if(d->ttl < now) { | ||||
1070 | lock_rw_unlock(&k->entry.lock); | ||||
1071 | return NULL((void*)0); | ||||
1072 | } | ||||
1073 | /* only secure or unchecked records that have signatures. */ | ||||
1074 | if( ! ( d->security == sec_status_secure || | ||||
1075 | (d->security == sec_status_unchecked && | ||||
1076 | d->rrsig_count > 0) ) ) { | ||||
1077 | lock_rw_unlock(&k->entry.lock); | ||||
1078 | return NULL((void*)0); | ||||
1079 | } | ||||
1080 | /* check if checktype is absent */ | ||||
1081 | if(checkbit && ( | ||||
1082 | (qtype == LDNS_RR_TYPE_NSEC && nsec_has_type(k, checktype)) || | ||||
1083 | (qtype == LDNS_RR_TYPE_NSEC3 && !nsec3_no_type(k, checktype)) | ||||
1084 | )) { | ||||
1085 | lock_rw_unlock(&k->entry.lock); | ||||
1086 | return NULL((void*)0); | ||||
1087 | } | ||||
1088 | /* looks OK! copy to region and return it */ | ||||
1089 | r = packed_rrset_copy_region(k, region, now); | ||||
1090 | /* if it failed, we return the NULL */ | ||||
1091 | lock_rw_unlock(&k->entry.lock); | ||||
1092 | return r; | ||||
1093 | } | ||||
1094 | |||||
1095 | /** | ||||
1096 | * Get best NSEC record for qname. Might be matching, covering or totally | ||||
1097 | * useless. | ||||
1098 | * @param neg_cache: neg cache | ||||
1099 | * @param qname: to lookup rrset name | ||||
1100 | * @param qname_len: length of qname. | ||||
1101 | * @param qclass: class of rrset to lookup, host order | ||||
1102 | * @param rrset_cache: rrset cache | ||||
1103 | * @param now: to check ttl against | ||||
1104 | * @param region: where to alloc result | ||||
1105 | * @return rrset or NULL | ||||
1106 | */ | ||||
1107 | static struct ub_packed_rrset_key* | ||||
1108 | neg_find_nsec(struct val_neg_cache* neg_cache, uint8_t* qname, size_t qname_len, | ||||
1109 | uint16_t qclass, struct rrset_cache* rrset_cache, time_t now, | ||||
1110 | struct regional* region) | ||||
1111 | { | ||||
1112 | int labs; | ||||
1113 | uint32_t flags; | ||||
1114 | struct val_neg_zone* zone; | ||||
1115 | struct val_neg_data* data; | ||||
1116 | struct ub_packed_rrset_key* nsec; | ||||
1117 | |||||
1118 | labs = dname_count_labels(qname); | ||||
1119 | lock_basic_lock(&neg_cache->lock); | ||||
1120 | zone = neg_closest_zone_parent(neg_cache, qname, qname_len, labs, | ||||
1121 | qclass); | ||||
1122 | while(zone && !zone->in_use) | ||||
1123 | zone = zone->parent; | ||||
1124 | if(!zone) { | ||||
1125 | lock_basic_unlock(&neg_cache->lock); | ||||
1126 | return NULL((void*)0); | ||||
1127 | } | ||||
1128 | |||||
1129 | /* NSEC only for now */ | ||||
1130 | if(zone->nsec3_hash) { | ||||
1131 | lock_basic_unlock(&neg_cache->lock); | ||||
1132 | return NULL((void*)0); | ||||
1133 | } | ||||
1134 | |||||
1135 | /* ignore return value, don't care if it is an exact or smaller match */ | ||||
1136 | (void)neg_closest_data(zone, qname, qname_len, labs, &data); | ||||
1137 | if(!data) { | ||||
1138 | lock_basic_unlock(&neg_cache->lock); | ||||
1139 | return NULL((void*)0); | ||||
1140 | } | ||||
1141 | |||||
1142 | /* ENT nodes are not in use, try the previous node. If the previous node | ||||
1143 | * is not in use, we don't have an useful NSEC and give up. */ | ||||
1144 | if(!data->in_use) { | ||||
1145 | data = (struct val_neg_data*)rbtree_previous((rbnode_type*)data); | ||||
1146 | if((rbnode_type*)data == RBTREE_NULL&rbtree_null_node || !data->in_use) { | ||||
1147 | lock_basic_unlock(&neg_cache->lock); | ||||
1148 | return NULL((void*)0); | ||||
1149 | } | ||||
1150 | } | ||||
1151 | |||||
1152 | flags = 0; | ||||
1153 | if(query_dname_compare(data->name, zone->name) == 0) | ||||
1154 | flags = PACKED_RRSET_NSEC_AT_APEX0x1; | ||||
1155 | |||||
1156 | nsec = grab_nsec(rrset_cache, data->name, data->len, LDNS_RR_TYPE_NSEC, | ||||
1157 | zone->dclass, flags, region, 0, 0, now); | ||||
1158 | lock_basic_unlock(&neg_cache->lock); | ||||
1159 | return nsec; | ||||
1160 | } | ||||
1161 | |||||
1162 | /** find nsec3 closest encloser in neg cache */ | ||||
1163 | static struct val_neg_data* | ||||
1164 | neg_find_nsec3_ce(struct val_neg_zone* zone, uint8_t* qname, size_t qname_len, | ||||
1165 | int qlabs, sldns_buffer* buf, uint8_t* hashnc, size_t* nclen) | ||||
1166 | { | ||||
1167 | struct val_neg_data* data; | ||||
1168 | uint8_t hashce[NSEC3_SHA_LEN20]; | ||||
1169 | uint8_t b32[257]; | ||||
1170 | size_t celen, b32len; | ||||
1171 | |||||
1172 | *nclen = 0; | ||||
1173 | while(qlabs > 0) { | ||||
1174 | /* hash */ | ||||
1175 | if(!(celen=nsec3_get_hashed(buf, qname, qname_len, | ||||
1176 | zone->nsec3_hash, zone->nsec3_iter, zone->nsec3_salt, | ||||
1177 | zone->nsec3_saltlen, hashce, sizeof(hashce)))) | ||||
1178 | return NULL((void*)0); | ||||
1179 | if(!(b32len=nsec3_hash_to_b32(hashce, celen, zone->name, | ||||
1180 | zone->len, b32, sizeof(b32)))) | ||||
1181 | return NULL((void*)0); | ||||
1182 | |||||
1183 | /* lookup (exact match only) */ | ||||
1184 | data = neg_find_data(zone, b32, b32len, zone->labs+1); | ||||
1185 | if(data && data->in_use) { | ||||
1186 | /* found ce match! */ | ||||
1187 | return data; | ||||
1188 | } | ||||
1189 | |||||
1190 | *nclen = celen; | ||||
1191 | memmove(hashnc, hashce, celen); | ||||
1192 | dname_remove_label(&qname, &qname_len); | ||||
1193 | qlabs --; | ||||
1194 | } | ||||
1195 | return NULL((void*)0); | ||||
1196 | } | ||||
1197 | |||||
1198 | /** check nsec3 parameters on nsec3 rrset with current zone values */ | ||||
1199 | static int | ||||
1200 | neg_params_ok(struct val_neg_zone* zone, struct ub_packed_rrset_key* rrset) | ||||
1201 | { | ||||
1202 | int h; | ||||
1203 | uint8_t* s; | ||||
1204 | size_t slen, it; | ||||
1205 | if(!nsec3_get_params(rrset, 0, &h, &it, &s, &slen)) | ||||
1206 | return 0; | ||||
1207 | return (h == zone->nsec3_hash && it == zone->nsec3_iter && | ||||
1208 | slen == zone->nsec3_saltlen && | ||||
1209 | memcmp(zone->nsec3_salt, s, slen) == 0); | ||||
1210 | } | ||||
1211 | |||||
1212 | /** get next closer for nsec3 proof */ | ||||
1213 | static struct ub_packed_rrset_key* | ||||
1214 | neg_nsec3_getnc(struct val_neg_zone* zone, uint8_t* hashnc, size_t nclen, | ||||
1215 | struct rrset_cache* rrset_cache, struct regional* region, | ||||
1216 | time_t now, uint8_t* b32, size_t maxb32) | ||||
1217 | { | ||||
1218 | struct ub_packed_rrset_key* nc_rrset; | ||||
1219 | struct val_neg_data* data; | ||||
1220 | size_t b32len; | ||||
1221 | |||||
1222 | if(!(b32len=nsec3_hash_to_b32(hashnc, nclen, zone->name, | ||||
1223 | zone->len, b32, maxb32))) | ||||
1224 | return NULL((void*)0); | ||||
1225 | (void)neg_closest_data(zone, b32, b32len, zone->labs+1, &data); | ||||
1226 | if(!data && zone->tree.count != 0) { | ||||
1227 | /* could be before the first entry ; return the last | ||||
1228 | * entry (possibly the rollover nsec3 at end) */ | ||||
1229 | data = (struct val_neg_data*)rbtree_last(&zone->tree); | ||||
1230 | } | ||||
1231 | while(data && !data->in_use) | ||||
1232 | data = data->parent; | ||||
1233 | if(!data) | ||||
1234 | return NULL((void*)0); | ||||
1235 | /* got a data element in tree, grab it */ | ||||
1236 | nc_rrset = grab_nsec(rrset_cache, data->name, data->len, | ||||
1237 | LDNS_RR_TYPE_NSEC3, zone->dclass, 0, region, 0, 0, now); | ||||
1238 | if(!nc_rrset) | ||||
1239 | return NULL((void*)0); | ||||
1240 | if(!neg_params_ok(zone, nc_rrset)) | ||||
1241 | return NULL((void*)0); | ||||
1242 | return nc_rrset; | ||||
1243 | } | ||||
1244 | |||||
1245 | /** neg cache nsec3 proof procedure*/ | ||||
1246 | static struct dns_msg* | ||||
1247 | neg_nsec3_proof_ds(struct val_neg_zone* zone, uint8_t* qname, size_t qname_len, | ||||
1248 | int qlabs, sldns_buffer* buf, struct rrset_cache* rrset_cache, | ||||
1249 | struct regional* region, time_t now, uint8_t* topname) | ||||
1250 | { | ||||
1251 | struct dns_msg* msg; | ||||
1252 | struct val_neg_data* data; | ||||
1253 | uint8_t hashnc[NSEC3_SHA_LEN20]; | ||||
1254 | size_t nclen; | ||||
1255 | struct ub_packed_rrset_key* ce_rrset, *nc_rrset; | ||||
1256 | struct nsec3_cached_hash c; | ||||
1257 | uint8_t nc_b32[257]; | ||||
1258 | |||||
1259 | /* for NSEC3 ; determine the closest encloser for which we | ||||
1260 | * can find an exact match. Remember the hashed lower name, | ||||
1261 | * since that is the one we need a closest match for. | ||||
1262 | * If we find a match straight away, then it becomes NODATA. | ||||
1263 | * Otherwise, NXDOMAIN or if OPTOUT, an insecure delegation. | ||||
1264 | * Also check that parameters are the same on closest encloser | ||||
1265 | * and on closest match. | ||||
1266 | */ | ||||
1267 | if(!zone->nsec3_hash) | ||||
1268 | return NULL((void*)0); /* not nsec3 zone */ | ||||
1269 | |||||
1270 | if(!(data=neg_find_nsec3_ce(zone, qname, qname_len, qlabs, buf, | ||||
1271 | hashnc, &nclen))) { | ||||
1272 | return NULL((void*)0); | ||||
1273 | } | ||||
1274 | |||||
1275 | /* grab the ce rrset */ | ||||
1276 | ce_rrset = grab_nsec(rrset_cache, data->name, data->len, | ||||
1277 | LDNS_RR_TYPE_NSEC3, zone->dclass, 0, region, 1, | ||||
1278 | LDNS_RR_TYPE_DS, now); | ||||
1279 | if(!ce_rrset) | ||||
1280 | return NULL((void*)0); | ||||
1281 | if(!neg_params_ok(zone, ce_rrset)) | ||||
1282 | return NULL((void*)0); | ||||
1283 | |||||
1284 | if(nclen == 0) { | ||||
1285 | /* exact match, just check the type bits */ | ||||
1286 | /* need: -SOA, -DS, +NS */ | ||||
1287 | if(nsec3_has_type(ce_rrset, 0, LDNS_RR_TYPE_SOA) || | ||||
1288 | nsec3_has_type(ce_rrset, 0, LDNS_RR_TYPE_DS) || | ||||
1289 | !nsec3_has_type(ce_rrset, 0, LDNS_RR_TYPE_NS)) | ||||
1290 | return NULL((void*)0); | ||||
1291 | if(!(msg = dns_msg_create(qname, qname_len, | ||||
1292 | LDNS_RR_TYPE_DS, zone->dclass, region, 1))) | ||||
1293 | return NULL((void*)0); | ||||
1294 | /* TTL reduced in grab_nsec */ | ||||
1295 | if(!dns_msg_authadd(msg, region, ce_rrset, 0)) | ||||
1296 | return NULL((void*)0); | ||||
1297 | return msg; | ||||
1298 | } | ||||
1299 | |||||
1300 | /* optout is not allowed without knowing the trust-anchor in use, | ||||
1301 | * otherwise the optout could spoof away that anchor */ | ||||
1302 | if(!topname) | ||||
1303 | return NULL((void*)0); | ||||
1304 | |||||
1305 | /* if there is no exact match, it must be in an optout span | ||||
1306 | * (an existing DS implies an NSEC3 must exist) */ | ||||
1307 | nc_rrset = neg_nsec3_getnc(zone, hashnc, nclen, rrset_cache, | ||||
1308 | region, now, nc_b32, sizeof(nc_b32)); | ||||
1309 | if(!nc_rrset) | ||||
1310 | return NULL((void*)0); | ||||
1311 | if(!neg_params_ok(zone, nc_rrset)) | ||||
1312 | return NULL((void*)0); | ||||
1313 | if(!nsec3_has_optout(nc_rrset, 0)) | ||||
1314 | return NULL((void*)0); | ||||
1315 | c.hash = hashnc; | ||||
1316 | c.hash_len = nclen; | ||||
1317 | c.b32 = nc_b32+1; | ||||
1318 | c.b32_len = (size_t)nc_b32[0]; | ||||
1319 | if(nsec3_covers(zone->name, &c, nc_rrset, 0, buf)) { | ||||
1320 | /* nc_rrset covers the next closer name. | ||||
1321 | * ce_rrset equals a closer encloser. | ||||
1322 | * nc_rrset is optout. | ||||
1323 | * No need to check wildcard for type DS */ | ||||
1324 | /* capacity=3: ce + nc + soa(if needed) */ | ||||
1325 | if(!(msg = dns_msg_create(qname, qname_len, | ||||
1326 | LDNS_RR_TYPE_DS, zone->dclass, region, 3))) | ||||
1327 | return NULL((void*)0); | ||||
1328 | /* now=0 because TTL was reduced in grab_nsec */ | ||||
1329 | if(!dns_msg_authadd(msg, region, ce_rrset, 0)) | ||||
1330 | return NULL((void*)0); | ||||
1331 | if(!dns_msg_authadd(msg, region, nc_rrset, 0)) | ||||
1332 | return NULL((void*)0); | ||||
1333 | return msg; | ||||
1334 | } | ||||
1335 | return NULL((void*)0); | ||||
1336 | } | ||||
1337 | |||||
1338 | /** | ||||
1339 | * Add SOA record for external responses. | ||||
1340 | * @param rrset_cache: to look into. | ||||
1341 | * @param now: current time. | ||||
1342 | * @param region: where to perform the allocation | ||||
1343 | * @param msg: current msg with NSEC. | ||||
1344 | * @param zone: val_neg_zone if we have one. | ||||
1345 | * @return false on lookup or alloc failure. | ||||
1346 | */ | ||||
1347 | static int add_soa(struct rrset_cache* rrset_cache, time_t now, | ||||
1348 | struct regional* region, struct dns_msg* msg, struct val_neg_zone* zone) | ||||
1349 | { | ||||
1350 | struct ub_packed_rrset_key* soa; | ||||
1351 | uint8_t* nm; | ||||
1352 | size_t nmlen; | ||||
1353 | uint16_t dclass; | ||||
1354 | if(zone) { | ||||
1355 | nm = zone->name; | ||||
1356 | nmlen = zone->len; | ||||
1357 | dclass = zone->dclass; | ||||
1358 | } else { | ||||
1359 | /* Assumes the signer is the zone SOA to add */ | ||||
1360 | nm = reply_nsec_signer(msg->rep, &nmlen, &dclass); | ||||
1361 | if(!nm) | ||||
1362 | return 0; | ||||
1363 | } | ||||
1364 | soa = rrset_cache_lookup(rrset_cache, nm, nmlen, LDNS_RR_TYPE_SOA, | ||||
1365 | dclass, PACKED_RRSET_SOA_NEG0x4, now, 0); | ||||
1366 | if(!soa) | ||||
1367 | return 0; | ||||
1368 | if(!dns_msg_authadd(msg, region, soa, now)) { | ||||
1369 | lock_rw_unlock(&soa->entry.lock); | ||||
1370 | return 0; | ||||
1371 | } | ||||
1372 | lock_rw_unlock(&soa->entry.lock); | ||||
1373 | return 1; | ||||
1374 | } | ||||
1375 | |||||
1376 | struct dns_msg* | ||||
1377 | val_neg_getmsg(struct val_neg_cache* neg, struct query_info* qinfo, | ||||
1378 | struct regional* region, struct rrset_cache* rrset_cache, | ||||
1379 | sldns_buffer* buf, time_t now, int addsoa, uint8_t* topname, | ||||
1380 | struct config_file* cfg) | ||||
1381 | { | ||||
1382 | struct dns_msg* msg; | ||||
1383 | struct ub_packed_rrset_key* nsec; /* qname matching/covering nsec */ | ||||
1384 | struct ub_packed_rrset_key* wcrr; /* wildcard record or nsec */ | ||||
1385 | uint8_t* nodata_wc = NULL((void*)0); | ||||
1386 | uint8_t* ce = NULL((void*)0); | ||||
1387 | size_t ce_len; | ||||
1388 | uint8_t wc_ce[LDNS_MAX_DOMAINLEN255+3]; | ||||
1389 | struct query_info wc_qinfo; | ||||
1390 | struct ub_packed_rrset_key* cache_wc; | ||||
1391 | struct packed_rrset_data* wcrr_data; | ||||
1392 | int rcode = LDNS_RCODE_NOERROR; | ||||
1393 | uint8_t* zname; | ||||
1394 | size_t zname_len; | ||||
1395 | int zname_labs; | ||||
1396 | struct val_neg_zone* zone; | ||||
1397 | |||||
1398 | /* only for DS queries when aggressive use of NSEC is disabled */ | ||||
1399 | if(qinfo->qtype != LDNS_RR_TYPE_DS && !cfg->aggressive_nsec) | ||||
1400 | return NULL((void*)0); | ||||
1401 | log_assert(!topname || dname_subdomain_c(qinfo->qname, topname)); | ||||
1402 | |||||
1403 | /* Get best available NSEC for qname */ | ||||
1404 | nsec = neg_find_nsec(neg, qinfo->qname, qinfo->qname_len, qinfo->qclass, | ||||
1405 | rrset_cache, now, region); | ||||
1406 | |||||
1407 | /* Matching NSEC, use to generate No Data answer. Not creating answers | ||||
1408 | * yet for No Data proven using wildcard. */ | ||||
1409 | if(nsec && nsec_proves_nodata(nsec, qinfo, &nodata_wc) && !nodata_wc) { | ||||
1410 | if(!(msg = dns_msg_create(qinfo->qname, qinfo->qname_len, | ||||
1411 | qinfo->qtype, qinfo->qclass, region, 2))) | ||||
1412 | return NULL((void*)0); | ||||
1413 | if(!dns_msg_authadd(msg, region, nsec, 0)) | ||||
1414 | return NULL((void*)0); | ||||
1415 | if(addsoa && !add_soa(rrset_cache, now, region, msg, NULL((void*)0))) | ||||
1416 | return NULL((void*)0); | ||||
1417 | |||||
1418 | lock_basic_lock(&neg->lock); | ||||
1419 | neg->num_neg_cache_noerror++; | ||||
1420 | lock_basic_unlock(&neg->lock); | ||||
1421 | return msg; | ||||
1422 | } else if(nsec && val_nsec_proves_name_error(nsec, qinfo->qname)) { | ||||
1423 | if(!(msg = dns_msg_create(qinfo->qname, qinfo->qname_len, | ||||
1424 | qinfo->qtype, qinfo->qclass, region, 3))) | ||||
1425 | return NULL((void*)0); | ||||
1426 | if(!(ce = nsec_closest_encloser(qinfo->qname, nsec))) | ||||
1427 | return NULL((void*)0); | ||||
1428 | dname_count_size_labels(ce, &ce_len); | ||||
1429 | |||||
1430 | /* No extra extra NSEC required if both nameerror qname and | ||||
1431 | * nodata *.ce. are proven already. */ | ||||
1432 | if(!nodata_wc || query_dname_compare(nodata_wc, ce) != 0) { | ||||
1433 | /* Qname proven non existing, get wildcard record for | ||||
1434 | * QTYPE or NSEC covering or matching wildcard. */ | ||||
1435 | |||||
1436 | /* Num labels in ce is always smaller than in qname, | ||||
1437 | * therefore adding the wildcard label cannot overflow | ||||
1438 | * buffer. */ | ||||
1439 | wc_ce[0] = 1; | ||||
1440 | wc_ce[1] = (uint8_t)'*'; | ||||
1441 | memmove(wc_ce+2, ce, ce_len); | ||||
1442 | wc_qinfo.qname = wc_ce; | ||||
1443 | wc_qinfo.qname_len = ce_len + 2; | ||||
1444 | wc_qinfo.qtype = qinfo->qtype; | ||||
1445 | |||||
1446 | |||||
1447 | if((cache_wc = rrset_cache_lookup(rrset_cache, wc_qinfo.qname, | ||||
1448 | wc_qinfo.qname_len, wc_qinfo.qtype, | ||||
1449 | qinfo->qclass, 0/*flags*/, now, 0/*read only*/))) { | ||||
1450 | /* Synthesize wildcard answer */ | ||||
1451 | wcrr_data = (struct packed_rrset_data*)cache_wc->entry.data; | ||||
1452 | if(!(wcrr_data->security == sec_status_secure || | ||||
1453 | (wcrr_data->security == sec_status_unchecked && | ||||
1454 | wcrr_data->rrsig_count > 0))) { | ||||
1455 | lock_rw_unlock(&cache_wc->entry.lock); | ||||
1456 | return NULL((void*)0); | ||||
1457 | } | ||||
1458 | if(!(wcrr = packed_rrset_copy_region(cache_wc, | ||||
1459 | region, now))) { | ||||
1460 | lock_rw_unlock(&cache_wc->entry.lock); | ||||
1461 | return NULL((void*)0); | ||||
1462 | }; | ||||
1463 | lock_rw_unlock(&cache_wc->entry.lock); | ||||
1464 | wcrr->rk.dname = qinfo->qname; | ||||
1465 | wcrr->rk.dname_len = qinfo->qname_len; | ||||
1466 | if(!dns_msg_ansadd(msg, region, wcrr, 0)) | ||||
1467 | return NULL((void*)0); | ||||
1468 | /* No SOA needed for wildcard synthesised | ||||
1469 | * answer. */ | ||||
1470 | addsoa = 0; | ||||
1471 | } else { | ||||
1472 | /* Get wildcard NSEC for possible non existence | ||||
1473 | * proof */ | ||||
1474 | if(!(wcrr = neg_find_nsec(neg, wc_qinfo.qname, | ||||
1475 | wc_qinfo.qname_len, qinfo->qclass, | ||||
1476 | rrset_cache, now, region))) | ||||
1477 | return NULL((void*)0); | ||||
1478 | |||||
1479 | nodata_wc = NULL((void*)0); | ||||
1480 | if(val_nsec_proves_name_error(wcrr, wc_ce)) | ||||
1481 | rcode = LDNS_RCODE_NXDOMAIN; | ||||
1482 | else if(!nsec_proves_nodata(wcrr, &wc_qinfo, | ||||
1483 | &nodata_wc) || nodata_wc) | ||||
1484 | /* &nodata_wc shouldn't be set, wc_qinfo | ||||
1485 | * already contains wildcard domain. */ | ||||
1486 | /* NSEC doesn't prove anything for | ||||
1487 | * wildcard. */ | ||||
1488 | return NULL((void*)0); | ||||
1489 | if(query_dname_compare(wcrr->rk.dname, | ||||
1490 | nsec->rk.dname) != 0) | ||||
1491 | if(!dns_msg_authadd(msg, region, wcrr, 0)) | ||||
1492 | return NULL((void*)0); | ||||
1493 | } | ||||
1494 | } | ||||
1495 | |||||
1496 | if(!dns_msg_authadd(msg, region, nsec, 0)) | ||||
1497 | return NULL((void*)0); | ||||
1498 | if(addsoa && !add_soa(rrset_cache, now, region, msg, NULL((void*)0))) | ||||
1499 | return NULL((void*)0); | ||||
1500 | |||||
1501 | /* Increment statistic counters */ | ||||
1502 | lock_basic_lock(&neg->lock); | ||||
1503 | if(rcode == LDNS_RCODE_NOERROR) | ||||
1504 | neg->num_neg_cache_noerror++; | ||||
1505 | else if(rcode == LDNS_RCODE_NXDOMAIN) | ||||
1506 | neg->num_neg_cache_nxdomain++; | ||||
1507 | lock_basic_unlock(&neg->lock); | ||||
1508 | |||||
1509 | FLAGS_SET_RCODE(msg->rep->flags, rcode)(msg->rep->flags = (((msg->rep->flags) & 0xfff0 ) | (rcode))); | ||||
1510 | return msg; | ||||
1511 | } | ||||
1512 | |||||
1513 | /* No aggressive use of NSEC3 for now, only proceed for DS types. */ | ||||
1514 | if(qinfo->qtype != LDNS_RR_TYPE_DS){ | ||||
1515 | return NULL((void*)0); | ||||
1516 | } | ||||
1517 | /* check NSEC3 neg cache for type DS */ | ||||
1518 | /* need to look one zone higher for DS type */ | ||||
1519 | zname = qinfo->qname; | ||||
1520 | zname_len = qinfo->qname_len; | ||||
1521 | dname_remove_label(&zname, &zname_len); | ||||
1522 | zname_labs = dname_count_labels(zname); | ||||
1523 | |||||
1524 | /* lookup closest zone */ | ||||
1525 | lock_basic_lock(&neg->lock); | ||||
1526 | zone = neg_closest_zone_parent(neg, zname, zname_len, zname_labs, | ||||
1527 | qinfo->qclass); | ||||
1528 | while(zone && !zone->in_use) | ||||
1529 | zone = zone->parent; | ||||
1530 | /* check that the zone is not too high up so that we do not pick data | ||||
1531 | * out of a zone that is above the last-seen key (or trust-anchor). */ | ||||
1532 | if(zone && topname) { | ||||
1533 | if(!dname_subdomain_c(zone->name, topname)) | ||||
1534 | zone = NULL((void*)0); | ||||
1535 | } | ||||
1536 | if(!zone) { | ||||
1537 | lock_basic_unlock(&neg->lock); | ||||
1538 | return NULL((void*)0); | ||||
1539 | } | ||||
1540 | |||||
1541 | msg = neg_nsec3_proof_ds(zone, qinfo->qname, qinfo->qname_len, | ||||
1542 | zname_labs+1, buf, rrset_cache, region, now, topname); | ||||
1543 | if(msg && addsoa && !add_soa(rrset_cache, now, region, msg, zone)) { | ||||
1544 | lock_basic_unlock(&neg->lock); | ||||
1545 | return NULL((void*)0); | ||||
1546 | } | ||||
1547 | lock_basic_unlock(&neg->lock); | ||||
1548 | return msg; | ||||
1549 | } |