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