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