diff --git a/Makefile.in b/Makefile.in index 0a2e7f9b6..22fb75c12 100644 --- a/Makefile.in +++ b/Makefile.in @@ -738,7 +738,7 @@ msgencode.lo msgencode.o: $(srcdir)/util/data/msgencode.c config.h $(srcdir)/uti msgparse.lo msgparse.o: $(srcdir)/util/data/msgparse.c config.h $(srcdir)/util/data/msgparse.h \ $(srcdir)/util/storage/lruhash.h $(srcdir)/util/locks.h $(srcdir)/util/log.h $(srcdir)/sldns/pkthdr.h \ $(srcdir)/sldns/rrdef.h $(srcdir)/util/data/msgreply.h $(srcdir)/util/data/packed_rrset.h \ - $(srcdir)/util/data/dname.h $(srcdir)/util/storage/lookup3.h $(srcdir)/util/regional.h $(srcdir)/sldns/sbuffer.h \ + $(srcdir)/util/data/dname.h $(srcdir)/util/storage/lookup3.h $(srcdir)/util/regional.h $(srcdir)/util/net_help.h $(srcdir)/sldns/sbuffer.h \ $(srcdir)/sldns/parseutil.h $(srcdir)/sldns/wire2str.h msgreply.lo msgreply.o: $(srcdir)/util/data/msgreply.c config.h $(srcdir)/util/data/msgreply.h \ $(srcdir)/util/storage/lruhash.h $(srcdir)/util/locks.h $(srcdir)/util/log.h $(srcdir)/util/data/packed_rrset.h \ @@ -793,7 +793,7 @@ iter_priv.lo iter_priv.o: $(srcdir)/iterator/iter_priv.c config.h $(srcdir)/iter $(srcdir)/util/data/msgparse.h $(srcdir)/sldns/pkthdr.h $(srcdir)/sldns/rrdef.h $(srcdir)/util/net_help.h \ $(srcdir)/util/storage/dnstree.h $(srcdir)/sldns/str2wire.h $(srcdir)/sldns/sbuffer.h iter_resptype.lo iter_resptype.o: $(srcdir)/iterator/iter_resptype.c config.h \ - $(srcdir)/iterator/iter_resptype.h $(srcdir)/iterator/iter_delegpt.h $(srcdir)/util/log.h \ + $(srcdir)/iterator/iter_resptype.h $(srcdir)/iterator/iter_delegpt.h $(srcdir)/iterator/iterator.h $(srcdir)/util/log.h \ $(srcdir)/services/cache/dns.h $(srcdir)/util/storage/lruhash.h $(srcdir)/util/locks.h \ $(srcdir)/util/data/msgreply.h $(srcdir)/util/data/packed_rrset.h $(srcdir)/util/net_help.h \ $(srcdir)/util/data/dname.h $(srcdir)/sldns/rrdef.h $(srcdir)/sldns/pkthdr.h diff --git a/cachedb/cachedb.c b/cachedb/cachedb.c index 30645268c..b912be8ed 100644 --- a/cachedb/cachedb.c +++ b/cachedb/cachedb.c @@ -265,11 +265,11 @@ cachedb_init(struct module_env* env, int id) return 0; } cachedb_env->enabled = 1; - if(env->cfg->serve_expired_reply_ttl) + if(env->cfg->serve_expired && env->cfg->serve_expired_reply_ttl) log_warn( "cachedb: serve-expired-reply-ttl is set but not working for data " - "originating from the external cache; 0 TLL is used for those."); - if(env->cfg->serve_expired_client_timeout) + "originating from the external cache; 0 TTL is used for those."); + if(env->cfg->serve_expired && env->cfg->serve_expired_client_timeout) log_warn( "cachedb: serve-expired-client-timeout is set but not working for " "data originating from the external cache; expired data are used " @@ -815,6 +815,11 @@ cachedb_handle_response(struct module_qstate* qstate, qstate->ext_state[id] = module_finished; return; } + if(qstate->env->cfg->cachedb_no_store) { + /* do not store the item in the external cache */ + qstate->ext_state[id] = module_finished; + return; + } /* store the item into the backend cache */ cachedb_extcache_store(qstate, ie); diff --git a/cachedb/redis.c b/cachedb/redis.c index 93a575a4c..6cc975901 100644 --- a/cachedb/redis.c +++ b/cachedb/redis.c @@ -59,11 +59,28 @@ struct redis_moddata { const char* server_path; /* server's unix path, or "", NULL if unused */ const char* server_password; /* server's AUTH password, or "", NULL if unused */ struct timeval timeout; /* timeout for connection setup and commands */ + int logical_db; /* the redis logical database to use */ }; static redisReply* redis_command(struct module_env*, struct cachedb_env*, const char*, const uint8_t*, size_t); +static void +moddata_clean(struct redis_moddata** moddata) { + if(!moddata || !*moddata) + return; + if((*moddata)->ctxs) { + int i; + for(i = 0; i < (*moddata)->numctxs; i++) { + if((*moddata)->ctxs[i]) + redisFree((*moddata)->ctxs[i]); + } + free((*moddata)->ctxs); + } + free(*moddata); + *moddata = NULL; +} + static redisContext* redis_connect(const struct redis_moddata* moddata) { @@ -97,10 +114,21 @@ redis_connect(const struct redis_moddata* moddata) } freeReplyObject(rep); } + if(moddata->logical_db > 0) { + redisReply* rep; + rep = redisCommand(ctx, "SELECT %d", moddata->logical_db); + if(!rep || rep->type == REDIS_REPLY_ERROR) { + log_err("failed to set logical database (%d)", + moddata->logical_db); + freeReplyObject(rep); + goto fail; + } + freeReplyObject(rep); + } verbose(VERB_OPS, "Connection to Redis established"); return ctx; - fail: +fail: if(ctx) redisFree(ctx); return NULL; @@ -117,14 +145,13 @@ redis_init(struct module_env* env, struct cachedb_env* cachedb_env) moddata = calloc(1, sizeof(struct redis_moddata)); if(!moddata) { log_err("out of memory"); - return 0; + goto fail; } moddata->numctxs = env->cfg->num_threads; moddata->ctxs = calloc(env->cfg->num_threads, sizeof(redisContext*)); if(!moddata->ctxs) { log_err("out of memory"); - free(moddata); - return 0; + goto fail; } /* note: server_host is a shallow reference to configured string. * we don't have to free it in this module. */ @@ -134,8 +161,15 @@ redis_init(struct module_env* env, struct cachedb_env* cachedb_env) moddata->server_password = env->cfg->redis_server_password; moddata->timeout.tv_sec = env->cfg->redis_timeout / 1000; moddata->timeout.tv_usec = (env->cfg->redis_timeout % 1000) * 1000; - for(i = 0; i < moddata->numctxs; i++) - moddata->ctxs[i] = redis_connect(moddata); + moddata->logical_db = env->cfg->redis_logical_db; + for(i = 0; i < moddata->numctxs; i++) { + redisContext* ctx = redis_connect(moddata); + if(!ctx) { + log_err("redis_init: failed to init redis"); + goto fail; + } + moddata->ctxs[i] = ctx; + } cachedb_env->backend_data = moddata; if(env->cfg->redis_expire_records) { redisReply* rep = NULL; @@ -148,7 +182,7 @@ redis_init(struct module_env* env, struct cachedb_env* cachedb_env) log_err("redis_init: failed to init redis, the " "redis-expire-records option requires the SETEX command " "(redis >= 2.0.0)"); - return 0; + goto fail; } redis_reply_type = rep->type; freeReplyObject(rep); @@ -160,11 +194,14 @@ redis_init(struct module_env* env, struct cachedb_env* cachedb_env) log_err("redis_init: failed to init redis, the " "redis-expire-records option requires the SETEX command " "(redis >= 2.0.0)"); - return 0; + goto fail; } } - return 1; + +fail: + moddata_clean(&moddata); + return 0; } static void @@ -175,18 +212,7 @@ redis_deinit(struct module_env* env, struct cachedb_env* cachedb_env) (void)env; verbose(VERB_OPS, "Redis deinitialization"); - - if(!moddata) - return; - if(moddata->ctxs) { - int i; - for(i = 0; i < moddata->numctxs; i++) { - if(moddata->ctxs[i]) - redisFree(moddata->ctxs[i]); - } - free(moddata->ctxs); - } - free(moddata); + moddata_clean(&moddata); } /* diff --git a/configure b/configure index 4521e0a6e..8324e9b34 100755 --- a/configure +++ b/configure @@ -4605,450 +4605,186 @@ fi default_cflags=yes fi -ac_ext=c -ac_cpp='$CPP $CPPFLAGS' -ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' -ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' -ac_compiler_gnu=$ac_cv_c_compiler_gnu -if test -n "$ac_tool_prefix"; then - # Extract the first word of "${ac_tool_prefix}gcc", so it can be a program name with args. -set dummy ${ac_tool_prefix}gcc; ac_word=$2 -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 -$as_echo_n "checking for $ac_word... " >&6; } -if ${ac_cv_prog_CC+:} false; then : - $as_echo_n "(cached) " >&6 -else - if test -n "$CC"; then - ac_cv_prog_CC="$CC" # Let the user override the test. -else -as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then - ac_cv_prog_CC="${ac_tool_prefix}gcc" - $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 - break 2 - fi -done - done -IFS=$as_save_IFS - -fi -fi -CC=$ac_cv_prog_CC -if test -n "$CC"; then - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 -$as_echo "$CC" >&6; } -else - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } -fi - - -fi -if test -z "$ac_cv_prog_CC"; then - ac_ct_CC=$CC - # Extract the first word of "gcc", so it can be a program name with args. -set dummy gcc; ac_word=$2 -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 -$as_echo_n "checking for $ac_word... " >&6; } -if ${ac_cv_prog_ac_ct_CC+:} false; then : - $as_echo_n "(cached) " >&6 -else - if test -n "$ac_ct_CC"; then - ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. -else -as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then - ac_cv_prog_ac_ct_CC="gcc" - $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 - break 2 - fi -done - done -IFS=$as_save_IFS - -fi -fi -ac_ct_CC=$ac_cv_prog_ac_ct_CC -if test -n "$ac_ct_CC"; then - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5 -$as_echo "$ac_ct_CC" >&6; } -else - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } -fi - - if test "x$ac_ct_CC" = x; then - CC="" - else - case $cross_compiling:$ac_tool_warned in -yes:) -{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 -$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} -ac_tool_warned=yes ;; -esac - CC=$ac_ct_CC - fi -else - CC="$ac_cv_prog_CC" -fi - -if test -z "$CC"; then - if test -n "$ac_tool_prefix"; then - # Extract the first word of "${ac_tool_prefix}cc", so it can be a program name with args. -set dummy ${ac_tool_prefix}cc; ac_word=$2 -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 -$as_echo_n "checking for $ac_word... " >&6; } -if ${ac_cv_prog_CC+:} false; then : - $as_echo_n "(cached) " >&6 -else - if test -n "$CC"; then - ac_cv_prog_CC="$CC" # Let the user override the test. -else -as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then - ac_cv_prog_CC="${ac_tool_prefix}cc" - $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 - break 2 - fi -done - done -IFS=$as_save_IFS - -fi -fi -CC=$ac_cv_prog_CC -if test -n "$CC"; then - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 -$as_echo "$CC" >&6; } -else - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } -fi - - - fi -fi -if test -z "$CC"; then - # Extract the first word of "cc", so it can be a program name with args. -set dummy cc; ac_word=$2 -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 -$as_echo_n "checking for $ac_word... " >&6; } -if ${ac_cv_prog_CC+:} false; then : - $as_echo_n "(cached) " >&6 -else - if test -n "$CC"; then - ac_cv_prog_CC="$CC" # Let the user override the test. -else - ac_prog_rejected=no -as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then - if test "$as_dir/$ac_word$ac_exec_ext" = "/usr/ucb/cc"; then - ac_prog_rejected=yes - continue - fi - ac_cv_prog_CC="cc" - $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 - break 2 - fi -done - done -IFS=$as_save_IFS - -if test $ac_prog_rejected = yes; then - # We found a bogon in the path, so make sure we never use it. - set dummy $ac_cv_prog_CC - shift - if test $# != 0; then - # We chose a different compiler from the bogus one. - # However, it has the same basename, so the bogon will be chosen - # first if we set CC to just the basename; use the full file name. - shift - ac_cv_prog_CC="$as_dir/$ac_word${1+' '}$@" - fi -fi -fi -fi -CC=$ac_cv_prog_CC -if test -n "$CC"; then - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 -$as_echo "$CC" >&6; } -else - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } -fi - - -fi -if test -z "$CC"; then - if test -n "$ac_tool_prefix"; then - for ac_prog in cl.exe - do - # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args. -set dummy $ac_tool_prefix$ac_prog; ac_word=$2 -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 -$as_echo_n "checking for $ac_word... " >&6; } -if ${ac_cv_prog_CC+:} false; then : - $as_echo_n "(cached) " >&6 -else - if test -n "$CC"; then - ac_cv_prog_CC="$CC" # Let the user override the test. -else -as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then - ac_cv_prog_CC="$ac_tool_prefix$ac_prog" - $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 - break 2 - fi -done - done -IFS=$as_save_IFS - -fi -fi -CC=$ac_cv_prog_CC -if test -n "$CC"; then - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 -$as_echo "$CC" >&6; } -else - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } -fi - - - test -n "$CC" && break - done -fi -if test -z "$CC"; then - ac_ct_CC=$CC - for ac_prog in cl.exe -do - # Extract the first word of "$ac_prog", so it can be a program name with args. -set dummy $ac_prog; ac_word=$2 -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 -$as_echo_n "checking for $ac_word... " >&6; } -if ${ac_cv_prog_ac_ct_CC+:} false; then : + case $ac_cv_prog_cc_stdc in #( + no) : + ac_cv_prog_cc_c99=no; ac_cv_prog_cc_c89=no ;; #( + *) : + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $CC option to accept ISO C99" >&5 +$as_echo_n "checking for $CC option to accept ISO C99... " >&6; } +if ${ac_cv_prog_cc_c99+:} false; then : $as_echo_n "(cached) " >&6 else - if test -n "$ac_ct_CC"; then - ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. -else -as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then - ac_cv_prog_ac_ct_CC="$ac_prog" - $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 - break 2 - fi -done - done -IFS=$as_save_IFS + ac_cv_prog_cc_c99=no +ac_save_CC=$CC +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +#include +#include +#include +#include -fi -fi -ac_ct_CC=$ac_cv_prog_ac_ct_CC -if test -n "$ac_ct_CC"; then - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5 -$as_echo "$ac_ct_CC" >&6; } -else - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } -fi +// Check varargs macros. These examples are taken from C99 6.10.3.5. +#define debug(...) fprintf (stderr, __VA_ARGS__) +#define showlist(...) puts (#__VA_ARGS__) +#define report(test,...) ((test) ? puts (#test) : printf (__VA_ARGS__)) +static void +test_varargs_macros (void) +{ + int x = 1234; + int y = 5678; + debug ("Flag"); + debug ("X = %d\n", x); + showlist (The first, second, and third items.); + report (x>y, "x is %d but y is %d", x, y); +} +// Check long long types. +#define BIG64 18446744073709551615ull +#define BIG32 4294967295ul +#define BIG_OK (BIG64 / BIG32 == 4294967297ull && BIG64 % BIG32 == 0) +#if !BIG_OK + your preprocessor is broken; +#endif +#if BIG_OK +#else + your preprocessor is broken; +#endif +static long long int bignum = -9223372036854775807LL; +static unsigned long long int ubignum = BIG64; - test -n "$ac_ct_CC" && break -done +struct incomplete_array +{ + int datasize; + double data[]; +}; - if test "x$ac_ct_CC" = x; then - CC="" - else - case $cross_compiling:$ac_tool_warned in -yes:) -{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 -$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} -ac_tool_warned=yes ;; -esac - CC=$ac_ct_CC - fi -fi +struct named_init { + int number; + const wchar_t *name; + double average; +}; -fi +typedef const char *ccp; +static inline int +test_restrict (ccp restrict text) +{ + // See if C++-style comments work. + // Iterate through items via the restricted pointer. + // Also check for declarations in for loops. + for (unsigned int i = 0; *(text+i) != '\0'; ++i) + continue; + return 0; +} -test -z "$CC" && { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 -$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} -as_fn_error $? "no acceptable C compiler found in \$PATH -See \`config.log' for more details" "$LINENO" 5; } +// Check varargs and va_copy. +static void +test_varargs (const char *format, ...) +{ + va_list args; + va_start (args, format); + va_list args_copy; + va_copy (args_copy, args); -# Provide some information about the compiler. -$as_echo "$as_me:${as_lineno-$LINENO}: checking for C compiler version" >&5 -set X $ac_compile -ac_compiler=$2 -for ac_option in --version -v -V -qversion; do - { { ac_try="$ac_compiler $ac_option >&5" -case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" -$as_echo "$ac_try_echo"; } >&5 - (eval "$ac_compiler $ac_option >&5") 2>conftest.err - ac_status=$? - if test -s conftest.err; then - sed '10a\ -... rest of stderr output deleted ... - 10q' conftest.err >conftest.er1 - cat conftest.er1 >&5 - fi - rm -f conftest.er1 conftest.err - $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 - test $ac_status = 0; } -done + const char *str; + int number; + float fnumber; -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are using the GNU C compiler" >&5 -$as_echo_n "checking whether we are using the GNU C compiler... " >&6; } -if ${ac_cv_c_compiler_gnu+:} false; then : - $as_echo_n "(cached) " >&6 -else - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ + while (*format) + { + switch (*format++) + { + case 's': // string + str = va_arg (args_copy, const char *); + break; + case 'd': // int + number = va_arg (args_copy, int); + break; + case 'f': // float + fnumber = va_arg (args_copy, double); + break; + default: + break; + } + } + va_end (args_copy); + va_end (args); +} int main () { -#ifndef __GNUC__ - choke me -#endif - ; - return 0; -} -_ACEOF -if ac_fn_c_try_compile "$LINENO"; then : - ac_compiler_gnu=yes -else - ac_compiler_gnu=no -fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext -ac_cv_c_compiler_gnu=$ac_compiler_gnu + // Check bool. + _Bool success = false; -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_compiler_gnu" >&5 -$as_echo "$ac_cv_c_compiler_gnu" >&6; } -if test $ac_compiler_gnu = yes; then - GCC=yes -else - GCC= -fi -ac_test_CFLAGS=${CFLAGS+set} -ac_save_CFLAGS=$CFLAGS -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CC accepts -g" >&5 -$as_echo_n "checking whether $CC accepts -g... " >&6; } -if ${ac_cv_prog_cc_g+:} false; then : - $as_echo_n "(cached) " >&6 -else - ac_save_c_werror_flag=$ac_c_werror_flag - ac_c_werror_flag=yes - ac_cv_prog_cc_g=no - CFLAGS="-g" - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ + // Check restrict. + if (test_restrict ("String literal") == 0) + success = true; + char *restrict newvar = "Another string"; -int -main () -{ + // Check varargs. + test_varargs ("s, d' f .", "string", 65, 34.234); + test_varargs_macros (); - ; - return 0; -} -_ACEOF -if ac_fn_c_try_compile "$LINENO"; then : - ac_cv_prog_cc_g=yes -else - CFLAGS="" - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ + // Check flexible array members. + struct incomplete_array *ia = + malloc (sizeof (struct incomplete_array) + (sizeof (double) * 10)); + ia->datasize = 10; + for (int i = 0; i < ia->datasize; ++i) + ia->data[i] = i * 1.234; -int -main () -{ + // Check named initializers. + struct named_init ni = { + .number = 34, + .name = L"Test wide string", + .average = 543.34343, + }; - ; - return 0; -} -_ACEOF -if ac_fn_c_try_compile "$LINENO"; then : + ni.number = 58; -else - ac_c_werror_flag=$ac_save_c_werror_flag - CFLAGS="-g" - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ + int dynamic_array[ni.number]; + dynamic_array[ni.number - 1] = 543; -int -main () -{ + // work around unused variable warnings + return (!success || bignum == 0LL || ubignum == 0uLL || newvar[0] == 'x' + || dynamic_array[ni.number - 1] != 543); ; return 0; } _ACEOF -if ac_fn_c_try_compile "$LINENO"; then : - ac_cv_prog_cc_g=yes -fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext -fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +for ac_arg in '' -std=gnu99 -std=c99 -c99 -AC99 -D_STDC_C99= -qlanglvl=extc99 +do + CC="$ac_save_CC $ac_arg" + if ac_fn_c_try_compile "$LINENO"; then : + ac_cv_prog_cc_c99=$ac_arg fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext - ac_c_werror_flag=$ac_save_c_werror_flag +rm -f core conftest.err conftest.$ac_objext + test "x$ac_cv_prog_cc_c99" != "xno" && break +done +rm -f conftest.$ac_ext +CC=$ac_save_CC + fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_g" >&5 -$as_echo "$ac_cv_prog_cc_g" >&6; } -if test "$ac_test_CFLAGS" = set; then - CFLAGS=$ac_save_CFLAGS -elif test $ac_cv_prog_cc_g = yes; then - if test "$GCC" = yes; then - CFLAGS="-g -O2" - else - CFLAGS="-g" - fi +# AC_CACHE_VAL +case "x$ac_cv_prog_cc_c99" in + x) + { $as_echo "$as_me:${as_lineno-$LINENO}: result: none needed" >&5 +$as_echo "none needed" >&6; } ;; + xno) + { $as_echo "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5 +$as_echo "unsupported" >&6; } ;; + *) + CC="$CC $ac_cv_prog_cc_c99" + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c99" >&5 +$as_echo "$ac_cv_prog_cc_c99" >&6; } ;; +esac +if test "x$ac_cv_prog_cc_c99" != xno; then : + ac_cv_prog_cc_stdc=$ac_cv_prog_cc_c99 else - if test "$GCC" = yes; then - CFLAGS="-O2" - else - CFLAGS= - fi -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $CC option to accept ISO C89" >&5 + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $CC option to accept ISO C89" >&5 $as_echo_n "checking for $CC option to accept ISO C89... " >&6; } if ${ac_cv_prog_cc_c89+:} false; then : $as_echo_n "(cached) " >&6 @@ -5135,14 +4871,31 @@ $as_echo "unsupported" >&6; } ;; $as_echo "$ac_cv_prog_cc_c89" >&6; } ;; esac if test "x$ac_cv_prog_cc_c89" != xno; then : + ac_cv_prog_cc_stdc=$ac_cv_prog_cc_c89 +else + ac_cv_prog_cc_stdc=no +fi +fi + ;; +esac + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $CC option to accept ISO Standard C" >&5 +$as_echo_n "checking for $CC option to accept ISO Standard C... " >&6; } + if ${ac_cv_prog_cc_stdc+:} false; then : + $as_echo_n "(cached) " >&6 fi -ac_ext=c -ac_cpp='$CPP $CPPFLAGS' -ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' -ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' -ac_compiler_gnu=$ac_cv_c_compiler_gnu + case $ac_cv_prog_cc_stdc in #( + no) : + { $as_echo "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5 +$as_echo "unsupported" >&6; } ;; #( + '') : + { $as_echo "$as_me:${as_lineno-$LINENO}: result: none needed" >&5 +$as_echo "none needed" >&6; } ;; #( + *) : + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_stdc" >&5 +$as_echo "$ac_cv_prog_cc_stdc" >&6; } ;; +esac { $as_echo "$as_me:${as_lineno-$LINENO}: checking $CC dependency flag" >&5 diff --git a/configure.ac b/configure.ac index f6f5f21fc..9aa781a12 100644 --- a/configure.ac +++ b/configure.ac @@ -280,7 +280,7 @@ ACX_CHECK_COMPILER_FLAG(g, [CFLAGS="$CFLAGS -g"]) ACX_CHECK_COMPILER_FLAG(O2, [CFLAGS="$CFLAGS -O2"]) default_cflags=yes fi -AC_PROG_CC +m4_version_prereq([2.70], [AC_PROG_CC], [AC_PROG_CC_STDC]) ACX_DEPFLAG ACX_DETERMINE_EXT_FLAGS_UNBOUND diff --git a/daemon/remote.c b/daemon/remote.c index 4990fc8e9..3eb711ce6 100644 --- a/daemon/remote.c +++ b/daemon/remote.c @@ -523,12 +523,13 @@ ssl_print_text(RES* res, const char* text) if(res->ssl) { ERR_clear_error(); if((r=SSL_write(res->ssl, text, (int)strlen(text))) <= 0) { - if(SSL_get_error(res->ssl, r) == SSL_ERROR_ZERO_RETURN) { + int r2; + if((r2=SSL_get_error(res->ssl, r)) == SSL_ERROR_ZERO_RETURN) { verbose(VERB_QUERY, "warning, in SSL_write, peer " "closed connection"); return 0; } - log_crypto_err("could not SSL_write"); + log_crypto_err_io("could not SSL_write", r2); return 0; } } else { @@ -579,11 +580,12 @@ ssl_read_line(RES* res, char* buf, size_t max) if(res->ssl) { ERR_clear_error(); if((r=SSL_read(res->ssl, buf+len, 1)) <= 0) { - if(SSL_get_error(res->ssl, r) == SSL_ERROR_ZERO_RETURN) { + int r2; + if((r2=SSL_get_error(res->ssl, r)) == SSL_ERROR_ZERO_RETURN) { buf[len] = 0; return 1; } - log_crypto_err("could not SSL_read"); + log_crypto_err_io("could not SSL_read", r2); return 0; } } else { @@ -596,7 +598,7 @@ ssl_read_line(RES* res, char* buf, size_t max) } if(errno == EINTR || errno == EAGAIN) continue; - log_err("could not recv: %s", + if(rr < 0) log_err("could not recv: %s", sock_strerror(errno)); return 0; } @@ -1223,8 +1225,8 @@ do_zones_add(RES* ssl, struct local_zones* zones) char buf[2048]; int num = 0; while(ssl_read_line(ssl, buf, sizeof(buf))) { - if(buf[0] == 0x04 && buf[1] == 0) - break; /* end of transmission */ + if(buf[0] == 0 || (buf[0] == 0x04 && buf[1] == 0)) + break; /* zero byte line or end of transmission */ if(!perform_zone_add(ssl, zones, buf)) { if(!ssl_printf(ssl, "error for input line: %s\n", buf)) return; @@ -1272,8 +1274,8 @@ do_zones_remove(RES* ssl, struct local_zones* zones) char buf[2048]; int num = 0; while(ssl_read_line(ssl, buf, sizeof(buf))) { - if(buf[0] == 0x04 && buf[1] == 0) - break; /* end of transmission */ + if(buf[0] == 0 || (buf[0] == 0x04 && buf[1] == 0)) + break; /* zero byte line or end of transmission */ if(!perform_zone_remove(ssl, zones, buf)) { if(!ssl_printf(ssl, "error for input line: %s\n", buf)) return; @@ -1336,8 +1338,8 @@ do_datas_add(RES* ssl, struct local_zones* zones) char buf[2048]; int num = 0, line = 0; while(ssl_read_line(ssl, buf, sizeof(buf))) { - if(buf[0] == 0x04 && buf[1] == 0) - break; /* end of transmission */ + if(buf[0] == 0 || (buf[0] == 0x04 && buf[1] == 0)) + break; /* zero byte line or end of transmission */ line++; if(perform_data_add(ssl, zones, buf, line)) num++; @@ -1376,8 +1378,8 @@ do_datas_remove(RES* ssl, struct local_zones* zones) char buf[2048]; int num = 0; while(ssl_read_line(ssl, buf, sizeof(buf))) { - if(buf[0] == 0x04 && buf[1] == 0) - break; /* end of transmission */ + if(buf[0] == 0 || (buf[0] == 0x04 && buf[1] == 0)) + break; /* zero byte line or end of transmission */ if(!perform_data_remove(ssl, zones, buf)) { if(!ssl_printf(ssl, "error for input line: %s\n", buf)) return; @@ -3222,9 +3224,10 @@ handle_req(struct daemon_remote* rc, struct rc_state* s, RES* res) if(res->ssl) { ERR_clear_error(); if((r=SSL_read(res->ssl, magic, (int)sizeof(magic)-1)) <= 0) { - if(SSL_get_error(res->ssl, r) == SSL_ERROR_ZERO_RETURN) + int r2; + if((r2=SSL_get_error(res->ssl, r)) == SSL_ERROR_ZERO_RETURN) return; - log_crypto_err("could not SSL_read"); + log_crypto_err_io("could not SSL_read", r2); return; } } else { @@ -3291,7 +3294,7 @@ remote_handshake_later(struct daemon_remote* rc, struct rc_state* s, log_err("remote control connection closed prematurely"); log_addr(VERB_OPS, "failed connection from", &s->c->repinfo.remote_addr, s->c->repinfo.remote_addrlen); - log_crypto_err("remote control failed ssl"); + log_crypto_err_io("remote control failed ssl", r2); clean_point(rc, s); } return 0; diff --git a/daemon/worker.c b/daemon/worker.c index 8c6fa3b9a..8ae05eb67 100644 --- a/daemon/worker.c +++ b/daemon/worker.c @@ -66,6 +66,7 @@ #include "util/data/msgencode.h" #include "util/data/dname.h" #include "util/fptr_wlist.h" +#include "util/proxy_protocol.h" #include "util/tube.h" #include "util/edns.h" #include "util/timeval_func.h" @@ -542,6 +543,8 @@ answer_norec_from_cache(struct worker* worker, struct query_info* qinfo, edns->udp_size = EDNS_ADVERTISED_SIZE; edns->ext_rcode = 0; edns->bits &= EDNS_DO; + if(worker->env.cfg->disable_edns_do && (edns->bits & EDNS_DO)) + edns->edns_present = 0; if(!inplace_cb_reply_cache_call(&worker->env, qinfo, NULL, msg->rep, (int)(flags&LDNS_RCODE_MASK), edns, repinfo, worker->scratchpad, worker->env.now_tv)) @@ -702,6 +705,8 @@ answer_from_cache(struct worker* worker, struct query_info* qinfo, edns->udp_size = EDNS_ADVERTISED_SIZE; edns->ext_rcode = 0; edns->bits &= EDNS_DO; + if(worker->env.cfg->disable_edns_do && (edns->bits & EDNS_DO)) + edns->edns_present = 0; if(!inplace_cb_reply_servfail_call(&worker->env, qinfo, NULL, rep, LDNS_RCODE_SERVFAIL, edns, repinfo, worker->scratchpad, worker->env.now_tv)) @@ -742,6 +747,8 @@ answer_from_cache(struct worker* worker, struct query_info* qinfo, edns->udp_size = EDNS_ADVERTISED_SIZE; edns->ext_rcode = 0; edns->bits &= EDNS_DO; + if(worker->env.cfg->disable_edns_do && (edns->bits & EDNS_DO)) + edns->edns_present = 0; *alias_rrset = NULL; /* avoid confusion if caller set it to non-NULL */ if((worker->daemon->use_response_ip || worker->daemon->use_rpz) && !partial_rep && !apply_respip_action(worker, qinfo, cinfo, rep, @@ -2317,6 +2324,7 @@ worker_init(struct worker* worker, struct config_file *cfg, worker->env.cfg->stat_interval); worker_restart_timer(worker); } + pp_init(&sldns_write_uint16, &sldns_write_uint32); return 1; } diff --git a/dns64/dns64.c b/dns64/dns64.c index 1e31f51e8..83fb02779 100644 --- a/dns64/dns64.c +++ b/dns64/dns64.c @@ -573,28 +573,29 @@ static enum module_ext_state handle_event_pass(struct module_qstate* qstate, int id) { struct dns64_qstate* iq = (struct dns64_qstate*)qstate->minfo[id]; - if (iq && iq->state == DNS64_NEW_QUERY - && qstate->qinfo.qtype == LDNS_RR_TYPE_PTR - && qstate->qinfo.qname_len == 74 - && !strcmp((char*)&qstate->qinfo.qname[64], "\03ip6\04arpa")) - /* Handle PTR queries for IPv6 addresses. */ - return handle_ipv6_ptr(qstate, id); - - if (qstate->env->cfg->dns64_synthall && - iq && iq->state == DNS64_NEW_QUERY - && qstate->qinfo.qtype == LDNS_RR_TYPE_AAAA) - return generate_type_A_query(qstate, id); + int synth_all_cfg = qstate->env->cfg->dns64_synthall; + int synth_qname = 0; + + if(iq && iq->state == DNS64_NEW_QUERY + && qstate->qinfo.qtype == LDNS_RR_TYPE_PTR + && qstate->qinfo.qname_len == 74 + && !strcmp((char*)&qstate->qinfo.qname[64], "\03ip6\04arpa")) { + /* Handle PTR queries for IPv6 addresses. */ + return handle_ipv6_ptr(qstate, id); + } - if(dns64_always_synth_for_qname(qstate, id) && - iq && iq->state == DNS64_NEW_QUERY - && !(qstate->query_flags & BIT_CD) - && qstate->qinfo.qtype == LDNS_RR_TYPE_AAAA) { - verbose(VERB_ALGO, "dns64: ignore-aaaa and synthesize anyway"); + if(iq && iq->state == DNS64_NEW_QUERY && + qstate->qinfo.qtype == LDNS_RR_TYPE_AAAA && + (synth_all_cfg || + (synth_qname=(dns64_always_synth_for_qname(qstate, id) + && !(qstate->query_flags & BIT_CD))))) { + if(synth_qname) + verbose(VERB_ALGO, "dns64: ignore-aaaa and synthesize anyway"); return generate_type_A_query(qstate, id); } /* We are finished when our sub-query is finished. */ - if (iq && iq->state == DNS64_SUBQUERY_FINISHED) + if(iq && iq->state == DNS64_SUBQUERY_FINISHED) return module_finished; /* Otherwise, pass request to next module. */ @@ -627,32 +628,37 @@ handle_event_moddone(struct module_qstate* qstate, int id) * synthesize in (sec 5.1.2 of RFC6147). * - A successful AAAA query with an answer. */ - if((!iq || iq->state != DNS64_INTERNAL_QUERY) - && qstate->qinfo.qtype == LDNS_RR_TYPE_AAAA - && !(qstate->query_flags & BIT_CD) - && !(qstate->return_msg && - qstate->return_msg->rep && - reply_find_answer_rrset(&qstate->qinfo, - qstate->return_msg->rep))) - /* not internal, type AAAA, not CD, and no answer RRset, - * So, this is a AAAA noerror/nodata answer */ - return generate_type_A_query(qstate, id); - if((!iq || iq->state != DNS64_INTERNAL_QUERY) - && qstate->qinfo.qtype == LDNS_RR_TYPE_AAAA - && !(qstate->query_flags & BIT_CD) - && dns64_always_synth_for_qname(qstate, id)) { - /* if it is not internal, AAAA, not CD and listed domain, - * generate from A record and ignore AAAA */ - verbose(VERB_ALGO, "dns64: ignore-aaaa and synthesize anyway"); + /* When an AAAA query completes check if we want to perform DNS64 + * synthesis. We skip queries with DNSSEC enabled (!CD) and + * ones generated by us to retrive the A/PTR record to use for + * synth. */ + int could_synth = + qstate->qinfo.qtype == LDNS_RR_TYPE_AAAA && + (!iq || iq->state != DNS64_INTERNAL_QUERY) && + !(qstate->query_flags & BIT_CD); + int has_data = /* whether query returned non-empty rrset */ + qstate->return_msg && + qstate->return_msg->rep && + reply_find_answer_rrset(&qstate->qinfo, qstate->return_msg->rep); + int synth_qname = 0; + + if(could_synth && + (!has_data || + (synth_qname=dns64_always_synth_for_qname(qstate, id)))) { + if(synth_qname) + verbose(VERB_ALGO, "dns64: ignore-aaaa and synthesize anyway"); return generate_type_A_query(qstate, id); } /* Store the response in cache. */ - if ( (!iq || !iq->started_no_cache_store) && - qstate->return_msg && qstate->return_msg->rep && - !dns_cache_store(qstate->env, &qstate->qinfo, qstate->return_msg->rep, - 0, 0, 0, NULL, qstate->query_flags, qstate->qstarttime)) + if( (!iq || !iq->started_no_cache_store) && + qstate->return_msg && + qstate->return_msg->rep && + !dns_cache_store( + qstate->env, &qstate->qinfo, qstate->return_msg->rep, + 0, 0, 0, NULL, + qstate->query_flags, qstate->qstarttime)) log_err("out of memory"); /* do nothing */ @@ -969,10 +975,19 @@ dns64_inform_super(struct module_qstate* qstate, int id, } super_dq->state = DNS64_SUBQUERY_FINISHED; - /* If there is no successful answer, we're done. */ - if (qstate->return_rcode != LDNS_RCODE_NOERROR - || !qstate->return_msg - || !qstate->return_msg->rep) { + /* If there is no successful answer, we're done. + * Guarantee that we have at least a NOERROR reply further on. */ + if(qstate->return_rcode != LDNS_RCODE_NOERROR + || !qstate->return_msg + || !qstate->return_msg->rep) { + return; + } + + /* When no A record is found for synthesis fall back to AAAA again. */ + if(qstate->qinfo.qtype == LDNS_RR_TYPE_A && + !reply_find_answer_rrset(&qstate->qinfo, + qstate->return_msg->rep)) { + super_dq->state = DNS64_INTERNAL_QUERY; return; } diff --git a/dnstap/dtstream.c b/dnstap/dtstream.c index 9153f0404..69c951276 100644 --- a/dnstap/dtstream.c +++ b/dnstap/dtstream.c @@ -788,7 +788,7 @@ static int dtio_write_ssl(struct dt_io_thread* dtio, uint8_t* buf, } return -1; } - log_crypto_err("dnstap io, could not SSL_write"); + log_crypto_err_io("dnstap io, could not SSL_write", want); return -1; } return r; @@ -1029,7 +1029,7 @@ static int ssl_read_bytes(struct dt_io_thread* dtio, void* buf, size_t len) "other side"); return 0; } - log_crypto_err("could not SSL_read"); + log_crypto_err_io("could not SSL_read", want); verbose(VERB_DETAIL, "dnstap io: output closed by the " "other side"); return 0; @@ -1431,8 +1431,8 @@ static int dtio_ssl_handshake(struct dt_io_thread* dtio, } else { unsigned long err = ERR_get_error(); if(!squelch_err_ssl_handshake(err)) { - log_crypto_err_code("dnstap io, ssl handshake failed", - err); + log_crypto_err_io_code("dnstap io, ssl handshake failed", + want, err); verbose(VERB_OPS, "dnstap io, ssl handshake failed " "from %s", dtio->ip_str); } diff --git a/dnstap/unbound-dnstap-socket.c b/dnstap/unbound-dnstap-socket.c index d172a6744..04fda74b8 100644 --- a/dnstap/unbound-dnstap-socket.c +++ b/dnstap/unbound-dnstap-socket.c @@ -708,7 +708,7 @@ static ssize_t ssl_read_bytes(struct tap_data* data, void* buf, size_t len) (data->id?data->id:"")); return 0; } - log_crypto_err("could not SSL_read"); + log_crypto_err_io("could not SSL_read", want); if(verbosity) log_info("dnstap client stream closed from %s", (data->id?data->id:"")); return 0; @@ -760,10 +760,11 @@ static int reply_with_accept(struct tap_data* data) fd_set_block(data->fd); if(data->ssl) { if((r=SSL_write(data->ssl, acceptframe, len)) <= 0) { - if(SSL_get_error(data->ssl, r) == SSL_ERROR_ZERO_RETURN) + int r2; + if((r2=SSL_get_error(data->ssl, r)) == SSL_ERROR_ZERO_RETURN) log_err("SSL_write, peer closed connection"); else - log_err("could not SSL_write"); + log_crypto_err_io("could not SSL_write", r2); fd_set_nonblock(data->fd); free(acceptframe); return 0; @@ -805,10 +806,11 @@ static int reply_with_finish(struct tap_data* data) if(data->ssl) { int r; if((r=SSL_write(data->ssl, finishframe, len)) <= 0) { - if(SSL_get_error(data->ssl, r) == SSL_ERROR_ZERO_RETURN) + int r2; + if((r2=SSL_get_error(data->ssl, r)) == SSL_ERROR_ZERO_RETURN) log_err("SSL_write, peer closed connection"); else - log_err("could not SSL_write"); + log_crypto_err_io("could not SSL_write", r2); fd_set_nonblock(data->fd); free(finishframe); return 0; diff --git a/doc/Changelog b/doc/Changelog index ecc2b188a..9ec9fa513 100644 --- a/doc/Changelog +++ b/doc/Changelog @@ -1,3 +1,111 @@ +20 October 2023: Wouter + - Merge #951: Cachedb no store. The cachedb-no-store: yes option is + used to stop cachedb from writing messages to the backend storage. + It reads messages when data is available from the backend. The + default is no. + +19 October 2023: Wouter + - Fix to print detailed errors when an SSL IO routine fails via + SSL_get_error. + +18 October 2023: George + - Mailing list patches from Daniel Gröber for DNS64 fallback to plain + AAAA when no A record exists for synthesis, and minor DNS64 code + refactoring for better readability. + - Fixes for the DNS64 patches. + - Update the dns64_lookup.rpl test for the DNS64 fallback patch. + - Merge #955 from buevsan: fix ipset wrong behavior. + - Update testdata/ipset.tdir test for ipset fix. + +17 October 2023: Wouter + - Fix #954: Inconsistent RPZ handling for A record returned along with + CNAME. + +16 October 2023: George + - Expose the script filename in the Python module environment 'mod_env' + instead of the config_file structure which includes the linked list + of scripts in a multi Python module setup; fixes #79. + - Expose the configured listening and outgoing interfaces, if any, as + a list of strings in the Python 'config_file' class instead of the + current Swig object proxy; fixes #79. + - For multi Python module setups, clean previously parsed module + functions in __main__'s dictionary, if any, so that only current + module functions are registered. + +13 October 2023: George + - Better fix for infinite loop when reading multiple lines of input on + a broken remote control socket, by treating a zero byte line the + same as transmission end. Addesses #947 and #948. + +12 October 2023: Wouter + - Merge #944: Disable EDNS DO. + Disable the EDNS DO flag in upstream requests. This can be helpful + for devices that cannot handle DNSSEC information. But it should not + be enabled otherwise, because that would stop DNSSEC validation. The + DNSSEC validation would not work for Unbound itself, and also not + for downstream users. Default is no. The option + is disable-edns-do: no + +11 October 2023: George + - Fix #850: [FR] Ability to use specific database in Redis, with new + redis-logical-db configuration option. + +11 October 2023: Wouter + - Fix #949: "could not create control compt". + - Fix that cachedb does not warn when serve-expired is disabled about + use of serve-expired-reply-ttl and serve-expired-client-timeout. + - Fix for #949: Fix pythonmod/ubmodule-tst.py for Python 3.x. + +10 October 2023: George + - Fix infinite loop when reading multiple lines of input on a broken + remote control socket. Addesses #947 and #948. + +9 October 2023: Wouter + - Fix edns subnet so that queries with a source prefix of zero cause + the recursor send no edns subnet option to the upstream. + - Fix that printout of EDNS options shows the EDNS cookie option by + name. + +4 October 2023: Wouter + - Fix #946: Forwarder returns servfail on upstream response noerror no + data. + +3 October 2023: George + - Merge #881: Generalise the proxy protocol code. + +2 October 2023: George + - Fix misplaced comment. + +22 September 2023: Wouter + - Fix #942: 1.18.0 libunbound DNS regression when built without + OpenSSL. + +18 September 2023: Wouter + - Fix rpz tcp-only action with rpz triggers nsdname and nsip. + +15 September 2023: Wouter + - Merge #936: Check for c99 with autoconf versions prior to 2.70. + - Fix to remove two c99 notations. + +14 September 2023: Wouter + - Fix authority zone answers for obscured DNAMEs and delegations. + +8 September 2023: Wouter + - Fix send of udp retries when ENOBUFS is returned. It stops looping + and also waits for the condition to go away. Reported by Florian + Obser. + +7 September 2023: Wouter + - Fix to scrub resource records of type A and AAAA that have an + inappropriate size. They are removed from responses. + - Fix to move msgparse_rrset_remove_rr code to util/msgparse.c. + - Fix to add EDE text when RRs have been removed due to length. + - Fix to set ede match in unit test for rr length removal. + - Fix to print EDE text in readable form in output logs. + +6 September 2023: Wouter + - Merge #931: Prevent warnings from -Wmissing-prototypes. + 31 August 2023: Wouter - Fix autoconf 2.69 warnings in configure. - Fix #927: unbound 1.18.0 make test error. Fix make test without SHA1. diff --git a/doc/example.conf.in b/doc/example.conf.in index 0980212e1..6bf1c668e 100644 --- a/doc/example.conf.in +++ b/doc/example.conf.in @@ -683,6 +683,11 @@ server: # that set CD but cannot validate themselves. # ignore-cd-flag: no + # Disable the DO flag in outgoing requests. It is helpful for upstream + # devices that cannot handle DNSSEC information. But do not enable it + # otherwise, because it would stop DNSSEC validation. + # disable-edns-do: no + # Serve expired responses from cache, with serve-expired-reply-ttl in # the response, and then attempt to fetch the data afresh. # serve-expired: no @@ -1221,6 +1226,8 @@ remote-control: # backend: "testframe" # # secret seed string to calculate hashed keys # secret-seed: "default" +# # if the backend should be read from, but not written to. +# cachedb-no-store: no # # # For "redis" backend: # # (to enable, use --with-libhiredis to configure before compiling) @@ -1236,6 +1243,8 @@ remote-control: # redis-timeout: 100 # # set timeout on redis records based on DNS response TTL # redis-expire-records: no +# # redis logical database to use, 0 is the default database. +# redis-logical-db: 0 # IPSet # Add specify domain into set via ipset. diff --git a/doc/unbound.conf.5.in b/doc/unbound.conf.5.in index 84b903f49..76cfa2383 100644 --- a/doc/unbound.conf.5.in +++ b/doc/unbound.conf.5.in @@ -1302,6 +1302,20 @@ servers that set the CD flag but cannot validate DNSSEC themselves are the clients, and then Unbound provides them with DNSSEC protection. The default value is "no". .TP +.B disable\-edns\-do: \fI +Disable the EDNS DO flag in upstream requests. +It breaks DNSSEC validation for Unbound's clients. +This results in the upstream name servers to not include DNSSEC records in +their replies and could be helpful for devices that cannot handle DNSSEC +information. +When the option is enabled, clients that set the DO flag receive no EDNS +record in the response to indicate the lack of support to them. +If this option is enabled but Unbound is already configured for DNSSEC +validation (i.e., the validator module is enabled; default) this option is +implicitly turned off with a warning as to not break DNSSEC validation in +Unbound. +Default is no. +.TP .B serve\-expired: \fI If enabled, Unbound attempts to serve old responses from cache with a TTL of \fBserve\-expired\-reply\-ttl\fR in the response without waiting for the @@ -2667,6 +2681,11 @@ operationally. If the backend database is shared by multiple Unbound instances, all instances must use the same secret seed. This option defaults to "default". +.TP +.B cachedb-no-store: \fI\fR +If the backend should be read from, but not written to. This makes this +instance not store dns messages in the backend. But if data is available it +is retrieved. The default is no. .P The following .B cachedb @@ -2707,6 +2726,17 @@ Unbound is configured with \fBserve-expired\fR and \fBserve-expired-ttl\fR is 0, this option is internally reverted to "no". Redis SETEX support is required for this option (Redis >= 2.0.0). This option defaults to no. +.TP +.B redis-logical-db: \fI +The logical database in Redis to use. +These are databases in the same Redis instance sharing the same configuration +and persisted in the same RDB/AOF file. +If unsure about using this option, Redis documentation +(https://redis.io/commands/select/) suggests not to use a single Redis instance +for multiple unrelated applications. +The default database in Redis is 0 while other logical databases need to be +explicitly SELECT'ed upon connecting. +This option defaults to 0. .SS DNSTAP Logging Options DNSTAP support, when compiled in by using \fB\-\-enable\-dnstap\fR, is enabled in the \fBdnstap:\fR section. diff --git a/dynlibmod/dynlibmod.c b/dynlibmod/dynlibmod.c index ffac7ff30..1e040a30e 100644 --- a/dynlibmod/dynlibmod.c +++ b/dynlibmod/dynlibmod.c @@ -75,6 +75,7 @@ int dynlibmod_init(struct module_env* env, int id) { struct config_strlist* cfg_item = env->cfg->dynlib_file; struct dynlibmod_env* de = (struct dynlibmod_env*)calloc(1, sizeof(struct dynlibmod_env)); __DYNMOD dynamic_library; + int i; if (!de) { log_err("dynlibmod[%d]: malloc failure", dynlib_mod_idx); @@ -84,7 +85,7 @@ int dynlibmod_init(struct module_env* env, int id) { env->modinfo[id] = (void*) de; de->fname = NULL; - for(int i = dynlib_mod_idx; + for(i = dynlib_mod_idx; i != 0 && cfg_item != NULL; i--, cfg_item = cfg_item->next) {} diff --git a/edns-subnet/subnetmod.c b/edns-subnet/subnetmod.c index 13fd669b5..cefde84e5 100644 --- a/edns-subnet/subnetmod.c +++ b/edns-subnet/subnetmod.c @@ -156,6 +156,7 @@ int ecs_whitelist_check(struct query_info* qinfo, qstate->no_cache_store = 0; } + sq->subnet_sent_no_subnet = 0; if(sq->ecs_server_out.subnet_validdata && ((sq->subnet_downstream && qstate->env->cfg->client_subnet_always_forward) || ecs_is_whitelisted(sn_env->whitelist, @@ -166,6 +167,14 @@ int ecs_whitelist_check(struct query_info* qinfo, * set. */ if(!edns_opt_list_find(qstate->edns_opts_back_out, qstate->env->cfg->client_subnet_opcode)) { + /* if the client is not wanting an EDNS subnet option, + * omit it and store that we omitted it but actually + * are doing EDNS subnet to the server. */ + if(sq->ecs_server_out.subnet_source_mask == 0) { + sq->subnet_sent_no_subnet = 1; + sq->subnet_sent = 0; + return 1; + } subnet_ecs_opt_list_append(&sq->ecs_server_out, &qstate->edns_opts_back_out, qstate, region); } @@ -515,7 +524,7 @@ eval_response(struct module_qstate *qstate, int id, struct subnet_qstate *sq) } /* We have not asked for subnet data */ - if (!sq->subnet_sent) { + if (!sq->subnet_sent && !sq->subnet_sent_no_subnet) { if (s_in->subnet_validdata) verbose(VERB_QUERY, "subnetcache: received spurious data"); if (sq->subnet_downstream) /* Copy back to client */ @@ -524,7 +533,7 @@ eval_response(struct module_qstate *qstate, int id, struct subnet_qstate *sq) } /* subnet sent but nothing came back */ - if (!s_in->subnet_validdata) { + if (!s_in->subnet_validdata && !sq->subnet_sent_no_subnet) { /* The authority indicated no support for edns subnet. As a * consequence the answer ended up in the regular cache. It * is still useful to put it in the edns subnet cache for @@ -540,6 +549,18 @@ eval_response(struct module_qstate *qstate, int id, struct subnet_qstate *sq) return module_finished; } + /* Purposefully there was no sent subnet, and there is consequently + * no subnet in the answer. If there was, use the subnet in the answer + * anyway. But if there is not, treat it as a prefix 0 answer. */ + if(sq->subnet_sent_no_subnet && !s_in->subnet_validdata) { + /* Fill in 0.0.0.0/0 scope 0, or ::0/0 scope 0, for caching. */ + s_in->subnet_addr_fam = s_out->subnet_addr_fam; + s_in->subnet_source_mask = 0; + s_in->subnet_scope_mask = 0; + memset(s_in->subnet_addr, 0, INET6_SIZE); + s_in->subnet_validdata = 1; + } + /* Being here means we have asked for and got a subnet specific * answer. Also, the answer from the authority is not yet cached * anywhere. */ @@ -556,6 +577,7 @@ eval_response(struct module_qstate *qstate, int id, struct subnet_qstate *sq) (void)edns_opt_list_remove(&qstate->edns_opts_back_out, qstate->env->cfg->client_subnet_opcode); sq->subnet_sent = 0; + sq->subnet_sent_no_subnet = 0; return module_restart_next; } @@ -676,6 +698,7 @@ ecs_query_response(struct module_qstate* qstate, struct dns_msg* response, edns_opt_list_remove(&qstate->edns_opts_back_out, qstate->env->cfg->client_subnet_opcode); sq->subnet_sent = 0; + sq->subnet_sent_no_subnet = 0; memset(&sq->ecs_server_out, 0, sizeof(sq->ecs_server_out)); } else if (!sq->track_max_scope && FLAGS_GET_RCODE(response->rep->flags) == LDNS_RCODE_NOERROR && @@ -737,6 +760,9 @@ ecs_edns_back_parsed(struct module_qstate* qstate, int id, sq->ecs_server_in.subnet_scope_mask > sq->max_scope)) sq->max_scope = sq->ecs_server_in.subnet_scope_mask; + } else if(sq->subnet_sent_no_subnet) { + /* The answer can be stored as scope 0, not in global cache. */ + qstate->no_cache_store = 1; } return 1; diff --git a/edns-subnet/subnetmod.h b/edns-subnet/subnetmod.h index f0bcaad33..1ff8a23ec 100644 --- a/edns-subnet/subnetmod.h +++ b/edns-subnet/subnetmod.h @@ -85,6 +85,13 @@ struct subnet_qstate { struct ecs_data ecs_server_out; int subnet_downstream; int subnet_sent; + /** + * If there was no subnet sent because the client used source prefix + * length 0 for omitting the information. Then the answer is cached + * like subnet was a /0 scope. Like the subnet_sent flag, but when + * the EDNS subnet option is omitted because the client asked. + */ + int subnet_sent_no_subnet; /** keep track of longest received scope, set after receiving CNAME for * incoming QNAME. */ int track_max_scope; diff --git a/ipset/ipset.c b/ipset/ipset.c index c61ebc205..af55de8d6 100644 --- a/ipset/ipset.c +++ b/ipset/ipset.c @@ -158,10 +158,10 @@ ipset_check_zones_for_rrset(struct module_env *env, struct ipset_env *ie, qs = NULL; plen = strlen(p->str); - if (dlen >= plen) { + if (dlen == plen || (dlen > plen && dname[dlen - plen - 1] == '.' )) { ds = dname + (dlen - plen); } - if (qlen >= plen) { + if (qlen == plen || (qlen > plen && qname[qlen - plen - 1] == '.' )) { qs = qname + (qlen - plen); } if ((ds && strncasecmp(p->str, ds, plen) == 0) diff --git a/iterator/iter_priv.c b/iterator/iter_priv.c index 90bea1746..be4219216 100644 --- a/iterator/iter_priv.c +++ b/iterator/iter_priv.c @@ -207,28 +207,6 @@ size_t priv_get_mem(struct iter_priv* priv) return sizeof(*priv) + regional_get_mem(priv->region); } -/** remove RR from msgparse RRset, return true if rrset is entirely bad */ -static int -remove_rr(const char* str, sldns_buffer* pkt, struct rrset_parse* rrset, - struct rr_parse* prev, struct rr_parse** rr, struct sockaddr_storage* addr, socklen_t addrlen) -{ - if(verbosity >= VERB_QUERY && rrset->dname_len <= LDNS_MAX_DOMAINLEN && str) { - uint8_t buf[LDNS_MAX_DOMAINLEN+1]; - dname_pkt_copy(pkt, buf, rrset->dname); - log_name_addr(VERB_QUERY, str, buf, addr, addrlen); - } - if(prev) - prev->next = (*rr)->next; - else rrset->rr_first = (*rr)->next; - if(rrset->rr_last == *rr) - rrset->rr_last = prev; - rrset->rr_count --; - rrset->size -= (*rr)->size; - /* rr struct still exists, but is unlinked, so that in the for loop - * the rr->next works fine to continue. */ - return rrset->rr_count == 0; -} - int priv_rrset_bad(struct iter_priv* priv, sldns_buffer* pkt, struct rrset_parse* rrset) { @@ -261,7 +239,7 @@ int priv_rrset_bad(struct iter_priv* priv, sldns_buffer* pkt, INET_SIZE); memmove(&addr, &sa, len); if(priv_lookup_addr(priv, &addr, len)) { - if(remove_rr("sanitize: removing public name with private address", pkt, rrset, prev, &rr, &addr, len)) + if(msgparse_rrset_remove_rr("sanitize: removing public name with private address", pkt, rrset, prev, rr, &addr, len)) return 1; continue; } @@ -284,7 +262,7 @@ int priv_rrset_bad(struct iter_priv* priv, sldns_buffer* pkt, INET6_SIZE); memmove(&addr, &sa, len); if(priv_lookup_addr(priv, &addr, len)) { - if(remove_rr("sanitize: removing public name with private address", pkt, rrset, prev, &rr, &addr, len)) + if(msgparse_rrset_remove_rr("sanitize: removing public name with private address", pkt, rrset, prev, rr, &addr, len)) return 1; continue; } diff --git a/iterator/iter_resptype.c b/iterator/iter_resptype.c index e85595b84..38e186e79 100644 --- a/iterator/iter_resptype.c +++ b/iterator/iter_resptype.c @@ -42,6 +42,7 @@ #include "config.h" #include "iterator/iter_resptype.h" #include "iterator/iter_delegpt.h" +#include "iterator/iterator.h" #include "services/cache/dns.h" #include "util/net_help.h" #include "util/data/dname.h" @@ -105,7 +106,8 @@ response_type_from_cache(struct dns_msg* msg, enum response_type response_type_from_server(int rdset, - struct dns_msg* msg, struct query_info* request, struct delegpt* dp) + struct dns_msg* msg, struct query_info* request, struct delegpt* dp, + int* empty_nodata_found) { uint8_t* origzone = (uint8_t*)"\000"; /* the default */ struct ub_packed_rrset_key* s; @@ -284,13 +286,22 @@ response_type_from_server(int rdset, /* If we've gotten this far, this is NOERROR/NODATA (which could * be an entirely empty message) */ - /* but ignore entirely empty messages, noerror/nodata has a soa - * negative ttl value in the authority section, this makes it try - * again at another authority. And turns it from a 5 second empty - * message into a 5 second servfail response. */ + /* For entirely empty messages, try again, at first, then accept + * it it happens more. A regular noerror/nodata response has a soa + * negative ttl value in the authority section. This makes it try + * again at another authority. And decides between storing a 5 second + * empty message or a 5 second servfail response. */ if(msg->rep->an_numrrsets == 0 && msg->rep->ns_numrrsets == 0 && - msg->rep->ar_numrrsets == 0) - return RESPONSE_TYPE_THROWAWAY; + msg->rep->ar_numrrsets == 0) { + if(empty_nodata_found) { + /* detect as throwaway at first, but accept later. */ + (*empty_nodata_found)++; + if(*empty_nodata_found < EMPTY_NODATA_RETRY_COUNT) + return RESPONSE_TYPE_THROWAWAY; + return RESPONSE_TYPE_ANSWER; + } + return RESPONSE_TYPE_ANSWER; + } /* check if recursive answer; saying it has empty cache */ if( (msg->rep->flags&BIT_RA) && !(msg->rep->flags&BIT_AA) && !rdset) return RESPONSE_TYPE_REC_LAME; diff --git a/iterator/iter_resptype.h b/iterator/iter_resptype.h index fee9ef35f..bfd4b664f 100644 --- a/iterator/iter_resptype.h +++ b/iterator/iter_resptype.h @@ -119,9 +119,11 @@ enum response_type response_type_from_cache(struct dns_msg* msg, * @param request: the request that generated the response. * @param dp: The delegation point that was being queried * when the response was returned. + * @param empty_nodata_found: flag to keep track of empty nodata detection. * @return the response type (CNAME or ANSWER). */ enum response_type response_type_from_server(int rdset, - struct dns_msg* msg, struct query_info* request, struct delegpt* dp); + struct dns_msg* msg, struct query_info* request, struct delegpt* dp, + int* empty_nodata_found); #endif /* ITERATOR_ITER_RESPTYPE_H */ diff --git a/iterator/iter_scrub.c b/iterator/iter_scrub.c index d1fedcd0f..5f2e30337 100644 --- a/iterator/iter_scrub.c +++ b/iterator/iter_scrub.c @@ -716,6 +716,56 @@ static int sanitize_nsec_is_overreach(sldns_buffer* pkt, return 0; } +/** Remove individual RRs, if the length is wrong. Returns true if the RRset + * has been removed. */ +static int +scrub_sanitize_rr_length(sldns_buffer* pkt, struct msg_parse* msg, + struct rrset_parse* prev, struct rrset_parse** rrset, int* added_ede, + struct module_qstate* qstate) +{ + struct rr_parse* rr, *rr_prev = NULL; + for(rr = (*rrset)->rr_first; rr; rr = rr->next) { + + /* Sanity check for length of records + * An A record should be 6 bytes only + * (2 bytes for length and 4 for IPv4 addr)*/ + if((*rrset)->type == LDNS_RR_TYPE_A && rr->size != 6 ) { + if(!*added_ede) { + *added_ede = 1; + errinf_ede(qstate, "sanitize: records of inappropriate length have been removed.", + LDNS_EDE_OTHER); + } + if(msgparse_rrset_remove_rr("sanitize: removing type A RR of inappropriate length:", + pkt, *rrset, rr_prev, rr, NULL, 0)) { + remove_rrset("sanitize: removing type A RRset of inappropriate length:", + pkt, msg, prev, rrset); + return 1; + } + continue; + } + + /* Sanity check for length of records + * An AAAA record should be 18 bytes only + * (2 bytes for length and 16 for IPv6 addr)*/ + if((*rrset)->type == LDNS_RR_TYPE_AAAA && rr->size != 18 ) { + if(!*added_ede) { + *added_ede = 1; + errinf_ede(qstate, "sanitize: records of inappropriate length have been removed.", + LDNS_EDE_OTHER); + } + if(msgparse_rrset_remove_rr("sanitize: removing type AAAA RR of inappropriate length:", + pkt, *rrset, rr_prev, rr, NULL, 0)) { + remove_rrset("sanitize: removing type AAAA RRset of inappropriate length:", + pkt, msg, prev, rrset); + return 1; + } + continue; + } + rr_prev = rr; + } + return 0; +} + /** * Given a response event, remove suspect RRsets from the response. * "Suspect" rrsets are potentially poison. Note that this routine expects @@ -728,15 +778,17 @@ static int sanitize_nsec_is_overreach(sldns_buffer* pkt, * @param zonename: name of server zone. * @param env: module environment with config and cache. * @param ie: iterator environment with private address data. + * @param qstate: for setting errinf for EDE error messages. * @return 0 on error. */ static int scrub_sanitize(sldns_buffer* pkt, struct msg_parse* msg, struct query_info* qinfo, uint8_t* zonename, struct module_env* env, - struct iter_env* ie) + struct iter_env* ie, struct module_qstate* qstate) { int del_addi = 0; /* if additional-holding rrsets are deleted, we do not trust the normalized additional-A-AAAA any more */ + int added_rrlen_ede = 0; struct rrset_parse* rrset, *prev; prev = NULL; rrset = msg->rrset_first; @@ -781,6 +833,14 @@ scrub_sanitize(sldns_buffer* pkt, struct msg_parse* msg, rrset = msg->rrset_first; while(rrset) { + /* Sanity check for length of records */ + if(rrset->type == LDNS_RR_TYPE_A || + rrset->type == LDNS_RR_TYPE_AAAA) { + if(scrub_sanitize_rr_length(pkt, msg, prev, &rrset, + &added_rrlen_ede, qstate)) + continue; + } + /* remove private addresses */ if( (rrset->type == LDNS_RR_TYPE_A || rrset->type == LDNS_RR_TYPE_AAAA)) { @@ -854,7 +914,8 @@ scrub_sanitize(sldns_buffer* pkt, struct msg_parse* msg, int scrub_message(sldns_buffer* pkt, struct msg_parse* msg, struct query_info* qinfo, uint8_t* zonename, struct regional* region, - struct module_env* env, struct iter_env* ie) + struct module_env* env, struct module_qstate* qstate, + struct iter_env* ie) { /* basic sanity checks */ log_nametypeclass(VERB_ALGO, "scrub for", zonename, LDNS_RR_TYPE_NS, @@ -886,7 +947,7 @@ scrub_message(sldns_buffer* pkt, struct msg_parse* msg, if(!scrub_normalize(pkt, msg, qinfo, region, env)) return 0; /* delete all out-of-zone information */ - if(!scrub_sanitize(pkt, msg, qinfo, zonename, env, ie)) + if(!scrub_sanitize(pkt, msg, qinfo, zonename, env, ie, qstate)) return 0; return 1; } diff --git a/iterator/iter_scrub.h b/iterator/iter_scrub.h index cbbaf73c9..4d6ce7166 100644 --- a/iterator/iter_scrub.h +++ b/iterator/iter_scrub.h @@ -48,6 +48,7 @@ struct query_info; struct regional; struct module_env; struct iter_env; +struct module_qstate; /** * Cleanup the passed dns message. @@ -59,11 +60,13 @@ struct iter_env; * Used to determine out of bailiwick information. * @param regional: where to allocate (new) parts of the message. * @param env: module environment with config settings and cache. + * @param qstate: for setting errinf for EDE error messages. * @param ie: iterator module environment data. * @return: false if the message is total waste. true if scrubbed with success. */ int scrub_message(struct sldns_buffer* pkt, struct msg_parse* msg, struct query_info* qinfo, uint8_t* zonename, struct regional* regional, - struct module_env* env, struct iter_env* ie); + struct module_env* env, struct module_qstate* qstate, + struct iter_env* ie); #endif /* ITERATOR_ITER_SCRUB_H */ diff --git a/iterator/iterator.c b/iterator/iterator.c index 1548dfcae..6ff811a27 100644 --- a/iterator/iterator.c +++ b/iterator/iterator.c @@ -1449,6 +1449,39 @@ processInitRequest(struct module_qstate* qstate, struct iter_qstate* iq, } iq->qchase.qname = sname; iq->qchase.qname_len = slen; + if(qstate->env->auth_zones) { + /* apply rpz qname triggers after cname */ + struct dns_msg* forged_response = + rpz_callback_from_iterator_cname(qstate, iq); + while(forged_response && reply_find_rrset_section_an( + forged_response->rep, iq->qchase.qname, + iq->qchase.qname_len, LDNS_RR_TYPE_CNAME, + iq->qchase.qclass)) { + /* another cname to follow */ + if(!handle_cname_response(qstate, iq, forged_response, + &sname, &slen)) { + errinf(qstate, "malloc failure, CNAME info"); + return error_response(qstate, id, LDNS_RCODE_SERVFAIL); + } + iq->qchase.qname = sname; + iq->qchase.qname_len = slen; + forged_response = + rpz_callback_from_iterator_cname(qstate, iq); + } + if(forged_response != NULL) { + qstate->ext_state[id] = module_finished; + qstate->return_rcode = LDNS_RCODE_NOERROR; + qstate->return_msg = forged_response; + iq->response = forged_response; + next_state(iq, FINISHED_STATE); + if(!iter_prepend(iq, qstate->return_msg, qstate->region)) { + log_err("rpz: after cached cname, prepend rrsets: out of memory"); + return error_response(qstate, id, LDNS_RCODE_SERVFAIL); + } + qstate->return_msg->qinfo = qstate->qinfo; + return 0; + } + } /* This *is* a query restart, even if it is a cheap * one. */ iq->dp = NULL; @@ -2875,7 +2908,8 @@ processQueryTargets(struct module_qstate* qstate, struct iter_qstate* iq, /* unset CD if to forwarder(RD set) and not dnssec retry * (blacklist nonempty) and no trust-anchors are configured * above the qname or on the first attempt when dnssec is on */ - EDNS_DO| ((iq->chase_to_rd||(iq->chase_flags&BIT_RD)!=0)&& + (qstate->env->cfg->disable_edns_do?0:EDNS_DO)| + ((iq->chase_to_rd||(iq->chase_flags&BIT_RD)!=0)&& !qstate->blacklist&&(!iter_qname_indicates_dnssec(qstate->env, &iq->qinfo_out)||target->attempts==1)?0:BIT_CD), iq->dnssec_expected, iq->caps_fallback || is_caps_whitelisted( @@ -2940,7 +2974,7 @@ static int processQueryResponse(struct module_qstate* qstate, struct iter_qstate* iq, struct iter_env* ie, int id) { - int dnsseclame = 0, origtypecname = 0; + int dnsseclame = 0, origtypecname = 0, orig_empty_nodata_found; enum response_type type; iq->num_current_queries--; @@ -2960,12 +2994,25 @@ processQueryResponse(struct module_qstate* qstate, struct iter_qstate* iq, return next_state(iq, QUERYTARGETS_STATE); } iq->timeout_count = 0; + orig_empty_nodata_found = iq->empty_nodata_found; type = response_type_from_server( (int)((iq->chase_flags&BIT_RD) || iq->chase_to_rd), - iq->response, &iq->qinfo_out, iq->dp); + iq->response, &iq->qinfo_out, iq->dp, &iq->empty_nodata_found); iq->chase_to_rd = 0; /* remove TC flag, if this is erroneously set by TCP upstream */ iq->response->rep->flags &= ~BIT_TC; + if(orig_empty_nodata_found != iq->empty_nodata_found && + iq->empty_nodata_found < EMPTY_NODATA_RETRY_COUNT) { + /* try to search at another server */ + if(qstate->reply) { + struct delegpt_addr* a = delegpt_find_addr( + iq->dp, &qstate->reply->remote_addr, + qstate->reply->remote_addrlen); + /* make selection disprefer it */ + if(a) a->lame = 1; + } + return next_state(iq, QUERYTARGETS_STATE); + } if(type == RESPONSE_TYPE_REFERRAL && (iq->chase_flags&BIT_RD) && !iq->auth_zone_response) { /* When forwarding (RD bit is set), we handle referrals @@ -3501,7 +3548,7 @@ processPrimeResponse(struct module_qstate* qstate, int id) iq->response->rep->flags &= ~(BIT_RD|BIT_RA); /* ignore rec-lame */ type = response_type_from_server( (int)((iq->chase_flags&BIT_RD) || iq->chase_to_rd), - iq->response, &iq->qchase, iq->dp); + iq->response, &iq->qchase, iq->dp, NULL); if(type == RESPONSE_TYPE_ANSWER) { qstate->return_rcode = LDNS_RCODE_NOERROR; qstate->return_msg = iq->response; @@ -3874,6 +3921,23 @@ processFinished(struct module_qstate* qstate, struct iter_qstate* iq, /* explicitly set the EDE string to NULL */ iq->response->rep->reason_bogus_str = NULL; + if((qstate->env->cfg->val_log_level >= 2 || + qstate->env->cfg->log_servfail) && qstate->errinf && + !qstate->env->cfg->val_log_squelch) { + char* err_str = errinf_to_str_misc(qstate); + if(err_str) { + size_t err_str_len = strlen(err_str); + verbose(VERB_ALGO, "iterator EDE: %s", err_str); + /* allocate space and store the error + * string */ + iq->response->rep->reason_bogus_str = regional_alloc( + qstate->region, + sizeof(char) * (err_str_len+1)); + memcpy(iq->response->rep->reason_bogus_str, + err_str, err_str_len+1); + } + free(err_str); + } /* we have finished processing this query */ qstate->ext_state[id] = module_finished; @@ -4098,7 +4162,7 @@ process_response(struct module_qstate* qstate, struct iter_qstate* iq, /* normalize and sanitize: easy to delete items from linked lists */ if(!scrub_message(pkt, prs, &iq->qinfo_out, iq->dp->name, - qstate->env->scratch, qstate->env, ie)) { + qstate->env->scratch, qstate->env, qstate, ie)) { /* if 0x20 enabled, start fallback, but we have no message */ if(event == module_event_capsfail && !iq->caps_fallback) { iq->caps_fallback = 1; diff --git a/iterator/iterator.h b/iterator/iterator.h index fad7f03e6..e253f3f7e 100644 --- a/iterator/iterator.h +++ b/iterator/iterator.h @@ -101,6 +101,8 @@ extern int BLACKLIST_PENALTY; * Chosen so that the UNKNOWN_SERVER_NICENESS falls within the band of a * fast server, this causes server exploration as a side benefit. msec. */ #define RTT_BAND 400 +/** Number of retries for empty nodata packets before it is accepted. */ +#define EMPTY_NODATA_RETRY_COUNT 2 /** * Global state for the iterator. @@ -415,6 +417,11 @@ struct iter_qstate { */ int refetch_glue; + /** + * This flag detects that a completely empty nodata was received, + * already so that it is accepted later. */ + int empty_nodata_found; + /** list of pending queries to authoritative servers. */ struct outbound_list outlist; diff --git a/libunbound/libworker.c b/libunbound/libworker.c index 104244937..0e1c40393 100644 --- a/libunbound/libworker.c +++ b/libunbound/libworker.c @@ -62,6 +62,7 @@ #include "util/random.h" #include "util/config_file.h" #include "util/netevent.h" +#include "util/proxy_protocol.h" #include "util/storage/lookup3.h" #include "util/storage/slabhash.h" #include "util/net_help.h" @@ -168,6 +169,7 @@ libworker_setup(struct ub_ctx* ctx, int is_bg, struct ub_event_base* eb) hints_delete(w->env->hints); w->env->hints = NULL; } +#ifdef HAVE_SSL w->sslctx = connect_sslctx_create(NULL, NULL, cfg->tls_cert_bundle, cfg->tls_win_cert); if(!w->sslctx) { @@ -175,6 +177,7 @@ libworker_setup(struct ub_ctx* ctx, int is_bg, struct ub_event_base* eb) hints_delete(w->env->hints); w->env->hints = NULL; } +#endif if(!w->is_bg || w->is_bg_thread) { lock_basic_unlock(&ctx->cfglock); } @@ -263,6 +266,7 @@ libworker_setup(struct ub_ctx* ctx, int is_bg, struct ub_event_base* eb) w->env->kill_sub = &mesh_state_delete; w->env->detect_cycle = &mesh_detect_cycle; comm_base_timept(w->base, &w->env->now, &w->env->now_tv); + pp_init(&sldns_write_uint16, &sldns_write_uint32); return w; } diff --git a/libunbound/python/libunbound.i b/libunbound/python/libunbound.i index 0cdb3d7e5..dc125146c 100644 --- a/libunbound/python/libunbound.i +++ b/libunbound/python/libunbound.i @@ -863,6 +863,9 @@ Result: ['74.125.43.147', '74.125.43.99', '74.125.43.103', '74.125.43.104'] %inline %{ //SWIG will see the ub_ctx as a class struct ub_ctx { + /* Dummy member, so the struct is not empty, MSVC complains about + * that. */ + int dummy; }; %} diff --git a/pythonmod/doc/examples/example0-1.py b/pythonmod/doc/examples/example0-1.py index 7904f73a5..506235eb4 100644 --- a/pythonmod/doc/examples/example0-1.py +++ b/pythonmod/doc/examples/example0-1.py @@ -1,9 +1,9 @@ def init(id, cfg): - log_info("pythonmod: init called, module id is %d port: %d script: %s" % (id, cfg.port, cfg.python_script)) + log_info("pythonmod: init called, module id is %d port: %d script: %s" % (id, cfg.port, mod_env['script'])) return True def init_standard(id, env): - log_info("pythonmod: init called, module id is %d port: %d script: %s" % (id, env.cfg.port, env.cfg.python_script)) + log_info("pythonmod: init called, module id is %d port: %d script: %s" % (id, env.cfg.port, mod_env['script'])) return True def deinit(id): diff --git a/pythonmod/doc/examples/example0.rst b/pythonmod/doc/examples/example0.rst index 693972a14..cee551de0 100644 --- a/pythonmod/doc/examples/example0.rst +++ b/pythonmod/doc/examples/example0.rst @@ -50,7 +50,7 @@ Script file must contain four compulsory functions: :: def init(id, cfg): - log_info("pythonmod: init called, module id is %d port: %d script: %s" % (id, cfg.port, cfg.python_script)) + log_info("pythonmod: init called, module id is %d port: %d script: %s" % (id, cfg.port, mod_env['script'])) return True @@ -69,7 +69,7 @@ Script file must contain four compulsory functions: :: def init_standard(id, env): - log_info("pythonmod: init called, module id is %d port: %d script: %s" % (id, env.cfg.port, env.cfg.python_script)) + log_info("pythonmod: init called, module id is %d port: %d script: %s" % (id, env.cfg.port, mod_env['script'])) return True diff --git a/pythonmod/doc/modules/config.rst b/pythonmod/doc/modules/config.rst index 89afbef8a..ac4db4c94 100644 --- a/pythonmod/doc/modules/config.rst +++ b/pythonmod/doc/modules/config.rst @@ -129,7 +129,7 @@ config_file .. attribute:: ifs - Interface description strings (IP addresses). + List of interface description strings (IP addresses). .. attribute:: num_out_ifs @@ -138,7 +138,7 @@ config_file .. attribute:: out_ifs - Outgoing interface description strings (IP addresses). + List of outgoing interface description strings (IP addresses). .. attribute:: root_hints @@ -339,4 +339,5 @@ config_file .. attribute:: python_script - Python script file. + Linked list of Python script files. + Deprecated; `mod_env['script']` should be used instead. diff --git a/pythonmod/doc/modules/env.rst b/pythonmod/doc/modules/env.rst index eae4c73c7..be5c3b1db 100644 --- a/pythonmod/doc/modules/env.rst +++ b/pythonmod/doc/modules/env.rst @@ -6,8 +6,11 @@ Global variables .. envvar:: mod_env - Module environment, contains data pointer for module-specific data. - See :class:`pythonmod_env`. + Module environment, it is the 'data' pointer for module-specific data + in :class:`pythonmod_env`. + It is initialized as a dictionary with the 'script' key pointing to the + module's python script. + It can be further populated during runtime for module-specific data. Predefined constants diff --git a/pythonmod/examples/edns.py b/pythonmod/examples/edns.py index ddcccc51c..4e2eebd4f 100644 --- a/pythonmod/examples/edns.py +++ b/pythonmod/examples/edns.py @@ -80,7 +80,7 @@ def init_standard(id, env): ..note:: The previously accessible configuration options can now be found in env.cfg. """ - log_info("python: inited script {}".format(env.cfg.python_script)) + log_info("python: inited script {}".format(mod_env['script'])) # Register EDNS option 65001 as a known EDNS option. if not register_edns_option(env, 65001, bypass_cache_stage=True, diff --git a/pythonmod/examples/inplace_callbacks.py b/pythonmod/examples/inplace_callbacks.py index e1caaecc7..42806daa1 100644 --- a/pythonmod/examples/inplace_callbacks.py +++ b/pythonmod/examples/inplace_callbacks.py @@ -287,7 +287,7 @@ def init_standard(id, env): env.cfg. """ - log_info("python: inited script {}".format(env.cfg.python_script)) + log_info("python: inited script {}".format(mod_env['script'])) # Register the inplace_reply_callback function as an inplace callback # function when answering a resolved query. diff --git a/pythonmod/examples/log.py b/pythonmod/examples/log.py index c17106b0f..03f741962 100644 --- a/pythonmod/examples/log.py +++ b/pythonmod/examples/log.py @@ -87,7 +87,7 @@ def logDnsMsg(qstate): print "-"*100 def init(id, cfg): - log_info("pythonmod: init called, module id is %d port: %d script: %s" % (id, cfg.port, cfg.python_script)) + log_info("pythonmod: init called, module id is %d port: %d script: %s" % (id, cfg.port, mod_env['script'])) return True def deinit(id): diff --git a/pythonmod/interface.i b/pythonmod/interface.i index a436389e1..d9839fc38 100644 --- a/pythonmod/interface.i +++ b/pythonmod/interface.i @@ -86,6 +86,20 @@ } return list; } + + /* converts an array of strings (char**) to a List of strings */ + PyObject* CharArrayAsStringList(char** array, int len) { + PyObject* list; + int i; + + if(!array||len==0) return PyList_New(0); + + list = PyList_New(len); + for (i=0; i < len; i++) { + PyList_SET_ITEM(list, i, PyString_FromString(array[i])); + } + return list; + } %} /* ************************************************************************************ * @@ -952,6 +966,9 @@ struct config_str2list { /* ************************************************************************************ * Structure config_file * ************************************************************************************ */ +%ignore config_file::ifs; +%ignore config_file::out_ifs; +%ignore config_file::python_script; struct config_file { int verbosity; int stat_interval; @@ -1035,6 +1052,25 @@ struct config_file { struct config_strlist* python_script; }; +%inline %{ + PyObject* _get_ifs_tuple(struct config_file* cfg) { + return CharArrayAsStringList(cfg->ifs, cfg->num_ifs); + } + PyObject* _get_ifs_out_tuple(struct config_file* cfg) { + return CharArrayAsStringList(cfg->out_ifs, cfg->num_out_ifs); + } +%} + +%extend config_file { + %pythoncode %{ + ifs = property(_unboundmodule._get_ifs_tuple) + out_ifs = property(_unboundmodule._get_ifs_out_tuple) + + def _deprecated_python_script(self): return "cfg.python_script is deprecated, you can use `mod_env['script']` instead." + python_script = property(_deprecated_python_script) + %} +} + /* ************************************************************************************ * ASN: Adding structures related to forwards_lookup and dns_cache_find_delegation * ************************************************************************************ */ diff --git a/pythonmod/pythonmod.c b/pythonmod/pythonmod.c index 628308612..c6294a1d5 100644 --- a/pythonmod/pythonmod.c +++ b/pythonmod/pythonmod.c @@ -112,6 +112,34 @@ struct pythonmod_qstate { PyObject* data; }; +/* The dict from __main__ could have remnants from a previous script + * invocation, in a multi python module setup. Usually this is fine since newer + * scripts will update their values. The obvious erroneous case is when mixing + * python scripts that make use of both 'init' and 'init_standard'. This + * results in 'init_standard' to persist on following scripts that don't use it + * (thus not replacing it). This is also problematic in case where a script + * does not define a required function but a previously loaded script did. The + * current solution is to make sure to clean offensive remnants that influence + * further parsing of the individual scripts. + */ +static void +clean_python_function_objects(PyObject* dict) { + const char* function_names[] = { + "init", + "init_standard", + "deinit", + "operate", + "inform_super" + }; + size_t i; + + for(i=0; imodule); pe->dict = PyModule_GetDict(pe->module); Py_XINCREF(pe->dict); + clean_python_function_objects(pe->dict); + pe->data = PyDict_New(); + /* add the script filename to the global "mod_env" for trivial access */ + fname = PyString_FromString(pe->fname); + if(PyDict_SetItemString(pe->data, "script", fname) < 0) { + log_err("pythonmod: could not add item to dictionary"); + Py_XDECREF(fname); + goto python_init_fail; + } + Py_XDECREF(fname); Py_XINCREF(pe->data); /* reference will be stolen below */ if(PyModule_AddObject(pe->module, "mod_env", pe->data) < 0) { log_err("pythonmod: could not add mod_env object"); diff --git a/pythonmod/ubmodule-msg.py b/pythonmod/ubmodule-msg.py index 648368080..6a690e281 100644 --- a/pythonmod/ubmodule-msg.py +++ b/pythonmod/ubmodule-msg.py @@ -35,7 +35,7 @@ import os def init(id, cfg): - log_info("pythonmod: init called, module id is %d port: %d script: %s" % (id, cfg.port, cfg.python_script)) + log_info("pythonmod: init called, module id is %d port: %d script: %s" % (id, cfg.port, mod_env['script'])) return True def deinit(id): diff --git a/pythonmod/ubmodule-tst.py b/pythonmod/ubmodule-tst.py index 0b9b5a9d2..07543e399 100644 --- a/pythonmod/ubmodule-tst.py +++ b/pythonmod/ubmodule-tst.py @@ -33,7 +33,7 @@ POSSIBILITY OF SUCH DAMAGE. ''' def init(id, cfg): - log_info("pythonmod: init called, module id is %d port: %d script: %s" % (id, cfg.port, cfg.python_script)) + log_info("pythonmod: init called, module id is %d port: %d script: %s" % (id, cfg.port, mod_env['script'])) return True def deinit(id): @@ -55,12 +55,15 @@ def setTTL(qstate, ttl): def dataHex(data, prefix=""): res = "" - for i in range(0, (len(data)+15)/16): + for i in range(0, int((len(data)+15)/16)): res += "%s0x%02X | " % (prefix, i*16) - d = map(lambda x:ord(x), data[i*16:i*16+17]) + if type(data[0]) == type(1): + d = map(lambda x:int(x), data[i*16:i*16+17]) + else: + d = map(lambda x:ord(x), data[i*16:i*16+17]) for ch in d: res += "%02X " % ch - for i in range(0,17-len(d)): + for i in range(0,17-len(data[i*16:i*16+17])): res += " " res += "| " for ch in d: @@ -72,31 +75,31 @@ def dataHex(data, prefix=""): return res def printReturnMsg(qstate): - print "Return MSG rep :: flags: %04X, QDcount: %d, Security:%d, TTL=%d" % (qstate.return_msg.rep.flags, qstate.return_msg.rep.qdcount,qstate.return_msg.rep.security, qstate.return_msg.rep.ttl) - print " qinfo :: qname:",qstate.return_msg.qinfo.qname_list, qstate.return_msg.qinfo.qname_str, "type:",qstate.return_msg.qinfo.qtype_str, "class:",qstate.return_msg.qinfo.qclass_str + print("Return MSG rep :: flags: %04X, QDcount: %d, Security:%d, TTL=%d" % (qstate.return_msg.rep.flags, qstate.return_msg.rep.qdcount,qstate.return_msg.rep.security, qstate.return_msg.rep.ttl)) + print(" qinfo :: qname:",qstate.return_msg.qinfo.qname_list, qstate.return_msg.qinfo.qname_str, "type:",qstate.return_msg.qinfo.qtype_str, "class:",qstate.return_msg.qinfo.qclass_str) if (qstate.return_msg.rep): - print "RRSets:",qstate.return_msg.rep.rrset_count + print("RRSets:",qstate.return_msg.rep.rrset_count) prevkey = None for i in range(0,qstate.return_msg.rep.rrset_count): r = qstate.return_msg.rep.rrsets[i] rk = r.rk - print i,":",rk.dname_list, rk.dname_str, "flags: %04X" % rk.flags, - print "type:",rk.type_str,"(%d)" % ntohs(rk.type), "class:",rk.rrset_class_str,"(%d)" % ntohs(rk.rrset_class) + print(i,":",rk.dname_list, rk.dname_str, "flags: %04X" % rk.flags) + print("type:",rk.type_str,"(%d)" % ntohs(rk.type), "class:",rk.rrset_class_str,"(%d)" % ntohs(rk.rrset_class)) d = r.entry.data - print " RRDatas:",d.count+d.rrsig_count + print(" RRDatas:",d.count+d.rrsig_count) for j in range(0,d.count+d.rrsig_count): - print " ",j,":","TTL=",d.rr_ttl[j],"RR data:" - print dataHex(d.rr_data[j]," ") + print(" ",j,":","TTL=",d.rr_ttl[j],"RR data:") + print(dataHex(d.rr_data[j]," ")) def operate(id, event, qstate, qdata): log_info("pythonmod: operate called, id: %d, event:%s" % (id, strmodulevent(event))) - #print "pythonmod: per query data", qdata + #print("pythonmod: per query data", qdata) - print "Query:", ''.join(map(lambda x:chr(max(32,ord(x))),qstate.qinfo.qname)), qstate.qinfo.qname_list, - print "Type:",qstate.qinfo.qtype_str,"(%d)" % qstate.qinfo.qtype, - print "Class:",qstate.qinfo.qclass_str,"(%d)" % qstate.qinfo.qclass - print + print("Query:", qstate.qinfo.qname, qstate.qinfo.qname_list, qstate.qinfo.qname_str) + print("Type:",qstate.qinfo.qtype_str,"(%d)" % qstate.qinfo.qtype) + print("Class:",qstate.qinfo.qclass_str,"(%d)" % qstate.qinfo.qclass) + print("") # TEST: # > dig @127.0.0.1 www.seznam.cz A @@ -118,7 +121,7 @@ def operate(id, event, qstate, qdata): invalidateQueryInCache(qstate, qstate.return_msg.qinfo) if (qstate.return_msg.rep.authoritative): - print "X"*300 + print("X"*300) setTTL(qstate, 10) #do cache nastavime TTL na 10 if not storeQueryInCache(qstate, qstate.return_msg.qinfo, qstate.return_msg.rep, 0): diff --git a/services/authzone.c b/services/authzone.c index cd3ef8dbb..a1b3d2278 100644 --- a/services/authzone.c +++ b/services/authzone.c @@ -2475,6 +2475,7 @@ az_find_ce(struct auth_zone* z, struct query_info* qinfo, struct auth_rrset** rrset) { struct auth_data* n = node; + struct auth_rrset* lookrrset; *ce = NULL; *rrset = NULL; if(!node_exact) { @@ -2497,21 +2498,23 @@ az_find_ce(struct auth_zone* z, struct query_info* qinfo, /* see if the current candidate has issues */ /* not zone apex and has type NS */ if(n->namelen != z->namelen && - (*rrset=az_domain_rrset(n, LDNS_RR_TYPE_NS)) && + (lookrrset=az_domain_rrset(n, LDNS_RR_TYPE_NS)) && /* delegate here, but DS at exact the dp has notype */ (qinfo->qtype != LDNS_RR_TYPE_DS || n->namelen != qinfo->qname_len)) { /* referral */ /* this is ce and the lowernode is nonexisting */ *ce = n; - return 0; + *rrset = lookrrset; + node_exact = 0; } /* not equal to qname and has type DNAME */ if(n->namelen != qinfo->qname_len && - (*rrset=az_domain_rrset(n, LDNS_RR_TYPE_DNAME))) { + (lookrrset=az_domain_rrset(n, LDNS_RR_TYPE_DNAME))) { /* this is ce and the lowernode is nonexisting */ *ce = n; - return 0; + *rrset = lookrrset; + node_exact = 0; } if(*ce == NULL && !domain_has_only_nsec3(n)) { diff --git a/services/listen_dnsport.c b/services/listen_dnsport.c index 60f9b41e5..753550978 100644 --- a/services/listen_dnsport.c +++ b/services/listen_dnsport.c @@ -1327,7 +1327,9 @@ ports_create_if(const char* ifname, int do_auto, int do_udp, int do_tcp, log_warn("socket timestamping is not available"); } if(!port_insert(list, s, is_dnscrypt - ?listen_type_udp_dnscrypt:listen_type_udp, + ?listen_type_udp_dnscrypt : + (sock_queue_timeout ? + listen_type_udpancil:listen_type_udp), is_pp2, ub_sock)) { sock_close(s); if(ub_sock->addr) @@ -1498,9 +1500,13 @@ listen_create(struct comm_base* base, struct listen_port* ports, } } else if(ports->ftype == listen_type_udpancil || ports->ftype == listen_type_udpancil_dnscrypt) { +#if defined(AF_INET6) && defined(IPV6_PKTINFO) && defined(HAVE_RECVMSG) cp = comm_point_create_udp_ancil(base, ports->fd, front->udp_buff, ports->pp2_enabled, cb, cb_arg, ports->socket); +#else + log_warn("This system does not support UDP ancilliary data."); +#endif } if(!cp) { log_err("can't create commpoint"); diff --git a/services/mesh.c b/services/mesh.c index 52d14a2d1..509bee36a 100644 --- a/services/mesh.c +++ b/services/mesh.c @@ -1197,6 +1197,8 @@ mesh_do_callback(struct mesh_state* m, int rcode, struct reply_info* rep, r->edns.udp_size = EDNS_ADVERTISED_SIZE; r->edns.ext_rcode = 0; r->edns.bits &= EDNS_DO; + if(m->s.env->cfg->disable_edns_do && (r->edns.bits&EDNS_DO)) + r->edns.edns_present = 0; if(!inplace_cb_reply_call(m->s.env, &m->s.qinfo, &m->s, rep, LDNS_RCODE_NOERROR, &r->edns, NULL, m->s.region, start_time) || @@ -1224,11 +1226,12 @@ static inline int mesh_is_rpz_respip_tcponly_action(struct mesh_state const* m) { struct respip_action_info const* respip_info = m->s.respip_action_info; - return respip_info == NULL + return (respip_info == NULL ? 0 : (respip_info->rpz_used && !respip_info->rpz_disabled - && respip_info->action == respip_truncate); + && respip_info->action == respip_truncate)) + || m->s.tcp_required; } static inline int @@ -1371,6 +1374,8 @@ mesh_send_reply(struct mesh_state* m, int rcode, struct reply_info* rep, r->edns.udp_size = EDNS_ADVERTISED_SIZE; r->edns.ext_rcode = 0; r->edns.bits &= EDNS_DO; + if(m->s.env->cfg->disable_edns_do && (r->edns.bits&EDNS_DO)) + r->edns.edns_present = 0; m->s.qinfo.qname = r->qname; m->s.qinfo.local_alias = r->local_alias; diff --git a/services/outside_network.c b/services/outside_network.c index 77de4563c..e403f5ee7 100644 --- a/services/outside_network.c +++ b/services/outside_network.c @@ -550,7 +550,6 @@ reuse_tcp_find(struct outside_network* outnet, struct sockaddr_storage* addr, log_assert(&key_p.reuse != (struct reuse_tcp*)result); log_assert(&key_p != ((struct reuse_tcp*)result)->pending); } - /* not found, return null */ /* It is possible that we search for something before the first element * in the tree. Replace a null pointer with the first element. @@ -560,6 +559,7 @@ reuse_tcp_find(struct outside_network* outnet, struct sockaddr_storage* addr, result = rbtree_first(&outnet->tcp_reuse); } + /* not found, return null */ if(!result || result == RBTREE_NULL) return NULL; diff --git a/services/rpz.c b/services/rpz.c index 6ce83cb66..18d76c07b 100644 --- a/services/rpz.c +++ b/services/rpz.c @@ -2162,7 +2162,7 @@ rpz_apply_nsip_trigger(struct module_qstate* ms, struct rpz* r, case RPZ_TCP_ONLY_ACTION: /* basically a passthru here but the tcp-only will be * honored before the query gets sent. */ - ms->respip_action_info->action = respip_truncate; + ms->tcp_required = 1; ret = NULL; break; case RPZ_DROP_ACTION: @@ -2217,7 +2217,7 @@ rpz_apply_nsdname_trigger(struct module_qstate* ms, struct rpz* r, case RPZ_TCP_ONLY_ACTION: /* basically a passthru here but the tcp-only will be * honored before the query gets sent. */ - ms->respip_action_info->action = respip_truncate; + ms->tcp_required = 1; ret = NULL; break; case RPZ_DROP_ACTION: @@ -2428,7 +2428,7 @@ struct dns_msg* rpz_callback_from_iterator_cname(struct module_qstate* ms, case RPZ_TCP_ONLY_ACTION: /* basically a passthru here but the tcp-only will be * honored before the query gets sent. */ - ms->respip_action_info->action = respip_truncate; + ms->tcp_required = 1; ret = NULL; break; case RPZ_DROP_ACTION: @@ -2448,6 +2448,10 @@ struct dns_msg* rpz_callback_from_iterator_cname(struct module_qstate* ms, rpz_action_to_string(localzone_type_to_rpz_action(lzt))); ret = NULL; } + if(r->log) + log_rpz_apply("qname", (z?z->name:NULL), NULL, + localzone_type_to_rpz_action(lzt), + &is->qchase, NULL, ms, r->log_name); lock_rw_unlock(&z->lock); lock_rw_unlock(&a->lock); return ret; diff --git a/sldns/wire2str.c b/sldns/wire2str.c index e6278ff56..2b5dc0513 100644 --- a/sldns/wire2str.c +++ b/sldns/wire2str.c @@ -192,6 +192,7 @@ static sldns_lookup_table sldns_edns_options_data[] = { { 6, "DHU" }, { 7, "N3U" }, { 8, "edns-client-subnet" }, + { 10, "COOKIE" }, { 11, "edns-tcp-keepalive"}, { 12, "Padding" }, { 15, "EDE"}, @@ -199,6 +200,38 @@ static sldns_lookup_table sldns_edns_options_data[] = { }; sldns_lookup_table* sldns_edns_options = sldns_edns_options_data; +/* From RFC8914 5.2 Table 3, the "Extended DNS Error Codes" registry. */ +static sldns_lookup_table sldns_edns_ede_codes_data[] = { + { LDNS_EDE_NONE, "None" }, + { LDNS_EDE_OTHER, "Other Error" }, + { LDNS_EDE_UNSUPPORTED_DNSKEY_ALG, "Unsupported DNSKEY Algorithm" }, + { LDNS_EDE_UNSUPPORTED_DS_DIGEST, "Unsupported DS Digest Type" }, + { LDNS_EDE_STALE_ANSWER, "Stale Answer" }, + { LDNS_EDE_FORGED_ANSWER, "Forged Answer" }, + { LDNS_EDE_DNSSEC_INDETERMINATE, "DNSSEC Indeterminate" }, + { LDNS_EDE_DNSSEC_BOGUS, "DNSSEC Bogus" }, + { LDNS_EDE_SIGNATURE_EXPIRED, "Signature Expired" }, + { LDNS_EDE_SIGNATURE_NOT_YET_VALID, "Signature Not Yet Valid" }, + { LDNS_EDE_DNSKEY_MISSING, "DNSKEY Missing" }, + { LDNS_EDE_RRSIGS_MISSING, "RRSIGs Missing" }, + { LDNS_EDE_NO_ZONE_KEY_BIT_SET, "No Zone Key Bit Set" }, + { LDNS_EDE_NSEC_MISSING, "NSEC Missing" }, + { LDNS_EDE_CACHED_ERROR, "Cached Error" }, + { LDNS_EDE_NOT_READY, "Not Ready" }, + { LDNS_EDE_BLOCKED, "Blocked" }, + { LDNS_EDE_CENSORED, "Censored" }, + { LDNS_EDE_FILTERED, "Filtered" }, + { LDNS_EDE_PROHIBITED, "Prohibited" }, + { LDNS_EDE_STALE_NXDOMAIN_ANSWER, "Stale NXDOMAIN Answer" }, + { LDNS_EDE_NOT_AUTHORITATIVE, "Not Authoritative" }, + { LDNS_EDE_NOT_SUPPORTED, "Not Supported" }, + { LDNS_EDE_NO_REACHABLE_AUTHORITY, "No Reachable Authority" }, + { LDNS_EDE_NETWORK_ERROR, "Network Error" }, + { LDNS_EDE_INVALID_DATA, "Invalid Data" }, + { 0, NULL} +}; +sldns_lookup_table* sldns_edns_ede_codes = sldns_edns_ede_codes_data; + static sldns_lookup_table sldns_tsig_errors_data[] = { { LDNS_TSIG_ERROR_NOERROR, "NOERROR" }, { LDNS_RCODE_FORMERR, "FORMERR" }, @@ -2234,6 +2267,52 @@ static int sldns_wire2str_edns_keepalive_print(char** s, size_t* sl, return w; } +int sldns_wire2str_edns_ede_print(char** s, size_t* sl, + uint8_t* data, size_t len) +{ + uint16_t ede_code; + int w = 0; + sldns_lookup_table *lt; + size_t i; + int printable; + + if(len < 2) { + w += sldns_str_print(s, sl, "malformed ede "); + w += print_hex_buf(s, sl, data, len); + return w; + } + + ede_code = sldns_read_uint16(data); + lt = sldns_lookup_by_id(sldns_edns_ede_codes, (int)ede_code); + if(lt && lt->name) + w += sldns_str_print(s, sl, "%s", lt->name); + else w += sldns_str_print(s, sl, "%d", (int)ede_code); + + if(len == 2) + return w; + + w += sldns_str_print(s, sl, " "); + + /* If it looks like text, show it as text. */ + printable=1; + for(i=2; iauto_trust_anchor_file_list, cfg->chrootdir, cfg); check_chroot_filelist_wild("trusted-keys-file", cfg->trusted_keys_file_list, cfg->chrootdir, cfg); + if(cfg->disable_edns_do && strstr(cfg->module_conf, "validator") + && (cfg->trust_anchor_file_list + || cfg->trust_anchor_list + || cfg->auto_trust_anchor_file_list + || cfg->trusted_keys_file_list)) { + char* key = NULL; + if(cfg->auto_trust_anchor_file_list) + key = cfg->auto_trust_anchor_file_list->str; + if(!key && cfg->trust_anchor_file_list) + key = cfg->trust_anchor_file_list->str; + if(!key && cfg->trust_anchor_list) + key = cfg->trust_anchor_list->str; + if(!key && cfg->trusted_keys_file_list) + key = cfg->trusted_keys_file_list->str; + if(!key) key = ""; + fatal_exit("disable-edns-do does not allow DNSSEC to work, but the validator module uses a trust anchor %s, turn off disable-edns-do or disable validation", key); + } #ifdef USE_IPSECMOD if(cfg->ipsecmod_enabled && strstr(cfg->module_conf, "ipsecmod")) { /* only check hook if enabled */ diff --git a/testcode/dohclient.c b/testcode/dohclient.c index de9f39d7d..2c12a5043 100644 --- a/testcode/dohclient.c +++ b/testcode/dohclient.c @@ -286,7 +286,7 @@ static ssize_t http2_recv_cb(nghttp2_session* ATTR_UNUSED(session), if(want == SSL_ERROR_ZERO_RETURN) { return NGHTTP2_ERR_EOF; } - log_crypto_err("could not SSL_read"); + log_crypto_err_io("could not SSL_read", want); return NGHTTP2_ERR_EOF; } return r; @@ -317,7 +317,7 @@ static ssize_t http2_send_cb(nghttp2_session* ATTR_UNUSED(session), if(want == SSL_ERROR_ZERO_RETURN) { return NGHTTP2_ERR_CALLBACK_FAILURE; } - log_crypto_err("could not SSL_write"); + log_crypto_err_io("could not SSL_write", want); return NGHTTP2_ERR_CALLBACK_FAILURE; } return r; @@ -526,7 +526,7 @@ run(struct http2_session* h2_session, int port, int no_tls, int count, char** q) r = SSL_get_error(ssl, r); if(r != SSL_ERROR_WANT_READ && r != SSL_ERROR_WANT_WRITE) { - log_crypto_err("could not ssl_handshake"); + log_crypto_err_io("could not ssl_handshake", r); exit(1); } } diff --git a/testcode/fake_event.c b/testcode/fake_event.c index 2140b212a..13970c377 100644 --- a/testcode/fake_event.c +++ b/testcode/fake_event.c @@ -1249,7 +1249,7 @@ struct serviced_query* outnet_serviced_query(struct outside_network* outnet, edns.edns_version = EDNS_ADVERTISED_VERSION; edns.udp_size = EDNS_ADVERTISED_SIZE; edns.bits = 0; - if(dnssec) + if((dnssec & EDNS_DO)) edns.bits = EDNS_DO; edns.padding_block_size = 0; edns.cookie_present = 0; diff --git a/testcode/streamtcp.1 b/testcode/streamtcp.1 index f02b168d2..55ed4a279 100644 --- a/testcode/streamtcp.1 +++ b/testcode/streamtcp.1 @@ -61,6 +61,17 @@ Specify the server to send the queries to. If not specified localhost (127.0.0.1 .B \-d \fIsecs Delay after the connection before sending query. This tests the timeout on the other side, eg. if shorter the connection is closed. +.TP +.B \-p \fIclient +Use proxy protocol to send the query. Specify the ipaddr@portnr of the client +to include in PROXYv2. +.TP +.B IXFR=serial +Pass the type of the query as IXFR=N to send an IXFR query with serial N. +.TP +.B NOTIFY[=serial] +Pass the type of the query as NOTIFY[=N] to send a notify packet. The serial N +of the new zone can be included. .SH "EXAMPLES" .LP Some examples of use. diff --git a/testcode/streamtcp.c b/testcode/streamtcp.c index 84d2b65f6..037bcfd8b 100644 --- a/testcode/streamtcp.c +++ b/testcode/streamtcp.c @@ -79,6 +79,8 @@ static void usage(char* argv[]) printf("-d secs delay after connection before sending query\n"); printf("-s use ssl\n"); printf("-h this help text\n"); + printf("IXFR=N for the type, sends ixfr query with serial N.\n"); + printf("NOTIFY[=N] for the type, sends notify. Can set new zone serial N.\n"); exit(1); } @@ -115,6 +117,29 @@ open_svr(const char* svr, int udp, struct sockaddr_storage* addr, return fd; } +/** Append a SOA record with serial number */ +static void +write_soa_serial_to_buf(sldns_buffer* buf, struct query_info* qinfo, + uint32_t serial) +{ + sldns_buffer_set_position(buf, sldns_buffer_limit(buf)); + sldns_buffer_set_limit(buf, sldns_buffer_capacity(buf)); + /* Write compressed reference to the query */ + sldns_buffer_write_u16(buf, PTR_CREATE(LDNS_HEADER_SIZE)); + sldns_buffer_write_u16(buf, LDNS_RR_TYPE_SOA); + sldns_buffer_write_u16(buf, qinfo->qclass); + sldns_buffer_write_u32(buf, 3600); /* TTL */ + sldns_buffer_write_u16(buf, 1+1+4*5); /* rdatalen */ + sldns_buffer_write_u8(buf, 0); /* primary "." */ + sldns_buffer_write_u8(buf, 0); /* email "." */ + sldns_buffer_write_u32(buf, serial); /* serial */ + sldns_buffer_write_u32(buf, 0); /* refresh */ + sldns_buffer_write_u32(buf, 0); /* retry */ + sldns_buffer_write_u32(buf, 0); /* expire */ + sldns_buffer_write_u32(buf, 0); /* minimum */ + sldns_buffer_flip(buf); +} + /** write a query over the TCP fd */ static void write_q(int fd, int udp, SSL* ssl, sldns_buffer* buf, uint16_t id, @@ -123,6 +148,8 @@ write_q(int fd, int udp, SSL* ssl, sldns_buffer* buf, uint16_t id, { struct query_info qinfo; size_t proxy_buf_limit = sldns_buffer_limit(proxy_buf); + int have_serial = 0, is_notify = 0; + uint32_t serial = 0; /* qname */ qinfo.qname = sldns_str2wire_dname(strname, &qinfo.qname_len); if(!qinfo.qname) { @@ -130,12 +157,27 @@ write_q(int fd, int udp, SSL* ssl, sldns_buffer* buf, uint16_t id, exit(1); } - /* qtype and qclass */ - qinfo.qtype = sldns_get_rr_type_by_name(strtype); - if(qinfo.qtype == 0 && strcmp(strtype, "TYPE0") != 0) { - printf("cannot parse query type: '%s'\n", strtype); - exit(1); + /* qtype */ + if(strncasecmp(strtype, "IXFR=", 5) == 0) { + serial = (uint32_t)atoi(strtype+5); + have_serial = 1; + qinfo.qtype = LDNS_RR_TYPE_IXFR; + } else if(strcasecmp(strtype, "NOTIFY") == 0) { + is_notify = 1; + qinfo.qtype = LDNS_RR_TYPE_SOA; + } else if(strncasecmp(strtype, "NOTIFY=", 7) == 0) { + serial = (uint32_t)atoi(strtype+7); + have_serial = 1; + is_notify = 1; + qinfo.qtype = LDNS_RR_TYPE_SOA; + } else { + qinfo.qtype = sldns_get_rr_type_by_name(strtype); + if(qinfo.qtype == 0 && strcmp(strtype, "TYPE0") != 0) { + printf("cannot parse query type: '%s'\n", strtype); + exit(1); + } } + /* qclass */ qinfo.qclass = sldns_get_rr_class_by_name(strclass); if(qinfo.qclass == 0 && strcmp(strclass, "CLASS0") != 0) { printf("cannot parse query class: '%s'\n", strclass); @@ -150,6 +192,21 @@ write_q(int fd, int udp, SSL* ssl, sldns_buffer* buf, uint16_t id, sldns_buffer_write_u16_at(buf, 0, id); sldns_buffer_write_u16_at(buf, 2, BIT_RD); + if(have_serial && qinfo.qtype == LDNS_RR_TYPE_IXFR) { + /* Attach serial to SOA record in the authority section. */ + write_soa_serial_to_buf(buf, &qinfo, serial); + LDNS_NSCOUNT_SET(sldns_buffer_begin(buf), 1); + } + if(is_notify) { + LDNS_OPCODE_SET(sldns_buffer_begin(buf), LDNS_PACKET_NOTIFY); + LDNS_RD_CLR(sldns_buffer_begin(buf)); + LDNS_AA_SET(sldns_buffer_begin(buf)); + if(have_serial) { + write_soa_serial_to_buf(buf, &qinfo, serial); + LDNS_ANCOUNT_SET(sldns_buffer_begin(buf), 1); + } + } + if(1) { /* add EDNS DO */ struct edns_data edns; @@ -361,6 +418,7 @@ static int parse_pp2_client(const char* pp2_client, int udp, sldns_buffer* proxy_buf) { struct sockaddr_storage pp2_addr; + size_t bytes_written; socklen_t pp2_addrlen = 0; memset(&pp2_addr, 0, sizeof(pp2_addr)); if(*pp2_client == 0) return 0; @@ -369,7 +427,9 @@ static int parse_pp2_client(const char* pp2_client, int udp, exit(1); } sldns_buffer_clear(proxy_buf); - pp2_write_to_buf(proxy_buf, &pp2_addr, !udp); + bytes_written = pp2_write_to_buf(sldns_buffer_begin(proxy_buf), + sldns_buffer_remaining(proxy_buf), &pp2_addr, !udp); + sldns_buffer_set_position(proxy_buf, bytes_written); sldns_buffer_flip(proxy_buf); return 1; } @@ -406,7 +466,7 @@ send_em(const char* svr, const char* pp2_client, int udp, int usessl, r = SSL_get_error(ssl, r); if(r != SSL_ERROR_WANT_READ && r != SSL_ERROR_WANT_WRITE) { - log_crypto_err("could not ssl_handshake"); + log_crypto_err_io("could not ssl_handshake", r); exit(1); } } @@ -541,6 +601,8 @@ int main(int argc, char** argv) break; case 'p': pp2_client = optarg; + pp_init(&sldns_write_uint16, + &sldns_write_uint32); break; case 'a': onarrival = 1; diff --git a/testcode/unitauth.c b/testcode/unitauth.c index d193526b8..11eeb43b2 100644 --- a/testcode/unitauth.c +++ b/testcode/unitauth.c @@ -76,10 +76,18 @@ static const char* zone_example_com = "out.example.com. 3600 IN CNAME www.example.com.\n" "plan.example.com. 3600 IN CNAME nonexist.example.com.\n" "redir.example.com. 3600 IN DNAME redir.example.org.\n" +"redir2.example.com. 3600 IN DNAME redir2.example.org.\n" +"obscured.redir2.example.com. 3600 IN A 10.0.0.12\n" +"under2.redir2.example.com. 3600 IN DNAME redir3.example.net.\n" +"doubleobscured.under2.redir2.example.com. 3600 IN A 10.0.0.13\n" "sub.example.com. 3600 IN NS ns1.sub.example.com.\n" "sub.example.com. 3600 IN NS ns2.sub.example.com.\n" "ns1.sub.example.com. 3600 IN A 10.0.0.6\n" "ns2.sub.example.com. 3600 IN AAAA 2001::7\n" +"sub2.example.com. 3600 IN NS ns1.sub.example.com.\n" +"obscured.sub2.example.com. 3600 IN A 10.0.0.10\n" +"under.sub2.example.com. 3600 IN NS ns.under.sub2.example.com.\n" +"doubleobscured.under.sub2.example.com. 3600 IN A 10.0.0.11\n" "*.wild.example.com. 3600 IN A 10.0.0.8\n" "*.wild2.example.com. 3600 IN CNAME www.example.com.\n" "*.wild3.example.com. 3600 IN A 10.0.0.8\n" @@ -281,6 +289,54 @@ static struct q_ans example_com_queries[] = { "foo.abc.redir.example.com. 0 IN CNAME foo.abc.redir.example.org.\n" }, + { "example.com", "redir2.example.com. DNAME", "", +";flags QR AA rcode NOERROR\n" +";answer section\n" +"redir2.example.com. 3600 IN DNAME redir2.example.org.\n" + }, + + { "example.com", "abc.redir2.example.com. A", "", +";flags QR AA rcode NOERROR\n" +";answer section\n" +"redir2.example.com. 3600 IN DNAME redir2.example.org.\n" +"abc.redir2.example.com. 0 IN CNAME abc.redir2.example.org.\n" + }, + + { "example.com", "obscured.redir2.example.com. A", "", +";flags QR AA rcode NOERROR\n" +";answer section\n" +"redir2.example.com. 3600 IN DNAME redir2.example.org.\n" +"obscured.redir2.example.com. 0 IN CNAME obscured.redir2.example.org.\n" + }, + + { "example.com", "under2.redir2.example.com. A", "", +";flags QR AA rcode NOERROR\n" +";answer section\n" +"redir2.example.com. 3600 IN DNAME redir2.example.org.\n" +"under2.redir2.example.com. 0 IN CNAME under2.redir2.example.org.\n" + }, + + { "example.com", "doubleobscured.under2.redir2.example.com. A", "", +";flags QR AA rcode NOERROR\n" +";answer section\n" +"redir2.example.com. 3600 IN DNAME redir2.example.org.\n" +"doubleobscured.under2.redir2.example.com. 0 IN CNAME doubleobscured.under2.redir2.example.org.\n" + }, + + { "example.com", "foo.doubleobscured.under2.redir2.example.com. A", "", +";flags QR AA rcode NOERROR\n" +";answer section\n" +"redir2.example.com. 3600 IN DNAME redir2.example.org.\n" +"foo.doubleobscured.under2.redir2.example.com. 0 IN CNAME foo.doubleobscured.under2.redir2.example.org.\n" + }, + + { "example.com", "foo.under2.redir2.example.com. A", "", +";flags QR AA rcode NOERROR\n" +";answer section\n" +"redir2.example.com. 3600 IN DNAME redir2.example.org.\n" +"foo.under2.redir2.example.com. 0 IN CNAME foo.under2.redir2.example.org.\n" + }, + { "example.com", "sub.example.com. NS", "", ";flags QR rcode NOERROR\n" ";authority section\n" @@ -357,6 +413,78 @@ static struct q_ans example_com_queries[] = { "ns2.sub.example.com. 3600 IN AAAA 2001::7\n" }, + { "example.com", "sub2.example.com. A", "", +";flags QR rcode NOERROR\n" +";authority section\n" +"sub2.example.com. 3600 IN NS ns1.sub.example.com.\n" +";additional section\n" +"ns1.sub.example.com. 3600 IN A 10.0.0.6\n" + }, + + { "example.com", "sub2.example.com. NS", "", +";flags QR rcode NOERROR\n" +";authority section\n" +"sub2.example.com. 3600 IN NS ns1.sub.example.com.\n" +";additional section\n" +"ns1.sub.example.com. 3600 IN A 10.0.0.6\n" + }, + + { "example.com", "obscured.sub2.example.com. A", "", +";flags QR rcode NOERROR\n" +";authority section\n" +"sub2.example.com. 3600 IN NS ns1.sub.example.com.\n" +";additional section\n" +"ns1.sub.example.com. 3600 IN A 10.0.0.6\n" + }, + + { "example.com", "abc.obscured.sub2.example.com. A", "", +";flags QR rcode NOERROR\n" +";authority section\n" +"sub2.example.com. 3600 IN NS ns1.sub.example.com.\n" +";additional section\n" +"ns1.sub.example.com. 3600 IN A 10.0.0.6\n" + }, + + { "example.com", "under.sub2.example.com. A", "", +";flags QR rcode NOERROR\n" +";authority section\n" +"sub2.example.com. 3600 IN NS ns1.sub.example.com.\n" +";additional section\n" +"ns1.sub.example.com. 3600 IN A 10.0.0.6\n" + }, + + { "example.com", "under.sub2.example.com. NS", "", +";flags QR rcode NOERROR\n" +";authority section\n" +"sub2.example.com. 3600 IN NS ns1.sub.example.com.\n" +";additional section\n" +"ns1.sub.example.com. 3600 IN A 10.0.0.6\n" + }, + + { "example.com", "abc.under.sub2.example.com. A", "", +";flags QR rcode NOERROR\n" +";authority section\n" +"sub2.example.com. 3600 IN NS ns1.sub.example.com.\n" +";additional section\n" +"ns1.sub.example.com. 3600 IN A 10.0.0.6\n" + }, + + { "example.com", "doubleobscured.under.sub2.example.com. A", "", +";flags QR rcode NOERROR\n" +";authority section\n" +"sub2.example.com. 3600 IN NS ns1.sub.example.com.\n" +";additional section\n" +"ns1.sub.example.com. 3600 IN A 10.0.0.6\n" + }, + + { "example.com", "abc.doubleobscured.under.sub2.example.com. A", "", +";flags QR rcode NOERROR\n" +";authority section\n" +"sub2.example.com. 3600 IN NS ns1.sub.example.com.\n" +";additional section\n" +"ns1.sub.example.com. 3600 IN A 10.0.0.6\n" + }, + { "example.com", "wild.example.com. A", "", ";flags QR AA rcode NOERROR\n" ";authority section\n" diff --git a/testdata/cachedb_no_store.tdir/cachedb_no_store.conf b/testdata/cachedb_no_store.tdir/cachedb_no_store.conf new file mode 100644 index 000000000..ff76cc379 --- /dev/null +++ b/testdata/cachedb_no_store.tdir/cachedb_no_store.conf @@ -0,0 +1,29 @@ +server: + verbosity: 4 + interface: 127.0.0.1 + port: @PORT@ + use-syslog: no + directory: "" + pidfile: "unbound.pid" + chroot: "" + username: "" + module-config: "cachedb iterator" + do-not-query-localhost: no + qname-minimisation: no + +forward-zone: + name: "." + forward-addr: 127.0.0.1@@TOPORT@ + +stub-zone: + name: "example.com" + stub-addr: 127.0.0.1@@TOPORT@ + +remote-control: + control-enable: yes + control-interface: @CONTROL_PATH@/controlpipe.@CONTROL_PID@ + control-use-cert: no + +cachedb: + backend: "testframe" + secret-seed: "testvalue" diff --git a/testdata/cachedb_no_store.tdir/cachedb_no_store.dsc b/testdata/cachedb_no_store.tdir/cachedb_no_store.dsc new file mode 100644 index 000000000..9d267436e --- /dev/null +++ b/testdata/cachedb_no_store.tdir/cachedb_no_store.dsc @@ -0,0 +1,16 @@ +BaseName: cachedb_no_store +Version: 1.0 +Description: cachedb test the cachedb-no-store option +CreationDate: Wed 11 Oct 11:00:00 CEST 2023 +Maintainer: dr. W.C.A. Wijngaards +Category: +Component: +CmdDepends: +Depends: +Help: +Pre: cachedb_no_store.pre +Post: cachedb_no_store.post +Test: cachedb_no_store.test +AuxFiles: +Passed: +Failure: diff --git a/testdata/cachedb_no_store.tdir/cachedb_no_store.post b/testdata/cachedb_no_store.tdir/cachedb_no_store.post new file mode 100644 index 000000000..901f01a87 --- /dev/null +++ b/testdata/cachedb_no_store.tdir/cachedb_no_store.post @@ -0,0 +1,20 @@ +# #-- cachedb_no_store.post --# +# source the master var file when it's there +[ -f ../.tpkg.var.master ] && source ../.tpkg.var.master +# source the test var file when it's there +[ -f .tpkg.var.test ] && source .tpkg.var.test +# +# do your teardown here +PRE="../.." +. ../common.sh + +echo "> cat logfiles" +cat fwd.log +if test -f fwd2.log; then cat fwd2.log; else echo "no fwd2.log"; fi +if test -f fwd3.log; then cat fwd3.log; else echo "no fwd3.log"; fi +if test -f fwd4.log; then cat fwd4.log; else echo "no fwd4.log"; fi +cat unbound.log +if test -f unbound2.log; then cat unbound2.log; else echo "no unbound2.log"; fi +kill_pid $FWD_PID +kill_pid `cat unbound.pid` +rm -f $CONTROL_PATH/controlpipe.$CONTROL_PID diff --git a/testdata/cachedb_no_store.tdir/cachedb_no_store.pre b/testdata/cachedb_no_store.tdir/cachedb_no_store.pre new file mode 100644 index 000000000..e59d3b8da --- /dev/null +++ b/testdata/cachedb_no_store.tdir/cachedb_no_store.pre @@ -0,0 +1,36 @@ +# #-- cachedb_no_store.pre--# +# source the master var file when it's there +[ -f ../.tpkg.var.master ] && source ../.tpkg.var.master +# use .tpkg.var.test for in test variable passing +[ -f .tpkg.var.test ] && source .tpkg.var.test + +PRE="../.." +. ../common.sh +if grep "define USE_CACHEDB 1" $PRE/config.h; then echo test enabled; else skip_test "test skipped"; fi + +get_random_port 2 +UNBOUND_PORT=$RND_PORT +FWD_PORT=$(($RND_PORT + 1)) +echo "UNBOUND_PORT=$UNBOUND_PORT" >> .tpkg.var.test +echo "FWD_PORT=$FWD_PORT" >> .tpkg.var.test + +# start forwarder +get_ldns_testns +$LDNS_TESTNS -p $FWD_PORT cachedb_no_store.testns >fwd.log 2>&1 & +FWD_PID=$! +echo "FWD_PID=$FWD_PID" >> .tpkg.var.test + +# make config file +CONTROL_PATH=/tmp +CONTROL_PID=$$ +sed -e 's/@PORT\@/'$UNBOUND_PORT'/' -e 's/@TOPORT\@/'$FWD_PORT'/' -e 's?@CONTROL_PATH\@?'$CONTROL_PATH'?' -e 's/@CONTROL_PID@/'$CONTROL_PID'/' < cachedb_no_store.conf > ub.conf +# start unbound in the background +$PRE/unbound -d -c ub.conf >unbound.log 2>&1 & +UNBOUND_PID=$! +echo "UNBOUND_PID=$UNBOUND_PID" >> .tpkg.var.test +echo "CONTROL_PATH=$CONTROL_PATH" >> .tpkg.var.test +echo "CONTROL_PID=$CONTROL_PID" >> .tpkg.var.test + +cat .tpkg.var.test +wait_ldns_testns_up fwd.log +wait_unbound_up unbound.log diff --git a/testdata/cachedb_no_store.tdir/cachedb_no_store.servfail.testns b/testdata/cachedb_no_store.tdir/cachedb_no_store.servfail.testns new file mode 100644 index 000000000..b41abb0ff --- /dev/null +++ b/testdata/cachedb_no_store.tdir/cachedb_no_store.servfail.testns @@ -0,0 +1,8 @@ +ENTRY_BEGIN +MATCH opcode +ADJUST copy_id copy_query +REPLY QR AA SERVFAIL +SECTION QUESTION +txt1.example.com. IN TXT +SECTION ANSWER +ENTRY_END diff --git a/testdata/cachedb_no_store.tdir/cachedb_no_store.test b/testdata/cachedb_no_store.tdir/cachedb_no_store.test new file mode 100644 index 000000000..352026844 --- /dev/null +++ b/testdata/cachedb_no_store.tdir/cachedb_no_store.test @@ -0,0 +1,132 @@ +# #-- cachedb_no_store.test --# +# source the master var file when it's there +[ -f ../.tpkg.var.master ] && source ../.tpkg.var.master +# use .tpkg.var.test for in test variable passing +[ -f .tpkg.var.test ] && source .tpkg.var.test + +PRE="../.." +. ../common.sh + +# do the test +get_ldns_testns + +# query for a text record that is stored by unbound's cache and cachedb +# in the testframe cache. +echo "> dig txt1.example.com." +dig @localhost -p $UNBOUND_PORT txt1.example.com. TXT | tee outfile +if grep "example text message" outfile; then + echo "OK" +else + echo "Not OK" + exit 1 +fi + +# stop the forwarder with servfail, to check the answer came from the cache +echo "> stop ldns-testns" +kill_pid $FWD_PID +echo "> start ldns-testns with servfails" +$LDNS_TESTNS -p $FWD_PORT cachedb_no_store.servfail.testns >fwd2.log 2>&1 & +FWD_PID=$! +echo "FWD_PID=$FWD_PID" >> .tpkg.var.test +wait_ldns_testns_up fwd2.log + +echo "> dig txt1.example.com. from unbound cache" +dig @localhost -p $UNBOUND_PORT txt1.example.com. TXT | tee outfile +if grep "example text message" outfile; then + echo "OK" +else + echo "Not OK" + exit 1 +fi + +# clear the cache of unbound, but not cachedb testframe cache +echo "> unbound-control flush" +$PRE/unbound-control -c ub.conf flush_type txt1.example.com. TXT +if test $? -ne 0; then + echo "wrong exit value." + exit 1 +else + echo "exit value: OK" +fi + +echo "> dig txt1.example.com. from cachedb" +dig @localhost -p $UNBOUND_PORT txt1.example.com. TXT | tee outfile +if grep "example text message" outfile; then + echo "OK" +else + echo "Not OK" + exit 1 +fi + +# start the forwarder again. +echo "> stop ldns-testns" +kill_pid $FWD_PID +echo "> start ldns-testns" +$LDNS_TESTNS -p $FWD_PORT cachedb_no_store.testns >fwd3.log 2>&1 & +FWD_PID=$! +echo "FWD_PID=$FWD_PID" >> .tpkg.var.test +wait_ldns_testns_up fwd3.log + +# stop unbound to flush the cachedb cache +echo "> stop unbound" +kill_pid `cat unbound.pid` + +echo "" +echo "> config unbound with cachedb-no-store: yes" +echo "cachedb: cachedb-no-store: yes" >> ub.conf + +# start unbound again. +echo "> start unbound" +$PRE/unbound -d -c ub.conf >unbound2.log 2>&1 & +UNBOUND_PID=$! +echo "UNBOUND_PID=$UNBOUND_PID" >> .tpkg.var.test +wait_unbound_up unbound2.log + +echo "" +echo "> dig txt1.example.com." +dig @localhost -p $UNBOUND_PORT txt1.example.com. TXT | tee outfile +if grep "example text message" outfile; then + echo "OK" +else + echo "Not OK" + exit 1 +fi + +# stop the forwarder with servfail, to check the answer came from the cache +echo "> stop ldns-testns" +kill_pid $FWD_PID +echo "> start ldns-testns with servfails" +$LDNS_TESTNS -p $FWD_PORT cachedb_no_store.servfail.testns >fwd4.log 2>&1 & +FWD_PID=$! +echo "FWD_PID=$FWD_PID" >> .tpkg.var.test +wait_ldns_testns_up fwd4.log + +echo "> dig txt1.example.com. from unbound cache" +dig @localhost -p $UNBOUND_PORT txt1.example.com. TXT | tee outfile +if grep "example text message" outfile; then + echo "OK" +else + echo "Not OK" + exit 1 +fi + +# clear the cache of unbound, but not cachedb testframe cache +echo "> unbound-control flush" +$PRE/unbound-control -c ub.conf flush_type txt1.example.com. TXT +if test $? -ne 0; then + echo "wrong exit value." + exit 1 +else + echo "exit value: OK" +fi + +echo "> dig txt1.example.com. from cachedb, but that has no message stored" +dig @localhost -p $UNBOUND_PORT txt1.example.com. TXT | tee outfile +if grep "SERVFAIL" outfile; then + echo "OK" +else + echo "Not OK" + exit 1 +fi + +exit 0 diff --git a/testdata/cachedb_no_store.tdir/cachedb_no_store.testns b/testdata/cachedb_no_store.tdir/cachedb_no_store.testns new file mode 100644 index 000000000..282b224f8 --- /dev/null +++ b/testdata/cachedb_no_store.tdir/cachedb_no_store.testns @@ -0,0 +1,9 @@ +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR AA NOERROR +SECTION QUESTION +txt1.example.com. IN TXT +SECTION ANSWER +txt1.example.com. IN TXT "example text message" +ENTRY_END diff --git a/testdata/disable_edns_do.rpl b/testdata/disable_edns_do.rpl new file mode 100644 index 000000000..82a16da06 --- /dev/null +++ b/testdata/disable_edns_do.rpl @@ -0,0 +1,164 @@ +; config options +; The island of trust is at example.com +server: + target-fetch-policy: "0 0 0 0 0" + qname-minimisation: "no" + trust-anchor-signaling: no + minimal-responses: no + disable-edns-do: yes + +stub-zone: + name: "." + stub-addr: 193.0.14.129 # K.ROOT-SERVERS.NET. +CONFIG_END + +SCENARIO_BEGIN Test lookup with disable-edns-do + +; K.ROOT-SERVERS.NET. +RANGE_BEGIN 0 100 + ADDRESS 193.0.14.129 +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR NOERROR +SECTION QUESTION +. IN NS +SECTION ANSWER +. IN NS K.ROOT-SERVERS.NET. +SECTION ADDITIONAL +K.ROOT-SERVERS.NET. IN A 193.0.14.129 +ENTRY_END + +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR NOERROR +SECTION QUESTION +www.example.com. IN A +SECTION AUTHORITY +com. IN NS a.gtld-servers.net. +SECTION ADDITIONAL +a.gtld-servers.net. IN A 192.5.6.30 +ENTRY_END +RANGE_END + +; a.gtld-servers.net. +RANGE_BEGIN 0 100 + ADDRESS 192.5.6.30 +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR NOERROR +SECTION QUESTION +com. IN NS +SECTION ANSWER +com. IN NS a.gtld-servers.net. +SECTION ADDITIONAL +a.gtld-servers.net. IN A 192.5.6.30 +ENTRY_END + +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR NOERROR +SECTION QUESTION +www.example.com. IN A +SECTION AUTHORITY +example.com. IN NS ns.example.com. +SECTION ADDITIONAL +ns.example.com. IN A 1.2.3.4 +ENTRY_END +RANGE_END + +; ns.example.com. +RANGE_BEGIN 0 100 + ADDRESS 1.2.3.4 +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR NOERROR +SECTION QUESTION +example.com. IN NS +SECTION ANSWER +example.com. IN NS ns.example.com. +example.com. 3600 IN RRSIG NS 3 2 3600 20070926134150 20070829134150 2854 example.com. MC0CFQCN+qHdJxoI/2tNKwsb08pra/G7aAIUAWA5sDdJTbrXA1/3OaesGBAO3sI= ;{id = 2854} +SECTION ADDITIONAL +ns.example.com. IN A 1.2.3.4 +ns.example.com. 3600 IN RRSIG A 3 3 3600 20070926135752 20070829135752 2854 example.com. MC0CFQCMSWxVehgOQLoYclB9PIAbNP229AIUeH0vNNGJhjnZiqgIOKvs1EhzqAo= ;{id = 2854} +ENTRY_END + +; response to DNSKEY priming query +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR NOERROR +SECTION QUESTION +example.com. IN DNSKEY +SECTION ANSWER +example.com. 3600 IN DNSKEY 256 3 3 ALXLUsWqUrY3JYER3T4TBJII s70j+sDS/UT2QRp61SE7S3E EXopNXoFE73JLRmvpi/UrOO/Vz4Se 6wXv/CYCKjGw06U4WRgR YXcpEhJROyNapmdIKSx hOzfLVE1gqA0PweZR8d tY3aNQSRn3sPpwJr6Mi /PqQKAMMrZ9ckJpf1+b QMOOvxgzz2U1GS18b3y ZKcgTMEaJzd/GZYzi/B N2DzQ0MsrSwYXfsNLFO Bbs8PJMW4LYIxeeOe6rUgkWOF 7CC9Dh/dduQ1QrsJhmZAEFfd6ByYV+ ;{id = 2854 (zsk), size = 1688b} +example.com. 3600 IN RRSIG DNSKEY 3 2 3600 20070926134802 20070829134802 2854 example.com. MCwCFG1yhRNtTEa3Eno2zhVVuy2EJX3wAhQeLyUp6+UXcpC5qGNu9tkrTEgPUg== ;{id = 2854} +SECTION AUTHORITY +example.com. IN NS ns.example.com. +example.com. 3600 IN RRSIG NS 3 2 3600 20070926134150 20070829134150 2854 example.com. MC0CFQCN+qHdJxoI/2tNKwsb08pra/G7aAIUAWA5sDdJTbrXA1/3OaesGBAO3sI= ;{id = 2854} +SECTION ADDITIONAL +ns.example.com. IN A 1.2.3.4 +ns.example.com. 3600 IN RRSIG A 3 3 3600 20070926135752 20070829135752 2854 example.com. MC0CFQCMSWxVehgOQLoYclB9PIAbNP229AIUeH0vNNGJhjnZiqgIOKvs1EhzqAo= ;{id = 2854} +ENTRY_END + +; response to query of interest, when sent with EDNS DO +ENTRY_BEGIN +MATCH opcode qtype qname DO +ADJUST copy_id +REPLY QR AA DO NOERROR +SECTION QUESTION +www.example.com. IN A +SECTION ANSWER +www.example.com. IN A 10.20.30.40 +ns.example.com. 3600 IN RRSIG A 3 3 3600 20070926134150 20070829134150 2854 example.com. MC0CFQCQMyTjn7WWwpwAR1LlVeLpRgZGuQIUCcJDEkwAuzytTDRlYK7nIMwH1CM= ;{id = 2854} +SECTION AUTHORITY +example.com. IN NS ns.example.com. +example.com. 3600 IN RRSIG NS 3 2 3600 20070926134150 20070829134150 2854 example.com. MC0CFQCN+qHdJxoI/2tNKwsb08pra/G7aAIUAWA5sDdJTbrXA1/3OaesGBAO3sI= ;{id = 2854} +SECTION ADDITIONAL +ns.example.com. IN A 1.2.3.4 +www.example.com. 3600 IN RRSIG A 3 3 3600 20070926134150 20070829134150 2854 example.com. MC0CFC99iE9K5y2WNgI0gFvBWaTi9wm6AhUAoUqOpDtG5Zct+Qr9F3mSdnbc6V4= ;{id = 2854} +ENTRY_END + +; response to query of interest, when sent without DO +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR AA NOERROR +SECTION QUESTION +www.example.com. IN A +SECTION ANSWER +www.example.com. IN A 10.20.30.40 +SECTION AUTHORITY +example.com. IN NS ns.example.com. +SECTION ADDITIONAL +ns.example.com. IN A 1.2.3.4 +ENTRY_END +RANGE_END + +STEP 1 QUERY +ENTRY_BEGIN +REPLY RD DO +SECTION QUESTION +www.example.com. IN A +ENTRY_END + +; recursion happens here. +STEP 10 CHECK_ANSWER +ENTRY_BEGIN +MATCH all +REPLY QR RD RA NOERROR +SECTION QUESTION +www.example.com. IN A +SECTION ANSWER +www.example.com. IN A 10.20.30.40 +SECTION AUTHORITY +example.com. IN NS ns.example.com. +SECTION ADDITIONAL +ns.example.com. IN A 1.2.3.4 +ENTRY_END + +SCENARIO_END diff --git a/testdata/dns64_lookup.rpl b/testdata/dns64_lookup.rpl index 898d0d01a..327f7dfed 100644 --- a/testdata/dns64_lookup.rpl +++ b/testdata/dns64_lookup.rpl @@ -4,6 +4,8 @@ server: qname-minimisation: "no" module-config: "dns64 validator iterator" dns64-prefix: 64:ff9b::0/96 + dns64-ignore-aaaa: ip6ignore.example.com + dns64-ignore-aaaa: ip6only.example.com minimal-responses: no stub-zone: @@ -15,6 +17,7 @@ SCENARIO_BEGIN Test dns64 lookup and synthesis. ; normal A lookup should still succeed ; AAAA is synthesized if not present. ; AAAA if present, is passed through unchanged. +; AAAA if present (but configured to be ignored) but no A, AAAA is passed through unchanged. ; K.ROOT-SERVERS.NET. RANGE_BEGIN 0 200 @@ -145,11 +148,13 @@ MATCH opcode qtype qname ADJUST copy_id REPLY QR NOERROR SECTION QUESTION -broken.example.com. IN AAAA +ip6.example.com. IN AAAA SECTION ANSWER -; NO AAAA present +ip6.example.com. IN AAAA 1:2:3::4 SECTION AUTHORITY -example.com. IN SOA a. b. 1 2 3 4 5 +example.com. IN NS ns.example.com. +SECTION ADDITIONAL +ns.example.com. IN A 1.2.3.4 ENTRY_END ENTRY_BEGIN @@ -157,10 +162,9 @@ MATCH opcode qtype qname ADJUST copy_id REPLY QR NOERROR SECTION QUESTION -broken.example.com. IN A +ip6ignore.example.com. IN AAAA SECTION ANSWER -broken.example.com. IN A 5.6.7.8 -broken.example.com. IN A \# 3 030405 +ip6ignore.example.com. IN AAAA 1:2:3::4 SECTION AUTHORITY example.com. IN NS ns.example.com. SECTION ADDITIONAL @@ -172,15 +176,42 @@ MATCH opcode qtype qname ADJUST copy_id REPLY QR NOERROR SECTION QUESTION -ip6.example.com. IN AAAA +ip6ignore.example.com. IN A SECTION ANSWER -ip6.example.com. IN AAAA 1:2:3::4 +ip6ignore.example.com. IN A 5.6.7.8 +SECTION AUTHORITY +example.com. IN NS ns.example.com. +SECTION ADDITIONAL +ns.example.com. IN A 1.2.3.4 +ENTRY_END + +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR NOERROR +SECTION QUESTION +ip6only.example.com. IN AAAA +SECTION ANSWER +ip6only.example.com. IN AAAA 1:2:3::4 SECTION AUTHORITY example.com. IN NS ns.example.com. SECTION ADDITIONAL ns.example.com. IN A 1.2.3.4 ENTRY_END +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR NOERROR +SECTION QUESTION +ip6only.example.com. IN A +SECTION ANSWER +; NO A present +SECTION AUTHORITY +example.com. IN SOA a. b. 1 2 3 4 5 +ENTRY_END + + ENTRY_BEGIN MATCH opcode qtype qname ADJUST copy_id @@ -284,12 +315,12 @@ SECTION AUTHORITY 7.6.5.in-addr.arpa. IN NS ns.example.com. ENTRY_END -; synthesize from broken, malformed A records +; ignore AAAA and synthesize from A record 5.6.7.8 STEP 80 QUERY ENTRY_BEGIN REPLY RD SECTION QUESTION -broken.example.com. IN AAAA +ip6ignore.example.com. IN AAAA ENTRY_END ; recursion happens here. @@ -298,11 +329,36 @@ ENTRY_BEGIN MATCH all REPLY QR RD RA NOERROR SECTION QUESTION -broken.example.com. IN AAAA +ip6ignore.example.com. IN AAAA SECTION ANSWER +ip6ignore.example.com. IN AAAA 64:ff9b::506:708 SECTION AUTHORITY -example.com. IN SOA a. b. 1 2 3 4 5 +example.com. IN NS ns.example.com. +SECTION ADDITIONAL +ns.example.com. IN A 1.2.3.4 +ENTRY_END + +; try to ignore AAAA but no A record to synthesize, fallback to AAAA +STEP 100 QUERY +ENTRY_BEGIN +REPLY RD +SECTION QUESTION +ip6only.example.com. IN AAAA +ENTRY_END + +; recursion happens here. +STEP 110 CHECK_ANSWER +ENTRY_BEGIN +MATCH all +REPLY QR RD RA NOERROR +SECTION QUESTION +ip6only.example.com. IN AAAA +SECTION ANSWER +ip6only.example.com. IN AAAA 1:2:3::4 +SECTION AUTHORITY +example.com. IN NS ns.example.com. SECTION ADDITIONAL +ns.example.com. IN A 1.2.3.4 ENTRY_END SCENARIO_END diff --git a/testdata/ipset.tdir/ipset.conf b/testdata/ipset.tdir/ipset.conf index 7cc34912d..3a5eb7b64 100644 --- a/testdata/ipset.tdir/ipset.conf +++ b/testdata/ipset.tdir/ipset.conf @@ -18,6 +18,9 @@ stub-zone: stub-zone: name: "example.com." stub-addr: "127.0.0.1@@TOPORT@" +stub-zone: + name: "lookslikeexample.net." + stub-addr: "127.0.0.1@@TOPORT@" ipset: name-v4: atotallymadeupnamefor4 name-v6: atotallymadeupnamefor6 diff --git a/testdata/ipset.tdir/ipset.test b/testdata/ipset.tdir/ipset.test index 4dab457ba..bfe17b6e6 100644 --- a/testdata/ipset.tdir/ipset.test +++ b/testdata/ipset.tdir/ipset.test @@ -146,6 +146,29 @@ else exit 1 fi +echo "> dig lookslikeexample.net. AAAA" +dig @127.0.0.1 -p $UNBOUND_PORT lookslikeexample.net. AAAA | tee outfile +echo "> check answer" +if grep "::4" outfile; then + echo "OK" +else + echo "> cat logfiles" + cat fwd.log + cat unbound.log + echo "Not OK" + exit 1 +fi +echo "> check ipset" +if grep "ipset: add ::4 to atotallymadeupnamefor6 for lookslikeexample.net." unbound.log; then + echo "> cat logfiles" + cat fwd.log + cat unbound.log + echo "Not OK" + exit 1 +else + echo "ipset OK" +fi + echo "> cat logfiles" cat tap.log cat tap.errlog diff --git a/testdata/ipset.tdir/ipset.testns b/testdata/ipset.tdir/ipset.testns index 2b626e915..f67d77ed6 100644 --- a/testdata/ipset.tdir/ipset.testns +++ b/testdata/ipset.tdir/ipset.testns @@ -101,3 +101,13 @@ target.example.com. IN AAAA SECTION ANSWER target.example.com. IN AAAA ::3 ENTRY_END + +ENTRY_BEGIN +MATCH opcode qtype qname +REPLY QR AA NOERROR +ADJUST copy_id +SECTION QUESTION +lookslikeexample.net. IN AAAA +SECTION ANSWER +lookslikeexample.net. IN AAAA ::4 +ENTRY_END diff --git a/testdata/iter_ignore_empty.rpl b/testdata/iter_ignore_empty.rpl index c70dd7e8d..4b2f695b8 100644 --- a/testdata/iter_ignore_empty.rpl +++ b/testdata/iter_ignore_empty.rpl @@ -78,6 +78,18 @@ example2.com. IN NS ns2.example2.com. SECTION ADDITIONAL ns2.example2.com. IN A 1.2.3.5 ENTRY_END + +ENTRY_BEGIN +MATCH opcode subdomain +ADJUST copy_id copy_query +REPLY QR NOERROR +SECTION QUESTION +foo.com. IN NS +SECTION AUTHORITY +foo.com. IN NS ns.foo.com. +SECTION ADDITIONAL +ns.foo.com. IN A 1.2.3.5 +ENTRY_END RANGE_END ; ns.example.com. @@ -172,6 +184,27 @@ www.example.com. IN A SECTION ANSWER www.example.com. IN A 10.20.30.40 ENTRY_END + +; foo.com +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR AA NOERROR +SECTION QUESTION +www.foo.com. IN A +SECTION ANSWER +ENTRY_END + +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR AA NOERROR +SECTION QUESTION +ns.foo.com. IN AAAA +SECTION ANSWER +SECTION AUTHORITY +;foo.com. IN SOA ns2.foo.com root.foo.com 4 14400 3600 604800 3600 +ENTRY_END RANGE_END STEP 1 QUERY @@ -195,4 +228,21 @@ ENTRY_END ; wait for pending nameserver lookups. STEP 20 TRAFFIC +; Test that a nodata stays a nodata. +STEP 30 QUERY +ENTRY_BEGIN +REPLY RD +SECTION QUESTION +www.foo.com. IN A +ENTRY_END + +STEP 40 CHECK_ANSWER +ENTRY_BEGIN +MATCH all +REPLY QR RD RA NOERROR +SECTION QUESTION +www.foo.com. IN A +SECTION ANSWER +ENTRY_END + SCENARIO_END diff --git a/testdata/iter_scrub_rr_length.rpl b/testdata/iter_scrub_rr_length.rpl new file mode 100644 index 000000000..2ef73c2fe --- /dev/null +++ b/testdata/iter_scrub_rr_length.rpl @@ -0,0 +1,298 @@ +; config options +server: + target-fetch-policy: "0 0 0 0 0" + qname-minimisation: "no" + minimal-responses: no + rrset-roundrobin: no + ede: yes + log-servfail: yes + +stub-zone: + name: "." + stub-addr: 193.0.14.129 # K.ROOT-SERVERS.NET. +CONFIG_END + +SCENARIO_BEGIN Test scrub of RRs of inappropriate length + +; K.ROOT-SERVERS.NET. +RANGE_BEGIN 0 200 + ADDRESS 193.0.14.129 +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR NOERROR +SECTION QUESTION +. IN NS +SECTION ANSWER +. IN NS K.ROOT-SERVERS.NET. +SECTION ADDITIONAL +K.ROOT-SERVERS.NET. IN A 193.0.14.129 +ENTRY_END + +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR NOERROR +SECTION QUESTION +www.example.com. IN A +SECTION AUTHORITY +com. IN NS a.gtld-servers.net. +SECTION ADDITIONAL +a.gtld-servers.net. IN A 192.5.6.30 +ENTRY_END +RANGE_END + +; a.gtld-servers.net. +RANGE_BEGIN 0 200 + ADDRESS 192.5.6.30 +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR NOERROR +SECTION QUESTION +com. IN NS +SECTION ANSWER +com. IN NS a.gtld-servers.net. +SECTION ADDITIONAL +a.gtld-servers.net. IN A 192.5.6.30 +ENTRY_END + +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR NOERROR +SECTION QUESTION +www.example.com. IN A +SECTION AUTHORITY +example.com. IN NS ns.example.com. +SECTION ADDITIONAL +ns.example.com. IN A 1.2.3.4 +ENTRY_END +RANGE_END + +; ns.example.com. +RANGE_BEGIN 0 200 + ADDRESS 1.2.3.4 +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR NOERROR +SECTION QUESTION +example.com. IN NS +SECTION ANSWER +example.com. IN NS ns.example.com. +SECTION ADDITIONAL +ns.example.com. IN A 1.2.3.4 +ENTRY_END + +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR AA NOERROR +SECTION QUESTION +www.example.com. IN A +SECTION ANSWER +www.example.com. IN A 10.20.30.40 +www.example.com. IN A \# 3 030405 +SECTION AUTHORITY +example.com. IN NS ns.example.com. +SECTION ADDITIONAL +ns.example.com. IN A 1.2.3.4 +ENTRY_END + +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR AA NOERROR +SECTION QUESTION +www.example.com. IN AAAA +SECTION ANSWER +www.example.com. IN AAAA 2001:db8::1234 +www.example.com. IN AAAA \# 48 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F +SECTION AUTHORITY +example.com. IN NS ns.example.com. +SECTION ADDITIONAL +ns.example.com. IN A 1.2.3.4 +ENTRY_END + +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR AA NOERROR +SECTION QUESTION +broken1.example.com. IN A +SECTION ANSWER +broken1.example.com. IN A \# 3 030405 +broken1.example.com. IN A \# 3 030406 +SECTION AUTHORITY +example.com. IN NS ns.example.com. +SECTION ADDITIONAL +ns.example.com. IN A 1.2.3.4 +ENTRY_END + +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR AA NOERROR +SECTION QUESTION +broken1.example.com. IN AAAA +SECTION ANSWER +broken1.example.com. IN AAAA \# 48 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F +broken1.example.com. IN AAAA \# 48 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E30 +broken1.example.com. IN AAAA \# 48 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E31 +SECTION AUTHORITY +example.com. IN NS ns.example.com. +SECTION ADDITIONAL +ns.example.com. IN A 1.2.3.4 +ENTRY_END + +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR AA NOERROR +SECTION QUESTION +broken2.example.com. IN A +SECTION ANSWER +broken2.example.com. IN A 1.2.3.4 +broken2.example.com. IN A \# 3 030405 +broken2.example.com. IN A 1.2.3.5 +broken2.example.com. IN A \# 3 030406 +SECTION AUTHORITY +example.com. IN NS ns.example.com. +SECTION ADDITIONAL +ns.example.com. IN A \# 3 030407 +ns.example.com. IN A 1.2.3.6 +ns.example.com. IN A \# 3 030408 +ns.example.com. IN A \# 3 030409 +ns.example.com. IN A 1.2.3.7 +ENTRY_END +RANGE_END + +STEP 1 QUERY +ENTRY_BEGIN +REPLY RD +SECTION QUESTION +www.example.com. IN A +ENTRY_END + +STEP 10 CHECK_ANSWER +ENTRY_BEGIN +MATCH all +REPLY QR RD RA NOERROR +SECTION QUESTION +www.example.com. IN A +SECTION ANSWER +www.example.com. IN A 10.20.30.40 +SECTION AUTHORITY +example.com. IN NS ns.example.com. +SECTION ADDITIONAL +ns.example.com. IN A 1.2.3.4 +ENTRY_END + +STEP 20 QUERY +ENTRY_BEGIN +REPLY RD +SECTION QUESTION +www.example.com. IN AAAA +ENTRY_END + +STEP 30 CHECK_ANSWER +ENTRY_BEGIN +MATCH all +REPLY QR RD RA NOERROR +SECTION QUESTION +www.example.com. IN AAAA +SECTION ANSWER +www.example.com. IN AAAA 2001:db8::1234 +SECTION AUTHORITY +example.com. IN NS ns.example.com. +SECTION ADDITIONAL +ns.example.com. IN A 1.2.3.4 +ENTRY_END + +STEP 40 QUERY +ENTRY_BEGIN +REPLY RD +SECTION QUESTION +broken1.example.com. IN A +ENTRY_END + +STEP 50 CHECK_ANSWER +ENTRY_BEGIN +MATCH all +REPLY QR RD RA NOERROR +SECTION QUESTION +broken1.example.com. IN A +SECTION ANSWER +SECTION AUTHORITY +example.com. IN NS ns.example.com. +SECTION ADDITIONAL +ns.example.com. IN A 1.2.3.4 +ENTRY_END + +STEP 60 QUERY +ENTRY_BEGIN +REPLY RD +SECTION QUESTION +broken1.example.com. IN AAAA +ENTRY_END + +STEP 70 CHECK_ANSWER +ENTRY_BEGIN +MATCH all +REPLY QR RD RA NOERROR +SECTION QUESTION +broken1.example.com. IN AAAA +SECTION ANSWER +SECTION AUTHORITY +example.com. IN NS ns.example.com. +SECTION ADDITIONAL +ns.example.com. IN A 1.2.3.4 +ENTRY_END + +STEP 80 QUERY +ENTRY_BEGIN +REPLY RD +SECTION QUESTION +broken2.example.com. IN A +ENTRY_END + +STEP 90 CHECK_ANSWER +ENTRY_BEGIN +MATCH all +REPLY QR RD RA NOERROR +SECTION QUESTION +broken2.example.com. IN A +SECTION ANSWER +broken2.example.com. IN A 1.2.3.4 +broken2.example.com. IN A 1.2.3.5 +SECTION AUTHORITY +example.com. IN NS ns.example.com. +SECTION ADDITIONAL +ns.example.com. IN A 1.2.3.6 +ns.example.com. IN A 1.2.3.7 +ENTRY_END + +STEP 100 QUERY +ENTRY_BEGIN +REPLY RD CD DO +SECTION QUESTION +www.example.com. IN A +ENTRY_END + +STEP 110 CHECK_ANSWER +ENTRY_BEGIN +MATCH all ede=0 +REPLY QR RD CD RA DO NOERROR +SECTION QUESTION +www.example.com. IN A +SECTION ANSWER +www.example.com. IN A 10.20.30.40 +SECTION AUTHORITY +example.com. IN NS ns.example.com. +SECTION ADDITIONAL +ns.example.com. IN A 1.2.3.6 +ns.example.com. IN A 1.2.3.7 +ENTRY_END + +SCENARIO_END diff --git a/testdata/pymod.tdir/pymod.py b/testdata/pymod.tdir/pymod.py index 1eb7af5b1..f1e988b39 100644 --- a/testdata/pymod.tdir/pymod.py +++ b/testdata/pymod.tdir/pymod.py @@ -37,12 +37,7 @@ import os def init(id, cfg): - scripts=[] - s = cfg.python_script - while s != None: - scripts.append(s.str) - s = s.next - log_info("pythonmod: init called, module id is %d port: %d script: %s" % (id, cfg.port, scripts)) + log_info("pythonmod: init called, module id is %d port: %d script: %s" % (id, cfg.port, mod_env['script'])) return True def deinit(id): diff --git a/testdata/pymod_thread.tdir/pymod_thread.py b/testdata/pymod_thread.tdir/pymod_thread.py index 30c258875..58304ab46 100644 --- a/testdata/pymod_thread.tdir/pymod_thread.py +++ b/testdata/pymod_thread.tdir/pymod_thread.py @@ -37,7 +37,7 @@ import os def init(id, cfg): - log_info("pythonmod: init called, module id is %d port: %d script: %s" % (id, cfg.port, cfg.python_script)) + log_info("pythonmod: init called, module id is %d port: %d script: %s" % (id, cfg.port, mod_env['script'])) return True def deinit(id): diff --git a/testdata/root_zonemd.tdir/root_zonemd.conf b/testdata/root_zonemd.tdir/root_zonemd.conf new file mode 100644 index 000000000..befb4fbe9 --- /dev/null +++ b/testdata/root_zonemd.tdir/root_zonemd.conf @@ -0,0 +1,34 @@ +server: + verbosity: 7 + # num-threads: 1 + interface: 127.0.0.1 + port: @PORT@ + use-syslog: no + directory: "" + pidfile: "unbound.pid" + chroot: "" + username: "" + do-not-query-localhost: no + # for the test, so that DNSSEC verification works. + #val-override-date: 20230929090000 + trust-anchor: ". DS 20326 8 2 E06D44B80B8F1D39A95C0B0D7C65D08458E880409BBC683457104237C7F8EC8D" + +remote-control: + control-enable: yes + control-interface: @CONTROL_PATH@/controlpipe.@CONTROL_PID@ + control-use-cert: no + +# for the test, an upstream server in the test setup. +stub-zone: + name: "." + stub-addr: 127.0.0.1@@TOPORT@ + +# hyperlocal root zone +auth-zone: + name: "." + fallback-enabled: yes + for-downstream: no + for-upstream: yes + zonefile: "root.zone" + zonemd-check: yes + zonemd-reject-absence: yes diff --git a/testdata/root_zonemd.tdir/root_zonemd.dsc b/testdata/root_zonemd.tdir/root_zonemd.dsc new file mode 100644 index 000000000..8015ac2d1 --- /dev/null +++ b/testdata/root_zonemd.tdir/root_zonemd.dsc @@ -0,0 +1,16 @@ +BaseName: root_zonemd +Version: 1.0 +Description: ZONEMD check for root zone +CreationDate: Fri 29 Sep 09:00:00 CEST 2023 +Maintainer: dr. W.C.A. Wijngaards +Category: +Component: +CmdDepends: +Depends: +Help: +Pre: root_zonemd.pre +Post: root_zonemd.post +Test: root_zonemd.test +AuxFiles: +Passed: +Failure: diff --git a/testdata/root_zonemd.tdir/root_zonemd.post b/testdata/root_zonemd.tdir/root_zonemd.post new file mode 100644 index 000000000..a28599faf --- /dev/null +++ b/testdata/root_zonemd.tdir/root_zonemd.post @@ -0,0 +1,14 @@ +# #-- root_zonemd.post --# +# source the master var file when it's there +[ -f ../.tpkg.var.master ] && source ../.tpkg.var.master +# source the test var file when it's there +[ -f .tpkg.var.test ] && source .tpkg.var.test +# +# do your teardown here +. ../common.sh +echo "> cat logfiles" +cat fwd.log +cat unbound.log +kill_pid $FWD_PID +kill_pid $UNBOUND_PID +rm -f $CONTROL_PATH/controlpipe.$CONTROL_PID diff --git a/testdata/root_zonemd.tdir/root_zonemd.pre b/testdata/root_zonemd.tdir/root_zonemd.pre new file mode 100644 index 000000000..fe369bb20 --- /dev/null +++ b/testdata/root_zonemd.tdir/root_zonemd.pre @@ -0,0 +1,50 @@ +# #-- root_zonemd.pre--# +# source the master var file when it's there +[ -f ../.tpkg.var.master ] && source ../.tpkg.var.master +# use .tpkg.var.test for in test variable passing +[ -f .tpkg.var.test ] && source .tpkg.var.test + +. ../common.sh + +# attempt to download the root zone +from=k.root-servers.net +dig @$from . AXFR > root.txt +if test $? -ne 0; then + echo "could not fetch root zone" + skip_test "could not fetch root zone" +fi +grep " SOA " root.txt | head -1 > root.soa +cat root.soa >> root.zone +grep -v " SOA " root.txt >> root.zone +echo "fetched root.zone" +ls -l root.zone +cat root.soa + +get_random_port 2 +UNBOUND_PORT=$RND_PORT +FWD_PORT=$(($RND_PORT + 1)) +echo "UNBOUND_PORT=$UNBOUND_PORT" >> .tpkg.var.test +echo "FWD_PORT=$FWD_PORT" >> .tpkg.var.test + +# start forwarder +get_ldns_testns +$LDNS_TESTNS -p $FWD_PORT root_zonemd.testns >fwd.log 2>&1 & +FWD_PID=$! +echo "FWD_PID=$FWD_PID" >> .tpkg.var.test + +# make config file +CONTROL_PATH=/tmp +CONTROL_PID=$$ +sed -e 's/@PORT\@/'$UNBOUND_PORT'/' -e 's/@TOPORT\@/'$FWD_PORT'/' -e 's?@CONTROL_PATH\@?'$CONTROL_PATH'?' -e 's/@CONTROL_PID@/'$CONTROL_PID'/' < root_zonemd.conf > ub.conf +# start unbound in the background +PRE="../.." +$PRE/unbound -d -c ub.conf >unbound.log 2>&1 & +UNBOUND_PID=$! +echo "UNBOUND_PID=$UNBOUND_PID" >> .tpkg.var.test +echo "CONTROL_PATH=$CONTROL_PATH" >> .tpkg.var.test +echo "CONTROL_PID=$CONTROL_PID" >> .tpkg.var.test + +cat .tpkg.var.test +wait_ldns_testns_up fwd.log +wait_unbound_up unbound.log + diff --git a/testdata/root_zonemd.tdir/root_zonemd.test b/testdata/root_zonemd.tdir/root_zonemd.test new file mode 100644 index 000000000..da64ab6e9 --- /dev/null +++ b/testdata/root_zonemd.tdir/root_zonemd.test @@ -0,0 +1,51 @@ +# #-- root_zonemd.test --# +# source the master var file when it's there +[ -f ../.tpkg.var.master ] && source ../.tpkg.var.master +# use .tpkg.var.test for in test variable passing +[ -f .tpkg.var.test ] && source .tpkg.var.test + +PRE="../.." +# do the test +echo "> dig www.example.com." +dig @localhost -p $UNBOUND_PORT . SOA | tee outfile +echo "> check answer" +if grep root-servers outfile | grep "nstld.verisign-grs.com"; then + echo "OK" +else + echo "Not OK" + exit 1 +fi + +echo "> unbound-control status" +$PRE/unbound-control -c ub.conf status +if test $? -ne 0; then + echo "wrong exit value." + exit 1 +else + echo "exit value: OK" +fi + +# This is the output when an unsupported algorithm is used. +if grep "auth zone . ZONEMD unsupported algorithm" unbound.log; then + echo "OK" +else + echo "ZONEMD verification not OK" + exit 1 +fi + +echo "> unbound-control auth_zone_reload ." +$PRE/unbound-control -c ub.conf auth_zone_reload . 2>&1 | tee outfile +if test $? -ne 0; then + echo "wrong exit value." + exit 1 +fi +# The output of the reload can be checked. +#echo "> check unbound-control output" +#if grep "example.com: ZONEMD verification successful" outfile; then + #echo "OK" +#else + #echo "Not OK" + #exit 1 +#fi + +exit 0 diff --git a/testdata/root_zonemd.tdir/root_zonemd.testns b/testdata/root_zonemd.tdir/root_zonemd.testns new file mode 100644 index 000000000..d538f2215 --- /dev/null +++ b/testdata/root_zonemd.tdir/root_zonemd.testns @@ -0,0 +1,9 @@ +# reply to everything +ENTRY_BEGIN +MATCH opcode +ADJUST copy_id copy_query +REPLY QR SERVFAIL +SECTION QUESTION +example.com. IN SOA +SECTION ANSWER +ENTRY_END diff --git a/testdata/rpz_cached_cname.rpl b/testdata/rpz_cached_cname.rpl new file mode 100644 index 000000000..198b94631 --- /dev/null +++ b/testdata/rpz_cached_cname.rpl @@ -0,0 +1,122 @@ +; config options +server: + module-config: "respip validator iterator" + target-fetch-policy: "0 0 0 0 0" + qname-minimisation: no + rrset-roundrobin: no + access-control: 192.0.0.0/8 allow + +rpz: + name: "rpz.example.com" + rpz-log: yes + rpz-log-name: "rpz.example.com" + zonefile: +TEMPFILE_NAME rpz.example.com +TEMPFILE_CONTENTS rpz.example.com +rpz.example.com. 3600 IN SOA ns.rpz.example.com. hostmaster.rpz.example.com. 1 3600 900 86400 3600 +rpz.example.com. 3600 IN NS ns.rpz.example.net. +a.foo.rpz.example.com. 120 IN A 10.99.99.99 +TEMPFILE_END + +stub-zone: + name: "." + stub-addr: 10.20.30.40 + +CONFIG_END + +SCENARIO_BEGIN Test RPZ with cached CNAME to A record + +RANGE_BEGIN 0 100 + ADDRESS 10.20.30.40 + +ENTRY_BEGIN +MATCH opcode qname qtype +ADJUST copy_id +REPLY QR NOERROR AA +SECTION QUESTION +. IN NS +SECTION ANSWER +. IN NS ns. +SECTION ADDITIONAL +ns. IN NS 10.20.30.40 +ENTRY_END + +ENTRY_BEGIN +MATCH opcode qname qtype +ADJUST copy_id +REPLY QR NOERROR AA +SECTION QUESTION +b.foo. IN A +SECTION ANSWER +b.foo. 30 CNAME a.foo. +a.foo. 30 A 1.2.3.4 +ENTRY_END + +ENTRY_BEGIN +MATCH opcode qname qtype +ADJUST copy_id +REPLY QR NOERROR AA +SECTION QUESTION +a.foo. IN A +SECTION ANSWER +a.foo. A 1.2.3.4 +ENTRY_END + +RANGE_END + +STEP 10 QUERY +ENTRY_BEGIN +REPLY RD +SECTION QUESTION +a.foo. IN A +ENTRY_END + +STEP 20 CHECK_ANSWER +ENTRY_BEGIN +MATCH all +REPLY QR RD RA AA NOERROR +SECTION QUESTION +a.foo. IN A +SECTION ANSWER +a.foo. 120 A 10.99.99.99 +ENTRY_END + +STEP 30 QUERY +ENTRY_BEGIN +REPLY RD +SECTION QUESTION +b.foo. IN A +ENTRY_END + +STEP 40 CHECK_ANSWER +ENTRY_BEGIN +MATCH all +REPLY QR RD RA AA NOERROR +SECTION QUESTION +b.foo. IN A +SECTION ANSWER +b.foo. 30 CNAME a.foo. +a.foo. 120 A 10.99.99.99 +ENTRY_END + +STEP 50 TIME_PASSES ELAPSE 3 + +STEP 60 QUERY +ENTRY_BEGIN +REPLY RD +SECTION QUESTION +b.foo. IN A +ENTRY_END + +STEP 70 CHECK_ANSWER +ENTRY_BEGIN +MATCH all +REPLY QR RD RA AA NOERROR +SECTION QUESTION +b.foo. IN A +SECTION ANSWER +b.foo. 30 CNAME a.foo. +a.foo. 120 A 10.99.99.99 +ENTRY_END + +SCENARIO_END diff --git a/testdata/rpz_nsdname.rpl b/testdata/rpz_nsdname.rpl index 1c678cc13..a4e9bb31d 100644 --- a/testdata/rpz_nsdname.rpl +++ b/testdata/rpz_nsdname.rpl @@ -225,6 +225,36 @@ ENTRY_END RANGE_END +; dd. ------------------------------------------------------------------------ +RANGE_BEGIN 0 100 + ADDRESS 8.8.3.8 + +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR NOERROR +SECTION QUESTION +dd. IN NS +SECTION ANSWER +dd. IN NS ns1.dd. +SECTION ADDITIONAL +ns1.dd. IN A 8.8.3.8 +ENTRY_END + +ENTRY_BEGIN +MATCH opcode subdomain +ADJUST copy_id copy_query +REPLY QR NOERROR +SECTION QUESTION +gotham.dd. IN A +SECTION AUTHORITY +gotham.dd. IN NS ns1.gotham.dd. +SECTION ADDITIONAL +ns1.gotham.dd. IN A 192.0.3.1 +ENTRY_END + +RANGE_END + ; ff. ------------------------------------------------------------------------ RANGE_BEGIN 0 100 ADDRESS 8.8.6.8 @@ -303,6 +333,22 @@ ENTRY_END RANGE_END +; ns1.gotham.dd. ------------------------------------------------------------- +RANGE_BEGIN 0 100 + ADDRESS 192.0.3.1 + +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR AA NOERROR +SECTION QUESTION +gotham.dd. IN A +SECTION ANSWER +gotham.dd. IN A 192.0.3.2 +ENTRY_END + +RANGE_END + ; ns1.gotham.ff. ------------------------------------------------------------- RANGE_BEGIN 0 100 ADDRESS 192.0.5.1 @@ -387,4 +433,39 @@ SECTION ANSWER gotham.ff. IN A 127.0.0.1 ENTRY_END +STEP 40 QUERY +ENTRY_BEGIN +REPLY RD +SECTION QUESTION +gotham.dd. IN A +ENTRY_END + +; should come back truncated because TCP is required. +STEP 41 CHECK_ANSWER +ENTRY_BEGIN +MATCH all +REPLY QR RD RA TC NOERROR +SECTION QUESTION +gotham.dd. IN A +SECTION ANSWER +ENTRY_END + +STEP 42 QUERY +ENTRY_BEGIN +MATCH TCP +REPLY RD +SECTION QUESTION +gotham.dd. IN A +ENTRY_END + +STEP 43 CHECK_ANSWER +ENTRY_BEGIN +MATCH all TCP +REPLY QR RD RA NOERROR +SECTION QUESTION +gotham.dd. IN A +SECTION ANSWER +gotham.dd. IN A 192.0.3.2 +ENTRY_END + SCENARIO_END diff --git a/testdata/subnet_prezero.crpl b/testdata/subnet_prezero.crpl new file mode 100644 index 000000000..22cdfffb0 --- /dev/null +++ b/testdata/subnet_prezero.crpl @@ -0,0 +1,155 @@ +; subnet unit test +server: + trust-anchor-signaling: no + send-client-subnet: 1.2.3.4 + send-client-subnet: 1.2.3.5 + target-fetch-policy: "0 0 0 0 0" + module-config: "subnetcache validator iterator" + qname-minimisation: no + minimal-responses: no + +stub-zone: + name: "example.com" + stub-addr: 1.2.3.4 +CONFIG_END + +SCENARIO_BEGIN Test subnetcache source prefix zero from client. +; In RFC7871 section-7.1.2 (para. 2). +; It says that the recursor must send no EDNS subnet or its own address +; in the EDNS subnet to the upstream server. And use that answer for the +; source prefix length zero query. That type of query is for privacy. +; The authority server is then going to use the resolver's IP, if any, to +; tailor the answer to the query source address. + +; ns.example.com +RANGE_BEGIN 0 100 + ADDRESS 1.2.3.4 + +; reply with 0.0.0.0/0 in reply +; For the test the answers for 0.0.0.0/0 queries are SERVFAIL, the normal +; answers are NOERROR. +ENTRY_BEGIN +MATCH opcode qtype qname ednsdata +ADJUST copy_id +REPLY QR AA DO SERVFAIL +SECTION QUESTION +www.example.com. IN A +SECTION ANSWER +www.example.com. IN CNAME star.c10r.example.com. +SECTION ADDITIONAL +HEX_EDNSDATA_BEGIN + 00 08 00 04 ; OPCODE=subnet, optlen + 00 01 00 00 ; ip4, scope 0, source 0 + ; 0.0.0.0/0 +HEX_EDNSDATA_END +ENTRY_END + +; reply without subnet +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR AA DO NOERROR +SECTION QUESTION +www.example.com. IN A +SECTION ANSWER +www.example.com. IN CNAME star.c10r.example.com. +ENTRY_END + +; delegation answer for c10r.example.com, with subnet /0 +ENTRY_BEGIN +MATCH opcode subdomain ednsdata +ADJUST copy_id copy_query +REPLY QR DO SERVFAIL +SECTION QUESTION +c10r.example.com. IN NS +SECTION AUTHORITY +c10r.example.com. IN NS ns.c10r.example.com. +SECTION ADDITIONAL +ns.c10r.example.com. IN A 1.2.3.5 +HEX_EDNSDATA_BEGIN + 00 08 00 04 ; OPCODE=subnet, optlen + 00 01 00 00 ; ip4, scope 0, source 0 + ; 0.0.0.0/0 +HEX_EDNSDATA_END +ENTRY_END + +; delegation answer for c10r.example.com, without subnet +ENTRY_BEGIN +MATCH opcode subdomain +ADJUST copy_id copy_query +REPLY QR DO NOERROR +SECTION QUESTION +c10r.example.com. IN NS +SECTION AUTHORITY +c10r.example.com. IN NS ns.c10r.example.com. +SECTION ADDITIONAL +ns.c10r.example.com. IN A 1.2.3.5 +ENTRY_END +RANGE_END + +; ns.c10r.example.com +RANGE_BEGIN 0 100 + ADDRESS 1.2.3.5 + +; reply with 0.0.0.0/0 in reply +ENTRY_BEGIN +MATCH opcode qtype qname ednsdata +ADJUST copy_id +REPLY QR AA DO SERVFAIL +SECTION QUESTION +star.c10r.example.com. IN A +SECTION ANSWER +star.c10r.example.com. IN A 1.2.3.6 +SECTION ADDITIONAL +HEX_EDNSDATA_BEGIN + 00 08 00 04 ; OPCODE=subnet, optlen + 00 01 00 00 ; ip4, scope 0, source 0 + ; 0.0.0.0/0 +HEX_EDNSDATA_END +ENTRY_END + +; reply without subnet +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR AA DO NOERROR +SECTION QUESTION +star.c10r.example.com. IN A +SECTION ANSWER +star.c10r.example.com. IN A 1.2.3.6 +ENTRY_END +RANGE_END + +; ask for www.example.com +; server answers with CNAME to a delegation, that then +; returns a /24 answer. +STEP 1 QUERY +ENTRY_BEGIN +REPLY RD DO +SECTION QUESTION +www.example.com. IN A +SECTION ADDITIONAL +HEX_EDNSDATA_BEGIN + 00 08 00 04 ; OPCODE=subnet, optlen + 00 01 00 00 ; ip4, scope 0, source 0 + ; 0.0.0.0/0 +HEX_EDNSDATA_END +ENTRY_END + +STEP 10 CHECK_ANSWER +ENTRY_BEGIN +MATCH all ednsdata +REPLY QR RD RA DO NOERROR +SECTION QUESTION +www.example.com. IN A +SECTION ANSWER +www.example.com. IN CNAME star.c10r.example.com. +star.c10r.example.com. IN A 1.2.3.6 +SECTION ADDITIONAL +HEX_EDNSDATA_BEGIN + 00 08 00 04 ; OPCODE=subnet, optlen + 00 01 00 00 ; ip4, scope 0, source 0 + ; 0.0.0.0/0 +HEX_EDNSDATA_END +ENTRY_END +SCENARIO_END diff --git a/testdata/val_scrub_rr_length.rpl b/testdata/val_scrub_rr_length.rpl new file mode 100644 index 000000000..0219b918e --- /dev/null +++ b/testdata/val_scrub_rr_length.rpl @@ -0,0 +1,164 @@ +; config options +; The island of trust is at example.com +server: + trust-anchor: "example.com. IN DS 55566 8 2 9c148338951ce1c3b5cd3da532f3d90dfcf92595148022f2c2fd98e5deee90af" + val-override-date: "20070916134226" + target-fetch-policy: "0 0 0 0 0" + qname-minimisation: "no" + trust-anchor-signaling: no + minimal-responses: no + rrset-roundrobin: no + ede: yes + log-servfail: yes + +stub-zone: + name: "." + stub-addr: 193.0.14.129 # K.ROOT-SERVERS.NET. +CONFIG_END + +SCENARIO_BEGIN Test validator with scrub of RR for inappropriate length + +; K.ROOT-SERVERS.NET. +RANGE_BEGIN 0 100 + ADDRESS 193.0.14.129 +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR NOERROR +SECTION QUESTION +. IN NS +SECTION ANSWER +. IN NS K.ROOT-SERVERS.NET. +SECTION ADDITIONAL +K.ROOT-SERVERS.NET. IN A 193.0.14.129 +ENTRY_END + +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR NOERROR +SECTION QUESTION +www.example.com. IN A +SECTION AUTHORITY +com. IN NS a.gtld-servers.net. +SECTION ADDITIONAL +a.gtld-servers.net. IN A 192.5.6.30 +ENTRY_END +RANGE_END + +; a.gtld-servers.net. +RANGE_BEGIN 0 100 + ADDRESS 192.5.6.30 +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR NOERROR +SECTION QUESTION +com. IN NS +SECTION ANSWER +com. IN NS a.gtld-servers.net. +SECTION ADDITIONAL +a.gtld-servers.net. IN A 192.5.6.30 +ENTRY_END + +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR NOERROR +SECTION QUESTION +www.example.com. IN A +SECTION AUTHORITY +example.com. IN NS ns.example.com. +SECTION ADDITIONAL +ns.example.com. IN A 1.2.3.4 +ENTRY_END +RANGE_END + +; ns.example.com. +RANGE_BEGIN 0 100 + ADDRESS 1.2.3.4 +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR NOERROR +SECTION QUESTION +example.com. IN NS +SECTION ANSWER +example.com. IN NS ns.example.com. +example.com. 3600 IN RRSIG NS 8 2 3600 20070926134150 20070829134150 55566 example.com. cHdLVCzujUQs6b67c1SmCX+/br4tgOg86Gj/R/x+PKUQmWHyeVwBSTlJuLOHbca3CQoyIQc+V2ilK6fjwjbY/dLk4uOlux8L+Zn7HsUXSOwJPIjsM3LuTa8CYDMvYhOP7KGR+vNpJVSsQ25pyDn6Rzsdl3E7DAf7uSkPV8VJwa8= +SECTION ADDITIONAL +ns.example.com. IN A 1.2.3.4 +ns.example.com. 3600 IN RRSIG A 8 3 3600 20070926134150 20070829134150 55566 example.com. PBwNifMNxTXlDorHX1neq1wUhWLmqk+PZ+PBZCI5BJAmakdgOXdLQiVqlKaErJyA/4uN+99fUf6/DqxwgxL8FIPdBkxMOTJaKrCFjEhL6qozTd3+DI6qFJPgTm1lrkpvb9W72MtK2vxAyT5I/bG2SWKdpzOaQXysbDb2hnxq3as= +ENTRY_END + +; response to DNSKEY priming query +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR NOERROR +SECTION QUESTION +example.com. IN DNSKEY +SECTION ANSWER +example.com. IN DNSKEY 256 3 8 AwEAAdug/L739i0mgN2nuK/bhxu3wFn5Ud9nK2+XUmZQlPUEZUC5YZvm1rfMmEWTGBn87fFxEu/kjFZHJ55JLzqsbbpVHLbmKCTT2gYR2FV2WDKROGKuYbVkJIXdKAjJ0ONuK507NinYvlWXIoxHn22KAWOd9wKgSTNHBlmGkX+ts3hh ;{id = 55566 (zsk), size = 1024b} +example.com. 3600 IN RRSIG DNSKEY 8 2 3600 20070926134150 20070829134150 55566 example.com. Ni7Q17l2dzKcAnHdU3Mycpdwo0I6qgGxRvBhBNI43xIUFHJpgKpbeMFxKvVTkbwHyMPMIuHmOaC82IBhOpGD10SExVh4erQhWS3Hvl+m4Cwl3WI9N+AW6CTB9yj+d4xzX3bHjjBt6MSk4bU8ABR7qIoAjgjY7zdtUDWQlaM+d18= +SECTION AUTHORITY +example.com. IN NS ns.example.com. +example.com. 3600 IN RRSIG NS 8 2 3600 20070926134150 20070829134150 55566 example.com. cHdLVCzujUQs6b67c1SmCX+/br4tgOg86Gj/R/x+PKUQmWHyeVwBSTlJuLOHbca3CQoyIQc+V2ilK6fjwjbY/dLk4uOlux8L+Zn7HsUXSOwJPIjsM3LuTa8CYDMvYhOP7KGR+vNpJVSsQ25pyDn6Rzsdl3E7DAf7uSkPV8VJwa8= +SECTION ADDITIONAL +ns.example.com. IN A 1.2.3.4 +ns.example.com. 3600 IN RRSIG A 8 3 3600 20070926134150 20070829134150 55566 example.com. PBwNifMNxTXlDorHX1neq1wUhWLmqk+PZ+PBZCI5BJAmakdgOXdLQiVqlKaErJyA/4uN+99fUf6/DqxwgxL8FIPdBkxMOTJaKrCFjEhL6qozTd3+DI6qFJPgTm1lrkpvb9W72MtK2vxAyT5I/bG2SWKdpzOaQXysbDb2hnxq3as= +ENTRY_END + +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR AA NOERROR +SECTION QUESTION +ns.example.com. IN AAAA +SECTION AUTHORITY +example.com. IN NS ns.example.com. +example.com. 3600 IN RRSIG NS 8 2 3600 20070926134150 20070829134150 55566 example.com. cHdLVCzujUQs6b67c1SmCX+/br4tgOg86Gj/R/x+PKUQmWHyeVwBSTlJuLOHbca3CQoyIQc+V2ilK6fjwjbY/dLk4uOlux8L+Zn7HsUXSOwJPIjsM3LuTa8CYDMvYhOP7KGR+vNpJVSsQ25pyDn6Rzsdl3E7DAf7uSkPV8VJwa8= +SECTION ADDITIONAL +ns.example.com. IN A 1.2.3.4 +ns.example.com. 3600 IN RRSIG A 8 3 3600 20070926134150 20070829134150 55566 example.com. PBwNifMNxTXlDorHX1neq1wUhWLmqk+PZ+PBZCI5BJAmakdgOXdLQiVqlKaErJyA/4uN+99fUf6/DqxwgxL8FIPdBkxMOTJaKrCFjEhL6qozTd3+DI6qFJPgTm1lrkpvb9W72MtK2vxAyT5I/bG2SWKdpzOaQXysbDb2hnxq3as= +ENTRY_END + +; response to query of interest +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR NOERROR +SECTION QUESTION +www.example.com. IN A +SECTION ANSWER +www.example.com. IN A 10.20.30.40 +www.example.com. IN A \# 5 0102030405 +; RRSIG includes the malformed record. +www.example.com. 3600 IN RRSIG A 8 3 3600 20070926134150 20070829134150 55566 example.com. W4WFu9B81uRvp3Dj8uLIscypznKWuLuKrZqVg1on5/45/3/xyjHvj3TjTL3gruWFXPiQpldvOstXLZ5eN3OpqILdkVey0eqVATujpHwIruY6GWztVx5WptmFfK6E6zzshZ3RmAARqq/czQ+IZli2A9xixdY2H0o1dSU6gohEjjE= +SECTION AUTHORITY +example.com. IN NS ns.example.com. +example.com. 3600 IN RRSIG NS 8 2 3600 20070926134150 20070829134150 55566 example.com. cHdLVCzujUQs6b67c1SmCX+/br4tgOg86Gj/R/x+PKUQmWHyeVwBSTlJuLOHbca3CQoyIQc+V2ilK6fjwjbY/dLk4uOlux8L+Zn7HsUXSOwJPIjsM3LuTa8CYDMvYhOP7KGR+vNpJVSsQ25pyDn6Rzsdl3E7DAf7uSkPV8VJwa8= +SECTION ADDITIONAL +ns.example.com. IN A 1.2.3.4 +ns.example.com. 3600 IN RRSIG A 8 3 3600 20070926134150 20070829134150 55566 example.com. PBwNifMNxTXlDorHX1neq1wUhWLmqk+PZ+PBZCI5BJAmakdgOXdLQiVqlKaErJyA/4uN+99fUf6/DqxwgxL8FIPdBkxMOTJaKrCFjEhL6qozTd3+DI6qFJPgTm1lrkpvb9W72MtK2vxAyT5I/bG2SWKdpzOaQXysbDb2hnxq3as= +ENTRY_END +RANGE_END + +STEP 1 QUERY +ENTRY_BEGIN +REPLY RD DO +SECTION QUESTION +www.example.com. IN A +ENTRY_END + +; recursion happens here. +STEP 10 CHECK_ANSWER +ENTRY_BEGIN +MATCH all ede=0 +REPLY QR RD RA DO SERVFAIL +SECTION QUESTION +www.example.com. IN A +SECTION ANSWER +ENTRY_END + +SCENARIO_END diff --git a/util/config_file.c b/util/config_file.c index 454096342..31ae8c808 100644 --- a/util/config_file.c +++ b/util/config_file.c @@ -271,6 +271,7 @@ config_create(void) cfg->val_permissive_mode = 0; cfg->aggressive_nsec = 1; cfg->ignore_cd = 0; + cfg->disable_edns_do = 0; cfg->serve_expired = 0; cfg->serve_expired_ttl = 0; cfg->serve_expired_ttl_reset = 0; @@ -381,6 +382,7 @@ config_create(void) #ifdef USE_CACHEDB if(!(cfg->cachedb_backend = strdup("testframe"))) goto error_exit; if(!(cfg->cachedb_secret = strdup("default"))) goto error_exit; + cfg->cachedb_no_store = 0; #ifdef USE_REDIS if(!(cfg->redis_server_host = strdup("127.0.0.1"))) goto error_exit; cfg->redis_server_path = NULL; @@ -388,6 +390,7 @@ config_create(void) cfg->redis_timeout = 100; cfg->redis_server_port = 6379; cfg->redis_expire_records = 0; + cfg->redis_logical_db = 0; #endif /* USE_REDIS */ #endif /* USE_CACHEDB */ #ifdef USE_IPSET @@ -690,6 +693,7 @@ int config_set_option(struct config_file* cfg, const char* opt, else S_YNO("val-permissive-mode:", val_permissive_mode) else S_YNO("aggressive-nsec:", aggressive_nsec) else S_YNO("ignore-cd-flag:", ignore_cd) + else S_YNO("disable-edns-do:", disable_edns_do) else if(strcmp(opt, "serve-expired:") == 0) { IS_YES_OR_NO; cfg->serve_expired = (strcmp(val, "yes") == 0); SERVE_EXPIRED = cfg->serve_expired; } @@ -819,6 +823,9 @@ int config_set_option(struct config_file* cfg, const char* opt, { IS_NUMBER_OR_ZERO; cfg->ipsecmod_max_ttl = atoi(val); } else S_YNO("ipsecmod-strict:", ipsecmod_strict) #endif +#ifdef USE_CACHEDB + else S_YNO("cachedb-no-store:", cachedb_no_store) +#endif /* USE_CACHEDB */ else if(strcmp(opt, "define-tag:") ==0) { return config_add_tag(cfg, val); /* val_sig_skew_min, max and val_max_restart are copied into val_env @@ -1149,6 +1156,7 @@ config_get_option(struct config_file* cfg, const char* opt, else O_YNO(opt, "val-permissive-mode", val_permissive_mode) else O_YNO(opt, "aggressive-nsec", aggressive_nsec) else O_YNO(opt, "ignore-cd-flag", ignore_cd) + else O_YNO(opt, "disable-edns-do", disable_edns_do) else O_YNO(opt, "serve-expired", serve_expired) else O_DEC(opt, "serve-expired-ttl", serve_expired_ttl) else O_YNO(opt, "serve-expired-ttl-reset", serve_expired_ttl_reset) @@ -1306,6 +1314,7 @@ config_get_option(struct config_file* cfg, const char* opt, #ifdef USE_CACHEDB else O_STR(opt, "backend", cachedb_backend) else O_STR(opt, "secret-seed", cachedb_secret) + else O_YNO(opt, "cachedb-no-store", cachedb_no_store) #ifdef USE_REDIS else O_STR(opt, "redis-server-host", redis_server_host) else O_DEC(opt, "redis-server-port", redis_server_port) @@ -1313,6 +1322,7 @@ config_get_option(struct config_file* cfg, const char* opt, else O_STR(opt, "redis-server-password", redis_server_password) else O_DEC(opt, "redis-timeout", redis_timeout) else O_YNO(opt, "redis-expire-records", redis_expire_records) + else O_DEC(opt, "redis-logical-db", redis_logical_db) #endif /* USE_REDIS */ #endif /* USE_CACHEDB */ #ifdef USE_IPSET diff --git a/util/config_file.h b/util/config_file.h index 452f3c6a7..ad22b8330 100644 --- a/util/config_file.h +++ b/util/config_file.h @@ -409,6 +409,8 @@ struct config_file { int aggressive_nsec; /** ignore the CD flag in incoming queries and refuse them bogus data */ int ignore_cd; + /** disable EDNS DO flag in outgoing requests */ + int disable_edns_do; /** serve expired entries and prefetch them */ int serve_expired; /** serve expired entries until TTL after expiration */ @@ -699,6 +701,8 @@ struct config_file { char* cachedb_backend; /** secret seed for hash key calculation */ char* cachedb_secret; + /** cachedb that does not store, but only reads from database, if on */ + int cachedb_no_store; #ifdef USE_REDIS /** redis server's IP address or host name */ char* redis_server_host; @@ -712,6 +716,8 @@ struct config_file { int redis_timeout; /** set timeout on redis records based on DNS response ttl */ int redis_expire_records; + /** set the redis logical database upon connection */ + int redis_logical_db; #endif #endif /** Downstream DNS Cookies */ diff --git a/util/configlexer.lex b/util/configlexer.lex index 3fcdfa62e..fdc267434 100644 --- a/util/configlexer.lex +++ b/util/configlexer.lex @@ -403,6 +403,7 @@ val-clean-additional{COLON} { YDVAR(1, VAR_VAL_CLEAN_ADDITIONAL) } val-permissive-mode{COLON} { YDVAR(1, VAR_VAL_PERMISSIVE_MODE) } aggressive-nsec{COLON} { YDVAR(1, VAR_AGGRESSIVE_NSEC) } ignore-cd-flag{COLON} { YDVAR(1, VAR_IGNORE_CD_FLAG) } +disable-edns-do{COLON} { YDVAR(1, VAR_DISABLE_EDNS_DO) } serve-expired{COLON} { YDVAR(1, VAR_SERVE_EXPIRED) } serve-expired-ttl{COLON} { YDVAR(1, VAR_SERVE_EXPIRED_TTL) } serve-expired-ttl-reset{COLON} { YDVAR(1, VAR_SERVE_EXPIRED_TTL_RESET) } @@ -557,12 +558,14 @@ ipsecmod-strict{COLON} { YDVAR(1, VAR_IPSECMOD_STRICT) } cachedb{COLON} { YDVAR(0, VAR_CACHEDB) } backend{COLON} { YDVAR(1, VAR_CACHEDB_BACKEND) } secret-seed{COLON} { YDVAR(1, VAR_CACHEDB_SECRETSEED) } +cachedb-no-store{COLON} { YDVAR(1, VAR_CACHEDB_NO_STORE) } redis-server-host{COLON} { YDVAR(1, VAR_CACHEDB_REDISHOST) } redis-server-port{COLON} { YDVAR(1, VAR_CACHEDB_REDISPORT) } redis-server-path{COLON} { YDVAR(1, VAR_CACHEDB_REDISPATH) } redis-server-password{COLON} { YDVAR(1, VAR_CACHEDB_REDISPASSWORD) } redis-timeout{COLON} { YDVAR(1, VAR_CACHEDB_REDISTIMEOUT) } redis-expire-records{COLON} { YDVAR(1, VAR_CACHEDB_REDISEXPIRERECORDS) } +redis-logical-db{COLON} { YDVAR(1, VAR_CACHEDB_REDISLOGICALDB) } ipset{COLON} { YDVAR(0, VAR_IPSET) } name-v4{COLON} { YDVAR(1, VAR_IPSET_NAME_V4) } name-v6{COLON} { YDVAR(1, VAR_IPSET_NAME_V6) } diff --git a/util/configparser.y b/util/configparser.y index d8f25a67e..da5d6608f 100644 --- a/util/configparser.y +++ b/util/configparser.y @@ -179,6 +179,7 @@ extern struct config_parser_state* cfg_parser; %token VAR_CACHEDB VAR_CACHEDB_BACKEND VAR_CACHEDB_SECRETSEED %token VAR_CACHEDB_REDISHOST VAR_CACHEDB_REDISPORT VAR_CACHEDB_REDISTIMEOUT %token VAR_CACHEDB_REDISEXPIRERECORDS VAR_CACHEDB_REDISPATH VAR_CACHEDB_REDISPASSWORD +%token VAR_CACHEDB_REDISLOGICALDB %token VAR_UDP_UPSTREAM_WITHOUT_DOWNSTREAM VAR_FOR_UPSTREAM %token VAR_AUTH_ZONE VAR_ZONEFILE VAR_MASTER VAR_URL VAR_FOR_DOWNSTREAM %token VAR_FALLBACK_ENABLED VAR_TLS_ADDITIONAL_PORT VAR_LOW_RTT VAR_LOW_RTT_PERMIL @@ -198,7 +199,7 @@ extern struct config_parser_state* cfg_parser; %token VAR_INTERFACE_ACTION VAR_INTERFACE_VIEW VAR_INTERFACE_TAG %token VAR_INTERFACE_TAG_ACTION VAR_INTERFACE_TAG_DATA %token VAR_PROXY_PROTOCOL_PORT VAR_STATISTICS_INHIBIT_ZERO -%token VAR_HARDEN_UNKNOWN_ADDITIONAL +%token VAR_HARDEN_UNKNOWN_ADDITIONAL VAR_DISABLE_EDNS_DO VAR_CACHEDB_NO_STORE %% toplevelvars: /* empty */ | toplevelvars toplevelvar ; @@ -332,7 +333,7 @@ content_server: server_num_threads | server_verbosity | server_port | server_tcp_reuse_timeout | server_tcp_auth_query_timeout | server_interface_automatic_ports | server_ede | server_proxy_protocol_port | server_statistics_inhibit_zero | - server_harden_unknown_additional + server_harden_unknown_additional | server_disable_edns_do ; stubstart: VAR_STUB_ZONE { @@ -2060,6 +2061,15 @@ server_ignore_cd_flag: VAR_IGNORE_CD_FLAG STRING_ARG free($2); } ; +server_disable_edns_do: VAR_DISABLE_EDNS_DO STRING_ARG + { + OUTYY(("P(server_disable_edns_do:%s)\n", $2)); + if(strcmp($2, "yes") != 0 && strcmp($2, "no") != 0) + yyerror("expected yes or no."); + else cfg_parser->cfg->disable_edns_do = (strcmp($2, "yes")==0); + free($2); + } + ; server_serve_expired: VAR_SERVE_EXPIRED STRING_ARG { OUTYY(("P(server_serve_expired:%s)\n", $2)); @@ -3701,7 +3711,8 @@ contents_cachedb: contents_cachedb content_cachedb | ; content_cachedb: cachedb_backend_name | cachedb_secret_seed | redis_server_host | redis_server_port | redis_timeout | - redis_expire_records | redis_server_path | redis_server_password + redis_expire_records | redis_server_path | redis_server_password | + cachedb_no_store | redis_logical_db ; cachedb_backend_name: VAR_CACHEDB_BACKEND STRING_ARG { @@ -3727,6 +3738,19 @@ cachedb_secret_seed: VAR_CACHEDB_SECRETSEED STRING_ARG #endif } ; +cachedb_no_store: VAR_CACHEDB_NO_STORE STRING_ARG + { + #ifdef USE_CACHEDB + OUTYY(("P(cachedb_no_store:%s)\n", $2)); + if(strcmp($2, "yes") != 0 && strcmp($2, "no") != 0) + yyerror("expected yes or no."); + else cfg_parser->cfg->cachedb_no_store = (strcmp($2, "yes")==0); + #else + OUTYY(("P(Compiled without cachedb, ignoring)\n")); + #endif + free($2); + } + ; redis_server_host: VAR_CACHEDB_REDISHOST STRING_ARG { #if defined(USE_CACHEDB) && defined(USE_REDIS) @@ -3804,6 +3828,21 @@ redis_expire_records: VAR_CACHEDB_REDISEXPIRERECORDS STRING_ARG free($2); } ; +redis_logical_db: VAR_CACHEDB_REDISLOGICALDB STRING_ARG + { + #if defined(USE_CACHEDB) && defined(USE_REDIS) + int db; + OUTYY(("P(redis_logical_db:%s)\n", $2)); + db = atoi($2); + if((db == 0 && strcmp($2, "0") != 0) || db < 0) + yyerror("valid redis logical database index expected"); + else cfg_parser->cfg->redis_logical_db = db; + #else + OUTYY(("P(Compiled without cachedb or redis, ignoring)\n")); + #endif + free($2); + } + ; server_tcp_connection_limit: VAR_TCP_CONNECTION_LIMIT STRING_ARG STRING_ARG { OUTYY(("P(server_tcp_connection_limit:%s %s)\n", $2, $3)); diff --git a/util/data/msgencode.c b/util/data/msgencode.c index a170eb7b8..80ae33a38 100644 --- a/util/data/msgencode.c +++ b/util/data/msgencode.c @@ -1012,8 +1012,10 @@ reply_info_answer_encode(struct query_info* qinf, struct reply_info* rep, ede_size = calc_ede_option_size(edns, &ede_txt_size); if(sldns_buffer_capacity(pkt) < udpsize) udpsize = sldns_buffer_capacity(pkt); + if(!edns || !edns->edns_present) { + attach_edns = 0; /* EDEs are optional, try to fit anything else before them */ - if(udpsize < LDNS_HEADER_SIZE + edns_field_size - ede_size) { + } else if(udpsize < LDNS_HEADER_SIZE + edns_field_size - ede_size) { /* packet too small to contain edns, omit it. */ attach_edns = 0; } else { diff --git a/util/data/msgparse.c b/util/data/msgparse.c index b5414c6d0..d06b7bb25 100644 --- a/util/data/msgparse.c +++ b/util/data/msgparse.c @@ -47,6 +47,7 @@ #include "util/regional.h" #include "util/rfc_1982.h" #include "util/edns.h" +#include "util/net_help.h" #include "sldns/rrdef.h" #include "sldns/sbuffer.h" #include "sldns/parseutil.h" @@ -1306,3 +1307,27 @@ log_edns_opt_list(enum verbosity_value level, const char* info_str, } } +/** remove RR from msgparse RRset, return true if rrset is entirely bad */ +int +msgparse_rrset_remove_rr(const char* str, sldns_buffer* pkt, struct rrset_parse* rrset, + struct rr_parse* prev, struct rr_parse* rr, struct sockaddr_storage* addr, socklen_t addrlen) +{ + if(verbosity >= VERB_QUERY && rrset->dname_len <= LDNS_MAX_DOMAINLEN && str) { + uint8_t buf[LDNS_MAX_DOMAINLEN+1]; + dname_pkt_copy(pkt, buf, rrset->dname); + if(addr) + log_name_addr(VERB_QUERY, str, buf, addr, addrlen); + else log_nametypeclass(VERB_QUERY, str, buf, + rrset->type, ntohs(rrset->rrset_class)); + } + if(prev) + prev->next = rr->next; + else rrset->rr_first = rr->next; + if(rrset->rr_last == rr) + rrset->rr_last = prev; + rrset->rr_count --; + rrset->size -= rr->size; + /* rr struct still exists, but is unlinked, so that in the for loop + * the rr->next works fine to continue. */ + return rrset->rr_count == 0; +} diff --git a/util/data/msgparse.h b/util/data/msgparse.h index b7dc235d6..8e5c94a28 100644 --- a/util/data/msgparse.h +++ b/util/data/msgparse.h @@ -371,4 +371,22 @@ void msgparse_bucket_remove(struct msg_parse* msg, struct rrset_parse* rrset); void log_edns_opt_list(enum verbosity_value level, const char* info_str, struct edns_option* list); +/** + * Remove RR from msgparse RRset. + * @param str: this string is used for logging if verbose. If NULL, there is + * no logging of the remove. + * @param pkt: packet in buffer that is removed from. Used to log the name + * of the item removed. + * @param rrset: RRset that the RR is removed from. + * @param prev: previous RR in list, or NULL. + * @param rr: RR that is removed. + * @param addr: address used for logging, if verbose, or NULL then it is not + * used. + * @param addrlen: length of addr, if that is not NULL. + * @return true if rrset is entirely bad, it would then need to be removed. + */ +int msgparse_rrset_remove_rr(const char* str, struct sldns_buffer* pkt, + struct rrset_parse* rrset, struct rr_parse* prev, struct rr_parse* rr, + struct sockaddr_storage* addr, socklen_t addrlen); + #endif /* UTIL_DATA_MSGPARSE_H */ diff --git a/util/fptr_wlist.c b/util/fptr_wlist.c index 3b88da235..43d38dc37 100644 --- a/util/fptr_wlist.c +++ b/util/fptr_wlist.c @@ -168,7 +168,9 @@ int fptr_whitelist_event(void (*fptr)(int, short, void *)) { if(fptr == &comm_point_udp_callback) return 1; +#if defined(AF_INET6) && defined(IPV6_PKTINFO) && defined(HAVE_RECVMSG) else if(fptr == &comm_point_udp_ancil_callback) return 1; +#endif else if(fptr == &comm_point_tcp_accept_callback) return 1; else if(fptr == &comm_point_tcp_handle_callback) return 1; else if(fptr == &comm_timer_callback) return 1; diff --git a/util/module.c b/util/module.c index 773dab853..62e5de4a0 100644 --- a/util/module.c +++ b/util/module.c @@ -194,6 +194,24 @@ char* errinf_to_str_servfail(struct module_qstate* qstate) return p; } +char* errinf_to_str_misc(struct module_qstate* qstate) +{ + char buf[20480]; + char* p = buf; + size_t left = sizeof(buf); + struct errinf_strlist* s; + if(!qstate->errinf) + snprintf(p, left, "misc failure"); + else for(s=qstate->errinf; s; s=s->next) { + snprintf(p, left, "%s%s", (s==qstate->errinf?"":" "), s->str); + left -= strlen(p); p += strlen(p); + } + p = strdup(buf); + if(!p) + log_err("malloc failure in errinf_to_str"); + return p; +} + void errinf_rrset(struct module_qstate* qstate, struct ub_packed_rrset_key *rr) { char buf[1024]; diff --git a/util/module.h b/util/module.h index 5b6fcc93c..8a9da3f93 100644 --- a/util/module.h +++ b/util/module.h @@ -691,6 +691,8 @@ struct module_qstate { struct respip_action_info* respip_action_info; /** if the query is rpz passthru, no further rpz processing for it */ int rpz_passthru; + /* Flag tcp required. */ + int tcp_required; /** whether the reply should be dropped */ int is_drop; @@ -842,6 +844,14 @@ sldns_ede_code errinf_to_reason_bogus(struct module_qstate* qstate); */ char* errinf_to_str_servfail(struct module_qstate* qstate); +/** + * Create error info in string. For misc failures that are not servfail. + * @param qstate: query state. + * @return string or NULL on malloc failure (already logged). + * This string is malloced and has to be freed by caller. + */ +char* errinf_to_str_misc(struct module_qstate* qstate); + /** * Initialize the edns known options by allocating the required space. * @param env: the module environment. diff --git a/util/net_help.c b/util/net_help.c index e559c9b2f..16692b788 100644 --- a/util/net_help.c +++ b/util/net_help.c @@ -809,6 +809,7 @@ addr_to_nat64(const struct sockaddr_storage* addr, struct sockaddr_in *sin = (struct sockaddr_in *)addr; struct sockaddr_in6 *sin6; uint8_t *v4_byte; + int i; /* This needs to be checked by the caller */ log_assert(addr->ss_family == AF_INET); @@ -826,7 +827,7 @@ addr_to_nat64(const struct sockaddr_storage* addr, nat64_prefixnet = nat64_prefixnet / 8; v4_byte = (uint8_t *)&sin->sin_addr.s_addr; - for(int i = 0; i < 4; i++) { + for(i = 0; i < 4; i++) { if(nat64_prefixnet == 8) { /* bits 64...71 are MBZ */ sin6->sin6_addr.s6_addr[nat64_prefixnet++] = 0; @@ -951,6 +952,110 @@ void log_crypto_err_code(const char* str, unsigned long err) #endif /* HAVE_SSL */ } +/** Print crypt erro with SSL_get_error want code and err_get_error code */ +static void log_crypto_err_io_code_arg(const char* str, int r, + unsigned long err, int err_present) +{ +#ifdef HAVE_SSL + int print_errno = 0, print_crypto_err = 0; + const char* inf = NULL; + + switch(r) { + case SSL_ERROR_NONE: + inf = "no error"; + break; + case SSL_ERROR_ZERO_RETURN: + inf = "channel closed"; + break; + case SSL_ERROR_WANT_READ: + inf = "want read"; + break; + case SSL_ERROR_WANT_WRITE: + inf = "want write"; + break; + case SSL_ERROR_WANT_CONNECT: + inf = "want connect"; + break; + case SSL_ERROR_WANT_ACCEPT: + inf = "want accept"; + break; + case SSL_ERROR_WANT_X509_LOOKUP: + inf = "want X509 lookup"; + break; + case SSL_ERROR_WANT_ASYNC: + inf = "want async"; + break; + case SSL_ERROR_WANT_ASYNC_JOB: + inf = "want async job"; + break; + case SSL_ERROR_WANT_CLIENT_HELLO_CB: + inf = "want client hello cb"; + break; + case SSL_ERROR_SYSCALL: + print_errno = 1; + inf = "syscall"; + break; + case SSL_ERROR_SSL: + print_crypto_err = 1; + inf = "SSL, usually protocol, error"; + break; + default: + inf = "unknown SSL_get_error result code"; + print_errno = 1; + print_crypto_err = 1; + } + if(print_crypto_err) { + if(print_errno) { + char buf[1024]; + snprintf(buf, sizeof(buf), "%s with errno %s", + str, strerror(errno)); + if(err_present) + log_crypto_err_code(buf, err); + else log_crypto_err(buf); + } else { + if(err_present) + log_crypto_err_code(str, err); + else log_crypto_err(str); + } + } else { + if(print_errno) { + if(errno == 0) + log_err("str: syscall error with errno %s", + strerror(errno)); + else log_err("str: %s", strerror(errno)); + } else { + log_err("str: %s", inf); + } + } +#else + (void)str; + (void)r; + (void)err; + (void)err_present; +#endif /* HAVE_SSL */ +} + +void log_crypto_err_io(const char* str, int r) +{ +#ifdef HAVE_SSL + log_crypto_err_io_code_arg(str, r, 0, 0); +#else + (void)str; + (void)r; +#endif /* HAVE_SSL */ +} + +void log_crypto_err_io_code(const char* str, int r, unsigned long err) +{ +#ifdef HAVE_SSL + log_crypto_err_io_code_arg(str, r, err, 1); +#else + (void)str; + (void)r; + (void)err; +#endif /* HAVE_SSL */ +} + #ifdef HAVE_SSL /** log certificate details */ void diff --git a/util/net_help.h b/util/net_help.h index a9de910d5..edaea4235 100644 --- a/util/net_help.h +++ b/util/net_help.h @@ -429,6 +429,24 @@ void log_crypto_err(const char* str); */ void log_crypto_err_code(const char* str, unsigned long err); +/** + * Log an error from libcrypto that came from SSL_write and so on, with + * a value from SSL_get_error, calls log_err. If that fails it logs with + * log_crypto_err. + * @param str: what failed + * @param r: output of SSL_get_error on the I/O operation result. + */ +void log_crypto_err_io(const char* str, int r); + +/** + * Log an error from libcrypt that came from an I/O routine with the + * errcode from ERR_get_error. Calls log_err() and log_crypto_err_code. + * @param str: what failed + * @param r: output of SSL_get_error on the I/O operation result. + * @param err: error code from ERR_get_error + */ +void log_crypto_err_io_code(const char* str, int r, unsigned long err); + /** * Log certificate details verbosity, string, of X509 cert * @param level: verbosity level diff --git a/util/netevent.c b/util/netevent.c index 204e4883c..c26b13e4a 100644 --- a/util/netevent.c +++ b/util/netevent.c @@ -116,6 +116,8 @@ /** timeout in millisec to wait for write to unblock, packets dropped after.*/ #define SEND_BLOCKED_WAIT_TIMEOUT 200 +/** max number of times to wait for write to unblock, packets dropped after.*/ +#define SEND_BLOCKED_MAX_RETRY 5 /** Let's make timestamping code cleaner and redefine SO_TIMESTAMP* */ #ifndef SO_TIMESTAMP @@ -402,9 +404,10 @@ comm_point_send_udp_msg(struct comm_point *c, sldns_buffer* packet, WSAGetLastError() == WSAENOBUFS || WSAGetLastError() == WSAEWOULDBLOCK) { #endif + int retries = 0; /* if we set the fd blocking, other threads suddenly * have a blocking fd that they operate on */ - while(sent == -1 && ( + while(sent == -1 && retries < SEND_BLOCKED_MAX_RETRY && ( #ifndef USE_WINSOCK errno == EAGAIN || errno == EINTR || # ifdef EWOULDBLOCK @@ -419,6 +422,13 @@ comm_point_send_udp_msg(struct comm_point *c, sldns_buffer* packet, #endif )) { #if defined(HAVE_POLL) || defined(USE_WINSOCK) + int send_nobufs = ( +#ifndef USE_WINSOCK + errno == ENOBUFS +#else + WSAGetLastError() == WSAENOBUFS +#endif + ); struct pollfd p; int pret; memset(&p, 0, sizeof(p)); @@ -457,8 +467,48 @@ comm_point_send_udp_msg(struct comm_point *c, sldns_buffer* packet, log_err("poll udp out failed: %s", sock_strerror(errno)); return 0; + } else if((pret < 0 && +#ifndef USE_WINSOCK + errno == ENOBUFS +#else + WSAGetLastError() == WSAENOBUFS +#endif + ) || (send_nobufs && retries > 0)) { + /* ENOBUFS, and poll returned without + * a timeout. Or the retried send call + * returned ENOBUFS. It is good to + * wait a bit for the error to clear. */ + /* The timeout is 20*(2^(retries+1)), + * it increases exponentially, starting + * at 40 msec. After 5 tries, 1240 msec + * have passed in total, when poll + * returned the error, and 1200 msec + * when send returned the errors. */ +#ifndef USE_WINSOCK + pret = poll(NULL, 0, (SEND_BLOCKED_WAIT_TIMEOUT/10)<<(retries+1)); +#else + pret = WSAPoll(NULL, 0, (SEND_BLOCKED_WAIT_TIMEOUT/10)<<(retries+1)); +#endif + if(pret < 0 && +#ifndef USE_WINSOCK + errno != EAGAIN && errno != EINTR && +# ifdef EWOULDBLOCK + errno != EWOULDBLOCK && +# endif + errno != ENOBUFS +#else + WSAGetLastError() != WSAEINPROGRESS && + WSAGetLastError() != WSAEINTR && + WSAGetLastError() != WSAENOBUFS && + WSAGetLastError() != WSAEWOULDBLOCK +#endif + ) { + log_err("poll udp out timer failed: %s", + sock_strerror(errno)); + } } #endif /* defined(HAVE_POLL) || defined(USE_WINSOCK) */ + retries++; if (!is_connected) { sent = sendto(c->fd, (void*)sldns_buffer_begin(packet), sldns_buffer_remaining(packet), 0, @@ -665,7 +715,8 @@ comm_point_send_udp_msg_if(struct comm_point *c, sldns_buffer* packet, WSAGetLastError() == WSAENOBUFS || WSAGetLastError() == WSAEWOULDBLOCK) { #endif - while(sent == -1 && ( + int retries = 0; + while(sent == -1 && retries < SEND_BLOCKED_MAX_RETRY && ( #ifndef USE_WINSOCK errno == EAGAIN || errno == EINTR || # ifdef EWOULDBLOCK @@ -680,6 +731,13 @@ comm_point_send_udp_msg_if(struct comm_point *c, sldns_buffer* packet, #endif )) { #if defined(HAVE_POLL) || defined(USE_WINSOCK) + int send_nobufs = ( +#ifndef USE_WINSOCK + errno == ENOBUFS +#else + WSAGetLastError() == WSAENOBUFS +#endif + ); struct pollfd p; int pret; memset(&p, 0, sizeof(p)); @@ -718,8 +776,48 @@ comm_point_send_udp_msg_if(struct comm_point *c, sldns_buffer* packet, log_err("poll udp out failed: %s", sock_strerror(errno)); return 0; + } else if((pret < 0 && +#ifndef USE_WINSOCK + errno == ENOBUFS +#else + WSAGetLastError() == WSAENOBUFS +#endif + ) || (send_nobufs && retries > 0)) { + /* ENOBUFS, and poll returned without + * a timeout. Or the retried send call + * returned ENOBUFS. It is good to + * wait a bit for the error to clear. */ + /* The timeout is 20*(2^(retries+1)), + * it increases exponentially, starting + * at 40 msec. After 5 tries, 1240 msec + * have passed in total, when poll + * returned the error, and 1200 msec + * when send returned the errors. */ +#ifndef USE_WINSOCK + pret = poll(NULL, 0, (SEND_BLOCKED_WAIT_TIMEOUT/10)<<(retries+1)); +#else + pret = WSAPoll(NULL, 0, (SEND_BLOCKED_WAIT_TIMEOUT/10)<<(retries+1)); +#endif + if(pret < 0 && +#ifndef USE_WINSOCK + errno != EAGAIN && errno != EINTR && +# ifdef EWOULDBLOCK + errno != EWOULDBLOCK && +# endif + errno != ENOBUFS +#else + WSAGetLastError() != WSAEINPROGRESS && + WSAGetLastError() != WSAEINTR && + WSAGetLastError() != WSAENOBUFS && + WSAGetLastError() != WSAEWOULDBLOCK +#endif + ) { + log_err("poll udp out timer failed: %s", + sock_strerror(errno)); + } } #endif /* defined(HAVE_POLL) || defined(USE_WINSOCK) */ + retries++; sent = sendmsg(c->fd, &msg, 0); } } @@ -794,15 +892,18 @@ static int udp_recv_needs_log(int err) static int consume_pp2_header(struct sldns_buffer* buf, struct comm_reply* rep, int stream) { size_t size; - struct pp2_header *header = pp2_read_header(buf); - if(header == NULL) return 0; + struct pp2_header *header; + int err = pp2_read_header(sldns_buffer_begin(buf), + sldns_buffer_remaining(buf)); + if(err) return 0; + header = (struct pp2_header*)sldns_buffer_begin(buf); size = PP2_HEADER_SIZE + ntohs(header->len); if((header->ver_cmd & 0xF) == PP2_CMD_LOCAL) { /* A connection from the proxy itself. * No need to do anything with addresses. */ goto done; } - if(header->fam_prot == 0x00) { + if(header->fam_prot == PP2_UNSPEC_UNSPEC) { /* Unspecified family and protocol. This could be used for * health checks by proxies. * No need to do anything with addresses. */ @@ -810,8 +911,8 @@ static int consume_pp2_header(struct sldns_buffer* buf, struct comm_reply* rep, } /* Read the proxied address */ switch(header->fam_prot) { - case 0x11: /* AF_INET|STREAM */ - case 0x12: /* AF_INET|DGRAM */ + case PP2_INET_STREAM: + case PP2_INET_DGRAM: { struct sockaddr_in* addr = (struct sockaddr_in*)&rep->client_addr; @@ -822,8 +923,8 @@ static int consume_pp2_header(struct sldns_buffer* buf, struct comm_reply* rep, } /* Ignore the destination address; it should be us. */ break; - case 0x21: /* AF_INET6|STREAM */ - case 0x22: /* AF_INET6|DGRAM */ + case PP2_INET6_STREAM: + case PP2_INET6_DGRAM: { struct sockaddr_in6* addr = (struct sockaddr_in6*)&rep->client_addr; @@ -836,6 +937,10 @@ static int consume_pp2_header(struct sldns_buffer* buf, struct comm_reply* rep, } /* Ignore the destination address; it should be us. */ break; + default: + log_err("proxy_protocol: unsupported family and " + "protocol 0x%x", (int)header->fam_prot); + return 0; } rep->is_proxied = 1; done: @@ -850,10 +955,10 @@ static int consume_pp2_header(struct sldns_buffer* buf, struct comm_reply* rep, return 1; } +#if defined(AF_INET6) && defined(IPV6_PKTINFO) && defined(HAVE_RECVMSG) void comm_point_udp_ancil_callback(int fd, short event, void* arg) { -#if defined(AF_INET6) && defined(IPV6_PKTINFO) && defined(HAVE_RECVMSG) struct comm_reply rep; struct msghdr msg; struct iovec iov[1]; @@ -972,14 +1077,8 @@ comm_point_udp_ancil_callback(int fd, short event, void* arg) if(!rep.c || rep.c->fd == -1) /* commpoint closed */ break; } -#else - (void)fd; - (void)event; - (void)arg; - fatal_exit("recvmsg: No support for IPV6_PKTINFO; IP_PKTINFO or IP_RECVDSTADDR. " - "Please disable interface-automatic"); -#endif /* AF_INET6 && IPV6_PKTINFO && HAVE_RECVMSG */ } +#endif /* AF_INET6 && IPV6_PKTINFO && HAVE_RECVMSG */ void comm_point_udp_callback(int fd, short event, void* arg) @@ -1567,7 +1666,8 @@ ssl_handshake(struct comm_point* c) } else { unsigned long err = ERR_get_error(); if(!squelch_err_ssl_handshake(err)) { - log_crypto_err_code("ssl handshake failed", err); + log_crypto_err_io_code("ssl handshake failed", + want, err); log_addr(VERB_OPS, "ssl handshake failed", &c->repinfo.remote_addr, c->repinfo.remote_addrlen); @@ -1717,23 +1817,30 @@ ssl_handle_read(struct comm_point* c) strerror(errno)); return 0; } - log_crypto_err("could not SSL_read"); + log_crypto_err_io("could not SSL_read", + want); return 0; } c->tcp_byte_count += r; + sldns_buffer_skip(c->buffer, r); if(c->tcp_byte_count != current_read_size) return 1; c->pp2_header_state = pp2_header_init; } } if(c->pp2_header_state == pp2_header_init) { - header = pp2_read_header(c->buffer); - if(!header) { + int err; + err = pp2_read_header( + sldns_buffer_begin(c->buffer), + sldns_buffer_limit(c->buffer)); + if(err) { log_err("proxy_protocol: could not parse " - "PROXYv2 header"); + "PROXYv2 header (%s)", + pp_lookup_error(err)); return 0; } + header = (struct pp2_header*)sldns_buffer_begin(c->buffer); want_read_size = ntohs(header->len); - if(sldns_buffer_remaining(c->buffer) < + if(sldns_buffer_limit(c->buffer) < PP2_HEADER_SIZE + want_read_size) { log_err_addr("proxy_protocol: not enough " "buffer size to read PROXYv2 header", "", @@ -1778,10 +1885,12 @@ ssl_handle_read(struct comm_point* c) strerror(errno)); return 0; } - log_crypto_err("could not SSL_read"); + log_crypto_err_io("could not SSL_read", + want); return 0; } c->tcp_byte_count += r; + sldns_buffer_skip(c->buffer, r); if(c->tcp_byte_count != current_read_size) return 1; c->pp2_header_state = pp2_header_done; } @@ -1792,6 +1901,7 @@ ssl_handle_read(struct comm_point* c) c->repinfo.remote_addrlen); return 0; } + sldns_buffer_flip(c->buffer); if(!consume_pp2_header(c->buffer, &c->repinfo, 1)) { log_err_addr("proxy_protocol: could not consume " "PROXYv2 header", "", &c->repinfo.remote_addr, @@ -1836,7 +1946,7 @@ ssl_handle_read(struct comm_point* c) strerror(errno)); return 0; } - log_crypto_err("could not SSL_read"); + log_crypto_err_io("could not SSL_read", want); return 0; } c->tcp_byte_count += r; @@ -1886,7 +1996,7 @@ ssl_handle_read(struct comm_point* c) strerror(errno)); return 0; } - log_crypto_err("could not SSL_read"); + log_crypto_err_io("could not SSL_read", want); return 0; } sldns_buffer_skip(c->buffer, (ssize_t)r); @@ -1977,7 +2087,7 @@ ssl_handle_write(struct comm_point* c) strerror(errno)); return 0; } - log_crypto_err("could not SSL_write"); + log_crypto_err_io("could not SSL_write", want); return 0; } if(c->tcp_write_and_read) { @@ -2029,7 +2139,7 @@ ssl_handle_write(struct comm_point* c) strerror(errno)); return 0; } - log_crypto_err("could not SSL_write"); + log_crypto_err_io("could not SSL_write", want); return 0; } if(c->tcp_write_and_read) { @@ -2113,19 +2223,25 @@ comm_point_tcp_handle_read(int fd, struct comm_point* c, int short_ok) goto recv_error_initial; } c->tcp_byte_count += r; + sldns_buffer_skip(c->buffer, r); if(c->tcp_byte_count != current_read_size) return 1; c->pp2_header_state = pp2_header_init; } } if(c->pp2_header_state == pp2_header_init) { - header = pp2_read_header(c->buffer); - if(!header) { + int err; + err = pp2_read_header( + sldns_buffer_begin(c->buffer), + sldns_buffer_limit(c->buffer)); + if(err) { log_err("proxy_protocol: could not parse " - "PROXYv2 header"); + "PROXYv2 header (%s)", + pp_lookup_error(err)); return 0; } + header = (struct pp2_header*)sldns_buffer_begin(c->buffer); want_read_size = ntohs(header->len); - if(sldns_buffer_remaining(c->buffer) < + if(sldns_buffer_limit(c->buffer) < PP2_HEADER_SIZE + want_read_size) { log_err_addr("proxy_protocol: not enough " "buffer size to read PROXYv2 header", "", @@ -2152,6 +2268,7 @@ comm_point_tcp_handle_read(int fd, struct comm_point* c, int short_ok) goto recv_error; } c->tcp_byte_count += r; + sldns_buffer_skip(c->buffer, r); if(c->tcp_byte_count != current_read_size) return 1; c->pp2_header_state = pp2_header_done; } @@ -2162,6 +2279,7 @@ comm_point_tcp_handle_read(int fd, struct comm_point* c, int short_ok) c->repinfo.remote_addrlen); return 0; } + sldns_buffer_flip(c->buffer); if(!consume_pp2_header(c->buffer, &c->repinfo, 1)) { log_err_addr("proxy_protocol: could not consume " "PROXYv2 header", "", &c->repinfo.remote_addr, @@ -2815,7 +2933,7 @@ ssl_http_read_more(struct comm_point* c) strerror(errno)); return 0; } - log_crypto_err("could not SSL_read"); + log_crypto_err_io("could not SSL_read", want); return 0; } verbose(VERB_ALGO, "ssl http read more skip to %d + %d", @@ -3266,7 +3384,7 @@ ssize_t http2_recv_cb(nghttp2_session* ATTR_UNUSED(session), uint8_t* buf, strerror(errno)); return NGHTTP2_ERR_CALLBACK_FAILURE; } - log_crypto_err("could not SSL_read"); + log_crypto_err_io("could not SSL_read", want); return NGHTTP2_ERR_CALLBACK_FAILURE; } return r; @@ -3521,7 +3639,7 @@ ssl_http_write_more(struct comm_point* c) strerror(errno)); return 0; } - log_crypto_err("could not SSL_write"); + log_crypto_err_io("could not SSL_write", want); return 0; } sldns_buffer_skip(c->buffer, (ssize_t)r); @@ -3594,7 +3712,7 @@ ssize_t http2_send_cb(nghttp2_session* ATTR_UNUSED(session), const uint8_t* buf, strerror(errno)); return NGHTTP2_ERR_CALLBACK_FAILURE; } - log_crypto_err("could not SSL_write"); + log_crypto_err_io("could not SSL_write", want); return NGHTTP2_ERR_CALLBACK_FAILURE; } return r; @@ -3860,11 +3978,7 @@ comm_point_create_udp(struct comm_base *base, int fd, sldns_buffer* buffer, evbits = UB_EV_READ | UB_EV_PERSIST; /* ub_event stuff */ c->ev->ev = ub_event_new(base->eb->base, c->fd, evbits, -#ifdef USE_WINSOCK comm_point_udp_callback, c); -#else - comm_point_udp_ancil_callback, c); -#endif if(c->ev->ev == NULL) { log_err("could not baseset udp event"); comm_point_delete(c); @@ -3879,6 +3993,7 @@ comm_point_create_udp(struct comm_base *base, int fd, sldns_buffer* buffer, return c; } +#if defined(AF_INET6) && defined(IPV6_PKTINFO) && defined(HAVE_RECVMSG) struct comm_point* comm_point_create_udp_ancil(struct comm_base *base, int fd, sldns_buffer* buffer, int pp2_enabled, @@ -3941,6 +4056,7 @@ comm_point_create_udp_ancil(struct comm_base *base, int fd, c->event_added = 1; return c; } +#endif static struct comm_point* comm_point_create_tcp_handler(struct comm_base *base, diff --git a/util/proxy_protocol.c b/util/proxy_protocol.c index 757c5141d..a18804974 100644 --- a/util/proxy_protocol.c +++ b/util/proxy_protocol.c @@ -38,102 +38,162 @@ * * This file contains PROXY protocol functions. */ -#include "config.h" -#include "util/log.h" #include "util/proxy_protocol.h" -int -pp2_write_to_buf(struct sldns_buffer* buf, struct sockaddr_storage* src, +/** + * Internal struct initialized with function pointers for writing uint16 and + * uint32. + */ +struct proxy_protocol_data { + void (*write_uint16)(void* buf, uint16_t data); + void (*write_uint32)(void* buf, uint32_t data); +}; +struct proxy_protocol_data pp_data; + +/** + * Internal lookup table; could be further generic like sldns_lookup_table + * for all the future generic stuff. + */ +struct proxy_protocol_lookup_table { + int id; + const char *text; +}; + +/** + * Internal parsing error text; could be exposed with pp_lookup_error. + */ +static struct proxy_protocol_lookup_table pp_parse_errors_data[] = { + { PP_PARSE_NOERROR, "no parse error" }, + { PP_PARSE_SIZE, "not enough space for header" }, + { PP_PARSE_WRONG_HEADERv2, "could not match PROXYv2 header" }, + { PP_PARSE_UNKNOWN_CMD, "unknown command" }, + { PP_PARSE_UNKNOWN_FAM_PROT, "unknown family and protocol" }, +}; + +void +pp_init(void (*write_uint16)(void* buf, uint16_t data), + void (*write_uint32)(void* buf, uint32_t data)) { + pp_data.write_uint16 = write_uint16; + pp_data.write_uint32 = write_uint32; +} + +const char* +pp_lookup_error(enum pp_parse_errors error) { + return pp_parse_errors_data[error].text; +} + +size_t +pp2_write_to_buf(uint8_t* buf, size_t buflen, +#ifdef INET6 + struct sockaddr_storage* src, +#else + struct sockaddr_in* src, +#endif int stream) { int af; + size_t expected_size; if(!src) return 0; af = (int)((struct sockaddr_in*)src)->sin_family; - if(sldns_buffer_remaining(buf) < - PP2_HEADER_SIZE + (af==AF_INET?12:36)) { + expected_size = PP2_HEADER_SIZE + (af==AF_INET?12:36); + if(buflen < expected_size) { return 0; } /* sig */ - sldns_buffer_write(buf, PP2_SIG, PP2_SIG_LEN); + memcpy(buf, PP2_SIG, PP2_SIG_LEN); + buf += PP2_SIG_LEN; /* version and command */ - sldns_buffer_write_u8(buf, (PP2_VERSION << 4) | PP2_CMD_PROXY); - if(af==AF_INET) { + *buf = (PP2_VERSION << 4) | PP2_CMD_PROXY; + buf++; + switch(af) { + case AF_INET: /* family and protocol */ - sldns_buffer_write_u8(buf, - (PP2_AF_INET<<4) | - (stream?PP2_PROT_STREAM:PP2_PROT_DGRAM)); + *buf = (PP2_AF_INET<<4) | + (stream?PP2_PROT_STREAM:PP2_PROT_DGRAM); + buf++; /* length */ - sldns_buffer_write_u16(buf, 12); + (*pp_data.write_uint16)(buf, 12); + buf += 2; /* src addr */ - sldns_buffer_write(buf, + memcpy(buf, &((struct sockaddr_in*)src)->sin_addr.s_addr, 4); + buf += 4; /* dst addr */ - sldns_buffer_write_u32(buf, 0); + (*pp_data.write_uint32)(buf, 0); + buf += 4; /* src port */ - sldns_buffer_write(buf, + memcpy(buf, &((struct sockaddr_in*)src)->sin_port, 2); + buf += 2; + /* dst addr */ /* dst port */ - sldns_buffer_write_u16(buf, 0); - } else { + (*pp_data.write_uint16)(buf, 12); + break; +#ifdef INET6 + case AF_INET6: /* family and protocol */ - sldns_buffer_write_u8(buf, - (PP2_AF_INET6<<4) | - (stream?PP2_PROT_STREAM:PP2_PROT_DGRAM)); + *buf = (PP2_AF_INET6<<4) | + (stream?PP2_PROT_STREAM:PP2_PROT_DGRAM); + buf++; /* length */ - sldns_buffer_write_u16(buf, 36); + (*pp_data.write_uint16)(buf, 36); + buf += 2; /* src addr */ - sldns_buffer_write(buf, + memcpy(buf, &((struct sockaddr_in6*)src)->sin6_addr, 16); + buf += 16; /* dst addr */ - sldns_buffer_set_at(buf, - sldns_buffer_position(buf), 0, 16); - sldns_buffer_skip(buf, 16); + memset(buf, 0, 16); + buf += 16; /* src port */ - sldns_buffer_write(buf, - &((struct sockaddr_in6*)src)->sin6_port, 2); + memcpy(buf, &((struct sockaddr_in6*)src)->sin6_port, 2); + buf += 2; /* dst port */ - sldns_buffer_write_u16(buf, 0); + (*pp_data.write_uint16)(buf, 0); + break; +#endif /* INET6 */ + case AF_UNIX: + /* fallthrough */ + default: + return 0; } - return 1; + return expected_size; } -struct pp2_header* -pp2_read_header(struct sldns_buffer* buf) +int +pp2_read_header(uint8_t* buf, size_t buflen) { size_t size; - struct pp2_header* header = (struct pp2_header*)sldns_buffer_begin(buf); + struct pp2_header* header = (struct pp2_header*)buf; /* Try to fail all the unsupported cases first. */ - if(sldns_buffer_remaining(buf) < PP2_HEADER_SIZE) { - log_err("proxy_protocol: not enough space for header"); - return NULL; + if(buflen < PP2_HEADER_SIZE) { + return PP_PARSE_SIZE; } /* Check for PROXYv2 header */ if(memcmp(header, PP2_SIG, PP2_SIG_LEN) != 0 || ((header->ver_cmd & 0xF0)>>4) != PP2_VERSION) { - log_err("proxy_protocol: could not match PROXYv2 header"); - return NULL; + return PP_PARSE_WRONG_HEADERv2; } /* Check the length */ size = PP2_HEADER_SIZE + ntohs(header->len); - if(sldns_buffer_remaining(buf) < size) { - log_err("proxy_protocol: not enough space for header"); - return NULL; + if(buflen < size) { + return PP_PARSE_SIZE; } /* Check for supported commands */ if((header->ver_cmd & 0xF) != PP2_CMD_LOCAL && (header->ver_cmd & 0xF) != PP2_CMD_PROXY) { - log_err("proxy_protocol: unsupported command"); - return NULL; + return PP_PARSE_UNKNOWN_CMD; } /* Check for supported family and protocol */ - if(header->fam_prot != 0x00 /* AF_UNSPEC|UNSPEC */ && - header->fam_prot != 0x11 /* AF_INET|STREAM */ && - header->fam_prot != 0x12 /* AF_INET|DGRAM */ && - header->fam_prot != 0x21 /* AF_INET6|STREAM */ && - header->fam_prot != 0x22 /* AF_INET6|DGRAM */) { - log_err("proxy_protocol: unsupported family and protocol"); - return NULL; + if(header->fam_prot != PP2_UNSPEC_UNSPEC && + header->fam_prot != PP2_INET_STREAM && + header->fam_prot != PP2_INET_DGRAM && + header->fam_prot != PP2_INET6_STREAM && + header->fam_prot != PP2_INET6_DGRAM && + header->fam_prot != PP2_UNIX_STREAM && + header->fam_prot != PP2_UNIX_DGRAM) { + return PP_PARSE_UNKNOWN_FAM_PROT; } /* We have a correct header */ - return header; + return PP_PARSE_NOERROR; } diff --git a/util/proxy_protocol.h b/util/proxy_protocol.h index 13cab9d74..ca81065bf 100644 --- a/util/proxy_protocol.h +++ b/util/proxy_protocol.h @@ -42,7 +42,7 @@ #ifndef PROXY_PROTOCOL_H #define PROXY_PROTOCOL_H -#include "sldns/sbuffer.h" +#include "config.h" /** PROXYv2 minimum header size */ #define PP2_HEADER_SIZE 16 @@ -51,11 +51,11 @@ #define PP2_SIG "\x0D\x0A\x0D\x0A\x00\x0D\x0A\x51\x55\x49\x54\x0A" #define PP2_SIG_LEN 12 -/** PROXYv2 version */ +/** PROXYv2 version (protocol value) */ #define PP2_VERSION 0x2 /** - * PROXYv2 command. + * PROXYv2 command (protocol value). */ enum pp2_command { PP2_CMD_LOCAL = 0x0, @@ -63,7 +63,7 @@ enum pp2_command { }; /** - * PROXYv2 address family. + * PROXYv2 address family (protocol value). */ enum pp2_af { PP2_AF_UNSPEC = 0x0, @@ -73,7 +73,7 @@ enum pp2_af { }; /** - * PROXYv2 protocol. + * PROXYv2 protocol (protocol value). */ enum pp2_protocol { PP2_PROT_UNSPEC = 0x0, @@ -81,6 +81,19 @@ enum pp2_protocol { PP2_PROT_DGRAM = 0x2 }; +/** + * Expected combinations of address family and protocol values used in checks. + */ +enum pp2_af_protocol_combination { + PP2_UNSPEC_UNSPEC = (PP2_AF_UNSPEC<<4)|PP2_PROT_UNSPEC, + PP2_INET_STREAM = (PP2_AF_INET<<4)|PP2_PROT_STREAM, + PP2_INET_DGRAM = (PP2_AF_INET<<4)|PP2_PROT_DGRAM, + PP2_INET6_STREAM = (PP2_AF_INET6<<4)|PP2_PROT_STREAM, + PP2_INET6_DGRAM = (PP2_AF_INET6<<4)|PP2_PROT_DGRAM, + PP2_UNIX_STREAM = (PP2_AF_UNIX<<4)|PP2_PROT_STREAM, + PP2_UNIX_DGRAM = (PP2_AF_UNIX<<4)|PP2_PROT_DGRAM +}; + /** * PROXYv2 header. */ @@ -109,23 +122,56 @@ struct pp2_header { } addr; }; +/** + * PROXY parse errors. + */ +enum pp_parse_errors { + PP_PARSE_NOERROR = 0, + PP_PARSE_SIZE, + PP_PARSE_WRONG_HEADERv2, + PP_PARSE_UNKNOWN_CMD, + PP_PARSE_UNKNOWN_FAM_PROT, +}; + +/** + * Initialize the internal proxy structure. + * @param write_uint16: pointer to a function that can write uint16. + * @param write_uint32: pointer to a function that can write uint32. + */ +void pp_init(void (*write_uint16)(void* buf, uint16_t data), + void (*write_uint32)(void* buf, uint32_t data)); + +/** + * Lookup the parsing error description. + * @param error: parsing error from pp2_read_header. + * @return the description. + */ +const char* pp_lookup_error(enum pp_parse_errors error); + /** * Write a PROXYv2 header at the current position of the buffer. - * @param buf: the buffer to write to. + * @param buf: pointer to the buffer to write data to. + * @param buflen: available size on the buffer. * @param src: the source address. * @param stream: if the protocol is stream or datagram. * @return 1 on success, 0 on failure. */ -int pp2_write_to_buf(struct sldns_buffer* buf, struct sockaddr_storage* src, +size_t pp2_write_to_buf(uint8_t* buf, size_t buflen, +#ifdef INET6 + struct sockaddr_storage* src, +#else + struct sockaddr_in* src, +#endif int stream); /** * Read a PROXYv2 header from the current position of the buffer. * It does initial validation and returns a pointer to the buffer position on * success. - * @param buf: the buffer to read from. - * @return the pointer to the buffer position on success, NULL on error. + * @param buf: pointer to the buffer data to read from. + * @param buflen: available size on the buffer. + * @return parsing error, 0 on success. */ -struct pp2_header* pp2_read_header(struct sldns_buffer* buf); +int pp2_read_header(uint8_t* buf, size_t buflen); #endif /* PROXY_PROTOCOL_H */ diff --git a/util/rfc_1982.c b/util/rfc_1982.c index c28deded6..cf64e21d0 100644 --- a/util/rfc_1982.c +++ b/util/rfc_1982.c @@ -39,6 +39,7 @@ * This file contains functions for RFC 1982 serial number arithmetic. */ #include "config.h" +#include "util/rfc_1982.h" int compare_1982(uint32_t a, uint32_t b) diff --git a/util/siphash.c b/util/siphash.c index 0e1b597d0..32797dff6 100644 --- a/util/siphash.c +++ b/util/siphash.c @@ -26,6 +26,11 @@ */ #include "config.h" +/** EDIT + * prevent warning from -Wmissing-prototypes + */ +#include "util/siphash.h" + /* default: SipHash-2-4 */ #define cROUNDS 2 #define dROUNDS 4 diff --git a/validator/val_anchor.c b/validator/val_anchor.c index b1a54e1f0..8466a8923 100644 --- a/validator/val_anchor.c +++ b/validator/val_anchor.c @@ -1322,3 +1322,24 @@ anchor_has_keytag(struct val_anchors* anchors, uint8_t* name, int namelabs, free(taglist); return 0; } + +struct trust_anchor* +anchors_find_any_noninsecure(struct val_anchors* anchors) +{ + struct trust_anchor* ta, *next; + lock_basic_lock(&anchors->lock); + ta=(struct trust_anchor*)rbtree_first(anchors->tree); + while((rbnode_type*)ta != RBTREE_NULL) { + next = (struct trust_anchor*)rbtree_next(&ta->node); + lock_basic_lock(&ta->lock); + if(ta->numDS != 0 || ta->numDNSKEY != 0) { + /* not an insecurepoint */ + lock_basic_unlock(&anchors->lock); + return ta; + } + lock_basic_unlock(&ta->lock); + ta = next; + } + lock_basic_unlock(&anchors->lock); + return NULL; +} diff --git a/validator/val_anchor.h b/validator/val_anchor.h index 1597a7d62..02e7e17b5 100644 --- a/validator/val_anchor.h +++ b/validator/val_anchor.h @@ -240,4 +240,12 @@ size_t anchor_list_keytags(struct trust_anchor* ta, uint16_t* list, size_t num); int anchor_has_keytag(struct val_anchors* anchors, uint8_t* name, int namelabs, size_t namelen, uint16_t dclass, uint16_t keytag); +/** + * Find an anchor that is not an insecure point, if any, or there are no + * DNSSEC verification anchors if none. + * @param anchors: anchor storage + * @return trust anchor or NULL. It is locked. + */ +struct trust_anchor* anchors_find_any_noninsecure(struct val_anchors* anchors); + #endif /* VALIDATOR_VAL_ANCHOR_H */ diff --git a/validator/validator.c b/validator/validator.c index 9de9d54db..6cd15cfc1 100644 --- a/validator/validator.c +++ b/validator/validator.c @@ -200,6 +200,17 @@ val_init(struct module_env* env, int id) log_err("validator: could not apply configuration settings."); return 0; } + if(env->cfg->disable_edns_do) { + struct trust_anchor* anchor = anchors_find_any_noninsecure( + env->anchors); + if(anchor) { + char b[LDNS_MAX_DOMAINLEN+2]; + dname_str(anchor->name, b); + log_warn("validator: disable-edns-do is enabled, but there is a trust anchor for '%s'. Since DNSSEC could not work, the disable-edns-do setting is turned off. Continuing without it.", b); + lock_basic_unlock(&anchor->lock); + env->cfg->disable_edns_do = 0; + } + } return 1; }