dbcreate.c revision 1.1.1.1
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 */ 31static size_t 32add_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 */ 56size_t 57rr_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 */ 69void 70udb_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 */ 82int 83udb_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 */ 101static int 102write_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 */ 113static int 114write_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 */ 148int 149write_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 183static int 184print_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 234static int 235print_header(zone_type* zone, FILE* out, time_t* now, const char* logs) 236{ 237 char buf[4096]; 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 248static int 249write_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 */ 277int 278create_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 return 0; 300 } 301 /* it already exists, OK, continue */ 302 } 303 *p = PATHSEP; 304 p = strchr(p+1, PATHSEP); 305 } 306 return 1; 307} 308 309/** create pathname components and check if file exists */ 310static int 311create_path_components(const char* path, int* notexist) 312{ 313 /* stat the file, to see if it exists, and if its directories exist */ 314 struct stat s; 315 if(stat(path, &s) != 0) { 316 if(errno == ENOENT) { 317 *notexist = 1; 318 /* see if we need to create pathname components */ 319 return create_dirs(path); 320 } 321 log_msg(LOG_ERR, "cannot stat %s: %s", path, strerror(errno)); 322 return 0; 323 } 324 *notexist = 0; 325 return 1; 326} 327 328void 329namedb_write_zonefile(struct nsd* nsd, zone_options_t* zopt) 330{ 331 const char* zfile; 332 int notexist = 0; 333 zone_type* zone; 334 /* if no zone exists, it has no contents or it has no zonefile 335 * configured, then no need to write data to disk */ 336 if(!zopt->pattern->zonefile) 337 return; 338 zone = namedb_find_zone(nsd->db, (const dname_type*)zopt->node.key); 339 if(!zone || !zone->apex || !zone->soa_rrset) 340 return; 341 /* write if file does not exist, or if changed */ 342 /* so, determine filename, create directory components, check exist*/ 343 zfile = config_make_zonefile(zopt, nsd); 344 if(!create_path_components(zfile, ¬exist)) { 345 log_msg(LOG_ERR, "could not write zone %s to file %s because " 346 "the path could not be created", zopt->name, zfile); 347 return; 348 } 349 350 /* if not changed, do not write. */ 351 if(notexist || zone->is_changed) { 352 char logs[4096]; 353 char bakfile[4096]; 354 struct timespec mtime; 355 udb_ptr zudb; 356 if(nsd->db->udb) { 357 if(!udb_zone_search(nsd->db->udb, &zudb, 358 dname_name(domain_dname(zone->apex)), 359 domain_dname(zone->apex)->name_size)) 360 return; /* zone does not exist in db */ 361 } 362 /* write to zfile~ first, then rename if that works */ 363 snprintf(bakfile, sizeof(bakfile), "%s~", zfile); 364 if(nsd->db->udb && ZONE(&zudb)->log_str.data) { 365 udb_ptr s; 366 udb_ptr_new(&s, nsd->db->udb, &ZONE(&zudb)->log_str); 367 strlcpy(logs, (char*)udb_ptr_data(&s), sizeof(logs)); 368 udb_ptr_unlink(&s, nsd->db->udb); 369 } else if(zone->logstr) { 370 strlcpy(logs, zone->logstr, sizeof(logs)); 371 } else logs[0] = 0; 372 VERBOSITY(1, (LOG_INFO, "writing zone %s to file %s", 373 zone->opts->name, zfile)); 374 if(!write_to_zonefile(zone, bakfile, logs)) { 375 if(nsd->db->udb) 376 udb_ptr_unlink(&zudb, nsd->db->udb); 377 (void)unlink(bakfile); /* delete failed file */ 378 return; /* error already printed */ 379 } 380 if(rename(bakfile, zfile) == -1) { 381 log_msg(LOG_ERR, "rename(%s to %s) failed: %s", 382 bakfile, zfile, strerror(errno)); 383 if(nsd->db->udb) 384 udb_ptr_unlink(&zudb, nsd->db->udb); 385 (void)unlink(bakfile); /* delete failed file */ 386 return; 387 } 388 zone->is_changed = 0; 389 /* fetch the mtime of the just created zonefile so we 390 * do not waste effort reading it back in */ 391 if(!file_get_mtime(zfile, &mtime, ¬exist)) { 392 get_time(&mtime); 393 } 394 if(nsd->db->udb) { 395 ZONE(&zudb)->mtime = (uint64_t)mtime.tv_sec; 396 ZONE(&zudb)->mtime_nsec = (uint64_t)mtime.tv_nsec; 397 ZONE(&zudb)->is_changed = 0; 398 udb_zone_set_log_str(nsd->db->udb, &zudb, NULL); 399 udb_ptr_unlink(&zudb, nsd->db->udb); 400 } else { 401 zone->mtime = mtime; 402 if(zone->filename) 403 region_recycle(nsd->db->region, zone->filename, 404 strlen(zone->filename)+1); 405 zone->filename = region_strdup(nsd->db->region, zfile); 406 if(zone->logstr) 407 region_recycle(nsd->db->region, zone->logstr, 408 strlen(zone->logstr)+1); 409 zone->logstr = NULL; 410 } 411 } 412} 413 414void 415namedb_write_zonefiles(struct nsd* nsd, nsd_options_t* options) 416{ 417 zone_options_t* zo; 418 RBTREE_FOR(zo, zone_options_t*, options->zone_options) { 419 namedb_write_zonefile(nsd, zo); 420 } 421} 422