162ac0c33Sjakob /*
262ac0c33Sjakob * dbcreate.c -- routines to create an nsd(8) name database
362ac0c33Sjakob *
4d3fecca9Ssthen * Copyright (c) 2001-2006, NLnet Labs. All rights reserved.
562ac0c33Sjakob *
662ac0c33Sjakob * See LICENSE for the license.
762ac0c33Sjakob *
862ac0c33Sjakob */
962ac0c33Sjakob
102c1ae072Ssthen #include "config.h"
1162ac0c33Sjakob
12d3fecca9Ssthen #include <sys/stat.h>
1362ac0c33Sjakob #include <sys/types.h>
1462ac0c33Sjakob #include <errno.h>
1562ac0c33Sjakob #include <fcntl.h>
1662ac0c33Sjakob #include <stdlib.h>
1762ac0c33Sjakob #include <string.h>
1862ac0c33Sjakob #include <unistd.h>
1962ac0c33Sjakob
2062ac0c33Sjakob #include "namedb.h"
21d3fecca9Ssthen #include "udb.h"
22d3fecca9Ssthen #include "options.h"
23cdb6bbddSbrad #include "nsd.h"
244564029fSflorian #include "ixfr.h"
2562ac0c33Sjakob
26d3fecca9Ssthen /* pathname directory separator character */
27d3fecca9Ssthen #define PATHSEP '/'
2862ac0c33Sjakob
29d3fecca9Ssthen /** add an rdata (uncompressed) to the destination */
30d3fecca9Ssthen static size_t
add_rdata(rr_type * rr,unsigned i,uint8_t * buf,size_t buflen)31d3fecca9Ssthen add_rdata(rr_type* rr, unsigned i, uint8_t* buf, size_t buflen)
3262ac0c33Sjakob {
33d3fecca9Ssthen switch(rdata_atom_wireformat_type(rr->type, i)) {
34d3fecca9Ssthen case RDATA_WF_COMPRESSED_DNAME:
35d3fecca9Ssthen case RDATA_WF_UNCOMPRESSED_DNAME:
36d3fecca9Ssthen {
37d3fecca9Ssthen const dname_type* dname = domain_dname(
38d3fecca9Ssthen rdata_atom_domain(rr->rdatas[i]));
39d3fecca9Ssthen if(dname->name_size > buflen)
40d3fecca9Ssthen return 0;
41d3fecca9Ssthen memmove(buf, dname_name(dname), dname->name_size);
42d3fecca9Ssthen return dname->name_size;
430c2b6c02Sjakob }
44d3fecca9Ssthen default:
45d3fecca9Ssthen break;
46d3fecca9Ssthen }
47cdb6bbddSbrad if(rdata_atom_size(rr->rdatas[i]) > buflen)
48cdb6bbddSbrad return 0;
49d3fecca9Ssthen memmove(buf, rdata_atom_data(rr->rdatas[i]),
50d3fecca9Ssthen rdata_atom_size(rr->rdatas[i]));
51d3fecca9Ssthen return rdata_atom_size(rr->rdatas[i]);
5262ac0c33Sjakob }
5362ac0c33Sjakob
54d3fecca9Ssthen /* marshal rdata into buffer, must be MAX_RDLENGTH in size */
55d3fecca9Ssthen size_t
rr_marshal_rdata(rr_type * rr,uint8_t * rdata,size_t sz)56d3fecca9Ssthen rr_marshal_rdata(rr_type* rr, uint8_t* rdata, size_t sz)
57d3fecca9Ssthen {
58d3fecca9Ssthen size_t len = 0;
59d3fecca9Ssthen unsigned i;
60d3fecca9Ssthen assert(rr);
61d3fecca9Ssthen for(i=0; i<rr->rdata_count; i++) {
62d3fecca9Ssthen len += add_rdata(rr, i, rdata+len, sz-len);
63d3fecca9Ssthen }
64d3fecca9Ssthen return len;
6562ac0c33Sjakob }
6662ac0c33Sjakob
678d298c9fSsthen int
print_rrs(FILE * out,struct zone * zone)68d3fecca9Ssthen print_rrs(FILE* out, struct zone* zone)
69d3fecca9Ssthen {
70d3fecca9Ssthen rrset_type *rrset;
71d3fecca9Ssthen domain_type *domain = zone->apex;
72d3fecca9Ssthen region_type* region = region_create(xalloc, free);
7315ed76cbSbrad region_type* rr_region = region_create(xalloc, free);
7415ed76cbSbrad buffer_type* rr_buffer = buffer_create(region, MAX_RDLENGTH);
75d3fecca9Ssthen struct state_pretty_rr* state = create_pretty_rr(region);
76d3fecca9Ssthen /* first print the SOA record for the zone */
77d3fecca9Ssthen if(zone->soa_rrset) {
78d3fecca9Ssthen size_t i;
79d3fecca9Ssthen for(i=0; i < zone->soa_rrset->rr_count; i++) {
8015ed76cbSbrad if(!print_rr(out, state, &zone->soa_rrset->rrs[i],
8115ed76cbSbrad rr_region, rr_buffer)){
82d3fecca9Ssthen log_msg(LOG_ERR, "There was an error "
83d3fecca9Ssthen "printing SOARR to zone %s",
84d3fecca9Ssthen zone->opts->name);
85d3fecca9Ssthen region_destroy(region);
8615ed76cbSbrad region_destroy(rr_region);
87d3fecca9Ssthen return 0;
88d3fecca9Ssthen }
89d3fecca9Ssthen }
90d3fecca9Ssthen }
91d3fecca9Ssthen /* go through entire tree below the zone apex (incl subzones) */
92d3fecca9Ssthen while(domain && domain_is_subdomain(domain, zone->apex))
93d3fecca9Ssthen {
94d3fecca9Ssthen for(rrset = domain->rrsets; rrset; rrset=rrset->next)
95d3fecca9Ssthen {
96d3fecca9Ssthen size_t i;
97d3fecca9Ssthen if(rrset->zone != zone || rrset == zone->soa_rrset)
98d3fecca9Ssthen continue;
99d3fecca9Ssthen for(i=0; i < rrset->rr_count; i++) {
10015ed76cbSbrad if(!print_rr(out, state, &rrset->rrs[i],
10115ed76cbSbrad rr_region, rr_buffer)){
102d3fecca9Ssthen log_msg(LOG_ERR, "There was an error "
103d3fecca9Ssthen "printing RR to zone %s",
104d3fecca9Ssthen zone->opts->name);
105d3fecca9Ssthen region_destroy(region);
10615ed76cbSbrad region_destroy(rr_region);
107d3fecca9Ssthen return 0;
108d3fecca9Ssthen }
109d3fecca9Ssthen }
110d3fecca9Ssthen }
111d3fecca9Ssthen domain = domain_next(domain);
112d3fecca9Ssthen }
113d3fecca9Ssthen region_destroy(region);
11415ed76cbSbrad region_destroy(rr_region);
115d3fecca9Ssthen return 1;
116d3fecca9Ssthen }
117d3fecca9Ssthen
118d3fecca9Ssthen static int
print_header(zone_type * zone,FILE * out,time_t * now,const char * logs)119d3fecca9Ssthen print_header(zone_type* zone, FILE* out, time_t* now, const char* logs)
120d3fecca9Ssthen {
121bfd0b123Sflorian char buf[4096+16];
122d3fecca9Ssthen /* ctime prints newline at end of this line */
123d3fecca9Ssthen snprintf(buf, sizeof(buf), "; zone %s written by NSD %s on %s",
124d3fecca9Ssthen zone->opts->name, PACKAGE_VERSION, ctime(now));
125d3fecca9Ssthen if(!write_data(out, buf, strlen(buf)))
126d3fecca9Ssthen return 0;
127d3fecca9Ssthen if(!logs || logs[0] == 0) return 1;
128d3fecca9Ssthen snprintf(buf, sizeof(buf), "; %s\n", logs);
129d3fecca9Ssthen return write_data(out, buf, strlen(buf));
130d3fecca9Ssthen }
131d3fecca9Ssthen
132d3fecca9Ssthen static int
write_to_zonefile(zone_type * zone,const char * filename,const char * logs)133d3fecca9Ssthen write_to_zonefile(zone_type* zone, const char* filename, const char* logs)
134d3fecca9Ssthen {
135d3fecca9Ssthen time_t now = time(0);
13615ed76cbSbrad FILE *out = fopen(filename, "w");
137d3fecca9Ssthen if(!out) {
138d3fecca9Ssthen log_msg(LOG_ERR, "cannot write zone %s file %s: %s",
139d3fecca9Ssthen zone->opts->name, filename, strerror(errno));
140d3fecca9Ssthen return 0;
141d3fecca9Ssthen }
142d3fecca9Ssthen if(!print_header(zone, out, &now, logs)) {
143d3fecca9Ssthen fclose(out);
144d3fecca9Ssthen log_msg(LOG_ERR, "There was an error printing "
145d3fecca9Ssthen "the header to zone %s", zone->opts->name);
146d3fecca9Ssthen return 0;
147d3fecca9Ssthen }
148d3fecca9Ssthen if(!print_rrs(out, zone)) {
149d3fecca9Ssthen fclose(out);
150d3fecca9Ssthen return 0;
151d3fecca9Ssthen }
15215ed76cbSbrad if(fclose(out) != 0) {
15315ed76cbSbrad log_msg(LOG_ERR, "cannot write zone %s to file %s: fclose: %s",
15415ed76cbSbrad zone->opts->name, filename, strerror(errno));
15515ed76cbSbrad return 0;
15615ed76cbSbrad }
157d3fecca9Ssthen return 1;
158d3fecca9Ssthen }
159d3fecca9Ssthen
160d3fecca9Ssthen /** create directories above this file, .../dir/dir/dir/file */
161d3fecca9Ssthen int
create_dirs(const char * path)162d3fecca9Ssthen create_dirs(const char* path)
163d3fecca9Ssthen {
164d3fecca9Ssthen char dir[4096];
165d3fecca9Ssthen char* p;
166d3fecca9Ssthen strlcpy(dir, path, sizeof(dir));
167d3fecca9Ssthen /* if we start with / then do not try to create '' */
168d3fecca9Ssthen if(dir[0] == PATHSEP)
169d3fecca9Ssthen p = strchr(dir+1, PATHSEP);
170d3fecca9Ssthen else p = strchr(dir, PATHSEP);
171d3fecca9Ssthen /* create each directory component from the left */
172d3fecca9Ssthen while(p) {
173d3fecca9Ssthen assert(*p == PATHSEP);
174d3fecca9Ssthen *p = 0; /* end the directory name here */
175d3fecca9Ssthen if(mkdir(dir
176d3fecca9Ssthen #ifndef MKDIR_HAS_ONE_ARG
177d3fecca9Ssthen , 0750
178d3fecca9Ssthen #endif
179d3fecca9Ssthen ) == -1) {
180d3fecca9Ssthen if(errno != EEXIST) {
181d3fecca9Ssthen log_msg(LOG_ERR, "create dir %s: %s",
182d3fecca9Ssthen dir, strerror(errno));
183308d2509Sflorian *p = PATHSEP; /* restore input string */
184d3fecca9Ssthen return 0;
185d3fecca9Ssthen }
186d3fecca9Ssthen /* it already exists, OK, continue */
187d3fecca9Ssthen }
188d3fecca9Ssthen *p = PATHSEP;
189d3fecca9Ssthen p = strchr(p+1, PATHSEP);
190d3fecca9Ssthen }
191d3fecca9Ssthen return 1;
192d3fecca9Ssthen }
193d3fecca9Ssthen
194d3fecca9Ssthen /** create pathname components and check if file exists */
195d3fecca9Ssthen static int
create_path_components(const char * path,int * notexist)196d3fecca9Ssthen create_path_components(const char* path, int* notexist)
197d3fecca9Ssthen {
198d3fecca9Ssthen /* stat the file, to see if it exists, and if its directories exist */
199d3fecca9Ssthen struct stat s;
200d3fecca9Ssthen if(stat(path, &s) != 0) {
201d3fecca9Ssthen if(errno == ENOENT) {
202d3fecca9Ssthen *notexist = 1;
203d3fecca9Ssthen /* see if we need to create pathname components */
204d3fecca9Ssthen return create_dirs(path);
205d3fecca9Ssthen }
206d3fecca9Ssthen log_msg(LOG_ERR, "cannot stat %s: %s", path, strerror(errno));
207d3fecca9Ssthen return 0;
208d3fecca9Ssthen }
209d3fecca9Ssthen *notexist = 0;
210d3fecca9Ssthen return 1;
211d3fecca9Ssthen }
21262ac0c33Sjakob
21362ac0c33Sjakob void
namedb_write_zonefile(struct nsd * nsd,struct zone_options * zopt)214fe5fe5f6Sflorian namedb_write_zonefile(struct nsd* nsd, struct zone_options* zopt)
21562ac0c33Sjakob {
216d3fecca9Ssthen const char* zfile;
217d3fecca9Ssthen int notexist = 0;
21862ac0c33Sjakob zone_type* zone;
219d3fecca9Ssthen /* if no zone exists, it has no contents or it has no zonefile
220d3fecca9Ssthen * configured, then no need to write data to disk */
221d3fecca9Ssthen if(!zopt->pattern->zonefile)
222d3fecca9Ssthen return;
223cdb6bbddSbrad zone = namedb_find_zone(nsd->db, (const dname_type*)zopt->node.key);
22415ed76cbSbrad if(!zone || !zone->apex || !zone->soa_rrset)
225d3fecca9Ssthen return;
226d3fecca9Ssthen /* write if file does not exist, or if changed */
227d3fecca9Ssthen /* so, determine filename, create directory components, check exist*/
228cdb6bbddSbrad zfile = config_make_zonefile(zopt, nsd);
229d3fecca9Ssthen if(!create_path_components(zfile, ¬exist)) {
230d3fecca9Ssthen log_msg(LOG_ERR, "could not write zone %s to file %s because "
231d3fecca9Ssthen "the path could not be created", zopt->name, zfile);
232d3fecca9Ssthen return;
233d3fecca9Ssthen }
23462ac0c33Sjakob
235d3fecca9Ssthen /* if not changed, do not write. */
236d3fecca9Ssthen if(notexist || zone->is_changed) {
237d3fecca9Ssthen char logs[4096];
238d3fecca9Ssthen char bakfile[4096];
239275a8d89Sflorian struct timespec mtime;
240d3fecca9Ssthen /* write to zfile~ first, then rename if that works */
241d3fecca9Ssthen snprintf(bakfile, sizeof(bakfile), "%s~", zfile);
242*b71395eaSflorian if(zone->logstr)
24315ed76cbSbrad strlcpy(logs, zone->logstr, sizeof(logs));
244*b71395eaSflorian else
245*b71395eaSflorian logs[0] = 0;
24615ed76cbSbrad VERBOSITY(1, (LOG_INFO, "writing zone %s to file %s",
24715ed76cbSbrad zone->opts->name, zfile));
248d3fecca9Ssthen if(!write_to_zonefile(zone, bakfile, logs)) {
24915ed76cbSbrad (void)unlink(bakfile); /* delete failed file */
250d3fecca9Ssthen return; /* error already printed */
251d3fecca9Ssthen }
252d3fecca9Ssthen if(rename(bakfile, zfile) == -1) {
253d3fecca9Ssthen log_msg(LOG_ERR, "rename(%s to %s) failed: %s",
254d3fecca9Ssthen bakfile, zfile, strerror(errno));
25515ed76cbSbrad (void)unlink(bakfile); /* delete failed file */
256d3fecca9Ssthen return;
257d3fecca9Ssthen }
258d3fecca9Ssthen zone->is_changed = 0;
259275a8d89Sflorian /* fetch the mtime of the just created zonefile so we
260275a8d89Sflorian * do not waste effort reading it back in */
261275a8d89Sflorian if(!file_get_mtime(zfile, &mtime, ¬exist)) {
262275a8d89Sflorian get_time(&mtime);
263275a8d89Sflorian }
264275a8d89Sflorian zone->mtime = mtime;
26515ed76cbSbrad if(zone->filename)
26615ed76cbSbrad region_recycle(nsd->db->region, zone->filename,
26715ed76cbSbrad strlen(zone->filename)+1);
26815ed76cbSbrad zone->filename = region_strdup(nsd->db->region, zfile);
26915ed76cbSbrad if(zone->logstr)
27015ed76cbSbrad region_recycle(nsd->db->region, zone->logstr,
27115ed76cbSbrad strlen(zone->logstr)+1);
27215ed76cbSbrad zone->logstr = NULL;
2734564029fSflorian if(zone_is_ixfr_enabled(zone) && zone->ixfr)
2744564029fSflorian ixfr_write_to_file(zone, zfile);
27562ac0c33Sjakob }
27662ac0c33Sjakob }
27762ac0c33Sjakob
278d3fecca9Ssthen void
namedb_write_zonefiles(struct nsd * nsd,struct nsd_options * options)279fe5fe5f6Sflorian namedb_write_zonefiles(struct nsd* nsd, struct nsd_options* options)
280d3fecca9Ssthen {
281fe5fe5f6Sflorian struct zone_options* zo;
282fe5fe5f6Sflorian RBTREE_FOR(zo, struct zone_options*, options->zone_options) {
283cdb6bbddSbrad namedb_write_zonefile(nsd, zo);
28462ac0c33Sjakob }
28562ac0c33Sjakob }
286