summaryrefslogtreecommitdiff
path: root/usr.sbin
diff options
context:
space:
mode:
authorBrad Smith <brad@cvs.openbsd.org>2014-09-16 16:54:07 +0000
committerBrad Smith <brad@cvs.openbsd.org>2014-09-16 16:54:07 +0000
commit2b306798f33a46a535e3b0f3ebdd05f3ad86f526 (patch)
tree89eaf541a39a437880bc8bea6dd6a370166c9242 /usr.sbin
parentb9bd487caa683ae9060dcce84b139082da8e3e4c (diff)
update to NSD 4.1.0, ok sthen@
Diffstat (limited to 'usr.sbin')
-rw-r--r--usr.sbin/nsd/compat/fake-rfc2553.c4
-rw-r--r--usr.sbin/nsd/configlexer.lex120
-rw-r--r--usr.sbin/nsd/dbaccess.c147
-rw-r--r--usr.sbin/nsd/dbcreate.c73
-rw-r--r--usr.sbin/nsd/difffile.c170
-rw-r--r--usr.sbin/nsd/ipc.c20
-rw-r--r--usr.sbin/nsd/namedb.h3
-rw-r--r--usr.sbin/nsd/nsd-checkzone.8.in33
-rw-r--r--usr.sbin/nsd/nsd-checkzone.c147
-rw-r--r--usr.sbin/nsd/nsd-control.8.in3
-rw-r--r--usr.sbin/nsd/nsd-mem.c23
-rw-r--r--usr.sbin/nsd/nsd.conf.sample.in16
-rw-r--r--usr.sbin/nsd/nsd.h4
-rw-r--r--usr.sbin/nsd/nsec3.h2
-rw-r--r--usr.sbin/nsd/options.c11
-rw-r--r--usr.sbin/nsd/options.h8
-rw-r--r--usr.sbin/nsd/packet.c21
-rw-r--r--usr.sbin/nsd/packet.h3
-rw-r--r--usr.sbin/nsd/remote.c26
-rw-r--r--usr.sbin/nsd/udb.c46
-rw-r--r--usr.sbin/nsd/udb.h21
-rw-r--r--usr.sbin/nsd/xfrd.h11
-rw-r--r--usr.sbin/nsd/zlexer.lex10
23 files changed, 749 insertions, 173 deletions
diff --git a/usr.sbin/nsd/compat/fake-rfc2553.c b/usr.sbin/nsd/compat/fake-rfc2553.c
index 91ddf8a809b..0f0f34f1fb2 100644
--- a/usr.sbin/nsd/compat/fake-rfc2553.c
+++ b/usr.sbin/nsd/compat/fake-rfc2553.c
@@ -120,12 +120,10 @@ addrinfo *malloc_ai(int port, u_long addr, const struct addrinfo *hints)
{
struct addrinfo *ai;
- ai = malloc(sizeof(*ai) + sizeof(struct sockaddr_in));
+ ai = calloc(1, sizeof(*ai) + sizeof(struct sockaddr_in));
if (ai == NULL)
return (NULL);
- memset(ai, '\0', sizeof(*ai) + sizeof(struct sockaddr_in));
-
ai->ai_addr = (struct sockaddr *)(ai + 1);
/* XXX -- ssh doesn't use sa_len */
ai->ai_addrlen = sizeof(struct sockaddr_in);
diff --git a/usr.sbin/nsd/configlexer.lex b/usr.sbin/nsd/configlexer.lex
index c9f71fc38f4..87b96b3c3e7 100644
--- a/usr.sbin/nsd/configlexer.lex
+++ b/usr.sbin/nsd/configlexer.lex
@@ -14,6 +14,9 @@
#include <errno.h>
#include <string.h>
#include <strings.h>
+#ifdef HAVE_GLOB_H
+# include <glob.h>
+#endif
#include "options.h"
#include "configyyrename.h"
@@ -27,22 +30,40 @@ void c_error(const char *message);
#endif
struct inc_state {
- const char* filename;
+ char* filename;
int line;
+ YY_BUFFER_STATE buffer;
+ struct inc_state* next;
};
-static struct inc_state parse_stack[MAXINCLUDES];
-static YY_BUFFER_STATE include_stack[MAXINCLUDES];
-static int config_include_stack_ptr = 0;
+static struct inc_state* config_include_stack = NULL;
+static int inc_depth = 0;
+static int inc_prev = 0;
+static int num_args = 0;
+
+void init_cfg_parse(void)
+{
+ config_include_stack = NULL;
+ inc_depth = 0;
+ inc_prev = 0;
+ num_args = 0;
+}
static void config_start_include(const char* filename)
{
FILE *input;
+ struct inc_state* s;
+ char* nm;
+ if(inc_depth++ > 10000000) {
+ c_error_msg("too many include files");
+ return;
+ }
if(strlen(filename) == 0) {
c_error_msg("empty include file name");
return;
}
- if(config_include_stack_ptr >= MAXINCLUDES) {
- c_error_msg("includes nested too deeply, skipped (>%d)", MAXINCLUDES);
+ s = (struct inc_state*)malloc(sizeof(*s));
+ if(!s) {
+ c_error_msg("include %s: malloc failure", filename);
return;
}
if (cfg_parser->chroot) {
@@ -54,29 +75,89 @@ static void config_start_include(const char* filename)
}
filename += l - 1; /* strip chroot without trailing slash */
}
+ nm = strdup(filename);
+ if(!nm) {
+ c_error_msg("include %s: strdup failure", filename);
+ free(s);
+ return;
+ }
input = fopen(filename, "r");
if(!input) {
c_error_msg("cannot open include file '%s': %s",
filename, strerror(errno));
+ free(s);
+ free(nm);
return;
}
LEXOUT(("switch_to_include_file(%s) ", filename));
- parse_stack[config_include_stack_ptr].filename = cfg_parser->filename;
- parse_stack[config_include_stack_ptr].line = cfg_parser->line;
- include_stack[config_include_stack_ptr] = YY_CURRENT_BUFFER;
- cfg_parser->filename = region_strdup(cfg_parser->opt->region, filename);
+ s->filename = cfg_parser->filename;
+ s->line = cfg_parser->line;
+ s->buffer = YY_CURRENT_BUFFER;
+ s->next = config_include_stack;
+ config_include_stack = s;
+
+ cfg_parser->filename = nm;
cfg_parser->line = 1;
yy_switch_to_buffer(yy_create_buffer(input, YY_BUF_SIZE));
- ++config_include_stack_ptr;
+}
+
+static void config_start_include_glob(const char* filename)
+{
+ /* check for wildcards */
+#ifdef HAVE_GLOB
+ glob_t g;
+ size_t i;
+ int r, flags;
+ if(!(!strchr(filename, '*') && !strchr(filename, '?') &&
+ !strchr(filename, '[') && !strchr(filename, '{') &&
+ !strchr(filename, '~'))) {
+ flags = 0
+#ifdef GLOB_ERR
+ | GLOB_ERR
+#endif
+#ifdef GLOB_NOSORT
+ | GLOB_NOSORT
+#endif
+#ifdef GLOB_BRACE
+ | GLOB_BRACE
+#endif
+#ifdef GLOB_TILDE
+ | GLOB_TILDE
+#endif
+ ;
+ memset(&g, 0, sizeof(g));
+ r = glob(filename, flags, NULL, &g);
+ if(r) {
+ /* some error */
+ globfree(&g);
+ if(r == GLOB_NOMATCH)
+ return; /* no matches for pattern */
+ config_start_include(filename); /* let original deal with it */
+ return;
+ }
+ /* process files found, if any */
+ for(i=0; i<(size_t)g.gl_pathc; i++) {
+ config_start_include(g.gl_pathv[i]);
+ }
+ globfree(&g);
+ return;
+ }
+#endif /* HAVE_GLOB */
+ config_start_include(filename);
}
static void config_end_include(void)
{
- --config_include_stack_ptr;
- cfg_parser->filename = parse_stack[config_include_stack_ptr].filename;
- cfg_parser->line = parse_stack[config_include_stack_ptr].line;
+ struct inc_state* s = config_include_stack;
+ --inc_depth;
+ if(!s) return;
+ free(cfg_parser->filename);
+ cfg_parser->filename = s->filename;
+ cfg_parser->line = s->line;
yy_delete_buffer(YY_CURRENT_BUFFER);
- yy_switch_to_buffer(include_stack[config_include_stack_ptr]);
+ yy_switch_to_buffer(s->buffer);
+ config_include_stack = s->next;
+ free(s);
}
#ifndef yy_set_bol /* compat definition, for flex 2.4.6 */
@@ -178,6 +259,9 @@ rrl-ipv6-prefix-length{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_RRL_IPV6_
rrl-whitelist-ratelimit{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_RRL_WHITELIST_RATELIMIT;}
rrl-whitelist{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_RRL_WHITELIST;}
zonefiles-check{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_ZONEFILES_CHECK;}
+zonefiles-write{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_ZONEFILES_WRITE;}
+log-time-ascii{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_LOG_TIME_ASCII;}
+round-robin{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_ROUND_ROBIN;}
{NEWLINE} { LEXOUT(("NL\n")); cfg_parser->line++;}
/* Quoted strings. Strip leading and ending quotes */
@@ -207,7 +291,7 @@ include{COLON} { LEXOUT(("v(%s) ", yytext)); BEGIN(include); }
<include>\" { LEXOUT(("IQS ")); BEGIN(include_quoted); }
<include>{UNQUOTEDLETTER}* {
LEXOUT(("Iunquotedstr(%s) ", yytext));
- config_start_include(yytext);
+ config_start_include_glob(yytext);
BEGIN(INITIAL);
}
<include_quoted><<EOF>> {
@@ -219,12 +303,12 @@ include{COLON} { LEXOUT(("v(%s) ", yytext)); BEGIN(include); }
<include_quoted>\" {
LEXOUT(("IQE "));
yytext[yyleng - 1] = '\0';
- config_start_include(yytext);
+ config_start_include_glob(yytext);
BEGIN(INITIAL);
}
<INITIAL><<EOF>> {
yy_set_bol(1); /* Set beginning of line, so "^" rules match. */
- if (config_include_stack_ptr == 0) {
+ if (!config_include_stack) {
yyterminate();
} else {
fclose(yyin);
diff --git a/usr.sbin/nsd/dbaccess.c b/usr.sbin/nsd/dbaccess.c
index 4ecbf7a990b..3a60d517665 100644
--- a/usr.sbin/nsd/dbaccess.c
+++ b/usr.sbin/nsd/dbaccess.c
@@ -32,8 +32,8 @@
#include "nsd.h"
static time_t udb_time = 0;
-static unsigned udb_rrsets = 0;
-static unsigned udb_rrset_count = 0;
+static unsigned long udb_rrsets = 0;
+static unsigned long udb_rrset_count = 0;
void
namedb_close(struct namedb* db)
@@ -193,7 +193,8 @@ static void read_node_elem(udb_base* udb, namedb_type* db,
if(++udb_rrsets % ZONEC_PCT_COUNT == 0 && time(NULL) > udb_time + ZONEC_PCT_TIME) {
udb_time = time(NULL);
VERBOSITY(1, (LOG_INFO, "read %s %d %%",
- zone->opts->name, udb_rrsets*100/udb_rrset_count));
+ zone->opts->name,
+ (int)(udb_rrsets*((unsigned long)100)/udb_rrset_count)));
}
}
region_free_all(dname_region);
@@ -268,6 +269,9 @@ namedb_zone_create(namedb_type* db, const dname_type* dname,
zone->dshashtree = NULL;
#endif
zone->opts = zo;
+ zone->filename = NULL;
+ zone->logstr = NULL;
+ zone->mtime = 0;
zone->is_secure = 0;
zone->is_changed = 0;
zone->is_ok = 1;
@@ -302,9 +306,16 @@ namedb_zone_delete(namedb_type* db, zone_type* zone)
hash_tree_delete(db->region, zone->wchashtree);
hash_tree_delete(db->region, zone->dshashtree);
#endif
+ 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));
}
+#ifdef HAVE_MMAP
/** read a zone */
static void
read_zone(udb_base* udb, namedb_type* db, nsd_options_t* opt,
@@ -334,7 +345,9 @@ read_zone(udb_base* udb, namedb_type* db, nsd_options_t* opt,
prehash_zone_complete(db, zone);
#endif
}
+#endif /* HAVE_MMAP */
+#ifdef HAVE_MMAP
/** read zones from nsd.db */
static void
read_zones(udb_base* udb, namedb_type* db, nsd_options_t* opt,
@@ -350,12 +363,15 @@ read_zones(udb_base* udb, namedb_type* db, nsd_options_t* opt,
udb_radix_next(udb, &n); /* store in case n is deleted */
read_zone(udb, db, opt, dname_region, &z);
udb_ptr_zero(&z, udb);
+ if(nsd.signal_hint_shutdown) break;
}
udb_ptr_unlink(&ztree, udb);
udb_ptr_unlink(&n, udb);
udb_ptr_unlink(&z, udb);
}
+#endif /* HAVE_MMAP */
+#ifdef HAVE_MMAP
/** try to read the udb file or fail */
static int
try_read_udb(namedb_type* db, int fd, const char* filename,
@@ -392,6 +408,7 @@ try_read_udb(namedb_type* db, int fd, const char* filename,
region_destroy(dname_region);
return 1;
}
+#endif /* HAVE_MMAP */
struct namedb *
namedb_open (const char* filename, nsd_options_t* opt)
@@ -405,15 +422,6 @@ namedb_open (const char* filename, nsd_options_t* opt)
region_type* db_region;
int fd;
- /* attempt to open, if does not exist, create a new one */
- fd = open(filename, O_RDWR);
- if(fd == -1) {
- if(errno != ENOENT) {
- log_msg(LOG_ERR, "%s: %s", filename, strerror(errno));
- return NULL;
- }
- }
-
#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);
@@ -427,15 +435,38 @@ namedb_open (const char* filename, nsd_options_t* opt)
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 %s: cannot initialize"
"timestamp", filename);
region_destroy(db_region);
- close(fd);
return NULL;
}
+ /* in dbless mode there is no file to read or mmap */
+ if(filename == NULL || filename[0] == 0) {
+ db->udb = NULL;
+ return db;
+ }
+
+#ifndef HAVE_MMAP
+ /* no mmap() system call, use dbless mode */
+ VERBOSITY(1, (LOG_INFO, "no mmap(), ignoring database %s", filename));
+ db->udb = NULL;
+ (void)fd; (void)opt;
+ return db;
+#else /* HAVE_MMAP */
+
+ /* attempt to open, if does not exist, create a new one */
+ fd = open(filename, O_RDWR);
+ if(fd == -1) {
+ if(errno != ENOENT) {
+ log_msg(LOG_ERR, "%s: %s", filename, strerror(errno));
+ region_destroy(db_region);
+ return NULL;
+ }
+ }
/* attempt to read the file (if it exists) */
if(fd != -1) {
if(!try_read_udb(db, fd, filename, opt))
@@ -453,8 +484,8 @@ namedb_open (const char* filename, nsd_options_t* opt)
return NULL;
}
}
- zonec_setup_parser(db);
return db;
+#endif /* HAVE_MMAP */
}
/** the the file mtime stat (or nonexist or error) */
@@ -480,7 +511,7 @@ namedb_read_zonefile(struct nsd* nsd, struct zone* zone, udb_base* taskudb,
int nonexist = 0;
unsigned int errors;
const char* fname;
- if(!nsd->db || !nsd->db->udb || !zone || !zone->opts || !zone->opts->pattern->zonefile)
+ if(!nsd->db || !zone || !zone->opts || !zone->opts->pattern->zonefile)
return;
fname = config_make_zonefile(zone->opts, nsd);
if(!file_get_mtime(fname, &mtime, &nonexist)) {
@@ -493,16 +524,21 @@ namedb_read_zonefile(struct nsd* nsd, struct zone* zone, udb_base* taskudb,
if(taskudb) task_new_soainfo(taskudb, last_task, zone, 0);
return;
} else {
- const char* zone_fname = udb_zone_get_file_str(nsd->db->udb,
- dname_name(domain_dname(zone->apex)), domain_dname(
- zone->apex)->name_size);
+ const char* zone_fname = zone->filename;
+ time_t zone_mtime = zone->mtime;
+ if(nsd->db->udb) {
+ zone_fname = udb_zone_get_file_str(nsd->db->udb,
+ dname_name(domain_dname(zone->apex)),
+ domain_dname(zone->apex)->name_size);
+ zone_mtime = (time_t)udb_zone_get_mtime(nsd->db->udb,
+ dname_name(domain_dname(zone->apex)),
+ domain_dname(zone->apex)->name_size);
+ }
/* 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 && udb_zone_get_mtime(nsd->db->udb,
- dname_name(domain_dname(zone->apex)), domain_dname(
- zone->apex)->name_size) >= (uint64_t)mtime) {
+ if(!zone_fname && zone_mtime >= mtime) {
VERBOSITY(3, (LOG_INFO, "zonefile %s is older than "
"zone transfer in memory", fname));
return;
@@ -510,10 +546,7 @@ namedb_read_zonefile(struct nsd* nsd, struct zone* zone, udb_base* taskudb,
/* 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 && fname &&
- strcmp(zone_fname, fname) == 0 &&
- udb_zone_get_mtime(nsd->db->udb, dname_name(domain_dname(
- zone->apex)), domain_dname(zone->apex)->name_size)
- >= (uint64_t)mtime) {
+ strcmp(zone_fname, fname) == 0 && zone_mtime >= mtime) {
VERBOSITY(3, (LOG_INFO, "zonefile %s is not modified",
fname));
return;
@@ -532,8 +565,6 @@ namedb_read_zonefile(struct nsd* nsd, struct zone* zone, udb_base* taskudb,
#endif /* NSEC3 */
errors = zonec_read(zone->opts->name, fname, zone);
if(errors > 0) {
- region_type* dname_region;
- udb_ptr z;
log_msg(LOG_ERR, "zone %s file %s read with %u errors",
zone->opts->name, fname, errors);
/* wipe (partial) zone from memory */
@@ -546,32 +577,57 @@ namedb_read_zonefile(struct nsd* nsd, struct zone* zone, udb_base* taskudb,
nsec3_clear_precompile(nsd->db, zone);
zone->nsec3_param = NULL;
#endif /* NSEC3 */
- /* see if we can revert to the udb stored version */
- if(!udb_zone_search(nsd->db->udb, &z, dname_name(domain_dname(
- zone->apex)), domain_dname(zone->apex)->name_size)) {
- /* tell that zone contents has been lost */
- if(taskudb) task_new_soainfo(taskudb, last_task, zone, 0);
- return;
+ if(nsd->db->udb) {
+ region_type* dname_region;
+ udb_ptr z;
+ /* see if we can revert to the udb stored version */
+ if(!udb_zone_search(nsd->db->udb, &z, dname_name(domain_dname(
+ zone->apex)), domain_dname(zone->apex)->name_size)) {
+ /* tell that zone contents has been lost */
+ if(taskudb) task_new_soainfo(taskudb, last_task, zone, 0);
+ return;
+ }
+ /* read from udb */
+ dname_region = region_create(xalloc, free);
+ udb_rrsets = 0;
+ udb_rrset_count = ZONE(&z)->rrset_count;
+ udb_time = time(NULL);
+ read_zone_data(nsd->db->udb, nsd->db, dname_region, &z, zone);
+ region_destroy(dname_region);
+ udb_ptr_unlink(&z, nsd->db->udb);
+ } else {
+ 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;
}
- /* read from udb */
- dname_region = region_create(xalloc, free);
- udb_rrsets = 0;
- udb_rrset_count = ZONE(&z)->rrset_count;
- udb_time = time(NULL);
- read_zone_data(nsd->db->udb, nsd->db, dname_region, &z, zone);
- region_destroy(dname_region);
- udb_ptr_unlink(&z, nsd->db->udb);
} else {
VERBOSITY(1, (LOG_INFO, "zone %s read with no errors",
zone->opts->name));
zone->is_ok = 1;
zone->is_changed = 0;
/* store zone into udb */
- if(!write_zone_to_udb(nsd->db->udb, zone, mtime, fname)) {
- log_msg(LOG_ERR, "failed to store zone in db");
+ if(nsd->db->udb) {
+ if(!write_zone_to_udb(nsd->db->udb, zone, mtime, fname)) {
+ log_msg(LOG_ERR, "failed to store zone in db");
+ } else {
+ VERBOSITY(2, (LOG_INFO, "zone %s written to db",
+ zone->opts->name));
+ }
} else {
- VERBOSITY(2, (LOG_INFO, "zone %s written to db",
- zone->opts->name));
+ 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(taskudb) task_new_soainfo(taskudb, last_task, zone, 0);
@@ -600,5 +656,6 @@ void namedb_check_zonefiles(struct nsd* nsd, nsd_options_t* opt,
/* check all zones in opt, create if not exist in main db */
RBTREE_FOR(zo, zone_options_t*, opt->zone_options) {
namedb_check_zonefile(nsd, taskudb, last_task, zo);
+ if(nsd->signal_hint_shutdown) break;
}
}
diff --git a/usr.sbin/nsd/dbcreate.c b/usr.sbin/nsd/dbcreate.c
index 92f3976e4bc..24866af774b 100644
--- a/usr.sbin/nsd/dbcreate.c
+++ b/usr.sbin/nsd/dbcreate.c
@@ -116,7 +116,7 @@ write_zone(udb_base* udb, udb_ptr* z, zone_type* zone)
/* write all domains in the zone */
domain_type* walk;
rrset_type* rrset;
- int n = 0, c = 0;
+ unsigned long n = 0, c = 0;
time_t t = time(NULL);
/* count domains: for pct logging */
@@ -138,7 +138,7 @@ write_zone(udb_base* udb, udb_ptr* z, zone_type* zone)
if(++c % ZONEC_PCT_COUNT == 0 && time(NULL) > t + ZONEC_PCT_TIME) {
t = time(NULL);
VERBOSITY(1, (LOG_INFO, "write %s %d %%",
- zone->opts->name, c*100/n));
+ zone->opts->name, (int)(c*((unsigned long)100)/n)));
}
}
return 1;
@@ -185,16 +185,20 @@ 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])){
+ 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;
}
}
@@ -208,11 +212,13 @@ print_rrs(FILE* out, struct zone* zone)
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])){
+ 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;
}
}
@@ -220,6 +226,7 @@ print_rrs(FILE* out, struct zone* zone)
domain = domain_next(domain);
}
region_destroy(region);
+ region_destroy(rr_region);
return 1;
}
@@ -241,11 +248,7 @@ static int
write_to_zonefile(zone_type* zone, const char* filename, const char* logs)
{
time_t now = time(0);
- FILE *out;
- VERBOSITY(1, (LOG_INFO, "writing zone %s to file %s",
- zone->opts->name, filename));
-
- out = fopen(filename, "w");
+ FILE *out = fopen(filename, "w");
if(!out) {
log_msg(LOG_ERR, "cannot write zone %s file %s: %s",
zone->opts->name, filename, strerror(errno));
@@ -261,7 +264,11 @@ write_to_zonefile(zone_type* zone, const char* filename, const char* logs)
fclose(out);
return 0;
}
- fclose(out);
+ 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;
}
@@ -328,7 +335,7 @@ namedb_write_zonefile(struct nsd* nsd, zone_options_t* zopt)
if(!zopt->pattern->zonefile)
return;
zone = namedb_find_zone(nsd->db, (const dname_type*)zopt->node.key);
- if(!zone || !zone->apex)
+ 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*/
@@ -344,33 +351,55 @@ namedb_write_zonefile(struct nsd* nsd, zone_options_t* zopt)
char logs[4096];
char bakfile[4096];
udb_ptr zudb;
- if(!udb_zone_search(nsd->db->udb, &zudb,
- dname_name(domain_dname(zone->apex)),
- domain_dname(zone->apex)->name_size))
- return; /* zone does not exist in db */
+ if(nsd->db->udb) {
+ if(!udb_zone_search(nsd->db->udb, &zudb,
+ dname_name(domain_dname(zone->apex)),
+ domain_dname(zone->apex)->name_size))
+ return; /* zone does not exist in db */
+ }
/* write to zfile~ first, then rename if that works */
snprintf(bakfile, sizeof(bakfile), "%s~", zfile);
- if(ZONE(&zudb)->log_str.data) {
+ if(nsd->db->udb && ZONE(&zudb)->log_str.data) {
udb_ptr s;
udb_ptr_new(&s, nsd->db->udb, &ZONE(&zudb)->log_str);
strlcpy(logs, (char*)udb_ptr_data(&s), sizeof(logs));
udb_ptr_unlink(&s, nsd->db->udb);
+ } else 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)) {
- udb_ptr_unlink(&zudb, nsd->db->udb);
+ if(nsd->db->udb)
+ udb_ptr_unlink(&zudb, nsd->db->udb);
+ (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));
- udb_ptr_unlink(&zudb, nsd->db->udb);
+ if(nsd->db->udb)
+ udb_ptr_unlink(&zudb, nsd->db->udb);
+ (void)unlink(bakfile); /* delete failed file */
return;
}
zone->is_changed = 0;
- ZONE(&zudb)->mtime = (uint64_t)time(0);
- ZONE(&zudb)->is_changed = 0;
- udb_zone_set_log_str(nsd->db->udb, &zudb, NULL);
- udb_ptr_unlink(&zudb, nsd->db->udb);
+ if(nsd->db->udb) {
+ ZONE(&zudb)->mtime = (uint64_t)time(0);
+ ZONE(&zudb)->is_changed = 0;
+ udb_zone_set_log_str(nsd->db->udb, &zudb, NULL);
+ udb_ptr_unlink(&zudb, nsd->db->udb);
+ } else {
+ zone->mtime = time(0);
+ 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;
+ }
}
}
diff --git a/usr.sbin/nsd/difffile.c b/usr.sbin/nsd/difffile.c
index 5b9bd789845..5510714f364 100644
--- a/usr.sbin/nsd/difffile.c
+++ b/usr.sbin/nsd/difffile.c
@@ -235,6 +235,28 @@ has_data_below(domain_type* top)
return 0;
}
+/** check if domain with 0 rrsets has become empty (nonexist) */
+static void
+rrset_zero_nonexist_check(domain_type* domain)
+{
+ /* is the node now an empty node (completely deleted) */
+ if(domain->rrsets == 0) {
+ /* if there is no data below it, it becomes non existing.
+ also empty nonterminals above it become nonexisting */
+ /* check for data below this node. */
+ if(!has_data_below(domain)) {
+ /* nonexist this domain and all parent empty nonterminals */
+ domain_type* p = domain;
+ while(p != NULL && p->rrsets == 0) {
+ if(has_data_below(p))
+ break;
+ p->is_existing = 0;
+ p = p->parent;
+ }
+ }
+ }
+}
+
/** remove rrset. Adjusts zone params. Does not remove domain */
static void
rrset_delete(namedb_type* db, domain_type* domain, rrset_type* rrset)
@@ -277,23 +299,6 @@ rrset_delete(namedb_type* db, domain_type* domain, rrset_type* rrset)
sizeof(rr_type) * rrset->rr_count);
rrset->rr_count = 0;
region_recycle(db->region, rrset, sizeof(rrset_type));
-
- /* is the node now an empty node (completely deleted) */
- if(domain->rrsets == 0) {
- /* if there is no data below it, it becomes non existing.
- also empty nonterminals above it become nonexisting */
- /* check for data below this node. */
- if(!has_data_below(domain)) {
- /* nonexist this domain and all parent empty nonterminals */
- domain_type* p = domain;
- while(p != NULL && p->rrsets == 0) {
- if(has_data_below(p))
- break;
- p->is_existing = 0;
- p = p->parent;
- }
- }
- }
}
static int
@@ -303,9 +308,13 @@ rdatas_equal(rdata_atom_type *a, rdata_atom_type *b, int num, uint16_t type,
int k, start, end;
start = 0;
end = num;
+ /**
+ * SOA RDATA comparisons in XFR are more lenient,
+ * only serial rdata is checked.
+ **/
if (type == TYPE_SOA) {
start = 2;
- end = 2;
+ end = 3;
}
for(k = start; k < end; k++)
{
@@ -447,8 +456,8 @@ nsec3_delete_rr_trigger(namedb_type* db, rr_type* rr, zone_type* zone,
else if(rr->type == TYPE_NSEC3PARAM && rr == zone->nsec3_param) {
/* clear trees, wipe hashes, wipe precompile */
nsec3_clear_precompile(db, zone);
- /* pick up new nsec3param from udb */
- nsec3_find_zone_param(db, zone, udbz);
+ /* pick up new nsec3param (from udb, or avoid deleted rr) */
+ nsec3_find_zone_param(db, zone, udbz, rr);
/* if no more NSEC3, done */
if(!zone->nsec3_param)
return;
@@ -550,7 +559,7 @@ nsec3_add_rr_trigger(namedb_type* db, rr_type* rr, zone_type* zone,
prehash_add(db->domains, rr->owner);
} else if(!zone->nsec3_param && rr->type == TYPE_NSEC3PARAM) {
/* see if this means NSEC3 chain can be used */
- nsec3_find_zone_param(db, zone, udbz);
+ nsec3_find_zone_param(db, zone, udbz, NULL);
if(!zone->nsec3_param)
return;
nsec3_zone_trees_create(db->region, zone);
@@ -660,7 +669,8 @@ delete_RR(namedb_type* db, const dname_type* dname,
return 1; /* not fatal error */
}
/* delete the normalized RR from the udb */
- udb_del_rr(db->udb, udbz, &rrset->rrs[rrnum]);
+ if(db->udb)
+ udb_del_rr(db->udb, udbz, &rrset->rrs[rrnum]);
#ifdef NSEC3
/* process triggers for RR deletions */
nsec3_delete_rr_trigger(db, &rrset->rrs[rrnum], zone, udbz);
@@ -671,6 +681,8 @@ delete_RR(namedb_type* db, const dname_type* dname,
if(rrset->rr_count == 1) {
/* delete entire rrset */
rrset_delete(db, domain, rrset);
+ /* check if domain is now nonexisting (or parents) */
+ rrset_zero_nonexist_check(domain);
#ifdef NSEC3
/* cleanup nsec3 */
nsec3_delete_rrset_trigger(db, domain, zone, type);
@@ -812,9 +824,11 @@ add_RR(namedb_type* db, const dname_type* dname,
}
/* write the just-normalized RR to the udb */
- if(!udb_write_rr(db->udb, udbz, &rrset->rrs[rrset->rr_count - 1])) {
- log_msg(LOG_ERR, "could not add RR to nsd.db, disk-space?");
- return 0;
+ if(db->udb) {
+ if(!udb_write_rr(db->udb, udbz, &rrset->rrs[rrset->rr_count - 1])) {
+ log_msg(LOG_ERR, "could not add RR to nsd.db, disk-space?");
+ return 0;
+ }
}
#ifdef NSEC3
if(rrset_added) {
@@ -876,6 +890,7 @@ delete_zone_rrs(namedb_type* db, zone_type* zone)
{
rrset_type *rrset;
domain_type *domain = zone->apex, *next;
+ int nonexist_check = 0;
/* go through entire tree below the zone apex (incl subzones) */
while(domain && domain_is_subdomain(domain, zone->apex))
{
@@ -887,6 +902,9 @@ delete_zone_rrs(namedb_type* db, zone_type* zone)
rrset_lower_usage(db, rrset);
/* rrset del does not delete our domain(yet) */
rrset_delete(db, domain, rrset);
+ /* no rrset_zero_nonexist_check, do that later */
+ if(domain->rrsets == 0)
+ nonexist_check = 1;
}
/* the delete upcoming could delete parents, but nothing next
* or after the domain so store next ptr */
@@ -896,10 +914,27 @@ delete_zone_rrs(namedb_type* db, zone_type* zone)
domain = next;
}
+ /* check if data deleteions have created nonexisting domain entries,
+ * but after deleting domains so the checks are faster */
+ if(nonexist_check) {
+ DEBUG(DEBUG_XFRD, 1, (LOG_INFO, "axfrdel: zero rrset check"));
+ domain = zone->apex;
+ while(domain && domain_is_subdomain(domain, zone->apex))
+ {
+ /* the interesting domains should be existing==1
+ * and rrsets==0, speeding up out processing of
+ * sub-zones, since we only spuriously check empty
+ * nonterminals */
+ if(domain->is_existing)
+ rrset_zero_nonexist_check(domain);
+ domain = domain_next(domain);
+ }
+ }
+
DEBUG(DEBUG_XFRD, 1, (LOG_INFO, "axfrdel: recyclebin holds %lu bytes",
(unsigned long) region_get_recycle_size(db->region)));
#ifndef NDEBUG
- if(nsd_debug_level >= 1)
+ if(nsd_debug_level >= 2)
region_log_stats(db->region);
#endif
@@ -1087,7 +1122,8 @@ apply_ixfr(namedb_type* db, FILE *in, const char* zone, uint32_t serialno,
nsec3_hash_tree_clear(zone_db);
#endif
delete_zone_rrs(db, zone_db);
- udb_zone_clear(db->udb, udbz);
+ if(db->udb)
+ udb_zone_clear(db->udb, udbz);
#ifdef NSEC3
nsec3_clear_precompile(db, zone_db);
zone_db->nsec3_param = NULL;
@@ -1117,7 +1153,8 @@ apply_ixfr(namedb_type* db, FILE *in, const char* zone, uint32_t serialno,
nsec3_hash_tree_clear(zone_db);
#endif
delete_zone_rrs(db, zone_db);
- udb_zone_clear(db->udb, udbz);
+ if(db->udb)
+ udb_zone_clear(db->udb, udbz);
#ifdef NSEC3
nsec3_clear_precompile(db, zone_db);
zone_db->nsec3_param = NULL;
@@ -1277,33 +1314,35 @@ apply_ixfr_for_zone(nsd_type* nsd, zone_type* zonedb, FILE* in,
udb_ptr z;
DEBUG(DEBUG_XFRD,1, (LOG_INFO, "processing xfr: %s", zone_buf));
- if(udb_base_get_userflags(nsd->db->udb) != 0) {
- log_msg(LOG_ERR, "database corrupted, cannot update");
- xfrd_unlink_xfrfile(nsd, xfrfilenr);
- exit(1);
- }
- /* all parts were checked by xfrd before commit */
- if(!udb_zone_search(nsd->db->udb, &z, dname_name(apex),
- apex->name_size)) {
- /* create it */
- if(!udb_zone_create(nsd->db->udb, &z, dname_name(apex),
+ if(nsd->db->udb) {
+ if(udb_base_get_userflags(nsd->db->udb) != 0) {
+ log_msg(LOG_ERR, "database corrupted, cannot update");
+ xfrd_unlink_xfrfile(nsd, xfrfilenr);
+ exit(1);
+ }
+ /* all parts were checked by xfrd before commit */
+ if(!udb_zone_search(nsd->db->udb, &z, dname_name(apex),
apex->name_size)) {
- /* out of disk space perhaps */
- log_msg(LOG_ERR, "could not udb_create_zone "
- "%s, disk space full?", log_buf);
- return 0;
+ /* create it */
+ if(!udb_zone_create(nsd->db->udb, &z, dname_name(apex),
+ apex->name_size)) {
+ /* out of disk space perhaps */
+ log_msg(LOG_ERR, "could not udb_create_zone "
+ "%s, disk space full?", log_buf);
+ return 0;
+ }
}
+ /* set the udb dirty until we are finished applying changes */
+ udb_base_set_userflags(nsd->db->udb, 1);
}
- /* set the udb dirty until we are finished applying changes */
- udb_base_set_userflags(nsd->db->udb, 1);
/* read and apply all of the parts */
for(i=0; i<num_parts; i++) {
int ret;
DEBUG(DEBUG_XFRD,2, (LOG_INFO, "processing xfr: apply part %d", (int)i));
ret = apply_ixfr(nsd->db, in, zone_buf, new_serial, opt,
i, num_parts, &is_axfr, &delete_mode,
- &rr_count, &z, &zonedb, patname_buf, &num_bytes,
- &softfail);
+ &rr_count, (nsd->db->udb?&z:NULL), &zonedb,
+ patname_buf, &num_bytes, &softfail);
if(ret == 0) {
log_msg(LOG_ERR, "bad ixfr packet part %d in diff file for %s", (int)i, zone_buf);
xfrd_unlink_xfrfile(nsd, xfrfilenr);
@@ -1313,7 +1352,8 @@ apply_ixfr_for_zone(nsd_type* nsd, zone_type* zonedb, FILE* in,
break;
}
}
- udb_base_set_userflags(nsd->db->udb, 0);
+ if(nsd->db->udb)
+ udb_base_set_userflags(nsd->db->udb, 0);
/* read the final log_str: but do not fail on it */
if(!diff_read_str(in, log_buf, sizeof(log_buf))) {
log_msg(LOG_ERR, "could not read log for transfer %s",
@@ -1324,11 +1364,23 @@ apply_ixfr_for_zone(nsd_type* nsd, zone_type* zonedb, FILE* in,
if(zonedb) prehash_zone(nsd->db, zonedb);
#endif /* NSEC3 */
zonedb->is_changed = 1;
- ZONE(&z)->is_changed = 1;
- ZONE(&z)->mtime = time_end_0;
- udb_zone_set_log_str(nsd->db->udb, &z, log_buf);
- udb_zone_set_file_str(nsd->db->udb, &z, NULL);
- udb_ptr_unlink(&z, nsd->db->udb);
+ if(nsd->db->udb) {
+ ZONE(&z)->is_changed = 1;
+ ZONE(&z)->mtime = time_end_0;
+ udb_zone_set_log_str(nsd->db->udb, &z, log_buf);
+ udb_zone_set_file_str(nsd->db->udb, &z, NULL);
+ udb_ptr_unlink(&z, nsd->db->udb);
+ } else {
+ zonedb->mtime = time_end_0;
+ if(zonedb->logstr)
+ region_recycle(nsd->db->region, zonedb->logstr,
+ strlen(zonedb->logstr)+1);
+ zonedb->logstr = region_strdup(nsd->db->region, log_buf);
+ if(zonedb->filename)
+ region_recycle(nsd->db->region, zonedb->filename,
+ strlen(zonedb->filename)+1);
+ zonedb->filename = NULL;
+ }
if(softfail && taskudb && !is_axfr) {
log_msg(LOG_ERR, "Failed to apply IXFR cleanly "
"(deletes nonexistent RRs, adds existing RRs). "
@@ -1809,7 +1861,6 @@ task_process_add_zone(struct nsd* nsd, udb_base* udb, udb_ptr* last_task,
static void
task_process_del_zone(struct nsd* nsd, struct task_list_d* task)
{
- udb_ptr udbz;
zone_type* zone;
zone_options_t* zopt;
DEBUG(DEBUG_IPC,1, (LOG_INFO, "delzone task %s", dname_to_string(
@@ -1822,10 +1873,13 @@ task_process_del_zone(struct nsd* nsd, struct task_list_d* task)
nsec3_hash_tree_clear(zone);
#endif
delete_zone_rrs(nsd->db, zone);
- if(udb_zone_search(nsd->db->udb, &udbz, dname_name(task->zname),
- task->zname->name_size)) {
- udb_zone_delete(nsd->db->udb, &udbz);
- udb_ptr_unlink(&udbz, nsd->db->udb);
+ if(nsd->db->udb) {
+ udb_ptr udbz;
+ if(udb_zone_search(nsd->db->udb, &udbz, dname_name(task->zname),
+ task->zname->name_size)) {
+ udb_zone_delete(nsd->db->udb, &udbz);
+ udb_ptr_unlink(&udbz, nsd->db->udb);
+ }
}
#ifdef NSEC3
nsec3_clear_precompile(nsd->db, zone);
@@ -1910,6 +1964,7 @@ task_process_apply_xfr(struct nsd* nsd, udb_base* udb, udb_ptr *last_task,
if(!zone) {
/* assume the zone has been deleted and a zone transfer was
* still waiting to be processed */
+ xfrd_unlink_xfrfile(nsd, TASKLIST(task)->yesno);
return;
}
/* apply the XFR */
@@ -1919,6 +1974,7 @@ task_process_apply_xfr(struct nsd* nsd, udb_base* udb, udb_ptr *last_task,
/* could not open file to update */
/* there is no reply to xfrd failed-update,
* because xfrd has a scan for apply-failures. */
+ xfrd_unlink_xfrfile(nsd, TASKLIST(task)->yesno);
return;
}
/* read and apply zone transfer */
diff --git a/usr.sbin/nsd/ipc.c b/usr.sbin/nsd/ipc.c
index 141b0f3a83d..e1bf2d35b92 100644
--- a/usr.sbin/nsd/ipc.c
+++ b/usr.sbin/nsd/ipc.c
@@ -224,10 +224,23 @@ child_is_done(struct nsd* nsd, int fd)
for(i=0; i<nsd->child_count; ++i)
if(nsd->children[i].child_fd == fd) {
nsd->children[i].child_fd = -1;
- nsd->children[i].has_exited = 1;
nsd->children[i].handler->fd = -1;
- DEBUG(DEBUG_IPC,1, (LOG_INFO, "server %d is done",
- (int)nsd->children[i].pid));
+ if(nsd->children[i].need_to_exit) {
+ DEBUG(DEBUG_IPC,1, (LOG_INFO, "server %d is done",
+ (int)nsd->children[i].pid));
+ nsd->children[i].has_exited = 1;
+ } else {
+ log_msg(LOG_WARNING,
+ "server %d died unexpectedly, restarting",
+ (int)nsd->children[i].pid);
+ /* this child is now going to be re-forked as
+ * a subprocess of this server-main, and if a
+ * reload is in progress the other children
+ * are subprocesses of reload. Until the
+ * reload is done and they are all reforked. */
+ nsd->children[i].pid = -1;
+ nsd->restart_children = 1;
+ }
}
parent_check_all_children_exited(nsd);
}
@@ -480,6 +493,7 @@ parent_handle_reload_command(netio_type *ATTR_UNUSED(netio),
handler->fd = -1;
}
log_msg(LOG_ERR, "handle_reload_cmd: reload closed cmd channel");
+ nsd->reload_failed = 1;
return;
}
switch (mode) {
diff --git a/usr.sbin/nsd/namedb.h b/usr.sbin/nsd/namedb.h
index 34de36be6fb..76f78dfa648 100644
--- a/usr.sbin/nsd/namedb.h
+++ b/usr.sbin/nsd/namedb.h
@@ -125,6 +125,9 @@ struct zone
rbtree_t* dshashtree; /* tree, ds-parent-hash domains */
#endif
struct zone_options* opts;
+ char* filename; /* set if read from file, which file */
+ char* logstr; /* set for zone xfer, the log string */
+ time_t mtime; /* time of last modification */
unsigned is_secure : 1; /* zone uses DNSSEC */
unsigned is_ok : 1; /* zone has not expired. */
unsigned is_changed : 1; /* zone was changed by AXFR */
diff --git a/usr.sbin/nsd/nsd-checkzone.8.in b/usr.sbin/nsd/nsd-checkzone.8.in
new file mode 100644
index 00000000000..1ab76bc5e2f
--- /dev/null
+++ b/usr.sbin/nsd/nsd-checkzone.8.in
@@ -0,0 +1,33 @@
+.TH "nsd\-checkzone" "8" "@date@" "NLnet Labs" "nsd @version@"
+.\" Copyright (c) 2014, NLnet Labs. All rights reserved.
+.\" See LICENSE for the license.
+.SH "NAME"
+.B nsd\-checkzone
+\- NSD zone file syntax checker.
+.SH "SYNOPSIS"
+.B nsd\-checkzone
+.RB [ \-h ]
+.I zonename
+.I zonefile
+.SH "DESCRIPTION"
+.B nsd\-checkzone
+reads a DNS zone file and checks it for errors. It prints errors to
+stderr. On failure it exits with nonzero exit status.
+.P
+This is used to check files before feeding them to the nsd(8) daemon.
+.SH "OPTIONS"
+.TP
+.B \-h
+Print usage help information and exit.
+.TP
+.I zonename
+The name of the zone to check, eg. "example.com".
+.TP
+.I zonefile
+The file to read, eg. "zones/example.com.zone.signed".
+.SH "SEE ALSO"
+\fInsd\fR(8), \fInsd-checkconf\fR(8)
+.SH "AUTHORS"
+.B NSD
+was written by NLnet Labs and RIPE NCC joint team. Please see
+CREDITS file in the distribution for further details.
diff --git a/usr.sbin/nsd/nsd-checkzone.c b/usr.sbin/nsd/nsd-checkzone.c
new file mode 100644
index 00000000000..95bb4e72836
--- /dev/null
+++ b/usr.sbin/nsd/nsd-checkzone.c
@@ -0,0 +1,147 @@
+/*
+ * nsd-checkzone.c -- nsd-checkzone(8) checks zones for syntax errors
+ *
+ * Copyright (c) 2013, NLnet Labs. All rights reserved.
+ *
+ * See LICENSE for the license.
+ *
+ */
+
+#include "config.h"
+
+#include <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+#include <errno.h>
+
+#include "nsd.h"
+#include "options.h"
+#include "util.h"
+#include "zonec.h"
+
+static void error(const char *format, ...) ATTR_FORMAT(printf, 1, 2);
+struct nsd nsd;
+
+/*
+ * Print the help text.
+ *
+ */
+static void
+usage (void)
+{
+ fprintf(stderr, "Usage: nsd-checkzone <zone name> <zone file>\n");
+ fprintf(stderr, "Version %s. Report bugs to <%s>.\n",
+ PACKAGE_VERSION, PACKAGE_BUGREPORT);
+}
+
+/*
+ * Something went wrong, give error messages and exit.
+ *
+ */
+static void
+error(const char *format, ...)
+{
+ va_list args;
+ va_start(args, format);
+ log_vmsg(LOG_ERR, format, args);
+ va_end(args);
+ exit(1);
+}
+
+static void
+check_zone(struct nsd* nsd, const char* name, const char* fname)
+{
+ const dname_type* dname;
+ zone_options_t* zo;
+ zone_type* zone;
+ unsigned errors;
+
+ /* init*/
+ nsd->db = namedb_open("", nsd->options);
+ dname = dname_parse(nsd->options->region, name);
+ if(!dname) {
+ /* parse failure */
+ error("cannot parse zone name '%s'", name);
+ }
+ zo = zone_options_create(nsd->options->region);
+ memset(zo, 0, sizeof(*zo));
+ zo->node.key = dname;
+ zo->name = name;
+ zone = namedb_zone_create(nsd->db, dname, zo);
+
+ /* read the zone */
+ errors = zonec_read(name, fname, zone);
+ if(errors > 0) {
+ printf("zone %s file %s has %u errors\n", name, fname, errors);
+ exit(1);
+ }
+ printf("zone %s is ok\n", name);
+ namedb_close(nsd->db);
+}
+
+/* dummy functions to link */
+int writepid(struct nsd * ATTR_UNUSED(nsd))
+{
+ return 0;
+}
+void unlinkpid(const char * ATTR_UNUSED(file))
+{
+}
+void bind8_stats(struct nsd * ATTR_UNUSED(nsd))
+{
+}
+
+void sig_handler(int ATTR_UNUSED(sig))
+{
+}
+
+extern char *optarg;
+extern int optind;
+
+int
+main(int argc, char *argv[])
+{
+ /* Scratch variables... */
+ int c;
+ struct nsd nsd;
+ memset(&nsd, 0, sizeof(nsd));
+
+ log_init("nsd-checkzone");
+
+ /* Parse the command line... */
+ while ((c = getopt(argc, argv, "h")) != -1) {
+ switch (c) {
+ case 'h':
+ usage();
+ exit(0);
+ case '?':
+ default:
+ usage();
+ exit(1);
+ }
+ }
+ argc -= optind;
+ argv += optind;
+
+ /* Commandline parse error */
+ if (argc != 2) {
+ fprintf(stderr, "wrong number of arguments.\n");
+ usage();
+ exit(1);
+ }
+
+ nsd.options = nsd_options_create(region_create_custom(xalloc, free,
+ DEFAULT_CHUNK_SIZE, DEFAULT_LARGE_OBJECT_SIZE,
+ DEFAULT_INITIAL_CLEANUP_SIZE, 1));
+ if (verbosity == 0)
+ verbosity = nsd.options->verbosity;
+
+ check_zone(&nsd, argv[0], argv[1]);
+ region_destroy(nsd.options->region);
+ /* yylex_destroy(); but, not available in all versions of flex */
+
+ exit(0);
+}
diff --git a/usr.sbin/nsd/nsd-control.8.in b/usr.sbin/nsd/nsd-control.8.in
index b8a8701bea5..2ee2e4c8d0a 100644
--- a/usr.sbin/nsd/nsd-control.8.in
+++ b/usr.sbin/nsd/nsd-control.8.in
@@ -1,8 +1,7 @@
-.TH "nsd\-control" "8" "Mar 14, 2014" "NLnet Labs" "nsd 4.0.3"
+.TH "nsd\-control" "8" "Sep 4, 2014" "NLnet Labs" "nsd 4.1.0"
.\" Copyright (c) 2011, NLnet Labs. All rights reserved.
.\" See LICENSE for the license.
.SH "NAME"
-.LP
.B nsd\-control,
.B nsd\-control\-setup
\- NSD remote server control utility.
diff --git a/usr.sbin/nsd/nsd-mem.c b/usr.sbin/nsd/nsd-mem.c
index e47bb9add1e..05710c42576 100644
--- a/usr.sbin/nsd/nsd-mem.c
+++ b/usr.sbin/nsd/nsd-mem.c
@@ -26,6 +26,7 @@
#include "util.h"
static void error(const char *format, ...) ATTR_FORMAT(printf, 1, 2);
+struct nsd nsd;
/*
* Print the help text.
@@ -104,9 +105,11 @@ account_zone(struct namedb* db, struct zone_mem* zmem)
{
zmem->data = region_get_mem(db->region);
zmem->data_unused = region_get_mem_unused(db->region);
- zmem->udb_data = (size_t)db->udb->alloc->disk->stat_data;
- zmem->udb_overhead = (size_t)(db->udb->alloc->disk->stat_alloc -
- db->udb->alloc->disk->stat_data);
+ if(db->udb) {
+ zmem->udb_data = (size_t)db->udb->alloc->disk->stat_data;
+ zmem->udb_overhead = (size_t)(db->udb->alloc->disk->stat_alloc -
+ db->udb->alloc->disk->stat_data);
+ }
zmem->domaincount = db->domains->nametree->count;
}
@@ -239,7 +242,9 @@ check_mem(nsd_options_t* opt)
char df[512];
memset(&totmem, 0, sizeof(totmem));
snprintf(tf, sizeof(tf), "./nsd-mem-task-%u.db", (unsigned)getpid());
- snprintf(df, sizeof(df), "./nsd-mem-db-%u.db", (unsigned)getpid());
+ if(opt->database == NULL || opt->database[0] == 0)
+ df[0] = 0;
+ else snprintf(df, sizeof(df), "./nsd-mem-db-%u.db", (unsigned)getpid());
/* read all zones and account memory */
RBTREE_FOR(zo, zone_options_t*, opt->zone_options) {
@@ -252,10 +257,12 @@ check_mem(nsd_options_t* opt)
print_tot_mem(&totmem);
/* final advice */
- printf("\nFinal advice estimate:\n");
- printf("(The partial mmap causes reload&AXFR to take longer(disk access))\n");
- pretty_mem(totmem.ram + totmem.disk, "data and big mmap");
- pretty_mem(totmem.ram + totmem.disk/6, "data and partial mmap");
+ if(opt->database != NULL && opt->database[0] != 0) {
+ printf("\nFinal advice estimate:\n");
+ printf("(The partial mmap causes reload&AXFR to take longer(disk access))\n");
+ pretty_mem(totmem.ram + totmem.disk, "data and big mmap");
+ pretty_mem(totmem.ram + totmem.disk/6, "data and partial mmap");
+ }
}
/* dummy functions to link */
diff --git a/usr.sbin/nsd/nsd.conf.sample.in b/usr.sbin/nsd/nsd.conf.sample.in
index aa0ab38a9fc..d6154526c07 100644
--- a/usr.sbin/nsd/nsd.conf.sample.in
+++ b/usr.sbin/nsd/nsd.conf.sample.in
@@ -8,7 +8,7 @@
# This is a comment.
# Sample configuration file
-# include: "file" # include that file's text over here.
+# include: "file" # include that file's text over here. Globbed, "*.conf"
# options for the nsd server
server:
@@ -55,6 +55,7 @@ server:
# zonelistfile: "@zonelistfile@"
# the database to use
+ # if set to "" then no disk-database is used, less memory usage.
# database: "@dbfile@"
# log messages to file. Default to stderr and syslog (with
@@ -66,7 +67,7 @@ server:
# The file where secondary zone refresh and expire timeouts are kept.
# If you delete this file, all secondary zones are forced to be
- # 'refreshing' (as if nsd got a notify).
+ # 'refreshing' (as if nsd got a notify). Set to "" to disable.
# xfrdfile: "@xfrdfile@"
# The directory where zone transfers are stored, in a subdir of it.
@@ -98,13 +99,24 @@ server:
# ipv6-edns-size: 4096
# statistics are produced every number of seconds. Prints to log.
+ # Default is 0, meaning no statistics are produced.
# statistics: 3600
# Number of seconds between reloads triggered by xfrd.
# xfrd-reload-timeout: 1
+
+ # log timestamp in ascii (y-m-d h:m:s.msec), yes is default.
+ # log-time-ascii: yes
+
+ # round robin rotation of records in the answer.
+ # round-robin: no
# check mtime of all zone files on start and sighup
# zonefiles-check: yes
+
+ # write changed zonefiles to disk, every N seconds.
+ # default is 0(disabled) or 3600(if database is "").
+ # zonefiles-write: 3600
# RRLconfig
# Response Rate Limiting, size of the hashtable. Default 1000000.
diff --git a/usr.sbin/nsd/nsd.h b/usr.sbin/nsd/nsd.h
index 955fc4fbae2..14e2dae55f4 100644
--- a/usr.sbin/nsd/nsd.h
+++ b/usr.sbin/nsd/nsd.h
@@ -158,6 +158,8 @@ struct nsd
size_t child_count;
struct nsd_child *children;
+ int restart_children;
+ int reload_failed;
/* NULL if this is the parent process. */
struct nsd_child *this_child;
@@ -224,6 +226,8 @@ struct nsd
struct nsd_options* options;
};
+extern struct nsd nsd;
+
/* nsd.c */
pid_t readpid(const char *file);
int writepid(struct nsd *nsd);
diff --git a/usr.sbin/nsd/nsec3.h b/usr.sbin/nsd/nsec3.h
index 96c4367ff33..663776f220b 100644
--- a/usr.sbin/nsd/nsec3.h
+++ b/usr.sbin/nsd/nsec3.h
@@ -92,7 +92,7 @@ int nsec3_condition_hash(struct domain* d, struct zone* z);
int nsec3_condition_dshash(struct domain* d, struct zone* z);
/* set nsec3param for this zone or NULL if no NSEC3 available */
void nsec3_find_zone_param(struct namedb* db, struct zone* zone,
- struct udb_ptr* z);
+ struct udb_ptr* z, struct rr* avoid_rr);
/* hash domain and wcchild, and lookup nsec3 in tree, and precompile */
void nsec3_precompile_domain(struct namedb* db, struct domain* domain,
struct zone* zone, struct region* tmpregion);
diff --git a/usr.sbin/nsd/options.c b/usr.sbin/nsd/options.c
index 060e5eba2bc..c7ef346ec7a 100644
--- a/usr.sbin/nsd/options.c
+++ b/usr.sbin/nsd/options.c
@@ -53,6 +53,8 @@ nsd_options_create(region_type* region)
opt->identity = 0;
opt->nsid = 0;
opt->logfile = 0;
+ opt->log_time_ascii = 1;
+ opt->round_robin = 0; /* also packet.h::round_robin */
opt->server_count = 1;
opt->tcp_count = 100;
opt->tcp_query_count = 0;
@@ -78,6 +80,9 @@ nsd_options_create(region_type* region)
opt->rrl_whitelist_ratelimit = RRL_WLIST_LIMIT/2;
#endif
opt->zonefiles_check = 1;
+ if(opt->database == NULL || opt->database[0] == 0)
+ opt->zonefiles_write = ZONEFILES_WRITE_INTERVAL;
+ else opt->zonefiles_write = 0;
opt->xfrd_reload_timeout = 1;
opt->control_enable = 0;
opt->control_interface = NULL;
@@ -128,7 +133,7 @@ parse_options_file(nsd_options_t* opt, const char* file,
}
cfg_parser->err = err;
cfg_parser->err_arg = err_arg;
- cfg_parser->filename = file;
+ cfg_parser->filename = (char*)file;
cfg_parser->line = 1;
cfg_parser->errors = 0;
cfg_parser->server_settings_seen = 0;
@@ -238,12 +243,12 @@ parse_options_file(nsd_options_t* opt, const char* file,
if(err) {
char m[MAXSYSLOGMSGLEN];
snprintf(m, sizeof(m), "read %s failed: %d errors in "
- "configuration file\n", cfg_parser->filename,
+ "configuration file\n", file,
cfg_parser->errors);
err(err_arg, m);
} else {
fprintf(stderr, "read %s failed: %d errors in "
- "configuration file\n", cfg_parser->filename,
+ "configuration file\n", file,
cfg_parser->errors);
}
return 0;
diff --git a/usr.sbin/nsd/options.h b/usr.sbin/nsd/options.h
index 062ac2842c8..f36b11f242f 100644
--- a/usr.sbin/nsd/options.h
+++ b/usr.sbin/nsd/options.h
@@ -80,6 +80,9 @@ struct nsd_options {
const char* nsid;
int xfrd_reload_timeout;
int zonefiles_check;
+ int zonefiles_write;
+ int log_time_ascii;
+ int round_robin;
/** remote control section. enable toggle. */
int control_enable;
@@ -226,11 +229,14 @@ struct zonelist_bucket {
struct zonelist_free* list;
};
+/* default zonefile write interval if database is "", in seconds */
+#define ZONEFILES_WRITE_INTERVAL 3600
+
/*
* Used during options parsing
*/
struct config_parser_state {
- const char* filename;
+ char* filename;
const char* chroot;
int line;
int errors;
diff --git a/usr.sbin/nsd/packet.c b/usr.sbin/nsd/packet.c
index 72ae4cedf03..08d91b269d7 100644
--- a/usr.sbin/nsd/packet.c
+++ b/usr.sbin/nsd/packet.c
@@ -15,6 +15,8 @@
#include "query.h"
#include "rdata.h"
+int round_robin = 0;
+
static void
encode_dname(query_type *q, domain_type *domain)
{
@@ -130,13 +132,30 @@ packet_encode_rrset(query_type *query,
section == AUTHORITY_SECTION ||
section == OPTIONAL_AUTHORITY_SECTION);
#endif
+ static int round_robin_off = 0;
+ int do_robin = (round_robin && section == ANSWER_SECTION &&
+ query->qtype != TYPE_AXFR && query->qtype != TYPE_IXFR);
+ uint16_t start;
rrset_type *rrsig;
assert(rrset->rr_count > 0);
truncation_mark = buffer_position(query->packet);
- for (i = 0; i < rrset->rr_count; ++i) {
+ if(do_robin && rrset->rr_count)
+ start = (uint16_t)(round_robin_off++ % rrset->rr_count);
+ else start = 0;
+ for (i = start; i < rrset->rr_count; ++i) {
+ if (packet_encode_rr(query, owner, &rrset->rrs[i],
+ rrset->rrs[i].ttl)) {
+ ++added;
+ } else {
+ all_added = 0;
+ start = 0;
+ break;
+ }
+ }
+ for (i = 0; i < start; ++i) {
if (packet_encode_rr(query, owner, &rrset->rrs[i],
rrset->rrs[i].ttl)) {
++added;
diff --git a/usr.sbin/nsd/packet.h b/usr.sbin/nsd/packet.h
index 47558394e0d..b07c6204c5c 100644
--- a/usr.sbin/nsd/packet.h
+++ b/usr.sbin/nsd/packet.h
@@ -143,6 +143,9 @@ struct query;
#define IPV4_MINIMAL_RESPONSE_SIZE 1480 /* Recommended minimal edns size for IPv4 */
#define IPV6_MINIMAL_RESPONSE_SIZE 1220 /* Recommended minimal edns size for IPv6 */
+/* use round robin rotation */
+extern int round_robin;
+
/*
* Encode RR with OWNER as owner name into QUERY. Returns the number
* of RRs successfully encoded.
diff --git a/usr.sbin/nsd/remote.c b/usr.sbin/nsd/remote.c
index 4b3659878f9..47664294d2d 100644
--- a/usr.sbin/nsd/remote.c
+++ b/usr.sbin/nsd/remote.c
@@ -512,7 +512,11 @@ static void
remote_accept_callback(int fd, short event, void* arg)
{
struct daemon_remote *rc = (struct daemon_remote*)arg;
+#ifdef INET6
struct sockaddr_storage addr;
+#else
+ struct sockaddr_in addr;
+#endif
socklen_t addrlen;
int newfd;
struct rc_state* n;
@@ -869,6 +873,7 @@ force_transfer_zone(xfrd_zone_t* zone)
/* pretend we not longer have it and force any
* zone to be downloaded (even same serial, w AXFR) */
zone->soa_disk_acquired = 0;
+ zone->soa_nsd_acquired = 0;
xfrd_handle_notify_and_start_xfr(zone, NULL);
}
@@ -1080,6 +1085,7 @@ do_stats(struct daemon_remote* rc, int peek, struct rc_state* rs)
static void
do_addzone(SSL* ssl, xfrd_state_t* xfrd, char* arg)
{
+ const dname_type* dname;
zone_options_t* zopt;
char* arg2 = NULL;
if(!find_arg2(ssl, arg, &arg2))
@@ -1095,10 +1101,28 @@ do_addzone(SSL* ssl, xfrd_state_t* xfrd, char* arg)
/* check that the pattern exists */
if(!rbtree_search(xfrd->nsd->options->patterns, arg2)) {
- (void)ssl_printf(ssl, "error pattern does not exist\n");
+ (void)ssl_printf(ssl, "error pattern %s does not exist\n",
+ arg2);
return;
}
+ dname = dname_parse(xfrd->region, arg);
+ if(!dname) {
+ (void)ssl_printf(ssl, "error cannot parse zone name\n");
+ return;
+ }
+
+ /* see if zone is a duplicate */
+ if( (zopt=zone_options_find(xfrd->nsd->options, dname)) ) {
+ region_recycle(xfrd->region, (void*)dname,
+ dname_total_size(dname));
+ (void)ssl_printf(ssl, "zone %s already exists\n", arg);
+ send_ok(ssl); /* a nop operation */
+ return;
+ }
+ region_recycle(xfrd->region, (void*)dname, dname_total_size(dname));
+ dname = NULL;
+
/* add to zonelist and adds to config in memory */
zopt = zone_list_add(xfrd->nsd->options, arg, arg2);
if(!zopt) {
diff --git a/usr.sbin/nsd/udb.c b/usr.sbin/nsd/udb.c
index c3b1de0347c..9b31fcfc310 100644
--- a/usr.sbin/nsd/udb.c
+++ b/usr.sbin/nsd/udb.c
@@ -168,11 +168,15 @@ udb_base_create_fd(const char* fname, int fd, udb_walk_relptr_func walkfunc,
goto fail;
}
udb->base_size = (size_t)g.fsize;
+#ifdef HAVE_MMAP
/* note the size_t casts must be there for portability, on some
* systems the layout of memory is otherwise broken. */
udb->base = mmap(NULL, (size_t)udb->base_size,
(int)PROT_READ|PROT_WRITE, (int)MAP_SHARED,
(int)udb->fd, (off_t)0);
+#else
+ udb->base = MAP_FAILED; errno = ENOSYS;
+#endif
if(udb->base == MAP_FAILED) {
udb->base = NULL;
log_msg(LOG_ERR, "mmap(size %u) error: %s",
@@ -297,7 +301,9 @@ udb_base_shrink(udb_base* udb, uint64_t nsize)
udb->glob_data->fsize = nsize;
/* sync, does not *seem* to be required on Linux, but it is
certainly required on OpenBSD. Otherwise changed data is lost. */
+#ifdef HAVE_MMAP
msync(udb->base, udb->base_size, MS_ASYNC);
+#endif
if(ftruncate(udb->fd, (off_t)nsize) != 0) {
log_msg(LOG_ERR, "%s: ftruncate(%u) %s", udb->fname,
(unsigned)nsize, strerror(errno));
@@ -320,9 +326,11 @@ void udb_base_close(udb_base* udb)
udb->fd = -1;
}
if(udb->base) {
+#ifdef HAVE_MMAP
if(munmap(udb->base, udb->base_size) == -1) {
log_msg(LOG_ERR, "munmap: %s", strerror(errno));
}
+#endif
udb->base = NULL;
}
}
@@ -354,10 +362,15 @@ void udb_base_free_keep_mmap(udb_base* udb)
void udb_base_sync(udb_base* udb, int wait)
{
+ if(!udb) return;
+#ifdef HAVE_MMAP
if(msync(udb->base, udb->base_size, wait?MS_SYNC:MS_ASYNC) != 0) {
log_msg(LOG_ERR, "msync(%s) error %s",
udb->fname, strerror(errno));
}
+#else
+ (void)wait;
+#endif
}
/** hash a chunk pointer */
@@ -499,6 +512,7 @@ uint8_t udb_base_get_userflags(udb_base* udb)
static void*
udb_base_remap(udb_base* udb, udb_alloc* alloc, uint64_t nsize)
{
+#ifdef HAVE_MMAP
void* nb;
/* for use with valgrind, do not use mremap, but the other version */
#ifdef MREMAP_MAYMOVE
@@ -545,6 +559,10 @@ udb_base_remap(udb_base* udb, udb_alloc* alloc, uint64_t nsize)
}
udb->base_size = nsize;
return nb;
+#else /* HAVE_MMAP */
+ (void)udb; (void)alloc; (void)nsize;
+ return NULL;
+#endif /* HAVE_MMAP */
}
void
@@ -1531,7 +1549,7 @@ coagulate_and_push(void* base, udb_alloc* alloc, udb_void last, int exp,
}
/** attempt to compact the data and move free space to the end */
-static int
+int
udb_alloc_compact(void* base, udb_alloc* alloc)
{
udb_void last;
@@ -1540,6 +1558,9 @@ udb_alloc_compact(void* base, udb_alloc* alloc)
uint64_t at = alloc->disk->nextgrow;
udb_void xl_start = 0;
uint64_t xl_sz = 0;
+ if(alloc->udb->inhibit_compact)
+ return 1;
+ alloc->udb->useful_compact = 0;
while(at > alloc->udb->glob_data->hsize) {
/* grab last entry */
exp = (int)*((uint8_t*)UDB_REL(base, at-1));
@@ -1657,6 +1678,21 @@ udb_alloc_compact(void* base, udb_alloc* alloc)
return 1;
}
+int
+udb_compact(udb_base* udb)
+{
+ if(!udb) return 1;
+ if(!udb->useful_compact) return 1;
+ DEBUG(DEBUG_DBACCESS, 1, (LOG_INFO, "Compacting database..."));
+ return udb_alloc_compact(udb->base, udb->alloc);
+}
+
+void udb_compact_inhibited(udb_base* udb, int inhibit)
+{
+ if(!udb) return;
+ udb->inhibit_compact = inhibit;
+}
+
#ifdef UDB_CHECK
/** check that rptrs are really zero before free */
void udb_check_rptr_zero(void* base, udb_rel_ptr* p, void* arg)
@@ -1733,6 +1769,10 @@ int udb_alloc_free(udb_alloc* alloc, udb_void r, size_t sz)
if(fp->exp == UDB_EXP_XL) {
udb_free_xl(base, alloc, f, (udb_xl_chunk_d*)fp, sz);
/* compact */
+ if(alloc->udb->inhibit_compact) {
+ alloc->udb->useful_compact = 1;
+ return 1;
+ }
return udb_alloc_compact(base, alloc);
}
/* it is a regular chunk of 2**exp size */
@@ -1776,6 +1816,10 @@ int udb_alloc_free(udb_alloc* alloc, udb_void r, size_t sz)
}
alloc->udb->glob_data->dirty_alloc = udb_dirty_clean;
/* compact */
+ if(alloc->udb->inhibit_compact) {
+ alloc->udb->useful_compact = 1;
+ return 1;
+ }
return udb_alloc_compact(base, alloc);
}
diff --git a/usr.sbin/nsd/udb.h b/usr.sbin/nsd/udb.h
index 691b78c817f..049760dd961 100644
--- a/usr.sbin/nsd/udb.h
+++ b/usr.sbin/nsd/udb.h
@@ -204,6 +204,11 @@ struct udb_base {
udb_walk_relptr_func* walkfunc;
/** user data for walkfunc */
void* walkarg;
+
+ /** compaction is inhibited */
+ int inhibit_compact;
+ /** compaction is useful; deletions performed. */
+ int useful_compact;
};
typedef enum udb_chunk_type udb_chunk_type;
@@ -549,6 +554,22 @@ udb_void udb_alloc_realloc(udb_alloc* alloc, udb_void r, size_t osz,
*/
int udb_alloc_grow(udb_alloc* alloc, size_t sz, size_t num);
+/**
+ * attempt to compact the data and move free space to the end
+ * can shrink the db, which calls sync on the db (for portability).
+ * @param udb: the udb base.
+ * @return 0 on failure (to remap the (possibly) changed udb base).
+ */
+int udb_compact(udb_base* udb);
+
+/**
+ * set the udb to inhibit or uninhibit compaction. Does not perform
+ * the compaction itself if enabled, for that call udb_compact.
+ * @param udb: the udb base
+ * @param inhibit: 0 or 1.
+ */
+void udb_compact_inhibited(udb_base* udb, int inhibit);
+
/**
* Set the alloc type for a newly alloced piece of data
* @param alloc: the udb space allocator.
diff --git a/usr.sbin/nsd/xfrd.h b/usr.sbin/nsd/xfrd.h
index 83cb93fa51b..5c731f6c085 100644
--- a/usr.sbin/nsd/xfrd.h
+++ b/usr.sbin/nsd/xfrd.h
@@ -73,6 +73,14 @@ struct xfrd_state {
time_t reload_cmd_last_sent;
uint8_t can_send_reload;
pid_t reload_pid;
+ /* timeout for lost sigchild and reaping children */
+ struct event child_timer;
+ int child_timer_added;
+
+ /* timeout event for zonefiles_write events */
+ struct event write_timer;
+ /* set to 1 if zones have received xfrs since the last write_timer */
+ int write_zonefile_needed;
/* communication channel with server_main */
struct event ipc_handler;
@@ -230,7 +238,8 @@ enum xfrd_packet_result {
extern xfrd_state_t* xfrd;
/* start xfrd, new start. Pass socket to server_main. */
-void xfrd_init(int socket, struct nsd* nsd, int shortsoa, int reload_active);
+void xfrd_init(int socket, struct nsd* nsd, int shortsoa, int reload_active,
+ pid_t nsd_pid);
/* add new slave zone, dname(from zone_opt) and given options */
void xfrd_init_slave_zone(xfrd_state_t* xfrd, zone_options_t* zone_opt);
diff --git a/usr.sbin/nsd/zlexer.lex b/usr.sbin/nsd/zlexer.lex
index bcb5661ab80..56ad724e108 100644
--- a/usr.sbin/nsd/zlexer.lex
+++ b/usr.sbin/nsd/zlexer.lex
@@ -107,7 +107,9 @@ parser_pop_stringbuf(void)
SPACE [ \t]
LETTER [a-zA-Z]
NEWLINE [\n\r]
-ZONESTR [^ \t\n\r();.\"\$]
+ZONESTR [^ \t\n\r();.\"\$]|\\.|\\\n
+CHARSTR [^ \t\n\r();.]|\\.|\\\n
+QUOTE \"
DOLLAR \$
COMMENT ;
DOT \.
@@ -281,7 +283,7 @@ ANY [^\"\n\\]|\\.
}
/* Quoted strings. Strip leading and ending quotes. */
-\" { BEGIN(quotedstring); LEXOUT(("\" ")); }
+{QUOTE} { BEGIN(quotedstring); LEXOUT(("\" ")); }
<quotedstring><<EOF>> {
zc_error("EOF inside quoted string");
BEGIN(INITIAL);
@@ -290,14 +292,14 @@ ANY [^\"\n\\]|\\.
}
<quotedstring>{ANY}* { LEXOUT(("STR ")); yymore(); }
<quotedstring>\n { ++parser->line; yymore(); }
-<quotedstring>\" {
+<quotedstring>{QUOTE} {
LEXOUT(("\" "));
BEGIN(INITIAL);
yytext[yyleng - 1] = '\0';
return parse_token(STR, yytext, &lexer_state);
}
-({ZONESTR}|\\.|\\\n)+ {
+{ZONESTR}({CHARSTR})* {
/* Any allowed word. */
return parse_token(STR, yytext, &lexer_state);
}