xref: /openbsd-src/usr.sbin/nsd/dbaccess.c (revision d13be5d47e4149db2549a9828e244d59dbc43f15)
1 /*
2  * dbaccess.c -- access methods for nsd(8) database
3  *
4  * Copyright (c) 2001-2011, 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 #include <stdio.h>		/* DEBUG */
21 
22 #include "dns.h"
23 #include "namedb.h"
24 #include "util.h"
25 #include "options.h"
26 
27 int
28 namedb_lookup(struct namedb    *db,
29 	      const dname_type *dname,
30 	      domain_type     **closest_match,
31 	      domain_type     **closest_encloser)
32 {
33 	return domain_table_search(
34 		db->domains, dname, closest_match, closest_encloser);
35 }
36 
37 static int
38 read_magic(namedb_type *db)
39 {
40 	char buf[NAMEDB_MAGIC_SIZE];
41 
42 	if (fread(buf, sizeof(char), sizeof(buf), db->fd) != sizeof(buf))
43 		return 0;
44 
45 	return memcmp(buf, NAMEDB_MAGIC, NAMEDB_MAGIC_SIZE) == 0;
46 }
47 
48 static const dname_type *
49 read_dname(FILE *fd, region_type *region)
50 {
51 	uint8_t size;
52 	uint8_t temp[MAXDOMAINLEN];
53 
54 	if (fread(&size, sizeof(uint8_t), 1, fd) != 1)
55 		return NULL;
56 	if (fread(temp, sizeof(uint8_t), size, fd) != size)
57 		return NULL;
58 
59 	return dname_make(region, temp, 1);
60 }
61 
62 static int
63 read_size(namedb_type *db, uint32_t *result)
64 {
65 	if (fread(result, sizeof(*result), 1, db->fd) == 1) {
66 		*result = ntohl(*result);
67 		return 1;
68 	} else {
69 		return 0;
70 	}
71 }
72 
73 static domain_type *
74 read_domain(namedb_type *db, uint32_t domain_count, domain_type **domains)
75 {
76 	uint32_t domain_number;
77 
78 	if (!read_size(db, &domain_number))
79 		return NULL;
80 
81 	if (domain_number == 0 || domain_number > domain_count)
82 		return NULL;
83 
84 	return domains[domain_number - 1];
85 }
86 
87 static zone_type *
88 read_zone(namedb_type *db, uint32_t zone_count, zone_type **zones)
89 {
90 	uint32_t zone_number;
91 
92 	if (!read_size(db, &zone_number))
93 		return NULL;
94 
95 	if (zone_number == 0 || zone_number > zone_count)
96 		return NULL;
97 
98 	return zones[zone_number - 1];
99 }
100 
101 static int
102 read_rdata_atom(namedb_type *db, uint16_t type, int index, uint32_t domain_count, domain_type **domains, rdata_atom_type *result)
103 {
104 	uint8_t data[65536];
105 
106 	if (rdata_atom_is_domain(type, index)) {
107 		result->domain = read_domain(db, domain_count, domains);
108 		if (!result->domain)
109 			return 0;
110 	} else {
111 		uint16_t size;
112 
113 		if (fread(&size, sizeof(size), 1, db->fd) != 1)
114 			return 0;
115 		size = ntohs(size);
116 		if (fread(data, sizeof(uint8_t), size, db->fd) != size)
117 			return 0;
118 
119 		result->data = (uint16_t *) region_alloc(
120 			db->region, sizeof(uint16_t) + size);
121 		memcpy(result->data, &size, sizeof(uint16_t));
122 		memcpy((uint8_t *) result->data + sizeof(uint16_t), data, size);
123 	}
124 
125 	return 1;
126 }
127 
128 static rrset_type *
129 read_rrset(namedb_type *db,
130 	   uint32_t domain_count, domain_type **domains,
131 	   uint32_t zone_count, zone_type **zones)
132 {
133 	rrset_type *rrset;
134 	int i, j;
135 	domain_type *owner;
136 	uint16_t type;
137 	uint16_t klass;
138 	uint32_t soa_minimum;
139 
140 	owner = read_domain(db, domain_count, domains);
141 	if (!owner)
142 		return NULL;
143 
144 	rrset = (rrset_type *) region_alloc(db->region, sizeof(rrset_type));
145 
146 	rrset->zone = read_zone(db, zone_count, zones);
147 	if (!rrset->zone)
148 		return NULL;
149 
150 	if (fread(&type, sizeof(type), 1, db->fd) != 1)
151 		return NULL;
152 	type = ntohs(type);
153 
154 	if (fread(&klass, sizeof(klass), 1, db->fd) != 1)
155 		return NULL;
156 	klass = ntohs(klass);
157 
158 	if (fread(&rrset->rr_count, sizeof(rrset->rr_count), 1, db->fd) != 1)
159 		return NULL;
160 	rrset->rr_count = ntohs(rrset->rr_count);
161 	rrset->rrs = (rr_type *) region_alloc(
162 		db->region, rrset->rr_count * sizeof(rr_type));
163 
164 	assert(rrset->rr_count > 0);
165 
166 	for (i = 0; i < rrset->rr_count; ++i) {
167 		rr_type *rr = &rrset->rrs[i];
168 
169 		rr->owner = owner;
170 		rr->type = type;
171 		rr->klass = klass;
172 
173 		if (fread(&rr->rdata_count, sizeof(rr->rdata_count), 1, db->fd) != 1)
174 			return NULL;
175 		rr->rdata_count = ntohs(rr->rdata_count);
176 		rr->rdatas = (rdata_atom_type *) region_alloc(
177 			db->region, rr->rdata_count * sizeof(rdata_atom_type));
178 
179 		if (fread(&rr->ttl, sizeof(rr->ttl), 1, db->fd) != 1)
180 			return NULL;
181 		rr->ttl = ntohl(rr->ttl);
182 
183 		for (j = 0; j < rr->rdata_count; ++j) {
184 			if (!read_rdata_atom(db, rr->type, j, domain_count, domains, &rr->rdatas[j]))
185 				return NULL;
186 		}
187 	}
188 
189 	domain_add_rrset(owner, rrset);
190 
191 	if (rrset_rrtype(rrset) == TYPE_SOA) {
192 		assert(owner == rrset->zone->apex);
193 		rrset->zone->soa_rrset = rrset;
194 
195 		/* BUG #103 add another soa with a tweaked ttl */
196 		rrset->zone->soa_nx_rrset = region_alloc(db->region, sizeof(rrset_type));
197 		rrset->zone->soa_nx_rrset->rrs =
198 			region_alloc(db->region, rrset->rr_count * sizeof(rr_type));
199 
200 		memcpy(rrset->zone->soa_nx_rrset->rrs, rrset->rrs, sizeof(rr_type));
201 		rrset->zone->soa_nx_rrset->rr_count = 1;
202 		rrset->zone->soa_nx_rrset->next = 0;
203 
204 		/* also add a link to the zone */
205 		rrset->zone->soa_nx_rrset->zone = rrset->zone;
206 
207 		/* check the ttl and MINIMUM value and set accordinly */
208 		memcpy(&soa_minimum, rdata_atom_data(rrset->rrs->rdatas[6]),
209 				rdata_atom_size(rrset->rrs->rdatas[6]));
210 		if (rrset->rrs->ttl > ntohl(soa_minimum)) {
211 			rrset->zone->soa_nx_rrset->rrs[0].ttl = ntohl(soa_minimum);
212 		}
213 
214 	} else if (owner == rrset->zone->apex
215 		   && rrset_rrtype(rrset) == TYPE_NS)
216 	{
217 		rrset->zone->ns_rrset = rrset;
218 	}
219 
220 	if (rrset_rrtype(rrset) == TYPE_RRSIG && owner == rrset->zone->apex) {
221 		for (i = 0; i < rrset->rr_count; ++i) {
222 			if (rr_rrsig_type_covered(&rrset->rrs[i]) == TYPE_SOA) {
223 				rrset->zone->is_secure = 1;
224 				break;
225 			}
226 		}
227 	}
228 	return rrset;
229 }
230 
231 struct namedb *
232 namedb_open (const char *filename, nsd_options_t* opt, size_t num_children)
233 {
234 	namedb_type *db;
235 
236 	/*
237 	 * Region used to store the loaded database.  The region is
238 	 * freed in namedb_close.
239 	 */
240 	region_type *db_region;
241 
242 	/*
243 	 * Temporary region used while loading domain names from the
244 	 * database.  The region is freed after each time a dname is
245 	 * read from the database.
246 	 */
247 	region_type *dname_region;
248 
249 	/*
250 	 * Temporary region used to store array of domains and zones
251 	 * while loading the database.  The region is freed before
252 	 * returning.
253 	 */
254 	region_type *temp_region;
255 
256 	uint32_t dname_count;
257 	domain_type **domains;	/* Indexed by domain number.  */
258 
259 	uint32_t zone_count;
260 	zone_type **zones;	/* Indexed by zone number.  */
261 
262 	uint32_t i;
263 	uint32_t rrset_count = 0;
264 	uint32_t rr_count = 0;
265 
266 	rrset_type *rrset;
267 
268 	DEBUG(DEBUG_DBACCESS, 2,
269 	      (LOG_INFO, "sizeof(namedb_type) = %lu\n", (unsigned long) sizeof(namedb_type)));
270 	DEBUG(DEBUG_DBACCESS, 2,
271 	      (LOG_INFO, "sizeof(zone_type) = %lu\n", (unsigned long) sizeof(zone_type)));
272 	DEBUG(DEBUG_DBACCESS, 2,
273 	      (LOG_INFO, "sizeof(domain_type) = %lu\n", (unsigned long) sizeof(domain_type)));
274 	DEBUG(DEBUG_DBACCESS, 2,
275 	      (LOG_INFO, "sizeof(rrset_type) = %lu\n", (unsigned long) sizeof(rrset_type)));
276 	DEBUG(DEBUG_DBACCESS, 2,
277 	      (LOG_INFO, "sizeof(rr_type) = %lu\n", (unsigned long) sizeof(rr_type)));
278 	DEBUG(DEBUG_DBACCESS, 2,
279 	      (LOG_INFO, "sizeof(rdata_atom_type) = %lu\n", (unsigned long) sizeof(rdata_atom_type)));
280 	DEBUG(DEBUG_DBACCESS, 2,
281 	      (LOG_INFO, "sizeof(rbnode_t) = %lu\n", (unsigned long) sizeof(rbnode_t)));
282 
283 #ifdef USE_MMAP_ALLOC
284 	db_region = region_create_custom(mmap_alloc, mmap_free, MMAP_ALLOC_CHUNK_SIZE,
285 		MMAP_ALLOC_LARGE_OBJECT_SIZE, MMAP_ALLOC_INITIAL_CLEANUP_SIZE, 1);
286 #else /* !USE_MMAP_ALLOC */
287 	db_region = region_create_custom(xalloc, free, DEFAULT_CHUNK_SIZE,
288 		DEFAULT_LARGE_OBJECT_SIZE, DEFAULT_INITIAL_CLEANUP_SIZE, 1);
289 #endif /* !USE_MMAP_ALLOC */
290 	db = (namedb_type *) region_alloc(db_region, sizeof(struct namedb));
291 	db->region = db_region;
292 	db->domains = domain_table_create(db->region);
293 	db->zones = NULL;
294 	db->zone_count = 0;
295 	db->filename = region_strdup(db->region, filename);
296 	db->crc = 0xffffffff;
297 	db->diff_skip = 0;
298 
299 	if (gettimeofday(&(db->diff_timestamp), NULL) != 0) {
300 		log_msg(LOG_ERR, "unable to load %s: cannot initialize"
301 				 "timestamp", db->filename);
302 		region_destroy(db_region);
303                 return NULL;
304         }
305 
306 	/* Open it... */
307 	db->fd = fopen(db->filename, "r");
308 	if (db->fd == NULL) {
309 		log_msg(LOG_ERR, "unable to load %s: %s",
310 			db->filename, strerror(errno));
311 		region_destroy(db_region);
312 		return NULL;
313 	}
314 
315 	if (!read_magic(db)) {
316 		log_msg(LOG_ERR, "corrupted database (read magic): %s", db->filename);
317 		namedb_close(db);
318 		return NULL;
319 	}
320 
321 	if (!read_size(db, &zone_count)) {
322 		log_msg(LOG_ERR, "corrupted database (read size): %s", db->filename);
323 		namedb_close(db);
324 		return NULL;
325 	}
326 
327 	DEBUG(DEBUG_DBACCESS, 1,
328 	      (LOG_INFO, "Retrieving %lu zones\n", (unsigned long) zone_count));
329 
330 	temp_region = region_create(xalloc, free);
331 	dname_region = region_create(xalloc, free);
332 
333 	db->zone_count = zone_count;
334 	zones = (zone_type **) region_alloc(temp_region,
335 					    zone_count * sizeof(zone_type *));
336 	for (i = 0; i < zone_count; ++i) {
337 		const dname_type *dname = read_dname(db->fd, dname_region);
338 		if (!dname) {
339 			log_msg(LOG_ERR, "corrupted database (read dname): %s", db->filename);
340 			region_destroy(dname_region);
341 			region_destroy(temp_region);
342 			namedb_close(db);
343 			return NULL;
344 		}
345 		zones[i] = (zone_type *) region_alloc(db->region,
346 						      sizeof(zone_type));
347 		zones[i]->next = db->zones;
348 		db->zones = zones[i];
349 		zones[i]->apex = domain_table_insert(db->domains, dname);
350 		zones[i]->soa_rrset = NULL;
351 		zones[i]->soa_nx_rrset = NULL;
352 		zones[i]->ns_rrset = NULL;
353 #ifdef NSEC3
354 		zones[i]->nsec3_soa_rr = NULL;
355 		zones[i]->nsec3_last = NULL;
356 #endif
357 		zones[i]->opts = zone_options_find(opt, domain_dname(zones[i]->apex));
358 		zones[i]->number = i + 1;
359 		zones[i]->is_secure = 0;
360 		zones[i]->updated = 1;
361 		zones[i]->is_ok = 0;
362 		zones[i]->dirty = region_alloc(db->region, sizeof(uint8_t)*num_children);
363 		memset(zones[i]->dirty, 0, sizeof(uint8_t)*num_children);
364 		if(!zones[i]->opts) {
365 			log_msg(LOG_ERR, "cannot load database. Zone %s in db "
366 					 "%s, but not in config file (might "
367 					 "happen if you edited the config "
368 					 "file). Please rebuild database and "
369 					 "start again.",
370 				dname_to_string(dname, NULL), db->filename);
371 			region_destroy(dname_region);
372 			region_destroy(temp_region);
373 			namedb_close(db);
374 			return NULL;
375 		}
376 
377 		region_free_all(dname_region);
378 	}
379 
380 	if (!read_size(db, &dname_count)) {
381 		log_msg(LOG_ERR, "corrupted database (read size): %s", db->filename);
382 		region_destroy(dname_region);
383 		region_destroy(temp_region);
384 		namedb_close(db);
385 		return NULL;
386 	}
387 
388 	DEBUG(DEBUG_DBACCESS, 1,
389 	      (LOG_INFO, "Retrieving %lu domain names\n", (unsigned long) dname_count));
390 
391 	domains = (domain_type **) region_alloc(
392 		temp_region, dname_count * sizeof(domain_type *));
393 	for (i = 0; i < dname_count; ++i) {
394 		const dname_type *dname = read_dname(db->fd, dname_region);
395 		if (!dname) {
396 			log_msg(LOG_ERR, "corrupted database (read dname): %s", db->filename);
397 			region_destroy(dname_region);
398 			region_destroy(temp_region);
399 			namedb_close(db);
400 			return NULL;
401 		}
402 		domains[i] = domain_table_insert(db->domains, dname);
403 		region_free_all(dname_region);
404 	}
405 
406 	region_destroy(dname_region);
407 
408 #ifndef NDEBUG
409 	fprintf(stderr, "database region after loading domain names: ");
410 	region_dump_stats(db->region, stderr);
411 	fprintf(stderr, "\n");
412 #endif
413 
414 	while ((rrset = read_rrset(db, dname_count, domains, zone_count, zones))) {
415 		++rrset_count;
416 		rr_count += rrset->rr_count;
417 	}
418 
419 	DEBUG(DEBUG_DBACCESS, 1,
420 	      (LOG_INFO, "Retrieved %lu RRs in %lu RRsets\n",
421 	       (unsigned long) rr_count, (unsigned long) rrset_count));
422 
423 	region_destroy(temp_region);
424 
425 	if ((db->crc_pos = ftello(db->fd)) == -1) {
426 		log_msg(LOG_ERR, "ftello %s failed: %s",
427 			db->filename, strerror(errno));
428 		namedb_close(db);
429 		return NULL;
430 	}
431 	if (!read_size(db, &db->crc)) {
432 		log_msg(LOG_ERR, "corrupted database (read size): %s", db->filename);
433 		namedb_close(db);
434 		return NULL;
435 	}
436 	if (!read_magic(db)) {
437 		log_msg(LOG_ERR, "corrupted database (read magic): %s", db->filename);
438 		namedb_close(db);
439 		return NULL;
440 	}
441 
442 	fclose(db->fd);
443 	db->fd = NULL;
444 
445 #ifndef NDEBUG
446 	fprintf(stderr, "database region after loading database: ");
447 	region_dump_stats(db->region, stderr);
448 	fprintf(stderr, "\n");
449 #endif
450 
451 	return db;
452 }
453 
454 void
455 namedb_close (struct namedb *db)
456 {
457 	namedb_fd_close(db);
458 	if (db) {
459 		region_destroy(db->region);
460 	}
461 }
462 
463 void
464 namedb_fd_close (struct namedb *db)
465 {
466 	if (db && db->fd) {
467 		fclose(db->fd);
468 	}
469 }
470 
471