Compare commits

..
Author SHA1 Message Date
W.C.A. Wijngaards 48d0aea60a - auth-load-thread, unit test for auth_load_http. 2026-08-11 16:50:24 +02:00
W.C.A. Wijngaards 17bbcac979 - auth-load-thread, fix buffer free in http processing. 2026-08-11 16:50:00 +02:00
W.C.A. Wijngaards fd8ebbb19c Merge branch 'master' into auth-load-thread 2026-08-11 16:23:20 +02:00
W.C.A. Wijngaards 2adbbea365 - auth-load-thread, print thread details in log at high verbosity, 8. 2026-08-11 16:23:03 +02:00
W.C.A. Wijngaards 8ee0bca833 - Fix stat_values.tdir test to have less test failures. 2026-08-11 10:07:38 +02:00
W.C.A. Wijngaards c58e6add2b - Fix #1492 from zacek: Data race in log_init() on
key_created/log_lock when calling ub_ctx_create()
  concurrently from multiple threads.
2026-08-11 09:42:30 +02:00
W.C.A. Wijngaards 156c00a727 Merge branch 'master' into auth-load-thread 2026-08-10 16:42:59 +02:00
W.C.A. Wijngaards d753f95956 - auth-load-thread, print time taken during auth load processing. 2026-08-10 16:42:04 +02:00
W.C.A. Wijngaards 93a56205cf - Fix #1489 from jplesnik: Replace removed Python 2 C API
macros for SWIG 4.5.0 compatibility.
2026-08-07 08:57:32 +02:00
W.C.A. Wijngaards 709f622658 Note issue number in Changlog entry. 2026-08-06 17:15:55 +02:00
akhanin-dnsfandGitHub 307fc6f062 - Fix bounds check in packed_rr_to_string, it checked the (#1488)
assembled rr length against the output string length
  dest_len, instead of against the size of the rr buffer it
  writes into. Callers in cachedump.c and remote.c pass a
  dest_len larger than that buffer.
- Unit test for packed_rr_to_string.
2026-08-06 17:04:05 +02:00
W.C.A. Wijngaards 8b33c5d7ff - Fix #1487: regression in 1.26.0, ipsecmod is now always
partly enabled.
2026-08-06 09:46:18 +02:00
W.C.A. Wijngaards 36bd52afb9 Fix typo in Changelog. 2026-08-06 09:08:33 +02:00
W.C.A. Wijngaards b7d13ff12b - Fix ##1485: the list_forwards command omits port numbers.
The list_forwards and list_stubs commands for
  unbound-control print port and tls auth name.
2026-08-06 09:08:17 +02:00
W.C.A. Wijngaards bdfcfb861f - Fix to set makedist.sh to not wget config.sub and
config.guess from git repo. The fetch times out, and the
  version from libtoolize is much more recent now than
  that it was when the wget was added.
2026-08-04 10:04:34 +02:00
W.C.A. Wijngaards b444deffd2 Note 1.26.0 release. 2026-08-04 10:01:59 +02:00
W.C.A. Wijngaards ff28b7e5cf - For #1483: The failure reason when an NSEC NXDOMAIN is
encountered when looking for an insecure delegation, is
  fixed to mention the NSEC records, instead of nonexistent
  NSEC3 records, that it attempted.
2026-07-31 09:53:47 +02:00
W.C.A. Wijngaards 79b84bbc91 - Fix #1482: DNS-over-QUIC doesn't work with simple config.
That fixes interface-automatic for use with doq service.
2026-07-30 08:24:42 +02:00
W.C.A. Wijngaards cbfc3b0342 - Tag for 1.26.0rc1. The repo continues with version 1.26.1. 2026-07-28 09:45:59 +02:00
W.C.A. Wijngaards 61d6c0e766 - auth-load-thread, implement active thread counter for auth load threads. 2026-07-07 17:21:16 +02:00
W.C.A. Wijngaards b4daa2d0fa - auth-load-thread, use define for constant for number of records before poll. 2026-07-07 16:26:42 +02:00
W.C.A. Wijngaards 425b701fb9 - auth-load-thread, fix memory leak on alloc failure when appending rrset
copy for ixfr main zone data copy.
2026-07-03 16:39:28 +02:00
W.C.A. Wijngaards 0a5cde80f1 - auth-load-thread, auth-task-threads: num config option that enables and
disables the auth load thread.
2026-07-03 14:03:15 +02:00
W.C.A. Wijngaards 58ede90fdb Merge branch 'master' into auth-load-thread 2026-07-02 15:28:24 +02:00
W.C.A. Wijngaards 55ae8da032 - auth-load-thread, basic test and fixes so it works. 2026-07-02 11:10:06 +02:00
W.C.A. Wijngaards 6bd86df72e - auth-load-thread, simplify cleanup in end transfer load process. 2026-07-02 08:55:13 +02:00
W.C.A. Wijngaards 0efd3605cc Merge branch 'master' into auth-load-thread 2026-07-01 16:54:55 +02:00
W.C.A. Wijngaards a2f2f53ef9 - auth-load-thread, process end of successful transfer. 2026-07-01 16:54:36 +02:00
W.C.A. Wijngaards b5a03093f6 - auth-load-thread, check for quit during the processing. 2026-07-01 14:41:40 +02:00
W.C.A. Wijngaards 8eba898135 - auth-load-thread, process AXFR, by loading, swap in, delete of old. 2026-07-01 14:27:31 +02:00
W.C.A. Wijngaards 56f66de89f - auth-load-thread, process IXFR, by making a copy, adjust changes, swap in. 2026-07-01 13:05:19 +02:00
W.C.A. Wijngaards 2fbaee2255 - auth-load-thread, process transfer content and swap result back in. 2026-06-30 16:53:27 +02:00
W.C.A. Wijngaards a122490461 - auth-load-thread, poll for quit and process load transfer end. 2026-06-29 16:52:20 +02:00
W.C.A. Wijngaards 7826b4f306 Merge branch 'master' into auth-load-thread 2026-06-29 16:00:01 +02:00
W.C.A. Wijngaards 380994219f - auth-load-thread, make and run auth load thread. 2026-06-26 17:11:12 +02:00
W.C.A. Wijngaards 153accb8de - auth-load-thread, put create_socketpair and sock_poll_timeout into
util/net_help.h
2026-06-26 15:22:08 +02:00
W.C.A. Wijngaards 7bb1c62263 - auth-load-thread, add authload.c to Makefile.in. 2026-06-26 14:27:12 +02:00
W.C.A. Wijngaards 1578b6e180 - auth-load-thread, add services/authload.c and services/authload.h 2026-06-26 14:00:04 +02:00
47 changed files with 2710 additions and 536 deletions
+4 -2
View File
@@ -125,7 +125,7 @@ services/localzone.c services/mesh.c services/modstack.c services/view.c \
services/rpz.c util/rfc_1982.c \
services/outbound_list.c services/outside_network.c util/alloc.c \
util/config_file.c util/configlexer.c util/configparser.c \
util/shm_side/shm_main.c services/authzone.c \
util/shm_side/shm_main.c services/authzone.c services/authload.c \
util/fptr_wlist.c util/locks.c util/log.c util/mini_event.c util/module.c \
util/netevent.c util/net_help.c util/random.c util/rbtree.c util/regional.c \
util/rtt.c util/siphash.c util/edns.c util/storage/dnstree.c util/storage/lookup3.c \
@@ -152,7 +152,8 @@ autotrust.lo val_anchor.lo rpz.lo rfc_1982.lo proxy_protocol.lo \
validator.lo val_kcache.lo val_kentry.lo val_neg.lo val_nsec3.lo val_nsec.lo \
val_secalgo.lo val_sigcrypt.lo val_utils.lo dns64.lo $(CACHEDB_OBJ) authzone.lo \
$(SUBNET_OBJ) $(PYTHONMOD_OBJ) $(CHECKLOCK_OBJ) $(DNSTAP_OBJ) $(DNSCRYPT_OBJ) \
$(IPSECMOD_OBJ) $(IPSET_OBJ) $(DYNLIBMOD_OBJ) respip.lo timeval_func.lo
$(IPSECMOD_OBJ) $(IPSET_OBJ) $(DYNLIBMOD_OBJ) respip.lo timeval_func.lo \
authload.lo
COMMON_OBJ_WITHOUT_UB_EVENT=$(COMMON_OBJ_WITHOUT_NETCALL) netevent.lo listen_dnsport.lo \
outside_network.lo
COMMON_OBJ=$(COMMON_OBJ_WITHOUT_UB_EVENT) ub_event.lo
@@ -721,6 +722,7 @@ depend:
ipset.lo ipset.o: $(srcdir)/ipset/ipset.c
doqclient.lo doqclient.o: $(srcdir)/testcode/doqclient.c
unitdoq.lo unitdoq.o: $(srcdir)/testcode/unitdoq.c
authload.lo authload.o: $(srcdir)/services/authload.c
# Dependencies
dns.lo dns.o: $(srcdir)/services/cache/dns.c config.h $(srcdir)/iterator/iter_delegpt.h $(srcdir)/util/log.h \
Vendored
+13 -12
View File
@@ -1,6 +1,6 @@
#! /bin/sh
# Guess values for system-dependent variables and create Makefiles.
# Generated by GNU Autoconf 2.71 for unbound 1.26.0.
# Generated by GNU Autoconf 2.71 for unbound 1.26.1.
#
# Report bugs to <unbound-bugs@nlnetlabs.nl or https://github.com/NLnetLabs/unbound/issues>.
#
@@ -622,8 +622,8 @@ MAKEFLAGS=
# Identity of this package.
PACKAGE_NAME='unbound'
PACKAGE_TARNAME='unbound'
PACKAGE_VERSION='1.26.0'
PACKAGE_STRING='unbound 1.26.0'
PACKAGE_VERSION='1.26.1'
PACKAGE_STRING='unbound 1.26.1'
PACKAGE_BUGREPORT='unbound-bugs@nlnetlabs.nl or https://github.com/NLnetLabs/unbound/issues'
PACKAGE_URL=''
@@ -1513,7 +1513,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.26.0 to adapt to many kinds of systems.
\`configure' configures unbound 1.26.1 to adapt to many kinds of systems.
Usage: $0 [OPTION]... [VAR=VALUE]...
@@ -1579,7 +1579,7 @@ fi
if test -n "$ac_init_help"; then
case $ac_init_help in
short | recursive ) echo "Configuration of unbound 1.26.0:";;
short | recursive ) echo "Configuration of unbound 1.26.1:";;
esac
cat <<\_ACEOF
@@ -1832,7 +1832,7 @@ fi
test -n "$ac_init_help" && exit $ac_status
if $ac_init_version; then
cat <<\_ACEOF
unbound configure 1.26.0
unbound configure 1.26.1
generated by GNU Autoconf 2.71
Copyright (C) 2021 Free Software Foundation, Inc.
@@ -2489,7 +2489,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.26.0, which was
It was created by unbound $as_me 1.26.1, which was
generated by GNU Autoconf 2.71. Invocation command line was
$ $0$ac_configure_args_raw
@@ -3253,11 +3253,11 @@ UNBOUND_VERSION_MAJOR=1
UNBOUND_VERSION_MINOR=26
UNBOUND_VERSION_MICRO=0
UNBOUND_VERSION_MICRO=1
LIBUNBOUND_CURRENT=9
LIBUNBOUND_REVISION=39
LIBUNBOUND_REVISION=40
LIBUNBOUND_AGE=1
# 1.0.0 had 0:12:0
# 1.0.1 had 0:13:0
@@ -3364,6 +3364,7 @@ LIBUNBOUND_AGE=1
# 1.25.1 had 9:37:1
# 1.25.2 had 9:38:1
# 1.26.0 had 9:39:1
# 1.26.1 had 9:40:1
# Current -- the number of the binary API that we're implementing
# Revision -- which iteration of the implementation of the binary
@@ -25711,7 +25712,7 @@ printf "%s\n" "#define MAXSYSLOGMSGLEN 10240" >>confdefs.h
version=1.26.0
version=1.26.1
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for build time" >&5
printf %s "checking for build time... " >&6; }
@@ -26241,7 +26242,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.26.0, which was
This file was extended by unbound $as_me 1.26.1, which was
generated by GNU Autoconf 2.71. Invocation command line was
CONFIG_FILES = $CONFIG_FILES
@@ -26309,7 +26310,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.26.0
unbound config.status 1.26.1
configured by $0, generated by GNU Autoconf 2.71,
with options \\"\$ac_cs_config\\"
+3 -2
View File
@@ -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],[26])
m4_define([VERSION_MICRO],[0])
m4_define([VERSION_MICRO],[1])
AC_INIT([unbound],m4_defn([VERSION_MAJOR]).m4_defn([VERSION_MINOR]).m4_defn([VERSION_MICRO]),[unbound-bugs@nlnetlabs.nl or https://github.com/NLnetLabs/unbound/issues],[unbound])
AC_SUBST(UNBOUND_VERSION_MAJOR, [VERSION_MAJOR])
AC_SUBST(UNBOUND_VERSION_MINOR, [VERSION_MINOR])
AC_SUBST(UNBOUND_VERSION_MICRO, [VERSION_MICRO])
LIBUNBOUND_CURRENT=9
LIBUNBOUND_REVISION=39
LIBUNBOUND_REVISION=40
LIBUNBOUND_AGE=1
# 1.0.0 had 0:12:0
# 1.0.1 had 0:13:0
@@ -126,6 +126,7 @@ LIBUNBOUND_AGE=1
# 1.25.1 had 9:37:1
# 1.25.2 had 9:38:1
# 1.26.0 had 9:39:1
# 1.26.1 had 9:40:1
# Current -- the number of the binary API that we're implementing
# Revision -- which iteration of the implementation of the binary
+13
View File
@@ -86,6 +86,7 @@
#include "services/view.h"
#include "services/modstack.h"
#include "services/authzone.h"
#include "services/authload.h"
#include "util/module.h"
#include "util/random.h"
#include "util/tube.h"
@@ -590,6 +591,17 @@ daemon_init(void)
free(daemon);
return NULL;
}
if(!(daemon->auth_load_info = auth_load_info_create())) {
edns_strings_delete(daemon->env->edns_strings);
auth_zones_delete(daemon->env->auth_zones);
acl_list_delete(daemon->acl_interface);
acl_list_delete(daemon->acl);
tcl_list_delete(daemon->tcl);
edns_known_options_delete(daemon->env);
free(daemon->env);
free(daemon);
return NULL;
}
return daemon;
}
@@ -1262,6 +1274,7 @@ daemon_delete(struct daemon* daemon)
edns_strings_delete(daemon->env->edns_strings);
auth_zones_delete(daemon->env->auth_zones);
}
auth_load_info_delete(daemon->auth_load_info);
ub_randfree(daemon->rand);
alloc_clear(&daemon->superalloc);
acl_list_delete(daemon->acl);
+3
View File
@@ -62,6 +62,7 @@ struct doq_table;
struct cookie_secrets;
struct fast_reload_thread;
struct fast_reload_printq;
struct auth_load_general_info;
struct shared_ports;
#include "dnstap/dnstap_config.h"
@@ -191,6 +192,8 @@ struct daemon {
int fast_reload_tcl_has_changes;
/** config file name */
char* cfgfile;
/** Auth load threads, the number of active threads. */
struct auth_load_general_info* auth_load_info;
};
/**
+23 -372
View File
@@ -115,9 +115,6 @@
#ifdef HAVE_NETDB_H
#include <netdb.h>
#endif
#ifdef HAVE_POLL_H
#include <poll.h>
#endif
/* just for portability */
#ifdef SQ
@@ -2681,7 +2678,7 @@ static int
ssl_print_name_dp(RES* ssl, const char* str, uint8_t* nm, uint16_t dclass,
struct delegpt* dp)
{
char buf[LDNS_MAX_DOMAINLEN];
char buf[LDNS_MAX_DOMAINLEN], portstr[128], tls_auth_name[256];
struct delegpt_ns* ns;
struct delegpt_addr* a;
int f = 0;
@@ -2696,13 +2693,32 @@ ssl_print_name_dp(RES* ssl, const char* str, uint8_t* nm, uint16_t dclass,
}
for(ns = dp->nslist; ns; ns = ns->next) {
dname_str(ns->name, buf);
if(!ssl_printf(ssl, "%s%s", (f?" ":""), buf))
if(ns->port != UNBOUND_DNS_PORT)
snprintf(portstr, sizeof(portstr), "@%d", ns->port);
else portstr[0]=0;
if(ns->tls_auth_name)
snprintf(tls_auth_name, sizeof(tls_auth_name), "#%s",
ns->tls_auth_name);
else tls_auth_name[0]=0;
if(!ssl_printf(ssl, "%s%s%s%s", (f?" ":""), buf, portstr,
tls_auth_name))
return 0;
f = 1;
}
for(a = dp->target_list; a; a = a->next_target) {
int port = (unsigned)((a->addr.ss_family == AF_INET) ?
ntohs(((struct sockaddr_in*)&a->addr)->sin_port) :
ntohs(((struct sockaddr_in6*)&a->addr)->sin6_port));
addr_to_str(&a->addr, a->addrlen, buf, sizeof(buf));
if(!ssl_printf(ssl, "%s%s", (f?" ":""), buf))
if(port != UNBOUND_DNS_PORT)
snprintf(portstr, sizeof(portstr), "@%d", port);
else portstr[0]=0;
if(a->tls_auth_name)
snprintf(tls_auth_name, sizeof(tls_auth_name), "#%s",
a->tls_auth_name);
else tls_auth_name[0]=0;
if(!ssl_printf(ssl, "%s%s%s%s", (f?" ":""), buf, portstr,
tls_auth_name))
return 0;
f = 1;
}
@@ -4447,92 +4463,6 @@ int remote_control_callback(struct comm_point* c, void* arg, int err,
return 0;
}
/**
* This routine polls a socket for readiness.
* @param fd: file descriptor, -1 uses no fd for a timer only.
* @param timeout: time in msec to wait. 0 means nonblocking test,
* -1 waits blocking for events.
* @param pollin: check for input event.
* @param pollout: check for output event.
* @param event: output variable, set to true if the event happens.
* It is false if there was an error or timeout.
* @return false is system call failure, also logged.
*/
static int
sock_poll_timeout(int fd, int timeout, int pollin, int pollout, int* event)
{
int loopcount = 0;
/* Loop if the system call returns an errno to do so, like EINTR. */
log_assert(pollin || pollout);
while(1) {
struct pollfd p, *fds;
int nfds, ret;
if(++loopcount > IPC_LOOP_MAX) {
log_err("sock_poll_timeout: loop");
if(event)
*event = 0;
return 0;
}
if(fd == -1) {
fds = NULL;
nfds = 0;
} else {
fds = &p;
nfds = 1;
memset(&p, 0, sizeof(p));
p.fd = fd;
#ifndef USE_WINSOCK
p.events = POLLERR
| POLLHUP
;
#endif
if(pollin)
p.events |= POLLIN;
if(pollout)
p.events |= POLLOUT;
}
#ifndef USE_WINSOCK
ret = poll(fds, nfds, timeout);
#else
if(fds == NULL) {
Sleep(timeout);
ret = 0;
} else {
ret = WSAPoll(fds, nfds, timeout);
}
#endif
if(ret == -1) {
#ifndef USE_WINSOCK
if(
errno == EINTR || errno == EAGAIN
# ifdef EWOULDBLOCK
|| errno == EWOULDBLOCK
# endif
) continue; /* Try again. */
#endif
/* For WSAPoll we only get errors here:
* o WSAENETDOWN
* o WSAEFAULT
* o WSAEINVAL
* o WSAENOBUFS
*/
log_err("poll: %s", sock_strerror(errno));
if(event)
*event = 0;
return 0;
} else if(ret == 0) {
/* Timeout */
if(event)
*event = 0;
return 1;
}
break;
}
if(event)
*event = 1;
return 1;
}
/** fast reload convert fast reload notification status to string */
static const char*
fr_notification_to_string(enum fast_reload_notification status)
@@ -6346,6 +6276,7 @@ fr_atomic_copy_cfg(struct config_file* oldcfg, struct config_file* cfg,
COPY_VAR_ptr(acls);
COPY_VAR_int(donotquery_localhost);
COPY_VAR_ptr(tcp_connection_limits);
COPY_VAR_int(auth_task_threads);
COPY_VAR_int(harden_short_bufsize);
COPY_VAR_int(harden_large_queries);
COPY_VAR_int(harden_glue);
@@ -7134,286 +7065,6 @@ done_error:
}
#endif /* !THREADS_DISABLED */
/** create a socketpair for bidirectional communication, false on failure */
static int
create_socketpair(int* pair, struct ub_randstate* rand)
{
#ifndef USE_WINSOCK
if(socketpair(AF_UNIX, SOCK_STREAM, 0, pair) == -1) {
log_err("socketpair: %s", strerror(errno));
return 0;
}
(void)rand;
#else
struct sockaddr_in addr, baddr, accaddr, connaddr;
socklen_t baddrlen, accaddrlen, connaddrlen;
uint8_t localhost[] = {127, 0, 0, 1};
uint8_t nonce[16], recvnonce[16];
size_t i;
int lst, pollin_event, bcount, loopcount;
int connect_poll_timeout = 200; /* msec to wait for connection */
ssize_t ret;
pair[0] = -1;
pair[1] = -1;
for(i=0; i<sizeof(nonce); i++) {
nonce[i] = ub_random_max(rand, 256);
}
lst = socket(AF_INET, SOCK_STREAM, 0);
if(lst == -1) {
log_err("create_socketpair: socket: %s", sock_strerror(errno));
return 0;
}
memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_port = 0;
memcpy(&addr.sin_addr, localhost, 4);
if(bind(lst, (struct sockaddr*)&addr, (socklen_t)sizeof(addr))
== -1) {
log_err("create socketpair: bind: %s", sock_strerror(errno));
sock_close(lst);
return 0;
}
if(listen(lst, 12) == -1) {
log_err("create socketpair: listen: %s", sock_strerror(errno));
sock_close(lst);
return 0;
}
pair[1] = socket(AF_INET, SOCK_STREAM, 0);
if(pair[1] == -1) {
log_err("create socketpair: socket: %s", sock_strerror(errno));
sock_close(lst);
return 0;
}
baddrlen = (socklen_t)sizeof(baddr);
if(getsockname(lst, (struct sockaddr*)&baddr, &baddrlen) == -1) {
log_err("create socketpair: getsockname: %s",
sock_strerror(errno));
sock_close(lst);
sock_close(pair[1]);
pair[1] = -1;
return 0;
}
if(baddrlen > (socklen_t)sizeof(baddr)) {
log_err("create socketpair: getsockname returned addr too big");
sock_close(lst);
sock_close(pair[1]);
pair[1] = -1;
return 0;
}
/* the socket is blocking */
if(connect(pair[1], (struct sockaddr*)&baddr, baddrlen) == -1) {
log_err("create socketpair: connect: %s",
sock_strerror(errno));
sock_close(lst);
sock_close(pair[1]);
pair[1] = -1;
return 0;
}
if(!sock_poll_timeout(lst, connect_poll_timeout, 1, 0, &pollin_event)) {
log_err("create socketpair: poll for accept failed: %s",
sock_strerror(errno));
sock_close(lst);
sock_close(pair[1]);
pair[1] = -1;
return 0;
}
if(!pollin_event) {
log_err("create socketpair: poll timeout for accept");
sock_close(lst);
sock_close(pair[1]);
pair[1] = -1;
return 0;
}
accaddrlen = (socklen_t)sizeof(accaddr);
pair[0] = accept(lst, (struct sockaddr*)&accaddr, &accaddrlen);
if(pair[0] == -1) {
log_err("create socketpair: accept: %s", sock_strerror(errno));
sock_close(lst);
sock_close(pair[1]);
pair[1] = -1;
return 0;
}
if(accaddrlen > (socklen_t)sizeof(accaddr)) {
log_err("create socketpair: accept returned addr too big");
sock_close(lst);
sock_close(pair[0]);
sock_close(pair[1]);
pair[0] = -1;
pair[1] = -1;
return 0;
}
if(accaddr.sin_family != AF_INET ||
memcmp(localhost, &accaddr.sin_addr, 4) != 0) {
log_err("create socketpair: accept from wrong address");
sock_close(lst);
sock_close(pair[0]);
sock_close(pair[1]);
pair[0] = -1;
pair[1] = -1;
return 0;
}
connaddrlen = (socklen_t)sizeof(connaddr);
if(getsockname(pair[1], (struct sockaddr*)&connaddr, &connaddrlen)
== -1) {
log_err("create socketpair: getsockname connectedaddr: %s",
sock_strerror(errno));
sock_close(lst);
sock_close(pair[0]);
sock_close(pair[1]);
pair[0] = -1;
pair[1] = -1;
return 0;
}
if(connaddrlen > (socklen_t)sizeof(connaddr)) {
log_err("create socketpair: getsockname connectedaddr returned addr too big");
sock_close(lst);
sock_close(pair[0]);
sock_close(pair[1]);
pair[0] = -1;
pair[1] = -1;
return 0;
}
if(connaddr.sin_family != AF_INET ||
memcmp(localhost, &connaddr.sin_addr, 4) != 0) {
log_err("create socketpair: getsockname connectedaddr returned wrong address");
sock_close(lst);
sock_close(pair[0]);
sock_close(pair[1]);
pair[0] = -1;
pair[1] = -1;
return 0;
}
if(accaddr.sin_port != connaddr.sin_port) {
log_err("create socketpair: accept from wrong port");
sock_close(lst);
sock_close(pair[0]);
sock_close(pair[1]);
pair[0] = -1;
pair[1] = -1;
return 0;
}
sock_close(lst);
loopcount = 0;
bcount = 0;
while(1) {
if(++loopcount > IPC_LOOP_MAX) {
log_err("create socketpair: send failed due to loop");
sock_close(pair[0]);
sock_close(pair[1]);
pair[0] = -1;
pair[1] = -1;
return 0;
}
ret = send(pair[1], (void*)(nonce+bcount),
sizeof(nonce)-bcount, 0);
if(ret == -1) {
if(
#ifndef USE_WINSOCK
errno == EINTR || errno == EAGAIN
# ifdef EWOULDBLOCK
|| errno == EWOULDBLOCK
# endif
#else
WSAGetLastError() == WSAEINTR ||
WSAGetLastError() == WSAEINPROGRESS ||
WSAGetLastError() == WSAEWOULDBLOCK
#endif
)
continue; /* Try again. */
log_err("create socketpair: send: %s", sock_strerror(errno));
sock_close(pair[0]);
sock_close(pair[1]);
pair[0] = -1;
pair[1] = -1;
return 0;
} else if(ret+(ssize_t)bcount != sizeof(nonce)) {
bcount += ret;
if((size_t)bcount < sizeof(nonce))
continue;
}
break;
}
if(!sock_poll_timeout(pair[0], connect_poll_timeout, 1, 0, &pollin_event)) {
log_err("create socketpair: poll failed: %s",
sock_strerror(errno));
sock_close(pair[0]);
sock_close(pair[1]);
pair[0] = -1;
pair[1] = -1;
return 0;
}
if(!pollin_event) {
log_err("create socketpair: poll timeout for recv");
sock_close(pair[0]);
sock_close(pair[1]);
pair[0] = -1;
pair[1] = -1;
return 0;
}
loopcount = 0;
bcount = 0;
while(1) {
if(++loopcount > IPC_LOOP_MAX) {
log_err("create socketpair: recv failed due to loop");
sock_close(pair[0]);
sock_close(pair[1]);
pair[0] = -1;
pair[1] = -1;
return 0;
}
ret = recv(pair[0], (void*)(recvnonce+bcount),
sizeof(nonce)-bcount, 0);
if(ret == -1) {
if(
#ifndef USE_WINSOCK
errno == EINTR || errno == EAGAIN
# ifdef EWOULDBLOCK
|| errno == EWOULDBLOCK
# endif
#else
WSAGetLastError() == WSAEINTR ||
WSAGetLastError() == WSAEINPROGRESS ||
WSAGetLastError() == WSAEWOULDBLOCK
#endif
)
continue; /* Try again. */
log_err("create socketpair: recv: %s", sock_strerror(errno));
sock_close(pair[0]);
sock_close(pair[1]);
pair[0] = -1;
pair[1] = -1;
return 0;
} else if(ret == 0) {
log_err("create socketpair: stream closed");
sock_close(pair[0]);
sock_close(pair[1]);
pair[0] = -1;
pair[1] = -1;
return 0;
} else if(ret+(ssize_t)bcount != sizeof(nonce)) {
bcount += ret;
if((size_t)bcount < sizeof(nonce))
continue;
}
break;
}
if(memcmp(nonce, recvnonce, sizeof(nonce)) != 0) {
log_err("create socketpair: recv wrong nonce");
sock_close(pair[0]);
sock_close(pair[1]);
pair[0] = -1;
pair[1] = -1;
return 0;
}
#endif
return 1;
}
/** fast reload thread. setup the thread info */
static int
fast_reload_thread_setup(struct worker* worker, int fr_verb, int fr_nopause,
+45
View File
@@ -1,3 +1,48 @@
11 August 2026: Wouter
- Fix #1492 from zacek: Data race in log_init() on
key_created/log_lock when calling ub_ctx_create()
concurrently from multiple threads.
- Fix stat_values.tdir test to have less test failures.
7 August 2026: Wouter
- Fix #1489 from jplesnik: Replace removed Python 2 C API
macros for SWIG 4.5.0 compatibility.
6 August 2026: Alex Khanin
- Fix #1488: bounds check in packed_rr_to_string, it checked
the assembled rr length against the output string length
dest_len, instead of against the size of the rr buffer it
writes into. Callers in cachedump.c and remote.c pass a
dest_len larger than that buffer.
- Unit test for packed_rr_to_string.
6 August 2026: Wouter
- Fix #1485: the list_forwards command omits port numbers.
The list_forwards and list_stubs commands for
unbound-control print port and tls auth name.
- Fix #1487: regression in 1.26.0, ipsecmod is now always
partly enabled.
4 August 2026: Wouter
- Fix to set makedist.sh to not wget config.sub and
config.guess from git repo. The fetch times out, and the
version from libtoolize is much more recent now than
that it was when the wget was added.
31 July 2026: Wouter
- For #1483: The failure reason when an NSEC NXDOMAIN is
encountered when looking for an insecure delegation, is
fixed to mention the NSEC records, instead of nonexistent
NSEC3 records, that it attempted.
30 July 2026: Wouter
- Fix #1482: DNS-over-QUIC doesn't work with simple config.
That fixes interface-automatic for use with doq service.
28 July 2026: Wouter
- Tag for 1.26.0rc1. The repo continues with version 1.26.1.
This became 1.26.0 on 4 aug 2026.
24 July 2026: Wouter
- Merge #1433 from jisakiel: Add new static zone type
block_aaaa to suppress AAAA queries.
+7 -3
View File
@@ -59,6 +59,11 @@
static int
ipsecmod_apply_cfg(struct ipsecmod_env* ipsecmod_env, struct config_file* cfg)
{
if(cfg->ipsecmod_whitelist &&
!ipsecmod_whitelist_apply_cfg(ipsecmod_env, cfg))
return 0;
if(!cfg->ipsecmod_enabled)
return 1;
if(!cfg->ipsecmod_hook || (cfg->ipsecmod_hook && !cfg->ipsecmod_hook[0])) {
log_err("ipsecmod: missing ipsecmod-hook.");
return 0;
@@ -68,9 +73,6 @@ ipsecmod_apply_cfg(struct ipsecmod_env* ipsecmod_env, struct config_file* cfg)
cfg->ipsecmod_hook, strerror(errno));
return 0;
}
if(cfg->ipsecmod_whitelist &&
!ipsecmod_whitelist_apply_cfg(ipsecmod_env, cfg))
return 0;
return 1;
}
@@ -626,6 +628,8 @@ ipsecmod_inform_super(struct module_qstate* qstate, int id,
verbose(VERB_ALGO, "super has no ipsecmod state");
return;
}
if(!siq->enabled)
return;
if(qstate->return_msg) {
struct ub_packed_rrset_key* rrset_key = reply_find_answer_rrset(
+49
View File
@@ -0,0 +1,49 @@
/*
* libunbound/authload.h - prototypes for auth load methods.
*
* Copyright (c) 2026, NLnet Labs. All rights reserved.
*
* This software is open source.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* Neither the name of the NLNET LABS nor the names of its contributors may
* be used to endorse or promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
* TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/**
* \file
*
* This file declares the methods that must be implemented to use the
* auth load service.
*/
#ifndef LIBUNBOUND_AUTHLOAD_H
#define LIBUNBOUND_AUTHLOAD_H
/** The worker routine that services the auth load connection. */
void worker_auth_load_service_cb(int fd, short bits, void* arg);
#endif /* LIBUNBOUND_AUTHLOAD_H */
+3
View File
@@ -608,6 +608,8 @@ rm -rf .git .travis.yml .gitattributes .github .gitignore || error_cleanup "Fail
info "Adding libtool utils (libtoolize)."
libtoolize -c --install || libtoolize -c || error_cleanup "Libtoolize failed."
# Turn this off, if the git repo times out for lookups.
if test "updateconfigsub" = "false"; then
# https://www.gnu.org/software/gettext/manual/html_node/config_002eguess.html
info "Updating config.guess and config.sub"
wget -O config.guess 'https://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.guess;hb=HEAD'
@@ -621,6 +623,7 @@ if [ `uname -s | grep -i -c darwin` -ne 0 ]; then
xattr -d com.apple.quarantine config.sub
fi
fi
fi
info "Building configure script (autoreconf)."
autoreconf -f || error_cleanup "Autoconf failed."
+14 -14
View File
@@ -79,7 +79,7 @@
i+(int)((unsigned int)name[i]) < len) {
memmove(buf, name + i + 1, (unsigned int)name[i]);
buf[(unsigned int)name[i]] = 0;
PyList_SetItem(list, cnt, PyString_FromString(buf));
PyList_SetItem(list, cnt, PyUnicode_FromString(buf));
}
i += ((unsigned int)name[i]) + 1;
cnt++;
@@ -96,7 +96,7 @@
list = PyList_New(len);
for (i=0; i < len; i++) {
PyList_SET_ITEM(list, i, PyString_FromString(array[i]));
PyList_SET_ITEM(list, i, PyUnicode_FromString(array[i]));
}
return list;
}
@@ -207,7 +207,7 @@ struct query_info {
char buf[LDNS_MAX_DOMAINLEN];
buf[0] = '\0';
dname_str((uint8_t*)PyBytes_AsString(dname), buf);
return PyString_FromString(buf);
return PyUnicode_FromString(buf);
}
%}
@@ -345,7 +345,7 @@ struct packed_rrset_data {
PyObject* _get_data_rr_len(struct packed_rrset_data* d, int idx) {
if ((d != NULL) && (idx >= 0) &&
((size_t)idx < (d->count+d->rrsig_count)))
return PyInt_FromLong(d->rr_len[idx]);
return PyLong_FromLong(d->rr_len[idx]);
return Py_None;
}
void _set_data_rr_ttl(struct packed_rrset_data* d, int idx, uint32_t ttl)
@@ -357,7 +357,7 @@ struct packed_rrset_data {
PyObject* _get_data_rr_ttl(struct packed_rrset_data* d, int idx) {
if ((d != NULL) && (idx >= 0) &&
((size_t)idx < (d->count+d->rrsig_count)))
return PyInt_FromLong(d->rr_ttl[idx]);
return PyLong_FromLong(d->rr_ttl[idx]);
return Py_None;
}
PyObject* _get_data_rr_data(struct packed_rrset_data* d, int idx) {
@@ -555,12 +555,12 @@ struct sockaddr_storage {};
if (ss->ss_family == AF_INET) {
const struct sockaddr_in *sa4 = (struct sockaddr_in *)ss;
return PyInt_FromLong(ntohs(sa4->sin_port));
return PyLong_FromLong(ntohs(sa4->sin_port));
}
if (ss->ss_family == AF_INET6) {
const struct sockaddr_in6 *sa6 = (struct sockaddr_in6 *)ss;
return PyInt_FromLong(ntohs(sa6->sin6_port));
return PyLong_FromLong(ntohs(sa6->sin6_port));
}
return Py_None;
@@ -574,7 +574,7 @@ struct sockaddr_storage {};
}
sa6 = (struct sockaddr_in6 *)ss;
return PyInt_FromLong(ntohl(sa6->sin6_flowinfo));
return PyLong_FromLong(ntohl(sa6->sin6_flowinfo));
}
PyObject *_sockaddr_storage_scope_id(const struct sockaddr_storage *ss) {
@@ -585,7 +585,7 @@ struct sockaddr_storage {};
}
sa6 = (struct sockaddr_in6 *)ss;
return PyInt_FromLong(ntohl(sa6->sin6_scope_id));
return PyLong_FromLong(ntohl(sa6->sin6_scope_id));
}
%}
@@ -661,7 +661,7 @@ struct edns_option {
%inline %{
PyObject* _edns_option_opt_code_get(struct edns_option* option) {
uint16_t opt_code = option->opt_code;
return PyInt_FromLong(opt_code);
return PyLong_FromLong(opt_code);
}
PyObject* _edns_option_opt_data_get(struct edns_option* option) {
@@ -1627,7 +1627,7 @@ int edns_opt_list_append(struct edns_option** list, uint16_t code, size_t len,
}
result = PyObject_Call(func, py_args, py_kwargs);
if (result) {
res = PyInt_AsLong(result);
res = PyLong_AsLong(result);
}
out:
Py_XDECREF(py_edns);
@@ -1711,7 +1711,7 @@ out:
}
result = PyObject_Call(func, py_args, py_kwargs);
if (result) {
res = PyInt_AsLong(result);
res = PyLong_AsLong(result);
}
out:
Py_XDECREF(py_qinfo);
@@ -1765,7 +1765,7 @@ out:
}
result = PyObject_Call(func, py_args, py_kwargs);
if (result) {
res = PyInt_AsLong(result);
res = PyLong_AsLong(result);
}
out:
Py_XDECREF(py_qstate);
@@ -1814,7 +1814,7 @@ out:
}
result = PyObject_Call(func, py_args, py_kwargs);
if (result) {
res = PyInt_AsLong(result);
res = PyLong_AsLong(result);
}
out:
Py_XDECREF(py_qstate);
+4 -4
View File
@@ -246,14 +246,14 @@ log_py_err(void)
}
/* And it should be a string all ready to go - duplicate it. */
if (!PyString_Check(obResult) && !PyUnicode_Check(obResult)) {
if (!PyBytes_Check(obResult) && !PyUnicode_Check(obResult)) {
log_err("pythonmod: cannot print exception, "
"StringIO.getvalue() result did not String_Check"
" or Unicode_Check");
goto cleanup;
}
if(PyString_Check(obResult)) {
result = PyString_AsString(obResult);
if(PyBytes_Check(obResult)) {
result = PyBytes_AsString(obResult);
} else {
ascstr = PyUnicode_AsASCIIString(obResult);
result = PyBytes_AsString(ascstr);
@@ -450,7 +450,7 @@ int pythonmod_init(struct module_env* env, int id)
pe->data = PyDict_New();
/* add the script filename to the global "mod_env" for trivial access */
fname = PyString_FromString(pe->fname);
fname = PyUnicode_FromString(pe->fname);
if(PyDict_SetItemString(pe->data, "script", fname) < 0) {
log_err("pythonmod: could not add item to dictionary");
Py_XDECREF(fname);
+921
View File
@@ -0,0 +1,921 @@
/*
* services/authload.c - authoritative zone load thread
*
* Copyright (c) 2026, NLnet Labs. All rights reserved.
*
* This software is open source.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* Neither the name of the NLNET LABS nor the names of its contributors may
* be used to endorse or promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
* TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/**
* \file
*
* This file contains the auth load thread. This loads authority zone
* and RPZ zone information in a thread, in a separate memory structure.
* When it is done, the information is swapped over to the running server.
*/
#include "config.h"
#include "services/authload.h"
#include "daemon/worker.h"
#include "daemon/daemon.h"
#include "services/authzone.h"
#include "libunbound/authload.h"
#include "util/net_help.h"
#include "util/log.h"
#include "util/ub_event.h"
#include "util/timeval_func.h"
#include "util/data/dname.h"
/** Get memory use of buffer. */
static size_t
buffer_get_mem(struct sldns_buffer* buf)
{
if(!buf) return 0;
return sizeof(*buf) + (buf->_data?buf->_capacity:0);
}
/** Auth load notification to string, for descriptive purposes. */
static const char*
auth_load_notification_to_string(enum auth_load_notification_type status)
{
switch(status) {
case auth_load_notification_exit:
return "auth_load_notification_exit";
default:
break;
}
return "unknown_auth_load_notification_value";
}
/** delete chunks */
static void
auth_chunk_list_delete(struct auth_chunk* first)
{
struct auth_chunk* c = first, *cn;
while(c) {
cn = c->next;
free(c->data);
free(c);
c = cn;
}
}
/** Delete auth load task item */
static void
auth_load_task_delete(struct auth_load_task* task)
{
if(!task)
return;
free(task->name);
free(task->host);
free(task->file);
auth_chunk_list_delete(task->chunks_first);
free(task);
}
/** Create new auth load task item */
static struct auth_load_task*
auth_load_task_create(void)
{
struct auth_load_task* task = (struct auth_load_task*)calloc(1,
sizeof(*task));
return task;
}
/** Pick up the work content of task transfer of auth xfr */
static int
auth_load_task_pickup_xfr(struct auth_load_task* task, struct auth_xfer* xfr)
{
task->name = memdup(xfr->name, xfr->namelen);
if(!task->name)
return 0;
task->namelen = xfr->namelen;
task->dclass = xfr->dclass;
if(xfr->task_transfer->master && xfr->task_transfer->master->host) {
task->host = strdup(xfr->task_transfer->master->host);
if(!task->host)
return 0;
}
if(xfr->task_transfer->master && xfr->task_transfer->master->file) {
task->file = strdup(xfr->task_transfer->master->file);
if(!task->file)
return 0;
}
if(xfr->task_transfer->master)
task->on_http = xfr->task_transfer->master->http;
task->on_ixfr = xfr->task_transfer->on_ixfr;
task->on_ixfr_is_axfr = xfr->task_transfer->on_ixfr_is_axfr;
task->serial = xfr->serial;
if(xfr->task_transfer->chunks_first) {
task->chunks_first = xfr->task_transfer->chunks_first;
task->chunks_last = xfr->task_transfer->chunks_last;
task->chunks_total = xfr->task_transfer->chunks_total;
/* The task now has the chunks. Remove them from the
* xfr structure. */
xfr->task_transfer->chunks_first = 0;
xfr->task_transfer->chunks_last = 0;
xfr->task_transfer->chunks_total = 0;
}
if(task->on_http)
task->task_type = AUTH_LOAD_TASK_HTTPCHUNKS;
else task->task_type = AUTH_LOAD_TASK_TRANSFER;
return 1;
}
/** Create xfr task */
static struct auth_load_task*
auth_load_task_create_xfr(struct auth_xfer* xfr, struct worker* worker)
{
struct auth_load_task* task = auth_load_task_create();
if(!task) {
log_err("out of memory");
return 0;
}
task->worker = worker;
if(!auth_load_task_pickup_xfr(task, xfr)) {
log_err("out of memory");
auth_load_task_delete(task);
return 0;
}
return task;
}
int
auth_load_thread_poll_for_quit(struct auth_load_thread* thr)
{
int inevent, loopexit = 0;
uint8_t cmd;
ssize_t ret;
if(!thr)
return 0;
if(thr->need_to_quit)
return 1;
/* Is there data? */
if(!sock_poll_timeout(thr->commpair[1], 0, 1, 0, &inevent)) {
log_err("auth_load_thread_poll_for_quit: poll failed");
return 0;
}
if(!inevent)
return 0;
/* Read the data */
while(1) {
if(++loopexit > 200) {
log_err("auth_load_thread_poll_for_quit: recv loops %s",
sock_strerror(errno));
return 0;
}
ret = recv(thr->commpair[1], ((char*)&cmd), sizeof(cmd), 0);
if(ret == -1) {
if(
#ifndef USE_WINSOCK
errno == EINTR || errno == EAGAIN
# ifdef EWOULDBLOCK
|| errno == EWOULDBLOCK
# endif
#else
WSAGetLastError() == WSAEINTR ||
WSAGetLastError() == WSAEINPROGRESS ||
WSAGetLastError() == WSAEWOULDBLOCK
#endif
)
continue; /* Try again. */
log_err("auth_load_thread_poll_for_quit: recv: %s",
sock_strerror(errno));
return 0;
} else if(ret == 0) {
log_err("auth_load_thread_poll_for_quit: recv: EOF");
return 0;
}
break;
}
if(cmd == auth_load_notification_exit) {
thr->need_to_quit = 1;
verbose(VERB_ALGO, "auth load: exit notification received");
return 1;
}
log_err("auth_load_thread_poll_for_quit: unknown notification status "
"received: %d %s", cmd, auth_load_notification_to_string(cmd));
return 0;
}
/** Signal the worker connected to an auth load thread the status */
static void
auth_load_thread_signal_worker(struct auth_load_thread* thr, int status)
{
int outevent, loopexit = 0;
ssize_t ret;
uint8_t to_send;
verbose(VERB_ALGO, "auth load thread: send status %d", status);
/* Make a blocking attempt to send. But meanwhile stay responsive,
* once in a while for quit commands. In case the server has to quit. */
/* see if there is incoming quit signals */
if(auth_load_thread_poll_for_quit(thr))
return;
to_send = (uint8_t)status;
while(1) {
if(++loopexit > 200) {
log_err("auth load thread: could not send status");
return;
}
/* wait for socket to become writable */
if(!sock_poll_timeout(thr->commpair[1],
200, /* msec wait before check for quit, and loop to
wait again. */
0, 1, &outevent)) {
log_err("auth load thread: poll failed");
return;
}
if(auth_load_thread_poll_for_quit(thr))
return;
if(!outevent)
continue;
ret = send(thr->commpair[1], &to_send, 1, 0);
if(ret == -1) {
if(
#ifndef USE_WINSOCK
errno == EINTR || errno == EAGAIN
# ifdef EWOULDBLOCK
|| errno == EWOULDBLOCK
# endif
#else
WSAGetLastError() == WSAEINTR ||
WSAGetLastError() == WSAEINPROGRESS ||
WSAGetLastError() == WSAEWOULDBLOCK
#endif
)
continue; /* Try again. */
log_err("auth load thread signal worker: send: %s",
sock_strerror(errno));
return;
} else if(ret < 1) {
continue;
}
break;
}
}
/** Create proxy auth zone structure, that is used to hold the data
* that is processed. */
static struct auth_zone*
auth_zone_create_proxy(uint8_t* nm, size_t nmlen, uint16_t dclass)
{
struct auth_zone* z = (struct auth_zone*)calloc(1, sizeof(*z));
if(!z) {
return NULL;
}
z->node.key = z;
z->dclass = dclass;
z->namelen = nmlen;
z->namelabs = dname_count_labels(nm);
z->name = memdup(nm, nmlen);
if(!z->name) {
free(z);
return NULL;
}
rbtree_init(&z->data, &auth_data_cmp);
return z;
}
/** Delete proxy auth zone structure */
static void
auth_zone_delete_proxy(struct auth_zone* z)
{
if(!z)
return;
traverse_postorder(&z->data, auth_data_del, NULL);
if(z->rpz)
rpz_delete(z->rpz);
free(z->name);
free(z);
}
/** Calculate memory use of the authload thread for this task.
* The size of the task struct, with the data chunks, and the proxy auth zone
* structure that is created while the other auth zone is used for queries,
* and other added memory.
*/
static void
auth_load_calc_mem(struct auth_load_task* task, struct auth_zone* z,
size_t other)
{
size_t m = 0;
if(verbosity < 8) {
task->mem_used = 0;
return;
}
m += other;
m += sizeof(*task);
m += task->namelen;
m += getmem_str(task->host);
m += getmem_str(task->file);
m += task->chunks_total;
m += auth_zone_get_mem(z);
task->mem_used = m;
}
/** Swap the final zone contents with the live zone */
static void
auth_load_swap_zone(struct auth_load_thread* thr, struct auth_zone* proxyz)
{
rbtree_type data;
struct rpz* rpz;
struct auth_zone* z;
lock_rw_rdlock(&thr->task->worker->env.auth_zones->lock);
z = auth_zone_find(thr->task->worker->env.auth_zones,
thr->task->name, thr->task->namelen, thr->task->dclass);
if(!z) {
lock_rw_unlock(&thr->task->worker->env.auth_zones->lock);
verbose(VERB_ALGO, "auth zone missing after auth load.");
return;
}
lock_rw_wrlock(&z->lock);
lock_rw_unlock(&thr->task->worker->env.auth_zones->lock);
data = proxyz->data;
proxyz->data = z->data;
z->data = data;
rpz = proxyz->rpz;
proxyz->rpz = z->rpz;
z->rpz = rpz;
lock_rw_unlock(&z->lock);
}
/** Process http transfer */
static int
auth_load_process_http(struct auth_load_thread* thr)
{
struct auth_load_task* task = thr->task;
struct sldns_buffer* scratch_buffer;
struct auth_zone* z;
size_t scratch_mem;
scratch_buffer = sldns_buffer_new(sldns_buffer_capacity(
thr->task->worker->env.scratch_buffer));
if(!scratch_buffer) {
log_err("out of memory");
return 0;
}
scratch_mem = buffer_get_mem(scratch_buffer);
z = auth_zone_create_proxy(task->name, task->namelen, task->dclass);
if(!z) {
log_err("out of memory");
sldns_buffer_free(scratch_buffer);
return 0;
}
if(auth_load_thread_poll_for_quit(thr)) {
sldns_buffer_free(scratch_buffer);
auth_zone_delete_proxy(z);
return 0;
}
xfr_http_preview(task->file, task->chunks_first);
if(!xfr_http_syntax_check(task->name, task->namelen, task->dclass,
task->host, task->file, task->chunks_first, scratch_buffer)) {
sldns_buffer_free(scratch_buffer);
auth_zone_delete_proxy(z);
return 0;
}
if(auth_load_thread_poll_for_quit(thr)) {
sldns_buffer_free(scratch_buffer);
auth_zone_delete_proxy(z);
return 0;
}
if(!xfr_apply_http(task->name, task->namelen, task->host, task->file,
task->chunks_first, z, scratch_buffer, thr)) {
sldns_buffer_free(scratch_buffer);
auth_zone_delete_proxy(z);
return 0;
}
sldns_buffer_free(scratch_buffer);
if(z->rpz)
rpz_finish_config(z->rpz);
if(auth_load_thread_poll_for_quit(thr)) {
auth_zone_delete_proxy(z);
return 0;
}
auth_load_calc_mem(task, z, scratch_mem);
auth_load_swap_zone(thr, z);
auth_zone_delete_proxy(z);
return 1;
}
/** Copy RRset and append it to the domain, update last pointer. */
static int
rrset_append_copy(struct auth_data* domain, struct auth_rrset* rrset,
struct auth_rrset** last)
{
struct auth_rrset* s = calloc(1, sizeof(*s));
if(!s)
return 0;
s->type = rrset->type;
s->data = (struct packed_rrset_data*)memdup(rrset->data,
packed_rrset_sizeof(rrset->data));
if(!s->data) {
free(s);
return 0;
}
packed_rrset_ptr_fixup(s->data);
if(!*last)
domain->rrsets = s;
else (*last)->next = s;
*last = s;
return 1;
}
/** Copy the existing zone for modification */
static int
auth_load_copy_into_zone(struct auth_load_thread* thr, struct auth_zone* proxyz)
{
int count = 0;
struct auth_zone* z;
struct auth_data* d;
lock_rw_rdlock(&thr->task->worker->env.auth_zones->lock);
z = auth_zone_find(thr->task->worker->env.auth_zones,
thr->task->name, thr->task->namelen, thr->task->dclass);
if(!z) {
lock_rw_unlock(&thr->task->worker->env.auth_zones->lock);
verbose(VERB_ALGO, "auth zone missing for copy for IXFR.");
return 0;
}
lock_rw_rdlock(&z->lock);
lock_rw_unlock(&thr->task->worker->env.auth_zones->lock);
/* Copy from z into proxyz. */
RBTREE_FOR(d, struct auth_data*, &z->data) {
struct auth_rrset* rrset, *last = NULL;
struct auth_data* proxy_d = az_domain_create(proxyz,
d->name, d->namelen);
if(!proxy_d) {
log_err("out of memory");
lock_rw_unlock(&z->lock);
return 0;
}
for(rrset = d->rrsets; rrset; rrset=rrset->next) {
if(!rrset_append_copy(proxy_d, rrset, &last)) {
log_err("out of memory");
lock_rw_unlock(&z->lock);
return 0;
}
if((count++)%10000 == 0) {
if(auth_load_thread_poll_for_quit(thr)) {
lock_rw_unlock(&z->lock);
return 0;
}
}
}
if((count++)%10000 == 0) {
if(auth_load_thread_poll_for_quit(thr)) {
lock_rw_unlock(&z->lock);
return 0;
}
}
}
lock_rw_unlock(&z->lock);
return 1;
}
/** Process ixfr transfer */
static int
auth_load_process_ixfr(struct auth_load_thread* thr)
{
struct auth_load_task* task = thr->task;
struct sldns_buffer* scratch_buffer;
struct auth_zone* z;
size_t scratch_mem;
scratch_buffer = sldns_buffer_new(sldns_buffer_capacity(
thr->task->worker->env.scratch_buffer));
if(!scratch_buffer) {
log_err("out of memory");
return 0;
}
scratch_mem = buffer_get_mem(scratch_buffer);
z = auth_zone_create_proxy(task->name, task->namelen, task->dclass);
if(!z) {
log_err("out of memory");
sldns_buffer_free(scratch_buffer);
return 0;
}
if(auth_load_thread_poll_for_quit(thr)) {
sldns_buffer_free(scratch_buffer);
auth_zone_delete_proxy(z);
return 0;
}
/* Copy the existing zone for modification, that uses a read lock.
* That then does not interrupt the service of threads. */
if(!auth_load_copy_into_zone(thr, z)) {
sldns_buffer_free(scratch_buffer);
auth_zone_delete_proxy(z);
return 0;
}
if(!xfr_apply_ixfr(task->chunks_first, task->serial, z,
scratch_buffer, thr)) {
sldns_buffer_free(scratch_buffer);
auth_zone_delete_proxy(z);
return 0;
}
sldns_buffer_free(scratch_buffer);
if(auth_load_thread_poll_for_quit(thr)) {
auth_zone_delete_proxy(z);
return 0;
}
auth_load_calc_mem(task, z, scratch_mem);
auth_load_swap_zone(thr, z);
auth_zone_delete_proxy(z);
return 1;
}
/** Process axfr transfer */
static int
auth_load_process_axfr(struct auth_load_thread* thr)
{
struct auth_load_task* task = thr->task;
struct sldns_buffer* scratch_buffer;
struct auth_zone* z;
size_t scratch_mem;
scratch_buffer = sldns_buffer_new(sldns_buffer_capacity(
thr->task->worker->env.scratch_buffer));
if(!scratch_buffer) {
log_err("out of memory");
return 0;
}
scratch_mem = buffer_get_mem(scratch_buffer);
z = auth_zone_create_proxy(task->name, task->namelen, task->dclass);
if(!z) {
log_err("out of memory");
sldns_buffer_free(scratch_buffer);
return 0;
}
if(auth_load_thread_poll_for_quit(thr)) {
sldns_buffer_free(scratch_buffer);
auth_zone_delete_proxy(z);
return 0;
}
if(!xfr_apply_axfr(task->chunks_first, z, scratch_buffer, thr)) {
sldns_buffer_free(scratch_buffer);
auth_zone_delete_proxy(z);
return 0;
}
sldns_buffer_free(scratch_buffer);
if(auth_load_thread_poll_for_quit(thr)) {
auth_zone_delete_proxy(z);
return 0;
}
auth_load_calc_mem(task, z, scratch_mem);
auth_load_swap_zone(thr, z);
auth_zone_delete_proxy(z);
return 1;
}
/** In the auth load thread, process the task */
static int
auth_load_thread_process(struct auth_load_thread* thr)
{
struct auth_load_task* task = thr->task;
struct timeval start, end;
if(gettimeofday(&start, NULL) < 0)
log_err("gettimeofday: %s", strerror(errno));
/* apply data */
if(task->on_http) {
if(!auth_load_process_http(thr))
return 0;
} else if(task->on_ixfr && !task->on_ixfr_is_axfr) {
if(!auth_load_process_ixfr(thr))
return 0;
} else {
if(!auth_load_process_axfr(thr))
return 0;
}
if(gettimeofday(&end, NULL) < 0)
log_err("gettimeofday: %s", strerror(errno));
timeval_subtract(&thr->task->time_taken, &end, &start);
return 1;
}
/** The auth load thread. The thread main function. */
static void*
auth_load_thread_main(void* arg)
{
struct auth_load_thread* thr = (struct auth_load_thread*)arg;
int s;
const char name[16] = "unbound/authld"; /* seems to be the safest size
between different OSes */
#if defined(HAVE_GETTID) && !defined(THREADS_DISABLED)
thr->thread_tid = gettid();
if(thr->thread_tid_log)
log_thread_set(&thr->thread_tid);
else
#endif
log_thread_set(&thr->threadnum);
ub_thread_setname(ub_thread_self(), name);
(void)name; /* When setname is not defined, ignore the name variable. */
verbose(VERB_ALGO, "start auth load thread");
s = auth_load_thread_process(thr);
/* The result is sent to the worker, that reaps the thread. */
auth_load_thread_signal_worker(thr, s);
verbose(VERB_ALGO, "stop auth load thread");
return NULL;
}
/** Delete auth load thread structure */
static void
auth_load_thread_delete(struct auth_load_thread* thr)
{
if(!thr)
return;
if(thr->service_event && thr->service_event_is_added) {
ub_event_del(thr->service_event);
thr->service_event_is_added = 0;
}
if(thr->service_event)
ub_event_free(thr->service_event);
if(thr->commpair[0] != -1)
sock_close(thr->commpair[0]);
if(thr->commpair[1] != -1)
sock_close(thr->commpair[1]);
auth_load_task_delete(thr->task);
free(thr);
}
/** Create auth load thread structure */
static struct auth_load_thread*
auth_load_thread_create(struct auth_load_task* task)
{
int numworkers;
struct auth_load_thread* thr = (struct auth_load_thread*)calloc(1,
sizeof(*thr));
if(!thr)
return NULL;
numworkers = task->worker->daemon->num;
/* This number is printed into the logs */
thr->threadnum = numworkers+3;
thr->task = task;
thr->commpair[0] = -1;
thr->commpair[1] = -1;
if(!create_socketpair(thr->commpair, task->worker->daemon->rand)) {
auth_load_thread_delete(thr);
return NULL;
}
#ifdef HAVE_GETTID
thr->thread_tid_log = task->worker->env.cfg->log_thread_id;
#endif
return thr;
}
/** The worker routine that services the auth load connection. */
void
worker_auth_load_service_cb(int ATTR_UNUSED(fd), short ATTR_UNUSED(bits),
void* arg)
{
struct auth_load_thread* thr = (struct auth_load_thread*)arg;
uint8_t recv_item;
ssize_t ret;
struct auth_xfer* xfr;
struct auth_chunk* chunk_list;
struct module_env* env = &thr->task->worker->env;
int ixfr_fail;
struct timeval time_taken;
size_t mem_used, chunks_total;
log_assert(thr->commpair[0] >= 0);
ret = recv(thr->commpair[0], &recv_item, 1, 0);
if(ret == -1) {
if(
#ifndef USE_WINSOCK
errno == EINTR || errno == EAGAIN
# ifdef EWOULDBLOCK
|| errno == EWOULDBLOCK
# endif
#else
WSAGetLastError() == WSAEINTR ||
WSAGetLastError() == WSAEINPROGRESS
#endif
)
return; /* Continue later. */
#ifdef USE_WINSOCK
if(WSAGetLastError() == WSAEWOULDBLOCK) {
ub_winsock_tcp_wouldblock(thr->service_event,
UB_EV_READ);
return; /* Continue later. */
}
#endif
log_err("read status from auth load thread, recv: %s",
sock_strerror(errno));
return;
} else if(ret == 0) {
verbose(VERB_ALGO, "closed connection from auth load thread");
/* handle this like an error */
recv_item = 0;
/* ret<1: No short read on 1 byte, to continue later on */
}
/* Deal with the result of auth load thread */
verbose(VERB_ALGO, "auth load status is %d", (int)recv_item);
verbose(VERB_ALGO, "join with auth load thread");
ub_thread_join(thr->tid);
verbose(VERB_ALGO, "joined with auth load thread");
lock_rw_rdlock(&thr->task->worker->env.auth_zones->lock);
xfr = auth_xfer_find(thr->task->worker->env.auth_zones,
thr->task->name, thr->task->namelen, thr->task->dclass);
if(!xfr) {
lock_rw_unlock(&thr->task->worker->env.auth_zones->lock);
verbose(VERB_ALGO, "auth load: xfr is gone");
auth_load_thread_delete(thr);
auth_load_info_release_thread(env);
return;
}
lock_basic_lock(&xfr->lock);
lock_rw_unlock(&thr->task->worker->env.auth_zones->lock);
ixfr_fail = thr->task->ixfr_fail;
time_taken = thr->task->time_taken;
mem_used = thr->task->mem_used;
chunks_total = thr->task->chunks_total;
if(thr->task->on_http) {
chunk_list = thr->task->chunks_first;
thr->task->chunks_first = NULL;
thr->task->chunks_last = NULL;
thr->task->chunks_total = 0;
} else {
chunk_list = NULL;
}
auth_load_thread_delete(thr);
auth_load_info_release_thread(env);
xfr_process_load_end_transfer(xfr, env, recv_item, ixfr_fail,
&time_taken, mem_used, chunks_total, chunk_list);
}
/** Attach worker to the auth load thread. */
static int
auth_load_thread_attach(struct auth_load_thread* thr, struct worker* worker)
{
/* Setup listener in worker, that connects via a pipe to the
* auth load thread.
* The listener has to be nonblocking, so the the remote servicing
* thread can continue to service DNS queries.
* The commpair[1] element can stay blocking, it is used by the
* auth load thread. The thread needs to wait at these times, when
* it has to check briefly it can use poll. */
verbose(VERB_ALGO, "auth_load_thread_attach");
fd_set_nonblock(thr->commpair[0]);
if(!comm_base_internal(worker->base)) {
verbose(VERB_ALGO, "auth load thread: no event base");
return 0;
}
thr->service_event = ub_event_new(comm_base_internal(worker->base),
thr->commpair[0], UB_EV_READ | UB_EV_PERSIST,
worker_auth_load_service_cb, thr);
if(!thr->service_event) {
log_err("out of memory");
return 0;
}
if(ub_event_add(thr->service_event, NULL) != 0) {
log_err("out of memory");
return 0;
}
thr->service_event_is_added = 1;
return 1;
}
/** Create and start the auth load thread, with the task */
static int
auth_load_start_thread(struct auth_load_task* task)
{
struct auth_load_thread* thr = auth_load_thread_create(task);
if(!thr) {
log_err("out of memory");
auth_load_task_delete(task);
return 0;
}
if(!auth_load_thread_attach(thr, task->worker)) {
log_err("out of memory");
auth_load_thread_delete(thr);
return 0;
}
/* Start auth load thread */
ub_thread_create(&thr->tid, auth_load_thread_main, thr);
return 1;
}
int auth_load_add_task_xfr(struct auth_xfer* xfr, struct worker* worker)
{
struct auth_load_task* task;
int can_run = 0;
verbose(VERB_ALGO, "auth load add task");
/* Check auth load count */
can_run = 1;
/* Create new thread */
task = auth_load_task_create_xfr(xfr, worker);
if(!task)
return 0;
if(can_run) {
verbose(VERB_ALGO, "auth load start thread");
if(!auth_load_start_thread(task))
return 0;
verbose(VERB_ALGO, "auth load thread started");
return 1;
}
/* Make wait item */
return 0;
}
struct auth_load_general_info* auth_load_info_create(void)
{
struct auth_load_general_info* auth_load_info =
(struct auth_load_general_info*)calloc(1,
sizeof(*auth_load_info));
if(!auth_load_info) {
log_err("malloc failure");
return NULL;
}
lock_basic_init(&auth_load_info->lock);
lock_protect(&auth_load_info->lock,
&auth_load_info->num_auth_load_threads,
sizeof(auth_load_info->num_auth_load_threads));
return auth_load_info;
}
void auth_load_info_delete(struct auth_load_general_info* auth_load_info)
{
if(!auth_load_info)
return;
lock_basic_destroy(&auth_load_info->lock);
free(auth_load_info);
}
int auth_load_info_grab_thread(struct module_env* env)
{
struct auth_load_general_info* auth_load_info =
env->worker->daemon->auth_load_info;
struct config_file* cfg = env->cfg;
int ret = 0;
lock_basic_lock(&auth_load_info->lock);
if(auth_load_info->num_auth_load_threads < cfg->auth_task_threads) {
ret = 1;
auth_load_info->num_auth_load_threads++;
}
lock_basic_unlock(&auth_load_info->lock);
return ret;
}
void auth_load_info_release_thread(struct module_env* env)
{
struct auth_load_general_info* auth_load_info =
env->worker->daemon->auth_load_info;
lock_basic_lock(&auth_load_info->lock);
if(auth_load_info->num_auth_load_threads == 0) {
verbose(VERB_ALGO, "release of auth load thread, but "
"num_auth_load_threads not > 0.");
} else {
auth_load_info->num_auth_load_threads--;
}
lock_basic_unlock(&auth_load_info->lock);
}
+203
View File
@@ -0,0 +1,203 @@
/*
* services/authload.h - authoritative zone load thread
*
* Copyright (c) 2026, NLnet Labs. All rights reserved.
*
* This software is open source.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* Neither the name of the NLNET LABS nor the names of its contributors may
* be used to endorse or promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
* TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/**
* \file
*
* This file contains the auth load thread. This loads authority zone
* and RPZ zone information in a thread, in a separate memory structure.
* When it is done, the information is swapped over to the running server.
*/
#ifndef SERVICES_AUTHLOAD_H
#define SERVICES_AUTHLOAD_H
#include "util/locks.h"
struct worker;
struct auth_xfer;
struct module_env;
struct auth_load_task;
/**
* General information for auth load threads. The number of active threads.
*/
struct auth_load_general_info {
/** lock on this structure */
lock_basic_type lock;
/** The number of active auth load threads. */
int num_auth_load_threads;
};
/**
* The types of notifications that the auth load thread sends around.
*/
enum auth_load_notification_type {
/** This is sent to make the auth load thread perform exit */
auth_load_notification_exit
};
/**
* The auth load thread. The thread runs to load authority zone information
* and RPZ information into memory. It loads into a copy. Then that is swapped
* over to the running server. This keeps the server responsive while the
* information is loaded.
*/
struct auth_load_thread {
/** the thread number for the thread,
* must be first to cast thread arg to int* in checklock code. */
int threadnum;
/** thread id, of the io thread */
ub_thread_type tid;
#ifdef HAVE_GETTID
/** thread tid, the LWP id */
pid_t thread_tid;
/** if logging should include the LWP id */
int thread_tid_log;
#endif
/** communication socket pair, that sends commands */
int commpair[2];
/** if the thread has to quit */
int need_to_quit;
/** the event that listens on the worker to commpair,
* it receives content from the auth load thread. */
void* service_event;
/** if the event that listens on the worker has
* been added to the comm base. */
int service_event_is_added;
/** the worker that the auth load is connected to */
struct worker* worker;
/** The task that the thread is working on */
struct auth_load_task* task;
};
/**
* The types of tasks that the auth load can perform.
*/
enum auth_load_task_type {
AUTH_LOAD_TASK_TRANSFER,
AUTH_LOAD_TASK_ZONEFILE_READ,
AUTH_LOAD_TASK_ZONEFILE_WRITE,
AUTH_LOAD_TASK_HTTPCHUNKS
};
/**
* The task for the auth load. The task can be to load a zone transfer, AXFR,
* IXFR, from zonefile, and from a http read, from chunks.
*/
struct auth_load_task {
/** The type of the task */
enum auth_load_task_type task_type;
/** The task is connected with this worker */
struct worker* worker;
/** The zone name */
uint8_t* name;
/** The zone namelen */
size_t namelen;
/** The zone class */
uint16_t dclass;
/** name of the host that the transfer comes from. */
char* host;
/** file part of the url that the transfer comes from, or NULL. */
char* file;
/** Set if the host is http transfer, if false it is AXFR or IXFR. */
int on_http;
/** Set if the transfer is doing IXFR */
int on_ixfr;
/** Set if the transfer is an IXFR but we detected an AXFR contents */
int on_ixfr_is_axfr;
/** Set if the ixfr failed. (So that there can be backoff to AXFR). */
int ixfr_fail;
/** current serial (from SOA), if we have no zone, 0
* This is for checking the IXFR result. */
uint32_t serial;
/** the data chunks, or NULL, to process. */
struct auth_chunk* chunks_first;
/** last data chunk */
struct auth_chunk* chunks_last;
/** size of data in data chunks. */
size_t chunks_total;
/** time taken for the task */
struct timeval time_taken;
/** memory used for the task */
size_t mem_used;
};
/**
* Add a new task to be performed by the auth load thread.
* It starts a thread, or makes a wait list item.
* @param xfr: zone transfer to start for.
* @param worker: worker that is connected to the task.
* @return false on failure.
*/
int auth_load_add_task_xfr(struct auth_xfer* xfr, struct worker* worker);
/** See if there is a quit signal, true if so. */
int auth_load_thread_poll_for_quit(struct auth_load_thread* thr);
/**
* Create auth load info structure.
* @return NULL on failure.
*/
struct auth_load_general_info* auth_load_info_create(void);
/**
* Delete auth load info structure.
* @param auth_load_info: to delete.
*/
void auth_load_info_delete(struct auth_load_general_info* auth_load_info);
/**
* Grab a new thread from the auth load count.
* @param env: with auth_load_info with the active thread count.
* and config file, with configured maximum.
* @return false on failure, like too many active, true if successful.
*/
int auth_load_info_grab_thread(struct module_env* env);
/**
* Release thread from auth load count. It is done.
* @param env: with auth_load_info with the active thread count.
*/
void auth_load_info_release_thread(struct module_env* env);
#endif /* SERVICES_AUTHLOAD_H */
+374 -121
View File
@@ -60,6 +60,7 @@
#include "services/outside_network.h"
#include "services/listen_dnsport.h"
#include "services/mesh.h"
#include "services/authload.h"
#include "sldns/rrdef.h"
#include "sldns/pkthdr.h"
#include "sldns/sbuffer.h"
@@ -98,6 +99,10 @@
#define NUM_TIMEOUTS_FALLBACK_IXFR 3
/** number of IXFRs before an AXFR is performed, to consolidate RPZ memory. */
#define NUM_IXFR_BEFORE_AXFR 5
/** number of records before polling if the auth-load-thread has to quit.
* It polls on the socket, once for this number of processed records, so
* it can quit when the signal is sent over the socket. */
#define NUM_RECORDS_BEFORE_SIGNAL_CHECK 10000
/** pick up nextprobe task to start waiting to perform transfer actions */
static void xfr_set_timeout(struct auth_xfer* xfr, struct module_env* env,
@@ -385,7 +390,7 @@ auth_data_delete(struct auth_data* n)
}
/** helper traverse to delete zones */
static void
void
auth_data_del(rbnode_type* n, void* ATTR_UNUSED(arg))
{
struct auth_data* z = (struct auth_data*)n->key;
@@ -597,7 +602,7 @@ auth_zone_set_fallback(struct auth_zone* z, char* fallbackstr)
}
/** create domain with the given name */
static struct auth_data*
struct auth_data*
az_domain_create(struct auth_zone* z, uint8_t* nm, size_t nmlen)
{
struct auth_data* n = (struct auth_data*)malloc(sizeof(*n));
@@ -4739,7 +4744,8 @@ chunkline_non_comment_RR(struct auth_chunk** chunk, size_t* chunk_pos,
* failure and return a string in the scratch buffer (first RR string)
* on failure. */
static int
http_zonefile_syntax_check(struct auth_xfer* xfr, sldns_buffer* buf)
http_zonefile_syntax_check(uint8_t* name, size_t namelen, uint16_t dclass,
struct auth_chunk* chunk_list, struct sldns_buffer* buf)
{
uint8_t rr[LDNS_RR_BUF_SIZE];
size_t rr_len, dname_len = 0;
@@ -4749,11 +4755,11 @@ http_zonefile_syntax_check(struct auth_xfer* xfr, sldns_buffer* buf)
int e;
memset(&pstate, 0, sizeof(pstate));
pstate.default_ttl = 3600;
if(xfr->namelen < sizeof(pstate.origin)) {
pstate.origin_len = xfr->namelen;
memmove(pstate.origin, xfr->name, xfr->namelen);
if(namelen < sizeof(pstate.origin)) {
pstate.origin_len = namelen;
memmove(pstate.origin, name, namelen);
}
chunk = xfr->task_transfer->chunks_first;
chunk = chunk_list;
chunk_pos = 0;
if(!chunkline_non_comment_RR(&chunk, &chunk_pos, buf, &pstate)) {
return 0;
@@ -4770,7 +4776,7 @@ http_zonefile_syntax_check(struct auth_xfer* xfr, sldns_buffer* buf)
return 0;
}
/* check that class is correct */
if(sldns_wirerr_get_class(rr, rr_len, dname_len) != xfr->dclass) {
if(sldns_wirerr_get_class(rr, rr_len, dname_len) != dclass) {
log_err("parse failure: first record in downloaded zonefile "
"from wrong RR class");
return 0;
@@ -4792,7 +4798,7 @@ chunklist_sum(struct auth_chunk* list)
/** for http download, parse and add RR to zone */
static int
http_parse_add_rr(struct auth_xfer* xfr, struct auth_zone* z,
http_parse_add_rr(const char* host, const char* file, struct auth_zone* z,
sldns_buffer* buf, struct sldns_file_parse_state* pstate)
{
uint8_t rr[LDNS_RR_BUF_SIZE];
@@ -4806,9 +4812,7 @@ http_parse_add_rr(struct auth_xfer* xfr, struct auth_zone* z,
pstate->prev_rr_len?pstate->prev_rr:NULL, pstate->prev_rr_len);
if(e != 0) {
log_err("%s/%s parse failure RR[%d]: %s in '%s'",
xfr->task_transfer->master->host,
xfr->task_transfer->master->file,
LDNS_WIREPARSE_OFFSET(e),
host, file, LDNS_WIREPARSE_OFFSET(e),
sldns_get_errorstr_parse(LDNS_WIREPARSE_ERROR(e)),
line);
return 0;
@@ -4828,10 +4832,10 @@ http_parse_add_rr(struct auth_xfer* xfr, struct auth_zone* z,
/** RR list iterator, returns RRs from answer section one by one from the
* dns packets in the chunklist */
static void
chunk_rrlist_start(struct auth_xfer* xfr, struct auth_chunk** rr_chunk,
chunk_rrlist_start(struct auth_chunk* chunk_list, struct auth_chunk** rr_chunk,
int* rr_num, size_t* rr_pos)
{
*rr_chunk = xfr->task_transfer->chunks_first;
*rr_chunk = chunk_list;
*rr_num = 0;
*rr_pos = 0;
}
@@ -4998,9 +5002,10 @@ ixfr_start_serial(struct auth_chunk* rr_chunk, int rr_num, size_t rr_pos,
}
/** apply IXFR to zone in memory. z is locked. false on failure(mallocfail) */
static int
apply_ixfr(struct auth_xfer* xfr, struct auth_zone* z,
struct sldns_buffer* scratch_buffer)
int
xfr_apply_ixfr(struct auth_chunk* chunk_list, uint32_t xfr_serial,
struct auth_zone* z, struct sldns_buffer* scratch_buffer,
struct auth_load_thread* thr)
{
struct auth_chunk* rr_chunk;
int rr_num;
@@ -5015,10 +5020,8 @@ apply_ixfr(struct auth_xfer* xfr, struct auth_zone* z,
int delmode = 0;
int softfail = 0;
xfr->num_ixfrs++;
/* start RR iterator over chunklist of packets */
chunk_rrlist_start(xfr, &rr_chunk, &rr_num, &rr_pos);
chunk_rrlist_start(chunk_list, &rr_chunk, &rr_num, &rr_pos);
while(!chunk_rrlist_end(rr_chunk, rr_num)) {
if(!chunk_rrlist_get_current(rr_chunk, rr_num, rr_pos,
&rr_dname, &rr_type, &rr_class, &rr_ttl, &rr_rdlen,
@@ -5049,7 +5052,7 @@ apply_ixfr(struct auth_xfer* xfr, struct auth_zone* z,
if(!ixfr_start_serial(rr_chunk, rr_num, rr_pos,
rr_dname, rr_type, rr_class, rr_ttl,
rr_rdlen, rr_rdata, rr_nextpos,
transfer_serial, xfr->serial)) {
transfer_serial, xfr_serial)) {
return 0;
}
} else if(transfer_serial == serial) {
@@ -5070,7 +5073,8 @@ apply_ixfr(struct auth_xfer* xfr, struct auth_zone* z,
* SOA 3 followed by add
* SOA 3 end */
/* ended by SOA record */
xfr->serial = transfer_serial;
/* xfr->serial is set by xfr_find_soa
* after this function. */
break;
}
}
@@ -5127,6 +5131,10 @@ apply_ixfr(struct auth_xfer* xfr, struct auth_zone* z,
}
rr_counter++;
if(thr && rr_counter % NUM_RECORDS_BEFORE_SIGNAL_CHECK == 0) {
if(auth_load_thread_poll_for_quit(thr))
return 0;
}
chunk_rrlist_gonext(&rr_chunk, &rr_num, &rr_pos, rr_nextpos);
}
if(softfail) {
@@ -5136,10 +5144,21 @@ apply_ixfr(struct auth_xfer* xfr, struct auth_zone* z,
return 1;
}
/** apply AXFR to zone in memory. z is locked. false on failure(mallocfail) */
/** apply IXFR to zone in memory. z is locked. false on failure(mallocfail) */
static int
apply_axfr(struct auth_xfer* xfr, struct auth_zone* z,
apply_ixfr(struct auth_xfer* xfr, struct auth_zone* z,
struct sldns_buffer* scratch_buffer)
{
xfr->num_ixfrs++;
return xfr_apply_ixfr(xfr->task_transfer->chunks_first, xfr->serial, z,
scratch_buffer, NULL);
}
/** apply AXFR to zone in memory. z is locked. false on failure(mallocfail) */
int
xfr_apply_axfr(struct auth_chunk* chunk_list, struct auth_zone* z,
struct sldns_buffer* scratch_buffer, struct auth_load_thread* thr)
{
struct auth_chunk* rr_chunk;
int rr_num;
@@ -5147,21 +5166,16 @@ apply_axfr(struct auth_xfer* xfr, struct auth_zone* z,
uint8_t* rr_dname, *rr_rdata;
uint16_t rr_type, rr_class, rr_rdlen;
uint32_t rr_ttl;
uint32_t serial = 0;
size_t rr_nextpos;
size_t rr_counter = 0;
int have_end_soa = 0;
auth_zone_clear_data(z);
xfr->have_zone = 0;
xfr->serial = 0;
xfr->soa_zone_acquired = 0;
xfr->num_ixfrs = 0;
/* insert all RRs in to the zone */
/* insert the SOA only once, skip the last one */
/* start RR iterator over chunklist of packets */
chunk_rrlist_start(xfr, &rr_chunk, &rr_num, &rr_pos);
chunk_rrlist_start(chunk_list, &rr_chunk, &rr_num, &rr_pos);
while(!chunk_rrlist_end(rr_chunk, rr_num)) {
if(!chunk_rrlist_get_current(rr_chunk, rr_num, rr_pos,
&rr_dname, &rr_type, &rr_class, &rr_ttl, &rr_rdlen,
@@ -5178,7 +5192,7 @@ apply_axfr(struct auth_xfer* xfr, struct auth_zone* z,
break;
}
if(rr_rdlen < 22) return 0; /* bad SOA rdlen */
serial = sldns_read_uint32(rr_rdata+rr_rdlen-20);
/* serial is at sldns_read_uint32(rr_rdata+rr_rdlen-20); */
}
/* add this RR */
@@ -5190,22 +5204,75 @@ apply_axfr(struct auth_xfer* xfr, struct auth_zone* z,
}
rr_counter++;
if(thr && rr_counter % NUM_RECORDS_BEFORE_SIGNAL_CHECK == 0) {
if(auth_load_thread_poll_for_quit(thr))
return 0;
}
chunk_rrlist_gonext(&rr_chunk, &rr_num, &rr_pos, rr_nextpos);
}
if(!have_end_soa) {
log_err("no end SOA record for AXFR");
return 0;
}
/* xfr->serial and xfr->have_zone are set by xfr_find_soa
* after this function. */
return 1;
}
xfr->serial = serial;
xfr->have_zone = 1;
/** apply AXFR to zone in memory. z is locked. false on failure(mallocfail) */
static int
apply_axfr(struct auth_xfer* xfr, struct auth_zone* z,
struct sldns_buffer* scratch_buffer)
{
xfr->have_zone = 0;
xfr->serial = 0;
xfr->soa_zone_acquired = 0;
xfr->num_ixfrs = 0;
return xfr_apply_axfr(xfr->task_transfer->chunks_first, z,
scratch_buffer, NULL);
}
void
xfr_http_preview(const char* file, struct auth_chunk* chunk_list)
{
if(verbosity >= VERB_ALGO)
verbose(VERB_ALGO, "http download %s of size %d",
file, (int)chunklist_sum(chunk_list));
if(chunk_list && verbosity >= VERB_ALGO) {
char preview[1024];
if(chunk_list->len+1 > sizeof(preview)) {
memmove(preview, chunk_list->data, sizeof(preview)-1);
preview[sizeof(preview)-1]=0;
} else {
memmove(preview, chunk_list->data, chunk_list->len);
preview[chunk_list->len]=0;
}
log_info("auth zone http downloaded content preview: %s",
preview);
}
}
int
xfr_http_syntax_check(uint8_t* name, size_t namelen, uint16_t dclass,
const char* host, const char* file, struct auth_chunk* chunk_list,
struct sldns_buffer* scratch_buffer)
{
/* perhaps a little syntax check before we try to apply the data? */
if(!http_zonefile_syntax_check(name, namelen, dclass, chunk_list,
scratch_buffer)) {
log_err("http download %s/%s does not contain a zonefile, "
"but got '%s'", host, file,
sldns_buffer_begin(scratch_buffer));
return 0;
}
return 1;
}
/** apply HTTP to zone in memory. z is locked. false on failure(mallocfail) */
static int
apply_http(struct auth_xfer* xfr, struct auth_zone* z,
struct sldns_buffer* scratch_buffer)
int
xfr_apply_http(uint8_t* name, size_t namelen, const char* host,
const char* file, struct auth_chunk* chunk_list, struct auth_zone* z,
struct sldns_buffer* scratch_buffer, struct auth_load_thread* thr)
{
/* parse data in chunks */
/* parse RR's and read into memory. ignore $INCLUDE from the
@@ -5216,52 +5283,24 @@ apply_http(struct auth_xfer* xfr, struct auth_zone* z,
int ret;
memset(&pstate, 0, sizeof(pstate));
pstate.default_ttl = 3600;
if(xfr->namelen < sizeof(pstate.origin)) {
pstate.origin_len = xfr->namelen;
memmove(pstate.origin, xfr->name, xfr->namelen);
}
if(verbosity >= VERB_ALGO)
verbose(VERB_ALGO, "http download %s of size %d",
xfr->task_transfer->master->file,
(int)chunklist_sum(xfr->task_transfer->chunks_first));
if(xfr->task_transfer->chunks_first && verbosity >= VERB_ALGO) {
char preview[1024];
if(xfr->task_transfer->chunks_first->len+1 > sizeof(preview)) {
memmove(preview, xfr->task_transfer->chunks_first->data,
sizeof(preview)-1);
preview[sizeof(preview)-1]=0;
} else {
memmove(preview, xfr->task_transfer->chunks_first->data,
xfr->task_transfer->chunks_first->len);
preview[xfr->task_transfer->chunks_first->len]=0;
}
log_info("auth zone http downloaded content preview: %s",
preview);
}
/* perhaps a little syntax check before we try to apply the data? */
if(!http_zonefile_syntax_check(xfr, scratch_buffer)) {
log_err("http download %s/%s does not contain a zonefile, "
"but got '%s'", xfr->task_transfer->master->host,
xfr->task_transfer->master->file,
sldns_buffer_begin(scratch_buffer));
return 0;
if(namelen < sizeof(pstate.origin)) {
pstate.origin_len = namelen;
memmove(pstate.origin, name, namelen);
}
auth_zone_clear_data(z);
xfr->have_zone = 0;
xfr->serial = 0;
xfr->soa_zone_acquired = 0;
xfr->num_ixfrs = 0;
chunk = xfr->task_transfer->chunks_first;
chunk = chunk_list;
chunk_pos = 0;
pstate.lineno = 0;
while(chunkline_get_line_collated(&chunk, &chunk_pos, scratch_buffer)) {
/* process this line */
pstate.lineno++;
chunkline_newline_removal(scratch_buffer);
if(thr && pstate.lineno % NUM_RECORDS_BEFORE_SIGNAL_CHECK == 0) {
if(auth_load_thread_poll_for_quit(thr))
return 0;
}
if(chunkline_is_comment_line_or_empty(scratch_buffer)) {
continue;
}
@@ -5269,8 +5308,7 @@ apply_http(struct auth_xfer* xfr, struct auth_zone* z,
if((ret=http_parse_origin(scratch_buffer, &pstate))!=0) {
if(ret == 2) {
verbose(VERB_ALGO, "error parsing ORIGIN on line [%s:%d] %s",
xfr->task_transfer->master->file,
pstate.lineno,
file, pstate.lineno,
sldns_buffer_begin(scratch_buffer));
return 0;
}
@@ -5279,17 +5317,15 @@ apply_http(struct auth_xfer* xfr, struct auth_zone* z,
if((ret=http_parse_ttl(scratch_buffer, &pstate))!=0) {
if(ret == 2) {
verbose(VERB_ALGO, "error parsing TTL on line [%s:%d] %s",
xfr->task_transfer->master->file,
pstate.lineno,
file, pstate.lineno,
sldns_buffer_begin(scratch_buffer));
return 0;
}
continue; /* $TTL has been handled */
}
if(!http_parse_add_rr(xfr, z, scratch_buffer, &pstate)) {
if(!http_parse_add_rr(host, file, z, scratch_buffer, &pstate)) {
verbose(VERB_ALGO, "error parsing line [%s:%d] %s",
xfr->task_transfer->master->file,
pstate.lineno,
file, pstate.lineno,
sldns_buffer_begin(scratch_buffer));
return 0;
}
@@ -5297,6 +5333,28 @@ apply_http(struct auth_xfer* xfr, struct auth_zone* z,
return 1;
}
/** apply HTTP to zone in memory. z is locked. false on failure(mallocfail) */
static int
apply_http(struct auth_xfer* xfr, struct auth_zone* z,
struct sldns_buffer* scratch_buffer)
{
xfr_http_preview(xfr->task_transfer->master->file,
xfr->task_transfer->chunks_first);
if(!xfr_http_syntax_check(xfr->name, xfr->namelen, xfr->dclass,
xfr->task_transfer->master->host,
xfr->task_transfer->master->file,
xfr->task_transfer->chunks_first, scratch_buffer))
return 0;
xfr->have_zone = 0;
xfr->serial = 0;
xfr->soa_zone_acquired = 0;
xfr->num_ixfrs = 0;
return xfr_apply_http(xfr->name, xfr->namelen,
xfr->task_transfer->master->host,
xfr->task_transfer->master->file,
xfr->task_transfer->chunks_first, z, scratch_buffer, NULL);
}
/** write http chunks to zonefile to create downloaded file */
static int
auth_zone_write_chunks(struct auth_xfer* xfr, const char* fname)
@@ -6289,50 +6347,13 @@ xfer_link_data(sldns_buffer* pkt, struct auth_xfer* xfr)
return 1;
}
/** task transfer. the list of data is complete. process it and if failed
* move to next master, if succeeded, end the task transfer */
/** task transfer, process the failure to process the zone transfer.
* It continues with the task transfer, possibly. */
static void
process_list_end_transfer(struct auth_xfer* xfr, struct module_env* env)
xfr_process_transfer_failed(struct auth_xfer* xfr, struct module_env* env,
int ixfr_fail)
{
int ixfr_fail = 0;
if(xfr_process_chunk_list(xfr, env, &ixfr_fail)) {
/* it worked! */
auth_chunks_delete(xfr->task_transfer);
/* we fetched the zone, move to wait task */
xfr_transfer_disown(xfr);
if(xfr->notify_received && (!xfr->notify_has_serial ||
(xfr->notify_has_serial &&
xfr_serial_means_update(xfr, xfr->notify_serial)))) {
uint32_t sr = xfr->notify_serial;
int has_sr = xfr->notify_has_serial;
/* we received a notify while probe/transfer was
* in progress. start a new probe and transfer */
xfr->notify_received = 0;
xfr->notify_has_serial = 0;
xfr->notify_serial = 0;
if(!xfr_start_probe(xfr, env, NULL)) {
/* if we couldn't start it, already in
* progress; restore notify serial,
* while xfr still locked */
xfr->notify_received = 1;
xfr->notify_has_serial = has_sr;
xfr->notify_serial = sr;
lock_basic_unlock(&xfr->lock);
}
return;
} else {
/* pick up the nextprobe task and wait (normail wait time) */
if(xfr->task_nextprobe->worker == NULL)
xfr_set_timeout(xfr, env, 0, 0);
}
lock_basic_unlock(&xfr->lock);
return;
}
/* processing failed */
/* when done, delete data from list */
auth_chunks_delete(xfr->task_transfer);
if(ixfr_fail) {
xfr->task_transfer->ixfr_fail = 1;
} else {
@@ -6341,6 +6362,238 @@ process_list_end_transfer(struct auth_xfer* xfr, struct module_env* env)
xfr_transfer_nexttarget_or_end(xfr, env);
}
/** task transfer, process the success to process the zone transfer.
* It continues with the task probe, possibly, due to notify. Or nextprobe. */
static void
xfr_process_transfer_success(struct auth_xfer* xfr, struct module_env* env)
{
/* we fetched the zone, move to wait task */
xfr_transfer_disown(xfr);
if(xfr->notify_received && (!xfr->notify_has_serial ||
(xfr->notify_has_serial &&
xfr_serial_means_update(xfr, xfr->notify_serial)))) {
uint32_t sr = xfr->notify_serial;
int has_sr = xfr->notify_has_serial;
/* we received a notify while probe/transfer was
* in progress. start a new probe and transfer */
xfr->notify_received = 0;
xfr->notify_has_serial = 0;
xfr->notify_serial = 0;
if(!xfr_start_probe(xfr, env, NULL)) {
/* if we couldn't start it, already in
* progress; restore notify serial,
* while xfr still locked */
xfr->notify_received = 1;
xfr->notify_has_serial = has_sr;
xfr->notify_serial = sr;
lock_basic_unlock(&xfr->lock);
}
return;
} else {
/* pick up the nextprobe task and wait (normail wait time) */
if(xfr->task_nextprobe->worker == NULL)
xfr_set_timeout(xfr, env, 0, 0);
}
lock_basic_unlock(&xfr->lock);
}
/** task transfer. the list of data is complete. process it and if failed
* move to next master, if succeeded, end the task transfer */
static void
process_list_end_transfer(struct auth_xfer* xfr, struct module_env* env)
{
int ixfr_fail = 0;
if(env->cfg->auth_task_threads != 0 /* auth load enabled */) {
if(auth_load_info_grab_thread(env)) {
/* Create auth load thread task to process the data. */
if(auth_load_add_task_xfr(xfr, env->worker)) {
/* Task is created, wait for it to be done. The worker
* is signalled with the result. */
/* When it is done, the xfr_process_load_end_transfer
* routine is called. */
lock_basic_unlock(&xfr->lock);
return;
}
auth_load_info_release_thread(env);
} else {
verbose(VERB_ALGO, "Auth load threads at capacity, not creating a new thread");
}
} else if(xfr_process_chunk_list(xfr, env, &ixfr_fail)) {
/* it worked! */
auth_chunks_delete(xfr->task_transfer);
xfr_process_transfer_success(xfr, env);
return;
}
/* when done, delete data from list */
auth_chunks_delete(xfr->task_transfer);
xfr_process_transfer_failed(xfr, env, ixfr_fail);
}
/** deal with successful load end, starts holding xfr lock,
* ends holding xfr lock. */
static int
xfr_process_loaded_transfer(struct auth_xfer* xfr, struct module_env* env,
int* gone, struct timeval* time_taken, size_t mem_used,
size_t chunks_total)
{
struct auth_zone* z = NULL;
verbose(VERB_ALGO, "xfr_process_loaded_transfer");
lock_basic_unlock(&xfr->lock);
if(!xfr_process_reacquire_locks(xfr, env, &z)) {
/* the zone is gone, ignore xfr results */
*gone = 1;
return 0;
}
/* holding xfr and z locks */
verbose(VERB_ALGO, "xfr_process_loaded_transfer: num_ixfrs");
if(xfr->task_transfer->master->http) {
xfr->num_ixfrs = 0;
xfr->have_zone = 0;
xfr->serial = 0;
} else if(xfr->task_transfer->on_ixfr &&
!xfr->task_transfer->on_ixfr_is_axfr) {
xfr->num_ixfrs++;
} else {
/* AXFR */
xfr->num_ixfrs = 0;
xfr->have_zone = 0;
xfr->serial = 0;
}
verbose(VERB_ALGO, "xfr_process_loaded_transfer: find_soa");
xfr->zone_expired = 0;
z->zone_expired = 0;
if(!xfr_find_soa(z, xfr)) {
verbose(VERB_ALGO, "xfr from %s: no SOA in zone after update"
" (or malformed RR)", xfr->task_transfer->master->host);
return 0;
}
z->soa_zone_acquired = *env->now;
xfr->soa_zone_acquired = *env->now;
xfr->is_rpz = (z->rpz!=NULL);
/* release xfr lock while verifying zonemd because it may have
* to spawn lookups in the state machines */
lock_basic_unlock(&xfr->lock);
verbose(VERB_ALGO, "xfr_process_loaded_transfer: verify_zonemd");
/* holding z lock */
auth_zone_verify_zonemd(z, env, &env->mesh->mods, NULL, 0, 0);
if(z->zone_expired) {
char zname[LDNS_MAX_DOMAINLEN];
/* ZONEMD must have failed */
/* reacquire locks, so we hold xfr lock on exit of routine,
* and both xfr and z again after releasing xfr for potential
* state machine mesh callbacks */
lock_rw_unlock(&z->lock);
if(!xfr_process_reacquire_locks(xfr, env, &z)) {
*gone = 1;
return 0;
}
dname_str(xfr->name, zname);
verbose(VERB_ALGO, "xfr from %s: ZONEMD failed for %s, transfer is failed", xfr->task_transfer->master->host, zname);
xfr->zone_expired = 1;
lock_rw_unlock(&z->lock);
return 0;
}
/* reacquire locks, so we hold xfr lock on exit of routine,
* and both xfr and z again after releasing xfr for potential
* state machine mesh callbacks */
lock_rw_unlock(&z->lock);
if(!xfr_process_reacquire_locks(xfr, env, &z)) {
*gone = 1;
return 0;
}
/* holding xfr and z locks */
verbose(VERB_ALGO, "xfr_process_loaded_transfer: lease");
if(xfr->have_zone)
xfr->lease_time = *env->now;
/* unlock */
lock_rw_unlock(&z->lock);
if(verbosity >= VERB_QUERY && xfr->have_zone) {
char zname[LDNS_MAX_DOMAINLEN];
dname_str(xfr->name, zname);
verbose(VERB_QUERY, "auth zone %s updated to serial %u",
zname, (unsigned)xfr->serial);
if(verbosity >= 8) {
char taskline[1024];
if(xfr->task_transfer->master->http) {
snprintf(taskline, sizeof(taskline),
"http transfer from %s/%s of %lu "
"bytes serial %u",
xfr->task_transfer->master->host,
xfr->task_transfer->master->file,
(unsigned long)chunks_total,
(unsigned)xfr->serial);
} else if(xfr->task_transfer->on_ixfr &&
!xfr->task_transfer->on_ixfr_is_axfr) {
snprintf(taskline, sizeof(taskline),
"IXFR transfer from %s of %lu "
"bytes serial %u",
xfr->task_transfer->master->host,
(unsigned long)chunks_total,
(unsigned)xfr->serial);
} else {
snprintf(taskline, sizeof(taskline),
"AXFR transfer from %s of %lu "
"bytes serial %u",
xfr->task_transfer->master->host,
(unsigned long)chunks_total,
(unsigned)xfr->serial);
}
verbose(VERB_ALGO, "auth zone %s details %s thread "
"time was %d.%6.6ds and used %lu bytes of "
"memory", zname, taskline,
(int)time_taken->tv_sec,
(int)time_taken->tv_usec,
(unsigned long)mem_used);
}
}
verbose(VERB_ALGO, "xfr_process_loaded_transfer: write after update");
/* see if we need to write to a zonefile */
xfr_write_after_update(xfr, env);
return 1;
}
void xfr_process_load_end_transfer(struct auth_xfer* xfr,
struct module_env* env, uint8_t status, int ixfr_fail,
struct timeval* time_taken, size_t mem_used, size_t chunks_total,
struct auth_chunk* chunk_list)
{
/* Chunks are put here for the auth zone write for the http case. */
verbose(VERB_ALGO, "xfr_process_load_end_transfer");
xfr->task_transfer->chunks_first = chunk_list;
if(status) {
int gone = 0;
if(!xfr_process_loaded_transfer(xfr, env, &gone, time_taken,
mem_used, chunks_total)) {
status = 0;
if(gone) {
/* the zone is gone from the authzones. */
lock_basic_unlock(&xfr->lock);
auth_chunks_delete(xfr->task_transfer);
return;
}
}
}
verbose(VERB_ALGO, "xfr_process_load_end_transfer: chunks delete");
auth_chunks_delete(xfr->task_transfer);
if(status) {
/* it worked! */
verbose(VERB_ALGO, "xfr_process_load_end_transfer: success");
xfr_process_transfer_success(xfr, env);
return;
}
/* The transfer failed */
verbose(VERB_ALGO, "xfr_process_load_end_transfer: failed");
xfr_process_transfer_failed(xfr, env, ixfr_fail);
}
/** callback for the task_transfer timer */
void
auth_xfer_transfer_timer_callback(void* arg)
@@ -8937,7 +9190,7 @@ auth_data_get_mem(struct auth_data* node)
}
/** Get memory usage of auth zone */
static size_t
size_t
auth_zone_get_mem(struct auth_zone* z)
{
size_t m = sizeof(*z) + z->namelen;
+39
View File
@@ -65,6 +65,7 @@ struct auth_probe;
struct auth_transfer;
struct auth_master;
struct auth_chunk;
struct auth_load_thread;
/**
* Authoritative zones, shared.
@@ -857,4 +858,42 @@ int chunkline_count_parens(struct sldns_buffer* buf, size_t start);
/** Clear data in auth zone */
void auth_zone_clear_data(struct auth_zone* z);
/** Get memory usage of auth zone */
size_t auth_zone_get_mem(struct auth_zone* z);
/** create domain with the given name */
struct auth_data* az_domain_create(struct auth_zone* z, uint8_t* nm,
size_t nmlen);
/** helper traverse to delete zones */
void auth_data_del(rbnode_type* n, void* arg);
/** Handle the end of an auth load task. */
void xfr_process_load_end_transfer(struct auth_xfer* xfr,
struct module_env* env, uint8_t status, int ixfr_fail,
struct timeval* time_taken, size_t mem_used, size_t chunks_total,
struct auth_chunk* chunk_list);
/** Log preview of http transfer */
void xfr_http_preview(const char* file, struct auth_chunk* chunk_list);
/** Check syntax of first part of the http download */
int xfr_http_syntax_check(uint8_t* name, size_t namelen, uint16_t dclass,
const char* host, const char* file, struct auth_chunk* chunk_list,
struct sldns_buffer* scratch_buffer);
/** Apply http transfer to auth_zone */
int xfr_apply_http(uint8_t* name, size_t namelen, const char* host,
const char* file, struct auth_chunk* chunk_list, struct auth_zone* z,
struct sldns_buffer* scratch_buffer, struct auth_load_thread* thr);
/** Apply IXFR transfer to auth_zone */
int xfr_apply_ixfr(struct auth_chunk* chunk_list, uint32_t xfr_serial,
struct auth_zone* z, struct sldns_buffer* scratch_buffer,
struct auth_load_thread* thr);
/** Apply AXFR transfer to auth_zone */
int xfr_apply_axfr(struct auth_chunk* chunk_list, struct auth_zone* z,
struct sldns_buffer* scratch_buffer, struct auth_load_thread* thr);
#endif /* SERVICES_AUTHZONE_H */
+22 -4
View File
@@ -1341,13 +1341,33 @@ ports_create_if(const char* ifname, int do_auto, int do_udp, int do_tcp,
if((is_doq) && !(is_https || is_ssl)) do_tcp = 0;
if(do_auto) {
enum listen_type auto_port_type;
ub_sock = calloc(1, sizeof(struct unbound_socket));
if(!ub_sock)
return 0;
if(is_dnscrypt) {
auto_port_type = listen_type_udpancil_dnscrypt;
add = "udpancil_dnscrypt";
} else if(is_doq) {
auto_port_type = listen_type_doq;
add = "doq";
if(if_listens_on(ifname, port, 53, NULL)) {
log_err("DNS over QUIC is strictly not "
"allowed on port 53 as per RFC 9250. "
"Port 53 is for DNS datagrams. Error "
"for interface '%s'.", ifname);
free(ub_sock->addr);
free(ub_sock);
return 0;
}
} else {
auto_port_type = listen_type_udpancil;
add = "udpancil";
}
if((s = make_sock_port(SOCK_DGRAM, ifname, port, hints, 1,
&noip6, rcv, snd, reuseport, transparent,
tcp_mss, nodelay, freebind, use_systemd, dscp, ub_sock,
(is_dnscrypt?"udpancil_dnscrypt":"udpancil"))) == -1) {
add)) == -1) {
free(ub_sock->addr);
free(ub_sock);
if(noip6) {
@@ -1366,9 +1386,7 @@ ports_create_if(const char* ifname, int do_auto, int do_udp, int do_tcp,
if (sock_queue_timeout && !set_recvtimestamp(s)) {
log_warn("socket timestamping is not available");
}
if(!port_insert(list, s, is_dnscrypt
?listen_type_udpancil_dnscrypt:listen_type_udpancil,
is_pp2, ub_sock)) {
if(!port_insert(list, s, auto_port_type, is_pp2, ub_sock)) {
sock_close(s);
free(ub_sock->addr);
free(ub_sock);
+3
View File
@@ -264,6 +264,9 @@ setup_config(FILE* in, int* lineno, int* pass_argc, char* pass_argv[])
fprintf(cfg, " pidfile: \"\"\n");
fprintf(cfg, " val-log-level: 2\n");
fprintf(cfg, " log-servfail: yes\n");
/* the extra thread needs pipe communication that is not available
* from fake_event calls. So auth-task-threads: 0 disables them. */
fprintf(cfg, " auth-task-threads: 0\n");
fprintf(cfg, "remote-control: control-enable: no\n");
while(fgets(line, MAX_LINE_LEN-1, in)) {
parse = line;
+84
View File
@@ -1337,6 +1337,89 @@ static void mesh_test(void)
free(c1);
}
#include "util/data/packed_rrset.h"
#include "sldns/sbuffer.h"
/** packed_rrset unit tests */
static void packed_rrset_test(void)
{
/* packed_rr_to_string assembles the dname, type, class, ttl and
* rdata of one rr into a buffer of 65535 bytes. Check that it
* refuses an rr that does not fit in there, also when the caller
* passes a dest_len that is larger than that, like the callers in
* daemon/cachedump.c and daemon/remote.c do. Without the check it
* writes past the end of the assembly buffer. */
uint8_t smalldname[] = "\003www\007example\003com";
uint8_t smallrdata[] = {0, 4, 1, 2, 3, 4};
uint8_t maxdname[LDNS_MAX_DOMAINLEN];
struct ub_packed_rrset_key rrk;
struct packed_rrset_data d;
uint8_t* rr_data[1];
size_t rr_len[1];
time_t rr_ttl[1];
size_t dest_len = 65535*4+2048; /* the size daemon/cachedump.c uses */
char* dest = (char*)malloc(dest_len);
int i;
unit_show_func("util/data/packed_rrset.c", "packed_rr_to_string");
if(!dest) fatal_exit("out of memory");
memset(&rrk, 0, sizeof(rrk));
memset(&d, 0, sizeof(d));
rrk.entry.data = &d;
rrk.rk.rrset_class = htons(LDNS_RR_CLASS_IN);
d.count = 1;
d.rr_len = rr_len;
d.rr_ttl = rr_ttl;
d.rr_data = rr_data;
rr_ttl[0] = 3600;
/* an ordinary rr is printed, also with the large dest_len */
rrk.rk.dname = smalldname;
rrk.rk.dname_len = sizeof(smalldname);
rrk.rk.type = htons(LDNS_RR_TYPE_A);
rr_data[0] = smallrdata;
rr_len[0] = sizeof(smallrdata);
unit_assert(packed_rr_to_string(&rrk, 0, 0, dest, dest_len) == 1);
unit_assert(strstr(dest, "1.2.3.4") != NULL);
/* a dname of the maximum length, 127 labels of one character */
for(i=0; i<127; i++) {
maxdname[i*2] = 1;
maxdname[i*2+1] = (uint8_t)'a';
}
maxdname[254] = 0;
rrk.rk.dname = maxdname;
rrk.rk.dname_len = sizeof(maxdname);
rrk.rk.type = htons(LDNS_RR_TYPE_TXT);
/* 255+2+2+4+65272 is exactly 65535, that still fits */
rr_len[0] = 65535 - 255 - 8;
rr_data[0] = (uint8_t*)calloc(1, rr_len[0]);
if(!rr_data[0]) fatal_exit("out of memory");
sldns_write_uint16(rr_data[0], (uint16_t)(rr_len[0]-2));
unit_assert(packed_rr_to_string(&rrk, 0, 0, dest, dest_len) == 1);
free(rr_data[0]);
/* one more byte of rdata does not fit and must be refused */
rr_len[0] = 65535 - 255 - 8 + 1;
rr_data[0] = (uint8_t*)calloc(1, rr_len[0]);
if(!rr_data[0]) fatal_exit("out of memory");
sldns_write_uint16(rr_data[0], (uint16_t)(rr_len[0]-2));
unit_assert(packed_rr_to_string(&rrk, 0, 0, dest, dest_len) == 0);
unit_assert(dest[0] == 0);
free(rr_data[0]);
/* the largest rdata an rr can hold, well over the buffer */
rr_len[0] = 2 + 65535;
rr_data[0] = (uint8_t*)calloc(1, rr_len[0]);
if(!rr_data[0]) fatal_exit("out of memory");
sldns_write_uint16(rr_data[0], 65535);
unit_assert(packed_rr_to_string(&rrk, 0, 0, dest, dest_len) == 0);
unit_assert(dest[0] == 0);
free(rr_data[0]);
free(dest);
}
void unit_show_func(const char* file, const char* func)
{
printf("test %s:%s\n", file, func);
@@ -1409,6 +1492,7 @@ main(int argc, char* argv[])
zonemd_test();
tcpreuse_test();
msgparse_test();
packed_rrset_test();
edns_ede_answer_encode_test();
localzone_test();
mesh_test();
+18
View File
@@ -0,0 +1,18 @@
server:
verbosity: 8
# num-threads: 1
interface: 127.0.0.1
port: @PORT@
use-syslog: no
directory: ""
pidfile: "unbound.pid"
chroot: ""
username: ""
do-not-query-localhost: no
auth-task-threads: 1
auth-zone:
name: "example.com"
for-upstream: yes
for-downstream: yes
master: "127.0.0.1@@TOPORT@"
+16
View File
@@ -0,0 +1,16 @@
BaseName: auth_load
Version: 1.0
Description: Perform AXFR for authority zone with auth load thread
CreationDate: Thu 2 Jul 09:35:40 CEST 2026
Maintainer: dr. W.C.A. Wijngaards
Category:
Component:
CmdDepends:
Depends:
Help:
Pre: auth_load.pre
Post: auth_load.post
Test: auth_load.test
AuxFiles:
Passed:
Failure:
+12
View File
@@ -0,0 +1,12 @@
# #-- auth_load.post --#
# source the master var file when it's there
[ -f ../.tpkg.var.master ] && source ../.tpkg.var.master
# source the test var file when it's there
[ -f .tpkg.var.test ] && source .tpkg.var.test
#
# do your teardown here
. ../common.sh
kill_pid $FWD_PID
kill_pid $UNBOUND_PID
cat fwd.log
cat unbound.log
+32
View File
@@ -0,0 +1,32 @@
# #-- auth_load.pre--#
# source the master var file when it's there
[ -f ../.tpkg.var.master ] && source ../.tpkg.var.master
# use .tpkg.var.test for in test variable passing
[ -f .tpkg.var.test ] && source .tpkg.var.test
. ../common.sh
get_random_port 2
UNBOUND_PORT=$RND_PORT
FWD_PORT=$(($RND_PORT + 1))
echo "UNBOUND_PORT=$UNBOUND_PORT" >> .tpkg.var.test
echo "FWD_PORT=$FWD_PORT" >> .tpkg.var.test
# start forwarder
get_ldns_testns
$LDNS_TESTNS -p $FWD_PORT auth_load.testns >fwd.log 2>&1 &
FWD_PID=$!
echo "FWD_PID=$FWD_PID" >> .tpkg.var.test
# make config file
sed -e 's/@PORT\@/'$UNBOUND_PORT'/' -e 's/@TOPORT\@/'$FWD_PORT'/' < auth_load.conf > ub.conf
# start unbound in the background
PRE="../.."
$PRE/unbound -d -c ub.conf >unbound.log 2>&1 &
#valgrind --leak-check=full --show-reachable=yes $PRE/unbound -d -c ub.conf >unbound.log 2>&1 &
UNBOUND_PID=$!
echo "UNBOUND_PID=$UNBOUND_PID" >> .tpkg.var.test
cat .tpkg.var.test
wait_ldns_testns_up fwd.log
wait_unbound_up unbound.log
+55
View File
@@ -0,0 +1,55 @@
# #-- auth_load.test --#
# source the master var file when it's there
[ -f ../.tpkg.var.master ] && source ../.tpkg.var.master
# use .tpkg.var.test for in test variable passing
[ -f .tpkg.var.test ] && source .tpkg.var.test
PRE="../.."
# do the test
echo "> dig www.example.com."
dig @127.0.0.1 -p $UNBOUND_PORT www.example.com. | tee outfile
if grep SERVFAIL outfile; then
echo "> try again"
dig @127.0.0.1 -p $UNBOUND_PORT www.example.com. | tee outfile
fi
if grep SERVFAIL outfile; then
echo "> try again"
sleep 1
dig @127.0.0.1 -p $UNBOUND_PORT www.example.com. | tee outfile
fi
if grep SERVFAIL outfile; then
echo "> try again"
sleep 1
dig @127.0.0.1 -p $UNBOUND_PORT www.example.com. | tee outfile
fi
if grep SERVFAIL outfile; then
echo "> try again"
sleep 1
dig @127.0.0.1 -p $UNBOUND_PORT www.example.com. | tee outfile
fi
if grep SERVFAIL outfile; then
echo "> try again"
sleep 3
dig @127.0.0.1 -p $UNBOUND_PORT www.example.com. | tee outfile
fi
if grep SERVFAIL outfile; then
echo "> try again"
sleep 3
dig @127.0.0.1 -p $UNBOUND_PORT www.example.com. | tee outfile
fi
echo "> check answer"
if grep "1.2.3.4" outfile; then
echo "OK"
else
echo "Not OK"
exit 1
fi
if grep "auth zone example.com. details" unbound.log; then
echo "OK"
else
echo "not OK"
exit 1
fi
exit 0
+27
View File
@@ -0,0 +1,27 @@
ENTRY_BEGIN
MATCH opcode qtype qname
ADJUST copy_id
REPLY QR AA NOERROR
SECTION QUESTION
example.com. IN SOA
SECTION ANSWER
example.com. IN SOA ns.example.com. hostmaster.example.com. 1 3600 900 86400 3600
ENTRY_END
ENTRY_BEGIN
MATCH opcode qtype qname
ADJUST copy_id
REPLY QR AA NOERROR
SECTION QUESTION
example.com. IN AXFR
SECTION ANSWER
example.com. IN SOA ns.example.com. hostmaster.example.com. 1 3600 900 86400 3600
example.com. IN NS ns.example.net.
EXTRA_PACKET
REPLY QR AA NOERROR
SECTION QUESTION
example.com. IN AXFR
SECTION ANSWER
www.example.com. IN A 1.2.3.4
example.com. IN SOA ns.example.com. hostmaster.example.com. 1 3600 900 86400 3600
ENTRY_END
@@ -0,0 +1,3 @@
example.com. IN SOA ns.example.com. hostmaster.example.com. 1 3600 900 86400 3600
example.com. IN NS ns.example.net.
www.example.com. IN A 1.2.3.4
+18
View File
@@ -0,0 +1,18 @@
server:
verbosity: 8
# num-threads: 1
interface: 127.0.0.1
port: @PORT@
use-syslog: no
directory: ""
pidfile: "unbound.pid"
chroot: ""
username: ""
do-not-query-localhost: no
auth-task-threads: 1
auth-zone:
name: "example.com"
for-upstream: yes
for-downstream: yes
url: "https://127.0.0.1:@TOPORT@/example.com.zone"
+16
View File
@@ -0,0 +1,16 @@
BaseName: auth_load_http
Version: 1.0
Description: Perform http transfer for authority zone with auth load thread
CreationDate: Tue 11 Aug 16:00:00 CEST 2026
Maintainer: dr. W.C.A. Wijngaards
Category:
Component:
CmdDepends:
Depends:
Help:
Pre: auth_load_http.pre
Post: auth_load_http.post
Test: auth_load_http.test
AuxFiles:
Passed:
Failure:
+12
View File
@@ -0,0 +1,12 @@
# #-- auth_load_http.post --#
# source the master var file when it's there
[ -f ../.tpkg.var.master ] && source ../.tpkg.var.master
# source the test var file when it's there
[ -f .tpkg.var.test ] && source .tpkg.var.test
#
# do your teardown here
. ../common.sh
kill_pid $UNBOUND_PID
kill_pid $PETAL_PID
cat petal.log
cat unbound.log
+34
View File
@@ -0,0 +1,34 @@
# #-- auth_load_http.pre--#
# source the master var file when it's there
[ -f ../.tpkg.var.master ] && source ../.tpkg.var.master
# use .tpkg.var.test for in test variable passing
[ -f .tpkg.var.test ] && source .tpkg.var.test
PRE="../.."
. ../common.sh
get_random_port 2
UNBOUND_PORT=$RND_PORT
PETAL_PORT=$(($RND_PORT + 1))
echo "UNBOUND_PORT=$UNBOUND_PORT" >> .tpkg.var.test
echo "PETAL_PORT=$PETAL_PORT" >> .tpkg.var.test
get_make
(cd $PRE; $MAKE petal)
# start https daemon
$PRE/petal -v -a "127.0.0.1" -p $PETAL_PORT >petal.log 2>&1 &
PETAL_PID=$!
echo "PETAL_PID=$PETAL_PID" >> .tpkg.var.test
wait_petal_up petal.log
# make config file
sed -e 's/@PORT\@/'$UNBOUND_PORT'/' -e 's/@TOPORT\@/'$PETAL_PORT'/' < auth_load_http.conf > ub.conf
# start unbound in the background
PRE="../.."
$PRE/unbound -d -c ub.conf >unbound.log 2>&1 &
#valgrind --leak-check=full --show-reachable=yes $PRE/unbound -d -c ub.conf >unbound.log 2>&1 &
UNBOUND_PID=$!
echo "UNBOUND_PID=$UNBOUND_PID" >> .tpkg.var.test
cat .tpkg.var.test
wait_unbound_up unbound.log
+55
View File
@@ -0,0 +1,55 @@
# #-- auth_load_http.test --#
# source the master var file when it's there
[ -f ../.tpkg.var.master ] && source ../.tpkg.var.master
# use .tpkg.var.test for in test variable passing
[ -f .tpkg.var.test ] && source .tpkg.var.test
PRE="../.."
# do the test
echo "> dig www.example.com."
dig @127.0.0.1 -p $UNBOUND_PORT www.example.com. | tee outfile
if grep SERVFAIL outfile; then
echo "> try again"
dig @127.0.0.1 -p $UNBOUND_PORT www.example.com. | tee outfile
fi
if grep SERVFAIL outfile; then
echo "> try again"
sleep 1
dig @127.0.0.1 -p $UNBOUND_PORT www.example.com. | tee outfile
fi
if grep SERVFAIL outfile; then
echo "> try again"
sleep 1
dig @127.0.0.1 -p $UNBOUND_PORT www.example.com. | tee outfile
fi
if grep SERVFAIL outfile; then
echo "> try again"
sleep 1
dig @127.0.0.1 -p $UNBOUND_PORT www.example.com. | tee outfile
fi
if grep SERVFAIL outfile; then
echo "> try again"
sleep 3
dig @127.0.0.1 -p $UNBOUND_PORT www.example.com. | tee outfile
fi
if grep SERVFAIL outfile; then
echo "> try again"
sleep 3
dig @127.0.0.1 -p $UNBOUND_PORT www.example.com. | tee outfile
fi
echo "> check answer"
if grep "1.2.3.4" outfile; then
echo "OK"
else
echo "Not OK"
exit 1
fi
if grep "auth zone example.com. details" unbound.log; then
echo "OK"
else
echo "not OK"
exit 1
fi
exit 0
+21
View File
@@ -0,0 +1,21 @@
-----BEGIN RSA PRIVATE KEY-----
MIIDfQIBAAKBwQC1xQ/Kca6zszZbcCtdOTIH2Uy2gOy/DfabMUU7TmNPm0dVE0NJ
RuN+Rm304SonpwghfP2/ULZNnuDgpG03/32yI7k/VzG6iA4hiF7tT/KAAWC/+2l1
QCsawCV2bSrFK0VhcZr7ALqXd8vkDaQ867K029ypjOQtAJ85qdO3mERy7TGtdUcu
O6hLeVet419YeQ2F8cfNxn63d7bOzNGLPW5xwaCd3UcgD+Ib0k4xfFvbinvPQUeU
J/i4YDWexFYSL+ECAwEAAQKBwCLXXQl+9O+5AEhSnd1Go1Jh0pSA7eBJOuXQcebG
Rb7ykp+6C4G2NtDziwwPRNdI6wQQQ0sym18RfyVQHydGr78/nbiIbB3HCn5e92Mh
mefzW6ow9Kvm2txLzGKA1lvoyRbNm81jnG/eygi3u7Nqd5PNv+4dHj2RkTlmxOeh
qnDMVP5md8uZPv6lYNnrnIzvLCR5vnPNdVwn89AqzI85IcDZdy0R9ZX4NBbsDgAU
6ig6uXuRXvSGiyJ/OUXSrnogaQJhAOjvkHUhVZQkPOxO90TNH4j0GdKKtbSWxIdz
lKfuJeBAEqs0TL+C6vbS81Xw3W1alyDdUBk3rJMOBqW6Ryq5HNL+j5H+Jfsh7fvc
Yle+5wHGci0P9zCFZCrY8It7n9XFIwJhAMfEi6oJa2G8waPJ1bQhxka82Tf9pnKM
XCn/1BBOFjVIx5F842cpA+zp5a62GENTGYPQTTRBB/2/ZwnW5aIkrlg54AtmbqBZ
Oh+2kJdJQD/tfoVmc5soUE2ScTHadK5RKwJhAN4w9kjkXS+MSZjX0kIMsBIBVkhh
C+aREjJqa9ir7/Ey7RvmLXdYuCxtGLRXp7/R8+rjcK49Tx6O+IRJZe042mfhbq3C
EhS1Tr86f4xXix9EXlDhs9bSxrOgcAN9Dv/opQJhAK7eBcPaav0rVfYh/8emqQHS
3fJ9Pu6WnzbEksWTFS2ff9KDGCx9YspIFJ5TF/oXDAaumGZdZrlgirm6O1kr8tGY
F97i04PZl1+bWAaWQH+1TUNI43m2WFUPE7coG2tb8QJgcddDg9VlXliZqgcETZfJ
kJmYETxrcSn3ao6v116N8yxhEgUgjkmsCTiFgx36iDVnXwK6PIt+sIu8MC7eYNa3
berrv/M21K0LRn20IWRxvUobG070weHCAgkko7fTWgr2
-----END RSA PRIVATE KEY-----
+14
View File
@@ -0,0 +1,14 @@
-----BEGIN CERTIFICATE-----
MIICFzCCAUACCQDO660L5y5LGDANBgkqhkiG9w0BAQUFADAQMQ4wDAYDVQQDEwVw
ZXRhbDAeFw0xMDA5MzAxMzQzMDFaFw0zMDA2MTcxMzQzMDFaMBAxDjAMBgNVBAMT
BXBldGFsMIHfMA0GCSqGSIb3DQEBAQUAA4HNADCByQKBwQC1xQ/Kca6zszZbcCtd
OTIH2Uy2gOy/DfabMUU7TmNPm0dVE0NJRuN+Rm304SonpwghfP2/ULZNnuDgpG03
/32yI7k/VzG6iA4hiF7tT/KAAWC/+2l1QCsawCV2bSrFK0VhcZr7ALqXd8vkDaQ8
67K029ypjOQtAJ85qdO3mERy7TGtdUcuO6hLeVet419YeQ2F8cfNxn63d7bOzNGL
PW5xwaCd3UcgD+Ib0k4xfFvbinvPQUeUJ/i4YDWexFYSL+ECAwEAATANBgkqhkiG
9w0BAQUFAAOBwQBBkX9KDP2RXbg+xPmdJ4P6CwvA5x1LZwC++ydVx4NlvT0pWicD
ZUnXjcWAJlkeOuUBAqFG7WHTrXpUUAjmdqFVq2yFjteUYBdrFz0RDB2jM9feeKYO
mTgxdZyT9a6humxCxt5VfgT02axLjm/2AqCyFPMbf4PASoJDln01AEuZLZ8Xl2gV
bYHMnHTGoD1Hu6FNEzRgkMC6XT8X3YjHvzQhpc/qL5wEfEsinQGdX4twsuWbf8xd
q7miNnkO8vd0maw=
-----END CERTIFICATE-----
+6 -1
View File
@@ -107,7 +107,12 @@ check_expected_stats () {
# Check that the rest (unspecified) stats are all 0 (no surprises).
check_rest_stats () {
echo "> Checking rest stats"
grep -F -x -v -f $EXPECTED_STATS_FILE $FILTERED_STATS_FILE > $REST_STATS_FILE
grep -F -x -v -f $EXPECTED_STATS_FILE $FILTERED_STATS_FILE | \
grep -F -v -e "msg.cache.max_collisions" \
-e "rrset.cache.max_collisions" | \
> $REST_STATS_FILE
# It filtered out the 'max_collisions', since at random, a msg
# or rrset cache entry could have a cache hash collision. */
if test $DEBUG -ne 0; then
echo "Rest:"
cat $REST_STATS_FILE
+82
View File
@@ -0,0 +1,82 @@
; config options
server:
; This is the test key 29332 in the testdata.
trust-anchor: ". 3600 IN DS 29332 8 2 b75e26316631b6e37cbc977323a08769f86e36a10fee888676d35f61e2ff4181"
val-override-date: "20201020135527"
target-fetch-policy: "0 0 0 0 0"
qname-minimisation: no
fake-sha1: yes
trust-anchor-signaling: no
minimal-responses: no
log-servfail: yes
forward-zone:
name: "."
forward-addr: 10.5.5.5
CONFIG_END
SCENARIO_BEGIN Test nxdomain that gets unsigned response
; and the DS lookup that it makes gets an NSEC NXDOMAIN response.
; 10.5.5.5 forwarder
RANGE_BEGIN 0 100
ADDRESS 10.5.5.5
; unsigned NXDOMAIN response, from the first forwarder, here it is served
; from the upstream.
ENTRY_BEGIN
MATCH opcode qtype qname
ADJUST copy_id
REPLY QR NXDOMAIN
SECTION QUESTION
example.veryinvalid. IN TXT
SECTION AUTHORITY
. 3600 IN SOA ns.root. host.root. 1 3600 3600 3600 3600
ENTRY_END
; DNSKEY answer, using CSK for test simplicity.
ENTRY_BEGIN
MATCH opcode qtype qname
ADJUST copy_id
REPLY QR NOERROR
SECTION QUESTION
. IN DNSKEY
SECTION ANSWER
. IN DNSKEY 257 3 8 AwEAAb4WMOTBLTFvmBra5m6SK4VfViOzmvyUAU0qv861ZQXeEFvwlndqNU9rwRsMxrSWAYs5nHErKDn49usC/HyxxW1477iGFHhfgL4mjNreJm9zft2QFB1VLbRbEPYdDMLCn4co0qnG7/KG8W2i8Pym1L7f+aREwbLo+/716AS2PbaKMhfWLKLiq5wnBcUClQMNzCiwhqxDJp1oePqfkVdeUgXOtgi0dYRIKyQFhJ5VWJ22npoi/Gif0XLCADAlAwRLKc8o/yJkCxskzgpHpw5Cki1lclg0aq4ssOuPRQ+ne6IHYCz9D2mwzulblhLFamKdq7aHzNt4NlyxhpANVFiKLD8= ;{id = 29332 (ksk), size = 2048b}
. 3600 IN RRSIG DNSKEY 8 0 3600 20201116135527 20201019135527 29332 . ToK8hJrGa+kNu6y8FpRwZq2FjDPBAk5Ctchia3Vu9yTth2dR7BhK2ALTWVBwAQGwiwxXKoVK9QCxdQM0ti7CVb9x75bejkd2E6UGWVmqyTRPpn3D43qYARm87y3ZVKG7LlWHp8UOf21XLp1H7R+wuipIvBJ1XA+QGXThPdbV9EEz1kKGdprBfdpFkQdcAiuYYrOTa5cJ11z32mGiQ12fWjpb4UUbfcoDD9YOoa/S5a6h7jYBOfm75ZB8UCW3Z/SlsN8KIfYZsg5CZphpf38XH5uNLMmzpaWYhfamJZJve9Isx4eILNmdMLK4E8ESwDFCVNzMIqdf20VRg6Lh7nwQeA==
ENTRY_END
; answer for DS, from another forwarder, here returned from the test upstream.
ENTRY_BEGIN
MATCH opcode qtype qname
ADJUST copy_id
REPLY QR NXDOMAIN
SECTION QUESTION
veryinvalid. IN DS
SECTION AUTHORITY
versicherung. 3600 IN NSEC vet. NS DS RRSIG NSEC
versicherung. 3600 IN RRSIG NSEC 8 1 3600 20201116135527 20201019135527 29332 . AVAON9Y7AVwX9YWQK8JPcB6Wk/tEfQT7JrLiCRlBBQA0+mpVYYYtMyrm4aMjkhusqYcnpIZoLGOI/dxJjIwDgnMkd4EqY2oICea3I260f8z2v9e7zNobyUTjkoWsmLPc7VRLtEGKu1XyVpt7DX6ElGoSUhU4JsTx7wkkXU0SGAakL0bhK8K68B92NEVwgKX4D7+kVfpjc0aHaB3rAkhQCM/G0jEFp0RuhTX1aru6IuYrZmjW0dvQ2niec6NaYzvuGnbhMLlFLuXqSmI2B7uIFx894usd1cVWnSRg49bAkuiEv5q04ltRel1huJBGGiZlLEwanS5g53C5DHfq10OUrw==
. 3600 IN NSEC aaa. NS SOA RRSIG NSEC DNSKEY ZONEMD
. 3600 IN RRSIG NSEC 8 0 3600 20201116135527 20201019135527 29332 . E8r6rpFgFBUda2GnFSMzHZLtjy1dT+ZS0wPRE12RNwVK547bo2vByv9EFhOHS6sEIFqX+AmIJotuiEPKnCFUTr6FKscaxtw38dJRZ3wldqV6dmqUiRmz91crDCV5nSL45FIbkWKk1Q+tnXie3sZ4zwBc12kGg2BttMAQ0i4sbMbf6EUNYZGwYzSB0/VhXVJcl8gl+5lfpiVqfWNZI7vTEaHqrC2gBC3UK1cQE9lQOqhJ6H5ThA1FR9j/mZFM9sG5vQ2Mqlzl2iiN2Y6mCptDY1vwfff6AnT0YeDwJ/XwGisMZrSvTCYaiRndb8CUUmCr23AFy5OER1rmeFGkHX5+WQ==
. 3600 IN SOA ns.root. host.root. 1 3600 3600 3600 3600
. 3600 IN RRSIG SOA 8 0 3600 20201116135527 20201019135527 29332 . tVeReLMXPnl6rk4QX94xy9lCodQ+xc39lokbNkvbXnTURNCOAwtNiMMPlAAJ3/HTpIxo175gPfupACIveBtgajdp85jUIvLMOM5B6lX80+dUPBGZ4gHVjf+8EGnr7q2wnW2+KcJu0OhN2g+YqCV6aPi8pzuAp+AMsBYcMfXqEQq9Lxqv6TL50MUCJN3GPCyBIdjbs/A+ZB7D1EOO1YgdbsMHK/pWKYt4UfBFfekoA6joIGf4vBKKRTWnoo0BcrFob3AW1SyJkoxoqEsN3YAVL9jNkJCkU0/adLypHgDNayLgsWI5/o4Ng8LxN6tNxAilkMhcGY80T5g0uo+ukY7McA==
ENTRY_END
RANGE_END
STEP 1 QUERY
ENTRY_BEGIN
REPLY RD DO
SECTION QUESTION
example.veryinvalid. IN TXT
ENTRY_END
STEP 10 CHECK_ANSWER
ENTRY_BEGIN
MATCH all
REPLY QR RD RA DO SERVFAIL
SECTION QUESTION
example.veryinvalid. IN TXT
SECTION ANSWER
ENTRY_END
SCENARIO_END
+4
View File
@@ -243,6 +243,7 @@ config_create(void)
cfg->views = NULL;
cfg->acls = NULL;
cfg->tcp_connection_limits = NULL;
cfg->auth_task_threads = 0;
cfg->harden_short_bufsize = 1;
cfg->harden_large_queries = 0;
cfg->harden_glue = 1;
@@ -457,6 +458,7 @@ struct config_file* config_create_forlib(void)
cfg->neg_cache_size = 100 * 1024;
cfg->donotquery_localhost = 0; /* allow, so that you can ask a
forward nameserver running on localhost */
cfg->auth_task_threads = 0; /* no extra threads to spawn in library */
cfg->val_log_level = 2; /* to fill why_bogus with */
cfg->val_log_squelch = 1;
cfg->minimal_responses = 0;
@@ -742,6 +744,7 @@ int config_set_option(struct config_file* cfg, const char* opt,
else S_SIZET_OR_ZERO("unwanted-reply-threshold:", unwanted_threshold)
else S_STRLIST("private-address:", private_address)
else S_STRLIST("private-domain:", private_domain)
else S_NUMBER_OR_ZERO("auth-task-threads:", auth_task_threads)
else S_YNO("do-not-query-localhost:", donotquery_localhost)
else S_STRLIST("do-not-query-address:", donotqueryaddrs)
else S_STRLIST("auto-trust-anchor-file:", auto_trust_anchor_file_list)
@@ -1284,6 +1287,7 @@ config_get_option(struct config_file* cfg, const char* opt,
else O_LST(opt, "root-hints", root_hints)
else O_LS2(opt, "access-control", acls)
else O_LS2(opt, "tcp-connection-limit", tcp_connection_limits)
else O_DEC(opt, "auth-task-threads", auth_task_threads)
else O_LST(opt, "do-not-query-address", donotqueryaddrs)
else O_LST(opt, "private-address", private_address)
else O_LST(opt, "private-domain", private_domain)
+2
View File
@@ -291,6 +291,8 @@ struct config_file {
/** list of tcp connection limitss, linked list */
struct config_str2list* tcp_connection_limits;
/** auth task threads, or 0 disabled. */
int auth_task_threads;
/** harden against very small edns buffer sizes */
int harden_short_bufsize;
+1
View File
@@ -595,6 +595,7 @@ name-v4{COLON} { YDVAR(1, VAR_IPSET_NAME_V4) }
name-v6{COLON} { YDVAR(1, VAR_IPSET_NAME_V6) }
udp-upstream-without-downstream{COLON} { YDVAR(1, VAR_UDP_UPSTREAM_WITHOUT_DOWNSTREAM) }
tcp-connection-limit{COLON} { YDVAR(2, VAR_TCP_CONNECTION_LIMIT) }
auth-task-threads{COLON} { YDVAR(1, VAR_AUTH_TASK_THREADS) }
answer-cookie{COLON} { YDVAR(1, VAR_ANSWER_COOKIE ) }
cookie-secret{COLON} { YDVAR(1, VAR_COOKIE_SECRET) }
cookie-secret-file{COLON} { YDVAR(1, VAR_COOKIE_SECRET_FILE) }
+11
View File
@@ -193,6 +193,7 @@ extern struct config_parser_state* cfg_parser;
%token VAR_FALLBACK_ENABLED VAR_TLS_ADDITIONAL_PORT VAR_LOW_RTT VAR_LOW_RTT_PERMIL
%token VAR_FAST_SERVER_PERMIL VAR_FAST_SERVER_NUM
%token VAR_ALLOW_NOTIFY VAR_TLS_WIN_CERT VAR_TCP_CONNECTION_LIMIT
%token VAR_AUTH_TASK_THREADS
%token VAR_ANSWER_COOKIE VAR_COOKIE_SECRET VAR_IP_RATELIMIT_COOKIE
%token VAR_FORWARD_NO_CACHE VAR_STUB_NO_CACHE VAR_LOG_SERVFAIL VAR_DENY_ANY
%token VAR_UNKNOWN_SERVER_TIME_LIMIT VAR_LOG_TAG_QUERYREPLY
@@ -343,6 +344,7 @@ content_server: server_num_threads | server_verbosity | server_port |
server_tls_cert_bundle | server_tls_additional_port | server_low_rtt |
server_fast_server_permil | server_fast_server_num | server_tls_win_cert |
server_tcp_connection_limit | server_log_servfail | server_deny_any |
server_auth_task_threads |
server_unknown_server_time_limit | server_log_tag_queryreply |
server_discard_timeout | server_wait_limit | server_wait_limit_cookie |
server_wait_limit_netblock | server_wait_limit_cookie_netblock |
@@ -4239,6 +4241,15 @@ server_tcp_connection_limit: VAR_TCP_CONNECTION_LIMIT STRING_ARG STRING_ARG
}
}
;
server_auth_task_threads: VAR_AUTH_TASK_THREADS STRING_ARG
{
OUTYY(("P(server_auth_task_threads:%s)\n", $2));
if(atoi($2) == 0 && strcmp($2, "0") != 0)
yyerror("number expected");
else cfg_parser->cfg->auth_task_threads = atoi($2);
free($2);
}
;
server_answer_cookie: VAR_ANSWER_COOKIE STRING_ARG
{
OUTYY(("P(server_answer_cookie:%s)\n", $2));
+3 -1
View File
@@ -280,7 +280,9 @@ int packed_rr_to_string(struct ub_packed_rrset_key* rrset, size_t i,
size_t rlen = rrset->rk.dname_len + 2 + 2 + 4 + d->rr_len[i];
time_t adjust = 0;
log_assert(dest_len > 0 && dest);
if(rlen > dest_len) {
/* rlen is the length written into rr, dest_len bounds the output
* string; check both, callers can pass a dest_len over sizeof(rr). */
if(rlen > dest_len || rlen > sizeof(rr)) {
dest[0] = 0;
return 0;
}
+2
View File
@@ -72,6 +72,7 @@
#include "libunbound/libworker.h"
#include "libunbound/context.h"
#include "libunbound/worker.h"
#include "libunbound/authload.h"
#include "util/tube.h"
#include "util/config_file.h"
#include "daemon/remote.h"
@@ -191,6 +192,7 @@ fptr_whitelist_event(void (*fptr)(int, short, void *))
else if(fptr == &comm_point_doq_callback) return 1;
#endif
else if(fptr == &fast_reload_service_cb) return 1;
else if(fptr == &worker_auth_load_service_cb) return 1;
#ifdef USE_DNSTAP
else if(fptr == &dtio_output_cb) return 1;
else if(fptr == &dtio_cmd_cb) return 1;
+21
View File
@@ -72,6 +72,14 @@ static ub_thread_key_type logkey;
#ifndef THREADS_DISABLED
/** pthread mutex to protect FILE* */
static lock_basic_type log_lock;
#ifdef HAVE_PTHREAD
/* Guards the one-time initialization below. Without this, two threads
* calling log_init() for the first time concurrently (e.g. via
* ub_ctx_create() from a multi-threaded application) can both observe
* key_created==0 and both call lock_basic_init(&log_lock), a data race
* that reinitializes/corrupts an in-use mutex. */
static pthread_once_t log_lock_once = PTHREAD_ONCE_INIT;
#endif
#endif
/** the identity of this executable/process */
static const char* ident="unbound";
@@ -85,15 +93,28 @@ static int log_time_asc = 0;
/** print time in iso format */
static int log_time_iso = 0;
#if !defined(THREADS_DISABLED) && defined(HAVE_PTHREAD)
static void log_lock_init_once(void)
{
ub_thread_key_create(&logkey, NULL);
lock_basic_init(&log_lock);
key_created = 1;
}
#endif
void
log_init(const char* filename, int use_syslog, const char* chrootdir)
{
FILE *f;
#if !defined(THREADS_DISABLED) && defined(HAVE_PTHREAD)
(void)pthread_once(&log_lock_once, log_lock_init_once);
#else
if(!key_created) {
key_created = 1;
ub_thread_key_create(&logkey, NULL);
lock_basic_init(&log_lock);
}
#endif
lock_basic_lock(&log_lock);
if(logfile
#if defined(HAVE_SYSLOG_H) || defined(UB_ON_WINDOWS)
+360
View File
@@ -58,6 +58,9 @@
#include "sldns/wire2str.h"
#include "sldns/str2wire.h"
#include <fcntl.h>
#ifdef HAVE_POLL_H
#include <poll.h>
#endif
#ifdef HAVE_OPENSSL_SSL_H
#include <openssl/ssl.h>
#include <openssl/evp.h>
@@ -92,6 +95,9 @@ int RRSET_ROUNDROBIN = 1;
/** log tag queries with name instead of 'info' for filtering */
int LOG_TAG_QUERYREPLY = 0;
/** What number of loop iterations is too much for sock poll retries */
#define SOCK_POLL_LOOP_MAX 200
#ifdef HAVE_SSL
static struct tls_session_ticket_key {
unsigned char *key_name;
@@ -2083,3 +2089,357 @@ hex_pton(const char* src, uint8_t* target, size_t targsize)
}
return t-target;
}
int
sock_poll_timeout(int fd, int timeout, int pollin, int pollout, int* event)
{
int loopcount = 0;
/* Loop if the system call returns an errno to do so, like EINTR. */
log_assert(pollin || pollout);
while(1) {
struct pollfd p, *fds;
int nfds, ret;
if(++loopcount > SOCK_POLL_LOOP_MAX) {
log_err("sock_poll_timeout: loop");
if(event)
*event = 0;
return 0;
}
if(fd == -1) {
fds = NULL;
nfds = 0;
} else {
fds = &p;
nfds = 1;
memset(&p, 0, sizeof(p));
p.fd = fd;
#ifndef USE_WINSOCK
p.events = POLLERR
| POLLHUP
;
#endif
if(pollin)
p.events |= POLLIN;
if(pollout)
p.events |= POLLOUT;
}
#ifndef USE_WINSOCK
ret = poll(fds, nfds, timeout);
#else
if(fds == NULL) {
Sleep(timeout);
ret = 0;
} else {
ret = WSAPoll(fds, nfds, timeout);
}
#endif
if(ret == -1) {
#ifndef USE_WINSOCK
if(
errno == EINTR || errno == EAGAIN
# ifdef EWOULDBLOCK
|| errno == EWOULDBLOCK
# endif
) continue; /* Try again. */
#endif
/* For WSAPoll we only get errors here:
* o WSAENETDOWN
* o WSAEFAULT
* o WSAEINVAL
* o WSAENOBUFS
*/
log_err("poll: %s", sock_strerror(errno));
if(event)
*event = 0;
return 0;
} else if(ret == 0) {
/* Timeout */
if(event)
*event = 0;
return 1;
}
break;
}
if(event)
*event = 1;
return 1;
}
int
create_socketpair(int* pair, struct ub_randstate* rand)
{
#ifndef USE_WINSOCK
if(socketpair(AF_UNIX, SOCK_STREAM, 0, pair) == -1) {
log_err("socketpair: %s", strerror(errno));
return 0;
}
(void)rand;
#else
struct sockaddr_in addr, baddr, accaddr, connaddr;
socklen_t baddrlen, accaddrlen, connaddrlen;
uint8_t localhost[] = {127, 0, 0, 1};
uint8_t nonce[16], recvnonce[16];
size_t i;
int lst, pollin_event, bcount, loopcount;
int connect_poll_timeout = 200; /* msec to wait for connection */
ssize_t ret;
pair[0] = -1;
pair[1] = -1;
for(i=0; i<sizeof(nonce); i++) {
nonce[i] = ub_random_max(rand, 256);
}
lst = socket(AF_INET, SOCK_STREAM, 0);
if(lst == -1) {
log_err("create_socketpair: socket: %s", sock_strerror(errno));
return 0;
}
memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_port = 0;
memcpy(&addr.sin_addr, localhost, 4);
if(bind(lst, (struct sockaddr*)&addr, (socklen_t)sizeof(addr))
== -1) {
log_err("create socketpair: bind: %s", sock_strerror(errno));
sock_close(lst);
return 0;
}
if(listen(lst, 12) == -1) {
log_err("create socketpair: listen: %s", sock_strerror(errno));
sock_close(lst);
return 0;
}
pair[1] = socket(AF_INET, SOCK_STREAM, 0);
if(pair[1] == -1) {
log_err("create socketpair: socket: %s", sock_strerror(errno));
sock_close(lst);
return 0;
}
baddrlen = (socklen_t)sizeof(baddr);
if(getsockname(lst, (struct sockaddr*)&baddr, &baddrlen) == -1) {
log_err("create socketpair: getsockname: %s",
sock_strerror(errno));
sock_close(lst);
sock_close(pair[1]);
pair[1] = -1;
return 0;
}
if(baddrlen > (socklen_t)sizeof(baddr)) {
log_err("create socketpair: getsockname returned addr too big");
sock_close(lst);
sock_close(pair[1]);
pair[1] = -1;
return 0;
}
/* the socket is blocking */
if(connect(pair[1], (struct sockaddr*)&baddr, baddrlen) == -1) {
log_err("create socketpair: connect: %s",
sock_strerror(errno));
sock_close(lst);
sock_close(pair[1]);
pair[1] = -1;
return 0;
}
if(!sock_poll_timeout(lst, connect_poll_timeout, 1, 0, &pollin_event)) {
log_err("create socketpair: poll for accept failed: %s",
sock_strerror(errno));
sock_close(lst);
sock_close(pair[1]);
pair[1] = -1;
return 0;
}
if(!pollin_event) {
log_err("create socketpair: poll timeout for accept");
sock_close(lst);
sock_close(pair[1]);
pair[1] = -1;
return 0;
}
accaddrlen = (socklen_t)sizeof(accaddr);
pair[0] = accept(lst, (struct sockaddr*)&accaddr, &accaddrlen);
if(pair[0] == -1) {
log_err("create socketpair: accept: %s", sock_strerror(errno));
sock_close(lst);
sock_close(pair[1]);
pair[1] = -1;
return 0;
}
if(accaddrlen > (socklen_t)sizeof(accaddr)) {
log_err("create socketpair: accept returned addr too big");
sock_close(lst);
sock_close(pair[0]);
sock_close(pair[1]);
pair[0] = -1;
pair[1] = -1;
return 0;
}
if(accaddr.sin_family != AF_INET ||
memcmp(localhost, &accaddr.sin_addr, 4) != 0) {
log_err("create socketpair: accept from wrong address");
sock_close(lst);
sock_close(pair[0]);
sock_close(pair[1]);
pair[0] = -1;
pair[1] = -1;
return 0;
}
connaddrlen = (socklen_t)sizeof(connaddr);
if(getsockname(pair[1], (struct sockaddr*)&connaddr, &connaddrlen)
== -1) {
log_err("create socketpair: getsockname connectedaddr: %s",
sock_strerror(errno));
sock_close(lst);
sock_close(pair[0]);
sock_close(pair[1]);
pair[0] = -1;
pair[1] = -1;
return 0;
}
if(connaddrlen > (socklen_t)sizeof(connaddr)) {
log_err("create socketpair: getsockname connectedaddr returned addr too big");
sock_close(lst);
sock_close(pair[0]);
sock_close(pair[1]);
pair[0] = -1;
pair[1] = -1;
return 0;
}
if(connaddr.sin_family != AF_INET ||
memcmp(localhost, &connaddr.sin_addr, 4) != 0) {
log_err("create socketpair: getsockname connectedaddr returned wrong address");
sock_close(lst);
sock_close(pair[0]);
sock_close(pair[1]);
pair[0] = -1;
pair[1] = -1;
return 0;
}
if(accaddr.sin_port != connaddr.sin_port) {
log_err("create socketpair: accept from wrong port");
sock_close(lst);
sock_close(pair[0]);
sock_close(pair[1]);
pair[0] = -1;
pair[1] = -1;
return 0;
}
sock_close(lst);
loopcount = 0;
bcount = 0;
while(1) {
if(++loopcount > SOCK_POLL_LOOP_MAX) {
log_err("create socketpair: send failed due to loop");
sock_close(pair[0]);
sock_close(pair[1]);
pair[0] = -1;
pair[1] = -1;
return 0;
}
ret = send(pair[1], (void*)(nonce+bcount),
sizeof(nonce)-bcount, 0);
if(ret == -1) {
if(
#ifndef USE_WINSOCK
errno == EINTR || errno == EAGAIN
# ifdef EWOULDBLOCK
|| errno == EWOULDBLOCK
# endif
#else
WSAGetLastError() == WSAEINTR ||
WSAGetLastError() == WSAEINPROGRESS ||
WSAGetLastError() == WSAEWOULDBLOCK
#endif
)
continue; /* Try again. */
log_err("create socketpair: send: %s", sock_strerror(errno));
sock_close(pair[0]);
sock_close(pair[1]);
pair[0] = -1;
pair[1] = -1;
return 0;
} else if(ret+(ssize_t)bcount != sizeof(nonce)) {
bcount += ret;
if((size_t)bcount < sizeof(nonce))
continue;
}
break;
}
if(!sock_poll_timeout(pair[0], connect_poll_timeout, 1, 0, &pollin_event)) {
log_err("create socketpair: poll failed: %s",
sock_strerror(errno));
sock_close(pair[0]);
sock_close(pair[1]);
pair[0] = -1;
pair[1] = -1;
return 0;
}
if(!pollin_event) {
log_err("create socketpair: poll timeout for recv");
sock_close(pair[0]);
sock_close(pair[1]);
pair[0] = -1;
pair[1] = -1;
return 0;
}
loopcount = 0;
bcount = 0;
while(1) {
if(++loopcount > SOCK_POLL_LOOP_MAX) {
log_err("create socketpair: recv failed due to loop");
sock_close(pair[0]);
sock_close(pair[1]);
pair[0] = -1;
pair[1] = -1;
return 0;
}
ret = recv(pair[0], (void*)(recvnonce+bcount),
sizeof(nonce)-bcount, 0);
if(ret == -1) {
if(
#ifndef USE_WINSOCK
errno == EINTR || errno == EAGAIN
# ifdef EWOULDBLOCK
|| errno == EWOULDBLOCK
# endif
#else
WSAGetLastError() == WSAEINTR ||
WSAGetLastError() == WSAEINPROGRESS ||
WSAGetLastError() == WSAEWOULDBLOCK
#endif
)
continue; /* Try again. */
log_err("create socketpair: recv: %s", sock_strerror(errno));
sock_close(pair[0]);
sock_close(pair[1]);
pair[0] = -1;
pair[1] = -1;
return 0;
} else if(ret == 0) {
log_err("create socketpair: stream closed");
sock_close(pair[0]);
sock_close(pair[1]);
pair[0] = -1;
pair[1] = -1;
return 0;
} else if(ret+(ssize_t)bcount != sizeof(nonce)) {
bcount += ret;
if((size_t)bcount < sizeof(nonce))
continue;
}
break;
}
if(memcmp(nonce, recvnonce, sizeof(nonce)) != 0) {
log_err("create socketpair: recv wrong nonce");
sock_close(pair[0]);
sock_close(pair[1]);
pair[0] = -1;
pair[1] = -1;
return 0;
}
#endif
return 1;
}
+16
View File
@@ -604,4 +604,20 @@ ssize_t hex_ntop(uint8_t const *src, size_t srclength, char *target,
/** Convert hexadecimal data to binary. */
ssize_t hex_pton(const char* src, uint8_t* target, size_t targsize);
/**
* This routine polls a socket for readiness.
* @param fd: file descriptor, -1 uses no fd for a timer only.
* @param timeout: time in msec to wait. 0 means nonblocking test,
* -1 waits blocking for events.
* @param pollin: check for input event.
* @param pollout: check for output event.
* @param event: output variable, set to true if the event happens.
* It is false if there was an error or timeout.
* @return false is system call failure, also logged.
*/
int sock_poll_timeout(int fd, int timeout, int pollin, int pollout, int* event);
/** create a socketpair for bidirectional communication, false on failure */
int create_socketpair(int* pair, struct ub_randstate* rand);
#endif /* NET_HELP_H */
+14
View File
@@ -1323,6 +1323,20 @@ int val_has_signed_nsecs(struct reply_info* rep, char** reason)
return 0;
}
void val_has_auth_nsecs(struct reply_info* rep, int* has_nsec, int* has_nsec3)
{
size_t i, num_nsec = 0, num_nsec3 = 0;
for(i=rep->an_numrrsets; i<rep->an_numrrsets+rep->ns_numrrsets; i++) {
if(rep->rrsets[i]->rk.type == htons(LDNS_RR_TYPE_NSEC))
num_nsec++;
else if(rep->rrsets[i]->rk.type == htons(LDNS_RR_TYPE_NSEC3))
num_nsec3++;
else continue;
}
*has_nsec = (num_nsec != 0);
*has_nsec3 = (num_nsec3 != 0);
}
struct dns_msg*
val_find_DS(struct module_env* env, uint8_t* nm, size_t nmlen, uint16_t c,
struct regional* region, uint8_t* topname)
+8
View File
@@ -410,6 +410,14 @@ void val_blacklist(struct sock_list** blacklist, struct regional* region,
*/
int val_has_signed_nsecs(struct reply_info* rep, char** reason);
/**
* See if there are NSECs, or NSEC3s in the authority section.
* @param rep: reply to check
* @param has_nsec: returned true if it has nsecs.
* @param has_nsec3: returned true if it has nsec3s.
*/
void val_has_auth_nsecs(struct reply_info* rep, int* has_nsec, int* has_nsec3);
/**
* Return algo number for favorite (best) algorithm that we support in DS.
* @param ds_rrset: the DSes in this rrset are inspected and best algo chosen.
+20
View File
@@ -3116,6 +3116,7 @@ ds_response_to_ke(struct module_qstate* qstate, struct val_qstate* vq,
case sec_status_unchecked:
default:
/* NSEC proof did not work, try next */
verbose(VERB_ALGO, "NSEC proof did not prove insecure delegation, try NSEC3");
break;
}
@@ -3151,6 +3152,25 @@ ds_response_to_ke(struct module_qstate* qstate, struct val_qstate* vq,
*ke = NULL;
return 0;
case sec_status_bogus:
/* It could be that the NSEC proof failed,
* and, then tried NSEC3. */
{
int has_nsec=0, has_nsec3=0;
val_has_auth_nsecs(msg->rep, &has_nsec,
&has_nsec3);
if(!has_nsec3 && has_nsec) {
/* The NSECs are the cause, mention that in the error message. */
verbose(VERB_DETAIL, "NSECs for the "
"referral did not prove no DS.");
errinf_ede(qstate, "NSECs for the referral did not prove no DS", LDNS_EDE_DNSSEC_BOGUS);
goto return_bogus;
}
if(!has_nsec3 && !has_nsec) {
verbose(VERB_DETAIL, "absence of NSECs and NSEC3s when attempting to prove no DS.");
errinf_ede(qstate, "no NSECs or NSEC3s when attempting to prove no DS", LDNS_EDE_DNSSEC_BOGUS);
goto return_bogus;
}
}
verbose(VERB_DETAIL, "NSEC3s for the "
"referral did not prove no DS.");
errinf_ede(qstate, reason, reason_bogus);