mirror of
https://github.com/NLnetLabs/unbound.git
synced 2024-09-21 06:37:08 +00:00
Merge branch 'master' into fast-reload-option
This commit is contained in:
commit
a00c28790b
@ -1298,7 +1298,8 @@ remote.lo remote.o: $(srcdir)/daemon/remote.c config.h $(srcdir)/daemon/remote.h
|
||||
$(srcdir)/validator/val_anchor.h $(srcdir)/iterator/iterator.h $(srcdir)/services/outbound_list.h \
|
||||
$(srcdir)/iterator/iter_fwd.h $(srcdir)/iterator/iter_hints.h $(srcdir)/iterator/iter_delegpt.h \
|
||||
$(srcdir)/services/outside_network.h $(srcdir)/sldns/str2wire.h $(srcdir)/sldns/parseutil.h \
|
||||
$(srcdir)/sldns/wire2str.h $(srcdir)/util/locks.h $(srcdir)/util/ub_event.h \
|
||||
$(srcdir)/sldns/wire2str.h $(srcdir)/util/edns.h \
|
||||
$(srcdir)/util/locks.h $(srcdir)/util/ub_event.h \
|
||||
$(srcdir)/util/tcp_conn_limit.h $(srcdir)/util/edns.h $(srcdir)/validator/val_neg.h \
|
||||
$(srcdir)/iterator/iter_utils.h $(srcdir)/iterator/iter_donotq.h $(srcdir)/iterator/iter_priv.h
|
||||
stats.lo stats.o: $(srcdir)/daemon/stats.c config.h $(srcdir)/daemon/stats.h $(srcdir)/util/timehist.h \
|
||||
|
@ -1499,6 +1499,7 @@ struct sockaddr_storage;
|
||||
# define calloc(n,s) unbound_stat_calloc_log(n, s, __FILE__, __LINE__, __func__)
|
||||
# define free(p) unbound_stat_free_log(p, __FILE__, __LINE__, __func__)
|
||||
# define realloc(p,s) unbound_stat_realloc_log(p, s, __FILE__, __LINE__, __func__)
|
||||
# define strdup(s) unbound_stat_strdup_log(s, __FILE__, __LINE__, __func__)
|
||||
void *unbound_stat_malloc(size_t size);
|
||||
void *unbound_stat_calloc(size_t nmemb, size_t size);
|
||||
void unbound_stat_free(void *ptr);
|
||||
@ -1511,6 +1512,8 @@ void unbound_stat_free_log(void *ptr, const char* file, int line,
|
||||
const char* func);
|
||||
void *unbound_stat_realloc_log(void *ptr, size_t size, const char* file,
|
||||
int line, const char* func);
|
||||
char *unbound_stat_strdup_log(const char *s, const char* file, int line,
|
||||
const char* func);
|
||||
#elif defined(UNBOUND_ALLOC_LITE)
|
||||
# include "util/alloc.h"
|
||||
#endif /* UNBOUND_ALLOC_LITE and UNBOUND_ALLOC_STATS */
|
||||
|
27
configure
vendored
27
configure
vendored
@ -1,6 +1,6 @@
|
||||
#! /bin/sh
|
||||
# Guess values for system-dependent variables and create Makefiles.
|
||||
# Generated by GNU Autoconf 2.71 for unbound 1.20.1.
|
||||
# Generated by GNU Autoconf 2.71 for unbound 1.21.1.
|
||||
#
|
||||
# Report bugs to <unbound-bugs@nlnetlabs.nl or https://github.com/NLnetLabs/unbound/issues>.
|
||||
#
|
||||
@ -622,8 +622,8 @@ MAKEFLAGS=
|
||||
# Identity of this package.
|
||||
PACKAGE_NAME='unbound'
|
||||
PACKAGE_TARNAME='unbound'
|
||||
PACKAGE_VERSION='1.20.1'
|
||||
PACKAGE_STRING='unbound 1.20.1'
|
||||
PACKAGE_VERSION='1.21.1'
|
||||
PACKAGE_STRING='unbound 1.21.1'
|
||||
PACKAGE_BUGREPORT='unbound-bugs@nlnetlabs.nl or https://github.com/NLnetLabs/unbound/issues'
|
||||
PACKAGE_URL=''
|
||||
|
||||
@ -1508,7 +1508,7 @@ if test "$ac_init_help" = "long"; then
|
||||
# Omit some internal or obsolete options to make the list less imposing.
|
||||
# This message is too long to be a string in the A/UX 3.1 sh.
|
||||
cat <<_ACEOF
|
||||
\`configure' configures unbound 1.20.1 to adapt to many kinds of systems.
|
||||
\`configure' configures unbound 1.21.1 to adapt to many kinds of systems.
|
||||
|
||||
Usage: $0 [OPTION]... [VAR=VALUE]...
|
||||
|
||||
@ -1574,7 +1574,7 @@ fi
|
||||
|
||||
if test -n "$ac_init_help"; then
|
||||
case $ac_init_help in
|
||||
short | recursive ) echo "Configuration of unbound 1.20.1:";;
|
||||
short | recursive ) echo "Configuration of unbound 1.21.1:";;
|
||||
esac
|
||||
cat <<\_ACEOF
|
||||
|
||||
@ -1822,7 +1822,7 @@ fi
|
||||
test -n "$ac_init_help" && exit $ac_status
|
||||
if $ac_init_version; then
|
||||
cat <<\_ACEOF
|
||||
unbound configure 1.20.1
|
||||
unbound configure 1.21.1
|
||||
generated by GNU Autoconf 2.71
|
||||
|
||||
Copyright (C) 2021 Free Software Foundation, Inc.
|
||||
@ -2479,7 +2479,7 @@ cat >config.log <<_ACEOF
|
||||
This file contains any messages produced by compilers while
|
||||
running configure, to aid debugging if configure makes a mistake.
|
||||
|
||||
It was created by unbound $as_me 1.20.1, which was
|
||||
It was created by unbound $as_me 1.21.1, which was
|
||||
generated by GNU Autoconf 2.71. Invocation command line was
|
||||
|
||||
$ $0$ac_configure_args_raw
|
||||
@ -3241,13 +3241,13 @@ ac_compiler_gnu=$ac_cv_c_compiler_gnu
|
||||
|
||||
UNBOUND_VERSION_MAJOR=1
|
||||
|
||||
UNBOUND_VERSION_MINOR=20
|
||||
UNBOUND_VERSION_MINOR=21
|
||||
|
||||
UNBOUND_VERSION_MICRO=1
|
||||
|
||||
|
||||
LIBUNBOUND_CURRENT=9
|
||||
LIBUNBOUND_REVISION=28
|
||||
LIBUNBOUND_REVISION=29
|
||||
LIBUNBOUND_AGE=1
|
||||
# 1.0.0 had 0:12:0
|
||||
# 1.0.1 had 0:13:0
|
||||
@ -3342,7 +3342,8 @@ LIBUNBOUND_AGE=1
|
||||
# 1.19.2 had 9:25:1
|
||||
# 1.19.3 had 9:26:1
|
||||
# 1.20.0 had 9:27:1
|
||||
# 1.20.1 had 9:28:1
|
||||
# 1.21.0 had 9:28:1
|
||||
# 1.21.1 had 9:29:1
|
||||
|
||||
# Current -- the number of the binary API that we're implementing
|
||||
# Revision -- which iteration of the implementation of the binary
|
||||
@ -24652,7 +24653,7 @@ printf "%s\n" "#define MAXSYSLOGMSGLEN 10240" >>confdefs.h
|
||||
|
||||
|
||||
|
||||
version=1.20.1
|
||||
version=1.21.1
|
||||
|
||||
date=`date +'%b %e, %Y'`
|
||||
|
||||
@ -25164,7 +25165,7 @@ cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
|
||||
# report actual input values of CONFIG_FILES etc. instead of their
|
||||
# values after options handling.
|
||||
ac_log="
|
||||
This file was extended by unbound $as_me 1.20.1, which was
|
||||
This file was extended by unbound $as_me 1.21.1, which was
|
||||
generated by GNU Autoconf 2.71. Invocation command line was
|
||||
|
||||
CONFIG_FILES = $CONFIG_FILES
|
||||
@ -25232,7 +25233,7 @@ ac_cs_config_escaped=`printf "%s\n" "$ac_cs_config" | sed "s/^ //; s/'/'\\\\\\\\
|
||||
cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
|
||||
ac_cs_config='$ac_cs_config_escaped'
|
||||
ac_cs_version="\\
|
||||
unbound config.status 1.20.1
|
||||
unbound config.status 1.21.1
|
||||
configured by $0, generated by GNU Autoconf 2.71,
|
||||
with options \\"\$ac_cs_config\\"
|
||||
|
||||
|
10
configure.ac
10
configure.ac
@ -10,7 +10,7 @@ sinclude(dnscrypt/dnscrypt.m4)
|
||||
|
||||
# must be numbers. ac_defun because of later processing
|
||||
m4_define([VERSION_MAJOR],[1])
|
||||
m4_define([VERSION_MINOR],[20])
|
||||
m4_define([VERSION_MINOR],[21])
|
||||
m4_define([VERSION_MICRO],[1])
|
||||
AC_INIT([unbound],m4_defn([VERSION_MAJOR]).m4_defn([VERSION_MINOR]).m4_defn([VERSION_MICRO]),[unbound-bugs@nlnetlabs.nl or https://github.com/NLnetLabs/unbound/issues],[unbound])
|
||||
AC_SUBST(UNBOUND_VERSION_MAJOR, [VERSION_MAJOR])
|
||||
@ -18,7 +18,7 @@ AC_SUBST(UNBOUND_VERSION_MINOR, [VERSION_MINOR])
|
||||
AC_SUBST(UNBOUND_VERSION_MICRO, [VERSION_MICRO])
|
||||
|
||||
LIBUNBOUND_CURRENT=9
|
||||
LIBUNBOUND_REVISION=28
|
||||
LIBUNBOUND_REVISION=29
|
||||
LIBUNBOUND_AGE=1
|
||||
# 1.0.0 had 0:12:0
|
||||
# 1.0.1 had 0:13:0
|
||||
@ -113,7 +113,8 @@ LIBUNBOUND_AGE=1
|
||||
# 1.19.2 had 9:25:1
|
||||
# 1.19.3 had 9:26:1
|
||||
# 1.20.0 had 9:27:1
|
||||
# 1.20.1 had 9:28:1
|
||||
# 1.21.0 had 9:28:1
|
||||
# 1.21.1 had 9:29:1
|
||||
|
||||
# Current -- the number of the binary API that we're implementing
|
||||
# Revision -- which iteration of the implementation of the binary
|
||||
@ -2330,6 +2331,7 @@ struct sockaddr_storage;
|
||||
# define calloc(n,s) unbound_stat_calloc_log(n, s, __FILE__, __LINE__, __func__)
|
||||
# define free(p) unbound_stat_free_log(p, __FILE__, __LINE__, __func__)
|
||||
# define realloc(p,s) unbound_stat_realloc_log(p, s, __FILE__, __LINE__, __func__)
|
||||
# define strdup(s) unbound_stat_strdup_log(s, __FILE__, __LINE__, __func__)
|
||||
void *unbound_stat_malloc(size_t size);
|
||||
void *unbound_stat_calloc(size_t nmemb, size_t size);
|
||||
void unbound_stat_free(void *ptr);
|
||||
@ -2342,6 +2344,8 @@ void unbound_stat_free_log(void *ptr, const char* file, int line,
|
||||
const char* func);
|
||||
void *unbound_stat_realloc_log(void *ptr, size_t size, const char* file,
|
||||
int line, const char* func);
|
||||
char *unbound_stat_strdup_log(const char *s, const char* file, int line,
|
||||
const char* func);
|
||||
#elif defined(UNBOUND_ALLOC_LITE)
|
||||
# include "util/alloc.h"
|
||||
#endif /* UNBOUND_ALLOC_LITE and UNBOUND_ALLOC_STATS */
|
||||
|
@ -734,6 +734,14 @@ daemon_fork(struct daemon* daemon)
|
||||
"dnscrypt support");
|
||||
#endif
|
||||
}
|
||||
if(daemon->cfg->cookie_secret_file &&
|
||||
daemon->cfg->cookie_secret_file[0]) {
|
||||
if(!(daemon->cookie_secrets = cookie_secrets_create()))
|
||||
fatal_exit("Could not create cookie_secrets: out of memory");
|
||||
if(!cookie_secrets_apply_cfg(daemon->cookie_secrets,
|
||||
daemon->cfg->cookie_secret_file))
|
||||
fatal_exit("Could not setup cookie_secrets");
|
||||
}
|
||||
/* create global local_zones */
|
||||
if(!(daemon->local_zones = local_zones_create()))
|
||||
fatal_exit("Could not create local zones: out of memory");
|
||||
@ -932,6 +940,7 @@ daemon_delete(struct daemon* daemon)
|
||||
acl_list_delete(daemon->acl);
|
||||
acl_list_delete(daemon->acl_interface);
|
||||
tcl_list_delete(daemon->tcl);
|
||||
cookie_secrets_delete(daemon->cookie_secrets);
|
||||
listen_desetup_locks();
|
||||
free(daemon->chroot);
|
||||
free(daemon->pidfile);
|
||||
|
@ -58,6 +58,7 @@ struct ub_randstate;
|
||||
struct daemon_remote;
|
||||
struct respip_set;
|
||||
struct shm_main_info;
|
||||
struct cookie_secrets;
|
||||
struct fast_reload_thread;
|
||||
struct fast_reload_printq;
|
||||
|
||||
@ -146,6 +147,8 @@ struct daemon {
|
||||
#endif
|
||||
/** reuse existing cache on reload if other conditions allow it. */
|
||||
int reuse_cache;
|
||||
/** the EDNS cookie secrets from the cookie-secret-file */
|
||||
struct cookie_secrets* cookie_secrets;
|
||||
/** the fast reload thread, or NULL */
|
||||
struct fast_reload_thread* fast_reload_thread;
|
||||
/** the fast reload printq list */
|
||||
|
213
daemon/remote.c
213
daemon/remote.c
@ -3278,6 +3278,210 @@ do_rpz_disable(RES* ssl, struct worker* worker, char* arg)
|
||||
do_rpz_enable_disable(ssl, worker, arg, 0);
|
||||
}
|
||||
|
||||
/** Write the cookie secrets to file, returns `0` on failure.
|
||||
* Caller has to hold the lock. */
|
||||
static int
|
||||
cookie_secret_file_dump(RES* ssl, struct worker* worker) {
|
||||
char const* secret_file = worker->env.cfg->cookie_secret_file;
|
||||
struct cookie_secrets* cookie_secrets = worker->daemon->cookie_secrets;
|
||||
char secret_hex[UNBOUND_COOKIE_SECRET_SIZE * 2 + 1];
|
||||
FILE* f;
|
||||
size_t i;
|
||||
if(secret_file == NULL || secret_file[0]==0) {
|
||||
(void)ssl_printf(ssl, "error: no cookie secret file configured\n");
|
||||
return 0;
|
||||
}
|
||||
log_assert( secret_file != NULL );
|
||||
|
||||
/* open write only and truncate */
|
||||
if((f = fopen(secret_file, "w")) == NULL ) {
|
||||
(void)ssl_printf(ssl, "unable to open cookie secret file %s: %s",
|
||||
secret_file, strerror(errno));
|
||||
return 0;
|
||||
}
|
||||
if(cookie_secrets == NULL) {
|
||||
/* nothing to write */
|
||||
fclose(f);
|
||||
return 1;
|
||||
}
|
||||
|
||||
for(i = 0; i < cookie_secrets->cookie_count; i++) {
|
||||
struct cookie_secret const* cs = &cookie_secrets->
|
||||
cookie_secrets[i];
|
||||
ssize_t const len = hex_ntop(cs->cookie_secret,
|
||||
UNBOUND_COOKIE_SECRET_SIZE, secret_hex,
|
||||
sizeof(secret_hex));
|
||||
(void)len; /* silence unused variable warning with -DNDEBUG */
|
||||
log_assert( len == UNBOUND_COOKIE_SECRET_SIZE * 2 );
|
||||
secret_hex[UNBOUND_COOKIE_SECRET_SIZE * 2] = '\0';
|
||||
fprintf(f, "%s\n", secret_hex);
|
||||
}
|
||||
explicit_bzero(secret_hex, sizeof(secret_hex));
|
||||
fclose(f);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/** Activate cookie secret */
|
||||
static void
|
||||
do_activate_cookie_secret(RES* ssl, struct worker* worker) {
|
||||
char const* secret_file = worker->env.cfg->cookie_secret_file;
|
||||
struct cookie_secrets* cookie_secrets = worker->daemon->cookie_secrets;
|
||||
|
||||
if(secret_file == NULL || secret_file[0] == 0) {
|
||||
(void)ssl_printf(ssl, "error: no cookie secret file configured\n");
|
||||
return;
|
||||
}
|
||||
if(cookie_secrets == NULL) {
|
||||
(void)ssl_printf(ssl, "error: there are no cookie_secrets.");
|
||||
return;
|
||||
}
|
||||
lock_basic_lock(&cookie_secrets->lock);
|
||||
|
||||
if(cookie_secrets->cookie_count <= 1 ) {
|
||||
lock_basic_unlock(&cookie_secrets->lock);
|
||||
(void)ssl_printf(ssl, "error: no staging cookie secret to activate\n");
|
||||
return;
|
||||
}
|
||||
/* Only the worker 0 writes to file, the others update state. */
|
||||
if(worker->thread_num == 0 && !cookie_secret_file_dump(ssl, worker)) {
|
||||
lock_basic_unlock(&cookie_secrets->lock);
|
||||
(void)ssl_printf(ssl, "error: writing to cookie secret file: \"%s\"\n",
|
||||
secret_file);
|
||||
return;
|
||||
}
|
||||
activate_cookie_secret(cookie_secrets);
|
||||
if(worker->thread_num == 0)
|
||||
(void)cookie_secret_file_dump(ssl, worker);
|
||||
lock_basic_unlock(&cookie_secrets->lock);
|
||||
send_ok(ssl);
|
||||
}
|
||||
|
||||
/** Drop cookie secret */
|
||||
static void
|
||||
do_drop_cookie_secret(RES* ssl, struct worker* worker) {
|
||||
char const* secret_file = worker->env.cfg->cookie_secret_file;
|
||||
struct cookie_secrets* cookie_secrets = worker->daemon->cookie_secrets;
|
||||
|
||||
if(secret_file == NULL || secret_file[0] == 0) {
|
||||
(void)ssl_printf(ssl, "error: no cookie secret file configured\n");
|
||||
return;
|
||||
}
|
||||
if(cookie_secrets == NULL) {
|
||||
(void)ssl_printf(ssl, "error: there are no cookie_secrets.");
|
||||
return;
|
||||
}
|
||||
lock_basic_lock(&cookie_secrets->lock);
|
||||
|
||||
if(cookie_secrets->cookie_count <= 1 ) {
|
||||
lock_basic_unlock(&cookie_secrets->lock);
|
||||
(void)ssl_printf(ssl, "error: can not drop the currently active cookie secret\n");
|
||||
return;
|
||||
}
|
||||
/* Only the worker 0 writes to file, the others update state. */
|
||||
if(worker->thread_num == 0 && !cookie_secret_file_dump(ssl, worker)) {
|
||||
lock_basic_unlock(&cookie_secrets->lock);
|
||||
(void)ssl_printf(ssl, "error: writing to cookie secret file: \"%s\"\n",
|
||||
secret_file);
|
||||
return;
|
||||
}
|
||||
drop_cookie_secret(cookie_secrets);
|
||||
if(worker->thread_num == 0)
|
||||
(void)cookie_secret_file_dump(ssl, worker);
|
||||
lock_basic_unlock(&cookie_secrets->lock);
|
||||
send_ok(ssl);
|
||||
}
|
||||
|
||||
/** Add cookie secret */
|
||||
static void
|
||||
do_add_cookie_secret(RES* ssl, struct worker* worker, char* arg) {
|
||||
uint8_t secret[UNBOUND_COOKIE_SECRET_SIZE];
|
||||
char const* secret_file = worker->env.cfg->cookie_secret_file;
|
||||
struct cookie_secrets* cookie_secrets = worker->daemon->cookie_secrets;
|
||||
|
||||
if(secret_file == NULL || secret_file[0] == 0) {
|
||||
(void)ssl_printf(ssl, "error: no cookie secret file configured\n");
|
||||
return;
|
||||
}
|
||||
if(cookie_secrets == NULL) {
|
||||
worker->daemon->cookie_secrets = cookie_secrets_create();
|
||||
if(!worker->daemon->cookie_secrets) {
|
||||
(void)ssl_printf(ssl, "error: out of memory");
|
||||
return;
|
||||
}
|
||||
cookie_secrets = worker->daemon->cookie_secrets;
|
||||
}
|
||||
lock_basic_lock(&cookie_secrets->lock);
|
||||
|
||||
if(*arg == '\0') {
|
||||
lock_basic_unlock(&cookie_secrets->lock);
|
||||
(void)ssl_printf(ssl, "error: missing argument (cookie_secret)\n");
|
||||
return;
|
||||
}
|
||||
if(strlen(arg) != 32) {
|
||||
lock_basic_unlock(&cookie_secrets->lock);
|
||||
explicit_bzero(arg, strlen(arg));
|
||||
(void)ssl_printf(ssl, "invalid cookie secret: invalid argument length\n");
|
||||
(void)ssl_printf(ssl, "please provide a 128bit hex encoded secret\n");
|
||||
return;
|
||||
}
|
||||
if(hex_pton(arg, secret, UNBOUND_COOKIE_SECRET_SIZE) !=
|
||||
UNBOUND_COOKIE_SECRET_SIZE ) {
|
||||
lock_basic_unlock(&cookie_secrets->lock);
|
||||
explicit_bzero(secret, UNBOUND_COOKIE_SECRET_SIZE);
|
||||
explicit_bzero(arg, strlen(arg));
|
||||
(void)ssl_printf(ssl, "invalid cookie secret: parse error\n");
|
||||
(void)ssl_printf(ssl, "please provide a 128bit hex encoded secret\n");
|
||||
return;
|
||||
}
|
||||
/* Only the worker 0 writes to file, the others update state. */
|
||||
if(worker->thread_num == 0 && !cookie_secret_file_dump(ssl, worker)) {
|
||||
lock_basic_unlock(&cookie_secrets->lock);
|
||||
explicit_bzero(secret, UNBOUND_COOKIE_SECRET_SIZE);
|
||||
explicit_bzero(arg, strlen(arg));
|
||||
(void)ssl_printf(ssl, "error: writing to cookie secret file: \"%s\"\n",
|
||||
secret_file);
|
||||
return;
|
||||
}
|
||||
add_cookie_secret(cookie_secrets, secret, UNBOUND_COOKIE_SECRET_SIZE);
|
||||
explicit_bzero(secret, UNBOUND_COOKIE_SECRET_SIZE);
|
||||
if(worker->thread_num == 0)
|
||||
(void)cookie_secret_file_dump(ssl, worker);
|
||||
lock_basic_unlock(&cookie_secrets->lock);
|
||||
explicit_bzero(arg, strlen(arg));
|
||||
send_ok(ssl);
|
||||
}
|
||||
|
||||
/** Print cookie secrets */
|
||||
static void
|
||||
do_print_cookie_secrets(RES* ssl, struct worker* worker) {
|
||||
struct cookie_secrets* cookie_secrets = worker->daemon->cookie_secrets;
|
||||
char secret_hex[UNBOUND_COOKIE_SECRET_SIZE * 2 + 1];
|
||||
int i;
|
||||
|
||||
if(!cookie_secrets)
|
||||
return; /* Output is empty. */
|
||||
lock_basic_lock(&cookie_secrets->lock);
|
||||
for(i = 0; (size_t)i < cookie_secrets->cookie_count; i++) {
|
||||
struct cookie_secret const* cs = &cookie_secrets->
|
||||
cookie_secrets[i];
|
||||
ssize_t const len = hex_ntop(cs->cookie_secret,
|
||||
UNBOUND_COOKIE_SECRET_SIZE, secret_hex,
|
||||
sizeof(secret_hex));
|
||||
(void)len; /* silence unused variable warning with -DNDEBUG */
|
||||
log_assert( len == UNBOUND_COOKIE_SECRET_SIZE * 2 );
|
||||
secret_hex[UNBOUND_COOKIE_SECRET_SIZE * 2] = '\0';
|
||||
if (i == 0)
|
||||
(void)ssl_printf(ssl, "active : %s\n", secret_hex);
|
||||
else if (cookie_secrets->cookie_count == 2)
|
||||
(void)ssl_printf(ssl, "staging: %s\n", secret_hex);
|
||||
else
|
||||
(void)ssl_printf(ssl, "staging[%d]: %s\n", i,
|
||||
secret_hex);
|
||||
}
|
||||
lock_basic_unlock(&cookie_secrets->lock);
|
||||
explicit_bzero(secret_hex, sizeof(secret_hex));
|
||||
}
|
||||
|
||||
/** check for name with end-of-string, space or tab after it */
|
||||
static int
|
||||
cmdcmp(char* p, const char* cmd, size_t len)
|
||||
@ -3413,6 +3617,9 @@ execute_cmd(struct daemon_remote* rc, struct rc_state* s, RES* ssl, char* cmd,
|
||||
} else if(cmdcmp(p, "view_local_datas", 16)) {
|
||||
do_view_datas_add(rc, ssl, worker, skipwhite(p+16));
|
||||
return;
|
||||
} else if(cmdcmp(p, "print_cookie_secrets", 20)) {
|
||||
do_print_cookie_secrets(ssl, worker);
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef THREADS_DISABLED
|
||||
@ -3477,6 +3684,12 @@ execute_cmd(struct daemon_remote* rc, struct rc_state* s, RES* ssl, char* cmd,
|
||||
do_rpz_enable(ssl, worker, skipwhite(p+10));
|
||||
} else if(cmdcmp(p, "rpz_disable", 11)) {
|
||||
do_rpz_disable(ssl, worker, skipwhite(p+11));
|
||||
} else if(cmdcmp(p, "add_cookie_secret", 17)) {
|
||||
do_add_cookie_secret(ssl, worker, skipwhite(p+17));
|
||||
} else if(cmdcmp(p, "drop_cookie_secret", 18)) {
|
||||
do_drop_cookie_secret(ssl, worker);
|
||||
} else if(cmdcmp(p, "activate_cookie_secret", 22)) {
|
||||
do_activate_cookie_secret(ssl, worker);
|
||||
} else {
|
||||
(void)ssl_printf(ssl, "error unknown command '%s'\n", p);
|
||||
}
|
||||
|
@ -160,9 +160,11 @@ worker_mem_report(struct worker* ATTR_UNUSED(worker),
|
||||
+ sizeof(worker->rndstate)
|
||||
+ regional_get_mem(worker->scratchpad)
|
||||
+ sizeof(*worker->env.scratch_buffer)
|
||||
+ sldns_buffer_capacity(worker->env.scratch_buffer)
|
||||
+ forwards_get_mem(worker->env.fwds)
|
||||
+ hints_get_mem(worker->env.hints);
|
||||
+ sldns_buffer_capacity(worker->env.scratch_buffer);
|
||||
if(worker->daemon->env->fwds)
|
||||
log_info("forwards=%u", (unsigned)forwards_get_mem(worker->env.fwds));
|
||||
if(worker->daemon->env->hints)
|
||||
log_info("hints=%u", (unsigned)hints_get_mem(worker->env.hints));
|
||||
if(worker->thread_num == 0)
|
||||
me += acl_list_get_mem(worker->daemon->acl);
|
||||
if(cur_serv) {
|
||||
@ -1660,7 +1662,8 @@ worker_handle_request(struct comm_point* c, void* arg, int error,
|
||||
if((ret=parse_edns_from_query_pkt(
|
||||
c->buffer, &edns, worker->env.cfg, c, repinfo,
|
||||
(worker->env.now ? *worker->env.now : time(NULL)),
|
||||
worker->scratchpad)) != 0) {
|
||||
worker->scratchpad,
|
||||
worker->daemon->cookie_secrets)) != 0) {
|
||||
struct edns_data reply_edns;
|
||||
verbose(VERB_ALGO, "worker parse edns: formerror.");
|
||||
log_addr(VERB_CLIENT, "from", &repinfo->client_addr,
|
||||
|
@ -200,18 +200,25 @@ static void tap_data_list_try_to_free_tail(struct tap_data_list* list)
|
||||
}
|
||||
|
||||
/** delete the tap structure */
|
||||
static void tap_data_free(struct tap_data* data)
|
||||
static void tap_data_free(struct tap_data* data, int free_tail)
|
||||
{
|
||||
ub_event_del(data->ev);
|
||||
ub_event_free(data->ev);
|
||||
if(!data)
|
||||
return;
|
||||
if(data->ev) {
|
||||
ub_event_del(data->ev);
|
||||
ub_event_free(data->ev);
|
||||
}
|
||||
#ifdef HAVE_SSL
|
||||
SSL_free(data->ssl);
|
||||
#endif
|
||||
close(data->fd);
|
||||
sock_close(data->fd);
|
||||
free(data->id);
|
||||
free(data->frame);
|
||||
data->data_list->d = NULL;
|
||||
tap_data_list_try_to_free_tail(data->data_list);
|
||||
if(data->data_list) {
|
||||
data->data_list->d = NULL;
|
||||
if(free_tail)
|
||||
tap_data_list_try_to_free_tail(data->data_list);
|
||||
}
|
||||
free(data);
|
||||
}
|
||||
|
||||
@ -237,7 +244,7 @@ static void tap_data_list_delete(struct tap_data_list* list)
|
||||
while(e) {
|
||||
next = e->next;
|
||||
if(e->d) {
|
||||
tap_data_free(e->d);
|
||||
tap_data_free(e->d, 0);
|
||||
e->d = NULL;
|
||||
}
|
||||
free(e);
|
||||
@ -260,7 +267,7 @@ static void tap_socket_close(struct tap_socket* s)
|
||||
{
|
||||
if(!s) return;
|
||||
if(s->fd == -1) return;
|
||||
close(s->fd);
|
||||
sock_close(s->fd);
|
||||
s->fd = -1;
|
||||
}
|
||||
|
||||
@ -992,7 +999,7 @@ static int tap_handshake(struct tap_data* data)
|
||||
return 0;
|
||||
} else if(r == 0) {
|
||||
/* closed */
|
||||
tap_data_free(data);
|
||||
tap_data_free(data, 1);
|
||||
return 0;
|
||||
} else if(want == SSL_ERROR_SYSCALL) {
|
||||
/* SYSCALL and errno==0 means closed uncleanly */
|
||||
@ -1010,7 +1017,7 @@ static int tap_handshake(struct tap_data* data)
|
||||
if(!silent)
|
||||
log_err("SSL_handshake syscall: %s",
|
||||
strerror(errno));
|
||||
tap_data_free(data);
|
||||
tap_data_free(data, 1);
|
||||
return 0;
|
||||
} else {
|
||||
unsigned long err = ERR_get_error();
|
||||
@ -1020,7 +1027,7 @@ static int tap_handshake(struct tap_data* data)
|
||||
verbose(VERB_OPS, "ssl handshake failed "
|
||||
"from %s", data->id);
|
||||
}
|
||||
tap_data_free(data);
|
||||
tap_data_free(data, 1);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
@ -1028,7 +1035,7 @@ static int tap_handshake(struct tap_data* data)
|
||||
data->ssl_handshake_done = 1;
|
||||
if(!tap_check_peer(data)) {
|
||||
/* closed */
|
||||
tap_data_free(data);
|
||||
tap_data_free(data, 1);
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
@ -1054,7 +1061,7 @@ void dtio_tap_callback(int ATTR_UNUSED(fd), short ATTR_UNUSED(bits), void* arg)
|
||||
if(verbosity>=4) log_info("s recv %d", (int)ret);
|
||||
if(ret == 0) {
|
||||
/* closed or error */
|
||||
tap_data_free(data);
|
||||
tap_data_free(data, 1);
|
||||
return;
|
||||
} else if(ret == -1) {
|
||||
/* continue later */
|
||||
@ -1076,7 +1083,7 @@ void dtio_tap_callback(int ATTR_UNUSED(fd), short ATTR_UNUSED(bits), void* arg)
|
||||
data->frame = calloc(1, data->len);
|
||||
if(!data->frame) {
|
||||
log_err("out of memory");
|
||||
tap_data_free(data);
|
||||
tap_data_free(data, 1);
|
||||
return;
|
||||
}
|
||||
}
|
||||
@ -1089,7 +1096,7 @@ void dtio_tap_callback(int ATTR_UNUSED(fd), short ATTR_UNUSED(bits), void* arg)
|
||||
if(verbosity>=4) log_info("f recv %d", (int)r);
|
||||
if(r == 0) {
|
||||
/* closed or error */
|
||||
tap_data_free(data);
|
||||
tap_data_free(data, 1);
|
||||
return;
|
||||
} else if(r == -1) {
|
||||
/* continue later */
|
||||
@ -1114,13 +1121,13 @@ void dtio_tap_callback(int ATTR_UNUSED(fd), short ATTR_UNUSED(bits), void* arg)
|
||||
data->is_bidirectional = 1;
|
||||
if(verbosity) log_info("bidirectional stream");
|
||||
if(!reply_with_accept(data)) {
|
||||
tap_data_free(data);
|
||||
tap_data_free(data, 1);
|
||||
return;
|
||||
}
|
||||
} else if(data->len >= 4 && sldns_read_uint32(data->frame) ==
|
||||
FSTRM_CONTROL_FRAME_STOP && data->is_bidirectional) {
|
||||
if(!reply_with_finish(data)) {
|
||||
tap_data_free(data);
|
||||
tap_data_free(data, 1);
|
||||
return;
|
||||
}
|
||||
}
|
||||
@ -1400,6 +1407,41 @@ static int internal_unittest()
|
||||
/* clean up */
|
||||
tap_data_list_delete(socket->data_list);
|
||||
free(socket);
|
||||
|
||||
/* Start again. Add two elements */
|
||||
socket = calloc(1, sizeof(*socket));
|
||||
log_assert(socket);
|
||||
for(i=0; i<2; i++) {
|
||||
datas[i] = calloc(1, sizeof(struct tap_data));
|
||||
log_assert(datas[i]);
|
||||
log_assert(tap_data_list_insert(&socket->data_list, datas[i]));
|
||||
}
|
||||
/* sanity base check */
|
||||
list = socket->data_list;
|
||||
for(i=0; list; i++) list = list->next;
|
||||
log_assert(i==2);
|
||||
|
||||
/* Free the last data, tail cannot be erased */
|
||||
list = socket->data_list;
|
||||
while(list->next) list = list->next;
|
||||
free(list->d);
|
||||
list->d = NULL;
|
||||
tap_data_list_try_to_free_tail(list);
|
||||
list = socket->data_list;
|
||||
for(i=0; list; i++) list = list->next;
|
||||
log_assert(i==2);
|
||||
|
||||
/* clean up */
|
||||
tap_data_list_delete(socket->data_list);
|
||||
free(socket);
|
||||
|
||||
if(log_get_lock()) {
|
||||
lock_basic_destroy((lock_basic_type*)log_get_lock());
|
||||
}
|
||||
checklock_stop();
|
||||
#ifdef USE_WINSOCK
|
||||
WSACleanup();
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -1531,6 +1573,9 @@ int main(int argc, char** argv)
|
||||
config_delstrlist(tcp_list.first);
|
||||
config_delstrlist(tls_list.first);
|
||||
|
||||
if(log_get_lock()) {
|
||||
lock_basic_destroy((lock_basic_type*)log_get_lock());
|
||||
}
|
||||
checklock_stop();
|
||||
#ifdef USE_WINSOCK
|
||||
WSACleanup();
|
||||
|
@ -1,3 +1,61 @@
|
||||
9 August 2024: Wouter
|
||||
- Fix spelling for the cache-min-negative-ttl entry in the
|
||||
example.conf.
|
||||
- Tag for release 1.21.0, the repository continues with 1.21.1
|
||||
in development.
|
||||
|
||||
8 August 2024: Wouter
|
||||
- Fix CAMP issues with global quota. Thanks to Huayi Duan, Marco
|
||||
Bearzi, Jodok Vieli, and Cagin Tanir from NetSec group, ETH Zurich.
|
||||
- Fix CacheFlush issues with limit on NS RRs. Thanks to Yehuda Afek,
|
||||
Anat Bremler-Barr, Shoham Danino and Yuval Shavitt (Tel-Aviv
|
||||
University and Reichman University).
|
||||
- Set version number to 1.21.0 for release. This has tag 1.21.0rc1.
|
||||
- Fix that for windows the module startup is called and sets up
|
||||
the module-config.
|
||||
|
||||
2 August 2024: Wouter
|
||||
- Fix that alloc stats has strdup checks, it stops debuggers from
|
||||
complaining about mismatch at free time.
|
||||
- Fix testbound for alloc stats strdup in util/alloc.c.
|
||||
- Merge #1090: Cookie secret file. Adds
|
||||
`cookie-secret-file: "unbound_cookiesecrets.txt"` option to store
|
||||
cookie secrets for EDNS COOKIE secret rollover. The remote control
|
||||
add_cookie_secret, activate_cookie_secret and drop_cookie_secret
|
||||
commands can be used for rollover, the command print_cookie_secrets
|
||||
shows the values in use.
|
||||
- Fix that alloc stats for forwards and hints are printed, and when
|
||||
alloc stats is enabled, the unit test for unbound control waits for
|
||||
reloads to complete.
|
||||
|
||||
1 August 2024: Wouter
|
||||
- Fix dnstap test program, cleans up to have clean memory on exit,
|
||||
for tap_data_free, does not delete NULL items. Also it does not try
|
||||
to free the tail, specifically in the free of the list since that
|
||||
picked up the next item in the list for its loop causing invalid
|
||||
free. Added internal unit test to unbound-dnstap-socket for that.
|
||||
- Fix that the worker mem report with alloc stats does not attempt
|
||||
to print memory use of forwards and hints if they have been
|
||||
deleted already.
|
||||
|
||||
31 July 2024: Wouter
|
||||
- Fix for #1114: Fix that cache fill for forward-host names is
|
||||
performed, so that with nonzero target-fetch-policy it fetches
|
||||
forwarder addresses and uses them from cache. Also updated that
|
||||
delegation point cache fill routines use CDflag for AAAA message
|
||||
lookups, so that its negative lookup stops a recursion since the
|
||||
cache uses the bit for disambiguation for dns64 but the recursion
|
||||
uses CDflag for the AAAA target lookups, so the check correctly
|
||||
stops a useless recursion by its cache lookup.
|
||||
|
||||
30 July 2024: Wouter
|
||||
- Fix to document parameters of auth_zone_verify_zonemd_with_key.
|
||||
|
||||
25 July 2024: Wouter
|
||||
- Add root key 38696 from 2024 for DNSSEC validation. It is added
|
||||
to the default root keys in unbound-anchor. The content can be
|
||||
inspected with `unbound-anchor -l`.
|
||||
|
||||
23 July 2024: Yorgos
|
||||
- Fix #1106: ratelimit-below-domain logs the wrong FROM address.
|
||||
- Cleanup ede.tdir test.
|
||||
|
@ -228,7 +228,7 @@ server:
|
||||
|
||||
# the time to live (TTL) value lower bound, in seconds. Default 0.
|
||||
# For negative responses in the cache. If disabled, default,
|
||||
# cache-min-tll applies if configured.
|
||||
# cache-min-ttl applies if configured.
|
||||
# cache-min-negative-ttl: 0
|
||||
|
||||
# the time to live (TTL) value for cached roundtrip times, lameness and
|
||||
@ -1044,6 +1044,11 @@ server:
|
||||
# example value "000102030405060708090a0b0c0d0e0f".
|
||||
# cookie-secret: <128 bit random hex string>
|
||||
|
||||
# File with cookie secrets, the 'cookie-secret:' option is ignored
|
||||
# and the file can be managed to have staging and active secrets
|
||||
# with remote control commands. Disabled with "". Default is "".
|
||||
# cookie-secret-file: "/usr/local/etc/unbound_cookiesecrets.txt"
|
||||
|
||||
# Enable to attach Extended DNS Error codes (RFC8914) to responses.
|
||||
# ede: no
|
||||
|
||||
|
@ -433,6 +433,41 @@ Remove a list of \fIlocal_data\fR for given view from stdin. Like local_datas_re
|
||||
.TP
|
||||
.B view_local_datas \fIview\fR
|
||||
Add a list of \fIlocal_data\fR for given view from stdin. Like local_datas.
|
||||
.TP
|
||||
.B add_cookie_secret <secret>
|
||||
Add or replace a cookie secret persistently. <secret> needs to be an 128 bit
|
||||
hex string.
|
||||
.IP
|
||||
Cookie secrets can be either \fIactive\fR or \fIstaging\fR. \fIActive\fR cookie
|
||||
secrets are used to create DNS Cookies, but verification of a DNS Cookie
|
||||
succeeds with any of the \fIactive\fR or \fIstaging\fR cookie secrets. The
|
||||
state of the current cookie secrets can be printed with the
|
||||
\fBprint_cookie_secrets\fR command.
|
||||
.IP
|
||||
When there are no cookie secrets configured yet, the <secret> is added as
|
||||
\fIactive\fR. If there is already an \fIactive\fR cookie secret, the <secret>
|
||||
is added as \fIstaging\fR or replacing an existing \fIstaging\fR secret.
|
||||
.IP
|
||||
To "roll" a cookie secret used in an anycast set. The new secret has to be
|
||||
added as staging secret to \fBall\fR nodes in the anycast set. When \fBall\fR
|
||||
nodes can verify DNS Cookies with the new secret, the new secret can be
|
||||
activated with the \fBactivate_cookie_secret\fR command. After \fBall\fR nodes
|
||||
have the new secret \fIactive\fR for at least one hour, the previous secret can
|
||||
be dropped with the \fBdrop_cookie_secret\fR command.
|
||||
.IP
|
||||
Persistence is accomplished by writing to a file which if configured with the
|
||||
\fBcookie\-secret\-file\fR option in the server section of the config file.
|
||||
This is disabled by default, "".
|
||||
.TP
|
||||
.B drop_cookie_secret
|
||||
Drop the \fIstaging\fR cookie secret.
|
||||
.TP
|
||||
.B activate_cookie_secret
|
||||
Make the current \fIstaging\fR cookie secret \fIactive\fR, and the current
|
||||
\fIactive\fR cookie secret \fIstaging\fR.
|
||||
.TP
|
||||
.B print_cookie_secrets
|
||||
Show the current configured cookie secrets with their status.
|
||||
.SH "EXIT CODE"
|
||||
The unbound\-control program exits with status code 1 on error, 0 on success.
|
||||
.SH "SET UP"
|
||||
|
@ -1983,6 +1983,20 @@ Useful to explicitly set for servers in an anycast deployment that need to
|
||||
share the secret in order to verify each other's Server Cookies.
|
||||
An example hex string would be "000102030405060708090a0b0c0d0e0f".
|
||||
Default is a 128 bits random secret generated at startup time.
|
||||
This option is ignored if a \fBcookie\-secret\-file\fR is
|
||||
present. In that case the secrets from that file are used in DNS Cookie
|
||||
calculations.
|
||||
.TP 5
|
||||
.B cookie\-secret\-file: \fI<filename>
|
||||
File from which the secrets are read used in DNS Cookie calculations. When this
|
||||
file exists, the secrets in this file are used and the secret specified by the
|
||||
\fBcookie-secret\fR option is ignored.
|
||||
Enable it by setting a filename, like "/usr/local/etc/unbound_cookiesecrets.txt".
|
||||
The content of this file must be manipulated with the \fBadd_cookie_secret\fR,
|
||||
\fBdrop_cookie_secret\fR and \fBactivate_cookie_secret\fR commands to the
|
||||
\fIunbound\-control\fR(8) tool. Please see that manpage on how to perform a
|
||||
safe cookie secret rollover.
|
||||
Default is "" (disabled).
|
||||
.TP 5
|
||||
.B edns\-client\-string: \fI<IP netblock> <string>
|
||||
Include an EDNS0 option containing configured ascii string in queries with
|
||||
|
@ -367,6 +367,47 @@ type_allowed_in_additional_section(uint16_t tp)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/** Shorten RRset */
|
||||
static void
|
||||
shorten_rrset(sldns_buffer* pkt, struct rrset_parse* rrset, int count)
|
||||
{
|
||||
/* The too large NS RRset is shortened. This is so that too large
|
||||
* content does not overwhelm the cache. It may make the rrset
|
||||
* bogus if it was signed, and then the domain is not resolved any
|
||||
* more, that is okay, the NS RRset was too large. During a referral
|
||||
* it can be shortened and then the first part of the list could
|
||||
* be used to resolve. The scrub continues to disallow glue for the
|
||||
* removed nameserver RRs and removes that too. Because the glue
|
||||
* is not marked as okay, since the RRs have been removed here. */
|
||||
int i;
|
||||
struct rr_parse* rr = rrset->rr_first, *prev = NULL;
|
||||
if(!rr)
|
||||
return;
|
||||
for(i=0; i<count; i++) {
|
||||
prev = rr;
|
||||
rr = rr->next;
|
||||
if(!rr)
|
||||
return; /* The RRset is already short. */
|
||||
}
|
||||
if(verbosity >= VERB_QUERY
|
||||
&& rrset->dname_len <= LDNS_MAX_DOMAINLEN) {
|
||||
uint8_t buf[LDNS_MAX_DOMAINLEN+1];
|
||||
dname_pkt_copy(pkt, buf, rrset->dname);
|
||||
log_nametypeclass(VERB_QUERY, "normalize: shorten RRset:", buf,
|
||||
rrset->type, ntohs(rrset->rrset_class));
|
||||
}
|
||||
/* remove further rrs */
|
||||
rrset->rr_last = prev;
|
||||
rrset->rr_count = count;
|
||||
while(rr) {
|
||||
rrset->size -= rr->size;
|
||||
rr = rr->next;
|
||||
}
|
||||
if(rrset->rr_last)
|
||||
rrset->rr_last->next = NULL;
|
||||
else rrset->rr_first = NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* This routine normalizes a response. This includes removing "irrelevant"
|
||||
* records from the answer and additional sections and (re)synthesizing
|
||||
@ -387,6 +428,7 @@ scrub_normalize(sldns_buffer* pkt, struct msg_parse* msg,
|
||||
uint8_t* sname = qinfo->qname;
|
||||
size_t snamelen = qinfo->qname_len;
|
||||
struct rrset_parse* rrset, *prev, *nsset=NULL;
|
||||
int cname_length = 0; /* number of CNAMEs, or DNAMEs */
|
||||
|
||||
if(FLAGS_GET_RCODE(msg->flags) != LDNS_RCODE_NOERROR &&
|
||||
FLAGS_GET_RCODE(msg->flags) != LDNS_RCODE_NXDOMAIN)
|
||||
@ -401,6 +443,16 @@ scrub_normalize(sldns_buffer* pkt, struct msg_parse* msg,
|
||||
prev = NULL;
|
||||
rrset = msg->rrset_first;
|
||||
while(rrset && rrset->section == LDNS_SECTION_ANSWER) {
|
||||
if(cname_length > 11 /* env->cfg.iter_scrub_cname */) {
|
||||
/* Too many CNAMEs, or DNAMEs, from the authority
|
||||
* server, scrub down the length to something
|
||||
* shorter. This deletes everything after the limit
|
||||
* is reached. The iterator is going to look up
|
||||
* the content one by one anyway. */
|
||||
remove_rrset("normalize: removing because too many cnames:",
|
||||
pkt, msg, prev, &rrset);
|
||||
continue;
|
||||
}
|
||||
if(rrset->type == LDNS_RR_TYPE_DNAME &&
|
||||
pkt_strict_sub(pkt, sname, rrset->dname)) {
|
||||
/* check if next rrset is correct CNAME. else,
|
||||
@ -420,6 +472,7 @@ scrub_normalize(sldns_buffer* pkt, struct msg_parse* msg,
|
||||
"too long");
|
||||
return 0;
|
||||
}
|
||||
cname_length++;
|
||||
if(nx && nx->type == LDNS_RR_TYPE_CNAME &&
|
||||
dname_pkt_compare(pkt, sname, nx->dname) == 0) {
|
||||
/* check next cname */
|
||||
@ -460,6 +513,7 @@ scrub_normalize(sldns_buffer* pkt, struct msg_parse* msg,
|
||||
if(rrset->type == LDNS_RR_TYPE_CNAME) {
|
||||
struct rrset_parse* nx = rrset->rrset_all_next;
|
||||
uint8_t* oldsname = sname;
|
||||
cname_length++;
|
||||
/* see if the next one is a DNAME, if so, swap them */
|
||||
if(nx && nx->section == LDNS_SECTION_ANSWER &&
|
||||
nx->type == LDNS_RR_TYPE_DNAME &&
|
||||
@ -507,6 +561,10 @@ scrub_normalize(sldns_buffer* pkt, struct msg_parse* msg,
|
||||
LDNS_SECTION_ANSWER &&
|
||||
dname_pkt_compare(pkt, oldsname,
|
||||
rrset->dname) == 0) {
|
||||
if(rrset->type == LDNS_RR_TYPE_NS &&
|
||||
rrset->rr_count > 20 /* env->cfg->iter_scrub_ns */) {
|
||||
shorten_rrset(pkt, rrset, 20 /* env->cfg->iter_scrub_ns */);
|
||||
}
|
||||
prev = rrset;
|
||||
rrset = rrset->rrset_all_next;
|
||||
}
|
||||
@ -522,6 +580,11 @@ scrub_normalize(sldns_buffer* pkt, struct msg_parse* msg,
|
||||
continue;
|
||||
}
|
||||
|
||||
if(rrset->type == LDNS_RR_TYPE_NS &&
|
||||
rrset->rr_count > 20 /* env->cfg->iter_scrub_ns */) {
|
||||
shorten_rrset(pkt, rrset, 20 /* env->cfg->iter_scrub_ns */);
|
||||
}
|
||||
|
||||
/* Mark the additional names from relevant rrset as OK. */
|
||||
/* only for RRsets that match the query name, other ones
|
||||
* will be removed by sanitize, so no additional for them */
|
||||
@ -578,6 +641,25 @@ scrub_normalize(sldns_buffer* pkt, struct msg_parse* msg,
|
||||
"RRset:", pkt, msg, prev, &rrset);
|
||||
continue;
|
||||
}
|
||||
if(rrset->rr_count > 20 /* env->cfg->iter_scrub_ns */) {
|
||||
/* If this is not a referral, and the NS RRset
|
||||
* is signed, then remove it entirely, so
|
||||
* that when it becomes bogus it does not
|
||||
* make the message that is otherwise fine
|
||||
* into a bogus message. */
|
||||
if(!(msg->an_rrsets == 0 &&
|
||||
FLAGS_GET_RCODE(msg->flags) ==
|
||||
LDNS_RCODE_NOERROR &&
|
||||
!soa_in_auth(msg) &&
|
||||
!(msg->flags & BIT_AA)) &&
|
||||
rrset->rrsig_count != 0) {
|
||||
remove_rrset("normalize: removing too large NS "
|
||||
"RRset:", pkt, msg, prev, &rrset);
|
||||
continue;
|
||||
} else {
|
||||
shorten_rrset(pkt, rrset, 20 /* env->cfg->iter_scrub_ns */);
|
||||
}
|
||||
}
|
||||
}
|
||||
/* if this is type DS and we query for type DS we just got
|
||||
* a referral answer for our type DS query, fix packet */
|
||||
|
@ -747,6 +747,14 @@ target_count_increase_nx(struct iter_qstate* iq, int num)
|
||||
iq->target_count[TARGET_COUNT_NX] += num;
|
||||
}
|
||||
|
||||
static void
|
||||
target_count_increase_global_quota(struct iter_qstate* iq, int num)
|
||||
{
|
||||
target_count_create(iq);
|
||||
if(iq->target_count)
|
||||
iq->target_count[TARGET_COUNT_GLOBAL_QUOTA] += num;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a subrequest.
|
||||
* Generate a local request event. Local events are tied to this module, and
|
||||
@ -1547,6 +1555,11 @@ processInitRequest(struct module_qstate* qstate, struct iter_qstate* iq,
|
||||
errinf(qstate, "malloc failure for forward zone");
|
||||
return error_response(qstate, id, LDNS_RCODE_SERVFAIL);
|
||||
}
|
||||
if(!cache_fill_missing(qstate->env, iq->qchase.qclass,
|
||||
qstate->region, iq->dp)) {
|
||||
errinf(qstate, "malloc failure, copy extra info into delegation point");
|
||||
return error_response(qstate, id, LDNS_RCODE_SERVFAIL);
|
||||
}
|
||||
if((qstate->query_flags&BIT_RD)==0) {
|
||||
/* If the server accepts RD=0 queries and forwards
|
||||
* with RD=1, then if the server is listed as an NS
|
||||
@ -2995,6 +3008,17 @@ processQueryTargets(struct module_qstate* qstate, struct iter_qstate* iq,
|
||||
}
|
||||
}
|
||||
|
||||
target_count_increase_global_quota(iq, 1);
|
||||
if(iq->target_count && iq->target_count[TARGET_COUNT_GLOBAL_QUOTA]
|
||||
> MAX_GLOBAL_QUOTA) {
|
||||
char s[LDNS_MAX_DOMAINLEN+1];
|
||||
dname_str(qstate->qinfo.qname, s);
|
||||
verbose(VERB_QUERY, "request %s has exceeded the maximum "
|
||||
"global quota on number of upstream queries %d", s,
|
||||
iq->target_count[TARGET_COUNT_GLOBAL_QUOTA]);
|
||||
return error_response_cache(qstate, id, LDNS_RCODE_SERVFAIL);
|
||||
}
|
||||
|
||||
/* Do not check ratelimit for forwarding queries or if we already got a
|
||||
* pass. */
|
||||
sq_check_ratelimit = (!(iq->chase_flags & BIT_RD) && !iq->ratelimit_ok);
|
||||
|
@ -53,6 +53,9 @@ struct rbtree_type;
|
||||
|
||||
/** max number of targets spawned for a query and its subqueries */
|
||||
#define MAX_TARGET_COUNT 64
|
||||
/** max number of upstream queries for a query and its subqueries, it is
|
||||
* never reset. */
|
||||
#define MAX_GLOBAL_QUOTA 128
|
||||
/** max number of target lookups per qstate, per delegation point */
|
||||
#define MAX_DP_TARGET_COUNT 16
|
||||
/** max number of nxdomains allowed for target lookups for a query and
|
||||
@ -254,6 +257,9 @@ enum target_count_variables {
|
||||
TARGET_COUNT_QUERIES,
|
||||
/** Number of nxdomain responses encountered. */
|
||||
TARGET_COUNT_NX,
|
||||
/** Global quota on number of queries to upstream servers per
|
||||
* client request, that is never reset. */
|
||||
TARGET_COUNT_GLOBAL_QUOTA,
|
||||
|
||||
/** This should stay last here, it is used for the allocation */
|
||||
TARGET_COUNT_MAX,
|
||||
|
14
services/cache/dns.c
vendored
14
services/cache/dns.c
vendored
@ -346,6 +346,13 @@ find_add_addrs(struct module_env* env, uint16_t qclass,
|
||||
* not use dns64 translation */
|
||||
neg = msg_cache_lookup(env, ns->name, ns->namelen,
|
||||
LDNS_RR_TYPE_AAAA, qclass, 0, now, 0);
|
||||
/* Because recursion for lookup uses BIT_CD, check
|
||||
* for that so it stops the recursion lookup, if a
|
||||
* negative answer is cached. Because the cache uses
|
||||
* the CD flag for type AAAA. */
|
||||
if(!neg)
|
||||
neg = msg_cache_lookup(env, ns->name, ns->namelen,
|
||||
LDNS_RR_TYPE_AAAA, qclass, BIT_CD, now, 0);
|
||||
if(neg) {
|
||||
delegpt_add_neg_msg(dp, neg);
|
||||
lock_rw_unlock(&neg->entry.lock);
|
||||
@ -405,6 +412,13 @@ cache_fill_missing(struct module_env* env, uint16_t qclass,
|
||||
* not use dns64 translation */
|
||||
neg = msg_cache_lookup(env, ns->name, ns->namelen,
|
||||
LDNS_RR_TYPE_AAAA, qclass, 0, now, 0);
|
||||
/* Because recursion for lookup uses BIT_CD, check
|
||||
* for that so it stops the recursion lookup, if a
|
||||
* negative answer is cached. Because the cache uses
|
||||
* the CD flag for type AAAA. */
|
||||
if(!neg)
|
||||
neg = msg_cache_lookup(env, ns->name, ns->namelen,
|
||||
LDNS_RR_TYPE_AAAA, qclass, BIT_CD, now, 0);
|
||||
if(neg) {
|
||||
delegpt_add_neg_msg(dp, neg);
|
||||
lock_rw_unlock(&neg->entry.lock);
|
||||
|
@ -183,7 +183,9 @@ static const char DS_TRUST_ANCHOR[] =
|
||||
/* The anchors must start on a new line with ". IN DS and end with \n"[;]
|
||||
* because the makedist script greps on the source here */
|
||||
/* anchor 20326 is from 2017 */
|
||||
". IN DS 20326 8 2 E06D44B80B8F1D39A95C0B0D7C65D08458E880409BBC683457104237C7F8EC8D\n";
|
||||
". IN DS 20326 8 2 E06D44B80B8F1D39A95C0B0D7C65D08458E880409BBC683457104237C7F8EC8D\n"
|
||||
/* anchor 38696 is from 2024 */
|
||||
". IN DS 38696 8 2 683D2D0ACB8C9B712A1948B27F741219298D0A450D612C483AF444A4C0FB2B16\n";
|
||||
|
||||
/** verbosity for this application */
|
||||
static int verb = 0;
|
||||
|
@ -190,6 +190,10 @@ usage(void)
|
||||
printf(" rpz_enable zone Enable the RPZ zone if it had previously\n");
|
||||
printf(" been disabled\n");
|
||||
printf(" rpz_disable zone Disable the RPZ zone\n");
|
||||
printf(" add_cookie_secret <secret> add (or replace) a new cookie secret <secret>\n");
|
||||
printf(" drop_cookie_secret drop a staging cookie secret\n");
|
||||
printf(" activate_cookie_secret make a staging cookie secret active\n");
|
||||
printf(" print_cookie_secrets show all cookie secrets with their status\n");
|
||||
printf("Version %s\n", PACKAGE_VERSION);
|
||||
printf("BSD licensed, see LICENSE in source package for details.\n");
|
||||
printf("Report bugs to %s\n", PACKAGE_BUGREPORT);
|
||||
|
@ -72,23 +72,6 @@ int daemon_main(int argc, char* argv[]);
|
||||
/** config files (removed at exit) */
|
||||
static struct config_strlist* cfgfiles = NULL;
|
||||
|
||||
#ifdef UNBOUND_ALLOC_STATS
|
||||
# define strdup(s) unbound_stat_strdup_log(s, __FILE__, __LINE__, __func__)
|
||||
char* unbound_stat_strdup_log(char* s, const char* file, int line,
|
||||
const char* func);
|
||||
char* unbound_stat_strdup_log(char* s, const char* file, int line,
|
||||
const char* func) {
|
||||
char* result;
|
||||
size_t len;
|
||||
if(!s) return NULL;
|
||||
len = strlen(s);
|
||||
log_info("%s:%d %s strdup(%u)", file, line, func, (unsigned)len+1);
|
||||
result = unbound_stat_malloc(len+1);
|
||||
memmove(result, s, len+1);
|
||||
return result;
|
||||
}
|
||||
#endif /* UNBOUND_ALLOC_STATS */
|
||||
|
||||
/** give commandline usage for testbound. */
|
||||
static void
|
||||
testbound_usage(void)
|
||||
|
@ -1117,7 +1117,7 @@ static void edns_ede_encode_encodedecode(struct query_info* qinfo,
|
||||
sldns_buffer_skip(pkt, 2 + 2);
|
||||
/* decode */
|
||||
unit_assert(parse_edns_from_query_pkt(pkt, edns, NULL, NULL, NULL, 0,
|
||||
region) == 0);
|
||||
region, NULL) == 0);
|
||||
}
|
||||
|
||||
static void edns_ede_encode_check(struct edns_data* edns, int* found_ede,
|
||||
|
@ -73,12 +73,53 @@ control_command () {
|
||||
$PRE/unbound-control $@ > outfile
|
||||
}
|
||||
|
||||
# Reload the server and check the reload has finished processing
|
||||
# when a lot of debug is enabled, a lot of log needs to be printed.
|
||||
control_reload () {
|
||||
prelines=`wc -l unbound.log | awk '{print $1;}'`
|
||||
cmd="$1"
|
||||
if test -z "$cmd"; then cmd="reload"; fi
|
||||
control_command -c ub.conf $cmd
|
||||
expect_exit_value 0
|
||||
# see if the reload has completed.
|
||||
lines1=`wc -l unbound.log | awk '{print $1;}'`
|
||||
count=0
|
||||
lines2=`wc -l unbound.log | awk '{print $1;}'`
|
||||
# See if the log finishes up without sleeping too long.
|
||||
while test "$lines1" -ne "$lines2"; do
|
||||
lines1=`wc -l unbound.log | awk '{print $1;}'`
|
||||
# There is no sleep here. The add and compare are a
|
||||
# brief wait.
|
||||
count=`expr "$count" + 1`
|
||||
if test "$count" -gt 30; then
|
||||
break;
|
||||
fi
|
||||
lines2=`wc -l unbound.log | awk '{print $1;}'`
|
||||
done
|
||||
if test "$lines1" -ne "$lines2"; then
|
||||
count=0
|
||||
while test "$lines1" -ne "$lines2"; do
|
||||
tail -1 unbound.log
|
||||
lines1=`wc -l unbound.log | awk '{print $1;}'`
|
||||
sleep 1
|
||||
count=`expr "$count" + 1`
|
||||
if test "$count" -gt 30; then
|
||||
echo "reload is taking too long"
|
||||
exit 1
|
||||
fi
|
||||
lines2=`wc -l unbound.log | awk '{print $1;}'`
|
||||
done
|
||||
if test "$count" -ne "0"; then
|
||||
echo "reload done with $count sec"
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
# Reload the server for a clean state
|
||||
clean_reload () {
|
||||
echo "> Reloading the server for a clean state"
|
||||
cp main.conf ub.conf
|
||||
control_command -c ub.conf reload
|
||||
expect_exit_value 0
|
||||
control_reload
|
||||
}
|
||||
|
||||
# Reload the server for a clean state and populate the cache
|
||||
@ -175,8 +216,7 @@ expect_exit_value 1
|
||||
# local-data element in the server.
|
||||
teststep "reload the server"
|
||||
echo "server: local-data: 'afterreload. IN A 5.6.7.8'" >> ub.conf
|
||||
control_command -c ub.conf reload
|
||||
expect_exit_value 0
|
||||
control_reload
|
||||
query afterreload.
|
||||
expect_answer "5.6.7.8"
|
||||
|
||||
@ -336,16 +376,14 @@ fi
|
||||
clean_reload_and_fill_cache
|
||||
|
||||
teststep "reload and check cache - should be empty"
|
||||
control_command -c ub.conf reload
|
||||
expect_exit_value 0
|
||||
control_reload
|
||||
query www.example.com +nordflag
|
||||
fail_answer "10.20.30.40"
|
||||
|
||||
clean_reload_and_fill_cache
|
||||
|
||||
teststep "reload_keep_cache and check cache - should not be empty"
|
||||
control_command -c ub.conf reload_keep_cache
|
||||
expect_exit_value 0
|
||||
control_reload reload_keep_cache
|
||||
query www.example.com +nordflag
|
||||
expect_answer "10.20.30.40"
|
||||
|
||||
@ -353,8 +391,7 @@ clean_reload_and_fill_cache
|
||||
|
||||
teststep "change msg-cache-size and reload_keep_cache - should be empty"
|
||||
echo "server: msg-cache-size: 2m" >> ub.conf
|
||||
control_command -c ub.conf reload_keep_cache
|
||||
expect_exit_value 0
|
||||
control_reload reload_keep_cache
|
||||
query www.example.com +nordflag
|
||||
fail_answer "10.20.30.40"
|
||||
|
||||
@ -362,8 +399,7 @@ clean_reload_and_fill_cache
|
||||
|
||||
teststep "change rrset-cache-size and reload_keep_cache - should be empty"
|
||||
echo "server: rrset-cache-size: 2m" >> ub.conf
|
||||
control_command -c ub.conf reload_keep_cache
|
||||
expect_exit_value 0
|
||||
control_reload reload_keep_cache
|
||||
query www.example.com +nordflag
|
||||
fail_answer "10.20.30.40"
|
||||
|
||||
@ -375,8 +411,7 @@ clean_reload_and_fill_cache
|
||||
|
||||
teststep "change num-threads and reload_keep_cache - should be empty"
|
||||
echo "server: num-threads: 2" >> ub.conf
|
||||
control_command -c ub.conf reload_keep_cache
|
||||
expect_exit_value 0
|
||||
control_reload reload_keep_cache
|
||||
query www.example.com +nordflag
|
||||
fail_answer "10.20.30.40"
|
||||
|
||||
@ -384,8 +419,7 @@ clean_reload_and_fill_cache
|
||||
|
||||
teststep "change minimal-responses and reload_keep_cache - should not be empty"
|
||||
echo "server: minimal-responses: no" >> ub.conf
|
||||
control_command -c ub.conf reload_keep_cache
|
||||
expect_exit_value 0
|
||||
control_reload reload_keep_cache
|
||||
query www.example.com +nordflag
|
||||
expect_answer "10.20.30.40"
|
||||
|
||||
|
19
testdata/cookie_file.tdir/cookie_file.conf
vendored
Normal file
19
testdata/cookie_file.tdir/cookie_file.conf
vendored
Normal file
@ -0,0 +1,19 @@
|
||||
server:
|
||||
verbosity: 7
|
||||
use-syslog: no
|
||||
directory: ""
|
||||
pidfile: "unbound.pid"
|
||||
chroot: ""
|
||||
username: ""
|
||||
do-not-query-localhost: no
|
||||
use-caps-for-id: no
|
||||
port: @SERVER_PORT@
|
||||
interface: 127.0.0.1
|
||||
cookie-secret-file: "cookie_secrets.txt"
|
||||
answer-cookie: yes
|
||||
access-control: 127.0.0.0/8 allow_cookie # BADCOOKIE for incomplete/invalid cookies
|
||||
|
||||
remote-control:
|
||||
control-enable: yes
|
||||
control-port: @CONTROL_PORT@
|
||||
control-use-cert: no
|
16
testdata/cookie_file.tdir/cookie_file.dsc
vendored
Normal file
16
testdata/cookie_file.tdir/cookie_file.dsc
vendored
Normal file
@ -0,0 +1,16 @@
|
||||
BaseName: cookie_file
|
||||
Version: 1.0
|
||||
Description: Check the cookie rollover
|
||||
CreationDate: Fri 14 Jun 11:00:00 CEST 2024
|
||||
Maintainer:
|
||||
Category:
|
||||
Component:
|
||||
CmdDepends:
|
||||
Depends:
|
||||
Help:
|
||||
Pre: cookie_file.pre
|
||||
Post: cookie_file.post
|
||||
Test: cookie_file.test
|
||||
AuxFiles:
|
||||
Passed:
|
||||
Failure:
|
10
testdata/cookie_file.tdir/cookie_file.post
vendored
Normal file
10
testdata/cookie_file.tdir/cookie_file.post
vendored
Normal file
@ -0,0 +1,10 @@
|
||||
# #-- cookie_file.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
|
||||
kill_from_pidfile "unbound.pid"
|
||||
cat unbound.log
|
24
testdata/cookie_file.tdir/cookie_file.pre
vendored
Normal file
24
testdata/cookie_file.tdir/cookie_file.pre
vendored
Normal file
@ -0,0 +1,24 @@
|
||||
# #-- cookie_file.pre--#
|
||||
PRE="../.."
|
||||
. ../common.sh
|
||||
|
||||
get_random_port 2
|
||||
SERVER_PORT=$RND_PORT
|
||||
CONTROL_PORT=$(($RND_PORT + 1))
|
||||
echo "SERVER_PORT=$SERVER_PORT" >> .tpkg.var.test
|
||||
echo "CONTROL_PORT=$CONTROL_PORT" >> .tpkg.var.test
|
||||
|
||||
# make config file
|
||||
sed \
|
||||
-e 's/@SERVER_PORT\@/'$SERVER_PORT'/' \
|
||||
-e 's/@CONTROL_PORT\@/'$CONTROL_PORT'/' \
|
||||
< cookie_file.conf > ub.conf
|
||||
|
||||
# empty cookie file
|
||||
touch cookie_secrets.txt
|
||||
|
||||
# start unbound in the background
|
||||
$PRE/unbound -d -c ub.conf > unbound.log 2>&1 &
|
||||
|
||||
cat .tpkg.var.test
|
||||
wait_unbound_up unbound.log
|
248
testdata/cookie_file.tdir/cookie_file.test
vendored
Normal file
248
testdata/cookie_file.tdir/cookie_file.test
vendored
Normal file
@ -0,0 +1,248 @@
|
||||
# #-- cookie_file.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
|
||||
|
||||
first_secret=dd3bdf9344b678b185a6f5cb60fca715
|
||||
second_secret=445536bcd2513298075a5d379663c962
|
||||
|
||||
|
||||
teststep "Add first secret"
|
||||
echo ">> add_cookie_secret $first_secret"
|
||||
$PRE/unbound-control -c ub.conf add_cookie_secret $first_secret
|
||||
# check secret is persisted
|
||||
outfile=cookie_secrets.1
|
||||
$PRE/unbound-control -c ub.conf print_cookie_secrets > $outfile
|
||||
if ! grep -q "$first_secret" $outfile
|
||||
then
|
||||
sleep 1
|
||||
$PRE/unbound-control -c ub.conf print_cookie_secrets > $outfile
|
||||
fi
|
||||
if ! grep -q "$first_secret" $outfile
|
||||
then
|
||||
sleep 1
|
||||
$PRE/unbound-control -c ub.conf print_cookie_secrets > $outfile
|
||||
fi
|
||||
if ! grep -q "$first_secret" $outfile
|
||||
then
|
||||
sleep 1
|
||||
$PRE/unbound-control -c ub.conf print_cookie_secrets > $outfile
|
||||
fi
|
||||
if ! grep -q "^active.*$first_secret" $outfile
|
||||
then
|
||||
cat $outfile
|
||||
echo "First secret was not provisioned"
|
||||
exit 1
|
||||
fi
|
||||
echo ">> print_cookie_secrets"
|
||||
cat $outfile
|
||||
|
||||
|
||||
teststep "Get a valid cookie for this secret"
|
||||
outfile=dig.output.1
|
||||
dig version.server ch txt @127.0.0.1 -p $SERVER_PORT +cookie=3132333435363738 > $outfile
|
||||
if ! grep -q "BADCOOKIE" $outfile
|
||||
then
|
||||
cat $outfile
|
||||
echo "Did not get a BADCOOKIE response for a client-only cookie"
|
||||
exit 1
|
||||
fi
|
||||
if ! grep -q "COOKIE: 3132333435363738" $outfile
|
||||
then
|
||||
cat $outfile
|
||||
echo "Did not get a cookie in the response"
|
||||
exit 1
|
||||
fi
|
||||
first_cookie=$(grep "; COOKIE:" $outfile | cut -d ' ' -f 3)
|
||||
cat $outfile
|
||||
echo "first cookie: $first_cookie"
|
||||
|
||||
|
||||
teststep "Verify the first cookie can be reused"
|
||||
outfile=dig.output.2
|
||||
dig version.server ch txt @127.0.0.1 -p $SERVER_PORT +cookie=$first_cookie > $outfile
|
||||
if grep -q "BADCOOKIE" $outfile
|
||||
then
|
||||
cat $outfile
|
||||
echo "Got BADCOOKIE response for a valid cookie"
|
||||
exit 1
|
||||
fi
|
||||
if ! grep -q "COOKIE: $first_cookie" $outfile
|
||||
then
|
||||
cat $outfile
|
||||
echo "Did not get the same first cookie in the response"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
|
||||
teststep "Add second secret"
|
||||
outfile=cookie_secrets.2
|
||||
echo ">> add_cookie_secret $second_secret"
|
||||
$PRE/unbound-control -c ub.conf add_cookie_secret $second_secret
|
||||
$PRE/unbound-control -c ub.conf print_cookie_secrets > $outfile
|
||||
if ! grep -q "$second_secret" $outfile
|
||||
then
|
||||
sleep 1
|
||||
$PRE/unbound-control -c ub.conf print_cookie_secrets > $outfile
|
||||
fi
|
||||
if ! grep -q "$second_secret" $outfile
|
||||
then
|
||||
sleep 1
|
||||
$PRE/unbound-control -c ub.conf print_cookie_secrets > $outfile
|
||||
fi
|
||||
if ! grep -q "$second_secret" $outfile
|
||||
then
|
||||
sleep 1
|
||||
$PRE/unbound-control -c ub.conf print_cookie_secrets > $outfile
|
||||
fi
|
||||
if ! grep -q "^staging.*$second_secret" $outfile \
|
||||
|| ! grep -q "^active.*$first_secret" $outfile
|
||||
then
|
||||
cat $outfile
|
||||
echo "Secrets were not provisioned"
|
||||
exit 1
|
||||
fi
|
||||
echo ">> print_cookie_secrets"
|
||||
cat $outfile
|
||||
echo ">> cookie_secrets.txt"
|
||||
cat cookie_secrets.txt
|
||||
|
||||
|
||||
teststep "Verify the first cookie can be reused"
|
||||
outfile=dig.output.3
|
||||
dig version.server ch txt @127.0.0.1 -p $SERVER_PORT +cookie=$first_cookie > $outfile
|
||||
if grep -q "BADCOOKIE" $outfile
|
||||
then
|
||||
cat $outfile
|
||||
echo "Got BADCOOKIE response for a valid cookie"
|
||||
exit 1
|
||||
fi
|
||||
if ! grep -q "COOKIE: $first_cookie" $outfile
|
||||
then
|
||||
cat $outfile
|
||||
echo "Did not get the same first cookie in the response"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
|
||||
teststep "Secret rollover"
|
||||
outfile=cookie_secrets.3
|
||||
$PRE/unbound-control -c ub.conf activate_cookie_secret
|
||||
$PRE/unbound-control -c ub.conf print_cookie_secrets > $outfile
|
||||
if ! grep -q "^active.*$second_secret" $outfile
|
||||
then
|
||||
sleep 1
|
||||
$PRE/unbound-control -c ub.conf print_cookie_secrets > $outfile
|
||||
fi
|
||||
if ! grep -q "^active.*$second_secret" $outfile
|
||||
then
|
||||
sleep 1
|
||||
$PRE/unbound-control -c ub.conf print_cookie_secrets > $outfile
|
||||
fi
|
||||
if ! grep -q "^active.*$second_secret" $outfile
|
||||
then
|
||||
sleep 1
|
||||
$PRE/unbound-control -c ub.conf print_cookie_secrets > $outfile
|
||||
fi
|
||||
if ! grep -q "^active.*$second_secret" $outfile \
|
||||
|| ! grep -q "^staging.*$first_secret" $outfile
|
||||
then
|
||||
cat $outfile
|
||||
echo "Second secret was not activated"
|
||||
exit 1
|
||||
fi
|
||||
echo ">> activate cookie secret, printout"
|
||||
cat $outfile
|
||||
echo ">> cookie_secrets.txt"
|
||||
cat cookie_secrets.txt
|
||||
|
||||
|
||||
teststep "Verify the first cookie can be reused but a new cookie is returned from the second secret"
|
||||
outfile=dig.output.4
|
||||
dig version.server ch txt @127.0.0.1 -p $SERVER_PORT +cookie=$first_cookie > $outfile
|
||||
if grep -q "BADCOOKIE" $outfile
|
||||
then
|
||||
cat $outfile
|
||||
echo "Got BADCOOKIE response for a valid cookie"
|
||||
exit 1
|
||||
fi
|
||||
if ! grep -q "COOKIE: 3132333435363738" $outfile
|
||||
then
|
||||
cat $outfile
|
||||
echo "Did not get a cookie in the response"
|
||||
exit 1
|
||||
fi
|
||||
if grep -q "COOKIE: $first_cookie" $outfile
|
||||
then
|
||||
cat $outfile
|
||||
echo "Got the same first cookie in the response while the second secret is active"
|
||||
exit 1
|
||||
fi
|
||||
second_cookie=$(grep "; COOKIE:" $outfile | cut -d ' ' -f 3)
|
||||
cat $outfile
|
||||
echo "second cookie: $second_cookie"
|
||||
|
||||
|
||||
teststep "Drop cookie secret"
|
||||
outfile=cookie_secrets.4
|
||||
$PRE/unbound-control -c ub.conf drop_cookie_secret
|
||||
$PRE/unbound-control -c ub.conf print_cookie_secrets > $outfile
|
||||
if grep -q "^staging.*$first_secret" $outfile
|
||||
then
|
||||
sleep 1
|
||||
$PRE/unbound-control -c ub.conf print_cookie_secrets > $outfile
|
||||
fi
|
||||
if grep -q "^staging.*$first_secret" $outfile
|
||||
then
|
||||
sleep 1
|
||||
$PRE/unbound-control -c ub.conf print_cookie_secrets > $outfile
|
||||
fi
|
||||
if grep -q "^staging.*$first_secret" $outfile
|
||||
then
|
||||
sleep 1
|
||||
$PRE/unbound-control -c ub.conf print_cookie_secrets > $outfile
|
||||
fi
|
||||
if grep -q "^staging.*$first_secret" $outfile
|
||||
then
|
||||
cat $outfile
|
||||
echo "First secret was not dropped"
|
||||
exit 1
|
||||
fi
|
||||
echo ">> drop cookie secret, printout"
|
||||
cat $outfile
|
||||
echo ">> cookie_secrets.txt"
|
||||
cat cookie_secrets.txt
|
||||
|
||||
|
||||
teststep "Verify the first cookie can not be reused and the second cookie is returned instead"
|
||||
outfile=dig.output.4
|
||||
dig version.server ch txt @127.0.0.1 -p $SERVER_PORT +cookie=$first_cookie > $outfile
|
||||
if ! grep -q "BADCOOKIE" $outfile
|
||||
then
|
||||
cat $outfile
|
||||
echo "Did not get BADCOOKIE response for an invalid cookie"
|
||||
exit 1
|
||||
fi
|
||||
if ! grep -q "COOKIE: 3132333435363738" $outfile
|
||||
then
|
||||
cat $outfile
|
||||
echo "Did not get a cookie in the response"
|
||||
exit 1
|
||||
fi
|
||||
if grep -q "COOKIE: $first_cookie" $outfile
|
||||
then
|
||||
cat $outfile
|
||||
echo "Got the same first cookie in the response while the second secret is active"
|
||||
exit 1
|
||||
fi
|
||||
if ! grep -q "COOKIE: $second_cookie" $outfile
|
||||
then
|
||||
cat $outfile
|
||||
echo "Did not get the same second cookie in the response"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
exit 0
|
2
testdata/dnstap.tdir/dnstap.post
vendored
2
testdata/dnstap.tdir/dnstap.post
vendored
@ -12,4 +12,6 @@ kill_pid $FWD_PID
|
||||
kill $UNBOUND_PID
|
||||
kill $UNBOUND_PID >/dev/null 2>&1
|
||||
cat unbound.log
|
||||
cat tap.log
|
||||
cat tap.errlog
|
||||
exit 0
|
||||
|
2
testdata/dnstap.tdir/dnstap.test
vendored
2
testdata/dnstap.tdir/dnstap.test
vendored
@ -122,8 +122,6 @@ if test $num_responses -gt 2; then
|
||||
fi
|
||||
|
||||
echo "> cat logfiles"
|
||||
cat tap.log
|
||||
cat tap.errlog
|
||||
cat fwd.log
|
||||
echo "> OK"
|
||||
exit 0
|
||||
|
152
testdata/fwd_name_lookup.rpl
vendored
Normal file
152
testdata/fwd_name_lookup.rpl
vendored
Normal file
@ -0,0 +1,152 @@
|
||||
; config options
|
||||
server:
|
||||
# must have target-fetch-policy to fetch forward-host name.
|
||||
target-fetch-policy: "3 2 1 0 0"
|
||||
qname-minimisation: no
|
||||
minimal-responses: no
|
||||
|
||||
forward-zone:
|
||||
name: "."
|
||||
forward-addr: 1.2.3.4
|
||||
forward-host: ns.example.com
|
||||
CONFIG_END
|
||||
|
||||
SCENARIO_BEGIN Test forward with forward-host lookup for more addresses
|
||||
|
||||
; Forward server
|
||||
RANGE_BEGIN 0 15
|
||||
ADDRESS 1.2.3.4
|
||||
ENTRY_BEGIN
|
||||
MATCH opcode qtype qname
|
||||
ADJUST copy_id
|
||||
REPLY QR NOERROR
|
||||
SECTION QUESTION
|
||||
ns.example.com. IN A
|
||||
SECTION ANSWER
|
||||
ns.example.com. IN A 1.2.3.4
|
||||
ns.example.com. IN A 1.2.3.5
|
||||
ENTRY_END
|
||||
|
||||
ENTRY_BEGIN
|
||||
MATCH opcode qtype qname
|
||||
ADJUST copy_id
|
||||
REPLY QR NOERROR
|
||||
SECTION QUESTION
|
||||
ns.example.com. IN AAAA
|
||||
SECTION ANSWER
|
||||
SECTION AUTHORITY
|
||||
example.com. IN SOA ns.example.com. host.example.com. 3 3600 300 86400 3600
|
||||
ENTRY_END
|
||||
|
||||
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 1.2.3.6
|
||||
ENTRY_END
|
||||
RANGE_END
|
||||
|
||||
; The forward server gives no answers.
|
||||
RANGE_BEGIN 20 55
|
||||
ADDRESS 1.2.3.4
|
||||
ENTRY_BEGIN
|
||||
MATCH opcode qtype qname
|
||||
ADJUST copy_id
|
||||
REPLY QR SERVFAIL
|
||||
SECTION QUESTION
|
||||
www2.example.com. IN A
|
||||
SECTION ANSWER
|
||||
ENTRY_END
|
||||
|
||||
ENTRY_BEGIN
|
||||
MATCH opcode qtype qname
|
||||
ADJUST copy_id
|
||||
REPLY QR SERVFAIL
|
||||
SECTION QUESTION
|
||||
www3.example.com. IN A
|
||||
SECTION ANSWER
|
||||
ENTRY_END
|
||||
RANGE_END
|
||||
|
||||
; The other forward server.
|
||||
RANGE_BEGIN 20 55
|
||||
ADDRESS 1.2.3.5
|
||||
ENTRY_BEGIN
|
||||
MATCH opcode qtype qname
|
||||
ADJUST copy_id
|
||||
REPLY QR NOERROR
|
||||
SECTION QUESTION
|
||||
www2.example.com. IN A
|
||||
SECTION ANSWER
|
||||
www2.example.com. IN A 1.2.3.7
|
||||
ENTRY_END
|
||||
|
||||
ENTRY_BEGIN
|
||||
MATCH opcode qtype qname
|
||||
ADJUST copy_id
|
||||
REPLY QR NOERROR
|
||||
SECTION QUESTION
|
||||
www3.example.com. IN A
|
||||
SECTION ANSWER
|
||||
www3.example.com. IN A 1.2.3.8
|
||||
ENTRY_END
|
||||
RANGE_END
|
||||
|
||||
STEP 1 QUERY
|
||||
ENTRY_BEGIN
|
||||
REPLY RD
|
||||
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 1.2.3.6
|
||||
ENTRY_END
|
||||
|
||||
; The address 1.2.3.4 is not responding so it has to fail over to the
|
||||
; address from the name lookup.
|
||||
STEP 20 QUERY
|
||||
ENTRY_BEGIN
|
||||
REPLY RD
|
||||
SECTION QUESTION
|
||||
www2.example.com. IN A
|
||||
ENTRY_END
|
||||
|
||||
STEP 30 CHECK_ANSWER
|
||||
ENTRY_BEGIN
|
||||
MATCH all
|
||||
REPLY QR RD RA NOERROR
|
||||
SECTION QUESTION
|
||||
www2.example.com. IN A
|
||||
SECTION ANSWER
|
||||
www2.example.com. IN A 1.2.3.7
|
||||
ENTRY_END
|
||||
|
||||
STEP 40 QUERY
|
||||
ENTRY_BEGIN
|
||||
REPLY RD
|
||||
SECTION QUESTION
|
||||
www3.example.com. IN A
|
||||
ENTRY_END
|
||||
|
||||
STEP 50 CHECK_ANSWER
|
||||
ENTRY_BEGIN
|
||||
MATCH all
|
||||
REPLY QR RD RA NOERROR
|
||||
SECTION QUESTION
|
||||
www3.example.com. IN A
|
||||
SECTION ANSWER
|
||||
www3.example.com. IN A 1.2.3.8
|
||||
ENTRY_END
|
||||
|
||||
SCENARIO_END
|
21
util/alloc.c
21
util/alloc.c
@ -466,6 +466,18 @@ void *unbound_stat_realloc(void *ptr, size_t size)
|
||||
memcpy(res+8, &mem_special, sizeof(mem_special));
|
||||
return res+16;
|
||||
}
|
||||
/** strdup with stats */
|
||||
char *unbound_stat_strdup(const char* s)
|
||||
{
|
||||
size_t len;
|
||||
char* res;
|
||||
if(!s) return NULL;
|
||||
len = strlen(s);
|
||||
res = unbound_stat_malloc(len+1);
|
||||
if(!res) return NULL;
|
||||
memmove(res, s, len+1);
|
||||
return res;
|
||||
}
|
||||
|
||||
/** log to file where alloc was done */
|
||||
void *unbound_stat_malloc_log(size_t size, const char* file, int line,
|
||||
@ -507,6 +519,15 @@ void *unbound_stat_realloc_log(void *ptr, size_t size, const char* file,
|
||||
return unbound_stat_realloc(ptr, size);
|
||||
}
|
||||
|
||||
/** log to file where strdup was done */
|
||||
char *unbound_stat_strdup_log(const char *s, const char* file, int line,
|
||||
const char* func)
|
||||
{
|
||||
log_info("%s:%d %s strdup size %u", file, line, func,
|
||||
(s?(unsigned)strlen(s)+1:0));
|
||||
return unbound_stat_strdup(s);
|
||||
}
|
||||
|
||||
#endif /* UNBOUND_ALLOC_STATS */
|
||||
#ifdef UNBOUND_ALLOC_LITE
|
||||
#undef malloc
|
||||
|
@ -387,6 +387,7 @@ config_create(void)
|
||||
memset(cfg->cookie_secret, 0, sizeof(cfg->cookie_secret));
|
||||
cfg->cookie_secret_len = 16;
|
||||
init_cookie_secret(cfg->cookie_secret, cfg->cookie_secret_len);
|
||||
cfg->cookie_secret_file = NULL;
|
||||
#ifdef USE_CACHEDB
|
||||
if(!(cfg->cachedb_backend = strdup("testframe"))) goto error_exit;
|
||||
if(!(cfg->cachedb_secret = strdup("default"))) goto error_exit;
|
||||
@ -839,6 +840,8 @@ 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
|
||||
else S_YNO("answer-cookie:", do_answer_cookie)
|
||||
else S_STR("cookie-secret-file:", cookie_secret_file)
|
||||
#ifdef USE_CACHEDB
|
||||
else S_YNO("cachedb-no-store:", cachedb_no_store)
|
||||
else S_YNO("cachedb-check-when-serve-expired:", cachedb_check_when_serve_expired)
|
||||
@ -1336,6 +1339,8 @@ config_get_option(struct config_file* cfg, const char* opt,
|
||||
else O_LST(opt, "ipsecmod-whitelist", ipsecmod_whitelist)
|
||||
else O_YNO(opt, "ipsecmod-strict", ipsecmod_strict)
|
||||
#endif
|
||||
else O_YNO(opt, "answer-cookie", do_answer_cookie)
|
||||
else O_STR(opt, "cookie-secret-file", cookie_secret_file)
|
||||
#ifdef USE_CACHEDB
|
||||
else O_STR(opt, "backend", cachedb_backend)
|
||||
else O_STR(opt, "secret-seed", cachedb_secret)
|
||||
@ -1721,6 +1726,7 @@ config_delete(struct config_file* cfg)
|
||||
free(cfg->ipsecmod_hook);
|
||||
config_delstrlist(cfg->ipsecmod_whitelist);
|
||||
#endif
|
||||
free(cfg->cookie_secret_file);
|
||||
#ifdef USE_CACHEDB
|
||||
free(cfg->cachedb_backend);
|
||||
free(cfg->cachedb_secret);
|
||||
|
@ -750,6 +750,8 @@ struct config_file {
|
||||
uint8_t cookie_secret[40];
|
||||
/** cookie secret length */
|
||||
size_t cookie_secret_len;
|
||||
/** path to cookie secret store */
|
||||
char* cookie_secret_file;
|
||||
|
||||
/* ipset module */
|
||||
#ifdef USE_IPSET
|
||||
|
@ -582,6 +582,7 @@ udp-upstream-without-downstream{COLON} { YDVAR(1, VAR_UDP_UPSTREAM_WITHOUT_DOWNS
|
||||
tcp-connection-limit{COLON} { YDVAR(2, VAR_TCP_CONNECTION_LIMIT) }
|
||||
answer-cookie{COLON} { YDVAR(1, VAR_ANSWER_COOKIE ) }
|
||||
cookie-secret{COLON} { YDVAR(1, VAR_COOKIE_SECRET) }
|
||||
cookie-secret-file{COLON} { YDVAR(1, VAR_COOKIE_SECRET_FILE) }
|
||||
edns-client-string{COLON} { YDVAR(2, VAR_EDNS_CLIENT_STRING) }
|
||||
edns-client-string-opcode{COLON} { YDVAR(1, VAR_EDNS_CLIENT_STRING_OPCODE) }
|
||||
nsid{COLON} { YDVAR(1, VAR_NSID ) }
|
||||
|
@ -205,6 +205,7 @@ extern struct config_parser_state* cfg_parser;
|
||||
%token VAR_PROXY_PROTOCOL_PORT VAR_STATISTICS_INHIBIT_ZERO
|
||||
%token VAR_HARDEN_UNKNOWN_ADDITIONAL VAR_DISABLE_EDNS_DO VAR_CACHEDB_NO_STORE
|
||||
%token VAR_LOG_DESTADDR VAR_CACHEDB_CHECK_WHEN_SERVE_EXPIRED
|
||||
%token VAR_COOKIE_SECRET_FILE
|
||||
|
||||
%%
|
||||
toplevelvars: /* empty */ | toplevelvars toplevelvar ;
|
||||
@ -342,7 +343,7 @@ content_server: server_num_threads | server_verbosity | server_port |
|
||||
server_interface_automatic_ports | server_ede |
|
||||
server_proxy_protocol_port | server_statistics_inhibit_zero |
|
||||
server_harden_unknown_additional | server_disable_edns_do |
|
||||
server_log_destaddr
|
||||
server_log_destaddr | server_cookie_secret_file
|
||||
;
|
||||
stubstart: VAR_STUB_ZONE
|
||||
{
|
||||
@ -3998,45 +3999,52 @@ server_cookie_secret: VAR_COOKIE_SECRET STRING_ARG
|
||||
free($2);
|
||||
}
|
||||
;
|
||||
ipsetstart: VAR_IPSET
|
||||
{
|
||||
OUTYY(("\nP(ipset:)\n"));
|
||||
cfg_parser->started_toplevel = 1;
|
||||
}
|
||||
;
|
||||
contents_ipset: contents_ipset content_ipset
|
||||
| ;
|
||||
content_ipset: ipset_name_v4 | ipset_name_v6
|
||||
;
|
||||
ipset_name_v4: VAR_IPSET_NAME_V4 STRING_ARG
|
||||
{
|
||||
#ifdef USE_IPSET
|
||||
OUTYY(("P(name-v4:%s)\n", $2));
|
||||
if(cfg_parser->cfg->ipset_name_v4)
|
||||
yyerror("ipset name v4 override, there must be one "
|
||||
"name for ip v4");
|
||||
free(cfg_parser->cfg->ipset_name_v4);
|
||||
cfg_parser->cfg->ipset_name_v4 = $2;
|
||||
#else
|
||||
OUTYY(("P(Compiled without ipset, ignoring)\n"));
|
||||
free($2);
|
||||
#endif
|
||||
}
|
||||
;
|
||||
ipset_name_v6: VAR_IPSET_NAME_V6 STRING_ARG
|
||||
server_cookie_secret_file: VAR_COOKIE_SECRET_FILE STRING_ARG
|
||||
{
|
||||
#ifdef USE_IPSET
|
||||
OUTYY(("P(name-v6:%s)\n", $2));
|
||||
if(cfg_parser->cfg->ipset_name_v6)
|
||||
yyerror("ipset name v6 override, there must be one "
|
||||
"name for ip v6");
|
||||
free(cfg_parser->cfg->ipset_name_v6);
|
||||
cfg_parser->cfg->ipset_name_v6 = $2;
|
||||
#else
|
||||
OUTYY(("P(Compiled without ipset, ignoring)\n"));
|
||||
free($2);
|
||||
#endif
|
||||
}
|
||||
OUTYY(("P(cookie_secret_file:%s)\n", $2));
|
||||
free(cfg_parser->cfg->cookie_secret_file);
|
||||
cfg_parser->cfg->cookie_secret_file = $2;
|
||||
}
|
||||
;
|
||||
ipsetstart: VAR_IPSET
|
||||
{
|
||||
OUTYY(("\nP(ipset:)\n"));
|
||||
cfg_parser->started_toplevel = 1;
|
||||
}
|
||||
;
|
||||
contents_ipset: contents_ipset content_ipset
|
||||
| ;
|
||||
content_ipset: ipset_name_v4 | ipset_name_v6
|
||||
;
|
||||
ipset_name_v4: VAR_IPSET_NAME_V4 STRING_ARG
|
||||
{
|
||||
#ifdef USE_IPSET
|
||||
OUTYY(("P(name-v4:%s)\n", $2));
|
||||
if(cfg_parser->cfg->ipset_name_v4)
|
||||
yyerror("ipset name v4 override, there must be one "
|
||||
"name for ip v4");
|
||||
free(cfg_parser->cfg->ipset_name_v4);
|
||||
cfg_parser->cfg->ipset_name_v4 = $2;
|
||||
#else
|
||||
OUTYY(("P(Compiled without ipset, ignoring)\n"));
|
||||
free($2);
|
||||
#endif
|
||||
}
|
||||
;
|
||||
ipset_name_v6: VAR_IPSET_NAME_V6 STRING_ARG
|
||||
{
|
||||
#ifdef USE_IPSET
|
||||
OUTYY(("P(name-v6:%s)\n", $2));
|
||||
if(cfg_parser->cfg->ipset_name_v6)
|
||||
yyerror("ipset name v6 override, there must be one "
|
||||
"name for ip v6");
|
||||
free(cfg_parser->cfg->ipset_name_v6);
|
||||
cfg_parser->cfg->ipset_name_v6 = $2;
|
||||
#else
|
||||
OUTYY(("P(Compiled without ipset, ignoring)\n"));
|
||||
free($2);
|
||||
#endif
|
||||
}
|
||||
;
|
||||
%%
|
||||
|
||||
|
@ -947,7 +947,8 @@ parse_packet(sldns_buffer* pkt, struct msg_parse* msg, struct regional* region)
|
||||
static int
|
||||
parse_edns_options_from_query(uint8_t* rdata_ptr, size_t rdata_len,
|
||||
struct edns_data* edns, struct config_file* cfg, struct comm_point* c,
|
||||
struct comm_reply* repinfo, uint32_t now, struct regional* region)
|
||||
struct comm_reply* repinfo, uint32_t now, struct regional* region,
|
||||
struct cookie_secrets* cookie_secrets)
|
||||
{
|
||||
/* To respond with a Keepalive option, the client connection must have
|
||||
* received one message with a TCP Keepalive EDNS option, and that
|
||||
@ -1070,13 +1071,24 @@ parse_edns_options_from_query(uint8_t* rdata_ptr, size_t rdata_len,
|
||||
&((struct sockaddr_in6*)&repinfo->remote_addr)->sin6_addr, 16);
|
||||
}
|
||||
|
||||
cookie_val_status = edns_cookie_server_validate(
|
||||
rdata_ptr, opt_len, cfg->cookie_secret,
|
||||
cfg->cookie_secret_len, cookie_is_v4,
|
||||
server_cookie, now);
|
||||
if(cfg->cookie_secret_file &&
|
||||
cfg->cookie_secret_file[0]) {
|
||||
/* Loop over the active and staging cookies. */
|
||||
cookie_val_status =
|
||||
cookie_secrets_server_validate(
|
||||
rdata_ptr, opt_len, cookie_secrets,
|
||||
cookie_is_v4, server_cookie, now);
|
||||
} else {
|
||||
/* Use the cookie option value to validate. */
|
||||
cookie_val_status = edns_cookie_server_validate(
|
||||
rdata_ptr, opt_len, cfg->cookie_secret,
|
||||
cfg->cookie_secret_len, cookie_is_v4,
|
||||
server_cookie, now);
|
||||
}
|
||||
if(cookie_val_status == COOKIE_STATUS_VALID_RENEW)
|
||||
edns->cookie_valid = 1;
|
||||
switch(cookie_val_status) {
|
||||
case COOKIE_STATUS_VALID:
|
||||
case COOKIE_STATUS_VALID_RENEW:
|
||||
edns->cookie_valid = 1;
|
||||
/* Reuse cookie */
|
||||
if(!edns_opt_list_append(
|
||||
@ -1093,12 +1105,28 @@ parse_edns_options_from_query(uint8_t* rdata_ptr, size_t rdata_len,
|
||||
edns->cookie_client = 1;
|
||||
ATTR_FALLTHROUGH
|
||||
/* fallthrough */
|
||||
case COOKIE_STATUS_VALID_RENEW:
|
||||
case COOKIE_STATUS_FUTURE:
|
||||
case COOKIE_STATUS_EXPIRED:
|
||||
case COOKIE_STATUS_INVALID:
|
||||
default:
|
||||
edns_cookie_server_write(server_cookie,
|
||||
cfg->cookie_secret, cookie_is_v4, now);
|
||||
if(cfg->cookie_secret_file &&
|
||||
cfg->cookie_secret_file[0]) {
|
||||
if(!cookie_secrets)
|
||||
break;
|
||||
lock_basic_lock(&cookie_secrets->lock);
|
||||
if(cookie_secrets->cookie_count < 1) {
|
||||
lock_basic_unlock(&cookie_secrets->lock);
|
||||
break;
|
||||
}
|
||||
edns_cookie_server_write(server_cookie,
|
||||
cookie_secrets->cookie_secrets[0].cookie_secret,
|
||||
cookie_is_v4, now);
|
||||
lock_basic_unlock(&cookie_secrets->lock);
|
||||
} else {
|
||||
edns_cookie_server_write(server_cookie,
|
||||
cfg->cookie_secret, cookie_is_v4, now);
|
||||
}
|
||||
if(!edns_opt_list_append(&edns->opt_list_out,
|
||||
LDNS_EDNS_COOKIE, 24, server_cookie,
|
||||
region)) {
|
||||
@ -1240,7 +1268,8 @@ skip_pkt_rrs(sldns_buffer* pkt, int num)
|
||||
int
|
||||
parse_edns_from_query_pkt(sldns_buffer* pkt, struct edns_data* edns,
|
||||
struct config_file* cfg, struct comm_point* c,
|
||||
struct comm_reply* repinfo, time_t now, struct regional* region)
|
||||
struct comm_reply* repinfo, time_t now, struct regional* region,
|
||||
struct cookie_secrets* cookie_secrets)
|
||||
{
|
||||
size_t rdata_len;
|
||||
uint8_t* rdata_ptr;
|
||||
@ -1286,7 +1315,7 @@ parse_edns_from_query_pkt(sldns_buffer* pkt, struct edns_data* edns,
|
||||
rdata_ptr = sldns_buffer_current(pkt);
|
||||
/* ignore rrsigs */
|
||||
return parse_edns_options_from_query(rdata_ptr, rdata_len, edns, cfg,
|
||||
c, repinfo, now, region);
|
||||
c, repinfo, now, region, cookie_secrets);
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -73,6 +73,7 @@ struct edns_option;
|
||||
struct config_file;
|
||||
struct comm_point;
|
||||
struct comm_reply;
|
||||
struct cookie_secrets;
|
||||
|
||||
/** number of buckets in parse rrset hash table. Must be power of 2. */
|
||||
#define PARSE_TABLE_SIZE 32
|
||||
@ -322,12 +323,14 @@ int skip_pkt_rrs(struct sldns_buffer* pkt, int num);
|
||||
* @param repinfo: commreply to determine the client address
|
||||
* @param now: current time
|
||||
* @param region: region to alloc results in (edns option contents)
|
||||
* @param cookie_secrets: the cookie secrets for EDNS COOKIE validation.
|
||||
* @return: 0 on success, or an RCODE on error.
|
||||
* RCODE formerr if OPT is badly formatted and so on.
|
||||
*/
|
||||
int parse_edns_from_query_pkt(struct sldns_buffer* pkt, struct edns_data* edns,
|
||||
struct config_file* cfg, struct comm_point* c,
|
||||
struct comm_reply* repinfo, time_t now, struct regional* region);
|
||||
struct comm_reply* repinfo, time_t now, struct regional* region,
|
||||
struct cookie_secrets* cookie_secrets);
|
||||
|
||||
/**
|
||||
* Calculate hash value for rrset in packet.
|
||||
|
186
util/edns.c
186
util/edns.c
@ -210,3 +210,189 @@ edns_cookie_server_validate(const uint8_t* cookie, size_t cookie_len,
|
||||
return COOKIE_STATUS_VALID_RENEW;
|
||||
return COOKIE_STATUS_VALID;
|
||||
}
|
||||
|
||||
struct cookie_secrets*
|
||||
cookie_secrets_create(void)
|
||||
{
|
||||
struct cookie_secrets* cookie_secrets = calloc(1,
|
||||
sizeof(*cookie_secrets));
|
||||
if(!cookie_secrets)
|
||||
return NULL;
|
||||
lock_basic_init(&cookie_secrets->lock);
|
||||
lock_protect(&cookie_secrets->lock, &cookie_secrets->cookie_count,
|
||||
sizeof(cookie_secrets->cookie_count));
|
||||
lock_protect(&cookie_secrets->lock, cookie_secrets->cookie_secrets,
|
||||
sizeof(cookie_secret_type)*UNBOUND_COOKIE_HISTORY_SIZE);
|
||||
return cookie_secrets;
|
||||
}
|
||||
|
||||
void
|
||||
cookie_secrets_delete(struct cookie_secrets* cookie_secrets)
|
||||
{
|
||||
if(!cookie_secrets)
|
||||
return;
|
||||
lock_basic_destroy(&cookie_secrets->lock);
|
||||
explicit_bzero(cookie_secrets->cookie_secrets,
|
||||
sizeof(cookie_secret_type)*UNBOUND_COOKIE_HISTORY_SIZE);
|
||||
free(cookie_secrets);
|
||||
}
|
||||
|
||||
/** Read the cookie secret file */
|
||||
static int
|
||||
cookie_secret_file_read(struct cookie_secrets* cookie_secrets,
|
||||
char* cookie_secret_file)
|
||||
{
|
||||
char secret[UNBOUND_COOKIE_SECRET_SIZE * 2 + 2/*'\n' and '\0'*/];
|
||||
FILE* f;
|
||||
int corrupt = 0;
|
||||
size_t count;
|
||||
|
||||
log_assert(cookie_secret_file != NULL);
|
||||
cookie_secrets->cookie_count = 0;
|
||||
f = fopen(cookie_secret_file, "r");
|
||||
/* a non-existing cookie file is not an error */
|
||||
if( f == NULL ) {
|
||||
if(errno != EPERM) {
|
||||
log_err("Could not read cookie-secret-file '%s': %s",
|
||||
cookie_secret_file, strerror(errno));
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
/* cookie secret file exists and is readable */
|
||||
for( count = 0; count < UNBOUND_COOKIE_HISTORY_SIZE; count++ ) {
|
||||
size_t secret_len = 0;
|
||||
ssize_t decoded_len = 0;
|
||||
if( fgets(secret, sizeof(secret), f) == NULL ) { break; }
|
||||
secret_len = strlen(secret);
|
||||
if( secret_len == 0 ) { break; }
|
||||
log_assert( secret_len <= sizeof(secret) );
|
||||
secret_len = secret[secret_len - 1] == '\n' ? secret_len - 1 : secret_len;
|
||||
if( secret_len != UNBOUND_COOKIE_SECRET_SIZE * 2 ) { corrupt++; break; }
|
||||
/* needed for `hex_pton`; stripping potential `\n` */
|
||||
secret[secret_len] = '\0';
|
||||
decoded_len = hex_pton(secret, cookie_secrets->cookie_secrets[count].cookie_secret,
|
||||
UNBOUND_COOKIE_SECRET_SIZE);
|
||||
if( decoded_len != UNBOUND_COOKIE_SECRET_SIZE ) { corrupt++; break; }
|
||||
cookie_secrets->cookie_count++;
|
||||
}
|
||||
fclose(f);
|
||||
return corrupt == 0;
|
||||
}
|
||||
|
||||
int
|
||||
cookie_secrets_apply_cfg(struct cookie_secrets* cookie_secrets,
|
||||
char* cookie_secret_file)
|
||||
{
|
||||
if(!cookie_secrets) {
|
||||
if(!cookie_secret_file || !cookie_secret_file[0])
|
||||
return 1; /* There is nothing to read anyway */
|
||||
log_err("Could not read cookie secrets, no structure alloced");
|
||||
return 0;
|
||||
}
|
||||
if(!cookie_secret_file_read(cookie_secrets, cookie_secret_file))
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
enum edns_cookie_val_status
|
||||
cookie_secrets_server_validate(const uint8_t* cookie, size_t cookie_len,
|
||||
struct cookie_secrets* cookie_secrets, int v4,
|
||||
const uint8_t* hash_input, uint32_t now)
|
||||
{
|
||||
size_t i;
|
||||
enum edns_cookie_val_status cookie_val_status,
|
||||
last = COOKIE_STATUS_INVALID;
|
||||
if(!cookie_secrets)
|
||||
return COOKIE_STATUS_INVALID; /* There are no cookie secrets.*/
|
||||
lock_basic_lock(&cookie_secrets->lock);
|
||||
if(cookie_secrets->cookie_count == 0) {
|
||||
lock_basic_unlock(&cookie_secrets->lock);
|
||||
return COOKIE_STATUS_INVALID; /* There are no cookie secrets.*/
|
||||
}
|
||||
for(i=0; i<cookie_secrets->cookie_count; i++) {
|
||||
cookie_val_status = edns_cookie_server_validate(cookie,
|
||||
cookie_len,
|
||||
cookie_secrets->cookie_secrets[i].cookie_secret,
|
||||
UNBOUND_COOKIE_SECRET_SIZE, v4, hash_input, now);
|
||||
if(cookie_val_status == COOKIE_STATUS_VALID ||
|
||||
cookie_val_status == COOKIE_STATUS_VALID_RENEW) {
|
||||
lock_basic_unlock(&cookie_secrets->lock);
|
||||
/* For staging cookies, write a fresh cookie. */
|
||||
if(i != 0)
|
||||
return COOKIE_STATUS_VALID_RENEW;
|
||||
return cookie_val_status;
|
||||
}
|
||||
if(last == COOKIE_STATUS_INVALID)
|
||||
last = cookie_val_status; /* Store more interesting
|
||||
failure to return. */
|
||||
}
|
||||
lock_basic_unlock(&cookie_secrets->lock);
|
||||
return last;
|
||||
}
|
||||
|
||||
void add_cookie_secret(struct cookie_secrets* cookie_secrets,
|
||||
uint8_t* secret, size_t secret_len)
|
||||
{
|
||||
log_assert(secret_len == UNBOUND_COOKIE_SECRET_SIZE);
|
||||
(void)secret_len;
|
||||
if(!cookie_secrets)
|
||||
return;
|
||||
|
||||
/* New cookie secret becomes the staging secret (position 1)
|
||||
* unless there is no active cookie yet, then it becomes the active
|
||||
* secret. If the UNBOUND_COOKIE_HISTORY_SIZE > 2 then all staging cookies
|
||||
* are moved one position down.
|
||||
*/
|
||||
if(cookie_secrets->cookie_count == 0) {
|
||||
memcpy( cookie_secrets->cookie_secrets->cookie_secret
|
||||
, secret, UNBOUND_COOKIE_SECRET_SIZE);
|
||||
cookie_secrets->cookie_count = 1;
|
||||
explicit_bzero(secret, UNBOUND_COOKIE_SECRET_SIZE);
|
||||
return;
|
||||
}
|
||||
#if UNBOUND_COOKIE_HISTORY_SIZE > 2
|
||||
memmove( &cookie_secrets->cookie_secrets[2], &cookie_secrets->cookie_secrets[1]
|
||||
, sizeof(struct cookie_secret) * (UNBOUND_COOKIE_HISTORY_SIZE - 2));
|
||||
#endif
|
||||
memcpy( cookie_secrets->cookie_secrets[1].cookie_secret
|
||||
, secret, UNBOUND_COOKIE_SECRET_SIZE);
|
||||
cookie_secrets->cookie_count = cookie_secrets->cookie_count < UNBOUND_COOKIE_HISTORY_SIZE
|
||||
? cookie_secrets->cookie_count + 1 : UNBOUND_COOKIE_HISTORY_SIZE;
|
||||
explicit_bzero(secret, UNBOUND_COOKIE_SECRET_SIZE);
|
||||
}
|
||||
|
||||
void activate_cookie_secret(struct cookie_secrets* cookie_secrets)
|
||||
{
|
||||
uint8_t active_secret[UNBOUND_COOKIE_SECRET_SIZE];
|
||||
if(!cookie_secrets)
|
||||
return;
|
||||
/* The staging secret becomes the active secret.
|
||||
* The active secret becomes a staging secret.
|
||||
* If the UNBOUND_COOKIE_HISTORY_SIZE > 2 then all staging secrets are moved
|
||||
* one position up and the previously active secret becomes the last
|
||||
* staging secret.
|
||||
*/
|
||||
if(cookie_secrets->cookie_count < 2)
|
||||
return;
|
||||
memcpy( active_secret, cookie_secrets->cookie_secrets[0].cookie_secret
|
||||
, UNBOUND_COOKIE_SECRET_SIZE);
|
||||
memmove( &cookie_secrets->cookie_secrets[0], &cookie_secrets->cookie_secrets[1]
|
||||
, sizeof(struct cookie_secret) * (UNBOUND_COOKIE_HISTORY_SIZE - 1));
|
||||
memcpy( cookie_secrets->cookie_secrets[cookie_secrets->cookie_count - 1].cookie_secret
|
||||
, active_secret, UNBOUND_COOKIE_SECRET_SIZE);
|
||||
explicit_bzero(active_secret, UNBOUND_COOKIE_SECRET_SIZE);
|
||||
}
|
||||
|
||||
void drop_cookie_secret(struct cookie_secrets* cookie_secrets)
|
||||
{
|
||||
if(!cookie_secrets)
|
||||
return;
|
||||
/* Drops a staging cookie secret. If there are more than one, it will
|
||||
* drop the last staging secret. */
|
||||
if(cookie_secrets->cookie_count < 2)
|
||||
return;
|
||||
explicit_bzero( cookie_secrets->cookie_secrets[cookie_secrets->cookie_count - 1].cookie_secret
|
||||
, UNBOUND_COOKIE_SECRET_SIZE);
|
||||
cookie_secrets->cookie_count -= 1;
|
||||
}
|
||||
|
85
util/edns.h
85
util/edns.h
@ -43,6 +43,7 @@
|
||||
#define UTIL_EDNS_H
|
||||
|
||||
#include "util/storage/dnstree.h"
|
||||
#include "util/locks.h"
|
||||
|
||||
struct edns_data;
|
||||
struct config_file;
|
||||
@ -75,6 +76,31 @@ struct edns_string_addr {
|
||||
size_t string_len;
|
||||
};
|
||||
|
||||
#define UNBOUND_COOKIE_HISTORY_SIZE 2
|
||||
#define UNBOUND_COOKIE_SECRET_SIZE 16
|
||||
|
||||
typedef struct cookie_secret cookie_secret_type;
|
||||
struct cookie_secret {
|
||||
/** cookie secret */
|
||||
uint8_t cookie_secret[UNBOUND_COOKIE_SECRET_SIZE];
|
||||
};
|
||||
|
||||
/**
|
||||
* The cookie secrets from the cookie-secret-file.
|
||||
*/
|
||||
struct cookie_secrets {
|
||||
/** lock on the structure, in case there are modifications
|
||||
* from remote control, this avoids race conditions. */
|
||||
lock_basic_type lock;
|
||||
|
||||
/** how many cookies are there in the cookies array */
|
||||
size_t cookie_count;
|
||||
|
||||
/* keep track of the last `UNBOUND_COOKIE_HISTORY_SIZE`
|
||||
* cookies as per rfc requirement .*/
|
||||
cookie_secret_type cookie_secrets[UNBOUND_COOKIE_HISTORY_SIZE];
|
||||
};
|
||||
|
||||
enum edns_cookie_val_status {
|
||||
COOKIE_STATUS_CLIENT_ONLY = -3,
|
||||
COOKIE_STATUS_FUTURE = -2,
|
||||
@ -181,4 +207,63 @@ enum edns_cookie_val_status edns_cookie_server_validate(const uint8_t* cookie,
|
||||
size_t cookie_len, const uint8_t* secret, size_t secret_len, int v4,
|
||||
const uint8_t* hash_input, uint32_t now);
|
||||
|
||||
/**
|
||||
* Create the cookie secrets structure.
|
||||
* @return the structure or NULL on failure.
|
||||
*/
|
||||
struct cookie_secrets* cookie_secrets_create(void);
|
||||
|
||||
/**
|
||||
* Delete the cookie secrets.
|
||||
* @param cookie_secrets: the cookie secrets.
|
||||
*/
|
||||
void cookie_secrets_delete(struct cookie_secrets* cookie_secrets);
|
||||
|
||||
/**
|
||||
* Apply configuration to cookie secrets, read them from file.
|
||||
* @param cookie_secrets: the cookie secrets structure.
|
||||
* @param cookie_secret_file: the file name, it is read.
|
||||
* @return false on failure.
|
||||
*/
|
||||
int cookie_secrets_apply_cfg(struct cookie_secrets* cookie_secrets,
|
||||
char* cookie_secret_file);
|
||||
|
||||
/**
|
||||
* Validate the cookie secrets, try all of them.
|
||||
* @param cookie: pointer to the cookie data.
|
||||
* @param cookie_len: the length of the cookie data.
|
||||
* @param cookie_secrets: struct of cookie secrets.
|
||||
* @param v4: if the client IP is v4 or v6.
|
||||
* @param hash_input: pointer to the hash input for validation. It needs to be:
|
||||
* Client Cookie | Version | Reserved | Timestamp | Client-IP
|
||||
* @param now: the current time.
|
||||
* return edns_cookie_val_status with the cookie validation status i.e.,
|
||||
* <=0 for invalid, else valid.
|
||||
*/
|
||||
enum edns_cookie_val_status cookie_secrets_server_validate(
|
||||
const uint8_t* cookie, size_t cookie_len,
|
||||
struct cookie_secrets* cookie_secrets, int v4,
|
||||
const uint8_t* hash_input, uint32_t now);
|
||||
|
||||
/**
|
||||
* Add a cookie secret. If there are no secrets yet, the secret will become
|
||||
* the active secret. Otherwise it will become the staging secret.
|
||||
* Active secrets are used to both verify and create new DNS Cookies.
|
||||
* Staging secrets are only used to verify DNS Cookies. Caller has to lock.
|
||||
*/
|
||||
void add_cookie_secret(struct cookie_secrets* cookie_secrets, uint8_t* secret,
|
||||
size_t secret_len);
|
||||
|
||||
/**
|
||||
* Makes the staging cookie secret active and the active secret staging.
|
||||
* Caller has to lock.
|
||||
*/
|
||||
void activate_cookie_secret(struct cookie_secrets* cookie_secrets);
|
||||
|
||||
/**
|
||||
* Drop a cookie secret. Drops the staging secret. An active secret will not
|
||||
* be dropped. Caller has to lock.
|
||||
*/
|
||||
void drop_cookie_secret(struct cookie_secrets* cookie_secrets);
|
||||
|
||||
#endif
|
||||
|
@ -47,6 +47,7 @@
|
||||
#ifdef HAVE_NETIOAPI_H
|
||||
#include <netioapi.h>
|
||||
#endif
|
||||
#include <ctype.h>
|
||||
#include "util/net_help.h"
|
||||
#include "util/log.h"
|
||||
#include "util/data/dname.h"
|
||||
@ -1871,3 +1872,42 @@ sock_close(int socket)
|
||||
closesocket(socket);
|
||||
}
|
||||
# endif /* USE_WINSOCK */
|
||||
|
||||
ssize_t
|
||||
hex_ntop(uint8_t const *src, size_t srclength, char *target, size_t targsize)
|
||||
{
|
||||
static char hexdigits[] = {
|
||||
'0', '1', '2', '3', '4', '5', '6', '7',
|
||||
'8', '9', 'a', 'b', 'c', 'd', 'e', 'f'
|
||||
};
|
||||
size_t i;
|
||||
|
||||
if (targsize < srclength * 2 + 1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
for (i = 0; i < srclength; ++i) {
|
||||
*target++ = hexdigits[src[i] >> 4U];
|
||||
*target++ = hexdigits[src[i] & 0xfU];
|
||||
}
|
||||
*target = '\0';
|
||||
return 2 * srclength;
|
||||
}
|
||||
|
||||
ssize_t
|
||||
hex_pton(const char* src, uint8_t* target, size_t targsize)
|
||||
{
|
||||
uint8_t *t = target;
|
||||
if(strlen(src) % 2 != 0 || strlen(src)/2 > targsize) {
|
||||
return -1;
|
||||
}
|
||||
while(*src) {
|
||||
if(!isxdigit((unsigned char)src[0]) ||
|
||||
!isxdigit((unsigned char)src[1]))
|
||||
return -1;
|
||||
*t++ = sldns_hexdigit_to_int(src[0]) * 16 +
|
||||
sldns_hexdigit_to_int(src[1]) ;
|
||||
src += 2;
|
||||
}
|
||||
return t-target;
|
||||
}
|
||||
|
@ -572,4 +572,13 @@ char* sock_strerror(int errn);
|
||||
/** close the socket with close, or wsa closesocket */
|
||||
void sock_close(int socket);
|
||||
|
||||
/**
|
||||
* Convert binary data to a string of hexadecimal characters.
|
||||
*/
|
||||
ssize_t hex_ntop(uint8_t const *src, size_t srclength, char *target,
|
||||
size_t targsize);
|
||||
|
||||
/** Convert hexadecimal data to binary. */
|
||||
ssize_t hex_pton(const char* src, uint8_t* target, size_t targsize);
|
||||
|
||||
#endif /* NET_HELP_H */
|
||||
|
@ -352,6 +352,10 @@ service_init(int r, struct daemon** d, struct config_file** c)
|
||||
daemon_apply_cfg(daemon, cfg);
|
||||
|
||||
if(!r) report_status(SERVICE_START_PENDING, NO_ERROR, 2300);
|
||||
if(!r) {
|
||||
if(!daemon_privileged(daemon))
|
||||
fatal_exit("could not do privileged setup");
|
||||
}
|
||||
if(!(daemon->rc = daemon_remote_create(cfg))) {
|
||||
log_err("could not set up remote-control");
|
||||
daemon_delete(daemon);
|
||||
|
Loading…
Reference in New Issue
Block a user