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 int 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, c*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, time_t 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; 169 ZONE(&z)->is_changed = 0; 170 udb_zone_set_log_str(udb, &z, NULL); 171 udb_zone_set_file_str(udb, &z, file_str); 172 /* write zone */ 173 if(!write_zone(udb, &z, zone)) { 174 udb_base_set_userflags(udb, 0); 175 return 0; 176 } 177 udb_ptr_unlink(&z, udb); 178 udb_base_set_userflags(udb, 0); 179 return 1; 180 } 181 182 static int 183 print_rrs(FILE* out, struct zone* zone) 184 { 185 rrset_type *rrset; 186 domain_type *domain = zone->apex; 187 region_type* region = region_create(xalloc, free); 188 struct state_pretty_rr* state = create_pretty_rr(region); 189 /* first print the SOA record for the zone */ 190 if(zone->soa_rrset) { 191 size_t i; 192 for(i=0; i < zone->soa_rrset->rr_count; i++) { 193 if(!print_rr(out, state, &zone->soa_rrset->rrs[i])){ 194 log_msg(LOG_ERR, "There was an error " 195 "printing SOARR to zone %s", 196 zone->opts->name); 197 region_destroy(region); 198 return 0; 199 } 200 } 201 } 202 /* go through entire tree below the zone apex (incl subzones) */ 203 while(domain && domain_is_subdomain(domain, zone->apex)) 204 { 205 for(rrset = domain->rrsets; rrset; rrset=rrset->next) 206 { 207 size_t i; 208 if(rrset->zone != zone || rrset == zone->soa_rrset) 209 continue; 210 for(i=0; i < rrset->rr_count; i++) { 211 if(!print_rr(out, state, &rrset->rrs[i])){ 212 log_msg(LOG_ERR, "There was an error " 213 "printing RR to zone %s", 214 zone->opts->name); 215 region_destroy(region); 216 return 0; 217 } 218 } 219 } 220 domain = domain_next(domain); 221 } 222 region_destroy(region); 223 return 1; 224 } 225 226 static int 227 print_header(zone_type* zone, FILE* out, time_t* now, const char* logs) 228 { 229 char buf[4096]; 230 /* ctime prints newline at end of this line */ 231 snprintf(buf, sizeof(buf), "; zone %s written by NSD %s on %s", 232 zone->opts->name, PACKAGE_VERSION, ctime(now)); 233 if(!write_data(out, buf, strlen(buf))) 234 return 0; 235 if(!logs || logs[0] == 0) return 1; 236 snprintf(buf, sizeof(buf), "; %s\n", logs); 237 return write_data(out, buf, strlen(buf)); 238 } 239 240 static int 241 write_to_zonefile(zone_type* zone, const char* filename, const char* logs) 242 { 243 time_t now = time(0); 244 FILE *out; 245 VERBOSITY(1, (LOG_INFO, "writing zone %s to file %s", 246 zone->opts->name, filename)); 247 248 out = fopen(filename, "w"); 249 if(!out) { 250 log_msg(LOG_ERR, "cannot write zone %s file %s: %s", 251 zone->opts->name, filename, strerror(errno)); 252 return 0; 253 } 254 if(!print_header(zone, out, &now, logs)) { 255 fclose(out); 256 log_msg(LOG_ERR, "There was an error printing " 257 "the header to zone %s", zone->opts->name); 258 return 0; 259 } 260 if(!print_rrs(out, zone)) { 261 fclose(out); 262 return 0; 263 } 264 fclose(out); 265 return 1; 266 } 267 268 /** create directories above this file, .../dir/dir/dir/file */ 269 int 270 create_dirs(const char* path) 271 { 272 char dir[4096]; 273 char* p; 274 strlcpy(dir, path, sizeof(dir)); 275 /* if we start with / then do not try to create '' */ 276 if(dir[0] == PATHSEP) 277 p = strchr(dir+1, PATHSEP); 278 else p = strchr(dir, PATHSEP); 279 /* create each directory component from the left */ 280 while(p) { 281 assert(*p == PATHSEP); 282 *p = 0; /* end the directory name here */ 283 if(mkdir(dir 284 #ifndef MKDIR_HAS_ONE_ARG 285 , 0750 286 #endif 287 ) == -1) { 288 if(errno != EEXIST) { 289 log_msg(LOG_ERR, "create dir %s: %s", 290 dir, strerror(errno)); 291 return 0; 292 } 293 /* it already exists, OK, continue */ 294 } 295 *p = PATHSEP; 296 p = strchr(p+1, PATHSEP); 297 } 298 return 1; 299 } 300 301 /** create pathname components and check if file exists */ 302 static int 303 create_path_components(const char* path, int* notexist) 304 { 305 /* stat the file, to see if it exists, and if its directories exist */ 306 struct stat s; 307 if(stat(path, &s) != 0) { 308 if(errno == ENOENT) { 309 *notexist = 1; 310 /* see if we need to create pathname components */ 311 return create_dirs(path); 312 } 313 log_msg(LOG_ERR, "cannot stat %s: %s", path, strerror(errno)); 314 return 0; 315 } 316 *notexist = 0; 317 return 1; 318 } 319 320 void 321 namedb_write_zonefile(struct nsd* nsd, zone_options_t* zopt) 322 { 323 const char* zfile; 324 int notexist = 0; 325 zone_type* zone; 326 /* if no zone exists, it has no contents or it has no zonefile 327 * configured, then no need to write data to disk */ 328 if(!zopt->pattern->zonefile) 329 return; 330 zone = namedb_find_zone(nsd->db, (const dname_type*)zopt->node.key); 331 if(!zone || !zone->apex) 332 return; 333 /* write if file does not exist, or if changed */ 334 /* so, determine filename, create directory components, check exist*/ 335 zfile = config_make_zonefile(zopt, nsd); 336 if(!create_path_components(zfile, ¬exist)) { 337 log_msg(LOG_ERR, "could not write zone %s to file %s because " 338 "the path could not be created", zopt->name, zfile); 339 return; 340 } 341 342 /* if not changed, do not write. */ 343 if(notexist || zone->is_changed) { 344 char logs[4096]; 345 char bakfile[4096]; 346 udb_ptr zudb; 347 if(!udb_zone_search(nsd->db->udb, &zudb, 348 dname_name(domain_dname(zone->apex)), 349 domain_dname(zone->apex)->name_size)) 350 return; /* zone does not exist in db */ 351 /* write to zfile~ first, then rename if that works */ 352 snprintf(bakfile, sizeof(bakfile), "%s~", zfile); 353 if(ZONE(&zudb)->log_str.data) { 354 udb_ptr s; 355 udb_ptr_new(&s, nsd->db->udb, &ZONE(&zudb)->log_str); 356 strlcpy(logs, (char*)udb_ptr_data(&s), sizeof(logs)); 357 udb_ptr_unlink(&s, nsd->db->udb); 358 } else logs[0] = 0; 359 if(!write_to_zonefile(zone, bakfile, logs)) { 360 udb_ptr_unlink(&zudb, nsd->db->udb); 361 return; /* error already printed */ 362 } 363 if(rename(bakfile, zfile) == -1) { 364 log_msg(LOG_ERR, "rename(%s to %s) failed: %s", 365 bakfile, zfile, strerror(errno)); 366 udb_ptr_unlink(&zudb, nsd->db->udb); 367 return; 368 } 369 zone->is_changed = 0; 370 ZONE(&zudb)->mtime = (uint64_t)time(0); 371 ZONE(&zudb)->is_changed = 0; 372 udb_zone_set_log_str(nsd->db->udb, &zudb, NULL); 373 udb_ptr_unlink(&zudb, nsd->db->udb); 374 } 375 } 376 377 void 378 namedb_write_zonefiles(struct nsd* nsd, nsd_options_t* options) 379 { 380 zone_options_t* zo; 381 RBTREE_FOR(zo, zone_options_t*, options->zone_options) { 382 namedb_write_zonefile(nsd, zo); 383 } 384 } 385