dbcreate.c revision 1.1.1.4
1224090Sdougb/* 2224090Sdougb * dbcreate.c -- routines to create an nsd(8) name database 3224090Sdougb * 4224090Sdougb * Copyright (c) 2001-2006, NLnet Labs. All rights reserved. 5224090Sdougb * 6224090Sdougb * See LICENSE for the license. 7224090Sdougb * 8224090Sdougb */ 9224090Sdougb 10224090Sdougb#include "config.h" 11224090Sdougb 12224090Sdougb#include <sys/stat.h> 13224090Sdougb#include <sys/types.h> 14224090Sdougb#include <errno.h> 15224090Sdougb#include <fcntl.h> 16224090Sdougb#include <stdlib.h> 17224090Sdougb#include <string.h> 18224090Sdougb#include <unistd.h> 19224090Sdougb 20234010Sdougb#include "namedb.h" 21224090Sdougb#include "udb.h" 22224090Sdougb#include "udbradtree.h" 23224090Sdougb#include "udbzone.h" 24224090Sdougb#include "options.h" 25224090Sdougb#include "nsd.h" 26224090Sdougb 27224090Sdougb/* pathname directory separator character */ 28224090Sdougb#define PATHSEP '/' 29224090Sdougb 30224090Sdougb/** add an rdata (uncompressed) to the destination */ 31224090Sdougbstatic size_t 32224090Sdougbadd_rdata(rr_type* rr, unsigned i, uint8_t* buf, size_t buflen) 33224090Sdougb{ 34224090Sdougb switch(rdata_atom_wireformat_type(rr->type, i)) { 35224090Sdougb case RDATA_WF_COMPRESSED_DNAME: 36224090Sdougb case RDATA_WF_UNCOMPRESSED_DNAME: 37224090Sdougb { 38224090Sdougb const dname_type* dname = domain_dname( 39224090Sdougb rdata_atom_domain(rr->rdatas[i])); 40224090Sdougb if(dname->name_size > buflen) 41224090Sdougb return 0; 42224090Sdougb memmove(buf, dname_name(dname), dname->name_size); 43224090Sdougb return dname->name_size; 44224090Sdougb } 45224090Sdougb default: 46224090Sdougb break; 47224090Sdougb } 48224090Sdougb if(rdata_atom_size(rr->rdatas[i]) > buflen) 49224090Sdougb return 0; 50224090Sdougb memmove(buf, rdata_atom_data(rr->rdatas[i]), 51224090Sdougb rdata_atom_size(rr->rdatas[i])); 52224090Sdougb return rdata_atom_size(rr->rdatas[i]); 53224090Sdougb} 54224090Sdougb 55224090Sdougb/* marshal rdata into buffer, must be MAX_RDLENGTH in size */ 56224090Sdougbsize_t 57224090Sdougbrr_marshal_rdata(rr_type* rr, uint8_t* rdata, size_t sz) 58224090Sdougb{ 59224090Sdougb size_t len = 0; 60224090Sdougb unsigned i; 61224090Sdougb assert(rr); 62224090Sdougb for(i=0; i<rr->rdata_count; i++) { 63224090Sdougb len += add_rdata(rr, i, rdata+len, sz-len); 64224090Sdougb } 65224090Sdougb return len; 66224090Sdougb} 67224090Sdougb 68224090Sdougb/** delete an RR */ 69224090Sdougbvoid 70224090Sdougbudb_del_rr(udb_base* udb, udb_ptr* z, rr_type* rr) 71224090Sdougb{ 72224090Sdougb /* marshal the rdata (uncompressed) into a buffer */ 73224090Sdougb uint8_t rdata[MAX_RDLENGTH]; 74224090Sdougb size_t rdatalen = rr_marshal_rdata(rr, rdata, sizeof(rdata)); 75224090Sdougb assert(udb); 76224090Sdougb udb_zone_del_rr(udb, z, dname_name(domain_dname(rr->owner)), 77224090Sdougb domain_dname(rr->owner)->name_size, rr->type, rr->klass, 78224090Sdougb rdata, rdatalen); 79224090Sdougb} 80224090Sdougb 81224090Sdougb/** write rr */ 82224090Sdougbint 83224090Sdougbudb_write_rr(udb_base* udb, udb_ptr* z, rr_type* rr) 84224090Sdougb{ 85224090Sdougb /* marshal the rdata (uncompressed) into a buffer */ 86224090Sdougb uint8_t rdata[MAX_RDLENGTH]; 87224090Sdougb size_t rdatalen = 0; 88224090Sdougb unsigned i; 89224090Sdougb assert(rr); 90224090Sdougb for(i=0; i<rr->rdata_count; i++) { 91224090Sdougb rdatalen += add_rdata(rr, i, rdata+rdatalen, 92224090Sdougb sizeof(rdata)-rdatalen); 93224090Sdougb } 94224090Sdougb assert(udb); 95224090Sdougb return udb_zone_add_rr(udb, z, dname_name(domain_dname(rr->owner)), 96224090Sdougb domain_dname(rr->owner)->name_size, rr->type, rr->klass, 97224090Sdougb rr->ttl, rdata, rdatalen); 98224090Sdougb} 99224090Sdougb 100224090Sdougb/** write rrset */ 101224090Sdougbstatic int 102224090Sdougbwrite_rrset(udb_base* udb, udb_ptr* z, rrset_type* rrset) 103224090Sdougb{ 104224090Sdougb unsigned i; 105224090Sdougb for(i=0; i<rrset->rr_count; i++) { 106224090Sdougb if(!udb_write_rr(udb, z, &rrset->rrs[i])) 107224090Sdougb return 0; 108224090Sdougb } 109224090Sdougb return 1; 110224090Sdougb} 111224090Sdougb 112224090Sdougb/** write a zone */ 113224090Sdougbstatic int 114224090Sdougbwrite_zone(udb_base* udb, udb_ptr* z, zone_type* zone) 115224090Sdougb{ 116224090Sdougb /* write all domains in the zone */ 117224090Sdougb domain_type* walk; 118224090Sdougb rrset_type* rrset; 119224090Sdougb unsigned long n = 0, c = 0; 120224090Sdougb time_t t = time(NULL); 121224090Sdougb 122224090Sdougb /* count domains: for pct logging */ 123224090Sdougb for(walk=zone->apex; walk && domain_is_subdomain(walk, zone->apex); 124224090Sdougb walk=domain_next(walk)) { 125224090Sdougb 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+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 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 *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 */ 311static int 312create_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 329void 330namedb_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 415void 416namedb_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