Home | History | Annotate | Line # | Download | only in dist
dbcreate.c revision 1.1.1.3
      1 /*
      2  * dbcreate.c -- routines to create an nsd(8) name 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/stat.h>
     13 #include <sys/types.h>
     14 #include <errno.h>
     15 #include <fcntl.h>
     16 #include <stdlib.h>
     17 #include <string.h>
     18 #include <unistd.h>
     19 
     20 #include "namedb.h"
     21 #include "udb.h"
     22 #include "udbradtree.h"
     23 #include "udbzone.h"
     24 #include "options.h"
     25 #include "nsd.h"
     26 
     27 /* pathname directory separator character */
     28 #define PATHSEP '/'
     29 
     30 /** add an rdata (uncompressed) to the destination */
     31 static size_t
     32 add_rdata(rr_type* rr, unsigned i, uint8_t* buf, size_t buflen)
     33 {
     34 	switch(rdata_atom_wireformat_type(rr->type, i)) {
     35 		case RDATA_WF_COMPRESSED_DNAME:
     36 		case RDATA_WF_UNCOMPRESSED_DNAME:
     37 		{
     38 			const dname_type* dname = domain_dname(
     39 				rdata_atom_domain(rr->rdatas[i]));
     40 			if(dname->name_size > buflen)
     41 				return 0;
     42 			memmove(buf, dname_name(dname), dname->name_size);
     43 			return dname->name_size;
     44 		}
     45 		default:
     46 			break;
     47 	}
     48 	if(rdata_atom_size(rr->rdatas[i]) > buflen)
     49 		return 0;
     50 	memmove(buf, rdata_atom_data(rr->rdatas[i]),
     51 		rdata_atom_size(rr->rdatas[i]));
     52 	return rdata_atom_size(rr->rdatas[i]);
     53 }
     54 
     55 /* marshal rdata into buffer, must be MAX_RDLENGTH in size */
     56 size_t
     57 rr_marshal_rdata(rr_type* rr, uint8_t* rdata, size_t sz)
     58 {
     59 	size_t len = 0;
     60 	unsigned i;
     61 	assert(rr);
     62 	for(i=0; i<rr->rdata_count; i++) {
     63 		len += add_rdata(rr, i, rdata+len, sz-len);
     64 	}
     65 	return len;
     66 }
     67 
     68 /** delete an RR */
     69 void
     70 udb_del_rr(udb_base* udb, udb_ptr* z, rr_type* rr)
     71 {
     72 	/* marshal the rdata (uncompressed) into a buffer */
     73 	uint8_t rdata[MAX_RDLENGTH];
     74 	size_t rdatalen = rr_marshal_rdata(rr, rdata, sizeof(rdata));
     75 	assert(udb);
     76 	udb_zone_del_rr(udb, z, dname_name(domain_dname(rr->owner)),
     77 		domain_dname(rr->owner)->name_size, rr->type, rr->klass,
     78 		rdata, rdatalen);
     79 }
     80 
     81 /** write rr */
     82 int
     83 udb_write_rr(udb_base* udb, udb_ptr* z, rr_type* rr)
     84 {
     85 	/* marshal the rdata (uncompressed) into a buffer */
     86 	uint8_t rdata[MAX_RDLENGTH];
     87 	size_t rdatalen = 0;
     88 	unsigned i;
     89 	assert(rr);
     90 	for(i=0; i<rr->rdata_count; i++) {
     91 		rdatalen += add_rdata(rr, i, rdata+rdatalen,
     92 			sizeof(rdata)-rdatalen);
     93 	}
     94 	assert(udb);
     95 	return udb_zone_add_rr(udb, z, dname_name(domain_dname(rr->owner)),
     96 		domain_dname(rr->owner)->name_size, rr->type, rr->klass,
     97 		rr->ttl, rdata, rdatalen);
     98 }
     99 
    100 /** write rrset */
    101 static int
    102 write_rrset(udb_base* udb, udb_ptr* z, rrset_type* rrset)
    103 {
    104 	unsigned i;
    105 	for(i=0; i<rrset->rr_count; i++) {
    106 		if(!udb_write_rr(udb, z, &rrset->rrs[i]))
    107 			return 0;
    108 	}
    109 	return 1;
    110 }
    111 
    112 /** write a zone */
    113 static int
    114 write_zone(udb_base* udb, udb_ptr* z, zone_type* zone)
    115 {
    116 	/* write all domains in the zone */
    117 	domain_type* walk;
    118 	rrset_type* rrset;
    119 	unsigned long n = 0, c = 0;
    120 	time_t t = time(NULL);
    121 
    122 	/* count domains: for pct logging */
    123 	for(walk=zone->apex; walk && domain_is_subdomain(walk, zone->apex);
    124 		walk=domain_next(walk)) {
    125 		n++;
    126 	}
    127 	/* write them */
    128 	for(walk=zone->apex; walk && domain_is_subdomain(walk, zone->apex);
    129 		walk=domain_next(walk)) {
    130 		/* write all rrsets (in the zone) for this domain */
    131 		for(rrset=walk->rrsets; rrset; rrset=rrset->next) {
    132 			if(rrset->zone == zone) {
    133 				if(!write_rrset(udb, z, rrset))
    134 					return 0;
    135 			}
    136 		}
    137 		/* only check every ... domains, and print pct */
    138 		if(++c % ZONEC_PCT_COUNT == 0 && time(NULL) > t + ZONEC_PCT_TIME) {
    139 			t = time(NULL);
    140 			VERBOSITY(1, (LOG_INFO, "write %s %d %%",
    141 				zone->opts->name, (int)(c*((unsigned long)100)/n)));
    142 		}
    143 	}
    144 	return 1;
    145 }
    146 
    147 /** create and write a zone */
    148 int
    149 write_zone_to_udb(udb_base* udb, zone_type* zone, struct timespec* mtime,
    150 	const char* file_str)
    151 {
    152 	udb_ptr z;
    153 	/* make udb dirty */
    154 	udb_base_set_userflags(udb, 1);
    155 	/* find or create zone */
    156 	if(udb_zone_search(udb, &z, dname_name(domain_dname(zone->apex)),
    157 		domain_dname(zone->apex)->name_size)) {
    158 		/* wipe existing contents */
    159 		udb_zone_clear(udb, &z);
    160 	} else {
    161 		if(!udb_zone_create(udb, &z, dname_name(domain_dname(
    162 			zone->apex)), domain_dname(zone->apex)->name_size)) {
    163 			udb_base_set_userflags(udb, 0);
    164 			return 0;
    165 		}
    166 	}
    167 	/* set mtime */
    168 	ZONE(&z)->mtime = (uint64_t)mtime->tv_sec;
    169 	ZONE(&z)->mtime_nsec = (uint64_t)mtime->tv_nsec;
    170 	ZONE(&z)->is_changed = 0;
    171 	udb_zone_set_log_str(udb, &z, NULL);
    172 	udb_zone_set_file_str(udb, &z, file_str);
    173 	/* write zone */
    174 	if(!write_zone(udb, &z, zone)) {
    175 		udb_base_set_userflags(udb, 0);
    176 		return 0;
    177 	}
    178 	udb_ptr_unlink(&z, udb);
    179 	udb_base_set_userflags(udb, 0);
    180 	return 1;
    181 }
    182 
    183 static int
    184 print_rrs(FILE* out, struct zone* zone)
    185 {
    186 	rrset_type *rrset;
    187 	domain_type *domain = zone->apex;
    188 	region_type* region = region_create(xalloc, free);
    189 	region_type* rr_region = region_create(xalloc, free);
    190 	buffer_type* rr_buffer = buffer_create(region, MAX_RDLENGTH);
    191 	struct state_pretty_rr* state = create_pretty_rr(region);
    192 	/* first print the SOA record for the zone */
    193 	if(zone->soa_rrset) {
    194 		size_t i;
    195 		for(i=0; i < zone->soa_rrset->rr_count; i++) {
    196 			if(!print_rr(out, state, &zone->soa_rrset->rrs[i],
    197 				rr_region, rr_buffer)){
    198 				log_msg(LOG_ERR, "There was an error "
    199 				   "printing SOARR to zone %s",
    200 				   zone->opts->name);
    201 				region_destroy(region);
    202 				region_destroy(rr_region);
    203 				return 0;
    204 			}
    205 		}
    206 	}
    207 	/* go through entire tree below the zone apex (incl subzones) */
    208 	while(domain && domain_is_subdomain(domain, zone->apex))
    209 	{
    210 		for(rrset = domain->rrsets; rrset; rrset=rrset->next)
    211 		{
    212 			size_t i;
    213 			if(rrset->zone != zone || rrset == zone->soa_rrset)
    214 				continue;
    215 			for(i=0; i < rrset->rr_count; i++) {
    216 				if(!print_rr(out, state, &rrset->rrs[i],
    217 					rr_region, rr_buffer)){
    218 					log_msg(LOG_ERR, "There was an error "
    219 					   "printing RR to zone %s",
    220 					   zone->opts->name);
    221 					region_destroy(region);
    222 					region_destroy(rr_region);
    223 					return 0;
    224 				}
    225 			}
    226 		}
    227 		domain = domain_next(domain);
    228 	}
    229 	region_destroy(region);
    230 	region_destroy(rr_region);
    231 	return 1;
    232 }
    233 
    234 static int
    235 print_header(zone_type* zone, FILE* out, time_t* now, const char* logs)
    236 {
    237 	char buf[4096+16];
    238 	/* ctime prints newline at end of this line */
    239 	snprintf(buf, sizeof(buf), "; zone %s written by NSD %s on %s",
    240 		zone->opts->name, PACKAGE_VERSION, ctime(now));
    241 	if(!write_data(out, buf, strlen(buf)))
    242 		return 0;
    243 	if(!logs || logs[0] == 0) return 1;
    244 	snprintf(buf, sizeof(buf), "; %s\n", logs);
    245 	return write_data(out, buf, strlen(buf));
    246 }
    247 
    248 static int
    249 write_to_zonefile(zone_type* zone, const char* filename, const char* logs)
    250 {
    251 	time_t now = time(0);
    252 	FILE *out = fopen(filename, "w");
    253 	if(!out) {
    254 		log_msg(LOG_ERR, "cannot write zone %s file %s: %s",
    255 			zone->opts->name, filename, strerror(errno));
    256 		return 0;
    257 	}
    258 	if(!print_header(zone, out, &now, logs)) {
    259 		fclose(out);
    260 		log_msg(LOG_ERR, "There was an error printing "
    261 			"the header to zone %s", zone->opts->name);
    262 		return 0;
    263 	}
    264 	if(!print_rrs(out, zone)) {
    265 		fclose(out);
    266 		return 0;
    267 	}
    268 	if(fclose(out) != 0) {
    269 		log_msg(LOG_ERR, "cannot write zone %s to file %s: fclose: %s",
    270 			zone->opts->name, filename, strerror(errno));
    271 		return 0;
    272 	}
    273 	return 1;
    274 }
    275 
    276 /** create directories above this file, .../dir/dir/dir/file */
    277 int
    278 create_dirs(const char* path)
    279 {
    280 	char dir[4096];
    281 	char* p;
    282 	strlcpy(dir, path, sizeof(dir));
    283 	/* if we start with / then do not try to create '' */
    284 	if(dir[0] == PATHSEP)
    285 		p = strchr(dir+1, PATHSEP);
    286 	else	p = strchr(dir, PATHSEP);
    287 	/* create each directory component from the left */
    288 	while(p) {
    289 		assert(*p == PATHSEP);
    290 		*p = 0; /* end the directory name here */
    291 		if(mkdir(dir
    292 #ifndef MKDIR_HAS_ONE_ARG
    293 			, 0750
    294 #endif
    295 			) == -1) {
    296 			if(errno != EEXIST) {
    297 				log_msg(LOG_ERR, "create dir %s: %s",
    298 					dir, strerror(errno));
    299 				return 0;
    300 			}
    301 			/* it already exists, OK, continue */
    302 		}
    303 		*p = PATHSEP;
    304 		p = strchr(p+1, PATHSEP);
    305 	}
    306 	return 1;
    307 }
    308 
    309 /** create pathname components and check if file exists */
    310 static int
    311 create_path_components(const char* path, int* notexist)
    312 {
    313 	/* stat the file, to see if it exists, and if its directories exist */
    314 	struct stat s;
    315 	if(stat(path, &s) != 0) {
    316 		if(errno == ENOENT) {
    317 			*notexist = 1;
    318 			/* see if we need to create pathname components */
    319 			return create_dirs(path);
    320 		}
    321 		log_msg(LOG_ERR, "cannot stat %s: %s", path, strerror(errno));
    322 		return 0;
    323 	}
    324 	*notexist = 0;
    325 	return 1;
    326 }
    327 
    328 void
    329 namedb_write_zonefile(struct nsd* nsd, struct zone_options* zopt)
    330 {
    331 	const char* zfile;
    332 	int notexist = 0;
    333 	zone_type* zone;
    334 	/* if no zone exists, it has no contents or it has no zonefile
    335 	 * configured, then no need to write data to disk */
    336 	if(!zopt->pattern->zonefile)
    337 		return;
    338 	zone = namedb_find_zone(nsd->db, (const dname_type*)zopt->node.key);
    339 	if(!zone || !zone->apex || !zone->soa_rrset)
    340 		return;
    341 	/* write if file does not exist, or if changed */
    342 	/* so, determine filename, create directory components, check exist*/
    343 	zfile = config_make_zonefile(zopt, nsd);
    344 	if(!create_path_components(zfile, &notexist)) {
    345 		log_msg(LOG_ERR, "could not write zone %s to file %s because "
    346 			"the path could not be created", zopt->name, zfile);
    347 		return;
    348 	}
    349 
    350 	/* if not changed, do not write. */
    351 	if(notexist || zone->is_changed) {
    352 		char logs[4096];
    353 		char bakfile[4096];
    354 		struct timespec mtime;
    355 		udb_ptr zudb;
    356 		if(nsd->db->udb) {
    357 			if(!udb_zone_search(nsd->db->udb, &zudb,
    358 				dname_name(domain_dname(zone->apex)),
    359 				domain_dname(zone->apex)->name_size))
    360 				return; /* zone does not exist in db */
    361 		}
    362 		/* write to zfile~ first, then rename if that works */
    363 		snprintf(bakfile, sizeof(bakfile), "%s~", zfile);
    364 		if(nsd->db->udb && ZONE(&zudb)->log_str.data) {
    365 			udb_ptr s;
    366 			udb_ptr_new(&s, nsd->db->udb, &ZONE(&zudb)->log_str);
    367 			strlcpy(logs, (char*)udb_ptr_data(&s), sizeof(logs));
    368 			udb_ptr_unlink(&s, nsd->db->udb);
    369 		} else if(zone->logstr) {
    370 			strlcpy(logs, zone->logstr, sizeof(logs));
    371 		} else logs[0] = 0;
    372 		VERBOSITY(1, (LOG_INFO, "writing zone %s to file %s",
    373 			zone->opts->name, zfile));
    374 		if(!write_to_zonefile(zone, bakfile, logs)) {
    375 			if(nsd->db->udb)
    376 				udb_ptr_unlink(&zudb, nsd->db->udb);
    377 			(void)unlink(bakfile); /* delete failed file */
    378 			return; /* error already printed */
    379 		}
    380 		if(rename(bakfile, zfile) == -1) {
    381 			log_msg(LOG_ERR, "rename(%s to %s) failed: %s",
    382 				bakfile, zfile, strerror(errno));
    383 			if(nsd->db->udb)
    384 				udb_ptr_unlink(&zudb, nsd->db->udb);
    385 			(void)unlink(bakfile); /* delete failed file */
    386 			return;
    387 		}
    388 		zone->is_changed = 0;
    389 		/* fetch the mtime of the just created zonefile so we
    390 		 * do not waste effort reading it back in */
    391 		if(!file_get_mtime(zfile, &mtime, &notexist)) {
    392 			get_time(&mtime);
    393 		}
    394 		if(nsd->db->udb) {
    395 			ZONE(&zudb)->mtime = (uint64_t)mtime.tv_sec;
    396 			ZONE(&zudb)->mtime_nsec = (uint64_t)mtime.tv_nsec;
    397 			ZONE(&zudb)->is_changed = 0;
    398 			udb_zone_set_log_str(nsd->db->udb, &zudb, NULL);
    399 			udb_ptr_unlink(&zudb, nsd->db->udb);
    400 		} else {
    401 			zone->mtime = mtime;
    402 			if(zone->filename)
    403 				region_recycle(nsd->db->region, zone->filename,
    404 					strlen(zone->filename)+1);
    405 			zone->filename = region_strdup(nsd->db->region, zfile);
    406 			if(zone->logstr)
    407 				region_recycle(nsd->db->region, zone->logstr,
    408 					strlen(zone->logstr)+1);
    409 			zone->logstr = NULL;
    410 		}
    411 	}
    412 }
    413 
    414 void
    415 namedb_write_zonefiles(struct nsd* nsd, struct nsd_options* options)
    416 {
    417 	struct zone_options* zo;
    418 	RBTREE_FOR(zo, struct zone_options*, options->zone_options) {
    419 		namedb_write_zonefile(nsd, zo);
    420 	}
    421 }
    422