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
27 /* pathname directory separator character */
28 #define PATHSEP '/'
29
30 /** add an rdata (uncompressed) to the destination */
31 static size_t
add_rdata(rr_type * rr,unsigned i,uint8_t * buf,size_t buflen)32 add_rdata(rr_type* rr, unsigned i, uint8_t* buf, size_t buflen)
33 {
34 switch(rdata_atom_wireformat_type(rr->type, i)) {
35 case RDATA_WF_COMPRESSED_DNAME:
36 case RDATA_WF_UNCOMPRESSED_DNAME:
37 {
38 const dname_type* dname = domain_dname(
39 rdata_atom_domain(rr->rdatas[i]));
40 if(dname->name_size > buflen)
41 return 0;
42 memmove(buf, dname_name(dname), dname->name_size);
43 return dname->name_size;
44 }
45 default:
46 break;
47 }
48 if(rdata_atom_size(rr->rdatas[i]) > buflen)
49 return 0;
50 memmove(buf, rdata_atom_data(rr->rdatas[i]),
51 rdata_atom_size(rr->rdatas[i]));
52 return rdata_atom_size(rr->rdatas[i]);
53 }
54
55 /* marshal rdata into buffer, must be MAX_RDLENGTH in size */
56 size_t
rr_marshal_rdata(rr_type * rr,uint8_t * rdata,size_t sz)57 rr_marshal_rdata(rr_type* rr, uint8_t* rdata, size_t sz)
58 {
59 size_t len = 0;
60 unsigned i;
61 assert(rr);
62 for(i=0; i<rr->rdata_count; i++) {
63 len += add_rdata(rr, i, rdata+len, sz-len);
64 }
65 return len;
66 }
67
68 /** delete an RR */
69 void
udb_del_rr(udb_base * udb,udb_ptr * z,rr_type * rr)70 udb_del_rr(udb_base* udb, udb_ptr* z, rr_type* rr)
71 {
72 /* marshal the rdata (uncompressed) into a buffer */
73 uint8_t rdata[MAX_RDLENGTH];
74 size_t rdatalen = rr_marshal_rdata(rr, rdata, sizeof(rdata));
75 assert(udb);
76 udb_zone_del_rr(udb, z, dname_name(domain_dname(rr->owner)),
77 domain_dname(rr->owner)->name_size, rr->type, rr->klass,
78 rdata, rdatalen);
79 }
80
81 /** write rr */
82 int
udb_write_rr(udb_base * udb,udb_ptr * z,rr_type * rr)83 udb_write_rr(udb_base* udb, udb_ptr* z, rr_type* rr)
84 {
85 /* marshal the rdata (uncompressed) into a buffer */
86 uint8_t rdata[MAX_RDLENGTH];
87 size_t rdatalen = 0;
88 unsigned i;
89 assert(rr);
90 for(i=0; i<rr->rdata_count; i++) {
91 rdatalen += add_rdata(rr, i, rdata+rdatalen,
92 sizeof(rdata)-rdatalen);
93 }
94 assert(udb);
95 return udb_zone_add_rr(udb, z, dname_name(domain_dname(rr->owner)),
96 domain_dname(rr->owner)->name_size, rr->type, rr->klass,
97 rr->ttl, rdata, rdatalen);
98 }
99
100 /** write rrset */
101 static int
write_rrset(udb_base * udb,udb_ptr * z,rrset_type * rrset)102 write_rrset(udb_base* udb, udb_ptr* z, rrset_type* rrset)
103 {
104 unsigned i;
105 for(i=0; i<rrset->rr_count; i++) {
106 if(!udb_write_rr(udb, z, &rrset->rrs[i]))
107 return 0;
108 }
109 return 1;
110 }
111
112 /** write a zone */
113 static int
write_zone(udb_base * udb,udb_ptr * z,zone_type * zone)114 write_zone(udb_base* udb, udb_ptr* z, zone_type* zone)
115 {
116 /* write all domains in the zone */
117 domain_type* walk;
118 rrset_type* rrset;
119 unsigned long n = 0, c = 0;
120 time_t t = time(NULL);
121
122 /* count domains: for pct logging */
123 for(walk=zone->apex; walk && domain_is_subdomain(walk, zone->apex);
124 walk=domain_next(walk)) {
125 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 */
148 int
write_zone_to_udb(udb_base * udb,zone_type * zone,struct timespec * mtime,const char * file_str)149 write_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
183 int
print_rrs(FILE * out,struct zone * zone)184 print_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
234 static int
print_header(zone_type * zone,FILE * out,time_t * now,const char * logs)235 print_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
248 static int
write_to_zonefile(zone_type * zone,const char * filename,const char * logs)249 write_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 */
277 int
create_dirs(const char * path)278 create_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 */
311 static int
create_path_components(const char * path,int * notexist)312 create_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
329 void
namedb_write_zonefile(struct nsd * nsd,struct zone_options * zopt)330 namedb_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, ¬exist)) {
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, ¬exist)) {
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
415 void
namedb_write_zonefiles(struct nsd * nsd,struct nsd_options * options)416 namedb_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