unbound: Vendor import 1.23.0

Release notes at
	https://nlnetlabs.nl/news/2025/Apr/24/unbound-1.23.0-released/

MFC after:	2 weeks

Merge commit '44bab727dfe28451b777dc9e47db4f748b709182'
This commit is contained in:
Cy Schubert
2025-04-25 07:48:44 -07:00
222 changed files with 41888 additions and 7173 deletions
+16 -7
View File
@@ -179,11 +179,11 @@ testcode/unitlruhash.c testcode/unitmain.c testcode/unitmsgparse.c \
testcode/unitneg.c testcode/unitregional.c testcode/unitslabhash.c \
testcode/unitverify.c testcode/readhex.c testcode/testpkts.c testcode/unitldns.c \
testcode/unitecs.c testcode/unitauth.c testcode/unitzonemd.c \
testcode/unittcpreuse.c testcode/unitdoq.c
testcode/unittcpreuse.c testcode/unitdoq.c testcode/unitinfra.c
UNITTEST_OBJ=unitanchor.lo unitdname.lo unitlruhash.lo unitmain.lo \
unitmsgparse.lo unitneg.lo unitregional.lo unitslabhash.lo unitverify.lo \
readhex.lo testpkts.lo unitldns.lo unitecs.lo unitauth.lo unitzonemd.lo \
unittcpreuse.lo unitdoq.lo
unittcpreuse.lo unitdoq.lo unitinfra.lo
UNITTEST_OBJ_LINK=$(UNITTEST_OBJ) worker_cb.lo $(COMMON_OBJ) $(SLDNS_OBJ) \
$(COMPAT_OBJ)
DAEMON_SRC=daemon/acl_list.c daemon/cachedump.c daemon/daemon.c \
@@ -509,10 +509,15 @@ util/configlexer.c: $(srcdir)/util/configlexer.lex util/configparser.h
fi
@if test ! -f $@; then echo "No $@ : need flex and bison to compile from source repository"; exit 1; fi
util/configparser.c util/configparser.h: $(srcdir)/util/configparser.y
# Builds both util/configparser.c and util/configparser.h.
# To avoid double-building we split one target out.
util/configparser.c: $(srcdir)/util/configparser.y
@-if test ! -d util; then $(INSTALL) -d util; fi
$(YACC) -d -o util/configparser.c $(srcdir)/util/configparser.y
util/configparser.h: util/configparser.c
touch $@
clean:
rm -f *.o *.d *.lo *~ tags
rm -f unbound$(EXEEXT) unbound-checkconf$(EXEEXT) unbound-host$(EXEEXT) unbound-control$(EXEEXT) unbound-anchor$(EXEEXT) unbound-control-setup libunbound.la unbound.h
@@ -876,7 +881,7 @@ view.lo view.o: $(srcdir)/services/view.c config.h $(srcdir)/services/view.h $(s
$(srcdir)/util/locks.h $(srcdir)/util/log.h $(srcdir)/services/localzone.h $(srcdir)/util/storage/dnstree.h \
$(srcdir)/util/module.h $(srcdir)/util/storage/lruhash.h $(srcdir)/util/data/msgreply.h \
$(srcdir)/util/data/packed_rrset.h $(srcdir)/util/data/msgparse.h $(srcdir)/sldns/pkthdr.h \
$(srcdir)/sldns/rrdef.h $(srcdir)/sldns/sbuffer.h $(srcdir)/util/config_file.h
$(srcdir)/sldns/rrdef.h $(srcdir)/sldns/sbuffer.h $(srcdir)/util/config_file.h $(srcdir)/respip/respip.h
rpz.lo rpz.o: $(srcdir)/services/rpz.c config.h $(srcdir)/services/rpz.h $(srcdir)/services/localzone.h \
$(srcdir)/util/rbtree.h $(srcdir)/util/locks.h $(srcdir)/util/log.h $(srcdir)/util/storage/dnstree.h \
$(srcdir)/util/module.h $(srcdir)/util/storage/lruhash.h $(srcdir)/util/data/msgreply.h \
@@ -972,7 +977,7 @@ fptr_wlist.lo fptr_wlist.o: $(srcdir)/util/fptr_wlist.c config.h $(srcdir)/util/
$(srcdir)/validator/val_nsec3.h $(srcdir)/validator/val_sigcrypt.h $(srcdir)/validator/val_kentry.h \
$(srcdir)/validator/val_neg.h $(srcdir)/validator/autotrust.h $(srcdir)/libunbound/libworker.h \
$(srcdir)/libunbound/context.h $(srcdir)/util/alloc.h $(srcdir)/libunbound/unbound-event.h \
$(srcdir)/libunbound/worker.h
$(srcdir)/libunbound/worker.h $(srcdir)/daemon/remote.h
locks.lo locks.o: $(srcdir)/util/locks.c config.h $(srcdir)/util/locks.h $(srcdir)/util/log.h
log.lo log.o: $(srcdir)/util/log.c config.h $(srcdir)/util/log.h $(srcdir)/util/locks.h $(srcdir)/sldns/sbuffer.h
mini_event.lo mini_event.o: $(srcdir)/util/mini_event.c config.h $(srcdir)/util/mini_event.h $(srcdir)/util/rbtree.h \
@@ -1059,7 +1064,7 @@ tube.lo tube.o: $(srcdir)/util/tube.c config.h $(srcdir)/util/tube.h $(srcdir)/u
$(srcdir)/libunbound/unbound.h $(srcdir)/respip/respip.h $(srcdir)/util/ub_event.h
ub_event.lo ub_event.o: $(srcdir)/util/ub_event.c config.h $(srcdir)/util/ub_event.h $(srcdir)/util/log.h \
$(srcdir)/util/netevent.h $(srcdir)/dnscrypt/dnscrypt.h \
$(srcdir)/util/tube.h $(srcdir)/util/mini_event.h $(srcdir)/util/rbtree.h
$(srcdir)/util/tube.h $(srcdir)/util/mini_event.h $(srcdir)/util/rbtree.h $(srcdir)/daemon/remote.h
ub_event_pluggable.lo ub_event_pluggable.o: $(srcdir)/util/ub_event_pluggable.c config.h $(srcdir)/util/ub_event.h \
$(srcdir)/libunbound/unbound-event.h $(srcdir)/util/netevent.h $(srcdir)/dnscrypt/dnscrypt.h \
$(srcdir)/util/log.h $(srcdir)/util/fptr_wlist.h \
@@ -1256,6 +1261,7 @@ unitzonemd.lo unitzonemd.o: $(srcdir)/testcode/unitzonemd.c config.h $(srcdir)/u
$(srcdir)/validator/val_anchor.h
unittcpreuse.lo unittcpreuse.o: $(srcdir)/testcode/unittcpreuse.c config.h $(srcdir)/services/outside_network.h \
$(srcdir)/util/random.h
unitinfra.lo unitinfra.o: $(srcdir)/testcode/unitinfra.c config.h $(srcdir)/util/config_file.h $(srcdir)/util/net_help.h $(srcdir)/iterator/iterator.h
acl_list.lo acl_list.o: $(srcdir)/daemon/acl_list.c config.h $(srcdir)/daemon/acl_list.h \
$(srcdir)/util/storage/dnstree.h $(srcdir)/util/rbtree.h $(srcdir)/services/view.h $(srcdir)/util/locks.h \
$(srcdir)/util/log.h $(srcdir)/util/regional.h $(srcdir)/util/config_file.h $(srcdir)/util/net_help.h \
@@ -1307,7 +1313,10 @@ 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/edns.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 \
$(srcdir)/libunbound/unbound.h $(srcdir)/daemon/worker.h $(srcdir)/libunbound/worker.h $(srcdir)/sldns/sbuffer.h \
$(srcdir)/util/data/packed_rrset.h $(srcdir)/util/storage/lruhash.h $(srcdir)/util/locks.h $(srcdir)/util/log.h \
+70
View File
@@ -0,0 +1,70 @@
# ===========================================================================
# https://www.gnu.org/software/autoconf-archive/ax_build_date_epoch.html
# ===========================================================================
#
# SYNOPSIS
#
# AX_BUILD_DATE_EPOCH(VARIABLE[, FORMAT[, ACTION-IF-FAIL]])
#
# DESCRIPTION
#
# Sets VARIABLE to a string representing the current time. It is
# formatted according to FORMAT if specified, otherwise it is formatted as
# the number of seconds (excluding leap seconds) since the UNIX epoch (01
# Jan 1970 00:00:00 UTC).
#
# If the SOURCE_DATE_EPOCH environment variable is set, it uses the value
# of that variable instead of the current time. See
# https://reproducible-builds.org/specs/source-date-epoch). If
# SOURCE_DATE_EPOCH is set but cannot be properly interpreted as a UNIX
# timestamp, then execute ACTION-IF-FAIL if specified, otherwise error.
#
# VARIABLE is AC_SUBST-ed.
#
# LICENSE
#
# Copyright (c) 2016 Eric Bavier <bavier@member.fsf.org>
#
# This program is free software: you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by the
# Free Software Foundation, either version 3 of the License, or (at your
# option) any later version.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
# Public License for more details.
#
# You should have received a copy of the GNU General Public License along
# with this program. If not, see <https://www.gnu.org/licenses/>.
#
# As a special exception, the respective Autoconf Macro's copyright owner
# gives unlimited permission to copy, distribute and modify the configure
# scripts that are the output of Autoconf when processing the Macro. You
# need not follow the terms of the GNU General Public License when using
# or distributing such scripts, even though portions of the text of the
# Macro appear in them. The GNU General Public License (GPL) does govern
# all other use of the material that constitutes the Autoconf Macro.
#
# This special exception to the GPL applies to versions of the Autoconf
# Macro released by the Autoconf Archive. When you make and distribute a
# modified version of the Autoconf Macro, you may extend this special
# exception to the GPL to apply to your modified version as well.
#serial 2
AC_DEFUN([AX_BUILD_DATE_EPOCH],
[dnl
AC_MSG_CHECKING([for build time])
ax_date_fmt="m4_default($2,%s)"
AS_IF([test x"$SOURCE_DATE_EPOCH" = x],
[$1=`date "+$ax_date_fmt"`],
[ax_build_date=`date -u -d "@$SOURCE_DATE_EPOCH" "+$ax_date_fmt" 2>/dev/null \
|| date -u -r "$SOURCE_DATE_EPOCH" "+$ax_date_fmt" 2>/dev/null`
AS_IF([test x"$ax_build_date" = x],
[m4_ifval([$3],
[$3],
[AC_MSG_ERROR([malformed SOURCE_DATE_EPOCH])])],
[$1=$ax_build_date])])
AC_MSG_RESULT([$$1])
])dnl AX_BUILD_DATE_EPOCH
+4 -1
View File
@@ -47,6 +47,7 @@
#include "util/regional.h"
#include "util/net_help.h"
#include "util/config_file.h"
#include "util/data/dname.h"
#include "util/data/msgreply.h"
#include "util/data/msgencode.h"
#include "services/cache/dns.h"
@@ -341,6 +342,7 @@ calc_hash(struct query_info* qinfo, struct module_env* env, char* buf,
/* copy the hash info into the clear buffer */
if(clen + qinfo->qname_len < sizeof(clear)) {
memmove(clear+clen, qinfo->qname, qinfo->qname_len);
query_dname_tolower(clear+clen);
clen += qinfo->qname_len;
}
if(clen + 4 < sizeof(clear)) {
@@ -755,7 +757,8 @@ cachedb_intcache_store(struct module_qstate* qstate, int msg_expired)
}
(void)dns_cache_store(qstate->env, &qstate->qinfo,
qstate->return_msg->rep, 0, qstate->prefetch_leeway, 0,
qstate->region, store_flags, qstate->qstarttime);
qstate->region, store_flags, qstate->qstarttime,
qstate->is_valrec);
if(serve_expired && msg_expired) {
if(qstate->env->cfg->serve_expired_client_timeout) {
/* No expired response from the query state, the
+203 -81
View File
@@ -52,19 +52,38 @@
#include "hiredis/hiredis.h"
struct redis_moddata {
redisContext** ctxs; /* thread-specific redis contexts */
int numctxs; /* number of ctx entries */
const char* server_host; /* server's IP address or host name */
int server_port; /* server's TCP port */
const char* server_path; /* server's unix path, or "", NULL if unused */
const char* server_password; /* server's AUTH password, or "", NULL if unused */
struct timeval command_timeout; /* timeout for commands */
struct timeval connect_timeout; /* timeout for connect */
int logical_db; /* the redis logical database to use */
/* thread-specific redis contexts */
redisContext** ctxs;
redisContext** replica_ctxs;
/* number of ctx entries */
int numctxs;
/* server's IP address or host name */
const char* server_host;
const char* replica_server_host;
/* server's TCP port */
int server_port;
int replica_server_port;
/* server's unix path, or "", NULL if unused */
const char* server_path;
const char* replica_server_path;
/* server's AUTH password, or "", NULL if unused */
const char* server_password;
const char* replica_server_password;
/* timeout for commands */
struct timeval command_timeout;
struct timeval replica_command_timeout;
/* timeout for connection setup */
struct timeval connect_timeout;
struct timeval replica_connect_timeout;
/* the redis logical database to use */
int logical_db;
int replica_logical_db;
/* if the SET with EX command is supported */
int set_with_ex_available;
};
static redisReply* redis_command(struct module_env*, struct cachedb_env*,
const char*, const uint8_t*, size_t);
const char*, const uint8_t*, size_t, int);
static void
moddata_clean(struct redis_moddata** moddata) {
@@ -78,21 +97,30 @@ moddata_clean(struct redis_moddata** moddata) {
}
free((*moddata)->ctxs);
}
if((*moddata)->replica_ctxs) {
int i;
for(i = 0; i < (*moddata)->numctxs; i++) {
if((*moddata)->replica_ctxs[i])
redisFree((*moddata)->replica_ctxs[i]);
}
free((*moddata)->replica_ctxs);
}
free(*moddata);
*moddata = NULL;
}
static redisContext*
redis_connect(const struct redis_moddata* moddata)
redis_connect(const char* host, int port, const char* path,
const char* password, int logical_db,
const struct timeval connect_timeout,
const struct timeval command_timeout)
{
redisContext* ctx;
if(moddata->server_path && moddata->server_path[0]!=0) {
ctx = redisConnectUnixWithTimeout(moddata->server_path,
moddata->connect_timeout);
if(path && path[0]!=0) {
ctx = redisConnectUnixWithTimeout(path, connect_timeout);
} else {
ctx = redisConnectWithTimeout(moddata->server_host,
moddata->server_port, moddata->connect_timeout);
ctx = redisConnectWithTimeout(host, port, connect_timeout);
}
if(!ctx || ctx->err) {
const char *errstr = "out of memory";
@@ -101,13 +129,13 @@ redis_connect(const struct redis_moddata* moddata)
log_err("failed to connect to redis server: %s", errstr);
goto fail;
}
if(redisSetTimeout(ctx, moddata->command_timeout) != REDIS_OK) {
log_err("failed to set redis timeout");
if(redisSetTimeout(ctx, command_timeout) != REDIS_OK) {
log_err("failed to set redis timeout, %s", ctx->errstr);
goto fail;
}
if(moddata->server_password && moddata->server_password[0]!=0) {
if(password && password[0]!=0) {
redisReply* rep;
rep = redisCommand(ctx, "AUTH %s", moddata->server_password);
rep = redisCommand(ctx, "AUTH %s", password);
if(!rep || rep->type == REDIS_REPLY_ERROR) {
log_err("failed to authenticate with password");
freeReplyObject(rep);
@@ -115,18 +143,25 @@ redis_connect(const struct redis_moddata* moddata)
}
freeReplyObject(rep);
}
if(moddata->logical_db > 0) {
if(logical_db > 0) {
redisReply* rep;
rep = redisCommand(ctx, "SELECT %d", moddata->logical_db);
rep = redisCommand(ctx, "SELECT %d", logical_db);
if(!rep || rep->type == REDIS_REPLY_ERROR) {
log_err("failed to set logical database (%d)",
moddata->logical_db);
logical_db);
freeReplyObject(rep);
goto fail;
}
freeReplyObject(rep);
}
verbose(VERB_OPS, "Connection to Redis established");
if(verbosity >= VERB_OPS) {
char port_str[6+1];
port_str[0] = ' ';
(void)snprintf(port_str+1, sizeof(port_str)-1, "%d", port);
verbose(VERB_OPS, "Connection to Redis established (%s%s)",
path&&path[0]!=0?path:host,
path&&path[0]!=0?"":port_str);
}
return ctx;
fail:
@@ -135,6 +170,14 @@ redis_connect(const struct redis_moddata* moddata)
return NULL;
}
static void
set_timeout(struct timeval* timeout, int value, int explicit_value)
{
int v = explicit_value != 0 ? explicit_value : value;
timeout->tv_sec = v / 1000;
timeout->tv_usec = (v % 1000) * 1000;
}
static int
redis_init(struct module_env* env, struct cachedb_env* cachedb_env)
{
@@ -149,57 +192,98 @@ redis_init(struct module_env* env, struct cachedb_env* cachedb_env)
goto fail;
}
moddata->numctxs = env->cfg->num_threads;
/* note: server_host and similar string configuration options are
* shallow references to configured strings; we don't have to free them
* in this module. */
moddata->server_host = env->cfg->redis_server_host;
moddata->replica_server_host = env->cfg->redis_replica_server_host;
moddata->server_port = env->cfg->redis_server_port;
moddata->replica_server_port = env->cfg->redis_replica_server_port;
moddata->server_path = env->cfg->redis_server_path;
moddata->replica_server_path = env->cfg->redis_replica_server_path;
moddata->server_password = env->cfg->redis_server_password;
moddata->replica_server_password = env->cfg->redis_replica_server_password;
set_timeout(&moddata->command_timeout,
env->cfg->redis_timeout,
env->cfg->redis_command_timeout);
set_timeout(&moddata->replica_command_timeout,
env->cfg->redis_replica_timeout,
env->cfg->redis_replica_command_timeout);
set_timeout(&moddata->connect_timeout,
env->cfg->redis_timeout,
env->cfg->redis_connect_timeout);
set_timeout(&moddata->replica_connect_timeout,
env->cfg->redis_replica_timeout,
env->cfg->redis_replica_connect_timeout);
moddata->logical_db = env->cfg->redis_logical_db;
moddata->replica_logical_db = env->cfg->redis_replica_logical_db;
moddata->ctxs = calloc(env->cfg->num_threads, sizeof(redisContext*));
if(!moddata->ctxs) {
log_err("out of memory");
goto fail;
}
/* note: server_host is a shallow reference to configured string.
* we don't have to free it in this module. */
moddata->server_host = env->cfg->redis_server_host;
moddata->server_port = env->cfg->redis_server_port;
moddata->server_path = env->cfg->redis_server_path;
moddata->server_password = env->cfg->redis_server_password;
moddata->command_timeout.tv_sec = env->cfg->redis_timeout / 1000;
moddata->command_timeout.tv_usec =
(env->cfg->redis_timeout % 1000) * 1000;
moddata->connect_timeout.tv_sec = env->cfg->redis_timeout / 1000;
moddata->connect_timeout.tv_usec =
(env->cfg->redis_timeout % 1000) * 1000;
if(env->cfg->redis_command_timeout != 0) {
moddata->command_timeout.tv_sec =
env->cfg->redis_command_timeout / 1000;
moddata->command_timeout.tv_usec =
(env->cfg->redis_command_timeout % 1000) * 1000;
}
if(env->cfg->redis_connect_timeout != 0) {
moddata->connect_timeout.tv_sec =
env->cfg->redis_connect_timeout / 1000;
moddata->connect_timeout.tv_usec =
(env->cfg->redis_connect_timeout % 1000) * 1000;
}
moddata->logical_db = env->cfg->redis_logical_db;
for(i = 0; i < moddata->numctxs; i++) {
redisContext* ctx = redis_connect(moddata);
if(!ctx) {
log_err("redis_init: failed to init redis");
if((moddata->replica_server_host && moddata->replica_server_host[0]!=0)
|| (moddata->replica_server_path && moddata->replica_server_path[0]!=0)) {
/* There is a replica configured, allocate ctxs */
moddata->replica_ctxs = calloc(env->cfg->num_threads, sizeof(redisContext*));
if(!moddata->replica_ctxs) {
log_err("out of memory");
goto fail;
}
}
for(i = 0; i < moddata->numctxs; i++) {
redisContext* ctx = redis_connect(
moddata->server_host,
moddata->server_port,
moddata->server_path,
moddata->server_password,
moddata->logical_db,
moddata->connect_timeout,
moddata->command_timeout);
if(!ctx) {
log_err("redis_init: failed to init redis "
"(for thread %d)", i);
/* And continue, the context can be established
* later, just like after a disconnect. */
}
moddata->ctxs[i] = ctx;
}
if(moddata->replica_ctxs) {
for(i = 0; i < moddata->numctxs; i++) {
redisContext* ctx = redis_connect(
moddata->replica_server_host,
moddata->replica_server_port,
moddata->replica_server_path,
moddata->replica_server_password,
moddata->replica_logical_db,
moddata->replica_connect_timeout,
moddata->replica_command_timeout);
if(!ctx) {
log_err("redis_init: failed to init redis "
"replica (for thread %d)", i);
/* And continue, the context can be established
* later, just like after a disconnect. */
}
moddata->replica_ctxs[i] = ctx;
}
}
cachedb_env->backend_data = moddata;
if(env->cfg->redis_expire_records) {
if(env->cfg->redis_expire_records &&
moddata->ctxs[env->alloc->thread_num] != NULL) {
redisReply* rep = NULL;
int redis_reply_type = 0;
/** check if setex command is supported */
/** check if set with ex command is supported */
rep = redis_command(env, cachedb_env,
"SETEX __UNBOUND_REDIS_CHECK__ 1 none", NULL, 0);
"SET __UNBOUND_REDIS_CHECK__ none EX 1", NULL, 0, 1);
if(!rep) {
/** init failed, no response from redis server*/
log_err("redis_init: failed to init redis, the "
"redis-expire-records option requires the SETEX command "
"(redis >= 2.0.0)");
goto fail;
goto set_with_ex_fail;
}
redis_reply_type = rep->type;
freeReplyObject(rep);
@@ -207,15 +291,18 @@ redis_init(struct module_env* env, struct cachedb_env* cachedb_env)
case REDIS_REPLY_STATUS:
break;
default:
/** init failed, setex command not supported */
log_err("redis_init: failed to init redis, the "
"redis-expire-records option requires the SETEX command "
"(redis >= 2.0.0)");
goto fail;
/** init failed, set_with_ex command not supported */
goto set_with_ex_fail;
}
moddata->set_with_ex_available = 1;
}
return 1;
set_with_ex_fail:
log_err("redis_init: failure during redis_init, the "
"redis-expire-records option requires the SET with EX command "
"(redis >= 2.6.2)");
return 1;
fail:
moddata_clean(&moddata);
return 0;
@@ -246,9 +333,9 @@ redis_deinit(struct module_env* env, struct cachedb_env* cachedb_env)
*/
static redisReply*
redis_command(struct module_env* env, struct cachedb_env* cachedb_env,
const char* command, const uint8_t* data, size_t data_len)
const char* command, const uint8_t* data, size_t data_len, int write)
{
redisContext* ctx;
redisContext* ctx, **ctx_selector;
redisReply* rep;
struct redis_moddata* d = (struct redis_moddata*)
cachedb_env->backend_data;
@@ -259,17 +346,38 @@ redis_command(struct module_env* env, struct cachedb_env* cachedb_env,
* assumption throughout the unbound architecture, so we simply assert
* it. */
log_assert(env->alloc->thread_num < d->numctxs);
ctx = d->ctxs[env->alloc->thread_num];
ctx_selector = !write && d->replica_ctxs
?d->replica_ctxs
:d->ctxs;
ctx = ctx_selector[env->alloc->thread_num];
/* If we've not established a connection to the server or we've closed
* it on a failure, try to re-establish a new one. Failures will be
* logged in redis_connect(). */
if(!ctx) {
ctx = redis_connect(d);
d->ctxs[env->alloc->thread_num] = ctx;
if(!write && d->replica_ctxs) {
ctx = redis_connect(
d->replica_server_host,
d->replica_server_port,
d->replica_server_path,
d->replica_server_password,
d->replica_logical_db,
d->replica_connect_timeout,
d->replica_command_timeout);
} else {
ctx = redis_connect(
d->server_host,
d->server_port,
d->server_path,
d->server_password,
d->logical_db,
d->connect_timeout,
d->command_timeout);
}
ctx_selector[env->alloc->thread_num] = ctx;
}
if(!ctx)
return NULL;
if(!ctx) return NULL;
/* Send the command and get a reply, synchronously. */
rep = (redisReply*)redisCommand(ctx, command, data, data_len);
@@ -279,7 +387,7 @@ redis_command(struct module_env* env, struct cachedb_env* cachedb_env,
log_err("redis_command: failed to receive a reply, "
"closing connection: %s", ctx->errstr);
redisFree(ctx);
d->ctxs[env->alloc->thread_num] = NULL;
ctx_selector[env->alloc->thread_num] = NULL;
return NULL;
}
@@ -309,7 +417,7 @@ redis_lookup(struct module_env* env, struct cachedb_env* cachedb_env,
return 0;
}
rep = redis_command(env, cachedb_env, cmdbuf, NULL, 0);
rep = redis_command(env, cachedb_env, cmdbuf, NULL, 0, 0);
if(!rep)
return 0;
switch(rep->type) {
@@ -346,11 +454,16 @@ redis_store(struct module_env* env, struct cachedb_env* cachedb_env,
{
redisReply* rep;
int n;
int set_ttl = (env->cfg->redis_expire_records &&
struct redis_moddata* moddata = (struct redis_moddata*)
cachedb_env->backend_data;
int set_ttl = (moddata->set_with_ex_available &&
env->cfg->redis_expire_records &&
(!env->cfg->serve_expired || env->cfg->serve_expired_ttl > 0));
/* Supported commands:
* - "SET " + key + " %b"
* - "SETEX " + key + " " + ttl + " %b"
* - "SET " + key + " %b EX " + ttl
* older redis 2.0.0 was "SETEX " + key + " " + ttl + " %b"
* - "EXPIRE " + key + " 0"
*/
char cmdbuf[6+(CACHEDB_HASHSIZE/8)*2+11+3+1];
@@ -358,14 +471,22 @@ redis_store(struct module_env* env, struct cachedb_env* cachedb_env,
verbose(VERB_ALGO, "redis_store %s (%d bytes)", key, (int)data_len);
/* build command to set to a binary safe string */
n = snprintf(cmdbuf, sizeof(cmdbuf), "SET %s %%b", key);
} else if(ttl == 0) {
/* use the EXPIRE command, SET with EX 0 is an invalid time. */
/* Replies with REDIS_REPLY_INTEGER of 1. */
verbose(VERB_ALGO, "redis_store expire %s (%d bytes)",
key, (int)data_len);
n = snprintf(cmdbuf, sizeof(cmdbuf), "EXPIRE %s 0", key);
data = NULL;
data_len = 0;
} else {
/* add expired ttl time to redis ttl to avoid premature eviction of key */
ttl += env->cfg->serve_expired_ttl;
verbose(VERB_ALGO, "redis_store %s (%d bytes) with ttl %u",
key, (int)data_len, (uint32_t)ttl);
key, (int)data_len, (unsigned)(uint32_t)ttl);
/* build command to set to a binary safe string */
n = snprintf(cmdbuf, sizeof(cmdbuf), "SETEX %s %u %%b", key,
(uint32_t)ttl);
n = snprintf(cmdbuf, sizeof(cmdbuf), "SET %s %%b EX %u", key,
(unsigned)(uint32_t)ttl);
}
@@ -374,11 +495,12 @@ redis_store(struct module_env* env, struct cachedb_env* cachedb_env,
return;
}
rep = redis_command(env, cachedb_env, cmdbuf, data, data_len);
rep = redis_command(env, cachedb_env, cmdbuf, data, data_len, 1);
if(rep) {
verbose(VERB_ALGO, "redis_store set completed");
if(rep->type != REDIS_REPLY_STATUS &&
rep->type != REDIS_REPLY_ERROR) {
rep->type != REDIS_REPLY_ERROR &&
rep->type != REDIS_REPLY_INTEGER) {
log_err("redis_store: unexpected type of reply (%d)",
rep->type);
}
-4
View File
@@ -5,12 +5,8 @@
#undef malloc
#include <sys/types.h>
#ifndef USE_WINSOCK
void *malloc ();
#else
/* provide a prototype */
void *malloc (size_t n);
#endif
/* Allocate an N-byte block of memory from the heap.
If N is zero, allocate a 1-byte block. */
+6
View File
@@ -378,6 +378,9 @@
/* Define if we have LibreSSL */
#undef HAVE_LIBRESSL
/* If we have atomic_store */
#undef HAVE_LINK_ATOMIC_STORE
/* Define to 1 if you have the <linux/net_tstamp.h> header file. */
#undef HAVE_LINUX_NET_TSTAMP_H
@@ -663,6 +666,9 @@
/* Define to 1 if you have the <stdarg.h> header file. */
#undef HAVE_STDARG_H
/* Define to 1 if you have the <stdatomic.h> header file. */
#undef HAVE_STDATOMIC_H
/* Define to 1 if you have the <stdbool.h> header file. */
#undef HAVE_STDBOOL_H
+108 -14
View File
@@ -1,6 +1,6 @@
#! /bin/sh
# Guess values for system-dependent variables and create Makefiles.
# Generated by GNU Autoconf 2.71 for unbound 1.22.0.
# Generated by GNU Autoconf 2.71 for unbound 1.23.0.
#
# 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.22.0'
PACKAGE_STRING='unbound 1.22.0'
PACKAGE_VERSION='1.23.0'
PACKAGE_STRING='unbound 1.23.0'
PACKAGE_BUGREPORT='unbound-bugs@nlnetlabs.nl or https://github.com/NLnetLabs/unbound/issues'
PACKAGE_URL=''
@@ -712,6 +712,7 @@ SSLLIB
HAVE_SSL
PC_CRYPTO_DEPENDENCY
CONFIG_DATE
SOURCE_DATE_EPOCH
GCC_DOCKER_LINTFLAGS
NETBSD_LINTFLAGS
PYUNBOUND_UNINSTALL
@@ -959,6 +960,7 @@ SYSTEMD_LIBS
SYSTEMD_DAEMON_CFLAGS
SYSTEMD_DAEMON_LIBS
PYTHON_VERSION
SOURCE_DATE_EPOCH
PROTOBUFC_CFLAGS
PROTOBUFC_LIBS'
@@ -1509,7 +1511,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.22.0 to adapt to many kinds of systems.
\`configure' configures unbound 1.23.0 to adapt to many kinds of systems.
Usage: $0 [OPTION]... [VAR=VALUE]...
@@ -1575,7 +1577,7 @@ fi
if test -n "$ac_init_help"; then
case $ac_init_help in
short | recursive ) echo "Configuration of unbound 1.22.0:";;
short | recursive ) echo "Configuration of unbound 1.23.0:";;
esac
cat <<\_ACEOF
@@ -1752,6 +1754,10 @@ Some influential environment variables:
The installed Python version to use, for example '2.3'. This
string will be appended to the Python interpreter canonical
name.
SOURCE_DATE_EPOCH
If it is set, it uses the value of that variable instead of the
current time as the build timestamp. The format is a unix
timestamp. This enables reproducible build output.
PROTOBUFC_CFLAGS
C compiler flags for PROTOBUFC, overriding pkg-config
PROTOBUFC_LIBS
@@ -1824,7 +1830,7 @@ fi
test -n "$ac_init_help" && exit $ac_status
if $ac_init_version; then
cat <<\_ACEOF
unbound configure 1.22.0
unbound configure 1.23.0
generated by GNU Autoconf 2.71
Copyright (C) 2021 Free Software Foundation, Inc.
@@ -2481,7 +2487,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.22.0, which was
It was created by unbound $as_me 1.23.0, which was
generated by GNU Autoconf 2.71. Invocation command line was
$ $0$ac_configure_args_raw
@@ -3243,13 +3249,13 @@ ac_compiler_gnu=$ac_cv_c_compiler_gnu
UNBOUND_VERSION_MAJOR=1
UNBOUND_VERSION_MINOR=22
UNBOUND_VERSION_MINOR=23
UNBOUND_VERSION_MICRO=0
LIBUNBOUND_CURRENT=9
LIBUNBOUND_REVISION=30
LIBUNBOUND_REVISION=31
LIBUNBOUND_AGE=1
# 1.0.0 had 0:12:0
# 1.0.1 had 0:13:0
@@ -3347,6 +3353,7 @@ LIBUNBOUND_AGE=1
# 1.21.0 had 9:28:1
# 1.21.1 had 9:29:1
# 1.22.0 had 9:30:1
# 1.23.0 had 9:31:1
# Current -- the number of the binary API that we're implementing
# Revision -- which iteration of the implementation of the binary
@@ -16093,6 +16100,14 @@ then :
fi
ac_fn_c_check_header_compile "$LINENO" "stdatomic.h" "ac_cv_header_stdatomic_h" "$ac_includes_default
"
if test "x$ac_cv_header_stdatomic_h" = xyes
then :
printf "%s\n" "#define HAVE_STDATOMIC_H 1" >>confdefs.h
fi
# check for types.
# Using own tests for int64* because autoconf builtin only give 32bit.
@@ -19893,7 +19908,26 @@ if test "`uname`" = "Linux"; then
GCC_DOCKER_LINTFLAGS='-syntax'
fi
CONFIG_DATE=`date +%Y%m%d`
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for build time" >&5
printf %s "checking for build time... " >&6; }
ax_date_fmt="%Y%m%d"
if test x"$SOURCE_DATE_EPOCH" = x
then :
CONFIG_DATE=`date "+$ax_date_fmt"`
else $as_nop
ax_build_date=`date -u -d "@$SOURCE_DATE_EPOCH" "+$ax_date_fmt" 2>/dev/null \
|| date -u -r "$SOURCE_DATE_EPOCH" "+$ax_date_fmt" 2>/dev/null`
if test x"$ax_build_date" = x
then :
as_fn_error $? "malformed SOURCE_DATE_EPOCH" "$LINENO" 5
else $as_nop
CONFIG_DATE=$ax_build_date
fi
fi
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $CONFIG_DATE" >&5
printf "%s\n" "$CONFIG_DATE" >&6; }
# Checks for libraries.
@@ -23496,6 +23530,48 @@ if echo $host_os | grep darwin8 > /dev/null; then
printf "%s\n" "#define DARWIN_BROKEN_SETREUID 1" >>confdefs.h
fi
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for atomic_store" >&5
printf %s "checking for atomic_store... " >&6; }
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
$ac_includes_default
#ifdef HAVE_STDATOMIC_H
#include <stdatomic.h>
#endif
int
main (void)
{
int newvar = 5, var = 0;
atomic_store((_Atomic int*)&var, newvar);
newvar = 0;
/* condition to use the variables. */
if(var == newvar) return 1;
;
return 0;
}
_ACEOF
if ac_fn_c_try_link "$LINENO"
then :
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5
printf "%s\n" "yes" >&6; }
printf "%s\n" "#define HAVE_LINK_ATOMIC_STORE 1" >>confdefs.h
else $as_nop
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
printf "%s\n" "no" >&6; }
fi
rm -f core conftest.err conftest.$ac_objext conftest.beam \
conftest$ac_exeext conftest.$ac_ext
ac_fn_check_decl "$LINENO" "inet_pton" "ac_cv_have_decl_inet_pton" "
$ac_includes_default
#ifdef HAVE_NETINET_IN_H
@@ -24997,9 +25073,27 @@ printf "%s\n" "#define MAXSYSLOGMSGLEN 10240" >>confdefs.h
version=1.22.0
version=1.23.0
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for build time" >&5
printf %s "checking for build time... " >&6; }
ax_date_fmt="%b %e, %Y"
if test x"$SOURCE_DATE_EPOCH" = x
then :
date=`date "+$ax_date_fmt"`
else $as_nop
ax_build_date=`date -u -d "@$SOURCE_DATE_EPOCH" "+$ax_date_fmt" 2>/dev/null \
|| date -u -r "$SOURCE_DATE_EPOCH" "+$ax_date_fmt" 2>/dev/null`
if test x"$ax_build_date" = x
then :
as_fn_error $? "malformed SOURCE_DATE_EPOCH" "$LINENO" 5
else $as_nop
date=$ax_build_date
fi
fi
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $date" >&5
printf "%s\n" "$date" >&6; }
date=`date +'%b %e, %Y'`
ac_config_files="$ac_config_files Makefile doc/example.conf doc/libunbound.3 doc/unbound.8 doc/unbound-anchor.8 doc/unbound-checkconf.8 doc/unbound.conf.5 doc/unbound-control.8 doc/unbound-host.1 smallapp/unbound-control-setup.sh dnstap/dnstap_config.h dnscrypt/dnscrypt_config.h contrib/libunbound.pc contrib/unbound.socket contrib/unbound.service contrib/unbound_portable.service"
@@ -25509,7 +25603,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.22.0, which was
This file was extended by unbound $as_me 1.23.0, which was
generated by GNU Autoconf 2.71. Invocation command line was
CONFIG_FILES = $CONFIG_FILES
@@ -25577,7 +25671,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.22.0
unbound config.status 1.23.0
configured by $0, generated by GNU Autoconf 2.71,
with options \\"\$ac_cs_config\\"
+28 -4
View File
@@ -2,6 +2,7 @@
# Process this file with autoconf to produce a configure script.
AC_PREREQ([2.56])
sinclude(acx_nlnetlabs.m4)
sinclude(ax_build_date_epoch.m4)
sinclude(ax_pthread.m4)
sinclude(acx_python.m4)
sinclude(ax_pkg_swig.m4)
@@ -10,7 +11,7 @@ sinclude(dnscrypt/dnscrypt.m4)
# must be numbers. ac_defun because of later processing
m4_define([VERSION_MAJOR],[1])
m4_define([VERSION_MINOR],[22])
m4_define([VERSION_MINOR],[23])
m4_define([VERSION_MICRO],[0])
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 +19,7 @@ AC_SUBST(UNBOUND_VERSION_MINOR, [VERSION_MINOR])
AC_SUBST(UNBOUND_VERSION_MICRO, [VERSION_MICRO])
LIBUNBOUND_CURRENT=9
LIBUNBOUND_REVISION=30
LIBUNBOUND_REVISION=31
LIBUNBOUND_AGE=1
# 1.0.0 had 0:12:0
# 1.0.1 had 0:13:0
@@ -116,6 +117,7 @@ LIBUNBOUND_AGE=1
# 1.21.0 had 9:28:1
# 1.21.1 had 9:29:1
# 1.22.0 had 9:30:1
# 1.23.0 had 9:31:1
# Current -- the number of the binary API that we're implementing
# Revision -- which iteration of the implementation of the binary
@@ -522,6 +524,7 @@ AC_CHECK_HEADERS([netioapi.h],,, [AC_INCLUDES_DEFAULT
# Check for Linux timestamping headers
AC_CHECK_HEADERS([linux/net_tstamp.h],,, [AC_INCLUDES_DEFAULT])
AC_CHECK_HEADERS([stdatomic.h],,, [AC_INCLUDES_DEFAULT])
# check for types.
# Using own tests for int64* because autoconf builtin only give 32bit.
@@ -907,7 +910,8 @@ if test "`uname`" = "Linux"; then
GCC_DOCKER_LINTFLAGS='-syntax'
AC_SUBST(GCC_DOCKER_LINTFLAGS)
fi
CONFIG_DATE=`date +%Y%m%d`
AX_BUILD_DATE_EPOCH(CONFIG_DATE, [%Y%m%d])
AC_ARG_VAR(SOURCE_DATE_EPOCH, [If it is set, it uses the value of that variable instead of the current time as the build timestamp. The format is a unix timestamp. This enables reproducible build output.])
AC_SUBST(CONFIG_DATE)
# Checks for libraries.
@@ -1814,6 +1818,25 @@ AC_CHECK_FUNCS([setresgid],,[AC_CHECK_FUNCS([setregid])])
if echo $host_os | grep darwin8 > /dev/null; then
AC_DEFINE(DARWIN_BROKEN_SETREUID, 1, [Define this if on macOSX10.4-darwin8 and setreuid and setregid do not work])
fi
AC_MSG_CHECKING([for atomic_store])
AC_LINK_IFELSE([AC_LANG_PROGRAM(AC_INCLUDES_DEFAULT [[
#ifdef HAVE_STDATOMIC_H
#include <stdatomic.h>
#endif
]], [[
int newvar = 5, var = 0;
atomic_store((_Atomic int*)&var, newvar);
newvar = 0;
/* condition to use the variables. */
if(var == newvar) return 1;
]])], [
AC_MSG_RESULT(yes)
AC_DEFINE(HAVE_LINK_ATOMIC_STORE, 1, [If we have atomic_store])
], [
AC_MSG_RESULT([no])
])
AC_CHECK_DECLS([inet_pton,inet_ntop], [], [], [
AC_INCLUDES_DEFAULT
#ifdef HAVE_NETINET_IN_H
@@ -2434,7 +2457,8 @@ char *unbound_stat_strdup_log(const char *s, const char* file, int line,
dnl if we build from source tree, the man pages need @date@ and @version@
dnl if this is a distro tarball, that was already done by makedist.sh
AC_SUBST(version, [VERSION_MAJOR.VERSION_MINOR.VERSION_MICRO])
AC_SUBST(date, [`date +'%b %e, %Y'`])
AX_BUILD_DATE_EPOCH(date, [[%b %e, %Y]])
AC_SUBST(date)
AC_CONFIG_FILES([Makefile doc/example.conf doc/libunbound.3 doc/unbound.8 doc/unbound-anchor.8 doc/unbound-checkconf.8 doc/unbound.conf.5 doc/unbound-control.8 doc/unbound-host.1 smallapp/unbound-control-setup.sh dnstap/dnstap_config.h dnscrypt/dnscrypt_config.h contrib/libunbound.pc contrib/unbound.socket contrib/unbound.service contrib/unbound_portable.service])
AC_CONFIG_HEADERS([config.h])
@@ -1,21 +1,24 @@
#!/usr/bin/env bash
LIBEXPAT_FNAME=expat-2.7.0
LIBEXPAT_VERSION_DIR=R_2_7_0
echo "Downloading Expat"
if ! curl -L -k -s -o expat-2.2.9.tar.gz https://github.com/libexpat/libexpat/releases/download/R_2_2_9/expat-2.2.9.tar.gz;
if ! curl -L -k -s -o $LIBEXPAT_FNAME.tar.gz https://github.com/libexpat/libexpat/releases/download/$LIBEXPAT_VERSION_DIR/$LIBEXPAT_FNAME.tar.gz;
then
echo "Failed to download Expat"
exit 1
fi
echo "Unpacking Expat"
rm -rf ./expat-2.2.9
if ! tar -xf expat-2.2.9.tar.gz;
rm -rf ./$LIBEXPAT_FNAME
if ! tar -xf $LIBEXPAT_FNAME.tar.gz;
then
echo "Failed to unpack Expat"
exit 1
fi
cd expat-2.2.9 || exit 1
cd $LIBEXPAT_FNAME || exit 1
echo "Configuring Expat"
if ! ./configure --build="$AUTOTOOLS_BUILD" --host="$AUTOTOOLS_HOST" --prefix="$ANDROID_PREFIX"; then
+11 -7
View File
@@ -1,28 +1,32 @@
#!/usr/bin/env bash
LIBEXPAT_FNAME=expat-2.7.0
LIBEXPAT_VERSION_DIR=R_2_7_0
echo "Downloading Expat"
if ! curl -L -k -s -o expat-2.2.9.tar.gz https://github.com/libexpat/libexpat/releases/download/R_2_2_9/expat-2.2.9.tar.gz;
if ! curl -L -k -s -o $LIBEXPAT_FNAME.tar.gz https://github.com/libexpat/libexpat/releases/download/$LIBEXPAT_VERSION_DIR/$LIBEXPAT_FNAME.tar.gz;
then
echo "Failed to download Expat"
exit 1
fi
echo "Unpacking Expat"
rm -rf ./expat-2.2.9
if ! tar -xf expat-2.2.9.tar.gz;
rm -rf ./$LIBEXPAT_FNAME
if ! tar -xf $LIBEXPAT_FNAME.tar.gz;
then
echo "Failed to unpack Expat"
exit 1
fi
cd expat-2.2.9 || exit 1
cd $LIBEXPAT_FNAME || exit 1
export PKG_CONFIG_PATH="$IOS_PREFIX/lib/pkgconfig"
echo "Configuring Expat"
if ! ./configure \
--build="$AUTOTOOLS_BUILD" --host="$AUTOTOOLS_HOST" \
--prefix="$IOS_PREFIX" ; then
if ! ./configure --without-tests \
--build="$AUTOTOOLS_BUILD" --host="$AUTOTOOLS_HOST" \
--prefix="$IOS_PREFIX" ;
then
echo "Error: Failed to configure Expat"
cat config.log
exit 1
+17 -13
View File
@@ -221,7 +221,9 @@ acl_interface_insert(struct acl_list* acl_interface,
struct sockaddr_storage* addr, socklen_t addrlen,
enum acl_access control)
{
return acl_find_or_create(acl_interface, addr, addrlen, control);
struct acl_addr* node = acl_find_or_create(acl_interface, addr, addrlen, control);
node->is_interface = 1;
return node;
}
/** apply acl_tag string */
@@ -551,17 +553,6 @@ acl_list_apply_cfg(struct acl_list* acl, struct config_file* cfg,
return 1;
}
int
acl_interface_compare(const void* k1, const void* k2)
{
struct addr_tree_node* n1 = (struct addr_tree_node*)k1;
struct addr_tree_node* n2 = (struct addr_tree_node*)k2;
return sockaddr_cmp(&n1->addr, n1->addrlen, &n2->addr,
n2->addrlen);
/* We don't care about comparing node->net. All addresses in the
* acl_interface tree have either 32 (ipv4) or 128 (ipv6). */
}
void
acl_interface_init(struct acl_list* acl_interface)
{
@@ -816,10 +807,23 @@ log_acl_action(const char* action, struct sockaddr_storage* addr,
addr_to_str(&acladdr->node.addr, acladdr->node.addrlen,
n, sizeof(n));
verbose(VERB_ALGO, "%s query from %s port %d because of "
"%s/%d %s", action, a, (int)port, n, acladdr->node.net,
"%s/%d %s%s", action, a, (int)port, n,
acladdr->node.net,
acladdr->is_interface?"(ACL on interface IP) ":"",
acl_access_to_str(acl));
} else {
verbose(VERB_ALGO, "%s query from %s port %d", action, a,
(int)port);
}
}
void acl_list_swap_tree(struct acl_list* acl, struct acl_list* data)
{
/* swap tree and region */
rbtree_type oldtree = acl->tree;
struct regional* oldregion = acl->region;
acl->tree = data->tree;
acl->region = data->region;
data->tree = oldtree;
data->region = oldregion;
}
+10 -3
View File
@@ -107,6 +107,8 @@ struct acl_addr {
struct config_strlist** tag_datas;
/** size of the tag_datas array */
size_t tag_datas_size;
/* If the acl node is for an interface */
int is_interface;
/* view element, NULL if none */
struct view* view;
};
@@ -147,9 +149,6 @@ acl_interface_insert(struct acl_list* acl_interface,
int acl_list_apply_cfg(struct acl_list* acl, struct config_file* cfg,
struct views* v);
/** compare ACL interface "addr_tree" nodes (+port) */
int acl_interface_compare(const void* k1, const void* k2);
/**
* Initialise (also clean) the acl_interface struct.
* @param acl_interface: where to store.
@@ -202,4 +201,12 @@ const char* acl_access_to_str(enum acl_access acl);
void log_acl_action(const char* action, struct sockaddr_storage* addr,
socklen_t addrlen, enum acl_access acl, struct acl_addr* acladdr);
/**
* Swap internal tree with preallocated entries.
* @param acl: the acl structure.
* @param data: the data structure used to take elements from. This contains
* the old elements on return.
*/
void acl_list_swap_tree(struct acl_list* acl, struct acl_list* data);
#endif /* DAEMON_ACL_LIST_H */
+2 -2
View File
@@ -692,7 +692,7 @@ load_msg(RES* ssl, sldns_buffer* buf, struct worker* worker)
return 1; /* skip this one, not all references satisfied */
if(!dns_cache_store(&worker->env, &qinf, &rep, 0, 0, 0, NULL, flags,
*worker->env.now)) {
*worker->env.now, 1)) {
log_warn("error out of memory");
return 0;
}
@@ -836,7 +836,7 @@ int print_deleg_lookup(RES* ssl, struct worker* worker, uint8_t* nm,
struct delegpt* dp;
struct dns_msg* msg;
struct regional* region = worker->scratchpad;
char b[260];
char b[LDNS_MAX_DOMAINLEN];
struct query_info qinfo;
struct iter_hints_stub* stub;
int nolock = 0;
+25 -17
View File
@@ -323,8 +323,7 @@ daemon_init(void)
return daemon;
}
static int setup_acl_for_ports(struct acl_list* list,
struct listen_port* port_list)
int setup_acl_for_ports(struct acl_list* list, struct listen_port* port_list)
{
struct acl_addr* acl_node;
for(; port_list; port_list=port_list->next) {
@@ -717,16 +716,16 @@ daemon_fork(struct daemon* daemon)
#endif
log_assert(daemon);
if(!(daemon->views = views_create()))
if(!(daemon->env->views = views_create()))
fatal_exit("Could not create views: out of memory");
/* create individual views and their localzone/data trees */
if(!views_apply_cfg(daemon->views, daemon->cfg))
if(!views_apply_cfg(daemon->env->views, daemon->cfg))
fatal_exit("Could not set up views");
if(!acl_list_apply_cfg(daemon->acl, daemon->cfg, daemon->views))
if(!acl_list_apply_cfg(daemon->acl, daemon->cfg, daemon->env->views))
fatal_exit("Could not setup access control list");
if(!acl_interface_apply_cfg(daemon->acl_interface, daemon->cfg,
daemon->views))
daemon->env->views))
fatal_exit("Could not setup interface control list");
if(!tcl_list_apply_cfg(daemon->tcl, daemon->cfg))
fatal_exit("Could not setup TCP connection limits");
@@ -762,15 +761,15 @@ daemon_fork(struct daemon* daemon)
fatal_exit("Could not set root or stub hints");
/* process raw response-ip configuration data */
if(!(daemon->respip_set = respip_set_create()))
if(!(daemon->env->respip_set = respip_set_create()))
fatal_exit("Could not create response IP set");
if(!respip_global_apply_cfg(daemon->respip_set, daemon->cfg))
if(!respip_global_apply_cfg(daemon->env->respip_set, daemon->cfg))
fatal_exit("Could not set up response IP set");
if(!respip_views_apply_cfg(daemon->views, daemon->cfg,
if(!respip_views_apply_cfg(daemon->env->views, daemon->cfg,
&have_view_respip_cfg))
fatal_exit("Could not set up per-view response IP sets");
daemon->use_response_ip = !respip_set_is_empty(daemon->respip_set) ||
have_view_respip_cfg;
daemon->use_response_ip = !respip_set_is_empty(
daemon->env->respip_set) || have_view_respip_cfg;
/* setup modules */
daemon_setup_modules(daemon);
@@ -886,14 +885,18 @@ daemon_cleanup(struct daemon* daemon)
daemon->env->hints = NULL;
local_zones_delete(daemon->local_zones);
daemon->local_zones = NULL;
respip_set_delete(daemon->respip_set);
daemon->respip_set = NULL;
views_delete(daemon->views);
daemon->views = NULL;
respip_set_delete(daemon->env->respip_set);
daemon->env->respip_set = NULL;
views_delete(daemon->env->views);
daemon->env->views = NULL;
if(daemon->env->auth_zones)
auth_zones_cleanup(daemon->env->auth_zones);
/* key cache is cleared by module deinit during next daemon_fork() */
daemon_remote_clear(daemon->rc);
if(daemon->fast_reload_thread)
fast_reload_thread_stop(daemon->fast_reload_thread);
if(daemon->fast_reload_printq_list)
fast_reload_printq_list_delete(daemon->fast_reload_printq_list);
for(i=0; i<daemon->num; i++)
worker_delete(daemon->workers[i]);
free(daemon->workers);
@@ -951,11 +954,16 @@ daemon_delete(struct daemon* daemon)
listen_desetup_locks();
free(daemon->chroot);
free(daemon->pidfile);
free(daemon->cfgfile);
free(daemon->env);
#ifdef HAVE_SSL
listen_sslctx_delete_ticket_keys();
SSL_CTX_free((SSL_CTX*)daemon->listen_sslctx);
SSL_CTX_free((SSL_CTX*)daemon->connect_sslctx);
SSL_CTX_free((SSL_CTX*)daemon->listen_dot_sslctx);
SSL_CTX_free((SSL_CTX*)daemon->listen_doh_sslctx);
SSL_CTX_free((SSL_CTX*)daemon->connect_dot_sslctx);
#endif
#ifdef HAVE_NGTCP2
SSL_CTX_free((SSL_CTX*)daemon->listen_quic_sslctx);
#endif
free(daemon);
/* lex cleanup */
+29 -6
View File
@@ -60,6 +60,8 @@ struct respip_set;
struct shm_main_info;
struct doq_table;
struct cookie_secrets;
struct fast_reload_thread;
struct fast_reload_printq;
#include "dnstap/dnstap_config.h"
#ifdef USE_DNSTAP
@@ -97,8 +99,14 @@ struct daemon {
struct listen_port* rc_ports;
/** remote control connections management (for first worker) */
struct daemon_remote* rc;
/** ssl context for listening to dnstcp over ssl, and connecting ssl */
void* listen_sslctx, *connect_sslctx;
/** ssl context for listening to dnstcp over ssl */
void* listen_dot_sslctx;
/** ssl context for connecting to dnstcp over ssl */
void* connect_dot_sslctx;
/** ssl context for listening to DoH */
void* listen_doh_sslctx;
/** ssl context for listening to quic */
void* listen_quic_sslctx;
/** num threads allocated */
int num;
/** num threads allocated in the previous config or 0 at first */
@@ -131,15 +139,11 @@ struct daemon {
struct timeval time_last_stat;
/** time when daemon started */
struct timeval time_boot;
/** views structure containing view tree */
struct views* views;
#ifdef USE_DNSTAP
/** the dnstap environment master value, copied and changed by threads*/
struct dt_env* dtenv;
#endif
struct shm_main_info* shm_info;
/** response-ip set with associated actions and tags. */
struct respip_set* respip_set;
/** some response-ip tags or actions are configured if true */
int use_response_ip;
/** some RPZ policies are configured */
@@ -154,6 +158,17 @@ struct daemon {
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 */
struct fast_reload_printq* fast_reload_printq_list;
/** the fast reload option to drop mesh queries, true if so. */
int fast_reload_drop_mesh;
/** for fast reload, if the tcl, tcp connection limits, has
* changes for workers */
int fast_reload_tcl_has_changes;
/** config file name */
char* cfgfile;
};
/**
@@ -206,4 +221,12 @@ void daemon_delete(struct daemon* daemon);
*/
void daemon_apply_cfg(struct daemon* daemon, struct config_file* cfg);
/**
* Setup acl list to have entries for the port list.
* @param list: the acl interface
* @param port_list: list of open ports, or none.
* @return false on failure
*/
int setup_acl_for_ports(struct acl_list* list, struct listen_port* port_list);
#endif /* DAEMON_H */
File diff suppressed because it is too large Load Diff
+167
View File
@@ -48,6 +48,7 @@
#ifdef HAVE_OPENSSL_SSL_H
#include <openssl/ssl.h>
#endif
#include "util/locks.h"
struct config_file;
struct listen_list;
struct listen_port;
@@ -55,6 +56,7 @@ struct worker;
struct comm_reply;
struct comm_point;
struct daemon_remote;
struct config_strlist_head;
/** number of milliseconds timeout on incoming remote control handshake */
#define REMOTE_CONTROL_TCP_TIMEOUT 120000
@@ -118,6 +120,137 @@ struct remote_stream {
};
typedef struct remote_stream RES;
/**
* Notification status. This is exchanged between the fast reload thread
* and the server thread, over the commpair sockets.
*/
enum fast_reload_notification {
/** nothing, not used */
fast_reload_notification_none = 0,
/** the fast reload thread is done */
fast_reload_notification_done = 1,
/** the fast reload thread is done but with an error, it failed */
fast_reload_notification_done_error = 2,
/** the fast reload thread is told to exit by the server thread.
* Sent on server quit while the reload is running. */
fast_reload_notification_exit = 3,
/** the fast reload thread has exited, after being told to exit */
fast_reload_notification_exited = 4,
/** the fast reload thread has information to print out */
fast_reload_notification_printout = 5,
/** stop as part of the reload the thread and other threads */
fast_reload_notification_reload_stop = 6,
/** ack the stop as part of the reload, and also ack start */
fast_reload_notification_reload_ack = 7,
/** resume from stop as part of the reload */
fast_reload_notification_reload_start = 8,
/** the fast reload thread wants the mainthread to poll workers,
* after the reload, sent when nopause is used */
fast_reload_notification_reload_nopause_poll = 9
};
/**
* Fast reload printout queue. Contains a list of strings, that need to be
* printed over the file descriptor.
*/
struct fast_reload_printq {
/** if this item is in a list, the previous and next */
struct fast_reload_printq *prev, *next;
/** if this item is in a list, it is true. */
int in_list;
/** list of strings to printout */
struct config_strlist_head* to_print;
/** the current item to print. It is malloced. NULL if none. */
char* client_item;
/** The length, strlen, of the client_item, that has to be sent. */
int client_len;
/** The number of bytes sent of client_item. */
int client_byte_count;
/** the comm point for the client connection, the remote control
* client. */
struct comm_point* client_cp;
/** the remote control connection to print output to. */
struct remote_stream remote;
/** the worker that the event is added in */
struct worker* worker;
};
/**
* Fast reload auth zone change. Keeps track if an auth zone was removed,
* added or changed. This is needed because workers can have events for
* dealing with auth zones, like transfers, and those have to be removed
* too, not just the auth zone structure from the tree. */
struct fast_reload_auth_change {
/** next in the list of auth zone changes. */
struct fast_reload_auth_change* next;
/** the zone in the old config */
struct auth_zone* old_z;
/** the zone in the new config */
struct auth_zone* new_z;
/** if the zone was deleted */
int is_deleted;
/** if the zone was added */
int is_added;
/** if the zone has been changed */
int is_changed;
};
/**
* Fast reload thread structure
*/
struct fast_reload_thread {
/** the thread number for the dtio thread,
* must be first to cast thread arg to int* in checklock code. */
int threadnum;
/** communication socket pair, that sends commands */
int commpair[2];
/** thread id, of the io thread */
ub_thread_type tid;
/** if the io processing has started */
int started;
/** if the thread has to quit */
int need_to_quit;
/** verbosity of the fast_reload command, the number of +v options */
int fr_verb;
/** option to not pause threads during reload */
int fr_nopause;
/** option to drop mesh queries */
int fr_drop_mesh;
/** the event that listens on the remote service worker to the
* commpair, it receives content from the fast reload thread. */
void* service_event;
/** if the event that listens on the remote service worker has
* been added to the comm base. */
int service_event_is_added;
/** the service event can read a cmd, nonblocking, so it can
* save the partial read cmd here */
uint32_t service_read_cmd;
/** the number of bytes in service_read_cmd */
int service_read_cmd_count;
/** the worker that the service_event is added in */
struct worker* worker;
/** the printout of output to the remote client. */
struct fast_reload_printq *printq;
/** lock on fr_output, to stop race when both remote control thread
* and fast reload thread use fr_output list. */
lock_basic_type fr_output_lock;
/** list of strings, that the fast reload thread produces that have
* to be printed. The remote control thread can pick them up with
* the lock. */
struct config_strlist_head* fr_output;
/** communication socket pair, to respond to the reload request */
int commreload[2];
/** the list of auth zone changes. */
struct fast_reload_auth_change* auth_zone_change_list;
/** the old tree of auth zones, to lookup. */
struct auth_zones* old_auth_zones;
};
/**
* Create new remote control state for the daemon.
* @param cfg: config file with key file settings.
@@ -203,4 +336,38 @@ int ssl_printf(RES* ssl, const char* format, ...)
int ssl_read_line(RES* ssl, char* buf, size_t max);
#endif /* HAVE_SSL */
/**
* Start fast reload thread
* @param ssl: the RES connection to print to.
* @param worker: the remote servicing worker.
* @param s: the rc_state that is servicing the remote control connection to
* the remote control client. It needs to be moved away to stay connected
* while the fast reload is running.
* @param fr_verb: verbosity to print output at. 0 is nothing, 1 is some
* and 2 is more detail.
* @param fr_nopause: option to not pause threads during reload.
* @param fr_drop_mesh: option to drop mesh queries.
*/
void fast_reload_thread_start(RES* ssl, struct worker* worker,
struct rc_state* s, int fr_verb, int fr_nopause, int fr_drop_mesh);
/**
* Stop fast reload thread
* @param fast_reload_thread: the thread struct.
*/
void fast_reload_thread_stop(struct fast_reload_thread* fast_reload_thread);
/** fast reload thread commands to remote service thread event callback */
void fast_reload_service_cb(int fd, short bits, void* arg);
/** fast reload callback for the remote control client connection */
int fast_reload_client_callback(struct comm_point* c, void* arg, int err,
struct comm_reply* rep);
/** fast reload printq delete list */
void fast_reload_printq_list_delete(struct fast_reload_printq* list);
/** Pick up per worker changes after a fast reload. */
void fast_reload_worker_pickup_changes(struct worker* worker);
#endif /* DAEMON_REMOTE_H */
+16 -16
View File
@@ -281,6 +281,12 @@ server_stats_compile(struct worker* worker, struct ub_stats_info* s, int reset)
s->svr.rpz_action[i] += (long long)worker->env.mesh->rpz_action[i];
timehist_export(worker->env.mesh->histogram, s->svr.hist,
NUM_BUCKETS_HIST);
s->svr.num_queries_discard_timeout +=
(long long)worker->env.mesh->num_queries_discard_timeout;
s->svr.num_queries_wait_limit +=
(long long)worker->env.mesh->num_queries_wait_limit;
s->svr.num_dns_error_reports +=
(long long)worker->env.mesh->num_dns_error_reports;
/* values from outside network */
s->svr.unwanted_replies = (long long)worker->back->unwanted_replies;
s->svr.qtcp_outgoing = (long long)worker->back->num_tcp_outgoing;
@@ -325,20 +331,8 @@ server_stats_compile(struct worker* worker, struct ub_stats_info* s, int reset)
s->svr.num_query_dnscrypt_replay = 0;
#endif /* USE_DNSCRYPT */
if(worker->env.auth_zones) {
if(reset && !worker->env.cfg->stat_cumulative) {
lock_rw_wrlock(&worker->env.auth_zones->lock);
} else {
lock_rw_rdlock(&worker->env.auth_zones->lock);
}
s->svr.num_query_authzone_up = (long long)worker->env.
auth_zones->num_query_up;
s->svr.num_query_authzone_down = (long long)worker->env.
auth_zones->num_query_down;
if(reset && !worker->env.cfg->stat_cumulative) {
worker->env.auth_zones->num_query_up = 0;
worker->env.auth_zones->num_query_down = 0;
}
lock_rw_unlock(&worker->env.auth_zones->lock);
s->svr.num_query_authzone_up += (long long)worker->env.mesh->num_query_authzone_up;
s->svr.num_query_authzone_down += (long long)worker->env.mesh->num_query_authzone_down;
}
s->svr.mem_stream_wait =
(long long)tcp_req_info_get_stream_buffer_size();
@@ -451,9 +445,15 @@ void server_stats_add(struct ub_stats_info* total, struct ub_stats_info* a)
total->svr.num_queries_cookie_valid += a->svr.num_queries_cookie_valid;
total->svr.num_queries_cookie_client += a->svr.num_queries_cookie_client;
total->svr.num_queries_cookie_invalid += a->svr.num_queries_cookie_invalid;
total->svr.num_queries_discard_timeout +=
a->svr.num_queries_discard_timeout;
total->svr.num_queries_wait_limit += a->svr.num_queries_wait_limit;
total->svr.num_dns_error_reports += a->svr.num_dns_error_reports;
total->svr.num_queries_missed_cache += a->svr.num_queries_missed_cache;
total->svr.num_queries_prefetch += a->svr.num_queries_prefetch;
total->svr.num_queries_timed_out += a->svr.num_queries_timed_out;
total->svr.num_query_authzone_up += a->svr.num_query_authzone_up;
total->svr.num_query_authzone_down += a->svr.num_query_authzone_down;
if (total->svr.max_query_time_us < a->svr.max_query_time_us)
total->svr.max_query_time_us = a->svr.max_query_time_us;
total->svr.sum_query_list_size += a->svr.sum_query_list_size;
@@ -461,9 +461,9 @@ void server_stats_add(struct ub_stats_info* total, struct ub_stats_info* a)
#ifdef USE_DNSCRYPT
total->svr.num_query_dnscrypt_crypted += a->svr.num_query_dnscrypt_crypted;
total->svr.num_query_dnscrypt_cert += a->svr.num_query_dnscrypt_cert;
total->svr.num_query_dnscrypt_cleartext += \
total->svr.num_query_dnscrypt_cleartext +=
a->svr.num_query_dnscrypt_cleartext;
total->svr.num_query_dnscrypt_crypted_malformed += \
total->svr.num_query_dnscrypt_crypted_malformed +=
a->svr.num_query_dnscrypt_crypted_malformed;
#endif /* USE_DNSCRYPT */
/* the max size reached is upped to higher of both */
+62 -30
View File
@@ -463,6 +463,64 @@ detach(void)
#endif /* HAVE_DAEMON */
}
/* setup a listening ssl context, fatal_exit() on any failure */
static void
setup_listen_sslctx(void** ctx, int is_dot, int is_doh, struct config_file* cfg)
{
#ifdef HAVE_SSL
if(!(*ctx = listen_sslctx_create(
cfg->ssl_service_key, cfg->ssl_service_pem, NULL,
cfg->tls_ciphers, cfg->tls_ciphersuites,
(cfg->tls_session_ticket_keys.first &&
cfg->tls_session_ticket_keys.first->str[0] != 0),
is_dot, is_doh))) {
fatal_exit("could not set up listen SSL_CTX");
}
#else /* HAVE_SSL */
(void)ctx;(void)is_dot;(void)is_doh;(void)cfg;
#endif /* HAVE_SSL */
}
/* setups the needed ssl contexts, fatal_exit() on any failure */
static void
setup_sslctxs(struct daemon* daemon, struct config_file* cfg)
{
#ifdef HAVE_SSL
if(!(daemon->rc = daemon_remote_create(cfg)))
fatal_exit("could not set up remote-control");
if(cfg->ssl_service_key && cfg->ssl_service_key[0]) {
/* setup the session keys; the callback to use them will be
* attached to each sslctx separately */
if(cfg->tls_session_ticket_keys.first &&
cfg->tls_session_ticket_keys.first->str[0] != 0) {
if(!listen_sslctx_setup_ticket_keys(
cfg->tls_session_ticket_keys.first)) {
fatal_exit("could not set session ticket SSL_CTX");
}
}
(void)setup_listen_sslctx(&daemon->listen_dot_sslctx, 1, 0, cfg);
#ifdef HAVE_NGHTTP2_NGHTTP2_H
if(cfg_has_https(cfg)) {
(void)setup_listen_sslctx(&daemon->listen_doh_sslctx, 0, 1, cfg);
}
#endif
#ifdef HAVE_NGTCP2
if(cfg_has_quic(cfg)) {
if(!(daemon->listen_quic_sslctx = quic_sslctx_create(
cfg->ssl_service_key, cfg->ssl_service_pem, NULL))) {
fatal_exit("could not set up quic SSL_CTX");
}
}
#endif /* HAVE_NGTCP2 */
}
if(!(daemon->connect_dot_sslctx = connect_sslctx_create(NULL, NULL,
cfg->tls_cert_bundle, cfg->tls_win_cert)))
fatal_exit("could not set up connect SSL_CTX");
#else /* HAVE_SSL */
(void)daemon;(void)cfg;
#endif /* HAVE_SSL */
}
/** daemonize, drop user privileges and chroot if needed */
static void
perform_setup(struct daemon* daemon, struct config_file* cfg, int debug_mode,
@@ -489,36 +547,7 @@ perform_setup(struct daemon* daemon, struct config_file* cfg, int debug_mode,
#endif
/* read ssl keys while superuser and outside chroot */
#ifdef HAVE_SSL
if(!(daemon->rc = daemon_remote_create(cfg)))
fatal_exit("could not set up remote-control");
if(cfg->ssl_service_key && cfg->ssl_service_key[0]) {
if(!(daemon->listen_sslctx = listen_sslctx_create(
cfg->ssl_service_key, cfg->ssl_service_pem, NULL)))
fatal_exit("could not set up listen SSL_CTX");
if(cfg->tls_ciphers && cfg->tls_ciphers[0]) {
if (!SSL_CTX_set_cipher_list(daemon->listen_sslctx, cfg->tls_ciphers)) {
fatal_exit("failed to set tls-cipher %s", cfg->tls_ciphers);
}
}
#ifdef HAVE_SSL_CTX_SET_CIPHERSUITES
if(cfg->tls_ciphersuites && cfg->tls_ciphersuites[0]) {
if (!SSL_CTX_set_ciphersuites(daemon->listen_sslctx, cfg->tls_ciphersuites)) {
fatal_exit("failed to set tls-ciphersuites %s", cfg->tls_ciphersuites);
}
}
#endif
if(cfg->tls_session_ticket_keys.first &&
cfg->tls_session_ticket_keys.first->str[0] != 0) {
if(!listen_sslctx_setup_ticket_keys(daemon->listen_sslctx, cfg->tls_session_ticket_keys.first)) {
fatal_exit("could not set session ticket SSL_CTX");
}
}
}
if(!(daemon->connect_sslctx = connect_sslctx_create(NULL, NULL,
cfg->tls_cert_bundle, cfg->tls_win_cert)))
fatal_exit("could not set up connect SSL_CTX");
#endif
(void)setup_sslctxs(daemon, cfg);
/* init syslog (as root) if needed, before daemonize, otherwise
* a fork error could not be printed since daemonize closed stderr.*/
@@ -681,6 +710,9 @@ perform_setup(struct daemon* daemon, struct config_file* cfg, int debug_mode,
* it would succeed on SIGHUP as well */
if(!cfg->use_syslog)
log_init(cfg->logfile, cfg->use_syslog, cfg->chrootdir);
daemon->cfgfile = strdup(*cfgfile);
if(!daemon->cfgfile)
fatal_exit("out of memory in daemon cfgfile strdup");
}
/**
+104 -14
View File
@@ -371,6 +371,84 @@ worker_check_request(sldns_buffer* pkt, struct worker* worker,
return;
}
/**
* Send fast-reload acknowledgement to the mainthread in one byte.
* This signals that this worker has received the previous command.
* The worker is waiting if that is after a reload_stop command.
* Or the worker has briefly processed the event itself, and in doing so
* released data pointers to old config, after a reload_poll command.
*/
static void
worker_send_reload_ack(struct worker* worker)
{
/* If this is clipped to 8 bits because thread_num>255, then that
* is not a problem, the receiver counts the number of bytes received.
* The number is informative only. */
uint8_t c = (uint8_t)worker->thread_num;
ssize_t ret;
while(1) {
ret = send(worker->daemon->fast_reload_thread->commreload[1],
(void*)&c, 1, 0);
if(ret == -1) {
if(
#ifndef USE_WINSOCK
errno == EINTR || errno == EAGAIN
# ifdef EWOULDBLOCK
|| errno == EWOULDBLOCK
# endif
#else
WSAGetLastError() == WSAEINTR ||
WSAGetLastError() == WSAEINPROGRESS ||
WSAGetLastError() == WSAEWOULDBLOCK
#endif
)
continue; /* Try again. */
log_err("worker reload ack reply: send failed: %s",
sock_strerror(errno));
break;
}
break;
}
}
/** stop and wait to resume the worker */
static void
worker_stop_and_wait(struct worker* worker)
{
uint8_t* buf = NULL;
uint32_t len = 0, cmd;
worker_send_reload_ack(worker);
/* wait for reload */
if(!tube_read_msg(worker->cmd, &buf, &len, 0)) {
log_err("worker reload read reply failed");
return;
}
if(len != sizeof(uint32_t)) {
log_err("worker reload reply, bad control msg length %d",
(int)len);
free(buf);
return;
}
cmd = sldns_read_uint32(buf);
free(buf);
if(cmd == worker_cmd_quit) {
/* quit anyway */
verbose(VERB_ALGO, "reload reply, control cmd quit");
comm_base_exit(worker->base);
return;
}
if(cmd != worker_cmd_reload_start) {
log_err("worker reload reply, wrong reply command");
}
if(worker->daemon->fast_reload_drop_mesh) {
verbose(VERB_ALGO, "worker: drop mesh queries after reload");
mesh_delete_all(worker->env.mesh);
}
fast_reload_worker_pickup_changes(worker);
worker_send_reload_ack(worker);
verbose(VERB_ALGO, "worker resume after reload");
}
void
worker_handle_control_cmd(struct tube* ATTR_UNUSED(tube), uint8_t* msg,
size_t len, int error, void* arg)
@@ -406,6 +484,15 @@ worker_handle_control_cmd(struct tube* ATTR_UNUSED(tube), uint8_t* msg,
verbose(VERB_ALGO, "got control cmd remote");
daemon_remote_exec(worker);
break;
case worker_cmd_reload_stop:
verbose(VERB_ALGO, "got control cmd reload_stop");
worker_stop_and_wait(worker);
break;
case worker_cmd_reload_poll:
verbose(VERB_ALGO, "got control cmd reload_poll");
fast_reload_worker_pickup_changes(worker);
worker_send_reload_ack(worker);
break;
default:
log_err("bad command %d", (int)cmd);
break;
@@ -600,7 +687,8 @@ apply_respip_action(struct worker* worker, const struct query_info* qinfo,
return 1;
if(!respip_rewrite_reply(qinfo, cinfo, rep, encode_repp, &actinfo,
alias_rrset, 0, worker->scratchpad, az, NULL))
alias_rrset, 0, worker->scratchpad, az, NULL,
worker->env.views, worker->env.respip_set))
return 0;
/* xxx_deny actions mean dropping the reply, unless the original reply
@@ -761,7 +849,8 @@ answer_from_cache(struct worker* worker, struct query_info* qinfo,
} else if(partial_rep &&
!respip_merge_cname(partial_rep, qinfo, rep, cinfo,
must_validate, &encode_rep, worker->scratchpad,
worker->env.auth_zones)) {
worker->env.auth_zones, worker->env.views,
worker->env.respip_set)) {
goto bail_out;
}
if(encode_rep != rep) {
@@ -1082,7 +1171,7 @@ answer_notify(struct worker* w, struct query_info* qinfo,
if(verbosity >= VERB_DETAIL) {
char buf[380];
char zname[255+1];
char zname[LDNS_MAX_DOMAINLEN];
char sr[25];
dname_str(qinfo->qname, zname);
sr[0]=0;
@@ -1413,7 +1502,7 @@ worker_handle_request(struct comm_point* c, void* arg, int error,
return 0;
}
if(c->dnscrypt && !repinfo->is_dnscrypted) {
char buf[LDNS_MAX_DOMAINLEN+1];
char buf[LDNS_MAX_DOMAINLEN];
/* Check if this is unencrypted and asking for certs */
worker_check_request(c->buffer, worker, &check_result);
if(check_result.value != 0) {
@@ -1813,7 +1902,7 @@ worker_handle_request(struct comm_point* c, void* arg, int error,
cinfo_tmp.tag_datas = acladdr->tag_datas;
cinfo_tmp.tag_datas_size = acladdr->tag_datas_size;
cinfo_tmp.view = acladdr->view;
cinfo_tmp.respip_set = worker->daemon->respip_set;
cinfo_tmp.view_name = NULL;
cinfo = &cinfo_tmp;
}
@@ -1845,10 +1934,10 @@ worker_handle_request(struct comm_point* c, void* arg, int error,
* its qname must be that used for cache
* lookup. */
if((worker->env.cfg->prefetch &&
*worker->env.now >= rep->prefetch_ttl) ||
rep->prefetch_ttl <= *worker->env.now) ||
(worker->env.cfg->serve_expired &&
*worker->env.now > rep->ttl)) {
rep->ttl < *worker->env.now &&
!(*worker->env.now < rep->serve_expired_norec_ttl))) {
time_t leeway = rep->ttl - *worker->env.now;
if(rep->ttl < *worker->env.now)
leeway = 0;
@@ -1966,13 +2055,13 @@ worker_handle_request(struct comm_point* c, void* arg, int error,
&repinfo->client_addr, repinfo->client_addrlen,
tv, 1, c->buffer,
(worker->env.cfg->log_destaddr?(void*)repinfo->c->socket->addr:NULL),
c->type);
c->type, c->ssl);
} else {
log_reply_info(NO_VERBOSE, &qinfo,
&repinfo->client_addr, repinfo->client_addrlen,
tv, 1, c->buffer,
(worker->env.cfg->log_destaddr?(void*)repinfo->c->socket->addr:NULL),
c->type);
c->type, c->ssl);
}
}
#ifdef USE_DNSCRYPT
@@ -2173,10 +2262,11 @@ worker_init(struct worker* worker, struct config_file *cfg,
: cfg->tcp_idle_timeout,
cfg->harden_large_queries, cfg->http_max_streams,
cfg->http_endpoint, cfg->http_notls_downstream,
worker->daemon->tcl, worker->daemon->listen_sslctx,
worker->daemon->tcl, worker->daemon->listen_dot_sslctx,
worker->daemon->listen_doh_sslctx,
worker->daemon->listen_quic_sslctx,
dtenv, worker->daemon->doq_table, worker->env.rnd,
cfg->ssl_service_key, cfg->ssl_service_pem, cfg,
worker_handle_request, worker);
cfg, worker_handle_request, worker);
if(!worker->front) {
log_err("could not create listening sockets");
worker_delete(worker);
@@ -2191,7 +2281,7 @@ worker_init(struct worker* worker, struct config_file *cfg,
cfg->unwanted_threshold, cfg->outgoing_tcp_mss,
&worker_alloc_cleanup, worker,
cfg->do_udp || cfg->udp_upstream_without_downstream,
worker->daemon->connect_sslctx, cfg->delay_close,
worker->daemon->connect_dot_sslctx, cfg->delay_close,
cfg->tls_use_sni, dtenv, cfg->udp_connect,
cfg->max_reuse_tcp_queries, cfg->tcp_reuse_timeout,
cfg->tcp_auth_query_timeout);
+7 -1
View File
@@ -72,7 +72,13 @@ enum worker_commands {
/** obtain statistics without statsclear */
worker_cmd_stats_noreset,
/** execute remote control command */
worker_cmd_remote
worker_cmd_remote,
/** for fast-reload, perform stop */
worker_cmd_reload_stop,
/** for fast-reload, start again */
worker_cmd_reload_start,
/** for fast-reload, poll to make sure worker has released data */
worker_cmd_reload_poll
};
/**
+4 -2
View File
@@ -658,7 +658,8 @@ handle_event_moddone(struct module_qstate* qstate, int id)
!dns_cache_store(
qstate->env, &qstate->qinfo, qstate->return_msg->rep,
0, qstate->prefetch_leeway, 0, NULL,
qstate->query_flags, qstate->qstarttime))
qstate->query_flags, qstate->qstarttime,
qstate->is_valrec))
log_err("out of memory");
/* do nothing */
@@ -1008,7 +1009,8 @@ dns64_inform_super(struct module_qstate* qstate, int id,
/* Store the generated response in cache. */
if ( (!super_dq || !super_dq->started_no_cache_store) &&
!dns_cache_store(super->env, &super->qinfo, super->return_msg->rep,
0, super->prefetch_leeway, 0, NULL, super->query_flags, qstate->qstarttime))
0, super->prefetch_leeway, 0, NULL, super->query_flags,
qstate->qstarttime, qstate->is_valrec))
log_err("out of memory");
}
+20 -8
View File
@@ -192,8 +192,11 @@ static void
dt_apply_identity(struct dt_env *env, struct config_file *cfg)
{
char buf[MAXHOSTNAMELEN+1];
if (!cfg->dnstap_send_identity)
if (!cfg->dnstap_send_identity) {
free(env->identity);
env->identity = NULL;
return;
}
free(env->identity);
if (cfg->dnstap_identity == NULL || cfg->dnstap_identity[0] == 0) {
if (gethostname(buf, MAXHOSTNAMELEN) == 0) {
@@ -215,8 +218,11 @@ dt_apply_identity(struct dt_env *env, struct config_file *cfg)
static void
dt_apply_version(struct dt_env *env, struct config_file *cfg)
{
if (!cfg->dnstap_send_version)
if (!cfg->dnstap_send_version) {
free(env->version);
env->version = NULL;
return;
}
free(env->version);
if (cfg->dnstap_version == NULL || cfg->dnstap_version[0] == 0)
env->version = strdup(PACKAGE_STRING);
@@ -230,13 +236,8 @@ dt_apply_version(struct dt_env *env, struct config_file *cfg)
}
void
dt_apply_cfg(struct dt_env *env, struct config_file *cfg)
dt_apply_logcfg(struct dt_env *env, struct config_file *cfg)
{
if (!cfg->dnstap)
return;
dt_apply_identity(env, cfg);
dt_apply_version(env, cfg);
if ((env->log_resolver_query_messages = (unsigned int)
cfg->dnstap_log_resolver_query_messages))
{
@@ -275,6 +276,17 @@ dt_apply_cfg(struct dt_env *env, struct config_file *cfg)
lock_basic_unlock(&env->sample_lock);
}
void
dt_apply_cfg(struct dt_env *env, struct config_file *cfg)
{
if (!cfg->dnstap)
return;
dt_apply_identity(env, cfg);
dt_apply_version(env, cfg);
dt_apply_logcfg(env, cfg);
}
int
dt_init(struct dt_env *env, struct comm_base* base)
{
+7
View File
@@ -106,6 +106,13 @@ dt_create(struct config_file* cfg);
void
dt_apply_cfg(struct dt_env *env, struct config_file *cfg);
/**
* Apply config settings for log enable for message types.
* @param env: dnstap environment object.
* @param cfg: new config settings.
*/
void dt_apply_logcfg(struct dt_env *env, struct config_file *cfg);
/**
* Initialize per-worker state in dnstap environment object.
* @param env: dnstap environment object to initialize, created with dt_create().
+16 -1
View File
@@ -346,7 +346,8 @@ static struct tap_socket* tap_socket_new_tlsaccept(char* ip,
s->fd = -1;
s->ev_cb = ev_cb;
s->data = data;
s->sslctx = listen_sslctx_create(server_key, server_cert, verifypem);
s->sslctx = listen_sslctx_create(server_key, server_cert, verifypem,
NULL, NULL, 0, 0, 0);
if(!s->sslctx) {
log_err("could not create ssl context");
free(s->ip);
@@ -1786,6 +1787,20 @@ void remote_get_opt_ssl(char* ATTR_UNUSED(str), void* ATTR_UNUSED(arg))
log_assert(0);
}
void fast_reload_service_cb(int ATTR_UNUSED(fd), short ATTR_UNUSED(ev),
void* ATTR_UNUSED(arg))
{
log_assert(0);
}
int fast_reload_client_callback(struct comm_point* ATTR_UNUSED(c),
void* ATTR_UNUSED(arg), int ATTR_UNUSED(error),
struct comm_reply* ATTR_UNUSED(repinfo))
{
log_assert(0);
return 0;
}
#ifdef HAVE_NGTCP2
void doq_client_event_cb(int ATTR_UNUSED(fd), short ATTR_UNUSED(ev),
void* ATTR_UNUSED(arg))
+265
View File
@@ -1,9 +1,273 @@
11 April 2025: Yorgos
- Merge #1265: Fix WSAPoll.
10 April 2025: Wouter
- Fix for print of connection type in log-replies for dot and doh.
9 April 2025: Wouter
- Fix to detect if atomic_store links in configure.
- Fix #1264: unbound 1.22.0 leaks memory when doing DoH.
8 April 2025: Wouter
- Tag for 1.23.0rc1.
- Fix fast_reload to print chroot with config file name.
7 April 2025: Yorgos
- Merge #902: DNS Error Reporting (RFC 9567). Introduces new
configuration option 'dns-error-reporting' and new statistics for
'num.dns_error_reports'.
4 April 2025: Wouter
- Fix mesh_copy_client_info to omit null contents from copy.
- Fix comment name in the rpz nsdname test.
- Fix nettle compile for warnings and ticket keys.
- Fix redis_replica test for unused option defaults and log printout.
- Fix test to speed up common.sh script kill_pid.
- Fix to update common.sh for speed of kill_pid.
4 April 2025: Yorgos
- Merge #1019: Redis read-only replica support.
Introduces new 'redis-replica-*' options for the Redis cache backend.
3 April 2025: Wouter
- Fix #1263: Exempt loopback addresses from wait-limit.
- Fix wait-limit-netblock and wait-limit-cookie-netblock config parse
to allow two arguments.
- Fix ub_event and include dnstap and win_svc headers.
- Fix test for stat_values for wait limit defaults for localhost.
- Fix parameter unused warning in net_help.c.
2 April 2025: Yorgos
- Merge #1262 from markyang92, fix build with
'gcc-15 -Wbuiltin-declaration-mismatch' error in compat/malloc.c.
- For #1262, ifdef is no longer needed.
2 April 2025: Wouter
- Fix unbound-control test so it counts the new flush_negative output,
also answers the _ta probe from testns and prints command output
and skip a thread specific test when no threads are available.
- Fix that ub_event has the facility to deal with callbacks for
fast reload, doq, windows-stop and dnstap.
- Fix fast reload test to check if pid exists before acting on it.
1 April 2025: Wouter
- Fix escape more characters when printing an RR type with an unquoted
string.
- Enable the auth_tls.tdir and auth_tls_failcert.tdir tests.
31 March 2025: Wouter
- iana portlist update.
- Merge #1042: Fast Reload. The unbound-control fast_reload is added.
It reads changed config in a thread, then only briefly pauses the
service threads, that keep running. DNS service is only interrupted
briefly, less than a second.
- Skip the unit tests for auth_tls.tdir and auth_tls_failcert.tdir.
27 March 2025: Wouter
- Fix unit test dname log printout typecast.
- Fix for ci test, expat is installed on the osx image.
26 March 2025: Yorgos
- Fix #1255: Multiple pinnings to vulnerable copies of libexpat.
- For #1255, for ios use an older expat version that does not require
C++11 language features.
- For #1255, for ios disable building tests that require C++11.
- For #1255, for ios try the latest expat version again.
24 March 2025: Wouter
- Fix #1254: `send failed: Socket is not connected` and
`remote address is 0.0.0.0 port 53`.
21 March 2025: Wouter
- Fix #1253: Cache entries fail to be removed from Redis cachedb
backend with unbound-control flush* +c.
- Fix for #1253: Fix for redis cachedb backend to expect an integer
reply for the EXPIRE command.
20 March 2025: Wouter
- Fix print of RR type NSAP-PTR, it is an unquoted string.
18 March 2025: Wouter
- Fix #1251: WSAPoll first argument cannot be NULL.
- Fix for windows compile create ssl contexts.
17 March 2025: Wouter
- Fix representation of types GPOS and RESINFO, add rdf type for
unquoted str.
16 March 2025: Yorgos
- Fix 'unbound-control flush_negative' when reporting removed data;
reported by David 'eqvinox' Lamparter.
28 February 2025: Wouter
- Merge #1238: Prefer SOURCE_DATE_EPOCH over actual time.
Add --help output description for the SOURCE_DATE_EPOCH variable.
25 February 2025: Wouter
- Merge #1243: Do not shadow tm on line 236.
24 February 2025: Yorgos
- Fix hash calculation for cachedb to ignore case. Previously, cached
records there were only relevant for same case queries (if not
already in Unbound's internal cache).
19 February 2025: Yorgos
- Fix static analysis report about unhandled EOF on error conditions
when reading anchor key files.
- Merge #1241: Fix infra-keep-probing for low infra-cache-max-rtt
values.
17 February 2025: Yorgos
- Consider reconfigurations when calculating the still_useful_timeout
for servers in the infrastructure cache.
30 January 2025: Wouter
- Fix #986: Resolving sas.com with dnssec-validation fails though
signed delegations seem to be (mostly) correct.
29 January 2025: Yorgos
- Make the default value of module-config "validator iterator"
regardless of compilation options. --enable-subnet would implicitly
change the value to enable the subnetcache module by default in the
past.
24 January 2025: Yorgos
- Merge #1220 from Petr Menšík, Add unbound members group access to
control key.
21 January 2025: Yorgos
- Use the same interface listening port discovery code for all needed
protocols.
- Port to string only when needed before getaddrinfo().
- Do not open unencrypted channels next to encrypted ones on the same
port.
- Merge #1224 from Theo Buehler: Do not use DSA API unless USE_DSA is
set.
21 January 2025: Wouter
- Fix compile of interface check code when dnscrypt or quic is
disabled.
- Fix encoding of RR type ATMA.
- Fix to check length in ATMA string to wire.
- Merge #1229: check before use daemon->shm_info.
20 January 2025: Yorgos
- Merge #1222: Unique DoT and DoH SSL contexts to allow for different
ALPN.
- Create the quic SSL listening context only when needed.
15 January 2025: Yorgos
- Merge #1221: Consider auth zones when checking for forwarders.
14 January 2025: Yorgos
- Add resolver.arpa and service.arpa to the default locally served
zones.
13 January 2025: Yorgos
- Fix #1213: Misleading error message on default access control causing
refuse.
10 January 2025: Yorgos
- Merge #1214: Use TCP_NODELAY on TLS sockets to speed up the TLS
handshake.
31 December 2024: Yorgos
- Merge #1174: Serve expired cache update fixes. Fixes a regression bug
with serve-expired that appeared in 1.22.0 and would not allow the
iterator to update the cache with not-yet-validated entries resulting
in increased outgoing traffic.
20 December 2024: Yorgos
- For #1207: [FR] Support for RESINFO RRType 261 (RFC9606), add
LDNS_RR_TYPE_RESINFO similar to LDNS_RR_TYPE_TXT.
13 December 2024: Yorgos
- Merge #1204: ci: set persist-credentials: false for actions/checkout
per zizmor suggestion.
3 December 2024: Yorgos
- Merge #1189: Fix the dname_str method to cause conversion errors
when the domain name length is 255.
- Merge #1197: dname_str() fixes.
- For #1175, the default value of serve-expired-ttl is set to 86400
(1 day) as suggested by RFC8767.
- Merge #1198: Fix log-servfail with serve expired and no useful cache
contents.
- Safeguard alias loop while looking in the cache for expired answers.
- Merge #1187: Create the SSL_CTX for QUIC before chroot and privilege
drop.
- Fix typo in log_servfail.tdir test.
22 November 2024: Yorgos
- Fix #1175: serve-expired does not adhere to secure-by-default
principle. The default value of serve-expired-client-timeout
is set to 1800 as suggested by RFC8767.
- For #1175, update serve-expired tests.
20 November 2024: Yorgos
- Fix comparison to help static analyzer.
19 November 2024: Yorgos
- Merge #1169 from Sergey Kacheev, fix: lock-free counters for
auth_zone up/down queries.
15 November 2024: Wouter
- Fix #1183: the data being used is released in method
nsec3_hash_test_entry.
- Fix for #1183: release nsec3 hashes per test file.
8 November 2024: Yorgos
- More descriptive text for 'harden-algo-downgrade'.
- Complete fix for max-global-quota to 200.
6 November 2024: Yorgos
- Increase the default of max-global-quota to 200 from 128 after
operational feedback. Still keeping the possible amplification
factor (CAMP related issues) in the hundreds.
5 November 2024: Wouter
- Fix for the serve expired DNSSEC information fix, it would not allow
current delegation information be updated in cache. The fix allows
current delegation and validation recursion information to be
updated, but as a consequence no longer has certain expired
information around for later dnssec valid expired responses.
- Fix to log redis timeout error string on failure.
5 November 2024: Yorgos
- Fix SETEX check during Redis (re)initialization.
4 November 2024: Wouter
- Fix redis that during a reload it does not fail if the redis
server does not connect or does not respond. It still logs the
errors and if the server is up checks expiration features.
- Merge #1167: Makefile.in: fix occasional parallel build failures
around bison rule.
1 November 2024: Yorgos
- Merge #1159: Stats for discard-timeout and wait-limit.
- Add test case for #1159.
- Some clean up for stat_values.test.
- Merge #1170 from Melroy van den Berg, Fix chroot manpage
description.
- Merge #1157 from Liang Zhu, Fix heap corruption when calling
ub_ctx_delete in Windows.
25 October 2024: Yorgos
- Fix #1163: Typos in unbound.conf documentation.
17 October 2024: Wouter
- Tag for 1.22.0 release. This did not contain the 1154 fix
from 16 oct. The code repository continues with
version 1.22.1 in development.
16 October 2024: Yorgos
- Fix for dnsoverquic and dnstap to use the correct dnstap
environment.
16 October 2024: Wouter
- Fix for dnstap with dnscrypt and dnstap without dnsoverquic.
- Fix #1154: Tag Incorrectly Applying for Other Interfaces
Using the Same IP. This fix is not for 1.22.0.
14 October 2024: Wouter
- Fix to display warning if quic-port is set but dnsoverquic is not
@@ -23,6 +287,7 @@
- Fix add reallocarray to alloc stats unit test, and disable
override of strdup in unbound-host, and the result of config
get option is freed properly.
- Tag for 1.22.0rc1.
9 October 2024: Wouter
- Merge #871: DNS over QUIC. This adds `quic-port: 853` and
+1 -1
View File
@@ -1,4 +1,4 @@
README for Unbound 1.22.0
README for Unbound 1.23.0
Copyright 2007 NLnet Labs
http://unbound.net
+42 -12
View File
@@ -1,7 +1,7 @@
#
# Example configuration file.
#
# See unbound.conf(5) man page, version 1.22.0.
# See unbound.conf(5) man page, version 1.23.0.
#
# this is a comment.
@@ -194,7 +194,7 @@ server:
# iter-scrub-cname: 11
# Limit on upstream queries for an incoming query and its recursion.
# max-global-quota: 128
# max-global-quota: 200
# msec for waiting for an unknown server to reply. Increase if you
# are behind a slow satellite link, to eg. 1128.
@@ -215,6 +215,12 @@ server:
# Apart from the default, the wait limit with cookie can be adjusted.
# wait-limit-cookie-netblock: 192.0.2.0/24 50000
# Defaults for loopback, it has no wait limit.
# wait-limit-netblock: 127.0.0.0/8 -1
# wait-limit-netblock: ::1/128 -1
# wait-limit-cookie-netblock: 127.0.0.0/8 -1
# wait-limit-cookie-netblock: ::1/128 -1
# the amount of memory to use for the RRset cache.
# plain value in bytes or you can append k, m or G. default is "4Mb".
# rrset-cache-size: 4m
@@ -556,8 +562,9 @@ server:
# harden-referral-path: no
# Harden against algorithm downgrade when multiple algorithms are
# advertised in the DS record. If no, allows the weakest algorithm
# to validate the zone.
# advertised in the DS record. If no, allows any algorithm
# to validate the zone which is the standard behavior for validators.
# Check the manpage for detailed information.
# harden-algo-downgrade: no
# Harden against unknown records in the authority section and the
@@ -730,12 +737,13 @@ server:
# disable-edns-do: no
# Serve expired responses from cache, with serve-expired-reply-ttl in
# the response, and then attempt to fetch the data afresh.
# the response. By default it first tries to refresh an expired answer.
# Can be configured with serve-expired-client-timeout.
# serve-expired: no
#
# Limit serving of expired responses to configured seconds after
# expiration. 0 disables the limit.
# serve-expired-ttl: 0
# serve-expired-ttl: 86400
#
# Set the TTL of expired records to the serve-expired-ttl value after a
# failed attempt to retrieve the record from upstream. This makes sure
@@ -748,10 +756,9 @@ server:
#
# Time in milliseconds before replying to the client with expired data.
# This essentially enables the serve-stale behavior as specified in
# RFC 8767 that first tries to resolve before
# immediately responding with expired data. 0 disables this behavior.
# A recommended value is 1800.
# serve-expired-client-timeout: 0
# RFC 8767 that first tries to resolve before immediately responding
# with expired data. 0 disables this behavior.
# serve-expired-client-timeout: 1800
# Return the original TTL as received from the upstream name server rather
# than the decrementing TTL as stored in the cache. Enabling this feature
@@ -810,6 +817,8 @@ server:
# local-zone: "127.in-addr.arpa." nodefault
# local-zone: "1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.ip6.arpa." nodefault
# local-zone: "home.arpa." nodefault
# local-zone: "resolver.arpa." nodefault
# local-zone: "service.arpa." nodefault
# local-zone: "onion." nodefault
# local-zone: "test." nodefault
# local-zone: "invalid." nodefault
@@ -1077,6 +1086,11 @@ server:
# Note that the ede option above needs to be enabled for this to work.
# ede-serve-expired: no
# Enable DNS Error Reporting (RFC9567).
# qname-minimisation is advised to be turned on as well to increase
# privacy on the outgoing reports.
# dns-error-reporting: no
# Specific options for ipsecmod. Unbound needs to be configured with
# --enable-ipsecmod for these to take effect.
#
@@ -1304,9 +1318,9 @@ remote-control:
# # redis server's TCP port
# redis-server-port: 6379
# # if the server uses a unix socket, set its path, or "" when not used.
# # redis-server-path: "/var/lib/redis/redis-server.sock"
# redis-server-path: "/var/lib/redis/redis-server.sock"
# # if the server uses an AUTH password, specify here, or "" when not used.
# # redis-server-password: ""
# redis-server-password: ""
# # timeout (in ms) for communication with the redis server
# redis-timeout: 100
# # timeout (in ms) for commands, if 0, uses redis-timeout.
@@ -1317,6 +1331,22 @@ remote-control:
# redis-expire-records: no
# # redis logical database to use, 0 is the default database.
# redis-logical-db: 0
# # redis replica server's IP address or host name
# redis-replica-server-host: 127.0.0.1
# # redis replica server's TCP port
# redis-replica-server-port: 6379
# # if the replica server uses a unix socket, set its path, or "" when not used.
# redis-replica-server-path: "/var/lib/redis/redis-server.sock"
# # if the replica server uses an AUTH password, specify here, or "" when not used.
# redis-replica-server-password: ""
# # timeout (in ms) for communication with the redis replica server
# redis-replica-timeout: 100
# # timeout (in ms) for redis replica commands, if 0, uses redis-replica-timeout.
# redis-replica-command-timeout: 0
# # timeout (in ms) for redis replica connection set up, if 0, uses redis-replica-timeout.
# redis-replica-connect-timeout: 0
# # redis logical database to use for the replica server, 0 is the default database.
# redis-replica-logical-db: 0
# IPSet
# Add specify domain into set via ipset.
+2 -2
View File
@@ -1,4 +1,4 @@
.TH "libunbound" "3" "Oct 17, 2024" "NLnet Labs" "unbound 1.22.0"
.TH "libunbound" "3" "Apr 24, 2025" "NLnet Labs" "unbound 1.23.0"
.\"
.\" libunbound.3 -- unbound library functions manual
.\"
@@ -44,7 +44,7 @@
.B ub_ctx_zone_remove,
.B ub_ctx_data_add,
.B ub_ctx_data_remove
\- Unbound DNS validating resolver 1.22.0 functions.
\- Unbound DNS validating resolver 1.23.0 functions.
.SH "SYNOPSIS"
.B #include <unbound.h>
.LP
+1 -1
View File
@@ -1,4 +1,4 @@
.TH "unbound-anchor" "8" "Oct 17, 2024" "NLnet Labs" "unbound 1.22.0"
.TH "unbound-anchor" "8" "Apr 24, 2025" "NLnet Labs" "unbound 1.23.0"
.\"
.\" unbound-anchor.8 -- unbound anchor maintenance utility manual
.\"
+1 -1
View File
@@ -1,4 +1,4 @@
.TH "unbound-checkconf" "8" "Oct 17, 2024" "NLnet Labs" "unbound 1.22.0"
.TH "unbound-checkconf" "8" "Apr 24, 2025" "NLnet Labs" "unbound 1.23.0"
.\"
.\" unbound-checkconf.8 -- unbound configuration checker manual
.\"
+168 -1
View File
@@ -1,4 +1,4 @@
.TH "unbound-control" "8" "Oct 17, 2024" "NLnet Labs" "unbound 1.22.0"
.TH "unbound-control" "8" "Apr 24, 2025" "NLnet Labs" "unbound 1.23.0"
.\"
.\" unbound-control.8 -- unbound remote control manual
.\"
@@ -60,6 +60,155 @@ Reload the server but try to keep the RRset and message cache if
That means the caches sizes and the number of threads must not change between
reloads.
.TP
.B fast_reload \fR[\fI+dpv\fR]
Reload the server, but keep downtime to a minimum, so that user queries
keep seeing service. This needs the code compiled with threads. The config
is loaded in a thread, and prepared, then it briefly pauses the existing
server and updates config options. The intent is that the pause does not
impact the service of user queries. The cache is kept. Also user queries
worked on are kept and continue, but with the new config options.
.IP
This command is experimental at this time.
.IP
The amount of temporal memory needed during a fast_reload is twice the
amount needed for configuration.
This is because Unbound temporarily needs to store both current configuration
values and new ones while trying to fast_reload.
Zones loaded from disk (authority zones and RPZ zones) are included in such
memory needs.
.IP
Options that can be changed are for
forwards,
stubs,
views,
authority zones,
RPZ zones and
local zones.
.IP
Also
access-control and similar options,
interface-action and similar options and
tcp-connection-limit.
It can reload some
define-tag
changes, more on that below.
Further options include
insecure-lan-zones,
domain-insecure,
trust-anchor-file,
trust-anchor,
trusted-keys-file,
auto-trust-anchor-file,
edns-client-string,
ipset,
log-identity,
infra-cache-numhosts,
msg-cache-size,
rrset-cache-size,
key-cache-size,
ratelimit-size,
neg-cache-size,
num-queries-per-thread,
jostle-timeout,
use-caps-for-id,
unwanted-reply-threshold,
tls-use-sni,
outgoing-tcp-mss,
ip-dscp,
max-reuse-tcp-queries,
tcp-reuse-timeout,
tcp-auth-query-timeout,
delay-close.
.IP
It does not work with
interface and
outgoing-interface changes,
also not with
remote control,
outgoing-port-permit,
outgoing-port-avoid,
msg-buffer-size,
any **\*-slabs** options and
statistics-interval changes.
.IP
For dnstap these options can be changed:
dnstap-log-resolver-query-messages,
dnstap-log-resolver-response-messages,
dnstap-log-client-query-messages,
dnstap-log-client-response-messages,
dnstap-log-forwarder-query-messages and
dnstap-log-forwarder-response-messages.
.IP
It does not work with these options:
dnstap-enable,
dnstap-bidirectional,
dnstap-socket-path,
dnstap-ip,
dnstap-tls,
dnstap-tls-server-name,
dnstap-tls-cert-bundle,
dnstap-tls-client-key-file and
dnstap-tls-client-cert-file.
.IP
The options
dnstap-send-identity,
dnstap-send-version,
dnstap-identity, and
dnstap-version can be loaded
when ``+p`` is not used.
.IP
The '+v' option makes the output verbose which includes the time it took to do
the reload.
With '+vv' it is more verbose which includes the amount of memory that was
allocated temporarily to perform the reload; this amount of memory can be big
if the config has large contents.
In the timing output the 'reload' time is the time during which the server was
paused.
.IP
The '+p' option makes the reload not pause threads, they keep running.
Locks are acquired, but items are updated in sequence, so it is possible
for threads to see an inconsistent state with some options from the old
and some options from the new config, such as cache TTL parameters from the
old config and forwards from the new config. The stubs and forwards are
updated at the same time, so that they are viewed consistently, either old
or new values together. The option makes the reload time take eg. 3
microseconds instead of 0.3 milliseconds during which the worker threads are
interrupted. So, the interruption is much shorter, at the expense of some
inconsistency. After the reload itself, every worker thread is briefly
contacted to make them release resources, this makes the delete timing
a little longer, and takes up time from the remote control servicing
worker thread.
.IP
With the nopause option, the reload does not work to reload some options,
that fast reload works on without the nopause option: val-bogus-ttl,
val-override-date, val-sig-skew-min, val-sig-skew-max, val-max-restart,
val-nsec3-keysize-iterations, target-fetch-policy, outbound-msg-retry,
max-sent-count, max-query-restarts, do-not-query-address,
do-not-query-localhost, private-address, private-domain, caps-exempt,
nat64-prefix, do-nat64, infra-host-ttl, infra-keep-probing, ratelimit,
ip-ratelimit, ip-ratelimit-cookie, wait-limit-netblock,
wait-limit-cookie-netblock, ratelimit-below-domain, ratelimit-for-domain.
.IP
The '+d' option makes the reload drop queries that the worker threads are
working on. This is like flush_requestlist. Without it the queries are kept
so that users keep getting answers for those queries that are currently
processed. The drop makes it so that queries during the life time of the
query processing see only old, or only new config options.
.IP
When there are changes to the config tags, from the \fBdefine\-tag\fR option,
then the '+d' option is implicitly turned on with a warning printout, and
queries are dropped.
This is to stop references to the old tag information, by the old
queries. If the number of tags is increased in the newly loaded config, by
adding tags at the end, then the implicit '+d' option is not needed.
.IP
For response ip, that is actions associated with IP addresses, and perhaps
intersected with access control tag and action information, those settings
are stored with a query when it comes in based on its source IP address.
The old information is kept with the query until the queries are done.
This is gone when those queries are resolved and finished, or it is possible
to flush the requestlist with '+d'.
.TP
.B verbosity \fInumber
Change verbosity value for logging. Same values as \fBverbosity\fR keyword in
\fIunbound.conf\fR(5). This new setting lasts until the server is issued
@@ -422,6 +571,12 @@ number of queries with a client part only DNS Cookie by thread
.I threadX.num.queries_cookie_invalid
number of queries with an invalid DNS Cookie by thread
.TP
.I threadX.num.queries_discard_timeout
number of queries removed due to discard-timeout by thread
.TP
.I threadX.num.queries_wait_limit
number of queries removed due to wait-limit by thread
.TP
.I threadX.num.cachehits
number of queries that were successfully answered using a cache lookup
.TP
@@ -441,6 +596,9 @@ request for certificates.
.I threadX.num.dnscrypt.malformed
number of request that were neither cleartext, not valid dnscrypt messages.
.TP
.I threadX.num.dns_error_reports
number of DNS Error Reports generated by thread
.TP
.I threadX.num.prefetch
number of cache prefetches performed. This number is included in
cachehits, as the original query had the unprefetched answer from cache,
@@ -511,6 +669,12 @@ summed over threads.
.I total.num.queries_cookie_invalid
summed over threads.
.TP
.I total.num.queries_discard_timeout
summed over threads.
.TP
.I total.num.queries_wait_limit
summed over threads.
.TP
.I total.num.cachehits
summed over threads.
.TP
@@ -529,6 +693,9 @@ summed over threads.
.I total.num.dnscrypt.malformed
summed over threads.
.TP
.I total.num.dns_error_reports
summed over threads.
.TP
.I total.num.prefetch
summed over threads.
.TP
+1 -1
View File
@@ -1,4 +1,4 @@
.TH "unbound\-host" "1" "Oct 17, 2024" "NLnet Labs" "unbound 1.22.0"
.TH "unbound\-host" "1" "Apr 24, 2025" "NLnet Labs" "unbound 1.23.0"
.\"
.\" unbound-host.1 -- unbound DNS lookup utility
.\"
+2 -2
View File
@@ -1,4 +1,4 @@
.TH "unbound" "8" "Oct 17, 2024" "NLnet Labs" "unbound 1.22.0"
.TH "unbound" "8" "Apr 24, 2025" "NLnet Labs" "unbound 1.23.0"
.\"
.\" unbound.8 -- unbound manual
.\"
@@ -9,7 +9,7 @@
.\"
.SH "NAME"
.B unbound
\- Unbound DNS validating resolver 1.22.0.
\- Unbound DNS validating resolver 1.23.0.
.SH "SYNOPSIS"
.B unbound
.RB [ \-h ]
+155 -49
View File
@@ -1,4 +1,4 @@
.TH "unbound.conf" "5" "Oct 17, 2024" "NLnet Labs" "unbound 1.22.0"
.TH "unbound.conf" "5" "Apr 24, 2025" "NLnet Labs" "unbound 1.23.0"
.\"
.\" unbound.conf.5 -- unbound.conf manual
.\"
@@ -326,11 +326,15 @@ The wait limit for the netblock. If not given the wait\-limit value is
used. The most specific netblock is used to determine the limit. Useful for
overriding the default for a specific, group or individual, server.
The value -1 disables wait limits for the netblock.
By default the loopback has a wait limit netblock of -1, it is not limited,
because it is separated from the rest of network for spoofed packets.
The loopback addresses 127.0.0.0/8 and ::1/128 are default at -1.
.TP
.B wait\-limit\-cookie\-netblock: \fI<netblock> <number>
The wait limit for the netblock, when the query has a DNS cookie.
If not given, the wait\-limit\-cookie value is used.
The value -1 disables wait limits for the netblock.
The loopback addresses 127.0.0.0/8 and ::1/128 are default at -1.
.TP
.B so\-rcvbuf: \fI<number>
If not 0, then set the SO_RCVBUF socket option to get more buffer
@@ -716,7 +720,7 @@ and initial ACL (check if the proxy itself is denied/refused by configuration).
The proxied address (if any) will then be used as the true client address and
will be used where applicable for logging, ACL, DNSTAP, RPZ and IP ratelimiting.
PROXYv2 is supported for UDP and TCP/TLS listening interfaces.
There is no support for PROXYv2 on a DoH or DNSCrypt listening interface.
There is no support for PROXYv2 on a DoH, DoQ or DNSCrypt listening interface.
Can list multiple, each on a new statement.
.TP
.B quic\-port: \fI<number>
@@ -902,9 +906,8 @@ outside of the chroot directory.
Additionally, Unbound may need to access /dev/urandom (for entropy)
from inside the chroot.
.IP
If given a chroot is done to the given directory. By default chroot is
enabled and the default is "@UNBOUND_CHROOT_DIR@". If you give "" no
chroot is performed.
If given a chroot is done to the given directory. The chroot is by default
set to "@UNBOUND_CHROOT_DIR@". If you give "" no chroot is performed.
.TP
.B username: \fI<name>
If given, after binding the port the user privileges are dropped. Default is
@@ -1056,11 +1059,11 @@ closer to that of BIND 9, while setting "\-1 \-1 \-1 \-1 \-1" gives behaviour
rumoured to be closer to that of BIND 8.
.TP
.B harden\-short\-bufsize: \fI<yes or no>
Very small EDNS buffer sizes from queries are ignored. Default is on, as
Very small EDNS buffer sizes from queries are ignored. Default is yes, as
described in the standard.
.TP
.B harden\-large\-queries: \fI<yes or no>
Very large queries are ignored. Default is off, since it is legal protocol
Very large queries are ignored. Default is no, since it is legal protocol
wise to send these, and could be necessary for operation if TSIG or EDNS
payload is very large.
.TP
@@ -1107,10 +1110,23 @@ to increase the max depth that is checked to.
.TP
.B harden\-algo\-downgrade: \fI<yes or no>
Harden against algorithm downgrade when multiple algorithms are
advertised in the DS record. If no, allows the weakest algorithm to
validate the zone. Default is no. Zone signers must produce zones
that allow this feature to work, but sometimes they do not, and turning
this option off avoids that validation failure.
advertised in the DS record.
This works by first choosing only the strongest DS digest type as per RFC 4509
(Unbound treats the highest algorithm as the strongest) and then
expecting signatures from all the advertised signing algorithms from the chosen
DS(es) to be present.
If no, allows any one supported algorithm to validate the zone, even if other advertised algorithms are broken.
Default is no.
RFC 6840 mandates that zone signers must produce zones signed with all
advertised algorithms, but sometimes they do not.
RFC 6840 also clarifies that this requirement is not for validators and
validators should accept any single valid path.
It should thus be explicitly noted that this option violates RFC 6840 for
DNSSEC validation and should only be used to perform a signature
completeness test to support troubleshooting.
Using this option may break DNSSEC resolution with non-RFC6840-conforming
signers and/or in multi-signer configurations that don't send all the
advertised signatures.
.TP
.B harden\-unknown\-additional: \fI<yes or no>
Harden against unknown records in the authority section and additional
@@ -1131,7 +1147,7 @@ queries. For domains that do not support 0x20 and also fail with fallback
because they keep sending different answers, like some load balancers.
Can be given multiple times, for different domains.
.TP
.B caps\-whitelist: \fI<yes or no>
.B caps\-whitelist: \fI<domain>
Alternate syntax for \fBcaps\-exempt\fR.
.TP
.B qname\-minimisation: \fI<yes or no>
@@ -1248,9 +1264,6 @@ Adding \fIrespip\fR to the front will cause RPZ processing to be done on
all queries.
The default is "\fIvalidator iterator\fR".
.IP
When the server is built with
EDNS client subnet support the default is "\fIsubnetcache validator
iterator\fR".
Most modules that need to be listed here have to be listed at the beginning
of the line. The subnetcachedb module has to be listed just before
the iterator.
@@ -1390,15 +1403,17 @@ Default is no.
.TP
.B serve\-expired: \fI<yes or no>
If enabled, Unbound attempts to serve old responses from cache with a
TTL of \fBserve\-expired\-reply\-ttl\fR in the response without waiting for the
actual resolution to finish. The actual resolution answer ends up in the cache
later on. Default is "no".
TTL of \fBserve\-expired\-reply\-ttl\fR in the response.
By default the expired answer will be used after a resolution attempt errored
out or is taking more than serve\-expired\-client\-timeout to resolve.
Default is "no".
.TP
.B serve\-expired\-ttl: \fI<seconds>
Limit serving of expired responses to configured seconds after expiration. 0
disables the limit. This option only applies when \fBserve\-expired\fR is
enabled. A suggested value per RFC 8767 is between
86400 (1 day) and 259200 (3 days). The default is 0.
Limit serving of expired responses to configured seconds after expiration.
0 disables the limit.
This option only applies when \fBserve\-expired\fR is enabled.
A suggested value per RFC 8767 is between 86400 (1 day) and 259200 (3 days).
The default is 86400.
.TP
.B serve\-expired\-ttl\-reset: \fI<yes or no>
Set the TTL of expired records to the \fBserve\-expired\-ttl\fR value after a
@@ -1412,12 +1427,14 @@ TTL value to use when replying with expired data. If
use 30 as the value (RFC 8767). The default is 30.
.TP
.B serve\-expired\-client\-timeout: \fI<msec>
Time in milliseconds before replying to the client with expired data. This
essentially enables the serve-stale behavior as specified in
Time in milliseconds before replying to the client with expired data.
This essentially enables the serve-stale behavior as specified in
RFC 8767 that first tries to resolve before immediately
responding with expired data. A recommended value per
RFC 8767 is 1800. Setting this to 0 will disable this
behavior. Default is 0.
responding with expired data.
Setting this to 0 will disable this behavior and instead serve the expired
record immediately from the cache before attempting to refresh it via
resolution.
Default is 1800.
.TP
.B serve\-original\-ttl: \fI<yes or no>
If enabled, Unbound will always return the original TTL as received from
@@ -1622,6 +1639,7 @@ given zone. Use \fInodefault\fR if you use exactly that zone, if you want to
use a subzone, use \fItransparent\fR.
.P
The default zones are localhost, reverse 127.0.0.1 and ::1, the home.arpa,
the resolver.arpa, the service.arpa,
the onion, test, invalid and the AS112 zones. The AS112 zones are reverse
DNS zones for private use and reserved IP addresses for which the servers
on the internet cannot provide correct answers. They are configured by
@@ -1677,6 +1695,24 @@ local\-data: "home.arpa. 10800 IN
SOA localhost. nobody.invalid. 1 3600 1200 604800 10800"
.fi
.TP 10
\h'5'\fIresolver.arpa (RFC 9462)\fR
Default content:
.nf
local\-zone: "resolver.arpa." static
local\-data: "resolver.arpa. 10800 IN NS localhost."
local\-data: "resolver.arpa. 10800 IN
SOA localhost. nobody.invalid. 1 3600 1200 604800 10800"
.fi
.TP 10
\h'5'\fIservice.arpa (draft-ietf-dnssd-srp-25)\fR
Default content:
.nf
local\-zone: "service.arpa." static
local\-data: "service.arpa. 10800 IN NS localhost."
local\-data: "service.arpa. 10800 IN
SOA localhost. nobody.invalid. 1 3600 1200 604800 10800"
.fi
.TP 10
\h'5'\fIonion (RFC 7686)\fR
Default content:
.nf
@@ -1998,7 +2034,7 @@ Default is 11.
Limit on the number of upstream queries sent out for an incoming query and
its subqueries from recursion. It is not reset during the resolution. When
it is exceeded the query is failed and the lookup process stops.
Default is 128.
Default is 200.
.TP 5
.B fast\-server\-permil: \fI<number>
Specify how many times out of 1000 to pick from the set of fastest servers.
@@ -2053,17 +2089,30 @@ be used. Default is 65001.
.TP 5
.B ede: \fI<yes or no>
If enabled, Unbound will respond with Extended DNS Error codes (RFC8914).
These EDEs attach informative error messages to a response for various
errors. Default is "no".
These EDEs provide additional information with a response mainly for, but not
limited to, DNS and DNSSEC errors.
When the \fBval-log-level\fR option is also set to \fB2\fR, responses with
Extended DNS Errors concerning DNSSEC failures that are not served from cache,
will also contain a descriptive text message about the reason for the failure.
Extended DNS Errors concerning DNSSEC failures will also contain a descriptive
text message about the reason for the failure.
Default is "no".
.TP 5
.B ede\-serve\-expired: \fI<yes or no>
If enabled, Unbound will attach an Extended DNS Error (RFC8914) Code 3 - Stale
Answer as EDNS0 option to the expired response. Note that this will not attach
the EDE code without setting the global \fBede\fR option to "yes" as well.
Answer as EDNS0 option to the expired response.
The \fBede\fR option needs to be enabled as well for this to work.
Default is "no".
.TP 5
.B dns\-error\-reporting: \fI<yes or no>
If enabled, Unbound will send DNS Error Reports (RFC9567).
The name servers need to express support by attaching the Report-Channel EDNS0
option on their replies specifying the reporting agent for the zone.
Any errors encountered during resolution that would result in Unbound
generating an Extended DNS Error (RFC8914) will be reported to the zone's
reporting agent.
The \fBede\fR option does not need to be enabled for this to work.
It is advised that the \fBqname\-minimisation\fR option is also enabled to
increase privacy on the outgoing reports.
Default is "no".
.SS "Remote Control Options"
In the
@@ -2476,8 +2525,8 @@ The dynamic library file to load. Repeat this option for every dynlib module
instance added to the \fBmodule\-config:\fR option.
.SS "DNS64 Module Options"
.LP
The dns64 module must be configured in the \fBmodule\-config:\fR "dns64
validator iterator" directive and be compiled into the daemon to be
The dns64 module must be configured in the \fBmodule\-config:\fR directive
e.g., "dns64 validator iterator" and be compiled into the daemon to be
enabled. These settings go in the \fBserver:\fR section.
.TP
.B dns64\-prefix: \fI<IPv6 prefix>\fR
@@ -2577,8 +2626,8 @@ in the dnscrypt nonce cache. Close to the number of cpus is
a fairly good setting.
.SS "EDNS Client Subnet Module Options"
.LP
The ECS module must be configured in the \fBmodule\-config:\fR "subnetcache
validator iterator" directive and be compiled into the daemon to be
The ECS module must be configured in the \fBmodule\-config:\fR directive e.g.,
"subnetcache validator iterator" and be compiled into the daemon to be
enabled. These settings go in the \fBserver:\fR section.
.LP
If the destination address is allowed in the configuration Unbound will add the
@@ -2599,6 +2648,15 @@ configuration file. On top of that, for each query only 100 different subnets
are allowed to be stored for each address family. Exceeding that number, older
entries will be purged from cache.
.LP
Note that due to the nature of how EDNS Client Subnet works, by segregating the
client IP space in order to try and have tailored responses for prefixes of
unknown sizes, resolution and cache response performance are impacted as a
result.
Usage of the subnetcache module should only be enabled in installations that
require such functionality where the resolver and the clients belong to
different networks.
An example of that is an open resolver installation.
.LP
This module does not interact with the \fBserve\-expired*\fR and
\fBprefetch:\fR options.
.TP
@@ -2649,8 +2707,8 @@ Specifies the maximum number of subnets ECS answers kept in the ECS radix tree.
This number applies for each qname/qclass/qtype tuple. Defaults to 100.
.SS "Opportunistic IPsec Support Module Options"
.LP
The IPsec module must be configured in the \fBmodule\-config:\fR "ipsecmod
validator iterator" directive and be compiled into Unbound by using
The IPsec module must be configured in the \fBmodule\-config:\fR directive
e.g., "ipsecmod validator iterator" and be compiled into Unbound by using
\fB\-\-enable\-ipsecmod\fR to be enabled.
These settings go in the \fBserver:\fR section.
.LP
@@ -2715,12 +2773,12 @@ Allow the ipsecmod functionality for the domain so that the module logic will be
executed. Can be given multiple times, for different domains. If the option is
not specified, all domains are treated as being allowed (default).
.TP
.B ipsecmod\-whitelist: \fI<yes or no>
.B ipsecmod\-whitelist: \fI<domain>
Alternate syntax for \fBipsecmod\-allow\fR.
.SS "Cache DB Module Options"
.LP
The Cache DB module must be configured in the \fBmodule\-config:\fR
"validator cachedb iterator" directive and be compiled into the daemon
The Cache DB module must be configured in the \fBmodule\-config:\fR directive
e.g., "validator cachedb iterator" and be compiled into the daemon
with \fB\-\-enable\-cachedb\fR.
If this module is enabled and configured, the specified backend database
works as a second level cache:
@@ -2815,12 +2873,12 @@ The TCP port number of the Redis server.
This option defaults to 6379.
.TP
.B redis-server-path: \fI<unix socket path>\fR
The unix socket path to connect to the redis server. Off by default, and it
The unix socket path to connect to the Redis server. Off by default, and it
can be set to "" to turn this off. Unix sockets may have better throughput
than the IP address option.
.TP
.B redis-server-password: \fI"<password>"\fR
The Redis AUTH password to use for the redis server.
The Redis AUTH password to use for the Redis server.
Only relevant if Redis is configured for client password authorisation.
Off by default, and it can be set to "" to turn this off.
.TP
@@ -2832,12 +2890,14 @@ re-establish a new connection later.
This option defaults to 100 milliseconds.
.TP
.B redis-command-timeout: \fI<msec>\fR
The timeout to use for redis commands, in milliseconds. If 0, it uses the
redis\-timeout value. The default is 0.
The timeout to use for Redis commands, in milliseconds.
If 0, it uses the \fBredis\-timeout\fR value.
The default is 0.
.TP
.B redis-connect-timeout: \fI<msec>\fR
The timeout to use for redis connection set up, in milliseconds. If 0, it
uses the redis\-timeout value. The default is 0.
The timeout to use for Redis connection set up, in milliseconds.
If 0, it uses the \fBredis\-timeout\fR value.
The default is 0.
.TP
.B redis-expire-records: \fI<yes or no>
If Redis record expiration is enabled. If yes, Unbound sets timeout for Redis
@@ -2857,6 +2917,52 @@ for multiple unrelated applications.
The default database in Redis is 0 while other logical databases need to be
explicitly SELECT'ed upon connecting.
This option defaults to 0.
.TP
.B redis-replica-server-host: \fI<server address or name>\fR
The IP (either v6 or v4) address or domain name of the Redis replica server.
In general an IP address should be specified as otherwise Unbound will have to
resolve the name of the server every time it establishes a connection
to the server.
This server is treated as a read-only replica server
(https://redis.io/docs/management/replication/#read-only-replica).
If specified, all Redis read commands will go to this replica server, while
the write commands will go to the \fBredis-server-host\fR.
This option defaults to "" (disabled).
.TP
.B redis-replica-server-port: \fI<port number>\fR
The TCP port number of the Redis replica server.
This option defaults to 6379.
.TP
.B redis-replica-server-path: \fI<unix socket path>\fR
The unix socket path to connect to the Redis server. Off by default, and it
can be set to "" to turn this off. Unix sockets may have better throughput
than the IP address option.
.TP
.B redis-replica-server-password: \fI"<password>"\fR
The Redis AUTH password to use for the Redis replica server.
Only relevant if Redis is configured for client password authorisation.
Off by default, and it can be set to "" to turn this off.
.TP
.B redis-replica-timeout: \fI<msec>\fR
The period until when Unbound waits for a response from the Redis replica sever.
If this timeout expires Unbound closes the connection, treats it as
if the Redis replica server does not have the requested data, and will try to
re-establish a new connection later.
This option defaults to 100 milliseconds.
.TP
.B redis-replica-command-timeout: \fI<msec>\fR
The timeout to use for Redis replica commands, in milliseconds.
If 0, it uses the \fBredis\-replica\-timeout\fR value.
The default is 0.
.TP
.B redis-replica-connect-timeout: \fI<msec>\fR
The timeout to use for Redis replica connection set up, in milliseconds.
If 0, it uses the \fBredis\-replica\-timeout\fR value.
The default is 0.
.TP
.B redis-replica-logical-db: \fI<logical database index>
Same as \fBredis-logical-db\fR but for the Redis replica server.
This option defaults to 0.
.SS DNSTAP Logging Options
DNSTAP support, when compiled in by using \fB\-\-enable\-dnstap\fR, is enabled
in the \fBdnstap:\fR section.
+2 -1
View File
@@ -456,7 +456,8 @@ ipsecmod_handle_query(struct module_qstate* qstate,
/* Store A/AAAA in cache. */
if(!dns_cache_store(qstate->env, &qstate->qinfo,
qstate->return_msg->rep, 0, qstate->prefetch_leeway,
0, qstate->region, qstate->query_flags, qstate->qstarttime)) {
0, qstate->region, qstate->query_flags, qstate->qstarttime,
qstate->is_valrec)) {
log_err("ipsecmod: out of memory caching record");
}
qstate->ext_state[id] = module_finished;
+1 -1
View File
@@ -278,7 +278,7 @@ delegpt_count_addr(struct delegpt* dp, size_t* numaddr, size_t* numres,
void delegpt_log(enum verbosity_value v, struct delegpt* dp)
{
char buf[LDNS_MAX_DOMAINLEN+1];
char buf[LDNS_MAX_DOMAINLEN];
struct delegpt_ns* ns;
struct delegpt_addr* a;
size_t missing=0, numns=0, numaddr=0, numres=0, numavail=0;
+51 -1
View File
@@ -129,7 +129,7 @@ forwards_insert_data(struct iter_forwards* fwd, uint16_t c, uint8_t* nm,
node->namelabs = nmlabs;
node->dp = dp;
if(!rbtree_insert(fwd->tree, &node->node)) {
char buf[257];
char buf[LDNS_MAX_DOMAINLEN];
dname_str(nm, buf);
log_err("duplicate forward zone %s ignored.", buf);
delegpt_free_mlc(dp);
@@ -331,6 +331,30 @@ make_stub_holes(struct iter_forwards* fwd, struct config_file* cfg)
return 1;
}
/** make NULL entries for auths */
static int
make_auth_holes(struct iter_forwards* fwd, struct config_file* cfg)
{
struct config_auth* a;
uint8_t* dname;
size_t dname_len;
for(a = cfg->auths; a; a = a->next) {
if(!a->name) continue;
dname = sldns_str2wire_dname(a->name, &dname_len);
if(!dname) {
log_err("cannot parse auth name '%s'", a->name);
return 0;
}
if(!fwd_add_stub_hole(fwd, LDNS_RR_CLASS_IN, dname)) {
free(dname);
log_err("out of memory");
return 0;
}
free(dname);
}
return 1;
}
int
forwards_apply_cfg(struct iter_forwards* fwd, struct config_file* cfg)
{
@@ -353,6 +377,16 @@ forwards_apply_cfg(struct iter_forwards* fwd, struct config_file* cfg)
lock_rw_unlock(&fwd->lock);
return 0;
}
/* TODO: Now we punch holes for auth zones as well so that in
* iterator:forward_request() we see the configured
* delegation point, but code flow/naming is hard to follow.
* Consider having a single tree with configured
* delegation points for all categories
* (stubs, forwards, auths). */
if(!make_auth_holes(fwd, cfg)) {
lock_rw_unlock(&fwd->lock);
return 0;
}
fwd_init_parents(fwd);
lock_rw_unlock(&fwd->lock);
return 1;
@@ -590,3 +624,19 @@ forwards_delete_stub_hole(struct iter_forwards* fwd, uint16_t c,
fwd_init_parents(fwd);
if(!nolock) { lock_rw_unlock(&fwd->lock); }
}
void
forwards_swap_tree(struct iter_forwards* fwd, struct iter_forwards* data)
{
rbtree_type* oldtree = fwd->tree;
if(oldtree) {
lock_unprotect(&fwd->lock, oldtree);
}
if(data->tree) {
lock_unprotect(&data->lock, data->tree);
}
fwd->tree = data->tree;
data->tree = oldtree;
lock_protect(&fwd->lock, fwd->tree, sizeof(*fwd->tree));
lock_protect(&data->lock, data->tree, sizeof(*data->tree));
}
+9
View File
@@ -234,4 +234,13 @@ int forwards_add_stub_hole(struct iter_forwards* fwd, uint16_t c,
void forwards_delete_stub_hole(struct iter_forwards* fwd, uint16_t c,
uint8_t* nm, int nolock);
/**
* Swap internal tree with preallocated entries. Caller should manage
* the locks.
* @param fwd: the forward data structure.
* @param data: the data structure used to take elements from. This contains
* the old elements on return.
*/
void forwards_swap_tree(struct iter_forwards* fwd, struct iter_forwards* data);
#endif /* ITERATOR_ITER_FWD_H */
+12 -1
View File
@@ -181,7 +181,7 @@ hints_insert(struct iter_hints* hints, uint16_t c, struct delegpt* dp,
node->noprime = (uint8_t)noprime;
if(!name_tree_insert(&hints->tree, &node->node, dp->name, dp->namelen,
dp->namelabs, c)) {
char buf[257];
char buf[LDNS_MAX_DOMAINLEN];
dname_str(dp->name, buf);
log_err("second hints for zone %s ignored.", buf);
delegpt_free_mlc(dp);
@@ -611,3 +611,14 @@ hints_delete_stub(struct iter_hints* hints, uint16_t c, uint8_t* nm,
name_tree_init_parents(&hints->tree);
if(!nolock) { lock_rw_unlock(&hints->lock); }
}
void
hints_swap_tree(struct iter_hints* hints, struct iter_hints* data)
{
rbnode_type* oldroot = hints->tree.root;
size_t oldcount = hints->tree.count;
hints->tree.root = data->tree.root;
hints->tree.count = data->tree.count;
data->tree.root = oldroot;
data->tree.count = oldcount;
}
+9
View File
@@ -198,4 +198,13 @@ int hints_add_stub(struct iter_hints* hints, uint16_t c, struct delegpt* dp,
void hints_delete_stub(struct iter_hints* hints, uint16_t c,
uint8_t* nm, int nolock);
/**
* Swap internal tree with preallocated entries. Caller should manage
* the locks.
* @param hints: the hints data structure.
* @param data: the data structure used to take elements from. This contains
* the old elements on return.
*/
void hints_swap_tree(struct iter_hints* hints, struct iter_hints* data);
#endif /* ITERATOR_ITER_HINTS_H */
+104 -49
View File
@@ -77,41 +77,73 @@
static const char DEFAULT_NAT64_PREFIX[] = "64:ff9b::/96";
/** fillup fetch policy array */
static void
fetch_fill(struct iter_env* ie, const char* str)
static int
fetch_fill(int* target_fetch_policy, int max_dependency_depth, const char* str)
{
char* s = (char*)str, *e;
int i;
for(i=0; i<ie->max_dependency_depth+1; i++) {
ie->target_fetch_policy[i] = strtol(s, &e, 10);
if(s == e)
fatal_exit("cannot parse fetch policy number %s", s);
for(i=0; i<max_dependency_depth+1; i++) {
target_fetch_policy[i] = strtol(s, &e, 10);
if(s == e) {
log_err("cannot parse fetch policy number %s", s);
return 0;
}
s = e;
}
return 1;
}
/** Read config string that represents the target fetch policy */
static int
read_fetch_policy(struct iter_env* ie, const char* str)
int
read_fetch_policy(int** target_fetch_policy, int* max_dependency_depth,
const char* str)
{
int count = cfg_count_numbers(str);
if(count < 1) {
log_err("Cannot parse target fetch policy: \"%s\"", str);
return 0;
}
ie->max_dependency_depth = count - 1;
ie->target_fetch_policy = (int*)calloc(
(size_t)ie->max_dependency_depth+1, sizeof(int));
if(!ie->target_fetch_policy) {
*max_dependency_depth = count - 1;
*target_fetch_policy = (int*)calloc(
(size_t)(*max_dependency_depth)+1, sizeof(int));
if(!*target_fetch_policy) {
log_err("alloc fetch policy: out of memory");
return 0;
}
fetch_fill(ie, str);
if(!fetch_fill(*target_fetch_policy, *max_dependency_depth, str))
return 0;
return 1;
}
/** apply config caps whitelist items to name tree */
static int
struct rbtree_type*
caps_white_create(void)
{
struct rbtree_type* caps_white = rbtree_create(name_tree_compare);
if(!caps_white)
log_err("out of memory");
return caps_white;
}
/** delete caps_whitelist element */
static void
caps_free(struct rbnode_type* n, void* ATTR_UNUSED(d))
{
if(n) {
free(((struct name_tree_node*)n)->name);
free(n);
}
}
void
caps_white_delete(struct rbtree_type* caps_white)
{
if(!caps_white)
return;
traverse_postorder(caps_white, caps_free, NULL);
free(caps_white);
}
int
caps_white_apply_cfg(rbtree_type* ntree, struct config_file* cfg)
{
struct config_strlist* p;
@@ -145,12 +177,41 @@ caps_white_apply_cfg(rbtree_type* ntree, struct config_file* cfg)
}
int
iter_apply_cfg(struct iter_env* iter_env, struct config_file* cfg)
nat64_apply_cfg(struct iter_nat64* nat64, struct config_file* cfg)
{
const char *nat64_prefix;
nat64_prefix = cfg->nat64_prefix;
if(!nat64_prefix)
nat64_prefix = cfg->dns64_prefix;
if(!nat64_prefix)
nat64_prefix = DEFAULT_NAT64_PREFIX;
if(!netblockstrtoaddr(nat64_prefix, 0, &nat64->nat64_prefix_addr,
&nat64->nat64_prefix_addrlen, &nat64->nat64_prefix_net)) {
log_err("cannot parse nat64-prefix netblock: %s", nat64_prefix);
return 0;
}
if(!addr_is_ip6(&nat64->nat64_prefix_addr,
nat64->nat64_prefix_addrlen)) {
log_err("nat64-prefix is not IPv6: %s", cfg->nat64_prefix);
return 0;
}
if(!prefixnet_is_nat64(nat64->nat64_prefix_net)) {
log_err("nat64-prefix length it not 32, 40, 48, 56, 64 or 96: %s",
nat64_prefix);
return 0;
}
nat64->use_nat64 = cfg->do_nat64;
return 1;
}
int
iter_apply_cfg(struct iter_env* iter_env, struct config_file* cfg)
{
int i;
/* target fetch policy */
if(!read_fetch_policy(iter_env, cfg->target_fetch_policy))
if(!read_fetch_policy(&iter_env->target_fetch_policy,
&iter_env->max_dependency_depth, cfg->target_fetch_policy))
return 0;
for(i=0; i<iter_env->max_dependency_depth+1; i++)
verbose(VERB_QUERY, "target fetch policy for level %d is %d",
@@ -170,7 +231,7 @@ iter_apply_cfg(struct iter_env* iter_env, struct config_file* cfg)
}
if(cfg->caps_whitelist) {
if(!iter_env->caps_white)
iter_env->caps_white = rbtree_create(name_tree_compare);
iter_env->caps_white = caps_white_create();
if(!iter_env->caps_white || !caps_white_apply_cfg(
iter_env->caps_white, cfg)) {
log_err("Could not set capsforid whitelist");
@@ -179,31 +240,13 @@ iter_apply_cfg(struct iter_env* iter_env, struct config_file* cfg)
}
nat64_prefix = cfg->nat64_prefix;
if(!nat64_prefix)
nat64_prefix = cfg->dns64_prefix;
if(!nat64_prefix)
nat64_prefix = DEFAULT_NAT64_PREFIX;
if(!netblockstrtoaddr(nat64_prefix, 0, &iter_env->nat64_prefix_addr,
&iter_env->nat64_prefix_addrlen,
&iter_env->nat64_prefix_net)) {
log_err("cannot parse nat64-prefix netblock: %s", nat64_prefix);
return 0;
}
if(!addr_is_ip6(&iter_env->nat64_prefix_addr,
iter_env->nat64_prefix_addrlen)) {
log_err("nat64-prefix is not IPv6: %s", cfg->nat64_prefix);
return 0;
}
if(!prefixnet_is_nat64(iter_env->nat64_prefix_net)) {
log_err("nat64-prefix length it not 32, 40, 48, 56, 64 or 96: %s",
nat64_prefix);
if(!nat64_apply_cfg(&iter_env->nat64, cfg)) {
log_err("Could not setup nat64");
return 0;
}
iter_env->supports_ipv6 = cfg->do_ip6;
iter_env->supports_ipv4 = cfg->do_ip4;
iter_env->use_nat64 = cfg->do_nat64;
iter_env->outbound_msg_retry = cfg->outbound_msg_retry;
iter_env->max_sent_count = cfg->max_sent_count;
iter_env->max_query_restarts = cfg->max_query_restarts;
@@ -270,7 +313,7 @@ iter_filter_unsuitable(struct iter_env* iter_env, struct module_env* env,
if(!iter_env->supports_ipv6 && addr_is_ip6(&a->addr, a->addrlen)) {
return -1; /* there is no ip6 available */
}
if(!iter_env->supports_ipv4 && !iter_env->use_nat64 &&
if(!iter_env->supports_ipv4 && !iter_env->nat64.use_nat64 &&
!addr_is_ip6(&a->addr, a->addrlen)) {
return -1; /* there is no ip4 available */
}
@@ -693,10 +736,11 @@ dns_copy_msg(struct dns_msg* from, struct regional* region)
void
iter_dns_store(struct module_env* env, struct query_info* msgqinf,
struct reply_info* msgrep, int is_referral, time_t leeway, int pside,
struct regional* region, uint16_t flags, time_t qstarttime)
struct regional* region, uint16_t flags, time_t qstarttime,
int is_valrec)
{
if(!dns_cache_store(env, msgqinf, msgrep, is_referral, leeway,
pside, region, flags, qstarttime))
pside, region, flags, qstarttime, is_valrec))
log_err("out of memory: cannot store data in cache");
}
@@ -1488,14 +1532,15 @@ iter_stub_fwd_no_cache(struct module_qstate *qstate, struct query_info *qinf,
/* check stub */
if (stub != NULL && stub->dp != NULL) {
enum verbosity_value level = VERB_ALGO;
int stub_no_cache = stub->dp->no_cache;
lock_rw_unlock(&qstate->env->fwds->lock);
if(stub_no_cache) {
char qname[255+1];
char dpname[255+1];
if(verbosity >= level && stub_no_cache) {
char qname[LDNS_MAX_DOMAINLEN];
char dpname[LDNS_MAX_DOMAINLEN];
dname_str(qinf->qname, qname);
dname_str(stub->dp->name, dpname);
verbose(VERB_ALGO, "stub for %s %s has no_cache", qname, dpname);
verbose(level, "stub for %s %s has no_cache", qname, dpname);
}
if(retdpname) {
if(stub->dp->namelen > dpname_storage_len) {
@@ -1516,14 +1561,15 @@ iter_stub_fwd_no_cache(struct module_qstate *qstate, struct query_info *qinf,
/* Check for forward. */
if (dp) {
enum verbosity_value level = VERB_ALGO;
int dp_no_cache = dp->no_cache;
lock_rw_unlock(&qstate->env->hints->lock);
if(dp_no_cache) {
char qname[255+1];
char dpname[255+1];
if(verbosity >= level && dp_no_cache) {
char qname[LDNS_MAX_DOMAINLEN];
char dpname[LDNS_MAX_DOMAINLEN];
dname_str(qinf->qname, qname);
dname_str(dp->name, dpname);
verbose(VERB_ALGO, "forward for %s %s has no_cache", qname, dpname);
verbose(level, "forward for %s %s has no_cache", qname, dpname);
}
if(retdpname) {
if(dp->namelen > dpname_storage_len) {
@@ -1606,3 +1652,12 @@ limit_nsec_ttl(struct dns_msg* msg)
}
}
}
void
iter_make_minimal(struct reply_info* rep)
{
size_t rem = rep->ns_numrrsets + rep->ar_numrrsets;
rep->ns_numrrsets = 0;
rep->ar_numrrsets = 0;
rep->rrset_count -= rem;
}
+48 -1
View File
@@ -61,6 +61,7 @@ struct sock_list;
struct ub_packed_rrset_key;
struct module_stack;
struct outside_network;
struct iter_nat64;
/* max number of lookups in the cache for target nameserver names.
* This stops, for large delegations, N*N lookups in the cache. */
@@ -142,6 +143,7 @@ struct dns_msg* dns_copy_msg(struct dns_msg* from, struct regional* regional);
* @param region: to copy modified (cache is better) rrs back to.
* @param flags: with BIT_CD for dns64 AAAA translated queries.
* @param qstarttime: time of query start.
* @param is_valrec: if the query is validation recursion and does not get
* return void, because we are not interested in alloc errors,
* the iterator and validator can operate on the results in their
* scratch space (the qstate.region) and are not dependent on the cache.
@@ -150,7 +152,8 @@ struct dns_msg* dns_copy_msg(struct dns_msg* from, struct regional* regional);
*/
void iter_dns_store(struct module_env* env, struct query_info* qinf,
struct reply_info* rep, int is_referral, time_t leeway, int pside,
struct regional* region, uint16_t flags, time_t qstarttime);
struct regional* region, uint16_t flags, time_t qstarttime,
int is_valrec);
/**
* Select randomly with n/m probability.
@@ -428,6 +431,43 @@ int iter_stub_fwd_no_cache(struct module_qstate *qstate,
void iterator_set_ip46_support(struct module_stack* mods,
struct module_env* env, struct outside_network* outnet);
/**
* Read config string that represents the target fetch policy.
* @param target_fetch_policy: alloced on return.
* @param max_dependency_depth: set on return.
* @param str: the config string
* @return false on failure.
*/
int read_fetch_policy(int** target_fetch_policy, int* max_dependency_depth,
const char* str);
/**
* Create caps exempt data structure.
* @return NULL on failure.
*/
struct rbtree_type* caps_white_create(void);
/**
* Delete caps exempt data structure.
* @param caps_white: caps exempt tree.
*/
void caps_white_delete(struct rbtree_type* caps_white);
/**
* Apply config caps whitelist items to name tree
* @param ntree: caps exempt tree.
* @param cfg: config with options.
*/
int caps_white_apply_cfg(struct rbtree_type* ntree, struct config_file* cfg);
/**
* Apply config for nat64
* @param nat64: the nat64 state.
* @param cfg: config with options.
* @return false on failure.
*/
int nat64_apply_cfg(struct iter_nat64* nat64, struct config_file* cfg);
/**
* Limit NSEC and NSEC3 TTL in response, RFC9077
* @param msg: dns message, the SOA record ttl is used to restrict ttls
@@ -435,4 +475,11 @@ void iterator_set_ip46_support(struct module_stack* mods,
*/
void limit_nsec_ttl(struct dns_msg* msg);
/**
* Make the response minimal. Removed authority and additional section,
* that works when there is an answer in the answer section.
* @param rep: reply to modify.
*/
void iter_make_minimal(struct reply_info* rep);
#endif /* ITERATOR_ITER_UTILS_H */
+40 -41
View File
@@ -71,13 +71,15 @@
#include "sldns/sbuffer.h"
/* number of packets */
int MAX_GLOBAL_QUOTA = 128;
int MAX_GLOBAL_QUOTA = 200;
/* in msec */
int UNKNOWN_SERVER_NICENESS = 376;
/* in msec */
int USEFUL_SERVER_TOP_TIMEOUT = 120000;
/* Equals USEFUL_SERVER_TOP_TIMEOUT*4 */
int BLACKLIST_PENALTY = (120000*4);
/** Timeout when only a single probe query per IP is allowed. */
int PROBE_MAXRTO = PROBE_MAXRTO_DEFAULT; /* in msec */
static void target_count_increase_nx(struct iter_qstate* iq, int num);
@@ -105,16 +107,6 @@ iter_init(struct module_env* env, int id)
return 1;
}
/** delete caps_whitelist element */
static void
caps_free(struct rbnode_type* n, void* ATTR_UNUSED(d))
{
if(n) {
free(((struct name_tree_node*)n)->name);
free(n);
}
}
void
iter_deinit(struct module_env* env, int id)
{
@@ -126,10 +118,7 @@ iter_deinit(struct module_env* env, int id)
free(iter_env->target_fetch_policy);
priv_delete(iter_env->priv);
donotq_delete(iter_env->donotq);
if(iter_env->caps_white) {
traverse_postorder(iter_env->caps_white, caps_free, NULL);
free(iter_env->caps_white);
}
caps_white_delete(iter_env->caps_white);
free(iter_env);
env->modinfo[id] = NULL;
}
@@ -258,7 +247,7 @@ error_supers(struct module_qstate* qstate, int id, struct module_qstate* super)
log_err("out of memory adding missing");
}
delegpt_mark_neg(dpns, qstate->qinfo.qtype);
if((dpns->got4 == 2 || (!ie->supports_ipv4 && !ie->use_nat64)) &&
if((dpns->got4 == 2 || (!ie->supports_ipv4 && !ie->nat64.use_nat64)) &&
(dpns->got6 == 2 || !ie->supports_ipv6)) {
dpns->resolved = 1; /* mark as failed */
target_count_increase_nx(super_iq, 1);
@@ -368,7 +357,7 @@ error_response_cache(struct module_qstate* qstate, int id, int rcode)
err.security = sec_status_indeterminate;
verbose(VERB_ALGO, "store error response in message cache");
iter_dns_store(qstate->env, &qstate->qinfo, &err, 0, 0, 0, NULL,
qstate->query_flags, qstate->qstarttime);
qstate->query_flags, qstate->qstarttime, qstate->is_valrec);
return error_response(qstate, id, rcode);
}
@@ -1092,7 +1081,7 @@ auth_zone_delegpt(struct module_qstate* qstate, struct iter_qstate* iq,
/* cache is blacklisted and fallback, and we
* already have an auth_zone dp */
if(verbosity>=VERB_ALGO) {
char buf[255+1];
char buf[LDNS_MAX_DOMAINLEN];
dname_str(z->name, buf);
verbose(VERB_ALGO, "auth_zone %s "
"fallback because cache blacklisted",
@@ -1109,7 +1098,7 @@ auth_zone_delegpt(struct module_qstate* qstate, struct iter_qstate* iq,
* validation failure, and the zone allows
* fallback to the internet, query there. */
if(verbosity>=VERB_ALGO) {
char buf[255+1];
char buf[LDNS_MAX_DOMAINLEN];
dname_str(z->name, buf);
verbose(VERB_ALGO, "auth_zone %s "
"fallback because cache blacklisted",
@@ -1723,7 +1712,7 @@ processInitRequest(struct module_qstate* qstate, struct iter_qstate* iq,
*/
if(iter_dp_is_useless(&qstate->qinfo, qstate->query_flags,
iq->dp, ie->supports_ipv4, ie->supports_ipv6,
ie->use_nat64)) {
ie->nat64.use_nat64)) {
int have_dp = 0;
if(!can_have_last_resort(qstate->env, iq->dp->name, iq->dp->namelen, iq->qchase.qclass, &have_dp, &iq->dp, qstate->region)) {
if(have_dp) {
@@ -2033,7 +2022,7 @@ query_for_targets(struct module_qstate* qstate, struct iter_qstate* iq,
return 1;
if(iq->depth > 0 && iq->target_count &&
iq->target_count[TARGET_COUNT_QUERIES] > MAX_TARGET_COUNT) {
char s[LDNS_MAX_DOMAINLEN+1];
char s[LDNS_MAX_DOMAINLEN];
dname_str(qstate->qinfo.qname, s);
verbose(VERB_QUERY, "request %s has exceeded the maximum "
"number of glue fetches %d", s,
@@ -2041,7 +2030,7 @@ query_for_targets(struct module_qstate* qstate, struct iter_qstate* iq,
return 2;
}
if(iq->dp_target_count > MAX_DP_TARGET_COUNT) {
char s[LDNS_MAX_DOMAINLEN+1];
char s[LDNS_MAX_DOMAINLEN];
dname_str(qstate->qinfo.qname, s);
verbose(VERB_QUERY, "request %s has exceeded the maximum "
"number of glue fetches %d to a single delegation point",
@@ -2087,7 +2076,7 @@ query_for_targets(struct module_qstate* qstate, struct iter_qstate* iq,
if(mesh_jostle_exceeded(qstate->env->mesh)) {
/* If no ip4 query is possible, that makes
* this ns resolved. */
if(!((ie->supports_ipv4 || ie->use_nat64) &&
if(!((ie->supports_ipv4 || ie->nat64.use_nat64) &&
((ns->lame && !ns->done_pside4) ||
(!ns->lame && !ns->got4)))) {
ns->resolved = 1;
@@ -2096,7 +2085,7 @@ query_for_targets(struct module_qstate* qstate, struct iter_qstate* iq,
}
}
/* Send the A request. */
if((ie->supports_ipv4 || ie->use_nat64) &&
if((ie->supports_ipv4 || ie->nat64.use_nat64) &&
((ns->lame && !ns->done_pside4) ||
(!ns->lame && !ns->got4))) {
if(!generate_target_query(qstate, iq, id,
@@ -2252,7 +2241,7 @@ processLastResort(struct module_qstate* qstate, struct iter_qstate* iq,
}
if(iq->depth > 0 && iq->target_count &&
iq->target_count[TARGET_COUNT_QUERIES] > MAX_TARGET_COUNT) {
char s[LDNS_MAX_DOMAINLEN+1];
char s[LDNS_MAX_DOMAINLEN];
dname_str(qstate->qinfo.qname, s);
verbose(VERB_QUERY, "request %s has exceeded the maximum "
"number of glue fetches %d", s,
@@ -2268,14 +2257,14 @@ processLastResort(struct module_qstate* qstate, struct iter_qstate* iq,
/* if this nameserver is at a delegation point, but that
* delegation point is a stub and we cannot go higher, skip*/
if( ((ie->supports_ipv6 && !ns->done_pside6) ||
((ie->supports_ipv4 || ie->use_nat64) && !ns->done_pside4)) &&
((ie->supports_ipv4 || ie->nat64.use_nat64) && !ns->done_pside4)) &&
!can_have_last_resort(qstate->env, ns->name, ns->namelen,
iq->qchase.qclass, NULL, NULL, NULL)) {
log_nametypeclass(VERB_ALGO, "cannot pside lookup ns "
"because it is also a stub/forward,",
ns->name, LDNS_RR_TYPE_NS, iq->qchase.qclass);
if(ie->supports_ipv6) ns->done_pside6 = 1;
if(ie->supports_ipv4 || ie->use_nat64) ns->done_pside4 = 1;
if(ie->supports_ipv4 || ie->nat64.use_nat64) ns->done_pside4 = 1;
continue;
}
/* query for parent-side A and AAAA for nameservers */
@@ -2300,7 +2289,7 @@ processLastResort(struct module_qstate* qstate, struct iter_qstate* iq,
return 0;
}
}
if((ie->supports_ipv4 || ie->use_nat64) && !ns->done_pside4) {
if((ie->supports_ipv4 || ie->nat64.use_nat64) && !ns->done_pside4) {
/* Send the A request. */
if(!generate_parentside_target_query(qstate, iq, id,
ns->name, ns->namelen,
@@ -2569,7 +2558,7 @@ processQueryTargets(struct module_qstate* qstate, struct iter_qstate* iq,
}
if(!ie->supports_ipv6)
delegpt_no_ipv6(iq->dp);
if(!ie->supports_ipv4 && !ie->use_nat64)
if(!ie->supports_ipv4 && !ie->nat64.use_nat64)
delegpt_no_ipv4(iq->dp);
delegpt_log(VERB_ALGO, iq->dp);
@@ -2741,9 +2730,7 @@ processQueryTargets(struct module_qstate* qstate, struct iter_qstate* iq,
if((iq->chase_flags&BIT_RD) && !(iq->response->rep->flags&BIT_AA)) {
verbose(VERB_ALGO, "forwarder, ignoring referral from auth zone");
} else {
lock_rw_wrlock(&qstate->env->auth_zones->lock);
qstate->env->auth_zones->num_query_up++;
lock_rw_unlock(&qstate->env->auth_zones->lock);
qstate->env->mesh->num_query_authzone_up++;
iq->num_current_queries++;
iq->chase_to_rd = 0;
iq->dnssec_lame_query = 0;
@@ -3046,7 +3033,7 @@ 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];
char s[LDNS_MAX_DOMAINLEN];
dname_str(qstate->qinfo.qname, s);
verbose(VERB_QUERY, "request %s has exceeded the maximum "
"global quota on number of upstream queries %d", s,
@@ -3070,9 +3057,9 @@ processQueryTargets(struct module_qstate* qstate, struct iter_qstate* iq,
real_addr = target->addr;
real_addrlen = target->addrlen;
if(ie->use_nat64 && target->addr.ss_family == AF_INET) {
addr_to_nat64(&target->addr, &ie->nat64_prefix_addr,
ie->nat64_prefix_addrlen, ie->nat64_prefix_net,
if(ie->nat64.use_nat64 && target->addr.ss_family == AF_INET) {
addr_to_nat64(&target->addr, &ie->nat64.nat64_prefix_addr,
ie->nat64.nat64_prefix_addrlen, ie->nat64.nat64_prefix_net,
&real_addr, &real_addrlen);
log_name_addr(VERB_QUERY, "applied NAT64:",
iq->dp->name, &real_addr, real_addrlen);
@@ -3296,6 +3283,16 @@ processQueryResponse(struct module_qstate* qstate, struct iter_qstate* iq,
iq->num_target_queries = 0;
return processDSNSFind(qstate, iq, id);
}
if(iq->qchase.qtype == LDNS_RR_TYPE_DNSKEY && SERVE_EXPIRED
&& qstate->is_valrec &&
reply_find_answer_rrset(&iq->qchase, iq->response->rep) != NULL) {
/* clean out the authority section, if any, so it
* does not overwrite dnssec valid data in the
* validation recursion lookup. */
verbose(VERB_ALGO, "make DNSKEY minimal for serve "
"expired");
iter_make_minimal(iq->response->rep);
}
if(!qstate->no_cache_store)
iter_dns_store(qstate->env, &iq->response->qinfo,
iq->response->rep,
@@ -3303,7 +3300,7 @@ processQueryResponse(struct module_qstate* qstate, struct iter_qstate* iq,
qstate->prefetch_leeway,
iq->dp&&iq->dp->has_parent_side_NS,
qstate->region, qstate->query_flags,
qstate->qstarttime);
qstate->qstarttime, qstate->is_valrec);
/* close down outstanding requests to be discarded */
outbound_list_clear(&iq->outlist);
iq->num_current_queries = 0;
@@ -3397,7 +3394,7 @@ processQueryResponse(struct module_qstate* qstate, struct iter_qstate* iq,
/* no prefetch-leeway, since its not the answer */
iter_dns_store(qstate->env, &iq->response->qinfo,
iq->response->rep, 1, 0, 0, NULL, 0,
qstate->qstarttime);
qstate->qstarttime, qstate->is_valrec);
if(iq->store_parent_NS)
iter_store_parentside_NS(qstate->env,
iq->response->rep);
@@ -3527,7 +3524,8 @@ processQueryResponse(struct module_qstate* qstate, struct iter_qstate* iq,
iter_dns_store(qstate->env, &iq->response->qinfo,
iq->response->rep, 1, qstate->prefetch_leeway,
iq->dp&&iq->dp->has_parent_side_NS, NULL,
qstate->query_flags, qstate->qstarttime);
qstate->query_flags, qstate->qstarttime,
qstate->is_valrec);
/* set the current request's qname to the new value. */
iq->qchase.qname = sname;
iq->qchase.qname_len = snamelen;
@@ -3871,7 +3869,7 @@ processTargetResponse(struct module_qstate* qstate, int id,
} else {
verbose(VERB_ALGO, "iterator TargetResponse failed");
delegpt_mark_neg(dpns, qstate->qinfo.qtype);
if((dpns->got4 == 2 || (!ie->supports_ipv4 && !ie->use_nat64)) &&
if((dpns->got4 == 2 || (!ie->supports_ipv4 && !ie->nat64.use_nat64)) &&
(dpns->got6 == 2 || !ie->supports_ipv6)) {
dpns->resolved = 1; /* fail the target */
/* do not count cached answers */
@@ -4154,7 +4152,7 @@ processFinished(struct module_qstate* qstate, struct iter_qstate* iq,
iq->response->rep, 0, qstate->prefetch_leeway,
iq->dp&&iq->dp->has_parent_side_NS,
qstate->region, qstate->query_flags,
qstate->qstarttime);
qstate->qstarttime, qstate->is_valrec);
}
}
qstate->return_rcode = LDNS_RCODE_NOERROR;
@@ -4334,6 +4332,7 @@ process_response(struct module_qstate* qstate, struct iter_qstate* iq,
}
/* Copy the edns options we may got from the back end */
qstate->edns_opts_back_in = NULL;
if(edns.opt_list_in) {
qstate->edns_opts_back_in = edns_opt_copy_region(edns.opt_list_in,
qstate->region);
+16 -10
View File
@@ -46,8 +46,6 @@
#include "util/data/msgreply.h"
#include "util/module.h"
struct delegpt;
struct iter_hints;
struct iter_forwards;
struct iter_donotq;
struct iter_prep_list;
struct iter_priv;
@@ -108,15 +106,9 @@ extern int BLACKLIST_PENALTY;
#define EMPTY_NODATA_RETRY_COUNT 2
/**
* Global state for the iterator.
* Iterator global state for nat64.
*/
struct iter_env {
/** A flag to indicate whether or not we have an IPv6 route */
int supports_ipv6;
/** A flag to indicate whether or not we have an IPv4 route */
int supports_ipv4;
struct iter_nat64 {
/** A flag to locally apply NAT64 to make IPv4 addrs into IPv6 */
int use_nat64;
@@ -128,6 +120,20 @@ struct iter_env {
/** CIDR mask length of NAT64 prefix */
int nat64_prefix_net;
};
/**
* Global state for the iterator.
*/
struct iter_env {
/** A flag to indicate whether or not we have an IPv6 route */
int supports_ipv6;
/** A flag to indicate whether or not we have an IPv4 route */
int supports_ipv4;
/** State for nat64 */
struct iter_nat64 nat64;
/** A set of inetaddrs that should never be queried. */
struct iter_donotq* donotq;
+15 -1
View File
@@ -423,7 +423,7 @@ int libworker_bg(struct ub_ctx* ctx)
static int
fill_canon(struct ub_result* res, uint8_t* s)
{
char buf[255+2];
char buf[LDNS_MAX_DOMAINLEN];
dname_str(s, buf);
res->canonname = strdup(buf);
return res->canonname != 0;
@@ -1059,6 +1059,20 @@ void dtio_mainfdcallback(int ATTR_UNUSED(fd), short ATTR_UNUSED(ev),
}
#endif
void fast_reload_service_cb(int ATTR_UNUSED(fd), short ATTR_UNUSED(ev),
void* ATTR_UNUSED(arg))
{
log_assert(0);
}
int fast_reload_client_callback(struct comm_point* ATTR_UNUSED(c),
void* ATTR_UNUSED(arg), int ATTR_UNUSED(error),
struct comm_reply* ATTR_UNUSED(repinfo))
{
log_assert(0);
return 0;
}
#ifdef HAVE_NGTCP2
void doq_client_event_cb(int ATTR_UNUSED(fd), short ATTR_UNUSED(ev),
void* ATTR_UNUSED(arg))
+6
View File
@@ -849,6 +849,12 @@ struct ub_server_stats {
long long mem_quic;
/** number of queries over (DNS over) QUIC */
long long qquic;
/** number of queries removed due to discard-timeout */
long long num_queries_discard_timeout;
/** number of queries removed due to wait-limit */
long long num_queries_wait_limit;
/** number of dns error reports generated */
long long num_dns_error_reports;
};
/**
File diff suppressed because it is too large Load Diff
+201
View File
@@ -0,0 +1,201 @@
/*
* pythonmod_utils.c: utilities used by wrapper
*
* Copyright (c) 2009, Zdenek Vasicek (vasicek AT fit.vutbr.cz)
* Marek Vavrusa (xvavru00 AT stud.fit.vutbr.cz)
*
* This software is open source.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* * Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* * Neither the name of the organization nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
/**
* \file
* Utility functions for the python module that perform stores and loads and
* conversions.
*/
#include "config.h"
#include "pythonmod/pythonmod_utils.h"
#include "util/module.h"
#include "util/netevent.h"
#include "util/net_help.h"
#include "services/cache/dns.h"
#include "services/cache/rrset.h"
#include "util/data/msgparse.h"
#include "util/data/msgreply.h"
#include "util/storage/slabhash.h"
#include "util/regional.h"
#include "iterator/iter_delegpt.h"
#include "sldns/sbuffer.h"
#undef _POSIX_C_SOURCE
#undef _XOPEN_SOURCE
#include <Python.h>
/* Store the reply_info and query_info pair in message cache
* (qstate->msg_cache) */
int storeQueryInCache(struct module_qstate* qstate, struct query_info* qinfo,
struct reply_info* msgrep, int is_referral)
{
if (!msgrep)
return 0;
/* authoritative answer can't be stored in cache */
if (msgrep->authoritative) {
PyErr_SetString(PyExc_ValueError,
"Authoritative answer can't be stored");
return 0;
}
return dns_cache_store(qstate->env, qinfo, msgrep, is_referral,
qstate->prefetch_leeway, 0, NULL, qstate->query_flags,
qstate->qstarttime, qstate->is_valrec);
}
/* Invalidate the message associated with query_info stored in message cache */
void invalidateQueryInCache(struct module_qstate* qstate,
struct query_info* qinfo)
{
hashvalue_type h;
struct lruhash_entry* e;
struct reply_info *r;
size_t i, j;
h = query_info_hash(qinfo, qstate->query_flags);
if ((e=slabhash_lookup(qstate->env->msg_cache, h, qinfo, 0))) {
r = (struct reply_info*)(e->data);
if (r) {
r->ttl = 0;
if(rrset_array_lock(r->ref, r->rrset_count, *qstate->env->now)) {
for(i=0; i< r->rrset_count; i++) {
struct packed_rrset_data* data =
(struct packed_rrset_data*) r->ref[i].key->entry.data;
if(i>0 && r->ref[i].key == r->ref[i-1].key)
continue;
data->ttl = r->ttl;
for(j=0; j<data->count + data->rrsig_count; j++)
data->rr_ttl[j] = r->ttl;
}
rrset_array_unlock(r->ref, r->rrset_count);
}
}
lock_rw_unlock(&e->lock);
} else {
log_info("invalidateQueryInCache: qinfo is not in cache");
}
}
/* Create response according to the ldns packet content */
int createResponse(struct module_qstate* qstate, sldns_buffer* pkt)
{
struct msg_parse* prs;
struct edns_data edns;
/* parse message */
prs = (struct msg_parse*) regional_alloc(qstate->env->scratch,
sizeof(struct msg_parse));
if(!prs) {
log_err("createResponse: out of memory on incoming message");
return 0;
}
memset(prs, 0, sizeof(*prs));
memset(&edns, 0, sizeof(edns));
sldns_buffer_set_position(pkt, 0);
if(parse_packet(pkt, prs, qstate->env->scratch) != LDNS_RCODE_NOERROR) {
verbose(VERB_ALGO, "createResponse: parse error on reply packet");
return 0;
}
/* edns is not examined, but removed from message to help cache */
if(parse_extract_edns_from_response_msg(prs, &edns, qstate->env->scratch) !=
LDNS_RCODE_NOERROR)
return 0;
/* remove CD-bit, we asked for in case we handle validation ourself */
prs->flags &= ~BIT_CD;
/* allocate response dns_msg in region */
qstate->return_msg = (struct dns_msg*) regional_alloc(qstate->region,
sizeof(struct dns_msg));
if(!qstate->return_msg)
return 0;
memset(qstate->return_msg, 0, sizeof(*qstate->return_msg));
if(!parse_create_msg(pkt, prs, NULL, &(qstate->return_msg)->qinfo,
&(qstate->return_msg)->rep, qstate->region)) {
log_err("createResponse: malloc failure: allocating incoming dns_msg");
return 0;
}
/* Make sure that the RA flag is set (since the presence of
* this module means that recursion is available) */
/* qstate->return_msg->rep->flags |= BIT_RA; */
/* Clear the AA flag */
/* FIXME: does this action go here or in some other module? */
/*qstate->return_msg->rep->flags &= ~BIT_AA; */
/* make sure QR flag is on */
/*qstate->return_msg->rep->flags |= BIT_QR; */
if(verbosity >= VERB_ALGO)
log_dns_msg("createResponse: packet:", &qstate->return_msg->qinfo,
qstate->return_msg->rep);
return 1;
}
/* Convert reply->client_addr to string */
void reply_addr2str(struct comm_reply* reply, char* dest, int maxlen)
{
int af = (int)((struct sockaddr_in*) &(reply->client_addr))->sin_family;
void* sinaddr = &((struct sockaddr_in*) &(reply->client_addr))->sin_addr;
if(af == AF_INET6)
sinaddr = &((struct sockaddr_in6*)&(reply->client_addr))->sin6_addr;
dest[0] = 0;
if (inet_ntop(af, sinaddr, dest, (socklen_t)maxlen) == 0)
return;
dest[maxlen-1] = 0;
}
/* Convert target->addr to string */
void delegpt_addr_addr2str(struct delegpt_addr* target, char *dest, int maxlen)
{
int af = (int)((struct sockaddr_in*) &(target->addr))->sin_family;
void* sinaddr = &((struct sockaddr_in*) &(target->addr))->sin_addr;
if(af == AF_INET6)
sinaddr = &((struct sockaddr_in6*)&(target->addr))->sin6_addr;
dest[0] = 0;
if (inet_ntop(af, sinaddr, dest, (socklen_t)maxlen) == 0)
return;
dest[maxlen-1] = 0;
}
+64 -10
View File
@@ -105,6 +105,7 @@ respip_sockaddr_find_or_create(struct respip_set* set, struct sockaddr_storage*
socklen_t addrlen, int net, int create, const char* ipstr)
{
struct resp_addr* node;
log_assert(set);
node = (struct resp_addr*)addr_tree_find(&set->ip_tree, addr, addrlen, net);
if(!node && create) {
node = regional_alloc_zero(set->region, sizeof(*node));
@@ -128,6 +129,7 @@ void
respip_sockaddr_delete(struct respip_set* set, struct resp_addr* node)
{
struct resp_addr* prev;
log_assert(set);
prev = (struct resp_addr*)rbtree_previous((struct rbnode_type*)node);
lock_rw_destroy(&node->lock);
(void)rbtree_delete(&set->ip_tree, node);
@@ -146,6 +148,7 @@ respip_find_or_create(struct respip_set* set, const char* ipstr, int create)
struct sockaddr_storage addr;
int net;
socklen_t addrlen;
log_assert(set);
if(!netblockstrtoaddr(ipstr, 0, &addr, &addrlen, &net)) {
log_err("cannot parse netblock: '%s'", ipstr);
@@ -160,6 +163,7 @@ respip_tag_cfg(struct respip_set* set, const char* ipstr,
const uint8_t* taglist, size_t taglen)
{
struct resp_addr* node;
log_assert(set);
if(!(node=respip_find_or_create(set, ipstr, 1)))
return 0;
@@ -183,6 +187,7 @@ respip_action_cfg(struct respip_set* set, const char* ipstr,
{
struct resp_addr* node;
enum respip_action action;
log_assert(set);
if(!(node=respip_find_or_create(set, ipstr, 1)))
return 0;
@@ -325,6 +330,7 @@ static int
respip_data_cfg(struct respip_set* set, const char* ipstr, const char* rrstr)
{
struct resp_addr* node;
log_assert(set);
node=respip_find_or_create(set, ipstr, 0);
if(!node || node->action == respip_none) {
@@ -344,6 +350,7 @@ respip_set_apply_cfg(struct respip_set* set, char* const* tagname, int num_tags,
struct config_strbytelist* p;
struct config_str2list* pa;
struct config_str2list* pd;
log_assert(set);
set->tagname = tagname;
set->num_tags = num_tags;
@@ -609,6 +616,7 @@ respip_addr_lookup(const struct reply_info *rep, struct respip_set* rs,
struct resp_addr* ra;
struct sockaddr_storage ss;
socklen_t addrlen;
log_assert(rs);
lock_rw_rdlock(&rs->lock);
for(i=0; i<rep->an_numrrsets; i++) {
@@ -867,7 +875,8 @@ respip_rewrite_reply(const struct query_info* qinfo,
const struct respip_client_info* cinfo, const struct reply_info* rep,
struct reply_info** new_repp, struct respip_action_info* actinfo,
struct ub_packed_rrset_key** alias_rrset, int search_only,
struct regional* region, struct auth_zones* az, int* rpz_passthru)
struct regional* region, struct auth_zones* az, int* rpz_passthru,
struct views* views, struct respip_set* ipset)
{
const uint8_t* ctaglist;
size_t ctaglen;
@@ -876,7 +885,6 @@ respip_rewrite_reply(const struct query_info* qinfo,
struct config_strlist** tag_datas;
size_t tag_datas_size;
struct view* view = NULL;
struct respip_set* ipset = NULL;
size_t rrset_id = 0, rr_id = 0;
enum respip_action action = respip_none;
int tag = -1;
@@ -899,8 +907,20 @@ respip_rewrite_reply(const struct query_info* qinfo,
tag_actions_size = cinfo->tag_actions_size;
tag_datas = cinfo->tag_datas;
tag_datas_size = cinfo->tag_datas_size;
view = cinfo->view;
ipset = cinfo->respip_set;
if(cinfo->view) {
view = cinfo->view;
lock_rw_rdlock(&view->lock);
} else if(cinfo->view_name) {
view = views_find_view(views, cinfo->view_name, 0);
if(!view) {
/* If the view no longer exists, the rewrite can not
* be processed further. */
verbose(VERB_ALGO, "respip: failed because view %s no "
"longer exists", cinfo->view_name);
return 0;
}
/* The view is rdlocked by views_find_view. */
}
log_assert(ipset);
@@ -915,7 +935,6 @@ respip_rewrite_reply(const struct query_info* qinfo,
* Note also that we assume 'view' is valid in this function, which
* should be safe (see unbound bug #1191) */
if(view) {
lock_rw_rdlock(&view->lock);
if(view->respip_set) {
if((raddr = respip_addr_lookup(rep,
view->respip_set, &rrset_id, &rr_id))) {
@@ -961,7 +980,7 @@ respip_rewrite_reply(const struct query_info* qinfo,
struct sockaddr_storage ss;
socklen_t ss_len = 0;
char nm[256], ip[256];
char qn[255+1];
char qn[LDNS_MAX_DOMAINLEN];
if(!rdata2sockaddr(rep->rrsets[rrset_id]->entry.data, ntohs(rep->rrsets[rrset_id]->rk.type), rr_id, &ss, &ss_len))
snprintf(ip, sizeof(ip), "invalidRRdata");
else
@@ -1101,7 +1120,8 @@ respip_operate(struct module_qstate* qstate, enum module_ev event, int id,
qstate->client_info, qstate->return_msg->rep,
&new_rep, &actinfo, &alias_rrset, 0,
qstate->region, qstate->env->auth_zones,
&qstate->rpz_passthru)) {
&qstate->rpz_passthru, qstate->env->views,
qstate->env->respip_set)) {
goto servfail;
}
if(actinfo.action != respip_none) {
@@ -1149,7 +1169,8 @@ respip_merge_cname(struct reply_info* base_rep,
const struct query_info* qinfo, const struct reply_info* tgt_rep,
const struct respip_client_info* cinfo, int must_validate,
struct reply_info** new_repp, struct regional* region,
struct auth_zones* az)
struct auth_zones* az, struct views* views,
struct respip_set* respip_set)
{
struct reply_info* new_rep;
struct reply_info* tmp_rep = NULL; /* just a placeholder */
@@ -1176,7 +1197,7 @@ respip_merge_cname(struct reply_info* base_rep,
/* see if the target reply would be subject to a response-ip action. */
if(!respip_rewrite_reply(qinfo, cinfo, tgt_rep, &tmp_rep, &actinfo,
&alias_rrset, 1, region, az, NULL))
&alias_rrset, 1, region, az, NULL, views, respip_set))
return 0;
if(actinfo.action != respip_none) {
log_info("CNAME target of redirect response-ip action would "
@@ -1229,7 +1250,8 @@ respip_inform_super(struct module_qstate* qstate, int id,
if(!respip_merge_cname(super->return_msg->rep, &qstate->qinfo,
qstate->return_msg->rep, super->client_info,
super->env->need_to_validate, &new_rep, super->region,
qstate->env->auth_zones))
qstate->env->auth_zones, qstate->env->views,
qstate->env->respip_set))
goto fail;
super->return_msg->rep = new_rep;
return;
@@ -1326,3 +1348,35 @@ respip_inform_print(struct respip_action_info* respip_actinfo, uint8_t* qname,
(actionstr) ? actionstr : "inform", srcip, port);
log_nametypeclass(NO_VERBOSE, txt, qname, qtype, qclass);
}
size_t respip_set_get_mem(struct respip_set* set)
{
size_t m;
if(!set) return 0;
m = sizeof(*set);
lock_rw_rdlock(&set->lock);
m += regional_get_mem(set->region);
lock_rw_unlock(&set->lock);
return m;
}
void
respip_set_swap_tree(struct respip_set* respip_set,
struct respip_set* data)
{
rbnode_type* oldroot = respip_set->ip_tree.root;
size_t oldcount = respip_set->ip_tree.count;
struct regional* oldregion = respip_set->region;
char* const* oldtagname = respip_set->tagname;
int oldnum_tags = respip_set->num_tags;
respip_set->ip_tree.root = data->ip_tree.root;
respip_set->ip_tree.count = data->ip_tree.count;
respip_set->region = data->region;
respip_set->tagname = data->tagname;
respip_set->num_tags = data->num_tags;
data->ip_tree.root = oldroot;
data->ip_tree.count = oldcount;
data->region = oldregion;
data->tagname = oldtagname;
data->num_tags = oldnum_tags;
}
+28 -5
View File
@@ -23,7 +23,8 @@
struct respip_set {
struct regional* region;
struct rbtree_type ip_tree;
lock_rw_type lock; /* lock on the respip tree */
lock_rw_type lock; /* lock on the respip tree. It is ordered
after views and before hints, stubs and local zones. */
char* const* tagname; /* shallow copy of tag names, for logging */
int num_tags; /* number of tagname entries */
};
@@ -59,7 +60,6 @@ struct respip_addr_info;
* This is essentially a subset of acl_addr (except for respip_set) but
* defined as a separate structure to avoid dependency on the daemon-specific
* structure.
* respip_set is supposed to refer to the response-ip set for the global view.
*/
struct respip_client_info {
uint8_t* taglist;
@@ -68,8 +68,12 @@ struct respip_client_info {
size_t tag_actions_size;
struct config_strlist** tag_datas;
size_t tag_datas_size;
/** The view for the action, during cache callback that is by
* pointer. */
struct view* view;
struct respip_set* respip_set;
/** If from module query state, the view pointer is NULL, but the
* name is stored in reference to the view. */
char* view_name;
};
/**
@@ -149,13 +153,16 @@ int respip_views_apply_cfg(struct views* vs, struct config_file* cfg,
* on error.
* @param region: allocator to build *new_repp.
* @param az: auth zones containing RPZ information.
* @param views: views tree to lookup view used.
* @param respip_set: the respip set for the global view.
* @return 1 on success, 0 on error.
*/
int respip_merge_cname(struct reply_info* base_rep,
const struct query_info* qinfo, const struct reply_info* tgt_rep,
const struct respip_client_info* cinfo, int must_validate,
struct reply_info** new_repp, struct regional* region,
struct auth_zones* az);
struct auth_zones* az, struct views* views,
struct respip_set* respip_set);
/**
* See if any IP-based action should apply to any IP address of AAAA/A answer
@@ -178,6 +185,8 @@ int respip_merge_cname(struct reply_info* base_rep,
* @param region: allocator to build *new_repp.
* @param rpz_passthru: keeps track of query state can have passthru that
* stops further rpz processing. Or NULL for cached answer processing.
* @param views: views tree to lookup view used.
* @param ipset: the respip set for the global view.
* @return 1 on success, 0 on error.
*/
int respip_rewrite_reply(const struct query_info* qinfo,
@@ -186,7 +195,7 @@ int respip_rewrite_reply(const struct query_info* qinfo,
struct respip_action_info* actinfo,
struct ub_packed_rrset_key** alias_rrset,
int search_only, struct regional* region, struct auth_zones* az,
int* rpz_passthru);
int* rpz_passthru, struct views* views, struct respip_set* ipset);
/**
* Get the response-ip function block.
@@ -302,4 +311,18 @@ respip_sockaddr_delete(struct respip_set* set, struct resp_addr* node);
struct ub_packed_rrset_key*
respip_copy_rrset(const struct ub_packed_rrset_key* key, struct regional* region);
/** Get memory usage of respip set tree. The routine locks and unlocks the
* set for reading. */
size_t respip_set_get_mem(struct respip_set* set);
/**
* Swap internal tree with preallocated entries. Caller should manage
* the locks.
* @param respip_set: response ip tree
* @param data: preallocated information.
*/
void respip_set_swap_tree(struct respip_set* respip_set,
struct respip_set* data);
#endif /* RESPIP_RESPIP_H */
+217 -61
View File
@@ -107,7 +107,7 @@ static void xfr_probe_send_or_end(struct auth_xfer* xfr,
static int xfr_start_probe(struct auth_xfer* xfr, struct module_env* env,
struct auth_master* spec);
/** delete xfer structure (not its tree entry) */
static void auth_xfer_delete(struct auth_xfer* xfr);
void auth_xfer_delete(struct auth_xfer* xfr);
/** create new dns_msg */
static struct dns_msg*
@@ -1578,7 +1578,7 @@ auth_zone_read_zonefile(struct auth_zone* z, struct config_file* cfg)
cfg->chrootdir, strlen(cfg->chrootdir)) == 0)
zfilename += strlen(cfg->chrootdir);
if(verbosity >= VERB_ALGO) {
char nm[255+1];
char nm[LDNS_MAX_DOMAINLEN];
dname_str(z->name, nm);
verbose(VERB_ALGO, "read zonefile %s for %s", zfilename, nm);
}
@@ -1942,7 +1942,7 @@ static int auth_zone_zonemd_check_hash(struct auth_zone* z,
unsupported_reason = *reason;
/* continue to check for valid ZONEMD */
if(verbosity >= VERB_ALGO) {
char zstr[255+1];
char zstr[LDNS_MAX_DOMAINLEN];
dname_str(z->name, zstr);
verbose(VERB_ALGO, "auth-zone %s ZONEMD %d %d is unsupported: %s", zstr, (int)scheme, (int)hashalgo, *reason);
}
@@ -1950,7 +1950,7 @@ static int auth_zone_zonemd_check_hash(struct auth_zone* z,
continue;
}
if(verbosity >= VERB_ALGO) {
char zstr[255+1];
char zstr[LDNS_MAX_DOMAINLEN];
dname_str(z->name, zstr);
if(!*reason)
verbose(VERB_ALGO, "auth-zone %s ZONEMD hash is correct", zstr);
@@ -1973,7 +1973,7 @@ static int auth_zone_zonemd_check_hash(struct auth_zone* z,
if(!*reason)
*reason = "no ZONEMD records found";
if(verbosity >= VERB_ALGO) {
char zstr[255+1];
char zstr[LDNS_MAX_DOMAINLEN];
dname_str(z->name, zstr);
verbose(VERB_ALGO, "auth-zone %s ZONEMD failed: %s", zstr, *reason);
}
@@ -2317,10 +2317,7 @@ auth_free_masters(struct auth_master* list)
}
}
/** delete auth xfer structure
* @param xfr: delete this xfer and its tasks.
*/
static void
void
auth_xfer_delete(struct auth_xfer* xfr)
{
if(!xfr) return;
@@ -3610,9 +3607,7 @@ int auth_zones_answer(struct auth_zones* az, struct module_env* env,
return 0;
}
lock_rw_unlock(&z->lock);
lock_rw_wrlock(&az->lock);
az->num_query_down++;
lock_rw_unlock(&az->lock);
env->mesh->num_query_authzone_down++;
auth_error_encode(qinfo, env, edns, repinfo, buf, temp,
LDNS_RCODE_SERVFAIL);
return 1;
@@ -3625,9 +3620,7 @@ int auth_zones_answer(struct auth_zones* az, struct module_env* env,
/* fallback to regular answering (recursive) */
return 0;
}
lock_rw_wrlock(&az->lock);
az->num_query_down++;
lock_rw_unlock(&az->lock);
env->mesh->num_query_authzone_down++;
/* encode answer */
if(!r)
@@ -4803,8 +4796,8 @@ log_rrlist_position(const char* label, struct auth_chunk* rr_chunk,
{
sldns_buffer pkt;
size_t dlen;
uint8_t buf[256];
char str[256];
uint8_t buf[LDNS_MAX_DOMAINLEN];
char str[LDNS_MAX_DOMAINLEN];
char typestr[32];
sldns_buffer_init_frm_data(&pkt, rr_chunk->data, rr_chunk->len);
sldns_buffer_set_position(&pkt, (size_t)(rr_dname -
@@ -5231,7 +5224,7 @@ xfr_write_after_update(struct auth_xfer* xfr, struct module_env* env)
cfg->chrootdir, strlen(cfg->chrootdir)) == 0)
zfilename += strlen(cfg->chrootdir);
if(verbosity >= VERB_ALGO) {
char nm[255+1];
char nm[LDNS_MAX_DOMAINLEN];
dname_str(z->name, nm);
verbose(VERB_ALGO, "write zonefile %s for %s", zfilename, nm);
}
@@ -5348,7 +5341,7 @@ xfr_process_chunk_list(struct auth_xfer* xfr, struct module_env* env,
/* holding z lock */
auth_zone_verify_zonemd(z, env, &env->mesh->mods, NULL, 0, 0);
if(z->zone_expired) {
char zname[256];
char zname[LDNS_MAX_DOMAINLEN];
/* ZONEMD must have failed */
/* reacquire locks, so we hold xfr lock on exit of routine,
* and both xfr and z again after releasing xfr for potential
@@ -5380,7 +5373,7 @@ xfr_process_chunk_list(struct auth_xfer* xfr, struct module_env* env,
lock_rw_unlock(&z->lock);
if(verbosity >= VERB_QUERY && xfr->have_zone) {
char zname[256];
char zname[LDNS_MAX_DOMAINLEN];
dname_str(xfr->name, zname);
verbose(VERB_QUERY, "auth zone %s updated to serial %u", zname,
(unsigned)xfr->serial);
@@ -5442,7 +5435,7 @@ xfr_transfer_lookup_host(struct auth_xfer* xfr, struct module_env* env)
qinfo.local_alias = NULL;
if(verbosity >= VERB_ALGO) {
char buf1[512];
char buf2[LDNS_MAX_DOMAINLEN+1];
char buf2[LDNS_MAX_DOMAINLEN];
dname_str(xfr->name, buf2);
snprintf(buf1, sizeof(buf1), "auth zone %s: master lookup"
" for task_transfer", buf2);
@@ -5498,7 +5491,7 @@ xfr_transfer_init_fetch(struct auth_xfer* xfr, struct module_env* env)
/* the ones that are not in addr format are supposed
* to be looked up. The lookup has failed however,
* so skip them */
char zname[255+1];
char zname[LDNS_MAX_DOMAINLEN];
dname_str(xfr->name, zname);
log_err("%s: failed lookup, cannot transfer from master %s",
zname, master->host);
@@ -5537,7 +5530,7 @@ xfr_transfer_init_fetch(struct auth_xfer* xfr, struct module_env* env)
&addr, addrlen, -1, master->ssl, master->host,
master->file, env->cfg);
if(!xfr->task_transfer->cp) {
char zname[255+1], as[256];
char zname[LDNS_MAX_DOMAINLEN], as[256];
dname_str(xfr->name, zname);
addr_port_to_str(&addr, addrlen, as, sizeof(as));
verbose(VERB_ALGO, "cannot create http cp "
@@ -5546,7 +5539,7 @@ xfr_transfer_init_fetch(struct auth_xfer* xfr, struct module_env* env)
}
comm_timer_set(xfr->task_transfer->timer, &t);
if(verbosity >= VERB_ALGO) {
char zname[255+1], as[256];
char zname[LDNS_MAX_DOMAINLEN], as[256];
dname_str(xfr->name, zname);
addr_port_to_str(&addr, addrlen, as, sizeof(as));
verbose(VERB_ALGO, "auth zone %s transfer next HTTP fetch from %s started", zname, as);
@@ -5569,7 +5562,7 @@ xfr_transfer_init_fetch(struct auth_xfer* xfr, struct module_env* env)
env->scratch_buffer, -1,
auth_name != NULL, auth_name);
if(!xfr->task_transfer->cp) {
char zname[255+1], as[256];
char zname[LDNS_MAX_DOMAINLEN], as[256];
dname_str(xfr->name, zname);
addr_port_to_str(&addr, addrlen, as, sizeof(as));
verbose(VERB_ALGO, "cannot create tcp cp connection for "
@@ -5578,7 +5571,7 @@ xfr_transfer_init_fetch(struct auth_xfer* xfr, struct module_env* env)
}
comm_timer_set(xfr->task_transfer->timer, &t);
if(verbosity >= VERB_ALGO) {
char zname[255+1], as[256];
char zname[LDNS_MAX_DOMAINLEN], as[256];
dname_str(xfr->name, zname);
addr_port_to_str(&addr, addrlen, as, sizeof(as));
verbose(VERB_ALGO, "auth zone %s transfer next %s fetch from %s started", zname,
@@ -5602,7 +5595,7 @@ xfr_transfer_nexttarget_or_end(struct auth_xfer* xfr, struct module_env* env)
* and that calls the callback just like a full
* lookup and lookup failures also call callback */
if(verbosity >= VERB_ALGO) {
char zname[255+1];
char zname[LDNS_MAX_DOMAINLEN];
dname_str(xfr->name, zname);
verbose(VERB_ALGO, "auth zone %s transfer next target lookup", zname);
}
@@ -5625,7 +5618,7 @@ xfr_transfer_nexttarget_or_end(struct auth_xfer* xfr, struct module_env* env)
xfr_transfer_nextmaster(xfr);
}
if(verbosity >= VERB_ALGO) {
char zname[255+1];
char zname[LDNS_MAX_DOMAINLEN];
dname_str(xfr->name, zname);
verbose(VERB_ALGO, "auth zone %s transfer failed, wait", zname);
}
@@ -5728,14 +5721,14 @@ void auth_xfer_transfer_lookup_callback(void* arg, int rcode, sldns_buffer* buf,
lookup_target, answer, wanted_qtype);
} else {
if(verbosity >= VERB_ALGO) {
char zname[255+1];
char zname[LDNS_MAX_DOMAINLEN];
dname_str(xfr->name, zname);
verbose(VERB_ALGO, "auth zone %s host %s type %s transfer lookup has nodata", zname, xfr->task_transfer->lookup_target->host, (xfr->task_transfer->lookup_aaaa?"AAAA":"A"));
}
}
} else {
if(verbosity >= VERB_ALGO) {
char zname[255+1];
char zname[LDNS_MAX_DOMAINLEN];
dname_str(xfr->name, zname);
verbose(VERB_ALGO, "auth zone %s host %s type %s transfer lookup has no answer", zname, xfr->task_transfer->lookup_target->host, (xfr->task_transfer->lookup_aaaa?"AAAA":"A"));
}
@@ -5743,7 +5736,7 @@ void auth_xfer_transfer_lookup_callback(void* arg, int rcode, sldns_buffer* buf,
regional_free_all(temp);
} else {
if(verbosity >= VERB_ALGO) {
char zname[255+1];
char zname[LDNS_MAX_DOMAINLEN];
dname_str(xfr->name, zname);
verbose(VERB_ALGO, "auth zone %s host %s type %s transfer lookup failed", zname, xfr->task_transfer->lookup_target->host, (xfr->task_transfer->lookup_aaaa?"AAAA":"A"));
}
@@ -6385,7 +6378,7 @@ xfr_probe_send_probe(struct auth_xfer* xfr, struct module_env* env,
/* the ones that are not in addr format are supposed
* to be looked up. The lookup has failed however,
* so skip them */
char zname[255+1];
char zname[LDNS_MAX_DOMAINLEN];
dname_str(xfr->name, zname);
log_err("%s: failed lookup, cannot probe to master %s",
zname, master->host);
@@ -6427,7 +6420,7 @@ xfr_probe_send_probe(struct auth_xfer* xfr, struct module_env* env,
xfr->task_probe->cp = outnet_comm_point_for_udp(env->outnet,
auth_xfer_probe_udp_callback, xfr, &addr, addrlen);
if(!xfr->task_probe->cp) {
char zname[255+1], as[256];
char zname[LDNS_MAX_DOMAINLEN], as[256];
dname_str(xfr->name, zname);
addr_port_to_str(&addr, addrlen, as, sizeof(as));
verbose(VERB_ALGO, "cannot create udp cp for "
@@ -6447,7 +6440,7 @@ xfr_probe_send_probe(struct auth_xfer* xfr, struct module_env* env,
/* send udp packet */
if(!comm_point_send_udp_msg(xfr->task_probe->cp, env->scratch_buffer,
(struct sockaddr*)&addr, addrlen, 0)) {
char zname[255+1], as[256];
char zname[LDNS_MAX_DOMAINLEN], as[256];
dname_str(xfr->name, zname);
addr_port_to_str(&addr, addrlen, as, sizeof(as));
verbose(VERB_ALGO, "failed to send soa probe for %s to %s",
@@ -6455,7 +6448,7 @@ xfr_probe_send_probe(struct auth_xfer* xfr, struct module_env* env,
return 0;
}
if(verbosity >= VERB_ALGO) {
char zname[255+1], as[256];
char zname[LDNS_MAX_DOMAINLEN], as[256];
dname_str(xfr->name, zname);
addr_port_to_str(&addr, addrlen, as, sizeof(as));
verbose(VERB_ALGO, "auth zone %s soa probe sent to %s", zname,
@@ -6486,7 +6479,7 @@ auth_xfer_probe_timer_callback(void* arg)
}
if(verbosity >= VERB_ALGO) {
char zname[255+1];
char zname[LDNS_MAX_DOMAINLEN];
dname_str(xfr->name, zname);
verbose(VERB_ALGO, "auth zone %s soa probe timeout", zname);
}
@@ -6534,7 +6527,7 @@ auth_xfer_probe_udp_callback(struct comm_point* c, void* arg, int err,
&serial)) {
/* successful lookup */
if(verbosity >= VERB_ALGO) {
char buf[256];
char buf[LDNS_MAX_DOMAINLEN];
dname_str(xfr->name, buf);
verbose(VERB_ALGO, "auth zone %s: soa probe "
"serial is %u", buf, (unsigned)serial);
@@ -6573,14 +6566,14 @@ auth_xfer_probe_udp_callback(struct comm_point* c, void* arg, int err,
}
} else {
if(verbosity >= VERB_ALGO) {
char buf[256];
char buf[LDNS_MAX_DOMAINLEN];
dname_str(xfr->name, buf);
verbose(VERB_ALGO, "auth zone %s: bad reply to soa probe", buf);
}
}
} else {
if(verbosity >= VERB_ALGO) {
char buf[256];
char buf[LDNS_MAX_DOMAINLEN];
dname_str(xfr->name, buf);
verbose(VERB_ALGO, "auth zone %s: soa probe failed", buf);
}
@@ -6637,7 +6630,7 @@ xfr_probe_lookup_host(struct auth_xfer* xfr, struct module_env* env)
qinfo.local_alias = NULL;
if(verbosity >= VERB_ALGO) {
char buf1[512];
char buf2[LDNS_MAX_DOMAINLEN+1];
char buf2[LDNS_MAX_DOMAINLEN];
dname_str(xfr->name, buf2);
snprintf(buf1, sizeof(buf1), "auth zone %s: master lookup"
" for task_probe", buf2);
@@ -6683,7 +6676,7 @@ xfr_probe_send_or_end(struct auth_xfer* xfr, struct module_env* env)
* and that calls the callback just like a full
* lookup and lookup failures also call callback */
if(verbosity >= VERB_ALGO) {
char zname[255+1];
char zname[LDNS_MAX_DOMAINLEN];
dname_str(xfr->name, zname);
verbose(VERB_ALGO, "auth zone %s probe next target lookup", zname);
}
@@ -6696,7 +6689,7 @@ xfr_probe_send_or_end(struct auth_xfer* xfr, struct module_env* env)
* allow_notify addrs */
probe_copy_masters_for_allow_notify(xfr);
if(verbosity >= VERB_ALGO) {
char zname[255+1];
char zname[LDNS_MAX_DOMAINLEN];
dname_str(xfr->name, zname);
verbose(VERB_ALGO, "auth zone %s probe: notify addrs updated", zname);
}
@@ -6704,7 +6697,7 @@ xfr_probe_send_or_end(struct auth_xfer* xfr, struct module_env* env)
/* only wanted lookups for copy, stop probe and start wait */
xfr->task_probe->only_lookup = 0;
if(verbosity >= VERB_ALGO) {
char zname[255+1];
char zname[LDNS_MAX_DOMAINLEN];
dname_str(xfr->name, zname);
verbose(VERB_ALGO, "auth zone %s probe: finished only_lookup", zname);
}
@@ -6730,7 +6723,7 @@ xfr_probe_send_or_end(struct auth_xfer* xfr, struct module_env* env)
if(xfr->task_probe->have_new_lease) {
/* if zone not updated, start the wait timer again */
if(verbosity >= VERB_ALGO) {
char zname[255+1];
char zname[LDNS_MAX_DOMAINLEN];
dname_str(xfr->name, zname);
verbose(VERB_ALGO, "auth_zone %s unchanged, new lease, wait", zname);
}
@@ -6741,7 +6734,7 @@ xfr_probe_send_or_end(struct auth_xfer* xfr, struct module_env* env)
xfr_set_timeout(xfr, env, 0, 0);
} else {
if(verbosity >= VERB_ALGO) {
char zname[255+1];
char zname[LDNS_MAX_DOMAINLEN];
dname_str(xfr->name, zname);
verbose(VERB_ALGO, "auth zone %s soa probe failed, wait to retry", zname);
}
@@ -6791,14 +6784,14 @@ void auth_xfer_probe_lookup_callback(void* arg, int rcode, sldns_buffer* buf,
lookup_target, answer, wanted_qtype);
} else {
if(verbosity >= VERB_ALGO) {
char zname[255+1];
char zname[LDNS_MAX_DOMAINLEN];
dname_str(xfr->name, zname);
verbose(VERB_ALGO, "auth zone %s host %s type %s probe lookup has nodata", zname, xfr->task_probe->lookup_target->host, (xfr->task_probe->lookup_aaaa?"AAAA":"A"));
}
}
} else {
if(verbosity >= VERB_ALGO) {
char zname[255+1];
char zname[LDNS_MAX_DOMAINLEN];
dname_str(xfr->name, zname);
verbose(VERB_ALGO, "auth zone %s host %s type %s probe lookup has no address", zname, xfr->task_probe->lookup_target->host, (xfr->task_probe->lookup_aaaa?"AAAA":"A"));
}
@@ -6806,7 +6799,7 @@ void auth_xfer_probe_lookup_callback(void* arg, int rcode, sldns_buffer* buf,
regional_free_all(temp);
} else {
if(verbosity >= VERB_ALGO) {
char zname[255+1];
char zname[LDNS_MAX_DOMAINLEN];
dname_str(xfr->name, zname);
verbose(VERB_ALGO, "auth zone %s host %s type %s probe lookup failed", zname, xfr->task_probe->lookup_target->host, (xfr->task_probe->lookup_aaaa?"AAAA":"A"));
}
@@ -6980,7 +6973,7 @@ xfr_set_timeout(struct auth_xfer* xfr, struct module_env* env,
if(!xfr->task_nextprobe->timer) {
/* failed to malloc memory. likely zone transfer
* also fails for that. skip the timeout */
char zname[255+1];
char zname[LDNS_MAX_DOMAINLEN];
dname_str(xfr->name, zname);
log_err("cannot allocate timer, no refresh for %s",
zname);
@@ -7001,7 +6994,7 @@ xfr_set_timeout(struct auth_xfer* xfr, struct module_env* env,
xfr->task_probe->only_lookup = 1;
}
if(verbosity >= VERB_ALGO) {
char zname[255+1];
char zname[LDNS_MAX_DOMAINLEN];
dname_str(xfr->name, zname);
verbose(VERB_ALGO, "auth zone %s timeout in %d seconds",
zname, (int)tv.tv_sec);
@@ -7010,6 +7003,18 @@ xfr_set_timeout(struct auth_xfer* xfr, struct module_env* env,
comm_timer_set(xfr->task_nextprobe->timer, &tv);
}
void auth_xfer_pickup_initial_zone(struct auth_xfer* x, struct module_env* env)
{
/* set lease_time, because we now have timestamp in env,
* (not earlier during startup and apply_cfg), and this
* notes the start time when the data was acquired */
if(x->have_zone)
x->lease_time = *env->now;
if(x->task_nextprobe && x->task_nextprobe->worker == NULL) {
xfr_set_timeout(x, env, 0, 1);
}
}
/** initial pick up of worker timeouts, ties events to worker event loop */
void
auth_xfer_pickup_initial(struct auth_zones* az, struct module_env* env)
@@ -7018,14 +7023,7 @@ auth_xfer_pickup_initial(struct auth_zones* az, struct module_env* env)
lock_rw_wrlock(&az->lock);
RBTREE_FOR(x, struct auth_xfer*, &az->xtree) {
lock_basic_lock(&x->lock);
/* set lease_time, because we now have timestamp in env,
* (not earlier during startup and apply_cfg), and this
* notes the start time when the data was acquired */
if(x->have_zone)
x->lease_time = *env->now;
if(x->task_nextprobe && x->task_nextprobe->worker == NULL) {
xfr_set_timeout(x, env, 0, 1);
}
auth_xfer_pickup_initial_zone(x, env);
lock_basic_unlock(&x->lock);
}
lock_rw_unlock(&az->lock);
@@ -7788,7 +7786,7 @@ static void auth_zone_log(uint8_t* name, enum verbosity_value level,
va_list args;
va_start(args, format);
if(verbosity >= level) {
char str[255+1];
char str[LDNS_MAX_DOMAINLEN];
char msg[MAXSYSLOGMSGLEN];
dname_str(name, str);
vsnprintf(msg, sizeof(msg), format, args);
@@ -7990,7 +7988,7 @@ static int zonemd_check_dnssec_soazonemd(struct auth_zone* z,
static void auth_zone_zonemd_fail(struct auth_zone* z, struct module_env* env,
char* reason, char* why_bogus, char** result)
{
char zstr[255+1];
char zstr[LDNS_MAX_DOMAINLEN];
/* if fail: log reason, and depending on config also take action
* and drop the zone, eg. it is gone from memory, set zone_expired */
dname_str(z->name, zstr);
@@ -8436,7 +8434,7 @@ zonemd_lookup_dnskey(struct auth_zone* z, struct module_env* env)
qinfo.local_alias = NULL;
if(verbosity >= VERB_ALGO) {
char buf1[512];
char buf2[LDNS_MAX_DOMAINLEN+1];
char buf2[LDNS_MAX_DOMAINLEN];
dname_str(z->name, buf2);
snprintf(buf1, sizeof(buf1), "auth zone %s: lookup %s "
"for zonemd verification", buf2,
@@ -8584,3 +8582,161 @@ void auth_zones_pickup_zonemd_verify(struct auth_zones* az,
}
lock_rw_unlock(&az->lock);
}
/** Get memory usage of auth rrset */
static size_t
auth_rrset_get_mem(struct auth_rrset* rrset)
{
size_t m = sizeof(*rrset) + packed_rrset_sizeof(rrset->data);
return m;
}
/** Get memory usage of auth data */
static size_t
auth_data_get_mem(struct auth_data* node)
{
size_t m = sizeof(*node) + node->namelen;
struct auth_rrset* rrset;
for(rrset = node->rrsets; rrset; rrset = rrset->next) {
m += auth_rrset_get_mem(rrset);
}
return m;
}
/** Get memory usage of auth zone */
static size_t
auth_zone_get_mem(struct auth_zone* z)
{
size_t m = sizeof(*z) + z->namelen;
struct auth_data* node;
if(z->zonefile)
m += strlen(z->zonefile)+1;
RBTREE_FOR(node, struct auth_data*, &z->data) {
m += auth_data_get_mem(node);
}
if(z->rpz)
m += rpz_get_mem(z->rpz);
return m;
}
/** Get memory usage of list of auth addr */
static size_t
auth_addrs_get_mem(struct auth_addr* list)
{
size_t m = 0;
struct auth_addr* a;
for(a = list; a; a = a->next) {
m += sizeof(*a);
}
return m;
}
/** Get memory usage of list of primaries for auth xfer */
static size_t
auth_primaries_get_mem(struct auth_master* list)
{
size_t m = 0;
struct auth_master* n;
for(n = list; n; n = n->next) {
m += sizeof(*n);
m += auth_addrs_get_mem(n->list);
if(n->host)
m += strlen(n->host)+1;
if(n->file)
m += strlen(n->file)+1;
}
return m;
}
/** Get memory usage or list of auth chunks */
static size_t
auth_chunks_get_mem(struct auth_chunk* list)
{
size_t m = 0;
struct auth_chunk* chunk;
for(chunk = list; chunk; chunk = chunk->next) {
m += sizeof(*chunk) + chunk->len;
}
return m;
}
/** Get memory usage of auth xfer */
static size_t
auth_xfer_get_mem(struct auth_xfer* xfr)
{
size_t m = sizeof(*xfr) + xfr->namelen;
/* auth_nextprobe */
m += comm_timer_get_mem(xfr->task_nextprobe->timer);
/* auth_probe */
m += auth_primaries_get_mem(xfr->task_probe->masters);
m += comm_point_get_mem(xfr->task_probe->cp);
m += comm_timer_get_mem(xfr->task_probe->timer);
/* auth_transfer */
m += auth_chunks_get_mem(xfr->task_transfer->chunks_first);
m += auth_primaries_get_mem(xfr->task_transfer->masters);
m += comm_point_get_mem(xfr->task_transfer->cp);
m += comm_timer_get_mem(xfr->task_transfer->timer);
/* allow_notify_list */
m += auth_primaries_get_mem(xfr->allow_notify_list);
return m;
}
/** Get memory usage of auth zones ztree */
static size_t
az_ztree_get_mem(struct auth_zones* az)
{
size_t m = 0;
struct auth_zone* z;
RBTREE_FOR(z, struct auth_zone*, &az->ztree) {
lock_rw_rdlock(&z->lock);
m += auth_zone_get_mem(z);
lock_rw_unlock(&z->lock);
}
return m;
}
/** Get memory usage of auth zones xtree */
static size_t
az_xtree_get_mem(struct auth_zones* az)
{
size_t m = 0;
struct auth_xfer* xfr;
RBTREE_FOR(xfr, struct auth_xfer*, &az->xtree) {
lock_basic_lock(&xfr->lock);
m += auth_xfer_get_mem(xfr);
lock_basic_unlock(&xfr->lock);
}
return m;
}
size_t auth_zones_get_mem(struct auth_zones* zones)
{
size_t m;
if(!zones) return 0;
m = sizeof(*zones);
lock_rw_rdlock(&zones->rpz_lock);
lock_rw_rdlock(&zones->lock);
m += az_ztree_get_mem(zones);
m += az_xtree_get_mem(zones);
lock_rw_unlock(&zones->lock);
lock_rw_unlock(&zones->rpz_lock);
return m;
}
void xfr_disown_tasks(struct auth_xfer* xfr, struct worker* worker)
{
if(xfr->task_nextprobe->worker == worker) {
xfr_nextprobe_disown(xfr);
}
if(xfr->task_probe->worker == worker) {
xfr_probe_disown(xfr);
}
if(xfr->task_transfer->worker == worker) {
xfr_transfer_disown(xfr);
}
}
+34 -6
View File
@@ -70,7 +70,8 @@ struct auth_chunk;
* Authoritative zones, shared.
*/
struct auth_zones {
/** lock on the authzone trees */
/** lock on the authzone trees. It is locked after views, respip,
* local_zones and before fwds and stubs. */
lock_rw_type lock;
/** rbtree of struct auth_zone */
rbtree_type ztree;
@@ -78,10 +79,6 @@ struct auth_zones {
rbtree_type xtree;
/** do we have downstream enabled */
int have_downstream;
/** number of queries upstream */
size_t num_query_up;
/** number of queries downstream */
size_t num_query_down;
/** first auth zone containing rpz item in linked list */
struct auth_zone* rpz_first;
/** rw lock for rpz linked list, needed when iterating or editing linked
@@ -211,7 +208,9 @@ struct auth_xfer {
* one of the tasks.
* Once it has the task assigned to it, the worker can access the
* other elements of the task structure without a lock, because that
* is necessary for the eventloop and callbacks from that. */
* is necessary for the eventloop and callbacks from that.
* The auth_zone->lock is locked before this lock.
*/
lock_basic_type lock;
/** zone name, in uncompressed wireformat */
@@ -787,4 +786,33 @@ void auth_zonemd_dnskey_lookup_callback(void* arg, int rcode,
void auth_zones_pickup_zonemd_verify(struct auth_zones* az,
struct module_env* env);
/** Get memory usage for auth zones. The routine locks and unlocks
* for reading. */
size_t auth_zones_get_mem(struct auth_zones* zones);
/**
* Initial pick up of the auth zone nextprobe timeout and that turns
* into further zone transfer work, if any. Also sets the lease time.
* @param x: xfer structure, locked by caller.
* @param env: environment of the worker that picks up the task.
*/
void auth_xfer_pickup_initial_zone(struct auth_xfer* x,
struct module_env* env);
/**
* Delete auth xfer structure
* @param xfr: delete this xfer and its tasks.
*/
void auth_xfer_delete(struct auth_xfer* xfr);
/**
* Disown tasks from the xfr that belong to this worker.
* Only tasks for the worker in question, the comm point and timer
* delete functions need to run in the thread of that worker to be
* able to delete the callback from the event base.
* @param xfr: xfr structure
* @param worker: the worker for which to stop tasks.
*/
void xfr_disown_tasks(struct auth_xfer* xfr, struct worker* worker);
#endif /* SERVICES_AUTHZONE_H */
+41 -4
View File
@@ -1056,8 +1056,9 @@ dns_cache_lookup(struct module_env* env,
int
dns_cache_store(struct module_env* env, struct query_info* msgqinf,
struct reply_info* msgrep, int is_referral, time_t leeway, int pside,
struct regional* region, uint32_t flags, time_t qstarttime)
struct reply_info* msgrep, int is_referral, time_t leeway, int pside,
struct regional* region, uint32_t flags, time_t qstarttime,
int is_valrec)
{
struct reply_info* rep = NULL;
if(SERVE_EXPIRED) {
@@ -1065,7 +1066,7 @@ dns_cache_store(struct module_env* env, struct query_info* msgqinf,
* useful expired record exists. */
struct msgreply_entry* e = msg_cache_lookup(env,
msgqinf->qname, msgqinf->qname_len, msgqinf->qtype,
msgqinf->qclass, flags, 0, 0);
msgqinf->qclass, flags, 0, 1);
if(e) {
struct reply_info* cached = e->entry.data;
if(cached->ttl < *env->now
@@ -1079,7 +1080,43 @@ dns_cache_store(struct module_env* env, struct query_info* msgqinf,
* one and let the validator manage caching. */
&& cached->security != sec_status_bogus
&& (env->need_to_validate &&
msgrep->security == sec_status_unchecked)) {
msgrep->security == sec_status_unchecked)
/* Exceptions to that rule are:
* o recursions that don't need validation but
* need to update the cache for coherence
* (delegation information while iterating,
* DNSKEY and DS lookups from validator)
* o explicit RRSIG queries that are not
* validated. */
&& !is_valrec
&& msgqinf->qtype != LDNS_RR_TYPE_RRSIG) {
if((int)FLAGS_GET_RCODE(msgrep->flags) !=
LDNS_RCODE_NOERROR &&
(int)FLAGS_GET_RCODE(msgrep->flags) !=
LDNS_RCODE_NXDOMAIN) {
/* The current response has an
* erroneous rcode. Adjust norec time
* so that additional lookups are not
* performed for some time. */
verbose(VERB_ALGO, "set "
"serve-expired-norec-ttl for "
"response in cache");
cached->serve_expired_norec_ttl =
NORR_TTL + *env->now;
if(env->cfg->serve_expired_ttl_reset &&
cached->serve_expired_ttl
< *env->now +
env->cfg->serve_expired_ttl) {
/* Reset serve-expired-ttl for
* valid response in cache. */
verbose(VERB_ALGO, "reset "
"serve-expired-ttl "
"for response in cache");
cached->serve_expired_ttl =
*env->now +
env->cfg->serve_expired_ttl;
}
}
verbose(VERB_ALGO, "a validated expired entry "
"could be overwritten, skip caching "
"the new message at this stage");
+4 -1
View File
@@ -90,11 +90,14 @@ struct dns_msg {
* (See DNSCACHE_STORE_xxx flags).
* @param qstarttime: time when the query was started, and thus when the
* delegations were looked up.
* @param is_valrec: if the query is validation recursion and does not get
* dnssec validation itself.
* @return 0 on alloc error (out of memory).
*/
int dns_cache_store(struct module_env* env, struct query_info* qinf,
struct reply_info* rep, int is_referral, time_t leeway, int pside,
struct regional* region, uint32_t flags, time_t qstarttime);
struct regional* region, uint32_t flags, time_t qstarttime,
int is_valrec);
/**
* Store message in the cache. Stores in message cache and rrset cache.
+105 -65
View File
@@ -52,24 +52,6 @@
#include "util/config_file.h"
#include "iterator/iterator.h"
/** Timeout when only a single probe query per IP is allowed. */
#define PROBE_MAXRTO 12000 /* in msec */
/** number of timeouts for a type when the domain can be blocked ;
* even if another type has completely rtt maxed it, the different type
* can do this number of packets (until those all timeout too) */
#define TIMEOUT_COUNT_MAX 3
/** Minus 1000 because that is outside of the RTTBAND, so
* blacklisted servers stay blacklisted if this is chosen.
* If USEFUL_SERVER_TOP_TIMEOUT is below 1000 (configured via RTT_MAX_TIMEOUT,
* infra-cache-max-rtt) change it to just above the RTT_BAND. */
#define STILL_USEFUL_TIMEOUT ( \
USEFUL_SERVER_TOP_TIMEOUT < 1000 || \
USEFUL_SERVER_TOP_TIMEOUT - 1000 <= RTT_BAND \
?RTT_BAND + 1 \
:USEFUL_SERVER_TOP_TIMEOUT - 1000)
/** ratelimit value for delegation point */
int infra_dp_ratelimit = 0;
@@ -82,6 +64,20 @@ int infra_ip_ratelimit = 0;
* For clients with a valid DNS Cookie. */
int infra_ip_ratelimit_cookie = 0;
/** Minus 1000 because that is outside of the RTTBAND, so
* blacklisted servers stay blacklisted if this is chosen.
* If USEFUL_SERVER_TOP_TIMEOUT is below 1000 (configured via RTT_MAX_TIMEOUT,
* infra-cache-max-rtt) change it to just above the RTT_BAND. */
int
still_useful_timeout()
{
return
USEFUL_SERVER_TOP_TIMEOUT < 1000 ||
USEFUL_SERVER_TOP_TIMEOUT - 1000 <= RTT_BAND
?RTT_BAND + 1
:USEFUL_SERVER_TOP_TIMEOUT - 1000;
}
size_t
infra_sizefunc(void* k, void* ATTR_UNUSED(d))
{
@@ -165,7 +161,7 @@ rate_deldatafunc(void* d, void* ATTR_UNUSED(arg))
/** find or create element in domainlimit tree */
static struct domain_limit_data* domain_limit_findcreate(
struct infra_cache* infra, char* name)
struct rbtree_type* domain_limits, char* name)
{
uint8_t* nm;
int labs;
@@ -181,8 +177,8 @@ static struct domain_limit_data* domain_limit_findcreate(
labs = dname_count_labels(nm);
/* can we find it? */
d = (struct domain_limit_data*)name_tree_find(&infra->domain_limits,
nm, nmlen, labs, LDNS_RR_CLASS_IN);
d = (struct domain_limit_data*)name_tree_find(domain_limits, nm,
nmlen, labs, LDNS_RR_CLASS_IN);
if(d) {
free(nm);
return d;
@@ -201,8 +197,8 @@ static struct domain_limit_data* domain_limit_findcreate(
d->node.dclass = LDNS_RR_CLASS_IN;
d->lim = -1;
d->below = -1;
if(!name_tree_insert(&infra->domain_limits, &d->node, nm, nmlen,
labs, LDNS_RR_CLASS_IN)) {
if(!name_tree_insert(domain_limits, &d->node, nm, nmlen, labs,
LDNS_RR_CLASS_IN)) {
log_err("duplicate element in domainlimit tree");
free(nm);
free(d);
@@ -212,19 +208,19 @@ static struct domain_limit_data* domain_limit_findcreate(
}
/** insert rate limit configuration into lookup tree */
static int infra_ratelimit_cfg_insert(struct infra_cache* infra,
static int infra_ratelimit_cfg_insert(struct rbtree_type* domain_limits,
struct config_file* cfg)
{
struct config_str2list* p;
struct domain_limit_data* d;
for(p = cfg->ratelimit_for_domain; p; p = p->next) {
d = domain_limit_findcreate(infra, p->str);
d = domain_limit_findcreate(domain_limits, p->str);
if(!d)
return 0;
d->lim = atoi(p->str2);
}
for(p = cfg->ratelimit_below_domain; p; p = p->next) {
d = domain_limit_findcreate(infra, p->str);
d = domain_limit_findcreate(domain_limits, p->str);
if(!d)
return 0;
d->below = atoi(p->str2);
@@ -232,24 +228,21 @@ static int infra_ratelimit_cfg_insert(struct infra_cache* infra,
return 1;
}
/** setup domain limits tree (0 on failure) */
static int
setup_domain_limits(struct infra_cache* infra, struct config_file* cfg)
int
setup_domain_limits(struct rbtree_type* domain_limits, struct config_file* cfg)
{
name_tree_init(&infra->domain_limits);
if(!infra_ratelimit_cfg_insert(infra, cfg)) {
name_tree_init(domain_limits);
if(!infra_ratelimit_cfg_insert(domain_limits, cfg)) {
return 0;
}
name_tree_init_parents(&infra->domain_limits);
name_tree_init_parents(domain_limits);
return 1;
}
/** find or create element in wait limit netblock tree */
static struct wait_limit_netblock_info*
wait_limit_netblock_findcreate(struct infra_cache* infra, char* str,
int cookie)
wait_limit_netblock_findcreate(struct rbtree_type* tree, char* str)
{
rbtree_type* tree;
struct sockaddr_storage addr;
int net;
socklen_t addrlen;
@@ -261,10 +254,6 @@ wait_limit_netblock_findcreate(struct infra_cache* infra, char* str,
}
/* can we find it? */
if(cookie)
tree = &infra->wait_limits_cookie_netblock;
else
tree = &infra->wait_limits_netblock;
d = (struct wait_limit_netblock_info*)addr_tree_find(tree, &addr,
addrlen, net);
if(d)
@@ -286,19 +275,21 @@ wait_limit_netblock_findcreate(struct infra_cache* infra, char* str,
/** insert wait limit information into lookup tree */
static int
infra_wait_limit_netblock_insert(struct infra_cache* infra,
struct config_file* cfg)
infra_wait_limit_netblock_insert(rbtree_type* wait_limits_netblock,
rbtree_type* wait_limits_cookie_netblock, struct config_file* cfg)
{
struct config_str2list* p;
struct wait_limit_netblock_info* d;
for(p = cfg->wait_limit_netblock; p; p = p->next) {
d = wait_limit_netblock_findcreate(infra, p->str, 0);
d = wait_limit_netblock_findcreate(wait_limits_netblock,
p->str);
if(!d)
return 0;
d->limit = atoi(p->str2);
}
for(p = cfg->wait_limit_cookie_netblock; p; p = p->next) {
d = wait_limit_netblock_findcreate(infra, p->str, 1);
d = wait_limit_netblock_findcreate(wait_limits_cookie_netblock,
p->str);
if(!d)
return 0;
d->limit = atoi(p->str2);
@@ -306,16 +297,48 @@ infra_wait_limit_netblock_insert(struct infra_cache* infra,
return 1;
}
/** setup wait limits tree (0 on failure) */
/** Add a default wait limit netblock */
static int
setup_wait_limits(struct infra_cache* infra, struct config_file* cfg)
wait_limit_netblock_default(struct rbtree_type* tree, char* str, int limit)
{
addr_tree_init(&infra->wait_limits_netblock);
addr_tree_init(&infra->wait_limits_cookie_netblock);
if(!infra_wait_limit_netblock_insert(infra, cfg))
struct wait_limit_netblock_info* d;
d = wait_limit_netblock_findcreate(tree, str);
if(!d)
return 0;
addr_tree_init_parents(&infra->wait_limits_netblock);
addr_tree_init_parents(&infra->wait_limits_cookie_netblock);
d->limit = limit;
return 1;
}
int
setup_wait_limits(rbtree_type* wait_limits_netblock,
rbtree_type* wait_limits_cookie_netblock, struct config_file* cfg)
{
addr_tree_init(wait_limits_netblock);
addr_tree_init(wait_limits_cookie_netblock);
/* Insert defaults */
/* The loopback address is separated from the rest of the network. */
/* wait-limit-netblock: 127.0.0.0/8 -1 */
if(!wait_limit_netblock_default(wait_limits_netblock, "127.0.0.0/8",
-1))
return 0;
/* wait-limit-netblock: ::1/128 -1 */
if(!wait_limit_netblock_default(wait_limits_netblock, "::1/128", -1))
return 0;
/* wait-limit-cookie-netblock: 127.0.0.0/8 -1 */
if(!wait_limit_netblock_default(wait_limits_cookie_netblock,
"127.0.0.0/8", -1))
return 0;
/* wait-limit-cookie-netblock: ::1/128 -1 */
if(!wait_limit_netblock_default(wait_limits_cookie_netblock,
"::1/128", -1))
return 0;
if(!infra_wait_limit_netblock_insert(wait_limits_netblock,
wait_limits_cookie_netblock, cfg))
return 0;
addr_tree_init_parents(wait_limits_netblock);
addr_tree_init_parents(wait_limits_cookie_netblock);
return 1;
}
@@ -348,11 +371,12 @@ infra_create(struct config_file* cfg)
return NULL;
}
/* insert config data into ratelimits */
if(!setup_domain_limits(infra, cfg)) {
if(!setup_domain_limits(&infra->domain_limits, cfg)) {
infra_delete(infra);
return NULL;
}
if(!setup_wait_limits(infra, cfg)) {
if(!setup_wait_limits(&infra->wait_limits_netblock,
&infra->wait_limits_cookie_netblock, cfg)) {
infra_delete(infra);
return NULL;
}
@@ -377,12 +401,29 @@ static void domain_limit_free(rbnode_type* n, void* ATTR_UNUSED(arg))
}
}
void
domain_limits_free(struct rbtree_type* domain_limits)
{
if(!domain_limits)
return;
traverse_postorder(domain_limits, domain_limit_free, NULL);
}
/** delete wait_limit_netblock_info entries */
static void wait_limit_netblock_del(rbnode_type* n, void* ATTR_UNUSED(arg))
{
free(n);
}
void
wait_limits_free(struct rbtree_type* wait_limits_tree)
{
if(!wait_limits_tree)
return;
traverse_postorder(wait_limits_tree, wait_limit_netblock_del,
NULL);
}
void
infra_delete(struct infra_cache* infra)
{
@@ -390,12 +431,10 @@ infra_delete(struct infra_cache* infra)
return;
slabhash_delete(infra->hosts);
slabhash_delete(infra->domain_rates);
traverse_postorder(&infra->domain_limits, domain_limit_free, NULL);
domain_limits_free(&infra->domain_limits);
slabhash_delete(infra->client_ip_rates);
traverse_postorder(&infra->wait_limits_netblock,
wait_limit_netblock_del, NULL);
traverse_postorder(&infra->wait_limits_cookie_netblock,
wait_limit_netblock_del, NULL);
wait_limits_free(&infra->wait_limits_netblock);
wait_limits_free(&infra->wait_limits_cookie_netblock);
free(infra);
}
@@ -426,7 +465,7 @@ infra_adjust(struct infra_cache* infra, struct config_file* cfg)
/* reapply domain limits */
traverse_postorder(&infra->domain_limits, domain_limit_free,
NULL);
if(!setup_domain_limits(infra, cfg)) {
if(!setup_domain_limits(&infra->domain_limits, cfg)) {
infra_delete(infra);
return NULL;
}
@@ -668,7 +707,7 @@ infra_update_tcp_works(struct infra_cache* infra,
if(data->rtt.rto >= RTT_MAX_TIMEOUT)
/* do not disqualify this server altogether, it is better
* than nothing */
data->rtt.rto = STILL_USEFUL_TIMEOUT;
data->rtt.rto = still_useful_timeout();
lock_rw_unlock(&e->lock);
}
@@ -808,7 +847,7 @@ infra_get_lame_rtt(struct infra_cache* infra,
&& infra->infra_keep_probing) {
/* single probe, keep probing */
if(*rtt >= USEFUL_SERVER_TOP_TIMEOUT)
*rtt = STILL_USEFUL_TIMEOUT;
*rtt = still_useful_timeout();
} else if(host->rtt.rto >= PROBE_MAXRTO && timenow < host->probedelay
&& rtt_notimeout(&host->rtt)*4 <= host->rtt.rto) {
/* single probe for this domain, and we are not probing */
@@ -816,15 +855,15 @@ infra_get_lame_rtt(struct infra_cache* infra,
if(qtype == LDNS_RR_TYPE_A) {
if(host->timeout_A >= TIMEOUT_COUNT_MAX)
*rtt = USEFUL_SERVER_TOP_TIMEOUT;
else *rtt = STILL_USEFUL_TIMEOUT;
else *rtt = still_useful_timeout();
} else if(qtype == LDNS_RR_TYPE_AAAA) {
if(host->timeout_AAAA >= TIMEOUT_COUNT_MAX)
*rtt = USEFUL_SERVER_TOP_TIMEOUT;
else *rtt = STILL_USEFUL_TIMEOUT;
else *rtt = still_useful_timeout();
} else {
if(host->timeout_other >= TIMEOUT_COUNT_MAX)
*rtt = USEFUL_SERVER_TOP_TIMEOUT;
else *rtt = STILL_USEFUL_TIMEOUT;
else *rtt = still_useful_timeout();
}
}
/* expired entry */
@@ -832,7 +871,7 @@ infra_get_lame_rtt(struct infra_cache* infra,
/* see if this can be a re-probe of an unresponsive server */
if(host->rtt.rto >= USEFUL_SERVER_TOP_TIMEOUT) {
lock_rw_unlock(&e->lock);
*rtt = STILL_USEFUL_TIMEOUT;
*rtt = still_useful_timeout();
*lame = 0;
*dnsseclame = 0;
*reclame = 0;
@@ -1081,7 +1120,8 @@ int infra_ratelimit_inc(struct infra_cache* infra, uint8_t* name,
lock_rw_unlock(&entry->lock);
if(premax <= lim && max > lim) {
char buf[257], qnm[257], ts[12], cs[12], ip[128];
char buf[LDNS_MAX_DOMAINLEN], qnm[LDNS_MAX_DOMAINLEN];
char ts[12], cs[12], ip[128];
dname_str(name, buf);
dname_str(qinfo->qname, qnm);
sldns_wire2str_type_buf(qinfo->qtype, ts, sizeof(ts));
+31
View File
@@ -52,6 +52,19 @@
struct slabhash;
struct config_file;
/** number of timeouts for a type when the domain can be blocked ;
* even if another type has completely rtt maxed it, the different type
* can do this number of packets (until those all timeout too) */
#define TIMEOUT_COUNT_MAX 3
/** Timeout when only a single probe query per IP is allowed.
* Any RTO above this number is considered a probe.
* It is synchronized (caped) with USEFUL_SERVER_TOP_TIMEOUT so that probing
* keeps working even if that configurable number drops below the default
* 12000 ms of probing. */
extern int PROBE_MAXRTO;
/**
* Host information kept for every server, per zone.
*/
@@ -502,4 +515,22 @@ void infra_wait_limit_inc(struct infra_cache* infra, struct comm_reply* rep,
void infra_wait_limit_dec(struct infra_cache* infra, struct comm_reply* rep,
struct config_file* cfg);
/** setup wait limits tree (0 on failure) */
int setup_wait_limits(struct rbtree_type* wait_limits_netblock,
struct rbtree_type* wait_limits_cookie_netblock,
struct config_file* cfg);
/** Free the wait limits and wait cookie limits tree. */
void wait_limits_free(struct rbtree_type* wait_limits_tree);
/** setup domain limits tree (0 on failure) */
int setup_domain_limits(struct rbtree_type* domain_limits,
struct config_file* cfg);
/** Free the domain limits tree. */
void domain_limits_free(struct rbtree_type* domain_limits);
/** exported for unit test */
int still_useful_timeout();
#endif /* SERVICES_CACHE_INFRA_H */
+82 -91
View File
@@ -703,7 +703,10 @@ create_tcp_accept_sock(struct addrinfo *addr, int v6only, int* noproto,
{
int s = -1;
char* err;
#if defined(SO_REUSEADDR) || defined(SO_REUSEPORT) || defined(IPV6_V6ONLY) || defined(IP_TRANSPARENT) || defined(IP_BINDANY) || defined(IP_FREEBIND) || defined(SO_BINDANY)
#if defined(SO_REUSEADDR) || defined(SO_REUSEPORT) \
|| defined(IPV6_V6ONLY) || defined(IP_TRANSPARENT) \
|| defined(IP_BINDANY) || defined(IP_FREEBIND) \
|| defined(SO_BINDANY) || defined(TCP_NODELAY)
int on = 1;
#endif
#ifdef HAVE_SYSTEMD
@@ -1031,7 +1034,7 @@ create_local_accept_sock(const char *path, int* noproto, int use_systemd)
* Create socket from getaddrinfo results
*/
static int
make_sock(int stype, const char* ifname, const char* port,
make_sock(int stype, const char* ifname, int port,
struct addrinfo *hints, int v6only, int* noip6, size_t rcv, size_t snd,
int* reuseport, int transparent, int tcp_mss, int nodelay, int freebind,
int use_systemd, int dscp, struct unbound_socket* ub_sock,
@@ -1039,9 +1042,11 @@ make_sock(int stype, const char* ifname, const char* port,
{
struct addrinfo *res = NULL;
int r, s, inuse, noproto;
char portbuf[32];
snprintf(portbuf, sizeof(portbuf), "%d", port);
hints->ai_socktype = stype;
*noip6 = 0;
if((r=getaddrinfo(ifname, port, hints, &res)) != 0 || !res) {
if((r=getaddrinfo(ifname, portbuf, hints, &res)) != 0 || !res) {
#ifdef USE_WINSOCK
if(r == EAI_NONAME && hints->ai_family == AF_INET6){
*noip6 = 1; /* 'Host not found' for IP6 on winXP */
@@ -1049,7 +1054,7 @@ make_sock(int stype, const char* ifname, const char* port,
}
#endif
log_err("node %s:%s getaddrinfo: %s %s",
ifname?ifname:"default", port, gai_strerror(r),
ifname?ifname:"default", portbuf, gai_strerror(r),
#ifdef EAI_SYSTEM
(r==EAI_SYSTEM?(char*)strerror(errno):"")
#else
@@ -1103,7 +1108,7 @@ make_sock(int stype, const char* ifname, const char* port,
/** make socket and first see if ifname contains port override info */
static int
make_sock_port(int stype, const char* ifname, const char* port,
make_sock_port(int stype, const char* ifname, int port,
struct addrinfo *hints, int v6only, int* noip6, size_t rcv, size_t snd,
int* reuseport, int transparent, int tcp_mss, int nodelay, int freebind,
int use_systemd, int dscp, struct unbound_socket* ub_sock,
@@ -1112,23 +1117,22 @@ make_sock_port(int stype, const char* ifname, const char* port,
char* s = strchr(ifname, '@');
if(s) {
/* override port with ifspec@port */
char p[16];
int port;
char newif[128];
if((size_t)(s-ifname) >= sizeof(newif)) {
log_err("ifname too long: %s", ifname);
*noip6 = 0;
return -1;
}
if(strlen(s+1) >= sizeof(p)) {
log_err("portnumber too long: %s", ifname);
port = atoi(s+1);
if(port < 0 || 0 == port || port > 65535) {
log_err("invalid portnumber in interface: %s", ifname);
*noip6 = 0;
return -1;
}
(void)strlcpy(newif, ifname, sizeof(newif));
newif[s-ifname] = 0;
(void)strlcpy(p, s+1, sizeof(p));
p[strlen(s+1)]=0;
return make_sock(stype, newif, p, hints, v6only, noip6, rcv,
return make_sock(stype, newif, port, hints, v6only, noip6, rcv,
snd, reuseport, transparent, tcp_mss, nodelay, freebind,
use_systemd, dscp, ub_sock, additional);
}
@@ -1237,26 +1241,6 @@ set_recvpktinfo(int s, int family)
return 1;
}
/** see if interface is ssl, its port number == the ssl port number */
static int
if_is_ssl(const char* ifname, const char* port, int ssl_port,
struct config_strlist* tls_additional_port)
{
struct config_strlist* s;
char* p = strchr(ifname, '@');
if(!p && atoi(port) == ssl_port)
return 1;
if(p && atoi(p+1) == ssl_port)
return 1;
for(s = tls_additional_port; s; s = s->next) {
if(p && atoi(p+1) == atoi(s->str))
return 1;
if(!p && atoi(port) == atoi(s->str))
return 1;
}
return 0;
}
/**
* Helper for ports_open. Creates one interface (or NULL for default).
* @param ifname: The interface ip address.
@@ -1265,7 +1249,7 @@ if_is_ssl(const char* ifname, const char* port, int ssl_port,
* @param do_udp: if udp should be used.
* @param do_tcp: if tcp should be used.
* @param hints: for getaddrinfo. family and flags have to be set by caller.
* @param port: Port number to use (as string).
* @param port: Port number to use.
* @param list: list of open ports, appended to, changed to point to list head.
* @param rcv: receive buffer size for UDP
* @param snd: send buffer size for UDP
@@ -1291,7 +1275,7 @@ if_is_ssl(const char* ifname, const char* port, int ssl_port,
*/
static int
ports_create_if(const char* ifname, int do_auto, int do_udp, int do_tcp,
struct addrinfo *hints, const char* port, struct listen_port** list,
struct addrinfo *hints, int port, struct listen_port** list,
size_t rcv, size_t snd, int ssl_port,
struct config_strlist* tls_additional_port, int https_port,
struct config_strlist* proxy_protocol_port,
@@ -1300,12 +1284,18 @@ ports_create_if(const char* ifname, int do_auto, int do_udp, int do_tcp,
int quic_port, int http_notls_downstream, int sock_queue_timeout)
{
int s, noip6=0;
int is_ssl = if_is_ssl(ifname, port, ssl_port, tls_additional_port);
int is_https = if_is_https(ifname, port, https_port);
int is_dnscrypt = if_is_dnscrypt(ifname, port, dnscrypt_port);
int is_pp2 = if_is_pp2(ifname, port, proxy_protocol_port);
int nodelay = is_https && http2_nodelay;
struct unbound_socket* ub_sock;
int is_doq = if_is_quic(ifname, port, quic_port);
/* Always set TCP_NODELAY on TLS connection as it speeds up the TLS
* handshake. DoH had already such option so we respect it.
* Otherwise the server waits before sending more handshake data for
* the client ACK (Nagle's algorithm), which is delayed because the
* client waits for more data before ACKing (delayed ACK). */
int nodelay = is_https?http2_nodelay:is_ssl;
struct unbound_socket* ub_sock;
const char* add = NULL;
if(!do_udp && !do_tcp)
@@ -1324,6 +1314,12 @@ ports_create_if(const char* ifname, int do_auto, int do_udp, int do_tcp,
}
}
/* Check if both UDP and TCP ports should be open.
* In the case of encrypted channels, probably an unencrypted channel
* at the same port is not desired. */
if((is_ssl || is_https) && !is_doq) do_udp = do_auto = 0;
if((is_doq) && !(is_https || is_ssl)) do_tcp = 0;
if(do_auto) {
ub_sock = calloc(1, sizeof(struct unbound_socket));
if(!ub_sock)
@@ -1369,13 +1365,11 @@ ports_create_if(const char* ifname, int do_auto, int do_udp, int do_tcp,
} else if(is_doq) {
udp_port_type = listen_type_doq;
add = "doq";
if(((strchr(ifname, '@') &&
atoi(strchr(ifname, '@')+1) == 53) ||
(!strchr(ifname, '@') && atoi(port) == 53))) {
log_err("DNS over QUIC is not allowed on "
"port 53. Port 53 is for DNS "
"datagrams. Error for "
"interface '%s'.", ifname);
if(if_listens_on(ifname, port, 53, NULL)) {
log_err("DNS over QUIC is strictly not "
"allowed on port 53 as per RFC 9250. "
"Port 53 is for DNS datagrams. Error "
"for interface '%s'.", ifname);
free(ub_sock->addr);
free(ub_sock);
return 0;
@@ -1423,8 +1417,6 @@ ports_create_if(const char* ifname, int do_auto, int do_udp, int do_tcp,
}
}
if(do_tcp) {
int is_ssl = if_is_ssl(ifname, port, ssl_port,
tls_additional_port);
enum listen_type port_type;
ub_sock = calloc(1, sizeof(struct unbound_socket));
if(!ub_sock)
@@ -1523,9 +1515,10 @@ listen_create(struct comm_base* base, struct listen_port* ports,
size_t bufsize, int tcp_accept_count, int tcp_idle_timeout,
int harden_large_queries, uint32_t http_max_streams,
char* http_endpoint, int http_notls, struct tcl_list* tcp_conn_limit,
void* sslctx, struct dt_env* dtenv, struct doq_table* doq_table,
struct ub_randstate* rnd, const char* ssl_service_key,
const char* ssl_service_pem, struct config_file* cfg,
void* dot_sslctx, void* doh_sslctx, void* quic_sslctx,
struct dt_env* dtenv,
struct doq_table* doq_table,
struct ub_randstate* rnd,struct config_file* cfg,
comm_point_callback_type* cb, void *cb_arg)
{
struct listen_dnsport* front = (struct listen_dnsport*)
@@ -1558,8 +1551,7 @@ listen_create(struct comm_base* base, struct listen_port* ports,
#endif
cp = comm_point_create_doq(base, ports->fd,
front->udp_buff, cb, cb_arg, ports->socket,
doq_table, rnd, ssl_service_key,
ssl_service_pem, cfg);
doq_table, rnd, quic_sslctx, cfg);
} else if(ports->ftype == listen_type_tcp ||
ports->ftype == listen_type_tcp_dnscrypt) {
cp = comm_point_create_tcp(base, ports->fd,
@@ -1578,7 +1570,7 @@ listen_create(struct comm_base* base, struct listen_port* ports,
ports->ftype, ports->pp2_enabled, cb, cb_arg,
ports->socket);
if(ports->ftype == listen_type_http) {
if(!sslctx && !http_notls) {
if(!doh_sslctx && !http_notls) {
log_warn("HTTPS port configured, but "
"no TLS tls-service-key or "
"tls-service-pem set");
@@ -1620,10 +1612,15 @@ listen_create(struct comm_base* base, struct listen_port* ports,
(ports->ftype == listen_type_udpancil) ||
(ports->ftype == listen_type_tcp_dnscrypt) ||
(ports->ftype == listen_type_udp_dnscrypt) ||
(ports->ftype == listen_type_udpancil_dnscrypt))
(ports->ftype == listen_type_udpancil_dnscrypt)) {
cp->ssl = NULL;
else
cp->ssl = sslctx;
} else if(ports->ftype == listen_type_doq) {
cp->ssl = quic_sslctx;
} else if(ports->ftype == listen_type_http) {
cp->ssl = doh_sslctx;
} else {
cp->ssl = dot_sslctx;
}
cp->dtenv = dtenv;
cp->do_not_close = 1;
#ifdef USE_DNSCRYPT
@@ -1887,8 +1884,6 @@ listening_ports_open(struct config_file* cfg, char** ifs, int num_ifs,
struct addrinfo hints;
int i, do_ip4, do_ip6;
int do_tcp, do_auto;
char portbuf[32];
snprintf(portbuf, sizeof(portbuf), "%d", cfg->port);
do_ip4 = cfg->do_ip4;
do_ip6 = cfg->do_ip6;
do_tcp = cfg->do_tcp;
@@ -1934,12 +1929,11 @@ listening_ports_open(struct config_file* cfg, char** ifs, int num_ifs,
return NULL;
}
now = after;
snprintf(portbuf, sizeof(portbuf), "%d", extraport);
if(do_ip6) {
hints.ai_family = AF_INET6;
if(!ports_create_if("::0",
do_auto, cfg->do_udp, do_tcp,
&hints, portbuf, &list,
&hints, extraport, &list,
cfg->so_rcvbuf, cfg->so_sndbuf,
cfg->ssl_port, cfg->tls_additional_port,
cfg->https_port,
@@ -1958,7 +1952,7 @@ listening_ports_open(struct config_file* cfg, char** ifs, int num_ifs,
hints.ai_family = AF_INET;
if(!ports_create_if("0.0.0.0",
do_auto, cfg->do_udp, do_tcp,
&hints, portbuf, &list,
&hints, extraport, &list,
cfg->so_rcvbuf, cfg->so_sndbuf,
cfg->ssl_port, cfg->tls_additional_port,
cfg->https_port,
@@ -1980,7 +1974,7 @@ listening_ports_open(struct config_file* cfg, char** ifs, int num_ifs,
hints.ai_family = AF_INET6;
if(!ports_create_if(do_auto?"::0":"::1",
do_auto, cfg->do_udp, do_tcp,
&hints, portbuf, &list,
&hints, cfg->port, &list,
cfg->so_rcvbuf, cfg->so_sndbuf,
cfg->ssl_port, cfg->tls_additional_port,
cfg->https_port, cfg->proxy_protocol_port,
@@ -1998,7 +1992,7 @@ listening_ports_open(struct config_file* cfg, char** ifs, int num_ifs,
hints.ai_family = AF_INET;
if(!ports_create_if(do_auto?"0.0.0.0":"127.0.0.1",
do_auto, cfg->do_udp, do_tcp,
&hints, portbuf, &list,
&hints, cfg->port, &list,
cfg->so_rcvbuf, cfg->so_sndbuf,
cfg->ssl_port, cfg->tls_additional_port,
cfg->https_port, cfg->proxy_protocol_port,
@@ -2018,7 +2012,7 @@ listening_ports_open(struct config_file* cfg, char** ifs, int num_ifs,
continue;
hints.ai_family = AF_INET6;
if(!ports_create_if(ifs[i], 0, cfg->do_udp,
do_tcp, &hints, portbuf, &list,
do_tcp, &hints, cfg->port, &list,
cfg->so_rcvbuf, cfg->so_sndbuf,
cfg->ssl_port, cfg->tls_additional_port,
cfg->https_port, cfg->proxy_protocol_port,
@@ -2036,7 +2030,7 @@ listening_ports_open(struct config_file* cfg, char** ifs, int num_ifs,
continue;
hints.ai_family = AF_INET;
if(!ports_create_if(ifs[i], 0, cfg->do_udp,
do_tcp, &hints, portbuf, &list,
do_tcp, &hints, cfg->port, &list,
cfg->so_rcvbuf, cfg->so_sndbuf,
cfg->ssl_port, cfg->tls_additional_port,
cfg->https_port, cfg->proxy_protocol_port,
@@ -4598,10 +4592,9 @@ doq_alpn_select_cb(SSL* ATTR_UNUSED(ssl), const unsigned char** out,
return SSL_TLSEXT_ERR_ALERT_FATAL;
}
/** create new tls session for server doq connection */
static SSL_CTX*
doq_ctx_server_setup(struct doq_server_socket* doq_socket)
void* quic_sslctx_create(char* key, char* pem, char* verifypem)
{
#ifdef HAVE_NGTCP2
char* sid_ctx = "unbound server";
#ifndef HAVE_NGTCP2_CRYPTO_QUICTLS_CONFIGURE_SERVER_CONTEXT
SSL_QUIC_METHOD* quic_method;
@@ -4611,6 +4604,16 @@ doq_ctx_server_setup(struct doq_server_socket* doq_socket)
log_crypto_err("Could not SSL_CTX_new");
return NULL;
}
if(!key || key[0] == 0) {
log_err("doq: error, no tls-service-key file specified");
SSL_CTX_free(ctx);
return NULL;
}
if(!pem || pem[0] == 0) {
log_err("doq: error, no tls-service-pem file specified");
SSL_CTX_free(ctx);
return NULL;
}
SSL_CTX_set_options(ctx,
(SSL_OP_ALL & ~SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS) |
SSL_OP_SINGLE_ECDH_USE |
@@ -4623,43 +4626,37 @@ doq_ctx_server_setup(struct doq_server_socket* doq_socket)
SSL_CTX_set_alpn_select_cb(ctx, doq_alpn_select_cb, NULL);
#endif
SSL_CTX_set_default_verify_paths(ctx);
if(!SSL_CTX_use_certificate_chain_file(ctx,
doq_socket->ssl_service_pem)) {
log_err("doq: error for cert file: %s",
doq_socket->ssl_service_pem);
if(!SSL_CTX_use_certificate_chain_file(ctx, pem)) {
log_err("doq: error for cert file: %s", pem);
log_crypto_err("doq: error in "
"SSL_CTX_use_certificate_chain_file");
SSL_CTX_free(ctx);
return NULL;
}
if(!SSL_CTX_use_PrivateKey_file(ctx, doq_socket->ssl_service_key,
SSL_FILETYPE_PEM)) {
log_err("doq: error for private key file: %s",
doq_socket->ssl_service_key);
if(!SSL_CTX_use_PrivateKey_file(ctx, key, SSL_FILETYPE_PEM)) {
log_err("doq: error for private key file: %s", key);
log_crypto_err("doq: error in SSL_CTX_use_PrivateKey_file");
SSL_CTX_free(ctx);
return NULL;
}
if(!SSL_CTX_check_private_key(ctx)) {
log_err("doq: error for key file: %s",
doq_socket->ssl_service_key);
log_err("doq: error for key file: %s", key);
log_crypto_err("doq: error in SSL_CTX_check_private_key");
SSL_CTX_free(ctx);
return NULL;
}
SSL_CTX_set_session_id_context(ctx, (void*)sid_ctx, strlen(sid_ctx));
if(doq_socket->ssl_verify_pem && doq_socket->ssl_verify_pem[0]) {
if(!SSL_CTX_load_verify_locations(ctx,
doq_socket->ssl_verify_pem, NULL)) {
if(verifypem && verifypem[0]) {
if(!SSL_CTX_load_verify_locations(ctx, verifypem, NULL)) {
log_err("doq: error for verify pem file: %s",
doq_socket->ssl_verify_pem);
verifypem);
log_crypto_err("doq: error in "
"SSL_CTX_load_verify_locations");
SSL_CTX_free(ctx);
return NULL;
}
SSL_CTX_set_client_CA_list(ctx, SSL_load_client_CA_file(
doq_socket->ssl_verify_pem));
verifypem));
SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER|
SSL_VERIFY_CLIENT_ONCE|
SSL_VERIFY_FAIL_IF_NO_PEER_CERT, NULL);
@@ -4672,7 +4669,7 @@ doq_ctx_server_setup(struct doq_server_socket* doq_socket)
SSL_CTX_free(ctx);
return NULL;
}
#else
#else /* HAVE_NGTCP2_CRYPTO_QUICTLS_CONFIGURE_SERVER_CONTEXT */
/* The quic_method needs to remain valid during the SSL_CTX
* lifetime, so we allocate it. It is freed with the
* doq_server_socket. */
@@ -4690,6 +4687,10 @@ doq_ctx_server_setup(struct doq_server_socket* doq_socket)
SSL_CTX_set_quic_method(ctx, doq_socket->quic_method);
#endif
return ctx;
#else /* HAVE_NGTCP2 */
(void)key; (void)pem; (void)verifypem;
return NULL;
#endif /* HAVE_NGTCP2 */
}
/** Get the ngtcp2_conn from ssl userdata of type ngtcp2_conn_ref */
@@ -4720,16 +4721,6 @@ doq_ssl_server_setup(SSL_CTX* ctx, struct doq_conn* conn)
return ssl;
}
/** setup the doq_socket server tls context */
int
doq_socket_setup_ctx(struct doq_server_socket* doq_socket)
{
doq_socket->ctx = doq_ctx_server_setup(doq_socket);
if(!doq_socket->ctx)
return 0;
return 1;
}
int
doq_conn_setup(struct doq_conn* conn, uint8_t* scid, size_t scidlen,
uint8_t* ocid, size_t ocidlen, const uint8_t* token, size_t tokenlen)
+16 -9
View File
@@ -194,12 +194,12 @@ int resolve_interface_names(char** ifs, int num_ifs,
* @param http_endpoint: HTTP endpoint to service queries on
* @param http_notls: no TLS for http downstream
* @param tcp_conn_limit: TCP connection limit info.
* @param sslctx: nonNULL if ssl context.
* @param dot_sslctx: nonNULL if dot ssl context.
* @param doh_sslctx: nonNULL if doh ssl context.
* @param quic_sslctx: nonNULL if quic ssl context.
* @param dtenv: nonNULL if dnstap enabled.
* @param doq_table: the doq connection table, with shared information.
* @param rnd: random state.
* @param ssl_service_key: the SSL service key file.
* @param ssl_service_pem: the SSL service pem file.
* @param cfg: config file struct.
* @param cb: callback function when a request arrives. It is passed
* the packet and user argument. Return true to send a reply.
@@ -211,9 +211,10 @@ listen_create(struct comm_base* base, struct listen_port* ports,
size_t bufsize, int tcp_accept_count, int tcp_idle_timeout,
int harden_large_queries, uint32_t http_max_streams,
char* http_endpoint, int http_notls, struct tcl_list* tcp_conn_limit,
void* sslctx, struct dt_env* dtenv, struct doq_table* doq_table,
struct ub_randstate* rnd, const char* ssl_service_key,
const char* ssl_service_pem, struct config_file* cfg,
void* dot_sslctx, void* doh_sslctx, void* quic_sslctx,
struct dt_env* dtenv,
struct doq_table* doq_table,
struct ub_randstate* rnd,struct config_file* cfg,
comm_point_callback_type* cb, void *cb_arg);
/**
@@ -512,6 +513,15 @@ struct doq_table {
size_t current_size;
};
/**
* create SSL context for QUIC
* @param key: private key file.
* @param pem: public key cert.
* @param verifypem: if nonNULL, verifylocation file.
* return SSL_CTX* or NULL on failure (logged).
*/
void* quic_sslctx_create(char* key, char* pem, char* verifypem);
/** create doq table */
struct doq_table* doq_table_create(struct config_file* cfg,
struct ub_randstate* rnd);
@@ -712,9 +722,6 @@ int doq_timer_cmp(const void* key1, const void* key2);
/** compare function of doq_stream */
int doq_stream_cmp(const void* key1, const void* key2);
/** setup the doq_socket server tls context */
int doq_socket_setup_ctx(struct doq_server_socket* doq_socket);
/** setup the doq connection callbacks, and settings. */
int doq_conn_setup(struct doq_conn* conn, uint8_t* scid, size_t scidlen,
uint8_t* ocid, size_t ocidlen, const uint8_t* token, size_t tokenlen);
+46 -4
View File
@@ -223,7 +223,7 @@ lz_enter_zone_dname(struct local_zones* zones, uint8_t* nm, size_t len,
lock_rw_wrlock(&z->lock);
if(!rbtree_insert(&zones->ztree, &z->node)) {
struct local_zone* oldz;
char str[256];
char str[LDNS_MAX_DOMAINLEN];
dname_str(nm, str);
log_warn("duplicate local-zone %s", str);
lock_rw_unlock(&z->lock);
@@ -943,6 +943,16 @@ int local_zone_enter_defaults(struct local_zones* zones, struct config_file* cfg
log_err("out of memory adding default zone");
return 0;
}
/* resolver.arpa. zone (RFC 9462) */
if(!add_empty_default(zones, cfg, "resolver.arpa.")) {
log_err("out of memory adding default zone");
return 0;
}
/* service.arpa. zone (draft-ietf-dnssd-srp-25) */
if(!add_empty_default(zones, cfg, "service.arpa.")) {
log_err("out of memory adding default zone");
return 0;
}
/* onion. zone (RFC 7686) */
if(!add_empty_default(zones, cfg, "onion.")) {
log_err("out of memory adding default zone");
@@ -1765,7 +1775,7 @@ lz_inform_print(struct local_zone* z, struct query_info* qinfo,
struct sockaddr_storage* addr, socklen_t addrlen)
{
char ip[128], txt[512];
char zname[LDNS_MAX_DOMAINLEN+1];
char zname[LDNS_MAX_DOMAINLEN];
uint16_t port = ntohs(((struct sockaddr_in*)addr)->sin_port);
dname_str(z->name, zname);
addr_to_str(addr, addrlen, ip, sizeof(ip));
@@ -1875,7 +1885,7 @@ local_zones_answer(struct local_zones* zones, struct module_env* env,
return 0;
}
if(z && verbosity >= VERB_ALGO) {
char zname[255+1];
char zname[LDNS_MAX_DOMAINLEN];
dname_str(z->name, zname);
verbose(VERB_ALGO, "using localzone %s %s from view %s",
zname, local_zone_type2str(lzt), view->name);
@@ -1897,7 +1907,7 @@ local_zones_answer(struct local_zones* zones, struct module_env* env,
z->override_tree, &tag, tagname, num_tags);
lock_rw_unlock(&zones->lock);
if(z && verbosity >= VERB_ALGO) {
char zname[255+1];
char zname[LDNS_MAX_DOMAINLEN];
dname_str(z->name, zname);
verbose(VERB_ALGO, "using localzone %s %s", zname,
local_zone_type2str(lzt));
@@ -2210,3 +2220,35 @@ void local_zones_del_data(struct local_zones* zones,
lock_rw_unlock(&z->lock);
}
/** Get memory usage for local_zone */
static size_t
local_zone_get_mem(struct local_zone* z)
{
size_t m = sizeof(*z);
lock_rw_rdlock(&z->lock);
m += z->namelen + z->taglen + regional_get_mem(z->region);
lock_rw_unlock(&z->lock);
return m;
}
size_t local_zones_get_mem(struct local_zones* zones)
{
struct local_zone* z;
size_t m;
if(!zones) return 0;
m = sizeof(*zones);
lock_rw_rdlock(&zones->lock);
RBTREE_FOR(z, struct local_zone*, &zones->ztree) {
m += local_zone_get_mem(z);
}
lock_rw_unlock(&zones->lock);
return m;
}
void local_zones_swap_tree(struct local_zones* zones, struct local_zones* data)
{
rbtree_type oldtree = zones->ztree;
zones->ztree = data->ztree;
data->ztree = oldtree;
}
+14
View File
@@ -642,6 +642,20 @@ local_zone_enter_rr(struct local_zone* z, uint8_t* nm, size_t nmlen,
struct local_data*
local_zone_find_data(struct local_zone* z, uint8_t* nm, size_t nmlen, int nmlabs);
/** Get memory usage for local_zones tree. The routine locks and unlocks
* the tree for reading. */
size_t local_zones_get_mem(struct local_zones* zones);
/**
* Swap internal tree with preallocated entries. Caller should manage
* the locks.
* @param zones: the local zones structure.
* @param data: the data structure used to take elements from. This contains
* the old elements on return.
*/
void local_zones_swap_tree(struct local_zones* zones,
struct local_zones* data);
/** Enter a new zone; returns with WRlock
* Made public for unit testing
* @param zones: the local zones tree
+282 -21
View File
@@ -77,6 +77,20 @@
#include <netdb.h>
#endif
/** Compare two views by name */
static int
view_name_compare(const char* v_a, const char* v_b)
{
if(v_a == NULL && v_b == NULL)
return 0;
/* The NULL name is smaller than if the name is set. */
if(v_a == NULL)
return -1;
if(v_b == NULL)
return 1;
return strcmp(v_a, v_b);
}
/**
* Compare two response-ip client info entries for the purpose of mesh state
* compare. It returns 0 if ci_a and ci_b are considered equal; otherwise
@@ -132,12 +146,14 @@ client_info_compare(const struct respip_client_info* ci_a,
}
if(ci_a->tag_datas != ci_b->tag_datas)
return ci_a->tag_datas < ci_b->tag_datas ? -1 : 1;
if(ci_a->view != ci_b->view)
return ci_a->view < ci_b->view ? -1 : 1;
/* For the unbound daemon these should be non-NULL and identical,
* but we check that just in case. */
if(ci_a->respip_set != ci_b->respip_set)
return ci_a->respip_set < ci_b->respip_set ? -1 : 1;
if(ci_a->view || ci_a->view_name || ci_b->view || ci_b->view_name) {
/* Compare the views by name. */
cmp = view_name_compare(
(ci_a->view?ci_a->view->name:ci_a->view_name),
(ci_b->view?ci_b->view->name:ci_b->view_name));
if(cmp != 0)
return cmp;
}
return 0;
}
@@ -214,6 +230,9 @@ mesh_create(struct module_stack* stack, struct module_env* env)
mesh->stats_dropped = 0;
mesh->ans_expired = 0;
mesh->ans_cachedb = 0;
mesh->num_queries_discard_timeout = 0;
mesh->num_queries_wait_limit = 0;
mesh->num_dns_error_reports = 0;
mesh->max_reply_states = env->cfg->num_queries_per_thread;
mesh->max_forever_states = (mesh->max_reply_states+1)/2;
#ifndef S_SPLINT_S
@@ -424,7 +443,7 @@ void mesh_new_client(struct mesh_area* mesh, struct query_info* qinfo,
verbose(VERB_ALGO, "Too many queries waiting from the IP. "
"dropping incoming query.");
comm_point_drop_reply(rep);
mesh->stats_dropped++;
mesh->num_queries_wait_limit++;
return;
}
if(!unique)
@@ -868,6 +887,78 @@ void mesh_report_reply(struct mesh_area* mesh, struct outbound_entry* e,
mesh_run(mesh, e->qstate->mesh_info, event, e);
}
/** copy strlist to region */
static struct config_strlist*
cfg_region_strlist_copy(struct regional* region, struct config_strlist* list)
{
struct config_strlist* result = NULL, *last = NULL, *s = list;
while(s) {
struct config_strlist* n = regional_alloc_zero(region,
sizeof(*n));
if(!n)
return NULL;
n->str = regional_strdup(region, s->str);
if(!n->str)
return NULL;
if(last)
last->next = n;
else result = n;
last = n;
s = s->next;
}
return result;
}
/** Copy the client info to the query region. */
static struct respip_client_info*
mesh_copy_client_info(struct regional* region, struct respip_client_info* cinfo)
{
size_t i;
struct respip_client_info* client_info;
client_info = regional_alloc_init(region, cinfo, sizeof(*cinfo));
if(!client_info)
return NULL;
/* Copy the client_info so that if the configuration changes,
* then the data stays valid. */
if(cinfo->taglist) {
client_info->taglist = regional_alloc_init(region, cinfo->taglist,
cinfo->taglen);
if(!client_info->taglist)
return NULL;
}
if(cinfo->tag_actions) {
client_info->tag_actions = regional_alloc_init(region, cinfo->tag_actions,
cinfo->tag_actions_size);
if(!client_info->tag_actions)
return NULL;
}
if(cinfo->tag_datas) {
client_info->tag_datas = regional_alloc_zero(region,
sizeof(struct config_strlist*)*cinfo->tag_datas_size);
if(!client_info->tag_datas)
return NULL;
for(i=0; i<cinfo->tag_datas_size; i++) {
if(cinfo->tag_datas[i]) {
client_info->tag_datas[i] = cfg_region_strlist_copy(
region, cinfo->tag_datas[i]);
if(!client_info->tag_datas[i])
return NULL;
}
}
}
if(cinfo->view) {
/* Do not copy the view pointer but store a name instead.
* The name is looked up later when done, this means that
* the view tree can be changed, by reloads. */
client_info->view = NULL;
client_info->view_name = regional_strdup(region,
cinfo->view->name);
if(!client_info->view_name)
return NULL;
}
return client_info;
}
struct mesh_state*
mesh_state_create(struct module_env* env, struct query_info* qinfo,
struct respip_client_info* cinfo, uint16_t qflags, int prime,
@@ -908,8 +999,7 @@ mesh_state_create(struct module_env* env, struct query_info* qinfo,
return NULL;
}
if(cinfo) {
mstate->s.client_info = regional_alloc_init(region, cinfo,
sizeof(*cinfo));
mstate->s.client_info = mesh_copy_client_info(region, cinfo);
if(!mstate->s.client_info) {
alloc_reg_release(env->alloc, region);
return NULL;
@@ -1489,10 +1579,121 @@ mesh_send_reply(struct mesh_state* m, int rcode, struct reply_info* rep,
&r->query_reply.client_addr,
r->query_reply.client_addrlen, duration, 0, r_buffer,
(m->s.env->cfg->log_destaddr?(void*)r->query_reply.c->socket->addr:NULL),
r->query_reply.c->type);
r->query_reply.c->type, r->query_reply.c->ssl);
}
}
/**
* Generate the DNS Error Report (RFC9567).
* If there is an EDE attached for this reply and there was a Report-Channel
* EDNS0 option from the upstream, fire up a report query.
* @param qstate: module qstate.
* @param rep: prepared reply to be sent.
*/
static void dns_error_reporting(struct module_qstate* qstate,
struct reply_info* rep)
{
struct query_info qinfo;
struct mesh_state* sub;
struct module_qstate* newq;
uint8_t buf[LDNS_MAX_DOMAINLEN];
size_t count = 0;
int written;
size_t expected_length;
struct edns_option* opt;
sldns_ede_code reason_bogus = LDNS_EDE_NONE;
sldns_rr_type qtype = qstate->qinfo.qtype;
uint8_t* qname = qstate->qinfo.qname;
size_t qname_len = qstate->qinfo.qname_len-1; /* skip the trailing \0 */
uint8_t* agent_domain;
size_t agent_domain_len;
/* We need a valid reporting agent;
* this is based on qstate->edns_opts_back_in that will probably have
* the latest reporting agent we found while iterating */
opt = edns_opt_list_find(qstate->edns_opts_back_in,
LDNS_EDNS_REPORT_CHANNEL);
if(!opt) return;
agent_domain_len = opt->opt_len;
agent_domain = opt->opt_data;
if(dname_valid(agent_domain, agent_domain_len) < 3) {
/* The agent domain needs to be a valid dname that is not the
* root; from RFC9567. */
return;
}
/* Get the EDE generated from the mesh state, these are mostly
* validator errors. If other errors are produced in the future (e.g.,
* RPZ) we would not want them to result in error reports. */
reason_bogus = errinf_to_reason_bogus(qstate);
if(rep && ((reason_bogus == LDNS_EDE_DNSSEC_BOGUS &&
rep->reason_bogus != LDNS_EDE_NONE) ||
reason_bogus == LDNS_EDE_NONE)) {
reason_bogus = rep->reason_bogus;
}
if(reason_bogus == LDNS_EDE_NONE ||
/* other, does not make sense without the text that comes
* with it */
reason_bogus == LDNS_EDE_OTHER) return;
/* Synthesize the error report query in the format:
* "_er.$qtype.$qname.$ede._er.$reporting-agent-domain" */
/* First check if the static length parts fit in the buffer.
* That is everything except for qtype and ede that need to be
* converted to decimal and checked further on. */
expected_length = 4/*_er*/+qname_len+4/*_er*/+agent_domain_len;
if(expected_length > LDNS_MAX_DOMAINLEN) goto skip;
memmove(buf+count, "\3_er", 4);
count += 4;
written = snprintf((char*)buf+count, LDNS_MAX_DOMAINLEN-count,
"X%d", qtype);
expected_length += written;
/* Skip on error, truncation or long expected length */
if(written < 0 || (size_t)written >= LDNS_MAX_DOMAINLEN-count ||
expected_length > LDNS_MAX_DOMAINLEN ) goto skip;
/* Put in the label length */
*(buf+count) = (char)(written - 1);
count += written;
memmove(buf+count, qname, qname_len);
count += qname_len;
written = snprintf((char*)buf+count, LDNS_MAX_DOMAINLEN-count,
"X%d", reason_bogus);
expected_length += written;
/* Skip on error, truncation or long expected length */
if(written < 0 || (size_t)written >= LDNS_MAX_DOMAINLEN-count ||
expected_length > LDNS_MAX_DOMAINLEN ) goto skip;
*(buf+count) = (char)(written - 1);
count += written;
memmove(buf+count, "\3_er", 4);
count += 4;
/* Copy the agent domain */
memmove(buf+count, agent_domain, agent_domain_len);
count += agent_domain_len;
qinfo.qname = buf;
qinfo.qname_len = count;
qinfo.qtype = LDNS_RR_TYPE_TXT;
qinfo.qclass = qstate->qinfo.qclass;
qinfo.local_alias = NULL;
log_query_info(VERB_ALGO, "DNS Error Reporting: generating report "
"query for", &qinfo);
if(mesh_add_sub(qstate, &qinfo, BIT_RD, 0, 0, &newq, &sub)) {
qstate->env->mesh->num_dns_error_reports++;
}
return;
skip:
verbose(VERB_ALGO, "DNS Error Reporting: report query qname too long; "
"skip");
return;
}
void mesh_query_done(struct mesh_state* mstate)
{
struct mesh_reply* r;
@@ -1510,8 +1711,10 @@ void mesh_query_done(struct mesh_state* mstate)
}
if(mstate->s.return_rcode == LDNS_RCODE_SERVFAIL ||
(rep && FLAGS_GET_RCODE(rep->flags) == LDNS_RCODE_SERVFAIL)) {
/* we are SERVFAILing; check for expired answer here */
mesh_serve_expired_callback(mstate);
if(mstate->s.env->cfg->serve_expired) {
/* we are SERVFAILing; check for expired answer here */
mesh_respond_serve_expired(mstate);
}
if((mstate->reply_list || mstate->cb_list)
&& mstate->s.env->cfg->log_servfail
&& !mstate->s.env->cfg->val_log_squelch) {
@@ -1519,6 +1722,10 @@ void mesh_query_done(struct mesh_state* mstate)
if(err) { log_err("%s", err); }
}
}
if(mstate->reply_list && mstate->s.env->cfg->dns_error_reporting)
dns_error_reporting(&mstate->s, rep);
for(r = mstate->reply_list; r; r = r->next) {
struct timeval old;
timeval_subtract(&old, mstate->s.env->now_tv, &r->start_time);
@@ -1540,7 +1747,7 @@ void mesh_query_done(struct mesh_state* mstate)
http2_stream_remove_mesh_state(r->h2_stream);
comm_point_drop_reply(&r->query_reply);
mstate->reply_list = reply_list;
mstate->s.env->mesh->stats_dropped++;
mstate->s.env->mesh->num_queries_discard_timeout++;
continue;
}
@@ -1682,6 +1889,25 @@ struct mesh_state* mesh_area_find(struct mesh_area* mesh,
return result;
}
/** remove mesh state callback */
int mesh_state_del_cb(struct mesh_state* s, mesh_cb_func_type cb, void* cb_arg)
{
struct mesh_cb* r, *prev = NULL;
r = s->cb_list;
while(r) {
if(r->cb == cb && r->cb_arg == cb_arg) {
/* Delete this entry. */
/* It was allocated in the s.region, so no free. */
if(prev) prev->next = r->next;
else s->cb_list = r->next;
return 1;
}
prev = r;
r = r->next;
}
return 0;
}
int mesh_state_add_cb(struct mesh_state* s, struct edns_data* edns,
sldns_buffer* buf, mesh_cb_func_type cb, void* cb_arg,
uint16_t qid, uint16_t qflags)
@@ -2029,6 +2255,8 @@ mesh_stats_clear(struct mesh_area* mesh)
{
if(!mesh)
return;
mesh->num_query_authzone_up = 0;
mesh->num_query_authzone_down = 0;
mesh->replies_sent = 0;
mesh->replies_sum_wait.tv_sec = 0;
mesh->replies_sum_wait.tv_usec = 0;
@@ -2042,6 +2270,9 @@ mesh_stats_clear(struct mesh_area* mesh)
memset(&mesh->ans_rcode[0], 0, sizeof(size_t)*UB_STATS_RCODE_NUM);
memset(&mesh->rpz_action[0], 0, sizeof(size_t)*UB_STATS_RPZ_ACTION_NUM);
mesh->ans_nodata = 0;
mesh->num_queries_discard_timeout = 0;
mesh->num_queries_wait_limit = 0;
mesh->num_dns_error_reports = 0;
}
size_t
@@ -2143,7 +2374,8 @@ apply_respip_action(struct module_qstate* qstate,
return 1;
if(!respip_rewrite_reply(qinfo, cinfo, rep, encode_repp, actinfo,
alias_rrset, 0, qstate->region, az, NULL))
alias_rrset, 0, qstate->region, az, NULL, qstate->env->views,
qstate->env->respip_set))
return 0;
/* xxx_deny actions mean dropping the reply, unless the original reply
@@ -2177,7 +2409,7 @@ mesh_serve_expired_callback(void* arg)
struct timeval tv = {0, 0};
int must_validate = (!(qstate->query_flags&BIT_CD)
|| qstate->env->cfg->ignore_cd) && qstate->env->need_to_validate;
int i = 0;
int i = 0, for_count;
int is_expired;
if(!qstate->serve_expired_data) return;
verbose(VERB_ALGO, "Serve expired: Trying to reply with expired data");
@@ -2190,15 +2422,21 @@ mesh_serve_expired_callback(void* arg)
"Serve expired: Not allowed to look into cache for stale");
return;
}
/* The following while is used instead of the `goto lookup_cache`
* like in the worker. */
while(1) {
/* The following for is used instead of the `goto lookup_cache`
* like in the worker. This loop should get max 2 passes if we need to
* do any aliasing. */
for(for_count = 0; for_count < 2; for_count++) {
fptr_ok(fptr_whitelist_serve_expired_lookup(
qstate->serve_expired_data->get_cached_answer));
msg = (*qstate->serve_expired_data->get_cached_answer)(qstate,
lookup_qinfo, &is_expired);
if(!msg)
if(!msg || (FLAGS_GET_RCODE(msg->rep->flags) != LDNS_RCODE_NOERROR
&& FLAGS_GET_RCODE(msg->rep->flags) != LDNS_RCODE_NXDOMAIN
&& FLAGS_GET_RCODE(msg->rep->flags) != LDNS_RCODE_YXDOMAIN)) {
/* We don't care for cached failure answers at this
* stage. */
return;
}
/* Reset these in case we pass a second time from here. */
encode_rep = msg->rep;
memset(&actinfo, 0, sizeof(actinfo));
@@ -2212,7 +2450,8 @@ mesh_serve_expired_callback(void* arg)
} else if(partial_rep &&
!respip_merge_cname(partial_rep, &qstate->qinfo, msg->rep,
qstate->client_info, must_validate, &encode_rep, qstate->region,
qstate->env->auth_zones)) {
qstate->env->auth_zones, qstate->env->views,
qstate->env->respip_set)) {
return;
}
if(!encode_rep || alias_rrset) {
@@ -2270,7 +2509,7 @@ mesh_serve_expired_callback(void* arg)
http2_stream_remove_mesh_state(r->h2_stream);
comm_point_drop_reply(&r->query_reply);
mstate->reply_list = reply_list;
mstate->s.env->mesh->stats_dropped++;
mstate->s.env->mesh->num_queries_discard_timeout++;
continue;
}
@@ -2366,3 +2605,25 @@ int mesh_jostle_exceeded(struct mesh_area* mesh)
return 0;
return 1;
}
void mesh_remove_callback(struct mesh_area* mesh, struct query_info* qinfo,
uint16_t qflags, mesh_cb_func_type cb, void* cb_arg)
{
struct mesh_state* s = NULL;
s = mesh_area_find(mesh, NULL, qinfo, qflags&(BIT_RD|BIT_CD), 0, 0);
if(!s) return;
if(!mesh_state_del_cb(s, cb, cb_arg)) return;
/* It was in the list and removed. */
log_assert(mesh->num_reply_addrs > 0);
mesh->num_reply_addrs--;
if(!s->reply_list && !s->cb_list) {
/* was a reply state, not anymore */
log_assert(mesh->num_reply_states > 0);
mesh->num_reply_states--;
}
if(!s->reply_list && !s->cb_list &&
s->super_set.count == 0) {
mesh->num_detached_states++;
}
}
+24
View File
@@ -90,6 +90,11 @@ struct mesh_area {
/** rbtree of all current queries (mesh_state.node)*/
rbtree_type all;
/** number of queries for unbound's auth_zones, upstream query */
size_t num_query_authzone_up;
/** number of queries for unbound's auth_zones, downstream answers */
size_t num_query_authzone_down;
/** count of the total number of mesh_reply entries */
size_t num_reply_addrs;
/** count of the number of mesh_states that have mesh_replies
@@ -132,6 +137,12 @@ struct mesh_area {
size_t ans_nodata;
/** (extended stats) type of applied RPZ action */
size_t rpz_action[UB_STATS_RPZ_ACTION_NUM];
/** stats, number of queries removed due to discard-timeout */
size_t num_queries_discard_timeout;
/** stats, number of queries removed due to wait-limit */
size_t num_queries_wait_limit;
/** stats, number of dns error reports generated */
size_t num_dns_error_reports;
/** backup of query if other operations recurse and need the
* network buffers */
@@ -697,4 +708,17 @@ int mesh_jostle_exceeded(struct mesh_area* mesh);
*/
void mesh_respond_serve_expired(struct mesh_state* mstate);
/**
* Remove callback from mesh. Removes the callback from the state.
* The state itself is left to run. Searches for the pointer values.
*
* @param mesh: the mesh.
* @param qinfo: query from client.
* @param qflags: flags from client query.
* @param cb: callback function.
* @param cb_arg: callback user arg.
*/
void mesh_remove_callback(struct mesh_area* mesh, struct query_info* qinfo,
uint16_t qflags, mesh_cb_func_type cb, void* cb_arg);
#endif /* SERVICES_MESH_H */
+25 -8
View File
@@ -262,12 +262,14 @@ pick_outgoing_tcp(struct pending_tcp* pend, struct waiting_tcp* w, int s)
/** get TCP file descriptor for address, returns -1 on failure,
* tcp_mss is 0 or maxseg size to set for TCP packets. */
int
outnet_get_tcp_fd(struct sockaddr_storage* addr, socklen_t addrlen, int tcp_mss, int dscp)
outnet_get_tcp_fd(struct sockaddr_storage* addr, socklen_t addrlen,
int tcp_mss, int dscp, int nodelay)
{
int s;
int af;
char* err;
#if defined(SO_REUSEADDR) || defined(IP_BIND_ADDRESS_NO_PORT)
#if defined(SO_REUSEADDR) || defined(IP_BIND_ADDRESS_NO_PORT) \
|| defined(TCP_NODELAY)
int on = 1;
#endif
#ifdef INET6
@@ -320,6 +322,18 @@ outnet_get_tcp_fd(struct sockaddr_storage* addr, socklen_t addrlen, int tcp_mss,
" setsockopt(.. IP_BIND_ADDRESS_NO_PORT ..) failed");
}
#endif /* IP_BIND_ADDRESS_NO_PORT */
if(nodelay) {
#if defined(IPPROTO_TCP) && defined(TCP_NODELAY)
if(setsockopt(s, IPPROTO_TCP, TCP_NODELAY, (void*)&on,
(socklen_t)sizeof(on)) < 0) {
verbose(VERB_ALGO, "outgoing tcp:"
" setsockopt(.. TCP_NODELAY ..) failed");
}
#else
verbose(VERB_ALGO, "outgoing tcp:"
" setsockopt(.. TCP_NODELAY ..) unsupported");
#endif /* defined(IPPROTO_TCP) && defined(TCP_NODELAY) */
}
return s;
}
@@ -649,7 +663,8 @@ outnet_tcp_take_into_use(struct waiting_tcp* w)
}
/* open socket */
s = outnet_get_tcp_fd(&w->addr, w->addrlen, w->outnet->tcp_mss, w->outnet->ip_dscp);
s = outnet_get_tcp_fd(&w->addr, w->addrlen, w->outnet->tcp_mss,
w->outnet->ip_dscp, w->ssl_upstream);
if(s == -1)
return 0;
@@ -1054,7 +1069,7 @@ reuse_move_writewait_away(struct outside_network* outnet,
if(verbosity >= VERB_CLIENT && pend->query->pkt_len > 12+2+2 &&
LDNS_QDCOUNT(pend->query->pkt) > 0 &&
dname_valid(pend->query->pkt+12, pend->query->pkt_len-12)) {
char buf[LDNS_MAX_DOMAINLEN+1];
char buf[LDNS_MAX_DOMAINLEN];
dname_str(pend->query->pkt+12, buf);
verbose(VERB_CLIENT, "reuse_move_writewait_away current %s %d bytes were written",
buf, (int)pend->c->tcp_write_byte_count);
@@ -1079,7 +1094,7 @@ reuse_move_writewait_away(struct outside_network* outnet,
if(verbosity >= VERB_CLIENT && w->pkt_len > 12+2+2 &&
LDNS_QDCOUNT(w->pkt) > 0 &&
dname_valid(w->pkt+12, w->pkt_len-12)) {
char buf[LDNS_MAX_DOMAINLEN+1];
char buf[LDNS_MAX_DOMAINLEN];
dname_str(w->pkt+12, buf);
verbose(VERB_CLIENT, "reuse_move_writewait_away item %s", buf);
}
@@ -2825,7 +2840,7 @@ serviced_perturb_qname(struct ub_randstate* rnd, uint8_t* qbuf, size_t len)
lablen = *d++;
}
if(verbosity >= VERB_ALGO) {
char buf[LDNS_MAX_DOMAINLEN+1];
char buf[LDNS_MAX_DOMAINLEN];
dname_str(qbuf+10, buf);
verbose(VERB_ALGO, "qname perturbed to %s", buf);
}
@@ -3718,7 +3733,8 @@ outnet_comm_point_for_tcp(struct outside_network* outnet,
sldns_buffer* query, int timeout, int ssl, char* host)
{
struct comm_point* cp;
int fd = outnet_get_tcp_fd(to_addr, to_addrlen, outnet->tcp_mss, outnet->ip_dscp);
int fd = outnet_get_tcp_fd(to_addr, to_addrlen, outnet->tcp_mss,
outnet->ip_dscp, ssl);
if(fd == -1) {
return 0;
}
@@ -3793,7 +3809,8 @@ outnet_comm_point_for_http(struct outside_network* outnet,
{
/* cp calls cb with err=NETEVENT_DONE when transfer is done */
struct comm_point* cp;
int fd = outnet_get_tcp_fd(to_addr, to_addrlen, outnet->tcp_mss, outnet->ip_dscp);
int fd = outnet_get_tcp_fd(to_addr, to_addrlen, outnet->tcp_mss,
outnet->ip_dscp, ssl);
if(fd == -1) {
return 0;
}
+4 -2
View File
@@ -743,9 +743,11 @@ void reuse_write_wait_remove(struct reuse_tcp* reuse, struct waiting_tcp* w);
void reuse_write_wait_push_back(struct reuse_tcp* reuse, struct waiting_tcp* w);
/** get TCP file descriptor for address, returns -1 on failure,
* tcp_mss is 0 or maxseg size to set for TCP packets. */
* tcp_mss is 0 or maxseg size to set for TCP packets,
* nodelay (TCP_NODELAY) should be set for TLS connections to speed up the TLS
* handshake.*/
int outnet_get_tcp_fd(struct sockaddr_storage* addr, socklen_t addrlen,
int tcp_mss, int dscp);
int tcp_mss, int dscp, int nodelay);
/**
* Create udp commpoint suitable for sending packets to the destination.
+36 -7
View File
@@ -666,7 +666,7 @@ rpz_insert_local_zones_trigger(struct local_zones* lz, uint8_t* dname,
int newzone = 0;
if(a == RPZ_INVALID_ACTION) {
char str[255+1];
char str[LDNS_MAX_DOMAINLEN];
if(rrtype == LDNS_RR_TYPE_SOA || rrtype == LDNS_RR_TYPE_NS ||
rrtype == LDNS_RR_TYPE_DNAME ||
rrtype == LDNS_RR_TYPE_DNSKEY ||
@@ -739,7 +739,7 @@ rpz_insert_local_zones_trigger(struct local_zones* lz, uint8_t* dname,
static void
rpz_log_dname(char const* msg, uint8_t* dname, size_t dname_len)
{
char buf[LDNS_MAX_DOMAINLEN+1];
char buf[LDNS_MAX_DOMAINLEN];
(void)dname_len;
dname_str(dname, buf);
verbose(VERB_ALGO, "rpz: %s: <%s>", msg, buf);
@@ -1062,7 +1062,7 @@ rpz_insert_response_ip_trigger(struct rpz* r, uint8_t* dname, size_t dnamelen,
if(a == RPZ_INVALID_ACTION ||
rpz_action_to_respip_action(a) == respip_invalid) {
char str[255+1];
char str[LDNS_MAX_DOMAINLEN];
dname_str(dname, str);
verbose(VERB_ALGO, "rpz: respip trigger, %s skipping unsupported action: %s",
str, rpz_action_to_string(a));
@@ -1633,7 +1633,7 @@ log_rpz_apply(char* trigger, uint8_t* dname, struct addr_tree_node* addrnode,
struct comm_reply* repinfo, struct module_qstate* ms, char* log_name)
{
char ip[128], txt[512], portstr[32];
char dnamestr[LDNS_MAX_DOMAINLEN+1];
char dnamestr[LDNS_MAX_DOMAINLEN];
uint16_t port = 0;
if(dname) {
dname_str(dname, dnamestr);
@@ -2427,7 +2427,8 @@ rpz_delegation_point_zone_lookup(struct delegpt* dp, struct local_zones* zones,
match->dname = nameserver->name;
match->dname_len = nameserver->namelen;
if(verbosity >= VERB_ALGO) {
char nm[255+1], zn[255+1];
char nm[LDNS_MAX_DOMAINLEN];
char zn[LDNS_MAX_DOMAINLEN];
dname_str(match->dname, nm);
dname_str(z->name, zn);
if(strcmp(nm, zn) != 0)
@@ -2581,7 +2582,7 @@ struct dns_msg* rpz_callback_from_iterator_cname(struct module_qstate* ms,
}
if(verbosity >= VERB_ALGO) {
char nm[255+1], zn[255+1];
char nm[LDNS_MAX_DOMAINLEN], zn[LDNS_MAX_DOMAINLEN];
dname_str(is->qchase.qname, nm);
dname_str(z->name, zn);
if(strcmp(zn, nm) != 0)
@@ -2758,7 +2759,7 @@ rpz_callback_from_worker_request(struct auth_zones* az, struct module_env* env,
}
if(verbosity >= VERB_ALGO) {
char nm[255+1], zn[255+1];
char nm[LDNS_MAX_DOMAINLEN], zn[LDNS_MAX_DOMAINLEN];
dname_str(qinfo->qname, nm);
dname_str(z->name, zn);
if(strcmp(zn, nm) != 0)
@@ -2791,3 +2792,31 @@ void rpz_disable(struct rpz* r)
return;
r->disabled = 1;
}
/** Get memory usage for clientip_synthesized_rrset. Ignores memory usage
* of locks. */
static size_t
rpz_clientip_synthesized_set_get_mem(struct clientip_synthesized_rrset* set)
{
size_t m = sizeof(*set);
lock_rw_rdlock(&set->lock);
m += regional_get_mem(set->region);
lock_rw_unlock(&set->lock);
return m;
}
size_t rpz_get_mem(struct rpz* r)
{
size_t m = sizeof(*r);
if(r->taglist)
m += r->taglistlen;
if(r->log_name)
m += strlen(r->log_name) + 1;
m += regional_get_mem(r->region);
m += local_zones_get_mem(r->local_zones);
m += local_zones_get_mem(r->nsdname_zones);
m += respip_set_get_mem(r->respip_set);
m += rpz_clientip_synthesized_set_get_mem(r->client_set);
m += rpz_clientip_synthesized_set_get_mem(r->ns_set);
return m;
}
+7
View File
@@ -269,4 +269,11 @@ void rpz_enable(struct rpz* r);
*/
void rpz_disable(struct rpz* r);
/**
* Get memory usage of rpz. Caller must manage locks.
* @param r: RPZ struct.
* @return memory usage.
*/
size_t rpz_get_mem(struct rpz* r);
#endif /* SERVICES_RPZ_H */
+36 -5
View File
@@ -43,6 +43,7 @@
#include "services/view.h"
#include "services/localzone.h"
#include "util/config_file.h"
#include "respip/respip.h"
int
view_cmp(const void* v1, const void* v2)
@@ -66,11 +67,6 @@ views_create(void)
return v;
}
/* \noop (ignore this comment for doxygen)
* This prototype is defined in in respip.h, but we want to avoid
* unnecessary dependencies */
void respip_set_delete(struct respip_set *set);
void
view_delete(struct view* v)
{
@@ -247,3 +243,38 @@ void views_print(struct views* v)
/* TODO implement print */
(void)v;
}
size_t views_get_mem(struct views* vs)
{
struct view* v;
size_t m;
if(!vs) return 0;
m = sizeof(struct views);
lock_rw_rdlock(&vs->lock);
RBTREE_FOR(v, struct view*, &vs->vtree) {
m += view_get_mem(v);
}
lock_rw_unlock(&vs->lock);
return m;
}
size_t view_get_mem(struct view* v)
{
size_t m = sizeof(*v);
lock_rw_rdlock(&v->lock);
m += getmem_str(v->name);
m += local_zones_get_mem(v->local_zones);
m += respip_set_get_mem(v->respip_set);
lock_rw_unlock(&v->lock);
return m;
}
void views_swap_tree(struct views* vs, struct views* data)
{
rbnode_type* oldroot = vs->vtree.root;
size_t oldcount = vs->vtree.count;
vs->vtree.root = data->vtree.root;
vs->vtree.count = data->vtree.count;
data->vtree.root = oldroot;
data->vtree.count = oldcount;
}
+25 -1
View File
@@ -54,7 +54,8 @@ struct respip_set;
* Views storage, shared.
*/
struct views {
/** lock on the view tree */
/** lock on the view tree. When locking order, the views lock
* is before the forwards,hints,anchors,localzones lock. */
lock_rw_type lock;
/** rbtree of struct view */
rbtree_type vtree;
@@ -135,4 +136,27 @@ void views_print(struct views* v);
*/
struct view* views_find_view(struct views* vs, const char* name, int write);
/**
* Calculate memory usage of views.
* @param vs: the views tree. The routine locks and unlocks the structure
* for reading.
* @return memory in bytes.
*/
size_t views_get_mem(struct views* vs);
/**
* Calculate memory usage of view.
* @param v: the view. The routine locks and unlocks the structure for reading.
* @return memory in bytes.
*/
size_t view_get_mem(struct view* v);
/**
* Swap internal tree with preallocated entries. Caller should manage
* the locks.
* @param vs: views tree
* @param data: preallocated information.
*/
void views_swap_tree(struct views* vs, struct views* data);
#endif /* SERVICES_VIEW_H */
+2
View File
@@ -195,6 +195,7 @@ void sldns_key_EVP_unload_gost(void)
}
#endif /* USE_GOST */
#ifdef USE_DSA
/* Retrieve params as BIGNUM from raw buffer */
static int
sldns_key_dsa_buf_bignum(unsigned char* key, size_t len, BIGNUM** p,
@@ -370,6 +371,7 @@ EVP_PKEY *sldns_key_dsa2pkey_raw(unsigned char* key, size_t len)
return evp_key;
#endif
}
#endif /* USE_DSA */
/* Retrieve params as BIGNUM from raw buffer, n is modulus, e is exponent */
static int
+8 -2
View File
@@ -72,7 +72,7 @@ static const sldns_rdf_type type_nsap_wireformat[] = {
LDNS_RDF_TYPE_NSAP
};
static const sldns_rdf_type type_nsap_ptr_wireformat[] = {
LDNS_RDF_TYPE_STR
LDNS_RDF_TYPE_UNQUOTED
};
static const sldns_rdf_type type_sig_wireformat[] = {
LDNS_RDF_TYPE_TYPE, LDNS_RDF_TYPE_ALG, LDNS_RDF_TYPE_INT8, LDNS_RDF_TYPE_INT32,
@@ -86,7 +86,7 @@ static const sldns_rdf_type type_px_wireformat[] = {
LDNS_RDF_TYPE_INT16, LDNS_RDF_TYPE_DNAME, LDNS_RDF_TYPE_DNAME
};
static const sldns_rdf_type type_gpos_wireformat[] = {
LDNS_RDF_TYPE_STR, LDNS_RDF_TYPE_STR, LDNS_RDF_TYPE_STR
LDNS_RDF_TYPE_UNQUOTED, LDNS_RDF_TYPE_UNQUOTED, LDNS_RDF_TYPE_UNQUOTED
};
static const sldns_rdf_type type_aaaa_wireformat[] = { LDNS_RDF_TYPE_AAAA };
static const sldns_rdf_type type_loc_wireformat[] = { LDNS_RDF_TYPE_LOC };
@@ -617,6 +617,12 @@ static sldns_rr_descriptor rdata_field_descriptors[] = {
{(enum sldns_enum_rr_type)0, "TYPE258", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 },
#endif
{(enum sldns_enum_rr_type)0, "TYPE259", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 },
{(enum sldns_enum_rr_type)0, "TYPE260", 1, 1, type_0_wireformat, LDNS_RDF_TYPE_NONE, LDNS_RR_NO_COMPRESS, 0 },
/* 261 */
{LDNS_RR_TYPE_RESINFO, "RESINFO", 1, 0, NULL, LDNS_RDF_TYPE_UNQUOTED, LDNS_RR_NO_COMPRESS, 0 },
/* split in array, no longer contiguous */
#ifdef DRAFT_RRTYPES
+6
View File
@@ -229,6 +229,8 @@ enum sldns_enum_rr_type
LDNS_RR_TYPE_CAA = 257, /* RFC 6844 */
LDNS_RR_TYPE_AVC = 258,
LDNS_RR_TYPE_RESINFO = 261, /* RFC 9606 */
/** DNSSEC Trust Authorities */
LDNS_RR_TYPE_TA = 32768,
/* RFC 4431, 5074, DNSSEC Lookaside Validation */
@@ -341,6 +343,9 @@ enum sldns_enum_rdf_type
/** 8 * 8 bit hex numbers separated by dashes. For EUI64. */
LDNS_RDF_TYPE_EUI64,
/** Character string without quotes. */
LDNS_RDF_TYPE_UNQUOTED,
/** A non-zero sequence of US-ASCII letters and numbers in lower case.
* For CAA.
*/
@@ -438,6 +443,7 @@ enum sldns_enum_edns_option
LDNS_EDNS_PADDING = 12, /* RFC7830 */
LDNS_EDNS_EDE = 15, /* RFC8914 */
LDNS_EDNS_CLIENT_TAG = 16, /* draft-bellis-dnsop-edns-tags-01 */
LDNS_EDNS_REPORT_CHANNEL = 18, /* RFC9567 */
LDNS_EDNS_UNBOUND_CACHEDB_TESTFRAME_TEST = 65534
};
typedef enum sldns_enum_edns_option sldns_edns_option;
+46 -8
View File
@@ -365,7 +365,8 @@ static int
sldns_rdf_type_maybe_quoted(sldns_rdf_type rdf_type)
{
return rdf_type == LDNS_RDF_TYPE_STR ||
rdf_type == LDNS_RDF_TYPE_LONG_STR;
rdf_type == LDNS_RDF_TYPE_LONG_STR ||
rdf_type == LDNS_RDF_TYPE_UNQUOTED;
}
/** see if rdata is quoted */
@@ -1719,6 +1720,8 @@ int sldns_str2wire_rdf_buf(const char* str, uint8_t* rd, size_t* len,
return sldns_str2wire_eui48_buf(str, rd, len);
case LDNS_RDF_TYPE_EUI64:
return sldns_str2wire_eui64_buf(str, rd, len);
case LDNS_RDF_TYPE_UNQUOTED:
return sldns_str2wire_unquoted_buf(str, rd, len);
case LDNS_RDF_TYPE_TAG:
return sldns_str2wire_tag_buf(str, rd, len);
case LDNS_RDF_TYPE_LONG_STR:
@@ -2554,12 +2557,42 @@ int sldns_str2wire_atma_buf(const char* str, uint8_t* rd, size_t* len)
{
const char* s = str;
size_t slen = strlen(str);
size_t dlen = 0; /* number of hexdigits parsed */
size_t dlen = 0; /* number of hexdigits parsed for hex,
digits for E.164 */
/* just a hex string with optional dots? */
/* notimpl e.164 format */
if(slen > LDNS_MAX_RDFLEN*2)
return LDNS_WIREPARSE_ERR_LABEL_OVERFLOW;
if(*len < 1)
return LDNS_WIREPARSE_ERR_BUFFER_TOO_SMALL;
if(*s == 0) {
/* empty string */
rd[0] = 0;
*len = 1;
return LDNS_WIREPARSE_ERR_OK;
}
if(s[0] == '+') {
rd[0] = 1; /* E.164 format */
/* digits '0'..'9', with skipped dots. */
s++;
while(*s) {
if(isspace((unsigned char)*s) || *s == '.') {
s++;
continue;
}
if(*s < '0' || *s > '9')
return RET_ERR(LDNS_WIREPARSE_ERR_SYNTAX, s-str);
if(*len < dlen + 2)
return RET_ERR(LDNS_WIREPARSE_ERR_BUFFER_TOO_SMALL,
s-str);
rd[dlen+1] = *s++;
dlen++;
}
*len = dlen+1;
return LDNS_WIREPARSE_ERR_OK;
}
rd[0] = 0; /* AESA format */
/* hex, with skipped dots. */
while(*s) {
if(isspace((unsigned char)*s) || *s == '.') {
s++;
@@ -2567,17 +2600,17 @@ int sldns_str2wire_atma_buf(const char* str, uint8_t* rd, size_t* len)
}
if(!isxdigit((unsigned char)*s))
return RET_ERR(LDNS_WIREPARSE_ERR_SYNTAX_HEX, s-str);
if(*len < dlen/2 + 1)
if(*len < dlen/2 + 2)
return RET_ERR(LDNS_WIREPARSE_ERR_BUFFER_TOO_SMALL,
s-str);
if((dlen&1)==0)
rd[dlen/2] = (uint8_t)sldns_hexdigit_to_int(*s++) * 16;
else rd[dlen/2] += sldns_hexdigit_to_int(*s++);
rd[dlen/2 + 1] = (uint8_t)sldns_hexdigit_to_int(*s++) * 16;
else rd[dlen/2 + 1] += sldns_hexdigit_to_int(*s++);
dlen++;
}
if((dlen&1)!=0)
return RET_ERR(LDNS_WIREPARSE_ERR_SYNTAX_HEX, s-str);
*len = dlen/2;
*len = dlen/2 + 1;
return LDNS_WIREPARSE_ERR_OK;
}
@@ -2746,6 +2779,11 @@ int sldns_str2wire_eui64_buf(const char* str, uint8_t* rd, size_t* len)
return LDNS_WIREPARSE_ERR_OK;
}
int sldns_str2wire_unquoted_buf(const char* str, uint8_t* rd, size_t* len)
{
return sldns_str2wire_str_buf(str, rd, len);
}
int sldns_str2wire_tag_buf(const char* str, uint8_t* rd, size_t* len)
{
size_t slen = strlen(str);
+9
View File
@@ -551,6 +551,15 @@ int sldns_str2wire_eui48_buf(const char* str, uint8_t* rd, size_t* len);
*/
int sldns_str2wire_eui64_buf(const char* str, uint8_t* rd, size_t* len);
/**
* Convert rdf of type LDNS_RDF_TYPE_UNQUOTED from string to wireformat.
* @param str: the text to convert for this rdata element.
* @param rd: rdata buffer for the wireformat.
* @param len: length of rd buffer on input, used length on output.
* @return 0 on success, error on failure.
*/
int sldns_str2wire_unquoted_buf(const char* str, uint8_t* rd, size_t* len);
/**
* Convert rdf of type LDNS_RDF_TYPE_TAG from string to wireformat.
* @param str: the text to convert for this rdata element.
+49 -1
View File
@@ -1344,6 +1344,8 @@ int sldns_wire2str_rdf_scan(uint8_t** d, size_t* dlen, char** s, size_t* slen,
return sldns_wire2str_eui48_scan(d, dlen, s, slen);
case LDNS_RDF_TYPE_EUI64:
return sldns_wire2str_eui64_scan(d, dlen, s, slen);
case LDNS_RDF_TYPE_UNQUOTED:
return sldns_wire2str_unquoted_scan(d, dlen, s, slen);
case LDNS_RDF_TYPE_TAG:
return sldns_wire2str_tag_scan(d, dlen, s, slen);
case LDNS_RDF_TYPE_LONG_STR:
@@ -1870,7 +1872,33 @@ int sldns_wire2str_nsap_scan(uint8_t** d, size_t* dl, char** s, size_t* sl)
int sldns_wire2str_atma_scan(uint8_t** d, size_t* dl, char** s, size_t* sl)
{
return print_remainder_hex("", d, dl, s, sl);
uint8_t format;
int w = 0;
size_t i;
if(*dl < 1) return -1;
format = (*d)[0];
(*d)+=1;
(*dl)-=1;
if(format == 0) {
/* AESA format (ATM End System Address). */
return print_remainder_hex("", d, dl, s, sl);
} else if(format == 1) {
/* E.164 format. */
w += sldns_str_print(s, sl, "+");
for(i=0; i<*dl; i++) {
if((*d)[i] < '0' || (*d)[0] > '9')
return -1;
w += sldns_str_print(s, sl, "%c", (*d)[i]);
}
(*d) += *dl;
(*dl) = 0;
} else {
/* Unknown format. */
return -1;
}
return w;
}
/* internal scan routine that can modify arguments on failure */
@@ -2021,6 +2049,26 @@ int sldns_wire2str_eui64_scan(uint8_t** d, size_t* dl, char** s, size_t* sl)
return w;
}
int sldns_wire2str_unquoted_scan(uint8_t** d, size_t* dl, char** s, size_t* sl)
{
int w = 0;
size_t i, len;
if(*dl < 1) return -1;
len = **d;
if(*dl < 1+len) return -1;
(*d)++;
(*dl)--;
for(i=0; i<len; i++) {
if(isspace((unsigned char)(*d)[i]) || (*d)[i] == '(' ||
(*d)[i] == ')' || (*d)[i] == '\'')
w += sldns_str_print(s, sl, "\\%c", (char)(*d)[i]);
else w += str_char_print(s, sl, (*d)[i]);
}
(*d)+=len;
(*dl)-=len;
return w;
}
int sldns_wire2str_tag_scan(uint8_t** d, size_t* dl, char** s, size_t* sl)
{
size_t i, n;
+13
View File
@@ -919,6 +919,19 @@ int sldns_wire2str_eui48_scan(uint8_t** data, size_t* data_len, char** str,
int sldns_wire2str_eui64_scan(uint8_t** data, size_t* data_len, char** str,
size_t* str_len);
/**
* Scan wireformat UNQUOTED field to string, with user buffers.
* It shifts the arguments to move along (see sldns_wire2str_pkt_scan).
* @param data: wireformat data.
* @param data_len: length of data buffer.
* @param str: string buffer.
* @param str_len: length of string buffer.
* @return number of characters (except null) needed to print.
* Can return -1 on failure.
*/
int sldns_wire2str_unquoted_scan(uint8_t** data, size_t* data_len, char** str,
size_t* str_len);
/**
* Scan wireformat TAG field to string, with user buffers.
* It shifts the arguments to move along (see sldns_wire2str_pkt_scan).
+7 -5
View File
@@ -342,8 +342,6 @@ interfacechecks(struct config_file* cfg)
int i, j, i2, j2;
char*** resif = NULL;
int* num_resif = NULL;
char portbuf[32];
snprintf(portbuf, sizeof(portbuf), "%d", cfg->port);
if(cfg->num_ifs != 0) {
resif = (char***)calloc(cfg->num_ifs, sizeof(char**));
@@ -366,15 +364,19 @@ interfacechecks(struct config_file* cfg)
cfg->ifs[i]);
}
/* check for port combinations that are not supported */
if(if_is_pp2(resif[i][0], portbuf, cfg->proxy_protocol_port)) {
if(if_is_dnscrypt(resif[i][0], portbuf,
if(if_is_pp2(resif[i][0], cfg->port, cfg->proxy_protocol_port)) {
if(if_is_dnscrypt(resif[i][0], cfg->port,
cfg->dnscrypt_port)) {
fatal_exit("PROXYv2 and DNSCrypt combination not "
"supported!");
} else if(if_is_https(resif[i][0], portbuf,
} else if(if_is_https(resif[i][0], cfg->port,
cfg->https_port)) {
fatal_exit("PROXYv2 and DoH combination not "
"supported!");
} else if(if_is_quic(resif[i][0], cfg->port,
cfg->quic_port)) {
fatal_exit("PROXYv2 and DoQ combination not "
"supported!");
}
}
/* search for duplicates in the returned addresses */
@@ -204,7 +204,8 @@ fi
# remove unused permissions
chmod o-rw \
"$SVR_BASE.pem" \
"$SVR_BASE.key" \
"$SVR_BASE.key"
chmod g+r,o-rw \
"$CTL_BASE.pem" \
"$CTL_BASE.key"
+19 -5
View File
@@ -109,6 +109,16 @@ usage(void)
printf(" That means the caches sizes and\n");
printf(" the number of threads must not\n");
printf(" change between reloads.\n");
printf(" fast_reload [+dpv] reloads the server but only briefly stops\n");
printf(" server processing, keeps cache, and changes\n");
printf(" most options; check unbound-control(8).\n");
printf(" +d drops running queries to keep consistency\n");
printf(" on changed options while reloading.\n");
printf(" +p does not pause threads for even faster\n");
printf(" reload but less options are supported\n");
printf(" ; check unbound-control(8).\n");
printf(" +v verbose output, it will include duration needed.\n");
printf(" +vv more verbose output, it will include memory needed.\n");
printf(" stats print statistics\n");
printf(" stats_noreset peek at statistics\n");
#ifdef HAVE_SHMGET
@@ -222,6 +232,9 @@ static void pr_stats(const char* nm, struct ub_stats_info* s)
s->svr.num_queries_cookie_client);
PR_UL_NM("num.queries_cookie_invalid",
s->svr.num_queries_cookie_invalid);
PR_UL_NM("num.queries_discard_timeout",
s->svr.num_queries_discard_timeout);
PR_UL_NM("num.queries_wait_limit", s->svr.num_queries_wait_limit);
PR_UL_NM("num.cachehits",
s->svr.num_queries - s->svr.num_queries_missed_cache);
PR_UL_NM("num.cachemiss", s->svr.num_queries_missed_cache);
@@ -231,12 +244,13 @@ static void pr_stats(const char* nm, struct ub_stats_info* s)
PR_UL_NM("num.expired", s->svr.ans_expired);
PR_UL_NM("num.recursivereplies", s->mesh_replies_sent);
#ifdef USE_DNSCRYPT
PR_UL_NM("num.dnscrypt.crypted", s->svr.num_query_dnscrypt_crypted);
PR_UL_NM("num.dnscrypt.cert", s->svr.num_query_dnscrypt_cert);
PR_UL_NM("num.dnscrypt.cleartext", s->svr.num_query_dnscrypt_cleartext);
PR_UL_NM("num.dnscrypt.malformed",
s->svr.num_query_dnscrypt_crypted_malformed);
PR_UL_NM("num.dnscrypt.crypted", s->svr.num_query_dnscrypt_crypted);
PR_UL_NM("num.dnscrypt.cert", s->svr.num_query_dnscrypt_cert);
PR_UL_NM("num.dnscrypt.cleartext", s->svr.num_query_dnscrypt_cleartext);
PR_UL_NM("num.dnscrypt.malformed",
s->svr.num_query_dnscrypt_crypted_malformed);
#endif /* USE_DNSCRYPT */
PR_UL_NM("num.dns_error_reports", s->svr.num_dns_error_reports);
printf("%s.requestlist.avg"SQ"%g\n", nm,
(s->svr.num_queries_missed_cache+s->svr.num_queries_prefetch)?
(double)s->svr.sum_query_list_size/
+14
View File
@@ -256,6 +256,20 @@ void dtio_mainfdcallback(int ATTR_UNUSED(fd), short ATTR_UNUSED(ev),
}
#endif
void fast_reload_service_cb(int ATTR_UNUSED(fd), short ATTR_UNUSED(ev),
void* ATTR_UNUSED(arg))
{
log_assert(0);
}
int fast_reload_client_callback(struct comm_point* ATTR_UNUSED(c),
void* ATTR_UNUSED(arg), int ATTR_UNUSED(error),
struct comm_reply* ATTR_UNUSED(repinfo))
{
log_assert(0);
return 0;
}
#ifdef HAVE_NGTCP2
void doq_client_event_cb(int ATTR_UNUSED(fd), short ATTR_UNUSED(ev),
void* ATTR_UNUSED(arg))
+916
View File
@@ -0,0 +1,916 @@
/**
* testcode/checklocks.c - wrapper on locks that checks access.
*
* Copyright (c) 2007, NLnet Labs. All rights reserved.
*
* This software is open source.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* Neither the name of the NLNET LABS nor the names of its contributors may
* be used to endorse or promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
* TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "config.h"
#include <signal.h>
#include "util/locks.h" /* include before checklocks.h */
#include "testcode/checklocks.h"
/**
* \file
* Locks that are checked.
*
* Ugly hack: uses the fact that workers start with an int thread_num, and
* are passed to thread_create to make the thread numbers here the same as
* those used for logging which is nice.
*
* Todo:
* - debug status print, of thread lock stacks, and current waiting.
*/
#ifdef USE_THREAD_DEBUG
/** How long to wait before lock attempt is a failure. */
#define CHECK_LOCK_TIMEOUT 120 /* seconds */
/** How long to wait before join attempt is a failure. */
#define CHECK_JOIN_TIMEOUT 120 /* seconds */
/** if key has been created */
static int key_created = 0;
/** if the key was deleted, i.e. we have quit */
static int key_deleted = 0;
/** we hide the thread debug info with this key. */
static ub_thread_key_type thr_debug_key;
/** the list of threads, so all threads can be examined. NULL if unused. */
static struct thr_check* thread_infos[THRDEBUG_MAX_THREADS];
/** stored maximum lock number for threads, when a thread is restarted the
* number is kept track of, because the new locks get new id numbers. */
static int thread_lockcount[THRDEBUG_MAX_THREADS];
/** do we check locking order */
int check_locking_order = 1;
/** the pid of this runset, reasonably unique. */
static pid_t check_lock_pid;
/** the name of the output file */
static const char* output_name = "ublocktrace";
/**
* Should checklocks print a trace of the lock and unlock calls.
* It uses fprintf for that because the log function uses a lock and that
* would loop otherwise.
*/
static int verbose_locking = 0;
/**
* Assume lock 0 0 (create_thread, create_instance), is the log lock and
* do not print for that. Otherwise the output is full of log lock accesses.
*/
static int verbose_locking_not_loglock = 1;
/** print all possible debug info on the state of the system */
static void total_debug_info(void);
/** print pretty lock error and exit (decl for NORETURN attribute) */
static void lock_error(struct checked_lock* lock, const char* func,
const char* file, int line, const char* err) ATTR_NORETURN;
/** print pretty lock error and exit */
static void lock_error(struct checked_lock* lock,
const char* func, const char* file, int line, const char* err)
{
log_err("lock error (description follows)");
log_err("Created at %s %s:%d", lock->create_func,
lock->create_file, lock->create_line);
if(lock->holder_func && lock->holder_file)
log_err("Previously %s %s:%d", lock->holder_func,
lock->holder_file, lock->holder_line);
log_err("At %s %s:%d", func, file, line);
log_err("Error for %s lock: %s",
(lock->type==check_lock_mutex)?"mutex": (
(lock->type==check_lock_spinlock)?"spinlock": (
(lock->type==check_lock_rwlock)?"rwlock": "badtype")), err);
log_err("complete status display:");
total_debug_info();
fatal_exit("bailing out");
}
/**
* Obtain lock on debug lock structure. This could be a deadlock by the caller.
* The debug code itself does not deadlock. Anyway, check with timeouts.
* @param lock: on what to acquire lock.
* @param func: user level caller identification.
* @param file: user level caller identification.
* @param line: user level caller identification.
*/
static void
acquire_locklock(struct checked_lock* lock,
const char* func, const char* file, int line)
{
struct timespec to;
int err;
int contend = 0;
/* first try; inc contention counter if not immediately */
if((err = pthread_mutex_trylock(&lock->lock))) {
if(err==EBUSY)
contend++;
else fatal_exit("error in mutex_trylock: %s", strerror(err));
}
if(!err)
return; /* immediate success */
to.tv_sec = time(NULL) + CHECK_LOCK_TIMEOUT;
to.tv_nsec = 0;
err = pthread_mutex_timedlock(&lock->lock, &to);
if(err) {
log_err("in acquiring locklock: %s", strerror(err));
lock_error(lock, func, file, line, "acquire locklock");
}
/* since we hold the lock, we can edit the contention_count */
lock->contention_count += contend;
}
/** add protected region */
void
lock_protect_place(void* p, void* area, size_t size, const char* def_func,
const char* def_file, int def_line, const char* def_area)
{
struct checked_lock* lock = *(struct checked_lock**)p;
struct protected_area* e = (struct protected_area*)malloc(
sizeof(struct protected_area));
if(!e)
fatal_exit("lock_protect: out of memory");
e->region = area;
e->size = size;
e->def_func = def_func;
e->def_file = def_file;
e->def_line = def_line;
e->def_area = def_area;
e->hold = malloc(size);
if(!e->hold)
fatal_exit("lock_protect: out of memory");
memcpy(e->hold, e->region, e->size);
acquire_locklock(lock, __func__, __FILE__, __LINE__);
e->next = lock->prot;
lock->prot = e;
LOCKRET(pthread_mutex_unlock(&lock->lock));
}
/** remove protected region */
void
lock_unprotect(void* mangled, void* area)
{
struct checked_lock* lock = *(struct checked_lock**)mangled;
struct protected_area* p, **prevp;
if(!lock)
return;
acquire_locklock(lock, __func__, __FILE__, __LINE__);
p = lock->prot;
prevp = &lock->prot;
while(p) {
if(p->region == area) {
*prevp = p->next;
free(p->hold);
free(p);
LOCKRET(pthread_mutex_unlock(&lock->lock));
return;
}
prevp = &p->next;
p = p->next;
}
LOCKRET(pthread_mutex_unlock(&lock->lock));
}
/**
* Check protected memory region. Memory compare. Exit on error.
* @param lock: which lock to check.
* @param func: location we are now (when failure is detected).
* @param file: location we are now (when failure is detected).
* @param line: location we are now (when failure is detected).
*/
static void
prot_check(struct checked_lock* lock,
const char* func, const char* file, int line)
{
struct protected_area* p = lock->prot;
while(p) {
if(memcmp(p->hold, p->region, p->size) != 0) {
log_hex("memory prev", p->hold, p->size);
log_hex("memory here", p->region, p->size);
log_err("lock_protect on %s %s:%d %s failed",
p->def_func, p->def_file, p->def_line,
p->def_area);
lock_error(lock, func, file, line,
"protected area modified");
}
p = p->next;
}
}
/** Copy protected memory region */
static void
prot_store(struct checked_lock* lock)
{
struct protected_area* p = lock->prot;
while(p) {
memcpy(p->hold, p->region, p->size);
p = p->next;
}
}
/** get memory held by lock */
size_t
lock_get_mem(void* pp)
{
size_t s;
struct checked_lock* lock = *(struct checked_lock**)pp;
struct protected_area* p;
s = sizeof(struct checked_lock);
acquire_locklock(lock, __func__, __FILE__, __LINE__);
for(p = lock->prot; p; p = p->next) {
s += sizeof(struct protected_area);
s += p->size;
}
LOCKRET(pthread_mutex_unlock(&lock->lock));
return s;
}
/** write lock trace info to file, while you hold those locks */
static void
ordercheck_locklock(struct thr_check* thr, struct checked_lock* lock)
{
int info[4];
if(!check_locking_order) return;
if(!thr->holding_first) return; /* no older lock, no info */
/* write: <lock id held> <lock id new> <file> <line> */
info[0] = thr->holding_first->create_thread;
info[1] = thr->holding_first->create_instance;
info[2] = lock->create_thread;
info[3] = lock->create_instance;
if(fwrite(info, 4*sizeof(int), 1, thr->order_info) != 1 ||
fwrite(lock->holder_file, strlen(lock->holder_file)+1, 1,
thr->order_info) != 1 ||
fwrite(&lock->holder_line, sizeof(int), 1,
thr->order_info) != 1)
log_err("fwrite: %s", strerror(errno));
}
/** write ordercheck lock creation details to file */
static void
ordercheck_lockcreate(struct thr_check* thr, struct checked_lock* lock)
{
/* write: <ffff = create> <lock id> <file> <line> */
int cmd = -1;
if(!check_locking_order) return;
if( fwrite(&cmd, sizeof(int), 1, thr->order_info) != 1 ||
fwrite(&lock->create_thread, sizeof(int), 1,
thr->order_info) != 1 ||
fwrite(&lock->create_instance, sizeof(int), 1,
thr->order_info) != 1 ||
fwrite(lock->create_file, strlen(lock->create_file)+1, 1,
thr->order_info) != 1 ||
fwrite(&lock->create_line, sizeof(int), 1,
thr->order_info) != 1)
log_err("fwrite: %s", strerror(errno));
}
/** alloc struct, init lock empty */
void
checklock_init(enum check_lock_type type, struct checked_lock** lock,
const char* func, const char* file, int line)
{
struct checked_lock* e = (struct checked_lock*)calloc(1,
sizeof(struct checked_lock));
struct thr_check *thr = (struct thr_check*)pthread_getspecific(
thr_debug_key);
if(!e)
fatal_exit("%s %s %d: out of memory", func, file, line);
if(!thr) {
/* this is called when log_init() calls lock_init()
* functions, and the test check code has not yet
* been initialised. But luckily, the checklock_start()
* routine can be called multiple times without ill effect.
*/
checklock_start();
thr = (struct thr_check*)pthread_getspecific(thr_debug_key);
}
if(!thr)
fatal_exit("%s %s %d: lock_init no thread info", func, file,
line);
*lock = e;
e->type = type;
e->create_func = func;
e->create_file = file;
e->create_line = line;
e->create_thread = thr->num;
e->create_instance = thr->locks_created++;
ordercheck_lockcreate(thr, e);
LOCKRET(pthread_mutex_init(&e->lock, NULL));
switch(e->type) {
case check_lock_mutex:
LOCKRET(pthread_mutex_init(&e->u.mutex, NULL));
break;
case check_lock_spinlock:
LOCKRET(pthread_spin_init(&e->u.spinlock, PTHREAD_PROCESS_PRIVATE));
break;
case check_lock_rwlock:
LOCKRET(pthread_rwlock_init(&e->u.rwlock, NULL));
break;
default:
log_assert(0);
}
}
/** delete prot items */
static void
prot_clear(struct checked_lock* lock)
{
struct protected_area* p=lock->prot, *np;
while(p) {
np = p->next;
free(p->hold);
free(p);
p = np;
}
}
/** check if type is OK for the lock given */
static void
checktype(enum check_lock_type type, struct checked_lock* lock,
const char* func, const char* file, int line)
{
if(!lock)
fatal_exit("use of null/deleted lock at %s %s:%d",
func, file, line);
if(type != lock->type) {
lock_error(lock, func, file, line, "wrong lock type");
}
}
/** check if OK, free struct */
void
checklock_destroy(enum check_lock_type type, struct checked_lock** lock,
const char* func, const char* file, int line)
{
const size_t contention_interest = 1; /* promille contented locks */
struct checked_lock* e;
if(!lock)
return;
e = *lock;
if(!e)
return;
checktype(type, e, func, file, line);
/* check if delete is OK */
acquire_locklock(e, func, file, line);
if(e->hold_count != 0)
lock_error(e, func, file, line, "delete while locked.");
if(e->wait_count != 0)
lock_error(e, func, file, line, "delete while waited on.");
prot_check(e, func, file, line);
*lock = NULL; /* use after free will fail */
LOCKRET(pthread_mutex_unlock(&e->lock));
/* contention, look at fraction in trouble. */
if(e->history_count > 1 &&
1000*e->contention_count/e->history_count > contention_interest) {
log_info("lock created %s %s %d has contention %u of %u (%d%%)",
e->create_func, e->create_file, e->create_line,
(unsigned int)e->contention_count,
(unsigned int)e->history_count,
(int)(100*e->contention_count/e->history_count));
}
/* delete it */
LOCKRET(pthread_mutex_destroy(&e->lock));
prot_clear(e);
/* since nobody holds the lock - see check above, no need to unlink
* from the thread-held locks list. */
switch(e->type) {
case check_lock_mutex:
LOCKRET(pthread_mutex_destroy(&e->u.mutex));
break;
case check_lock_spinlock:
LOCKRET(pthread_spin_destroy(&e->u.spinlock));
break;
case check_lock_rwlock:
LOCKRET(pthread_rwlock_destroy(&e->u.rwlock));
break;
default:
log_assert(0);
}
memset(e, 0, sizeof(struct checked_lock));
free(e);
}
/** finish acquiring lock, shared between _(rd|wr||)lock() routines */
static void
finish_acquire_lock(struct thr_check* thr, struct checked_lock* lock,
const char* func, const char* file, int line)
{
thr->waiting = NULL;
lock->wait_count --;
lock->holder = thr;
lock->hold_count ++;
lock->holder_func = func;
lock->holder_file = file;
lock->holder_line = line;
ordercheck_locklock(thr, lock);
/* insert in thread lock list, as first */
lock->prev_held_lock[thr->num] = NULL;
lock->next_held_lock[thr->num] = thr->holding_first;
if(thr->holding_first)
/* no need to lock it, since this thread already holds the
* lock (since it is on this list) and we only edit thr->num
* member in array. So it is safe. */
thr->holding_first->prev_held_lock[thr->num] = lock;
else thr->holding_last = lock;
thr->holding_first = lock;
}
/**
* Locking routine.
* @param type: as passed by user.
* @param lock: as passed by user.
* @param func: caller location.
* @param file: caller location.
* @param line: caller location.
* @param tryfunc: the pthread_mutex_trylock or similar function.
* @param timedfunc: the pthread_mutex_timedlock or similar function.
* Uses absolute timeout value.
* @param arg: what to pass to tryfunc and timedlock.
* @param exclusive: if lock must be exclusive (only one allowed).
* @param getwr: if attempts to get writelock (or readlock) for rwlocks.
*/
static void
checklock_lockit(enum check_lock_type type, struct checked_lock* lock,
const char* func, const char* file, int line,
int (*tryfunc)(void*), int (*timedfunc)(void*, struct timespec*),
void* arg, int exclusive, int getwr)
{
int err;
int contend = 0;
struct thr_check *thr = (struct thr_check*)pthread_getspecific(
thr_debug_key);
checktype(type, lock, func, file, line);
if(!thr) lock_error(lock, func, file, line, "no thread info");
acquire_locklock(lock, func, file, line);
lock->wait_count ++;
thr->waiting = lock;
if(exclusive && lock->hold_count > 0 && lock->holder == thr)
lock_error(lock, func, file, line, "thread already owns lock");
if(type==check_lock_rwlock && getwr && lock->writeholder == thr)
lock_error(lock, func, file, line, "thread already has wrlock");
LOCKRET(pthread_mutex_unlock(&lock->lock));
/* first try; if busy increase contention counter */
if((err=tryfunc(arg))) {
struct timespec to;
if(err != EBUSY) log_err("trylock: %s", strerror(err));
to.tv_sec = time(NULL) + CHECK_LOCK_TIMEOUT;
to.tv_nsec = 0;
if((err=timedfunc(arg, &to))) {
if(err == ETIMEDOUT)
lock_error(lock, func, file, line,
"timeout possible deadlock");
log_err("timedlock: %s", strerror(err));
}
contend ++;
}
/* got the lock */
acquire_locklock(lock, func, file, line);
lock->contention_count += contend;
lock->history_count++;
if(exclusive && lock->hold_count > 0)
lock_error(lock, func, file, line, "got nonexclusive lock");
if(type==check_lock_rwlock && getwr && lock->writeholder)
lock_error(lock, func, file, line, "got nonexclusive wrlock");
if(type==check_lock_rwlock && getwr)
lock->writeholder = thr;
/* check the memory areas for unauthorized changes,
* between last unlock time and current lock time.
* we check while holding the lock (threadsafe).
*/
if(getwr || exclusive)
prot_check(lock, func, file, line);
finish_acquire_lock(thr, lock, func, file, line);
LOCKRET(pthread_mutex_unlock(&lock->lock));
}
/** helper for rdlock: try */
static int try_rd(void* arg)
{ return pthread_rwlock_tryrdlock((pthread_rwlock_t*)arg); }
/** helper for rdlock: timed */
static int timed_rd(void* arg, struct timespec* to)
{ return pthread_rwlock_timedrdlock((pthread_rwlock_t*)arg, to); }
/** check if OK, lock */
void
checklock_rdlock(enum check_lock_type type, struct checked_lock* lock,
const char* func, const char* file, int line)
{
if(key_deleted)
return;
if(verbose_locking && !(verbose_locking_not_loglock &&
lock->create_thread == 0 && lock->create_instance == 0))
fprintf(stderr, "checklock_rdlock lock %d %d %s:%d at %s:%d\n", lock->create_thread, lock->create_instance, lock->create_file, lock->create_line, file, line);
log_assert(type == check_lock_rwlock);
checklock_lockit(type, lock, func, file, line,
try_rd, timed_rd, &lock->u.rwlock, 0, 0);
}
/** helper for wrlock: try */
static int try_wr(void* arg)
{ return pthread_rwlock_trywrlock((pthread_rwlock_t*)arg); }
/** helper for wrlock: timed */
static int timed_wr(void* arg, struct timespec* to)
{ return pthread_rwlock_timedwrlock((pthread_rwlock_t*)arg, to); }
/** check if OK, lock */
void
checklock_wrlock(enum check_lock_type type, struct checked_lock* lock,
const char* func, const char* file, int line)
{
if(key_deleted)
return;
log_assert(type == check_lock_rwlock);
if(verbose_locking && !(verbose_locking_not_loglock &&
lock->create_thread == 0 && lock->create_instance == 0))
fprintf(stderr, "checklock_wrlock lock %d %d %s:%d at %s:%d\n", lock->create_thread, lock->create_instance, lock->create_file, lock->create_line, file, line);
checklock_lockit(type, lock, func, file, line,
try_wr, timed_wr, &lock->u.rwlock, 0, 1);
}
/** helper for lock mutex: try */
static int try_mutex(void* arg)
{ return pthread_mutex_trylock((pthread_mutex_t*)arg); }
/** helper for lock mutex: timed */
static int timed_mutex(void* arg, struct timespec* to)
{ return pthread_mutex_timedlock((pthread_mutex_t*)arg, to); }
/** helper for lock spinlock: try */
static int try_spinlock(void* arg)
{ return pthread_spin_trylock((pthread_spinlock_t*)arg); }
/** helper for lock spinlock: timed */
static int timed_spinlock(void* arg, struct timespec* to)
{
int err;
/* spin for 5 seconds. (ouch for the CPU, but it beats forever) */
while( (err=try_spinlock(arg)) == EBUSY) {
#ifndef S_SPLINT_S
if(time(NULL) >= to->tv_sec)
return ETIMEDOUT;
usleep(1000); /* in 1/1000000s of a second */
#endif
}
return err;
}
/** check if OK, lock */
void
checklock_lock(enum check_lock_type type, struct checked_lock* lock,
const char* func, const char* file, int line)
{
if(key_deleted)
return;
log_assert(type != check_lock_rwlock);
if(verbose_locking && !(verbose_locking_not_loglock &&
lock->create_thread == 0 && lock->create_instance == 0))
fprintf(stderr, "checklock_lock lock %d %d %s:%d at %s:%d\n", lock->create_thread, lock->create_instance, lock->create_file, lock->create_line, file, line);
switch(type) {
case check_lock_mutex:
checklock_lockit(type, lock, func, file, line,
try_mutex, timed_mutex, &lock->u.mutex, 1, 0);
break;
case check_lock_spinlock:
/* void* cast needed because 'volatile' on some OS */
checklock_lockit(type, lock, func, file, line,
try_spinlock, timed_spinlock,
(void*)&lock->u.spinlock, 1, 0);
break;
default:
log_assert(0);
}
}
/** check if OK, unlock */
void
checklock_unlock(enum check_lock_type type, struct checked_lock* lock,
const char* func, const char* file, int line)
{
struct thr_check *thr;
if(key_deleted)
return;
thr = (struct thr_check*)pthread_getspecific(thr_debug_key);
checktype(type, lock, func, file, line);
if(!thr) lock_error(lock, func, file, line, "no thread info");
acquire_locklock(lock, func, file, line);
/* was this thread even holding this lock? */
if(thr->holding_first != lock &&
lock->prev_held_lock[thr->num] == NULL) {
lock_error(lock, func, file, line, "unlock nonlocked lock");
}
if(lock->hold_count <= 0)
lock_error(lock, func, file, line, "too many unlocks");
if(verbose_locking && !(verbose_locking_not_loglock &&
lock->create_thread == 0 && lock->create_instance == 0))
fprintf(stderr, "checklock_unlock lock %d %d %s:%d at %s:%d\n", lock->create_thread, lock->create_instance, lock->create_file, lock->create_line, file, line);
/* store this point as last touched by */
lock->holder = thr;
lock->hold_count --;
lock->holder_func = func;
lock->holder_file = file;
lock->holder_line = line;
/* delete from thread holder list */
/* no need to lock other lockstructs, because they are all on the
* held-locks list, and this thread holds their locks.
* we only touch the thr->num members, so it is safe. */
if(thr->holding_first == lock)
thr->holding_first = lock->next_held_lock[thr->num];
if(thr->holding_last == lock)
thr->holding_last = lock->prev_held_lock[thr->num];
if(lock->next_held_lock[thr->num])
lock->next_held_lock[thr->num]->prev_held_lock[thr->num] =
lock->prev_held_lock[thr->num];
if(lock->prev_held_lock[thr->num])
lock->prev_held_lock[thr->num]->next_held_lock[thr->num] =
lock->next_held_lock[thr->num];
lock->next_held_lock[thr->num] = NULL;
lock->prev_held_lock[thr->num] = NULL;
if(type==check_lock_rwlock && lock->writeholder == thr) {
lock->writeholder = NULL;
prot_store(lock);
} else if(type != check_lock_rwlock) {
/* store memory areas that are protected, for later checks */
prot_store(lock);
}
LOCKRET(pthread_mutex_unlock(&lock->lock));
/* unlock it */
switch(type) {
case check_lock_mutex:
LOCKRET(pthread_mutex_unlock(&lock->u.mutex));
break;
case check_lock_spinlock:
LOCKRET(pthread_spin_unlock(&lock->u.spinlock));
break;
case check_lock_rwlock:
LOCKRET(pthread_rwlock_unlock(&lock->u.rwlock));
break;
default:
log_assert(0);
}
}
void
checklock_set_output_name(const char* name)
{
output_name = name;
}
/** open order info debug file, thr->num must be valid */
static void
open_lockorder(struct thr_check* thr)
{
char buf[24];
time_t t;
snprintf(buf, sizeof(buf), "%s.%d", output_name, thr->num);
thr->locks_created = thread_lockcount[thr->num];
if(thr->locks_created == 0) {
thr->order_info = fopen(buf, "w");
if(!thr->order_info)
fatal_exit("could not open %s: %s", buf, strerror(errno));
} else {
/* There is already a file to append on with the previous
* thread information. */
thr->order_info = fopen(buf, "a");
if(!thr->order_info)
fatal_exit("could not open for append %s: %s", buf, strerror(errno));
return;
}
t = time(NULL);
/* write: <time_stamp> <runpid> <thread_num> */
if(fwrite(&t, sizeof(t), 1, thr->order_info) != 1 ||
fwrite(&thr->num, sizeof(thr->num), 1, thr->order_info) != 1 ||
fwrite(&check_lock_pid, sizeof(check_lock_pid), 1,
thr->order_info) != 1)
log_err("fwrite: %s", strerror(errno));
}
/** checklock thread main, Inits thread structure */
static void* checklock_main(void* arg)
{
struct thr_check* thr = (struct thr_check*)arg;
void* ret;
thr->id = pthread_self();
/* Hack to get same numbers as in log file */
thr->num = *(int*)(thr->arg);
log_assert(thr->num < THRDEBUG_MAX_THREADS);
/* as an aside, due to this, won't work for libunbound bg thread */
if(thread_infos[thr->num] != NULL)
log_warn("thread warning, thr->num %d not NULL", thr->num);
thread_infos[thr->num] = thr;
LOCKRET(pthread_setspecific(thr_debug_key, thr));
if(check_locking_order)
open_lockorder(thr);
ret = thr->func(thr->arg);
thread_lockcount[thr->num] = thr->locks_created;
thread_infos[thr->num] = NULL;
if(check_locking_order)
fclose(thr->order_info);
free(thr);
return ret;
}
/** init the main thread */
void checklock_start(void)
{
if(key_deleted)
return;
if(!key_created) {
struct thr_check* thisthr = (struct thr_check*)calloc(1,
sizeof(struct thr_check));
if(!thisthr)
fatal_exit("thrcreate: out of memory");
key_created = 1;
check_lock_pid = getpid();
LOCKRET(pthread_key_create(&thr_debug_key, NULL));
LOCKRET(pthread_setspecific(thr_debug_key, thisthr));
thread_infos[0] = thisthr;
if(check_locking_order)
open_lockorder(thisthr);
}
}
/** stop checklocks */
void checklock_stop(void)
{
if(key_created) {
int i;
key_deleted = 1;
if(check_locking_order)
fclose(thread_infos[0]->order_info);
free(thread_infos[0]);
thread_infos[0] = NULL;
for(i = 0; i < THRDEBUG_MAX_THREADS; i++)
log_assert(thread_infos[i] == NULL);
/* should have been cleaned up. */
LOCKRET(pthread_key_delete(thr_debug_key));
key_created = 0;
}
}
/** allocate debug info and create thread */
void
checklock_thrcreate(pthread_t* id, void* (*func)(void*), void* arg)
{
struct thr_check* thr = (struct thr_check*)calloc(1,
sizeof(struct thr_check));
if(!thr)
fatal_exit("thrcreate: out of memory");
if(!key_created) {
checklock_start();
}
thr->func = func;
thr->arg = arg;
LOCKRET(pthread_create(id, NULL, checklock_main, thr));
}
/** count number of thread infos */
static int
count_thread_infos(void)
{
int cnt = 0;
int i;
for(i=0; i<THRDEBUG_MAX_THREADS; i++)
if(thread_infos[i])
cnt++;
return cnt;
}
/** print lots of info on a lock */
static void
lock_debug_info(struct checked_lock* lock)
{
if(!lock) return;
log_info("+++ Lock %llx, %d %d create %s %s %d",
(unsigned long long)(size_t)lock,
lock->create_thread, lock->create_instance,
lock->create_func, lock->create_file, lock->create_line);
log_info("lock type: %s",
(lock->type==check_lock_mutex)?"mutex": (
(lock->type==check_lock_spinlock)?"spinlock": (
(lock->type==check_lock_rwlock)?"rwlock": "badtype")));
log_info("lock contention %u, history:%u, hold:%d, wait:%d",
(unsigned)lock->contention_count, (unsigned)lock->history_count,
lock->hold_count, lock->wait_count);
log_info("last touch %s %s %d", lock->holder_func, lock->holder_file,
lock->holder_line);
log_info("holder thread %d, writeholder thread %d",
lock->holder?lock->holder->num:-1,
lock->writeholder?lock->writeholder->num:-1);
}
/** print debug locks held by a thread */
static void
held_debug_info(struct thr_check* thr, struct checked_lock* lock)
{
if(!lock) return;
lock_debug_info(lock);
held_debug_info(thr, lock->next_held_lock[thr->num]);
}
/** print debug info for a thread */
static void
thread_debug_info(struct thr_check* thr)
{
struct checked_lock* w = NULL;
struct checked_lock* f = NULL;
struct checked_lock* l = NULL;
if(!thr) return;
log_info("pthread id is %x", (int)thr->id);
log_info("thread func is %llx", (unsigned long long)(size_t)thr->func);
log_info("thread arg is %llx (%d)",
(unsigned long long)(size_t)thr->arg,
(thr->arg?*(int*)thr->arg:0));
log_info("thread num is %d", thr->num);
log_info("locks created %d", thr->locks_created);
log_info("open file for lockinfo: %s",
thr->order_info?"yes, flushing":"no");
fflush(thr->order_info);
w = thr->waiting;
f = thr->holding_first;
l = thr->holding_last;
log_info("thread waiting for a lock: %s %llx", w?"yes":"no",
(unsigned long long)(size_t)w);
lock_debug_info(w);
log_info("thread holding first: %s, last: %s", f?"yes":"no",
l?"yes":"no");
held_debug_info(thr, f);
}
static void
total_debug_info(void)
{
int i;
log_info("checklocks: supervising %d threads.",
count_thread_infos());
if(!key_created) {
log_info("No thread debug key created yet");
}
for(i=0; i<THRDEBUG_MAX_THREADS; i++) {
if(thread_infos[i]) {
log_info("*** Thread %d information: ***", i);
thread_debug_info(thread_infos[i]);
}
}
}
/** signal handler for join timeout, Exits */
static RETSIGTYPE joinalarm(int ATTR_UNUSED(sig))
{
log_err("join thread timeout. hangup or deadlock. Info follows.");
total_debug_info();
fatal_exit("join thread timeout. hangup or deadlock.");
}
/** wait for thread with a timeout */
void
checklock_thrjoin(pthread_t thread)
{
/* wait with a timeout */
if(signal(SIGALRM, joinalarm) == SIG_ERR)
fatal_exit("signal(): %s", strerror(errno));
(void)alarm(CHECK_JOIN_TIMEOUT);
LOCKRET(pthread_join(thread, NULL));
(void)alarm(0);
}
#endif /* USE_THREAD_DEBUG */
+76
View File
@@ -0,0 +1,76 @@
#!/usr/bin/env bash
. testdata/common.sh
quiet=0
if test "$1" = "-q"; then
quiet=1
tdirarg="-q"
shift
fi
NEED_SPLINT='00-lint.tdir'
NEED_DOXYGEN='01-doc.tdir'
NEED_XXD='fwd_compress_c00c.tdir fwd_zero.tdir'
NEED_NC='fwd_compress_c00c.tdir fwd_zero.tdir'
NEED_CURL='06-ianaports.tdir root_anchor.tdir'
NEED_WHOAMI='07-confroot.tdir'
NEED_IPV6='fwd_ancil.tdir fwd_tcp_tc6.tdir stub_udp6.tdir edns_cache.tdir'
NEED_NOMINGW='tcp_sigpipe.tdir 07-confroot.tdir 08-host-lib.tdir fwd_ancil.tdir'
NEED_DNSCRYPT_PROXY='dnscrypt_queries.tdir dnscrypt_queries_chacha.tdir'
NEED_UNSHARE='acl_interface.tdir proxy_protocol.tdir'
NEED_REDIS_SERVER='redis_replica.tdir'
# test if dig and ldns-testns are available.
test_tool_avail "dig"
test_tool_avail "ldns-testns"
# test for ipv6, uses streamtcp peculiarity.
if ./streamtcp -f ::1 2>&1 | grep "not supported" >/dev/null 2>&1; then
HAVE_IPV6=no
else
HAVE_IPV6=yes
fi
# test mingw. no signals and so on.
if uname | grep MINGW >/dev/null; then
HAVE_MINGW=yes
else
HAVE_MINGW=no
fi
# stop tests from notifying systemd, if that is compiled in.
export -n NOTIFY_SOCKET
cd testdata;
sh ../testcode/mini_tdir.sh $tdirarg clean
rm -f .perfstats.txt
for test in `ls -d *.tdir`; do
SKIP=0
skip_if_in_list $test "$NEED_SPLINT" "splint"
skip_if_in_list $test "$NEED_DOXYGEN" "doxygen"
skip_if_in_list $test "$NEED_CURL" "curl"
skip_if_in_list $test "$NEED_XXD" "xxd"
skip_if_in_list $test "$NEED_NC" "nc"
skip_if_in_list $test "$NEED_WHOAMI" "whoami"
skip_if_in_list $test "$NEED_DNSCRYPT_PROXY" "dnscrypt-proxy"
skip_if_in_list $test "$NEED_UNSHARE" "unshare"
skip_if_in_list $test "$NEED_REDIS_SERVER" "redis-server"
if echo $NEED_IPV6 | grep $test >/dev/null; then
if test "$HAVE_IPV6" = no; then
SKIP=1;
fi
fi
if echo $NEED_NOMINGW | grep $test >/dev/null; then
if test "$HAVE_MINGW" = yes; then
SKIP=1;
fi
fi
if test $SKIP -eq 0; then
echo $test
sh ../testcode/mini_tdir.sh -a ../.. $tdirarg exe $test
else
echo "skip $test"
fi
done
sh ../testcode/mini_tdir.sh $tdirarg report
cat .perfstats.txt
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+668
View File
@@ -0,0 +1,668 @@
/*
* testcode/testbound.c - test program for unbound.
*
* Copyright (c) 2007, NLnet Labs. All rights reserved.
*
* This software is open source.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* Neither the name of the NLNET LABS nor the names of its contributors may
* be used to endorse or promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
* TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
/**
* \file
* Exits with code 1 on a failure. 0 if all unit tests are successful.
*/
#include "config.h"
#ifdef HAVE_TIME_H
# include <time.h>
#endif
#include <ctype.h>
#include "testcode/testpkts.h"
#include "testcode/replay.h"
#include "testcode/fake_event.h"
#include "daemon/remote.h"
#include "libunbound/worker.h"
#include "daemon/worker.h"
#include "util/config_file.h"
#include "sldns/keyraw.h"
#ifdef UB_ON_WINDOWS
#include "winrc/win_svc.h"
#endif
/** signal that this is a testbound compile */
#define unbound_testbound 1
/** renamed main routine */
int daemon_main(int argc, char* argv[]);
/**
* include the main program from the unbound daemon.
* rename main to daemon_main to call it
*/
#define main daemon_main
#include "daemon/unbound.c"
#undef main
/** maximum line length for lines in the replay file. */
#define MAX_LINE_LEN 1024
/** config files (removed at exit) */
static struct config_strlist* cfgfiles = NULL;
/** give commandline usage for testbound. */
static void
testbound_usage(void)
{
printf("usage: testbound [options]\n");
printf("\ttest the unbound daemon.\n");
printf("-h this help\n");
printf("-p file playback text file\n");
printf("-1 detect SHA1 support (exit code 0 or 1)\n");
printf("-2 detect SHA256 support (exit code 0 or 1)\n");
printf("-g detect GOST support (exit code 0 or 1)\n");
printf("-e detect ECDSA support (exit code 0 or 1)\n");
printf("-c detect CLIENT_SUBNET support (exit code 0 or 1)\n");
printf("-i detect IPSECMOD support (exit code 0 or 1)\n");
printf("-s testbound self-test - unit test of testbound parts.\n");
printf("-o str unbound commandline options separated by spaces.\n");
printf("Version %s\n", PACKAGE_VERSION);
printf("BSD licensed, see LICENSE file in source package.\n");
printf("Report bugs to %s.\n", PACKAGE_BUGREPORT);
}
/** Max number of arguments to pass to unbound. */
#define MAXARG 100
/**
* Add options from string to passed argc. splits on whitespace.
* @param args: the option argument, "-v -p 12345" or so.
* @param pass_argc: ptr to the argc for unbound. Modified.
* @param pass_argv: the argv to pass to unbound. Modified.
*/
static void
add_opts(const char* args, int* pass_argc, char* pass_argv[])
{
const char *p = args, *np;
size_t len;
while(p && isspace((unsigned char)*p))
p++;
while(p && *p) {
/* find location of next string and length of this one */
if((np = strchr(p, ' ')))
len = (size_t)(np-p);
else len = strlen(p);
/* allocate and copy option */
if(*pass_argc >= MAXARG-1)
fatal_exit("too many arguments: '%s'", p);
pass_argv[*pass_argc] = (char*)malloc(len+1);
if(!pass_argv[*pass_argc])
fatal_exit("add_opts: out of memory");
memcpy(pass_argv[*pass_argc], p, len);
pass_argv[*pass_argc][len] = 0;
(*pass_argc)++;
/* go to next option */
p = np;
while(p && isspace((unsigned char)*p))
p++;
}
}
/** pretty print commandline for unbound in this test */
static void
echo_cmdline(int argc, char* argv[])
{
int i;
fprintf(stderr, "testbound is starting:");
for(i=0; i<argc; i++) {
fprintf(stderr, " [%s]", argv[i]);
}
fprintf(stderr, "\n");
}
/** spool temp file name */
static void
spool_temp_file_name(int* lineno, FILE* cfg, char* id)
{
char line[MAX_LINE_LEN];
/* find filename for new file */
while(isspace((unsigned char)*id))
id++;
if(*id == '\0')
fatal_exit("TEMPFILE_NAME must have id, line %d", *lineno);
strip_end_white(id);
fake_temp_file("_temp_", id, line, sizeof(line));
fprintf(cfg, "\"%s\"\n", line);
}
/** spool temp file */
static void
spool_temp_file(FILE* in, int* lineno, char* id)
{
char line[MAX_LINE_LEN];
char* parse;
FILE* spool;
/* find filename for new file */
while(isspace((unsigned char)*id))
id++;
if(*id == '\0')
fatal_exit("TEMPFILE_CONTENTS must have id, line %d", *lineno);
strip_end_white(id);
fake_temp_file("_temp_", id, line, sizeof(line));
/* open file and spool to it */
spool = fopen(line, "w");
if(!spool) fatal_exit("could not open %s: %s", line, strerror(errno));
fprintf(stderr, "testbound is spooling temp file: %s\n", line);
if(!cfg_strlist_insert(&cfgfiles, strdup(line)))
fatal_exit("out of memory");
line[sizeof(line)-1] = 0;
while(fgets(line, MAX_LINE_LEN-1, in)) {
parse = line;
(*lineno)++;
while(isspace((unsigned char)*parse))
parse++;
if(strncmp(parse, "$INCLUDE_TEMPFILE", 17) == 0) {
char l2[MAX_LINE_LEN-30]; /* -30 makes it fit with
a preceding $INCLUDE in the buf line[] */
char* tid = parse+17;
while(isspace((unsigned char)*tid))
tid++;
strip_end_white(tid);
fake_temp_file("_temp_", tid, l2, sizeof(l2));
snprintf(line, sizeof(line), "$INCLUDE %s\n", l2);
}
if(strncmp(parse, "TEMPFILE_END", 12) == 0) {
fclose(spool);
return;
}
fputs(line, spool);
}
fatal_exit("no TEMPFILE_END in input file");
}
/** spool autotrust file */
static void
spool_auto_file(FILE* in, int* lineno, FILE* cfg, char* id)
{
char line[MAX_LINE_LEN];
char* parse;
FILE* spool;
/* find filename for new file */
while(isspace((unsigned char)*id))
id++;
if(*id == '\0')
fatal_exit("AUTROTRUST_FILE must have id, line %d", *lineno);
strip_end_white(id);
fake_temp_file("_auto_", id, line, sizeof(line));
/* add option for the file */
fprintf(cfg, "server: auto-trust-anchor-file: \"%s\"\n", line);
/* open file and spool to it */
spool = fopen(line, "w");
if(!spool) fatal_exit("could not open %s: %s", line, strerror(errno));
fprintf(stderr, "testbound is spooling key file: %s\n", line);
if(!cfg_strlist_insert(&cfgfiles, strdup(line)))
fatal_exit("out of memory");
line[sizeof(line)-1] = 0;
while(fgets(line, MAX_LINE_LEN-1, in)) {
parse = line;
(*lineno)++;
while(isspace((unsigned char)*parse))
parse++;
if(strncmp(parse, "AUTOTRUST_END", 13) == 0) {
fclose(spool);
return;
}
fputs(line, spool);
}
fatal_exit("no AUTOTRUST_END in input file");
}
/** process config elements */
static void
setup_config(FILE* in, int* lineno, int* pass_argc, char* pass_argv[])
{
char configfile[MAX_LINE_LEN];
char line[MAX_LINE_LEN];
char* parse;
FILE* cfg;
fake_temp_file("_cfg", "", configfile, sizeof(configfile));
add_opts("-c", pass_argc, pass_argv);
add_opts(configfile, pass_argc, pass_argv);
cfg = fopen(configfile, "w");
if(!cfg) fatal_exit("could not open %s: %s",
configfile, strerror(errno));
if(!cfg_strlist_insert(&cfgfiles, strdup(configfile)))
fatal_exit("out of memory");
line[sizeof(line)-1] = 0;
/* some basic settings to not pollute the host system */
fprintf(cfg, "server: use-syslog: no\n");
fprintf(cfg, " directory: \"\"\n");
fprintf(cfg, " chroot: \"\"\n");
fprintf(cfg, " username: \"\"\n");
fprintf(cfg, " pidfile: \"\"\n");
fprintf(cfg, " val-log-level: 2\n");
fprintf(cfg, " log-servfail: yes\n");
fprintf(cfg, "remote-control: control-enable: no\n");
while(fgets(line, MAX_LINE_LEN-1, in)) {
parse = line;
(*lineno)++;
while(isspace((unsigned char)*parse))
parse++;
if(!*parse || parse[0] == ';')
continue;
if(strncmp(parse, "COMMANDLINE", 11) == 0) {
parse[strlen(parse)-1] = 0; /* strip off \n */
add_opts(parse+11, pass_argc, pass_argv);
continue;
}
if(strncmp(parse, "AUTOTRUST_FILE", 14) == 0) {
spool_auto_file(in, lineno, cfg, parse+14);
continue;
}
if(strncmp(parse, "TEMPFILE_NAME", 13) == 0) {
spool_temp_file_name(lineno, cfg, parse+13);
continue;
}
if(strncmp(parse, "TEMPFILE_CONTENTS", 17) == 0) {
spool_temp_file(in, lineno, parse+17);
continue;
}
if(strncmp(parse, "CONFIG_END", 10) == 0) {
fclose(cfg);
return;
}
fputs(line, cfg);
}
fatal_exit("No CONFIG_END in input file");
}
/** read playback file */
static struct replay_scenario*
setup_playback(const char* filename, int* pass_argc, char* pass_argv[])
{
struct replay_scenario* scen = NULL;
int lineno = 0;
if(filename) {
FILE *in = fopen(filename, "rb");
if(!in) {
perror(filename);
exit(1);
}
setup_config(in, &lineno, pass_argc, pass_argv);
scen = replay_scenario_read(in, filename, &lineno);
fclose(in);
if(!scen)
fatal_exit("Could not read: %s", filename);
}
else fatal_exit("need a playback file (-p)");
log_info("Scenario: %s", scen->title);
return scen;
}
/** remove config file at exit */
static void remove_configfile(void)
{
struct config_strlist* p;
for(p=cfgfiles; p; p=p->next)
unlink(p->str);
config_delstrlist(cfgfiles);
cfgfiles = NULL;
}
/**
* Main fake event test program. Setup, teardown and report errors.
* @param argc: arg count.
* @param argv: array of commandline arguments.
* @return program failure if test fails.
*/
int
main(int argc, char* argv[])
{
int c, res;
int pass_argc = 0;
char* pass_argv[MAXARG];
char* playback_file = NULL;
int init_optind = optind;
char* init_optarg = optarg;
struct replay_scenario* scen = NULL;
/* we do not want the test to depend on the timezone */
(void)putenv("TZ=UTC");
memset(pass_argv, 0, sizeof(pass_argv));
#ifdef HAVE_SYSTEMD
/* we do not want the test to use systemd daemon startup notification*/
(void)unsetenv("NOTIFY_SOCKET");
#endif /* HAVE_SYSTEMD */
checklock_start();
log_init(NULL, 0, NULL);
/* determine commandline options for the daemon */
pass_argc = 1;
pass_argv[0] = "unbound";
add_opts("-d", &pass_argc, pass_argv);
while( (c=getopt(argc, argv, "12egciho:p:s")) != -1) {
switch(c) {
case 's':
free(pass_argv[1]);
testbound_selftest();
checklock_stop();
if(log_get_lock()) {
lock_basic_destroy((lock_basic_type*)log_get_lock());
}
exit(0);
case '1':
#ifdef USE_SHA1
printf("SHA1 supported\n");
exit(0);
#else
printf("SHA1 not supported\n");
exit(1);
#endif
break;
case '2':
#if (defined(HAVE_EVP_SHA256) || defined(HAVE_NSS) || defined(HAVE_NETTLE)) && defined(USE_SHA2)
printf("SHA256 supported\n");
exit(0);
#else
printf("SHA256 not supported\n");
exit(1);
#endif
break;
case 'e':
#if defined(USE_ECDSA)
printf("ECDSA supported\n");
exit(0);
#else
printf("ECDSA not supported\n");
exit(1);
#endif
break;
case 'g':
#ifdef USE_GOST
if(sldns_key_EVP_load_gost_id()) {
printf("GOST supported\n");
exit(0);
} else {
printf("GOST not supported\n");
exit(1);
}
#else
printf("GOST not supported\n");
exit(1);
#endif
break;
case 'c':
#ifdef CLIENT_SUBNET
printf("CLIENT_SUBNET supported\n");
exit(0);
#else
printf("CLIENT_SUBNET not supported\n");
exit(1);
#endif
break;
case 'i':
#ifdef USE_IPSECMOD
printf("IPSECMOD supported\n");
exit(0);
#else
printf("IPSECMOD not supported\n");
exit(1);
#endif
break;
case 'p':
playback_file = optarg;
break;
case 'o':
add_opts(optarg, &pass_argc, pass_argv);
break;
case '?':
case 'h':
default:
testbound_usage();
exit(1);
}
}
argc -= optind;
/* argv += optind; not using further arguments */
if(argc != 0) {
testbound_usage();
exit(1);
}
log_info("Start of %s testbound program.", PACKAGE_STRING);
if(atexit(&remove_configfile) != 0)
fatal_exit("atexit() failed: %s", strerror(errno));
/* setup test environment */
scen = setup_playback(playback_file, &pass_argc, pass_argv);
/* init fake event backend */
fake_event_init(scen);
pass_argv[pass_argc] = NULL;
echo_cmdline(pass_argc, pass_argv);
/* reset getopt processing */
optind = init_optind;
optarg = init_optarg;
/* run the normal daemon */
res = daemon_main(pass_argc, pass_argv);
fake_event_cleanup();
for(c=1; c<pass_argc; c++)
free(pass_argv[c]);
if(res == 0) {
log_info("Testbound Exit Success\n");
/* remove configfile from here, the atexit() is for when
* there is a crash to remove the tmpdir file.
* This one removes the file while alloc and log locks are
* still valid, and can be logged (for memory calculation),
* it leaves the ptr NULL so the atexit does nothing. */
remove_configfile();
if(log_get_lock()) {
lock_basic_destroy((lock_basic_type*)log_get_lock());
}
#ifdef HAVE_PTHREAD
/* dlopen frees its thread state (dlopen of gost engine) */
pthread_exit(NULL);
#endif
}
return res;
}
/* fake remote control */
struct listen_port* daemon_remote_open_ports(struct config_file*
ATTR_UNUSED(cfg))
{
return NULL;
}
struct daemon_remote* daemon_remote_create(struct config_file* ATTR_UNUSED(cfg))
{
return (struct daemon_remote*)calloc(1, sizeof(struct daemon_remote));
}
void daemon_remote_delete(struct daemon_remote* rc)
{
free(rc);
}
void daemon_remote_clear(struct daemon_remote* ATTR_UNUSED(rc))
{
/* nothing */
}
int daemon_remote_open_accept(struct daemon_remote* ATTR_UNUSED(rc),
struct listen_port* ATTR_UNUSED(ports), struct worker* worker)
{
struct replay_runtime* runtime = (struct replay_runtime*)worker->base;
runtime->daemon = worker->daemon;
return 1;
}
int remote_accept_callback(struct comm_point* ATTR_UNUSED(c),
void* ATTR_UNUSED(arg), int ATTR_UNUSED(error),
struct comm_reply* ATTR_UNUSED(repinfo))
{
log_assert(0);
return 0;
}
int remote_control_callback(struct comm_point* ATTR_UNUSED(c),
void* ATTR_UNUSED(arg), int ATTR_UNUSED(error),
struct comm_reply* ATTR_UNUSED(repinfo))
{
log_assert(0);
return 0;
}
void remote_get_opt_ssl(char* ATTR_UNUSED(str), void* ATTR_UNUSED(arg))
{
log_assert(0);
}
#ifdef UB_ON_WINDOWS
void wsvc_command_option(const char* ATTR_UNUSED(wopt),
const char* ATTR_UNUSED(cfgfile), int ATTR_UNUSED(v),
int ATTR_UNUSED(c))
{
log_assert(0);
}
#endif
#ifdef UB_ON_WINDOWS
void wsvc_setup_worker(struct worker* ATTR_UNUSED(worker))
{
/* do nothing */
}
#endif
#ifdef UB_ON_WINDOWS
void wsvc_desetup_worker(struct worker* ATTR_UNUSED(worker))
{
/* do nothing */
}
#endif
#ifdef UB_ON_WINDOWS
void worker_win_stop_cb(int ATTR_UNUSED(fd), short ATTR_UNUSED(ev),
void* ATTR_UNUSED(arg))
{
log_assert(0);
}
void wsvc_cron_cb(void* ATTR_UNUSED(arg))
{
log_assert(0);
}
#endif /* UB_ON_WINDOWS */
int tcp_connect_errno_needs_log(struct sockaddr* ATTR_UNUSED(addr),
socklen_t ATTR_UNUSED(addrlen))
{
return 1;
}
int squelch_err_ssl_handshake(unsigned long ATTR_UNUSED(err))
{
return 0;
}
void listen_setup_locks(void)
{
/* nothing */
}
void listen_desetup_locks(void)
{
/* nothing */
}
void fast_reload_printq_list_delete(
struct fast_reload_printq* ATTR_UNUSED(list))
{
/* nothing */
}
void fast_reload_worker_pickup_changes(struct worker* ATTR_UNUSED(worker))
{
/* nothing */
}
#ifdef HAVE_NGTCP2
void* quic_sslctx_create(char* ATTR_UNUSED(key), char* ATTR_UNUSED(pem),
char* ATTR_UNUSED(verifypem))
{
return NULL;
}
void comm_point_doq_callback(int ATTR_UNUSED(fd), short ATTR_UNUSED(event),
void* ATTR_UNUSED(arg))
{
/* nothing */
}
int doq_conn_cmp(const void* ATTR_UNUSED(key1), const void* ATTR_UNUSED(key2))
{
return 0;
}
int doq_conid_cmp(const void* ATTR_UNUSED(key1), const void* ATTR_UNUSED(key2))
{
return 0;
}
int doq_timer_cmp(const void* ATTR_UNUSED(key1), const void* ATTR_UNUSED(key2))
{
return 0;
}
int doq_stream_cmp(const void* ATTR_UNUSED(key1), const void* ATTR_UNUSED(key2))
{
return 0;
}
struct doq_table* doq_table_create(struct config_file* ATTR_UNUSED(cfg),
struct ub_randstate* ATTR_UNUSED(rnd))
{
return calloc(1, sizeof(struct doq_table));
}
void doq_table_delete(struct doq_table* table)
{
free(table);
}
void doq_timer_cb(void* ATTR_UNUSED(arg))
{
/* nothing */
}
size_t doq_table_quic_size_get(struct doq_table* ATTR_UNUSED(table))
{
return 0;
}
#endif
File diff suppressed because it is too large Load Diff
+209
View File
@@ -0,0 +1,209 @@
/*
* testcode/unitinfra.c - unit test for infra cache.
*
* Copyright (c) 2025, NLnet Labs. All rights reserved.
*
* This software is open source.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* Neither the name of the NLNET LABS nor the names of its contributors may
* be used to endorse or promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
* TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
/**
* \file
* Tests the infra functionality.
*/
#include "config.h"
#include "testcode/unitmain.h"
#include "iterator/iterator.h"
#include "services/cache/infra.h"
#include "util/config_file.h"
#include "util/net_help.h"
/* lookup and get key and data structs easily */
static struct infra_data* infra_lookup_host(struct infra_cache* infra,
struct sockaddr_storage* addr, socklen_t addrlen, uint8_t* zone,
size_t zonelen, int wr, time_t now, struct infra_key** k)
{
struct infra_data* d;
struct lruhash_entry* e = infra_lookup_nottl(infra, addr, addrlen,
zone, zonelen, wr);
if(!e) return NULL;
d = (struct infra_data*)e->data;
if(d->ttl < now) {
lock_rw_unlock(&e->lock);
return NULL;
}
*k = (struct infra_key*)e->key;
return d;
}
static void test_keep_probing(struct infra_cache* slab,
struct config_file* cfg, struct sockaddr_storage one, socklen_t onelen,
uint8_t* zone, size_t zonelen, time_t *now, int keep_probing,
int rtt_max_timeout)
{
uint8_t edns_lame;
int vs, to, lame, dnsseclame, reclame, probedelay;
struct infra_key* k;
struct infra_data* d;
/* configure */
cfg->infra_cache_max_rtt = rtt_max_timeout;
config_apply_max_rtt(rtt_max_timeout);
slab->infra_keep_probing = keep_probing;
/* expired previous entry */
*now += cfg->host_ttl + 10;
unit_assert( infra_host(slab, &one, onelen, zone, zonelen,
*now, &vs, &edns_lame, &to) );
/* simulate timeouts until the USEFUL_SERVER_TOP_TIMEOUT is reached */
while(to < USEFUL_SERVER_TOP_TIMEOUT) {
unit_assert( infra_rtt_update(slab, &one, onelen, zone, zonelen,
LDNS_RR_TYPE_A, -1, to, *now) );
unit_assert( infra_host(slab, &one, onelen, zone, zonelen,
*now, &vs, &edns_lame, &to) );
unit_assert( vs == 0 && to <= USEFUL_SERVER_TOP_TIMEOUT && edns_lame == 0 );
}
unit_assert( vs == 0 && to == USEFUL_SERVER_TOP_TIMEOUT && edns_lame == 0 );
/* don't let the record expire */
unit_assert( (d=infra_lookup_host(slab, &one, onelen, zone, zonelen, 0, *now, &k)) );
unit_assert( d->timeout_A >= TIMEOUT_COUNT_MAX );
unit_assert( d->probedelay > 0 );
probedelay = d->probedelay;
lock_rw_unlock(&k->entry.lock);
cfg->host_ttl = cfg->host_ttl + *now < probedelay
?cfg->host_ttl :probedelay + 10;
/* advance time and check that probing is as expected; we already had a
* lot of A timeouts (checked above). */
*now = probedelay;
unit_assert( infra_get_lame_rtt(slab, &one, onelen, zone, zonelen,
LDNS_RR_TYPE_A, &lame, &dnsseclame, &reclame, &to, *now) );
unit_assert( lame == 0 && dnsseclame == 0 && reclame == 0
&& to == keep_probing ?still_useful_timeout() :USEFUL_SERVER_TOP_TIMEOUT);
}
/** test host cache */
void infra_test(void)
{
struct sockaddr_storage one;
socklen_t onelen;
uint8_t* zone = (uint8_t*)"\007example\003com\000";
size_t zonelen = 13;
struct infra_cache* slab;
struct config_file* cfg = config_create();
time_t now = 0;
uint8_t edns_lame;
int vs, to;
struct infra_key* k;
struct infra_data* d;
int init = UNKNOWN_SERVER_NICENESS;
int default_max_rtt = USEFUL_SERVER_TOP_TIMEOUT;
unit_show_feature("infra cache");
unit_assert(ipstrtoaddr("127.0.0.1", 53, &one, &onelen));
slab = infra_create(cfg);
/* insert new record */
unit_assert( infra_host(slab, &one, onelen, zone, zonelen, now,
&vs, &edns_lame, &to) );
unit_assert( vs == 0 && to == init && edns_lame == 0 );
/* simulate no answer */
unit_assert( infra_rtt_update(slab, &one, onelen, zone, zonelen, LDNS_RR_TYPE_A, -1, init, now) );
unit_assert( infra_host(slab, &one, onelen, zone, zonelen,
now, &vs, &edns_lame, &to) );
unit_assert( vs == 0 && to == init*2 && edns_lame == 0 );
/* simulate EDNS lame */
unit_assert( infra_edns_update(slab, &one, onelen, zone, zonelen, -1, now) );
unit_assert( infra_host(slab, &one, onelen, zone, zonelen,
now, &vs, &edns_lame, &to) );
unit_assert( vs == -1 && to == init*2 && edns_lame == 1);
/* simulate cache expiry */
now += cfg->host_ttl + 10;
unit_assert( infra_host(slab, &one, onelen, zone, zonelen,
now, &vs, &edns_lame, &to) );
unit_assert( vs == 0 && to == init && edns_lame == 0 );
/* simulate no lame answer */
unit_assert( infra_set_lame(slab, &one, onelen,
zone, zonelen, now, 0, 0, LDNS_RR_TYPE_A) );
unit_assert( (d=infra_lookup_host(slab, &one, onelen, zone, zonelen, 0, now, &k)) );
unit_assert( d->ttl == now+cfg->host_ttl );
unit_assert( d->edns_version == 0 );
unit_assert(!d->isdnsseclame && !d->rec_lame && d->lame_type_A &&
!d->lame_other);
lock_rw_unlock(&k->entry.lock);
/* test merge of data */
unit_assert( infra_set_lame(slab, &one, onelen,
zone, zonelen, now, 0, 0, LDNS_RR_TYPE_AAAA) );
unit_assert( (d=infra_lookup_host(slab, &one, onelen, zone, zonelen, 0, now, &k)) );
unit_assert(!d->isdnsseclame && !d->rec_lame && d->lame_type_A &&
d->lame_other);
lock_rw_unlock(&k->entry.lock);
/* test that noEDNS cannot overwrite known-yesEDNS */
now += cfg->host_ttl + 10;
unit_assert( infra_host(slab, &one, onelen, zone, zonelen,
now, &vs, &edns_lame, &to) );
unit_assert( vs == 0 && to == init && edns_lame == 0 );
unit_assert( infra_edns_update(slab, &one, onelen, zone, zonelen, 0, now) );
unit_assert( infra_host(slab, &one, onelen, zone, zonelen,
now, &vs, &edns_lame, &to) );
unit_assert( vs == 0 && to == init && edns_lame == 1 );
unit_assert( infra_edns_update(slab, &one, onelen, zone, zonelen, -1, now) );
unit_assert( infra_host(slab, &one, onelen, zone, zonelen,
now, &vs, &edns_lame, &to) );
unit_assert( vs == 0 && to == init && edns_lame == 1 );
unit_show_feature("infra cache probing (keep-probing off, default infra-cache-max-rtt)");
test_keep_probing(slab, cfg, one, onelen, zone, zonelen, &now, 0, default_max_rtt);
unit_show_feature("infra cache probing (keep-probing on, default infra-cache-max-rtt)");
test_keep_probing(slab, cfg, one, onelen, zone, zonelen, &now, 1, default_max_rtt);
unit_show_feature("infra cache probing (keep-probing off, low infra-cache-max-rtt)");
test_keep_probing(slab, cfg, one, onelen, zone, zonelen, &now, 0, 3000);
unit_show_feature("infra cache probing (keep-probing on, low infra-cache-max-rtt)");
test_keep_probing(slab, cfg, one, onelen, zone, zonelen, &now, 1, 3000);
/* Re-apply defaults for other unit tests that follow */
config_apply_max_rtt(default_max_rtt);
infra_delete(slab);
config_delete(cfg);
}
+284
View File
@@ -0,0 +1,284 @@
/*
* testcode/unitldns.c - unit test for ldns routines.
*
* Copyright (c) 2014, NLnet Labs. All rights reserved.
*
* This software is open source.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* Neither the name of the NLNET LABS nor the names of its contributors may
* be used to endorse or promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
* TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
/**
* \file
* Calls ldns unit tests. Exits with code 1 on a failure.
*/
#include "config.h"
#include "util/log.h"
#include "testcode/unitmain.h"
#include "sldns/sbuffer.h"
#include "sldns/str2wire.h"
#include "sldns/wire2str.h"
#include "sldns/parseutil.h"
/** verbose this unit test */
static int vbmp = 0;
/** print buffer to hex into string */
static void
buf_to_hex(uint8_t* b, size_t blen, char* s, size_t slen)
{
const char* h = "0123456789ABCDEF";
size_t i;
if(slen < blen*2+2 && vbmp) printf("hexstring buffer too small\n");
unit_assert(slen >= blen*2+2);
for(i=0; i<blen; i++) {
s[i*2] = h[(b[i]&0xf0)>>4];
s[i*2+1] = h[b[i]&0x0f];
}
s[blen*2] = '\n';
s[blen*2+1] = 0;
}
/** Transform input.
* @param txt_in: input text format.
* @param wire1: output wireformat in hex (txt_in converted to wire).
* @param txt_out: output text format (converted from wire_out).
* @param wire2: output wireformat in hex, txt_out converted back to wireformat.
* @param bufs: size of the text buffers.
*/
static void
rr_transform(char* txt_in, char* wire1, char* txt_out, char* wire2,
size_t bufs)
{
uint8_t b[65536];
size_t len;
int err;
len = sizeof(b);
err = sldns_str2wire_rr_buf(txt_in, b, &len, NULL, 3600,
NULL, 0, NULL, 0);
if(err != 0) {
if(vbmp) printf("sldns_str2wire_rr_buf, pos %d: %s\n",
LDNS_WIREPARSE_OFFSET(err),
sldns_get_errorstr_parse(err));
}
unit_assert(err == 0);
buf_to_hex(b, len, wire1, bufs);
if(vbmp) printf("wire1: %s", wire1);
err = sldns_wire2str_rr_buf(b, len, txt_out, bufs);
unit_assert(err < (int)bufs && err > 0);
if(vbmp) printf("txt: %s", txt_out);
len = sizeof(b);
err = sldns_str2wire_rr_buf(txt_out, b, &len, NULL, 3600,
NULL, 0, NULL, 0);
if(err != 0) {
if(vbmp) printf("sldns_str2wire_rr_buf-2, pos %d: %s\n",
LDNS_WIREPARSE_OFFSET(err),
sldns_get_errorstr_parse(err));
}
unit_assert(err == 0);
buf_to_hex(b, len, wire2, bufs);
if(vbmp) printf("wire2: %s", wire2);
}
/** Check if results are correct */
static void
rr_checks(char* wire_chk, char* txt_chk, char* txt_out, char* wire_out,
char* back)
{
#ifdef __APPLE__
/* the wiretostr on ipv6 is weird on apple, we cannot check it.
* skip AAAA on OSX */
if(strstr(txt_out, "IN AAAA"))
txt_out = txt_chk; /* skip this test, but test wirefmt */
/* so we know that txt_out back to wire is the same */
#endif
if(strcmp(txt_chk, txt_out) != 0 && vbmp)
printf("txt different\n");
if(strcmp(wire_chk, wire_out) != 0 && vbmp)
printf("wire1 different\n");
if(strcmp(wire_chk, back) != 0 && vbmp)
printf("wire2 different\n");
unit_assert(strcmp(txt_chk, txt_out) == 0);
unit_assert(strcmp(wire_chk, wire_out) == 0);
unit_assert(strcmp(wire_chk, back) == 0);
}
/** read rrs to and from string, and wireformat
* Skips empty lines and comments.
* @param input: input file with text format.
* @param check: check file with hex and then textformat
*/
static void
rr_test_file(const char* input, const char* check)
{
size_t bufs = 131072;
FILE* inf, *chf, *of;
int lineno = 0, chlineno = 0;
char* txt_in = (char*)malloc(bufs);
char* txt_out = (char*)malloc(bufs);
char* txt_chk = (char*)malloc(bufs);
char* wire_out = (char*)malloc(bufs);
char* wire_chk = (char*)malloc(bufs);
char* back = (char*)malloc(bufs);
if(!txt_in || !txt_out || !txt_chk || !wire_out || !wire_chk || !back)
fatal_exit("malloc failure");
inf = fopen(input, "r");
if(!inf) fatal_exit("cannot open %s: %s", input, strerror(errno));
chf = fopen(check, "r");
if(!chf) fatal_exit("cannot open %s: %s", check, strerror(errno));
of = NULL;
if(0) {
/* debug: create check file */
of = fopen("outputfile", "w");
if(!of) fatal_exit("cannot write output: %s", strerror(errno));
}
while(fgets(txt_in, (int)bufs, inf)) {
lineno++;
if(vbmp) printf("\n%s:%d %s", input, lineno, txt_in);
/* skip empty lines and comments */
if(txt_in[0] == 0 || txt_in[0] == '\n' || txt_in[0] == ';')
continue;
/* read check lines */
if(!fgets(wire_chk, (int)bufs, chf)) {
printf("%s too short\n", check);
unit_assert(0);
}
if(!fgets(txt_chk, (int)bufs, chf)) {
printf("%s too short\n", check);
unit_assert(0);
}
chlineno += 2;
if(vbmp) printf("%s:%d %s", check, chlineno-1, wire_chk);
if(vbmp) printf("%s:%d %s", check, chlineno, txt_chk);
/* generate results */
rr_transform(txt_in, wire_out, txt_out, back, bufs);
/* checks */
if(of) {
fprintf(of, "%s%s", wire_out, txt_out);
} else {
rr_checks(wire_chk, txt_chk, txt_out, wire_out, back);
}
}
if(of) fclose(of);
fclose(inf);
fclose(chf);
free(txt_in);
free(txt_out);
free(txt_chk);
free(wire_out);
free(wire_chk);
free(back);
}
#define xstr(s) str(s)
#define str(s) #s
#define SRCDIRSTR xstr(SRCDIR)
/** read rrs to and from string, to and from wireformat */
static void
rr_tests(void)
{
rr_test_file(SRCDIRSTR "/testdata/test_ldnsrr.1",
SRCDIRSTR "/testdata/test_ldnsrr.c1");
rr_test_file(SRCDIRSTR "/testdata/test_ldnsrr.2",
SRCDIRSTR "/testdata/test_ldnsrr.c2");
rr_test_file(SRCDIRSTR "/testdata/test_ldnsrr.3",
SRCDIRSTR "/testdata/test_ldnsrr.c3");
rr_test_file(SRCDIRSTR "/testdata/test_ldnsrr.4",
SRCDIRSTR "/testdata/test_ldnsrr.c4");
rr_test_file(SRCDIRSTR "/testdata/test_ldnsrr.5",
SRCDIRSTR "/testdata/test_ldnsrr.c5");
}
/** test various base64 decoding options */
static void
b64_test(void)
{
/* "normal" b64 alphabet, with padding */
char* p1 = "aGVsbG8="; /* "hello" */
char* p2 = "aGVsbG8+"; /* "hello>" */
char* p3 = "aGVsbG8/IQ=="; /* "hello?!" */
char* p4 = "aGVsbG8"; /* "hel" + extra garbage */
/* base64 url, without padding */
char* u1 = "aGVsbG8"; /* "hello" */
char* u2 = "aGVsbG8-"; /* "hello>" */
char* u3 = "aGVsbG8_IQ"; /* "hello?!" */
char* u4 = "aaaaa"; /* garbage */
char target[128];
size_t tarsize = 128;
int result;
memset(target, 0, sizeof(target));
result = sldns_b64_pton(p1, (uint8_t*)target, tarsize);
unit_assert(result == (int)strlen("hello") && strcmp(target, "hello") == 0);
memset(target, 0, sizeof(target));
result = sldns_b64_pton(p2, (uint8_t*)target, tarsize);
unit_assert(result == (int)strlen("hello>") && strcmp(target, "hello>") == 0);
memset(target, 0, sizeof(target));
result = sldns_b64_pton(p3, (uint8_t*)target, tarsize);
unit_assert(result == (int)strlen("hello?!") && strcmp(target, "hello?!") == 0);
memset(target, 0, sizeof(target));
result = sldns_b64_pton(p4, (uint8_t*)target, tarsize);
/* when padding is used everything that is not a block of 4 will be
* ignored */
unit_assert(result == (int)strlen("hel") && strcmp(target, "hel") == 0);
memset(target, 0, sizeof(target));
result = sldns_b64url_pton(u1, strlen(u1), (uint8_t*)target, tarsize);
unit_assert(result == (int)strlen("hello") && strcmp(target, "hello") == 0);
memset(target, 0, sizeof(target));
result = sldns_b64url_pton(u2, strlen(u2), (uint8_t*)target, tarsize);
unit_assert(result == (int)strlen("hello>") && strcmp(target, "hello>") == 0);
memset(target, 0, sizeof(target));
result = sldns_b64url_pton(u3, strlen(u3), (uint8_t*)target, tarsize);
unit_assert(result == (int)strlen("hello+/") && strcmp(target, "hello?!") == 0);
/* one item in block of four is not allowed */
memset(target, 0, sizeof(target));
result = sldns_b64url_pton(u4, strlen(u4), (uint8_t*)target, tarsize);
unit_assert(result == -1);
}
void
ldns_test(void)
{
unit_show_feature("sldns");
rr_tests();
b64_test();
}
File diff suppressed because it is too large Load Diff
+92
View File
@@ -0,0 +1,92 @@
/*
* testcode/unitmain.h - unit test main program for unbound.
*
* Copyright (c) 2007, NLnet Labs. All rights reserved.
*
* This software is open source.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* Neither the name of the NLNET LABS nor the names of its contributors may
* be used to endorse or promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
* TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
/**
* \file
* Declarations useful for the unit tests.
*/
#ifndef TESTCODE_UNITMAIN_H
#define TESTCODE_UNITMAIN_H
#include "util/log.h"
/** number of tests done */
extern int testcount;
/** test bool x, exits on failure, increases testcount. */
#ifdef DEBUG_UNBOUND
#define unit_assert(x) do {testcount++; log_assert(x);} while(0)
#else
#define unit_assert(x) do {testcount++; if(!(x)) { fprintf(stderr, "assertion failure %s:%d\n", __FILE__, __LINE__); exit(1);}} while(0)
#endif
/** we are now testing this function */
void unit_show_func(const char* file, const char* func);
/** we are testing this functionality */
void unit_show_feature(const char* feature);
/** unit test lruhashtable implementation */
void lruhash_test(void);
/** unit test slabhashtable implementation */
void slabhash_test(void);
/** unit test for msgreply and msgparse */
void msgparse_test(void);
/** unit test dname handling functions */
void dname_test(void);
/** unit test trust anchor storage functions */
void anchors_test(void);
/** unit test for verification functions */
void verify_test(void);
/** unit test for negative cache functions */
void neg_test(void);
/** unit test for regional allocator functions */
void regional_test(void);
#ifdef CLIENT_SUBNET
/** Unit test for ECS functions */
void ecs_test(void);
#endif /* CLIENT_SUBNET */
/** unit test for ldns functions */
void ldns_test(void);
/** unit test for auth zone functions */
void authzone_test(void);
/** unit test for zonemd functions */
void zonemd_test(void);
/** unit test for tcp_reuse functions */
void tcpreuse_test(void);
/** unit test for doq functions */
void doq_test(void);
/** unit test for infra cache functions */
void infra_test(void);
#endif /* TESTCODE_UNITMAIN_H */
+545
View File
@@ -0,0 +1,545 @@
/*
* testcode/unitneg.c - unit test for negative cache routines.
*
* Copyright (c) 2008, NLnet Labs. All rights reserved.
*
* This software is open source.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* Neither the name of the NLNET LABS nor the names of its contributors may
* be used to endorse or promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
* TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
/**
* \file
* Calls negative cache unit tests. Exits with code 1 on a failure.
*/
#include "config.h"
#include "util/log.h"
#include "util/net_help.h"
#include "util/data/packed_rrset.h"
#include "util/data/dname.h"
#include "testcode/unitmain.h"
#include "validator/val_neg.h"
#include "sldns/rrdef.h"
/** verbose unit test for negative cache */
static int negverbose = 0;
/** debug printout of neg cache */
static void print_neg_cache(struct val_neg_cache* neg)
{
char buf[LDNS_MAX_DOMAINLEN];
struct val_neg_zone* z;
struct val_neg_data* d;
printf("neg_cache print\n");
printf("memuse %d of %d\n", (int)neg->use, (int)neg->max);
printf("maxiter %d\n", (int)neg->nsec3_max_iter);
printf("%d zones\n", (int)neg->tree.count);
RBTREE_FOR(z, struct val_neg_zone*, &neg->tree) {
dname_str(z->name, buf);
printf("%24s", buf);
printf(" len=%2.2d labs=%d inuse=%d count=%d tree.count=%d\n",
(int)z->len, z->labs, (int)z->in_use, z->count,
(int)z->tree.count);
}
RBTREE_FOR(z, struct val_neg_zone*, &neg->tree) {
printf("\n");
dname_print(stdout, NULL, z->name);
printf(" zone details\n");
printf("len=%2.2d labs=%d inuse=%d count=%d tree.count=%d\n",
(int)z->len, z->labs, (int)z->in_use, z->count,
(int)z->tree.count);
if(z->parent) {
printf("parent=");
dname_print(stdout, NULL, z->parent->name);
printf("\n");
} else {
printf("parent=NULL\n");
}
RBTREE_FOR(d, struct val_neg_data*, &z->tree) {
dname_str(d->name, buf);
printf("%24s", buf);
printf(" len=%2.2d labs=%d inuse=%d count=%d\n",
(int)d->len, d->labs, (int)d->in_use, d->count);
}
}
}
/** get static pointer to random zone name */
static char* get_random_zone(void)
{
static char zname[36];
int labels = random() % 3;
int i;
char* p = zname;
int labnum;
for(i=0; i<labels; i++) {
labnum = random()%10;
snprintf(p, sizeof(zname)-(p-zname), "\003%3.3d", labnum);
p+=4;
}
snprintf(p, sizeof(zname)-(p-zname), "\007example\003com");
return zname;
}
/** get static pointer to random data names from and to */
static void get_random_data(char** fromp, char** top, char* zname)
{
static char buf1[256], buf2[256];
int type;
int lab1, lab2;
int labnum1[10], labnum2[10];
int i;
char* p;
memset(labnum1, 0, sizeof(int)*10);
memset(labnum2, 0, sizeof(int)*10);
*fromp = buf1;
*top = buf2;
type = random()%10;
if(type == 0) {
/* ENT */
lab1 = random() %3 + 1;
lab2 = lab1 + random()%3 + 1;
for(i=0; i<lab1; i++) {
labnum1[i] = random()%100;
labnum2[i] = labnum1[i];
}
for(i=lab1; i<lab2; i++) {
labnum2[i] = random()%100;
}
} else if(type == 1) {
/* end of zone */
lab2 = 0;
lab1 = random()%3 + 1;
for(i=0; i<lab1; i++) {
labnum1[i] = random()%100;
}
} else if(type == 2) {
/* start of zone */
lab1 = 0;
lab2 = random()%3 + 1;
for(i=0; i<lab2; i++) {
labnum2[i] = random()%100;
}
} else {
/* normal item */
int common = random()%3;
lab1 = random() %3 + 1;
lab2 = random() %3 + 1;
for(i=0; i<common; i++) {
labnum1[i] = random()%100;
labnum2[i] = labnum1[i];
}
labnum1[common] = random()%100;
labnum2[common] = labnum1[common] + random()%20;
for(i=common; i<lab1; i++)
labnum1[i] = random()%100;
for(i=common; i<lab2; i++)
labnum2[i] = random()%100;
}
/* construct first */
p = buf1;
for(i=0; i<lab1; i++) {
snprintf(p, 256-(p-buf1), "\003%3.3d", labnum1[i]);
p+=4;
}
snprintf(p, 256-(p-buf1), "%s", zname);
/* construct 2nd */
p = buf2+2;
for(i=0; i<lab2; i++) {
snprintf(p, 256-(p-buf2)-3, "\003%3.3d", labnum2[i]);
p+=4;
}
snprintf(p, 256-(p-buf2)-3, "%s", zname);
buf2[0] = (char)(strlen(buf2+2)+1);
buf2[1] = 0;
if(negverbose) {
log_nametypeclass(0, "add from", (uint8_t*)buf1, 0, 0);
log_nametypeclass(0, "add to ", (uint8_t*)buf2+2, 0, 0);
}
}
/** add a random item */
static void add_item(struct val_neg_cache* neg)
{
struct val_neg_zone* z;
struct packed_rrset_data rd;
struct ub_packed_rrset_key nsec;
size_t rr_len;
time_t rr_ttl;
uint8_t* rr_data;
char* zname = get_random_zone();
char* from, *to;
lock_basic_lock(&neg->lock);
if(negverbose)
log_nametypeclass(0, "add to zone", (uint8_t*)zname, 0, 0);
z = neg_find_zone(neg, (uint8_t*)zname, strlen(zname)+1,
LDNS_RR_CLASS_IN);
if(!z) {
z = neg_create_zone(neg, (uint8_t*)zname, strlen(zname)+1,
LDNS_RR_CLASS_IN);
}
unit_assert(z);
val_neg_zone_take_inuse(z);
/* construct random NSEC item */
get_random_data(&from, &to, zname);
/* create nsec and insert it */
memset(&rd, 0, sizeof(rd));
memset(&nsec, 0, sizeof(nsec));
nsec.rk.dname = (uint8_t*)from;
nsec.rk.dname_len = strlen(from)+1;
nsec.rk.type = htons(LDNS_RR_TYPE_NSEC);
nsec.rk.rrset_class = htons(LDNS_RR_CLASS_IN);
nsec.entry.data = &rd;
rd.security = sec_status_secure;
rd.count = 1;
rd.rr_len = &rr_len;
rr_len = 19;
rd.rr_ttl = &rr_ttl;
rr_ttl = 0;
rd.rr_data = &rr_data;
rr_data = (uint8_t*)to;
neg_insert_data(neg, z, &nsec);
lock_basic_unlock(&neg->lock);
}
/** remove a random item */
static void remove_item(struct val_neg_cache* neg)
{
int n, i;
struct val_neg_data* d;
rbnode_type* walk;
struct val_neg_zone* z;
lock_basic_lock(&neg->lock);
if(neg->tree.count == 0) {
lock_basic_unlock(&neg->lock);
return; /* nothing to delete */
}
/* pick a random zone */
walk = rbtree_first(&neg->tree); /* first highest parent, big count */
z = (struct val_neg_zone*)walk;
n = random() % (int)(z->count);
if(negverbose)
printf("neg stress delete zone %d\n", n);
i=0;
walk = rbtree_first(&neg->tree);
z = (struct val_neg_zone*)walk;
while(i!=n+1 && walk && walk != RBTREE_NULL && !z->in_use) {
walk = rbtree_next(walk);
z = (struct val_neg_zone*)walk;
if(z->in_use)
i++;
}
if(!walk || walk == RBTREE_NULL) {
lock_basic_unlock(&neg->lock);
return;
}
if(!z->in_use) {
lock_basic_unlock(&neg->lock);
return;
}
if(negverbose)
log_nametypeclass(0, "delete zone", z->name, 0, 0);
/* pick a random nsec item. - that is in use */
walk = rbtree_first(&z->tree); /* first is highest parent */
d = (struct val_neg_data*)walk;
n = random() % (int)(d->count);
if(negverbose)
printf("neg stress delete item %d\n", n);
i=0;
walk = rbtree_first(&z->tree);
d = (struct val_neg_data*)walk;
while(i!=n+1 && walk && walk != RBTREE_NULL && !d->in_use) {
walk = rbtree_next(walk);
d = (struct val_neg_data*)walk;
if(d->in_use)
i++;
}
if(!walk || walk == RBTREE_NULL) {
lock_basic_unlock(&neg->lock);
return;
}
if(d->in_use) {
if(negverbose)
log_nametypeclass(0, "neg delete item:", d->name, 0, 0);
neg_delete_data(neg, d);
}
lock_basic_unlock(&neg->lock);
}
/** sum up the zone trees */
static size_t sumtrees_all(struct val_neg_cache* neg)
{
size_t res = 0;
struct val_neg_zone* z;
RBTREE_FOR(z, struct val_neg_zone*, &neg->tree) {
res += z->tree.count;
}
return res;
}
/** sum up the zone trees, in_use only */
static size_t sumtrees_inuse(struct val_neg_cache* neg)
{
size_t res = 0;
struct val_neg_zone* z;
struct val_neg_data* d;
RBTREE_FOR(z, struct val_neg_zone*, &neg->tree) {
/* get count of highest parent for num in use */
d = (struct val_neg_data*)rbtree_first(&z->tree);
if(d && (rbnode_type*)d!=RBTREE_NULL)
res += d->count;
}
return res;
}
/** check if lru is still valid */
static void check_lru(struct val_neg_cache* neg)
{
struct val_neg_data* p, *np;
size_t num = 0;
size_t inuse;
p = neg->first;
while(p) {
if(!p->prev) {
unit_assert(neg->first == p);
}
np = p->next;
if(np) {
unit_assert(np->prev == p);
} else {
unit_assert(neg->last == p);
}
num++;
p = np;
}
inuse = sumtrees_inuse(neg);
if(negverbose)
printf("num lru %d, inuse %d, all %d\n",
(int)num, (int)sumtrees_inuse(neg),
(int)sumtrees_all(neg));
unit_assert( num == inuse);
unit_assert( inuse <= sumtrees_all(neg));
}
/** sum up number of items inuse in subtree */
static int sum_subtree_inuse(struct val_neg_zone* zone,
struct val_neg_data* data)
{
struct val_neg_data* d;
int num = 0;
RBTREE_FOR(d, struct val_neg_data*, &zone->tree) {
if(dname_subdomain_c(d->name, data->name)) {
if(d->in_use)
num++;
}
}
return num;
}
/** sum up number of items inuse in subtree */
static int sum_zone_subtree_inuse(struct val_neg_cache* neg,
struct val_neg_zone* zone)
{
struct val_neg_zone* z;
int num = 0;
RBTREE_FOR(z, struct val_neg_zone*, &neg->tree) {
if(dname_subdomain_c(z->name, zone->name)) {
if(z->in_use)
num++;
}
}
return num;
}
/** check point in data tree */
static void check_data(struct val_neg_zone* zone, struct val_neg_data* data)
{
unit_assert(data->count > 0);
if(data->parent) {
unit_assert(data->parent->count >= data->count);
if(data->parent->in_use) {
unit_assert(data->parent->count > data->count);
}
unit_assert(data->parent->labs == data->labs-1);
/* and parent must be one label shorter */
unit_assert(data->name[0] == (data->len-data->parent->len-1));
unit_assert(query_dname_compare(data->name + data->name[0]+1,
data->parent->name) == 0);
} else {
/* must be apex */
unit_assert(dname_is_root(data->name));
}
/* tree property: */
unit_assert(data->count == sum_subtree_inuse(zone, data));
}
/** check if tree of data in zone is valid */
static void checkzonetree(struct val_neg_zone* zone)
{
struct val_neg_data* d;
/* check all data in tree */
RBTREE_FOR(d, struct val_neg_data*, &zone->tree) {
check_data(zone, d);
}
}
/** check if negative cache is still valid */
static void check_zone_invariants(struct val_neg_cache* neg,
struct val_neg_zone* zone)
{
unit_assert(zone->nsec3_hash == 0);
unit_assert(zone->tree.cmp == &val_neg_data_compare);
unit_assert(zone->count != 0);
if(zone->tree.count == 0)
unit_assert(!zone->in_use);
else {
if(!zone->in_use) {
/* details on error */
log_nametypeclass(0, "zone", zone->name, 0, 0);
log_err("inuse %d count=%d tree.count=%d",
zone->in_use, zone->count,
(int)zone->tree.count);
if(negverbose)
print_neg_cache(neg);
}
unit_assert(zone->in_use);
}
if(zone->parent) {
unit_assert(zone->parent->count >= zone->count);
if(zone->parent->in_use) {
unit_assert(zone->parent->count > zone->count);
}
unit_assert(zone->parent->labs == zone->labs-1);
/* and parent must be one label shorter */
unit_assert(zone->name[0] == (zone->len-zone->parent->len-1));
unit_assert(query_dname_compare(zone->name + zone->name[0]+1,
zone->parent->name) == 0);
} else {
/* must be apex */
unit_assert(dname_is_root(zone->name));
}
/* tree property: */
unit_assert(zone->count == sum_zone_subtree_inuse(neg, zone));
/* check structure of zone data tree */
checkzonetree(zone);
}
/** check if negative cache is still valid */
static void check_neg_invariants(struct val_neg_cache* neg)
{
struct val_neg_zone* z;
/* check structure of LRU list */
lock_basic_lock(&neg->lock);
check_lru(neg);
unit_assert(neg->max == 1024*1024);
unit_assert(neg->nsec3_max_iter == 1500);
unit_assert(neg->tree.cmp == &val_neg_zone_compare);
if(neg->tree.count == 0) {
/* empty */
unit_assert(neg->tree.count == 0);
unit_assert(neg->first == NULL);
unit_assert(neg->last == NULL);
unit_assert(neg->use == 0);
lock_basic_unlock(&neg->lock);
return;
}
unit_assert(neg->first != NULL);
unit_assert(neg->last != NULL);
RBTREE_FOR(z, struct val_neg_zone*, &neg->tree) {
check_zone_invariants(neg, z);
}
lock_basic_unlock(&neg->lock);
}
/** perform stress test on insert and delete in neg cache */
static void stress_test(struct val_neg_cache* neg)
{
int i;
if(negverbose)
printf("negcache test\n");
for(i=0; i<100; i++) {
if(random() % 10 < 8)
add_item(neg);
else remove_item(neg);
check_neg_invariants(neg);
}
/* empty it */
if(negverbose)
printf("neg stress empty\n");
while(neg->first) {
remove_item(neg);
check_neg_invariants(neg);
}
if(negverbose)
printf("neg stress emptied\n");
unit_assert(neg->first == NULL);
/* insert again */
for(i=0; i<100; i++) {
if(random() % 10 < 8)
add_item(neg);
else remove_item(neg);
check_neg_invariants(neg);
}
}
void neg_test(void)
{
struct val_neg_cache* neg;
srandom(48);
unit_show_feature("negative cache");
/* create with defaults */
neg = val_neg_create(NULL, 1500);
unit_assert(neg);
stress_test(neg);
neg_cache_delete(neg);
}
+573
View File
@@ -0,0 +1,573 @@
/*
* testcode/unitverify.c - unit test for signature verification routines.
*
* Copyright (c) 2007, NLnet Labs. All rights reserved.
*
* This software is open source.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* Neither the name of the NLNET LABS nor the names of its contributors may
* be used to endorse or promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
* TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
/**
* \file
* Calls verification unit tests. Exits with code 1 on a failure.
*/
#include "config.h"
#include "util/log.h"
#include "testcode/unitmain.h"
#include "validator/val_sigcrypt.h"
#include "validator/val_secalgo.h"
#include "validator/val_nsec.h"
#include "validator/val_nsec3.h"
#include "validator/validator.h"
#include "testcode/testpkts.h"
#include "util/data/msgreply.h"
#include "util/data/msgparse.h"
#include "util/data/dname.h"
#include "util/regional.h"
#include "util/alloc.h"
#include "util/rbtree.h"
#include "util/net_help.h"
#include "util/module.h"
#include "util/config_file.h"
#include "sldns/sbuffer.h"
#include "sldns/keyraw.h"
#include "sldns/str2wire.h"
#include "sldns/wire2str.h"
/** verbose signature test */
static int vsig = 0;
/** entry to packet buffer with wireformat */
static void
entry_to_buf(struct entry* e, sldns_buffer* pkt)
{
unit_assert(e->reply_list);
if(e->reply_list->reply_from_hex) {
sldns_buffer_copy(pkt, e->reply_list->reply_from_hex);
} else {
sldns_buffer_clear(pkt);
sldns_buffer_write(pkt, e->reply_list->reply_pkt,
e->reply_list->reply_len);
sldns_buffer_flip(pkt);
}
}
/** entry to reply info conversion */
static void
entry_to_repinfo(struct entry* e, struct alloc_cache* alloc,
struct regional* region, sldns_buffer* pkt, struct query_info* qi,
struct reply_info** rep)
{
int ret;
struct edns_data edns;
entry_to_buf(e, pkt);
/* lock alloc lock to please lock checking software.
* alloc_special_obtain assumes it is talking to a ub-alloc,
* and does not need to perform locking. Here the alloc is
* the only one, so we lock it here */
lock_quick_lock(&alloc->lock);
ret = reply_info_parse(pkt, alloc, qi, rep, region, &edns);
lock_quick_unlock(&alloc->lock);
if(ret != 0) {
char rcode[16];
sldns_wire2str_rcode_buf(ret, rcode, sizeof(rcode));
printf("parse code %d: %s\n", ret, rcode);
unit_assert(ret != 0);
}
}
/** extract DNSKEY rrset from answer and convert it */
static struct ub_packed_rrset_key*
extract_keys(struct entry* e, struct alloc_cache* alloc,
struct regional* region, sldns_buffer* pkt)
{
struct ub_packed_rrset_key* dnskey = NULL;
struct query_info qinfo;
struct reply_info* rep = NULL;
size_t i;
entry_to_repinfo(e, alloc, region, pkt, &qinfo, &rep);
for(i=0; i<rep->an_numrrsets; i++) {
if(ntohs(rep->rrsets[i]->rk.type) == LDNS_RR_TYPE_DNSKEY) {
dnskey = rep->rrsets[i];
rep->rrsets[i] = NULL;
break;
}
}
unit_assert(dnskey);
reply_info_parsedelete(rep, alloc);
query_info_clear(&qinfo);
return dnskey;
}
/** return true if answer should be bogus */
static int
should_be_bogus(struct ub_packed_rrset_key* rrset, struct query_info* qinfo)
{
struct packed_rrset_data* d = (struct packed_rrset_data*)rrset->
entry.data;
if(d->rrsig_count == 0)
return 1;
/* name 'bogus' as first label signals bogus */
if(rrset->rk.dname_len > 6 && memcmp(rrset->rk.dname+1, "bogus", 5)==0)
return 1;
if(qinfo->qname_len > 6 && memcmp(qinfo->qname+1, "bogus", 5)==0)
return 1;
return 0;
}
/** return number of rrs in an rrset */
static size_t
rrset_get_count(struct ub_packed_rrset_key* rrset)
{
struct packed_rrset_data* d = (struct packed_rrset_data*)
rrset->entry.data;
if(!d) return 0;
return d->count;
}
/** setup sig alg list from dnskey */
static void
setup_sigalg(struct ub_packed_rrset_key* dnskey, uint8_t* sigalg)
{
uint8_t a[ALGO_NEEDS_MAX];
size_t i, n = 0;
memset(a, 0, sizeof(a));
for(i=0; i<rrset_get_count(dnskey); i++) {
uint8_t algo = (uint8_t)dnskey_get_algo(dnskey, i);
if(a[algo] == 0) {
a[algo] = 1;
sigalg[n++] = algo;
}
}
sigalg[n] = 0;
}
/** verify and test one rrset against the key rrset */
static void
verifytest_rrset(struct module_env* env, struct val_env* ve,
struct ub_packed_rrset_key* rrset, struct ub_packed_rrset_key* dnskey,
struct query_info* qinfo)
{
enum sec_status sec;
char reasonbuf[256];
char* reason = NULL;
uint8_t sigalg[ALGO_NEEDS_MAX+1];
int verified = 0;
if(vsig) {
log_nametypeclass(VERB_QUERY, "verify of rrset",
rrset->rk.dname, ntohs(rrset->rk.type),
ntohs(rrset->rk.rrset_class));
}
setup_sigalg(dnskey, sigalg); /* check all algorithms in the dnskey */
/* ok to give null as qstate here, won't be used for answer section. */
sec = dnskeyset_verify_rrset(env, ve, rrset, dnskey, sigalg, &reason,
NULL, LDNS_SECTION_ANSWER, NULL, &verified, reasonbuf,
sizeof(reasonbuf));
if(vsig) {
printf("verify outcome is: %s %s\n", sec_status_to_string(sec),
reason?reason:"");
}
if(should_be_bogus(rrset, qinfo)) {
unit_assert(sec == sec_status_bogus);
} else {
unit_assert(sec == sec_status_secure);
}
}
/** verify and test an entry - every rr in the message */
static void
verifytest_entry(struct entry* e, struct alloc_cache* alloc,
struct regional* region, sldns_buffer* pkt,
struct ub_packed_rrset_key* dnskey, struct module_env* env,
struct val_env* ve)
{
struct query_info qinfo;
struct reply_info* rep = NULL;
size_t i;
regional_free_all(region);
if(vsig) {
char* s = sldns_wire2str_pkt(e->reply_list->reply_pkt,
e->reply_list->reply_len);
printf("verifying pkt:\n%s\n", s?s:"outofmemory");
free(s);
}
entry_to_repinfo(e, alloc, region, pkt, &qinfo, &rep);
for(i=0; i<rep->rrset_count; i++) {
verifytest_rrset(env, ve, rep->rrsets[i], dnskey, &qinfo);
}
reply_info_parsedelete(rep, alloc);
query_info_clear(&qinfo);
}
/** find RRset in reply by type */
static struct ub_packed_rrset_key*
find_rrset_type(struct reply_info* rep, uint16_t type)
{
size_t i;
for(i=0; i<rep->rrset_count; i++) {
if(ntohs(rep->rrsets[i]->rk.type) == type)
return rep->rrsets[i];
}
return NULL;
}
/** DS sig test an entry - get DNSKEY and DS in entry and verify */
static void
dstest_entry(struct entry* e, struct alloc_cache* alloc,
struct regional* region, sldns_buffer* pkt, struct module_env* env)
{
struct query_info qinfo;
struct reply_info* rep = NULL;
struct ub_packed_rrset_key* ds, *dnskey;
int ret;
regional_free_all(region);
if(vsig) {
char* s = sldns_wire2str_pkt(e->reply_list->reply_pkt,
e->reply_list->reply_len);
printf("verifying DS-DNSKEY match:\n%s\n", s?s:"outofmemory");
free(s);
}
entry_to_repinfo(e, alloc, region, pkt, &qinfo, &rep);
ds = find_rrset_type(rep, LDNS_RR_TYPE_DS);
dnskey = find_rrset_type(rep, LDNS_RR_TYPE_DNSKEY);
/* check test is OK */
unit_assert(ds && dnskey);
ret = ds_digest_match_dnskey(env, dnskey, 0, ds, 0);
if(strncmp((char*)qinfo.qname, "\003yes", 4) == 0) {
if(vsig) {
printf("result(yes)= %s\n", ret?"yes":"no");
}
unit_assert(ret);
} else if (strncmp((char*)qinfo.qname, "\002no", 3) == 0) {
if(vsig) {
printf("result(no)= %s\n", ret?"yes":"no");
}
unit_assert(!ret);
verbose(VERB_QUERY, "DS fail: OK; matched unit test");
} else {
fatal_exit("Bad qname in DS unit test, yes or no");
}
reply_info_parsedelete(rep, alloc);
query_info_clear(&qinfo);
}
/** verify from a file */
static void
verifytest_file(const char* fname, const char* at_date)
{
/*
* The file contains a list of ldns-testpkts entries.
* The first entry must be a query for DNSKEY.
* The answer rrset is the keyset that will be used for verification
*/
struct ub_packed_rrset_key* dnskey;
struct regional* region = regional_create();
struct alloc_cache alloc;
sldns_buffer* buf = sldns_buffer_new(65535);
struct entry* e;
struct entry* list = read_datafile(fname, 1);
struct module_env env;
struct val_env ve;
time_t now = time(NULL);
unit_show_func("signature verify", fname);
if(!list)
fatal_exit("could not read %s: %s", fname, strerror(errno));
alloc_init(&alloc, NULL, 1);
memset(&env, 0, sizeof(env));
memset(&ve, 0, sizeof(ve));
env.scratch = region;
env.scratch_buffer = buf;
env.now = &now;
ve.date_override = cfg_convert_timeval(at_date);
unit_assert(region && buf);
dnskey = extract_keys(list, &alloc, region, buf);
if(vsig) log_nametypeclass(VERB_QUERY, "test dnskey",
dnskey->rk.dname, ntohs(dnskey->rk.type),
ntohs(dnskey->rk.rrset_class));
/* ready to go! */
for(e = list->next; e; e = e->next) {
verifytest_entry(e, &alloc, region, buf, dnskey, &env, &ve);
}
ub_packed_rrset_parsedelete(dnskey, &alloc);
delete_entry(list);
regional_destroy(region);
alloc_clear(&alloc);
sldns_buffer_free(buf);
}
/** verify DS matches DNSKEY from a file */
static void
dstest_file(const char* fname)
{
/*
* The file contains a list of ldns-testpkts entries.
* The first entry must be a query for DNSKEY.
* The answer rrset is the keyset that will be used for verification
*/
struct regional* region = regional_create();
struct alloc_cache alloc;
sldns_buffer* buf = sldns_buffer_new(65535);
struct entry* e;
struct entry* list = read_datafile(fname, 1);
struct module_env env;
unit_show_func("DS verify", fname);
if(!list)
fatal_exit("could not read %s: %s", fname, strerror(errno));
alloc_init(&alloc, NULL, 1);
memset(&env, 0, sizeof(env));
env.scratch = region;
env.scratch_buffer = buf;
unit_assert(region && buf);
/* ready to go! */
for(e = list; e; e = e->next) {
dstest_entry(e, &alloc, region, buf, &env);
}
delete_entry(list);
regional_destroy(region);
alloc_clear(&alloc);
sldns_buffer_free(buf);
}
/** helper for unittest of NSEC routines */
static int
unitest_nsec_has_type_rdata(char* bitmap, size_t len, uint16_t type)
{
return nsecbitmap_has_type_rdata((uint8_t*)bitmap, len, type);
}
/** Test NSEC type bitmap routine */
static void
nsectest(void)
{
/* bitmap starts at type bitmap rdata field */
/* from rfc 4034 example */
char* bitmap = "\000\006\100\001\000\000\000\003"
"\004\033\000\000\000\000\000\000"
"\000\000\000\000\000\000\000\000"
"\000\000\000\000\000\000\000\000"
"\000\000\000\000\040";
size_t len = 37;
unit_assert(!unitest_nsec_has_type_rdata(bitmap, len, 0));
unit_assert(unitest_nsec_has_type_rdata(bitmap, len, LDNS_RR_TYPE_A));
unit_assert(!unitest_nsec_has_type_rdata(bitmap, len, 2));
unit_assert(!unitest_nsec_has_type_rdata(bitmap, len, 3));
unit_assert(!unitest_nsec_has_type_rdata(bitmap, len, 4));
unit_assert(!unitest_nsec_has_type_rdata(bitmap, len, 5));
unit_assert(!unitest_nsec_has_type_rdata(bitmap, len, 6));
unit_assert(!unitest_nsec_has_type_rdata(bitmap, len, 7));
unit_assert(!unitest_nsec_has_type_rdata(bitmap, len, 8));
unit_assert(!unitest_nsec_has_type_rdata(bitmap, len, 9));
unit_assert(!unitest_nsec_has_type_rdata(bitmap, len, 10));
unit_assert(!unitest_nsec_has_type_rdata(bitmap, len, 11));
unit_assert(!unitest_nsec_has_type_rdata(bitmap, len, 12));
unit_assert(!unitest_nsec_has_type_rdata(bitmap, len, 13));
unit_assert(!unitest_nsec_has_type_rdata(bitmap, len, 14));
unit_assert(unitest_nsec_has_type_rdata(bitmap, len, LDNS_RR_TYPE_MX));
unit_assert(unitest_nsec_has_type_rdata(bitmap, len, LDNS_RR_TYPE_RRSIG));
unit_assert(unitest_nsec_has_type_rdata(bitmap, len, LDNS_RR_TYPE_NSEC));
unit_assert(unitest_nsec_has_type_rdata(bitmap, len, 1234));
unit_assert(!unitest_nsec_has_type_rdata(bitmap, len, 1233));
unit_assert(!unitest_nsec_has_type_rdata(bitmap, len, 1235));
unit_assert(!unitest_nsec_has_type_rdata(bitmap, len, 1236));
unit_assert(!unitest_nsec_has_type_rdata(bitmap, len, 1237));
unit_assert(!unitest_nsec_has_type_rdata(bitmap, len, 1238));
unit_assert(!unitest_nsec_has_type_rdata(bitmap, len, 1239));
unit_assert(!unitest_nsec_has_type_rdata(bitmap, len, 1240));
unit_assert(!unitest_nsec_has_type_rdata(bitmap, len, 2230));
}
/** Test hash algo - NSEC3 hash it and compare result */
static void
nsec3_hash_test_entry(struct entry* e, rbtree_type* ct,
struct alloc_cache* alloc, struct regional* region,
sldns_buffer* buf)
{
struct query_info qinfo;
struct reply_info* rep = NULL;
struct ub_packed_rrset_key* answer, *nsec3, *nsec3_region;
struct nsec3_cached_hash* hash = NULL;
int ret;
uint8_t* qname;
if(vsig) {
char* s = sldns_wire2str_pkt(e->reply_list->reply_pkt,
e->reply_list->reply_len);
printf("verifying NSEC3 hash:\n%s\n", s?s:"outofmemory");
free(s);
}
entry_to_repinfo(e, alloc, region, buf, &qinfo, &rep);
nsec3 = find_rrset_type(rep, LDNS_RR_TYPE_NSEC3);
answer = find_rrset_type(rep, LDNS_RR_TYPE_AAAA);
qname = regional_alloc_init(region, qinfo.qname, qinfo.qname_len);
/* check test is OK */
unit_assert(nsec3 && answer && qname);
/* Copy the nsec3 to the region, so it can stay referenced by the
* ct tree entry. The region is freed when the file is done. */
nsec3_region = packed_rrset_copy_region(nsec3, region, 0);
ret = nsec3_hash_name(ct, region, buf, nsec3_region, 0, qname,
qinfo.qname_len, &hash);
if(ret < 1) {
printf("Bad nsec3_hash_name retcode %d\n", ret);
unit_assert(ret == 1 || ret == 2);
}
unit_assert(hash->dname && hash->hash && hash->hash_len &&
hash->b32 && hash->b32_len);
unit_assert(hash->b32_len == (size_t)answer->rk.dname[0]);
/* does not do lowercasing. */
unit_assert(memcmp(hash->b32, answer->rk.dname+1, hash->b32_len)
== 0);
reply_info_parsedelete(rep, alloc);
query_info_clear(&qinfo);
}
/** Read file to test NSEC3 hash algo */
static void
nsec3_hash_test(const char* fname)
{
/*
* The list contains a list of ldns-testpkts entries.
* Every entry is a test.
* The qname is hashed.
* The answer section AAAA RR name is the required result.
* The auth section NSEC3 is used to get hash parameters.
* The hash cache is maintained per file.
*
* The test does not perform canonicalization during the compare.
*/
rbtree_type ct;
struct regional* region = regional_create();
struct alloc_cache alloc;
sldns_buffer* buf = sldns_buffer_new(65535);
struct entry* e;
struct entry* list = read_datafile(fname, 1);
unit_show_func("NSEC3 hash", fname);
if(!list)
fatal_exit("could not read %s: %s", fname, strerror(errno));
rbtree_init(&ct, &nsec3_hash_cmp);
alloc_init(&alloc, NULL, 1);
unit_assert(region && buf);
/* ready to go! */
for(e = list; e; e = e->next) {
nsec3_hash_test_entry(e, &ct, &alloc, region, buf);
}
delete_entry(list);
regional_destroy(region);
alloc_clear(&alloc);
sldns_buffer_free(buf);
}
#define xstr(s) str(s)
#define str(s) #s
#define SRCDIRSTR xstr(SRCDIR)
void
verify_test(void)
{
unit_show_feature("signature verify");
#ifdef USE_SHA1
verifytest_file(SRCDIRSTR "/testdata/test_signatures.1", "20070818005004");
#endif
#if defined(USE_DSA) && defined(USE_SHA1)
verifytest_file(SRCDIRSTR "/testdata/test_signatures.2", "20080414005004");
verifytest_file(SRCDIRSTR "/testdata/test_signatures.3", "20080416005004");
verifytest_file(SRCDIRSTR "/testdata/test_signatures.4", "20080416005004");
verifytest_file(SRCDIRSTR "/testdata/test_signatures.5", "20080416005004");
verifytest_file(SRCDIRSTR "/testdata/test_signatures.6", "20080416005004");
verifytest_file(SRCDIRSTR "/testdata/test_signatures.7", "20070829144150");
#endif /* USE_DSA */
#ifdef USE_SHA1
verifytest_file(SRCDIRSTR "/testdata/test_signatures.8", "20070829144150");
#endif
#if (defined(HAVE_EVP_SHA256) || defined(HAVE_NSS) || defined(HAVE_NETTLE)) && defined(USE_SHA2)
verifytest_file(SRCDIRSTR "/testdata/test_sigs.rsasha256", "20070829144150");
# ifdef USE_SHA1
verifytest_file(SRCDIRSTR "/testdata/test_sigs.sha1_and_256", "20070829144150");
# endif
verifytest_file(SRCDIRSTR "/testdata/test_sigs.rsasha256_draft", "20090101000000");
#endif
#if (defined(HAVE_EVP_SHA512) || defined(HAVE_NSS) || defined(HAVE_NETTLE)) && defined(USE_SHA2)
verifytest_file(SRCDIRSTR "/testdata/test_sigs.rsasha512_draft", "20070829144150");
verifytest_file(SRCDIRSTR "/testdata/test_signatures.9", "20171215000000");
#endif
#ifdef USE_SHA1
verifytest_file(SRCDIRSTR "/testdata/test_sigs.hinfo", "20090107100022");
verifytest_file(SRCDIRSTR "/testdata/test_sigs.revoked", "20080414005004");
#endif
#ifdef USE_GOST
if(sldns_key_EVP_load_gost_id())
verifytest_file(SRCDIRSTR "/testdata/test_sigs.gost", "20090807060504");
else printf("Warning: skipped GOST, openssl does not provide gost.\n");
#endif
#ifdef USE_ECDSA
/* test for support in case we use libNSS and ECC is removed */
if(dnskey_algo_id_is_supported(LDNS_ECDSAP256SHA256)) {
verifytest_file(SRCDIRSTR "/testdata/test_sigs.ecdsa_p256", "20100908100439");
verifytest_file(SRCDIRSTR "/testdata/test_sigs.ecdsa_p384", "20100908100439");
}
dstest_file(SRCDIRSTR "/testdata/test_ds.sha384");
#endif
#ifdef USE_ED25519
if(dnskey_algo_id_is_supported(LDNS_ED25519)) {
verifytest_file(SRCDIRSTR "/testdata/test_sigs.ed25519", "20170530140439");
}
#endif
#ifdef USE_ED448
if(dnskey_algo_id_is_supported(LDNS_ED448)) {
verifytest_file(SRCDIRSTR "/testdata/test_sigs.ed448", "20180408143630");
}
#endif
#ifdef USE_SHA1
dstest_file(SRCDIRSTR "/testdata/test_ds.sha1");
#endif
nsectest();
nsec3_hash_test(SRCDIRSTR "/testdata/test_nsec3_hash.1");
}
+541
View File
@@ -0,0 +1,541 @@
/*
* testcode/unitzonemd.c - unit test for zonemd.
*
* Copyright (c) 2020, NLnet Labs. All rights reserved.
*
* This software is open source.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* Neither the name of the NLNET LABS nor the names of its contributors may
* be used to endorse or promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
* TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
/**
* \file
* Unit tests for ZONEMD functionality.
*/
#include "config.h"
#include <ctype.h>
#include "util/log.h"
#include "testcode/unitmain.h"
#include "sldns/str2wire.h"
#include "services/authzone.h"
#include "util/data/dname.h"
#include "util/regional.h"
#include "validator/val_anchor.h"
#define xstr(s) str(s)
#define str(s) #s
#define SRCDIRSTR xstr(SRCDIR)
/** Add zone from file for testing */
struct auth_zone* authtest_addzone(struct auth_zones* az, const char* name,
char* fname);
/** zonemd unit test, generate a zonemd digest and check if correct */
static void zonemd_generate_test(const char* zname, char* zfile,
int scheme, int hashalgo, const char* digest)
{
uint8_t zonemd_hash[512];
size_t hashlen = 0;
char output[1024+1];
size_t i;
struct auth_zones* az;
struct auth_zone* z;
int result;
struct regional* region = NULL;
struct sldns_buffer* buf = NULL;
char* reason = NULL;
char* digestdup;
if(!zonemd_hashalgo_supported(hashalgo))
return; /* cannot test unsupported algo */
/* setup environment */
az = auth_zones_create();
unit_assert(az);
region = regional_create();
unit_assert(region);
buf = sldns_buffer_new(65535);
unit_assert(buf);
/* read file */
z = authtest_addzone(az, zname, zfile);
unit_assert(z);
lock_rw_wrlock(&z->lock);
z->zonemd_check = 1;
lock_rw_unlock(&z->lock);
/* create zonemd digest */
result = auth_zone_generate_zonemd_hash(z, scheme, hashalgo,
zonemd_hash, sizeof(zonemd_hash), &hashlen, region, buf,
&reason);
if(reason) printf("zonemd failure reason: %s\n", reason);
unit_assert(result);
/* check digest */
unit_assert(hashlen*2+1 <= sizeof(output));
for(i=0; i<hashlen; i++) {
const char* hexl = "0123456789ABCDEF";
output[i*2] = hexl[(zonemd_hash[i]&0xf0)>>4];
output[i*2+1] = hexl[zonemd_hash[i]&0xf];
}
output[hashlen*2] = 0;
digestdup = strdup(digest);
unit_assert(digestdup);
for(i=0; i<strlen(digestdup); i++) {
digestdup[i] = toupper((unsigned char)digestdup[i]);
}
if(verbosity >= VERB_ALGO) {
char zname[LDNS_MAX_DOMAINLEN];
dname_str(z->name, zname);
printf("zonemd generated for %s in %s with "
"scheme=%d hashalgo=%d\n", zname, z->zonefile,
scheme, hashalgo);
printf("digest %s\n", output);
printf("wanted %s\n", digestdup);
}
unit_assert(strcmp(output, digestdup) == 0);
/* delete environment */
free(digestdup);
auth_zones_delete(az);
regional_destroy(region);
sldns_buffer_free(buf);
if(verbosity >= VERB_ALGO) {
printf("\n");
}
}
/** loop over files and test generated zonemd digest */
static void zonemd_generate_tests(void)
{
unit_show_func("services/authzone.c", "auth_zone_generate_zonemd_hash");
zonemd_generate_test("example.org", SRCDIRSTR "/testdata/zonemd.example1.zone",
1, 2, "20564D10F50A0CEBEC856C64032B7DFB53D3C449A421A5BC7A21F7627B4ACEA4DF29F2C6FE82ED9C23ADF6F4D420D5DD63EF6E6349D60FDAB910B65DF8D481B7");
/* https://tools.ietf.org/html/draft-ietf-dnsop-dns-zone-digest-12
* from section A.1 */
zonemd_generate_test("example", SRCDIRSTR "/testdata/zonemd.example_a1.zone",
1, 1, "c68090d90a7aed716bc459f9340e3d7c1370d4d24b7e2fc3a1ddc0b9a87153b9a9713b3c9ae5cc27777f98b8e730044c");
/* https://tools.ietf.org/html/draft-ietf-dnsop-dns-zone-digest-12
* from section A.2 */
zonemd_generate_test("example", SRCDIRSTR "/testdata/zonemd.example_a2.zone",
1, 1, "31cefb03814f5062ad12fa951ba0ef5f8da6ae354a415767246f7dc932ceb1e742a2108f529db6a33a11c01493de358d");
/* https://tools.ietf.org/html/draft-ietf-dnsop-dns-zone-digest-12
* from section A.3 SHA384 digest */
zonemd_generate_test("example", SRCDIRSTR "/testdata/zonemd.example_a3.zone",
1, 1, "62e6cf51b02e54b9b5f967d547ce43136792901f9f88e637493daaf401c92c279dd10f0edb1c56f8080211f8480ee306");
/* https://tools.ietf.org/html/draft-ietf-dnsop-dns-zone-digest-12
* from section A.3 SHA512 digest*/
zonemd_generate_test("example", SRCDIRSTR "/testdata/zonemd.example_a3.zone",
1, 2, "08cfa1115c7b948c4163a901270395ea226a930cd2cbcf2fa9a5e6eb85f37c8a4e114d884e66f176eab121cb02db7d652e0cc4827e7a3204f166b47e5613fd27");
/* https://tools.ietf.org/html/draft-ietf-dnsop-dns-zone-digest-12
* from section A.4 */
zonemd_generate_test("uri.arpa", SRCDIRSTR "/testdata/zonemd.example_a4.zone",
1, 1, "1291b78ddf7669b1a39d014d87626b709b55774c5d7d58fadc556439889a10eaf6f11d615900a4f996bd46279514e473");
/* https://tools.ietf.org/html/draft-ietf-dnsop-dns-zone-digest-12
* from section A.5.
* Adjusted with renumbered B.root. */
zonemd_generate_test("root-servers.net", SRCDIRSTR "/testdata/zonemd.example_a5.zone",
1, 1, "5a9521d88984ee123d9626191e2a327a43a16fd4339dd4ecc13d8672d5bae527d066d33645e35778677800005247d199");
}
/** test the zonemd check routine */
static void zonemd_check_test(void)
{
const char* zname = "example.org";
char* zfile = SRCDIRSTR "/testdata/zonemd.example1.zone";
int scheme = 1;
int hashalgo = 2;
const char* digest = "20564D10F50A0CEBEC856C64032B7DFB53D3C449A421A5BC7A21F7627B4ACEA4DF29F2C6FE82ED9C23ADF6F4D420D5DD63EF6E6349D60FDAB910B65DF8D481B7";
const char* digestwrong = "20564D10F50A0CEBEC856C64032B7DFB53D3C449A421A5BC7A21F7627B4ACEA4DF29F2C6FE82ED9C23ADF6F4D420D5DD63EF6E6349D60FDAB910B65DF8D48100";
uint8_t hash[512], hashwrong[512];
size_t hashlen = 0, hashwronglen = 0;
struct auth_zones* az;
struct auth_zone* z;
int result;
struct regional* region = NULL;
struct sldns_buffer* buf = NULL;
char* reason = NULL;
if(!zonemd_hashalgo_supported(hashalgo))
return; /* cannot test unsupported algo */
unit_show_func("services/authzone.c", "auth_zone_generate_zonemd_check");
/* setup environment */
az = auth_zones_create();
unit_assert(az);
region = regional_create();
unit_assert(region);
buf = sldns_buffer_new(65535);
unit_assert(buf);
/* read file */
z = authtest_addzone(az, zname, zfile);
unit_assert(z);
lock_rw_wrlock(&z->lock);
z->zonemd_check = 1;
lock_rw_unlock(&z->lock);
hashlen = sizeof(hash);
if(sldns_str2wire_hex_buf(digest, hash, &hashlen) != 0) {
unit_assert(0); /* parse failure */
}
hashwronglen = sizeof(hashwrong);
if(sldns_str2wire_hex_buf(digestwrong, hashwrong, &hashwronglen) != 0) {
unit_assert(0); /* parse failure */
}
/* check return values of the check routine */
result = auth_zone_generate_zonemd_check(z, scheme, hashalgo,
hash, hashlen, region, buf, &reason);
unit_assert(result && reason == NULL);
result = auth_zone_generate_zonemd_check(z, 241, hashalgo,
hash, hashlen, region, buf, &reason);
unit_assert(result && strcmp(reason, "unsupported scheme")==0);
result = auth_zone_generate_zonemd_check(z, scheme, 242,
hash, hashlen, region, buf, &reason);
unit_assert(result && strcmp(reason, "unsupported algorithm")==0);
result = auth_zone_generate_zonemd_check(z, scheme, hashalgo,
hash, 2, region, buf, &reason);
unit_assert(!result && strcmp(reason, "digest length too small, less than 12")==0);
result = auth_zone_generate_zonemd_check(z, scheme, hashalgo,
hashwrong, hashwronglen, region, buf, &reason);
unit_assert(!result && strcmp(reason, "incorrect digest")==0);
result = auth_zone_generate_zonemd_check(z, scheme, hashalgo,
hashwrong, hashwronglen-3, region, buf, &reason);
unit_assert(!result && strcmp(reason, "incorrect digest length")==0);
/* delete environment */
auth_zones_delete(az);
regional_destroy(region);
sldns_buffer_free(buf);
if(verbosity >= VERB_ALGO) {
printf("\n");
}
}
/** zonemd test verify */
static void zonemd_verify_test(char* zname, char* zfile, char* tastr,
char* date_override, char* result_wanted)
{
time_t now = 0;
struct module_stack mods;
struct module_env env;
char* result = NULL;
struct auth_zone* z;
/* setup test harness */
memset(&env, 0, sizeof(env));
env.scratch = regional_create();
if(!env.scratch)
fatal_exit("out of memory");
env.scratch_buffer = sldns_buffer_new(65553);
if(!env.scratch_buffer)
fatal_exit("out of memory");
env.cfg = config_create();
if(!env.cfg)
fatal_exit("out of memory");
env.now = &now;
env.cfg->val_date_override = cfg_convert_timeval(date_override);
if(!env.cfg->val_date_override)
fatal_exit("could not parse datetime %s", date_override);
if(env.cfg->module_conf)
free(env.cfg->module_conf);
env.cfg->module_conf = strdup("validator iterator");
if(!env.cfg->module_conf)
fatal_exit("out of memory");
if(tastr) {
if(!cfg_strlist_insert(&env.cfg->trust_anchor_list,
strdup(tastr)))
fatal_exit("out of memory");
}
env.anchors = anchors_create();
if(!env.anchors)
fatal_exit("out of memory");
env.auth_zones = auth_zones_create();
if(!env.auth_zones)
fatal_exit("out of memory");
modstack_init(&mods);
if(!modstack_call_startup(&mods, env.cfg->module_conf, &env))
fatal_exit("could not modstack_startup");
if(!modstack_call_init(&mods, env.cfg->module_conf, &env))
fatal_exit("could not modstack_call_init");
env.mesh = mesh_create(&mods, &env);
if(!env.mesh)
fatal_exit("out of memory");
/* load data */
z = authtest_addzone(env.auth_zones, zname, zfile);
if(!z)
fatal_exit("could not addzone %s %s", zname, zfile);
/* test */
lock_rw_wrlock(&z->lock);
z->zonemd_check = 1;
auth_zone_verify_zonemd(z, &env, &mods, &result, 1, 0);
lock_rw_unlock(&z->lock);
if(verbosity >= VERB_ALGO) {
printf("auth zone %s: ZONEMD verification %s: %s\n", zname,
(strcmp(result, "ZONEMD verification successful")==0?"successful":"failed"),
result);
}
if(!result)
fatal_exit("out of memory");
unit_assert(strcmp(result, result_wanted) == 0);
if(strcmp(result, "ZONEMD verification successful") == 0 ||
strcmp(result, "DNSSEC verified nonexistence of ZONEMD") == 0 ||
strcmp(result, "no ZONEMD present") == 0) {
lock_rw_rdlock(&z->lock);
unit_assert(!z->zone_expired);
lock_rw_unlock(&z->lock);
} else {
lock_rw_rdlock(&z->lock);
unit_assert(z->zone_expired);
lock_rw_unlock(&z->lock);
}
free(result);
/* desetup test harness */
mesh_delete(env.mesh);
modstack_call_deinit(&mods, &env);
modstack_call_destartup(&mods, &env);
modstack_free(&mods);
auth_zones_delete(env.auth_zones);
anchors_delete(env.anchors);
config_delete(env.cfg);
regional_destroy(env.scratch);
sldns_buffer_free(env.scratch_buffer);
if(verbosity >= VERB_ALGO) {
printf("\n");
}
}
/** zonemd test verify suite */
static void zonemd_verify_tests(void)
{
unit_show_func("services/authzone.c", "auth_zone_verify_zonemd");
/* give trustanchor for unsigned zone, should fail */
zonemd_verify_test("example.org",
SRCDIRSTR "/testdata/zonemd.example1.zone",
"example.org. IN DS 55566 8 2 9c148338951ce1c3b5cd3da532f3d90dfcf92595148022f2c2fd98e5deee90af",
"20180302005009",
"verify DNSKEY RRset with trust anchor failed: have trust anchor, but zone has no DNSKEY");
/* unsigned zone without ZONEMD in it */
zonemd_verify_test("example.org",
SRCDIRSTR "/testdata/zonemd.example1.zone",
NULL,
"20180302005009",
"no ZONEMD present");
/* no trust anchor, so it succeeds for zone with a correct ZONEMD */
zonemd_verify_test("example.com",
SRCDIRSTR "/testdata/zonemd.example2.zone",
NULL,
"20180302005009",
"ZONEMD verification successful");
/* trust anchor for another zone, so it is indeterminate */
zonemd_verify_test("example.com",
SRCDIRSTR "/testdata/zonemd.example2.zone",
"example.org. IN DS 55566 8 2 9c148338951ce1c3b5cd3da532f3d90dfcf92595148022f2c2fd98e5deee90af",
"20180302005009",
"ZONEMD verification successful");
/* load a DNSSEC signed zone, but no trust anchor */
/* this zonefile has an incorrect ZONEMD digest, with correct
* DNSSEC signature. */
zonemd_verify_test("example.com",
SRCDIRSTR "/testdata/zonemd.example3.zone",
NULL,
"20180302005009",
"incorrect digest");
/* load a DNSSEC zone with NSEC3, but no trust anchor */
/* this zonefile has an incorrect ZONEMD digest, with correct
* DNSSEC signature. */
zonemd_verify_test("example.com",
SRCDIRSTR "/testdata/zonemd.example4.zone",
NULL,
"20180302005009",
"incorrect digest");
/* valid zonemd, in dnssec signed zone, no trust anchor*/
/* this zonefile has a correct ZONEMD digest and
* correct DNSSEC signature */
zonemd_verify_test("example.com",
SRCDIRSTR "/testdata/zonemd.example5.zone",
NULL,
"20180302005009",
"ZONEMD verification successful");
/* valid zonemd, in dnssec NSEC3 zone, no trust anchor*/
zonemd_verify_test("example.com",
SRCDIRSTR "/testdata/zonemd.example6.zone",
NULL,
"20180302005009",
"ZONEMD verification successful");
/* load a DNSSEC signed zone with a trust anchor, valid ZONEMD */
zonemd_verify_test("example.com",
SRCDIRSTR "/testdata/zonemd.example5.zone",
"example.com. IN DS 55566 8 2 9c148338951ce1c3b5cd3da532f3d90dfcf92595148022f2c2fd98e5deee90af",
"20201020135527",
"ZONEMD verification successful");
/* load a DNSSEC NSEC3 signed zone with a trust anchor, valid ZONEMD */
zonemd_verify_test("example.com",
SRCDIRSTR "/testdata/zonemd.example6.zone",
"example.com. IN DS 55566 8 2 9c148338951ce1c3b5cd3da532f3d90dfcf92595148022f2c2fd98e5deee90af",
"20201020135527",
"ZONEMD verification successful");
/* load a DNSSEC NSEC zone without ZONEMD */
zonemd_verify_test("example.com",
SRCDIRSTR "/testdata/zonemd.example7.zone",
"example.com. IN DS 55566 8 2 9c148338951ce1c3b5cd3da532f3d90dfcf92595148022f2c2fd98e5deee90af",
"20201020135527",
"DNSSEC verified nonexistence of ZONEMD");
/* load a DNSSEC NSEC3 zone without ZONEMD */
zonemd_verify_test("example.com",
SRCDIRSTR "/testdata/zonemd.example8.zone",
"example.com. IN DS 55566 8 2 9c148338951ce1c3b5cd3da532f3d90dfcf92595148022f2c2fd98e5deee90af",
"20201020135527",
"DNSSEC verified nonexistence of ZONEMD");
/* load DNSSEC zone but RRSIG on ZONEMD is wrong */
zonemd_verify_test("example.com",
SRCDIRSTR "/testdata/zonemd.example9.zone",
"example.com. IN DS 55566 8 2 9c148338951ce1c3b5cd3da532f3d90dfcf92595148022f2c2fd98e5deee90af",
"20201020135527",
#ifdef HAVE_SSL
"DNSSEC verify failed for ZONEMD RRset: signature crypto failed"
#else /* HAVE_NETTLE */
"DNSSEC verify failed for ZONEMD RRset: RSA signature verification failed"
#endif
);
/* load DNSSEC zone but RRSIG on SOA is wrong */
zonemd_verify_test("example.com",
SRCDIRSTR "/testdata/zonemd.example10.zone",
"example.com. IN DS 55566 8 2 9c148338951ce1c3b5cd3da532f3d90dfcf92595148022f2c2fd98e5deee90af",
"20201020135527",
#ifdef HAVE_SSL
"DNSSEC verify failed for SOA RRset: signature crypto failed"
#else /* HAVE_NETTLE */
"DNSSEC verify failed for SOA RRset: RSA signature verification failed"
#endif
);
/* load DNSSEC zone without ZONEMD, but NSEC bitmap says it exists */
zonemd_verify_test("example.com",
SRCDIRSTR "/testdata/zonemd.example11.zone",
"example.com. IN DS 55566 8 2 9c148338951ce1c3b5cd3da532f3d90dfcf92595148022f2c2fd98e5deee90af",
"20201020135527",
"DNSSEC NSEC bitmap says type ZONEMD exists");
/* load DNSSEC zone without ZONEMD, but NSEC3 bitmap says it exists */
zonemd_verify_test("example.com",
SRCDIRSTR "/testdata/zonemd.example12.zone",
"example.com. IN DS 55566 8 2 9c148338951ce1c3b5cd3da532f3d90dfcf92595148022f2c2fd98e5deee90af",
"20201020135527",
"DNSSEC NSEC3 bitmap says type ZONEMD exists");
/* load DNSSEC zone without ZONEMD, but RRSIG on NSEC not okay */
zonemd_verify_test("example.com",
SRCDIRSTR "/testdata/zonemd.example13.zone",
"example.com. IN DS 55566 8 2 9c148338951ce1c3b5cd3da532f3d90dfcf92595148022f2c2fd98e5deee90af",
"20201020135527",
#ifdef HAVE_SSL
"DNSSEC verify failed for NSEC RRset: signature crypto failed"
#else /* HAVE_NETTLE */
"DNSSEC verify failed for NSEC RRset: RSA signature verification failed"
#endif
);
/* load DNSSEC zone without ZONEMD, but RRSIG on NSEC3 not okay */
zonemd_verify_test("example.com",
SRCDIRSTR "/testdata/zonemd.example14.zone",
"example.com. IN DS 55566 8 2 9c148338951ce1c3b5cd3da532f3d90dfcf92595148022f2c2fd98e5deee90af",
"20201020135527",
#ifdef HAVE_SSL
"DNSSEC verify failed for NSEC3 RRset: signature crypto failed"
#else /* HAVE_NETTLE */
"DNSSEC verify failed for NSEC3 RRset: RSA signature verification failed"
#endif
);
/* load DNSSEC zone, with ZONEMD, but DNSKEY RRSIG is not okay. */
zonemd_verify_test("example.com",
SRCDIRSTR "/testdata/zonemd.example15.zone",
"example.com. IN DS 55566 8 2 9c148338951ce1c3b5cd3da532f3d90dfcf92595148022f2c2fd98e5deee90af",
"20201020135527",
#ifdef HAVE_SSL
"verify DNSKEY RRset with trust anchor failed: signature crypto failed"
#else /* HAVE_NETTLE */
"verify DNSKEY RRset with trust anchor failed: RSA signature verification failed"
#endif
);
/* load DNSSEC zone, but trust anchor mismatches DNSKEY */
zonemd_verify_test("example.com",
SRCDIRSTR "/testdata/zonemd.example5.zone",
/* okay anchor is
"example.com. IN DS 55566 8 2 9c148338951ce1c3b5cd3da532f3d90dfcf92595148022f2c2fd98e5deee90af", */
"example.com. IN DS 55566 8 2 0000000000111111222223333444444dfcf92595148022f2c2fd98e5deee90af",
"20201020135527",
"verify DNSKEY RRset with trust anchor failed: DS hash mismatches key");
/* load DNSSEC zone, but trust anchor fails because the zone
* has expired signatures. We set the date for it */
zonemd_verify_test("example.com",
SRCDIRSTR "/testdata/zonemd.example5.zone",
"example.com. IN DS 55566 8 2 9c148338951ce1c3b5cd3da532f3d90dfcf92595148022f2c2fd98e5deee90af",
/* okay date: "20201020135527", */
"20221020135527",
"verify DNSKEY RRset with trust anchor failed: signature expired");
/* duplicate zonemd with same scheme and algorithm */
zonemd_verify_test("example.com",
SRCDIRSTR "/testdata/zonemd.example16.zone",
NULL,
"20180302005009",
"ZONEMD RRSet contains more than one RR with the same scheme and hash algorithm");
/* different capitalisation of ns name and owner names, should
* be canonicalized. */
zonemd_verify_test("example.com",
SRCDIRSTR "/testdata/zonemd.example17.zone",
NULL,
"20180302005009",
"ZONEMD verification successful");
}
/** zonemd unit tests */
void zonemd_test(void)
{
unit_show_feature("zonemd");
zonemd_generate_tests();
zonemd_check_test();
zonemd_verify_tests();
}
@@ -0,0 +1,31 @@
server:
verbosity: 5
num-threads: 1 # This is dynamically handled by the test when needed
interface: 127.0.0.1@@PORT@
use-syslog: no
directory: ""
pidfile: "unbound.pid"
chroot: ""
username: ""
do-not-query-localhost: no
access-control: 127.0.0.1 allow_snoop
access-control-view: 127.0.0.1 testview
msg-cache-size: 4m
rrset-cache-size: 4m
minimal-responses: yes
trust-anchor: "always.empty. 3600 IN DS 50602 8 2 FA8EE175C47325F4BD46D8A4083C3EBEB11C977D689069F2B41F1A29 B22446B1" # This is nonsense, just to kick the validator
view:
name: testview
view-first: yes # Allow falling back to global local data
remote-control:
control-enable: yes
control-interface: 127.0.0.1
# control-interface: ::1
control-port: @CONTROL_PORT@
server-key-file: "unbound_server.key"
server-cert-file: "unbound_server.pem"
control-key-file: "unbound_control.key"
control-cert-file: "unbound_control.pem"
forward-zone:
name: "."
forward-addr: "127.0.0.1@@TOPORT@"
@@ -0,0 +1,493 @@
# #-- 09-unbound-control.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
# End the test
# $1: exit value
end () {
echo "> cat logfiles"
cat fwd.log
cat unbound.log
exit $1
}
# Expect a given exit value of the previous command
# $1: the expected exit value
# $2: optional text to print when failing
expect_exit_value () {
if test $? -ne $1; then
if test -z "$2"; then
if test $1 -eq 1; then
msg="on error"
else
msg="after success"
fi
else
msg="$2"
fi
echo "wrong exit value $msg"
end 1
fi
}
# Helper function for quering
# $@: at least the domain name to query and optional dig arguments
query () {
echo "> dig $@"
dig @127.0.0.1 -p $UNBOUND_PORT $@ | tee outfile
}
# Expect something in the answer
# $1: expected regular expression
expect_answer () {
echo "> check answer for \"$1\""
if grep "$1" outfile; then
echo "OK"
else
echo "Not OK"
end 1
fi
}
# Fail the test for unexpected answers
# $1: unexpected regular expression
fail_answer () {
echo "> \"$1\" should not be in answer"
if grep "$1" outfile; then
echo "Not OK"
end 1
else
echo "OK"
fi
}
# Issue an unbound-control command
# $@: command arguments
control_command() {
echo "$PRE/unbound-control $@"
$PRE/unbound-control $@ > outfile
exitstatus=$?
cat outfile
return $exitstatus
}
# 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_reload
}
# Reload the server for a clean state and populate the cache
clean_reload_and_fill_cache () {
clean_reload
echo "> Populating the cache"
query www.example.com
expect_answer "10.20.30.40"
if test "$have_threads" = "no"; then
# Try to get the answer in all processes' cache.
for (( try=0 ; try < num_threads * 2 * 2 ; try++ )) ; do
query www.example.com
expect_answer "10.20.30.40"
done
fi
}
# Dump the cache contents
# $@: optional options to unbound-control
cache_dump () {
echo "$PRE/unbound-control $@ dump_cache > cache.dump"
$PRE/unbound-control $@ dump_cache > cache.dump
}
# Load cache contents
# $@: optional options to unbound-control
cache_load () {
echo "$PRE/unbound-control $@ load_cache < cache.dump"
$PRE/unbound-control $@ load_cache < cache.dump
}
# Expect an entry in the cache dump
# $1: expected regular expression
expect_in_cache_dump () {
echo "> check cache dump for \"$1\""
if grep "$1" cache.dump; then
echo "OK cache dump"
else
echo "Not OK cache dump"
end 1
fi
}
# Fail the test for unexpected entry in the cache dump
# $1: unexpected regular expression
fail_in_cache_dump () {
echo "> \"$1\" should not be in cache dump"
if grep "$1" cache.dump; then
echo "Not OK cache dump"
end 1
else
echo "OK cache dump"
fi
}
# Check if multi-threading or multi-process environment
have_threads="no"
if grep "define HAVE_PTHREAD 1" $PRE/config.h; then have_threads="yes"; fi
if grep "define HAVE_SOLARIS_THREADS 1" $PRE/config.h; then have_threads="yes"; fi
if grep "define HAVE_WINDOWS_THREADS 1" $PRE/config.h; then have_threads="yes"; fi
# start the test; keep the original conf file around
cp ub.conf orig.conf
# START - thread configuration
# Do both single thread/process and multi thread/process runs.
# The number of threads can only go up from the initial configuration between
# reloads so starting with 1.
for num_threads in 1 4; do
cp orig.conf ub.conf
echo "> setting num-threads: $num_threads"
echo "server: num-threads: $num_threads" >> ub.conf
cp ub.conf main.conf
clean_reload
teststep "exit value is 1 on usage"
control_command -h
expect_exit_value 1 "for usage"
# use lock-verify if possible
teststep "test if the server is up"
query www.example.com.
expect_answer "10.20.30.40"
teststep "exit value is 1 when a bad command is given"
control_command -c ub.conf blablargh
expect_exit_value 1
# reload the server. test if the server came up by putting a new
# local-data element in the server.
teststep "reload the server"
echo "server: local-data: 'afterreload. IN A 5.6.7.8'" >> ub.conf
control_reload
query afterreload.
expect_answer "5.6.7.8"
teststep "must have had at least 1 query since reload"
control_command -c ub.conf stats
expect_exit_value 0
expect_answer "^total.num.queries=[1-9][0-9]*$"
teststep "check verbosity"
control_command -c ub.conf verbosity 2
expect_exit_value 0
teststep "check syntax error in parse"
control_command -c ub.conf verbosity jkdf
expect_exit_value 1
teststep "check bad credentials"
cp ub.conf bad.conf
cat conf.bad_credentials >> bad.conf
control_command -c bad.conf verbosity 2
expect_exit_value 1
teststep "check spoofed client credentials"
rm -f bad.conf
cp ub.conf bad.conf
cat conf.spoofed_credentials >> bad.conf
control_command -c bad.conf verbosity 2
expect_exit_value 1
teststep "clean reload"
clean_reload
# The flush negative only works if the server is either on 1 thread,
# or there is threading enabled. Multiple processes does not work for the
# test, since the printout does not have the stats of a global cache.
if test $num_threads -le 1 -o "$have_threads" = "yes"; then
teststep "Check negative flushing"
query always.empty.
expect_answer "SERVFAIL"
query always.empty. DNSKEY
expect_answer "SERVFAIL"
control_command -c ub.conf flush_negative
expect_exit_value 0
expect_answer "^ok removed .*, 3 messages and 1 key entries"
control_command -c ub.conf flush_negative
expect_exit_value 0
expect_answer "^ok removed .*, 0 messages and 0 key entries"
else
echo "> skip Check negative flushing, because no threads"
fi
teststep "create a new local zone"
control_command -c ub.conf local_zone example.net static
expect_exit_value 0
control_command -c ub.conf local_data www.example.net A 192.0.2.1
expect_exit_value 0
teststep "check that www.example.net exists"
query www.example.net.
expect_answer "192.0.2.1"
teststep "check that mail.example.net has nxdomain"
query mail.example.net.
expect_answer "NXDOMAIN"
teststep "remove www.example.net - check it gets nxdomain"
control_command -c ub.conf local_data_remove www.example.net
expect_exit_value 0
query www.example.net.
expect_answer "NXDOMAIN"
teststep "remove nonexistent name - check bug#287(segfault) does not happen"
control_command -c ub.conf local_data_remove test.example.net
# if crash then then we get: error: could not SSL_read from unbound-control
expect_exit_value 0
teststep "remove example.net - check its gone"
control_command -c ub.conf local_zone_remove example.net
expect_exit_value 0
query www.example.net.
expect_answer "SERVFAIL"
teststep "load local-zones from file"
control_command -c ub.conf local_zones < local_zones
expect_exit_value 0
query localzonefromfile
expect_answer "REFUSED"
if test "$have_threads" = "no"; then
# Try to see if a process other than the first one
# has updated data from stdin.
for (( try=0 ; try < num_threads * 2 ; try++ )) ; do
query localzonefromfile
expect_answer "REFUSED"
done
fi
teststep "load local-data from file"
control_command -c ub.conf local_datas < local_data
expect_exit_value 0
query -t txt localdatafromfile
expect_answer "local data from file OK"
if test "$have_threads" = "no"; then
# Try to see if a process other than the first one
# has updated data from stdin.
for (( try=0 ; try < num_threads * 2 ; try++ )) ; do
query -t txt localdatafromfile
expect_answer "local data from file OK"
done
fi
teststep "load view-local-data from file"
control_command -c ub.conf view_local_datas testview < view_local_data
expect_exit_value 0
control_command -c ub.conf view_list_local_zones testview
query -t txt viewlocaldatafromfile
expect_answer "view local data from file OK"
if test "$have_threads" = "no"; then
# Try to see if a process other than the first one
# has updated data from stdin.
for (( try=0 ; try < num_threads * 2 ; try++ )) ; do
query -t txt viewlocaldatafromfile
expect_answer "view local data from file OK"
done
fi
teststep "remove local-zone, local-data and view-local-data from file"
control_command -c ub.conf local_zones_remove < local_zones_remove
expect_exit_value 0
control_command -c ub.conf local_datas_remove < local_data_remove
expect_exit_value 0
control_command -c ub.conf view_local_datas_remove testview < view_local_data_remove
expect_exit_value 0
control_command -c ub.conf list_local_zones
fail_answer "localzonefromfile"
fail_answer "local data from file OK"
expect_answer "otherlocalzone"
control_command -c ub.conf view_list_local_data testview
fail_answer "viewlocaldatafromfile"
teststep "flushing"
control_command -c ub.conf flush www.example.net
expect_exit_value 0
control_command -c ub.conf flush_type www.example.net TXT
expect_exit_value 0
control_command -c ub.conf flush_zone example.net
expect_exit_value 0
# START - single thread/process tests only
if test $num_threads -le 1; then
clean_reload_and_fill_cache
teststep "dump the cache"
query www.example.com.
cache_dump -c ub.conf
expect_exit_value 0
cat cache.dump
expect_in_cache_dump "10.20.30.40"
control_command -c ub.conf lookup www.example.com
expect_exit_value 0
# answer to lookup is meaningless because of use a forwarder, oh well.
teststep "load the cache dump"
cache_load -c ub.conf
expect_exit_value 0
query www.example.com. +nordflag
expect_answer "10.20.30.40"
else
echo ""
echo "> skip test parts that need single thread/process"
fi
# END - single thread/process tests only
clean_reload_and_fill_cache
teststep "reload and check cache - should be empty"
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_reload reload_keep_cache
query www.example.com +nordflag
expect_answer "10.20.30.40"
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_reload reload_keep_cache
query www.example.com +nordflag
fail_answer "10.20.30.40"
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_reload reload_keep_cache
query www.example.com +nordflag
fail_answer "10.20.30.40"
# START - have_threads tests
# This part of the test needs threads for combined output.
if test "$have_threads" = "yes"; then
clean_reload_and_fill_cache
teststep "change num-threads and reload_keep_cache - should be empty"
echo "server: num-threads: 2" >> ub.conf
control_reload reload_keep_cache
query www.example.com +nordflag
fail_answer "10.20.30.40"
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_reload reload_keep_cache
query www.example.com +nordflag
expect_answer "10.20.30.40"
else
echo ""
echo "> skip test parts that need threads, have_threads=no"
fi
# END - have_threads tests
done
# END - thread configuration
teststep "now stop the server"
control_command -c ub.conf stop
expect_exit_value 0
teststep "see if the server has really exited"
TRY_MAX=20
for (( try=0 ; try <= $TRY_MAX ; try++ )) ; do
if kill -0 $UNBOUND_PID 2>&1 | tee tmp.$$; then
echo "not stopped yet, waiting"
sleep 1
else
echo "stopped OK; break"
break;
fi
if grep "No such process" tmp.$$; then
echo "stopped OK; break"
break;
fi
done
if kill -0 $UNBOUND_PID; then
echo "still up!"
echo "not stopped, failure"
end 1
else
echo "stopped OK"
if test -f ublocktrace.0; then
if $PRE/lock-verify ublocktrace.*; then
echo "lock-verify test worked."
else
echo "lock-verify test failed."
end 1
fi
fi
fi
end 0
@@ -0,0 +1,44 @@
; nameserver test file
$TTL 3600
ENTRY_BEGIN
MATCH opcode qtype qname
REPLY QR AA NOERROR
ADJUST copy_id
SECTION QUESTION
www.example.com. IN A
SECTION ANSWER
www.example.com. IN A 10.20.30.40
ENTRY_END
ENTRY_BEGIN
MATCH opcode qtype qname
REPLY QR AA SERVFAIL
ADJUST copy_id
SECTION QUESTION
www.example.net. IN A
ENTRY_END
ENTRY_BEGIN
MATCH opcode qtype qname
REPLY QR AA NOERROR
ADJUST copy_id
SECTION QUESTION
always.empty. IN A
ENTRY_END
ENTRY_BEGIN
MATCH opcode qtype qname
REPLY QR AA NOERROR
ADJUST copy_id
SECTION QUESTION
always.empty. IN DNSKEY
ENTRY_END
ENTRY_BEGIN
MATCH opcode qtype qname
REPLY QR AA NOERROR
ADJUST copy_id
SECTION QUESTION
_ta-c5aa.always.empty. IN NULL
ENTRY_END
@@ -0,0 +1,198 @@
server:
verbosity: 7
use-syslog: no
directory: ""
pidfile: "unbound.pid"
chroot: ""
username: ""
module-config: "respip validator iterator" # respip for the RPZ part
do-not-query-localhost: no
use-caps-for-id: no
define-tag: "one two refuse rpz-one rpz-two rpz-nx"
# Interface configuration for IPv4
interface: @IPV4_ADDR@@@PORT_ALLOW@
interface: @IPV4_ADDR@@@PORT_DENY@
interface: @IPV4_ADDR@@@PORT_REFUSE@
interface: @IPV4_ADDR@@@PORT_TAG_1@
interface: @IPV4_ADDR@@@PORT_TAG_2@
interface: @IPV4_ADDR@@@PORT_TAG_3@
interface: @IPV4_ADDR@@@PORT_RPZ_1@
interface: @IPV4_ADDR@@@PORT_RPZ_2@
interface: @IPV4_ADDR@@@PORT_RPZ_NX@
interface: @IPV4_ADDR@@@PORT_VIEW_INT@
interface: @IPV4_ADDR@@@PORT_VIEW_EXT@
interface: @IPV4_ADDR@@@PORT_VIEW_INTEXT@
interface-action: @IPV4_ADDR@@@PORT_ALLOW@ allow
interface-action: @IPV4_ADDR@@@PORT_DENY@ deny
# interface-action: @IPV4_ADDR@@@PORT_REFUSE@ refuse # This is the default action
interface-action: @IPV4_ADDR@@@PORT_TAG_1@ allow
interface-action: @IPV4_ADDR@@@PORT_TAG_2@ allow
interface-action: @IPV4_ADDR@@@PORT_TAG_3@ allow
interface-action: @IPV4_ADDR@@@PORT_RPZ_1@ allow
interface-action: @IPV4_ADDR@@@PORT_RPZ_2@ allow
interface-action: @IPV4_ADDR@@@PORT_RPZ_NX@ allow
interface-action: @IPV4_ADDR@@@PORT_VIEW_INT@ allow
interface-action: @IPV4_ADDR@@@PORT_VIEW_EXT@ allow
interface-action: @IPV4_ADDR@@@PORT_VIEW_INTEXT@ allow
interface-tag: @IPV4_ADDR@@@PORT_TAG_1@ "one"
interface-tag: @IPV4_ADDR@@@PORT_TAG_2@ "two"
interface-tag: @IPV4_ADDR@@@PORT_TAG_3@ "refuse"
interface-tag: @IPV4_ADDR@@@PORT_RPZ_1@ "rpz-one"
interface-tag: @IPV4_ADDR@@@PORT_RPZ_2@ "rpz-two"
interface-tag: @IPV4_ADDR@@@PORT_RPZ_NX@ "rpz-nx"
interface-tag-action: @IPV4_ADDR@@@PORT_TAG_1@ one redirect
interface-tag-data: @IPV4_ADDR@@@PORT_TAG_1@ one "A 1.1.1.1"
interface-tag-action: @IPV4_ADDR@@@PORT_TAG_2@ two redirect
interface-tag-data: @IPV4_ADDR@@@PORT_TAG_2@ two "A 2.2.2.2"
interface-tag-action: @IPV4_ADDR@@@PORT_TAG_3@ refuse always_refuse
interface-view: @IPV4_ADDR@@@PORT_VIEW_INT@ "int"
interface-view: @IPV4_ADDR@@@PORT_VIEW_EXT@ "ext"
interface-view: @IPV4_ADDR@@@PORT_VIEW_INTEXT@ "intext"
# Mirrored interface configuration for IPv6
interface: @IPV6_ADDR@@@PORT_ALLOW@
interface: @IPV6_ADDR@@@PORT_DENY@
interface: @IPV6_ADDR@@@PORT_REFUSE@
interface: @IPV6_ADDR@@@PORT_TAG_1@
interface: @IPV6_ADDR@@@PORT_TAG_2@
interface: @IPV6_ADDR@@@PORT_TAG_3@
interface: @IPV6_ADDR@@@PORT_RPZ_1@
interface: @IPV6_ADDR@@@PORT_RPZ_2@
interface: @IPV6_ADDR@@@PORT_RPZ_NX@
interface: @IPV6_ADDR@@@PORT_VIEW_INT@
interface: @IPV6_ADDR@@@PORT_VIEW_EXT@
interface: @IPV6_ADDR@@@PORT_VIEW_INTEXT@
interface-action: @IPV6_ADDR@@@PORT_ALLOW@ allow
interface-action: @IPV6_ADDR@@@PORT_DENY@ deny
# interface-action: @IPV6_ADDR@@@PORT_REFUSE@ refuse # This is the default action
interface-action: @IPV6_ADDR@@@PORT_TAG_1@ allow
interface-action: @IPV6_ADDR@@@PORT_TAG_2@ allow
interface-action: @IPV6_ADDR@@@PORT_TAG_3@ allow
interface-action: @IPV6_ADDR@@@PORT_RPZ_1@ allow
interface-action: @IPV6_ADDR@@@PORT_RPZ_2@ allow
interface-action: @IPV6_ADDR@@@PORT_RPZ_NX@ allow
interface-action: @IPV6_ADDR@@@PORT_VIEW_INT@ allow
interface-action: @IPV6_ADDR@@@PORT_VIEW_EXT@ allow
interface-action: @IPV6_ADDR@@@PORT_VIEW_INTEXT@ allow
interface-tag: @IPV6_ADDR@@@PORT_TAG_1@ "one"
interface-tag: @IPV6_ADDR@@@PORT_TAG_2@ "two"
interface-tag: @IPV6_ADDR@@@PORT_TAG_3@ "refuse"
interface-tag: @IPV6_ADDR@@@PORT_RPZ_1@ "rpz-one"
interface-tag: @IPV6_ADDR@@@PORT_RPZ_2@ "rpz-two"
interface-tag: @IPV6_ADDR@@@PORT_RPZ_NX@ "rpz-nx"
interface-tag-action: @IPV6_ADDR@@@PORT_TAG_1@ one redirect
interface-tag-data: @IPV6_ADDR@@@PORT_TAG_1@ one "A 1.1.1.1"
interface-tag-action: @IPV6_ADDR@@@PORT_TAG_2@ two redirect
interface-tag-data: @IPV6_ADDR@@@PORT_TAG_2@ two "A 2.2.2.2"
interface-tag-action: @IPV6_ADDR@@@PORT_TAG_3@ refuse always_refuse
interface-view: @IPV6_ADDR@@@PORT_VIEW_INT@ "int"
interface-view: @IPV6_ADDR@@@PORT_VIEW_EXT@ "ext"
interface-view: @IPV6_ADDR@@@PORT_VIEW_INTEXT@ "intext"
# Mirrored interface configuration for interface name
interface: @INTERFACE@@@PORT_ALLOW@
interface: @INTERFACE@@@PORT_DENY@
interface: @INTERFACE@@@PORT_REFUSE@
interface: @INTERFACE@@@PORT_TAG_1@
interface: @INTERFACE@@@PORT_TAG_2@
interface: @INTERFACE@@@PORT_TAG_3@
interface: @INTERFACE@@@PORT_RPZ_1@
interface: @INTERFACE@@@PORT_RPZ_2@
interface: @INTERFACE@@@PORT_RPZ_NX@
interface: @INTERFACE@@@PORT_VIEW_INT@
interface: @INTERFACE@@@PORT_VIEW_EXT@
interface: @INTERFACE@@@PORT_VIEW_INTEXT@
interface-action: @INTERFACE@@@PORT_ALLOW@ allow
interface-action: @INTERFACE@@@PORT_DENY@ deny
# interface-action: @INTERFACE@@@PORT_REFUSE@ refuse # This is the default action
interface-action: @INTERFACE@@@PORT_TAG_1@ allow
interface-action: @INTERFACE@@@PORT_TAG_2@ allow
interface-action: @INTERFACE@@@PORT_TAG_3@ allow
interface-action: @INTERFACE@@@PORT_RPZ_1@ allow
interface-action: @INTERFACE@@@PORT_RPZ_2@ allow
interface-action: @INTERFACE@@@PORT_RPZ_NX@ allow
interface-action: @INTERFACE@@@PORT_VIEW_INT@ allow
interface-action: @INTERFACE@@@PORT_VIEW_EXT@ allow
interface-action: @INTERFACE@@@PORT_VIEW_INTEXT@ allow
interface-tag: @INTERFACE@@@PORT_TAG_1@ "one"
interface-tag: @INTERFACE@@@PORT_TAG_2@ "two"
interface-tag: @INTERFACE@@@PORT_TAG_3@ "refuse"
interface-tag: @INTERFACE@@@PORT_RPZ_1@ "rpz-one"
interface-tag: @INTERFACE@@@PORT_RPZ_2@ "rpz-two"
interface-tag: @INTERFACE@@@PORT_RPZ_NX@ "rpz-nx"
interface-tag-action: @INTERFACE@@@PORT_TAG_1@ one redirect
interface-tag-data: @INTERFACE@@@PORT_TAG_1@ one "A 1.1.1.1"
interface-tag-action: @INTERFACE@@@PORT_TAG_2@ two redirect
interface-tag-data: @INTERFACE@@@PORT_TAG_2@ two "A 2.2.2.2"
interface-tag-action: @INTERFACE@@@PORT_TAG_3@ refuse always_refuse
interface-view: @INTERFACE@@@PORT_VIEW_INT@ "int"
interface-view: @INTERFACE@@@PORT_VIEW_EXT@ "ext"
interface-view: @INTERFACE@@@PORT_VIEW_INTEXT@ "intext"
# Interface with scope_id
interface: @INTERFACE@vlan50@@PORT_ALLOW@
interface: @INTERFACE@vlan51@@PORT_ALLOW@
interface-tag: @INTERFACE@vlan50@@PORT_ALLOW@ "one"
interface-tag: @INTERFACE@vlan51@@PORT_ALLOW@ "two"
interface-action: @INTERFACE@vlan50@@PORT_ALLOW@ allow
interface-action: @INTERFACE@vlan51@@PORT_ALLOW@ allow
local-zone: one.vtest. static
local-data: "one.vtest. A 1.1.1.1"
local-zone-tag: one.vtest. "one"
local-zone: two.vtest. static
local-data: "two.vtest. A 2.2.2.2"
local-zone-tag: two.vtest. "two"
# Local zones configuration
local-zone: local. transparent
local-data: "local. A 0.0.0.0"
local-zone-tag: local. "one two refuse"
# Views configuration
view:
name: "int"
view-first: yes
local-zone: "." refuse
local-zone: "internal" transparent
view:
name: "ext"
view-first: yes
local-zone: "internal" refuse
view:
name: "intext"
view-first: yes
# RPZ configuration
rpz:
name: "rpz-one"
zonefile: "rpz-one.zone"
tags: "rpz-one"
rpz:
name: "rpz-two"
zonefile: "rpz-two.zone"
tags: "rpz-two"
rpz:
name: "rpz-nx"
zonefile: "rpz-nx.zone"
tags: "rpz-nx"
# Stubs configuration
forward-zone:
name: "."
forward-addr: @IPV4_ADDR@@@FORWARD_PORT@
stub-zone:
name: "internal"
stub-addr: @IPV4_ADDR@@@STUB_PORT@
@@ -0,0 +1,268 @@
# #-- acl_interface.test.scenario --#
# 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
ip addr add $IPV4_ADDR dev lo
ip addr add $IPV6_ADDR dev lo
ip link set lo up
ip link add $INTERFACE type dummy
ip addr add $INTERFACE_ADDR_1 dev $INTERFACE
ip addr add $INTERFACE_ADDR_2 dev $INTERFACE
ip addr add $INTERFACE_ADDR_3 dev $INTERFACE
ip addr add $INTERFACE_ADDR_4 dev $INTERFACE
ip link set $INTERFACE up
ip link add ${INTERFACE}vlan50 type dummy
ip addr add fe80::2/64 dev ${INTERFACE}vlan50
ip link add ${INTERFACE}vlan51 type dummy
ip addr add fe80::2/64 dev ${INTERFACE}vlan51
ip link set ${INTERFACE}vlan50 up
ip link set ${INTERFACE}vlan51 up
ip addr show
# start the forwarder in the background
get_ldns_testns
$LDNS_TESTNS -p $FORWARD_PORT acl_interface.testns >fwd.log 2>&1 &
FWD_PID=$!
echo "FWD_PID=$FWD_PID" >> .tpkg.var.test
# start the stub in the background
$LDNS_TESTNS -p $STUB_PORT acl_interface.testns2 >fwd2.log 2>&1 &
STUB_PID=$!
echo "STUB_PID=$STUB_PID" >> .tpkg.var.test
# start unbound in the background
$PRE/unbound -d -c ub.conf >unbound.log 2>&1 &
UNBOUND_PID=$!
echo "UNBOUND_PID=$UNBOUND_PID" >> .tpkg.var.test
cat .tpkg.var.test
wait_ldns_testns_up fwd.log
wait_ldns_testns_up fwd2.log
wait_unbound_up unbound.log
end () {
echo "> cat logfiles"
cat fwd.log
cat fwd2.log
cat unbound.log
exit $1
}
# Query for the given domain to the given port
# $1: address family [4, 6]
# $2: port
# $3: dname
query () {
addr=$IPV4_ADDR
if test "$1" -eq 6; then
addr=$IPV6_ADDR
fi
echo "> dig -p $2 $3"
dig @"$addr" -p $2 $3 | tee outfile
}
# Query for the given domain to the given port
# $1: address
# $2: port
# $3: dname
query_addr () {
echo "> dig @$1 -p $2 $3"
dig @"$1" -p $2 $3 | tee outfile
}
expect_refused () {
echo "> check answer for REFUSED"
if grep "REFUSED" outfile; then
echo "OK"
else
echo "Not OK"
end 1
fi
}
expect_nx_answer () {
echo "> check answer for NXDOMAIN"
if grep "NXDOMAIN" outfile; then
echo "OK"
else
echo "Not OK"
end 1
fi
}
expect_external_answer () {
echo "> check external answer"
if grep "1.2.3.4" outfile; then
echo "OK"
else
echo "Not OK"
end 1
fi
}
expect_internal_answer () {
echo "> check internal answer"
if grep "10.20.30.40" outfile; then
echo "OK"
else
echo "Not OK"
end 1
fi
}
expect_tag_one_answer () {
echo "> check tag 'one' answer"
if grep "1.1.1.1" outfile; then
echo "OK"
else
echo "Not OK"
end 1
fi
}
expect_tag_two_answer () {
echo "> check tag 'two' answer"
if grep "2.2.2.2" outfile; then
echo "OK"
else
echo "Not OK"
end 1
fi
}
expect_rpz_one_answer () {
echo "> check tag 'one' answer"
if grep "11.11.11.11" outfile; then
echo "OK"
else
echo "Not OK"
end 1
fi
}
expect_rpz_two_answer () {
echo "> check tag 'two' answer"
if grep "22.22.22.22" outfile; then
echo "OK"
else
echo "Not OK"
end 1
fi
}
# do the test
for i in 4 6; do
query $i $PORT_REFUSE "www.external"
expect_refused
query $i $PORT_REFUSE "www.internal"
expect_refused
query $i $PORT_ALLOW "www.external"
expect_external_answer
query $i $PORT_ALLOW "www.internal"
expect_internal_answer
query $i $PORT_TAG_1 "local"
expect_tag_one_answer
query $i $PORT_TAG_2 "local"
expect_tag_two_answer
query $i $PORT_TAG_3 "local"
expect_refused
query $i $PORT_RPZ_1 "local"
expect_rpz_one_answer
query $i $PORT_RPZ_2 "local"
expect_rpz_two_answer
query $i $PORT_RPZ_NX "local"
expect_nx_answer
query $i $PORT_VIEW_INT "www.internal"
expect_internal_answer
query $i $PORT_VIEW_INT "www.external"
expect_refused
query $i $PORT_VIEW_EXT "www.internal"
expect_refused
query $i $PORT_VIEW_EXT "www.external"
expect_external_answer
query $i $PORT_VIEW_INTEXT "www.internal"
expect_internal_answer
query $i $PORT_VIEW_INTEXT "www.external"
expect_external_answer
done
for addr in $INTERFACE_ADDR_1 $INTERFACE_ADDR_2 $INTERFACE_ADDR_3 $INTERFACE_ADDR_4; do
query_addr $addr $PORT_REFUSE "www.external"
expect_refused
query_addr $addr $PORT_REFUSE "www.internal"
expect_refused
query_addr $addr $PORT_ALLOW "www.external"
expect_external_answer
query_addr $addr $PORT_ALLOW "www.internal"
expect_internal_answer
query_addr $addr $PORT_TAG_1 "local"
expect_tag_one_answer
query_addr $addr $PORT_TAG_2 "local"
expect_tag_two_answer
query_addr $addr $PORT_TAG_3 "local"
expect_refused
query_addr $addr $PORT_RPZ_1 "local"
expect_rpz_one_answer
query_addr $addr $PORT_RPZ_2 "local"
expect_rpz_two_answer
query_addr $addr $PORT_RPZ_NX "local"
expect_nx_answer
query_addr $addr $PORT_VIEW_INT "www.internal"
expect_internal_answer
query_addr $addr $PORT_VIEW_INT "www.external"
expect_refused
query_addr $addr $PORT_VIEW_EXT "www.internal"
expect_refused
query_addr $addr $PORT_VIEW_EXT "www.external"
expect_external_answer
query_addr $addr $PORT_VIEW_INTEXT "www.internal"
expect_internal_answer
query_addr $addr $PORT_VIEW_INTEXT "www.external"
expect_external_answer
done
query_addr fe80::2%${INTERFACE}vlan50 $PORT_ALLOW "one.vtest."
expect_tag_one_answer
query_addr fe80::2%${INTERFACE}vlan51 $PORT_ALLOW "two.vtest."
expect_tag_two_answer
end 0
+48
View File
@@ -0,0 +1,48 @@
# #-- auth_tls.pre--#
# source the master var file when it's there
[ -f ../.tpkg.var.master ] && source ../.tpkg.var.master
# use .tpkg.var.test for in test variable passing
[ -f .tpkg.var.test ] && source .tpkg.var.test
. ../common.sh
#skip_test "Skip test due to no UDP service for SOA query"
PRE="../.."
if test -n "$NSD"; then
:
else
if `which nsd >/dev/null 2>&1`; then
# need nsd >= 4.2.0
NSD="nsd"
else
if test -f $PRE/../nsd/nsd; then
NSD="$PRE/../nsd/nsd"
else
skip_test "need nsd"
fi
fi
fi
echo "NSD=$NSD"
get_random_port 2
UNBOUND_PORT=$RND_PORT
NSD_PORT=$(($RND_PORT + 1))
echo "UNBOUND_PORT=$UNBOUND_PORT" >> .tpkg.var.test
echo "NSD_PORT=$NSD_PORT" >> .tpkg.var.test
# make config file
sed -e 's/@UNBOUND_PORT\@/'$UNBOUND_PORT'/' -e 's/@NSD_PORT\@/'$NSD_PORT'/' < auth_tls.ub.conf > ub.conf
sed -e 's/@UNBOUND_PORT\@/'$UNBOUND_PORT'/' -e 's/@NSD_PORT\@/'$NSD_PORT'/' < auth_tls.nsd.conf > nsd.conf
# start nsd
$NSD -d -c nsd.conf >nsd.log 2>&1 &
NSD_PID=$!
echo "NSD_PID=$NSD_PID" >> .tpkg.var.test
# start unbound in the background
$PRE/unbound -d -c ub.conf >unbound.log 2>&1 &
UNBOUND_PID=$!
echo "UNBOUND_PID=$UNBOUND_PID" >> .tpkg.var.test
cat .tpkg.var.test
wait_nsd_up nsd.log
wait_unbound_up unbound.log
@@ -0,0 +1,48 @@
# #-- auth_tls_failcert.pre--#
# source the master var file when it's there
[ -f ../.tpkg.var.master ] && source ../.tpkg.var.master
# use .tpkg.var.test for in test variable passing
[ -f .tpkg.var.test ] && source .tpkg.var.test
. ../common.sh
#skip_test "Skip test due to no UDP service for SOA query"
PRE="../.."
if test -n "$NSD"; then
:
else
if `which nsd >/dev/null 2>&1`; then
# need nsd >= 4.2.0
NSD="nsd"
else
if test -f $PRE/../nsd/nsd; then
NSD="$PRE/../nsd/nsd"
else
skip_test "need nsd"
fi
fi
fi
echo "NSD=$NSD"
get_random_port 2
UNBOUND_PORT=$RND_PORT
NSD_PORT=$(($RND_PORT + 1))
echo "UNBOUND_PORT=$UNBOUND_PORT" >> .tpkg.var.test
echo "NSD_PORT=$NSD_PORT" >> .tpkg.var.test
# make config file
sed -e 's/@UNBOUND_PORT\@/'$UNBOUND_PORT'/' -e 's/@NSD_PORT\@/'$NSD_PORT'/' < auth_tls_failcert.ub.conf > ub.conf
sed -e 's/@UNBOUND_PORT\@/'$UNBOUND_PORT'/' -e 's/@NSD_PORT\@/'$NSD_PORT'/' < auth_tls_failcert.nsd.conf > nsd.conf
# start nsd
$NSD -d -c nsd.conf >nsd.log 2>&1 &
NSD_PID=$!
echo "NSD_PID=$NSD_PID" >> .tpkg.var.test
# start unbound in the background
$PRE/unbound -d -c ub.conf >unbound.log 2>&1 &
UNBOUND_PID=$!
echo "UNBOUND_PID=$UNBOUND_PID" >> .tpkg.var.test
cat .tpkg.var.test
wait_nsd_up nsd.log
wait_unbound_up unbound.log

Some files were not shown because too many files have changed in this diff Show More