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 #include "ixfr.h" 27 28 /* pathname directory separator character */ 29 #define PATHSEP '/' 30 31 /** add an rdata (uncompressed) to the destination */ 32 static size_t 33 add_rdata(rr_type* rr, unsigned i, uint8_t* buf, size_t buflen) 34 { 35 switch(rdata_atom_wireformat_type(rr->type, i)) { 36 case RDATA_WF_COMPRESSED_DNAME: 37 case RDATA_WF_UNCOMPRESSED_DNAME: 38 { 39 const dname_type* dname = domain_dname( 40 rdata_atom_domain(rr->rdatas[i])); 41 if(dname->name_size > buflen) 42 return 0; 43 memmove(buf, dname_name(dname), dname->name_size); 44 return dname->name_size; 45 } 46 default: 47 break; 48 } 49 if(rdata_atom_size(rr->rdatas[i]) > buflen) 50 return 0; 51 memmove(buf, rdata_atom_data(rr->rdatas[i]), 52 rdata_atom_size(rr->rdatas[i])); 53 return rdata_atom_size(rr->rdatas[i]); 54 } 55 56 /* marshal rdata into buffer, must be MAX_RDLENGTH in size */ 57 size_t 58 rr_marshal_rdata(rr_type* rr, uint8_t* rdata, size_t sz) 59 { 60 size_t len = 0; 61 unsigned i; 62 assert(rr); 63 for(i=0; i<rr->rdata_count; i++) { 64 len += add_rdata(rr, i, rdata+len, sz-len); 65 } 66 return len; 67 } 68 69 /** delete an RR */ 70 void 71 udb_del_rr(udb_base* udb, udb_ptr* z, rr_type* rr) 72 { 73 /* marshal the rdata (uncompressed) into a buffer */ 74 uint8_t rdata[MAX_RDLENGTH]; 75 size_t rdatalen = rr_marshal_rdata(rr, rdata, sizeof(rdata)); 76 assert(udb); 77 udb_zone_del_rr(udb, z, dname_name(domain_dname(rr->owner)), 78 domain_dname(rr->owner)->name_size, rr->type, rr->klass, 79 rdata, rdatalen); 80 } 81 82 /** write rr */ 83 int 84 udb_write_rr(udb_base* udb, udb_ptr* z, rr_type* rr) 85 { 86 /* marshal the rdata (uncompressed) into a buffer */ 87 uint8_t rdata[MAX_RDLENGTH]; 88 size_t rdatalen = 0; 89 unsigned i; 90 assert(rr); 91 for(i=0; i<rr->rdata_count; i++) { 92 rdatalen += add_rdata(rr, i, rdata+rdatalen, 93 sizeof(rdata)-rdatalen); 94 } 95 assert(udb); 96 return udb_zone_add_rr(udb, z, dname_name(domain_dname(rr->owner)), 97 domain_dname(rr->owner)->name_size, rr->type, rr->klass, 98 rr->ttl, rdata, rdatalen); 99 } 100 101 /** write rrset */ 102 static int 103 write_rrset(udb_base* udb, udb_ptr* z, rrset_type* rrset) 104 { 105 unsigned i; 106 for(i=0; i<rrset->rr_count; i++) { 107 if(!udb_write_rr(udb, z, &rrset->rrs[i])) 108 return 0; 109 } 110 return 1; 111 } 112 113 /** write a zone */ 114 static int 115 write_zone(udb_base* udb, udb_ptr* z, zone_type* zone) 116 { 117 /* write all domains in the zone */ 118 domain_type* walk; 119 rrset_type* rrset; 120 unsigned long n = 0, c = 0; 121 time_t t = time(NULL); 122 123 /* count domains: for pct logging */ 124 for(walk=zone->apex; walk && domain_is_subdomain(walk, zone->apex); 125 walk=domain_next(walk)) { 126 n++; 127 } 128 /* write them */ 129 for(walk=zone->apex; walk && domain_is_subdomain(walk, zone->apex); 130 walk=domain_next(walk)) { 131 /* write all rrsets (in the zone) for this domain */ 132 for(rrset=walk->rrsets; rrset; rrset=rrset->next) { 133 if(rrset->zone == zone) { 134 if(!write_rrset(udb, z, rrset)) 135 return 0; 136 } 137 } 138 /* only check every ... domains, and print pct */ 139 if(++c % ZONEC_PCT_COUNT == 0 && time(NULL) > t + ZONEC_PCT_TIME) { 140 t = time(NULL); 141 VERBOSITY(1, (LOG_INFO, "write %s %d %%", 142 zone->opts->name, (n==0)?0:(int)(c*((unsigned long)100)/n))); 143 } 144 } 145 return 1; 146 } 147 148 /** create and write a zone */ 149 int 150 write_zone_to_udb(udb_base* udb, zone_type* zone, struct timespec* mtime, 151 const char* file_str) 152 { 153 udb_ptr z; 154 /* make udb dirty */ 155 udb_base_set_userflags(udb, 1); 156 /* find or create zone */ 157 if(udb_zone_search(udb, &z, dname_name(domain_dname(zone->apex)), 158 domain_dname(zone->apex)->name_size)) { 159 /* wipe existing contents */ 160 udb_zone_clear(udb, &z); 161 } else { 162 if(!udb_zone_create(udb, &z, dname_name(domain_dname( 163 zone->apex)), domain_dname(zone->apex)->name_size)) { 164 udb_base_set_userflags(udb, 0); 165 return 0; 166 } 167 } 168 /* set mtime */ 169 ZONE(&z)->mtime = (uint64_t)mtime->tv_sec; 170 ZONE(&z)->mtime_nsec = (uint64_t)mtime->tv_nsec; 171 ZONE(&z)->is_changed = 0; 172 udb_zone_set_log_str(udb, &z, NULL); 173 udb_zone_set_file_str(udb, &z, file_str); 174 /* write zone */ 175 if(!write_zone(udb, &z, zone)) { 176 udb_base_set_userflags(udb, 0); 177 return 0; 178 } 179 udb_ptr_unlink(&z, udb); 180 udb_base_set_userflags(udb, 0); 181 return 1; 182 } 183 184 int 185 print_rrs(FILE* out, struct zone* zone) 186 { 187 rrset_type *rrset; 188 domain_type *domain = zone->apex; 189 region_type* region = region_create(xalloc, free); 190 region_type* rr_region = region_create(xalloc, free); 191 buffer_type* rr_buffer = buffer_create(region, MAX_RDLENGTH); 192 struct state_pretty_rr* state = create_pretty_rr(region); 193 /* first print the SOA record for the zone */ 194 if(zone->soa_rrset) { 195 size_t i; 196 for(i=0; i < zone->soa_rrset->rr_count; i++) { 197 if(!print_rr(out, state, &zone->soa_rrset->rrs[i], 198 rr_region, rr_buffer)){ 199 log_msg(LOG_ERR, "There was an error " 200 "printing SOARR to zone %s", 201 zone->opts->name); 202 region_destroy(region); 203 region_destroy(rr_region); 204 return 0; 205 } 206 } 207 } 208 /* go through entire tree below the zone apex (incl subzones) */ 209 while(domain && domain_is_subdomain(domain, zone->apex)) 210 { 211 for(rrset = domain->rrsets; rrset; rrset=rrset->next) 212 { 213 size_t i; 214 if(rrset->zone != zone || rrset == zone->soa_rrset) 215 continue; 216 for(i=0; i < rrset->rr_count; i++) { 217 if(!print_rr(out, state, &rrset->rrs[i], 218 rr_region, rr_buffer)){ 219 log_msg(LOG_ERR, "There was an error " 220 "printing RR to zone %s", 221 zone->opts->name); 222 region_destroy(region); 223 region_destroy(rr_region); 224 return 0; 225 } 226 } 227 } 228 domain = domain_next(domain); 229 } 230 region_destroy(region); 231 region_destroy(rr_region); 232 return 1; 233 } 234 235 static int 236 print_header(zone_type* zone, FILE* out, time_t* now, const char* logs) 237 { 238 char buf[4096+16]; 239 /* ctime prints newline at end of this line */ 240 snprintf(buf, sizeof(buf), "; zone %s written by NSD %s on %s", 241 zone->opts->name, PACKAGE_VERSION, ctime(now)); 242 if(!write_data(out, buf, strlen(buf))) 243 return 0; 244 if(!logs || logs[0] == 0) return 1; 245 snprintf(buf, sizeof(buf), "; %s\n", logs); 246 return write_data(out, buf, strlen(buf)); 247 } 248 249 static int 250 write_to_zonefile(zone_type* zone, const char* filename, const char* logs) 251 { 252 time_t now = time(0); 253 FILE *out = fopen(filename, "w"); 254 if(!out) { 255 log_msg(LOG_ERR, "cannot write zone %s file %s: %s", 256 zone->opts->name, filename, strerror(errno)); 257 return 0; 258 } 259 if(!print_header(zone, out, &now, logs)) { 260 fclose(out); 261 log_msg(LOG_ERR, "There was an error printing " 262 "the header to zone %s", zone->opts->name); 263 return 0; 264 } 265 if(!print_rrs(out, zone)) { 266 fclose(out); 267 return 0; 268 } 269 if(fclose(out) != 0) { 270 log_msg(LOG_ERR, "cannot write zone %s to file %s: fclose: %s", 271 zone->opts->name, filename, strerror(errno)); 272 return 0; 273 } 274 return 1; 275 } 276 277 /** create directories above this file, .../dir/dir/dir/file */ 278 int 279 create_dirs(const char* path) 280 { 281 char dir[4096]; 282 char* p; 283 strlcpy(dir, path, sizeof(dir)); 284 /* if we start with / then do not try to create '' */ 285 if(dir[0] == PATHSEP) 286 p = strchr(dir+1, PATHSEP); 287 else p = strchr(dir, PATHSEP); 288 /* create each directory component from the left */ 289 while(p) { 290 assert(*p == PATHSEP); 291 *p = 0; /* end the directory name here */ 292 if(mkdir(dir 293 #ifndef MKDIR_HAS_ONE_ARG 294 , 0750 295 #endif 296 ) == -1) { 297 if(errno != EEXIST) { 298 log_msg(LOG_ERR, "create dir %s: %s", 299 dir, strerror(errno)); 300 *p = PATHSEP; /* restore input string */ 301 return 0; 302 } 303 /* it already exists, OK, continue */ 304 } 305 *p = PATHSEP; 306 p = strchr(p+1, PATHSEP); 307 } 308 return 1; 309 } 310 311 /** create pathname components and check if file exists */ 312 static int 313 create_path_components(const char* path, int* notexist) 314 { 315 /* stat the file, to see if it exists, and if its directories exist */ 316 struct stat s; 317 if(stat(path, &s) != 0) { 318 if(errno == ENOENT) { 319 *notexist = 1; 320 /* see if we need to create pathname components */ 321 return create_dirs(path); 322 } 323 log_msg(LOG_ERR, "cannot stat %s: %s", path, strerror(errno)); 324 return 0; 325 } 326 *notexist = 0; 327 return 1; 328 } 329 330 void 331 namedb_write_zonefile(struct nsd* nsd, struct zone_options* zopt) 332 { 333 const char* zfile; 334 int notexist = 0; 335 zone_type* zone; 336 /* if no zone exists, it has no contents or it has no zonefile 337 * configured, then no need to write data to disk */ 338 if(!zopt->pattern->zonefile) 339 return; 340 zone = namedb_find_zone(nsd->db, (const dname_type*)zopt->node.key); 341 if(!zone || !zone->apex || !zone->soa_rrset) 342 return; 343 /* write if file does not exist, or if changed */ 344 /* so, determine filename, create directory components, check exist*/ 345 zfile = config_make_zonefile(zopt, nsd); 346 if(!create_path_components(zfile, ¬exist)) { 347 log_msg(LOG_ERR, "could not write zone %s to file %s because " 348 "the path could not be created", zopt->name, zfile); 349 return; 350 } 351 352 /* if not changed, do not write. */ 353 if(notexist || zone->is_changed) { 354 char logs[4096]; 355 char bakfile[4096]; 356 struct timespec mtime; 357 udb_ptr zudb; 358 if(nsd->db->udb) { 359 if(!udb_zone_search(nsd->db->udb, &zudb, 360 dname_name(domain_dname(zone->apex)), 361 domain_dname(zone->apex)->name_size)) 362 return; /* zone does not exist in db */ 363 } 364 /* write to zfile~ first, then rename if that works */ 365 snprintf(bakfile, sizeof(bakfile), "%s~", zfile); 366 if(nsd->db->udb && ZONE(&zudb)->log_str.data) { 367 udb_ptr s; 368 udb_ptr_new(&s, nsd->db->udb, &ZONE(&zudb)->log_str); 369 strlcpy(logs, (char*)udb_ptr_data(&s), sizeof(logs)); 370 udb_ptr_unlink(&s, nsd->db->udb); 371 } else if(zone->logstr) { 372 strlcpy(logs, zone->logstr, sizeof(logs)); 373 } else logs[0] = 0; 374 VERBOSITY(1, (LOG_INFO, "writing zone %s to file %s", 375 zone->opts->name, zfile)); 376 if(!write_to_zonefile(zone, bakfile, logs)) { 377 if(nsd->db->udb) 378 udb_ptr_unlink(&zudb, nsd->db->udb); 379 (void)unlink(bakfile); /* delete failed file */ 380 return; /* error already printed */ 381 } 382 if(rename(bakfile, zfile) == -1) { 383 log_msg(LOG_ERR, "rename(%s to %s) failed: %s", 384 bakfile, zfile, strerror(errno)); 385 if(nsd->db->udb) 386 udb_ptr_unlink(&zudb, nsd->db->udb); 387 (void)unlink(bakfile); /* delete failed file */ 388 return; 389 } 390 zone->is_changed = 0; 391 /* fetch the mtime of the just created zonefile so we 392 * do not waste effort reading it back in */ 393 if(!file_get_mtime(zfile, &mtime, ¬exist)) { 394 get_time(&mtime); 395 } 396 if(nsd->db->udb) { 397 ZONE(&zudb)->mtime = (uint64_t)mtime.tv_sec; 398 ZONE(&zudb)->mtime_nsec = (uint64_t)mtime.tv_nsec; 399 ZONE(&zudb)->is_changed = 0; 400 udb_zone_set_log_str(nsd->db->udb, &zudb, NULL); 401 udb_ptr_unlink(&zudb, nsd->db->udb); 402 } else { 403 zone->mtime = mtime; 404 if(zone->filename) 405 region_recycle(nsd->db->region, zone->filename, 406 strlen(zone->filename)+1); 407 zone->filename = region_strdup(nsd->db->region, zfile); 408 if(zone->logstr) 409 region_recycle(nsd->db->region, zone->logstr, 410 strlen(zone->logstr)+1); 411 zone->logstr = NULL; 412 } 413 if(zone_is_ixfr_enabled(zone) && zone->ixfr) 414 ixfr_write_to_file(zone, zfile); 415 } 416 } 417 418 void 419 namedb_write_zonefiles(struct nsd* nsd, struct nsd_options* options) 420 { 421 struct zone_options* zo; 422 RBTREE_FOR(zo, struct zone_options*, options->zone_options) { 423 namedb_write_zonefile(nsd, zo); 424 } 425 } 426