I have been prodded by dhill & brad, so here is a diff. Running with it now but haven't reviewed it, yet.
(I have a git repo with all intermediate diffs if someone wants to look at those...) Tests / OKs? (I will review the diff myself before committing) diff --git Makefile.in Makefile.in index 3fbd01b..3391cd0 100644 --- Makefile.in +++ Makefile.in @@ -317,7 +317,7 @@ DEPEND_TMP2=depend1074.tmp DEPEND_TARGET=Makefile DEPEND_TARGET2=Makefile.in depend: - (cd $(srcdir) ; $(CC) -MM $(CPPFLAGS) *.c compat/*.c tpkg/cutest/*.c) | \ + (cd $(srcdir) ; $(CC) -MM $(CPPFLAGS) *.c compat/*.c `if test -d tpkg/cutest; then echo tpkg/cutest/*.c; fi`) | \ sed -e 's? *\([^ ]*\.[ch]\)? $$(srcdir)/\1?g' | \ sed -e 's?$$(srcdir)/config.h?config.h?g' \ -e 's?$$(srcdir)/configlexer.c?configlexer.c?g' \ diff --git axfr.c axfr.c index 09eb082..92d4f2f 100644 --- axfr.c +++ axfr.c @@ -91,7 +91,7 @@ query_axfr(struct nsd *nsd, struct query *query) query->edns.status = EDNS_NOT_PRESENT; buffer_set_limit(query->packet, QHEADERSZ); QDCOUNT_SET(query->packet, 0); - query_prepare_response(query, nsd); + query_prepare_response(query); } /* Add zone RRs until answer is full. */ diff --git configlexer.lex configlexer.lex index d536352..0ce80bd 100644 --- configlexer.lex +++ configlexer.lex @@ -7,6 +7,8 @@ * See LICENSE for the license. * */ +/* because flex keeps having sign-unsigned compare problems that are unfixed*/ +#pragma GCC diagnostic ignored "-Wsign-compare" #include "config.h" @@ -273,6 +275,7 @@ max-refresh-time{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_MAX_REFRESH_TIM 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;} +multi-master-check{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_MULTI_MASTER_CHECK;} {NEWLINE} { LEXOUT(("NL\n")); cfg_parser->line++;} /* Quoted strings. Strip leading and ending quotes */ diff --git configparser.y configparser.y index 9089665..204d236 100644 --- configparser.y +++ configparser.y @@ -71,6 +71,7 @@ extern config_parser_state_t* cfg_parser; %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 +%token VAR_MULTI_MASTER_CHECK %% toplevelvars: /* empty */ | toplevelvars toplevelvar ; @@ -602,7 +603,7 @@ zone_config_item: zone_zonefile | zone_allow_notify | zone_request_xfr | zone_outgoing_interface | zone_allow_axfr_fallback | include_pattern | 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; + zone_size_limit_xfr | zone_multi_master_check; pattern_name: VAR_NAME STRING { OUTYY(("P(pattern_name:%s)\n", $2)); @@ -871,6 +872,13 @@ zone_min_retry_time: VAR_MIN_RETRY_TIME STRING cfg_parser->current_pattern->min_retry_time_is_default = 0; } }; +zone_multi_master_check: VAR_MULTI_MASTER_CHECK STRING + { + OUTYY(("P(zone_multi_master_check:%s)\n", $2)); + if(strcmp($2, "yes") != 0 && strcmp($2, "no") != 0) + yyerror("expected yes or no."); + else cfg_parser->current_pattern->multi_master_check = (strcmp($2, "yes")==0); + } /* key: declaration */ keystart: VAR_KEY @@ -883,6 +891,7 @@ keystart: VAR_KEY key_options_insert(cfg_parser->opt, cfg_parser->current_key); } cfg_parser->current_key = key_options_create(cfg_parser->opt->region); + cfg_parser->current_key->algorithm = region_strdup(cfg_parser->opt->region, "sha256"); } ; contents_key: contents_key content_key | content_key; @@ -907,6 +916,8 @@ key_algorithm: VAR_ALGORITHM STRING #ifndef NDEBUG assert(cfg_parser->current_key); #endif + if(cfg_parser->current_key->algorithm) + region_recycle(cfg_parser->opt->region, cfg_parser->current_key->algorithm, strlen(cfg_parser->current_key->algorithm)+1); cfg_parser->current_key->algorithm = region_strdup(cfg_parser->opt->region, $2); if(tsig_get_algorithm_by_name($2) == NULL) c_error_msg("Bad tsig algorithm %s", $2); diff --git configure configure index 4ac0670..41bb666 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.12. +# Generated by GNU Autoconf 2.69 for NSD 4.1.13. # # Report bugs to <nsd-b...@nlnetlabs.nl>. # @@ -580,8 +580,8 @@ MAKEFLAGS= # Identity of this package. PACKAGE_NAME='NSD' PACKAGE_TARNAME='nsd' -PACKAGE_VERSION='4.1.12' -PACKAGE_STRING='NSD 4.1.12' +PACKAGE_VERSION='4.1.13' +PACKAGE_STRING='NSD 4.1.13' PACKAGE_BUGREPORT='nsd-b...@nlnetlabs.nl' PACKAGE_URL='' @@ -731,6 +731,7 @@ with_ssl enable_nsec3 enable_minimal_responses enable_mmap +enable_radix_tree ' ac_precious_vars='build_alias host_alias @@ -1283,7 +1284,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.12 to adapt to many kinds of systems. +\`configure' configures NSD 4.1.13 to adapt to many kinds of systems. Usage: $0 [OPTION]... [VAR=VALUE]... @@ -1344,7 +1345,7 @@ fi if test -n "$ac_init_help"; then case $ac_init_help in - short | recursive ) echo "Configuration of NSD 4.1.12:";; + short | recursive ) echo "Configuration of NSD 4.1.13:";; esac cat <<\_ACEOF @@ -1377,6 +1378,9 @@ Optional Features: --disable-minimal-responses Disable response minimization. More truncation. --enable-mmap Use mmap instead of malloc. Experimental. + --disable-radix-tree You can disable the radix tree and use the red-black + tree for the main lookups, the red-black tree uses + less memory, but uses some more CPU. Optional Packages: --with-PACKAGE[=ARG] use PACKAGE [ARG=yes] @@ -1487,7 +1491,7 @@ fi test -n "$ac_init_help" && exit $ac_status if $ac_init_version; then cat <<\_ACEOF -NSD configure 4.1.12 +NSD configure 4.1.13 generated by GNU Autoconf 2.69 Copyright (C) 2012 Free Software Foundation, Inc. @@ -2196,7 +2200,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.12, which was +It was created by NSD $as_me 4.1.13, which was generated by GNU Autoconf 2.69. Invocation command line was $ $0 $@ @@ -8675,7 +8679,6 @@ _ACEOF esac - # we need SSL for TSIG (and maybe also for NSEC3). @@ -8978,7 +8981,7 @@ fi done - for ac_func in HMAC_CTX_reset HMAC_CTX_new + for ac_func in HMAC_CTX_reset HMAC_CTX_new EVP_cleanup ERR_load_crypto_strings OPENSSL_init_crypto do : as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh` ac_fn_c_check_func "$LINENO" "$ac_func" "$as_ac_var" @@ -8990,6 +8993,22 @@ _ACEOF fi done + + BAKLIBS="$LIBS" + LIBS="-lssl $LIBS" + for ac_func in OPENSSL_init_ssl +do : + ac_fn_c_check_func "$LINENO" "OPENSSL_init_ssl" "ac_cv_func_OPENSSL_init_ssl" +if test "x$ac_cv_func_OPENSSL_init_ssl" = xyes; then : + cat >>confdefs.h <<_ACEOF +#define HAVE_OPENSSL_INIT_SSL 1 +_ACEOF + +fi +done + + LIBS="$BAKLIBS" + else { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: No SSL, therefore remote-control is disabled" >&5 $as_echo "$as_me: WARNING: No SSL, therefore remote-control is disabled" >&2;} @@ -9135,6 +9154,23 @@ _ACEOF ;; esac +# Check whether --enable-radix-tree was given. +if test "${enable_radix_tree+set}" = set; then : + enableval=$enable_radix_tree; +fi + +case "$enable_radix_tree" in + no) + ;; + yes|*) + +cat >>confdefs.h <<_ACEOF +#define USE_RADIX_TREE /**/ +_ACEOF + + ;; +esac + @@ -9681,7 +9717,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.12, which was +This file was extended by NSD $as_me 4.1.13, which was generated by GNU Autoconf 2.69. Invocation command line was CONFIG_FILES = $CONFIG_FILES @@ -9743,7 +9779,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.12 +NSD config.status 4.1.13 configured by $0, generated by GNU Autoconf 2.69, with options \\"\$ac_cs_config\\" diff --git configure.ac configure.ac index 75a44c5..e2aca4d 100644 --- configure.ac +++ configure.ac @@ -4,7 +4,7 @@ dnl sinclude(acx_nlnetlabs.m4) -AC_INIT(NSD,4.1.12,nsd-b...@nlnetlabs.nl) +AC_INIT(NSD,4.1.13,nsd-b...@nlnetlabs.nl) AC_CONFIG_HEADER([config.h]) CFLAGS="$CFLAGS" @@ -825,7 +825,6 @@ case "$enable_ratelimit_default_is_off" in esac AC_SUBST(ratelimit_default) - # we need SSL for TSIG (and maybe also for NSEC3). CHECK_SSL if test x$HAVE_SSL = x"yes"; then @@ -862,7 +861,13 @@ if test x$HAVE_SSL = x"yes"; then AC_CHECK_HEADERS([openssl/ssl.h],,, [AC_INCLUDES_DEFAULT]) AC_CHECK_HEADERS([openssl/err.h],,, [AC_INCLUDES_DEFAULT]) AC_CHECK_HEADERS([openssl/rand.h],,, [AC_INCLUDES_DEFAULT]) - AC_CHECK_FUNCS([HMAC_CTX_reset HMAC_CTX_new]) + AC_CHECK_FUNCS([HMAC_CTX_reset HMAC_CTX_new EVP_cleanup ERR_load_crypto_strings OPENSSL_init_crypto]) + + BAKLIBS="$LIBS" + LIBS="-lssl $LIBS" + AC_CHECK_FUNCS([OPENSSL_init_ssl]) + LIBS="$BAKLIBS" + else AC_MSG_WARN([No SSL, therefore remote-control is disabled]) fi @@ -904,6 +909,15 @@ case "$enable_mmap" in ;; esac +AC_ARG_ENABLE(radix-tree, AC_HELP_STRING([--disable-radix-tree], [You can disable the radix tree and use the red-black tree for the main lookups, the red-black tree uses less memory, but uses some more CPU.])) +case "$enable_radix_tree" in + no) + ;; + yes|*) + AC_DEFINE_UNQUOTED([USE_RADIX_TREE], [], [Define this to configure to use the radix tree.]) + ;; +esac + AH_BOTTOM([ /* define before includes as it specifies what standard to use. */ #if (defined(HAVE_PSELECT) && !defined (HAVE_PSELECT_PROTO)) \ diff --git difffile.c difffile.c index b2325c5..6890a31 100644 --- difffile.c +++ difffile.c @@ -372,14 +372,14 @@ debug_find_rr_num(rrset_type* rrset, uint16_t type, uint16_t klass, if (rrset->rrs[i].type != type) { log_msg(LOG_WARNING, "diff: RR <%s, %s> does not match " "RR num %d type %s", - dname_to_string(rrset->rrs[i].owner->dname,0), + dname_to_string(domain_dname(rrset->rrs[i].owner),0), rrtype_to_string(type), i, rrtype_to_string(rrset->rrs[i].type)); } if (rrset->rrs[i].klass != klass) { log_msg(LOG_WARNING, "diff: RR <%s, %s> class %d " "does not match RR num %d class %d", - dname_to_string(rrset->rrs[i].owner->dname,0), + dname_to_string(domain_dname(rrset->rrs[i].owner),0), rrtype_to_string(type), klass, i, rrset->rrs[i].klass); @@ -387,7 +387,7 @@ debug_find_rr_num(rrset_type* rrset, uint16_t type, uint16_t klass, if (rrset->rrs[i].rdata_count != rdata_num) { log_msg(LOG_WARNING, "diff: RR <%s, %s> rdlen %u " "does not match RR num %d rdlen %d", - dname_to_string(rrset->rrs[i].owner->dname,0), + dname_to_string(domain_dname(rrset->rrs[i].owner),0), rrtype_to_string(type), (unsigned) rdata_num, i, (unsigned) rrset->rrs[i].rdata_count); @@ -396,7 +396,7 @@ debug_find_rr_num(rrset_type* rrset, uint16_t type, uint16_t klass, &rd, &reason)) { log_msg(LOG_WARNING, "diff: RR <%s, %s> rdata element " "%d differs from RR num %d rdata (%s)", - dname_to_string(rrset->rrs[i].owner->dname,0), + dname_to_string(domain_dname(rrset->rrs[i].owner),0), rrtype_to_string(type), rd, i, reason); } @@ -1299,9 +1299,9 @@ apply_ixfr_for_zone(nsd_type* nsd, zone_type* zonedb, FILE* in, } /* has been read in completely */ - if(strcmp(zone_buf, dname_to_string(zonedb->apex->dname,0)) != 0) { + if(strcmp(zone_buf, domain_to_string(zonedb->apex)) != 0) { log_msg(LOG_ERR, "file %s does not match task %s", - zone_buf, dname_to_string(zonedb->apex->dname,0)); + zone_buf, domain_to_string(zonedb->apex)); return 0; } if(!committed) { @@ -1321,7 +1321,7 @@ apply_ixfr_for_zone(nsd_type* nsd, zone_type* zonedb, FILE* in, if(committed) { int is_axfr=0, delete_mode=0, rr_count=0, softfail=0; - const dname_type* apex = zonedb->apex->dname; + const dname_type* apex = domain_dname_const(zonedb->apex); udb_ptr z; DEBUG(DEBUG_XFRD,1, (LOG_INFO, "processing xfr: %s", zone_buf)); diff --git dns.c dns.c index 037cd4c..c50fbe7 100644 --- dns.c +++ dns.c @@ -297,9 +297,10 @@ static rrtype_descriptor_type rrtype_descriptors[(RRTYPE_DESCRIPTORS_LENGTH+1)] { RDATA_WF_SHORT, RDATA_WF_BYTE, RDATA_WF_BYTE, RDATA_WF_BINARY }, { RDATA_ZF_SHORT, RDATA_ZF_BYTE, RDATA_ZF_ALGORITHM, RDATA_ZF_BASE64 } }, - /* 61 */ - { 61, NULL, T_UTYPE, 1, 1, { RDATA_WF_BINARY }, { RDATA_ZF_UNKNOWN } }, - /* 62 */ + /* 61 - OPENPGPKEY */ + { TYPE_OPENPGPKEY, "OPENPGPKEY", T_OPENPGPKEY, 1, 1, + { RDATA_WF_BINARY }, { RDATA_ZF_BASE64 } }, + /* 62 - CSYNC */ { TYPE_CSYNC, "CSYNC", T_CSYNC, 3, 3, { RDATA_WF_LONG, RDATA_WF_SHORT, RDATA_WF_BINARY }, { RDATA_ZF_LONG, RDATA_ZF_SHORT, RDATA_ZF_NSEC } }, /* 63 */ diff --git dns.h dns.h index 810cd70..5fc3504 100644 --- dns.h +++ dns.h @@ -138,6 +138,7 @@ typedef enum nsd_rc nsd_rc_type; #define TYPE_TLSA 52 /* RFC 6698 */ #define TYPE_CDS 59 /* RFC 7344 */ #define TYPE_CDNSKEY 60 /* RFC 7344 */ +#define TYPE_OPENPGPKEY 61 /* RFC 7929 */ #define TYPE_CSYNC 62 /* RFC 7477 */ #define TYPE_SPF 99 /* RFC 4408 */ diff --git edns.c edns.c index d37e624..72a38d0 100644 --- edns.c +++ edns.c @@ -14,6 +14,8 @@ #include "dns.h" #include "edns.h" +#include "nsd.h" +#include "query.h" void edns_init_data(edns_data_type *data, uint16_t max_length) @@ -53,12 +55,42 @@ edns_init_record(edns_record_type *edns) edns->status = EDNS_NOT_PRESENT; edns->position = 0; edns->maxlen = 0; + edns->opt_reserved_space = 0; edns->dnssec_ok = 0; edns->nsid = 0; } +/** handle a single edns option in the query */ +static int +edns_handle_option(uint16_t optcode, uint16_t optlen, buffer_type* packet, + edns_record_type* edns, struct query* query, nsd_type* nsd) +{ + (void) query; /* in case edns options need the query structure */ + /* handle opt code and read the optlen bytes from the packet */ + switch(optcode) { + case NSID_CODE: + /* is NSID enabled? */ + if(nsd->nsid_len > 0) { + edns->nsid = 1; + /* we have to check optlen, and move the buffer along */ + buffer_skip(packet, optlen); + /* in the reply we need space for optcode+optlen+nsid_bytes */ + edns->opt_reserved_space += OPT_HDR + nsd->nsid_len; + } else { + /* ignore option */ + buffer_skip(packet, optlen); + } + break; + default: + buffer_skip(packet, optlen); + break; + } + return 1; +} + int -edns_parse_record(edns_record_type *edns, buffer_type *packet) +edns_parse_record(edns_record_type *edns, buffer_type *packet, + query_type* query, nsd_type* nsd) { /* OPT record type... */ uint8_t opt_owner; @@ -67,7 +99,6 @@ edns_parse_record(edns_record_type *edns, buffer_type *packet) uint8_t opt_version; uint16_t opt_flags; uint16_t opt_rdlen; - uint16_t opt_nsid; edns->position = buffer_position(packet); @@ -97,13 +128,19 @@ edns_parse_record(edns_record_type *edns, buffer_type *packet) if (opt_rdlen > 0) { if(!buffer_available(packet, opt_rdlen)) return 0; - if(opt_rdlen < 4) + /* there is more to come, read opt code */ + while(opt_rdlen >= 4) { + uint16_t optcode = buffer_read_u16(packet); + uint16_t optlen = buffer_read_u16(packet); + if(opt_rdlen < 4+optlen) + return 0; /* opt too long, formerr */ + opt_rdlen -= (4+optlen); + if(!edns_handle_option(optcode, optlen, packet, + edns, query, nsd)) + return 0; + } + if(opt_rdlen != 0) return 0; - /* there is more to come, read opt code - * should be NSID - there are no others */ - opt_nsid = buffer_read_u16(packet); - edns->nsid = (opt_nsid == NSID_CODE); - /* extra check for the value */ } edns->status = EDNS_OK; @@ -116,5 +153,5 @@ size_t edns_reserved_space(edns_record_type *edns) { /* MIEK; when a pkt is too large?? */ - return edns->status == EDNS_NOT_PRESENT ? 0 : (OPT_LEN + OPT_RDATA); + return edns->status == EDNS_NOT_PRESENT ? 0 : (OPT_LEN + OPT_RDATA + edns->opt_reserved_space); } diff --git edns.h edns.h index b8643e9..9325beb 100644 --- edns.h +++ edns.h @@ -11,6 +11,8 @@ #define _EDNS_H_ #include "buffer.h" +struct nsd; +struct query; #define OPT_LEN 9U /* Length of the NSD EDNS response record minus 2 */ #define OPT_RDATA 2 /* holds the rdata length comes after OPT_LEN */ @@ -42,6 +44,7 @@ struct edns_record edns_status_type status; size_t position; size_t maxlen; + size_t opt_reserved_space; int dnssec_ok; int nsid; }; @@ -49,7 +52,8 @@ typedef struct edns_record edns_record_type; void edns_init_data(edns_data_type *data, uint16_t max_length); void edns_init_record(edns_record_type *data); -int edns_parse_record(edns_record_type *data, buffer_type *packet); +int edns_parse_record(edns_record_type *data, buffer_type *packet, + struct query* q, struct nsd* nsd); /* * The amount of space to reserve in the response for the EDNS data diff --git namedb.c namedb.c index dbe8efd..fd381e9 100644 --- namedb.c +++ namedb.c @@ -33,7 +33,12 @@ allocate_domain_info(domain_table_type* table, result = (domain_type *) region_alloc(table->region, sizeof(domain_type)); - result->dname = dname_partial_copy( +#ifdef USE_RADIX_TREE + result->dname +#else + result->node.key +#endif + = dname_partial_copy( table->region, dname, domain_dname(parent)->label_count + 1); result->parent = parent; result->wildcard_child_closest_match = result; @@ -252,9 +257,13 @@ do_deldomain(namedb_type* db, domain_type* domain) domain_previous_existing_child(domain); /* actual removal */ +#ifdef USE_RADIX_TREE radix_delete(db->domains->nametree, domain->rnode); - region_recycle(db->domains->region, (dname_type*)domain->dname, - dname_total_size(domain->dname)); +#else + rbtree_delete(db->domains->names_to_domains, domain->node.key); +#endif + region_recycle(db->domains->region, domain_dname(domain), + dname_total_size(domain_dname(domain))); region_recycle(db->domains->region, domain, sizeof(domain_type)); } @@ -313,7 +322,12 @@ domain_table_create(region_type* region) origin = dname_make(region, (uint8_t *) "", 0); root = (domain_type *) region_alloc(region, sizeof(domain_type)); - root->dname = origin; +#ifdef USE_RADIX_TREE + root->dname +#else + root->node.key +#endif + = origin; root->parent = NULL; root->wildcard_child_closest_match = root; root->rrsets = NULL; @@ -330,9 +344,15 @@ domain_table_create(region_type* region) result = (domain_table_type *) region_alloc(region, sizeof(domain_table_type)); result->region = region; +#ifdef USE_RADIX_TREE result->nametree = radix_tree_create(region); root->rnode = radname_insert(result->nametree, dname_name(root->dname), root->dname->name_size, root); +#else + result->names_to_domains = rbtree_create( + region, (int (*)(const void *, const void *)) dname_compare); + rbtree_insert(result->names_to_domains, (rbnode_t *) root); +#endif result->root = root; result->numlist_last = root; @@ -357,9 +377,13 @@ domain_table_search(domain_table_type *table, assert(closest_match); assert(closest_encloser); +#ifdef USE_RADIX_TREE exact = radname_find_less_equal(table->nametree, dname_name(dname), dname->name_size, (struct radnode**)closest_match); *closest_match = (domain_type*)((*(struct radnode**)closest_match)->elem); +#else + exact = rbtree_find_less_equal(table->names_to_domains, dname, (rbnode_t **) closest_match); +#endif assert(*closest_match); *closest_encloser = *closest_match; @@ -416,9 +440,13 @@ domain_table_insert(domain_table_type* table, result = allocate_domain_info(table, dname, closest_encloser); +#ifdef USE_RADIX_TREE result->rnode = radname_insert(table->nametree, dname_name(result->dname), result->dname->name_size, result); +#else + rbtree_insert(table->names_to_domains, (rbnode_t *) result); +#endif /* * If the newly added domain name is larger diff --git namedb.h namedb.h index e5cf36c..d8cefeb 100644 --- namedb.h +++ namedb.h @@ -37,7 +37,11 @@ typedef struct namedb namedb_type; struct domain_table { region_type* region; +#ifdef USE_RADIX_TREE struct radtree *nametree; +#else + rbtree_t *names_to_domains; +#endif domain_type* root; /* ptr to biggest domain.number and last in list. * the root is the lowest and first in the list. */ @@ -86,8 +90,12 @@ struct nsec3_domain_data { struct domain { +#ifdef USE_RADIX_TREE struct radnode* rnode; const dname_type* dname; +#else + rbnode_t node; +#endif domain_type* parent; domain_type* wildcard_child_closest_match; rrset_type* rrsets; @@ -190,7 +198,11 @@ int domain_table_search(domain_table_type* table, static inline uint32_t domain_table_count(domain_table_type* table) { +#ifdef USE_RADIX_TREE return table->nametree->count; +#else + return table->names_to_domains->count; +#endif } /* @@ -247,24 +259,48 @@ domain_type *domain_previous_existing_child(domain_type* domain); int zone_is_secure(zone_type* zone); -static inline const dname_type * +static inline dname_type * domain_dname(domain_type* domain) { +#ifdef USE_RADIX_TREE + return (dname_type *) domain->dname; +#else + return (dname_type *) domain->node.key; +#endif +} + +static inline const dname_type * +domain_dname_const(const domain_type* domain) +{ +#ifdef USE_RADIX_TREE return domain->dname; +#else + return (const dname_type *) domain->node.key; +#endif } static inline domain_type * domain_previous(domain_type* domain) { +#ifdef USE_RADIX_TREE struct radnode* prev = radix_prev(domain->rnode); return prev == NULL ? NULL : (domain_type*)prev->elem; +#else + rbnode_t *prev = rbtree_previous((rbnode_t *) domain); + return prev == RBTREE_NULL ? NULL : (domain_type *) prev; +#endif } static inline domain_type * domain_next(domain_type* domain) { +#ifdef USE_RADIX_TREE struct radnode* next = radix_next(domain->rnode); return next == NULL ? NULL : (domain_type*)next->elem; +#else + rbnode_t *next = rbtree_next((rbnode_t *) domain); + return next == RBTREE_NULL ? NULL : (domain_type *) next; +#endif } /* easy comparison for subdomain, true if d1 is subdomain of d2. */ diff --git nsd-checkconf.c nsd-checkconf.c index b7afdd1..4559c3a 100644 --- nsd-checkconf.c +++ nsd-checkconf.c @@ -320,6 +320,7 @@ config_print_zone(nsd_options_t* opt, const char* k, int s, const char *o, #ifdef RATELIMIT ZONE_GET_RRL(rrl_whitelist, o, zone->pattern); #endif + ZONE_GET_BIN(multi_master_check, o, zone->pattern); printf("Zone option not handled: %s %s\n", z, o); exit(1); } else if(pat) { @@ -350,6 +351,7 @@ config_print_zone(nsd_options_t* opt, const char* k, int s, const char *o, #ifdef RATELIMIT ZONE_GET_RRL(rrl_whitelist, o, p); #endif + ZONE_GET_BIN(multi_master_check, o, p); printf("Pattern option not handled: %s %s\n", pat, o); exit(1); } else { @@ -437,6 +439,8 @@ static void print_zone_content_elems(pattern_options_t* pat) #endif print_acl("allow-notify:", pat->allow_notify); print_acl("request-xfr:", pat->request_xfr); + if(pat->multi_master_check) + printf("\tmulti-master-check: %s\n", pat->multi_master_check?"yes":"no"); if(!pat->notify_retry_is_default) printf("\tnotify-retry: %d\n", pat->notify_retry); print_acl("notify:", pat->notify); diff --git nsd-control.c nsd-control.c index 3b7a2c6..0b48283 100644 --- nsd-control.c +++ nsd-control.c @@ -367,10 +367,22 @@ int main(int argc, char* argv[]) #endif log_init("nsd-control"); +#ifdef HAVE_ERR_LOAD_CRYPTO_STRINGS ERR_load_crypto_strings(); +#endif ERR_load_SSL_strings(); +#if OPENSSL_VERSION_NUMBER < 0x10100000 || !defined(HAVE_OPENSSL_INIT_CRYPTO) OpenSSL_add_all_algorithms(); +#else + OPENSSL_init_crypto(OPENSSL_INIT_ADD_ALL_CIPHERS + | OPENSSL_INIT_ADD_ALL_DIGESTS + | OPENSSL_INIT_LOAD_CRYPTO_STRINGS, NULL); +#endif +#if OPENSSL_VERSION_NUMBER < 0x10100000 || !defined(HAVE_OPENSSL_INIT_SSL) (void)SSL_library_init(); +#else + OPENSSL_init_ssl(0, NULL); +#endif if(!RAND_status()) { /* try to seed it */ diff --git nsd-mem.c nsd-mem.c index 05710c4..105b88d 100644 --- nsd-mem.c +++ nsd-mem.c @@ -110,7 +110,7 @@ account_zone(struct namedb* db, struct zone_mem* zmem) zmem->udb_overhead = (size_t)(db->udb->alloc->disk->stat_alloc - db->udb->alloc->disk->stat_data); } - zmem->domaincount = db->domains->nametree->count; + zmem->domaincount = domain_table_count(db->domains); } static void diff --git nsd.conf.5.in nsd.conf.5.in index bcec054..6b2588f 100644 --- nsd.conf.5.in +++ nsd.conf.5.in @@ -666,6 +666,11 @@ are logged in the loglines when a subnet is blocked (in verbosity 2). The RRL classification types are: nxdomain, error, referral, any, rrsig, wildcard, nodata, dnskey, positive, all. .\" rrlend +.TP +.B multi\-master\-check:\fR <yes or no> +Default no. If enabled, checks all masters for the last version. It uses +the higher version of all the configured masters. Useful if you have multiple +masters that have different version numbers served. .SS "Key Declarations" The .B key: @@ -674,9 +679,15 @@ the following attributes. .TP .B name:\fR <string> The key name. Used to refer to this key in the access control list. +The key name has to be correct for tsig to work. +This is because the key name is output on the wire. .TP .B algorithm:\fR <string> -Authentication algorithm for this key. +Authentication algorithm for this key. Such as hmac\-md5, hmac\-sha1, +hmac\-sha224, hmac\-sha256, hmac\-sha384 and hmac\-sha512. Can also be +abbreviated as 'sha1', 'sha256'. Default is sha256. +Algorithms are only available when they were compiled in (available in the +crypto library). .TP .B secret:\fR <base64 blob> The base64 encoded shared secret. It is possible to put the @@ -685,6 +696,9 @@ declaration (and base64 blob) into a different file, and then to .B include: that file. In this way the key secret and the rest of the configuration file, which may have different security policies, can be split apart. +The content of the secret is the agreed base64 secret content. To make it +up, enter a password (its length must be a multiple of 4 characters, A\-Za\-z0\-9), or use +dev-random output through a base64 encode filter. .SH "NSD CONFIGURATION FOR BIND9 HACKERS" BIND9 is a name server implementation with its own configuration file format, named.conf(5). BIND9 types zones as 'Master' or 'Slave'. diff --git nsd.conf.sample.in nsd.conf.sample.in index cda7dd0..2f2214c 100644 --- nsd.conf.sample.in +++ nsd.conf.sample.in @@ -204,8 +204,8 @@ remote-control: # key: # The key name is sent to the other party, it must be the same #name: "keyname" - # algorithm hmac-md5, or hmac-sha1, or hmac-sha256 (if compiled in) - #algorithm: hmac-sha256 + # algorithm hmac-md5, or sha1, sha256, sha224, sha384, sha512 + #algorithm: sha256 # secret material, must be the same as the other party uses. # base64 encoded random number. # e.g. from dd if=/dev/random of=/dev/stdout count=1 bs=32 | base64 @@ -263,6 +263,9 @@ remote-control: #min-refresh-time: 0 #max-retry-time: 1209600 #min-retry-time: 0 + # Slave server tries zone transfer to all masters and picks highest + # zone version available, for when masters have different versions. + #multi-master-check: no # limit the zone transfer size (in bytes), stops very large transfers # 0 is no limits enforced. diff --git nsec3.c nsec3.c index bad5af8..777b9f9 100644 --- nsec3.c +++ nsec3.c @@ -27,6 +27,8 @@ cmp_hash_tree(const void* x, const void* y) { const domain_type* a = (const domain_type*)x; const domain_type* b = (const domain_type*)y; + if(!a->nsec3) return (b->nsec3?-1:0); + if(!b->nsec3) return 1; return memcmp(a->nsec3->nsec3_hash, b->nsec3->nsec3_hash, NSEC3_HASH_LEN); } @@ -37,6 +39,8 @@ cmp_wchash_tree(const void* x, const void* y) { const domain_type* a = (const domain_type*)x; const domain_type* b = (const domain_type*)y; + if(!a->nsec3) return (b->nsec3?-1:0); + if(!b->nsec3) return 1; return memcmp(a->nsec3->nsec3_wc_hash, b->nsec3->nsec3_wc_hash, NSEC3_HASH_LEN); } @@ -47,6 +51,8 @@ cmp_dshash_tree(const void* x, const void* y) { const domain_type* a = (const domain_type*)x; const domain_type* b = (const domain_type*)y; + if(!a->nsec3) return (b->nsec3?-1:0); + if(!b->nsec3) return 1; return memcmp(a->nsec3->nsec3_ds_parent_hash, b->nsec3->nsec3_ds_parent_hash, NSEC3_HASH_LEN); } @@ -59,9 +65,9 @@ cmp_nsec3_tree(const void* x, const void* y) const domain_type* a = (const domain_type*)x; const domain_type* b = (const domain_type*)y; /* labelcount + 32long label */ - assert(dname_name(a->dname)[0] == 32); - assert(dname_name(b->dname)[0] == 32); - return memcmp(dname_name(a->dname), dname_name(b->dname), 33); + assert(dname_name(domain_dname_const(a))[0] == 32); + assert(dname_name(domain_dname_const(b))[0] == 32); + return memcmp(dname_name(domain_dname_const(a)), dname_name(domain_dname_const(b)), 33); } void nsec3_zone_trees_create(struct region* region, zone_type* zone) @@ -438,7 +444,7 @@ nsec3_tree_zone(namedb_type* db, domain_type* d) rrset_rrtype(rrset) == TYPE_DNSKEY || rrset_rrtype(rrset) == TYPE_NSEC3PARAM) return rrset->zone; - return namedb_find_zone(db, d->dname); + return namedb_find_zone(db, domain_dname(d)); } d = d->parent; } @@ -465,7 +471,11 @@ nsec3_find_cover(zone_type* zone, uint8_t* hash, size_t hashlen, /* nsec3tree is sorted by b32 encoded domain name of the NSEC3 */ b32_ntop(hash, hashlen, (char*)(n+5), sizeof(n)-5); +#ifdef USE_RADIX_TREE d.dname = (dname_type*)n; +#else + d.node.key = n; +#endif n[0] = 34; /* name_size */ n[1] = 2; /* label_count */ n[2] = 0; /* label_offset[0] */ diff --git options.c options.c index 058ceec..8c1e58a 100644 --- options.c +++ options.c @@ -834,6 +834,7 @@ pattern_options_create(region_type* region) #ifdef RATELIMIT p->rrl_whitelist = 0; #endif + p->multi_master_check = 0; return p; } @@ -964,6 +965,7 @@ copy_pat_fixed(region_type* region, pattern_options_t* orig, #ifdef RATELIMIT orig->rrl_whitelist = p->rrl_whitelist; #endif + orig->multi_master_check = p->multi_master_check; } void @@ -1049,6 +1051,7 @@ pattern_options_equal(pattern_options_t* p, pattern_options_t* q) #ifdef RATELIMIT if(p->rrl_whitelist != q->rrl_whitelist) return 0; #endif + if(!booleq(p->multi_master_check,q->multi_master_check)) return 0; if(p->size_limit_xfr != q->size_limit_xfr) return 0; return 1; } @@ -1208,6 +1211,7 @@ pattern_options_marshal(struct buffer* b, pattern_options_t* p) 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); + marshal_u8(b, p->multi_master_check); } pattern_options_t* @@ -1239,6 +1243,7 @@ pattern_options_unmarshal(region_type* r, struct buffer* 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); + p->multi_master_check = unmarshal_u8(b); return p; } @@ -1594,7 +1599,10 @@ acl_key_matches(acl_options_t* acl, struct query* q) return 0; /* wrong key name */ } if(tsig_strlowercmp(q->tsig.algorithm->short_name, - acl->key_options->algorithm) != 0) { + acl->key_options->algorithm) != 0 && ( + strncmp("hmac-", q->tsig.algorithm->short_name, 5) != 0 || + tsig_strlowercmp(q->tsig.algorithm->short_name+5, + acl->key_options->algorithm) != 0) ) { DEBUG(DEBUG_XFRD,2, (LOG_ERR, "query tsig wrong algorithm")); return 0; /* no such algo */ } @@ -1979,6 +1987,8 @@ config_apply_pattern(const char* name) pat->provide_xfr); append_acl(&a->outgoing_interface, &cfg_parser-> current_outgoing_interface, pat->outgoing_interface); + if(pat->multi_master_check) + a->multi_master_check = pat->multi_master_check; } void diff --git options.h options.h index e0f749c..d826c03 100644 --- options.h +++ options.h @@ -163,6 +163,7 @@ struct pattern_options { uint32_t min_retry_time; uint8_t min_retry_time_is_default; uint64_t size_limit_xfr; + uint8_t multi_master_check; }; #define PATTERN_IMPLICIT_MARKER "_implicit_" diff --git query.c query.c index 7256449..fc2d45e 100644 --- query.c +++ query.c @@ -580,7 +580,11 @@ find_covering_nsec(domain_type *closest_match, assert(nsec_rrset); /* loop away temporary created domains. For real ones it is &RBTREE_NULL */ +#ifdef USE_RADIX_TREE while (closest_match->rnode == NULL) +#else + while (closest_match->node.parent == NULL) +#endif closest_match = closest_match->parent; while (closest_match) { *nsec_rrset = domain_find_rrset(closest_match, zone, TYPE_NSEC); @@ -656,8 +660,13 @@ add_additional_rrsets(struct query *query, answer_type *answer, domain_type *wildcard_child = domain_wildcard_child(match); domain_type *temp = (domain_type *) region_alloc( query->region, sizeof(domain_type)); +#ifdef USE_RADIX_TREE temp->rnode = NULL; temp->dname = additional->dname; +#else + memcpy(&temp->node, &additional->node, sizeof(rbnode_t)); + temp->node.parent = NULL; +#endif temp->number = additional->number; temp->parent = match; temp->wildcard_child_closest_match = temp; @@ -728,6 +737,10 @@ add_rrset(struct query *query, add_additional_rrsets(query, answer, rrset, 1, 0, rt_additional_rr_types); break; + case TYPE_SRV: + add_additional_rrsets(query, answer, rrset, 3, 0, + default_additional_rr_types); + break; default: break; } @@ -764,7 +777,11 @@ query_synthesize_cname(struct query* q, struct answer* answer, const dname_type* return 0; newdom->is_existing = 1; newdom->parent = lastparent; +#ifdef USE_RADIX_TREE newdom->dname +#else + newdom->node.key +#endif = dname_partial_copy(q->region, from_name, domain_dname(src)->label_count + i + 1); if(dname_compare(domain_dname(newdom), q->qname) == 0) { @@ -787,7 +804,11 @@ query_synthesize_cname(struct query* q, struct answer* answer, const dname_type* return 0; newdom->is_existing = 0; newdom->parent = lastparent; +#ifdef USE_RADIX_TREE newdom->dname +#else + newdom->node.key +#endif = dname_partial_copy(q->region, to_name, domain_dname(to_closest_encloser)->label_count + i + 1); DEBUG(DEBUG_QUERY,2, (LOG_INFO, "created temp domain dest %d. %s nr %d", i, @@ -1088,8 +1109,13 @@ answer_authoritative(struct nsd *nsd, match = (domain_type *) region_alloc(q->region, sizeof(domain_type)); +#ifdef USE_RADIX_TREE match->rnode = NULL; match->dname = wildcard_child->dname; +#else + memcpy(&match->node, &wildcard_child->node, sizeof(rbnode_t)); + match->node.parent = NULL; +#endif match->parent = closest_encloser; match->wildcard_child_closest_match = match; match->number = domain_number; @@ -1284,7 +1310,7 @@ answer_query(struct nsd *nsd, struct query *q) } void -query_prepare_response(query_type *q, nsd_type *nsd) +query_prepare_response(query_type *q) { uint16_t flags; @@ -1299,9 +1325,6 @@ query_prepare_response(query_type *q, nsd_type *nsd) */ q->reserved_space = edns_reserved_space(&q->edns); q->reserved_space += tsig_reserved_space(&q->tsig); - if(q->edns.nsid == 1 && nsd->nsid_len > 0 && - q->edns.status != EDNS_NOT_PRESENT) - q->reserved_space += OPT_HDR + nsd->nsid_len; /* Update the flags. */ flags = FLAGS(q->packet); @@ -1401,7 +1424,7 @@ query_process(query_type *q, nsd_type *nsd) } /* See if there is an OPT RR. */ if (arcount > 0) { - if (edns_parse_record(&q->edns, q->packet)) + if (edns_parse_record(&q->edns, q->packet, q, nsd)) --arcount; } /* See if there is a TSIG RR. */ @@ -1440,7 +1463,7 @@ query_process(query_type *q, nsd_type *nsd) return query_error(q, NSD_RC_OK); } - query_prepare_response(q, nsd); + query_prepare_response(q); if (q->qclass != CLASS_IN && q->qclass != CLASS_ANY) { if (q->qclass == CLASS_CH) { @@ -1479,17 +1502,20 @@ query_add_optional(query_type *q, nsd_type *nsd) if (q->edns.dnssec_ok) edns->ok[7] = 0x80; else edns->ok[7] = 0x00; buffer_write(q->packet, edns->ok, OPT_LEN); - if (nsd->nsid_len > 0 && q->edns.nsid == 1 && buffer_available( - q->packet, OPT_RDATA+OPT_HDR+nsd->nsid_len)) { - /* rdata length */ - buffer_write(q->packet, edns->rdata_nsid, OPT_RDATA); - /* nsid opt header */ - buffer_write(q->packet, edns->nsid, OPT_HDR); - /* nsid payload */ - buffer_write(q->packet, nsd->nsid, nsd->nsid_len); - } else { + if(q->edns.opt_reserved_space == 0 || !buffer_available( + q->packet, 2+q->edns.opt_reserved_space)) { /* fill with NULLs */ buffer_write(q->packet, edns->rdata_none, OPT_RDATA); + } else { + /* rdata length */ + buffer_write_u16(q->packet, q->edns.opt_reserved_space); + /* edns options */ + if(q->edns.nsid) { + /* nsid opt header */ + buffer_write(q->packet, edns->nsid, OPT_HDR); + /* nsid payload */ + buffer_write(q->packet, nsd->nsid, nsd->nsid_len); + } } ARCOUNT_SET(q->packet, ARCOUNT(q->packet) + 1); STATUP(nsd, edns); diff --git query.h query.h index a950de3..f22b722 100644 --- query.h +++ query.h @@ -191,7 +191,7 @@ query_state_type query_process(query_type *q, nsd_type *nsd); * includes the packet header and question section. Space is reserved * for the optional EDNS record, if required. */ -void query_prepare_response(query_type *q, nsd_type* nsd); +void query_prepare_response(query_type *q); /* * Add EDNS0 information to the response if required. diff --git remote.c remote.c index c0319c4..d961dbf 100644 --- remote.c +++ remote.c @@ -237,10 +237,22 @@ daemon_remote_create(nsd_options_t* cfg) assert(cfg->control_enable); /* init SSL library */ +#ifdef HAVE_ERR_LOAD_CRYPTO_STRINGS ERR_load_crypto_strings(); +#endif ERR_load_SSL_strings(); +#if OPENSSL_VERSION_NUMBER < 0x10100000 || !defined(HAVE_OPENSSL_INIT_CRYPTO) OpenSSL_add_all_algorithms(); +#else + OPENSSL_init_crypto(OPENSSL_INIT_ADD_ALL_CIPHERS + | OPENSSL_INIT_ADD_ALL_DIGESTS + | OPENSSL_INIT_LOAD_CRYPTO_STRINGS, NULL); +#endif +#if OPENSSL_VERSION_NUMBER < 0x10100000 || !defined(HAVE_OPENSSL_INIT_SSL) (void)SSL_library_init(); +#else + OPENSSL_init_ssl(0, NULL); +#endif if(!RAND_status()) { /* try to seed it */ diff --git tsig-openssl.c tsig-openssl.c index 62203dc..f38284e 100644 --- tsig-openssl.c +++ tsig-openssl.c @@ -61,7 +61,11 @@ int tsig_openssl_init(region_type *region) { int count = 0; +#if OPENSSL_VERSION_NUMBER < 0x10100000 || !defined(HAVE_OPENSSL_INIT_CRYPTO) OpenSSL_add_all_digests(); +#else + OPENSSL_init_crypto(OPENSSL_INIT_ADD_ALL_DIGESTS, NULL); +#endif count += tsig_openssl_init_algorithm(region, "md5", "hmac-md5","hmac-md5.sig-alg.reg.int."); @@ -137,7 +141,9 @@ final(void *context, uint8_t *digest, size_t *size) void tsig_openssl_finalize() { +#ifdef HAVE_EVP_CLEANUP EVP_cleanup(); +#endif } #endif /* defined(HAVE_SSL) */ diff --git tsig.c tsig.c index 316f477..b58191c 100644 --- tsig.c +++ tsig.c @@ -192,6 +192,9 @@ tsig_get_algorithm_by_name(const char *name) { return algorithm_entry->algorithm; } + if(strncmp("hmac-", algorithm_entry->algorithm->short_name, 5) == 0 && tsig_strlowercmp(name, algorithm_entry->algorithm->short_name+5) == 0) { + return algorithm_entry->algorithm; + } } return NULL; diff --git xfrd-disk.c xfrd-disk.c index 654e78e..c724a2c 100644 --- xfrd-disk.c +++ xfrd-disk.c @@ -267,14 +267,14 @@ xfrd_read_state(struct xfrd_state* xfrd) * 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) + if (soa_refresh > (time_t)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) + else if (soa_refresh < (time_t)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 - > soa_refresh)) + > (uint32_t)soa_refresh)) { zone->state = xfrd_zone_refreshing; xfrd_set_refresh_now(zone); diff --git xfrd-tcp.c xfrd-tcp.c index 9bc01d3..a4a7a23 100644 --- xfrd-tcp.c +++ xfrd-tcp.c @@ -870,6 +870,11 @@ xfrd_tcp_read(struct xfrd_tcp_pipeline* tp) tp->num_skip++; /* fall through to remove zone from tp */ case xfrd_packet_transfer: + if(zone->zone_options->pattern->multi_master_check) { + xfrd_tcp_release(xfrd->tcp_set, zone); + xfrd_make_request(zone); + break; + } xfrd_tcp_release(xfrd->tcp_set, zone); assert(zone->round_num == -1); break; diff --git xfrd.c xfrd.c index 0eacce7..a0b3444 100644 --- xfrd.c +++ xfrd.c @@ -437,6 +437,8 @@ xfrd_init_slave_zone(xfrd_state_t* xfrd, zone_options_t* zone_opt) xzone->udp_waiting = 0; xzone->is_activated = 0; + xzone->multi_master_first_master = -1; + xzone->multi_master_update_check = -1; tsig_create_record_custom(&xzone->tsig, NULL, 0, 0, 4); /* set refreshing anyway, if we have data it may be old */ @@ -703,9 +705,9 @@ xfrd_set_timer_refresh(xfrd_zone_t* zone) } /* refresh or expire timeout, whichever is earlier */ set_refresh = ntohl(zone->soa_disk.refresh); - if (set_refresh > zone->zone_options->pattern->max_refresh_time) + if (set_refresh > (time_t)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) + else if (set_refresh < (time_t)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); @@ -750,9 +752,9 @@ xfrd_set_timer_retry(xfrd_zone_t* zone) zone->soa_disk_acquired + (time_t)ntohl(zone->soa_disk.expire)) { set_retry = ntohl(zone->soa_disk.retry); - if(set_retry > zone->zone_options->pattern->max_retry_time) + if(set_retry > (time_t)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) + else if(set_retry < (time_t)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; @@ -875,8 +877,31 @@ xfrd_make_request(xfrd_zone_t* zone) DEBUG(DEBUG_XFRD,1, (LOG_INFO, "xfrd zone %s makereq wait_retry, rd %d mr %d nx %d", zone->apex_str, zone->round_num, zone->master_num, zone->next_master)); + zone->multi_master_first_master = -1; + return; + } + } + + /* multi-master-check */ + if(zone->zone_options->pattern->multi_master_check) { + if(zone->multi_master_first_master == zone->master_num && + zone->round_num > 0 && + zone->state != xfrd_zone_expired) { + /* tried all servers and update zone */ + if(zone->multi_master_update_check >= 0) { + VERBOSITY(2, (LOG_INFO, "xfrd: multi master " + "check: zone %s completed transfers", + zone->apex_str)); + } + zone->round_num = -1; /* next try start anew */ + zone->multi_master_first_master = -1; + xfrd_set_timer_refresh(zone); return; } + if(zone->multi_master_first_master < 0) { + zone->multi_master_first_master = zone->master_num; + zone->multi_master_update_check = -1; + } } /* cache ixfr_disabled only for XFRD_NO_IXFR_CACHE time */ @@ -1267,6 +1292,11 @@ xfrd_udp_read(xfrd_zone_t* zone) xfrd_tcp_obtain(xfrd->tcp_set, zone); break; case xfrd_packet_transfer: + if(zone->zone_options->pattern->multi_master_check) { + xfrd_udp_release(zone); + xfrd_make_request(zone); + break; + } case xfrd_packet_newlease: /* nothing more to do */ assert(zone->round_num == -1); @@ -1835,6 +1865,10 @@ xfrd_parse_received_xfr_packet(xfrd_zone_t* zone, buffer_type* packet, zone->soa_disk_acquired = xfrd_time(); if(zone->soa_nsd.serial == soa->serial) zone->soa_nsd_acquired = xfrd_time(); + if(zone->zone_options->pattern->multi_master_check) { + region_destroy(tempregion); + return xfrd_packet_drop; + } xfrd_set_zone_state(zone, xfrd_zone_ok); DEBUG(DEBUG_XFRD,1, (LOG_INFO, "xfrd: zone %s is ok", zone->apex_str)); @@ -1992,7 +2026,7 @@ xfrd_handle_received_xfr_packet(xfrd_zone_t* zone, buffer_type* packet) 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); + log_msg(LOG_INFO, "xfrd : transferred zone data was too large %llu", (long long unsigned)xfrfile_size); return xfrd_packet_bad; } if(res == xfrd_packet_more) { @@ -2048,6 +2082,11 @@ xfrd_handle_received_xfr_packet(xfrd_zone_t* zone, buffer_type* packet) DEBUG(DEBUG_XFRD,1, (LOG_INFO, "xfrd: zone %s is waiting for reload", zone->apex_str)); + if(zone->zone_options->pattern->multi_master_check) { + zone->multi_master_update_check = zone->master_num; + xfrd_set_reload_timeout(); + return xfrd_packet_transfer; + } zone->round_num = -1; /* next try start anew */ xfrd_set_timer_refresh(zone); xfrd_set_reload_timeout(); @@ -2256,8 +2295,17 @@ xfrd_check_failed_updates() "transfer (notified zone)", zone->apex_str, (unsigned)ntohl(zone->soa_disk.serial)); /* revert the soa; it has not been acquired properly */ - zone->soa_disk_acquired = zone->soa_nsd_acquired; - zone->soa_disk = zone->soa_nsd; + if(zone->soa_disk_acquired == zone->soa_nsd_acquired) { + /* this was the same as served, + * perform force_axfr , re-download + * same serial from master */ + zone->soa_disk_acquired = 0; + zone->soa_nsd_acquired = 0; + } else { + /* revert soa to the one in server */ + zone->soa_disk_acquired = zone->soa_nsd_acquired; + zone->soa_disk = zone->soa_nsd; + } /* pretend we are notified with disk soa. This will cause a refetch of the data, and reload. */ xfrd_handle_incoming_notify(zone, &dumped_soa); diff --git xfrd.h xfrd.h index ff903db..24541db 100644 --- xfrd.h +++ xfrd.h @@ -217,6 +217,8 @@ struct xfrd_zone { tsig_record_type tsig; /* tsig state for IXFR/AXFR */ uint64_t xfrfilenumber; /* identifier for file to store xfr into, valid if msg_seq_nr nonzero */ + int multi_master_first_master; /* >0: first check master_num */ + int multi_master_update_check; /* -1: not update >0: last update master_num */ }; enum xfrd_packet_result { diff --git zlexer.lex zlexer.lex index 90a1df3..a25d7af 100644 --- zlexer.lex +++ zlexer.lex @@ -7,6 +7,8 @@ * See LICENSE for the license. * */ +/* because flex keeps having sign-unsigned compare problems that are unfixed*/ +#pragma GCC diagnostic ignored "-Wsign-compare" #include "config.h" diff --git zparser.y zparser.y index d823478..a2e6fcd 100644 --- zparser.y +++ zparser.y @@ -68,7 +68,7 @@ nsec3_add_params(const char* hash_algo_str, const char* flag_str, %token <type> T_AXFR T_MAILB T_MAILA T_DS T_DLV T_SSHFP T_RRSIG T_NSEC T_DNSKEY %token <type> T_SPF T_NSEC3 T_IPSECKEY T_DHCID T_NSEC3PARAM T_TLSA T_URI %token <type> T_NID T_L32 T_L64 T_LP T_EUI48 T_EUI64 T_CAA T_CDS T_CDNSKEY -%token <type> T_CSYNC +%token <type> T_OPENPGPKEY T_CSYNC /* other tokens */ %token DOLLAR_TTL DOLLAR_ORIGIN NL SP @@ -633,6 +633,8 @@ type_and_rdata: | T_CDS sp rdata_unknown { $$ = $1; parse_unknown_rdata($1, $3); } | T_CDNSKEY sp rdata_dnskey | T_CDNSKEY sp rdata_unknown { $$ = $1; parse_unknown_rdata($1, $3); } + | T_OPENPGPKEY sp rdata_openpgpkey + | T_OPENPGPKEY sp rdata_unknown { $$ = $1; parse_unknown_rdata($1, $3); } | T_CSYNC sp rdata_csync | T_CSYNC sp rdata_unknown { $$ = $1; parse_unknown_rdata($1, $3); } | T_URI sp rdata_uri @@ -1053,6 +1055,13 @@ rdata_caa: STR sp STR sp STR trail } ; +/* RFC7929 */ +rdata_openpgpkey: str_sp_seq trail + { + zadd_rdata_wireformat(zparser_conv_b64(parser->region, $1.str)); + } + ; + /* RFC7477 */ rdata_csync: STR sp STR nsec_seq { -- I'm not entirely sure you are real.