1 /* 2 * dbaccess.c -- access methods for nsd(8) database 3 * 4 * Copyright (c) 2001-2006, NLnet Labs. All rights reserved. 5 * 6 * See LICENSE for the license. 7 * 8 */ 9 10 #include "config.h" 11 12 #include <sys/types.h> 13 #include <sys/stat.h> 14 15 #include <errno.h> 16 #include <stdlib.h> 17 #include <string.h> 18 #include <unistd.h> 19 #include <fcntl.h> 20 21 #include "dns.h" 22 #include "namedb.h" 23 #include "util.h" 24 #include "options.h" 25 #include "rdata.h" 26 #include "udb.h" 27 #include "udbradtree.h" 28 #include "udbzone.h" 29 #include "zonec.h" 30 #include "nsec3.h" 31 #include "difffile.h" 32 #include "nsd.h" 33 34 static time_t udb_time = 0; 35 static unsigned long udb_rrsets = 0; 36 static unsigned long udb_rrset_count = 0; 37 38 void 39 namedb_close(struct namedb* db) 40 { 41 if(db) { 42 if(db->udb) { 43 udb_base_close(db->udb); 44 udb_base_free(db->udb); 45 db->udb = NULL; 46 } 47 zonec_desetup_parser(); 48 region_destroy(db->region); 49 } 50 } 51 52 void 53 namedb_close_udb(struct namedb* db) 54 { 55 if(db) { 56 /* we cannot actually munmap the data, because other 57 * processes still need to access the udb, so cleanup the 58 * udb */ 59 udb_base_free_keep_mmap(db->udb); 60 db->udb = NULL; 61 } 62 } 63 64 void 65 apex_rrset_checks(namedb_type* db, rrset_type* rrset, domain_type* domain) 66 { 67 uint32_t soa_minimum; 68 unsigned i; 69 zone_type* zone = rrset->zone; 70 assert(domain == zone->apex); 71 (void)domain; 72 if (rrset_rrtype(rrset) == TYPE_SOA) { 73 zone->soa_rrset = rrset; 74 75 /* BUG #103 add another soa with a tweaked ttl */ 76 if(zone->soa_nx_rrset == 0) { 77 zone->soa_nx_rrset = region_alloc(db->region, 78 sizeof(rrset_type)); 79 zone->soa_nx_rrset->rr_count = 1; 80 zone->soa_nx_rrset->next = 0; 81 zone->soa_nx_rrset->zone = zone; 82 zone->soa_nx_rrset->rrs = region_alloc(db->region, 83 sizeof(rr_type)); 84 } 85 memcpy(zone->soa_nx_rrset->rrs, rrset->rrs, sizeof(rr_type)); 86 87 /* check the ttl and MINIMUM value and set accordinly */ 88 memcpy(&soa_minimum, rdata_atom_data(rrset->rrs->rdatas[6]), 89 rdata_atom_size(rrset->rrs->rdatas[6])); 90 if (rrset->rrs->ttl > ntohl(soa_minimum)) { 91 zone->soa_nx_rrset->rrs[0].ttl = ntohl(soa_minimum); 92 } 93 } else if (rrset_rrtype(rrset) == TYPE_NS) { 94 zone->ns_rrset = rrset; 95 } else if (rrset_rrtype(rrset) == TYPE_RRSIG) { 96 for (i = 0; i < rrset->rr_count; ++i) { 97 if(rr_rrsig_type_covered(&rrset->rrs[i])==TYPE_DNSKEY){ 98 zone->is_secure = 1; 99 break; 100 } 101 } 102 } 103 } 104 105 /** read rr */ 106 static void 107 read_rr(namedb_type* db, rr_type* rr, udb_ptr* urr, domain_type* domain) 108 { 109 buffer_type buffer; 110 ssize_t c; 111 assert(udb_ptr_get_type(urr) == udb_chunk_type_rr); 112 rr->owner = domain; 113 rr->type = RR(urr)->type; 114 rr->klass = RR(urr)->klass; 115 rr->ttl = RR(urr)->ttl; 116 117 buffer_create_from(&buffer, RR(urr)->wire, RR(urr)->len); 118 c = rdata_wireformat_to_rdata_atoms(db->region, db->domains, 119 rr->type, RR(urr)->len, &buffer, &rr->rdatas); 120 if(c == -1) { 121 /* safe on error */ 122 rr->rdata_count = 0; 123 rr->rdatas = NULL; 124 return; 125 } 126 rr->rdata_count = c; 127 } 128 129 /** calculate rr count */ 130 static uint16_t 131 calculate_rr_count(udb_base* udb, udb_ptr* rrset) 132 { 133 udb_ptr rr; 134 uint16_t num = 0; 135 udb_ptr_new(&rr, udb, &RRSET(rrset)->rrs); 136 while(rr.data) { 137 num++; 138 udb_ptr_set_rptr(&rr, udb, &RR(&rr)->next); 139 } 140 udb_ptr_unlink(&rr, udb); 141 return num; 142 } 143 144 /** read rrset */ 145 static void 146 read_rrset(udb_base* udb, namedb_type* db, zone_type* zone, 147 domain_type* domain, udb_ptr* urrset) 148 { 149 rrset_type* rrset; 150 udb_ptr urr; 151 unsigned i; 152 assert(udb_ptr_get_type(urrset) == udb_chunk_type_rrset); 153 /* if no RRs, do not create anything (robust) */ 154 if(RRSET(urrset)->rrs.data == 0) 155 return; 156 rrset = (rrset_type *) region_alloc(db->region, sizeof(rrset_type)); 157 rrset->zone = zone; 158 rrset->rr_count = calculate_rr_count(udb, urrset); 159 rrset->rrs = (rr_type *) region_alloc( 160 db->region, rrset->rr_count * sizeof(rr_type)); 161 /* add the RRs */ 162 udb_ptr_new(&urr, udb, &RRSET(urrset)->rrs); 163 for(i=0; i<rrset->rr_count; i++) { 164 read_rr(db, &rrset->rrs[i], &urr, domain); 165 udb_ptr_set_rptr(&urr, udb, &RR(&urr)->next); 166 } 167 udb_ptr_unlink(&urr, udb); 168 domain_add_rrset(domain, rrset); 169 if(domain == zone->apex) 170 apex_rrset_checks(db, rrset, domain); 171 } 172 173 /** read one elem from db, of type domain_d */ 174 static void read_node_elem(udb_base* udb, namedb_type* db, 175 region_type* dname_region, zone_type* zone, struct domain_d* d) 176 { 177 const dname_type* dname; 178 domain_type* domain; 179 udb_ptr urrset; 180 181 dname = dname_make(dname_region, d->name, 0); 182 if(!dname) return; 183 domain = domain_table_insert(db->domains, dname); 184 assert(domain); /* domain_table_insert should always return non-NULL */ 185 186 /* add rrsets */ 187 udb_ptr_init(&urrset, udb); 188 udb_ptr_set_rptr(&urrset, udb, &d->rrsets); 189 while(urrset.data) { 190 read_rrset(udb, db, zone, domain, &urrset); 191 udb_ptr_set_rptr(&urrset, udb, &RRSET(&urrset)->next); 192 193 if(++udb_rrsets % ZONEC_PCT_COUNT == 0 && time(NULL) > udb_time + ZONEC_PCT_TIME) { 194 udb_time = time(NULL); 195 VERBOSITY(1, (LOG_INFO, "read %s %d %%", 196 zone->opts->name, 197 (int)(udb_rrsets*((unsigned long)100)/udb_rrset_count))); 198 } 199 } 200 region_free_all(dname_region); 201 udb_ptr_unlink(&urrset, udb); 202 } 203 204 /** recurse read radix from disk. This radix tree is by domain name, so max of 205 * 256 depth, and thus the stack usage is small. */ 206 static void read_zone_recurse(udb_base* udb, namedb_type* db, 207 region_type* dname_region, zone_type* zone, struct udb_radnode_d* node) 208 { 209 if(node->elem.data) { 210 /* pre-order process of node->elem, for radix tree this is 211 * also in-order processing (identical to order tree_next()) */ 212 read_node_elem(udb, db, dname_region, zone, (struct domain_d*) 213 (udb->base + node->elem.data)); 214 } 215 if(node->lookup.data) { 216 uint16_t i; 217 struct udb_radarray_d* a = (struct udb_radarray_d*) 218 (udb->base + node->lookup.data); 219 /* we do not care for what the exact radix key is, we want 220 * to add all of them and the read routine does not need 221 * the radix-key, it has it stored */ 222 for(i=0; i<a->len; i++) { 223 if(a->array[i].node.data) { 224 read_zone_recurse(udb, db, dname_region, zone, 225 (struct udb_radnode_d*)(udb->base + 226 a->array[i].node.data)); 227 } 228 } 229 } 230 } 231 232 /** read zone data */ 233 static void 234 read_zone_data(udb_base* udb, namedb_type* db, region_type* dname_region, 235 udb_ptr* z, zone_type* zone) 236 { 237 udb_ptr dtree; 238 /* recursively read domains, we only read so ptrs stay valid */ 239 udb_ptr_new(&dtree, udb, &ZONE(z)->domains); 240 if(RADTREE(&dtree)->root.data) 241 read_zone_recurse(udb, db, dname_region, zone, 242 (struct udb_radnode_d*) 243 (udb->base + RADTREE(&dtree)->root.data)); 244 udb_ptr_unlink(&dtree, udb); 245 } 246 247 /** create a zone */ 248 zone_type* 249 namedb_zone_create(namedb_type* db, const dname_type* dname, 250 zone_options_t* zo) 251 { 252 zone_type* zone = (zone_type *) region_alloc(db->region, 253 sizeof(zone_type)); 254 zone->node = radname_insert(db->zonetree, dname_name(dname), 255 dname->name_size, zone); 256 assert(zone->node); 257 zone->apex = domain_table_insert(db->domains, dname); 258 zone->apex->usage++; /* the zone.apex reference */ 259 zone->apex->is_apex = 1; 260 zone->soa_rrset = NULL; 261 zone->soa_nx_rrset = NULL; 262 zone->ns_rrset = NULL; 263 #ifdef NSEC3 264 zone->nsec3_param = NULL; 265 zone->nsec3_last = NULL; 266 zone->nsec3tree = NULL; 267 zone->hashtree = NULL; 268 zone->wchashtree = NULL; 269 zone->dshashtree = NULL; 270 #endif 271 zone->opts = zo; 272 zone->filename = NULL; 273 zone->logstr = NULL; 274 zone->mtime = 0; 275 zone->zonestatid = 0; 276 zone->is_secure = 0; 277 zone->is_changed = 0; 278 zone->is_ok = 1; 279 return zone; 280 } 281 282 void 283 namedb_zone_delete(namedb_type* db, zone_type* zone) 284 { 285 /* RRs and UDB and NSEC3 and so on must be already deleted */ 286 radix_delete(db->zonetree, zone->node); 287 288 /* see if apex can be deleted */ 289 if(zone->apex) { 290 zone->apex->usage --; 291 if(zone->apex->usage == 0) { 292 /* delete the apex, possibly */ 293 domain_table_deldomain(db, zone->apex); 294 } 295 } 296 297 /* soa_rrset is freed when the SOA was deleted */ 298 if(zone->soa_nx_rrset) { 299 region_recycle(db->region, zone->soa_nx_rrset->rrs, 300 sizeof(rr_type)); 301 region_recycle(db->region, zone->soa_nx_rrset, 302 sizeof(rrset_type)); 303 } 304 #ifdef NSEC3 305 hash_tree_delete(db->region, zone->nsec3tree); 306 hash_tree_delete(db->region, zone->hashtree); 307 hash_tree_delete(db->region, zone->wchashtree); 308 hash_tree_delete(db->region, zone->dshashtree); 309 #endif 310 if(zone->filename) 311 region_recycle(db->region, zone->filename, 312 strlen(zone->filename)+1); 313 if(zone->logstr) 314 region_recycle(db->region, zone->logstr, 315 strlen(zone->logstr)+1); 316 region_recycle(db->region, zone, sizeof(zone_type)); 317 } 318 319 #ifdef HAVE_MMAP 320 /** read a zone */ 321 static void 322 read_zone(udb_base* udb, namedb_type* db, nsd_options_t* opt, 323 region_type* dname_region, udb_ptr* z) 324 { 325 /* construct dname */ 326 const dname_type* dname = dname_make(dname_region, ZONE(z)->name, 0); 327 zone_options_t* zo = dname?zone_options_find(opt, dname):NULL; 328 zone_type* zone; 329 if(!dname) return; 330 if(!zo) { 331 /* deleted from the options, remove it from the nsd.db too */ 332 VERBOSITY(2, (LOG_WARNING, "zone %s is deleted", 333 dname_to_string(dname, NULL))); 334 udb_zone_delete(udb, z); 335 region_free_all(dname_region); 336 return; 337 } 338 assert(udb_ptr_get_type(z) == udb_chunk_type_zone); 339 udb_rrsets = 0; 340 udb_rrset_count = ZONE(z)->rrset_count; 341 zone = namedb_zone_create(db, dname, zo); 342 region_free_all(dname_region); 343 read_zone_data(udb, db, dname_region, z, zone); 344 zone->is_changed = (ZONE(z)->is_changed != 0); 345 #ifdef NSEC3 346 prehash_zone_complete(db, zone); 347 #endif 348 } 349 #endif /* HAVE_MMAP */ 350 351 #ifdef HAVE_MMAP 352 /** read zones from nsd.db */ 353 static void 354 read_zones(udb_base* udb, namedb_type* db, nsd_options_t* opt, 355 region_type* dname_region) 356 { 357 udb_ptr ztree, n, z; 358 udb_ptr_init(&z, udb); 359 udb_ptr_new(&ztree, udb, udb_base_get_userdata(udb)); 360 udb_radix_first(udb,&ztree,&n); 361 udb_time = time(NULL); 362 while(n.data) { 363 udb_ptr_set_rptr(&z, udb, &RADNODE(&n)->elem); 364 udb_radix_next(udb, &n); /* store in case n is deleted */ 365 read_zone(udb, db, opt, dname_region, &z); 366 udb_ptr_zero(&z, udb); 367 if(nsd.signal_hint_shutdown) break; 368 } 369 udb_ptr_unlink(&ztree, udb); 370 udb_ptr_unlink(&n, udb); 371 udb_ptr_unlink(&z, udb); 372 } 373 #endif /* HAVE_MMAP */ 374 375 #ifdef HAVE_MMAP 376 /** try to read the udb file or fail */ 377 static int 378 try_read_udb(namedb_type* db, int fd, const char* filename, 379 nsd_options_t* opt) 380 { 381 /* 382 * Temporary region used while loading domain names from the 383 * database. The region is freed after each time a dname is 384 * read from the database. 385 */ 386 region_type* dname_region; 387 388 assert(fd != -1); 389 if(!(db->udb=udb_base_create_fd(filename, fd, &namedb_walkfunc, 390 NULL))) { 391 /* fd is closed by failed udb create call */ 392 VERBOSITY(1, (LOG_WARNING, "can not use %s, " 393 "will create anew", filename)); 394 return 0; 395 } 396 /* sanity check if can be opened */ 397 if(udb_base_get_userflags(db->udb) != 0) { 398 log_msg(LOG_WARNING, "%s was not closed properly, it might " 399 "be corrupted, will create anew", filename); 400 udb_base_free(db->udb); 401 db->udb = NULL; 402 return 0; 403 } 404 /* read if it can be opened */ 405 dname_region = region_create(xalloc, free); 406 /* this operation does not fail, we end up with 407 * something, even if that is an empty namedb */ 408 read_zones(db->udb, db, opt, dname_region); 409 region_destroy(dname_region); 410 return 1; 411 } 412 #endif /* HAVE_MMAP */ 413 414 struct namedb * 415 namedb_open (const char* filename, nsd_options_t* opt) 416 { 417 namedb_type* db; 418 419 /* 420 * Region used to store the loaded database. The region is 421 * freed in namedb_close. 422 */ 423 region_type* db_region; 424 int fd; 425 426 #ifdef USE_MMAP_ALLOC 427 db_region = region_create_custom(mmap_alloc, mmap_free, MMAP_ALLOC_CHUNK_SIZE, 428 MMAP_ALLOC_LARGE_OBJECT_SIZE, MMAP_ALLOC_INITIAL_CLEANUP_SIZE, 1); 429 #else /* !USE_MMAP_ALLOC */ 430 db_region = region_create_custom(xalloc, free, DEFAULT_CHUNK_SIZE, 431 DEFAULT_LARGE_OBJECT_SIZE, DEFAULT_INITIAL_CLEANUP_SIZE, 1); 432 #endif /* !USE_MMAP_ALLOC */ 433 db = (namedb_type *) region_alloc(db_region, sizeof(struct namedb)); 434 db->region = db_region; 435 db->domains = domain_table_create(db->region); 436 db->zonetree = radix_tree_create(db->region); 437 db->diff_skip = 0; 438 db->diff_pos = 0; 439 zonec_setup_parser(db); 440 441 if (gettimeofday(&(db->diff_timestamp), NULL) != 0) { 442 log_msg(LOG_ERR, "unable to load %s: cannot initialize" 443 "timestamp", filename); 444 region_destroy(db_region); 445 return NULL; 446 } 447 448 /* in dbless mode there is no file to read or mmap */ 449 if(filename == NULL || filename[0] == 0) { 450 db->udb = NULL; 451 return db; 452 } 453 454 #ifndef HAVE_MMAP 455 /* no mmap() system call, use dbless mode */ 456 VERBOSITY(1, (LOG_INFO, "no mmap(), ignoring database %s", filename)); 457 db->udb = NULL; 458 (void)fd; (void)opt; 459 return db; 460 #else /* HAVE_MMAP */ 461 462 /* attempt to open, if does not exist, create a new one */ 463 fd = open(filename, O_RDWR); 464 if(fd == -1) { 465 if(errno != ENOENT) { 466 log_msg(LOG_ERR, "%s: %s", filename, strerror(errno)); 467 region_destroy(db_region); 468 return NULL; 469 } 470 } 471 /* attempt to read the file (if it exists) */ 472 if(fd != -1) { 473 if(!try_read_udb(db, fd, filename, opt)) 474 fd = -1; 475 } 476 /* attempt to create the file (if necessary or failed read) */ 477 if(fd == -1) { 478 if(!(db->udb=udb_base_create_new(filename, &namedb_walkfunc, 479 NULL))) { 480 region_destroy(db_region); 481 return NULL; 482 } 483 if(!udb_dns_init_file(db->udb)) { 484 region_destroy(db->region); 485 return NULL; 486 } 487 } 488 return db; 489 #endif /* HAVE_MMAP */ 490 } 491 492 /** the the file mtime stat (or nonexist or error) */ 493 static int 494 file_get_mtime(const char* file, time_t* mtime, int* nonexist) 495 { 496 struct stat s; 497 if(stat(file, &s) != 0) { 498 *mtime = 0; 499 *nonexist = (errno == ENOENT); 500 return 0; 501 } 502 *nonexist = 0; 503 *mtime = s.st_mtime; 504 return 1; 505 } 506 507 void 508 namedb_read_zonefile(struct nsd* nsd, struct zone* zone, udb_base* taskudb, 509 udb_ptr* last_task) 510 { 511 time_t mtime = 0; 512 int nonexist = 0; 513 unsigned int errors; 514 const char* fname; 515 if(!nsd->db || !zone || !zone->opts || !zone->opts->pattern->zonefile) 516 return; 517 fname = config_make_zonefile(zone->opts, nsd); 518 if(!file_get_mtime(fname, &mtime, &nonexist)) { 519 if(nonexist) { 520 VERBOSITY(2, (LOG_INFO, "zonefile %s does not exist", 521 fname)); 522 } else 523 log_msg(LOG_ERR, "zonefile %s: %s", 524 fname, strerror(errno)); 525 if(taskudb) task_new_soainfo(taskudb, last_task, zone, 0); 526 return; 527 } else { 528 const char* zone_fname = zone->filename; 529 time_t zone_mtime = zone->mtime; 530 if(nsd->db->udb) { 531 zone_fname = udb_zone_get_file_str(nsd->db->udb, 532 dname_name(domain_dname(zone->apex)), 533 domain_dname(zone->apex)->name_size); 534 zone_mtime = (time_t)udb_zone_get_mtime(nsd->db->udb, 535 dname_name(domain_dname(zone->apex)), 536 domain_dname(zone->apex)->name_size); 537 } 538 /* if no zone_fname, then it was acquired in zone transfer, 539 * see if the file is newer than the zone transfer 540 * (regardless if this is a different file), because the 541 * zone transfer is a different content source too */ 542 if(!zone_fname && zone_mtime >= mtime) { 543 VERBOSITY(3, (LOG_INFO, "zonefile %s is older than " 544 "zone transfer in memory", fname)); 545 return; 546 547 /* if zone_fname, then the file was acquired from reading it, 548 * and see if filename changed or mtime newer to read it */ 549 } else if(zone_fname && fname && 550 strcmp(zone_fname, fname) == 0 && zone_mtime >= mtime) { 551 VERBOSITY(3, (LOG_INFO, "zonefile %s is not modified", 552 fname)); 553 return; 554 } 555 } 556 557 assert(parser); 558 /* wipe zone from memory */ 559 #ifdef NSEC3 560 nsec3_hash_tree_clear(zone); 561 #endif 562 delete_zone_rrs(nsd->db, zone); 563 #ifdef NSEC3 564 nsec3_clear_precompile(nsd->db, zone); 565 zone->nsec3_param = NULL; 566 #endif /* NSEC3 */ 567 errors = zonec_read(zone->opts->name, fname, zone); 568 if(errors > 0) { 569 log_msg(LOG_ERR, "zone %s file %s read with %u errors", 570 zone->opts->name, fname, errors); 571 /* wipe (partial) zone from memory */ 572 zone->is_ok = 1; 573 #ifdef NSEC3 574 nsec3_hash_tree_clear(zone); 575 #endif 576 delete_zone_rrs(nsd->db, zone); 577 #ifdef NSEC3 578 nsec3_clear_precompile(nsd->db, zone); 579 zone->nsec3_param = NULL; 580 #endif /* NSEC3 */ 581 if(nsd->db->udb) { 582 region_type* dname_region; 583 udb_ptr z; 584 /* see if we can revert to the udb stored version */ 585 if(!udb_zone_search(nsd->db->udb, &z, dname_name(domain_dname( 586 zone->apex)), domain_dname(zone->apex)->name_size)) { 587 /* tell that zone contents has been lost */ 588 if(taskudb) task_new_soainfo(taskudb, last_task, zone, 0); 589 return; 590 } 591 /* read from udb */ 592 dname_region = region_create(xalloc, free); 593 udb_rrsets = 0; 594 udb_rrset_count = ZONE(&z)->rrset_count; 595 udb_time = time(NULL); 596 read_zone_data(nsd->db->udb, nsd->db, dname_region, &z, zone); 597 region_destroy(dname_region); 598 udb_ptr_unlink(&z, nsd->db->udb); 599 } else { 600 if(zone->filename) 601 region_recycle(nsd->db->region, zone->filename, 602 strlen(zone->filename)+1); 603 zone->filename = NULL; 604 if(zone->logstr) 605 region_recycle(nsd->db->region, zone->logstr, 606 strlen(zone->logstr)+1); 607 zone->logstr = NULL; 608 } 609 } else { 610 VERBOSITY(1, (LOG_INFO, "zone %s read with no errors", 611 zone->opts->name)); 612 zone->is_ok = 1; 613 zone->is_changed = 0; 614 /* store zone into udb */ 615 if(nsd->db->udb) { 616 if(!write_zone_to_udb(nsd->db->udb, zone, mtime, fname)) { 617 log_msg(LOG_ERR, "failed to store zone in db"); 618 } else { 619 VERBOSITY(2, (LOG_INFO, "zone %s written to db", 620 zone->opts->name)); 621 } 622 } else { 623 zone->mtime = mtime; 624 if(zone->filename) 625 region_recycle(nsd->db->region, zone->filename, 626 strlen(zone->filename)+1); 627 zone->filename = region_strdup(nsd->db->region, fname); 628 if(zone->logstr) 629 region_recycle(nsd->db->region, zone->logstr, 630 strlen(zone->logstr)+1); 631 zone->logstr = NULL; 632 } 633 } 634 if(taskudb) task_new_soainfo(taskudb, last_task, zone, 0); 635 #ifdef NSEC3 636 prehash_zone_complete(nsd->db, zone); 637 #endif 638 } 639 640 void namedb_check_zonefile(struct nsd* nsd, udb_base* taskudb, 641 udb_ptr* last_task, zone_options_t* zopt) 642 { 643 zone_type* zone; 644 const dname_type* dname = (const dname_type*)zopt->node.key; 645 /* find zone to go with it, or create it */ 646 zone = namedb_find_zone(nsd->db, dname); 647 if(!zone) { 648 zone = namedb_zone_create(nsd->db, dname, zopt); 649 } 650 namedb_read_zonefile(nsd, zone, taskudb, last_task); 651 } 652 653 void namedb_check_zonefiles(struct nsd* nsd, nsd_options_t* opt, 654 udb_base* taskudb, udb_ptr* last_task) 655 { 656 zone_options_t* zo; 657 /* check all zones in opt, create if not exist in main db */ 658 RBTREE_FOR(zo, zone_options_t*, opt->zone_options) { 659 namedb_check_zonefile(nsd, taskudb, last_task, zo); 660 if(nsd->signal_hint_shutdown) break; 661 } 662 } 663