dbcreate.c revision 1.1.1.4
1224090Sdougb/*
2224090Sdougb * dbcreate.c -- routines to create an nsd(8) name database
3224090Sdougb *
4224090Sdougb * Copyright (c) 2001-2006, NLnet Labs. All rights reserved.
5224090Sdougb *
6224090Sdougb * See LICENSE for the license.
7224090Sdougb *
8224090Sdougb */
9224090Sdougb
10224090Sdougb#include "config.h"
11224090Sdougb
12224090Sdougb#include <sys/stat.h>
13224090Sdougb#include <sys/types.h>
14224090Sdougb#include <errno.h>
15224090Sdougb#include <fcntl.h>
16224090Sdougb#include <stdlib.h>
17224090Sdougb#include <string.h>
18224090Sdougb#include <unistd.h>
19224090Sdougb
20234010Sdougb#include "namedb.h"
21224090Sdougb#include "udb.h"
22224090Sdougb#include "udbradtree.h"
23224090Sdougb#include "udbzone.h"
24224090Sdougb#include "options.h"
25224090Sdougb#include "nsd.h"
26224090Sdougb
27224090Sdougb/* pathname directory separator character */
28224090Sdougb#define PATHSEP '/'
29224090Sdougb
30224090Sdougb/** add an rdata (uncompressed) to the destination */
31224090Sdougbstatic size_t
32224090Sdougbadd_rdata(rr_type* rr, unsigned i, uint8_t* buf, size_t buflen)
33224090Sdougb{
34224090Sdougb	switch(rdata_atom_wireformat_type(rr->type, i)) {
35224090Sdougb		case RDATA_WF_COMPRESSED_DNAME:
36224090Sdougb		case RDATA_WF_UNCOMPRESSED_DNAME:
37224090Sdougb		{
38224090Sdougb			const dname_type* dname = domain_dname(
39224090Sdougb				rdata_atom_domain(rr->rdatas[i]));
40224090Sdougb			if(dname->name_size > buflen)
41224090Sdougb				return 0;
42224090Sdougb			memmove(buf, dname_name(dname), dname->name_size);
43224090Sdougb			return dname->name_size;
44224090Sdougb		}
45224090Sdougb		default:
46224090Sdougb			break;
47224090Sdougb	}
48224090Sdougb	if(rdata_atom_size(rr->rdatas[i]) > buflen)
49224090Sdougb		return 0;
50224090Sdougb	memmove(buf, rdata_atom_data(rr->rdatas[i]),
51224090Sdougb		rdata_atom_size(rr->rdatas[i]));
52224090Sdougb	return rdata_atom_size(rr->rdatas[i]);
53224090Sdougb}
54224090Sdougb
55224090Sdougb/* marshal rdata into buffer, must be MAX_RDLENGTH in size */
56224090Sdougbsize_t
57224090Sdougbrr_marshal_rdata(rr_type* rr, uint8_t* rdata, size_t sz)
58224090Sdougb{
59224090Sdougb	size_t len = 0;
60224090Sdougb	unsigned i;
61224090Sdougb	assert(rr);
62224090Sdougb	for(i=0; i<rr->rdata_count; i++) {
63224090Sdougb		len += add_rdata(rr, i, rdata+len, sz-len);
64224090Sdougb	}
65224090Sdougb	return len;
66224090Sdougb}
67224090Sdougb
68224090Sdougb/** delete an RR */
69224090Sdougbvoid
70224090Sdougbudb_del_rr(udb_base* udb, udb_ptr* z, rr_type* rr)
71224090Sdougb{
72224090Sdougb	/* marshal the rdata (uncompressed) into a buffer */
73224090Sdougb	uint8_t rdata[MAX_RDLENGTH];
74224090Sdougb	size_t rdatalen = rr_marshal_rdata(rr, rdata, sizeof(rdata));
75224090Sdougb	assert(udb);
76224090Sdougb	udb_zone_del_rr(udb, z, dname_name(domain_dname(rr->owner)),
77224090Sdougb		domain_dname(rr->owner)->name_size, rr->type, rr->klass,
78224090Sdougb		rdata, rdatalen);
79224090Sdougb}
80224090Sdougb
81224090Sdougb/** write rr */
82224090Sdougbint
83224090Sdougbudb_write_rr(udb_base* udb, udb_ptr* z, rr_type* rr)
84224090Sdougb{
85224090Sdougb	/* marshal the rdata (uncompressed) into a buffer */
86224090Sdougb	uint8_t rdata[MAX_RDLENGTH];
87224090Sdougb	size_t rdatalen = 0;
88224090Sdougb	unsigned i;
89224090Sdougb	assert(rr);
90224090Sdougb	for(i=0; i<rr->rdata_count; i++) {
91224090Sdougb		rdatalen += add_rdata(rr, i, rdata+rdatalen,
92224090Sdougb			sizeof(rdata)-rdatalen);
93224090Sdougb	}
94224090Sdougb	assert(udb);
95224090Sdougb	return udb_zone_add_rr(udb, z, dname_name(domain_dname(rr->owner)),
96224090Sdougb		domain_dname(rr->owner)->name_size, rr->type, rr->klass,
97224090Sdougb		rr->ttl, rdata, rdatalen);
98224090Sdougb}
99224090Sdougb
100224090Sdougb/** write rrset */
101224090Sdougbstatic int
102224090Sdougbwrite_rrset(udb_base* udb, udb_ptr* z, rrset_type* rrset)
103224090Sdougb{
104224090Sdougb	unsigned i;
105224090Sdougb	for(i=0; i<rrset->rr_count; i++) {
106224090Sdougb		if(!udb_write_rr(udb, z, &rrset->rrs[i]))
107224090Sdougb			return 0;
108224090Sdougb	}
109224090Sdougb	return 1;
110224090Sdougb}
111224090Sdougb
112224090Sdougb/** write a zone */
113224090Sdougbstatic int
114224090Sdougbwrite_zone(udb_base* udb, udb_ptr* z, zone_type* zone)
115224090Sdougb{
116224090Sdougb	/* write all domains in the zone */
117224090Sdougb	domain_type* walk;
118224090Sdougb	rrset_type* rrset;
119224090Sdougb	unsigned long n = 0, c = 0;
120224090Sdougb	time_t t = time(NULL);
121224090Sdougb
122224090Sdougb	/* count domains: for pct logging */
123224090Sdougb	for(walk=zone->apex; walk && domain_is_subdomain(walk, zone->apex);
124224090Sdougb		walk=domain_next(walk)) {
125224090Sdougb		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 */
148int
149write_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
183static int
184print_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
234static int
235print_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
248static int
249write_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 */
277int
278create_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				*p = PATHSEP; /* restore input string */
300				return 0;
301			}
302			/* it already exists, OK, continue */
303		}
304		*p = PATHSEP;
305		p = strchr(p+1, PATHSEP);
306	}
307	return 1;
308}
309
310/** create pathname components and check if file exists */
311static int
312create_path_components(const char* path, int* notexist)
313{
314	/* stat the file, to see if it exists, and if its directories exist */
315	struct stat s;
316	if(stat(path, &s) != 0) {
317		if(errno == ENOENT) {
318			*notexist = 1;
319			/* see if we need to create pathname components */
320			return create_dirs(path);
321		}
322		log_msg(LOG_ERR, "cannot stat %s: %s", path, strerror(errno));
323		return 0;
324	}
325	*notexist = 0;
326	return 1;
327}
328
329void
330namedb_write_zonefile(struct nsd* nsd, struct zone_options* zopt)
331{
332	const char* zfile;
333	int notexist = 0;
334	zone_type* zone;
335	/* if no zone exists, it has no contents or it has no zonefile
336	 * configured, then no need to write data to disk */
337	if(!zopt->pattern->zonefile)
338		return;
339	zone = namedb_find_zone(nsd->db, (const dname_type*)zopt->node.key);
340	if(!zone || !zone->apex || !zone->soa_rrset)
341		return;
342	/* write if file does not exist, or if changed */
343	/* so, determine filename, create directory components, check exist*/
344	zfile = config_make_zonefile(zopt, nsd);
345	if(!create_path_components(zfile, &notexist)) {
346		log_msg(LOG_ERR, "could not write zone %s to file %s because "
347			"the path could not be created", zopt->name, zfile);
348		return;
349	}
350
351	/* if not changed, do not write. */
352	if(notexist || zone->is_changed) {
353		char logs[4096];
354		char bakfile[4096];
355		struct timespec mtime;
356		udb_ptr zudb;
357		if(nsd->db->udb) {
358			if(!udb_zone_search(nsd->db->udb, &zudb,
359				dname_name(domain_dname(zone->apex)),
360				domain_dname(zone->apex)->name_size))
361				return; /* zone does not exist in db */
362		}
363		/* write to zfile~ first, then rename if that works */
364		snprintf(bakfile, sizeof(bakfile), "%s~", zfile);
365		if(nsd->db->udb && ZONE(&zudb)->log_str.data) {
366			udb_ptr s;
367			udb_ptr_new(&s, nsd->db->udb, &ZONE(&zudb)->log_str);
368			strlcpy(logs, (char*)udb_ptr_data(&s), sizeof(logs));
369			udb_ptr_unlink(&s, nsd->db->udb);
370		} else if(zone->logstr) {
371			strlcpy(logs, zone->logstr, sizeof(logs));
372		} else logs[0] = 0;
373		VERBOSITY(1, (LOG_INFO, "writing zone %s to file %s",
374			zone->opts->name, zfile));
375		if(!write_to_zonefile(zone, bakfile, logs)) {
376			if(nsd->db->udb)
377				udb_ptr_unlink(&zudb, nsd->db->udb);
378			(void)unlink(bakfile); /* delete failed file */
379			return; /* error already printed */
380		}
381		if(rename(bakfile, zfile) == -1) {
382			log_msg(LOG_ERR, "rename(%s to %s) failed: %s",
383				bakfile, zfile, strerror(errno));
384			if(nsd->db->udb)
385				udb_ptr_unlink(&zudb, nsd->db->udb);
386			(void)unlink(bakfile); /* delete failed file */
387			return;
388		}
389		zone->is_changed = 0;
390		/* fetch the mtime of the just created zonefile so we
391		 * do not waste effort reading it back in */
392		if(!file_get_mtime(zfile, &mtime, &notexist)) {
393			get_time(&mtime);
394		}
395		if(nsd->db->udb) {
396			ZONE(&zudb)->mtime = (uint64_t)mtime.tv_sec;
397			ZONE(&zudb)->mtime_nsec = (uint64_t)mtime.tv_nsec;
398			ZONE(&zudb)->is_changed = 0;
399			udb_zone_set_log_str(nsd->db->udb, &zudb, NULL);
400			udb_ptr_unlink(&zudb, nsd->db->udb);
401		} else {
402			zone->mtime = mtime;
403			if(zone->filename)
404				region_recycle(nsd->db->region, zone->filename,
405					strlen(zone->filename)+1);
406			zone->filename = region_strdup(nsd->db->region, zfile);
407			if(zone->logstr)
408				region_recycle(nsd->db->region, zone->logstr,
409					strlen(zone->logstr)+1);
410			zone->logstr = NULL;
411		}
412	}
413}
414
415void
416namedb_write_zonefiles(struct nsd* nsd, struct nsd_options* options)
417{
418	struct zone_options* zo;
419	RBTREE_FOR(zo, struct zone_options*, options->zone_options) {
420		namedb_write_zonefile(nsd, zo);
421	}
422}
423