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