1 /* 2 * dbaccess.c -- access methods for nsd(8) 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/types.h> 13 #include <sys/stat.h> 14 15 #include <errno.h> 16 #include <stdlib.h> 17 #include <string.h> 18 #include <unistd.h> 19 #include <fcntl.h> 20 21 #include "dns.h" 22 #include "namedb.h" 23 #include "util.h" 24 #include "options.h" 25 #include "rdata.h" 26 #include "udb.h" 27 #include "zonec.h" 28 #include "nsec3.h" 29 #include "difffile.h" 30 #include "nsd.h" 31 #include "ixfr.h" 32 #include "ixfrcreate.h" 33 34 void 35 namedb_close(struct namedb* db) 36 { 37 if(db) { 38 region_destroy(db->region); 39 } 40 } 41 42 void 43 namedb_free_ixfr(struct namedb* db) 44 { 45 struct radnode* n; 46 for(n=radix_first(db->zonetree); n; n=radix_next(n)) { 47 zone_ixfr_free(((zone_type*)n->elem)->ixfr); 48 } 49 } 50 51 /** create a zone */ 52 zone_type* 53 namedb_zone_create(namedb_type* db, const dname_type* dname, 54 struct zone_options* zo) 55 { 56 zone_type* zone = (zone_type *) region_alloc(db->region, 57 sizeof(zone_type)); 58 zone->node = radname_insert(db->zonetree, dname_name(dname), 59 dname->name_size, zone); 60 assert(zone->node); 61 zone->apex = domain_table_insert(db->domains, dname); 62 zone->apex->usage++; /* the zone.apex reference */ 63 zone->apex->is_apex = 1; 64 zone->soa_rrset = NULL; 65 zone->soa_nx_rrset = NULL; 66 zone->ns_rrset = NULL; 67 #ifdef NSEC3 68 zone->nsec3_param = NULL; 69 zone->nsec3_last = NULL; 70 zone->nsec3tree = NULL; 71 zone->hashtree = NULL; 72 zone->wchashtree = NULL; 73 zone->dshashtree = NULL; 74 #endif 75 zone->opts = zo; 76 zone->ixfr = NULL; 77 zone->filename = NULL; 78 zone->includes.count = 0; 79 zone->includes.paths = NULL; 80 zone->logstr = NULL; 81 zone->mtime.tv_sec = 0; 82 zone->mtime.tv_nsec = 0; 83 zone->zonestatid = 0; 84 zone->is_secure = 0; 85 zone->is_changed = 0; 86 zone->is_updated = 0; 87 zone->is_skipped = 0; 88 zone->is_checked = 0; 89 zone->is_bad = 0; 90 zone->is_ok = 1; 91 return zone; 92 } 93 94 void 95 namedb_zone_free_filenames(namedb_type *db, zone_type* zone) 96 { 97 assert(!zone->includes.paths == !zone->includes.count); 98 99 if (zone->filename) { 100 region_recycle( 101 db->region, zone->filename, strlen(zone->filename) + 1); 102 zone->filename = NULL; 103 } 104 105 if (zone->includes.count) { 106 for (size_t i=0; i < zone->includes.count; i++) { 107 region_recycle( 108 db->region, 109 zone->includes.paths[i], 110 strlen(zone->includes.paths[i]) + 1); 111 } 112 113 region_recycle( 114 db->region, 115 zone->includes.paths, 116 zone->includes.count * sizeof(*zone->includes.paths)); 117 zone->includes.count = 0; 118 zone->includes.paths = NULL; 119 } 120 } 121 122 void 123 namedb_zone_delete(namedb_type* db, zone_type* zone) 124 { 125 /* RRs and UDB and NSEC3 and so on must be already deleted */ 126 radix_delete(db->zonetree, zone->node); 127 128 /* see if apex can be deleted */ 129 if(zone->apex) { 130 zone->apex->usage --; 131 zone->apex->is_apex = 0; 132 if(zone->apex->usage == 0) { 133 /* delete the apex, possibly */ 134 domain_table_deldomain(db, zone->apex); 135 } 136 } 137 138 /* soa_nx_rrset is kept around, between zone delete and new zone 139 * contents. When the SOA was deleted, the usage for domain references 140 * was lowered. */ 141 if(zone->soa_nx_rrset) { 142 region_recycle(db->region, zone->soa_nx_rrset->rrs[0], 143 sizeof(rr_type)+zone->soa_nx_rrset->rrs[0]->rdlength); 144 #ifndef PACKED_STRUCTS 145 region_recycle(db->region, zone->soa_nx_rrset->rrs, 146 sizeof(rr_type*)); 147 #endif 148 region_recycle(db->region, zone->soa_nx_rrset, 149 sizeof(rrset_type) 150 #ifdef PACKED_STRUCTS 151 + sizeof(rr_type*) 152 #endif 153 ); 154 } 155 #ifdef NSEC3 156 hash_tree_delete(db->region, zone->nsec3tree); 157 hash_tree_delete(db->region, zone->hashtree); 158 hash_tree_delete(db->region, zone->wchashtree); 159 hash_tree_delete(db->region, zone->dshashtree); 160 #endif 161 zone_ixfr_free(zone->ixfr); 162 namedb_zone_free_filenames(db, zone); 163 if(zone->logstr) 164 region_recycle(db->region, zone->logstr, 165 strlen(zone->logstr)+1); 166 region_recycle(db->region, zone, sizeof(zone_type)); 167 } 168 169 struct namedb * 170 namedb_open (struct nsd_options* opt) 171 { 172 namedb_type* db; 173 174 /* 175 * Region used to store the loaded database. The region is 176 * freed in namedb_close. 177 */ 178 region_type* db_region; 179 180 (void)opt; 181 182 #ifdef USE_MMAP_ALLOC 183 db_region = region_create_custom(mmap_alloc, mmap_free, MMAP_ALLOC_CHUNK_SIZE, 184 MMAP_ALLOC_LARGE_OBJECT_SIZE, MMAP_ALLOC_INITIAL_CLEANUP_SIZE, 1); 185 #else /* !USE_MMAP_ALLOC */ 186 db_region = region_create_custom(xalloc, free, DEFAULT_CHUNK_SIZE, 187 DEFAULT_LARGE_OBJECT_SIZE, DEFAULT_INITIAL_CLEANUP_SIZE, 1); 188 #endif /* !USE_MMAP_ALLOC */ 189 db = (namedb_type *) region_alloc(db_region, sizeof(struct namedb)); 190 db->region = db_region; 191 db->domains = domain_table_create(db->region); 192 db->zonetree = radix_tree_create(db->region); 193 db->diff_skip = 0; 194 db->diff_pos = 0; 195 196 if (gettimeofday(&(db->diff_timestamp), NULL) != 0) { 197 log_msg(LOG_ERR, "unable to load namedb: cannot initialize timestamp"); 198 region_destroy(db_region); 199 return NULL; 200 } 201 202 return db; 203 } 204 205 /** get the file mtime stat (or nonexist or error) */ 206 int 207 file_get_mtime(const char* file, struct timespec* mtime, int* nonexist) 208 { 209 struct stat s; 210 if(stat(file, &s) != 0) { 211 mtime->tv_sec = 0; 212 mtime->tv_nsec = 0; 213 *nonexist = (errno == ENOENT); 214 return 0; 215 } 216 *nonexist = 0; 217 mtime->tv_sec = s.st_mtime; 218 #ifdef HAVE_STRUCT_STAT_ST_MTIMENSEC 219 mtime->tv_nsec = s.st_mtimensec; 220 #elif defined(HAVE_STRUCT_STAT_ST_MTIM_TV_NSEC) 221 mtime->tv_nsec = s.st_mtim.tv_nsec; 222 #else 223 mtime->tv_nsec = 0; 224 #endif 225 return 1; 226 } 227 228 void 229 namedb_read_zonefile(struct nsd* nsd, struct zone* zone, udb_base* taskudb, 230 udb_ptr* last_task) 231 { 232 struct timespec mtime; 233 int nonexist = 0; 234 unsigned int errors; 235 const char* fname; 236 struct ixfr_create* ixfrcr = NULL; 237 int ixfr_create_already_done = 0; 238 if(!nsd->db || !zone || !zone->opts || !zone->opts->pattern->zonefile) 239 return; 240 mtime.tv_sec = 0; 241 mtime.tv_nsec = 0; 242 fname = config_make_zonefile(zone->opts, nsd); 243 assert(fname); 244 if(!file_get_mtime(fname, &mtime, &nonexist)) { 245 if(nonexist) { 246 if(zone_is_slave(zone->opts)) { 247 /* for slave zones not as bad, no zonefile 248 * may just mean we have to transfer it */ 249 VERBOSITY(2, (LOG_INFO, "zonefile %s does not exist", 250 fname)); 251 } else { 252 /* without a download option, we can never 253 * serve data, more severe error printout */ 254 log_msg(LOG_ERR, "zonefile %s does not exist", fname); 255 } 256 257 } else 258 log_msg(LOG_ERR, "zonefile %s: %s", 259 fname, strerror(errno)); 260 if(taskudb) task_new_soainfo(taskudb, last_task, zone, 0); 261 return; 262 } else { 263 const char* zone_fname = zone->filename; 264 struct timespec zone_mtime = zone->mtime; 265 /* if no zone_fname, then it was acquired in zone transfer, 266 * see if the file is newer than the zone transfer 267 * (regardless if this is a different file), because the 268 * zone transfer is a different content source too */ 269 if(!zone_fname && timespec_compare(&zone_mtime, &mtime) >= 0) { 270 VERBOSITY(3, (LOG_INFO, "zonefile %s is older than " 271 "zone transfer in memory", fname)); 272 return; 273 274 /* if zone_fname, then the file was acquired from reading it, 275 * and see if filename changed or mtime newer to read it */ 276 } else if(zone_fname && strcmp(zone_fname, fname) == 0 && 277 timespec_compare(&zone_mtime, &mtime) == 0) { 278 int changed = 0; 279 struct timespec include_mtime; 280 /* one of the includes may have been deleted, changed, etc */ 281 for (size_t i=0; i < zone->includes.count; i++) { 282 if (!file_get_mtime(zone->includes.paths[i], &include_mtime, &nonexist)) { 283 changed = 1; 284 } else if (timespec_compare(&zone_mtime, &include_mtime) < 0) { 285 mtime = include_mtime; 286 changed = 1; 287 } 288 } 289 290 if (!changed) { 291 VERBOSITY(3, (LOG_INFO, "zonefile %s is not modified", 292 fname)); 293 return; 294 } 295 } 296 } 297 298 if(ixfr_create_from_difference(zone, fname, 299 &ixfr_create_already_done)) { 300 ixfrcr = ixfr_create_start(zone, fname, 301 zone->opts->pattern->ixfr_size, 0); 302 if(!ixfrcr) { 303 /* leaves the ixfrcr at NULL, so it is not created */ 304 log_msg(LOG_ERR, "out of memory starting ixfr create"); 305 } 306 } 307 308 namedb_zone_free_filenames(nsd->db, zone); 309 zone->filename = region_strdup(nsd->db->region, fname); 310 311 /* wipe zone from memory */ 312 #ifdef NSEC3 313 nsec3_clear_precompile(nsd->db, zone); 314 zone->nsec3_param = NULL; 315 #endif 316 delete_zone_rrs(nsd->db, zone); 317 VERBOSITY(5, (LOG_INFO, "zone %s zonec_read(%s)", 318 zone->opts->name, fname)); 319 errors = zonec_read(nsd->db, nsd->db->domains, zone->opts->name, fname, zone); 320 if(errors > 0) { 321 log_msg(LOG_ERR, "zone %s file %s read with %u errors", 322 zone->opts->name, fname, errors); 323 /* wipe (partial) zone from memory */ 324 zone->is_ok = 1; 325 #ifdef NSEC3 326 nsec3_clear_precompile(nsd->db, zone); 327 zone->nsec3_param = NULL; 328 #endif 329 delete_zone_rrs(nsd->db, zone); 330 namedb_zone_free_filenames(nsd->db, zone); 331 if(zone->logstr) 332 region_recycle(nsd->db->region, zone->logstr, 333 strlen(zone->logstr)+1); 334 zone->logstr = NULL; 335 } else { 336 VERBOSITY(1, (LOG_INFO, "zone %s read with success", 337 zone->opts->name)); 338 zone->is_ok = 1; 339 zone->is_changed = 0; 340 /* store zone into udb */ 341 zone->mtime = mtime; 342 if(zone->logstr) 343 region_recycle(nsd->db->region, zone->logstr, 344 strlen(zone->logstr)+1); 345 zone->logstr = NULL; 346 if(ixfr_create_already_done) { 347 ixfr_readup_exist(zone, nsd, fname); 348 } else if(ixfrcr) { 349 if(!ixfr_create_perform(ixfrcr, zone, 1, nsd, fname, 350 zone->opts->pattern->ixfr_number)) { 351 log_msg(LOG_ERR, "failed to create IXFR"); 352 } else { 353 VERBOSITY(2, (LOG_INFO, "zone %s created IXFR %s.ixfr", 354 zone->opts->name, fname)); 355 } 356 ixfr_create_free(ixfrcr); 357 } else if(zone_is_ixfr_enabled(zone)) { 358 ixfr_read_from_file(nsd, zone, fname); 359 } 360 } 361 if(taskudb) task_new_soainfo(taskudb, last_task, zone, 0); 362 #ifdef NSEC3 363 prehash_zone_complete(nsd->db, zone); 364 #endif 365 } 366 367 void namedb_check_zonefile(struct nsd* nsd, udb_base* taskudb, 368 udb_ptr* last_task, struct zone_options* zopt) 369 { 370 zone_type* zone; 371 const dname_type* dname = (const dname_type*)zopt->node.key; 372 /* find zone to go with it, or create it */ 373 zone = namedb_find_zone(nsd->db, dname); 374 if(!zone) { 375 zone = namedb_zone_create(nsd->db, dname, zopt); 376 } 377 namedb_read_zonefile(nsd, zone, taskudb, last_task); 378 } 379 380 void namedb_check_zonefiles(struct nsd* nsd, struct nsd_options* opt, 381 udb_base* taskudb, udb_ptr* last_task) 382 { 383 struct zone_options* zo; 384 /* check all zones in opt, create if not exist in main db */ 385 RBTREE_FOR(zo, struct zone_options*, opt->zone_options) { 386 namedb_check_zonefile(nsd, taskudb, last_task, zo); 387 if(nsd->signal_hint_shutdown) break; 388 } 389 } 390