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 */
30 static size_t
add_rdata(rr_type * rr,unsigned i,uint8_t * buf,size_t buflen)31 add_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 */
55 size_t
rr_marshal_rdata(rr_type * rr,uint8_t * rdata,size_t sz)56 rr_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
67 int
print_rrs(FILE * out,struct zone * zone)68 print_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
118 static int
print_header(zone_type * zone,FILE * out,time_t * now,const char * logs)119 print_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
132 static int
write_to_zonefile(zone_type * zone,const char * filename,const char * logs)133 write_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 */
161 int
create_dirs(const char * path)162 create_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 */
195 static int
create_path_components(const char * path,int * notexist)196 create_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
213 void
namedb_write_zonefile(struct nsd * nsd,struct zone_options * zopt)214 namedb_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, ¬exist)) {
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, ¬exist)) {
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
278 void
namedb_write_zonefiles(struct nsd * nsd,struct nsd_options * options)279 namedb_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