dbaccess.c revision 1.4
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
34static time_t udb_time = 0;
35static unsigned long udb_rrsets = 0;
36static unsigned long udb_rrset_count = 0;
37
38void
39namedb_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
52void
53namedb_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
64void
65apex_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 accordingly */
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 */
106static void
107read_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 */
130static uint16_t
131calculate_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 */
145static void
146read_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_array(
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 */
174static 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. */
206static 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 */
233static void
234read_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 */
248zone_type*
249namedb_zone_create(namedb_type* db, const dname_type* dname,
250	struct zone_options* 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.tv_sec = 0;
275	zone->mtime.tv_nsec = 0;
276	zone->zonestatid = 0;
277	zone->is_secure = 0;
278	zone->is_changed = 0;
279	zone->is_ok = 1;
280	return zone;
281}
282
283void
284namedb_zone_delete(namedb_type* db, zone_type* zone)
285{
286	/* RRs and UDB and NSEC3 and so on must be already deleted */
287	radix_delete(db->zonetree, zone->node);
288
289	/* see if apex can be deleted */
290	if(zone->apex) {
291		zone->apex->usage --;
292		zone->apex->is_apex = 0;
293		if(zone->apex->usage == 0) {
294			/* delete the apex, possibly */
295			domain_table_deldomain(db, zone->apex);
296		}
297	}
298
299	/* soa_rrset is freed when the SOA was deleted */
300	if(zone->soa_nx_rrset) {
301		region_recycle(db->region, zone->soa_nx_rrset->rrs,
302			sizeof(rr_type));
303		region_recycle(db->region, zone->soa_nx_rrset,
304			sizeof(rrset_type));
305	}
306#ifdef NSEC3
307	hash_tree_delete(db->region, zone->nsec3tree);
308	hash_tree_delete(db->region, zone->hashtree);
309	hash_tree_delete(db->region, zone->wchashtree);
310	hash_tree_delete(db->region, zone->dshashtree);
311#endif
312	if(zone->filename)
313		region_recycle(db->region, zone->filename,
314			strlen(zone->filename)+1);
315	if(zone->logstr)
316		region_recycle(db->region, zone->logstr,
317			strlen(zone->logstr)+1);
318	region_recycle(db->region, zone, sizeof(zone_type));
319}
320
321#ifdef HAVE_MMAP
322/** read a zone */
323static void
324read_zone(udb_base* udb, namedb_type* db, struct nsd_options* opt,
325	region_type* dname_region, udb_ptr* z)
326{
327	/* construct dname */
328	const dname_type* dname = dname_make(dname_region, ZONE(z)->name, 0);
329	struct zone_options* zo = dname?zone_options_find(opt, dname):NULL;
330	zone_type* zone;
331	if(!dname) return;
332	if(!zo) {
333		/* deleted from the options, remove it from the nsd.db too */
334		VERBOSITY(2, (LOG_WARNING, "zone %s is deleted",
335			dname_to_string(dname, NULL)));
336		udb_zone_delete(udb, z);
337		region_free_all(dname_region);
338		return;
339	}
340	assert(udb_ptr_get_type(z) == udb_chunk_type_zone);
341	udb_rrsets = 0;
342	udb_rrset_count = ZONE(z)->rrset_count;
343	zone = namedb_zone_create(db, dname, zo);
344	region_free_all(dname_region);
345	read_zone_data(udb, db, dname_region, z, zone);
346	zone->is_changed = (ZONE(z)->is_changed != 0);
347#ifdef NSEC3
348	prehash_zone_complete(db, zone);
349#endif
350}
351#endif /* HAVE_MMAP */
352
353#ifdef HAVE_MMAP
354/** read zones from nsd.db */
355static void
356read_zones(udb_base* udb, namedb_type* db, struct nsd_options* opt,
357	region_type* dname_region)
358{
359	udb_ptr ztree, n, z;
360	udb_ptr_init(&z, udb);
361	udb_ptr_new(&ztree, udb, udb_base_get_userdata(udb));
362	udb_radix_first(udb,&ztree,&n);
363	udb_time = time(NULL);
364	while(n.data) {
365		udb_ptr_set_rptr(&z, udb, &RADNODE(&n)->elem);
366		udb_radix_next(udb, &n); /* store in case n is deleted */
367		read_zone(udb, db, opt, dname_region, &z);
368		udb_ptr_zero(&z, udb);
369		if(nsd.signal_hint_shutdown) break;
370	}
371	udb_ptr_unlink(&ztree, udb);
372	udb_ptr_unlink(&n, udb);
373	udb_ptr_unlink(&z, udb);
374}
375#endif /* HAVE_MMAP */
376
377#ifdef HAVE_MMAP
378/** try to read the udb file or fail */
379static int
380try_read_udb(namedb_type* db, int fd, const char* filename,
381	struct nsd_options* opt)
382{
383	/*
384	 * Temporary region used while loading domain names from the
385	 * database.  The region is freed after each time a dname is
386	 * read from the database.
387	 */
388	region_type* dname_region;
389
390	assert(fd != -1);
391	if(!(db->udb=udb_base_create_fd(filename, fd, &namedb_walkfunc,
392		NULL))) {
393		/* fd is closed by failed udb create call */
394		VERBOSITY(1, (LOG_WARNING, "can not use %s, "
395			"will create anew", filename));
396		return 0;
397	}
398	/* sanity check if can be opened */
399	if(udb_base_get_userflags(db->udb) != 0) {
400		log_msg(LOG_WARNING, "%s was not closed properly, it might "
401			"be corrupted, will create anew", filename);
402		udb_base_free(db->udb);
403		db->udb = NULL;
404		return 0;
405	}
406	/* read if it can be opened */
407	dname_region = region_create(xalloc, free);
408	/* this operation does not fail, we end up with
409	 * something, even if that is an empty namedb */
410	read_zones(db->udb, db, opt, dname_region);
411	region_destroy(dname_region);
412	return 1;
413}
414#endif /* HAVE_MMAP */
415
416struct namedb *
417namedb_open (const char* filename, struct nsd_options* opt)
418{
419	namedb_type* db;
420
421	/*
422	 * Region used to store the loaded database.  The region is
423	 * freed in namedb_close.
424	 */
425	region_type* db_region;
426	int fd;
427
428#ifdef USE_MMAP_ALLOC
429	db_region = region_create_custom(mmap_alloc, mmap_free, MMAP_ALLOC_CHUNK_SIZE,
430		MMAP_ALLOC_LARGE_OBJECT_SIZE, MMAP_ALLOC_INITIAL_CLEANUP_SIZE, 1);
431#else /* !USE_MMAP_ALLOC */
432	db_region = region_create_custom(xalloc, free, DEFAULT_CHUNK_SIZE,
433		DEFAULT_LARGE_OBJECT_SIZE, DEFAULT_INITIAL_CLEANUP_SIZE, 1);
434#endif /* !USE_MMAP_ALLOC */
435	db = (namedb_type *) region_alloc(db_region, sizeof(struct namedb));
436	db->region = db_region;
437	db->domains = domain_table_create(db->region);
438	db->zonetree = radix_tree_create(db->region);
439	db->diff_skip = 0;
440	db->diff_pos = 0;
441	zonec_setup_parser(db);
442
443	if (gettimeofday(&(db->diff_timestamp), NULL) != 0) {
444		log_msg(LOG_ERR, "unable to load %s: cannot initialize"
445				 "timestamp", filename);
446		region_destroy(db_region);
447		return NULL;
448        }
449
450	/* in dbless mode there is no file to read or mmap */
451	if(filename == NULL || filename[0] == 0) {
452		db->udb = NULL;
453		return db;
454	}
455
456#ifndef HAVE_MMAP
457	/* no mmap() system call, use dbless mode */
458	VERBOSITY(1, (LOG_INFO, "no mmap(), ignoring database %s", filename));
459	db->udb = NULL;
460	(void)fd; (void)opt;
461	return db;
462#else /* HAVE_MMAP */
463
464	/* attempt to open, if does not exist, create a new one */
465	fd = open(filename, O_RDWR);
466	if(fd == -1) {
467		if(errno != ENOENT) {
468			log_msg(LOG_ERR, "%s: %s", filename, strerror(errno));
469			region_destroy(db_region);
470			return NULL;
471		}
472	}
473	/* attempt to read the file (if it exists) */
474	if(fd != -1) {
475		if(!try_read_udb(db, fd, filename, opt))
476			fd = -1;
477	}
478	/* attempt to create the file (if necessary or failed read) */
479	if(fd == -1) {
480		if(!(db->udb=udb_base_create_new(filename, &namedb_walkfunc,
481			NULL))) {
482			region_destroy(db_region);
483			return NULL;
484		}
485		if(!udb_dns_init_file(db->udb)) {
486			region_destroy(db->region);
487			return NULL;
488		}
489	}
490	return db;
491#endif /* HAVE_MMAP */
492}
493
494/** the the file mtime stat (or nonexist or error) */
495int
496file_get_mtime(const char* file, struct timespec* mtime, int* nonexist)
497{
498	struct stat s;
499	if(stat(file, &s) != 0) {
500		mtime->tv_sec = 0;
501		mtime->tv_nsec = 0;
502		*nonexist = (errno == ENOENT);
503		return 0;
504	}
505	*nonexist = 0;
506	mtime->tv_sec = s.st_mtime;
507#ifdef HAVE_STRUCT_STAT_ST_MTIMENSEC
508	mtime->tv_nsec = s.st_mtimensec;
509#elif defined(HAVE_STRUCT_STAT_ST_MTIM_TV_NSEC)
510	mtime->tv_nsec = s.st_mtim.tv_nsec;
511#else
512	mtime->tv_nsec = 0;
513#endif
514	return 1;
515}
516
517void
518namedb_read_zonefile(struct nsd* nsd, struct zone* zone, udb_base* taskudb,
519	udb_ptr* last_task)
520{
521	struct timespec mtime;
522	int nonexist = 0;
523	unsigned int errors;
524	const char* fname;
525	if(!nsd->db || !zone || !zone->opts || !zone->opts->pattern->zonefile)
526		return;
527	mtime.tv_sec = 0;
528	mtime.tv_nsec = 0;
529	fname = config_make_zonefile(zone->opts, nsd);
530	if(!file_get_mtime(fname, &mtime, &nonexist)) {
531		if(nonexist) {
532			VERBOSITY(2, (LOG_INFO, "zonefile %s does not exist",
533				fname));
534		} else
535			log_msg(LOG_ERR, "zonefile %s: %s",
536				fname, strerror(errno));
537		if(taskudb) task_new_soainfo(taskudb, last_task, zone, 0);
538		return;
539	} else {
540		const char* zone_fname = zone->filename;
541		struct timespec zone_mtime = zone->mtime;
542		if(nsd->db->udb) {
543			zone_fname = udb_zone_get_file_str(nsd->db->udb,
544				dname_name(domain_dname(zone->apex)),
545				domain_dname(zone->apex)->name_size);
546			udb_zone_get_mtime(nsd->db->udb,
547				dname_name(domain_dname(zone->apex)),
548				domain_dname(zone->apex)->name_size,
549				&zone_mtime);
550		}
551		/* if no zone_fname, then it was acquired in zone transfer,
552		 * see if the file is newer than the zone transfer
553		 * (regardless if this is a different file), because the
554		 * zone transfer is a different content source too */
555		if(!zone_fname && timespec_compare(&zone_mtime, &mtime) >= 0) {
556			VERBOSITY(3, (LOG_INFO, "zonefile %s is older than "
557				"zone transfer in memory", fname));
558			return;
559
560		/* if zone_fname, then the file was acquired from reading it,
561		 * and see if filename changed or mtime newer to read it */
562		} else if(zone_fname && fname &&
563		   strcmp(zone_fname, fname) == 0 &&
564		   timespec_compare(&zone_mtime, &mtime) == 0) {
565			VERBOSITY(3, (LOG_INFO, "zonefile %s is not modified",
566				fname));
567			return;
568		}
569	}
570
571	assert(parser);
572	/* wipe zone from memory */
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	errors = zonec_read(zone->opts->name, fname, zone);
582	if(errors > 0) {
583		log_msg(LOG_ERR, "zone %s file %s read with %u errors",
584			zone->opts->name, fname, errors);
585		/* wipe (partial) zone from memory */
586		zone->is_ok = 1;
587#ifdef NSEC3
588		nsec3_hash_tree_clear(zone);
589#endif
590		delete_zone_rrs(nsd->db, zone);
591#ifdef NSEC3
592		nsec3_clear_precompile(nsd->db, zone);
593		zone->nsec3_param = NULL;
594#endif /* NSEC3 */
595		if(nsd->db->udb) {
596			region_type* dname_region;
597			udb_ptr z;
598			/* see if we can revert to the udb stored version */
599			if(!udb_zone_search(nsd->db->udb, &z, dname_name(domain_dname(
600				zone->apex)), domain_dname(zone->apex)->name_size)) {
601				/* tell that zone contents has been lost */
602				if(taskudb) task_new_soainfo(taskudb, last_task, zone, 0);
603				return;
604			}
605			/* read from udb */
606			dname_region = region_create(xalloc, free);
607			udb_rrsets = 0;
608			udb_rrset_count = ZONE(&z)->rrset_count;
609			udb_time = time(NULL);
610			read_zone_data(nsd->db->udb, nsd->db, dname_region, &z, zone);
611			region_destroy(dname_region);
612			udb_ptr_unlink(&z, nsd->db->udb);
613		} else {
614			if(zone->filename)
615				region_recycle(nsd->db->region, zone->filename,
616					strlen(zone->filename)+1);
617			zone->filename = NULL;
618			if(zone->logstr)
619				region_recycle(nsd->db->region, zone->logstr,
620					strlen(zone->logstr)+1);
621			zone->logstr = NULL;
622		}
623	} else {
624		VERBOSITY(1, (LOG_INFO, "zone %s read with success",
625			zone->opts->name));
626		zone->is_ok = 1;
627		zone->is_changed = 0;
628		/* store zone into udb */
629		if(nsd->db->udb) {
630			if(!write_zone_to_udb(nsd->db->udb, zone, &mtime,
631				fname)) {
632				log_msg(LOG_ERR, "failed to store zone in db");
633			} else {
634				VERBOSITY(2, (LOG_INFO, "zone %s written to db",
635					zone->opts->name));
636			}
637		} else {
638			zone->mtime = mtime;
639			if(zone->filename)
640				region_recycle(nsd->db->region, zone->filename,
641					strlen(zone->filename)+1);
642			zone->filename = region_strdup(nsd->db->region, fname);
643			if(zone->logstr)
644				region_recycle(nsd->db->region, zone->logstr,
645					strlen(zone->logstr)+1);
646			zone->logstr = NULL;
647		}
648	}
649	if(taskudb) task_new_soainfo(taskudb, last_task, zone, 0);
650#ifdef NSEC3
651	prehash_zone_complete(nsd->db, zone);
652#endif
653}
654
655void namedb_check_zonefile(struct nsd* nsd, udb_base* taskudb,
656	udb_ptr* last_task, struct zone_options* zopt)
657{
658	zone_type* zone;
659	const dname_type* dname = (const dname_type*)zopt->node.key;
660	/* find zone to go with it, or create it */
661	zone = namedb_find_zone(nsd->db, dname);
662	if(!zone) {
663		zone = namedb_zone_create(nsd->db, dname, zopt);
664	}
665	namedb_read_zonefile(nsd, zone, taskudb, last_task);
666}
667
668void namedb_check_zonefiles(struct nsd* nsd, struct nsd_options* opt,
669	udb_base* taskudb, udb_ptr* last_task)
670{
671	struct zone_options* zo;
672	/* check all zones in opt, create if not exist in main db */
673	RBTREE_FOR(zo, struct zone_options*, opt->zone_options) {
674		namedb_check_zonefile(nsd, taskudb, last_task, zo);
675		if(nsd->signal_hint_shutdown) break;
676	}
677}
678