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