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 */ 31 static size_t 32 add_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 */ 56 size_t 57 rr_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 */ 69 void 70 udb_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 */ 82 int 83 udb_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 */ 101 static int 102 write_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 */ 113 static int 114 write_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 */ 148 int 149 write_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 183 int 184 print_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 234 static int 235 print_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 248 static int 249 write_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 */ 277 int 278 create_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 */ 311 static int 312 create_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 329 void 330 namedb_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 415 void 416 namedb_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