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 */ 30static size_t 31add_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 */ 55size_t 56rr_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 67int 68print_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 118static int 119print_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 132static int 133write_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 */ 161int 162create_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 */ 195static int 196create_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 213void 214namedb_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 278void 279namedb_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