diff options
author | Brad Smith <brad@cvs.openbsd.org> | 2014-09-16 16:54:07 +0000 |
---|---|---|
committer | Brad Smith <brad@cvs.openbsd.org> | 2014-09-16 16:54:07 +0000 |
commit | 2b306798f33a46a535e3b0f3ebdd05f3ad86f526 (patch) | |
tree | 89eaf541a39a437880bc8bea6dd6a370166c9242 /usr.sbin | |
parent | b9bd487caa683ae9060dcce84b139082da8e3e4c (diff) |
update to NSD 4.1.0, ok sthen@
Diffstat (limited to 'usr.sbin')
-rw-r--r-- | usr.sbin/nsd/compat/fake-rfc2553.c | 4 | ||||
-rw-r--r-- | usr.sbin/nsd/configlexer.lex | 120 | ||||
-rw-r--r-- | usr.sbin/nsd/dbaccess.c | 147 | ||||
-rw-r--r-- | usr.sbin/nsd/dbcreate.c | 73 | ||||
-rw-r--r-- | usr.sbin/nsd/difffile.c | 170 | ||||
-rw-r--r-- | usr.sbin/nsd/ipc.c | 20 | ||||
-rw-r--r-- | usr.sbin/nsd/namedb.h | 3 | ||||
-rw-r--r-- | usr.sbin/nsd/nsd-checkzone.8.in | 33 | ||||
-rw-r--r-- | usr.sbin/nsd/nsd-checkzone.c | 147 | ||||
-rw-r--r-- | usr.sbin/nsd/nsd-control.8.in | 3 | ||||
-rw-r--r-- | usr.sbin/nsd/nsd-mem.c | 23 | ||||
-rw-r--r-- | usr.sbin/nsd/nsd.conf.sample.in | 16 | ||||
-rw-r--r-- | usr.sbin/nsd/nsd.h | 4 | ||||
-rw-r--r-- | usr.sbin/nsd/nsec3.h | 2 | ||||
-rw-r--r-- | usr.sbin/nsd/options.c | 11 | ||||
-rw-r--r-- | usr.sbin/nsd/options.h | 8 | ||||
-rw-r--r-- | usr.sbin/nsd/packet.c | 21 | ||||
-rw-r--r-- | usr.sbin/nsd/packet.h | 3 | ||||
-rw-r--r-- | usr.sbin/nsd/remote.c | 26 | ||||
-rw-r--r-- | usr.sbin/nsd/udb.c | 46 | ||||
-rw-r--r-- | usr.sbin/nsd/udb.h | 21 | ||||
-rw-r--r-- | usr.sbin/nsd/xfrd.h | 11 | ||||
-rw-r--r-- | usr.sbin/nsd/zlexer.lex | 10 |
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); } |