1 /* 2 * nsec3.c -- nsec3 handling. 3 * 4 * Copyright (c) 2001-2006, NLnet Labs. All rights reserved. 5 * 6 * See LICENSE for the license. 7 * 8 */ 9 #include "config.h" 10 #ifdef NSEC3 11 #include <stdio.h> 12 #include <stdlib.h> 13 14 #include "nsec3.h" 15 #include "iterated_hash.h" 16 #include "namedb.h" 17 #include "nsd.h" 18 #include "answer.h" 19 #include "udbzone.h" 20 #include "options.h" 21 22 #define NSEC3_RDATA_BITMAP 5 23 24 /* compare nsec3 hashes in nsec3 tree */ 25 static int 26 cmp_hash_tree(const void* x, const void* y) 27 { 28 const domain_type* a = (const domain_type*)x; 29 const domain_type* b = (const domain_type*)y; 30 if(!a->nsec3) return (b->nsec3?-1:0); 31 if(!b->nsec3) return 1; 32 if(!a->nsec3->hash_wc) return (b->nsec3->hash_wc?-1:0); 33 if(!b->nsec3->hash_wc) return 1; 34 return memcmp(a->nsec3->hash_wc->hash.hash, 35 b->nsec3->hash_wc->hash.hash, NSEC3_HASH_LEN); 36 } 37 38 /* compare nsec3 hashes in nsec3 wc tree */ 39 static int 40 cmp_wchash_tree(const void* x, const void* y) 41 { 42 const domain_type* a = (const domain_type*)x; 43 const domain_type* b = (const domain_type*)y; 44 if(!a->nsec3) return (b->nsec3?-1:0); 45 if(!b->nsec3) return 1; 46 if(!a->nsec3->hash_wc) return (b->nsec3->hash_wc?-1:0); 47 if(!b->nsec3->hash_wc) return 1; 48 return memcmp(a->nsec3->hash_wc->wc.hash, 49 b->nsec3->hash_wc->wc.hash, NSEC3_HASH_LEN); 50 } 51 52 /* compare nsec3 hashes in nsec3 ds tree */ 53 static int 54 cmp_dshash_tree(const void* x, const void* y) 55 { 56 const domain_type* a = (const domain_type*)x; 57 const domain_type* b = (const domain_type*)y; 58 if(!a->nsec3) return (b->nsec3?-1:0); 59 if(!b->nsec3) return 1; 60 if(!a->nsec3->ds_parent_hash) return (b->nsec3->ds_parent_hash?-1:0); 61 if(!b->nsec3->ds_parent_hash) return 1; 62 return memcmp(a->nsec3->ds_parent_hash->hash, 63 b->nsec3->ds_parent_hash->hash, NSEC3_HASH_LEN); 64 } 65 66 /* compare base32-encoded nsec3 hashes in nsec3 rr tree, they are 67 * stored in the domain name of the node */ 68 static int 69 cmp_nsec3_tree(const void* x, const void* y) 70 { 71 const domain_type* a = (const domain_type*)x; 72 const domain_type* b = (const domain_type*)y; 73 /* labelcount + 32long label */ 74 assert(dname_name(domain_dname_const(a))[0] == 32); 75 assert(dname_name(domain_dname_const(b))[0] == 32); 76 return memcmp(dname_name(domain_dname_const(a)), dname_name(domain_dname_const(b)), 33); 77 } 78 79 void nsec3_zone_trees_create(struct region* region, zone_type* zone) 80 { 81 if(!zone->nsec3tree) 82 zone->nsec3tree = rbtree_create(region, cmp_nsec3_tree); 83 if(!zone->hashtree) 84 zone->hashtree = rbtree_create(region, cmp_hash_tree); 85 if(!zone->wchashtree) 86 zone->wchashtree = rbtree_create(region, cmp_wchash_tree); 87 if(!zone->dshashtree) 88 zone->dshashtree = rbtree_create(region, cmp_dshash_tree); 89 } 90 91 static void 92 detect_nsec3_params(rr_type* nsec3_apex, 93 const unsigned char** salt, int* salt_len, int* iter) 94 { 95 assert(salt && salt_len && iter); 96 assert(nsec3_apex); 97 *salt_len = rdata_atom_data(nsec3_apex->rdatas[3])[0]; 98 *salt = (unsigned char*)(rdata_atom_data(nsec3_apex->rdatas[3])+1); 99 *iter = read_uint16(rdata_atom_data(nsec3_apex->rdatas[2])); 100 } 101 102 const dname_type * 103 nsec3_b32_create(region_type* region, zone_type* zone, unsigned char* hash) 104 { 105 const dname_type* dname; 106 char b32[SHA_DIGEST_LENGTH*2+1]; 107 b32_ntop(hash, SHA_DIGEST_LENGTH, b32, sizeof(b32)); 108 dname=dname_parse(region, b32); 109 dname=dname_concatenate(region, dname, domain_dname(zone->apex)); 110 return dname; 111 } 112 113 void 114 nsec3_hash_and_store(zone_type* zone, const dname_type* dname, uint8_t* store) 115 { 116 const unsigned char* nsec3_salt = NULL; 117 int nsec3_saltlength = 0; 118 int nsec3_iterations = 0; 119 120 detect_nsec3_params(zone->nsec3_param, &nsec3_salt, 121 &nsec3_saltlength, &nsec3_iterations); 122 iterated_hash((unsigned char*)store, nsec3_salt, nsec3_saltlength, 123 dname_name(dname), dname->name_size, nsec3_iterations); 124 } 125 126 #define STORE_HASH(x,y) memmove(domain->nsec3->x,y,NSEC3_HASH_LEN); domain->nsec3->have_##x =1; 127 128 /** find hash or create it and store it */ 129 static void 130 nsec3_lookup_hash_and_wc(region_type* region, zone_type* zone, 131 const dname_type* dname, domain_type* domain, region_type* tmpregion) 132 { 133 const dname_type* wcard; 134 if(domain->nsec3->hash_wc) { 135 return; 136 } 137 /* lookup failed; disk failure or so */ 138 domain->nsec3->hash_wc = (nsec3_hash_wc_node_type *) 139 region_alloc(region, sizeof(nsec3_hash_wc_node_type)); 140 domain->nsec3->hash_wc->hash.node.key = NULL; 141 domain->nsec3->hash_wc->wc.node.key = NULL; 142 nsec3_hash_and_store(zone, dname, domain->nsec3->hash_wc->hash.hash); 143 wcard = dname_parse(tmpregion, "*"); 144 wcard = dname_concatenate(tmpregion, wcard, dname); 145 nsec3_hash_and_store(zone, wcard, domain->nsec3->hash_wc->wc.hash); 146 } 147 148 static void 149 nsec3_lookup_hash_ds(region_type* region, zone_type* zone, 150 const dname_type* dname, domain_type* domain) 151 { 152 if(domain->nsec3->ds_parent_hash) { 153 return; 154 } 155 /* lookup failed; disk failure or so */ 156 domain->nsec3->ds_parent_hash = (nsec3_hash_node_type *) 157 region_alloc(region, sizeof(nsec3_hash_node_type)); 158 domain->nsec3->ds_parent_hash->node.key = NULL; 159 nsec3_hash_and_store(zone, dname, domain->nsec3->ds_parent_hash->hash); 160 } 161 162 static int 163 nsec3_has_soa(rr_type* rr) 164 { 165 if(rdata_atom_size(rr->rdatas[NSEC3_RDATA_BITMAP]) >= 3 && /* has types in bitmap */ 166 rdata_atom_data(rr->rdatas[NSEC3_RDATA_BITMAP])[0] == 0 && /* first window = 0, */ 167 /* [1]: bitmap length must be >= 1 */ 168 /* [2]: bit[6] = SOA, thus mask first bitmap octet with 0x02 */ 169 rdata_atom_data(rr->rdatas[NSEC3_RDATA_BITMAP])[2]&0x02) { /* SOA bit set */ 170 return 1; 171 } 172 return 0; 173 } 174 175 static rr_type* 176 check_apex_soa(namedb_type* namedb, zone_type *zone, int nolog) 177 { 178 uint8_t h[NSEC3_HASH_LEN]; 179 domain_type* domain; 180 const dname_type* hashed_apex, *dname = domain_dname(zone->apex); 181 unsigned j; 182 rrset_type* nsec3_rrset; 183 region_type* tmpregion; 184 185 nsec3_hash_and_store(zone, dname, h); 186 tmpregion = region_create(xalloc, free); 187 hashed_apex = nsec3_b32_create(tmpregion, zone, h); 188 domain = domain_table_find(namedb->domains, hashed_apex); 189 if(!domain) { 190 if(!nolog) { 191 log_msg(LOG_ERR, "%s NSEC3PARAM entry has no hash(apex).", 192 domain_to_string(zone->apex)); 193 log_msg(LOG_ERR, "hash(apex)= %s", 194 dname_to_string(hashed_apex, NULL)); 195 } 196 region_destroy(tmpregion); 197 return NULL; 198 } 199 nsec3_rrset = domain_find_rrset(domain, zone, TYPE_NSEC3); 200 if(!nsec3_rrset) { 201 if(!nolog) { 202 log_msg(LOG_ERR, "%s NSEC3PARAM entry: hash(apex) has no NSEC3 RRset.", 203 domain_to_string(zone->apex)); 204 log_msg(LOG_ERR, "hash(apex)= %s", 205 dname_to_string(hashed_apex, NULL)); 206 } 207 region_destroy(tmpregion); 208 return NULL; 209 } 210 for(j=0; j<nsec3_rrset->rr_count; j++) { 211 if(nsec3_has_soa(&nsec3_rrset->rrs[j])) { 212 region_destroy(tmpregion); 213 return &nsec3_rrset->rrs[j]; 214 } 215 } 216 if(!nolog) { 217 log_msg(LOG_ERR, "%s NSEC3PARAM entry: hash(apex) NSEC3 has no SOA flag.", 218 domain_to_string(zone->apex)); 219 log_msg(LOG_ERR, "hash(apex)= %s", 220 dname_to_string(hashed_apex, NULL)); 221 } 222 region_destroy(tmpregion); 223 return NULL; 224 } 225 226 static void 227 nsec3param_to_str(struct rr* rr, char* str, size_t buflen) 228 { 229 rdata_atom_type* rd = rr->rdatas; 230 size_t len; 231 len = snprintf(str, buflen, "%u %u %u ", 232 (unsigned)rdata_atom_data(rd[0])[0], 233 (unsigned)rdata_atom_data(rd[1])[0], 234 (unsigned)read_uint16(rdata_atom_data(rd[2]))); 235 if(rdata_atom_data(rd[3])[0] == 0) { 236 if(buflen > len + 2) 237 str[len++] = '-'; 238 } else { 239 len += hex_ntop(rdata_atom_data(rd[3])+1, 240 rdata_atom_data(rd[3])[0], str+len, buflen-len-1); 241 } 242 if(buflen > len + 1) 243 str[len] = 0; 244 } 245 246 static struct rr* 247 db_find_nsec3param(struct namedb* db, struct zone* z, struct rr* avoid_rr, 248 int checkchain) 249 { 250 unsigned i; 251 rrset_type* rrset = domain_find_rrset(z->apex, z, TYPE_NSEC3PARAM); 252 if(!rrset) /* no NSEC3PARAM in mem */ 253 return NULL; 254 /* find first nsec3param we can support (SHA1, no flags) */ 255 for(i=0; i<rrset->rr_count; i++) { 256 rdata_atom_type* rd = rrset->rrs[i].rdatas; 257 /* do not use the RR that is going to be deleted (in IXFR) */ 258 if(&rrset->rrs[i] == avoid_rr) continue; 259 if(rrset->rrs[i].rdata_count < 4) continue; 260 if(rdata_atom_data(rd[0])[0] == NSEC3_SHA1_HASH && 261 rdata_atom_data(rd[1])[0] == 0) { 262 if(checkchain) { 263 z->nsec3_param = &rrset->rrs[i]; 264 if(!check_apex_soa(db, z, 1)) { 265 char str[MAX_RDLENGTH*2+16]; 266 nsec3param_to_str(z->nsec3_param, 267 str, sizeof(str)); 268 VERBOSITY(1, (LOG_WARNING, "zone %s NSEC3PARAM %s has broken chain, ignoring", domain_to_string(z->apex), str)); 269 continue; /* don't use broken chain */ 270 } 271 } 272 if(2 <= verbosity) { 273 char str[MAX_RDLENGTH*2+16]; 274 nsec3param_to_str(&rrset->rrs[i], str, 275 sizeof(str)); 276 VERBOSITY(2, (LOG_INFO, "rehash of zone %s with parameters %s", 277 domain_to_string(z->apex), str)); 278 } 279 return &rrset->rrs[i]; 280 } 281 } 282 return NULL; 283 } 284 285 static struct rr* 286 udb_zone_find_nsec3param(struct namedb* db, udb_base* udb, udb_ptr* uz, 287 struct zone* z, int checkchain) 288 { 289 udb_ptr urr; 290 unsigned i; 291 rrset_type* rrset = domain_find_rrset(z->apex, z, TYPE_NSEC3PARAM); 292 if(!rrset) /* no NSEC3PARAM in mem */ 293 return NULL; 294 udb_ptr_new(&urr, udb, &ZONE(uz)->nsec3param); 295 if(!urr.data || RR(&urr)->len < 5) { 296 /* no NSEC3PARAM in udb */ 297 udb_ptr_unlink(&urr, udb); 298 return NULL; 299 } 300 /* find matching NSEC3PARAM RR in memory */ 301 for(i=0; i<rrset->rr_count; i++) { 302 /* if this RR matches the udb RR then we are done */ 303 rdata_atom_type* rd = rrset->rrs[i].rdatas; 304 if(rrset->rrs[i].rdata_count < 4) continue; 305 if(RR(&urr)->wire[0] == rdata_atom_data(rd[0])[0] && /*alg*/ 306 RR(&urr)->wire[1] == rdata_atom_data(rd[1])[0] && /*flg*/ 307 RR(&urr)->wire[2] == rdata_atom_data(rd[2])[0] && /*iter*/ 308 RR(&urr)->wire[3] == rdata_atom_data(rd[2])[1] && 309 RR(&urr)->wire[4] == rdata_atom_data(rd[3])[0] && /*slen*/ 310 RR(&urr)->len >= 5 + RR(&urr)->wire[4] && 311 memcmp(RR(&urr)->wire+5, rdata_atom_data(rd[3])+1, 312 rdata_atom_data(rd[3])[0]) == 0) { 313 udb_ptr_unlink(&urr, udb); 314 if(checkchain) { 315 z->nsec3_param = &rrset->rrs[i]; 316 if(!check_apex_soa(db, z, 1)) 317 return db_find_nsec3param(db, z, 318 NULL, checkchain); 319 } 320 return &rrset->rrs[i]; 321 } 322 } 323 udb_ptr_unlink(&urr, udb); 324 return NULL; 325 } 326 327 void 328 nsec3_find_zone_param(struct namedb* db, struct zone* zone, udb_ptr* z, 329 struct rr* avoid_rr, int checkchain) 330 { 331 /* get nsec3param RR from udb */ 332 if(db->udb) 333 zone->nsec3_param = udb_zone_find_nsec3param(db, db->udb, 334 z, zone, checkchain); 335 /* no db, get from memory, avoid using the rr that is going to be 336 * deleted, avoid_rr */ 337 else zone->nsec3_param = db_find_nsec3param(db, zone, avoid_rr, 338 checkchain); 339 } 340 341 /* check params ok for one RR */ 342 static int 343 nsec3_rdata_params_ok(rdata_atom_type* prd, rdata_atom_type* rd) 344 { 345 return (rdata_atom_data(rd[0])[0] == 346 rdata_atom_data(prd[0])[0] && /* hash algo */ 347 rdata_atom_data(rd[2])[0] == 348 rdata_atom_data(prd[2])[0] && /* iterations 0 */ 349 rdata_atom_data(rd[2])[1] == 350 rdata_atom_data(prd[2])[1] && /* iterations 1 */ 351 rdata_atom_data(rd[3])[0] == 352 rdata_atom_data(prd[3])[0] && /* salt length */ 353 memcmp(rdata_atom_data(rd[3])+1, 354 rdata_atom_data(prd[3])+1, rdata_atom_data(rd[3])[0]) 355 == 0 ); 356 } 357 358 int 359 nsec3_rr_uses_params(rr_type* rr, zone_type* zone) 360 { 361 if(!rr || rr->rdata_count < 4) 362 return 0; 363 return nsec3_rdata_params_ok(zone->nsec3_param->rdatas, rr->rdatas); 364 } 365 366 int 367 nsec3_in_chain_count(domain_type* domain, zone_type* zone) 368 { 369 rrset_type* rrset = domain_find_rrset(domain, zone, TYPE_NSEC3); 370 unsigned i; 371 int count = 0; 372 if(!rrset || !zone->nsec3_param) 373 return 0; /* no NSEC3s, none in the chain */ 374 for(i=0; i<rrset->rr_count; i++) { 375 if(nsec3_rr_uses_params(&rrset->rrs[i], zone)) 376 count++; 377 } 378 return count; 379 } 380 381 struct domain* 382 nsec3_chain_find_prev(struct zone* zone, struct domain* domain) 383 { 384 if(domain->nsec3 && domain->nsec3->nsec3_node.key) { 385 /* see if there is a prev */ 386 rbnode_type* r = rbtree_previous(&domain->nsec3->nsec3_node); 387 if(r != RBTREE_NULL) { 388 /* found a previous, which is not the root-node in 389 * the prehash tree (and thus points to the tree) */ 390 return (domain_type*)r->key; 391 } 392 } 393 if(zone->nsec3_last) 394 return zone->nsec3_last; 395 return NULL; 396 } 397 398 void 399 nsec3_clear_precompile(struct namedb* db, zone_type* zone) 400 { 401 domain_type* walk; 402 /* clear prehash items (there must not be items for other zones) */ 403 prehash_clear(db->domains); 404 /* clear trees */ 405 hash_tree_clear(zone->nsec3tree); 406 hash_tree_clear(zone->hashtree); 407 hash_tree_clear(zone->wchashtree); 408 hash_tree_clear(zone->dshashtree); 409 /* wipe hashes */ 410 411 /* wipe precompile */ 412 walk = zone->apex; 413 while(walk && domain_is_subdomain(walk, zone->apex)) { 414 if(walk->nsec3) { 415 if(nsec3_condition_hash(walk, zone)) { 416 walk->nsec3->nsec3_node.key = NULL; 417 walk->nsec3->nsec3_cover = NULL; 418 walk->nsec3->nsec3_wcard_child_cover = NULL; 419 walk->nsec3->nsec3_is_exact = 0; 420 if (walk->nsec3->hash_wc) { 421 region_recycle(db->domains->region, 422 walk->nsec3->hash_wc, 423 sizeof(nsec3_hash_wc_node_type)); 424 walk->nsec3->hash_wc = NULL; 425 } 426 } 427 if(nsec3_condition_dshash(walk, zone)) { 428 walk->nsec3->nsec3_ds_parent_cover = NULL; 429 walk->nsec3->nsec3_ds_parent_is_exact = 0; 430 if (walk->nsec3->ds_parent_hash) { 431 region_recycle(db->domains->region, 432 walk->nsec3->ds_parent_hash, 433 sizeof(nsec3_hash_node_type)); 434 walk->nsec3->ds_parent_hash = NULL; 435 } 436 } 437 } 438 walk = domain_next(walk); 439 } 440 zone->nsec3_last = NULL; 441 } 442 443 /* see if domain name is part of (existing names in) the nsec3 zone */ 444 int 445 nsec3_domain_part_of_zone(domain_type* d, zone_type* z) 446 { 447 while(d) { 448 if(d->is_apex) 449 return (z->apex == d); /* zonecut, if right zone*/ 450 d = d->parent; 451 } 452 return 0; 453 } 454 455 /* condition when a domain is precompiled */ 456 int 457 nsec3_condition_hash(domain_type* d, zone_type* z) 458 { 459 return d->is_existing && !domain_has_only_NSEC3(d, z) && 460 nsec3_domain_part_of_zone(d, z) && !domain_is_glue(d, z); 461 } 462 463 /* condition when a domain is ds precompiled */ 464 int 465 nsec3_condition_dshash(domain_type* d, zone_type* z) 466 { 467 return d->is_existing && !domain_has_only_NSEC3(d, z) && 468 (domain_find_rrset(d, z, TYPE_DS) || 469 domain_find_rrset(d, z, TYPE_NS)) && d != z->apex 470 && nsec3_domain_part_of_zone(d->parent, z); 471 } 472 473 zone_type* 474 nsec3_tree_zone(namedb_type* db, domain_type* d) 475 { 476 /* see nsec3_domain_part_of_zone; domains part of zone that has 477 * apex above them */ 478 /* this does not use the rrset->zone pointer because there may be 479 * no rrsets left at apex (no SOA), e.g. during IXFR */ 480 while(d) { 481 if(d->is_apex) { 482 /* we can try a SOA if its present (faster than tree)*/ 483 /* DNSKEY and NSEC3PARAM are also good indicators */ 484 rrset_type *rrset; 485 for (rrset = d->rrsets; rrset; rrset = rrset->next) 486 if (rrset_rrtype(rrset) == TYPE_SOA || 487 rrset_rrtype(rrset) == TYPE_DNSKEY || 488 rrset_rrtype(rrset) == TYPE_NSEC3PARAM) 489 return rrset->zone; 490 return namedb_find_zone(db, domain_dname(d)); 491 } 492 d = d->parent; 493 } 494 return NULL; 495 } 496 497 zone_type* 498 nsec3_tree_dszone(namedb_type* db, domain_type* d) 499 { 500 /* the DStree does not contain nodes with d==z->apex */ 501 if(d->is_apex) 502 d = d->parent; 503 return nsec3_tree_zone(db, d); 504 } 505 506 int 507 nsec3_find_cover(zone_type* zone, uint8_t* hash, size_t hashlen, 508 domain_type** result) 509 { 510 rbnode_type* r = NULL; 511 int exact; 512 domain_type d; 513 uint8_t n[48]; 514 515 /* nsec3tree is sorted by b32 encoded domain name of the NSEC3 */ 516 b32_ntop(hash, hashlen, (char*)(n+5), sizeof(n)-5); 517 #ifdef USE_RADIX_TREE 518 d.dname = (dname_type*)n; 519 #else 520 d.node.key = n; 521 #endif 522 n[0] = 34; /* name_size */ 523 n[1] = 2; /* label_count */ 524 n[2] = 0; /* label_offset[0] */ 525 n[3] = 0; /* label_offset[1] */ 526 n[4] = 32; /* label-size[0] */ 527 528 assert(result); 529 assert(zone->nsec3_param && zone->nsec3tree); 530 531 exact = rbtree_find_less_equal(zone->nsec3tree, &d, &r); 532 if(r) { 533 *result = (domain_type*)r->key; 534 } else { 535 *result = zone->nsec3_last; 536 } 537 return exact; 538 } 539 540 void 541 nsec3_precompile_domain(struct namedb* db, struct domain* domain, 542 struct zone* zone, region_type* tmpregion) 543 { 544 domain_type* result = 0; 545 int exact; 546 allocate_domain_nsec3(db->domains, domain); 547 548 /* hash it */ 549 nsec3_lookup_hash_and_wc(db->region, 550 zone, domain_dname(domain), domain, tmpregion); 551 552 /* add into tree */ 553 zone_add_domain_in_hash_tree(db->region, &zone->hashtree, 554 cmp_hash_tree, domain, &domain->nsec3->hash_wc->hash.node); 555 zone_add_domain_in_hash_tree(db->region, &zone->wchashtree, 556 cmp_wchash_tree, domain, &domain->nsec3->hash_wc->wc.node); 557 558 /* lookup in tree cover ptr (or exact) */ 559 exact = nsec3_find_cover(zone, domain->nsec3->hash_wc->hash.hash, 560 sizeof(domain->nsec3->hash_wc->hash.hash), &result); 561 domain->nsec3->nsec3_cover = result; 562 if(exact) 563 domain->nsec3->nsec3_is_exact = 1; 564 else domain->nsec3->nsec3_is_exact = 0; 565 566 /* find cover for *.domain for wildcard denial */ 567 (void)nsec3_find_cover(zone, domain->nsec3->hash_wc->wc.hash, 568 sizeof(domain->nsec3->hash_wc->wc.hash), &result); 569 domain->nsec3->nsec3_wcard_child_cover = result; 570 } 571 572 void 573 nsec3_precompile_domain_ds(struct namedb* db, struct domain* domain, 574 struct zone* zone) 575 { 576 domain_type* result = 0; 577 int exact; 578 allocate_domain_nsec3(db->domains, domain); 579 580 /* hash it : it could have different hash parameters then the 581 other hash for this domain name */ 582 nsec3_lookup_hash_ds(db->region, zone, domain_dname(domain), domain); 583 /* lookup in tree cover ptr (or exact) */ 584 exact = nsec3_find_cover(zone, domain->nsec3->ds_parent_hash->hash, 585 sizeof(domain->nsec3->ds_parent_hash->hash), &result); 586 if(exact) 587 domain->nsec3->nsec3_ds_parent_is_exact = 1; 588 else domain->nsec3->nsec3_ds_parent_is_exact = 0; 589 domain->nsec3->nsec3_ds_parent_cover = result; 590 /* add into tree */ 591 zone_add_domain_in_hash_tree(db->region, &zone->dshashtree, 592 cmp_dshash_tree, domain, &domain->nsec3->ds_parent_hash->node); 593 } 594 595 static void 596 parse_nsec3_name(const dname_type* dname, uint8_t* hash, size_t buflen) 597 { 598 /* first label must be the match, */ 599 size_t lablen = (buflen-1) * 8 / 5; 600 const uint8_t* wire = dname_name(dname); 601 assert(lablen == 32 && buflen == NSEC3_HASH_LEN+1); 602 /* labels of length 32 for SHA1, and must have space+1 for convert */ 603 if(wire[0] != lablen) { 604 /* not NSEC3 */ 605 memset(hash, 0, buflen); 606 return; 607 } 608 (void)b32_pton((char*)wire+1, hash, buflen); 609 } 610 611 void 612 nsec3_precompile_nsec3rr(namedb_type* db, struct domain* domain, 613 struct zone* zone) 614 { 615 allocate_domain_nsec3(db->domains, domain); 616 /* add into nsec3tree */ 617 zone_add_domain_in_hash_tree(db->region, &zone->nsec3tree, 618 cmp_nsec3_tree, domain, &domain->nsec3->nsec3_node); 619 /* fixup the last in the zone */ 620 if(rbtree_last(zone->nsec3tree)->key == domain) { 621 zone->nsec3_last = domain; 622 } 623 } 624 625 void 626 nsec3_precompile_newparam(namedb_type* db, zone_type* zone) 627 { 628 region_type* tmpregion = region_create(xalloc, free); 629 domain_type* walk; 630 time_t s = time(NULL); 631 unsigned long n = 0, c = 0; 632 633 /* add nsec3s of chain to nsec3tree */ 634 for(walk=zone->apex; walk && domain_is_subdomain(walk, zone->apex); 635 walk = domain_next(walk)) { 636 n++; 637 if(nsec3_in_chain_count(walk, zone) != 0) { 638 nsec3_precompile_nsec3rr(db, walk, zone); 639 } 640 } 641 /* hash and precompile zone */ 642 for(walk=zone->apex; walk && domain_is_subdomain(walk, zone->apex); 643 walk = domain_next(walk)) { 644 if(nsec3_condition_hash(walk, zone)) { 645 nsec3_precompile_domain(db, walk, zone, tmpregion); 646 region_free_all(tmpregion); 647 } 648 if(nsec3_condition_dshash(walk, zone)) 649 nsec3_precompile_domain_ds(db, walk, zone); 650 if(++c % ZONEC_PCT_COUNT == 0 && time(NULL) > s + ZONEC_PCT_TIME) { 651 s = time(NULL); 652 VERBOSITY(1, (LOG_INFO, "nsec3 %s %d %%", 653 zone->opts->name, 654 (int)(c*((unsigned long)100)/n))); 655 } 656 } 657 region_destroy(tmpregion); 658 } 659 660 void 661 prehash_zone_complete(struct namedb* db, struct zone* zone) 662 { 663 udb_ptr udbz; 664 665 /* robust clear it */ 666 nsec3_clear_precompile(db, zone); 667 /* find zone settings */ 668 669 assert(db && zone); 670 udbz.data = 0; 671 if(db->udb) { 672 if(!udb_zone_search(db->udb, &udbz, dname_name(domain_dname( 673 zone->apex)), domain_dname(zone->apex)->name_size)) { 674 udb_ptr_init(&udbz, db->udb); /* zero the ptr */ 675 } 676 } 677 nsec3_find_zone_param(db, zone, &udbz, NULL, 1); 678 if(!zone->nsec3_param || !check_apex_soa(db, zone, 0)) { 679 zone->nsec3_param = NULL; 680 zone->nsec3_last = NULL; 681 if(udbz.data) 682 udb_ptr_unlink(&udbz, db->udb); 683 return; 684 } 685 if(udbz.data) 686 udb_ptr_unlink(&udbz, db->udb); 687 nsec3_precompile_newparam(db, zone); 688 } 689 690 static void 691 init_lookup_key_hash_tree(domain_type* d, uint8_t* hash) 692 { memcpy(d->nsec3->hash_wc->hash.hash, hash, NSEC3_HASH_LEN); } 693 694 static void 695 init_lookup_key_wc_tree(domain_type* d, uint8_t* hash) 696 { memcpy(d->nsec3->hash_wc->wc.hash, hash, NSEC3_HASH_LEN); } 697 698 static void 699 init_lookup_key_ds_tree(domain_type* d, uint8_t* hash) 700 { memcpy(d->nsec3->ds_parent_hash->hash, hash, NSEC3_HASH_LEN); } 701 702 /* find first in the tree and true if the first to process it */ 703 static int 704 process_first(rbtree_type* tree, uint8_t* hash, rbnode_type** p, 705 void (*init)(domain_type*, uint8_t*)) 706 { 707 domain_type d; 708 struct nsec3_domain_data n; 709 nsec3_hash_wc_node_type hash_wc; 710 nsec3_hash_node_type ds_parent_hash; 711 712 if(!tree) { 713 *p = RBTREE_NULL; 714 return 0; 715 } 716 hash_wc.hash.node.key = NULL; 717 hash_wc.wc.node.key = NULL; 718 n.hash_wc = &hash_wc; 719 ds_parent_hash.node.key = NULL; 720 n.ds_parent_hash = &ds_parent_hash; 721 d.nsec3 = &n; 722 init(&d, hash); 723 if(rbtree_find_less_equal(tree, &d, p)) { 724 /* found an exact match */ 725 return 1; 726 } 727 if(!*p) /* before first, go from first */ 728 *p = rbtree_first(tree); 729 /* the inexact, smaller, match we found, does not itself need to 730 * be edited */ 731 else 732 *p = rbtree_next(*p); /* if this becomes NULL, nothing to do */ 733 return 0; 734 } 735 736 /* set end pointer if possible */ 737 static void 738 process_end(rbtree_type* tree, uint8_t* hash, rbnode_type** p, 739 void (*init)(domain_type*, uint8_t*)) 740 { 741 domain_type d; 742 struct nsec3_domain_data n; 743 nsec3_hash_wc_node_type hash_wc; 744 nsec3_hash_node_type ds_parent_hash; 745 746 if(!tree) { 747 *p = RBTREE_NULL; 748 return; 749 } 750 hash_wc.hash.node.key = NULL; 751 hash_wc.wc.node.key = NULL; 752 n.hash_wc = &hash_wc; 753 ds_parent_hash.node.key = NULL; 754 n.ds_parent_hash = &ds_parent_hash; 755 d.nsec3 = &n; 756 init(&d, hash); 757 if(rbtree_find_less_equal(tree, &d, p)) { 758 /* an exact match, fine, because this one does not get 759 * processed */ 760 return; 761 } 762 /* inexact element, but if NULL, until first element in tree */ 763 if(!*p) { 764 *p = rbtree_first(tree); 765 return; 766 } 767 /* inexact match, use next element, if possible, the smaller 768 * element is part of the range */ 769 *p = rbtree_next(*p); 770 /* if next returns null, we go until the end of the tree */ 771 } 772 773 /* prehash domains in hash range start to end */ 774 static void 775 process_range(zone_type* zone, domain_type* start, 776 domain_type* end, domain_type* nsec3) 777 { 778 /* start NULL means from first in tree */ 779 /* end NULL means to last in tree */ 780 rbnode_type *p = RBTREE_NULL, *pwc = RBTREE_NULL, *pds = RBTREE_NULL; 781 rbnode_type *p_end = RBTREE_NULL, *pwc_end = RBTREE_NULL, *pds_end = RBTREE_NULL; 782 /* because the nodes are on the prehashlist, the domain->nsec3 is 783 * already allocated, and we need not allocate it here */ 784 /* set start */ 785 if(start) { 786 uint8_t hash[NSEC3_HASH_LEN+1]; 787 parse_nsec3_name(domain_dname(start), hash, sizeof(hash)); 788 /* if exact match on first, set is_exact */ 789 if(process_first(zone->hashtree, hash, &p, init_lookup_key_hash_tree)) { 790 ((domain_type*)(p->key))->nsec3->nsec3_cover = nsec3; 791 ((domain_type*)(p->key))->nsec3->nsec3_is_exact = 1; 792 p = rbtree_next(p); 793 } 794 (void)process_first(zone->wchashtree, hash, &pwc, init_lookup_key_wc_tree); 795 if(process_first(zone->dshashtree, hash, &pds, init_lookup_key_ds_tree)){ 796 ((domain_type*)(pds->key))->nsec3-> 797 nsec3_ds_parent_cover = nsec3; 798 ((domain_type*)(pds->key))->nsec3-> 799 nsec3_ds_parent_is_exact = 1; 800 pds = rbtree_next(pds); 801 } 802 } else { 803 if(zone->hashtree) 804 p = rbtree_first(zone->hashtree); 805 if(zone->wchashtree) 806 pwc = rbtree_first(zone->wchashtree); 807 if(zone->dshashtree) 808 pds = rbtree_first(zone->dshashtree); 809 } 810 /* set end */ 811 if(end) { 812 uint8_t hash[NSEC3_HASH_LEN+1]; 813 parse_nsec3_name(domain_dname(end), hash, sizeof(hash)); 814 process_end(zone->hashtree, hash, &p_end, init_lookup_key_hash_tree); 815 process_end(zone->wchashtree, hash, &pwc_end, init_lookup_key_wc_tree); 816 process_end(zone->dshashtree, hash, &pds_end, init_lookup_key_ds_tree); 817 } 818 819 /* precompile */ 820 while(p != RBTREE_NULL && p != p_end) { 821 ((domain_type*)(p->key))->nsec3->nsec3_cover = nsec3; 822 ((domain_type*)(p->key))->nsec3->nsec3_is_exact = 0; 823 p = rbtree_next(p); 824 } 825 while(pwc != RBTREE_NULL && pwc != pwc_end) { 826 ((domain_type*)(pwc->key))->nsec3-> 827 nsec3_wcard_child_cover = nsec3; 828 pwc = rbtree_next(pwc); 829 } 830 while(pds != RBTREE_NULL && pds != pds_end) { 831 ((domain_type*)(pds->key))->nsec3-> 832 nsec3_ds_parent_cover = nsec3; 833 ((domain_type*)(pds->key))->nsec3-> 834 nsec3_ds_parent_is_exact = 0; 835 pds = rbtree_next(pds); 836 } 837 } 838 839 /* prehash a domain from the prehash list */ 840 static void 841 process_prehash_domain(domain_type* domain, zone_type* zone) 842 { 843 /* in the hashtree, wchashtree, dshashtree walk through to next NSEC3 844 * and set precompile pointers to point to this domain (or is_exact), 845 * the first domain can be is_exact. If it is the last NSEC3, also 846 * process the initial part (before the first) */ 847 rbnode_type* nx; 848 849 /* this domain is part of the prehash list and therefore the 850 * domain->nsec3 is allocated and need not be allocated here */ 851 assert(domain->nsec3 && domain->nsec3->nsec3_node.key); 852 nx = rbtree_next(&domain->nsec3->nsec3_node); 853 if(nx != RBTREE_NULL) { 854 /* process until next nsec3 */ 855 domain_type* end = (domain_type*)nx->key; 856 process_range(zone, domain, end, domain); 857 } else { 858 /* first is root, but then comes the first nsec3 */ 859 domain_type* first = (domain_type*)(rbtree_first( 860 zone->nsec3tree)->key); 861 /* last in zone */ 862 process_range(zone, domain, NULL, domain); 863 /* also process before first in zone */ 864 process_range(zone, NULL, first, domain); 865 } 866 } 867 868 void prehash_zone(struct namedb* db, struct zone* zone) 869 { 870 domain_type* d; 871 if(!zone->nsec3_param) { 872 prehash_clear(db->domains); 873 return; 874 } 875 if(!check_apex_soa(db, zone, 1)) { 876 /* the zone fails apex soa check, prehash complete may 877 * detect other valid chains */ 878 prehash_clear(db->domains); 879 prehash_zone_complete(db, zone); 880 return; 881 } 882 /* process prehash list */ 883 for(d = db->domains->prehash_list; d; d = d->nsec3->prehash_next) { 884 process_prehash_domain(d, zone); 885 } 886 /* clear prehash list */ 887 prehash_clear(db->domains); 888 889 if(!check_apex_soa(db, zone, 0)) { 890 zone->nsec3_param = NULL; 891 zone->nsec3_last = NULL; 892 } 893 } 894 895 /* add the NSEC3 rrset to the query answer at the given domain */ 896 static void 897 nsec3_add_rrset(struct query* query, struct answer* answer, 898 rr_section_type section, struct domain* domain) 899 { 900 if(domain) { 901 rrset_type* rrset = domain_find_rrset(domain, query->zone, TYPE_NSEC3); 902 if(rrset) 903 answer_add_rrset(answer, section, domain, rrset); 904 } 905 } 906 907 /* this routine does hashing at query-time. slow. */ 908 static void 909 nsec3_add_nonexist_proof(struct query* query, struct answer* answer, 910 struct domain* encloser, const dname_type* qname) 911 { 912 uint8_t hash[NSEC3_HASH_LEN]; 913 const dname_type* to_prove; 914 domain_type* cover=0; 915 assert(encloser); 916 /* if query=a.b.c.d encloser=c.d. then proof needed for b.c.d. */ 917 /* if query=a.b.c.d encloser=*.c.d. then proof needed for b.c.d. */ 918 to_prove = dname_partial_copy(query->region, qname, 919 dname_label_match_count(qname, domain_dname(encloser))+1); 920 /* generate proof that one label below closest encloser does not exist */ 921 nsec3_hash_and_store(query->zone, to_prove, hash); 922 if(nsec3_find_cover(query->zone, hash, sizeof(hash), &cover)) 923 { 924 /* exact match, hash collision */ 925 domain_type* walk; 926 char hashbuf[512]; 927 char reversebuf[512]; 928 (void)b32_ntop(hash, sizeof(hash), hashbuf, sizeof(hashbuf)); 929 snprintf(reversebuf, sizeof(reversebuf), "(no name in the zone hashes to this nsec3 record)"); 930 walk = query->zone->apex; 931 while(walk) { 932 if(walk->nsec3 && walk->nsec3->nsec3_cover == cover) { 933 snprintf(reversebuf, sizeof(reversebuf), 934 "%s %s", domain_to_string(walk), 935 walk->nsec3->nsec3_is_exact?"exact":"no_exact_hash_match"); 936 if(walk->nsec3->nsec3_is_exact) 937 break; 938 } 939 if(walk->nsec3 && walk->nsec3->nsec3_ds_parent_cover == cover) { 940 snprintf(reversebuf, sizeof(reversebuf), 941 "%s %s", domain_to_string(walk), 942 walk->nsec3->nsec3_ds_parent_is_exact?"exact":"no_exact_hash_match"); 943 if(walk->nsec3->nsec3_ds_parent_is_exact) 944 break; 945 } 946 walk = domain_next(walk); 947 } 948 949 950 /* the hashed name of the query corresponds to an existing name. */ 951 VERBOSITY(3, (LOG_ERR, "nsec3 hash collision for name=%s hash=%s reverse=%s", 952 dname_to_string(to_prove, NULL), hashbuf, reversebuf)); 953 RCODE_SET(query->packet, RCODE_SERVFAIL); 954 return; 955 } 956 else 957 { 958 /* cover proves the qname does not exist */ 959 nsec3_add_rrset(query, answer, AUTHORITY_SECTION, cover); 960 } 961 } 962 963 static void 964 nsec3_add_closest_encloser_proof( 965 struct query* query, struct answer* answer, 966 struct domain* closest_encloser, const dname_type* qname) 967 { 968 if(!closest_encloser) 969 return; 970 /* prove that below closest encloser nothing exists */ 971 nsec3_add_nonexist_proof(query, answer, closest_encloser, qname); 972 /* proof that closest encloser exists */ 973 if(closest_encloser->nsec3 && closest_encloser->nsec3->nsec3_is_exact) 974 nsec3_add_rrset(query, answer, AUTHORITY_SECTION, 975 closest_encloser->nsec3->nsec3_cover); 976 } 977 978 void 979 nsec3_answer_wildcard(struct query *query, struct answer *answer, 980 struct domain *wildcard, const dname_type* qname) 981 { 982 if(!wildcard) 983 return; 984 if(!query->zone->nsec3_param) 985 return; 986 nsec3_add_nonexist_proof(query, answer, wildcard, qname); 987 } 988 989 static void 990 nsec3_add_ds_proof(struct query *query, struct answer *answer, 991 struct domain *domain, int delegpt) 992 { 993 /* assert we are above the zone cut */ 994 assert(domain != query->zone->apex); 995 if(domain->nsec3 && domain->nsec3->nsec3_ds_parent_is_exact) { 996 /* use NSEC3 record from above the zone cut. */ 997 nsec3_add_rrset(query, answer, AUTHORITY_SECTION, 998 domain->nsec3->nsec3_ds_parent_cover); 999 } else if (!delegpt && domain->nsec3 && domain->nsec3->nsec3_is_exact 1000 && nsec3_domain_part_of_zone(domain->nsec3->nsec3_cover, 1001 query->zone)) { 1002 nsec3_add_rrset(query, answer, AUTHORITY_SECTION, 1003 domain->nsec3->nsec3_cover); 1004 } else { 1005 /* prove closest provable encloser */ 1006 domain_type* par = domain->parent; 1007 domain_type* prev_par = 0; 1008 1009 while(par && (!par->nsec3 || !par->nsec3->nsec3_is_exact)) 1010 { 1011 prev_par = par; 1012 par = par->parent; 1013 } 1014 assert(par); /* parent zone apex must be provable, thus this ends */ 1015 if(!par->nsec3) return; 1016 nsec3_add_rrset(query, answer, AUTHORITY_SECTION, 1017 par->nsec3->nsec3_cover); 1018 /* we took several steps to go to the provable parent, so 1019 the one below it has no exact nsec3, disprove it. 1020 disprove is easy, it has a prehashed cover ptr. */ 1021 if(prev_par && prev_par->nsec3) { 1022 assert(prev_par != domain && 1023 !prev_par->nsec3->nsec3_is_exact); 1024 nsec3_add_rrset(query, answer, AUTHORITY_SECTION, 1025 prev_par->nsec3->nsec3_cover); 1026 } 1027 /* add optout range from parent zone */ 1028 /* note: no check of optout bit, resolver checks it */ 1029 if(domain->nsec3) 1030 nsec3_add_rrset(query, answer, AUTHORITY_SECTION, 1031 domain->nsec3->nsec3_ds_parent_cover); 1032 } 1033 } 1034 1035 void 1036 nsec3_answer_nodata(struct query* query, struct answer* answer, 1037 struct domain* original) 1038 { 1039 if(!query->zone->nsec3_param) 1040 return; 1041 /* nodata when asking for secure delegation */ 1042 if(query->qtype == TYPE_DS) 1043 { 1044 if(original == query->zone->apex) { 1045 /* DS at zone apex, but server not authoritative for parent zone */ 1046 /* so answer at the child zone level */ 1047 if(original->nsec3 && original->nsec3->nsec3_is_exact) 1048 nsec3_add_rrset(query, answer, AUTHORITY_SECTION, 1049 original->nsec3->nsec3_cover); 1050 return; 1051 } 1052 /* query->zone must be the parent zone */ 1053 nsec3_add_ds_proof(query, answer, original, 0); 1054 } 1055 /* the nodata is result from a wildcard match */ 1056 else if (original==original->wildcard_child_closest_match 1057 && label_is_wildcard(dname_name(domain_dname(original)))) { 1058 /* denial for wildcard is already there */ 1059 1060 /* add parent proof to have a closest encloser proof for wildcard parent */ 1061 /* in other words: nsec3 matching closest encloser */ 1062 if(original->parent && original->parent->nsec3 && 1063 original->parent->nsec3->nsec3_is_exact) 1064 nsec3_add_rrset(query, answer, AUTHORITY_SECTION, 1065 original->parent->nsec3->nsec3_cover); 1066 /* proof for wildcard itself */ 1067 /* in other words: nsec3 matching source of synthesis */ 1068 if(original->nsec3) 1069 nsec3_add_rrset(query, answer, AUTHORITY_SECTION, 1070 original->nsec3->nsec3_cover); 1071 } 1072 else { /* add nsec3 to prove rrset does not exist */ 1073 if(original->nsec3) { 1074 if(!original->nsec3->nsec3_is_exact) { 1075 /* go up to an existing parent */ 1076 while(original->parent && original->parent->nsec3 && !original->parent->nsec3->nsec3_is_exact) 1077 original = original->parent; 1078 } 1079 nsec3_add_rrset(query, answer, AUTHORITY_SECTION, 1080 original->nsec3->nsec3_cover); 1081 if(!original->nsec3->nsec3_is_exact) { 1082 if(original->parent && original->parent->nsec3 && original->parent->nsec3->nsec3_is_exact) 1083 nsec3_add_rrset(query, answer, AUTHORITY_SECTION, 1084 original->parent->nsec3->nsec3_cover); 1085 1086 } 1087 } 1088 } 1089 } 1090 1091 void 1092 nsec3_answer_delegation(struct query *query, struct answer *answer) 1093 { 1094 if(!query->zone->nsec3_param) 1095 return; 1096 nsec3_add_ds_proof(query, answer, query->delegation_domain, 1); 1097 } 1098 1099 int 1100 domain_has_only_NSEC3(struct domain* domain, struct zone* zone) 1101 { 1102 /* check for only NSEC3/RRSIG */ 1103 rrset_type* rrset = domain->rrsets; 1104 int nsec3_seen = 0; 1105 while(rrset) 1106 { 1107 if(!zone || rrset->zone == zone) 1108 { 1109 if(rrset->rrs[0].type == TYPE_NSEC3) 1110 nsec3_seen = 1; 1111 else if(rrset->rrs[0].type != TYPE_RRSIG) 1112 return 0; 1113 } 1114 rrset = rrset->next; 1115 } 1116 return nsec3_seen; 1117 } 1118 1119 void 1120 nsec3_answer_authoritative(struct domain** match, struct query *query, 1121 struct answer *answer, struct domain* closest_encloser, 1122 const dname_type* qname) 1123 { 1124 if(!query->zone->nsec3_param) 1125 return; 1126 assert(match); 1127 /* there is a match, this has 1 RRset, which is NSEC3, but qtype is not. */ 1128 /* !is_existing: no RR types exist at the QNAME, nor at any descendant of QNAME */ 1129 if(*match && !(*match)->is_existing && 1130 #if 0 1131 query->qtype != TYPE_NSEC3 && 1132 #endif 1133 domain_has_only_NSEC3(*match, query->zone)) 1134 { 1135 /* act as if the NSEC3 domain did not exist, name error */ 1136 *match = 0; 1137 /* all nsec3s are directly below the apex, that is closest encloser */ 1138 if(query->zone->apex->nsec3 && 1139 query->zone->apex->nsec3->nsec3_is_exact) 1140 nsec3_add_rrset(query, answer, AUTHORITY_SECTION, 1141 query->zone->apex->nsec3->nsec3_cover); 1142 /* disprove the nsec3 record. */ 1143 if(closest_encloser->nsec3) 1144 nsec3_add_rrset(query, answer, AUTHORITY_SECTION, closest_encloser->nsec3->nsec3_cover); 1145 /* disprove a wildcard */ 1146 if(query->zone->apex->nsec3) 1147 nsec3_add_rrset(query, answer, AUTHORITY_SECTION, 1148 query->zone->apex->nsec3->nsec3_wcard_child_cover); 1149 if (domain_wildcard_child(query->zone->apex)) { 1150 /* wildcard exists below the domain */ 1151 /* wildcard and nsec3 domain clash. server failure. */ 1152 RCODE_SET(query->packet, RCODE_SERVFAIL); 1153 } 1154 return; 1155 } 1156 else if(*match && (*match)->is_existing && 1157 #if 0 1158 query->qtype != TYPE_NSEC3 && 1159 #endif 1160 (domain_has_only_NSEC3(*match, query->zone) || 1161 !domain_find_any_rrset(*match, query->zone))) 1162 { 1163 /* this looks like a NSEC3 domain, but is actually an empty non-terminal. */ 1164 nsec3_answer_nodata(query, answer, *match); 1165 return; 1166 } 1167 if(!*match) { 1168 /* name error, domain does not exist */ 1169 nsec3_add_closest_encloser_proof(query, answer, closest_encloser, 1170 qname); 1171 if(closest_encloser->nsec3) 1172 nsec3_add_rrset(query, answer, AUTHORITY_SECTION, 1173 closest_encloser->nsec3->nsec3_wcard_child_cover); 1174 } 1175 } 1176 1177 #endif /* NSEC3 */ 1178