285 lines
7.4 KiB
C
285 lines
7.4 KiB
C
/*
|
|
* 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 <sys/stat.h>
|
|
#include <sys/types.h>
|
|
#include <errno.h>
|
|
#include <fcntl.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <unistd.h>
|
|
|
|
#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; i<rr->rdata_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);
|
|
}
|
|
}
|