/* * dbaccess.c -- access methods for nsd(8) database * * Copyright (c) 2001-2006, NLnet Labs. All rights reserved. * * See LICENSE for the license. * */ #include "config.h" #include #include #include #include #include #include #include #include "dns.h" #include "namedb.h" #include "util.h" #include "options.h" #include "rdata.h" #include "udb.h" #include "zonec.h" #include "nsec3.h" #include "difffile.h" #include "nsd.h" #include "ixfr.h" #include "ixfrcreate.h" void namedb_close(struct namedb* db) { if(db) { zonec_desetup_parser(); region_destroy(db->region); } } void namedb_free_ixfr(struct namedb* db) { struct radnode* n; for(n=radix_first(db->zonetree); n; n=radix_next(n)) { zone_ixfr_free(((zone_type*)n->elem)->ixfr); } } /** create a zone */ zone_type* namedb_zone_create(namedb_type* db, const dname_type* dname, struct zone_options* zo) { zone_type* zone = (zone_type *) region_alloc(db->region, sizeof(zone_type)); zone->node = radname_insert(db->zonetree, dname_name(dname), dname->name_size, zone); assert(zone->node); zone->apex = domain_table_insert(db->domains, dname); zone->apex->usage++; /* the zone.apex reference */ zone->apex->is_apex = 1; zone->soa_rrset = NULL; zone->soa_nx_rrset = NULL; zone->ns_rrset = NULL; #ifdef NSEC3 zone->nsec3_param = NULL; zone->nsec3_last = NULL; zone->nsec3tree = NULL; zone->hashtree = NULL; zone->wchashtree = NULL; zone->dshashtree = NULL; #endif zone->opts = zo; zone->ixfr = NULL; zone->filename = NULL; zone->logstr = NULL; zone->mtime.tv_sec = 0; zone->mtime.tv_nsec = 0; zone->zonestatid = 0; zone->is_secure = 0; zone->is_changed = 0; zone->is_updated = 0; zone->is_skipped = 0; zone->is_checked = 0; zone->is_bad = 0; zone->is_ok = 1; return zone; } void namedb_zone_delete(namedb_type* db, zone_type* zone) { /* RRs and UDB and NSEC3 and so on must be already deleted */ radix_delete(db->zonetree, zone->node); /* see if apex can be deleted */ if(zone->apex) { zone->apex->usage --; zone->apex->is_apex = 0; if(zone->apex->usage == 0) { /* delete the apex, possibly */ domain_table_deldomain(db, zone->apex); } } /* soa_rrset is freed when the SOA was deleted */ if(zone->soa_nx_rrset) { region_recycle(db->region, zone->soa_nx_rrset->rrs, sizeof(rr_type)); region_recycle(db->region, zone->soa_nx_rrset, sizeof(rrset_type)); } #ifdef NSEC3 hash_tree_delete(db->region, zone->nsec3tree); hash_tree_delete(db->region, zone->hashtree); hash_tree_delete(db->region, zone->wchashtree); hash_tree_delete(db->region, zone->dshashtree); #endif zone_ixfr_free(zone->ixfr); if(zone->filename) region_recycle(db->region, zone->filename, strlen(zone->filename)+1); if(zone->logstr) region_recycle(db->region, zone->logstr, strlen(zone->logstr)+1); region_recycle(db->region, zone, sizeof(zone_type)); } struct namedb * namedb_open (struct nsd_options* opt) { namedb_type* db; /* * Region used to store the loaded database. The region is * freed in namedb_close. */ region_type* db_region; (void)opt; #ifdef USE_MMAP_ALLOC db_region = region_create_custom(mmap_alloc, mmap_free, MMAP_ALLOC_CHUNK_SIZE, MMAP_ALLOC_LARGE_OBJECT_SIZE, MMAP_ALLOC_INITIAL_CLEANUP_SIZE, 1); #else /* !USE_MMAP_ALLOC */ db_region = region_create_custom(xalloc, free, DEFAULT_CHUNK_SIZE, DEFAULT_LARGE_OBJECT_SIZE, DEFAULT_INITIAL_CLEANUP_SIZE, 1); #endif /* !USE_MMAP_ALLOC */ db = (namedb_type *) region_alloc(db_region, sizeof(struct namedb)); db->region = db_region; db->domains = domain_table_create(db->region); db->zonetree = radix_tree_create(db->region); db->diff_skip = 0; db->diff_pos = 0; zonec_setup_parser(db); if (gettimeofday(&(db->diff_timestamp), NULL) != 0) { log_msg(LOG_ERR, "unable to load namedb: cannot initialize timestamp"); region_destroy(db_region); return NULL; } return db; } /** get the file mtime stat (or nonexist or error) */ int file_get_mtime(const char* file, struct timespec* mtime, int* nonexist) { struct stat s; if(stat(file, &s) != 0) { mtime->tv_sec = 0; mtime->tv_nsec = 0; *nonexist = (errno == ENOENT); return 0; } *nonexist = 0; mtime->tv_sec = s.st_mtime; #ifdef HAVE_STRUCT_STAT_ST_MTIMENSEC mtime->tv_nsec = s.st_mtimensec; #elif defined(HAVE_STRUCT_STAT_ST_MTIM_TV_NSEC) mtime->tv_nsec = s.st_mtim.tv_nsec; #else mtime->tv_nsec = 0; #endif return 1; } void namedb_read_zonefile(struct nsd* nsd, struct zone* zone, udb_base* taskudb, udb_ptr* last_task) { struct timespec mtime; int nonexist = 0; unsigned int errors; const char* fname; struct ixfr_create* ixfrcr = NULL; int ixfr_create_already_done = 0; if(!nsd->db || !zone || !zone->opts || !zone->opts->pattern->zonefile) return; mtime.tv_sec = 0; mtime.tv_nsec = 0; fname = config_make_zonefile(zone->opts, nsd); assert(fname); if(!file_get_mtime(fname, &mtime, &nonexist)) { if(nonexist) { if(zone_is_slave(zone->opts)) { /* for slave zones not as bad, no zonefile * may just mean we have to transfer it */ VERBOSITY(2, (LOG_INFO, "zonefile %s does not exist", fname)); } else { /* without a download option, we can never * serve data, more severe error printout */ log_msg(LOG_ERR, "zonefile %s does not exist", fname); } } else log_msg(LOG_ERR, "zonefile %s: %s", fname, strerror(errno)); if(taskudb) task_new_soainfo(taskudb, last_task, zone, 0); return; } else { const char* zone_fname = zone->filename; struct timespec zone_mtime = zone->mtime; /* if no zone_fname, then it was acquired in zone transfer, * see if the file is newer than the zone transfer * (regardless if this is a different file), because the * zone transfer is a different content source too */ if(!zone_fname && timespec_compare(&zone_mtime, &mtime) >= 0) { VERBOSITY(3, (LOG_INFO, "zonefile %s is older than " "zone transfer in memory", fname)); return; /* if zone_fname, then the file was acquired from reading it, * and see if filename changed or mtime newer to read it */ } else if(zone_fname && strcmp(zone_fname, fname) == 0 && timespec_compare(&zone_mtime, &mtime) == 0) { VERBOSITY(3, (LOG_INFO, "zonefile %s is not modified", fname)); return; } } if(ixfr_create_from_difference(zone, fname, &ixfr_create_already_done)) { ixfrcr = ixfr_create_start(zone, fname, zone->opts->pattern->ixfr_size, 0); if(!ixfrcr) { /* leaves the ixfrcr at NULL, so it is not created */ log_msg(LOG_ERR, "out of memory starting ixfr create"); } } assert(parser); /* wipe zone from memory */ #ifdef NSEC3 nsec3_clear_precompile(nsd->db, zone); zone->nsec3_param = NULL; #endif delete_zone_rrs(nsd->db, zone); errors = zonec_read(zone->opts->name, fname, zone); if(errors > 0) { log_msg(LOG_ERR, "zone %s file %s read with %u errors", zone->opts->name, fname, errors); /* wipe (partial) zone from memory */ zone->is_ok = 1; #ifdef NSEC3 nsec3_clear_precompile(nsd->db, zone); zone->nsec3_param = NULL; #endif delete_zone_rrs(nsd->db, zone); if(zone->filename) region_recycle(nsd->db->region, zone->filename, strlen(zone->filename)+1); zone->filename = NULL; if(zone->logstr) region_recycle(nsd->db->region, zone->logstr, strlen(zone->logstr)+1); zone->logstr = NULL; } else { VERBOSITY(1, (LOG_INFO, "zone %s read with success", zone->opts->name)); zone->is_ok = 1; zone->is_changed = 0; /* store zone into udb */ zone->mtime = mtime; if(zone->filename) region_recycle(nsd->db->region, zone->filename, strlen(zone->filename)+1); zone->filename = region_strdup(nsd->db->region, fname); if(zone->logstr) region_recycle(nsd->db->region, zone->logstr, strlen(zone->logstr)+1); zone->logstr = NULL; if(ixfr_create_already_done) { ixfr_readup_exist(zone, nsd, fname); } else if(ixfrcr) { if(!ixfr_create_perform(ixfrcr, zone, 1, nsd, fname, zone->opts->pattern->ixfr_number)) { log_msg(LOG_ERR, "failed to create IXFR"); } else { VERBOSITY(2, (LOG_INFO, "zone %s created IXFR %s.ixfr", zone->opts->name, fname)); } ixfr_create_free(ixfrcr); } else if(zone_is_ixfr_enabled(zone)) { ixfr_read_from_file(nsd, zone, fname); } } if(taskudb) task_new_soainfo(taskudb, last_task, zone, 0); #ifdef NSEC3 prehash_zone_complete(nsd->db, zone); #endif } void namedb_check_zonefile(struct nsd* nsd, udb_base* taskudb, udb_ptr* last_task, struct zone_options* zopt) { zone_type* zone; const dname_type* dname = (const dname_type*)zopt->node.key; /* find zone to go with it, or create it */ zone = namedb_find_zone(nsd->db, dname); if(!zone) { zone = namedb_zone_create(nsd->db, dname, zopt); } namedb_read_zonefile(nsd, zone, taskudb, last_task); } void namedb_check_zonefiles(struct nsd* nsd, struct nsd_options* opt, udb_base* taskudb, udb_ptr* last_task) { struct zone_options* zo; /* check all zones in opt, create if not exist in main db */ RBTREE_FOR(zo, struct zone_options*, opt->zone_options) { namedb_check_zonefile(nsd, taskudb, last_task, zo); if(nsd->signal_hint_shutdown) break; } }