xref: /openbsd-src/usr.sbin/nsd/dbcreate.c (revision b71395ea3d4830c6fd338870b804059761b8292d)
162ac0c33Sjakob /*
262ac0c33Sjakob  * dbcreate.c -- routines to create an nsd(8) name database
362ac0c33Sjakob  *
4d3fecca9Ssthen  * Copyright (c) 2001-2006, NLnet Labs. All rights reserved.
562ac0c33Sjakob  *
662ac0c33Sjakob  * See LICENSE for the license.
762ac0c33Sjakob  *
862ac0c33Sjakob  */
962ac0c33Sjakob 
102c1ae072Ssthen #include "config.h"
1162ac0c33Sjakob 
12d3fecca9Ssthen #include <sys/stat.h>
1362ac0c33Sjakob #include <sys/types.h>
1462ac0c33Sjakob #include <errno.h>
1562ac0c33Sjakob #include <fcntl.h>
1662ac0c33Sjakob #include <stdlib.h>
1762ac0c33Sjakob #include <string.h>
1862ac0c33Sjakob #include <unistd.h>
1962ac0c33Sjakob 
2062ac0c33Sjakob #include "namedb.h"
21d3fecca9Ssthen #include "udb.h"
22d3fecca9Ssthen #include "options.h"
23cdb6bbddSbrad #include "nsd.h"
244564029fSflorian #include "ixfr.h"
2562ac0c33Sjakob 
26d3fecca9Ssthen /* pathname directory separator character */
27d3fecca9Ssthen #define PATHSEP '/'
2862ac0c33Sjakob 
29d3fecca9Ssthen /** add an rdata (uncompressed) to the destination */
30d3fecca9Ssthen static size_t
add_rdata(rr_type * rr,unsigned i,uint8_t * buf,size_t buflen)31d3fecca9Ssthen add_rdata(rr_type* rr, unsigned i, uint8_t* buf, size_t buflen)
3262ac0c33Sjakob {
33d3fecca9Ssthen 	switch(rdata_atom_wireformat_type(rr->type, i)) {
34d3fecca9Ssthen 		case RDATA_WF_COMPRESSED_DNAME:
35d3fecca9Ssthen 		case RDATA_WF_UNCOMPRESSED_DNAME:
36d3fecca9Ssthen 		{
37d3fecca9Ssthen 			const dname_type* dname = domain_dname(
38d3fecca9Ssthen 				rdata_atom_domain(rr->rdatas[i]));
39d3fecca9Ssthen 			if(dname->name_size > buflen)
40d3fecca9Ssthen 				return 0;
41d3fecca9Ssthen 			memmove(buf, dname_name(dname), dname->name_size);
42d3fecca9Ssthen 			return dname->name_size;
430c2b6c02Sjakob 		}
44d3fecca9Ssthen 		default:
45d3fecca9Ssthen 			break;
46d3fecca9Ssthen 	}
47cdb6bbddSbrad 	if(rdata_atom_size(rr->rdatas[i]) > buflen)
48cdb6bbddSbrad 		return 0;
49d3fecca9Ssthen 	memmove(buf, rdata_atom_data(rr->rdatas[i]),
50d3fecca9Ssthen 		rdata_atom_size(rr->rdatas[i]));
51d3fecca9Ssthen 	return rdata_atom_size(rr->rdatas[i]);
5262ac0c33Sjakob }
5362ac0c33Sjakob 
54d3fecca9Ssthen /* marshal rdata into buffer, must be MAX_RDLENGTH in size */
55d3fecca9Ssthen size_t
rr_marshal_rdata(rr_type * rr,uint8_t * rdata,size_t sz)56d3fecca9Ssthen rr_marshal_rdata(rr_type* rr, uint8_t* rdata, size_t sz)
57d3fecca9Ssthen {
58d3fecca9Ssthen 	size_t len = 0;
59d3fecca9Ssthen 	unsigned i;
60d3fecca9Ssthen 	assert(rr);
61d3fecca9Ssthen 	for(i=0; i<rr->rdata_count; i++) {
62d3fecca9Ssthen 		len += add_rdata(rr, i, rdata+len, sz-len);
63d3fecca9Ssthen 	}
64d3fecca9Ssthen 	return len;
6562ac0c33Sjakob }
6662ac0c33Sjakob 
678d298c9fSsthen int
print_rrs(FILE * out,struct zone * zone)68d3fecca9Ssthen print_rrs(FILE* out, struct zone* zone)
69d3fecca9Ssthen {
70d3fecca9Ssthen 	rrset_type *rrset;
71d3fecca9Ssthen 	domain_type *domain = zone->apex;
72d3fecca9Ssthen 	region_type* region = region_create(xalloc, free);
7315ed76cbSbrad 	region_type* rr_region = region_create(xalloc, free);
7415ed76cbSbrad 	buffer_type* rr_buffer = buffer_create(region, MAX_RDLENGTH);
75d3fecca9Ssthen 	struct state_pretty_rr* state = create_pretty_rr(region);
76d3fecca9Ssthen 	/* first print the SOA record for the zone */
77d3fecca9Ssthen 	if(zone->soa_rrset) {
78d3fecca9Ssthen 		size_t i;
79d3fecca9Ssthen 		for(i=0; i < zone->soa_rrset->rr_count; i++) {
8015ed76cbSbrad 			if(!print_rr(out, state, &zone->soa_rrset->rrs[i],
8115ed76cbSbrad 				rr_region, rr_buffer)){
82d3fecca9Ssthen 				log_msg(LOG_ERR, "There was an error "
83d3fecca9Ssthen 				   "printing SOARR to zone %s",
84d3fecca9Ssthen 				   zone->opts->name);
85d3fecca9Ssthen 				region_destroy(region);
8615ed76cbSbrad 				region_destroy(rr_region);
87d3fecca9Ssthen 				return 0;
88d3fecca9Ssthen 			}
89d3fecca9Ssthen 		}
90d3fecca9Ssthen 	}
91d3fecca9Ssthen 	/* go through entire tree below the zone apex (incl subzones) */
92d3fecca9Ssthen 	while(domain && domain_is_subdomain(domain, zone->apex))
93d3fecca9Ssthen 	{
94d3fecca9Ssthen 		for(rrset = domain->rrsets; rrset; rrset=rrset->next)
95d3fecca9Ssthen 		{
96d3fecca9Ssthen 			size_t i;
97d3fecca9Ssthen 			if(rrset->zone != zone || rrset == zone->soa_rrset)
98d3fecca9Ssthen 				continue;
99d3fecca9Ssthen 			for(i=0; i < rrset->rr_count; i++) {
10015ed76cbSbrad 				if(!print_rr(out, state, &rrset->rrs[i],
10115ed76cbSbrad 					rr_region, rr_buffer)){
102d3fecca9Ssthen 					log_msg(LOG_ERR, "There was an error "
103d3fecca9Ssthen 					   "printing RR to zone %s",
104d3fecca9Ssthen 					   zone->opts->name);
105d3fecca9Ssthen 					region_destroy(region);
10615ed76cbSbrad 					region_destroy(rr_region);
107d3fecca9Ssthen 					return 0;
108d3fecca9Ssthen 				}
109d3fecca9Ssthen 			}
110d3fecca9Ssthen 		}
111d3fecca9Ssthen 		domain = domain_next(domain);
112d3fecca9Ssthen 	}
113d3fecca9Ssthen 	region_destroy(region);
11415ed76cbSbrad 	region_destroy(rr_region);
115d3fecca9Ssthen 	return 1;
116d3fecca9Ssthen }
117d3fecca9Ssthen 
118d3fecca9Ssthen static int
print_header(zone_type * zone,FILE * out,time_t * now,const char * logs)119d3fecca9Ssthen print_header(zone_type* zone, FILE* out, time_t* now, const char* logs)
120d3fecca9Ssthen {
121bfd0b123Sflorian 	char buf[4096+16];
122d3fecca9Ssthen 	/* ctime prints newline at end of this line */
123d3fecca9Ssthen 	snprintf(buf, sizeof(buf), "; zone %s written by NSD %s on %s",
124d3fecca9Ssthen 		zone->opts->name, PACKAGE_VERSION, ctime(now));
125d3fecca9Ssthen 	if(!write_data(out, buf, strlen(buf)))
126d3fecca9Ssthen 		return 0;
127d3fecca9Ssthen 	if(!logs || logs[0] == 0) return 1;
128d3fecca9Ssthen 	snprintf(buf, sizeof(buf), "; %s\n", logs);
129d3fecca9Ssthen 	return write_data(out, buf, strlen(buf));
130d3fecca9Ssthen }
131d3fecca9Ssthen 
132d3fecca9Ssthen static int
write_to_zonefile(zone_type * zone,const char * filename,const char * logs)133d3fecca9Ssthen write_to_zonefile(zone_type* zone, const char* filename, const char* logs)
134d3fecca9Ssthen {
135d3fecca9Ssthen 	time_t now = time(0);
13615ed76cbSbrad 	FILE *out = fopen(filename, "w");
137d3fecca9Ssthen 	if(!out) {
138d3fecca9Ssthen 		log_msg(LOG_ERR, "cannot write zone %s file %s: %s",
139d3fecca9Ssthen 			zone->opts->name, filename, strerror(errno));
140d3fecca9Ssthen 		return 0;
141d3fecca9Ssthen 	}
142d3fecca9Ssthen 	if(!print_header(zone, out, &now, logs)) {
143d3fecca9Ssthen 		fclose(out);
144d3fecca9Ssthen 		log_msg(LOG_ERR, "There was an error printing "
145d3fecca9Ssthen 			"the header to zone %s", zone->opts->name);
146d3fecca9Ssthen 		return 0;
147d3fecca9Ssthen 	}
148d3fecca9Ssthen 	if(!print_rrs(out, zone)) {
149d3fecca9Ssthen 		fclose(out);
150d3fecca9Ssthen 		return 0;
151d3fecca9Ssthen 	}
15215ed76cbSbrad 	if(fclose(out) != 0) {
15315ed76cbSbrad 		log_msg(LOG_ERR, "cannot write zone %s to file %s: fclose: %s",
15415ed76cbSbrad 			zone->opts->name, filename, strerror(errno));
15515ed76cbSbrad 		return 0;
15615ed76cbSbrad 	}
157d3fecca9Ssthen 	return 1;
158d3fecca9Ssthen }
159d3fecca9Ssthen 
160d3fecca9Ssthen /** create directories above this file, .../dir/dir/dir/file */
161d3fecca9Ssthen int
create_dirs(const char * path)162d3fecca9Ssthen create_dirs(const char* path)
163d3fecca9Ssthen {
164d3fecca9Ssthen 	char dir[4096];
165d3fecca9Ssthen 	char* p;
166d3fecca9Ssthen 	strlcpy(dir, path, sizeof(dir));
167d3fecca9Ssthen 	/* if we start with / then do not try to create '' */
168d3fecca9Ssthen 	if(dir[0] == PATHSEP)
169d3fecca9Ssthen 		p = strchr(dir+1, PATHSEP);
170d3fecca9Ssthen 	else	p = strchr(dir, PATHSEP);
171d3fecca9Ssthen 	/* create each directory component from the left */
172d3fecca9Ssthen 	while(p) {
173d3fecca9Ssthen 		assert(*p == PATHSEP);
174d3fecca9Ssthen 		*p = 0; /* end the directory name here */
175d3fecca9Ssthen 		if(mkdir(dir
176d3fecca9Ssthen #ifndef MKDIR_HAS_ONE_ARG
177d3fecca9Ssthen 			, 0750
178d3fecca9Ssthen #endif
179d3fecca9Ssthen 			) == -1) {
180d3fecca9Ssthen 			if(errno != EEXIST) {
181d3fecca9Ssthen 				log_msg(LOG_ERR, "create dir %s: %s",
182d3fecca9Ssthen 					dir, strerror(errno));
183308d2509Sflorian 				*p = PATHSEP; /* restore input string */
184d3fecca9Ssthen 				return 0;
185d3fecca9Ssthen 			}
186d3fecca9Ssthen 			/* it already exists, OK, continue */
187d3fecca9Ssthen 		}
188d3fecca9Ssthen 		*p = PATHSEP;
189d3fecca9Ssthen 		p = strchr(p+1, PATHSEP);
190d3fecca9Ssthen 	}
191d3fecca9Ssthen 	return 1;
192d3fecca9Ssthen }
193d3fecca9Ssthen 
194d3fecca9Ssthen /** create pathname components and check if file exists */
195d3fecca9Ssthen static int
create_path_components(const char * path,int * notexist)196d3fecca9Ssthen create_path_components(const char* path, int* notexist)
197d3fecca9Ssthen {
198d3fecca9Ssthen 	/* stat the file, to see if it exists, and if its directories exist */
199d3fecca9Ssthen 	struct stat s;
200d3fecca9Ssthen 	if(stat(path, &s) != 0) {
201d3fecca9Ssthen 		if(errno == ENOENT) {
202d3fecca9Ssthen 			*notexist = 1;
203d3fecca9Ssthen 			/* see if we need to create pathname components */
204d3fecca9Ssthen 			return create_dirs(path);
205d3fecca9Ssthen 		}
206d3fecca9Ssthen 		log_msg(LOG_ERR, "cannot stat %s: %s", path, strerror(errno));
207d3fecca9Ssthen 		return 0;
208d3fecca9Ssthen 	}
209d3fecca9Ssthen 	*notexist = 0;
210d3fecca9Ssthen 	return 1;
211d3fecca9Ssthen }
21262ac0c33Sjakob 
21362ac0c33Sjakob void
namedb_write_zonefile(struct nsd * nsd,struct zone_options * zopt)214fe5fe5f6Sflorian namedb_write_zonefile(struct nsd* nsd, struct zone_options* zopt)
21562ac0c33Sjakob {
216d3fecca9Ssthen 	const char* zfile;
217d3fecca9Ssthen 	int notexist = 0;
21862ac0c33Sjakob 	zone_type* zone;
219d3fecca9Ssthen 	/* if no zone exists, it has no contents or it has no zonefile
220d3fecca9Ssthen 	 * configured, then no need to write data to disk */
221d3fecca9Ssthen 	if(!zopt->pattern->zonefile)
222d3fecca9Ssthen 		return;
223cdb6bbddSbrad 	zone = namedb_find_zone(nsd->db, (const dname_type*)zopt->node.key);
22415ed76cbSbrad 	if(!zone || !zone->apex || !zone->soa_rrset)
225d3fecca9Ssthen 		return;
226d3fecca9Ssthen 	/* write if file does not exist, or if changed */
227d3fecca9Ssthen 	/* so, determine filename, create directory components, check exist*/
228cdb6bbddSbrad 	zfile = config_make_zonefile(zopt, nsd);
229d3fecca9Ssthen 	if(!create_path_components(zfile, &notexist)) {
230d3fecca9Ssthen 		log_msg(LOG_ERR, "could not write zone %s to file %s because "
231d3fecca9Ssthen 			"the path could not be created", zopt->name, zfile);
232d3fecca9Ssthen 		return;
233d3fecca9Ssthen 	}
23462ac0c33Sjakob 
235d3fecca9Ssthen 	/* if not changed, do not write. */
236d3fecca9Ssthen 	if(notexist || zone->is_changed) {
237d3fecca9Ssthen 		char logs[4096];
238d3fecca9Ssthen 		char bakfile[4096];
239275a8d89Sflorian 		struct timespec mtime;
240d3fecca9Ssthen 		/* write to zfile~ first, then rename if that works */
241d3fecca9Ssthen 		snprintf(bakfile, sizeof(bakfile), "%s~", zfile);
242*b71395eaSflorian 		if(zone->logstr)
24315ed76cbSbrad 			strlcpy(logs, zone->logstr, sizeof(logs));
244*b71395eaSflorian 		else
245*b71395eaSflorian 			logs[0] = 0;
24615ed76cbSbrad 		VERBOSITY(1, (LOG_INFO, "writing zone %s to file %s",
24715ed76cbSbrad 			zone->opts->name, zfile));
248d3fecca9Ssthen 		if(!write_to_zonefile(zone, bakfile, logs)) {
24915ed76cbSbrad 			(void)unlink(bakfile); /* delete failed file */
250d3fecca9Ssthen 			return; /* error already printed */
251d3fecca9Ssthen 		}
252d3fecca9Ssthen 		if(rename(bakfile, zfile) == -1) {
253d3fecca9Ssthen 			log_msg(LOG_ERR, "rename(%s to %s) failed: %s",
254d3fecca9Ssthen 				bakfile, zfile, strerror(errno));
25515ed76cbSbrad 			(void)unlink(bakfile); /* delete failed file */
256d3fecca9Ssthen 			return;
257d3fecca9Ssthen 		}
258d3fecca9Ssthen 		zone->is_changed = 0;
259275a8d89Sflorian 		/* fetch the mtime of the just created zonefile so we
260275a8d89Sflorian 		 * do not waste effort reading it back in */
261275a8d89Sflorian 		if(!file_get_mtime(zfile, &mtime, &notexist)) {
262275a8d89Sflorian 			get_time(&mtime);
263275a8d89Sflorian 		}
264275a8d89Sflorian 		zone->mtime = mtime;
26515ed76cbSbrad 		if(zone->filename)
26615ed76cbSbrad 			region_recycle(nsd->db->region, zone->filename,
26715ed76cbSbrad 				strlen(zone->filename)+1);
26815ed76cbSbrad 		zone->filename = region_strdup(nsd->db->region, zfile);
26915ed76cbSbrad 		if(zone->logstr)
27015ed76cbSbrad 			region_recycle(nsd->db->region, zone->logstr,
27115ed76cbSbrad 				strlen(zone->logstr)+1);
27215ed76cbSbrad 		zone->logstr = NULL;
2734564029fSflorian 		if(zone_is_ixfr_enabled(zone) && zone->ixfr)
2744564029fSflorian 			ixfr_write_to_file(zone, zfile);
27562ac0c33Sjakob 	}
27662ac0c33Sjakob }
27762ac0c33Sjakob 
278d3fecca9Ssthen void
namedb_write_zonefiles(struct nsd * nsd,struct nsd_options * options)279fe5fe5f6Sflorian namedb_write_zonefiles(struct nsd* nsd, struct nsd_options* options)
280d3fecca9Ssthen {
281fe5fe5f6Sflorian 	struct zone_options* zo;
282fe5fe5f6Sflorian 	RBTREE_FOR(zo, struct zone_options*, options->zone_options) {
283cdb6bbddSbrad 		namedb_write_zonefile(nsd, zo);
28462ac0c33Sjakob 	}
28562ac0c33Sjakob }
286