summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDag-Erling Smørgrav <des@FreeBSD.org>2026-07-22 12:15:55 +0000
committerDag-Erling Smørgrav <des@FreeBSD.org>2026-07-22 12:15:55 +0000
commitc68e7bcd81d62e9f5364c6da22fd9917976acf85 (patch)
tree47fef24c9b7ae049d7afe24404d07a93e4ea7d8e
parent22e58f330a151944c24e010d23ec3881df6681b6 (diff)
-rwxr-xr-xconfig.guess17
-rwxr-xr-xconfig.sub11
-rwxr-xr-xconfigure48
-rw-r--r--configure.ac21
-rw-r--r--daemon/daemon.c12
-rw-r--r--daemon/daemon.h3
-rw-r--r--daemon/remote.c16
-rw-r--r--daemon/worker.c30
-rw-r--r--daemon/worker.h8
-rw-r--r--dnscrypt/dnscrypt.c26
-rw-r--r--dnscrypt/dnscrypt.h3
-rw-r--r--dnstap/unbound-dnstap-socket.c5
-rw-r--r--doc/README2
-rw-r--r--doc/example.conf.in2
-rw-r--r--doc/libunbound.3.in4
-rw-r--r--doc/unbound-anchor.8.in4
-rw-r--r--doc/unbound-checkconf.8.in4
-rw-r--r--doc/unbound-control.8.in4
-rw-r--r--doc/unbound-host.1.in4
-rw-r--r--doc/unbound.8.in4
-rw-r--r--doc/unbound.conf.5.in4
-rw-r--r--doc/unbound.conf.rst7
-rw-r--r--iterator/iter_donotq.c12
-rw-r--r--iterator/iterator.c67
-rw-r--r--libunbound/libworker.c13
-rw-r--r--libunbound/libworker.h3
-rw-r--r--respip/respip.c8
-rw-r--r--services/authzone.c28
-rw-r--r--services/cache/dns.c4
-rw-r--r--services/cache/rrset.c44
-rw-r--r--services/listen_dnsport.c234
-rw-r--r--services/listen_dnsport.h37
-rw-r--r--services/localzone.h2
-rw-r--r--services/mesh.c66
-rw-r--r--services/mesh.h8
-rw-r--r--services/outside_network.c388
-rw-r--r--services/outside_network.h107
-rw-r--r--smallapp/worker_cb.c6
-rw-r--r--testcode/doqclient.c9
-rw-r--r--testcode/fake_event.c30
-rw-r--r--testcode/unitmain.c56
-rw-r--r--testcode/unittcpreuse.c276
-rw-r--r--testdata/dns_error_reporting.rpl1
-rw-r--r--testdata/dnscrypt_cert.tdir/dnscrypt_cert.test17
-rw-r--r--testdata/dnscrypt_cert_chacha.tdir/dnscrypt_cert_chacha.test17
-rw-r--r--testdata/iter_prefetch_fail.rpl8
-rw-r--r--util/data/msgparse.c9
-rw-r--r--util/data/packed_rrset.c1
-rw-r--r--util/fptr_wlist.c1
-rw-r--r--util/module.h6
-rw-r--r--util/netevent.c44
-rw-r--r--util/netevent.h6
-rw-r--r--validator/val_sigcrypt.c18
-rw-r--r--validator/val_utils.c17
-rw-r--r--validator/val_utils.h4
-rw-r--r--validator/validator.c59
56 files changed, 1501 insertions, 344 deletions
diff --git a/config.guess b/config.guess
index a9d01fde4617..c7f4c3294a63 100755
--- a/config.guess
+++ b/config.guess
@@ -1,10 +1,10 @@
#! /bin/sh
# Attempt to guess a canonical system name.
-# Copyright 1992-2025 Free Software Foundation, Inc.
+# Copyright 1992-2026 Free Software Foundation, Inc.
# shellcheck disable=SC2006,SC2268 # see below for rationale
-timestamp='2025-07-10'
+timestamp='2026-05-17'
# This file is free software; you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by
@@ -60,7 +60,7 @@ version="\
GNU config.guess ($timestamp)
Originally written by Per Bothner.
-Copyright 1992-2025 Free Software Foundation, Inc.
+Copyright 1992-2026 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE."
@@ -150,7 +150,7 @@ UNAME_SYSTEM=`(uname -s) 2>/dev/null` || UNAME_SYSTEM=unknown
UNAME_VERSION=`(uname -v) 2>/dev/null` || UNAME_VERSION=unknown
case $UNAME_SYSTEM in
-Linux|GNU|GNU/*)
+Ironclad|Linux|GNU|GNU/*)
LIBC=unknown
set_cc_for_build
@@ -167,6 +167,8 @@ Linux|GNU|GNU/*)
LIBC=gnu
#elif defined(__LLVM_LIBC__)
LIBC=llvm
+ #elif defined(__mlibc__)
+ LIBC=mlibc
#else
#include <stdarg.h>
/* First heuristic to detect musl libc. */
@@ -1186,6 +1188,9 @@ EOF
sparc:Linux:*:* | sparc64:Linux:*:*)
GUESS=$UNAME_MACHINE-unknown-linux-$LIBC
;;
+ sw_64:Linux:*:*)
+ GUESS=$UNAME_MACHINE-unknown-linux-$LIBC
+ ;;
tile*:Linux:*:*)
GUESS=$UNAME_MACHINE-unknown-linux-$LIBC
;;
@@ -1598,10 +1603,10 @@ EOF
GUESS=$UNAME_MACHINE-unknown-unleashed$UNAME_RELEASE
;;
x86_64:[Ii]ronclad:*:*|i?86:[Ii]ronclad:*:*)
- GUESS=$UNAME_MACHINE-pc-ironclad-mlibc
+ GUESS=$UNAME_MACHINE-pc-ironclad-$LIBC
;;
*:[Ii]ronclad:*:*)
- GUESS=$UNAME_MACHINE-unknown-ironclad-mlibc
+ GUESS=$UNAME_MACHINE-unknown-ironclad-$LIBC
;;
esac
diff --git a/config.sub b/config.sub
index 3d35cde174de..404aa0824443 100755
--- a/config.sub
+++ b/config.sub
@@ -1,10 +1,10 @@
#! /bin/sh
# Configuration validation subroutine script.
-# Copyright 1992-2025 Free Software Foundation, Inc.
+# Copyright 1992-2026 Free Software Foundation, Inc.
# shellcheck disable=SC2006,SC2268,SC2162 # see below for rationale
-timestamp='2025-07-10'
+timestamp='2026-05-17'
# This file is free software; you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by
@@ -76,7 +76,7 @@ Report bugs and patches to <config-patches@gnu.org>."
version="\
GNU config.sub ($timestamp)
-Copyright 1992-2025 Free Software Foundation, Inc.
+Copyright 1992-2026 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE."
@@ -1432,6 +1432,7 @@ case $cpu-$vendor in
| sparcv9v \
| spu \
| sv1 \
+ | sw_64 \
| sx* \
| tahoe \
| thumbv7* \
@@ -1525,7 +1526,7 @@ EOF
;;
ironclad*)
kernel=ironclad
- os=`echo "$basic_os" | sed -e 's|ironclad|mlibc|'`
+ os=`echo "$basic_os" | sed -e 's|ironclad|gnu|'`
;;
linux*)
kernel=linux
@@ -2220,7 +2221,7 @@ case $kernel-$os-$obj in
;;
uclinux-uclibc*- | uclinux-gnu*- )
;;
- ironclad-mlibc*-)
+ ironclad-gnu*- | ironclad-mlibc*- )
;;
managarm-mlibc*- | managarm-kernel*- )
;;
diff --git a/configure b/configure
index 2c48dc207575..9efc0bafd338 100755
--- a/configure
+++ b/configure
@@ -1,6 +1,6 @@
#! /bin/sh
# Guess values for system-dependent variables and create Makefiles.
-# Generated by GNU Autoconf 2.71 for unbound 1.25.1.
+# Generated by GNU Autoconf 2.71 for unbound 1.25.2.
#
# 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.25.1'
-PACKAGE_STRING='unbound 1.25.1'
+PACKAGE_VERSION='1.25.2'
+PACKAGE_STRING='unbound 1.25.2'
PACKAGE_BUGREPORT='unbound-bugs@nlnetlabs.nl or https://github.com/NLnetLabs/unbound/issues'
PACKAGE_URL=''
@@ -1516,7 +1516,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.25.1 to adapt to many kinds of systems.
+\`configure' configures unbound 1.25.2 to adapt to many kinds of systems.
Usage: $0 [OPTION]... [VAR=VALUE]...
@@ -1582,7 +1582,7 @@ fi
if test -n "$ac_init_help"; then
case $ac_init_help in
- short | recursive ) echo "Configuration of unbound 1.25.1:";;
+ short | recursive ) echo "Configuration of unbound 1.25.2:";;
esac
cat <<\_ACEOF
@@ -1835,7 +1835,7 @@ fi
test -n "$ac_init_help" && exit $ac_status
if $ac_init_version; then
cat <<\_ACEOF
-unbound configure 1.25.1
+unbound configure 1.25.2
generated by GNU Autoconf 2.71
Copyright (C) 2021 Free Software Foundation, Inc.
@@ -2492,7 +2492,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.25.1, which was
+It was created by unbound $as_me 1.25.2, which was
generated by GNU Autoconf 2.71. Invocation command line was
$ $0$ac_configure_args_raw
@@ -3256,11 +3256,11 @@ UNBOUND_VERSION_MAJOR=1
UNBOUND_VERSION_MINOR=25
-UNBOUND_VERSION_MICRO=1
+UNBOUND_VERSION_MICRO=2
LIBUNBOUND_CURRENT=9
-LIBUNBOUND_REVISION=37
+LIBUNBOUND_REVISION=38
LIBUNBOUND_AGE=1
# 1.0.0 had 0:12:0
# 1.0.1 had 0:13:0
@@ -3365,6 +3365,7 @@ LIBUNBOUND_AGE=1
# 1.24.2 had 9:35:1
# 1.25.0 had 9:36:1
# 1.25.1 had 9:37:1
+# 1.25.2 had 9:38:1
# Current -- the number of the binary API that we're implementing
# Revision -- which iteration of the implementation of the binary
@@ -23677,6 +23678,29 @@ printf "%s\n" "no" >&6; }
fi
rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
+ ac_fn_check_decl "$LINENO" "CLOCK_MONOTONIC
+ " "ac_cv_have_decl_CLOCK_MONOTONIC_________" "$ac_includes_default
+#ifdef TIME_WITH_SYS_TIME
+# include <sys/time.h>
+# include <time.h>
+#else
+# ifdef HAVE_SYS_TIME_H
+# include <sys/time.h>
+# else
+# include <time.h>
+# endif
+#endif
+
+" "$ac_c_undeclared_builtin_options" "CFLAGS"
+if test "x$ac_cv_have_decl_CLOCK_MONOTONIC_________" = xyes
+then :
+
+
+else $as_nop
+ as_fn_error $? "ngtcp2 for QUIC needs at least CLOCK_MONOTONIC on the system" "$LINENO" 5
+
+fi
+
fi
# set static linking for uninstalled libraries if requested
@@ -26307,7 +26331,7 @@ printf "%s\n" "#define MAXSYSLOGMSGLEN 10240" >>confdefs.h
-version=1.25.1
+version=1.25.2
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for build time" >&5
printf %s "checking for build time... " >&6; }
@@ -26837,7 +26861,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.25.1, which was
+This file was extended by unbound $as_me 1.25.2, which was
generated by GNU Autoconf 2.71. Invocation command line was
CONFIG_FILES = $CONFIG_FILES
@@ -26905,7 +26929,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.25.1
+unbound config.status 1.25.2
configured by $0, generated by GNU Autoconf 2.71,
with options \\"\$ac_cs_config\\"
diff --git a/configure.ac b/configure.ac
index f1c5b0419782..a4b6b17bdc65 100644
--- a/configure.ac
+++ b/configure.ac
@@ -12,14 +12,14 @@ sinclude(dnscrypt/dnscrypt.m4)
# must be numbers. ac_defun because of later processing
m4_define([VERSION_MAJOR],[1])
m4_define([VERSION_MINOR],[25])
-m4_define([VERSION_MICRO],[1])
+m4_define([VERSION_MICRO],[2])
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])
AC_SUBST(UNBOUND_VERSION_MINOR, [VERSION_MINOR])
AC_SUBST(UNBOUND_VERSION_MICRO, [VERSION_MICRO])
LIBUNBOUND_CURRENT=9
-LIBUNBOUND_REVISION=37
+LIBUNBOUND_REVISION=38
LIBUNBOUND_AGE=1
# 1.0.0 had 0:12:0
# 1.0.1 had 0:13:0
@@ -124,6 +124,7 @@ LIBUNBOUND_AGE=1
# 1.24.2 had 9:35:1
# 1.25.0 had 9:36:1
# 1.25.1 had 9:37:1
+# 1.25.2 had 9:38:1
# Current -- the number of the binary API that we're implementing
# Revision -- which iteration of the implementation of the binary
@@ -1735,6 +1736,22 @@ if test x_$withval = x_yes -o x_$withval != x_no; then
AC_MSG_RESULT(no)
])
+ AC_CHECK_DECL([CLOCK_MONOTONIC]
+ , []
+ , [AC_MSG_ERROR([ngtcp2 for QUIC needs at least CLOCK_MONOTONIC on the system])]
+ , [AC_INCLUDES_DEFAULT
+#ifdef TIME_WITH_SYS_TIME
+# include <sys/time.h>
+# include <time.h>
+#else
+# ifdef HAVE_SYS_TIME_H
+# include <sys/time.h>
+# else
+# include <time.h>
+# endif
+#endif
+ ])
+
fi
# set static linking for uninstalled libraries if requested
diff --git a/daemon/daemon.c b/daemon/daemon.c
index ea4e83e70932..51dd51de38d4 100644
--- a/daemon/daemon.c
+++ b/daemon/daemon.c
@@ -79,6 +79,7 @@
#include "util/tcp_conn_limit.h"
#include "util/edns.h"
#include "services/listen_dnsport.h"
+#include "services/outside_network.h"
#include "services/cache/rrset.h"
#include "services/cache/infra.h"
#include "services/localzone.h"
@@ -813,6 +814,10 @@ daemon_create_workers(struct daemon* daemon)
fatal_exit("out of memory during daemon init");
numport = daemon_get_shufport(daemon, shufport);
verbose(VERB_ALGO, "total of %d outgoing ports available", numport);
+ if(!(daemon->shared_ports = shared_ports_create(daemon->cfg->out_ifs,
+ daemon->cfg->num_out_ifs, daemon->cfg->do_ip4,
+ daemon->cfg->do_ip6, shufport, numport)))
+ fatal_exit("could not setup shared ports: out of memory");
#ifdef HAVE_NGTCP2
if (cfg_has_quic(daemon->cfg)) {
@@ -843,10 +848,7 @@ daemon_create_workers(struct daemon* daemon)
#endif
}
for(i=0; i<daemon->num; i++) {
- if(!(daemon->workers[i] = worker_create(daemon, i,
- shufport+numport*i/daemon->num,
- numport*(i+1)/daemon->num - numport*i/daemon->num)))
- /* the above is not ports/numthr, due to rounding */
+ if(!(daemon->workers[i] = worker_create(daemon, i)))
fatal_exit("could not create worker");
}
/* create per-worker alloc caches if not reusing existing ones. */
@@ -1204,6 +1206,8 @@ daemon_cleanup(struct daemon* daemon)
if(!daemon->reuse_cache || daemon->need_to_exit)
daemon_clear_allocs(daemon);
daemon->num = 0;
+ shared_ports_delete(daemon->shared_ports);
+ daemon->shared_ports = NULL;
#ifdef USE_DNSTAP
dt_delete(daemon->dtenv);
daemon->dtenv = NULL;
diff --git a/daemon/daemon.h b/daemon/daemon.h
index 20386d7fc9a0..e6f099629be1 100644
--- a/daemon/daemon.h
+++ b/daemon/daemon.h
@@ -62,6 +62,7 @@ struct doq_table;
struct cookie_secrets;
struct fast_reload_thread;
struct fast_reload_printq;
+struct shared_ports;
#include "dnstap/dnstap_config.h"
#ifdef USE_DNSTAP
@@ -97,6 +98,8 @@ struct daemon {
int rc_port;
/** listening ports for remote control */
struct listen_port* rc_ports;
+ /** the shared ports structure, with random ports numbers. */
+ struct shared_ports* shared_ports;
/** remote control connections management (for first worker) */
struct daemon_remote* rc;
/** ssl context for listening to dnstcp over ssl */
diff --git a/daemon/remote.c b/daemon/remote.c
index 1eaf90016b88..61beb7c2fa69 100644
--- a/daemon/remote.c
+++ b/daemon/remote.c
@@ -1658,6 +1658,14 @@ do_view_data_add(RES* ssl, struct worker* worker, char* arg)
ssl_printf(ssl,"error out of memory\n");
return;
}
+ if(!v->isfirst) {
+ /* Global local-zone is not used for this view,
+ * therefore add defaults to this view-specific
+ * local-zone. */
+ struct config_file lz_cfg;
+ memset(&lz_cfg, 0, sizeof(lz_cfg));
+ local_zone_enter_defaults(v->local_zones, &lz_cfg);
+ }
}
do_data_add(ssl, v->local_zones, arg2);
lock_rw_unlock(&v->lock);
@@ -1683,6 +1691,14 @@ do_view_datas_add(struct daemon_remote* rc, RES* ssl, struct worker* worker,
ssl_printf(ssl,"error out of memory\n");
return;
}
+ if(!v->isfirst) {
+ /* Global local-zone is not used for this view,
+ * therefore add defaults to this view-specific
+ * local-zone. */
+ struct config_file lz_cfg;
+ memset(&lz_cfg, 0, sizeof(lz_cfg));
+ local_zone_enter_defaults(v->local_zones, &lz_cfg);
+ }
}
/* put the view name in the command buf */
(void)snprintf(buf+strlen(buf), sizeof(buf)-strlen(buf), "%s ", arg);
diff --git a/daemon/worker.c b/daemon/worker.c
index a5dd9bc028e8..765fb2299a55 100644
--- a/daemon/worker.c
+++ b/daemon/worker.c
@@ -1550,6 +1550,7 @@ worker_handle_request(struct comm_point* c, void* arg, int error,
return 0;
}
query_error(c->buffer, LDNS_RCODE_FORMERR, 0);
+ sldns_buffer_copy(c->dnscrypt_buffer, c->buffer);
return 1;
}
dname_str(qinfo.qname, buf);
@@ -1568,6 +1569,7 @@ worker_handle_request(struct comm_point* c, void* arg, int error,
query_error(c->buffer, LDNS_RCODE_SERVFAIL,
qinfo.qname_len);
worker->stats.num_query_dnscrypt_cleartext++;
+ sldns_buffer_copy(c->dnscrypt_buffer, c->buffer);
return 1;
}
worker->stats.num_query_dnscrypt_cert++;
@@ -1828,7 +1830,13 @@ worker_handle_request(struct comm_point* c, void* arg, int error,
server_stats_insquery(&worker->stats, c, qinfo.qtype,
qinfo.qclass, &edns, repinfo);
if(c->type != comm_udp)
+#ifdef USE_DNSCRYPT
+ edns.udp_size = (c->dnscrypt && repinfo->is_dnscrypted)
+ ? sldns_buffer_capacity(c->buffer) - DNSCRYPT_REPLY_HEADER_SIZE
+ : 65535;
+#else
edns.udp_size = 65535; /* max size for TCP replies */
+#endif
if(qinfo.qclass == LDNS_RR_CLASS_CH && answer_chaos(worker, &qinfo,
&edns, repinfo, c->buffer)) {
regional_free_all(worker->scratchpad);
@@ -2112,7 +2120,7 @@ send_reply_rc:
}
}
#ifdef USE_DNSCRYPT
- if(!dnsc_handle_uncurved_request(repinfo)) {
+ if(!dnsc_handle_uncurved_request(repinfo, c->buffer)) {
return 0;
}
#endif
@@ -2225,23 +2233,16 @@ void worker_probe_timer_cb(void* arg)
}
struct worker*
-worker_create(struct daemon* daemon, int id, int* ports, int n)
+worker_create(struct daemon* daemon, int id)
{
unsigned int seed;
struct worker* worker = (struct worker*)calloc(1,
sizeof(struct worker));
if(!worker)
return NULL;
- worker->numports = n;
- worker->ports = (int*)memdup(ports, sizeof(int)*n);
- if(!worker->ports) {
- free(worker);
- return NULL;
- }
worker->daemon = daemon;
worker->thread_num = id;
if(!(worker->cmd = tube_create())) {
- free(worker->ports);
free(worker);
return NULL;
}
@@ -2249,7 +2250,6 @@ worker_create(struct daemon* daemon, int id, int* ports, int n)
if(!(worker->rndstate = ub_initstate(daemon->rand))) {
log_err("could not init random numbers.");
tube_delete(worker->cmd);
- free(worker->ports);
free(worker);
return NULL;
}
@@ -2348,14 +2348,14 @@ worker_init(struct worker* worker, struct config_file *cfg,
cfg->out_ifs, cfg->num_out_ifs, cfg->do_ip4, cfg->do_ip6,
cfg->do_tcp?cfg->outgoing_num_tcp:0, cfg->ip_dscp,
worker->daemon->env->infra_cache, worker->rndstate,
- cfg->use_caps_bits_for_id, worker->ports, worker->numports,
+ cfg->use_caps_bits_for_id,
cfg->unwanted_threshold, cfg->outgoing_tcp_mss,
&worker_alloc_cleanup, worker,
cfg->do_udp || cfg->udp_upstream_without_downstream,
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);
+ cfg->tcp_auth_query_timeout, worker->daemon->shared_ports);
if(!worker->back) {
log_err("could not create outgoing sockets");
worker_delete(worker);
@@ -2506,7 +2506,6 @@ worker_delete(struct worker* worker)
tube_delete(worker->cmd);
comm_timer_delete(worker->stat_timer);
comm_timer_delete(worker->env.probe_timer);
- free(worker->ports);
if(worker->thread_num == 0) {
#ifdef UB_ON_WINDOWS
wsvc_desetup_worker(worker);
@@ -2638,6 +2637,11 @@ void libworker_event_done_cb(void* ATTR_UNUSED(arg), int ATTR_UNUSED(rcode),
log_assert(0);
}
+void libworker_alloc_cleanup(void* ATTR_UNUSED(arg))
+{
+ log_assert(0);
+}
+
int context_query_cmp(const void* ATTR_UNUSED(a), const void* ATTR_UNUSED(b))
{
log_assert(0);
diff --git a/daemon/worker.h b/daemon/worker.h
index b7bb52fd715b..37f3728efef1 100644
--- a/daemon/worker.h
+++ b/daemon/worker.h
@@ -104,10 +104,6 @@ struct worker {
struct listen_dnsport* front;
/** the backside outside network interface to the auth servers */
struct outside_network* back;
- /** ports to be used by this worker. */
- int* ports;
- /** number of ports for this worker */
- int numports;
/** the signal handler */
struct comm_signal* comsig;
/** commpoint to listen to commands. */
@@ -146,11 +142,9 @@ struct worker {
* with backpointers only. Use worker_init on it later.
* @param daemon: the daemon that this worker thread is part of.
* @param id: the thread number from 0.. numthreads-1.
- * @param ports: the ports it is allowed to use, array.
- * @param n: the number of ports.
* @return: the new worker or NULL on alloc failure.
*/
-struct worker* worker_create(struct daemon* daemon, int id, int* ports, int n);
+struct worker* worker_create(struct daemon* daemon, int id);
/**
* Initialize worker.
diff --git a/dnscrypt/dnscrypt.c b/dnscrypt/dnscrypt.c
index 173484cdf0b1..6f1a236ad7e5 100644
--- a/dnscrypt/dnscrypt.c
+++ b/dnscrypt/dnscrypt.c
@@ -474,10 +474,18 @@ dnscrypt_server_curve(const dnsccert *cert,
uint8_t *const buf = sldns_buffer_begin(buffer);
size_t len = sldns_buffer_limit(buffer);
+ if(len + DNSCRYPT_REPLY_HEADER_SIZE > sldns_buffer_capacity(buffer))
+ return -1;
+ sldns_buffer_clear(buffer);
+
if(udp){
if (max_len > max_reply_size)
max_len = max_reply_size;
}
+ if(max_len > sldns_buffer_capacity(buffer))
+ max_len = sldns_buffer_capacity(buffer);
+ if(max_len > 65535)
+ max_len = 65535;
memcpy(nonce, client_nonce, crypto_box_HALF_NONCEBYTES);
@@ -520,6 +528,7 @@ dnscrypt_server_curve(const dnsccert *cert,
DNSCRYPT_MAGIC_HEADER_LEN,
nonce,
crypto_box_NONCEBYTES);
+ sldns_buffer_flip(buffer);
sldns_buffer_set_limit(buffer, len + DNSCRYPT_REPLY_HEADER_SIZE);
return 0;
}
@@ -663,6 +672,8 @@ dnsc_find_cert(struct dnsc_env* dnscenv, struct sldns_buffer* buffer)
}
dnscrypt_header = (struct dnscrypt_query_header *)sldns_buffer_begin(buffer);
for (i = 0U; i < dnscenv->signed_certs_count; i++) {
+ if(!certs[i].keypair)
+ continue;
if (memcmp(certs[i].magic_query, dnscrypt_header->magic_query,
DNSCRYPT_MAGIC_HEADER_LEN) == 0) {
return &certs[i];
@@ -804,6 +815,7 @@ dnsc_parse_keys(struct dnsc_env *env, struct config_file *cfg)
sizeof *env->keypairs);
env->certs = sodium_allocarray(env->signed_certs_count,
sizeof *env->certs);
+ memset(env->certs, 0, env->signed_certs_count * sizeof(*env->certs));
cert_id = 0U;
keypair_id = 0U;
@@ -912,12 +924,13 @@ dnsc_handle_curved_request(struct dnsc_env* dnscenv,
}
int
-dnsc_handle_uncurved_request(struct comm_reply *repinfo)
+dnsc_handle_uncurved_request(struct comm_reply *repinfo,
+ struct sldns_buffer* buffer)
{
if(!repinfo->c->dnscrypt) {
return 1;
}
- sldns_buffer_copy(repinfo->c->dnscrypt_buffer, repinfo->c->buffer);
+ sldns_buffer_copy(repinfo->c->dnscrypt_buffer, buffer);
if(!repinfo->is_dnscrypted) {
return 1;
}
@@ -963,12 +976,19 @@ dnsc_create(void)
int
dnsc_apply_cfg(struct dnsc_env *env, struct config_file *cfg)
{
+ int nkeys;
if(dnsc_parse_certs(env, cfg) <= 0) {
fatal_exit("dnsc_apply_cfg: no cert file loaded");
}
- if(dnsc_parse_keys(env, cfg) <= 0) {
+ nkeys = dnsc_parse_keys(env, cfg);
+ if(nkeys <= 0) {
fatal_exit("dnsc_apply_cfg: no key file loaded");
}
+ if((size_t)nkeys < env->signed_certs_count) {
+ fatal_exit("dnsc_apply_cfg: %u dnscrypt-provider-cert file(s) have no "
+ "matching dnscrypt-secret-key",
+ (unsigned)(env->signed_certs_count - (size_t)nkeys));
+ }
randombytes_buf(env->hash_key, sizeof env->hash_key);
env->provider_name = cfg->dnscrypt_provider;
diff --git a/dnscrypt/dnscrypt.h b/dnscrypt/dnscrypt.h
index b0da9b732681..998237d48e83 100644
--- a/dnscrypt/dnscrypt.h
+++ b/dnscrypt/dnscrypt.h
@@ -128,7 +128,8 @@ int dnsc_handle_curved_request(struct dnsc_env* dnscenv,
* \return 0 in case of failure.
*/
-int dnsc_handle_uncurved_request(struct comm_reply *repinfo);
+int dnsc_handle_uncurved_request(struct comm_reply *repinfo,
+ struct sldns_buffer* buffer);
/**
* Computes the size of the shared secret cache entry.
diff --git a/dnstap/unbound-dnstap-socket.c b/dnstap/unbound-dnstap-socket.c
index 90b0f6003ff6..2bf017430fe0 100644
--- a/dnstap/unbound-dnstap-socket.c
+++ b/dnstap/unbound-dnstap-socket.c
@@ -1735,6 +1735,11 @@ void libworker_event_done_cb(void* ATTR_UNUSED(arg), int ATTR_UNUSED(rcode),
log_assert(0);
}
+void libworker_alloc_cleanup(void* ATTR_UNUSED(arg))
+{
+ log_assert(0);
+}
+
int context_query_cmp(const void* ATTR_UNUSED(a), const void* ATTR_UNUSED(b))
{
log_assert(0);
diff --git a/doc/README b/doc/README
index 265dc1592f9f..fa60d3b0b570 100644
--- a/doc/README
+++ b/doc/README
@@ -1,4 +1,4 @@
-README for Unbound 1.25.1
+README for Unbound 1.25.2
Copyright 2007 NLnet Labs
http://unbound.net
diff --git a/doc/example.conf.in b/doc/example.conf.in
index 5514328b1235..e03d41153b36 100644
--- a/doc/example.conf.in
+++ b/doc/example.conf.in
@@ -1,7 +1,7 @@
#
# Example configuration file.
#
-# See unbound.conf(5) man page, version 1.25.1.
+# See unbound.conf(5) man page, version 1.25.2.
#
# this is a comment.
diff --git a/doc/libunbound.3.in b/doc/libunbound.3.in
index 91092e67d898..b9158ef902a7 100644
--- a/doc/libunbound.3.in
+++ b/doc/libunbound.3.in
@@ -27,9 +27,9 @@ level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
..
-.TH "LIBUNBOUND" "3" "May 20, 2026" "1.25.1" "Unbound"
+.TH "LIBUNBOUND" "3" "Jul 22, 2026" "1.25.2" "Unbound"
.SH NAME
-libunbound \- Unbound DNS validating resolver 1.25.1 functions.
+libunbound \- Unbound DNS validating resolver 1.25.2 functions.
.SH SYNOPSIS
.sp
\fB#include <unbound.h>\fP
diff --git a/doc/unbound-anchor.8.in b/doc/unbound-anchor.8.in
index fa1486d18864..c9810f8f2c03 100644
--- a/doc/unbound-anchor.8.in
+++ b/doc/unbound-anchor.8.in
@@ -27,9 +27,9 @@ level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
..
-.TH "UNBOUND-ANCHOR" "8" "May 20, 2026" "1.25.1" "Unbound"
+.TH "UNBOUND-ANCHOR" "8" "Jul 22, 2026" "1.25.2" "Unbound"
.SH NAME
-unbound-anchor \- Unbound 1.25.1 anchor utility.
+unbound-anchor \- Unbound 1.25.2 anchor utility.
.SH SYNOPSIS
.sp
\fBunbound\-anchor\fP [\fBopts\fP]
diff --git a/doc/unbound-checkconf.8.in b/doc/unbound-checkconf.8.in
index cfc4e2375e47..a3094e486a7b 100644
--- a/doc/unbound-checkconf.8.in
+++ b/doc/unbound-checkconf.8.in
@@ -27,9 +27,9 @@ level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
..
-.TH "UNBOUND-CHECKCONF" "8" "May 20, 2026" "1.25.1" "Unbound"
+.TH "UNBOUND-CHECKCONF" "8" "Jul 22, 2026" "1.25.2" "Unbound"
.SH NAME
-unbound-checkconf \- Check Unbound 1.25.1 configuration file for errors.
+unbound-checkconf \- Check Unbound 1.25.2 configuration file for errors.
.SH SYNOPSIS
.sp
\fBunbound\-checkconf\fP [\fB\-hf\fP] [\fB\-o option\fP] [cfgfile]
diff --git a/doc/unbound-control.8.in b/doc/unbound-control.8.in
index faf3534d7af8..c1ad6b8d68c6 100644
--- a/doc/unbound-control.8.in
+++ b/doc/unbound-control.8.in
@@ -27,9 +27,9 @@ level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
..
-.TH "UNBOUND-CONTROL" "8" "May 20, 2026" "1.25.1" "Unbound"
+.TH "UNBOUND-CONTROL" "8" "Jul 22, 2026" "1.25.2" "Unbound"
.SH NAME
-unbound-control \- Unbound 1.25.1 remote server control utility.
+unbound-control \- Unbound 1.25.2 remote server control utility.
.SH SYNOPSIS
.sp
\fBunbound\-control\fP [\fB\-hq\fP] [\fB\-c cfgfile\fP] [\fB\-s server\fP] command
diff --git a/doc/unbound-host.1.in b/doc/unbound-host.1.in
index 6f3ef3f2593e..1582fd54813b 100644
--- a/doc/unbound-host.1.in
+++ b/doc/unbound-host.1.in
@@ -27,9 +27,9 @@ level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
..
-.TH "UNBOUND-HOST" "1" "May 20, 2026" "1.25.1" "Unbound"
+.TH "UNBOUND-HOST" "1" "Jul 22, 2026" "1.25.2" "Unbound"
.SH NAME
-unbound-host \- Unbound 1.25.1 DNS lookup utility.
+unbound-host \- Unbound 1.25.2 DNS lookup utility.
.SH SYNOPSIS
.sp
\fBunbound\-host\fP [\fB\-C configfile\fP] [\fB\-vdhr46D\fP] [\fB\-c class\fP]
diff --git a/doc/unbound.8.in b/doc/unbound.8.in
index a66ee68469a3..cf15d5b2b4a9 100644
--- a/doc/unbound.8.in
+++ b/doc/unbound.8.in
@@ -27,9 +27,9 @@ level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
..
-.TH "UNBOUND" "8" "May 20, 2026" "1.25.1" "Unbound"
+.TH "UNBOUND" "8" "Jul 22, 2026" "1.25.2" "Unbound"
.SH NAME
-unbound \- Unbound DNS validating resolver 1.25.1.
+unbound \- Unbound DNS validating resolver 1.25.2.
.SH SYNOPSIS
.sp
\fBunbound\fP [\fB\-hdpVv\fP] [\fB\-c <cfgfile>\fP]
diff --git a/doc/unbound.conf.5.in b/doc/unbound.conf.5.in
index 14dc4ae8e057..1d333938d414 100644
--- a/doc/unbound.conf.5.in
+++ b/doc/unbound.conf.5.in
@@ -27,9 +27,9 @@ level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
..
-.TH "UNBOUND.CONF" "5" "May 20, 2026" "1.25.1" "Unbound"
+.TH "UNBOUND.CONF" "5" "Jul 22, 2026" "1.25.2" "Unbound"
.SH NAME
-unbound.conf \- Unbound 1.25.1 configuration file.
+unbound.conf \- Unbound 1.25.2 configuration file.
.SH SYNOPSIS
.sp
\fBunbound.conf\fP
diff --git a/doc/unbound.conf.rst b/doc/unbound.conf.rst
index cc01451cd432..2e37e2e5d45d 100644
--- a/doc/unbound.conf.rst
+++ b/doc/unbound.conf.rst
@@ -2055,6 +2055,13 @@ These options are part of the ``server:`` section.
flushing away any poison.
A value of 10 million is suggested.
+ It is useful to add 0.0.0.0/8 and '::' to the
+ :ref:`do-not-query-address<unbound.conf.do-not-query-address>` list.
+ Otherwise they may be answered, from localhost, and the different source
+ makes an unwanted reply that unnecessarily ticks up.
+ The :ref:`do-not-query-localhost<unbound.conf.do-not-query-localhost>`
+ option includes them, the zero subnets, when it is enabled.
+
Default: 0 (disabled)
diff --git a/iterator/iter_donotq.c b/iterator/iter_donotq.c
index 40ffb45c49aa..7eecf1354f79 100644
--- a/iterator/iter_donotq.c
+++ b/iterator/iter_donotq.c
@@ -132,6 +132,18 @@ donotq_apply_cfg(struct iter_donotq* dq, struct config_file* cfg)
if(cfg->do_ip6) {
if(!donotq_str_cfg(dq, "::1"))
return 0;
+ if(!donotq_str_cfg(dq, "::ffff:127.0.0.0/104"))
+ return 0;
+ }
+ /* RFC 1122 3.2.1.3 / RFC 6890 / RFC 4291 2.5.2: not valid as
+ * destination; on Linux these route to the local host. */
+ if(!donotq_str_cfg(dq, "0.0.0.0/8"))
+ return 0;
+ if(cfg->do_ip6) {
+ if(!donotq_str_cfg(dq, "::"))
+ return 0;
+ if(!donotq_str_cfg(dq, "::ffff:0:0/96"))
+ return 0;
}
}
addr_tree_init_parents(&dq->tree);
diff --git a/iterator/iterator.c b/iterator/iterator.c
index cc38348b39da..78cd9485bad4 100644
--- a/iterator/iterator.c
+++ b/iterator/iterator.c
@@ -81,7 +81,8 @@ 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);
+static void target_count_increase_nx(struct module_qstate* qstate,
+ struct iter_qstate* iq, int num);
int
iter_init(struct module_env* env, int id)
@@ -250,7 +251,7 @@ error_supers(struct module_qstate* qstate, int id, struct module_qstate* super)
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);
+ target_count_increase_nx(super, super_iq, 1);
}
}
if(qstate->qinfo.qtype == LDNS_RR_TYPE_NS) {
@@ -734,7 +735,7 @@ is_caps_whitelisted(struct iter_env* ie, struct iter_qstate* iq)
* created for the parent query.
*/
static void
-target_count_create(struct iter_qstate* iq)
+target_count_create(struct module_qstate* qstate, struct iter_qstate* iq)
{
if(!iq->target_count) {
iq->target_count = (int*)calloc(TARGET_COUNT_MAX, sizeof(int));
@@ -742,33 +743,57 @@ target_count_create(struct iter_qstate* iq)
if(iq->target_count) {
iq->target_count[TARGET_COUNT_REF] = 1;
iq->nxns_dp = (uint8_t**)calloc(1, sizeof(uint8_t*));
+ /* continue global quota from where it was. */
+ if(qstate->global_quota_reached >
+ iq->target_count[TARGET_COUNT_GLOBAL_QUOTA])
+ iq->target_count[TARGET_COUNT_GLOBAL_QUOTA] =
+ qstate->global_quota_reached;
}
}
}
static void
-target_count_increase(struct iter_qstate* iq, int num)
+target_count_store(struct module_qstate* qstate, struct iter_qstate* iq)
{
- target_count_create(iq);
+ if(iq->target_count) {
+ /* By storing the global quota counter, it stays
+ * there to be picked up if the module is restarted,
+ * eg. due to a validator retry, and then the
+ * target_count_create routine picks it up. */
+ if(iq->target_count[TARGET_COUNT_GLOBAL_QUOTA] >
+ qstate->global_quota_reached)
+ qstate->global_quota_reached =
+ iq->target_count[TARGET_COUNT_GLOBAL_QUOTA];
+ }
+}
+
+static void
+target_count_increase(struct module_qstate* qstate,
+ struct iter_qstate* iq, int num)
+{
+ target_count_create(qstate, iq);
if(iq->target_count)
iq->target_count[TARGET_COUNT_QUERIES] += num;
iq->dp_target_count++;
}
static void
-target_count_increase_nx(struct iter_qstate* iq, int num)
+target_count_increase_nx(struct module_qstate* qstate,
+ struct iter_qstate* iq, int num)
{
- target_count_create(iq);
+ target_count_create(qstate, iq);
if(iq->target_count)
iq->target_count[TARGET_COUNT_NX] += num;
}
static void
-target_count_increase_global_quota(struct iter_qstate* iq, int num)
+target_count_increase_global_quota(struct module_qstate* qstate,
+ struct iter_qstate* iq, int num)
{
- target_count_create(iq);
+ target_count_create(qstate, iq);
if(iq->target_count)
iq->target_count[TARGET_COUNT_GLOBAL_QUOTA] += num;
+ target_count_store(qstate, iq);
}
/**
@@ -861,7 +886,7 @@ generate_sub_request(uint8_t* qname, size_t qnamelen, uint16_t qtype,
subiq = (struct iter_qstate*)subq->minfo[id];
memset(subiq, 0, sizeof(*subiq));
subiq->num_target_queries = 0;
- target_count_create(iq);
+ target_count_create(qstate, iq);
subiq->target_count = iq->target_count;
if(iq->target_count) {
iq->target_count[TARGET_COUNT_REF] ++; /* extra reference */
@@ -2234,7 +2259,7 @@ processLastResort(struct module_qstate* qstate, struct iter_qstate* iq,
return error_response_cache(qstate, id, LDNS_RCODE_SERVFAIL);
}
iq->num_target_queries += qs;
- target_count_increase(iq, qs);
+ target_count_increase(qstate, iq, qs);
if(qs != 0) {
qstate->ext_state[id] = module_wait_subquery;
return 0; /* and wait for them */
@@ -2290,7 +2315,7 @@ processLastResort(struct module_qstate* qstate, struct iter_qstate* iq,
* lookups at a time. */
verbose(VERB_ALGO, "try parent-side glue lookup");
iq->num_target_queries += query_count;
- target_count_increase(iq, query_count);
+ target_count_increase(qstate, iq, query_count);
qstate->ext_state[id] = module_wait_subquery;
return 0;
}
@@ -2310,7 +2335,7 @@ processLastResort(struct module_qstate* qstate, struct iter_qstate* iq,
if(query_count != 0) { /* suspend to await results */
verbose(VERB_ALGO, "try parent-side glue lookup");
iq->num_target_queries += query_count;
- target_count_increase(iq, query_count);
+ target_count_increase(qstate, iq, query_count);
qstate->ext_state[id] = module_wait_subquery;
return 0;
}
@@ -2788,7 +2813,7 @@ processQueryTargets(struct module_qstate* qstate, struct iter_qstate* iq,
return error_response_cache(qstate, id, LDNS_RCODE_SERVFAIL);
}
iq->num_target_queries += extra;
- target_count_increase(iq, extra);
+ target_count_increase(qstate, iq, extra);
if(iq->num_target_queries > 0) {
/* wait to get all targets, we want to try em */
verbose(VERB_ALGO, "wait for all targets for fallback");
@@ -2839,7 +2864,7 @@ processQueryTargets(struct module_qstate* qstate, struct iter_qstate* iq,
/* errors ignored, these targets are not strictly necessary for
* this result, we do not have to reply with SERVFAIL */
iq->num_target_queries += extra;
- target_count_increase(iq, extra);
+ target_count_increase(qstate, iq, extra);
}
/* Add the current set of unused targets to our queue. */
@@ -2962,7 +2987,7 @@ processQueryTargets(struct module_qstate* qstate, struct iter_qstate* iq,
LDNS_RCODE_SERVFAIL);
}
iq->num_target_queries += qs;
- target_count_increase(iq, qs);
+ target_count_increase(qstate, iq, qs);
}
/* Since a target query might have been made, we
* need to check again. */
@@ -3022,7 +3047,7 @@ processQueryTargets(struct module_qstate* qstate, struct iter_qstate* iq,
* this result, we do not have to reply with SERVFAIL */
if(extra > 0) {
iq->num_target_queries += extra;
- target_count_increase(iq, extra);
+ target_count_increase(qstate, iq, extra);
check_waiting_queries(iq, qstate, id);
/* undo qname minimise step because we'll get back here
* to do it again */
@@ -3035,7 +3060,7 @@ processQueryTargets(struct module_qstate* qstate, struct iter_qstate* iq,
}
}
- target_count_increase_global_quota(iq, 1);
+ target_count_increase_global_quota(qstate, iq, 1);
if(iq->target_count && iq->target_count[TARGET_COUNT_GLOBAL_QUOTA]
> MAX_GLOBAL_QUOTA) {
char s[LDNS_MAX_DOMAINLEN];
@@ -3879,7 +3904,7 @@ processTargetResponse(struct module_qstate* qstate, int id,
/* no new addresses, increase the nxns counter, like
* this could be a list of wildcards with no new
* addresses */
- target_count_increase_nx(foriq, 1);
+ target_count_increase_nx(qstate, foriq, 1);
}
verbose(VERB_ALGO, "added target response");
delegpt_log(VERB_ALGO, foriq->dp);
@@ -3891,7 +3916,7 @@ processTargetResponse(struct module_qstate* qstate, int id,
dpns->resolved = 1; /* fail the target */
/* do not count cached answers */
if(qstate->reply_origin && qstate->reply_origin->len != 0) {
- target_count_increase_nx(foriq, 1);
+ target_count_increase_nx(qstate, foriq, 1);
}
}
}
@@ -4116,6 +4141,7 @@ processFinished(struct module_qstate* qstate, struct iter_qstate* iq,
iter_store_parentside_neg(qstate->env, &qstate->qinfo,
iq->deleg_msg?iq->deleg_msg->rep:
(iq->response?iq->response->rep:NULL));
+ target_count_store(qstate, iq);
if(!iq->response) {
verbose(VERB_ALGO, "No response is set, servfail");
errinf(qstate, "(no response found at query finish)");
@@ -4531,6 +4557,7 @@ iter_clear(struct module_qstate* qstate, int id)
iq = (struct iter_qstate*)qstate->minfo[id];
if(iq) {
outbound_list_clear(&iq->outlist);
+ target_count_store(qstate, iq);
if(iq->target_count && --iq->target_count[TARGET_COUNT_REF] == 0) {
free(iq->target_count);
if(*iq->nxns_dp) free(*iq->nxns_dp);
diff --git a/libunbound/libworker.c b/libunbound/libworker.c
index 6e7244c03fee..d70527f59f66 100644
--- a/libunbound/libworker.c
+++ b/libunbound/libworker.c
@@ -105,6 +105,7 @@ libworker_delete_env(struct libworker* w)
SSL_CTX_free(w->sslctx);
#endif
outside_network_delete(w->back);
+ shared_ports_delete(w->shared_ports);
}
/** delete libworker struct */
@@ -219,17 +220,25 @@ libworker_setup(struct ub_ctx* ctx, int is_bg, struct ub_event_base* eb)
libworker_delete(w);
return NULL;
}
+ if(!(w->shared_ports = shared_ports_create(cfg->out_ifs,
+ cfg->num_out_ifs, cfg->do_ip4, cfg->do_ip6, ports, numports))) {
+ if(!w->is_bg || w->is_bg_thread) {
+ lock_basic_unlock(&ctx->cfglock);
+ }
+ libworker_delete(w);
+ return NULL;
+ }
w->back = outside_network_create(w->base, cfg->msg_buffer_size,
(size_t)cfg->outgoing_num_ports, cfg->out_ifs,
cfg->num_out_ifs, cfg->do_ip4, cfg->do_ip6,
cfg->do_tcp?cfg->outgoing_num_tcp:0, cfg->ip_dscp,
w->env->infra_cache, w->env->rnd, cfg->use_caps_bits_for_id,
- ports, numports, cfg->unwanted_threshold,
+ cfg->unwanted_threshold,
cfg->outgoing_tcp_mss, &libworker_alloc_cleanup, w,
cfg->do_udp || cfg->udp_upstream_without_downstream, w->sslctx,
cfg->delay_close, cfg->tls_use_sni, NULL, cfg->udp_connect,
cfg->max_reuse_tcp_queries, cfg->tcp_reuse_timeout,
- cfg->tcp_auth_query_timeout);
+ cfg->tcp_auth_query_timeout, w->shared_ports);
w->env->outnet = w->back;
if(!w->is_bg || w->is_bg_thread) {
lock_basic_unlock(&ctx->cfglock);
diff --git a/libunbound/libworker.h b/libunbound/libworker.h
index 42aa5bae3567..f527cb09329e 100644
--- a/libunbound/libworker.h
+++ b/libunbound/libworker.h
@@ -60,6 +60,7 @@ struct tube;
struct sldns_buffer;
struct ub_event_base;
struct query_info;
+struct shared_ports;
/**
* The library-worker status structure
@@ -84,6 +85,8 @@ struct libworker {
struct comm_base* base;
/** the backside outside network interface to the auth servers */
struct outside_network* back;
+ /** shared ports structure */
+ struct shared_ports* shared_ports;
/** random() table for this worker. */
struct ub_randstate* rndstate;
/** sslcontext for SSL wrapped DNS over TCP queries */
diff --git a/respip/respip.c b/respip/respip.c
index ff12114dec97..0a7dedd07ab6 100644
--- a/respip/respip.c
+++ b/respip/respip.c
@@ -1114,7 +1114,13 @@ respip_operate(struct module_qstate* qstate, enum module_ev event, int id,
if((qstate->qinfo.qtype == LDNS_RR_TYPE_A ||
qstate->qinfo.qtype == LDNS_RR_TYPE_AAAA ||
qstate->qinfo.qtype == LDNS_RR_TYPE_ANY) &&
- qstate->return_msg && qstate->return_msg->rep) {
+ qstate->return_msg && qstate->return_msg->rep &&
+ !(qstate->env->need_to_validate &&
+ (!(qstate->query_flags & BIT_CD)
+ || qstate->env->cfg->ignore_cd) &&
+ (qstate->return_msg->rep->security <= sec_status_bogus
+ || qstate->return_msg->rep->security ==
+ sec_status_secure_sentinel_fail))) {
struct reply_info* new_rep = qstate->return_msg->rep;
struct ub_packed_rrset_key* alias_rrset = NULL;
struct respip_action_info actinfo = {0, 0, 0, 0, NULL, 0, NULL};
diff --git a/services/authzone.c b/services/authzone.c
index ebcdc7e43643..e7a55391650d 100644
--- a/services/authzone.c
+++ b/services/authzone.c
@@ -5745,8 +5745,7 @@ xfr_master_add_addrs(struct auth_master* m, struct ub_packed_rrset_key* rrset,
/** callback for task_transfer lookup of host name, of A or AAAA */
void auth_xfer_transfer_lookup_callback(void* arg, int rcode, sldns_buffer* buf,
- enum sec_status ATTR_UNUSED(sec), char* ATTR_UNUSED(why_bogus),
- int ATTR_UNUSED(was_ratelimited))
+ enum sec_status sec, char* why_bogus, int ATTR_UNUSED(was_ratelimited))
{
struct auth_xfer* xfr = (struct auth_xfer*)arg;
struct module_env* env;
@@ -5759,7 +5758,16 @@ void auth_xfer_transfer_lookup_callback(void* arg, int rcode, sldns_buffer* buf,
}
/* process result */
- if(rcode == LDNS_RCODE_NOERROR) {
+ if(sec == sec_status_bogus || sec == sec_status_secure_sentinel_fail) {
+ if(verbosity >= VERB_OPS) {
+ char zname[LDNS_MAX_DOMAINLEN];
+ dname_str(xfr->name, zname);
+ verbose(VERB_OPS, "auth zone %s: primary %s address lookup is DNSSEC bogus: %s",
+ zname, xfr->task_transfer->lookup_target->host,
+ (why_bogus?why_bogus:""));
+ }
+ /* fall through to next-lookup / next-master */
+ } else if(rcode == LDNS_RCODE_NOERROR) {
uint16_t wanted_qtype = LDNS_RR_TYPE_A;
struct regional* temp = env->scratch;
struct query_info rq;
@@ -6830,8 +6838,7 @@ xfr_probe_send_or_end(struct auth_xfer* xfr, struct module_env* env)
/** callback for task_probe lookup of host name, of A or AAAA */
void auth_xfer_probe_lookup_callback(void* arg, int rcode, sldns_buffer* buf,
- enum sec_status ATTR_UNUSED(sec), char* ATTR_UNUSED(why_bogus),
- int ATTR_UNUSED(was_ratelimited))
+ enum sec_status sec, char* why_bogus, int ATTR_UNUSED(was_ratelimited))
{
struct auth_xfer* xfr = (struct auth_xfer*)arg;
struct module_env* env;
@@ -6844,7 +6851,16 @@ void auth_xfer_probe_lookup_callback(void* arg, int rcode, sldns_buffer* buf,
}
/* process result */
- if(rcode == LDNS_RCODE_NOERROR) {
+ if(sec == sec_status_bogus || sec == sec_status_secure_sentinel_fail) {
+ if(verbosity >= VERB_OPS) {
+ char zname[LDNS_MAX_DOMAINLEN];
+ dname_str(xfr->name, zname);
+ verbose(VERB_OPS, "auth zone %s: primary %s address probe lookup is DNSSEC bogus: %s",
+ zname, xfr->task_transfer->lookup_target->host,
+ (why_bogus?why_bogus:""));
+ }
+ /* fall through to next-lookup / next-master */
+ } else if(rcode == LDNS_RCODE_NOERROR) {
uint16_t wanted_qtype = LDNS_RR_TYPE_A;
struct regional* temp = env->scratch;
struct query_info rq;
diff --git a/services/cache/dns.c b/services/cache/dns.c
index f6ce272a5ea6..9fc79dad012c 100644
--- a/services/cache/dns.c
+++ b/services/cache/dns.c
@@ -277,6 +277,8 @@ find_closest_of_type(struct module_env* env, uint8_t* qname, size_t qnamelen,
/* snip off front label */
lablen = *qname;
+ if(lablen == 0)
+ break;
qname += lablen + 1;
qnamelen -= lablen + 1;
}
@@ -1065,7 +1067,7 @@ dns_cache_lookup(struct module_env* env,
if(env->cfg->harden_below_nxdomain) {
while(!dname_is_root(k.qname)) {
if(dpname && dpnamelen
- && !dname_subdomain_c(k.qname, dpname))
+ && !dname_strict_subdomain_c(k.qname, dpname))
break; /* no synth nxdomain above the stub */
dname_remove_label(&k.qname, &k.qname_len);
h = query_info_hash(&k, flags);
diff --git a/services/cache/rrset.c b/services/cache/rrset.c
index ab4f4c8e0afc..79bf473bb53d 100644
--- a/services/cache/rrset.c
+++ b/services/cache/rrset.c
@@ -50,6 +50,7 @@
#include "util/regional.h"
#include "util/alloc.h"
#include "util/net_help.h"
+#include "validator/val_utils.h"
void
rrset_markdel(void* key)
@@ -126,7 +127,8 @@ rrset_cache_touch(struct rrset_cache* r, struct ub_packed_rrset_key* key,
/** see if rrset needs to be updated in the cache */
static int
-need_to_update_rrset(void* nd, void* cd, time_t timenow, int equal, int ns)
+need_to_update_rrset(void* nd, void* cd, time_t timenow, int equal, int ns,
+ int a_aaaa)
{
struct packed_rrset_data* newd = (struct packed_rrset_data*)nd;
struct packed_rrset_data* cached = (struct packed_rrset_data*)cd;
@@ -151,9 +153,13 @@ need_to_update_rrset(void* nd, void* cd, time_t timenow, int equal, int ns)
return 0;
/* ghost-domain: never let an NS overwrite extend lifetime
* past the entry it replaces, regardless of trust. */
- if(ns && !TTL_IS_EXPIRED(cached->ttl, timenow) &&
+ /* Also for A/AAAA and it is glue. */
+ if((ns ||
+ (a_aaaa && cached->trust==rrset_trust_add_noAA))
+ && !TTL_IS_EXPIRED(cached->ttl, timenow) &&
newd->ttl > cached->ttl) {
size_t i;
+ if(a_aaaa) newd->trust=rrset_trust_add_noAA;
newd->ttl = cached->ttl;
for(i=0; i<(newd->count+newd->rrsig_count); i++)
if(newd->rr_ttl[i] > newd->ttl)
@@ -223,7 +229,8 @@ rrset_cache_update(struct rrset_cache* r, struct rrset_ref* ref,
equal = rrsetdata_equal((struct packed_rrset_data*)k->entry.
data, (struct packed_rrset_data*)e->data);
if(!need_to_update_rrset(k->entry.data, e->data, timenow,
- equal, (rrset_type==LDNS_RR_TYPE_NS))) {
+ equal, (rrset_type==LDNS_RR_TYPE_NS),
+ (rrset_type==LDNS_RR_TYPE_A || rrset_type==LDNS_RR_TYPE_AAAA))) {
/* cache is superior, return that value */
lock_rw_unlock(&e->lock);
ub_packed_rrset_parsedelete(k, alloc);
@@ -255,12 +262,43 @@ rrset_cache_update(struct rrset_cache* r, struct rrset_ref* ref,
return 0;
}
+/** See if the name is a within signer authority */
+static int
+dname_subdomain_rrsig_signers(uint8_t* dname,
+ struct ub_packed_rrset_key* rrset)
+{
+ struct packed_rrset_data* d = (struct packed_rrset_data*)
+ rrset->entry.data;
+ size_t i;
+ if(!d || !d->rrsig_count)
+ return 0;
+ for(i=0; i<d->rrsig_count; i++) {
+ uint8_t* sname = NULL;
+ size_t slen = 0;
+ rrsig_get_signer(d->rr_data[d->count+i], d->rr_len[d->count+i],
+ &sname, &slen);
+ if(!sname || !slen)
+ return 0; /* malformed */
+ if(!dname_subdomain_c(dname, sname))
+ return 0; /* not a subdomain */
+ }
+ return 1;
+}
+
void rrset_cache_update_wildcard(struct rrset_cache* rrset_cache,
struct ub_packed_rrset_key* rrset, uint8_t* ce, size_t ce_len,
struct alloc_cache* alloc, time_t timenow)
{
struct rrset_ref ref;
uint8_t wc_dname[LDNS_MAX_DOMAINLEN+3];
+
+ /* See if the RRSIG signer name allows this wildcard,
+ * the new rrset should fall within the zone of the RRSIG signer(s). */
+ if(!dname_subdomain_rrsig_signers(ce, rrset)) {
+ verbose(VERB_ALGO, "wildcard canonical parent outside signer authority");
+ return;
+ }
+
rrset = packed_rrset_copy_alloc(rrset, alloc, timenow);
if(!rrset) {
log_err("malloc failure in rrset_cache_update_wildcard");
diff --git a/services/listen_dnsport.c b/services/listen_dnsport.c
index 5db2b940bc04..38b493a256db 100644
--- a/services/listen_dnsport.c
+++ b/services/listen_dnsport.c
@@ -42,7 +42,6 @@
#ifdef HAVE_SYS_TYPES_H
# include <sys/types.h>
#endif
-#include <sys/time.h>
#include <limits.h>
#ifdef USE_TCP_FASTOPEN
#include <netinet/tcp.h>
@@ -2167,7 +2166,8 @@ void tcp_req_info_clear(struct tcp_req_info* req)
open = req->open_req_list;
while(open) {
nopen = open->next;
- mesh_state_remove_reply(open->mesh, open->mesh_state, req->cp);
+ mesh_state_remove_reply(open->mesh, open->mesh_state, req->cp,
+ NULL);
free(open);
open = nopen;
}
@@ -3399,14 +3399,13 @@ doq_table_delete(struct doq_table* table)
}
struct doq_timer*
-doq_timer_find_time(struct doq_table* table, struct timeval* tv)
+doq_timer_find_time(struct doq_table* table, ngtcp2_tstamp ts)
{
struct doq_timer key;
struct rbnode_type* node;
log_assert(table != NULL);
memset(&key, 0, sizeof(key));
- key.time.tv_sec = tv->tv_sec;
- key.time.tv_usec = tv->tv_usec;
+ key.time_mono = ts;
node = rbtree_search(table->timer_tree, &key);
if(node)
return (struct doq_timer*)node->key;
@@ -3454,7 +3453,7 @@ doq_timer_list_remove(struct doq_table* table, struct doq_timer* timer)
if(!timer->timer_in_list)
return;
/* The item in the rbtree has the list start and end. */
- rb_timer = doq_timer_find_time(table, &timer->time);
+ rb_timer = doq_timer_find_time(table, timer->time_mono);
if(rb_timer) {
if(timer->setlist_prev)
timer->setlist_prev->setlist_next = timer->setlist_next;
@@ -3500,7 +3499,8 @@ doq_timer_unset(struct doq_table* table, struct doq_timer* timer)
}
void doq_timer_set(struct doq_table* table, struct doq_timer* timer,
- struct doq_server_socket* worker_doq_socket, struct timeval* tv)
+ struct doq_server_socket* worker_doq_socket, struct timeval* tv,
+ ngtcp2_tstamp ts)
{
struct doq_timer* rb_timer;
if(verbosity >= VERB_ALGO && timer->conn) {
@@ -3514,14 +3514,14 @@ void doq_timer_set(struct doq_table* table, struct doq_timer* timer,
(int)rel.tv_sec, (int)rel.tv_usec);
}
if(timer->timer_in_tree || timer->timer_in_list) {
- if(timer->time.tv_sec == tv->tv_sec &&
- timer->time.tv_usec == tv->tv_usec)
+ if(timer->time_mono == ts)
return; /* already set on that time */
doq_timer_unset(table, timer);
}
- timer->time.tv_sec = tv->tv_sec;
- timer->time.tv_usec = tv->tv_usec;
- rb_timer = doq_timer_find_time(table, tv);
+ timer->time_real.tv_sec = tv->tv_sec;
+ timer->time_real.tv_usec = tv->tv_usec;
+ timer->time_mono = ts;
+ rb_timer = doq_timer_find_time(table, ts);
if(rb_timer) {
/* There is a timeout already with this value. Timer is
* added to the setlist. */
@@ -3597,15 +3597,29 @@ doq_conn_create(struct comm_point* c, struct doq_pkt_addr* paddr,
return conn;
}
+/** The arguments for doq stream tree del. */
+struct doq_stream_tree_del_args {
+ /** The doq table. */
+ struct doq_table* table;
+ /** The doq connection for the stream. */
+ struct doq_conn* conn;
+};
+
/** delete stream tree node */
static void
stream_tree_del(rbnode_type* node, void* arg)
{
- struct doq_table* table = (struct doq_table*)arg;
+ struct doq_stream_tree_del_args* args = (struct doq_stream_tree_del_args*)arg;
+ struct doq_table* table = args->table;
struct doq_stream* stream;
if(!node)
return;
stream = (struct doq_stream*)node;
+ if(stream->mesh_state) {
+ mesh_state_remove_reply(stream->mesh, stream->mesh_state,
+ args->conn->doq_socket->cp, stream);
+ stream->mesh_state = NULL;
+ }
if(stream->in)
doq_table_quic_size_subtract(table, stream->inlen);
if(stream->out)
@@ -3627,7 +3641,11 @@ doq_conn_delete(struct doq_conn* conn, struct doq_table* table)
* because the ngtcp2 conn is deleted. */
SSL_set_app_data(conn->ssl, NULL);
if(conn->stream_tree.count != 0) {
- traverse_postorder(&conn->stream_tree, stream_tree_del, table);
+ struct doq_stream_tree_del_args args;
+ memset(&args, 0, sizeof(args));
+ args.table = table;
+ args.conn = conn;
+ traverse_postorder(&conn->stream_tree, stream_tree_del, &args);
}
free(conn->key.dcid);
SSL_free(conn->ssl);
@@ -3700,13 +3718,9 @@ int doq_timer_cmp(const void* key1, const void* key2)
{
struct doq_timer* e = (struct doq_timer*)key1;
struct doq_timer* f = (struct doq_timer*)key2;
- if(e->time.tv_sec < f->time.tv_sec)
- return -1;
- if(e->time.tv_sec > f->time.tv_sec)
- return 1;
- if(e->time.tv_usec < f->time.tv_usec)
+ if(e->time_mono < f->time_mono)
return -1;
- if(e->time.tv_usec > f->time.tv_usec)
+ if(e->time_mono > f->time_mono)
return 1;
return 0;
}
@@ -3940,6 +3954,11 @@ doq_stream_close(struct doq_conn* conn, struct doq_stream* stream,
if(stream->is_closed)
return 1;
stream->is_closed = 1;
+ if(stream->mesh_state) {
+ mesh_state_remove_reply(stream->mesh, stream->mesh_state,
+ conn->doq_socket->cp, stream);
+ stream->mesh_state = NULL;
+ }
doq_stream_off_write_list(conn, stream);
if(send_shutdown) {
verbose(VERB_ALGO, "doq: shutdown stream_id %d with app_error_code %d",
@@ -3969,7 +3988,8 @@ doq_stream_close(struct doq_conn* conn, struct doq_stream* stream,
/** doq stream pick up answer data from buffer */
static int
-doq_stream_pickup_answer(struct doq_stream* stream, struct sldns_buffer* buf)
+doq_stream_pickup_answer(struct doq_conn* conn, struct doq_stream* stream,
+ struct sldns_buffer* buf)
{
stream->is_answer_available = 1;
if(stream->out) {
@@ -3979,6 +3999,11 @@ doq_stream_pickup_answer(struct doq_stream* stream, struct sldns_buffer* buf)
}
stream->nwrite = 0;
stream->outlen = sldns_buffer_limit(buf);
+ if(!doq_table_quic_size_available(conn->doq_socket->table,
+ conn->doq_socket->cfg, stream->outlen)) {
+ verbose(VERB_ALGO, "doq stream: no space for reply length");
+ return 0;
+ }
/* For quic the output bytes have to stay allocated and available,
* for potential resends, until the remote end has acknowledged them.
* This includes the tcplen start uint16_t, in outlen_wire. */
@@ -4005,24 +4030,56 @@ doq_stream_send_reply(struct doq_conn* conn, struct doq_stream* stream,
if(stream->out)
doq_table_quic_size_subtract(conn->doq_socket->table,
stream->outlen);
- if(!doq_stream_pickup_answer(stream, buf))
+ if(!doq_stream_pickup_answer(conn, stream, buf))
return 0;
doq_table_quic_size_add(conn->doq_socket->table, stream->outlen);
doq_stream_on_write_list(conn, stream);
doq_conn_write_enable(conn);
return 1;
}
+#endif /* HAVE_NGTCP2 */
+
+void
+doq_stream_add_meshstate(struct doq_stream* stream,
+ struct mesh_area* mesh, struct mesh_state* m)
+{
+#ifdef HAVE_NGTCP2
+ stream->mesh = mesh;
+ stream->mesh_state = m;
+#else
+ (void)stream; (void)mesh; (void)m;
+#endif
+}
+void
+doq_stream_remove_mesh_state(struct doq_stream* stream)
+{
+#ifdef HAVE_NGTCP2
+ if(!stream)
+ return;
+ stream->mesh_state = NULL;
+#else
+ (void)stream;
+#endif
+}
+
+#ifdef HAVE_NGTCP2
/** doq stream data length has completed, allocations can be done. False on
* allocation failure. */
static int
-doq_stream_datalen_complete(struct doq_stream* stream, struct doq_table* table)
+doq_stream_datalen_complete(struct doq_conn* conn, struct doq_stream* stream,
+ struct doq_table* table)
{
if(stream->inlen > 1024*1024) {
log_err("doq stream in length too large %d",
(int)stream->inlen);
return 0;
}
+ if(!doq_table_quic_size_available(table, conn->doq_socket->cfg,
+ stream->inlen)) {
+ verbose(VERB_ALGO, "doq stream: no space for query length");
+ return 0;
+ }
stream->in = calloc(1, stream->inlen);
if(!stream->in) {
log_err("doq could not read stream, calloc failed: "
@@ -4067,6 +4124,7 @@ doq_stream_data_complete(struct doq_conn* conn, struct doq_stream* stream)
return 0;
}
c->repinfo.doq_streamid = stream->stream_id;
+ c->repinfo.doq_stream = stream;
conn->doq_socket->current_conn = conn;
fptr_ok(fptr_whitelist_comm_point(c->callback));
if( (*c->callback)(c, c->cb_arg, NETEVENT_NOERROR, &c->repinfo)) {
@@ -4083,8 +4141,9 @@ doq_stream_data_complete(struct doq_conn* conn, struct doq_stream* stream)
/** doq receive data for a stream, more bytes of the incoming data */
static int
-doq_stream_recv_data(struct doq_stream* stream, const uint8_t* data,
- size_t datalen, int* recv_done, struct doq_table* table)
+doq_stream_recv_data(struct doq_conn* conn, struct doq_stream* stream,
+ const uint8_t* data, size_t datalen, int* recv_done,
+ struct doq_table* table)
{
int got_data = 0;
/* read the tcplength uint16_t at the start */
@@ -4105,7 +4164,7 @@ doq_stream_recv_data(struct doq_stream* stream, const uint8_t* data,
if(stream->nread == 2) {
/* the initial length value is completed */
stream->inlen = ntohs(tcplen);
- if(!doq_stream_datalen_complete(stream, table))
+ if(!doq_stream_datalen_complete(conn, stream, table))
return 0;
} else {
/* store for later */
@@ -4254,12 +4313,11 @@ doq_submit_new_token(struct doq_conn* conn)
ngtcp2_ssize tokenlen;
int ret;
const ngtcp2_path* path = ngtcp2_conn_get_path(conn->conn);
- ngtcp2_tstamp ts = doq_get_timestamp_nanosec();
tokenlen = ngtcp2_crypto_generate_regular_token(token,
conn->doq_socket->static_secret,
conn->doq_socket->static_secret_len, path->remote.addr,
- path->remote.addrlen, ts);
+ path->remote.addrlen, doq_get_timestamp_nanosec());
if(tokenlen < 0) {
log_err("doq ngtcp2_crypto_generate_regular_token failed");
return 1;
@@ -4322,8 +4380,7 @@ doq_stream_open_cb(ngtcp2_conn* ATTR_UNUSED(conn), int64_t stream_id,
verbose(VERB_ALGO, "doq: stream with this id already exists");
return 0;
}
- if(stream_id != 0 && stream_id != 4 && /* allow one stream on a new connection */
- !doq_table_quic_size_available(doq_conn->doq_socket->table,
+ if(!doq_table_quic_size_available(doq_conn->doq_socket->table,
doq_conn->doq_socket->cfg, sizeof(*stream)
+ 100 /* estimated query in */
+ 512 /* estimated response out */
@@ -4381,8 +4438,8 @@ doq_recv_stream_data_cb(ngtcp2_conn* ATTR_UNUSED(conn), uint32_t flags,
return 0;
}
if(datalen != 0) {
- if(!doq_stream_recv_data(stream, data, datalen, &recv_done,
- doq_conn->doq_socket->table))
+ if(!doq_stream_recv_data(doq_conn, stream, data, datalen,
+ &recv_done, doq_conn->doq_socket->table))
return NGTCP2_ERR_CALLBACK_FAILURE;
}
if((flags&NGTCP2_STREAM_DATA_FLAG_FIN)!=0) {
@@ -4451,6 +4508,29 @@ doq_stream_reset_cb(ngtcp2_conn* ATTR_UNUSED(conn), int64_t stream_id,
return 0;
}
+/** ngtcp2 extend_max_stream_data function */
+int doq_extend_max_stream_data_cb(ngtcp2_conn* ATTR_UNUSED(conn),
+ int64_t stream_id, uint64_t max_data, void* user_data,
+ void* ATTR_UNUSED(stream_user_data))
+{
+ struct doq_conn* doq_conn = (struct doq_conn*)user_data;
+ struct doq_stream* stream;
+ verbose(VERB_ALGO, "doq extend_max_stream_data stream id %d "
+ "max_data %d ", (int)stream_id, (int)max_data);
+ if(max_data == 0)
+ return 0;
+ stream = doq_stream_find(doq_conn, stream_id);
+ if(!stream) {
+ verbose(VERB_ALGO, "doq: unknown stream %d", (int)stream_id);
+ return 0;
+ }
+ if(!stream->is_answer_available)
+ return 0;
+ doq_stream_on_write_list(doq_conn, stream);
+ doq_conn_write_enable(doq_conn);
+ return 0;
+}
+
/** ngtcp2 acked_stream_data_offset callback function */
static int
doq_acked_stream_data_offset_cb(ngtcp2_conn* ATTR_UNUSED(conn),
@@ -4825,6 +4905,7 @@ doq_conn_setup(struct doq_conn* conn, uint8_t* scid, size_t scidlen,
callbacks.stream_open = doq_stream_open_cb;
callbacks.stream_close = doq_stream_close_cb;
callbacks.stream_reset = doq_stream_reset_cb;
+ callbacks.extend_max_stream_data = doq_extend_max_stream_data_cb;
callbacks.acked_stream_data_offset = doq_acked_stream_data_offset_cb;
callbacks.recv_stream_data = doq_recv_stream_data_cb;
@@ -5101,23 +5182,30 @@ doq_conn_clear_conids(struct doq_conn* conn)
ngtcp2_tstamp doq_get_timestamp_nanosec(void)
{
-#ifdef CLOCK_REALTIME
struct timespec tp;
memset(&tp, 0, sizeof(tp));
- /* Get a nanosecond time, that can be compared with the event base. */
- if(clock_gettime(CLOCK_REALTIME, &tp) == -1) {
- log_err("clock_gettime failed: %s", strerror(errno));
+#ifdef CLOCK_BOOTTIME
+ if(clock_gettime(CLOCK_BOOTTIME, &tp) == -1) {
+#endif
+ if(clock_gettime(CLOCK_MONOTONIC, &tp) == -1) {
+ log_err("clock_gettime failed: %s", strerror(errno));
+ }
+#ifdef CLOCK_BOOTTIME
}
+#endif
return ((uint64_t)tp.tv_sec)*((uint64_t)1000000000) +
((uint64_t)tp.tv_nsec);
-#else
+}
+
+static struct timeval doq_get_timevalue(void)
+{
struct timeval tv;
+ memset(&tv, 0, sizeof(tv));
if(gettimeofday(&tv, NULL) < 0) {
log_err("gettimeofday failed: %s", strerror(errno));
+ memset(&tv, 0, sizeof(tv));
}
- return ((uint64_t)tv.tv_sec)*((uint64_t)1000000000) +
- ((uint64_t)tv.tv_usec)*((uint64_t)1000);
-#endif /* CLOCK_REALTIME */
+ return tv;
}
/** doq start the closing period for the connection. */
@@ -5240,18 +5328,17 @@ doq_conn_recv(struct comm_point* c, struct doq_pkt_addr* paddr,
int* err_drop)
{
int ret;
- ngtcp2_tstamp ts;
struct ngtcp2_path path;
memset(&path, 0, sizeof(path));
path.remote.addr = (struct sockaddr*)&paddr->addr;
path.remote.addrlen = paddr->addrlen;
path.local.addr = (struct sockaddr*)&paddr->localaddr;
path.local.addrlen = paddr->localaddrlen;
- ts = doq_get_timestamp_nanosec();
ret = ngtcp2_conn_read_pkt(conn->conn, &path, pi,
sldns_buffer_begin(c->doq_socket->pkt_buf),
- sldns_buffer_limit(c->doq_socket->pkt_buf), ts);
+ sldns_buffer_limit(c->doq_socket->pkt_buf),
+ doq_get_timestamp_nanosec());
if(ret != 0) {
if(err_retry)
*err_retry = 0;
@@ -5339,7 +5426,6 @@ doq_conn_write_streams(struct comm_point* c, struct doq_conn* conn,
{
struct doq_stream* stream = conn->stream_write_first;
ngtcp2_path_storage ps;
- ngtcp2_tstamp ts = doq_get_timestamp_nanosec();
size_t num_packets = 0, max_packets = 65535;
ngtcp2_path_storage_zero(&ps);
@@ -5392,7 +5478,8 @@ doq_conn_write_streams(struct comm_point* c, struct doq_conn* conn,
ret = ngtcp2_conn_writev_stream(conn->conn, &ps.path, &pi,
sldns_buffer_begin(c->doq_socket->pkt_buf),
sldns_buffer_remaining(c->doq_socket->pkt_buf),
- &ndatalen, flags, stream_id, datav, datav_count, ts);
+ &ndatalen, flags, stream_id, datav, datav_count,
+ doq_get_timestamp_nanosec());
if(ret < 0) {
if(ret == NGTCP2_ERR_WRITE_MORE) {
verbose(VERB_ALGO, "doq: write more, ndatalen %d", (int)ndatalen);
@@ -5407,26 +5494,20 @@ doq_conn_write_streams(struct comm_point* c, struct doq_conn* conn,
continue;
} else if(ret == NGTCP2_ERR_STREAM_DATA_BLOCKED) {
verbose(VERB_ALGO, "doq: ngtcp2_conn_writev_stream returned NGTCP2_ERR_STREAM_DATA_BLOCKED");
-#ifdef HAVE_NGTCP2_CCERR_DEFAULT
- ngtcp2_ccerr_set_application_error(
- &conn->ccerr, -1, NULL, 0);
-#else
- ngtcp2_connection_close_error_set_application_error(&conn->last_error, -1, NULL, 0);
-#endif
- if(err_drop)
- *err_drop = 0;
- if(!doq_conn_close_error(c, conn)) {
- if(err_drop)
- *err_drop = 1;
+ if(stream) {
+ doq_stream_off_write_list(conn, stream);
+ stream = stream->write_next;
+ continue;
+ } else {
+ break;
}
- return 0;
} else if(ret == NGTCP2_ERR_STREAM_SHUT_WR) {
verbose(VERB_ALGO, "doq: ngtcp2_conn_writev_stream returned NGTCP2_ERR_STREAM_SHUT_WR");
#ifdef HAVE_NGTCP2_CCERR_DEFAULT
ngtcp2_ccerr_set_application_error(
- &conn->ccerr, -1, NULL, 0);
+ &conn->ccerr, DOQ_APP_ERROR_CODE, NULL, 0);
#else
- ngtcp2_connection_close_error_set_application_error(&conn->last_error, -1, NULL, 0);
+ ngtcp2_connection_close_error_set_application_error(&conn->last_error, DOQ_APP_ERROR_CODE, NULL, 0);
#endif
if(err_drop)
*err_drop = 0;
@@ -5464,7 +5545,8 @@ doq_conn_write_streams(struct comm_point* c, struct doq_conn* conn,
if(ret == 0) {
/* congestion limited */
doq_conn_write_disable(conn);
- ngtcp2_conn_update_pkt_tx_time(conn->conn, ts);
+ ngtcp2_conn_update_pkt_tx_time(conn->conn,
+ doq_get_timestamp_nanosec());
return 1;
}
sldns_buffer_set_position(c->doq_socket->pkt_buf, ret);
@@ -5478,7 +5560,7 @@ doq_conn_write_streams(struct comm_point* c, struct doq_conn* conn,
if(stream)
stream = stream->write_next;
}
- ngtcp2_conn_update_pkt_tx_time(conn->conn, ts);
+ ngtcp2_conn_update_pkt_tx_time(conn->conn, doq_get_timestamp_nanosec());
return 1;
}
@@ -5555,32 +5637,35 @@ doq_table_pop_first(struct doq_table* table)
}
int
-doq_conn_check_timer(struct doq_conn* conn, struct timeval* tv)
+doq_conn_check_timer(struct doq_conn* conn, struct timeval* tv, ngtcp2_tstamp* ts)
{
- ngtcp2_tstamp expiry = ngtcp2_conn_get_expiry(conn->conn);
- ngtcp2_tstamp now = doq_get_timestamp_nanosec();
+ ngtcp2_tstamp doq_expiry = ngtcp2_conn_get_expiry(conn->conn);
+ ngtcp2_tstamp doq_now = doq_get_timestamp_nanosec();
ngtcp2_tstamp t;
+ struct timeval now = doq_get_timevalue();
- if(expiry <= now) {
+ if(doq_expiry <= doq_now || doq_expiry == UINT64_MAX) {
+ /* UINT64_MAX means there is no next expiry. */
/* The timer has already expired, add with zero timeout.
* This should call the callback straight away. Calling it
* from the event callbacks is cleaner than calling it here,
* because then it is always called with the same locks and
* so on. This routine only has the conn.lock. */
- t = now;
+ t = doq_now;
+ memcpy(tv, &now, sizeof(*tv));
} else {
- t = expiry;
+ t = doq_expiry;
+ memset(tv, 0, sizeof(*tv));
+ tv->tv_sec = (doq_expiry - doq_now) / NGTCP2_SECONDS;
+ tv->tv_usec = ((doq_expiry - doq_now) / NGTCP2_MICROSECONDS)%1000000;
+ timeval_add(tv, &now);
}
- /* convert to timeval */
- memset(tv, 0, sizeof(*tv));
- tv->tv_sec = t / NGTCP2_SECONDS;
- tv->tv_usec = (t / NGTCP2_MICROSECONDS)%1000000;
+ *ts = t;
/* If we already have a timer, is it the right value? */
if(conn->timer.timer_in_tree || conn->timer.timer_in_list) {
- if(conn->timer.time.tv_sec == tv->tv_sec &&
- conn->timer.time.tv_usec == tv->tv_usec)
+ if(conn->timer.time_mono == *ts)
return 0;
}
return 1;
@@ -5601,13 +5686,12 @@ doq_conn_log_line(struct doq_conn* conn, char* s)
int
doq_conn_handle_timeout(struct doq_conn* conn)
{
- ngtcp2_tstamp now = doq_get_timestamp_nanosec();
int rv;
if(verbosity >= VERB_ALGO)
doq_conn_log_line(conn, "timeout");
- rv = ngtcp2_conn_handle_expiry(conn->conn, now);
+ rv = ngtcp2_conn_handle_expiry(conn->conn, doq_get_timestamp_nanosec());
if(rv != 0) {
verbose(VERB_ALGO, "ngtcp2_conn_handle_expiry failed: %s",
ngtcp2_strerror(rv));
diff --git a/services/listen_dnsport.h b/services/listen_dnsport.h
index 963595a1ccc5..ae0463468520 100644
--- a/services/listen_dnsport.h
+++ b/services/listen_dnsport.h
@@ -61,6 +61,8 @@ struct config_file;
struct addrinfo;
struct sldns_buffer;
struct tcl_list;
+struct mesh_area;
+struct mesh_state;
/**
* Listening for queries structure.
@@ -538,8 +540,11 @@ void doq_table_delete(struct doq_table* table);
struct doq_timer {
/** The rbnode in the tree sorted by timeout value. Key this struct. */
struct rbnode_type node;
+ /** The timeout value. Monotonic value used with ngtcp2.
+ * This time value is used for the tree operations. */
+ ngtcp2_tstamp time_mono;
/** The timeout value. Absolute time value. */
- struct timeval time;
+ struct timeval time_real;
/** If the timer is in the time tree, with the node. */
int timer_in_tree;
/** If there are more timers with the exact same timeout value,
@@ -689,6 +694,11 @@ struct doq_stream {
uint8_t* out;
/** if the stream is on the write list */
uint8_t on_write_list;
+ /** The mesh area and mesh state, set when this stream's query was
+ * dispatched into the mesh; used to detach the reply on stream close */
+ struct mesh_area* mesh;
+ /** the mesh state for the query, is nonNULL when there is one. */
+ struct mesh_state* mesh_state;
/** the prev and next on the write list, if on the list */
struct doq_stream* write_prev, *write_next;
};
@@ -791,7 +801,16 @@ int doq_stream_close(struct doq_conn* conn, struct doq_stream* stream,
/** send reply for a connection */
int doq_stream_send_reply(struct doq_conn* conn, struct doq_stream* stream,
struct sldns_buffer* buf);
+#endif /* HAVE_NGTCP2 */
+/** add mesh state to doq stream */
+void doq_stream_add_meshstate(struct doq_stream* stream,
+ struct mesh_area* mesh, struct mesh_state* m);
+
+/** remove mesh state from doq stream */
+void doq_stream_remove_mesh_state(struct doq_stream* stream);
+
+#ifdef HAVE_NGTCP2
/** the connection has write interest, wants to write packets */
void doq_conn_write_enable(struct doq_conn* conn);
@@ -813,10 +832,12 @@ struct doq_conn* doq_table_pop_first(struct doq_table* table);
* doq check if the timer for the conn needs to be changed.
* @param conn: connection, caller must hold lock on it.
* @param tv: time value, absolute time, returned.
+ * @param ts: time stamp, absolute time, returned.
* @return true if timer needs to be set to tv, false if no change is needed
* to the timer. The timer is already set to the right time in that case.
*/
-int doq_conn_check_timer(struct doq_conn* conn, struct timeval* tv);
+int doq_conn_check_timer(struct doq_conn* conn, struct timeval* tv,
+ ngtcp2_tstamp* ts);
/** doq remove timer from tree */
void doq_timer_tree_remove(struct doq_table* table, struct doq_timer* timer);
@@ -829,11 +850,12 @@ void doq_timer_unset(struct doq_table* table, struct doq_timer* timer);
/** doq set the timer and add it. */
void doq_timer_set(struct doq_table* table, struct doq_timer* timer,
- struct doq_server_socket* worker_doq_socket, struct timeval* tv);
+ struct doq_server_socket* worker_doq_socket, struct timeval* tv,
+ ngtcp2_tstamp ts);
/** doq find a timeout in the timer tree */
struct doq_timer* doq_timer_find_time(struct doq_table* table,
- struct timeval* tv);
+ ngtcp2_tstamp ts);
/** doq handle timeout for a connection. Pass conn locked. Returns false for
* deletion. */
@@ -851,6 +873,9 @@ int doq_table_quic_size_available(struct doq_table* table,
/** doq get the quic size value */
size_t doq_table_quic_size_get(struct doq_table* table);
+
+/** get a timestamp in nanoseconds */
+ngtcp2_tstamp doq_get_timestamp_nanosec(void);
#endif /* HAVE_NGTCP2 */
char* set_ip_dscp(int socket, int addrfamily, int ds);
@@ -866,8 +891,4 @@ void doq_client_event_cb(int fd, short event, void* arg);
/** timer event callback for testcode/doqclient */
void doq_client_timer_cb(int fd, short event, void* arg);
-#ifdef HAVE_NGTCP2
-/** get a timestamp in nanoseconds */
-ngtcp2_tstamp doq_get_timestamp_nanosec(void);
-#endif
#endif /* LISTEN_DNSPORT_H */
diff --git a/services/localzone.h b/services/localzone.h
index 76c011836030..e3fb0afe3e4a 100644
--- a/services/localzone.h
+++ b/services/localzone.h
@@ -573,7 +573,7 @@ enum respip_action {
respip_always_nxdomain = local_zone_always_nxdomain,
/** answer with nodata response */
respip_always_nodata = local_zone_always_nodata,
- /** answer with nodata response */
+ /** drop query */
respip_always_deny = local_zone_always_deny,
/** RPZ: truncate answer in order to force switch to tcp */
respip_truncate = local_zone_truncate,
diff --git a/services/mesh.c b/services/mesh.c
index 286901047698..99361b17f964 100644
--- a/services/mesh.c
+++ b/services/mesh.c
@@ -467,6 +467,8 @@ void mesh_new_client(struct mesh_area* mesh, struct query_info* qinfo,
"incoming query.");
if(rep->c->use_h2)
http2_stream_remove_mesh_state(rep->c->h2_stream);
+ else if(rep->c->type == comm_doq && rep->doq_stream)
+ doq_stream_remove_mesh_state(rep->doq_stream);
comm_point_drop_reply(rep);
mesh->stats_dropped++;
return;
@@ -480,6 +482,8 @@ void mesh_new_client(struct mesh_area* mesh, struct query_info* qinfo,
"dropping incoming query.");
if(rep->c->use_h2)
http2_stream_remove_mesh_state(rep->c->h2_stream);
+ else if(rep->c->type == comm_doq && rep->doq_stream)
+ doq_stream_remove_mesh_state(rep->doq_stream);
comm_point_drop_reply(rep);
mesh->num_queries_replyaddr_limit++;
return;
@@ -552,6 +556,8 @@ void mesh_new_client(struct mesh_area* mesh, struct query_info* qinfo,
}
if(rep->c->use_h2) {
http2_stream_add_meshstate(rep->c->h2_stream, mesh, s);
+ } else if(rep->c->type == comm_doq && rep->doq_stream) {
+ doq_stream_add_meshstate(rep->doq_stream, mesh, s);
}
/* add serve expired timer if required and not already there */
if(timeout && !mesh_serve_expired_init(s, timeout)) {
@@ -605,6 +611,8 @@ servfail_mem:
qinfo, qid, qflags, edns);
if(rep->c->use_h2)
http2_stream_remove_mesh_state(rep->c->h2_stream);
+ else if(rep->c->type == comm_doq && rep->doq_stream)
+ doq_stream_remove_mesh_state(rep->doq_stream);
comm_point_send_reply(rep);
if(added)
mesh_state_delete(&s->s);
@@ -925,8 +933,7 @@ cfg_region_strlist_copy(struct regional* region, struct config_strlist* list)
return result;
}
-/** Copy the client info to the query region. */
-static struct respip_client_info*
+struct respip_client_info*
mesh_copy_client_info(struct regional* region, struct respip_client_info* cinfo)
{
size_t i;
@@ -971,6 +978,11 @@ mesh_copy_client_info(struct regional* region, struct respip_client_info* cinfo)
cinfo->view->name);
if(!client_info->view_name)
return NULL;
+ } else if(cinfo->view_name) {
+ client_info->view_name = regional_strdup(region,
+ cinfo->view_name);
+ if(!client_info->view_name)
+ return NULL;
}
return client_info;
}
@@ -1484,6 +1496,10 @@ mesh_send_reply(struct mesh_state* m, int rcode, struct reply_info* rep,
* for HTTP/2 stream to refer to mesh state, in case
* connection gets cleanup before HTTP/2 stream close. */
r->h2_stream->mesh_state = NULL;
+#ifdef HAVE_NGTCP2
+ } else if(r->query_reply.doq_stream) {
+ r->query_reply.doq_stream->mesh_state = NULL;
+#endif
}
/* send the reply */
/* We don't reuse the encoded answer if:
@@ -1638,9 +1654,9 @@ static void dns_error_reporting(struct module_qstate* qstate,
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) {
+ agent_domain_len = dname_valid(agent_domain, opt->opt_len);
+ if(agent_domain_len < 3) {
/* The agent domain needs to be a valid dname that is not the
* root; from RFC9567. */
return;
@@ -1777,6 +1793,8 @@ void mesh_query_done(struct mesh_state* mstate)
mstate->reply_list = NULL;
if(r->query_reply.c->use_h2)
http2_stream_remove_mesh_state(r->h2_stream);
+ else if(r->query_reply.doq_stream)
+ doq_stream_remove_mesh_state(r->query_reply.doq_stream);
comm_point_drop_reply(&r->query_reply);
mstate->reply_list = reply_list;
log_assert(mstate->s.env->mesh->num_reply_addrs > 0);
@@ -1814,6 +1832,8 @@ void mesh_query_done(struct mesh_state* mstate)
mstate->reply_list = NULL;
if(r->query_reply.c->use_h2) {
http2_stream_remove_mesh_state(r->h2_stream);
+ } else if(r->query_reply.doq_stream) {
+ doq_stream_remove_mesh_state(r->query_reply.doq_stream);
}
comm_point_drop_reply(&r->query_reply);
mstate->reply_list = reply_list;
@@ -2009,6 +2029,8 @@ int mesh_state_add_reply(struct mesh_state* s, struct edns_data* edns,
if(rep->c->use_h2)
r->h2_stream = rep->c->h2_stream;
else r->h2_stream = NULL;
+ if(rep->c->type != comm_doq)
+ r->query_reply.doq_stream = NULL;
/* Data related to local alias stored in 'qinfo' (if any) is ephemeral
* and can be different for different original queries (even if the
@@ -2366,7 +2388,7 @@ void mesh_list_remove(struct mesh_state* m, struct mesh_state** fp,
}
void mesh_state_remove_reply(struct mesh_area* mesh, struct mesh_state* m,
- struct comm_point* cp)
+ struct comm_point* cp, struct doq_stream* doq_stream)
{
struct mesh_reply* n, *prev = NULL;
n = m->reply_list;
@@ -2374,7 +2396,8 @@ void mesh_state_remove_reply(struct mesh_area* mesh, struct mesh_state* m,
* there is no accounting twice */
if(!n) return; /* nothing to remove, also no accounting needed */
while(n) {
- if(n->query_reply.c == cp) {
+ if(n->query_reply.c == cp
+ && (!doq_stream || n->query_reply.doq_stream == doq_stream)) {
/* unlink it */
if(prev) prev->next = n->next;
else m->reply_list = n->next;
@@ -2387,6 +2410,10 @@ void mesh_state_remove_reply(struct mesh_area* mesh, struct mesh_state* m,
* share the same comm_point); make sure the streams
* don't point back. */
if(n->h2_stream) n->h2_stream->mesh_state = NULL;
+#ifdef HAVE_NGTCP2
+ if(n->query_reply.doq_stream)
+ n->query_reply.doq_stream->mesh_state = NULL;
+#endif
/* prev = prev; */
n = n->next;
@@ -2427,9 +2454,10 @@ apply_respip_action(struct module_qstate* qstate,
/* xxx_deny actions mean dropping the reply, unless the original reply
* was redirected to response-ip data. */
- if((actinfo->action == respip_deny ||
+ if(actinfo->action == respip_always_deny ||
+ ((actinfo->action == respip_deny ||
actinfo->action == respip_inform_deny) &&
- *encode_repp == rep)
+ *encode_repp == rep))
*encode_repp = NULL;
return 1;
@@ -2494,12 +2522,15 @@ mesh_serve_expired_callback(void* arg)
qstate->client_info, &actinfo, msg->rep, &alias_rrset, &encode_rep,
qstate->env->auth_zones)) {
return;
- } else if(partial_rep &&
- !respip_merge_cname(partial_rep, &qstate->qinfo, msg->rep,
+ } else if(partial_rep) {
+ if(!respip_merge_cname(partial_rep, &qstate->qinfo, msg->rep,
qstate->client_info, must_validate, &encode_rep, qstate->region,
qstate->env->auth_zones, qstate->env->views,
qstate->env->respip_set)) {
- return;
+ return;
+ }
+ /* merge succeeded; final reply, no further alias pass */
+ partial_rep = NULL;
}
if(!encode_rep || alias_rrset) {
if(!encode_rep) {
@@ -2510,6 +2541,7 @@ mesh_serve_expired_callback(void* arg)
partial_rep = encode_rep;
}
}
+ msg->rep = encode_rep;
/* We've found a partial reply ending with an
* alias. Replace the lookup qinfo for the
* alias target and lookup the cache again to
@@ -2536,9 +2568,10 @@ mesh_serve_expired_callback(void* arg)
log_dns_msg("Serve expired lookup", &qstate->qinfo, msg->rep);
for(r = mstate->reply_list; r; r = r->next) {
- struct timeval old;
- timeval_subtract(&old, mstate->s.env->now_tv, &r->start_time);
- if(mstate->s.env->cfg->discard_timeout != 0 &&
+ if(mesh_is_udp(r)) {
+ struct timeval old;
+ timeval_subtract(&old, mstate->s.env->now_tv, &r->start_time);
+ if(mstate->s.env->cfg->discard_timeout != 0 &&
((int)old.tv_sec)*1000+((int)old.tv_usec)/1000 >
mstate->s.env->cfg->discard_timeout) {
/* Drop the reply, it is too old */
@@ -2554,10 +2587,15 @@ mesh_serve_expired_callback(void* arg)
mstate->reply_list = NULL;
if(r->query_reply.c->use_h2)
http2_stream_remove_mesh_state(r->h2_stream);
+ else if(r->query_reply.doq_stream)
+ doq_stream_remove_mesh_state(r->query_reply.doq_stream);
comm_point_drop_reply(&r->query_reply);
mstate->reply_list = reply_list;
+ log_assert(mstate->s.env->mesh->num_reply_addrs > 0);
+ mstate->s.env->mesh->num_reply_addrs--;
mstate->s.env->mesh->num_queries_discard_timeout++;
continue;
+ }
}
i++;
diff --git a/services/mesh.h b/services/mesh.h
index 9ee585156fd4..6ea63d098916 100644
--- a/services/mesh.h
+++ b/services/mesh.h
@@ -683,9 +683,11 @@ void mesh_list_remove(struct mesh_state* m, struct mesh_state** fp,
* @param mesh: to update the counters.
* @param m: the mesh state.
* @param cp: the comm_point to remove from the list.
+ * @param doq_stream: if not NULL, it specifies the doq_stream to match
+ * for the delete.
*/
void mesh_state_remove_reply(struct mesh_area* mesh, struct mesh_state* m,
- struct comm_point* cp);
+ struct comm_point* cp, struct doq_stream* doq_stream);
/** Callback for when the serve expired client timer has run out. Tries to
* find an expired answer in the cache and reply that to the client.
@@ -736,4 +738,8 @@ void mesh_respond_serve_expired(struct mesh_state* mstate);
void mesh_remove_callback(struct mesh_area* mesh, struct query_info* qinfo,
uint16_t qflags, mesh_cb_func_type cb, void* cb_arg);
+/** Copy the client info to the query region. */
+struct respip_client_info* mesh_copy_client_info(struct regional* region,
+ struct respip_client_info* cinfo);
+
#endif /* SERVICES_MESH_H */
diff --git a/services/outside_network.c b/services/outside_network.c
index 8034ff60ba10..9dfa8b4d0c46 100644
--- a/services/outside_network.c
+++ b/services/outside_network.c
@@ -208,6 +208,7 @@ static void
waiting_tcp_delete(struct waiting_tcp* w)
{
if(!w) return;
+ free(w->tls_auth_name);
if(w->timer)
comm_timer_delete(w->timer);
free(w);
@@ -1480,7 +1481,7 @@ portcomm_loweruse(struct outside_network* outnet, struct port_comm* pc)
pif = pc->pif;
log_assert(pif->inuse > 0);
#ifndef DISABLE_EXPLICIT_PORT_RANDOMISATION
- pif->avail_ports[pif->avail_total - pif->inuse] = pc->number;
+ shared_ports_return_port(outnet->shared_ports, pif->shpif, pc->number);
#endif
pif->inuse--;
pif->out[pc->index] = pif->out[pif->inuse];
@@ -1694,19 +1695,19 @@ create_pending_tcp(struct outside_network* outnet, size_t bufsize)
}
/** setup an outgoing interface, ready address */
-static int setup_if(struct port_if* pif, const char* addrstr,
- int* avail, int numavail, size_t numfd)
+static int setup_if(struct port_if* pif, const char* addrstr, size_t numfd,
+ struct shared_ports* shp)
{
-#ifndef DISABLE_EXPLICIT_PORT_RANDOMISATION
- pif->avail_total = numavail;
- pif->avail_ports = (int*)memdup(avail, (size_t)numavail*sizeof(int));
- if(!pif->avail_ports)
- return 0;
-#endif
if(!ipstrtoaddr(addrstr, UNBOUND_DNS_PORT, &pif->addr, &pif->addrlen) &&
!netblockstrtoaddr(addrstr, UNBOUND_DNS_PORT,
&pif->addr, &pif->addrlen, &pif->pfxlen))
return 0;
+#ifndef DISABLE_EXPLICIT_PORT_RANDOMISATION
+ pif->shpif = shared_ports_find_if(shp, &pif->addr, pif->addrlen,
+ pif->pfxlen);
+#else
+ (void)shp;
+#endif
pif->maxout = (int)numfd;
pif->inuse = 0;
pif->out = (struct port_comm**)calloc(numfd,
@@ -1720,12 +1721,12 @@ struct outside_network*
outside_network_create(struct comm_base *base, size_t bufsize,
size_t num_ports, char** ifs, int num_ifs, int do_ip4,
int do_ip6, size_t num_tcp, int dscp, struct infra_cache* infra,
- struct ub_randstate* rnd, int use_caps_for_id, int* availports,
- int numavailports, size_t unwanted_threshold, int tcp_mss,
+ struct ub_randstate* rnd, int use_caps_for_id,
+ size_t unwanted_threshold, int tcp_mss,
void (*unwanted_action)(void*), void* unwanted_param, int do_udp,
void* sslctx, int delayclose, int tls_use_sni, struct dt_env* dtenv,
int udp_connect, int max_reuse_tcp_queries, int tcp_reuse_timeout,
- int tcp_auth_query_timeout)
+ int tcp_auth_query_timeout, struct shared_ports* shared_ports)
{
struct outside_network* outnet = (struct outside_network*)
calloc(1, sizeof(struct outside_network));
@@ -1760,6 +1761,7 @@ outside_network_create(struct comm_base *base, size_t bufsize,
outnet->do_udp = do_udp;
outnet->tcp_mss = tcp_mss;
outnet->ip_dscp = dscp;
+ outnet->shared_ports = shared_ports;
#ifndef S_SPLINT_S
if(delayclose) {
outnet->delayclose = 1;
@@ -1770,7 +1772,7 @@ outside_network_create(struct comm_base *base, size_t bufsize,
if(udp_connect) {
outnet->udp_connect = 1;
}
- if(numavailports == 0 || num_ports == 0) {
+ if(num_ports == 0) {
log_err("no outgoing ports available");
outside_network_delete(outnet);
return NULL;
@@ -1831,13 +1833,13 @@ outside_network_create(struct comm_base *base, size_t bufsize,
/* allocate interfaces */
if(num_ifs == 0) {
if(do_ip4 && !setup_if(&outnet->ip4_ifs[0], "0.0.0.0",
- availports, numavailports, num_ports)) {
+ num_ports, outnet->shared_ports)) {
log_err("malloc failed");
outside_network_delete(outnet);
return NULL;
}
if(do_ip6 && !setup_if(&outnet->ip6_ifs[0], "::",
- availports, numavailports, num_ports)) {
+ num_ports, outnet->shared_ports)) {
log_err("malloc failed");
outside_network_delete(outnet);
return NULL;
@@ -1848,7 +1850,7 @@ outside_network_create(struct comm_base *base, size_t bufsize,
for(i=0; i<num_ifs; i++) {
if(str_is_ip6(ifs[i]) && do_ip6) {
if(!setup_if(&outnet->ip6_ifs[done_6], ifs[i],
- availports, numavailports, num_ports)){
+ num_ports, outnet->shared_ports)){
log_err("malloc failed");
outside_network_delete(outnet);
return NULL;
@@ -1857,7 +1859,7 @@ outside_network_create(struct comm_base *base, size_t bufsize,
}
if(!str_is_ip6(ifs[i]) && do_ip4) {
if(!setup_if(&outnet->ip4_ifs[done_4], ifs[i],
- availports, numavailports, num_ports)){
+ num_ports, outnet->shared_ports)){
log_err("malloc failed");
outside_network_delete(outnet);
return NULL;
@@ -1935,9 +1937,6 @@ outside_network_delete(struct outside_network* outnet)
comm_point_delete(pc->cp);
free(pc);
}
-#ifndef DISABLE_EXPLICIT_PORT_RANDOMISATION
- free(outnet->ip4_ifs[i].avail_ports);
-#endif
free(outnet->ip4_ifs[i].out);
}
free(outnet->ip4_ifs);
@@ -1951,9 +1950,6 @@ outside_network_delete(struct outside_network* outnet)
comm_point_delete(pc->cp);
free(pc);
}
-#ifndef DISABLE_EXPLICIT_PORT_RANDOMISATION
- free(outnet->ip6_ifs[i].avail_ports);
-#endif
free(outnet->ip6_ifs[i].out);
}
free(outnet->ip6_ifs);
@@ -2163,7 +2159,10 @@ static int
select_ifport(struct outside_network* outnet, struct pending* pend,
int num_if, struct port_if* ifs)
{
- int my_if, my_port, fd, portno, inuse, tries=0;
+ int my_if, fd, portno, inuse, tries=0;
+#ifndef DISABLE_EXPLICIT_PORT_RANDOMISATION
+ int reused;
+#endif
struct port_if* pif;
/* randomly select interface and port */
if(num_if == 0) {
@@ -2177,37 +2176,35 @@ select_ifport(struct outside_network* outnet, struct pending* pend,
my_if = ub_random_max(outnet->rnd, num_if);
pif = &ifs[my_if];
#ifndef DISABLE_EXPLICIT_PORT_RANDOMISATION
- if(outnet->udp_connect) {
- /* if we connect() we cannot reuse fds for a port */
- if(pif->inuse >= pif->avail_total) {
- tries++;
- if(tries < MAX_PORT_RETRY)
- continue;
- log_err("failed to find an open port, drop msg");
- return 0;
- }
- my_port = pif->inuse + ub_random_max(outnet->rnd,
- pif->avail_total - pif->inuse);
- } else {
- my_port = ub_random_max(outnet->rnd, pif->avail_total);
- if(my_port < pif->inuse) {
- /* port already open */
- pend->pc = pif->out[my_port];
- verbose(VERB_ALGO, "using UDP if=%d port=%d",
- my_if, pend->pc->number);
- break;
- }
+ if(!shared_ports_fetch_random(outnet->shared_ports,
+ pif->shpif, outnet->rnd, outnet->udp_connect,
+ pif->inuse, &portno, &reused)) {
+ tries++;
+ if(tries < MAX_PORT_RETRY)
+ continue;
+ log_err("failed to find an open port, drop msg");
+ return 0;
+ }
+ if(reused) {
+ /* port already open */
+ log_assert(portno < pif->inuse);
+ pend->pc = pif->out[portno];
+ verbose(VERB_ALGO, "using UDP if=%d port=%d",
+ my_if, pend->pc->number);
+ break;
}
- /* try to open new port, if fails, loop to try again */
- log_assert(pif->inuse < pif->maxout);
- portno = pif->avail_ports[my_port - pif->inuse];
#else
- my_port = portno = 0;
+ portno = 0;
#endif
+ /* try to open new port, if fails, loop to try again */
fd = udp_sockport(&pif->addr, pif->addrlen, pif->pfxlen,
portno, &inuse, outnet->rnd, outnet->ip_dscp);
if(fd == -1 && !inuse) {
/* nonrecoverable error making socket */
+#ifndef DISABLE_EXPLICIT_PORT_RANDOMISATION
+ shared_ports_return_port(outnet->shared_ports,
+ pif->shpif, portno);
+#endif
return 0;
}
if(fd != -1) {
@@ -2224,6 +2221,11 @@ select_ifport(struct outside_network* outnet, struct pending* pend,
pend->addrlen);
}
sock_close(fd);
+#ifndef DISABLE_EXPLICIT_PORT_RANDOMISATION
+ shared_ports_return_port(
+ outnet->shared_ports,
+ pif->shpif, portno);
+#endif
return 0;
}
}
@@ -2241,14 +2243,14 @@ select_ifport(struct outside_network* outnet, struct pending* pend,
/* grab port in interface */
pif->out[pif->inuse] = pend->pc;
-#ifndef DISABLE_EXPLICIT_PORT_RANDOMISATION
- pif->avail_ports[my_port - pif->inuse] =
- pif->avail_ports[pif->avail_total-pif->inuse-1];
-#endif
pif->inuse++;
break;
}
/* failed, already in use */
+#ifndef DISABLE_EXPLICIT_PORT_RANDOMISATION
+ shared_ports_return_port(outnet->shared_ports, pif->shpif,
+ portno);
+#endif
verbose(VERB_QUERY, "port %d in use, trying another", portno);
tries++;
if(tries == MAX_PORT_RETRY) {
@@ -2540,7 +2542,16 @@ pending_tcp_query(struct serviced_query* sq, sldns_buffer* packet,
w->cb = callback;
w->cb_arg = callback_arg;
w->ssl_upstream = sq->ssl_upstream;
- w->tls_auth_name = sq->tls_auth_name;
+ if(sq->tls_auth_name) {
+ w->tls_auth_name = strdup(sq->tls_auth_name);
+ if(!w->tls_auth_name) {
+ comm_timer_delete(w->timer);
+ free(w);
+ return NULL;
+ }
+ } else {
+ w->tls_auth_name = NULL;
+ }
w->timeout = timeout;
w->id_node.key = NULL;
w->write_wait_prev = NULL;
@@ -3630,13 +3641,16 @@ fd_for_dest(struct outside_network* outnet, struct sockaddr_storage* to_addr,
{
struct sockaddr_storage* addr;
socklen_t addrlen;
- int i, try, pnum, dscp;
+ int i, try, dscp;
struct port_if* pif;
/* create fd */
dscp = outnet->ip_dscp;
for(try = 0; try<1000; try++) {
int port = 0;
+#ifndef DISABLE_EXPLICIT_PORT_RANDOMISATION
+ int reused = 0;
+#endif
int freebind = 0;
int noproto = 0;
int inuse = 0;
@@ -3665,16 +3679,18 @@ fd_for_dest(struct outside_network* outnet, struct sockaddr_storage* to_addr,
addr = &pif->addr;
addrlen = pif->addrlen;
#ifndef DISABLE_EXPLICIT_PORT_RANDOMISATION
- pnum = ub_random_max(outnet->rnd, pif->avail_total);
- if(pnum < pif->inuse) {
- /* port already open */
- port = pif->out[pnum]->number;
- } else {
- /* unused ports in start part of array */
- port = pif->avail_ports[pnum - pif->inuse];
+ if(!shared_ports_fetch_random(outnet->shared_ports,
+ pif->shpif, outnet->rnd, 0, pif->inuse,
+ &port, &reused)) {
+ /* try again, perhaps another interface. */
+ continue;
+ }
+ if(reused) {
+ log_assert(port < pif->inuse);
+ port = pif->out[port]->number;
}
#else
- pnum = port = 0;
+ port = 0;
#endif
if(addr_is_ip6(to_addr, to_addrlen)) {
struct sockaddr_in6 sa = *(struct sockaddr_in6*)addr;
@@ -3689,6 +3705,14 @@ fd_for_dest(struct outside_network* outnet, struct sockaddr_storage* to_addr,
(struct sockaddr*)addr, addrlen, 1, &inuse, &noproto,
0, 0, 0, NULL, 0, freebind, 0, dscp);
}
+#ifndef DISABLE_EXPLICIT_PORT_RANDOMISATION
+ if(!reused) {
+ /* Return the port to the pool, since the caller does
+ * not keep track of it, also have done fd, and bind. */
+ shared_ports_return_port(outnet->shared_ports,
+ pif->shpif, port);
+ }
+#endif
if(fd != -1) {
return fd;
}
@@ -3919,11 +3943,7 @@ if_get_mem(struct port_if* pif)
{
size_t s;
int i;
- s = sizeof(*pif) +
-#ifndef DISABLE_EXPLICIT_PORT_RANDOMISATION
- sizeof(int)*pif->avail_total +
-#endif
- sizeof(struct port_comm*)*pif->maxout;
+ s = sizeof(*pif) + sizeof(struct port_comm*)*pif->maxout;
for(i=0; i<pif->inuse; i++)
s += sizeof(*pif->out[i]) +
comm_point_get_mem(pif->out[i]->cp);
@@ -4011,3 +4031,237 @@ serviced_get_mem(struct serviced_query* sq)
return s;
}
+#ifndef DISABLE_EXPLICIT_PORT_RANDOMISATION
+/** Setup shared port interface */
+static int shared_ports_setup_if(struct shared_ports_if* shpif, char* str,
+ int* availports, int numavailports)
+{
+ shpif->avail_ports = (int*)memdup(availports,
+ (size_t)numavailports*sizeof(int));
+ if(!shpif->avail_ports)
+ return 0;
+ shpif->avail_total = numavailports;
+ shpif->inuse = 0;
+ shpif->pfxlen = 0;
+ if(!ipstrtoaddr(str, UNBOUND_DNS_PORT, &shpif->addr, &shpif->addrlen) &&
+ !netblockstrtoaddr(str, UNBOUND_DNS_PORT, &shpif->addr,
+ &shpif->addrlen, &shpif->pfxlen))
+ return 0;
+ return 1;
+}
+#endif
+
+#ifndef DISABLE_EXPLICIT_PORT_RANDOMISATION
+/** Allocate shared ports interfaces */
+static int shared_ports_alloc_ifs(struct shared_ports* shp, char** ifs,
+ int num_ifs, int do_ip4, int do_ip6, int* availports,
+ int numavailports)
+{
+#ifndef INET6
+ do_ip6 = 0;
+#endif
+ calc_num46(ifs, num_ifs, do_ip4, do_ip6,
+ &shp->num_ip4, &shp->num_ip6);
+ if(shp->num_ip4 != 0) {
+ if(!(shp->ip4_ifs = (struct shared_ports_if*)calloc(
+ (size_t)shp->num_ip4,
+ sizeof(struct shared_ports_if))))
+ return 0;
+ }
+ if(shp->num_ip6 != 0) {
+ if(!(shp->ip6_ifs = (struct shared_ports_if*)calloc(
+ (size_t)shp->num_ip6,
+ sizeof(struct shared_ports_if))))
+ return 0;
+ }
+ if(num_ifs == 0) {
+ if(do_ip4 && !shared_ports_setup_if(&shp->ip4_ifs[0],
+ "0.0.0.0", availports, numavailports))
+ return 0;
+ if(do_ip6 && !shared_ports_setup_if(&shp->ip6_ifs[0],
+ "::", availports, numavailports))
+ return 0;
+ } else {
+ size_t done_4 = 0, done_6 = 0;
+ int i;
+ for(i=0; i<num_ifs; i++) {
+ if(str_is_ip6(ifs[i]) && do_ip6) {
+ if(!shared_ports_setup_if(&shp->ip6_ifs[done_6],
+ ifs[i], availports, numavailports))
+ return 0;
+ done_6++;
+ }
+ if(!str_is_ip6(ifs[i]) && do_ip4) {
+ if(!shared_ports_setup_if(&shp->ip4_ifs[done_4],
+ ifs[i], availports, numavailports))
+ return 0;
+ done_4++;
+ }
+ }
+ }
+ return 1;
+}
+#endif
+
+struct shared_ports* shared_ports_create(char** ifs, int num_ifs, int do_ip4,
+ int do_ip6, int* availports, int numavailports)
+{
+ struct shared_ports* shp = calloc(1, sizeof(*shp));
+ if(!shp) {
+ log_err("malloc failed");
+ return NULL;
+ }
+ lock_basic_init(&shp->lock);
+ lock_protect(&shp->lock, shp, sizeof(*shp));
+
+#ifndef DISABLE_EXPLICIT_PORT_RANDOMISATION
+ /* Allocate interfaces */
+ if(!shared_ports_alloc_ifs(shp, ifs, num_ifs, do_ip4, do_ip6,
+ availports, numavailports)) {
+ log_err("malloc failed");
+ shared_ports_delete(shp);
+ return NULL;
+ }
+#else
+ (void)ifs; (void)num_ifs; (void)do_ip4; (void)do_ip6;
+ (void)availports; (void)numavailports;
+#endif
+ return shp;
+}
+
+#ifndef DISABLE_EXPLICIT_PORT_RANDOMISATION
+/** Delete shared ports interface structure */
+static void shared_ports_if_delete(struct shared_ports_if* shpif)
+{
+ if(!shpif)
+ return;
+ free(shpif->avail_ports);
+}
+#endif
+
+void shared_ports_delete(struct shared_ports* shp)
+{
+#ifndef DISABLE_EXPLICIT_PORT_RANDOMISATION
+ int i;
+#endif
+ if(!shp)
+ return;
+ lock_basic_destroy(&shp->lock);
+#ifndef DISABLE_EXPLICIT_PORT_RANDOMISATION
+ for(i=0; i<shp->num_ip4; i++) {
+ shared_ports_if_delete(&shp->ip4_ifs[i]);
+ }
+ free(shp->ip4_ifs);
+ for(i=0; i<shp->num_ip6; i++) {
+ shared_ports_if_delete(&shp->ip6_ifs[i]);
+ }
+ free(shp->ip6_ifs);
+#endif
+ free(shp);
+}
+
+struct shared_ports_if* shared_ports_find_if(struct shared_ports* shp,
+ struct sockaddr_storage* addr, socklen_t addrlen, int pfxlen)
+{
+#ifndef DISABLE_EXPLICIT_PORT_RANDOMISATION
+ struct shared_ports_if* ret, *ifs = NULL;
+ int i, num_ifs = 0;
+ lock_basic_lock(&shp->lock);
+ if(addr_is_ip6(addr, addrlen)) {
+ ifs = shp->ip6_ifs;
+ num_ifs = shp->num_ip6;
+ } else {
+ ifs = shp->ip4_ifs;
+ num_ifs = shp->num_ip4;
+ }
+ for(i=0; i<num_ifs; i++) {
+ if(sockaddr_cmp(addr, addrlen, &ifs[i].addr,
+ ifs[i].addrlen) == 0
+ && pfxlen == ifs[i].pfxlen) {
+ ret = &ifs[i];
+ lock_basic_unlock(&shp->lock);
+ return ret;
+ }
+ }
+ lock_basic_unlock(&shp->lock);
+ return NULL;
+#else
+ (void)shp; (void)addr; (void)addrlen; (void)pfxlen;
+ return NULL;
+#endif
+}
+
+int shared_ports_fetch_random(struct shared_ports* shp,
+ struct shared_ports_if* shpif, struct ub_randstate* rnd,
+ int udp_connect, int reusenum, int* port, int* reused)
+{
+#ifndef DISABLE_EXPLICIT_PORT_RANDOMISATION
+ int portno = 0, my_port = 0;
+ if(!shpif)
+ return 0;
+ lock_basic_lock(&shp->lock);
+ if(udp_connect) {
+ /* if we connect() we cannot reuse fds for a port. */
+ if(shpif->inuse >= shpif->avail_total) {
+ lock_basic_unlock(&shp->lock);
+ return 0;
+ }
+ my_port = ub_random_max(rnd,
+ shpif->avail_total - shpif->inuse);
+ } else {
+ /* select from free ports and open ports on this thread. */
+ if(shpif->inuse >= shpif->avail_total) {
+ lock_basic_unlock(&shp->lock);
+ if(reusenum == 0) {
+ return 0;
+ }
+ my_port = ub_random_max(rnd, reusenum);
+ *port = my_port;
+ *reused = 1;
+ return 1;
+ }
+ my_port = ub_random_max(rnd, shpif->avail_total - shpif->inuse
+ + reusenum);
+ if(my_port < reusenum) {
+ /* port already open */
+ lock_basic_unlock(&shp->lock);
+ *port = my_port;
+ *reused = 1;
+ return 1;
+ }
+ my_port -= reusenum;
+ }
+ log_assert(shpif->inuse < shpif->avail_total);
+ log_assert(my_port >= 0 && my_port < shpif->avail_total);
+ portno = shpif->avail_ports[my_port];
+ shpif->avail_ports[my_port] =
+ shpif->avail_ports[shpif->avail_total-shpif->inuse-1];
+ shpif->inuse++;
+ lock_basic_unlock(&shp->lock);
+ *port = portno;
+ *reused = 0;
+ return 1;
+#else
+ (void)shp; (void)shpif; (void)rnd; (void)udp_connect;
+ (void)reusenum;
+ *port = 0;
+ *reused = 0;
+ return 1;
+#endif
+}
+
+void shared_ports_return_port(struct shared_ports* shp,
+ struct shared_ports_if* shpif, int port)
+{
+#ifndef DISABLE_EXPLICIT_PORT_RANDOMISATION
+ if(!shpif)
+ return;
+ lock_basic_lock(&shp->lock);
+ log_assert(shpif->inuse > 0);
+ shpif->avail_ports[shpif->avail_total - shpif->inuse] = port;
+ shpif->inuse--;
+ lock_basic_unlock(&shp->lock);
+#else
+ (void)shp; (void)shpif; (void)port;
+#endif
+}
diff --git a/services/outside_network.h b/services/outside_network.h
index e30ce92eb3fc..8841c607d8cc 100644
--- a/services/outside_network.h
+++ b/services/outside_network.h
@@ -70,6 +70,8 @@ struct module_env;
struct module_qstate;
struct query_info;
struct config_file;
+struct shared_ports;
+struct shared_ports_if;
/**
* Send queries to outside servers and wait for answers from servers.
@@ -119,6 +121,9 @@ struct outside_network {
int udp_connect;
/** number of udp packets sent. */
size_t num_udp_outgoing;
+ /** the shared ports structure, with random ports numbers.
+ * This is a reference to the member in the daemon structure. */
+ struct shared_ports* shared_ports;
/** array of outgoing IP4 interfaces */
struct port_if* ip4_ifs;
@@ -211,11 +216,8 @@ struct port_if {
int pfxlen;
#ifndef DISABLE_EXPLICIT_PORT_RANDOMISATION
- /** the available ports array. These are unused.
- * Only the first total-inuse part is filled. */
- int* avail_ports;
- /** the total number of available ports (size of the array) */
- int avail_total;
+ /** the shared port numbers for this interface. */
+ struct shared_ports_if* shpif;
#endif
/** array of the commpoints currently in use.
@@ -246,6 +248,42 @@ struct port_comm {
};
/**
+ * Shared ports, the list of ports shared across threads
+ */
+struct shared_ports {
+ /** mutex on the ports */
+ lock_basic_type lock;
+ /** array of IP4 interfaces */
+ struct shared_ports_if* ip4_ifs;
+ /** number of outgoing IP4 interfaces */
+ int num_ip4;
+ /** array of IP6 interfaces */
+ struct shared_ports_if* ip6_ifs;
+ /** number of outgoing IP6 interfaces */
+ int num_ip6;
+};
+
+/**
+ * Shared ports for an interface.
+ */
+struct shared_ports_if {
+ /** address ready to allocate new socket (except port no). */
+ struct sockaddr_storage addr;
+ /** length of addr field */
+ socklen_t addrlen;
+ /** if a netblock, the prefix */
+ int pfxlen;
+
+ /** the available ports array. These are unused.
+ * Only the first total-inuse part is filled. */
+ int* avail_ports;
+ /** the total number of available ports (size of the array) */
+ int avail_total;
+ /** the number in use. */
+ int inuse;
+};
+
+/**
* Reuse TCP connection, still open can be used again.
*/
struct reuse_tcp {
@@ -419,7 +457,7 @@ struct waiting_tcp {
void* cb_arg;
/** if it uses ssl upstream */
int ssl_upstream;
- /** ref to the tls_auth_name from the serviced_query */
+ /** owned copy of the tls_auth_name (malloced) */
char* tls_auth_name;
/** the packet was involved in an error, to stop looping errors */
int error_count;
@@ -551,8 +589,6 @@ struct serviced_query {
* @param infra: pointer to infra cached used for serviced queries.
* @param rnd: stored to create random numbers for serviced queries.
* @param use_caps_for_id: enable to use 0x20 bits to encode id randomness.
- * @param availports: array of available ports.
- * @param numavailports: number of available ports in array.
* @param unwanted_threshold: when to take defensive action.
* @param unwanted_action: the action to take.
* @param unwanted_param: user parameter to action.
@@ -567,17 +603,18 @@ struct serviced_query {
* @param max_reuse_tcp_queries: max number of queries on a reuse connection.
* @param tcp_reuse_timeout: timeout for REUSE entries in milliseconds.
* @param tcp_auth_query_timeout: timeout in milliseconds for TCP queries to auth servers.
+ * @param shared_ports: the shared_ports structure.
* @return: the new structure (with no pending answers) or NULL on error.
*/
struct outside_network* outside_network_create(struct comm_base* base,
size_t bufsize, size_t num_ports, char** ifs, int num_ifs,
int do_ip4, int do_ip6, size_t num_tcp, int dscp, struct infra_cache* infra,
- struct ub_randstate* rnd, int use_caps_for_id, int* availports,
- int numavailports, size_t unwanted_threshold, int tcp_mss,
+ struct ub_randstate* rnd, int use_caps_for_id,
+ size_t unwanted_threshold, int tcp_mss,
void (*unwanted_action)(void*), void* unwanted_param, int do_udp,
void* sslctx, int delayclose, int tls_use_sni, struct dt_env *dtenv,
int udp_connect, int max_reuse_tcp_queries, int tcp_reuse_timeout,
- int tcp_auth_query_timeout);
+ int tcp_auth_query_timeout, struct shared_ports* shared_ports);
/**
* Delete outside_network structure.
@@ -819,6 +856,54 @@ struct comm_point* outnet_comm_point_for_http(struct outside_network* outnet,
/** connect tcp connection to addr, 0 on failure */
int outnet_tcp_connect(int s, struct sockaddr_storage* addr, socklen_t addrlen);
+/**
+ * Create new shared ports structure.
+ * @param ifs: interface names (or NULL for default interface).
+ * These interfaces must be able to access all authoritative servers.
+ * @param num_ifs: number of names in array ifs.
+ * @param do_ip4: service IP4.
+ * @param do_ip6: service IP6.
+ * @param availports: array of available ports.
+ * @param numavailports: number of available ports in array.
+ * @return new, or NULL on failure.
+ */
+struct shared_ports* shared_ports_create(char** ifs, int num_ifs, int do_ip4,
+ int do_ip6, int* availports, int numavailports);
+
+/**
+ * Delete shared ports structure.
+ * @param shp: shared ports structure.
+ */
+void shared_ports_delete(struct shared_ports* shp);
+
+/** Find interface in shared ports. */
+struct shared_ports_if* shared_ports_find_if(struct shared_ports* shp,
+ struct sockaddr_storage* addr, socklen_t addrlen, int pfxlen);
+
+/**
+ * Get a shared port from the list of random ports.
+ * @param shp: shared ports structure.
+ * @param shpif: the shared ports interface.
+ * @param rnd: used to make random numbers.
+ * @param udp_connect: set to true if no reuse is possible.
+ * @param reusenum: number of ports that can be reused (already open).
+ * @param port: the port number is returned.
+ * @param reused: if the port numer is reused, returned.
+ * @return false on failure. That can mean no more free ports to use.
+ */
+int shared_ports_fetch_random(struct shared_ports* shp,
+ struct shared_ports_if* shpif, struct ub_randstate* rnd,
+ int udp_connect, int reusenum, int* port, int* reused);
+
+/**
+ * Return a shared port to the list of random ports.
+ * @param shp: shared ports structure.
+ * @param shpif: the shared ports interface.
+ * @param port: port number to return to be used again.
+ */
+void shared_ports_return_port(struct shared_ports* shp,
+ struct shared_ports_if* shpif, int port);
+
/** callback for incoming udp answers from the network */
int outnet_udp_cb(struct comm_point* c, void* arg, int error,
struct comm_reply *reply_info);
diff --git a/smallapp/worker_cb.c b/smallapp/worker_cb.c
index 92ebe386d269..876c7db4e439 100644
--- a/smallapp/worker_cb.c
+++ b/smallapp/worker_cb.c
@@ -128,6 +128,12 @@ worker_alloc_cleanup(void* ATTR_UNUSED(arg))
log_assert(0);
}
+void
+libworker_alloc_cleanup(void* ATTR_UNUSED(arg))
+{
+ log_assert(0);
+}
+
struct outbound_entry* libworker_send_query(
struct query_info* ATTR_UNUSED(qinfo), uint16_t ATTR_UNUSED(flags),
int ATTR_UNUSED(dnssec), int ATTR_UNUSED(want_dnssec),
diff --git a/testcode/doqclient.c b/testcode/doqclient.c
index 8a34ca31b122..4c2142211cc5 100644
--- a/testcode/doqclient.c
+++ b/testcode/doqclient.c
@@ -1519,9 +1519,9 @@ doq_client_send_pkt(struct doq_client_data* data, uint32_t ecn, uint8_t* buf,
}
log_err("doq sendmsg: %s", strerror(errno));
#ifdef HAVE_NGTCP2_CCERR_DEFAULT
- ngtcp2_ccerr_set_application_error(&data->ccerr, -1, NULL, 0);
+ ngtcp2_ccerr_set_application_error(&data->ccerr, 1, NULL, 0);
#else
- ngtcp2_connection_close_error_set_application_error(&data->last_error, -1, NULL, 0);
+ ngtcp2_connection_close_error_set_application_error(&data->last_error, 1, NULL, 0);
#endif
return 0;
}
@@ -2671,6 +2671,11 @@ void libworker_event_done_cb(void* ATTR_UNUSED(arg), int ATTR_UNUSED(rcode),
log_assert(0);
}
+void libworker_alloc_cleanup(void* ATTR_UNUSED(arg))
+{
+ log_assert(0);
+}
+
int context_query_cmp(const void* ATTR_UNUSED(a), const void* ATTR_UNUSED(b))
{
log_assert(0);
diff --git a/testcode/fake_event.c b/testcode/fake_event.c
index ce439edd1294..4ca357770747 100644
--- a/testcode/fake_event.c
+++ b/testcode/fake_event.c
@@ -1126,15 +1126,16 @@ outside_network_create(struct comm_base* base, size_t bufsize,
int ATTR_UNUSED(dscp),
struct infra_cache* infra,
struct ub_randstate* ATTR_UNUSED(rnd),
- int ATTR_UNUSED(use_caps_for_id), int* ATTR_UNUSED(availports),
- int ATTR_UNUSED(numavailports), size_t ATTR_UNUSED(unwanted_threshold),
+ int ATTR_UNUSED(use_caps_for_id),
+ size_t ATTR_UNUSED(unwanted_threshold),
int ATTR_UNUSED(outgoing_tcp_mss),
void (*unwanted_action)(void*), void* ATTR_UNUSED(unwanted_param),
int ATTR_UNUSED(do_udp), void* ATTR_UNUSED(sslctx),
int ATTR_UNUSED(delayclose), int ATTR_UNUSED(tls_use_sni),
struct dt_env* ATTR_UNUSED(dtenv), int ATTR_UNUSED(udp_connect),
int ATTR_UNUSED(max_reuse_tcp_queries), int ATTR_UNUSED(tcp_reuse_timeout),
- int ATTR_UNUSED(tcp_auth_query_timeout))
+ int ATTR_UNUSED(tcp_auth_query_timeout),
+ struct shared_ports* ATTR_UNUSED(shared_ports))
{
struct replay_runtime* runtime = (struct replay_runtime*)base;
struct outside_network* outnet = calloc(1,
@@ -1980,6 +1981,20 @@ int outnet_tcp_connect(int ATTR_UNUSED(s), struct sockaddr_storage* ATTR_UNUSED(
return 0;
}
+struct shared_ports* shared_ports_create(char** ATTR_UNUSED(ifs),
+ int ATTR_UNUSED(num_ifs), int ATTR_UNUSED(do_ip4),
+ int ATTR_UNUSED(do_ip6), int* ATTR_UNUSED(availports),
+ int ATTR_UNUSED(numavailports))
+{
+ return calloc(1, sizeof(struct shared_ports));
+}
+
+void shared_ports_delete(struct shared_ports* shp)
+{
+ if(!shp) return;
+ free(shp);
+}
+
int tcp_req_info_add_meshstate(struct tcp_req_info* ATTR_UNUSED(req),
struct mesh_area* ATTR_UNUSED(mesh), struct mesh_state* ATTR_UNUSED(m))
{
@@ -2021,6 +2036,15 @@ void http2_stream_remove_mesh_state(struct http2_stream* ATTR_UNUSED(h2_stream))
{
}
+void doq_stream_add_meshstate(struct doq_stream* ATTR_UNUSED(stream),
+ struct mesh_area* ATTR_UNUSED(mesh), struct mesh_state* ATTR_UNUSED(m))
+{
+}
+
+void doq_stream_remove_mesh_state(struct doq_stream* ATTR_UNUSED(stream))
+{
+}
+
void fast_reload_service_cb(int ATTR_UNUSED(fd), short ATTR_UNUSED(event),
void* ATTR_UNUSED(arg))
{
diff --git a/testcode/unitmain.c b/testcode/unitmain.c
index 4bc756a0705f..62f37375f756 100644
--- a/testcode/unitmain.c
+++ b/testcode/unitmain.c
@@ -1282,6 +1282,61 @@ static void localzone_test(void)
localzone_parents_test();
}
+#include "services/mesh.h"
+/** mesh unit tests */
+static void mesh_test(void)
+{
+ struct regional* r2, *r3;
+ struct respip_client_info* c1, *c2, *c3;
+ unit_show_func("services/mesh.c", "mesh_copy_client_info");
+ r2 = regional_create();
+ r3 = regional_create();
+ if(!r2 || !r3) fatal_exit("out of memory");
+
+ c1 = calloc(1, sizeof(*c1));
+ if(!c1) fatal_exit("out of memory");
+ c1->view = calloc(1, sizeof(*c1->view));
+ if(!c1->view) fatal_exit("out of memory");
+ c1->view->name = strdup("view1");
+ if(!c1->view->name) fatal_exit("out of memory");
+
+ c2 = mesh_copy_client_info(r2, c1);
+ if(!c2) fatal_exit("out of memory");
+ c3 = mesh_copy_client_info(r3, c2);
+ if(!c3) fatal_exit("out of memory");
+
+ unit_assert(strcmp(c1->view->name, c2->view_name) == 0);
+ unit_assert(strcmp(c1->view->name, c3->view_name) == 0);
+
+ /* make sure that the c3 view_name is in the r3 region. */
+ unit_assert(r3->next == NULL); /* only the first chunk present atm */
+ if(strlen(c3->view_name) >= r3->large_object_size) {
+ char* a = r3->large_list;
+ int found = 0;
+ while(a) {
+ if(strcmp(c3->view_name,
+ a + /* ALIGNEMENT */ sizeof(uint64_t)) == 0) {
+ found = 1;
+ break;
+ }
+ a = *(char**)a;
+ }
+ unit_assert(found == 1);
+ } else {
+ /* The allocation is expected in the r3 region first chunk */
+ unit_assert((uint8_t*)c3->view_name < ((uint8_t*)r3)+r3->first_size);
+ }
+
+ regional_destroy(r2);
+ /* ASAN should complain for the freed access below */
+ unit_assert(strcmp(c1->view->name, c3->view_name) == 0);
+
+ regional_destroy(r3);
+ free(c1->view->name);
+ free(c1->view);
+ free(c1);
+}
+
void unit_show_func(const char* file, const char* func)
{
printf("test %s:%s\n", file, func);
@@ -1356,6 +1411,7 @@ main(int argc, char* argv[])
msgparse_test();
edns_ede_answer_encode_test();
localzone_test();
+ mesh_test();
#ifdef CLIENT_SUBNET
ecs_test();
#endif /* CLIENT_SUBNET */
diff --git a/testcode/unittcpreuse.c b/testcode/unittcpreuse.c
index 5f45a4b456f1..ce62e33257c6 100644
--- a/testcode/unittcpreuse.c
+++ b/testcode/unittcpreuse.c
@@ -41,6 +41,7 @@
#include "config.h"
#include "testcode/unitmain.h"
#include "util/log.h"
+#include "util/net_help.h"
#include "util/random.h"
#include "services/outside_network.h"
@@ -479,6 +480,278 @@ static void reuse_write_wait_test(void)
check_reuse_write_wait_removal(1, &reuse, store, 0, 1);
}
+static void shared_port_test_ifs(void)
+{
+ struct shared_ports* shp;
+ struct shared_ports_if* shpif;
+ char* ifs[] = {"1.2.3.4", "1.2.3.5", "::1:2", "::1:3"};
+ int availports[] = {1, 2, 3, 4};
+ struct sockaddr_storage addr;
+ socklen_t addrlen;
+
+ shp = shared_ports_create(ifs, 4, 1, 1, availports, 4);
+ unit_assert(shp);
+
+ if(!ipstrtoaddr("1.2.3.4", UNBOUND_DNS_PORT, &addr, &addrlen))
+ log_err("could not parse");
+ shpif = shared_ports_find_if(shp, &addr, addrlen, 0);
+ unit_assert(shpif);
+
+ if(!ipstrtoaddr("1.2.3.5", UNBOUND_DNS_PORT, &addr, &addrlen))
+ log_err("could not parse");
+ shpif = shared_ports_find_if(shp, &addr, addrlen, 0);
+ unit_assert(shpif);
+
+ if(!ipstrtoaddr("::1:2", UNBOUND_DNS_PORT, &addr, &addrlen))
+ log_err("could not parse");
+ shpif = shared_ports_find_if(shp, &addr, addrlen, 0);
+ unit_assert(shpif);
+
+ if(!ipstrtoaddr("::1:3", UNBOUND_DNS_PORT, &addr, &addrlen))
+ log_err("could not parse");
+ shpif = shared_ports_find_if(shp, &addr, addrlen, 0);
+ unit_assert(shpif);
+
+ shared_ports_delete(shp);
+}
+
+/** See if a port is on the shared_ports ports list */
+static int
+pif_list_contains(struct shared_ports_if* shpif, int item)
+{
+ int i;
+ unit_assert(shpif->inuse >= 0 && shpif->inuse <= shpif->avail_total);
+ for(i=0; i< shpif->avail_total - shpif->inuse; i++) {
+ if(shpif->avail_ports[i] == item)
+ return 1;
+ }
+ return 0;
+}
+
+/** See if a number of ports are on the shared_ports list */
+static int
+pif_list_contains_items(struct shared_ports_if* shpif, int item1,
+ int item2, int item3, int item4)
+{
+ if(item1 != -1 && !pif_list_contains(shpif, item1))
+ return 0;
+ if(item2 != -1 && !pif_list_contains(shpif, item2))
+ return 0;
+ if(item3 != -1 && !pif_list_contains(shpif, item3))
+ return 0;
+ if(item4 != -1 && !pif_list_contains(shpif, item4))
+ return 0;
+ return 1;
+}
+
+static void shared_port_test_port(void)
+{
+ struct shared_ports* shp;
+ struct shared_ports_if* shpif;
+ char* ifs[] = {"1.2.3.4", "1.2.3.5"};
+ int availports[] = {1, 2, 3, 4};
+ struct sockaddr_storage addr;
+ socklen_t addrlen;
+ int p1, p2, p3, reused;
+ struct ub_randstate* rnd;
+
+ rnd = ub_initstate(NULL);
+ unit_assert(rnd);
+
+ shp = shared_ports_create(ifs, 2, 1, 1, availports, 4);
+ unit_assert(shp);
+
+ if(!ipstrtoaddr("1.2.3.4", UNBOUND_DNS_PORT, &addr, &addrlen))
+ log_err("could not parse");
+ shpif = shared_ports_find_if(shp, &addr, addrlen, 0);
+ unit_assert(shpif);
+
+ unit_assert(shpif->avail_total == 4);
+ unit_assert(shpif->inuse == 0);
+ unit_assert(pif_list_contains_items(shpif, 1, 2, 3, 4));
+
+ if(!shared_ports_fetch_random(shp, shpif, rnd,
+ 0, 0, &p1, &reused)) {
+ unit_assert(0); /* should succeed */
+ }
+ unit_assert(reused == 0);
+ unit_assert(p1 != 0);
+ unit_assert(!pif_list_contains(shpif, p1));
+ if(p1 != 1) unit_assert(pif_list_contains(shpif, 1));
+ if(p1 != 2) unit_assert(pif_list_contains(shpif, 2));
+ if(p1 != 3) unit_assert(pif_list_contains(shpif, 3));
+ if(p1 != 4) unit_assert(pif_list_contains(shpif, 4));
+ unit_assert(shpif->avail_total == 4);
+ unit_assert(shpif->inuse == 1);
+
+ shared_ports_return_port(shp, shpif, p1);
+ unit_assert(shpif->avail_total == 4);
+ unit_assert(shpif->inuse == 0);
+ unit_assert(pif_list_contains_items(shpif, 1, 2, 3, 4));
+
+ /* pick up two items */
+ if(!shared_ports_fetch_random(shp, shpif, rnd,
+ 0, 0, &p1, &reused)) {
+ unit_assert(0); /* should succeed */
+ }
+ unit_assert(reused == 0);
+ unit_assert(p1 != 0);
+ if(!shared_ports_fetch_random(shp, shpif, rnd,
+ 0, 0, &p2, &reused)) {
+ unit_assert(0); /* should succeed */
+ }
+ unit_assert(reused == 0);
+ unit_assert(p2 != 0);
+ unit_assert(!pif_list_contains(shpif, p1));
+ unit_assert(!pif_list_contains(shpif, p2));
+ if(p1 != 1 && p2 != 1) unit_assert(pif_list_contains(shpif, 1));
+ if(p1 != 2 && p2 != 2) unit_assert(pif_list_contains(shpif, 2));
+ if(p1 != 3 && p2 != 3) unit_assert(pif_list_contains(shpif, 3));
+ if(p1 != 4 && p2 != 4) unit_assert(pif_list_contains(shpif, 4));
+ unit_assert(shpif->avail_total == 4);
+ unit_assert(shpif->inuse == 2);
+
+ shared_ports_return_port(shp, shpif, p1);
+ unit_assert(pif_list_contains(shpif, p1));
+ unit_assert(shpif->avail_total == 4);
+ unit_assert(shpif->inuse == 1);
+
+ shared_ports_return_port(shp, shpif, p2);
+ unit_assert(pif_list_contains(shpif, p2));
+ unit_assert(shpif->avail_total == 4);
+ unit_assert(shpif->inuse == 0);
+ unit_assert(pif_list_contains_items(shpif, 1, 2, 3, 4));
+
+ /* pick up three items */
+ if(!shared_ports_fetch_random(shp, shpif, rnd,
+ 0, 0, &p1, &reused)) {
+ unit_assert(0); /* should succeed */
+ }
+ unit_assert(reused == 0);
+ unit_assert(p1 != 0);
+ if(!shared_ports_fetch_random(shp, shpif, rnd,
+ 0, 0, &p2, &reused)) {
+ unit_assert(0); /* should succeed */
+ }
+ unit_assert(reused == 0);
+ unit_assert(p2 != 0);
+ if(!shared_ports_fetch_random(shp, shpif, rnd,
+ 0, 0, &p3, &reused)) {
+ unit_assert(0); /* should succeed */
+ }
+ unit_assert(reused == 0);
+ unit_assert(p3 != 0);
+ unit_assert(!pif_list_contains(shpif, p1));
+ unit_assert(!pif_list_contains(shpif, p2));
+ unit_assert(!pif_list_contains(shpif, p3));
+ if(p1 != 1 && p2 != 1 && p3 != 1)
+ unit_assert(pif_list_contains(shpif, 1));
+ if(p1 != 2 && p2 != 2 && p3 != 2)
+ unit_assert(pif_list_contains(shpif, 2));
+ if(p1 != 3 && p2 != 3 && p3 != 3)
+ unit_assert(pif_list_contains(shpif, 3));
+ if(p1 != 4 && p2 != 4 && p3 != 4)
+ unit_assert(pif_list_contains(shpif, 4));
+ unit_assert(shpif->avail_total == 4);
+ unit_assert(shpif->inuse == 3);
+
+ shared_ports_return_port(shp, shpif, p1);
+ unit_assert(pif_list_contains(shpif, p1));
+ unit_assert(shpif->avail_total == 4);
+ unit_assert(shpif->inuse == 2);
+
+ shared_ports_return_port(shp, shpif, p2);
+ unit_assert(pif_list_contains(shpif, p2));
+ unit_assert(shpif->avail_total == 4);
+ unit_assert(shpif->inuse == 1);
+
+ shared_ports_return_port(shp, shpif, p3);
+ unit_assert(pif_list_contains(shpif, p3));
+ unit_assert(shpif->avail_total == 4);
+ unit_assert(shpif->inuse == 0);
+ unit_assert(pif_list_contains_items(shpif, 1, 2, 3, 4));
+
+ /* pick up all four items */
+ if(!shared_ports_fetch_random(shp, shpif, rnd,
+ 0, 0, &p1, &reused)) {
+ unit_assert(0); /* should succeed */
+ }
+ unit_assert(reused == 0);
+ unit_assert(p1 != 0);
+
+ if(!shared_ports_fetch_random(shp, shpif, rnd,
+ 0, 0, &p1, &reused)) {
+ unit_assert(0); /* should succeed */
+ }
+ unit_assert(reused == 0);
+ unit_assert(p1 != 0);
+
+ if(!shared_ports_fetch_random(shp, shpif, rnd,
+ 0, 0, &p1, &reused)) {
+ unit_assert(0); /* should succeed */
+ }
+ unit_assert(reused == 0);
+ unit_assert(p1 != 0);
+
+ if(!shared_ports_fetch_random(shp, shpif, rnd,
+ 0, 0, &p1, &reused)) {
+ unit_assert(0); /* should succeed */
+ }
+ unit_assert(reused == 0);
+ unit_assert(p1 != 0);
+ unit_assert(!pif_list_contains(shpif, 1));
+ unit_assert(!pif_list_contains(shpif, 2));
+ unit_assert(!pif_list_contains(shpif, 3));
+ unit_assert(!pif_list_contains(shpif, 4));
+ unit_assert(shpif->avail_total == 4);
+ unit_assert(shpif->inuse == 4);
+
+ /* more fetches fail, it is fully inuse. */
+ unit_assert(!shared_ports_fetch_random(shp, shpif, rnd, 0, 0, &p2,
+ &reused));
+ unit_assert(!shared_ports_fetch_random(shp, shpif, rnd, 0, 0, &p3,
+ &reused));
+ unit_assert(shpif->avail_total == 4);
+ unit_assert(shpif->inuse == 4);
+
+ /* reuse is then always the case */
+ if(!shared_ports_fetch_random(shp, shpif, rnd,
+ 0 /* can reuse */, 4 /* reusenum */, &p1, &reused)) {
+ unit_assert(0); /* should succeed */
+ }
+ unit_assert(reused == 1);
+ unit_assert(p1 >= 0 && p1 < 4 /* reusenum */);
+
+ if(!shared_ports_fetch_random(shp, shpif, rnd,
+ 0 /* can reuse */, 4 /* reusenum */, &p1, &reused)) {
+ unit_assert(0); /* should succeed */
+ }
+ unit_assert(reused == 1);
+ unit_assert(p1 >= 0 && p1 < 4 /* reusenum */);
+
+ /* return all the ports */
+ shared_ports_return_port(shp, shpif, 1);
+ unit_assert(pif_list_contains(shpif, 1));
+ unit_assert(shpif->avail_total == 4);
+ unit_assert(shpif->inuse == 3);
+ shared_ports_return_port(shp, shpif, 2);
+ unit_assert(pif_list_contains(shpif, 2));
+ unit_assert(shpif->avail_total == 4);
+ unit_assert(shpif->inuse == 2);
+ shared_ports_return_port(shp, shpif, 3);
+ unit_assert(pif_list_contains(shpif, 3));
+ unit_assert(shpif->avail_total == 4);
+ unit_assert(shpif->inuse == 1);
+ shared_ports_return_port(shp, shpif, 4);
+ unit_assert(pif_list_contains(shpif, 4));
+ unit_assert(shpif->avail_total == 4);
+ unit_assert(shpif->inuse == 0);
+ unit_assert(pif_list_contains_items(shpif, 1, 2, 3, 4));
+
+ shared_ports_delete(shp);
+ ub_randfree(rnd);
+}
+
void tcpreuse_test(void)
{
unit_show_feature("tcp_reuse");
@@ -486,4 +759,7 @@ void tcpreuse_test(void)
tcp_reuse_tree_list_test();
waiting_tcp_list_test();
reuse_write_wait_test();
+ unit_show_feature("shared_ports");
+ shared_port_test_ifs();
+ shared_port_test_port();
}
diff --git a/testdata/dns_error_reporting.rpl b/testdata/dns_error_reporting.rpl
index f1fac12a2284..22175caded16 100644
--- a/testdata/dns_error_reporting.rpl
+++ b/testdata/dns_error_reporting.rpl
@@ -12,6 +12,7 @@ server:
ede: no # It is not needed for dns-error-reporting; only for clients to receive EDEs
dns-error-reporting: yes
do-ip6: no
+ do-not-query-localhost: no
stub-zone:
name: domain
diff --git a/testdata/dnscrypt_cert.tdir/dnscrypt_cert.test b/testdata/dnscrypt_cert.tdir/dnscrypt_cert.test
index fdb88e8f9490..4631d02e13ab 100644
--- a/testdata/dnscrypt_cert.tdir/dnscrypt_cert.test
+++ b/testdata/dnscrypt_cert.tdir/dnscrypt_cert.test
@@ -9,20 +9,21 @@ PRE="../.."
# do the test
-# Query plain request over DNSCrypt channel get closed
-# We use TCP to avoid hanging on waiting for UDP.
-# We expect `outfile` to contain no DNS payload
-echo "> dig TCP www.example.com. DNSCrypt port"
-dig +tcp @127.0.0.1 -p $DNSCRYPT_PORT www.example.com. A | tee outfile
+# Query plain request over DNSCrypt.
+# This used to close the channel; now it returns SERVFAIL.
+# Old: We use TCP to avoid hanging on waiting for UDP.
+# We expect `outfile` to contain no DNS payload
+echo "> dig www.example.com. DNSCrypt port"
+dig @127.0.0.1 -p $DNSCRYPT_PORT www.example.com. A | tee outfile
echo "> cat logfiles"
cat fwd.log
cat unbound.log
echo "> check answer"
-if grep "QUESTION SECTION" outfile; then
+if grep "SERVFAIL" outfile; then
+ echo "OK"
+else
echo "NOK"
exit 1
-else
- echo "OK"
fi
diff --git a/testdata/dnscrypt_cert_chacha.tdir/dnscrypt_cert_chacha.test b/testdata/dnscrypt_cert_chacha.tdir/dnscrypt_cert_chacha.test
index 2db073ad6a40..a92f196f5787 100644
--- a/testdata/dnscrypt_cert_chacha.tdir/dnscrypt_cert_chacha.test
+++ b/testdata/dnscrypt_cert_chacha.tdir/dnscrypt_cert_chacha.test
@@ -9,20 +9,21 @@ PRE="../.."
# do the test
-# Query plain request over DNSCrypt channel get closed
-# We use TCP to avoid hanging on waiting for UDP.
-# We expect `outfile` to contain no DNS payload
-echo "> dig TCP www.example.com. DNSCrypt port"
-dig +tcp @127.0.0.1 -p $DNSCRYPT_PORT www.example.com. A | tee outfile
+# Query plain request over DNSCrypt.
+# This used to close the channel; now it returns SERVFAIL.
+# Old: We use TCP to avoid hanging on waiting for UDP.
+# We expect `outfile` to contain no DNS payload
+echo "> dig www.example.com. DNSCrypt port"
+dig @127.0.0.1 -p $DNSCRYPT_PORT www.example.com. A | tee outfile
echo "> cat logfiles"
cat fwd.log
cat unbound.log
echo "> check answer"
-if grep "QUESTION SECTION" outfile; then
+if grep "SERVFAIL" outfile; then
+ echo "OK"
+else
echo "NOK"
exit 1
-else
- echo "OK"
fi
diff --git a/testdata/iter_prefetch_fail.rpl b/testdata/iter_prefetch_fail.rpl
index d1e308305b8b..aa94d0fe5826 100644
--- a/testdata/iter_prefetch_fail.rpl
+++ b/testdata/iter_prefetch_fail.rpl
@@ -319,7 +319,7 @@ example.com. 360 IN NS ns.example.com.
SECTION ADDITIONAL
; this is picked up from the parent (because this simulation has the
; parent respond with servfail, not actually timeout)
-ns.example.com. 3600 IN A 1.2.3.4
+ns.example.com. 360 IN A 1.2.3.4
ENTRY_END
; another query to see if there is another lookup towards the authority
@@ -342,7 +342,7 @@ www.example.com. 360 IN A 10.20.30.40
SECTION AUTHORITY
example.com. 360 IN NS ns.example.com.
SECTION ADDITIONAL
-ns.example.com. 3600 IN A 1.2.3.4
+ns.example.com. 360 IN A 1.2.3.4
ENTRY_END
; some time later another query, and now it is fine to bother the authority
@@ -367,7 +367,7 @@ www.example.com. 330 IN A 10.20.30.40
SECTION AUTHORITY
example.com. 330 IN NS ns.example.com.
SECTION ADDITIONAL
-ns.example.com. 3570 IN A 1.2.3.4
+ns.example.com. 330 IN A 1.2.3.4
ENTRY_END
; now the just-looked-up entry
STEP 190 QUERY
@@ -388,7 +388,7 @@ www.example.com. 3600 IN A 10.20.30.40
SECTION AUTHORITY
example.com. 3600 IN NS ns.example.com.
SECTION ADDITIONAL
-ns.example.com. 3570 IN A 1.2.3.4
+ns.example.com. 3600 IN A 1.2.3.4
ENTRY_END
diff --git a/util/data/msgparse.c b/util/data/msgparse.c
index 9239f8fe396a..4be1a72dcb5a 100644
--- a/util/data/msgparse.c
+++ b/util/data/msgparse.c
@@ -687,6 +687,9 @@ calc_size(sldns_buffer* pkt, uint16_t type, struct rr_parse* rr)
}
rdf++;
}
+ /* rdata ended before all _dname_count names were seen */
+ if(count != 0)
+ return 0; /* the rdata is too short. */
}
/* remaining rdata */
rr->size += pkt_len;
@@ -1068,13 +1071,13 @@ parse_edns_options_from_query(uint8_t* rdata_ptr, size_t rdata_len,
* purposes. It will be overwritten if (re)creation
* is needed.
*/
- if(repinfo->remote_addr.ss_family == AF_INET) {
+ if(repinfo->client_addr.ss_family == AF_INET) {
memcpy(server_cookie + 16,
- &((struct sockaddr_in*)&repinfo->remote_addr)->sin_addr, 4);
+ &((struct sockaddr_in*)&repinfo->client_addr)->sin_addr, 4);
} else {
cookie_is_v4 = 0;
memcpy(server_cookie + 16,
- &((struct sockaddr_in6*)&repinfo->remote_addr)->sin6_addr, 16);
+ &((struct sockaddr_in6*)&repinfo->client_addr)->sin6_addr, 16);
}
if(cfg->cookie_secret_file &&
diff --git a/util/data/packed_rrset.c b/util/data/packed_rrset.c
index 89ece3c031f5..b695c1b7162d 100644
--- a/util/data/packed_rrset.c
+++ b/util/data/packed_rrset.c
@@ -198,6 +198,7 @@ get_cname_target(struct ub_packed_rrset_key* rrset, uint8_t** dname,
{
struct packed_rrset_data* d;
size_t len;
+ if(!rrset) return;
if(ntohs(rrset->rk.type) != LDNS_RR_TYPE_CNAME &&
ntohs(rrset->rk.type) != LDNS_RR_TYPE_DNAME)
return;
diff --git a/util/fptr_wlist.c b/util/fptr_wlist.c
index a451340650ab..5edd8adf270a 100644
--- a/util/fptr_wlist.c
+++ b/util/fptr_wlist.c
@@ -610,6 +610,7 @@ int
fptr_whitelist_alloc_cleanup(void (*fptr)(void*))
{
if(fptr == &worker_alloc_cleanup) return 1;
+ else if(fptr == &libworker_alloc_cleanup) return 1;
return 0;
}
diff --git a/util/module.h b/util/module.h
index a6fa6be9062a..75c3675e1be2 100644
--- a/util/module.h
+++ b/util/module.h
@@ -721,6 +721,12 @@ struct module_qstate {
/** whether the reply should be dropped */
int is_drop;
+ /** the global quota that was reached, by one of the modules.
+ * So that continued counting can go on from that point. */
+ int global_quota_reached;
+ /** the global quota that a query started with, it is a subquery,
+ * so that calling mesh states can see the increase. */
+ int global_quota_started;
};
/**
diff --git a/util/netevent.c b/util/netevent.c
index a86e22518fb8..b7092b88a876 100644
--- a/util/netevent.c
+++ b/util/netevent.c
@@ -1827,7 +1827,6 @@ doq_send_retry(struct comm_point* c, struct doq_pkt_addr* paddr,
char host[256], port[32];
struct ngtcp2_cid scid;
uint8_t token[NGTCP2_CRYPTO_MAX_RETRY_TOKENLEN];
- ngtcp2_tstamp ts;
ngtcp2_ssize tokenlen, ret;
if(!doq_print_addr_port(&paddr->addr, paddr->addrlen, host,
@@ -1841,12 +1840,10 @@ doq_send_retry(struct comm_point* c, struct doq_pkt_addr* paddr,
scid.datalen = c->doq_socket->sv_scidlen;
doq_cid_randfill(&scid, scid.datalen, c->doq_socket->rnd);
- ts = doq_get_timestamp_nanosec();
-
tokenlen = ngtcp2_crypto_generate_retry_token(token,
c->doq_socket->static_secret, c->doq_socket->static_secret_len,
hd->version, (void*)&paddr->addr, paddr->addrlen, &scid,
- &hd->dcid, ts);
+ &hd->dcid, doq_get_timestamp_nanosec());
if(tokenlen < 0) {
log_err("ngtcp2_crypto_generate_retry_token failed: %s",
ngtcp2_strerror(tokenlen));
@@ -1895,13 +1892,11 @@ doq_verify_retry_token(struct comm_point* c, struct doq_pkt_addr* paddr,
struct ngtcp2_cid* ocid, struct ngtcp2_pkt_hd* hd)
{
char host[256], port[32];
- ngtcp2_tstamp ts;
if(!doq_print_addr_port(&paddr->addr, paddr->addrlen, host,
sizeof(host), port, sizeof(port))) {
log_err("doq_verify_retry_token failed");
return 0;
}
- ts = doq_get_timestamp_nanosec();
verbose(VERB_ALGO, "doq: verifying retry token from %s %s", host,
port);
if(ngtcp2_crypto_verify_retry_token(ocid,
@@ -1913,7 +1908,7 @@ doq_verify_retry_token(struct comm_point* c, struct doq_pkt_addr* paddr,
c->doq_socket->static_secret,
c->doq_socket->static_secret_len, hd->version,
(void*)&paddr->addr, paddr->addrlen, &hd->dcid,
- 10*NGTCP2_SECONDS, ts) != 0) {
+ 10*NGTCP2_SECONDS, doq_get_timestamp_nanosec()) != 0) {
verbose(VERB_ALGO, "doq: could not verify retry token "
"from %s %s", host, port);
return 0;
@@ -1928,13 +1923,11 @@ doq_verify_token(struct comm_point* c, struct doq_pkt_addr* paddr,
struct ngtcp2_pkt_hd* hd)
{
char host[256], port[32];
- ngtcp2_tstamp ts;
if(!doq_print_addr_port(&paddr->addr, paddr->addrlen, host,
sizeof(host), port, sizeof(port))) {
log_err("doq_verify_token failed");
return 0;
}
- ts = doq_get_timestamp_nanosec();
verbose(VERB_ALGO, "doq: verifying token from %s %s", host, port);
if(ngtcp2_crypto_verify_regular_token(
#ifdef HAVE_STRUCT_NGTCP2_PKT_HD_TOKENLEN
@@ -1944,7 +1937,7 @@ doq_verify_token(struct comm_point* c, struct doq_pkt_addr* paddr,
#endif
c->doq_socket->static_secret, c->doq_socket->static_secret_len,
(void*)&paddr->addr, paddr->addrlen, 3600*NGTCP2_SECONDS,
- ts) != 0) {
+ doq_get_timestamp_nanosec()) != 0) {
verbose(VERB_ALGO, "doq: could not verify token from %s %s",
host, port);
return 0;
@@ -2171,6 +2164,7 @@ doq_pickup_timer(struct comm_point* c)
{
struct doq_timer* t;
struct timeval tv;
+ ngtcp2_tstamp ts = 0;
int have_time = 0;
memset(&tv, 0, sizeof(tv));
@@ -2180,27 +2174,24 @@ doq_pickup_timer(struct comm_point* c)
t->worker_doq_socket == c->doq_socket) {
/* pick up this element */
t->worker_doq_socket = c->doq_socket;
+ memcpy(&tv, &t->time_real, sizeof(tv));
+ ts = t->time_mono;
have_time = 1;
- memcpy(&tv, &t->time, sizeof(tv));
break;
}
}
lock_rw_unlock(&c->doq_socket->table->lock);
-
+ c->doq_socket->marked_time = ts;
if(have_time) {
struct timeval rel;
timeval_subtract(&rel, &tv, c->doq_socket->now_tv);
comm_timer_set(c->doq_socket->timer, &rel);
- memcpy(&c->doq_socket->marked_time, &tv,
- sizeof(c->doq_socket->marked_time));
verbose(VERB_ALGO, "doq pickup timer at %d.%6.6d in %d.%6.6d",
(int)tv.tv_sec, (int)tv.tv_usec, (int)rel.tv_sec,
(int)rel.tv_usec);
} else {
if(comm_timer_is_set(c->doq_socket->timer))
comm_timer_disable(c->doq_socket->timer);
- memset(&c->doq_socket->marked_time, 0,
- sizeof(c->doq_socket->marked_time));
verbose(VERB_ALGO, "doq timer disabled");
}
}
@@ -2213,13 +2204,14 @@ doq_done_setup_timer_and_write(struct comm_point* c, struct doq_conn* conn)
uint8_t cid[NGTCP2_MAX_CIDLEN];
rbnode_type* node;
struct timeval new_tv;
+ ngtcp2_tstamp new_ts;
int write_change = 0, timer_change = 0;
/* No longer in callbacks, so the pointer to doq_socket is back
* to NULL. */
conn->doq_socket = NULL;
- if(doq_conn_check_timer(conn, &new_tv))
+ if(doq_conn_check_timer(conn, &new_tv, &new_ts))
timer_change = 1;
if( (conn->write_interest && !conn->on_write_list) ||
(!conn->write_interest && conn->on_write_list))
@@ -2265,7 +2257,7 @@ doq_done_setup_timer_and_write(struct comm_point* c, struct doq_conn* conn)
}
if(timer_change) {
doq_timer_set(c->doq_socket->table, &conn->timer,
- c->doq_socket, &new_tv);
+ c->doq_socket, &new_tv, new_ts);
}
lock_rw_unlock(&c->doq_socket->table->lock);
lock_basic_unlock(&conn->lock);
@@ -2429,7 +2421,7 @@ doq_write_blocked_pkt(struct comm_point* c)
return 1;
}
-/** doq find a timer that timeouted and return the conn, locked. */
+/** doq find a timer that timed out and return the conn, locked. */
static struct doq_conn*
doq_timer_timeout_conn(struct doq_server_socket* doq_socket)
{
@@ -2442,7 +2434,7 @@ doq_timer_timeout_conn(struct doq_server_socket* doq_socket)
conn = t->conn;
/* If now < timer then no further timeouts in tree. */
- if(timeval_smaller(doq_socket->now_tv, &t->time)) {
+ if(timeval_smaller(doq_socket->now_tv, &t->time_real)) {
lock_rw_unlock(&doq_socket->table->lock);
return NULL;
}
@@ -2465,11 +2457,11 @@ doq_timer_erase_marker(struct doq_server_socket* doq_socket)
{
struct doq_timer* t;
lock_rw_wrlock(&doq_socket->table->lock);
- t = doq_timer_find_time(doq_socket->table, &doq_socket->marked_time);
+ t = doq_timer_find_time(doq_socket->table, doq_socket->marked_time);
if(t && t->worker_doq_socket == doq_socket)
t->worker_doq_socket = NULL;
lock_rw_unlock(&doq_socket->table->lock);
- memset(&doq_socket->marked_time, 0, sizeof(doq_socket->marked_time));
+ doq_socket->marked_time = 0;
}
void
@@ -2776,7 +2768,7 @@ doq_server_socket_create(struct doq_table* table, struct ub_randstate* rnd,
free(doq_socket);
return NULL;
}
- memset(&doq_socket->marked_time, 0, sizeof(doq_socket->marked_time));
+ doq_socket->marked_time = 0;
comm_base_timept(base, &doq_socket->now_tt, &doq_socket->now_tv);
doq_socket->cfg = cfg;
return doq_socket;
@@ -3174,7 +3166,7 @@ static void http2_stream_delete(struct http2_session* h2_session,
{
if(h2_stream->mesh_state) {
mesh_state_remove_reply(h2_stream->mesh, h2_stream->mesh_state,
- h2_session->c);
+ h2_session->c, NULL);
h2_stream->mesh_state = NULL;
}
http2_req_stream_clear(h2_stream);
@@ -6685,7 +6677,9 @@ comm_point_send_reply(struct comm_reply *repinfo)
log_assert(repinfo && repinfo->c);
#ifdef USE_DNSCRYPT
buffer = repinfo->c->dnscrypt_buffer;
- if(!dnsc_handle_uncurved_request(repinfo)) {
+ if(!dnsc_handle_uncurved_request(repinfo,
+ repinfo->c->tcp_req_info?
+ repinfo->c->tcp_req_info->spool_buffer:repinfo->c->buffer)) {
return;
}
#else
diff --git a/util/netevent.h b/util/netevent.h
index c5114bbbef27..aee8a653988c 100644
--- a/util/netevent.h
+++ b/util/netevent.h
@@ -187,6 +187,8 @@ struct comm_reply {
/** port number for doq */
int doq_srcport;
#endif /* HAVE_NGTCP2 */
+ /** The doq stream to register mesh states to. */
+ struct doq_stream* doq_stream;
};
/**
@@ -1093,8 +1095,10 @@ struct doq_server_socket {
struct doq_pkt_addr* blocked_paddr;
/** timer for this worker on this comm_point to wait on. */
struct comm_timer* timer;
+#ifdef HAVE_NGTCP2
/** the timer that is marked by the doq_socket as waited on. */
- struct timeval marked_time;
+ ngtcp2_tstamp marked_time;
+#endif
/** the current time for use by time functions, time_t. */
time_t* now_tt;
/** the current time for use by time functions, timeval. */
diff --git a/validator/val_sigcrypt.c b/validator/val_sigcrypt.c
index 9f27f9cc9bb7..16c01d2ee372 100644
--- a/validator/val_sigcrypt.c
+++ b/validator/val_sigcrypt.c
@@ -1094,6 +1094,7 @@ canonicalize_rdata(sldns_buffer* buf, struct ub_packed_rrset_key* rrset,
size_t len)
{
uint8_t* datstart = sldns_buffer_current(buf)-len+2;
+ size_t firstlen;
switch(ntohs(rrset->rk.type)) {
case LDNS_RR_TYPE_NXT:
case LDNS_RR_TYPE_NS:
@@ -1113,8 +1114,9 @@ canonicalize_rdata(sldns_buffer* buf, struct ub_packed_rrset_key* rrset,
case LDNS_RR_TYPE_SOA:
/* two names after another */
query_dname_tolower(datstart);
- query_dname_tolower(datstart +
- dname_valid(datstart, len-2));
+ firstlen = dname_valid(datstart, len-2);
+ if(firstlen && firstlen < len-2)
+ query_dname_tolower(datstart + firstlen);
return;
case LDNS_RR_TYPE_RT:
case LDNS_RR_TYPE_AFSDB:
@@ -1141,8 +1143,9 @@ canonicalize_rdata(sldns_buffer* buf, struct ub_packed_rrset_key* rrset,
return;
datstart += 2;
query_dname_tolower(datstart);
- query_dname_tolower(datstart +
- dname_valid(datstart, len-2-2));
+ firstlen = dname_valid(datstart, len-2-2);
+ if(firstlen && firstlen < len-2-2)
+ query_dname_tolower(datstart + firstlen);
return;
case LDNS_RR_TYPE_NAPTR:
if(len < 2+4)
@@ -1666,6 +1669,13 @@ dnskey_verify_rrset_sig(struct regional* region, sldns_buffer* buf,
*reason_bogus = LDNS_EDE_DNSSEC_BOGUS;
return sec_status_bogus;
}
+ if((int)sig[2+3] < dname_signame_label_count(signer)) {
+ verbose(VERB_QUERY, "verify: RRSIG label count too low for signer");
+ *reason = "signature labelcount lower than signature signer";
+ if(reason_bogus)
+ *reason_bogus = LDNS_EDE_DNSSEC_BOGUS;
+ return sec_status_bogus;
+ }
/* original ttl, always ok */
diff --git a/validator/val_utils.c b/validator/val_utils.c
index 8e4c9190043d..6754a8bab69e 100644
--- a/validator/val_utils.c
+++ b/validator/val_utils.c
@@ -157,7 +157,7 @@ val_classify_response(uint16_t query_flags, struct query_info* origqinf,
}
/** Get signer name from RRSIG */
-static void
+void
rrsig_get_signer(uint8_t* data, size_t len, uint8_t** sname, size_t* slen)
{
/* RRSIG rdata is not allowed to be compressed, it is stored
@@ -439,10 +439,15 @@ val_verify_rrset(struct module_env* env, struct val_env* ve,
* only improves security status
* and bogus is set only once, even if we rechecked the status */
if(sec > d->security) {
+ int wc_expanded = 0;
d->security = sec;
- if(sec == sec_status_secure)
+ if(sec == sec_status_secure) {
+ uint8_t* wc = NULL;
+ size_t wclen = 0;
d->trust = rrset_trust_validated;
- else if(sec == sec_status_bogus) {
+ if(val_rrset_wildcard(rrset, &wc, &wclen) && wc)
+ wc_expanded = 1;
+ } else if(sec == sec_status_bogus) {
size_t i;
/* update ttl for rrset to fixed value. */
d->ttl = ve->bogus_ttl;
@@ -455,7 +460,11 @@ val_verify_rrset(struct module_env* env, struct val_env* ve,
lock_basic_unlock(&ve->bogus_lock);
}
/* if status updated - store in cache for reuse */
- rrset_update_sec_status(env->rrset_cache, rrset, *env->now);
+ /* For a wildcard rrset, that is secure, do not store this
+ * into the cache, because it changes proofs around the
+ * item. */
+ if(!wc_expanded)
+ rrset_update_sec_status(env->rrset_cache, rrset, *env->now);
}
return sec;
diff --git a/validator/val_utils.h b/validator/val_utils.h
index e0c6499024e8..f3750742b5d1 100644
--- a/validator/val_utils.h
+++ b/validator/val_utils.h
@@ -438,4 +438,8 @@ struct dns_msg* val_find_DS(struct module_env* env, uint8_t* nm, size_t nmlen,
int derive_cname_from_dname(struct ub_packed_rrset_key* cname,
struct ub_packed_rrset_key* dname, uint8_t* out, size_t outlen);
+/** Get signer name from RRSIG, sname is NULL if malformed. */
+void rrsig_get_signer(uint8_t* data, size_t len, uint8_t** sname,
+ size_t* slen);
+
#endif /* VALIDATOR_VAL_UTILS_H */
diff --git a/validator/validator.c b/validator/validator.c
index e7992b6e3711..f27ae5b4290a 100644
--- a/validator/validator.c
+++ b/validator/validator.c
@@ -517,6 +517,14 @@ generate_request(struct module_qstate* qstate, int id, uint8_t* name,
/* add our blacklist to the query blacklist */
sock_list_merge(&(*newq)->blacklist, (*newq)->region,
vq->chain_blacklist);
+ /* start its global quota counter where this one is. */
+ if(qstate->global_quota_reached >
+ (*newq)->global_quota_reached) {
+ (*newq)->global_quota_started =
+ qstate->global_quota_reached;
+ (*newq)->global_quota_reached =
+ qstate->global_quota_reached;
+ }
}
qstate->ext_state[id] = module_wait_subquery;
return 1;
@@ -1043,7 +1051,14 @@ validate_positive_response(struct module_env* env, struct val_env* ve,
uint8_t* wc = NULL;
size_t wl;
int wc_cached = 0;
+ int wc_to_cache = 0;
+ uint8_t* cache_wc = NULL;
+ size_t cache_wl = 0;
+ struct ub_packed_rrset_key* cache_s = NULL;
int wc_NSEC_ok = 0;
+ /* This is used to update the RRset cache, with the combination
+ * of the dname expansion and this wildcard, for security status. */
+ struct ub_packed_rrset_key* wc_rrset = NULL;
int nsec3s_seen = 0;
size_t i;
struct ub_packed_rrset_key* s;
@@ -1062,14 +1077,20 @@ validate_positive_response(struct module_env* env, struct val_env* ve,
ntohs(s->rk.type), ntohs(s->rk.rrset_class));
chase_reply->security = sec_status_bogus;
update_reason_bogus(chase_reply, LDNS_EDE_DNSSEC_BOGUS);
+ if(wc_rrset)
+ ((struct packed_rrset_data*)wc_rrset->
+ entry.data)->security = sec_status_bogus;
return;
}
if(wc && !wc_cached && env->cfg->aggressive_nsec) {
- rrset_cache_update_wildcard(env->rrset_cache, s, wc, wl,
- env->alloc, *env->now);
+ /* Postpone cache adjust until proof has succeeded. */
+ wc_to_cache = 1;
+ cache_wc = wc;
+ cache_wl = wl;
+ cache_s = s;
wc_cached = 1;
}
-
+ if(wc) wc_rrset = s;
}
/* validate the AUTHORITY section as well - this will generally be
@@ -1126,8 +1147,15 @@ validate_positive_response(struct module_env* env, struct val_env* ve,
"did not exist");
chase_reply->security = sec_status_bogus;
update_reason_bogus(chase_reply, LDNS_EDE_DNSSEC_BOGUS);
+ if(wc_rrset)
+ ((struct packed_rrset_data*)wc_rrset->
+ entry.data)->security = sec_status_bogus;
return;
}
+ if(wc_to_cache) {
+ rrset_cache_update_wildcard(env->rrset_cache, cache_s,
+ cache_wc, cache_wl, env->alloc, *env->now);
+ }
verbose(VERB_ALGO, "Successfully validated positive response");
chase_reply->security = sec_status_secure;
@@ -1527,6 +1555,16 @@ validate_any_response(struct module_env* env, struct val_env* ve,
"did not exist");
chase_reply->security = sec_status_bogus;
update_reason_bogus(chase_reply, LDNS_EDE_DNSSEC_BOGUS);
+ /* Make the expanded name and wildcard RRSIG rrsets bogus */
+ for(i=0; i<chase_reply->an_numrrsets; i++) {
+ uint8_t* cwc = NULL;
+ size_t cwl = 0;
+ s = chase_reply->rrsets[i];
+ if(val_rrset_wildcard(s, &cwc, &cwl) && cwc) {
+ ((struct packed_rrset_data*)s->
+ entry.data)->security = sec_status_bogus;
+ }
+ }
return;
}
@@ -1564,6 +1602,9 @@ validate_cname_response(struct module_env* env, struct val_env* ve,
uint8_t* wc = NULL;
size_t wl;
int wc_NSEC_ok = 0;
+ /* This is used to update the RRset cache, with the combination
+ * of the dname expansion and this wildcard, for security status. */
+ struct ub_packed_rrset_key* wc_rrset = NULL;
int nsec3s_seen = 0;
size_t i;
struct ub_packed_rrset_key* s;
@@ -1584,6 +1625,7 @@ validate_cname_response(struct module_env* env, struct val_env* ve,
update_reason_bogus(chase_reply, LDNS_EDE_DNSSEC_BOGUS);
return;
}
+ if(wc) wc_rrset = s;
/* Refuse wildcarded DNAMEs rfc 4597.
* Do not follow a wildcarded DNAME because
@@ -1595,6 +1637,9 @@ validate_cname_response(struct module_env* env, struct val_env* ve,
ntohs(s->rk.type), ntohs(s->rk.rrset_class));
chase_reply->security = sec_status_bogus;
update_reason_bogus(chase_reply, LDNS_EDE_DNSSEC_BOGUS);
+ if(wc_rrset)
+ ((struct packed_rrset_data*)wc_rrset->
+ entry.data)->security = sec_status_bogus;
return;
}
@@ -1659,6 +1704,9 @@ validate_cname_response(struct module_env* env, struct val_env* ve,
"did not exist");
chase_reply->security = sec_status_bogus;
update_reason_bogus(chase_reply, LDNS_EDE_DNSSEC_BOGUS);
+ if(wc_rrset)
+ ((struct packed_rrset_data*)wc_rrset->
+ entry.data)->security = sec_status_bogus;
return;
}
@@ -3536,6 +3584,11 @@ val_inform_super(struct module_qstate* qstate, int id,
verbose(VERB_ALGO, "super: has no validator state");
return;
}
+ /* Pick up the global quota limit from the subquery. */
+ if(qstate->global_quota_reached > qstate->global_quota_started) {
+ super->global_quota_reached += qstate->global_quota_reached -
+ qstate->global_quota_started;
+ }
if(vq->wait_prime_ta) {
vq->wait_prime_ta = 0;
process_prime_response(super, vq, id, qstate->return_rcode,