Compare commits

...
25 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 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 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
31 changed files with 2339 additions and 492 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 \
+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;
};
/**
+1 -369
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
@@ -4466,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)
@@ -6365,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);
@@ -7153,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,
+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 */
+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 */
+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;
+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-----
+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));
+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;
+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 */