xref: /openbsd-src/usr.sbin/nsd/dbaccess.c (revision b71395ea3d4830c6fd338870b804059761b8292d)
162ac0c33Sjakob /*
262ac0c33Sjakob  * dbaccess.c -- access methods for nsd(8) database
362ac0c33Sjakob  *
4d3fecca9Ssthen  * Copyright (c) 2001-2006, NLnet Labs. All rights reserved.
562ac0c33Sjakob  *
662ac0c33Sjakob  * See LICENSE for the license.
762ac0c33Sjakob  *
862ac0c33Sjakob  */
962ac0c33Sjakob 
10aee1b7aaSsthen #include "config.h"
1162ac0c33Sjakob 
1262ac0c33Sjakob #include <sys/types.h>
1362ac0c33Sjakob #include <sys/stat.h>
1462ac0c33Sjakob 
1562ac0c33Sjakob #include <errno.h>
1662ac0c33Sjakob #include <stdlib.h>
1762ac0c33Sjakob #include <string.h>
1862ac0c33Sjakob #include <unistd.h>
1962ac0c33Sjakob #include <fcntl.h>
2062ac0c33Sjakob 
2162ac0c33Sjakob #include "dns.h"
2262ac0c33Sjakob #include "namedb.h"
2362ac0c33Sjakob #include "util.h"
2462ac0c33Sjakob #include "options.h"
25d3fecca9Ssthen #include "rdata.h"
26d3fecca9Ssthen #include "udb.h"
27d3fecca9Ssthen #include "zonec.h"
28d3fecca9Ssthen #include "nsec3.h"
29d3fecca9Ssthen #include "difffile.h"
30cdb6bbddSbrad #include "nsd.h"
314564029fSflorian #include "ixfr.h"
324564029fSflorian #include "ixfrcreate.h"
3362ac0c33Sjakob 
34d3fecca9Ssthen void
namedb_close(struct namedb * db)35d3fecca9Ssthen namedb_close(struct namedb* db)
3662ac0c33Sjakob {
37d3fecca9Ssthen 	if(db) {
38d3fecca9Ssthen 		zonec_desetup_parser();
39d3fecca9Ssthen 		region_destroy(db->region);
40d3fecca9Ssthen 	}
4162ac0c33Sjakob }
4262ac0c33Sjakob 
43d3fecca9Ssthen void
namedb_free_ixfr(struct namedb * db)444564029fSflorian namedb_free_ixfr(struct namedb* db)
4562ac0c33Sjakob {
464564029fSflorian 	struct radnode* n;
474564029fSflorian 	for(n=radix_first(db->zonetree); n; n=radix_next(n)) {
484564029fSflorian 		zone_ixfr_free(((zone_type*)n->elem)->ixfr);
4962ac0c33Sjakob 	}
5062ac0c33Sjakob }
5162ac0c33Sjakob 
52d3fecca9Ssthen /** create a zone */
53d3fecca9Ssthen zone_type*
namedb_zone_create(namedb_type * db,const dname_type * dname,struct zone_options * zo)54d3fecca9Ssthen namedb_zone_create(namedb_type* db, const dname_type* dname,
55fe5fe5f6Sflorian 	struct zone_options* zo)
56d3fecca9Ssthen {
57d3fecca9Ssthen 	zone_type* zone = (zone_type *) region_alloc(db->region,
58d3fecca9Ssthen 		sizeof(zone_type));
59d3fecca9Ssthen 	zone->node = radname_insert(db->zonetree, dname_name(dname),
60d3fecca9Ssthen 		dname->name_size, zone);
61d3fecca9Ssthen 	assert(zone->node);
62d3fecca9Ssthen 	zone->apex = domain_table_insert(db->domains, dname);
63d3fecca9Ssthen 	zone->apex->usage++; /* the zone.apex reference */
64d3fecca9Ssthen 	zone->apex->is_apex = 1;
65d3fecca9Ssthen 	zone->soa_rrset = NULL;
66d3fecca9Ssthen 	zone->soa_nx_rrset = NULL;
67d3fecca9Ssthen 	zone->ns_rrset = NULL;
68d3fecca9Ssthen #ifdef NSEC3
69d3fecca9Ssthen 	zone->nsec3_param = NULL;
70d3fecca9Ssthen 	zone->nsec3_last = NULL;
71d3fecca9Ssthen 	zone->nsec3tree = NULL;
72d3fecca9Ssthen 	zone->hashtree = NULL;
73d3fecca9Ssthen 	zone->wchashtree = NULL;
74d3fecca9Ssthen 	zone->dshashtree = NULL;
75d3fecca9Ssthen #endif
76d3fecca9Ssthen 	zone->opts = zo;
774564029fSflorian 	zone->ixfr = NULL;
7815ed76cbSbrad 	zone->filename = NULL;
7915ed76cbSbrad 	zone->logstr = NULL;
80275a8d89Sflorian 	zone->mtime.tv_sec = 0;
81275a8d89Sflorian 	zone->mtime.tv_nsec = 0;
829e506f0aSbrad 	zone->zonestatid = 0;
83d3fecca9Ssthen 	zone->is_secure = 0;
84d3fecca9Ssthen 	zone->is_changed = 0;
853f21e8ccSflorian 	zone->is_updated = 0;
863f21e8ccSflorian 	zone->is_skipped = 0;
873f21e8ccSflorian 	zone->is_checked = 0;
883f21e8ccSflorian 	zone->is_bad = 0;
89d3fecca9Ssthen 	zone->is_ok = 1;
90d3fecca9Ssthen 	return zone;
91d3fecca9Ssthen }
92d3fecca9Ssthen 
93d3fecca9Ssthen void
namedb_zone_delete(namedb_type * db,zone_type * zone)94d3fecca9Ssthen namedb_zone_delete(namedb_type* db, zone_type* zone)
95d3fecca9Ssthen {
96d3fecca9Ssthen 	/* RRs and UDB and NSEC3 and so on must be already deleted */
97d3fecca9Ssthen 	radix_delete(db->zonetree, zone->node);
98d3fecca9Ssthen 
99d3fecca9Ssthen 	/* see if apex can be deleted */
100d3fecca9Ssthen 	if(zone->apex) {
101d3fecca9Ssthen 		zone->apex->usage --;
102c939baa4Ssthen 		zone->apex->is_apex = 0;
103d3fecca9Ssthen 		if(zone->apex->usage == 0) {
104d3fecca9Ssthen 			/* delete the apex, possibly */
105d3fecca9Ssthen 			domain_table_deldomain(db, zone->apex);
106d3fecca9Ssthen 		}
107d3fecca9Ssthen 	}
108d3fecca9Ssthen 
109d3fecca9Ssthen 	/* soa_rrset is freed when the SOA was deleted */
110d3fecca9Ssthen 	if(zone->soa_nx_rrset) {
111d3fecca9Ssthen 		region_recycle(db->region, zone->soa_nx_rrset->rrs,
112d3fecca9Ssthen 			sizeof(rr_type));
113d3fecca9Ssthen 		region_recycle(db->region, zone->soa_nx_rrset,
114d3fecca9Ssthen 			sizeof(rrset_type));
115d3fecca9Ssthen 	}
116d3fecca9Ssthen #ifdef NSEC3
117d3fecca9Ssthen 	hash_tree_delete(db->region, zone->nsec3tree);
118d3fecca9Ssthen 	hash_tree_delete(db->region, zone->hashtree);
119d3fecca9Ssthen 	hash_tree_delete(db->region, zone->wchashtree);
120d3fecca9Ssthen 	hash_tree_delete(db->region, zone->dshashtree);
121d3fecca9Ssthen #endif
1224564029fSflorian 	zone_ixfr_free(zone->ixfr);
12315ed76cbSbrad 	if(zone->filename)
12415ed76cbSbrad 		region_recycle(db->region, zone->filename,
12515ed76cbSbrad 			strlen(zone->filename)+1);
12615ed76cbSbrad 	if(zone->logstr)
12715ed76cbSbrad 		region_recycle(db->region, zone->logstr,
12815ed76cbSbrad 			strlen(zone->logstr)+1);
129d3fecca9Ssthen 	region_recycle(db->region, zone, sizeof(zone_type));
130d3fecca9Ssthen }
131d3fecca9Ssthen 
132d3fecca9Ssthen struct namedb *
namedb_open(struct nsd_options * opt)133*b71395eaSflorian namedb_open (struct nsd_options* opt)
134d3fecca9Ssthen {
135d3fecca9Ssthen 	namedb_type* db;
136d3fecca9Ssthen 
13762ac0c33Sjakob 	/*
138d3fecca9Ssthen 	 * Region used to store the loaded database.  The region is
139d3fecca9Ssthen 	 * freed in namedb_close.
14062ac0c33Sjakob 	 */
141d3fecca9Ssthen 	region_type* db_region;
142*b71395eaSflorian 
143*b71395eaSflorian 	(void)opt;
14462ac0c33Sjakob 
145d3fecca9Ssthen #ifdef USE_MMAP_ALLOC
146d3fecca9Ssthen 	db_region = region_create_custom(mmap_alloc, mmap_free, MMAP_ALLOC_CHUNK_SIZE,
147d3fecca9Ssthen 		MMAP_ALLOC_LARGE_OBJECT_SIZE, MMAP_ALLOC_INITIAL_CLEANUP_SIZE, 1);
148d3fecca9Ssthen #else /* !USE_MMAP_ALLOC */
149d3fecca9Ssthen 	db_region = region_create_custom(xalloc, free, DEFAULT_CHUNK_SIZE,
150d3fecca9Ssthen 		DEFAULT_LARGE_OBJECT_SIZE, DEFAULT_INITIAL_CLEANUP_SIZE, 1);
151d3fecca9Ssthen #endif /* !USE_MMAP_ALLOC */
152d3fecca9Ssthen 	db = (namedb_type *) region_alloc(db_region, sizeof(struct namedb));
153d3fecca9Ssthen 	db->region = db_region;
154d3fecca9Ssthen 	db->domains = domain_table_create(db->region);
155d3fecca9Ssthen 	db->zonetree = radix_tree_create(db->region);
156d3fecca9Ssthen 	db->diff_skip = 0;
157d3fecca9Ssthen 	db->diff_pos = 0;
15815ed76cbSbrad 	zonec_setup_parser(db);
15962ac0c33Sjakob 
16062ac0c33Sjakob 	if (gettimeofday(&(db->diff_timestamp), NULL) != 0) {
161*b71395eaSflorian 		log_msg(LOG_ERR, "unable to load namedb: cannot initialize timestamp");
162d3fecca9Ssthen 		region_destroy(db_region);
16362ac0c33Sjakob 		return NULL;
16462ac0c33Sjakob 	}
16562ac0c33Sjakob 
16615ed76cbSbrad 	return db;
16715ed76cbSbrad }
16815ed76cbSbrad 
169bc6311d7Sflorian /** get the file mtime stat (or nonexist or error) */
170275a8d89Sflorian int
file_get_mtime(const char * file,struct timespec * mtime,int * nonexist)171275a8d89Sflorian file_get_mtime(const char* file, struct timespec* mtime, int* nonexist)
17262ac0c33Sjakob {
173d3fecca9Ssthen 	struct stat s;
174d3fecca9Ssthen 	if(stat(file, &s) != 0) {
175275a8d89Sflorian 		mtime->tv_sec = 0;
176275a8d89Sflorian 		mtime->tv_nsec = 0;
177d3fecca9Ssthen 		*nonexist = (errno == ENOENT);
178d3fecca9Ssthen 		return 0;
17962ac0c33Sjakob 	}
180d3fecca9Ssthen 	*nonexist = 0;
181275a8d89Sflorian 	mtime->tv_sec = s.st_mtime;
182275a8d89Sflorian #ifdef HAVE_STRUCT_STAT_ST_MTIMENSEC
183275a8d89Sflorian 	mtime->tv_nsec = s.st_mtimensec;
184275a8d89Sflorian #elif defined(HAVE_STRUCT_STAT_ST_MTIM_TV_NSEC)
185275a8d89Sflorian 	mtime->tv_nsec = s.st_mtim.tv_nsec;
186275a8d89Sflorian #else
187275a8d89Sflorian 	mtime->tv_nsec = 0;
188275a8d89Sflorian #endif
189d3fecca9Ssthen 	return 1;
19062ac0c33Sjakob }
191a8b34139Sjakob 
192a8b34139Sjakob void
namedb_read_zonefile(struct nsd * nsd,struct zone * zone,udb_base * taskudb,udb_ptr * last_task)193cdb6bbddSbrad namedb_read_zonefile(struct nsd* nsd, struct zone* zone, udb_base* taskudb,
194d3fecca9Ssthen 	udb_ptr* last_task)
195a8b34139Sjakob {
196275a8d89Sflorian 	struct timespec mtime;
197d3fecca9Ssthen 	int nonexist = 0;
198d3fecca9Ssthen 	unsigned int errors;
199d3fecca9Ssthen 	const char* fname;
2004564029fSflorian 	struct ixfr_create* ixfrcr = NULL;
2014564029fSflorian 	int ixfr_create_already_done = 0;
20215ed76cbSbrad 	if(!nsd->db || !zone || !zone->opts || !zone->opts->pattern->zonefile)
203d3fecca9Ssthen 		return;
204275a8d89Sflorian 	mtime.tv_sec = 0;
205275a8d89Sflorian 	mtime.tv_nsec = 0;
206cdb6bbddSbrad 	fname = config_make_zonefile(zone->opts, nsd);
207308d2509Sflorian 	assert(fname);
208d3fecca9Ssthen 	if(!file_get_mtime(fname, &mtime, &nonexist)) {
209d3fecca9Ssthen 		if(nonexist) {
210063644e9Sflorian 			if(zone_is_slave(zone->opts)) {
211063644e9Sflorian 				/* for slave zones not as bad, no zonefile
212063644e9Sflorian 				 * may just mean we have to transfer it */
213d3fecca9Ssthen 				VERBOSITY(2, (LOG_INFO, "zonefile %s does not exist",
214d3fecca9Ssthen 					fname));
215063644e9Sflorian 			} else {
216063644e9Sflorian 				/* without a download option, we can never
217063644e9Sflorian 				 * serve data, more severe error printout */
218063644e9Sflorian 				log_msg(LOG_ERR, "zonefile %s does not exist", fname);
219063644e9Sflorian 			}
220063644e9Sflorian 
221d3fecca9Ssthen 		} else
222d3fecca9Ssthen 			log_msg(LOG_ERR, "zonefile %s: %s",
223d3fecca9Ssthen 				fname, strerror(errno));
224cdb6bbddSbrad 		if(taskudb) task_new_soainfo(taskudb, last_task, zone, 0);
225d3fecca9Ssthen 		return;
226d3fecca9Ssthen 	} else {
22715ed76cbSbrad 		const char* zone_fname = zone->filename;
228275a8d89Sflorian 		struct timespec zone_mtime = zone->mtime;
229cbbc2d6cSbrad 		/* if no zone_fname, then it was acquired in zone transfer,
230cbbc2d6cSbrad 		 * see if the file is newer than the zone transfer
231cbbc2d6cSbrad 		 * (regardless if this is a different file), because the
232cbbc2d6cSbrad 		 * zone transfer is a different content source too */
233275a8d89Sflorian 		if(!zone_fname && timespec_compare(&zone_mtime, &mtime) >= 0) {
234cbbc2d6cSbrad 			VERBOSITY(3, (LOG_INFO, "zonefile %s is older than "
235cbbc2d6cSbrad 				"zone transfer in memory", fname));
236cbbc2d6cSbrad 			return;
237cbbc2d6cSbrad 
238cbbc2d6cSbrad 		/* if zone_fname, then the file was acquired from reading it,
239cbbc2d6cSbrad 		 * and see if filename changed or mtime newer to read it */
240308d2509Sflorian 		} else if(zone_fname && strcmp(zone_fname, fname) == 0 &&
241275a8d89Sflorian 		   timespec_compare(&zone_mtime, &mtime) == 0) {
242d3fecca9Ssthen 			VERBOSITY(3, (LOG_INFO, "zonefile %s is not modified",
243d3fecca9Ssthen 				fname));
244d3fecca9Ssthen 			return;
245a8b34139Sjakob 		}
246a8b34139Sjakob 	}
2474564029fSflorian 	if(ixfr_create_from_difference(zone, fname,
2484564029fSflorian 		&ixfr_create_already_done)) {
2494564029fSflorian 		ixfrcr = ixfr_create_start(zone, fname,
2504564029fSflorian 			zone->opts->pattern->ixfr_size, 0);
2514564029fSflorian 		if(!ixfrcr) {
2524564029fSflorian 			/* leaves the ixfrcr at NULL, so it is not created */
2534564029fSflorian 			log_msg(LOG_ERR, "out of memory starting ixfr create");
2544564029fSflorian 		}
2554564029fSflorian 	}
256a8b34139Sjakob 
257d3fecca9Ssthen 	assert(parser);
258d3fecca9Ssthen 	/* wipe zone from memory */
259d3fecca9Ssthen #ifdef NSEC3
260cdb6bbddSbrad 	nsec3_clear_precompile(nsd->db, zone);
261d3fecca9Ssthen 	zone->nsec3_param = NULL;
2624b6a9f59Sflorian #endif
2634b6a9f59Sflorian 	delete_zone_rrs(nsd->db, zone);
264d3fecca9Ssthen 	errors = zonec_read(zone->opts->name, fname, zone);
265d3fecca9Ssthen 	if(errors > 0) {
266d3fecca9Ssthen 		log_msg(LOG_ERR, "zone %s file %s read with %u errors",
267d3fecca9Ssthen 			zone->opts->name, fname, errors);
268d3fecca9Ssthen 		/* wipe (partial) zone from memory */
269d3fecca9Ssthen 		zone->is_ok = 1;
270d3fecca9Ssthen #ifdef NSEC3
271cdb6bbddSbrad 		nsec3_clear_precompile(nsd->db, zone);
272d3fecca9Ssthen 		zone->nsec3_param = NULL;
2734b6a9f59Sflorian #endif
2744b6a9f59Sflorian 		delete_zone_rrs(nsd->db, zone);
27515ed76cbSbrad 		if(zone->filename)
27615ed76cbSbrad 			region_recycle(nsd->db->region, zone->filename,
27715ed76cbSbrad 				strlen(zone->filename)+1);
27815ed76cbSbrad 		zone->filename = NULL;
27915ed76cbSbrad 		if(zone->logstr)
28015ed76cbSbrad 			region_recycle(nsd->db->region, zone->logstr,
28115ed76cbSbrad 				strlen(zone->logstr)+1);
28215ed76cbSbrad 		zone->logstr = NULL;
28315ed76cbSbrad 	} else {
284c939baa4Ssthen 		VERBOSITY(1, (LOG_INFO, "zone %s read with success",
285d3fecca9Ssthen 			zone->opts->name));
286d3fecca9Ssthen 		zone->is_ok = 1;
287d3fecca9Ssthen 		zone->is_changed = 0;
288d3fecca9Ssthen 		/* store zone into udb */
28915ed76cbSbrad 		zone->mtime = mtime;
29015ed76cbSbrad 		if(zone->filename)
29115ed76cbSbrad 			region_recycle(nsd->db->region, zone->filename,
29215ed76cbSbrad 				strlen(zone->filename)+1);
29315ed76cbSbrad 		zone->filename = region_strdup(nsd->db->region, fname);
29415ed76cbSbrad 		if(zone->logstr)
29515ed76cbSbrad 			region_recycle(nsd->db->region, zone->logstr,
29615ed76cbSbrad 				strlen(zone->logstr)+1);
29715ed76cbSbrad 		zone->logstr = NULL;
2984564029fSflorian 		if(ixfr_create_already_done) {
2994564029fSflorian 			ixfr_readup_exist(zone, nsd, fname);
3004564029fSflorian 		} else if(ixfrcr) {
3014564029fSflorian 			if(!ixfr_create_perform(ixfrcr, zone, 1, nsd, fname,
3024564029fSflorian 				zone->opts->pattern->ixfr_number)) {
3034564029fSflorian 				log_msg(LOG_ERR, "failed to create IXFR");
3044564029fSflorian 			} else {
3054564029fSflorian 				VERBOSITY(2, (LOG_INFO, "zone %s created IXFR %s.ixfr",
3064564029fSflorian 					zone->opts->name, fname));
3074564029fSflorian 			}
3084564029fSflorian 			ixfr_create_free(ixfrcr);
3094564029fSflorian 		} else if(zone_is_ixfr_enabled(zone)) {
3104564029fSflorian 			ixfr_read_from_file(nsd, zone, fname);
3114564029fSflorian 		}
312d3fecca9Ssthen 	}
313cdb6bbddSbrad 	if(taskudb) task_new_soainfo(taskudb, last_task, zone, 0);
314d3fecca9Ssthen #ifdef NSEC3
315cdb6bbddSbrad 	prehash_zone_complete(nsd->db, zone);
316d3fecca9Ssthen #endif
317d3fecca9Ssthen }
318d3fecca9Ssthen 
namedb_check_zonefile(struct nsd * nsd,udb_base * taskudb,udb_ptr * last_task,struct zone_options * zopt)319cdb6bbddSbrad void namedb_check_zonefile(struct nsd* nsd, udb_base* taskudb,
320fe5fe5f6Sflorian 	udb_ptr* last_task, struct zone_options* zopt)
321d3fecca9Ssthen {
322d3fecca9Ssthen 	zone_type* zone;
323d3fecca9Ssthen 	const dname_type* dname = (const dname_type*)zopt->node.key;
324d3fecca9Ssthen 	/* find zone to go with it, or create it */
325cdb6bbddSbrad 	zone = namedb_find_zone(nsd->db, dname);
326d3fecca9Ssthen 	if(!zone) {
327cdb6bbddSbrad 		zone = namedb_zone_create(nsd->db, dname, zopt);
328d3fecca9Ssthen 	}
329cdb6bbddSbrad 	namedb_read_zonefile(nsd, zone, taskudb, last_task);
330d3fecca9Ssthen }
331d3fecca9Ssthen 
namedb_check_zonefiles(struct nsd * nsd,struct nsd_options * opt,udb_base * taskudb,udb_ptr * last_task)332fe5fe5f6Sflorian void namedb_check_zonefiles(struct nsd* nsd, struct nsd_options* opt,
333d3fecca9Ssthen 	udb_base* taskudb, udb_ptr* last_task)
334d3fecca9Ssthen {
335fe5fe5f6Sflorian 	struct zone_options* zo;
336d3fecca9Ssthen 	/* check all zones in opt, create if not exist in main db */
337fe5fe5f6Sflorian 	RBTREE_FOR(zo, struct zone_options*, opt->zone_options) {
338cdb6bbddSbrad 		namedb_check_zonefile(nsd, taskudb, last_task, zo);
33915ed76cbSbrad 		if(nsd->signal_hint_shutdown) break;
340d3fecca9Ssthen 	}
341d3fecca9Ssthen }
342