/* * dbcreate.c -- routines to create an nsd(8) name 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 "namedb.h" #include "udb.h" #include "options.h" #include "nsd.h" #include "ixfr.h" /* pathname directory separator character */ #define PATHSEP '/' /** add an rdata (uncompressed) to the destination */ static size_t add_rdata(rr_type* rr, unsigned i, uint8_t* buf, size_t buflen) { switch(rdata_atom_wireformat_type(rr->type, i)) { case RDATA_WF_COMPRESSED_DNAME: case RDATA_WF_UNCOMPRESSED_DNAME: { const dname_type* dname = domain_dname( rdata_atom_domain(rr->rdatas[i])); if(dname->name_size > buflen) return 0; memmove(buf, dname_name(dname), dname->name_size); return dname->name_size; } default: break; } if(rdata_atom_size(rr->rdatas[i]) > buflen) return 0; memmove(buf, rdata_atom_data(rr->rdatas[i]), rdata_atom_size(rr->rdatas[i])); return rdata_atom_size(rr->rdatas[i]); } /* marshal rdata into buffer, must be MAX_RDLENGTH in size */ size_t rr_marshal_rdata(rr_type* rr, uint8_t* rdata, size_t sz) { size_t len = 0; unsigned i; assert(rr); for(i=0; irdata_count; i++) { len += add_rdata(rr, i, rdata+len, sz-len); } return len; } int print_rrs(FILE* out, struct zone* zone) { rrset_type *rrset; domain_type *domain = zone->apex; region_type* region = region_create(xalloc, free); region_type* rr_region = region_create(xalloc, free); buffer_type* rr_buffer = buffer_create(region, MAX_RDLENGTH); struct state_pretty_rr* state = create_pretty_rr(region); /* first print the SOA record for the zone */ if(zone->soa_rrset) { size_t i; for(i=0; i < zone->soa_rrset->rr_count; i++) { if(!print_rr(out, state, &zone->soa_rrset->rrs[i], rr_region, rr_buffer)){ log_msg(LOG_ERR, "There was an error " "printing SOARR to zone %s", zone->opts->name); region_destroy(region); region_destroy(rr_region); return 0; } } } /* go through entire tree below the zone apex (incl subzones) */ while(domain && domain_is_subdomain(domain, zone->apex)) { for(rrset = domain->rrsets; rrset; rrset=rrset->next) { size_t i; if(rrset->zone != zone || rrset == zone->soa_rrset) continue; for(i=0; i < rrset->rr_count; i++) { if(!print_rr(out, state, &rrset->rrs[i], rr_region, rr_buffer)){ log_msg(LOG_ERR, "There was an error " "printing RR to zone %s", zone->opts->name); region_destroy(region); region_destroy(rr_region); return 0; } } } domain = domain_next(domain); } region_destroy(region); region_destroy(rr_region); return 1; } static int print_header(zone_type* zone, FILE* out, time_t* now, const char* logs) { char buf[4096+16]; /* ctime prints newline at end of this line */ snprintf(buf, sizeof(buf), "; zone %s written by NSD %s on %s", zone->opts->name, PACKAGE_VERSION, ctime(now)); if(!write_data(out, buf, strlen(buf))) return 0; if(!logs || logs[0] == 0) return 1; snprintf(buf, sizeof(buf), "; %s\n", logs); return write_data(out, buf, strlen(buf)); } static int write_to_zonefile(zone_type* zone, const char* filename, const char* logs) { time_t now = time(0); FILE *out = fopen(filename, "w"); if(!out) { log_msg(LOG_ERR, "cannot write zone %s file %s: %s", zone->opts->name, filename, strerror(errno)); return 0; } if(!print_header(zone, out, &now, logs)) { fclose(out); log_msg(LOG_ERR, "There was an error printing " "the header to zone %s", zone->opts->name); return 0; } if(!print_rrs(out, zone)) { fclose(out); return 0; } if(fclose(out) != 0) { log_msg(LOG_ERR, "cannot write zone %s to file %s: fclose: %s", zone->opts->name, filename, strerror(errno)); return 0; } return 1; } /** create directories above this file, .../dir/dir/dir/file */ int create_dirs(const char* path) { char dir[4096]; char* p; strlcpy(dir, path, sizeof(dir)); /* if we start with / then do not try to create '' */ if(dir[0] == PATHSEP) p = strchr(dir+1, PATHSEP); else p = strchr(dir, PATHSEP); /* create each directory component from the left */ while(p) { assert(*p == PATHSEP); *p = 0; /* end the directory name here */ if(mkdir(dir #ifndef MKDIR_HAS_ONE_ARG , 0750 #endif ) == -1) { if(errno != EEXIST) { log_msg(LOG_ERR, "create dir %s: %s", dir, strerror(errno)); *p = PATHSEP; /* restore input string */ return 0; } /* it already exists, OK, continue */ } *p = PATHSEP; p = strchr(p+1, PATHSEP); } return 1; } /** create pathname components and check if file exists */ static int create_path_components(const char* path, int* notexist) { /* stat the file, to see if it exists, and if its directories exist */ struct stat s; if(stat(path, &s) != 0) { if(errno == ENOENT) { *notexist = 1; /* see if we need to create pathname components */ return create_dirs(path); } log_msg(LOG_ERR, "cannot stat %s: %s", path, strerror(errno)); return 0; } *notexist = 0; return 1; } void namedb_write_zonefile(struct nsd* nsd, struct zone_options* zopt) { const char* zfile; int notexist = 0; zone_type* zone; /* if no zone exists, it has no contents or it has no zonefile * configured, then no need to write data to disk */ if(!zopt->pattern->zonefile) return; zone = namedb_find_zone(nsd->db, (const dname_type*)zopt->node.key); if(!zone || !zone->apex || !zone->soa_rrset) return; /* write if file does not exist, or if changed */ /* so, determine filename, create directory components, check exist*/ zfile = config_make_zonefile(zopt, nsd); if(!create_path_components(zfile, ¬exist)) { log_msg(LOG_ERR, "could not write zone %s to file %s because " "the path could not be created", zopt->name, zfile); return; } /* if not changed, do not write. */ if(notexist || zone->is_changed) { char logs[4096]; char bakfile[4096]; struct timespec mtime; /* write to zfile~ first, then rename if that works */ snprintf(bakfile, sizeof(bakfile), "%s~", zfile); if(zone->logstr) strlcpy(logs, zone->logstr, sizeof(logs)); else logs[0] = 0; VERBOSITY(1, (LOG_INFO, "writing zone %s to file %s", zone->opts->name, zfile)); if(!write_to_zonefile(zone, bakfile, logs)) { (void)unlink(bakfile); /* delete failed file */ return; /* error already printed */ } if(rename(bakfile, zfile) == -1) { log_msg(LOG_ERR, "rename(%s to %s) failed: %s", bakfile, zfile, strerror(errno)); (void)unlink(bakfile); /* delete failed file */ return; } zone->is_changed = 0; /* fetch the mtime of the just created zonefile so we * do not waste effort reading it back in */ if(!file_get_mtime(zfile, &mtime, ¬exist)) { get_time(&mtime); } zone->mtime = mtime; if(zone->filename) region_recycle(nsd->db->region, zone->filename, strlen(zone->filename)+1); zone->filename = region_strdup(nsd->db->region, zfile); if(zone->logstr) region_recycle(nsd->db->region, zone->logstr, strlen(zone->logstr)+1); zone->logstr = NULL; if(zone_is_ixfr_enabled(zone) && zone->ixfr) ixfr_write_to_file(zone, zfile); } } void namedb_write_zonefiles(struct nsd* nsd, struct nsd_options* options) { struct zone_options* zo; RBTREE_FOR(zo, struct zone_options*, options->zone_options) { namedb_write_zonefile(nsd, zo); } }