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 "udbradtree.h"
23 #include "udbzone.h"
24 #include "options.h"
25 #include "nsd.h"
26 #include "ixfr.h"
27
28 /* pathname directory separator character */
29 #define PATHSEP '/'
30
31 /** add an rdata (uncompressed) to the destination */
32 static size_t
add_rdata(rr_type * rr,unsigned i,uint8_t * buf,size_t buflen)33 add_rdata(rr_type* rr, unsigned i, uint8_t* buf, size_t buflen)
34 {
35 switch(rdata_atom_wireformat_type(rr->type, i)) {
36 case RDATA_WF_COMPRESSED_DNAME:
37 case RDATA_WF_UNCOMPRESSED_DNAME:
38 {
39 const dname_type* dname = domain_dname(
40 rdata_atom_domain(rr->rdatas[i]));
41 if(dname->name_size > buflen)
42 return 0;
43 memmove(buf, dname_name(dname), dname->name_size);
44 return dname->name_size;
45 }
46 default:
47 break;
48 }
49 if(rdata_atom_size(rr->rdatas[i]) > buflen)
50 return 0;
51 memmove(buf, rdata_atom_data(rr->rdatas[i]),
52 rdata_atom_size(rr->rdatas[i]));
53 return rdata_atom_size(rr->rdatas[i]);
54 }
55
56 /* marshal rdata into buffer, must be MAX_RDLENGTH in size */
57 size_t
rr_marshal_rdata(rr_type * rr,uint8_t * rdata,size_t sz)58 rr_marshal_rdata(rr_type* rr, uint8_t* rdata, size_t sz)
59 {
60 size_t len = 0;
61 unsigned i;
62 assert(rr);
63 for(i=0; i<rr->rdata_count; i++) {
64 len += add_rdata(rr, i, rdata+len, sz-len);
65 }
66 return len;
67 }
68
69 /** delete an RR */
70 void
udb_del_rr(udb_base * udb,udb_ptr * z,rr_type * rr)71 udb_del_rr(udb_base* udb, udb_ptr* z, rr_type* rr)
72 {
73 /* marshal the rdata (uncompressed) into a buffer */
74 uint8_t rdata[MAX_RDLENGTH];
75 size_t rdatalen = rr_marshal_rdata(rr, rdata, sizeof(rdata));
76 assert(udb);
77 udb_zone_del_rr(udb, z, dname_name(domain_dname(rr->owner)),
78 domain_dname(rr->owner)->name_size, rr->type, rr->klass,
79 rdata, rdatalen);
80 }
81
82 /** write rr */
83 int
udb_write_rr(udb_base * udb,udb_ptr * z,rr_type * rr)84 udb_write_rr(udb_base* udb, udb_ptr* z, rr_type* rr)
85 {
86 /* marshal the rdata (uncompressed) into a buffer */
87 uint8_t rdata[MAX_RDLENGTH];
88 size_t rdatalen = 0;
89 unsigned i;
90 assert(rr);
91 for(i=0; i<rr->rdata_count; i++) {
92 rdatalen += add_rdata(rr, i, rdata+rdatalen,
93 sizeof(rdata)-rdatalen);
94 }
95 assert(udb);
96 return udb_zone_add_rr(udb, z, dname_name(domain_dname(rr->owner)),
97 domain_dname(rr->owner)->name_size, rr->type, rr->klass,
98 rr->ttl, rdata, rdatalen);
99 }
100
101 /** write rrset */
102 static int
write_rrset(udb_base * udb,udb_ptr * z,rrset_type * rrset)103 write_rrset(udb_base* udb, udb_ptr* z, rrset_type* rrset)
104 {
105 unsigned i;
106 for(i=0; i<rrset->rr_count; i++) {
107 if(!udb_write_rr(udb, z, &rrset->rrs[i]))
108 return 0;
109 }
110 return 1;
111 }
112
113 /** write a zone */
114 static int
write_zone(udb_base * udb,udb_ptr * z,zone_type * zone)115 write_zone(udb_base* udb, udb_ptr* z, zone_type* zone)
116 {
117 /* write all domains in the zone */
118 domain_type* walk;
119 rrset_type* rrset;
120 unsigned long n = 0, c = 0;
121 time_t t = time(NULL);
122
123 /* count domains: for pct logging */
124 for(walk=zone->apex; walk && domain_is_subdomain(walk, zone->apex);
125 walk=domain_next(walk)) {
126 n++;
127 }
128 /* write them */
129 for(walk=zone->apex; walk && domain_is_subdomain(walk, zone->apex);
130 walk=domain_next(walk)) {
131 /* write all rrsets (in the zone) for this domain */
132 for(rrset=walk->rrsets; rrset; rrset=rrset->next) {
133 if(rrset->zone == zone) {
134 if(!write_rrset(udb, z, rrset))
135 return 0;
136 }
137 }
138 /* only check every ... domains, and print pct */
139 if(++c % ZONEC_PCT_COUNT == 0 && time(NULL) > t + ZONEC_PCT_TIME) {
140 t = time(NULL);
141 VERBOSITY(1, (LOG_INFO, "write %s %d %%",
142 zone->opts->name, (n==0)?0:(int)(c*((unsigned long)100)/n)));
143 }
144 }
145 return 1;
146 }
147
148 /** create and write a zone */
149 int
write_zone_to_udb(udb_base * udb,zone_type * zone,struct timespec * mtime,const char * file_str)150 write_zone_to_udb(udb_base* udb, zone_type* zone, struct timespec* mtime,
151 const char* file_str)
152 {
153 udb_ptr z;
154 /* make udb dirty */
155 udb_base_set_userflags(udb, 1);
156 /* find or create zone */
157 if(udb_zone_search(udb, &z, dname_name(domain_dname(zone->apex)),
158 domain_dname(zone->apex)->name_size)) {
159 /* wipe existing contents */
160 udb_zone_clear(udb, &z);
161 } else {
162 if(!udb_zone_create(udb, &z, dname_name(domain_dname(
163 zone->apex)), domain_dname(zone->apex)->name_size)) {
164 udb_base_set_userflags(udb, 0);
165 return 0;
166 }
167 }
168 /* set mtime */
169 ZONE(&z)->mtime = (uint64_t)mtime->tv_sec;
170 ZONE(&z)->mtime_nsec = (uint64_t)mtime->tv_nsec;
171 ZONE(&z)->is_changed = 0;
172 udb_zone_set_log_str(udb, &z, NULL);
173 udb_zone_set_file_str(udb, &z, file_str);
174 /* write zone */
175 if(!write_zone(udb, &z, zone)) {
176 udb_base_set_userflags(udb, 0);
177 return 0;
178 }
179 udb_ptr_unlink(&z, udb);
180 udb_base_set_userflags(udb, 0);
181 return 1;
182 }
183
184 int
print_rrs(FILE * out,struct zone * zone)185 print_rrs(FILE* out, struct zone* zone)
186 {
187 rrset_type *rrset;
188 domain_type *domain = zone->apex;
189 region_type* region = region_create(xalloc, free);
190 region_type* rr_region = region_create(xalloc, free);
191 buffer_type* rr_buffer = buffer_create(region, MAX_RDLENGTH);
192 struct state_pretty_rr* state = create_pretty_rr(region);
193 /* first print the SOA record for the zone */
194 if(zone->soa_rrset) {
195 size_t i;
196 for(i=0; i < zone->soa_rrset->rr_count; i++) {
197 if(!print_rr(out, state, &zone->soa_rrset->rrs[i],
198 rr_region, rr_buffer)){
199 log_msg(LOG_ERR, "There was an error "
200 "printing SOARR to zone %s",
201 zone->opts->name);
202 region_destroy(region);
203 region_destroy(rr_region);
204 return 0;
205 }
206 }
207 }
208 /* go through entire tree below the zone apex (incl subzones) */
209 while(domain && domain_is_subdomain(domain, zone->apex))
210 {
211 for(rrset = domain->rrsets; rrset; rrset=rrset->next)
212 {
213 size_t i;
214 if(rrset->zone != zone || rrset == zone->soa_rrset)
215 continue;
216 for(i=0; i < rrset->rr_count; i++) {
217 if(!print_rr(out, state, &rrset->rrs[i],
218 rr_region, rr_buffer)){
219 log_msg(LOG_ERR, "There was an error "
220 "printing RR to zone %s",
221 zone->opts->name);
222 region_destroy(region);
223 region_destroy(rr_region);
224 return 0;
225 }
226 }
227 }
228 domain = domain_next(domain);
229 }
230 region_destroy(region);
231 region_destroy(rr_region);
232 return 1;
233 }
234
235 static int
print_header(zone_type * zone,FILE * out,time_t * now,const char * logs)236 print_header(zone_type* zone, FILE* out, time_t* now, const char* logs)
237 {
238 char buf[4096+16];
239 /* ctime prints newline at end of this line */
240 snprintf(buf, sizeof(buf), "; zone %s written by NSD %s on %s",
241 zone->opts->name, PACKAGE_VERSION, ctime(now));
242 if(!write_data(out, buf, strlen(buf)))
243 return 0;
244 if(!logs || logs[0] == 0) return 1;
245 snprintf(buf, sizeof(buf), "; %s\n", logs);
246 return write_data(out, buf, strlen(buf));
247 }
248
249 static int
write_to_zonefile(zone_type * zone,const char * filename,const char * logs)250 write_to_zonefile(zone_type* zone, const char* filename, const char* logs)
251 {
252 time_t now = time(0);
253 FILE *out = fopen(filename, "w");
254 if(!out) {
255 log_msg(LOG_ERR, "cannot write zone %s file %s: %s",
256 zone->opts->name, filename, strerror(errno));
257 return 0;
258 }
259 if(!print_header(zone, out, &now, logs)) {
260 fclose(out);
261 log_msg(LOG_ERR, "There was an error printing "
262 "the header to zone %s", zone->opts->name);
263 return 0;
264 }
265 if(!print_rrs(out, zone)) {
266 fclose(out);
267 return 0;
268 }
269 if(fclose(out) != 0) {
270 log_msg(LOG_ERR, "cannot write zone %s to file %s: fclose: %s",
271 zone->opts->name, filename, strerror(errno));
272 return 0;
273 }
274 return 1;
275 }
276
277 /** create directories above this file, .../dir/dir/dir/file */
278 int
create_dirs(const char * path)279 create_dirs(const char* path)
280 {
281 char dir[4096];
282 char* p;
283 strlcpy(dir, path, sizeof(dir));
284 /* if we start with / then do not try to create '' */
285 if(dir[0] == PATHSEP)
286 p = strchr(dir+1, PATHSEP);
287 else p = strchr(dir, PATHSEP);
288 /* create each directory component from the left */
289 while(p) {
290 assert(*p == PATHSEP);
291 *p = 0; /* end the directory name here */
292 if(mkdir(dir
293 #ifndef MKDIR_HAS_ONE_ARG
294 , 0750
295 #endif
296 ) == -1) {
297 if(errno != EEXIST) {
298 log_msg(LOG_ERR, "create dir %s: %s",
299 dir, strerror(errno));
300 *p = PATHSEP; /* restore input string */
301 return 0;
302 }
303 /* it already exists, OK, continue */
304 }
305 *p = PATHSEP;
306 p = strchr(p+1, PATHSEP);
307 }
308 return 1;
309 }
310
311 /** create pathname components and check if file exists */
312 static int
create_path_components(const char * path,int * notexist)313 create_path_components(const char* path, int* notexist)
314 {
315 /* stat the file, to see if it exists, and if its directories exist */
316 struct stat s;
317 if(stat(path, &s) != 0) {
318 if(errno == ENOENT) {
319 *notexist = 1;
320 /* see if we need to create pathname components */
321 return create_dirs(path);
322 }
323 log_msg(LOG_ERR, "cannot stat %s: %s", path, strerror(errno));
324 return 0;
325 }
326 *notexist = 0;
327 return 1;
328 }
329
330 void
namedb_write_zonefile(struct nsd * nsd,struct zone_options * zopt)331 namedb_write_zonefile(struct nsd* nsd, struct zone_options* zopt)
332 {
333 const char* zfile;
334 int notexist = 0;
335 zone_type* zone;
336 /* if no zone exists, it has no contents or it has no zonefile
337 * configured, then no need to write data to disk */
338 if(!zopt->pattern->zonefile)
339 return;
340 zone = namedb_find_zone(nsd->db, (const dname_type*)zopt->node.key);
341 if(!zone || !zone->apex || !zone->soa_rrset)
342 return;
343 /* write if file does not exist, or if changed */
344 /* so, determine filename, create directory components, check exist*/
345 zfile = config_make_zonefile(zopt, nsd);
346 if(!create_path_components(zfile, ¬exist)) {
347 log_msg(LOG_ERR, "could not write zone %s to file %s because "
348 "the path could not be created", zopt->name, zfile);
349 return;
350 }
351
352 /* if not changed, do not write. */
353 if(notexist || zone->is_changed) {
354 char logs[4096];
355 char bakfile[4096];
356 struct timespec mtime;
357 udb_ptr zudb;
358 if(nsd->db->udb) {
359 if(!udb_zone_search(nsd->db->udb, &zudb,
360 dname_name(domain_dname(zone->apex)),
361 domain_dname(zone->apex)->name_size))
362 return; /* zone does not exist in db */
363 }
364 /* write to zfile~ first, then rename if that works */
365 snprintf(bakfile, sizeof(bakfile), "%s~", zfile);
366 if(nsd->db->udb && ZONE(&zudb)->log_str.data) {
367 udb_ptr s;
368 udb_ptr_new(&s, nsd->db->udb, &ZONE(&zudb)->log_str);
369 strlcpy(logs, (char*)udb_ptr_data(&s), sizeof(logs));
370 udb_ptr_unlink(&s, nsd->db->udb);
371 } else if(zone->logstr) {
372 strlcpy(logs, zone->logstr, sizeof(logs));
373 } else logs[0] = 0;
374 VERBOSITY(1, (LOG_INFO, "writing zone %s to file %s",
375 zone->opts->name, zfile));
376 if(!write_to_zonefile(zone, bakfile, logs)) {
377 if(nsd->db->udb)
378 udb_ptr_unlink(&zudb, nsd->db->udb);
379 (void)unlink(bakfile); /* delete failed file */
380 return; /* error already printed */
381 }
382 if(rename(bakfile, zfile) == -1) {
383 log_msg(LOG_ERR, "rename(%s to %s) failed: %s",
384 bakfile, zfile, strerror(errno));
385 if(nsd->db->udb)
386 udb_ptr_unlink(&zudb, nsd->db->udb);
387 (void)unlink(bakfile); /* delete failed file */
388 return;
389 }
390 zone->is_changed = 0;
391 /* fetch the mtime of the just created zonefile so we
392 * do not waste effort reading it back in */
393 if(!file_get_mtime(zfile, &mtime, ¬exist)) {
394 get_time(&mtime);
395 }
396 if(nsd->db->udb) {
397 ZONE(&zudb)->mtime = (uint64_t)mtime.tv_sec;
398 ZONE(&zudb)->mtime_nsec = (uint64_t)mtime.tv_nsec;
399 ZONE(&zudb)->is_changed = 0;
400 udb_zone_set_log_str(nsd->db->udb, &zudb, NULL);
401 udb_ptr_unlink(&zudb, nsd->db->udb);
402 } else {
403 zone->mtime = mtime;
404 if(zone->filename)
405 region_recycle(nsd->db->region, zone->filename,
406 strlen(zone->filename)+1);
407 zone->filename = region_strdup(nsd->db->region, zfile);
408 if(zone->logstr)
409 region_recycle(nsd->db->region, zone->logstr,
410 strlen(zone->logstr)+1);
411 zone->logstr = NULL;
412 }
413 if(zone_is_ixfr_enabled(zone) && zone->ixfr)
414 ixfr_write_to_file(zone, zfile);
415 }
416 }
417
418 void
namedb_write_zonefiles(struct nsd * nsd,struct nsd_options * options)419 namedb_write_zonefiles(struct nsd* nsd, struct nsd_options* options)
420 {
421 struct zone_options* zo;
422 RBTREE_FOR(zo, struct zone_options*, options->zone_options) {
423 namedb_write_zonefile(nsd, zo);
424 }
425 }
426