xref: /openbsd/usr.sbin/nsd/dbaccess.c (revision b71395ea)
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 "zonec.h"
28 #include "nsec3.h"
29 #include "difffile.h"
30 #include "nsd.h"
31 #include "ixfr.h"
32 #include "ixfrcreate.h"
33 
34 void
namedb_close(struct namedb * db)35 namedb_close(struct namedb* db)
36 {
37 	if(db) {
38 		zonec_desetup_parser();
39 		region_destroy(db->region);
40 	}
41 }
42 
43 void
namedb_free_ixfr(struct namedb * db)44 namedb_free_ixfr(struct namedb* db)
45 {
46 	struct radnode* n;
47 	for(n=radix_first(db->zonetree); n; n=radix_next(n)) {
48 		zone_ixfr_free(((zone_type*)n->elem)->ixfr);
49 	}
50 }
51 
52 /** create a zone */
53 zone_type*
namedb_zone_create(namedb_type * db,const dname_type * dname,struct zone_options * zo)54 namedb_zone_create(namedb_type* db, const dname_type* dname,
55 	struct zone_options* zo)
56 {
57 	zone_type* zone = (zone_type *) region_alloc(db->region,
58 		sizeof(zone_type));
59 	zone->node = radname_insert(db->zonetree, dname_name(dname),
60 		dname->name_size, zone);
61 	assert(zone->node);
62 	zone->apex = domain_table_insert(db->domains, dname);
63 	zone->apex->usage++; /* the zone.apex reference */
64 	zone->apex->is_apex = 1;
65 	zone->soa_rrset = NULL;
66 	zone->soa_nx_rrset = NULL;
67 	zone->ns_rrset = NULL;
68 #ifdef NSEC3
69 	zone->nsec3_param = NULL;
70 	zone->nsec3_last = NULL;
71 	zone->nsec3tree = NULL;
72 	zone->hashtree = NULL;
73 	zone->wchashtree = NULL;
74 	zone->dshashtree = NULL;
75 #endif
76 	zone->opts = zo;
77 	zone->ixfr = NULL;
78 	zone->filename = NULL;
79 	zone->logstr = NULL;
80 	zone->mtime.tv_sec = 0;
81 	zone->mtime.tv_nsec = 0;
82 	zone->zonestatid = 0;
83 	zone->is_secure = 0;
84 	zone->is_changed = 0;
85 	zone->is_updated = 0;
86 	zone->is_skipped = 0;
87 	zone->is_checked = 0;
88 	zone->is_bad = 0;
89 	zone->is_ok = 1;
90 	return zone;
91 }
92 
93 void
namedb_zone_delete(namedb_type * db,zone_type * zone)94 namedb_zone_delete(namedb_type* db, zone_type* zone)
95 {
96 	/* RRs and UDB and NSEC3 and so on must be already deleted */
97 	radix_delete(db->zonetree, zone->node);
98 
99 	/* see if apex can be deleted */
100 	if(zone->apex) {
101 		zone->apex->usage --;
102 		zone->apex->is_apex = 0;
103 		if(zone->apex->usage == 0) {
104 			/* delete the apex, possibly */
105 			domain_table_deldomain(db, zone->apex);
106 		}
107 	}
108 
109 	/* soa_rrset is freed when the SOA was deleted */
110 	if(zone->soa_nx_rrset) {
111 		region_recycle(db->region, zone->soa_nx_rrset->rrs,
112 			sizeof(rr_type));
113 		region_recycle(db->region, zone->soa_nx_rrset,
114 			sizeof(rrset_type));
115 	}
116 #ifdef NSEC3
117 	hash_tree_delete(db->region, zone->nsec3tree);
118 	hash_tree_delete(db->region, zone->hashtree);
119 	hash_tree_delete(db->region, zone->wchashtree);
120 	hash_tree_delete(db->region, zone->dshashtree);
121 #endif
122 	zone_ixfr_free(zone->ixfr);
123 	if(zone->filename)
124 		region_recycle(db->region, zone->filename,
125 			strlen(zone->filename)+1);
126 	if(zone->logstr)
127 		region_recycle(db->region, zone->logstr,
128 			strlen(zone->logstr)+1);
129 	region_recycle(db->region, zone, sizeof(zone_type));
130 }
131 
132 struct namedb *
namedb_open(struct nsd_options * opt)133 namedb_open (struct nsd_options* opt)
134 {
135 	namedb_type* db;
136 
137 	/*
138 	 * Region used to store the loaded database.  The region is
139 	 * freed in namedb_close.
140 	 */
141 	region_type* db_region;
142 
143 	(void)opt;
144 
145 #ifdef USE_MMAP_ALLOC
146 	db_region = region_create_custom(mmap_alloc, mmap_free, MMAP_ALLOC_CHUNK_SIZE,
147 		MMAP_ALLOC_LARGE_OBJECT_SIZE, MMAP_ALLOC_INITIAL_CLEANUP_SIZE, 1);
148 #else /* !USE_MMAP_ALLOC */
149 	db_region = region_create_custom(xalloc, free, DEFAULT_CHUNK_SIZE,
150 		DEFAULT_LARGE_OBJECT_SIZE, DEFAULT_INITIAL_CLEANUP_SIZE, 1);
151 #endif /* !USE_MMAP_ALLOC */
152 	db = (namedb_type *) region_alloc(db_region, sizeof(struct namedb));
153 	db->region = db_region;
154 	db->domains = domain_table_create(db->region);
155 	db->zonetree = radix_tree_create(db->region);
156 	db->diff_skip = 0;
157 	db->diff_pos = 0;
158 	zonec_setup_parser(db);
159 
160 	if (gettimeofday(&(db->diff_timestamp), NULL) != 0) {
161 		log_msg(LOG_ERR, "unable to load namedb: cannot initialize timestamp");
162 		region_destroy(db_region);
163 		return NULL;
164 	}
165 
166 	return db;
167 }
168 
169 /** get the file mtime stat (or nonexist or error) */
170 int
file_get_mtime(const char * file,struct timespec * mtime,int * nonexist)171 file_get_mtime(const char* file, struct timespec* mtime, int* nonexist)
172 {
173 	struct stat s;
174 	if(stat(file, &s) != 0) {
175 		mtime->tv_sec = 0;
176 		mtime->tv_nsec = 0;
177 		*nonexist = (errno == ENOENT);
178 		return 0;
179 	}
180 	*nonexist = 0;
181 	mtime->tv_sec = s.st_mtime;
182 #ifdef HAVE_STRUCT_STAT_ST_MTIMENSEC
183 	mtime->tv_nsec = s.st_mtimensec;
184 #elif defined(HAVE_STRUCT_STAT_ST_MTIM_TV_NSEC)
185 	mtime->tv_nsec = s.st_mtim.tv_nsec;
186 #else
187 	mtime->tv_nsec = 0;
188 #endif
189 	return 1;
190 }
191 
192 void
namedb_read_zonefile(struct nsd * nsd,struct zone * zone,udb_base * taskudb,udb_ptr * last_task)193 namedb_read_zonefile(struct nsd* nsd, struct zone* zone, udb_base* taskudb,
194 	udb_ptr* last_task)
195 {
196 	struct timespec mtime;
197 	int nonexist = 0;
198 	unsigned int errors;
199 	const char* fname;
200 	struct ixfr_create* ixfrcr = NULL;
201 	int ixfr_create_already_done = 0;
202 	if(!nsd->db || !zone || !zone->opts || !zone->opts->pattern->zonefile)
203 		return;
204 	mtime.tv_sec = 0;
205 	mtime.tv_nsec = 0;
206 	fname = config_make_zonefile(zone->opts, nsd);
207 	assert(fname);
208 	if(!file_get_mtime(fname, &mtime, &nonexist)) {
209 		if(nonexist) {
210 			if(zone_is_slave(zone->opts)) {
211 				/* for slave zones not as bad, no zonefile
212 				 * may just mean we have to transfer it */
213 				VERBOSITY(2, (LOG_INFO, "zonefile %s does not exist",
214 					fname));
215 			} else {
216 				/* without a download option, we can never
217 				 * serve data, more severe error printout */
218 				log_msg(LOG_ERR, "zonefile %s does not exist", fname);
219 			}
220 
221 		} else
222 			log_msg(LOG_ERR, "zonefile %s: %s",
223 				fname, strerror(errno));
224 		if(taskudb) task_new_soainfo(taskudb, last_task, zone, 0);
225 		return;
226 	} else {
227 		const char* zone_fname = zone->filename;
228 		struct timespec zone_mtime = zone->mtime;
229 		/* if no zone_fname, then it was acquired in zone transfer,
230 		 * see if the file is newer than the zone transfer
231 		 * (regardless if this is a different file), because the
232 		 * zone transfer is a different content source too */
233 		if(!zone_fname && timespec_compare(&zone_mtime, &mtime) >= 0) {
234 			VERBOSITY(3, (LOG_INFO, "zonefile %s is older than "
235 				"zone transfer in memory", fname));
236 			return;
237 
238 		/* if zone_fname, then the file was acquired from reading it,
239 		 * and see if filename changed or mtime newer to read it */
240 		} else if(zone_fname && strcmp(zone_fname, fname) == 0 &&
241 		   timespec_compare(&zone_mtime, &mtime) == 0) {
242 			VERBOSITY(3, (LOG_INFO, "zonefile %s is not modified",
243 				fname));
244 			return;
245 		}
246 	}
247 	if(ixfr_create_from_difference(zone, fname,
248 		&ixfr_create_already_done)) {
249 		ixfrcr = ixfr_create_start(zone, fname,
250 			zone->opts->pattern->ixfr_size, 0);
251 		if(!ixfrcr) {
252 			/* leaves the ixfrcr at NULL, so it is not created */
253 			log_msg(LOG_ERR, "out of memory starting ixfr create");
254 		}
255 	}
256 
257 	assert(parser);
258 	/* wipe zone from memory */
259 #ifdef NSEC3
260 	nsec3_clear_precompile(nsd->db, zone);
261 	zone->nsec3_param = NULL;
262 #endif
263 	delete_zone_rrs(nsd->db, zone);
264 	errors = zonec_read(zone->opts->name, fname, zone);
265 	if(errors > 0) {
266 		log_msg(LOG_ERR, "zone %s file %s read with %u errors",
267 			zone->opts->name, fname, errors);
268 		/* wipe (partial) zone from memory */
269 		zone->is_ok = 1;
270 #ifdef NSEC3
271 		nsec3_clear_precompile(nsd->db, zone);
272 		zone->nsec3_param = NULL;
273 #endif
274 		delete_zone_rrs(nsd->db, zone);
275 		if(zone->filename)
276 			region_recycle(nsd->db->region, zone->filename,
277 				strlen(zone->filename)+1);
278 		zone->filename = NULL;
279 		if(zone->logstr)
280 			region_recycle(nsd->db->region, zone->logstr,
281 				strlen(zone->logstr)+1);
282 		zone->logstr = NULL;
283 	} else {
284 		VERBOSITY(1, (LOG_INFO, "zone %s read with success",
285 			zone->opts->name));
286 		zone->is_ok = 1;
287 		zone->is_changed = 0;
288 		/* store zone into udb */
289 		zone->mtime = mtime;
290 		if(zone->filename)
291 			region_recycle(nsd->db->region, zone->filename,
292 				strlen(zone->filename)+1);
293 		zone->filename = region_strdup(nsd->db->region, fname);
294 		if(zone->logstr)
295 			region_recycle(nsd->db->region, zone->logstr,
296 				strlen(zone->logstr)+1);
297 		zone->logstr = NULL;
298 		if(ixfr_create_already_done) {
299 			ixfr_readup_exist(zone, nsd, fname);
300 		} else if(ixfrcr) {
301 			if(!ixfr_create_perform(ixfrcr, zone, 1, nsd, fname,
302 				zone->opts->pattern->ixfr_number)) {
303 				log_msg(LOG_ERR, "failed to create IXFR");
304 			} else {
305 				VERBOSITY(2, (LOG_INFO, "zone %s created IXFR %s.ixfr",
306 					zone->opts->name, fname));
307 			}
308 			ixfr_create_free(ixfrcr);
309 		} else if(zone_is_ixfr_enabled(zone)) {
310 			ixfr_read_from_file(nsd, zone, fname);
311 		}
312 	}
313 	if(taskudb) task_new_soainfo(taskudb, last_task, zone, 0);
314 #ifdef NSEC3
315 	prehash_zone_complete(nsd->db, zone);
316 #endif
317 }
318 
namedb_check_zonefile(struct nsd * nsd,udb_base * taskudb,udb_ptr * last_task,struct zone_options * zopt)319 void namedb_check_zonefile(struct nsd* nsd, udb_base* taskudb,
320 	udb_ptr* last_task, struct zone_options* zopt)
321 {
322 	zone_type* zone;
323 	const dname_type* dname = (const dname_type*)zopt->node.key;
324 	/* find zone to go with it, or create it */
325 	zone = namedb_find_zone(nsd->db, dname);
326 	if(!zone) {
327 		zone = namedb_zone_create(nsd->db, dname, zopt);
328 	}
329 	namedb_read_zonefile(nsd, zone, taskudb, last_task);
330 }
331 
namedb_check_zonefiles(struct nsd * nsd,struct nsd_options * opt,udb_base * taskudb,udb_ptr * last_task)332 void namedb_check_zonefiles(struct nsd* nsd, struct nsd_options* opt,
333 	udb_base* taskudb, udb_ptr* last_task)
334 {
335 	struct zone_options* zo;
336 	/* check all zones in opt, create if not exist in main db */
337 	RBTREE_FOR(zo, struct zone_options*, opt->zone_options) {
338 		namedb_check_zonefile(nsd, taskudb, last_task, zo);
339 		if(nsd->signal_hint_shutdown) break;
340 	}
341 }
342