nsd 4.1.11

classic Classic list List threaded Threaded
2 messages Options
Reply | Threaded
Open this post in threaded view
|

nsd 4.1.11

Florian Obser-2
OK?

diff --git buffer.h buffer.h
index bee7d8b..9e17bc9 100644
--- buffer.h
+++ buffer.h
@@ -315,6 +315,20 @@ buffer_write_u32(buffer_type *buffer, uint32_t data)
 }
 
 static inline void
+buffer_write_u64_at(buffer_type *buffer, size_t at, uint64_t data)
+{
+ assert(buffer_available_at(buffer, at, sizeof(data)));
+ write_uint64(buffer->_data + at, data);
+}
+
+static inline void
+buffer_write_u64(buffer_type *buffer, uint64_t data)
+{
+ buffer_write_u64_at(buffer, buffer->_position, data);
+ buffer->_position += sizeof(data);
+}
+
+static inline void
 buffer_read_at(buffer_type *buffer, size_t at, void *data, size_t count)
 {
  assert(buffer_available_at(buffer, at, count));
@@ -373,6 +387,21 @@ buffer_read_u32(buffer_type *buffer)
  return result;
 }
 
+static inline uint64_t
+buffer_read_u64_at(buffer_type *buffer, size_t at)
+{
+ assert(buffer_available_at(buffer, at, sizeof(uint64_t)));
+ return read_uint64(buffer->_data + at);
+}
+
+static inline uint64_t
+buffer_read_u64(buffer_type *buffer)
+{
+ uint64_t result = buffer_read_u64_at(buffer, buffer->_position);
+ buffer->_position += sizeof(uint64_t);
+ return result;
+}
+
 /*
  * Print to the buffer, increasing the capacity if required using
  * buffer_reserve(). The buffer's position is set to the terminating
diff --git configlexer.lex configlexer.lex
index 113fa22..d536352 100644
--- configlexer.lex
+++ configlexer.lex
@@ -236,6 +236,7 @@ zone{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_ZONE;}
 zonefile{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_ZONEFILE;}
 zonestats{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_ZONESTATS;}
 allow-notify{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_ALLOW_NOTIFY;}
+size-limit-xfr{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_SIZE_LIMIT_XFR;}
 request-xfr{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_REQUEST_XFR;}
 notify{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_NOTIFY;}
 notify-retry{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_NOTIFY_RETRY;}
@@ -268,6 +269,10 @@ 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;}
+max-refresh-time{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_MAX_REFRESH_TIME;}
+min-refresh-time{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_MIN_REFRESH_TIME;}
+max-retry-time{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_MAX_RETRY_TIME;}
+min-retry-time{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_MIN_RETRY_TIME;}
 {NEWLINE} { LEXOUT(("NL\n")); cfg_parser->line++;}
 
  /* Quoted strings. Strip leading and ending quotes */
diff --git configparser.y configparser.y
index 1d824d1..9089665 100644
--- configparser.y
+++ configparser.y
@@ -54,7 +54,7 @@ extern config_parser_state_t* cfg_parser;
 %token VAR_TCP_MSS VAR_OUTGOING_TCP_MSS VAR_IP_FREEBIND
 %token VAR_ZONEFILE
 %token VAR_ZONE
-%token VAR_ALLOW_NOTIFY VAR_REQUEST_XFR VAR_NOTIFY VAR_PROVIDE_XFR
+%token VAR_ALLOW_NOTIFY VAR_REQUEST_XFR VAR_NOTIFY VAR_PROVIDE_XFR VAR_SIZE_LIMIT_XFR
 %token VAR_NOTIFY_RETRY VAR_OUTGOING_INTERFACE VAR_ALLOW_AXFR_FALLBACK
 %token VAR_KEY
 %token VAR_ALGORITHM VAR_SECRET
@@ -69,6 +69,8 @@ extern config_parser_state_t* cfg_parser;
 %token VAR_RRL_WHITELIST_RATELIMIT VAR_RRL_WHITELIST
 %token VAR_ZONEFILES_CHECK VAR_ZONEFILES_WRITE VAR_LOG_TIME_ASCII
 %token VAR_ROUND_ROBIN VAR_ZONESTATS VAR_REUSEPORT VAR_VERSION
+%token VAR_MAX_REFRESH_TIME VAR_MIN_REFRESH_TIME
+%token VAR_MAX_RETRY_TIME VAR_MIN_RETRY_TIME
 
 %%
 toplevelvars: /* empty */ | toplevelvars toplevelvar ;
@@ -598,7 +600,9 @@ content_pattern: pattern_name | zone_config_item;
 zone_config_item: zone_zonefile | zone_allow_notify | zone_request_xfr |
  zone_notify | zone_notify_retry | zone_provide_xfr |
  zone_outgoing_interface | zone_allow_axfr_fallback | include_pattern |
- zone_rrl_whitelist | zone_zonestats;
+ zone_rrl_whitelist | zone_zonestats | zone_max_refresh_time |
+ zone_min_refresh_time | zone_max_retry_time | zone_min_retry_time |
+ zone_size_limit_xfr;
 pattern_name: VAR_NAME STRING
  {
  OUTYY(("P(pattern_name:%s)\n", $2));
@@ -714,6 +718,14 @@ zone_request_xfr: VAR_REQUEST_XFR zone_request_xfr_data
  {
  }
  ;
+zone_size_limit_xfr: VAR_SIZE_LIMIT_XFR STRING
+ {
+ OUTYY(("P(size_limit_xfr:%s)\n", $2));
+ if(atoll($2) < 0)
+ yyerror("number >= 0 expected");
+ else cfg_parser->current_pattern->size_limit_xfr = atoll($2);
+ }
+ ;
 zone_request_xfr_data: STRING STRING
  {
  acl_options_t* acl = parse_acl_info(cfg_parser->opt->region, $1, $2);
@@ -819,6 +831,46 @@ zone_rrl_whitelist: VAR_RRL_WHITELIST STRING
 #endif
  }
  ;
+zone_max_refresh_time: VAR_MAX_REFRESH_TIME STRING
+{
+ OUTYY(("P(zone_max_refresh_time:%s)\n", $2));
+ if(atoi($2) == 0 && strcmp($2, "0") != 0)
+ yyerror("number expected");
+ else {
+ cfg_parser->current_pattern->max_refresh_time = atoi($2);
+ cfg_parser->current_pattern->max_refresh_time_is_default = 0;
+ }
+};
+zone_min_refresh_time: VAR_MIN_REFRESH_TIME STRING
+{
+ OUTYY(("P(zone_min_refresh_time:%s)\n", $2));
+ if(atoi($2) == 0 && strcmp($2, "0") != 0)
+ yyerror("number expected");
+ else {
+ cfg_parser->current_pattern->min_refresh_time = atoi($2);
+ cfg_parser->current_pattern->min_refresh_time_is_default = 0;
+ }
+};
+zone_max_retry_time: VAR_MAX_RETRY_TIME STRING
+{
+ OUTYY(("P(zone_max_retry_time:%s)\n", $2));
+ if(atoi($2) == 0 && strcmp($2, "0") != 0)
+ yyerror("number expected");
+ else {
+ cfg_parser->current_pattern->max_retry_time = atoi($2);
+ cfg_parser->current_pattern->max_retry_time_is_default = 0;
+ }
+};
+zone_min_retry_time: VAR_MIN_RETRY_TIME STRING
+{
+ OUTYY(("P(zone_min_retry_time:%s)\n", $2));
+ if(atoi($2) == 0 && strcmp($2, "0") != 0)
+ yyerror("number expected");
+ else {
+ cfg_parser->current_pattern->min_retry_time = atoi($2);
+ cfg_parser->current_pattern->min_retry_time_is_default = 0;
+ }
+};
 
 /* key: declaration */
 keystart: VAR_KEY
diff --git configure configure
index a525e08..fc6a02b 100644
--- configure
+++ configure
@@ -1,6 +1,6 @@
 #! /bin/sh
 # Guess values for system-dependent variables and create Makefiles.
-# Generated by GNU Autoconf 2.69 for NSD 4.1.10.
+# Generated by GNU Autoconf 2.69 for NSD 4.1.11.
 #
 # Report bugs to <[hidden email]>.
 #
@@ -580,8 +580,8 @@ MAKEFLAGS=
 # Identity of this package.
 PACKAGE_NAME='NSD'
 PACKAGE_TARNAME='nsd'
-PACKAGE_VERSION='4.1.10'
-PACKAGE_STRING='NSD 4.1.10'
+PACKAGE_VERSION='4.1.11'
+PACKAGE_STRING='NSD 4.1.11'
 PACKAGE_BUGREPORT='[hidden email]'
 PACKAGE_URL=''
 
@@ -1283,7 +1283,7 @@ if test "$ac_init_help" = "long"; then
   # Omit some internal or obsolete options to make the list less imposing.
   # This message is too long to be a string in the A/UX 3.1 sh.
   cat <<_ACEOF
-\`configure' configures NSD 4.1.10 to adapt to many kinds of systems.
+\`configure' configures NSD 4.1.11 to adapt to many kinds of systems.
 
 Usage: $0 [OPTION]... [VAR=VALUE]...
 
@@ -1344,7 +1344,7 @@ fi
 
 if test -n "$ac_init_help"; then
   case $ac_init_help in
-     short | recursive ) echo "Configuration of NSD 4.1.10:";;
+     short | recursive ) echo "Configuration of NSD 4.1.11:";;
    esac
   cat <<\_ACEOF
 
@@ -1487,7 +1487,7 @@ fi
 test -n "$ac_init_help" && exit $ac_status
 if $ac_init_version; then
   cat <<\_ACEOF
-NSD configure 4.1.10
+NSD configure 4.1.11
 generated by GNU Autoconf 2.69
 
 Copyright (C) 2012 Free Software Foundation, Inc.
@@ -2196,7 +2196,7 @@ cat >config.log <<_ACEOF
 This file contains any messages produced by compilers while
 running configure, to aid debugging if configure makes a mistake.
 
-It was created by NSD $as_me 4.1.10, which was
+It was created by NSD $as_me 4.1.11, which was
 generated by GNU Autoconf 2.69.  Invocation command line was
 
   $ $0 $@
@@ -9681,7 +9681,7 @@ cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
 # report actual input values of CONFIG_FILES etc. instead of their
 # values after options handling.
 ac_log="
-This file was extended by NSD $as_me 4.1.10, which was
+This file was extended by NSD $as_me 4.1.11, which was
 generated by GNU Autoconf 2.69.  Invocation command line was
 
   CONFIG_FILES    = $CONFIG_FILES
@@ -9743,7 +9743,7 @@ _ACEOF
 cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
 ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`"
 ac_cs_version="\\
-NSD config.status 4.1.10
+NSD config.status 4.1.11
 configured by $0, generated by GNU Autoconf 2.69,
   with options \\"\$ac_cs_config\\"
 
diff --git configure.ac configure.ac
index 4c451bd..da75edc 100644
--- configure.ac
+++ configure.ac
@@ -4,7 +4,7 @@ dnl
 
 sinclude(acx_nlnetlabs.m4)
 
-AC_INIT(NSD,4.1.10,[hidden email])
+AC_INIT(NSD,4.1.11,[hidden email])
 AC_CONFIG_HEADER([config.h])
 
 CFLAGS="$CFLAGS"
diff --git ipc.c ipc.c
index 1f188e5..8b5cba4 100644
--- ipc.c
+++ ipc.c
@@ -82,7 +82,10 @@ child_handle_parent_command(int fd, short event, void* arg)
  server_close_all_sockets(data->nsd->udp, data->nsd->ifs);
  server_close_all_sockets(data->nsd->tcp, data->nsd->ifs);
  /* mode == NSD_QUIT_CHILD */
- (void)write(fd, &mode, sizeof(mode));
+ if(write(fd, &mode, sizeof(mode)) == -1) {
+ VERBOSITY(3, (LOG_INFO, "quit child write: %s",
+ strerror(errno)));
+ }
  ipc_child_quit(data->nsd);
  break;
  case NSD_QUIT_WITH_STATS:
diff --git namedb.c namedb.c
index db90965..dbe8efd 100644
--- namedb.c
+++ namedb.c
@@ -533,7 +533,7 @@ domain_find_zone(namedb_type* db, domain_type* domain)
 }
 
 zone_type *
-domain_find_parent_zone(zone_type* zone)
+domain_find_parent_zone(namedb_type* db, zone_type* zone)
 {
  rrset_type* rrset;
 
@@ -544,6 +544,10 @@ domain_find_parent_zone(zone_type* zone)
  return rrset->zone;
  }
  }
+ /* the NS record in the parent zone above this zone is not present,
+ * workaround to find that parent zone anyway */
+ if(zone->apex->parent)
+ return domain_find_zone(db, zone->apex->parent);
  return NULL;
 }
 
diff --git namedb.h namedb.h
index ca46477..e5cf36c 100644
--- namedb.h
+++ namedb.h
@@ -232,7 +232,7 @@ rrset_type* domain_find_rrset(domain_type* domain, zone_type* zone, uint16_t typ
 rrset_type* domain_find_any_rrset(domain_type* domain, zone_type* zone);
 
 zone_type* domain_find_zone(namedb_type* db, domain_type* domain);
-zone_type* domain_find_parent_zone(zone_type* zone);
+zone_type* domain_find_parent_zone(namedb_type* db, zone_type* zone);
 
 domain_type* domain_find_ns_rrsets(domain_type* domain, zone_type* zone, rrset_type **ns);
 /* find DNAME rrset in domain->parent or higher and return that domain */
diff --git nsd-checkconf.c nsd-checkconf.c
index e5f669f..b7afdd1 100644
--- nsd-checkconf.c
+++ nsd-checkconf.c
@@ -59,6 +59,12 @@ extern int optind;
  return; \
  }
 
+#define ZONE_GET_INT(NAME, VAR, PATTERN) \
+ if (strcasecmp(#NAME, (VAR)) == 0) { \
+ printf("%d\n", (int) PATTERN->NAME); \
+ return; \
+ }
+
 #define SERV_GET_BIN(NAME, VAR) \
  if (strcasecmp(#NAME, (VAR)) == 0) { \
  printf("%s\n", opt->NAME?"yes":"no"); \
@@ -306,6 +312,11 @@ config_print_zone(nsd_options_t* opt, const char* k, int s, const char *o,
  ZONE_GET_STR(zonestats, o, zone->pattern);
  ZONE_GET_OUTGOING(outgoing_interface, o, zone->pattern);
  ZONE_GET_BIN(allow_axfr_fallback, o, zone->pattern);
+ ZONE_GET_INT(max_refresh_time, o, zone->pattern);
+ ZONE_GET_INT(min_refresh_time, o, zone->pattern);
+ ZONE_GET_INT(max_retry_time, o, zone->pattern);
+ ZONE_GET_INT(min_retry_time, o, zone->pattern);
+ ZONE_GET_INT(size_limit_xfr, o, zone->pattern);
 #ifdef RATELIMIT
  ZONE_GET_RRL(rrl_whitelist, o, zone->pattern);
 #endif
@@ -331,6 +342,11 @@ config_print_zone(nsd_options_t* opt, const char* k, int s, const char *o,
  ZONE_GET_STR(zonestats, o, p);
  ZONE_GET_OUTGOING(outgoing_interface, o, p);
  ZONE_GET_BIN(allow_axfr_fallback, o, p);
+ ZONE_GET_INT(max_refresh_time, o, p);
+ ZONE_GET_INT(min_refresh_time, o, p);
+ ZONE_GET_INT(max_retry_time, o, p);
+ ZONE_GET_INT(min_retry_time, o, p);
+ ZONE_GET_INT(size_limit_xfr, o, p);
 #ifdef RATELIMIT
  ZONE_GET_RRL(rrl_whitelist, o, p);
 #endif
@@ -431,6 +447,17 @@ static void print_zone_content_elems(pattern_options_t* pat)
  if(!pat->allow_axfr_fallback_is_default)
  printf("\tallow-axfr-fallback: %s\n",
  pat->allow_axfr_fallback?"yes":"no");
+ if(!pat->max_refresh_time_is_default)
+ printf("\tmax-refresh-time: %d\n", pat->max_refresh_time);
+ if(!pat->min_refresh_time_is_default)
+ printf("\tmin-refresh-time: %d\n", pat->min_refresh_time);
+ if(!pat->max_retry_time_is_default)
+ printf("\tmax-retry-time: %d\n", pat->max_retry_time);
+ if(!pat->min_retry_time_is_default)
+ printf("\tmin-retry-time: %d\n", pat->min_retry_time);
+ if(pat->size_limit_xfr != 0)
+ printf("\tsize-limit-xfr: %llu\n",
+ (long long unsigned)pat->size_limit_xfr);
 }
 
 void
@@ -559,6 +586,13 @@ additional_checks(nsd_options_t* opt, const char* filename)
  fprintf(stderr, "%s: cannot parse zone name syntax for zone %s.\n", filename, zone->name);
  errors ++;
  }
+#ifndef ROOT_SERVER
+ /* Is it a root zone? Are we a root server then? Idiot proof. */
+ if(dname->label_count == 1) {
+ fprintf(stderr, "%s: not configured as a root server.\n", filename);
+ errors ++;
+ }
+#endif
  if(zone->pattern->allow_notify && !zone->pattern->request_xfr) {
  fprintf(stderr, "%s: zone %s has allow-notify but no request-xfr"
  " items. Where can it get a zone transfer when a notify "
diff --git nsd.conf.5.in nsd.conf.5.in
index 44493c7..bcec054 100644
--- nsd.conf.5.in
+++ nsd.conf.5.in
@@ -583,6 +583,10 @@ transmitted using TCP.
 This option should be accompanied by request\-xfr. It (dis)allows NSD (as secondary)
 to fallback to AXFR if the primary name server does not support IXFR. Default is yes.
 .TP
+.B size\-limit\-xfr:\fR <number>
+This option should be accompanied by request\-xfr. It specifies XFR temporary file size limit.  It can be used to stop very large zone retrieval, that could otherwise use up a lot of memory and disk space.
+If this option is 0, unlimited. Default value is 0.
+.TP
 .B notify:\fR <ip\-address> <key\-name | NOKEY>
 Access control list. The listed address (a secondary) is notified
 of updates to this zone. A port number can be added using a suffix of @number,
@@ -624,6 +628,22 @@ A port number can be added using a suffix of @number, for example
 1.2.3.4@5300.
 .RE
 .TP
+.B max\-refresh\-time:\fR <seconds>
+Limit refresh time for secondary zones.  This is the timer which checks to see
+if the zone has to be refetched when it expires.  Normally the value from the
+SOA record is used, but this option restricts that value.
+.TP
+.B min\-refresh\-time:\fR <seconds>
+Limit refresh time for secondary zones.
+.TP
+.B max\-retry\-time:\fR <seconds>
+Limit retry time for secondary zones.  This is the timeout after a failed
+fetch attempt for the zone.  Normally the value from the SOA record is used,
+but this option restricts that value.
+.TP
+.B min\-retry\-time:\fR <seconds>
+Limit retry time for secondary zones.
+.TP
 .B zonestats:\fR <name>
 When compiled with \-\-enable\-zone\-stats NSD can collect statistics per zone.
 This name gives the group where statistics are added to.  The groups are
diff --git nsd.conf.sample.in nsd.conf.sample.in
index 48eef14..cda7dd0 100644
--- nsd.conf.sample.in
+++ nsd.conf.sample.in
@@ -258,6 +258,15 @@ remote-control:
  # set local interface for sending zone transfer requests.
  # default is let the OS choose.
  #outgoing-interface: 10.0.0.10
+ # limit the refresh and retry interval in seconds.
+ #max-refresh-time: 2419200
+ #min-refresh-time: 0
+ #max-retry-time: 1209600
+ #min-retry-time: 0
+
+ # limit the zone transfer size (in bytes), stops very large transfers
+ # 0 is no limits enforced.
+ # size-limit-xfr: 0
 
  # if compiled with --enable-zone-stats, give name of stat block for
  # this zone (or group of zones).  Output from nsd-control stats.
diff --git nsec3.c nsec3.c
index d4fd1a2..bad5af8 100644
--- nsec3.c
+++ nsec3.c
@@ -893,7 +893,9 @@ nsec3_add_ds_proof(struct query *query, struct answer *answer,
  /* use NSEC3 record from above the zone cut. */
  nsec3_add_rrset(query, answer, AUTHORITY_SECTION,
  domain->nsec3->nsec3_ds_parent_cover);
- } else if (!delegpt && domain->nsec3 && domain->nsec3->nsec3_is_exact) {
+ } else if (!delegpt && domain->nsec3 && domain->nsec3->nsec3_is_exact
+ && nsec3_domain_part_of_zone(domain->nsec3->nsec3_cover,
+ query->zone)) {
  nsec3_add_rrset(query, answer, AUTHORITY_SECTION,
  domain->nsec3->nsec3_cover);
  } else {
diff --git options.c options.c
index 349e271..058ceec 100644
--- options.c
+++ options.c
@@ -813,6 +813,7 @@ pattern_options_create(region_type* region)
  p->zonestats = 0;
  p->allow_notify = 0;
  p->request_xfr = 0;
+ p->size_limit_xfr = 0;
  p->notify = 0;
  p->provide_xfr = 0;
  p->outgoing_interface = 0;
@@ -822,6 +823,14 @@ pattern_options_create(region_type* region)
  p->allow_axfr_fallback_is_default = 1;
  p->implicit = 0;
  p->xfrd_flags = 0;
+ p->max_refresh_time = 2419200; /* 4 weeks */
+ p->max_refresh_time_is_default = 1;
+ p->min_refresh_time = 0;
+ p->min_refresh_time_is_default = 1;
+ p->max_retry_time = 1209600; /* 2 weeks */
+ p->max_retry_time_is_default = 1;
+ p->min_retry_time = 0;
+ p->min_retry_time_is_default = 1;
 #ifdef RATELIMIT
  p->rrl_whitelist = 0;
 #endif
@@ -944,6 +953,14 @@ copy_pat_fixed(region_type* region, pattern_options_t* orig,
  if(p->zonestats)
  orig->zonestats = region_strdup(region, p->zonestats);
  else orig->zonestats = NULL;
+ orig->max_refresh_time = p->max_refresh_time;
+ orig->max_refresh_time_is_default = p->max_refresh_time_is_default;
+ orig->min_refresh_time = p->min_refresh_time;
+ orig->min_refresh_time_is_default = p->min_refresh_time_is_default;
+ orig->max_retry_time = p->max_retry_time;
+ orig->max_retry_time_is_default = p->max_retry_time_is_default;
+ orig->min_retry_time = p->min_retry_time;
+ orig->min_retry_time_is_default = p->min_retry_time_is_default;
 #ifdef RATELIMIT
  orig->rrl_whitelist = p->rrl_whitelist;
 #endif
@@ -1017,9 +1034,22 @@ pattern_options_equal(pattern_options_t* p, pattern_options_t* q)
  if(!acl_list_equal(p->provide_xfr, q->provide_xfr)) return 0;
  if(!acl_list_equal(p->outgoing_interface, q->outgoing_interface))
  return 0;
+ if(p->max_refresh_time != q->max_refresh_time) return 0;
+ if(!booleq(p->max_refresh_time_is_default,
+ q->max_refresh_time_is_default)) return 0;
+ if(p->min_refresh_time != q->min_refresh_time) return 0;
+ if(!booleq(p->min_refresh_time_is_default,
+ q->min_refresh_time_is_default)) return 0;
+ if(p->max_retry_time != q->max_retry_time) return 0;
+ if(!booleq(p->max_retry_time_is_default,
+ q->max_retry_time_is_default)) return 0;
+ if(p->min_retry_time != q->min_retry_time) return 0;
+ if(!booleq(p->min_retry_time_is_default,
+ q->min_retry_time_is_default)) return 0;
 #ifdef RATELIMIT
  if(p->rrl_whitelist != q->rrl_whitelist) return 0;
 #endif
+ if(p->size_limit_xfr != q->size_limit_xfr) return 0;
  return 1;
 }
 
@@ -1036,6 +1066,19 @@ unmarshal_u8(struct buffer* b)
  return buffer_read_u8(b);
 }
 
+static void
+marshal_u64(struct buffer* b, uint64_t v)
+{
+ buffer_reserve(b, 8);
+ buffer_write_u64(b, v);
+}
+
+static uint64_t
+unmarshal_u64(struct buffer* b)
+{
+ return buffer_read_u64(b);
+}
+
 #ifdef RATELIMIT
 static void
 marshal_u16(struct buffer* b, uint16_t v)
@@ -1054,6 +1097,19 @@ unmarshal_u16(struct buffer* b)
 #endif
 
 static void
+marshal_u32(struct buffer* b, uint32_t v)
+{
+ buffer_reserve(b, 4);
+ buffer_write_u32(b, v);
+}
+
+static uint32_t
+unmarshal_u32(struct buffer* b)
+{
+ return buffer_read_u32(b);
+}
+
+static void
 marshal_str(struct buffer* b, const char* s)
 {
  if(!s) marshal_u8(b, 0);
@@ -1138,11 +1194,20 @@ pattern_options_marshal(struct buffer* b, pattern_options_t* p)
  marshal_u8(b, p->notify_retry);
  marshal_u8(b, p->notify_retry_is_default);
  marshal_u8(b, p->implicit);
+ marshal_u64(b, p->size_limit_xfr);
  marshal_acl_list(b, p->allow_notify);
  marshal_acl_list(b, p->request_xfr);
  marshal_acl_list(b, p->notify);
  marshal_acl_list(b, p->provide_xfr);
  marshal_acl_list(b, p->outgoing_interface);
+ marshal_u32(b, p->max_refresh_time);
+ marshal_u8(b, p->max_refresh_time_is_default);
+ marshal_u32(b, p->min_refresh_time);
+ marshal_u8(b, p->min_refresh_time_is_default);
+ marshal_u32(b, p->max_retry_time);
+ marshal_u8(b, p->max_retry_time_is_default);
+ marshal_u32(b, p->min_retry_time);
+ marshal_u8(b, p->min_retry_time_is_default);
 }
 
 pattern_options_t*
@@ -1160,11 +1225,20 @@ pattern_options_unmarshal(region_type* r, struct buffer* b)
  p->notify_retry = unmarshal_u8(b);
  p->notify_retry_is_default = unmarshal_u8(b);
  p->implicit = unmarshal_u8(b);
+ p->size_limit_xfr = unmarshal_u64(b);
  p->allow_notify = unmarshal_acl_list(r, b);
  p->request_xfr = unmarshal_acl_list(r, b);
  p->notify = unmarshal_acl_list(r, b);
  p->provide_xfr = unmarshal_acl_list(r, b);
  p->outgoing_interface = unmarshal_acl_list(r, b);
+ p->max_refresh_time = unmarshal_u32(b);
+ p->max_refresh_time_is_default = unmarshal_u8(b);
+ p->min_refresh_time = unmarshal_u32(b);
+ p->min_refresh_time_is_default = unmarshal_u8(b);
+ p->max_retry_time = unmarshal_u32(b);
+ p->max_retry_time_is_default = unmarshal_u8(b);
+ p->min_retry_time = unmarshal_u32(b);
+ p->min_retry_time_is_default = unmarshal_u8(b);
  return p;
 }
 
@@ -1875,6 +1949,23 @@ config_apply_pattern(const char* name)
  a->notify_retry = pat->notify_retry;
  a->notify_retry_is_default = 0;
  }
+ if(!pat->max_refresh_time_is_default) {
+ a->max_refresh_time = pat->max_refresh_time;
+ a->max_refresh_time_is_default = 0;
+ }
+ if(!pat->min_refresh_time_is_default) {
+ a->min_refresh_time = pat->min_refresh_time;
+ a->min_refresh_time_is_default = 0;
+ }
+ if(!pat->max_retry_time_is_default) {
+ a->max_retry_time = pat->max_retry_time;
+ a->max_retry_time_is_default = 0;
+ }
+ if(!pat->min_retry_time_is_default) {
+ a->min_retry_time = pat->min_retry_time;
+ a->min_retry_time_is_default = 0;
+ }
+ a->size_limit_xfr = pat->size_limit_xfr;
 #ifdef RATELIMIT
  a->rrl_whitelist |= pat->rrl_whitelist;
 #endif
diff --git options.h options.h
index ceba624..e0f749c 100644
--- options.h
+++ options.h
@@ -154,6 +154,15 @@ struct pattern_options {
  uint8_t notify_retry_is_default;
  uint8_t implicit; /* pattern is implicit, part_of_config zone used */
  uint8_t xfrd_flags;
+ uint32_t max_refresh_time;
+ uint8_t max_refresh_time_is_default;
+ uint32_t min_refresh_time;
+ uint8_t min_refresh_time_is_default;
+ uint32_t max_retry_time;
+ uint8_t max_retry_time_is_default;
+ uint32_t min_retry_time;
+ uint8_t min_retry_time_is_default;
+ uint64_t size_limit_xfr;
 };
 
 #define PATTERN_IMPLICIT_MARKER "_implicit_"
diff --git query.c query.c
index f8a429c..7256449 100644
--- query.c
+++ query.c
@@ -704,11 +704,16 @@ add_rrset(struct query   *query,
  result = answer_add_rrset(answer, section, owner, rrset);
  switch (rrset_rrtype(rrset)) {
  case TYPE_NS:
+#if defined(INET6)
  /* if query over IPv6, swap A and AAAA; put AAAA first */
  add_additional_rrsets(query, answer, rrset, 0, 1,
  (query->addr.ss_family == AF_INET6)?
  swap_aaaa_additional_rr_types:
  default_additional_rr_types);
+#else
+ add_additional_rrsets(query, answer, rrset, 0, 1,
+      default_additional_rr_types);
+#endif
  break;
  case TYPE_MB:
  add_additional_rrsets(query, answer, rrset, 0, 0,
@@ -1205,7 +1210,7 @@ answer_lookup_zone(struct nsd *nsd, struct query *q, answer_type *answer,
  * parent zone to generate the answer if we are
  * authoritative for the parent zone.
  */
- zone_type *zone = domain_find_parent_zone(q->zone);
+ zone_type *zone = domain_find_parent_zone(nsd->db, q->zone);
  if (zone)
  q->zone = zone;
  }
diff --git server.c server.c
index 530b443..ae27312 100644
--- server.c
+++ server.c
@@ -160,6 +160,11 @@ struct tcp_handler_data
  * The number of queries handled by this specific TCP connection.
  */
  int query_count;
+
+ /*
+ * The timeout in msec for this tcp connection
+ */
+ int tcp_timeout;
 };
 
 /*
@@ -2630,8 +2635,8 @@ handle_tcp_reading(int fd, short event, void* arg)
  data->query->tcplen = buffer_remaining(data->query->packet);
  data->bytes_transmitted = 0;
 
- timeout.tv_sec = data->nsd->tcp_timeout;
- timeout.tv_usec = 0L;
+ timeout.tv_sec = data->tcp_timeout / 1000;
+ timeout.tv_usec = (data->tcp_timeout % 1000)*1000;
 
  ev_base = data->event.ev_base;
  event_del(&data->event);
@@ -2763,8 +2768,8 @@ handle_tcp_writing(int fd, short event, void* arg)
  q->tcplen = buffer_remaining(q->packet);
  data->bytes_transmitted = 0;
  /* Reset timeout.  */
- timeout.tv_sec = data->nsd->tcp_timeout;
- timeout.tv_usec = 0L;
+ timeout.tv_sec = data->tcp_timeout / 1000;
+ timeout.tv_usec = (data->tcp_timeout % 1000)*1000;
  ev_base = data->event.ev_base;
  event_del(&data->event);
  event_set(&data->event, fd, EV_PERSIST | EV_WRITE | EV_TIMEOUT,
@@ -2794,8 +2799,8 @@ handle_tcp_writing(int fd, short event, void* arg)
 
  data->bytes_transmitted = 0;
 
- timeout.tv_sec = data->nsd->tcp_timeout;
- timeout.tv_usec = 0L;
+ timeout.tv_sec = data->tcp_timeout / 1000;
+ timeout.tv_usec = (data->tcp_timeout % 1000)*1000;
  ev_base = data->event.ev_base;
  event_del(&data->event);
  event_set(&data->event, fd, EV_PERSIST | EV_READ | EV_TIMEOUT,
@@ -2909,8 +2914,13 @@ handle_tcp_accept(int fd, short event, void* arg)
  memcpy(&tcp_data->query->addr, &addr, addrlen);
  tcp_data->query->addrlen = addrlen;
 
- timeout.tv_sec = data->nsd->tcp_timeout;
- timeout.tv_usec = 0;
+ tcp_data->tcp_timeout = data->nsd->tcp_timeout * 1000;
+ if (data->nsd->current_tcp_count > data->nsd->maximum_tcp_count/2) {
+ /* very busy, give smaller timeout */
+ tcp_data->tcp_timeout = 200;
+ }
+ timeout.tv_sec = tcp_data->tcp_timeout / 1000;
+ timeout.tv_usec = (tcp_data->tcp_timeout % 1000)*1000;
 
  event_set(&tcp_data->event, s, EV_PERSIST | EV_READ | EV_TIMEOUT,
  handle_tcp_reading, tcp_data);
diff --git util.h util.h
index 702674f..b59b7b6 100644
--- util.h
+++ util.h
@@ -198,6 +198,20 @@ write_uint32(void *dst, uint32_t data)
 #endif
 }
 
+static inline void
+write_uint64(void *dst, uint64_t data)
+{
+ uint8_t *p = (uint8_t *) dst;
+ p[0] = (uint8_t) ((data >> 56) & 0xff);
+ p[1] = (uint8_t) ((data >> 48) & 0xff);
+ p[2] = (uint8_t) ((data >> 40) & 0xff);
+ p[3] = (uint8_t) ((data >> 32) & 0xff);
+ p[4] = (uint8_t) ((data >> 24) & 0xff);
+ p[5] = (uint8_t) ((data >> 16) & 0xff);
+ p[6] = (uint8_t) ((data >> 8) & 0xff);
+ p[7] = (uint8_t) (data & 0xff);
+}
+
 /*
  * Copy data allowing for unaligned accesses in network byte order
  * (big endian).
@@ -224,6 +238,21 @@ read_uint32(const void *src)
 #endif
 }
 
+static inline uint64_t
+read_uint64(const void *src)
+{
+ uint8_t *p = (uint8_t *) src;
+ return
+    ((uint64_t)p[0] << 56) |
+    ((uint64_t)p[1] << 48) |
+    ((uint64_t)p[2] << 40) |
+    ((uint64_t)p[3] << 32) |
+    ((uint64_t)p[4] << 24) |
+    ((uint64_t)p[5] << 16) |
+    ((uint64_t)p[6] <<  8) |
+    (uint64_t)p[7];
+}
+
 /*
  * Print debugging information using log_msg,
  * set the logfile as /dev/stdout or /dev/stderr if you like.
diff --git xfrd-disk.c xfrd-disk.c
index 3fa8630..654e78e 100644
--- xfrd-disk.c
+++ xfrd-disk.c
@@ -147,6 +147,7 @@ xfrd_read_state(struct xfrd_state* xfrd)
  uint32_t filetime = 0;
  uint32_t numzones, i;
  region_type *tempregion;
+ time_t soa_refresh;
 
  tempregion = region_create(xalloc, free);
  if(!tempregion)
@@ -265,10 +266,15 @@ xfrd_read_state(struct xfrd_state* xfrd)
  * or there is a notification,
  * or there is a soa && current time is past refresh point
  */
+ soa_refresh = ntohl(soa_disk_read.refresh);
+ if (soa_refresh > zone->zone_options->pattern->max_refresh_time)
+ soa_refresh = zone->zone_options->pattern->max_refresh_time;
+ else if (soa_refresh < zone->zone_options->pattern->min_refresh_time)
+ soa_refresh = zone->zone_options->pattern->min_refresh_time;
  if(timeout == 0 || soa_notified_acquired_read != 0 ||
  (soa_disk_acquired_read != 0 &&
  (uint32_t)xfrd_time() - soa_disk_acquired_read
- > ntohl(soa_disk_read.refresh)))
+ > soa_refresh))
  {
  zone->state = xfrd_zone_refreshing;
  xfrd_set_refresh_now(zone);
@@ -564,3 +570,17 @@ xfrd_unlink_xfrfile(struct nsd* nsd, uint64_t number)
  strerror(errno));
  }
 }
+
+uint64_t
+xfrd_get_xfrfile_size(struct nsd* nsd, uint64_t number )
+{
+ char fname[1024];
+ struct stat tempxfr_stat;
+ tempxfrname(fname, sizeof(fname), nsd, number);
+ if( stat( fname, &tempxfr_stat ) < 0 ) {
+    log_msg(LOG_WARNING, "could not get file size %s: %s", fname,
+    strerror(errno));
+    return 0;
+ }
+ return (uint64_t)tempxfr_stat.st_size;
+}
diff --git xfrd-disk.h xfrd-disk.h
index 2c8e23f..b7e2d10 100644
--- xfrd-disk.h
+++ xfrd-disk.h
@@ -29,5 +29,7 @@ void xfrd_del_tempdir(struct nsd* nsd);
 FILE* xfrd_open_xfrfile(struct nsd* nsd, uint64_t number, char* mode);
 /* unlink temp file */
 void xfrd_unlink_xfrfile(struct nsd* nsd, uint64_t number);
+/* get temp file size */
+uint64_t xfrd_get_xfrfile_size(struct nsd* nsd, uint64_t number );
 
 #endif /* XFRD_DISK_H */
diff --git xfrd.c xfrd.c
index c2c75ed..0eacce7 100644
--- xfrd.c
+++ xfrd.c
@@ -702,7 +702,12 @@ xfrd_set_timer_refresh(xfrd_zone_t* zone)
  return;
  }
  /* refresh or expire timeout, whichever is earlier */
- set_refresh = zone->soa_disk_acquired + ntohl(zone->soa_disk.refresh);
+ set_refresh = ntohl(zone->soa_disk.refresh);
+ if (set_refresh > zone->zone_options->pattern->max_refresh_time)
+ set_refresh = zone->zone_options->pattern->max_refresh_time;
+ else if (set_refresh < zone->zone_options->pattern->min_refresh_time)
+ set_refresh = zone->zone_options->pattern->min_refresh_time;
+ set_refresh += zone->soa_disk_acquired;
  set_expire = zone->soa_disk_acquired + ntohl(zone->soa_disk.expire);
  if(set_refresh < set_expire)
  set = set_refresh;
@@ -719,6 +724,7 @@ xfrd_set_timer_refresh(xfrd_zone_t* zone)
 static void
 xfrd_set_timer_retry(xfrd_zone_t* zone)
 {
+ time_t set_retry;
  /* set timer for next retry or expire timeout if earlier. */
  if(zone->soa_disk_acquired == 0) {
  /* if no information, use reasonable timeout */
@@ -743,10 +749,14 @@ xfrd_set_timer_retry(xfrd_zone_t* zone)
  xfrd_time() + (time_t)ntohl(zone->soa_disk.retry) <
  zone->soa_disk_acquired + (time_t)ntohl(zone->soa_disk.expire))
  {
- if(ntohl(zone->soa_disk.retry) < XFRD_LOWERBOUND_RETRY)
- xfrd_set_timer(zone, XFRD_LOWERBOUND_RETRY);
- else
- xfrd_set_timer(zone, ntohl(zone->soa_disk.retry));
+ set_retry = ntohl(zone->soa_disk.retry);
+ if(set_retry > zone->zone_options->pattern->max_retry_time)
+ set_retry = zone->zone_options->pattern->max_retry_time;
+ else if(set_retry < zone->zone_options->pattern->min_retry_time)
+ set_retry = zone->zone_options->pattern->min_retry_time;
+ if(set_retry < XFRD_LOWERBOUND_RETRY)
+ set_retry =  XFRD_LOWERBOUND_RETRY;
+ xfrd_set_timer(zone, set_retry);
  } else {
  if(ntohl(zone->soa_disk.expire) < XFRD_LOWERBOUND_RETRY)
  xfrd_set_timer(zone, XFRD_LOWERBOUND_RETRY);
@@ -1918,6 +1928,7 @@ xfrd_handle_received_xfr_packet(xfrd_zone_t* zone, buffer_type* packet)
 {
  xfrd_soa_t soa;
  enum xfrd_packet_result res;
+        uint64_t xfrfile_size;
 
  /* parse and check the packet - see if it ends the xfr */
  switch((res=xfrd_parse_received_xfr_packet(zone, packet, &soa)))
@@ -1975,6 +1986,15 @@ xfrd_handle_received_xfr_packet(xfrd_zone_t* zone, buffer_type* packet)
  "disk", zone->apex_str, zone->master->ip_address_spec,
  (int)zone->msg_new_serial));
  zone->msg_seq_nr++;
+
+        xfrfile_size = xfrd_get_xfrfile_size(xfrd->nsd, zone->xfrfilenumber);
+ if( zone->zone_options->pattern->size_limit_xfr != 0 &&
+    xfrfile_size > zone->zone_options->pattern->size_limit_xfr ) {
+            /*    xfrd_unlink_xfrfile(xfrd->nsd, zone->xfrfilenumber);
+                    xfrd_set_reload_timeout(); */
+            log_msg(LOG_INFO, "xfrd : transfered zone data was too large %llu", (long long unsigned)xfrfile_size);
+    return xfrd_packet_bad;
+ }
  if(res == xfrd_packet_more) {
  /* wait for more */
  return xfrd_packet_more;
diff --git zonec.c zonec.c
index 666b0cc..c186171 100644
--- zonec.c
+++ zonec.c
@@ -953,7 +953,10 @@ zparser_conv_loc(region_type *region, char *str)
  }
 
  /* Meters of altitude... */
- (void) strtol(str, &str, 10);
+ if(strtol(str, &str, 10) == LONG_MAX) {
+ zc_error_prev_line("altitude too large, number overflow");
+ return NULL;
+ }
  switch(*str) {
  case ' ':
  case '\0':
@@ -1576,21 +1579,21 @@ zonec_read(const char* name, const char* zonefile, zone_type* zone)
  dname = dname_parse(parser->rr_region, name);
  if (!dname) {
  zc_error("incorrect zone name '%s'", name);
- return 0;
+ return 1;
  }
 
 #ifndef ROOT_SERVER
  /* Is it a root zone? Are we a root server then? Idiot proof. */
  if (dname->label_count == 1) {
  zc_error("not configured as a root server");
- return 0;
+ return 1;
  }
 #endif
 
  /* Open the zone file */
  if (!zone_open(zonefile, 3600, CLASS_IN, dname)) {
  zc_error("cannot open '%s': %s", zonefile, strerror(errno));
- return 0;
+ return 1;
  }
  parser->current_zone = zone;
 

--
I'm not entirely sure you are real.

Reply | Threaded
Open this post in threaded view
|

Re: nsd 4.1.11

Florian Obser-2
On Tue, Aug 16, 2016 at 07:00:39PM +0000, Florian Obser wrote:
> OK?
>

anyone?


diff --git buffer.h buffer.h
index bee7d8b..9e17bc9 100644
--- buffer.h
+++ buffer.h
@@ -315,6 +315,20 @@ buffer_write_u32(buffer_type *buffer, uint32_t data)
 }
 
 static inline void
+buffer_write_u64_at(buffer_type *buffer, size_t at, uint64_t data)
+{
+ assert(buffer_available_at(buffer, at, sizeof(data)));
+ write_uint64(buffer->_data + at, data);
+}
+
+static inline void
+buffer_write_u64(buffer_type *buffer, uint64_t data)
+{
+ buffer_write_u64_at(buffer, buffer->_position, data);
+ buffer->_position += sizeof(data);
+}
+
+static inline void
 buffer_read_at(buffer_type *buffer, size_t at, void *data, size_t count)
 {
  assert(buffer_available_at(buffer, at, count));
@@ -373,6 +387,21 @@ buffer_read_u32(buffer_type *buffer)
  return result;
 }
 
+static inline uint64_t
+buffer_read_u64_at(buffer_type *buffer, size_t at)
+{
+ assert(buffer_available_at(buffer, at, sizeof(uint64_t)));
+ return read_uint64(buffer->_data + at);
+}
+
+static inline uint64_t
+buffer_read_u64(buffer_type *buffer)
+{
+ uint64_t result = buffer_read_u64_at(buffer, buffer->_position);
+ buffer->_position += sizeof(uint64_t);
+ return result;
+}
+
 /*
  * Print to the buffer, increasing the capacity if required using
  * buffer_reserve(). The buffer's position is set to the terminating
diff --git configlexer.lex configlexer.lex
index 113fa22..d536352 100644
--- configlexer.lex
+++ configlexer.lex
@@ -236,6 +236,7 @@ zone{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_ZONE;}
 zonefile{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_ZONEFILE;}
 zonestats{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_ZONESTATS;}
 allow-notify{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_ALLOW_NOTIFY;}
+size-limit-xfr{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_SIZE_LIMIT_XFR;}
 request-xfr{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_REQUEST_XFR;}
 notify{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_NOTIFY;}
 notify-retry{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_NOTIFY_RETRY;}
@@ -268,6 +269,10 @@ 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;}
+max-refresh-time{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_MAX_REFRESH_TIME;}
+min-refresh-time{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_MIN_REFRESH_TIME;}
+max-retry-time{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_MAX_RETRY_TIME;}
+min-retry-time{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_MIN_RETRY_TIME;}
 {NEWLINE} { LEXOUT(("NL\n")); cfg_parser->line++;}
 
  /* Quoted strings. Strip leading and ending quotes */
diff --git configparser.y configparser.y
index 1d824d1..9089665 100644
--- configparser.y
+++ configparser.y
@@ -54,7 +54,7 @@ extern config_parser_state_t* cfg_parser;
 %token VAR_TCP_MSS VAR_OUTGOING_TCP_MSS VAR_IP_FREEBIND
 %token VAR_ZONEFILE
 %token VAR_ZONE
-%token VAR_ALLOW_NOTIFY VAR_REQUEST_XFR VAR_NOTIFY VAR_PROVIDE_XFR
+%token VAR_ALLOW_NOTIFY VAR_REQUEST_XFR VAR_NOTIFY VAR_PROVIDE_XFR VAR_SIZE_LIMIT_XFR
 %token VAR_NOTIFY_RETRY VAR_OUTGOING_INTERFACE VAR_ALLOW_AXFR_FALLBACK
 %token VAR_KEY
 %token VAR_ALGORITHM VAR_SECRET
@@ -69,6 +69,8 @@ extern config_parser_state_t* cfg_parser;
 %token VAR_RRL_WHITELIST_RATELIMIT VAR_RRL_WHITELIST
 %token VAR_ZONEFILES_CHECK VAR_ZONEFILES_WRITE VAR_LOG_TIME_ASCII
 %token VAR_ROUND_ROBIN VAR_ZONESTATS VAR_REUSEPORT VAR_VERSION
+%token VAR_MAX_REFRESH_TIME VAR_MIN_REFRESH_TIME
+%token VAR_MAX_RETRY_TIME VAR_MIN_RETRY_TIME
 
 %%
 toplevelvars: /* empty */ | toplevelvars toplevelvar ;
@@ -598,7 +600,9 @@ content_pattern: pattern_name | zone_config_item;
 zone_config_item: zone_zonefile | zone_allow_notify | zone_request_xfr |
  zone_notify | zone_notify_retry | zone_provide_xfr |
  zone_outgoing_interface | zone_allow_axfr_fallback | include_pattern |
- zone_rrl_whitelist | zone_zonestats;
+ zone_rrl_whitelist | zone_zonestats | zone_max_refresh_time |
+ zone_min_refresh_time | zone_max_retry_time | zone_min_retry_time |
+ zone_size_limit_xfr;
 pattern_name: VAR_NAME STRING
  {
  OUTYY(("P(pattern_name:%s)\n", $2));
@@ -714,6 +718,14 @@ zone_request_xfr: VAR_REQUEST_XFR zone_request_xfr_data
  {
  }
  ;
+zone_size_limit_xfr: VAR_SIZE_LIMIT_XFR STRING
+ {
+ OUTYY(("P(size_limit_xfr:%s)\n", $2));
+ if(atoll($2) < 0)
+ yyerror("number >= 0 expected");
+ else cfg_parser->current_pattern->size_limit_xfr = atoll($2);
+ }
+ ;
 zone_request_xfr_data: STRING STRING
  {
  acl_options_t* acl = parse_acl_info(cfg_parser->opt->region, $1, $2);
@@ -819,6 +831,46 @@ zone_rrl_whitelist: VAR_RRL_WHITELIST STRING
 #endif
  }
  ;
+zone_max_refresh_time: VAR_MAX_REFRESH_TIME STRING
+{
+ OUTYY(("P(zone_max_refresh_time:%s)\n", $2));
+ if(atoi($2) == 0 && strcmp($2, "0") != 0)
+ yyerror("number expected");
+ else {
+ cfg_parser->current_pattern->max_refresh_time = atoi($2);
+ cfg_parser->current_pattern->max_refresh_time_is_default = 0;
+ }
+};
+zone_min_refresh_time: VAR_MIN_REFRESH_TIME STRING
+{
+ OUTYY(("P(zone_min_refresh_time:%s)\n", $2));
+ if(atoi($2) == 0 && strcmp($2, "0") != 0)
+ yyerror("number expected");
+ else {
+ cfg_parser->current_pattern->min_refresh_time = atoi($2);
+ cfg_parser->current_pattern->min_refresh_time_is_default = 0;
+ }
+};
+zone_max_retry_time: VAR_MAX_RETRY_TIME STRING
+{
+ OUTYY(("P(zone_max_retry_time:%s)\n", $2));
+ if(atoi($2) == 0 && strcmp($2, "0") != 0)
+ yyerror("number expected");
+ else {
+ cfg_parser->current_pattern->max_retry_time = atoi($2);
+ cfg_parser->current_pattern->max_retry_time_is_default = 0;
+ }
+};
+zone_min_retry_time: VAR_MIN_RETRY_TIME STRING
+{
+ OUTYY(("P(zone_min_retry_time:%s)\n", $2));
+ if(atoi($2) == 0 && strcmp($2, "0") != 0)
+ yyerror("number expected");
+ else {
+ cfg_parser->current_pattern->min_retry_time = atoi($2);
+ cfg_parser->current_pattern->min_retry_time_is_default = 0;
+ }
+};
 
 /* key: declaration */
 keystart: VAR_KEY
diff --git configure configure
index a525e08..fc6a02b 100644
--- configure
+++ configure
@@ -1,6 +1,6 @@
 #! /bin/sh
 # Guess values for system-dependent variables and create Makefiles.
-# Generated by GNU Autoconf 2.69 for NSD 4.1.10.
+# Generated by GNU Autoconf 2.69 for NSD 4.1.11.
 #
 # Report bugs to <[hidden email]>.
 #
@@ -580,8 +580,8 @@ MAKEFLAGS=
 # Identity of this package.
 PACKAGE_NAME='NSD'
 PACKAGE_TARNAME='nsd'
-PACKAGE_VERSION='4.1.10'
-PACKAGE_STRING='NSD 4.1.10'
+PACKAGE_VERSION='4.1.11'
+PACKAGE_STRING='NSD 4.1.11'
 PACKAGE_BUGREPORT='[hidden email]'
 PACKAGE_URL=''
 
@@ -1283,7 +1283,7 @@ if test "$ac_init_help" = "long"; then
   # Omit some internal or obsolete options to make the list less imposing.
   # This message is too long to be a string in the A/UX 3.1 sh.
   cat <<_ACEOF
-\`configure' configures NSD 4.1.10 to adapt to many kinds of systems.
+\`configure' configures NSD 4.1.11 to adapt to many kinds of systems.
 
 Usage: $0 [OPTION]... [VAR=VALUE]...
 
@@ -1344,7 +1344,7 @@ fi
 
 if test -n "$ac_init_help"; then
   case $ac_init_help in
-     short | recursive ) echo "Configuration of NSD 4.1.10:";;
+     short | recursive ) echo "Configuration of NSD 4.1.11:";;
    esac
   cat <<\_ACEOF
 
@@ -1487,7 +1487,7 @@ fi
 test -n "$ac_init_help" && exit $ac_status
 if $ac_init_version; then
   cat <<\_ACEOF
-NSD configure 4.1.10
+NSD configure 4.1.11
 generated by GNU Autoconf 2.69
 
 Copyright (C) 2012 Free Software Foundation, Inc.
@@ -2196,7 +2196,7 @@ cat >config.log <<_ACEOF
 This file contains any messages produced by compilers while
 running configure, to aid debugging if configure makes a mistake.
 
-It was created by NSD $as_me 4.1.10, which was
+It was created by NSD $as_me 4.1.11, which was
 generated by GNU Autoconf 2.69.  Invocation command line was
 
   $ $0 $@
@@ -9681,7 +9681,7 @@ cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
 # report actual input values of CONFIG_FILES etc. instead of their
 # values after options handling.
 ac_log="
-This file was extended by NSD $as_me 4.1.10, which was
+This file was extended by NSD $as_me 4.1.11, which was
 generated by GNU Autoconf 2.69.  Invocation command line was
 
   CONFIG_FILES    = $CONFIG_FILES
@@ -9743,7 +9743,7 @@ _ACEOF
 cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
 ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`"
 ac_cs_version="\\
-NSD config.status 4.1.10
+NSD config.status 4.1.11
 configured by $0, generated by GNU Autoconf 2.69,
   with options \\"\$ac_cs_config\\"
 
diff --git configure.ac configure.ac
index 4c451bd..da75edc 100644
--- configure.ac
+++ configure.ac
@@ -4,7 +4,7 @@ dnl
 
 sinclude(acx_nlnetlabs.m4)
 
-AC_INIT(NSD,4.1.10,[hidden email])
+AC_INIT(NSD,4.1.11,[hidden email])
 AC_CONFIG_HEADER([config.h])
 
 CFLAGS="$CFLAGS"
diff --git ipc.c ipc.c
index 1f188e5..8b5cba4 100644
--- ipc.c
+++ ipc.c
@@ -82,7 +82,10 @@ child_handle_parent_command(int fd, short event, void* arg)
  server_close_all_sockets(data->nsd->udp, data->nsd->ifs);
  server_close_all_sockets(data->nsd->tcp, data->nsd->ifs);
  /* mode == NSD_QUIT_CHILD */
- (void)write(fd, &mode, sizeof(mode));
+ if(write(fd, &mode, sizeof(mode)) == -1) {
+ VERBOSITY(3, (LOG_INFO, "quit child write: %s",
+ strerror(errno)));
+ }
  ipc_child_quit(data->nsd);
  break;
  case NSD_QUIT_WITH_STATS:
diff --git namedb.c namedb.c
index db90965..dbe8efd 100644
--- namedb.c
+++ namedb.c
@@ -533,7 +533,7 @@ domain_find_zone(namedb_type* db, domain_type* domain)
 }
 
 zone_type *
-domain_find_parent_zone(zone_type* zone)
+domain_find_parent_zone(namedb_type* db, zone_type* zone)
 {
  rrset_type* rrset;
 
@@ -544,6 +544,10 @@ domain_find_parent_zone(zone_type* zone)
  return rrset->zone;
  }
  }
+ /* the NS record in the parent zone above this zone is not present,
+ * workaround to find that parent zone anyway */
+ if(zone->apex->parent)
+ return domain_find_zone(db, zone->apex->parent);
  return NULL;
 }
 
diff --git namedb.h namedb.h
index ca46477..e5cf36c 100644
--- namedb.h
+++ namedb.h
@@ -232,7 +232,7 @@ rrset_type* domain_find_rrset(domain_type* domain, zone_type* zone, uint16_t typ
 rrset_type* domain_find_any_rrset(domain_type* domain, zone_type* zone);
 
 zone_type* domain_find_zone(namedb_type* db, domain_type* domain);
-zone_type* domain_find_parent_zone(zone_type* zone);
+zone_type* domain_find_parent_zone(namedb_type* db, zone_type* zone);
 
 domain_type* domain_find_ns_rrsets(domain_type* domain, zone_type* zone, rrset_type **ns);
 /* find DNAME rrset in domain->parent or higher and return that domain */
diff --git nsd-checkconf.c nsd-checkconf.c
index e5f669f..b7afdd1 100644
--- nsd-checkconf.c
+++ nsd-checkconf.c
@@ -59,6 +59,12 @@ extern int optind;
  return; \
  }
 
+#define ZONE_GET_INT(NAME, VAR, PATTERN) \
+ if (strcasecmp(#NAME, (VAR)) == 0) { \
+ printf("%d\n", (int) PATTERN->NAME); \
+ return; \
+ }
+
 #define SERV_GET_BIN(NAME, VAR) \
  if (strcasecmp(#NAME, (VAR)) == 0) { \
  printf("%s\n", opt->NAME?"yes":"no"); \
@@ -306,6 +312,11 @@ config_print_zone(nsd_options_t* opt, const char* k, int s, const char *o,
  ZONE_GET_STR(zonestats, o, zone->pattern);
  ZONE_GET_OUTGOING(outgoing_interface, o, zone->pattern);
  ZONE_GET_BIN(allow_axfr_fallback, o, zone->pattern);
+ ZONE_GET_INT(max_refresh_time, o, zone->pattern);
+ ZONE_GET_INT(min_refresh_time, o, zone->pattern);
+ ZONE_GET_INT(max_retry_time, o, zone->pattern);
+ ZONE_GET_INT(min_retry_time, o, zone->pattern);
+ ZONE_GET_INT(size_limit_xfr, o, zone->pattern);
 #ifdef RATELIMIT
  ZONE_GET_RRL(rrl_whitelist, o, zone->pattern);
 #endif
@@ -331,6 +342,11 @@ config_print_zone(nsd_options_t* opt, const char* k, int s, const char *o,
  ZONE_GET_STR(zonestats, o, p);
  ZONE_GET_OUTGOING(outgoing_interface, o, p);
  ZONE_GET_BIN(allow_axfr_fallback, o, p);
+ ZONE_GET_INT(max_refresh_time, o, p);
+ ZONE_GET_INT(min_refresh_time, o, p);
+ ZONE_GET_INT(max_retry_time, o, p);
+ ZONE_GET_INT(min_retry_time, o, p);
+ ZONE_GET_INT(size_limit_xfr, o, p);
 #ifdef RATELIMIT
  ZONE_GET_RRL(rrl_whitelist, o, p);
 #endif
@@ -431,6 +447,17 @@ static void print_zone_content_elems(pattern_options_t* pat)
  if(!pat->allow_axfr_fallback_is_default)
  printf("\tallow-axfr-fallback: %s\n",
  pat->allow_axfr_fallback?"yes":"no");
+ if(!pat->max_refresh_time_is_default)
+ printf("\tmax-refresh-time: %d\n", pat->max_refresh_time);
+ if(!pat->min_refresh_time_is_default)
+ printf("\tmin-refresh-time: %d\n", pat->min_refresh_time);
+ if(!pat->max_retry_time_is_default)
+ printf("\tmax-retry-time: %d\n", pat->max_retry_time);
+ if(!pat->min_retry_time_is_default)
+ printf("\tmin-retry-time: %d\n", pat->min_retry_time);
+ if(pat->size_limit_xfr != 0)
+ printf("\tsize-limit-xfr: %llu\n",
+ (long long unsigned)pat->size_limit_xfr);
 }
 
 void
@@ -559,6 +586,13 @@ additional_checks(nsd_options_t* opt, const char* filename)
  fprintf(stderr, "%s: cannot parse zone name syntax for zone %s.\n", filename, zone->name);
  errors ++;
  }
+#ifndef ROOT_SERVER
+ /* Is it a root zone? Are we a root server then? Idiot proof. */
+ if(dname->label_count == 1) {
+ fprintf(stderr, "%s: not configured as a root server.\n", filename);
+ errors ++;
+ }
+#endif
  if(zone->pattern->allow_notify && !zone->pattern->request_xfr) {
  fprintf(stderr, "%s: zone %s has allow-notify but no request-xfr"
  " items. Where can it get a zone transfer when a notify "
diff --git nsd.conf.5.in nsd.conf.5.in
index 44493c7..bcec054 100644
--- nsd.conf.5.in
+++ nsd.conf.5.in
@@ -583,6 +583,10 @@ transmitted using TCP.
 This option should be accompanied by request\-xfr. It (dis)allows NSD (as secondary)
 to fallback to AXFR if the primary name server does not support IXFR. Default is yes.
 .TP
+.B size\-limit\-xfr:\fR <number>
+This option should be accompanied by request\-xfr. It specifies XFR temporary file size limit.  It can be used to stop very large zone retrieval, that could otherwise use up a lot of memory and disk space.
+If this option is 0, unlimited. Default value is 0.
+.TP
 .B notify:\fR <ip\-address> <key\-name | NOKEY>
 Access control list. The listed address (a secondary) is notified
 of updates to this zone. A port number can be added using a suffix of @number,
@@ -624,6 +628,22 @@ A port number can be added using a suffix of @number, for example
 1.2.3.4@5300.
 .RE
 .TP
+.B max\-refresh\-time:\fR <seconds>
+Limit refresh time for secondary zones.  This is the timer which checks to see
+if the zone has to be refetched when it expires.  Normally the value from the
+SOA record is used, but this option restricts that value.
+.TP
+.B min\-refresh\-time:\fR <seconds>
+Limit refresh time for secondary zones.
+.TP
+.B max\-retry\-time:\fR <seconds>
+Limit retry time for secondary zones.  This is the timeout after a failed
+fetch attempt for the zone.  Normally the value from the SOA record is used,
+but this option restricts that value.
+.TP
+.B min\-retry\-time:\fR <seconds>
+Limit retry time for secondary zones.
+.TP
 .B zonestats:\fR <name>
 When compiled with \-\-enable\-zone\-stats NSD can collect statistics per zone.
 This name gives the group where statistics are added to.  The groups are
diff --git nsd.conf.sample.in nsd.conf.sample.in
index 48eef14..cda7dd0 100644
--- nsd.conf.sample.in
+++ nsd.conf.sample.in
@@ -258,6 +258,15 @@ remote-control:
  # set local interface for sending zone transfer requests.
  # default is let the OS choose.
  #outgoing-interface: 10.0.0.10
+ # limit the refresh and retry interval in seconds.
+ #max-refresh-time: 2419200
+ #min-refresh-time: 0
+ #max-retry-time: 1209600
+ #min-retry-time: 0
+
+ # limit the zone transfer size (in bytes), stops very large transfers
+ # 0 is no limits enforced.
+ # size-limit-xfr: 0
 
  # if compiled with --enable-zone-stats, give name of stat block for
  # this zone (or group of zones).  Output from nsd-control stats.
diff --git nsec3.c nsec3.c
index d4fd1a2..bad5af8 100644
--- nsec3.c
+++ nsec3.c
@@ -893,7 +893,9 @@ nsec3_add_ds_proof(struct query *query, struct answer *answer,
  /* use NSEC3 record from above the zone cut. */
  nsec3_add_rrset(query, answer, AUTHORITY_SECTION,
  domain->nsec3->nsec3_ds_parent_cover);
- } else if (!delegpt && domain->nsec3 && domain->nsec3->nsec3_is_exact) {
+ } else if (!delegpt && domain->nsec3 && domain->nsec3->nsec3_is_exact
+ && nsec3_domain_part_of_zone(domain->nsec3->nsec3_cover,
+ query->zone)) {
  nsec3_add_rrset(query, answer, AUTHORITY_SECTION,
  domain->nsec3->nsec3_cover);
  } else {
diff --git options.c options.c
index 349e271..058ceec 100644
--- options.c
+++ options.c
@@ -813,6 +813,7 @@ pattern_options_create(region_type* region)
  p->zonestats = 0;
  p->allow_notify = 0;
  p->request_xfr = 0;
+ p->size_limit_xfr = 0;
  p->notify = 0;
  p->provide_xfr = 0;
  p->outgoing_interface = 0;
@@ -822,6 +823,14 @@ pattern_options_create(region_type* region)
  p->allow_axfr_fallback_is_default = 1;
  p->implicit = 0;
  p->xfrd_flags = 0;
+ p->max_refresh_time = 2419200; /* 4 weeks */
+ p->max_refresh_time_is_default = 1;
+ p->min_refresh_time = 0;
+ p->min_refresh_time_is_default = 1;
+ p->max_retry_time = 1209600; /* 2 weeks */
+ p->max_retry_time_is_default = 1;
+ p->min_retry_time = 0;
+ p->min_retry_time_is_default = 1;
 #ifdef RATELIMIT
  p->rrl_whitelist = 0;
 #endif
@@ -944,6 +953,14 @@ copy_pat_fixed(region_type* region, pattern_options_t* orig,
  if(p->zonestats)
  orig->zonestats = region_strdup(region, p->zonestats);
  else orig->zonestats = NULL;
+ orig->max_refresh_time = p->max_refresh_time;
+ orig->max_refresh_time_is_default = p->max_refresh_time_is_default;
+ orig->min_refresh_time = p->min_refresh_time;
+ orig->min_refresh_time_is_default = p->min_refresh_time_is_default;
+ orig->max_retry_time = p->max_retry_time;
+ orig->max_retry_time_is_default = p->max_retry_time_is_default;
+ orig->min_retry_time = p->min_retry_time;
+ orig->min_retry_time_is_default = p->min_retry_time_is_default;
 #ifdef RATELIMIT
  orig->rrl_whitelist = p->rrl_whitelist;
 #endif
@@ -1017,9 +1034,22 @@ pattern_options_equal(pattern_options_t* p, pattern_options_t* q)
  if(!acl_list_equal(p->provide_xfr, q->provide_xfr)) return 0;
  if(!acl_list_equal(p->outgoing_interface, q->outgoing_interface))
  return 0;
+ if(p->max_refresh_time != q->max_refresh_time) return 0;
+ if(!booleq(p->max_refresh_time_is_default,
+ q->max_refresh_time_is_default)) return 0;
+ if(p->min_refresh_time != q->min_refresh_time) return 0;
+ if(!booleq(p->min_refresh_time_is_default,
+ q->min_refresh_time_is_default)) return 0;
+ if(p->max_retry_time != q->max_retry_time) return 0;
+ if(!booleq(p->max_retry_time_is_default,
+ q->max_retry_time_is_default)) return 0;
+ if(p->min_retry_time != q->min_retry_time) return 0;
+ if(!booleq(p->min_retry_time_is_default,
+ q->min_retry_time_is_default)) return 0;
 #ifdef RATELIMIT
  if(p->rrl_whitelist != q->rrl_whitelist) return 0;
 #endif
+ if(p->size_limit_xfr != q->size_limit_xfr) return 0;
  return 1;
 }
 
@@ -1036,6 +1066,19 @@ unmarshal_u8(struct buffer* b)
  return buffer_read_u8(b);
 }
 
+static void
+marshal_u64(struct buffer* b, uint64_t v)
+{
+ buffer_reserve(b, 8);
+ buffer_write_u64(b, v);
+}
+
+static uint64_t
+unmarshal_u64(struct buffer* b)
+{
+ return buffer_read_u64(b);
+}
+
 #ifdef RATELIMIT
 static void
 marshal_u16(struct buffer* b, uint16_t v)
@@ -1054,6 +1097,19 @@ unmarshal_u16(struct buffer* b)
 #endif
 
 static void
+marshal_u32(struct buffer* b, uint32_t v)
+{
+ buffer_reserve(b, 4);
+ buffer_write_u32(b, v);
+}
+
+static uint32_t
+unmarshal_u32(struct buffer* b)
+{
+ return buffer_read_u32(b);
+}
+
+static void
 marshal_str(struct buffer* b, const char* s)
 {
  if(!s) marshal_u8(b, 0);
@@ -1138,11 +1194,20 @@ pattern_options_marshal(struct buffer* b, pattern_options_t* p)
  marshal_u8(b, p->notify_retry);
  marshal_u8(b, p->notify_retry_is_default);
  marshal_u8(b, p->implicit);
+ marshal_u64(b, p->size_limit_xfr);
  marshal_acl_list(b, p->allow_notify);
  marshal_acl_list(b, p->request_xfr);
  marshal_acl_list(b, p->notify);
  marshal_acl_list(b, p->provide_xfr);
  marshal_acl_list(b, p->outgoing_interface);
+ marshal_u32(b, p->max_refresh_time);
+ marshal_u8(b, p->max_refresh_time_is_default);
+ marshal_u32(b, p->min_refresh_time);
+ marshal_u8(b, p->min_refresh_time_is_default);
+ marshal_u32(b, p->max_retry_time);
+ marshal_u8(b, p->max_retry_time_is_default);
+ marshal_u32(b, p->min_retry_time);
+ marshal_u8(b, p->min_retry_time_is_default);
 }
 
 pattern_options_t*
@@ -1160,11 +1225,20 @@ pattern_options_unmarshal(region_type* r, struct buffer* b)
  p->notify_retry = unmarshal_u8(b);
  p->notify_retry_is_default = unmarshal_u8(b);
  p->implicit = unmarshal_u8(b);
+ p->size_limit_xfr = unmarshal_u64(b);
  p->allow_notify = unmarshal_acl_list(r, b);
  p->request_xfr = unmarshal_acl_list(r, b);
  p->notify = unmarshal_acl_list(r, b);
  p->provide_xfr = unmarshal_acl_list(r, b);
  p->outgoing_interface = unmarshal_acl_list(r, b);
+ p->max_refresh_time = unmarshal_u32(b);
+ p->max_refresh_time_is_default = unmarshal_u8(b);
+ p->min_refresh_time = unmarshal_u32(b);
+ p->min_refresh_time_is_default = unmarshal_u8(b);
+ p->max_retry_time = unmarshal_u32(b);
+ p->max_retry_time_is_default = unmarshal_u8(b);
+ p->min_retry_time = unmarshal_u32(b);
+ p->min_retry_time_is_default = unmarshal_u8(b);
  return p;
 }
 
@@ -1875,6 +1949,23 @@ config_apply_pattern(const char* name)
  a->notify_retry = pat->notify_retry;
  a->notify_retry_is_default = 0;
  }
+ if(!pat->max_refresh_time_is_default) {
+ a->max_refresh_time = pat->max_refresh_time;
+ a->max_refresh_time_is_default = 0;
+ }
+ if(!pat->min_refresh_time_is_default) {
+ a->min_refresh_time = pat->min_refresh_time;
+ a->min_refresh_time_is_default = 0;
+ }
+ if(!pat->max_retry_time_is_default) {
+ a->max_retry_time = pat->max_retry_time;
+ a->max_retry_time_is_default = 0;
+ }
+ if(!pat->min_retry_time_is_default) {
+ a->min_retry_time = pat->min_retry_time;
+ a->min_retry_time_is_default = 0;
+ }
+ a->size_limit_xfr = pat->size_limit_xfr;
 #ifdef RATELIMIT
  a->rrl_whitelist |= pat->rrl_whitelist;
 #endif
diff --git options.h options.h
index ceba624..e0f749c 100644
--- options.h
+++ options.h
@@ -154,6 +154,15 @@ struct pattern_options {
  uint8_t notify_retry_is_default;
  uint8_t implicit; /* pattern is implicit, part_of_config zone used */
  uint8_t xfrd_flags;
+ uint32_t max_refresh_time;
+ uint8_t max_refresh_time_is_default;
+ uint32_t min_refresh_time;
+ uint8_t min_refresh_time_is_default;
+ uint32_t max_retry_time;
+ uint8_t max_retry_time_is_default;
+ uint32_t min_retry_time;
+ uint8_t min_retry_time_is_default;
+ uint64_t size_limit_xfr;
 };
 
 #define PATTERN_IMPLICIT_MARKER "_implicit_"
diff --git query.c query.c
index f8a429c..7256449 100644
--- query.c
+++ query.c
@@ -704,11 +704,16 @@ add_rrset(struct query   *query,
  result = answer_add_rrset(answer, section, owner, rrset);
  switch (rrset_rrtype(rrset)) {
  case TYPE_NS:
+#if defined(INET6)
  /* if query over IPv6, swap A and AAAA; put AAAA first */
  add_additional_rrsets(query, answer, rrset, 0, 1,
  (query->addr.ss_family == AF_INET6)?
  swap_aaaa_additional_rr_types:
  default_additional_rr_types);
+#else
+ add_additional_rrsets(query, answer, rrset, 0, 1,
+      default_additional_rr_types);
+#endif
  break;
  case TYPE_MB:
  add_additional_rrsets(query, answer, rrset, 0, 0,
@@ -1205,7 +1210,7 @@ answer_lookup_zone(struct nsd *nsd, struct query *q, answer_type *answer,
  * parent zone to generate the answer if we are
  * authoritative for the parent zone.
  */
- zone_type *zone = domain_find_parent_zone(q->zone);
+ zone_type *zone = domain_find_parent_zone(nsd->db, q->zone);
  if (zone)
  q->zone = zone;
  }
diff --git server.c server.c
index 530b443..ae27312 100644
--- server.c
+++ server.c
@@ -160,6 +160,11 @@ struct tcp_handler_data
  * The number of queries handled by this specific TCP connection.
  */
  int query_count;
+
+ /*
+ * The timeout in msec for this tcp connection
+ */
+ int tcp_timeout;
 };
 
 /*
@@ -2630,8 +2635,8 @@ handle_tcp_reading(int fd, short event, void* arg)
  data->query->tcplen = buffer_remaining(data->query->packet);
  data->bytes_transmitted = 0;
 
- timeout.tv_sec = data->nsd->tcp_timeout;
- timeout.tv_usec = 0L;
+ timeout.tv_sec = data->tcp_timeout / 1000;
+ timeout.tv_usec = (data->tcp_timeout % 1000)*1000;
 
  ev_base = data->event.ev_base;
  event_del(&data->event);
@@ -2763,8 +2768,8 @@ handle_tcp_writing(int fd, short event, void* arg)
  q->tcplen = buffer_remaining(q->packet);
  data->bytes_transmitted = 0;
  /* Reset timeout.  */
- timeout.tv_sec = data->nsd->tcp_timeout;
- timeout.tv_usec = 0L;
+ timeout.tv_sec = data->tcp_timeout / 1000;
+ timeout.tv_usec = (data->tcp_timeout % 1000)*1000;
  ev_base = data->event.ev_base;
  event_del(&data->event);
  event_set(&data->event, fd, EV_PERSIST | EV_WRITE | EV_TIMEOUT,
@@ -2794,8 +2799,8 @@ handle_tcp_writing(int fd, short event, void* arg)
 
  data->bytes_transmitted = 0;
 
- timeout.tv_sec = data->nsd->tcp_timeout;
- timeout.tv_usec = 0L;
+ timeout.tv_sec = data->tcp_timeout / 1000;
+ timeout.tv_usec = (data->tcp_timeout % 1000)*1000;
  ev_base = data->event.ev_base;
  event_del(&data->event);
  event_set(&data->event, fd, EV_PERSIST | EV_READ | EV_TIMEOUT,
@@ -2909,8 +2914,13 @@ handle_tcp_accept(int fd, short event, void* arg)
  memcpy(&tcp_data->query->addr, &addr, addrlen);
  tcp_data->query->addrlen = addrlen;
 
- timeout.tv_sec = data->nsd->tcp_timeout;
- timeout.tv_usec = 0;
+ tcp_data->tcp_timeout = data->nsd->tcp_timeout * 1000;
+ if (data->nsd->current_tcp_count > data->nsd->maximum_tcp_count/2) {
+ /* very busy, give smaller timeout */
+ tcp_data->tcp_timeout = 200;
+ }
+ timeout.tv_sec = tcp_data->tcp_timeout / 1000;
+ timeout.tv_usec = (tcp_data->tcp_timeout % 1000)*1000;
 
  event_set(&tcp_data->event, s, EV_PERSIST | EV_READ | EV_TIMEOUT,
  handle_tcp_reading, tcp_data);
diff --git util.h util.h
index 702674f..b59b7b6 100644
--- util.h
+++ util.h
@@ -198,6 +198,20 @@ write_uint32(void *dst, uint32_t data)
 #endif
 }
 
+static inline void
+write_uint64(void *dst, uint64_t data)
+{
+ uint8_t *p = (uint8_t *) dst;
+ p[0] = (uint8_t) ((data >> 56) & 0xff);
+ p[1] = (uint8_t) ((data >> 48) & 0xff);
+ p[2] = (uint8_t) ((data >> 40) & 0xff);
+ p[3] = (uint8_t) ((data >> 32) & 0xff);
+ p[4] = (uint8_t) ((data >> 24) & 0xff);
+ p[5] = (uint8_t) ((data >> 16) & 0xff);
+ p[6] = (uint8_t) ((data >> 8) & 0xff);
+ p[7] = (uint8_t) (data & 0xff);
+}
+
 /*
  * Copy data allowing for unaligned accesses in network byte order
  * (big endian).
@@ -224,6 +238,21 @@ read_uint32(const void *src)
 #endif
 }
 
+static inline uint64_t
+read_uint64(const void *src)
+{
+ uint8_t *p = (uint8_t *) src;
+ return
+    ((uint64_t)p[0] << 56) |
+    ((uint64_t)p[1] << 48) |
+    ((uint64_t)p[2] << 40) |
+    ((uint64_t)p[3] << 32) |
+    ((uint64_t)p[4] << 24) |
+    ((uint64_t)p[5] << 16) |
+    ((uint64_t)p[6] <<  8) |
+    (uint64_t)p[7];
+}
+
 /*
  * Print debugging information using log_msg,
  * set the logfile as /dev/stdout or /dev/stderr if you like.
diff --git xfrd-disk.c xfrd-disk.c
index 3fa8630..654e78e 100644
--- xfrd-disk.c
+++ xfrd-disk.c
@@ -147,6 +147,7 @@ xfrd_read_state(struct xfrd_state* xfrd)
  uint32_t filetime = 0;
  uint32_t numzones, i;
  region_type *tempregion;
+ time_t soa_refresh;
 
  tempregion = region_create(xalloc, free);
  if(!tempregion)
@@ -265,10 +266,15 @@ xfrd_read_state(struct xfrd_state* xfrd)
  * or there is a notification,
  * or there is a soa && current time is past refresh point
  */
+ soa_refresh = ntohl(soa_disk_read.refresh);
+ if (soa_refresh > zone->zone_options->pattern->max_refresh_time)
+ soa_refresh = zone->zone_options->pattern->max_refresh_time;
+ else if (soa_refresh < zone->zone_options->pattern->min_refresh_time)
+ soa_refresh = zone->zone_options->pattern->min_refresh_time;
  if(timeout == 0 || soa_notified_acquired_read != 0 ||
  (soa_disk_acquired_read != 0 &&
  (uint32_t)xfrd_time() - soa_disk_acquired_read
- > ntohl(soa_disk_read.refresh)))
+ > soa_refresh))
  {
  zone->state = xfrd_zone_refreshing;
  xfrd_set_refresh_now(zone);
@@ -564,3 +570,17 @@ xfrd_unlink_xfrfile(struct nsd* nsd, uint64_t number)
  strerror(errno));
  }
 }
+
+uint64_t
+xfrd_get_xfrfile_size(struct nsd* nsd, uint64_t number )
+{
+ char fname[1024];
+ struct stat tempxfr_stat;
+ tempxfrname(fname, sizeof(fname), nsd, number);
+ if( stat( fname, &tempxfr_stat ) < 0 ) {
+    log_msg(LOG_WARNING, "could not get file size %s: %s", fname,
+    strerror(errno));
+    return 0;
+ }
+ return (uint64_t)tempxfr_stat.st_size;
+}
diff --git xfrd-disk.h xfrd-disk.h
index 2c8e23f..b7e2d10 100644
--- xfrd-disk.h
+++ xfrd-disk.h
@@ -29,5 +29,7 @@ void xfrd_del_tempdir(struct nsd* nsd);
 FILE* xfrd_open_xfrfile(struct nsd* nsd, uint64_t number, char* mode);
 /* unlink temp file */
 void xfrd_unlink_xfrfile(struct nsd* nsd, uint64_t number);
+/* get temp file size */
+uint64_t xfrd_get_xfrfile_size(struct nsd* nsd, uint64_t number );
 
 #endif /* XFRD_DISK_H */
diff --git xfrd.c xfrd.c
index c2c75ed..0eacce7 100644
--- xfrd.c
+++ xfrd.c
@@ -702,7 +702,12 @@ xfrd_set_timer_refresh(xfrd_zone_t* zone)
  return;
  }
  /* refresh or expire timeout, whichever is earlier */
- set_refresh = zone->soa_disk_acquired + ntohl(zone->soa_disk.refresh);
+ set_refresh = ntohl(zone->soa_disk.refresh);
+ if (set_refresh > zone->zone_options->pattern->max_refresh_time)
+ set_refresh = zone->zone_options->pattern->max_refresh_time;
+ else if (set_refresh < zone->zone_options->pattern->min_refresh_time)
+ set_refresh = zone->zone_options->pattern->min_refresh_time;
+ set_refresh += zone->soa_disk_acquired;
  set_expire = zone->soa_disk_acquired + ntohl(zone->soa_disk.expire);
  if(set_refresh < set_expire)
  set = set_refresh;
@@ -719,6 +724,7 @@ xfrd_set_timer_refresh(xfrd_zone_t* zone)
 static void
 xfrd_set_timer_retry(xfrd_zone_t* zone)
 {
+ time_t set_retry;
  /* set timer for next retry or expire timeout if earlier. */
  if(zone->soa_disk_acquired == 0) {
  /* if no information, use reasonable timeout */
@@ -743,10 +749,14 @@ xfrd_set_timer_retry(xfrd_zone_t* zone)
  xfrd_time() + (time_t)ntohl(zone->soa_disk.retry) <
  zone->soa_disk_acquired + (time_t)ntohl(zone->soa_disk.expire))
  {
- if(ntohl(zone->soa_disk.retry) < XFRD_LOWERBOUND_RETRY)
- xfrd_set_timer(zone, XFRD_LOWERBOUND_RETRY);
- else
- xfrd_set_timer(zone, ntohl(zone->soa_disk.retry));
+ set_retry = ntohl(zone->soa_disk.retry);
+ if(set_retry > zone->zone_options->pattern->max_retry_time)
+ set_retry = zone->zone_options->pattern->max_retry_time;
+ else if(set_retry < zone->zone_options->pattern->min_retry_time)
+ set_retry = zone->zone_options->pattern->min_retry_time;
+ if(set_retry < XFRD_LOWERBOUND_RETRY)
+ set_retry =  XFRD_LOWERBOUND_RETRY;
+ xfrd_set_timer(zone, set_retry);
  } else {
  if(ntohl(zone->soa_disk.expire) < XFRD_LOWERBOUND_RETRY)
  xfrd_set_timer(zone, XFRD_LOWERBOUND_RETRY);
@@ -1918,6 +1928,7 @@ xfrd_handle_received_xfr_packet(xfrd_zone_t* zone, buffer_type* packet)
 {
  xfrd_soa_t soa;
  enum xfrd_packet_result res;
+        uint64_t xfrfile_size;
 
  /* parse and check the packet - see if it ends the xfr */
  switch((res=xfrd_parse_received_xfr_packet(zone, packet, &soa)))
@@ -1975,6 +1986,15 @@ xfrd_handle_received_xfr_packet(xfrd_zone_t* zone, buffer_type* packet)
  "disk", zone->apex_str, zone->master->ip_address_spec,
  (int)zone->msg_new_serial));
  zone->msg_seq_nr++;
+
+        xfrfile_size = xfrd_get_xfrfile_size(xfrd->nsd, zone->xfrfilenumber);
+ if( zone->zone_options->pattern->size_limit_xfr != 0 &&
+    xfrfile_size > zone->zone_options->pattern->size_limit_xfr ) {
+            /*    xfrd_unlink_xfrfile(xfrd->nsd, zone->xfrfilenumber);
+                    xfrd_set_reload_timeout(); */
+            log_msg(LOG_INFO, "xfrd : transfered zone data was too large %llu", (long long unsigned)xfrfile_size);
+    return xfrd_packet_bad;
+ }
  if(res == xfrd_packet_more) {
  /* wait for more */
  return xfrd_packet_more;
diff --git zonec.c zonec.c
index 666b0cc..c186171 100644
--- zonec.c
+++ zonec.c
@@ -953,7 +953,10 @@ zparser_conv_loc(region_type *region, char *str)
  }
 
  /* Meters of altitude... */
- (void) strtol(str, &str, 10);
+ if(strtol(str, &str, 10) == LONG_MAX) {
+ zc_error_prev_line("altitude too large, number overflow");
+ return NULL;
+ }
  switch(*str) {
  case ' ':
  case '\0':
@@ -1576,21 +1579,21 @@ zonec_read(const char* name, const char* zonefile, zone_type* zone)
  dname = dname_parse(parser->rr_region, name);
  if (!dname) {
  zc_error("incorrect zone name '%s'", name);
- return 0;
+ return 1;
  }
 
 #ifndef ROOT_SERVER
  /* Is it a root zone? Are we a root server then? Idiot proof. */
  if (dname->label_count == 1) {
  zc_error("not configured as a root server");
- return 0;
+ return 1;
  }
 #endif
 
  /* Open the zone file */
  if (!zone_open(zonefile, 3600, CLASS_IN, dname)) {
  zc_error("cannot open '%s': %s", zonefile, strerror(errno));
- return 0;
+ return 1;
  }
  parser->current_zone = zone;
 


--
I'm not entirely sure you are real.