Home | History | Annotate | Line # | Download | only in dist
      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 "options.h"
     23 #include "nsd.h"
     24 #include "ixfr.h"
     25 
     26 /* pathname directory separator character */
     27 #define PATHSEP '/'
     28 
     29 int
     30 print_rrs(FILE* out, struct zone* zone)
     31 {
     32 	rrset_type *rrset;
     33 	domain_type *domain = zone->apex;
     34 	region_type* region = region_create(xalloc, free);
     35 	region_type* rr_region = region_create(xalloc, free);
     36 	buffer_type* rr_buffer = buffer_create(region, MAX_RDLENGTH);
     37 	struct state_pretty_rr* state = create_pretty_rr(region);
     38 	/* first print the SOA record for the zone */
     39 	if(zone->soa_rrset) {
     40 		size_t i;
     41 		for(i=0; i < zone->soa_rrset->rr_count; i++) {
     42 			if(!print_rr(out, state, zone->soa_rrset->rrs[i],
     43 				rr_region, rr_buffer)){
     44 				log_msg(LOG_ERR, "There was an error "
     45 				   "printing SOARR to zone %s",
     46 				   zone->opts->name);
     47 				region_destroy(region);
     48 				region_destroy(rr_region);
     49 				return 0;
     50 			}
     51 		}
     52 	}
     53 	/* go through entire tree below the zone apex (incl subzones) */
     54 	while(domain && domain_is_subdomain(domain, zone->apex))
     55 	{
     56 		for(rrset = domain->rrsets; rrset; rrset=rrset->next)
     57 		{
     58 			size_t i;
     59 			if(rrset->zone != zone || rrset == zone->soa_rrset)
     60 				continue;
     61 			for(i=0; i < rrset->rr_count; i++) {
     62 				if(!print_rr(out, state, rrset->rrs[i],
     63 					rr_region, rr_buffer)){
     64 					log_msg(LOG_ERR, "There was an error "
     65 					   "printing RR to zone %s",
     66 					   zone->opts->name);
     67 					region_destroy(region);
     68 					region_destroy(rr_region);
     69 					return 0;
     70 				}
     71 			}
     72 		}
     73 		domain = domain_next(domain);
     74 	}
     75 	region_destroy(region);
     76 	region_destroy(rr_region);
     77 	return 1;
     78 }
     79 
     80 static int
     81 print_header(zone_type* zone, FILE* out, time_t* now, const char* logs)
     82 {
     83 	char buf[4096+16];
     84 	/* ctime prints newline at end of this line */
     85 	snprintf(buf, sizeof(buf), "; zone %s written by NSD %s on %s",
     86 		zone->opts->name, PACKAGE_VERSION, ctime(now));
     87 	if(!write_data(out, buf, strlen(buf)))
     88 		return 0;
     89 	if(!logs || logs[0] == 0) return 1;
     90 	snprintf(buf, sizeof(buf), "; %s\n", logs);
     91 	return write_data(out, buf, strlen(buf));
     92 }
     93 
     94 static int
     95 write_to_zonefile(zone_type* zone, const char* filename, const char* logs)
     96 {
     97 	time_t now = time(0);
     98 	FILE *out = fopen(filename, "w");
     99 	if(!out) {
    100 		log_msg(LOG_ERR, "cannot write zone %s file %s: %s",
    101 			zone->opts->name, filename, strerror(errno));
    102 		return 0;
    103 	}
    104 	if(!print_header(zone, out, &now, logs)) {
    105 		fclose(out);
    106 		log_msg(LOG_ERR, "There was an error printing "
    107 			"the header to zone %s", zone->opts->name);
    108 		return 0;
    109 	}
    110 	if(!print_rrs(out, zone)) {
    111 		fclose(out);
    112 		return 0;
    113 	}
    114 	if(fclose(out) != 0) {
    115 		log_msg(LOG_ERR, "cannot write zone %s to file %s: fclose: %s",
    116 			zone->opts->name, filename, strerror(errno));
    117 		return 0;
    118 	}
    119 	return 1;
    120 }
    121 
    122 /** create directories above this file, .../dir/dir/dir/file */
    123 int
    124 create_dirs(const char* path)
    125 {
    126 	char dir[4096];
    127 	char* p;
    128 	strlcpy(dir, path, sizeof(dir));
    129 	/* if we start with / then do not try to create '' */
    130 	if(dir[0] == PATHSEP)
    131 		p = strchr(dir+1, PATHSEP);
    132 	else	p = strchr(dir, PATHSEP);
    133 	/* create each directory component from the left */
    134 	while(p) {
    135 		assert(*p == PATHSEP);
    136 		*p = 0; /* end the directory name here */
    137 		if(mkdir(dir
    138 #ifndef MKDIR_HAS_ONE_ARG
    139 			, 0750
    140 #endif
    141 			) == -1) {
    142 			if(errno != EEXIST) {
    143 				log_msg(LOG_ERR, "create dir %s: %s",
    144 					dir, strerror(errno));
    145 				*p = PATHSEP; /* restore input string */
    146 				return 0;
    147 			}
    148 			/* it already exists, OK, continue */
    149 		}
    150 		*p = PATHSEP;
    151 		p = strchr(p+1, PATHSEP);
    152 	}
    153 	return 1;
    154 }
    155 
    156 /** create pathname components and check if file exists */
    157 static int
    158 create_path_components(const char* path, int* notexist)
    159 {
    160 	/* stat the file, to see if it exists, and if its directories exist */
    161 	struct stat s;
    162 	if(stat(path, &s) != 0) {
    163 		if(errno == ENOENT) {
    164 			*notexist = 1;
    165 			/* see if we need to create pathname components */
    166 			return create_dirs(path);
    167 		}
    168 		log_msg(LOG_ERR, "cannot stat %s: %s", path, strerror(errno));
    169 		return 0;
    170 	}
    171 	*notexist = 0;
    172 	return 1;
    173 }
    174 
    175 void
    176 namedb_write_zonefile(struct nsd* nsd, struct zone_options* zopt)
    177 {
    178 	const char* zfile;
    179 	int notexist = 0;
    180 	zone_type* zone;
    181 	/* if no zone exists, it has no contents or it has no zonefile
    182 	 * configured, then no need to write data to disk */
    183 	if(!zopt->pattern->zonefile)
    184 		return;
    185 	zone = namedb_find_zone(nsd->db, (const dname_type*)zopt->node.key);
    186 	if(!zone || !zone->apex || !zone->soa_rrset)
    187 		return;
    188 	/* write if file does not exist, or if changed */
    189 	/* so, determine filename, create directory components, check exist*/
    190 	zfile = config_make_zonefile(zopt, nsd);
    191 	if(!create_path_components(zfile, &notexist)) {
    192 		log_msg(LOG_ERR, "could not write zone %s to file %s because "
    193 			"the path could not be created", zopt->name, zfile);
    194 		return;
    195 	}
    196 
    197 	/* if not changed, do not write. */
    198 	if(notexist || zone->is_changed) {
    199 		char logs[4096];
    200 		char bakfile[4096];
    201 		struct timespec mtime;
    202 		/* write to zfile~ first, then rename if that works */
    203 		snprintf(bakfile, sizeof(bakfile), "%s~", zfile);
    204 		if(zone->logstr)
    205 			strlcpy(logs, zone->logstr, sizeof(logs));
    206 		else
    207 			logs[0] = 0;
    208 		VERBOSITY(1, (LOG_INFO, "writing zone %s to file %s",
    209 			zone->opts->name, zfile));
    210 		if(!write_to_zonefile(zone, bakfile, logs)) {
    211 			(void)unlink(bakfile); /* delete failed file */
    212 			return; /* error already printed */
    213 		}
    214 		if(rename(bakfile, zfile) == -1) {
    215 			log_msg(LOG_ERR, "rename(%s to %s) failed: %s",
    216 				bakfile, zfile, strerror(errno));
    217 			(void)unlink(bakfile); /* delete failed file */
    218 			return;
    219 		}
    220 		zone->is_changed = 0;
    221 		VERBOSITY(3, (LOG_INFO, "zone %s written to file %s",
    222 			zone->opts->name, zfile));
    223 		/* fetch the mtime of the just created zonefile so we
    224 		 * do not waste effort reading it back in */
    225 		if(!file_get_mtime(zfile, &mtime, &notexist)) {
    226 			get_time(&mtime);
    227 		}
    228 		zone->mtime = mtime;
    229 		if(zone->filename)
    230 			region_recycle(nsd->db->region, zone->filename,
    231 				strlen(zone->filename)+1);
    232 		zone->filename = region_strdup(nsd->db->region, zfile);
    233 		if(zone->logstr)
    234 			region_recycle(nsd->db->region, zone->logstr,
    235 				strlen(zone->logstr)+1);
    236 		zone->logstr = NULL;
    237 		if(zone_is_ixfr_enabled(zone) && zone->ixfr)
    238 			ixfr_write_to_file(zone, zfile);
    239 	}
    240 }
    241 
    242 void
    243 namedb_write_zonefiles(struct nsd* nsd, struct nsd_options* options)
    244 {
    245 	struct zone_options* zo;
    246 	RBTREE_FOR(zo, struct zone_options*, options->zone_options) {
    247 		namedb_write_zonefile(nsd, zo);
    248 	}
    249 }
    250