Compare commits

..
Author SHA1 Message Date
W.C.A. Wijngaards 48d0aea60a - auth-load-thread, unit test for auth_load_http. 2026-08-11 16:50:24 +02:00
W.C.A. Wijngaards 17bbcac979 - auth-load-thread, fix buffer free in http processing. 2026-08-11 16:50:00 +02:00
W.C.A. Wijngaards fd8ebbb19c Merge branch 'master' into auth-load-thread 2026-08-11 16:23:20 +02:00
W.C.A. Wijngaards 2adbbea365 - auth-load-thread, print thread details in log at high verbosity, 8. 2026-08-11 16:23:03 +02:00
W.C.A. Wijngaards 8ee0bca833 - Fix stat_values.tdir test to have less test failures. 2026-08-11 10:07:38 +02:00
W.C.A. Wijngaards c58e6add2b - Fix #1492 from zacek: Data race in log_init() on
key_created/log_lock when calling ub_ctx_create()
  concurrently from multiple threads.
2026-08-11 09:42:30 +02:00
W.C.A. Wijngaards 156c00a727 Merge branch 'master' into auth-load-thread 2026-08-10 16:42:59 +02:00
W.C.A. Wijngaards d753f95956 - auth-load-thread, print time taken during auth load processing. 2026-08-10 16:42:04 +02:00
W.C.A. Wijngaards 93a56205cf - Fix #1489 from jplesnik: Replace removed Python 2 C API
macros for SWIG 4.5.0 compatibility.
2026-08-07 08:57:32 +02:00
W.C.A. Wijngaards 709f622658 Note issue number in Changlog entry. 2026-08-06 17:15:55 +02:00
akhanin-dnsfandGitHub 307fc6f062 - Fix bounds check in packed_rr_to_string, it checked the (#1488)
assembled rr length against the output string length
  dest_len, instead of against the size of the rr buffer it
  writes into. Callers in cachedump.c and remote.c pass a
  dest_len larger than that buffer.
- Unit test for packed_rr_to_string.
2026-08-06 17:04:05 +02:00
W.C.A. Wijngaards 8b33c5d7ff - Fix #1487: regression in 1.26.0, ipsecmod is now always
partly enabled.
2026-08-06 09:46:18 +02:00
W.C.A. Wijngaards 36bd52afb9 Fix typo in Changelog. 2026-08-06 09:08:33 +02:00
W.C.A. Wijngaards b7d13ff12b - Fix ##1485: the list_forwards command omits port numbers.
The list_forwards and list_stubs commands for
  unbound-control print port and tls auth name.
2026-08-06 09:08:17 +02:00
W.C.A. Wijngaards bdfcfb861f - Fix to set makedist.sh to not wget config.sub and
config.guess from git repo. The fetch times out, and the
  version from libtoolize is much more recent now than
  that it was when the wget was added.
2026-08-04 10:04:34 +02:00
W.C.A. Wijngaards b444deffd2 Note 1.26.0 release. 2026-08-04 10:01:59 +02:00
W.C.A. Wijngaards ff28b7e5cf - For #1483: The failure reason when an NSEC NXDOMAIN is
encountered when looking for an insecure delegation, is
  fixed to mention the NSEC records, instead of nonexistent
  NSEC3 records, that it attempted.
2026-07-31 09:53:47 +02:00
W.C.A. Wijngaards 79b84bbc91 - Fix #1482: DNS-over-QUIC doesn't work with simple config.
That fixes interface-automatic for use with doq service.
2026-07-30 08:24:42 +02:00
W.C.A. Wijngaards cbfc3b0342 - Tag for 1.26.0rc1. The repo continues with version 1.26.1. 2026-07-28 09:45:59 +02:00
W.C.A. Wijngaards 61d6c0e766 - auth-load-thread, implement active thread counter for auth load threads. 2026-07-07 17:21:16 +02:00
W.C.A. Wijngaards b4daa2d0fa - auth-load-thread, use define for constant for number of records before poll. 2026-07-07 16:26:42 +02:00
W.C.A. Wijngaards 425b701fb9 - auth-load-thread, fix memory leak on alloc failure when appending rrset
copy for ixfr main zone data copy.
2026-07-03 16:39:28 +02:00
W.C.A. Wijngaards 0a5cde80f1 - auth-load-thread, auth-task-threads: num config option that enables and
disables the auth load thread.
2026-07-03 14:03:15 +02:00
W.C.A. Wijngaards 58ede90fdb Merge branch 'master' into auth-load-thread 2026-07-02 15:28:24 +02:00
W.C.A. Wijngaards 55ae8da032 - auth-load-thread, basic test and fixes so it works. 2026-07-02 11:10:06 +02:00
W.C.A. Wijngaards 6bd86df72e - auth-load-thread, simplify cleanup in end transfer load process. 2026-07-02 08:55:13 +02:00
W.C.A. Wijngaards 0efd3605cc Merge branch 'master' into auth-load-thread 2026-07-01 16:54:55 +02:00
W.C.A. Wijngaards a2f2f53ef9 - auth-load-thread, process end of successful transfer. 2026-07-01 16:54:36 +02:00
W.C.A. Wijngaards b5a03093f6 - auth-load-thread, check for quit during the processing. 2026-07-01 14:41:40 +02:00
W.C.A. Wijngaards 8eba898135 - auth-load-thread, process AXFR, by loading, swap in, delete of old. 2026-07-01 14:27:31 +02:00
W.C.A. Wijngaards 56f66de89f - auth-load-thread, process IXFR, by making a copy, adjust changes, swap in. 2026-07-01 13:05:19 +02:00
W.C.A. Wijngaards 2fbaee2255 - auth-load-thread, process transfer content and swap result back in. 2026-06-30 16:53:27 +02:00
W.C.A. Wijngaards a122490461 - auth-load-thread, poll for quit and process load transfer end. 2026-06-29 16:52:20 +02:00
W.C.A. Wijngaards 7826b4f306 Merge branch 'master' into auth-load-thread 2026-06-29 16:00:01 +02:00
W.C.A. Wijngaards 380994219f - auth-load-thread, make and run auth load thread. 2026-06-26 17:11:12 +02:00
W.C.A. Wijngaards 153accb8de - auth-load-thread, put create_socketpair and sock_poll_timeout into
util/net_help.h
2026-06-26 15:22:08 +02:00
W.C.A. Wijngaards 7bb1c62263 - auth-load-thread, add authload.c to Makefile.in. 2026-06-26 14:27:12 +02:00
W.C.A. Wijngaards 1578b6e180 - auth-load-thread, add services/authload.c and services/authload.h 2026-06-26 14:00:04 +02:00
71 changed files with 2982 additions and 1543 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;
};
/**
+23 -374
View File
@@ -115,9 +115,6 @@
#ifdef HAVE_NETDB_H
#include <netdb.h>
#endif
#ifdef HAVE_POLL_H
#include <poll.h>
#endif
/* just for portability */
#ifdef SQ
@@ -2681,7 +2678,7 @@ static int
ssl_print_name_dp(RES* ssl, const char* str, uint8_t* nm, uint16_t dclass,
struct delegpt* dp)
{
char buf[LDNS_MAX_DOMAINLEN];
char buf[LDNS_MAX_DOMAINLEN], portstr[128], tls_auth_name[256];
struct delegpt_ns* ns;
struct delegpt_addr* a;
int f = 0;
@@ -2696,13 +2693,32 @@ ssl_print_name_dp(RES* ssl, const char* str, uint8_t* nm, uint16_t dclass,
}
for(ns = dp->nslist; ns; ns = ns->next) {
dname_str(ns->name, buf);
if(!ssl_printf(ssl, "%s%s", (f?" ":""), buf))
if(ns->port != UNBOUND_DNS_PORT)
snprintf(portstr, sizeof(portstr), "@%d", ns->port);
else portstr[0]=0;
if(ns->tls_auth_name)
snprintf(tls_auth_name, sizeof(tls_auth_name), "#%s",
ns->tls_auth_name);
else tls_auth_name[0]=0;
if(!ssl_printf(ssl, "%s%s%s%s", (f?" ":""), buf, portstr,
tls_auth_name))
return 0;
f = 1;
}
for(a = dp->target_list; a; a = a->next_target) {
int port = (unsigned)((a->addr.ss_family == AF_INET) ?
ntohs(((struct sockaddr_in*)&a->addr)->sin_port) :
ntohs(((struct sockaddr_in6*)&a->addr)->sin6_port));
addr_to_str(&a->addr, a->addrlen, buf, sizeof(buf));
if(!ssl_printf(ssl, "%s%s", (f?" ":""), buf))
if(port != UNBOUND_DNS_PORT)
snprintf(portstr, sizeof(portstr), "@%d", port);
else portstr[0]=0;
if(a->tls_auth_name)
snprintf(tls_auth_name, sizeof(tls_auth_name), "#%s",
a->tls_auth_name);
else tls_auth_name[0]=0;
if(!ssl_printf(ssl, "%s%s%s%s", (f?" ":""), buf, portstr,
tls_auth_name))
return 0;
f = 1;
}
@@ -4447,92 +4463,6 @@ int remote_control_callback(struct comm_point* c, void* arg, int err,
return 0;
}
/**
* This routine polls a socket for readiness.
* @param fd: file descriptor, -1 uses no fd for a timer only.
* @param timeout: time in msec to wait. 0 means nonblocking test,
* -1 waits blocking for events.
* @param pollin: check for input event.
* @param pollout: check for output event.
* @param event: output variable, set to true if the event happens.
* It is false if there was an error or timeout.
* @return false is system call failure, also logged.
*/
static int
sock_poll_timeout(int fd, int timeout, int pollin, int pollout, int* event)
{
int loopcount = 0;
/* Loop if the system call returns an errno to do so, like EINTR. */
log_assert(pollin || pollout);
while(1) {
struct pollfd p, *fds;
int nfds, ret;
if(++loopcount > IPC_LOOP_MAX) {
log_err("sock_poll_timeout: loop");
if(event)
*event = 0;
return 0;
}
if(fd == -1) {
fds = NULL;
nfds = 0;
} else {
fds = &p;
nfds = 1;
memset(&p, 0, sizeof(p));
p.fd = fd;
#ifndef USE_WINSOCK
p.events = POLLERR
| POLLHUP
;
#endif
if(pollin)
p.events |= POLLIN;
if(pollout)
p.events |= POLLOUT;
}
#ifndef USE_WINSOCK
ret = poll(fds, nfds, timeout);
#else
if(fds == NULL) {
Sleep(timeout);
ret = 0;
} else {
ret = WSAPoll(fds, nfds, timeout);
}
#endif
if(ret == -1) {
#ifndef USE_WINSOCK
if(
errno == EINTR || errno == EAGAIN
# ifdef EWOULDBLOCK
|| errno == EWOULDBLOCK
# endif
) continue; /* Try again. */
#endif
/* For WSAPoll we only get errors here:
* o WSAENETDOWN
* o WSAEFAULT
* o WSAEINVAL
* o WSAENOBUFS
*/
log_err("poll: %s", sock_strerror(errno));
if(event)
*event = 0;
return 0;
} else if(ret == 0) {
/* Timeout */
if(event)
*event = 0;
return 1;
}
break;
}
if(event)
*event = 1;
return 1;
}
/** fast reload convert fast reload notification status to string */
static const char*
fr_notification_to_string(enum fast_reload_notification status)
@@ -6346,6 +6276,7 @@ fr_atomic_copy_cfg(struct config_file* oldcfg, struct config_file* cfg,
COPY_VAR_ptr(acls);
COPY_VAR_int(donotquery_localhost);
COPY_VAR_ptr(tcp_connection_limits);
COPY_VAR_int(auth_task_threads);
COPY_VAR_int(harden_short_bufsize);
COPY_VAR_int(harden_large_queries);
COPY_VAR_int(harden_glue);
@@ -6590,8 +6521,6 @@ fr_atomic_copy_cfg(struct config_file* oldcfg, struct config_file* cfg,
COPY_VAR_ptr(ipset_name_v6);
#endif
COPY_VAR_int(ede);
COPY_VAR_int(val_validation_attempts);
COPY_VAR_int(val_hash_attempts);
COPY_VAR_int(iter_scrub_ns);
COPY_VAR_int(iter_scrub_cname);
COPY_VAR_int(iter_scrub_rrsig);
@@ -7136,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,
+1 -1
View File
@@ -863,7 +863,7 @@ answer_from_cache(struct worker* worker, struct query_info* qinfo,
"validation");
goto bail_out; /* need to validate cache entry first */
} else if(rep->security == sec_status_secure) {
if(reply_an_ns_rrsets_secure(rep)) {
if(reply_all_rrsets_secure(rep)) {
*is_secure_answer = 1;
} else {
if(must_validate) {
+45
View File
@@ -1,3 +1,48 @@
11 August 2026: Wouter
- Fix #1492 from zacek: Data race in log_init() on
key_created/log_lock when calling ub_ctx_create()
concurrently from multiple threads.
- Fix stat_values.tdir test to have less test failures.
7 August 2026: Wouter
- Fix #1489 from jplesnik: Replace removed Python 2 C API
macros for SWIG 4.5.0 compatibility.
6 August 2026: Alex Khanin
- Fix #1488: bounds check in packed_rr_to_string, it checked
the assembled rr length against the output string length
dest_len, instead of against the size of the rr buffer it
writes into. Callers in cachedump.c and remote.c pass a
dest_len larger than that buffer.
- Unit test for packed_rr_to_string.
6 August 2026: Wouter
- Fix #1485: the list_forwards command omits port numbers.
The list_forwards and list_stubs commands for
unbound-control print port and tls auth name.
- Fix #1487: regression in 1.26.0, ipsecmod is now always
partly enabled.
4 August 2026: Wouter
- Fix to set makedist.sh to not wget config.sub and
config.guess from git repo. The fetch times out, and the
version from libtoolize is much more recent now than
that it was when the wget was added.
31 July 2026: Wouter
- For #1483: The failure reason when an NSEC NXDOMAIN is
encountered when looking for an insecure delegation, is
fixed to mention the NSEC records, instead of nonexistent
NSEC3 records, that it attempted.
30 July 2026: Wouter
- Fix #1482: DNS-over-QUIC doesn't work with simple config.
That fixes interface-automatic for use with doq service.
28 July 2026: Wouter
- Tag for 1.26.0rc1. The repo continues with version 1.26.1.
This became 1.26.0 on 4 aug 2026.
24 July 2026: Wouter
- Merge #1433 from jisakiel: Add new static zone type
block_aaaa to suppress AAAA queries.
+1 -7
View File
@@ -203,12 +203,6 @@ server:
# protects against poison attempts.
# iter-scrub-promiscuous: yes
# Limit on number of DNSSEC validation attempts for a query.
# val-validation-attempts: 32
# Limit on number of DNSSEC hash attempts for a query.
# val-hash-attempts: 32
# msec for waiting for an unknown server to reply. Increase if you
# are behind a slow satellite link, to eg. 1128.
# unknown-server-time-limit: 376
@@ -734,7 +728,7 @@ server:
# non-secure data. Useful to shield the users of this validator from
# potential bogus data in the additional section. All unsigned data
# in the additional section is removed from secure messages.
# val-clean-additional: no
# val-clean-additional: yes
# Turn permissive mode on to permit bogus messages. Thus, messages
# for which security checks failed will be returned to clients,
+1 -21
View File
@@ -2320,7 +2320,7 @@ These options are part of the ``server:`` section.
Use this setting to protect the users that rely on this validator for
authentication from potentially bad data in the additional section.
Default: no
Default: yes
@@UAHL@unbound.conf@val-log-level@@: *<number>*
@@ -3358,26 +3358,6 @@ These options are part of the ``server:`` section.
Default: yes
@@UAHL@unbound.conf@val-validation-attempts@@: *<number>*
Limit on the number of DNSSEC validation attempts for a query.
This protects against too large numbers of cryptographic operations,
like for a deep delegation chain.
This counts attempts to validate RRSIGs.
When it is exceeded, the query fails.
Default: 32
@@UAHL@unbound.conf@val-hash-attempts@@: *<number>*
Limit on the number of DNSSEC hash attempts for a query.
This protects against too large numbers of cryptographic operations,
like for a deep delegation chain.
This counts DS hash attempts to match DNSKEYs.
When it is exceeded, the query fails.
Default: 32
@@UAHL@unbound.conf@fast-server-permil@@: *<number>*
Specify how many times out of 1000 to pick from the set of fastest servers.
0 turns the feature off.
+7 -3
View File
@@ -59,6 +59,11 @@
static int
ipsecmod_apply_cfg(struct ipsecmod_env* ipsecmod_env, struct config_file* cfg)
{
if(cfg->ipsecmod_whitelist &&
!ipsecmod_whitelist_apply_cfg(ipsecmod_env, cfg))
return 0;
if(!cfg->ipsecmod_enabled)
return 1;
if(!cfg->ipsecmod_hook || (cfg->ipsecmod_hook && !cfg->ipsecmod_hook[0])) {
log_err("ipsecmod: missing ipsecmod-hook.");
return 0;
@@ -68,9 +73,6 @@ ipsecmod_apply_cfg(struct ipsecmod_env* ipsecmod_env, struct config_file* cfg)
cfg->ipsecmod_hook, strerror(errno));
return 0;
}
if(cfg->ipsecmod_whitelist &&
!ipsecmod_whitelist_apply_cfg(ipsecmod_env, cfg))
return 0;
return 1;
}
@@ -626,6 +628,8 @@ ipsecmod_inform_super(struct module_qstate* qstate, int id,
verbose(VERB_ALGO, "super has no ipsecmod state");
return;
}
if(!siq->enabled)
return;
if(qstate->return_msg) {
struct ub_packed_rrset_key* rrset_key = reply_find_answer_rrset(
+1 -19
View File
@@ -294,14 +294,7 @@ synth_cname_rrset(uint8_t** sname, size_t* snamelen, uint8_t* alias,
if(ttl_t > MAX_TTL) ttl_t = MAX_TTL;
ttl = (uint32_t)ttl_t;
sldns_write_uint32(cn->rr_first->ttl_data, ttl);
/* Do NOT write the clamp back into the packet buffer:
* parse_packet already sized every name from the original
* bytes and rdata_copy re-walks them trusting those sizes;
* mutating packet bytes between the walks breaks that
* invariant (compression pointers can target these TTL
* bytes). The DNAME rrset receives the same clamp at store
* time in rdata_copy, so the DNAME and the synthesized
* CNAME still carry equal TTLs in the cache. */
sldns_write_uint32(rrset->rr_first->ttl_data, ttl);
}
sldns_write_uint16(cn->rr_first->ttl_data+4, aliaslen);
memmove(cn->rr_first->ttl_data+6, alias, aliaslen);
@@ -655,9 +648,6 @@ scrub_normalize(sldns_buffer* pkt, struct msg_parse* msg,
if(rrset->type == LDNS_RR_TYPE_NS &&
rrset->rr_count > env->cfg->iter_scrub_ns) {
shorten_rrset(pkt, rrset, env->cfg->iter_scrub_ns);
} else if(rrset->type == LDNS_RR_TYPE_DS &&
rrset->rr_count > env->cfg->iter_scrub_ns) {
shorten_rrset(pkt, rrset, env->cfg->iter_scrub_ns);
}
prev = rrset;
rrset = rrset->rrset_all_next;
@@ -677,9 +667,6 @@ scrub_normalize(sldns_buffer* pkt, struct msg_parse* msg,
if(rrset->type == LDNS_RR_TYPE_NS &&
rrset->rr_count > env->cfg->iter_scrub_ns) {
shorten_rrset(pkt, rrset, env->cfg->iter_scrub_ns);
} else if(rrset->type == LDNS_RR_TYPE_DS &&
rrset->rr_count > env->cfg->iter_scrub_ns) {
shorten_rrset(pkt, rrset, env->cfg->iter_scrub_ns);
}
/* Mark the additional names from relevant rrset as OK. */
@@ -804,11 +791,6 @@ scrub_normalize(sldns_buffer* pkt, struct msg_parse* msg,
shorten_rrset(pkt, rrset, env->cfg->iter_scrub_ns);
}
}
} else if(rrset->type==LDNS_RR_TYPE_DS) {
if(rrset->rr_count > env->cfg->iter_scrub_ns) {
shorten_rrset(pkt, rrset,
env->cfg->iter_scrub_ns);
}
}
/* if this is type DS and we query for type DS we just got
* a referral answer for our type DS query, fix packet */
+49
View File
@@ -0,0 +1,49 @@
/*
* libunbound/authload.h - prototypes for auth load methods.
*
* Copyright (c) 2026, NLnet Labs. All rights reserved.
*
* This software is open source.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* Neither the name of the NLNET LABS nor the names of its contributors may
* be used to endorse or promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
* TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/**
* \file
*
* This file declares the methods that must be implemented to use the
* auth load service.
*/
#ifndef LIBUNBOUND_AUTHLOAD_H
#define LIBUNBOUND_AUTHLOAD_H
/** The worker routine that services the auth load connection. */
void worker_auth_load_service_cb(int fd, short bits, void* arg);
#endif /* LIBUNBOUND_AUTHLOAD_H */
+3
View File
@@ -608,6 +608,8 @@ rm -rf .git .travis.yml .gitattributes .github .gitignore || error_cleanup "Fail
info "Adding libtool utils (libtoolize)."
libtoolize -c --install || libtoolize -c || error_cleanup "Libtoolize failed."
# Turn this off, if the git repo times out for lookups.
if test "updateconfigsub" = "false"; then
# https://www.gnu.org/software/gettext/manual/html_node/config_002eguess.html
info "Updating config.guess and config.sub"
wget -O config.guess 'https://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.guess;hb=HEAD'
@@ -621,6 +623,7 @@ if [ `uname -s | grep -i -c darwin` -ne 0 ]; then
xattr -d com.apple.quarantine config.sub
fi
fi
fi
info "Building configure script (autoreconf)."
autoreconf -f || error_cleanup "Autoconf failed."
+14 -14
View File
@@ -79,7 +79,7 @@
i+(int)((unsigned int)name[i]) < len) {
memmove(buf, name + i + 1, (unsigned int)name[i]);
buf[(unsigned int)name[i]] = 0;
PyList_SetItem(list, cnt, PyString_FromString(buf));
PyList_SetItem(list, cnt, PyUnicode_FromString(buf));
}
i += ((unsigned int)name[i]) + 1;
cnt++;
@@ -96,7 +96,7 @@
list = PyList_New(len);
for (i=0; i < len; i++) {
PyList_SET_ITEM(list, i, PyString_FromString(array[i]));
PyList_SET_ITEM(list, i, PyUnicode_FromString(array[i]));
}
return list;
}
@@ -207,7 +207,7 @@ struct query_info {
char buf[LDNS_MAX_DOMAINLEN];
buf[0] = '\0';
dname_str((uint8_t*)PyBytes_AsString(dname), buf);
return PyString_FromString(buf);
return PyUnicode_FromString(buf);
}
%}
@@ -345,7 +345,7 @@ struct packed_rrset_data {
PyObject* _get_data_rr_len(struct packed_rrset_data* d, int idx) {
if ((d != NULL) && (idx >= 0) &&
((size_t)idx < (d->count+d->rrsig_count)))
return PyInt_FromLong(d->rr_len[idx]);
return PyLong_FromLong(d->rr_len[idx]);
return Py_None;
}
void _set_data_rr_ttl(struct packed_rrset_data* d, int idx, uint32_t ttl)
@@ -357,7 +357,7 @@ struct packed_rrset_data {
PyObject* _get_data_rr_ttl(struct packed_rrset_data* d, int idx) {
if ((d != NULL) && (idx >= 0) &&
((size_t)idx < (d->count+d->rrsig_count)))
return PyInt_FromLong(d->rr_ttl[idx]);
return PyLong_FromLong(d->rr_ttl[idx]);
return Py_None;
}
PyObject* _get_data_rr_data(struct packed_rrset_data* d, int idx) {
@@ -555,12 +555,12 @@ struct sockaddr_storage {};
if (ss->ss_family == AF_INET) {
const struct sockaddr_in *sa4 = (struct sockaddr_in *)ss;
return PyInt_FromLong(ntohs(sa4->sin_port));
return PyLong_FromLong(ntohs(sa4->sin_port));
}
if (ss->ss_family == AF_INET6) {
const struct sockaddr_in6 *sa6 = (struct sockaddr_in6 *)ss;
return PyInt_FromLong(ntohs(sa6->sin6_port));
return PyLong_FromLong(ntohs(sa6->sin6_port));
}
return Py_None;
@@ -574,7 +574,7 @@ struct sockaddr_storage {};
}
sa6 = (struct sockaddr_in6 *)ss;
return PyInt_FromLong(ntohl(sa6->sin6_flowinfo));
return PyLong_FromLong(ntohl(sa6->sin6_flowinfo));
}
PyObject *_sockaddr_storage_scope_id(const struct sockaddr_storage *ss) {
@@ -585,7 +585,7 @@ struct sockaddr_storage {};
}
sa6 = (struct sockaddr_in6 *)ss;
return PyInt_FromLong(ntohl(sa6->sin6_scope_id));
return PyLong_FromLong(ntohl(sa6->sin6_scope_id));
}
%}
@@ -661,7 +661,7 @@ struct edns_option {
%inline %{
PyObject* _edns_option_opt_code_get(struct edns_option* option) {
uint16_t opt_code = option->opt_code;
return PyInt_FromLong(opt_code);
return PyLong_FromLong(opt_code);
}
PyObject* _edns_option_opt_data_get(struct edns_option* option) {
@@ -1627,7 +1627,7 @@ int edns_opt_list_append(struct edns_option** list, uint16_t code, size_t len,
}
result = PyObject_Call(func, py_args, py_kwargs);
if (result) {
res = PyInt_AsLong(result);
res = PyLong_AsLong(result);
}
out:
Py_XDECREF(py_edns);
@@ -1711,7 +1711,7 @@ out:
}
result = PyObject_Call(func, py_args, py_kwargs);
if (result) {
res = PyInt_AsLong(result);
res = PyLong_AsLong(result);
}
out:
Py_XDECREF(py_qinfo);
@@ -1765,7 +1765,7 @@ out:
}
result = PyObject_Call(func, py_args, py_kwargs);
if (result) {
res = PyInt_AsLong(result);
res = PyLong_AsLong(result);
}
out:
Py_XDECREF(py_qstate);
@@ -1814,7 +1814,7 @@ out:
}
result = PyObject_Call(func, py_args, py_kwargs);
if (result) {
res = PyInt_AsLong(result);
res = PyLong_AsLong(result);
}
out:
Py_XDECREF(py_qstate);
+4 -4
View File
@@ -246,14 +246,14 @@ log_py_err(void)
}
/* And it should be a string all ready to go - duplicate it. */
if (!PyString_Check(obResult) && !PyUnicode_Check(obResult)) {
if (!PyBytes_Check(obResult) && !PyUnicode_Check(obResult)) {
log_err("pythonmod: cannot print exception, "
"StringIO.getvalue() result did not String_Check"
" or Unicode_Check");
goto cleanup;
}
if(PyString_Check(obResult)) {
result = PyString_AsString(obResult);
if(PyBytes_Check(obResult)) {
result = PyBytes_AsString(obResult);
} else {
ascstr = PyUnicode_AsASCIIString(obResult);
result = PyBytes_AsString(ascstr);
@@ -450,7 +450,7 @@ int pythonmod_init(struct module_env* env, int id)
pe->data = PyDict_New();
/* add the script filename to the global "mod_env" for trivial access */
fname = PyString_FromString(pe->fname);
fname = PyUnicode_FromString(pe->fname);
if(PyDict_SetItemString(pe->data, "script", fname) < 0) {
log_err("pythonmod: could not add item to dictionary");
Py_XDECREF(fname);
+921
View File
@@ -0,0 +1,921 @@
/*
* services/authload.c - authoritative zone load thread
*
* Copyright (c) 2026, NLnet Labs. All rights reserved.
*
* This software is open source.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* Neither the name of the NLNET LABS nor the names of its contributors may
* be used to endorse or promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
* TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/**
* \file
*
* This file contains the auth load thread. This loads authority zone
* and RPZ zone information in a thread, in a separate memory structure.
* When it is done, the information is swapped over to the running server.
*/
#include "config.h"
#include "services/authload.h"
#include "daemon/worker.h"
#include "daemon/daemon.h"
#include "services/authzone.h"
#include "libunbound/authload.h"
#include "util/net_help.h"
#include "util/log.h"
#include "util/ub_event.h"
#include "util/timeval_func.h"
#include "util/data/dname.h"
/** Get memory use of buffer. */
static size_t
buffer_get_mem(struct sldns_buffer* buf)
{
if(!buf) return 0;
return sizeof(*buf) + (buf->_data?buf->_capacity:0);
}
/** Auth load notification to string, for descriptive purposes. */
static const char*
auth_load_notification_to_string(enum auth_load_notification_type status)
{
switch(status) {
case auth_load_notification_exit:
return "auth_load_notification_exit";
default:
break;
}
return "unknown_auth_load_notification_value";
}
/** delete chunks */
static void
auth_chunk_list_delete(struct auth_chunk* first)
{
struct auth_chunk* c = first, *cn;
while(c) {
cn = c->next;
free(c->data);
free(c);
c = cn;
}
}
/** Delete auth load task item */
static void
auth_load_task_delete(struct auth_load_task* task)
{
if(!task)
return;
free(task->name);
free(task->host);
free(task->file);
auth_chunk_list_delete(task->chunks_first);
free(task);
}
/** Create new auth load task item */
static struct auth_load_task*
auth_load_task_create(void)
{
struct auth_load_task* task = (struct auth_load_task*)calloc(1,
sizeof(*task));
return task;
}
/** Pick up the work content of task transfer of auth xfr */
static int
auth_load_task_pickup_xfr(struct auth_load_task* task, struct auth_xfer* xfr)
{
task->name = memdup(xfr->name, xfr->namelen);
if(!task->name)
return 0;
task->namelen = xfr->namelen;
task->dclass = xfr->dclass;
if(xfr->task_transfer->master && xfr->task_transfer->master->host) {
task->host = strdup(xfr->task_transfer->master->host);
if(!task->host)
return 0;
}
if(xfr->task_transfer->master && xfr->task_transfer->master->file) {
task->file = strdup(xfr->task_transfer->master->file);
if(!task->file)
return 0;
}
if(xfr->task_transfer->master)
task->on_http = xfr->task_transfer->master->http;
task->on_ixfr = xfr->task_transfer->on_ixfr;
task->on_ixfr_is_axfr = xfr->task_transfer->on_ixfr_is_axfr;
task->serial = xfr->serial;
if(xfr->task_transfer->chunks_first) {
task->chunks_first = xfr->task_transfer->chunks_first;
task->chunks_last = xfr->task_transfer->chunks_last;
task->chunks_total = xfr->task_transfer->chunks_total;
/* The task now has the chunks. Remove them from the
* xfr structure. */
xfr->task_transfer->chunks_first = 0;
xfr->task_transfer->chunks_last = 0;
xfr->task_transfer->chunks_total = 0;
}
if(task->on_http)
task->task_type = AUTH_LOAD_TASK_HTTPCHUNKS;
else task->task_type = AUTH_LOAD_TASK_TRANSFER;
return 1;
}
/** Create xfr task */
static struct auth_load_task*
auth_load_task_create_xfr(struct auth_xfer* xfr, struct worker* worker)
{
struct auth_load_task* task = auth_load_task_create();
if(!task) {
log_err("out of memory");
return 0;
}
task->worker = worker;
if(!auth_load_task_pickup_xfr(task, xfr)) {
log_err("out of memory");
auth_load_task_delete(task);
return 0;
}
return task;
}
int
auth_load_thread_poll_for_quit(struct auth_load_thread* thr)
{
int inevent, loopexit = 0;
uint8_t cmd;
ssize_t ret;
if(!thr)
return 0;
if(thr->need_to_quit)
return 1;
/* Is there data? */
if(!sock_poll_timeout(thr->commpair[1], 0, 1, 0, &inevent)) {
log_err("auth_load_thread_poll_for_quit: poll failed");
return 0;
}
if(!inevent)
return 0;
/* Read the data */
while(1) {
if(++loopexit > 200) {
log_err("auth_load_thread_poll_for_quit: recv loops %s",
sock_strerror(errno));
return 0;
}
ret = recv(thr->commpair[1], ((char*)&cmd), sizeof(cmd), 0);
if(ret == -1) {
if(
#ifndef USE_WINSOCK
errno == EINTR || errno == EAGAIN
# ifdef EWOULDBLOCK
|| errno == EWOULDBLOCK
# endif
#else
WSAGetLastError() == WSAEINTR ||
WSAGetLastError() == WSAEINPROGRESS ||
WSAGetLastError() == WSAEWOULDBLOCK
#endif
)
continue; /* Try again. */
log_err("auth_load_thread_poll_for_quit: recv: %s",
sock_strerror(errno));
return 0;
} else if(ret == 0) {
log_err("auth_load_thread_poll_for_quit: recv: EOF");
return 0;
}
break;
}
if(cmd == auth_load_notification_exit) {
thr->need_to_quit = 1;
verbose(VERB_ALGO, "auth load: exit notification received");
return 1;
}
log_err("auth_load_thread_poll_for_quit: unknown notification status "
"received: %d %s", cmd, auth_load_notification_to_string(cmd));
return 0;
}
/** Signal the worker connected to an auth load thread the status */
static void
auth_load_thread_signal_worker(struct auth_load_thread* thr, int status)
{
int outevent, loopexit = 0;
ssize_t ret;
uint8_t to_send;
verbose(VERB_ALGO, "auth load thread: send status %d", status);
/* Make a blocking attempt to send. But meanwhile stay responsive,
* once in a while for quit commands. In case the server has to quit. */
/* see if there is incoming quit signals */
if(auth_load_thread_poll_for_quit(thr))
return;
to_send = (uint8_t)status;
while(1) {
if(++loopexit > 200) {
log_err("auth load thread: could not send status");
return;
}
/* wait for socket to become writable */
if(!sock_poll_timeout(thr->commpair[1],
200, /* msec wait before check for quit, and loop to
wait again. */
0, 1, &outevent)) {
log_err("auth load thread: poll failed");
return;
}
if(auth_load_thread_poll_for_quit(thr))
return;
if(!outevent)
continue;
ret = send(thr->commpair[1], &to_send, 1, 0);
if(ret == -1) {
if(
#ifndef USE_WINSOCK
errno == EINTR || errno == EAGAIN
# ifdef EWOULDBLOCK
|| errno == EWOULDBLOCK
# endif
#else
WSAGetLastError() == WSAEINTR ||
WSAGetLastError() == WSAEINPROGRESS ||
WSAGetLastError() == WSAEWOULDBLOCK
#endif
)
continue; /* Try again. */
log_err("auth load thread signal worker: send: %s",
sock_strerror(errno));
return;
} else if(ret < 1) {
continue;
}
break;
}
}
/** Create proxy auth zone structure, that is used to hold the data
* that is processed. */
static struct auth_zone*
auth_zone_create_proxy(uint8_t* nm, size_t nmlen, uint16_t dclass)
{
struct auth_zone* z = (struct auth_zone*)calloc(1, sizeof(*z));
if(!z) {
return NULL;
}
z->node.key = z;
z->dclass = dclass;
z->namelen = nmlen;
z->namelabs = dname_count_labels(nm);
z->name = memdup(nm, nmlen);
if(!z->name) {
free(z);
return NULL;
}
rbtree_init(&z->data, &auth_data_cmp);
return z;
}
/** Delete proxy auth zone structure */
static void
auth_zone_delete_proxy(struct auth_zone* z)
{
if(!z)
return;
traverse_postorder(&z->data, auth_data_del, NULL);
if(z->rpz)
rpz_delete(z->rpz);
free(z->name);
free(z);
}
/** Calculate memory use of the authload thread for this task.
* The size of the task struct, with the data chunks, and the proxy auth zone
* structure that is created while the other auth zone is used for queries,
* and other added memory.
*/
static void
auth_load_calc_mem(struct auth_load_task* task, struct auth_zone* z,
size_t other)
{
size_t m = 0;
if(verbosity < 8) {
task->mem_used = 0;
return;
}
m += other;
m += sizeof(*task);
m += task->namelen;
m += getmem_str(task->host);
m += getmem_str(task->file);
m += task->chunks_total;
m += auth_zone_get_mem(z);
task->mem_used = m;
}
/** Swap the final zone contents with the live zone */
static void
auth_load_swap_zone(struct auth_load_thread* thr, struct auth_zone* proxyz)
{
rbtree_type data;
struct rpz* rpz;
struct auth_zone* z;
lock_rw_rdlock(&thr->task->worker->env.auth_zones->lock);
z = auth_zone_find(thr->task->worker->env.auth_zones,
thr->task->name, thr->task->namelen, thr->task->dclass);
if(!z) {
lock_rw_unlock(&thr->task->worker->env.auth_zones->lock);
verbose(VERB_ALGO, "auth zone missing after auth load.");
return;
}
lock_rw_wrlock(&z->lock);
lock_rw_unlock(&thr->task->worker->env.auth_zones->lock);
data = proxyz->data;
proxyz->data = z->data;
z->data = data;
rpz = proxyz->rpz;
proxyz->rpz = z->rpz;
z->rpz = rpz;
lock_rw_unlock(&z->lock);
}
/** Process http transfer */
static int
auth_load_process_http(struct auth_load_thread* thr)
{
struct auth_load_task* task = thr->task;
struct sldns_buffer* scratch_buffer;
struct auth_zone* z;
size_t scratch_mem;
scratch_buffer = sldns_buffer_new(sldns_buffer_capacity(
thr->task->worker->env.scratch_buffer));
if(!scratch_buffer) {
log_err("out of memory");
return 0;
}
scratch_mem = buffer_get_mem(scratch_buffer);
z = auth_zone_create_proxy(task->name, task->namelen, task->dclass);
if(!z) {
log_err("out of memory");
sldns_buffer_free(scratch_buffer);
return 0;
}
if(auth_load_thread_poll_for_quit(thr)) {
sldns_buffer_free(scratch_buffer);
auth_zone_delete_proxy(z);
return 0;
}
xfr_http_preview(task->file, task->chunks_first);
if(!xfr_http_syntax_check(task->name, task->namelen, task->dclass,
task->host, task->file, task->chunks_first, scratch_buffer)) {
sldns_buffer_free(scratch_buffer);
auth_zone_delete_proxy(z);
return 0;
}
if(auth_load_thread_poll_for_quit(thr)) {
sldns_buffer_free(scratch_buffer);
auth_zone_delete_proxy(z);
return 0;
}
if(!xfr_apply_http(task->name, task->namelen, task->host, task->file,
task->chunks_first, z, scratch_buffer, thr)) {
sldns_buffer_free(scratch_buffer);
auth_zone_delete_proxy(z);
return 0;
}
sldns_buffer_free(scratch_buffer);
if(z->rpz)
rpz_finish_config(z->rpz);
if(auth_load_thread_poll_for_quit(thr)) {
auth_zone_delete_proxy(z);
return 0;
}
auth_load_calc_mem(task, z, scratch_mem);
auth_load_swap_zone(thr, z);
auth_zone_delete_proxy(z);
return 1;
}
/** Copy RRset and append it to the domain, update last pointer. */
static int
rrset_append_copy(struct auth_data* domain, struct auth_rrset* rrset,
struct auth_rrset** last)
{
struct auth_rrset* s = calloc(1, sizeof(*s));
if(!s)
return 0;
s->type = rrset->type;
s->data = (struct packed_rrset_data*)memdup(rrset->data,
packed_rrset_sizeof(rrset->data));
if(!s->data) {
free(s);
return 0;
}
packed_rrset_ptr_fixup(s->data);
if(!*last)
domain->rrsets = s;
else (*last)->next = s;
*last = s;
return 1;
}
/** Copy the existing zone for modification */
static int
auth_load_copy_into_zone(struct auth_load_thread* thr, struct auth_zone* proxyz)
{
int count = 0;
struct auth_zone* z;
struct auth_data* d;
lock_rw_rdlock(&thr->task->worker->env.auth_zones->lock);
z = auth_zone_find(thr->task->worker->env.auth_zones,
thr->task->name, thr->task->namelen, thr->task->dclass);
if(!z) {
lock_rw_unlock(&thr->task->worker->env.auth_zones->lock);
verbose(VERB_ALGO, "auth zone missing for copy for IXFR.");
return 0;
}
lock_rw_rdlock(&z->lock);
lock_rw_unlock(&thr->task->worker->env.auth_zones->lock);
/* Copy from z into proxyz. */
RBTREE_FOR(d, struct auth_data*, &z->data) {
struct auth_rrset* rrset, *last = NULL;
struct auth_data* proxy_d = az_domain_create(proxyz,
d->name, d->namelen);
if(!proxy_d) {
log_err("out of memory");
lock_rw_unlock(&z->lock);
return 0;
}
for(rrset = d->rrsets; rrset; rrset=rrset->next) {
if(!rrset_append_copy(proxy_d, rrset, &last)) {
log_err("out of memory");
lock_rw_unlock(&z->lock);
return 0;
}
if((count++)%10000 == 0) {
if(auth_load_thread_poll_for_quit(thr)) {
lock_rw_unlock(&z->lock);
return 0;
}
}
}
if((count++)%10000 == 0) {
if(auth_load_thread_poll_for_quit(thr)) {
lock_rw_unlock(&z->lock);
return 0;
}
}
}
lock_rw_unlock(&z->lock);
return 1;
}
/** Process ixfr transfer */
static int
auth_load_process_ixfr(struct auth_load_thread* thr)
{
struct auth_load_task* task = thr->task;
struct sldns_buffer* scratch_buffer;
struct auth_zone* z;
size_t scratch_mem;
scratch_buffer = sldns_buffer_new(sldns_buffer_capacity(
thr->task->worker->env.scratch_buffer));
if(!scratch_buffer) {
log_err("out of memory");
return 0;
}
scratch_mem = buffer_get_mem(scratch_buffer);
z = auth_zone_create_proxy(task->name, task->namelen, task->dclass);
if(!z) {
log_err("out of memory");
sldns_buffer_free(scratch_buffer);
return 0;
}
if(auth_load_thread_poll_for_quit(thr)) {
sldns_buffer_free(scratch_buffer);
auth_zone_delete_proxy(z);
return 0;
}
/* Copy the existing zone for modification, that uses a read lock.
* That then does not interrupt the service of threads. */
if(!auth_load_copy_into_zone(thr, z)) {
sldns_buffer_free(scratch_buffer);
auth_zone_delete_proxy(z);
return 0;
}
if(!xfr_apply_ixfr(task->chunks_first, task->serial, z,
scratch_buffer, thr)) {
sldns_buffer_free(scratch_buffer);
auth_zone_delete_proxy(z);
return 0;
}
sldns_buffer_free(scratch_buffer);
if(auth_load_thread_poll_for_quit(thr)) {
auth_zone_delete_proxy(z);
return 0;
}
auth_load_calc_mem(task, z, scratch_mem);
auth_load_swap_zone(thr, z);
auth_zone_delete_proxy(z);
return 1;
}
/** Process axfr transfer */
static int
auth_load_process_axfr(struct auth_load_thread* thr)
{
struct auth_load_task* task = thr->task;
struct sldns_buffer* scratch_buffer;
struct auth_zone* z;
size_t scratch_mem;
scratch_buffer = sldns_buffer_new(sldns_buffer_capacity(
thr->task->worker->env.scratch_buffer));
if(!scratch_buffer) {
log_err("out of memory");
return 0;
}
scratch_mem = buffer_get_mem(scratch_buffer);
z = auth_zone_create_proxy(task->name, task->namelen, task->dclass);
if(!z) {
log_err("out of memory");
sldns_buffer_free(scratch_buffer);
return 0;
}
if(auth_load_thread_poll_for_quit(thr)) {
sldns_buffer_free(scratch_buffer);
auth_zone_delete_proxy(z);
return 0;
}
if(!xfr_apply_axfr(task->chunks_first, z, scratch_buffer, thr)) {
sldns_buffer_free(scratch_buffer);
auth_zone_delete_proxy(z);
return 0;
}
sldns_buffer_free(scratch_buffer);
if(auth_load_thread_poll_for_quit(thr)) {
auth_zone_delete_proxy(z);
return 0;
}
auth_load_calc_mem(task, z, scratch_mem);
auth_load_swap_zone(thr, z);
auth_zone_delete_proxy(z);
return 1;
}
/** In the auth load thread, process the task */
static int
auth_load_thread_process(struct auth_load_thread* thr)
{
struct auth_load_task* task = thr->task;
struct timeval start, end;
if(gettimeofday(&start, NULL) < 0)
log_err("gettimeofday: %s", strerror(errno));
/* apply data */
if(task->on_http) {
if(!auth_load_process_http(thr))
return 0;
} else if(task->on_ixfr && !task->on_ixfr_is_axfr) {
if(!auth_load_process_ixfr(thr))
return 0;
} else {
if(!auth_load_process_axfr(thr))
return 0;
}
if(gettimeofday(&end, NULL) < 0)
log_err("gettimeofday: %s", strerror(errno));
timeval_subtract(&thr->task->time_taken, &end, &start);
return 1;
}
/** The auth load thread. The thread main function. */
static void*
auth_load_thread_main(void* arg)
{
struct auth_load_thread* thr = (struct auth_load_thread*)arg;
int s;
const char name[16] = "unbound/authld"; /* seems to be the safest size
between different OSes */
#if defined(HAVE_GETTID) && !defined(THREADS_DISABLED)
thr->thread_tid = gettid();
if(thr->thread_tid_log)
log_thread_set(&thr->thread_tid);
else
#endif
log_thread_set(&thr->threadnum);
ub_thread_setname(ub_thread_self(), name);
(void)name; /* When setname is not defined, ignore the name variable. */
verbose(VERB_ALGO, "start auth load thread");
s = auth_load_thread_process(thr);
/* The result is sent to the worker, that reaps the thread. */
auth_load_thread_signal_worker(thr, s);
verbose(VERB_ALGO, "stop auth load thread");
return NULL;
}
/** Delete auth load thread structure */
static void
auth_load_thread_delete(struct auth_load_thread* thr)
{
if(!thr)
return;
if(thr->service_event && thr->service_event_is_added) {
ub_event_del(thr->service_event);
thr->service_event_is_added = 0;
}
if(thr->service_event)
ub_event_free(thr->service_event);
if(thr->commpair[0] != -1)
sock_close(thr->commpair[0]);
if(thr->commpair[1] != -1)
sock_close(thr->commpair[1]);
auth_load_task_delete(thr->task);
free(thr);
}
/** Create auth load thread structure */
static struct auth_load_thread*
auth_load_thread_create(struct auth_load_task* task)
{
int numworkers;
struct auth_load_thread* thr = (struct auth_load_thread*)calloc(1,
sizeof(*thr));
if(!thr)
return NULL;
numworkers = task->worker->daemon->num;
/* This number is printed into the logs */
thr->threadnum = numworkers+3;
thr->task = task;
thr->commpair[0] = -1;
thr->commpair[1] = -1;
if(!create_socketpair(thr->commpair, task->worker->daemon->rand)) {
auth_load_thread_delete(thr);
return NULL;
}
#ifdef HAVE_GETTID
thr->thread_tid_log = task->worker->env.cfg->log_thread_id;
#endif
return thr;
}
/** The worker routine that services the auth load connection. */
void
worker_auth_load_service_cb(int ATTR_UNUSED(fd), short ATTR_UNUSED(bits),
void* arg)
{
struct auth_load_thread* thr = (struct auth_load_thread*)arg;
uint8_t recv_item;
ssize_t ret;
struct auth_xfer* xfr;
struct auth_chunk* chunk_list;
struct module_env* env = &thr->task->worker->env;
int ixfr_fail;
struct timeval time_taken;
size_t mem_used, chunks_total;
log_assert(thr->commpair[0] >= 0);
ret = recv(thr->commpair[0], &recv_item, 1, 0);
if(ret == -1) {
if(
#ifndef USE_WINSOCK
errno == EINTR || errno == EAGAIN
# ifdef EWOULDBLOCK
|| errno == EWOULDBLOCK
# endif
#else
WSAGetLastError() == WSAEINTR ||
WSAGetLastError() == WSAEINPROGRESS
#endif
)
return; /* Continue later. */
#ifdef USE_WINSOCK
if(WSAGetLastError() == WSAEWOULDBLOCK) {
ub_winsock_tcp_wouldblock(thr->service_event,
UB_EV_READ);
return; /* Continue later. */
}
#endif
log_err("read status from auth load thread, recv: %s",
sock_strerror(errno));
return;
} else if(ret == 0) {
verbose(VERB_ALGO, "closed connection from auth load thread");
/* handle this like an error */
recv_item = 0;
/* ret<1: No short read on 1 byte, to continue later on */
}
/* Deal with the result of auth load thread */
verbose(VERB_ALGO, "auth load status is %d", (int)recv_item);
verbose(VERB_ALGO, "join with auth load thread");
ub_thread_join(thr->tid);
verbose(VERB_ALGO, "joined with auth load thread");
lock_rw_rdlock(&thr->task->worker->env.auth_zones->lock);
xfr = auth_xfer_find(thr->task->worker->env.auth_zones,
thr->task->name, thr->task->namelen, thr->task->dclass);
if(!xfr) {
lock_rw_unlock(&thr->task->worker->env.auth_zones->lock);
verbose(VERB_ALGO, "auth load: xfr is gone");
auth_load_thread_delete(thr);
auth_load_info_release_thread(env);
return;
}
lock_basic_lock(&xfr->lock);
lock_rw_unlock(&thr->task->worker->env.auth_zones->lock);
ixfr_fail = thr->task->ixfr_fail;
time_taken = thr->task->time_taken;
mem_used = thr->task->mem_used;
chunks_total = thr->task->chunks_total;
if(thr->task->on_http) {
chunk_list = thr->task->chunks_first;
thr->task->chunks_first = NULL;
thr->task->chunks_last = NULL;
thr->task->chunks_total = 0;
} else {
chunk_list = NULL;
}
auth_load_thread_delete(thr);
auth_load_info_release_thread(env);
xfr_process_load_end_transfer(xfr, env, recv_item, ixfr_fail,
&time_taken, mem_used, chunks_total, chunk_list);
}
/** Attach worker to the auth load thread. */
static int
auth_load_thread_attach(struct auth_load_thread* thr, struct worker* worker)
{
/* Setup listener in worker, that connects via a pipe to the
* auth load thread.
* The listener has to be nonblocking, so the the remote servicing
* thread can continue to service DNS queries.
* The commpair[1] element can stay blocking, it is used by the
* auth load thread. The thread needs to wait at these times, when
* it has to check briefly it can use poll. */
verbose(VERB_ALGO, "auth_load_thread_attach");
fd_set_nonblock(thr->commpair[0]);
if(!comm_base_internal(worker->base)) {
verbose(VERB_ALGO, "auth load thread: no event base");
return 0;
}
thr->service_event = ub_event_new(comm_base_internal(worker->base),
thr->commpair[0], UB_EV_READ | UB_EV_PERSIST,
worker_auth_load_service_cb, thr);
if(!thr->service_event) {
log_err("out of memory");
return 0;
}
if(ub_event_add(thr->service_event, NULL) != 0) {
log_err("out of memory");
return 0;
}
thr->service_event_is_added = 1;
return 1;
}
/** Create and start the auth load thread, with the task */
static int
auth_load_start_thread(struct auth_load_task* task)
{
struct auth_load_thread* thr = auth_load_thread_create(task);
if(!thr) {
log_err("out of memory");
auth_load_task_delete(task);
return 0;
}
if(!auth_load_thread_attach(thr, task->worker)) {
log_err("out of memory");
auth_load_thread_delete(thr);
return 0;
}
/* Start auth load thread */
ub_thread_create(&thr->tid, auth_load_thread_main, thr);
return 1;
}
int auth_load_add_task_xfr(struct auth_xfer* xfr, struct worker* worker)
{
struct auth_load_task* task;
int can_run = 0;
verbose(VERB_ALGO, "auth load add task");
/* Check auth load count */
can_run = 1;
/* Create new thread */
task = auth_load_task_create_xfr(xfr, worker);
if(!task)
return 0;
if(can_run) {
verbose(VERB_ALGO, "auth load start thread");
if(!auth_load_start_thread(task))
return 0;
verbose(VERB_ALGO, "auth load thread started");
return 1;
}
/* Make wait item */
return 0;
}
struct auth_load_general_info* auth_load_info_create(void)
{
struct auth_load_general_info* auth_load_info =
(struct auth_load_general_info*)calloc(1,
sizeof(*auth_load_info));
if(!auth_load_info) {
log_err("malloc failure");
return NULL;
}
lock_basic_init(&auth_load_info->lock);
lock_protect(&auth_load_info->lock,
&auth_load_info->num_auth_load_threads,
sizeof(auth_load_info->num_auth_load_threads));
return auth_load_info;
}
void auth_load_info_delete(struct auth_load_general_info* auth_load_info)
{
if(!auth_load_info)
return;
lock_basic_destroy(&auth_load_info->lock);
free(auth_load_info);
}
int auth_load_info_grab_thread(struct module_env* env)
{
struct auth_load_general_info* auth_load_info =
env->worker->daemon->auth_load_info;
struct config_file* cfg = env->cfg;
int ret = 0;
lock_basic_lock(&auth_load_info->lock);
if(auth_load_info->num_auth_load_threads < cfg->auth_task_threads) {
ret = 1;
auth_load_info->num_auth_load_threads++;
}
lock_basic_unlock(&auth_load_info->lock);
return ret;
}
void auth_load_info_release_thread(struct module_env* env)
{
struct auth_load_general_info* auth_load_info =
env->worker->daemon->auth_load_info;
lock_basic_lock(&auth_load_info->lock);
if(auth_load_info->num_auth_load_threads == 0) {
verbose(VERB_ALGO, "release of auth load thread, but "
"num_auth_load_threads not > 0.");
} else {
auth_load_info->num_auth_load_threads--;
}
lock_basic_unlock(&auth_load_info->lock);
}
+203
View File
@@ -0,0 +1,203 @@
/*
* services/authload.h - authoritative zone load thread
*
* Copyright (c) 2026, NLnet Labs. All rights reserved.
*
* This software is open source.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* Neither the name of the NLNET LABS nor the names of its contributors may
* be used to endorse or promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
* TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/**
* \file
*
* This file contains the auth load thread. This loads authority zone
* and RPZ zone information in a thread, in a separate memory structure.
* When it is done, the information is swapped over to the running server.
*/
#ifndef SERVICES_AUTHLOAD_H
#define SERVICES_AUTHLOAD_H
#include "util/locks.h"
struct worker;
struct auth_xfer;
struct module_env;
struct auth_load_task;
/**
* General information for auth load threads. The number of active threads.
*/
struct auth_load_general_info {
/** lock on this structure */
lock_basic_type lock;
/** The number of active auth load threads. */
int num_auth_load_threads;
};
/**
* The types of notifications that the auth load thread sends around.
*/
enum auth_load_notification_type {
/** This is sent to make the auth load thread perform exit */
auth_load_notification_exit
};
/**
* The auth load thread. The thread runs to load authority zone information
* and RPZ information into memory. It loads into a copy. Then that is swapped
* over to the running server. This keeps the server responsive while the
* information is loaded.
*/
struct auth_load_thread {
/** the thread number for the thread,
* must be first to cast thread arg to int* in checklock code. */
int threadnum;
/** thread id, of the io thread */
ub_thread_type tid;
#ifdef HAVE_GETTID
/** thread tid, the LWP id */
pid_t thread_tid;
/** if logging should include the LWP id */
int thread_tid_log;
#endif
/** communication socket pair, that sends commands */
int commpair[2];
/** if the thread has to quit */
int need_to_quit;
/** the event that listens on the worker to commpair,
* it receives content from the auth load thread. */
void* service_event;
/** if the event that listens on the worker has
* been added to the comm base. */
int service_event_is_added;
/** the worker that the auth load is connected to */
struct worker* worker;
/** The task that the thread is working on */
struct auth_load_task* task;
};
/**
* The types of tasks that the auth load can perform.
*/
enum auth_load_task_type {
AUTH_LOAD_TASK_TRANSFER,
AUTH_LOAD_TASK_ZONEFILE_READ,
AUTH_LOAD_TASK_ZONEFILE_WRITE,
AUTH_LOAD_TASK_HTTPCHUNKS
};
/**
* The task for the auth load. The task can be to load a zone transfer, AXFR,
* IXFR, from zonefile, and from a http read, from chunks.
*/
struct auth_load_task {
/** The type of the task */
enum auth_load_task_type task_type;
/** The task is connected with this worker */
struct worker* worker;
/** The zone name */
uint8_t* name;
/** The zone namelen */
size_t namelen;
/** The zone class */
uint16_t dclass;
/** name of the host that the transfer comes from. */
char* host;
/** file part of the url that the transfer comes from, or NULL. */
char* file;
/** Set if the host is http transfer, if false it is AXFR or IXFR. */
int on_http;
/** Set if the transfer is doing IXFR */
int on_ixfr;
/** Set if the transfer is an IXFR but we detected an AXFR contents */
int on_ixfr_is_axfr;
/** Set if the ixfr failed. (So that there can be backoff to AXFR). */
int ixfr_fail;
/** current serial (from SOA), if we have no zone, 0
* This is for checking the IXFR result. */
uint32_t serial;
/** the data chunks, or NULL, to process. */
struct auth_chunk* chunks_first;
/** last data chunk */
struct auth_chunk* chunks_last;
/** size of data in data chunks. */
size_t chunks_total;
/** time taken for the task */
struct timeval time_taken;
/** memory used for the task */
size_t mem_used;
};
/**
* Add a new task to be performed by the auth load thread.
* It starts a thread, or makes a wait list item.
* @param xfr: zone transfer to start for.
* @param worker: worker that is connected to the task.
* @return false on failure.
*/
int auth_load_add_task_xfr(struct auth_xfer* xfr, struct worker* worker);
/** See if there is a quit signal, true if so. */
int auth_load_thread_poll_for_quit(struct auth_load_thread* thr);
/**
* Create auth load info structure.
* @return NULL on failure.
*/
struct auth_load_general_info* auth_load_info_create(void);
/**
* Delete auth load info structure.
* @param auth_load_info: to delete.
*/
void auth_load_info_delete(struct auth_load_general_info* auth_load_info);
/**
* Grab a new thread from the auth load count.
* @param env: with auth_load_info with the active thread count.
* and config file, with configured maximum.
* @return false on failure, like too many active, true if successful.
*/
int auth_load_info_grab_thread(struct module_env* env);
/**
* Release thread from auth load count. It is done.
* @param env: with auth_load_info with the active thread count.
*/
void auth_load_info_release_thread(struct module_env* env);
#endif /* SERVICES_AUTHLOAD_H */
+442 -299
View File
File diff suppressed because it is too large Load Diff
+39 -4
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.
@@ -146,10 +147,6 @@ struct auth_zone {
uint16_t zonemd_callback_qtype;
/** for the zonemd callback, the unique info */
void* zonemd_callback_unique_info;
/** if the zonemd callback should write to file */
int zonemd_callback_perform_write;
/** chunklist to write for chunked transfer. */
struct auth_chunk* perform_write_chunk_list;
/** zone has been deleted */
int zone_deleted;
/** deletelist pointer, unused normally except during delete */
@@ -861,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 */
+1 -1
View File
@@ -677,7 +677,7 @@ tomsg(struct module_env* env, struct query_info* q, struct reply_info* r,
rrset_array_unlock(r->ref, r->rrset_count);
return NULL;
}
if(r->security == sec_status_secure && !reply_an_ns_rrsets_secure(r)) {
if(r->security == sec_status_secure && !reply_all_rrsets_secure(r)) {
/* message rrsets have changed status, revalidate */
rrset_array_unlock(r->ref, r->rrset_count);
return NULL;
+24 -16
View File
@@ -1341,13 +1341,33 @@ ports_create_if(const char* ifname, int do_auto, int do_udp, int do_tcp,
if((is_doq) && !(is_https || is_ssl)) do_tcp = 0;
if(do_auto) {
enum listen_type auto_port_type;
ub_sock = calloc(1, sizeof(struct unbound_socket));
if(!ub_sock)
return 0;
if(is_dnscrypt) {
auto_port_type = listen_type_udpancil_dnscrypt;
add = "udpancil_dnscrypt";
} else if(is_doq) {
auto_port_type = listen_type_doq;
add = "doq";
if(if_listens_on(ifname, port, 53, NULL)) {
log_err("DNS over QUIC is strictly not "
"allowed on port 53 as per RFC 9250. "
"Port 53 is for DNS datagrams. Error "
"for interface '%s'.", ifname);
free(ub_sock->addr);
free(ub_sock);
return 0;
}
} else {
auto_port_type = listen_type_udpancil;
add = "udpancil";
}
if((s = make_sock_port(SOCK_DGRAM, ifname, port, hints, 1,
&noip6, rcv, snd, reuseport, transparent,
tcp_mss, nodelay, freebind, use_systemd, dscp, ub_sock,
(is_dnscrypt?"udpancil_dnscrypt":"udpancil"))) == -1) {
add)) == -1) {
free(ub_sock->addr);
free(ub_sock);
if(noip6) {
@@ -1366,9 +1386,7 @@ ports_create_if(const char* ifname, int do_auto, int do_udp, int do_tcp,
if (sock_queue_timeout && !set_recvtimestamp(s)) {
log_warn("socket timestamping is not available");
}
if(!port_insert(list, s, is_dnscrypt
?listen_type_udpancil_dnscrypt:listen_type_udpancil,
is_pp2, ub_sock)) {
if(!port_insert(list, s, auto_port_type, is_pp2, ub_sock)) {
sock_close(s);
free(ub_sock->addr);
free(ub_sock);
@@ -2133,7 +2151,7 @@ void listen_start_accept(struct listen_dnsport* listen)
}
struct tcp_req_info*
tcp_req_info_create(struct comm_base* base, struct sldns_buffer* spoolbuf)
tcp_req_info_create(struct sldns_buffer* spoolbuf)
{
struct tcp_req_info* req = (struct tcp_req_info*)malloc(sizeof(*req));
if(!req) {
@@ -2141,12 +2159,6 @@ tcp_req_info_create(struct comm_base* base, struct sldns_buffer* spoolbuf)
return NULL;
}
memset(req, 0, sizeof(*req));
req->read_again_timer = comm_timer_create(base, tcp_read_again_cb, req);
if(!req->read_again_timer) {
log_err("malloc failure");
free(req);
return NULL;
}
req->spool_buffer = spoolbuf;
return req;
}
@@ -2156,7 +2168,6 @@ tcp_req_info_delete(struct tcp_req_info* req)
{
if(!req) return;
tcp_req_info_clear(req);
comm_timer_delete(req->read_again_timer);
/* cp is pointer back to commpoint that owns this struct and
* called delete on us */
/* spool_buffer is shared udp buffer, not deleted here */
@@ -2196,9 +2207,6 @@ void tcp_req_info_clear(struct tcp_req_info* req)
req->done_req_list = NULL;
req->num_done_req = 0;
req->read_is_closed = 0;
if(comm_timer_is_set(req->read_again_timer))
comm_timer_disable(req->read_again_timer);
}
void
@@ -4514,7 +4522,7 @@ doq_stream_reset_cb(ngtcp2_conn* ATTR_UNUSED(conn), int64_t stream_id,
"unknown stream %d", (int)stream_id);
return 0;
}
if(!doq_stream_close(doq_conn, stream, 1))
if(!doq_stream_close(doq_conn, stream, 0))
return NGTCP2_ERR_CALLBACK_FAILURE;
return 0;
}
+1 -7
View File
@@ -347,10 +347,6 @@ struct tcp_req_info {
int num_done_req;
/** list of pending writable result packets, malloced one at a time */
struct tcp_req_done_item* done_req_list;
/** the read again timer, when the number of pipelined TCP queries
* is large, it waits, zero time, for a new event loop to service
* the remainder of the TCP traffic on the fd. */
struct comm_timer* read_again_timer;
};
/**
@@ -381,12 +377,10 @@ struct tcp_req_done_item {
* Create tcp request info structure that keeps track of open
* requests on the TCP channel that are resolved at the same time,
* and the pending results that have to get written back to that client.
* @param base: comm base for read again timer.
* @param spoolbuf: shared buffer
* @return new structure or NULL on alloc failure.
*/
struct tcp_req_info* tcp_req_info_create(struct comm_base* base,
struct sldns_buffer* spoolbuf);
struct tcp_req_info* tcp_req_info_create(struct sldns_buffer* spoolbuf);
/**
* Delete tcp request structure. Called by owning commpoint.
+75 -73
View File
@@ -373,7 +373,7 @@ mesh_serve_expired_lookup(struct module_qstate* qstate,
"validation");
goto bail_out; /* need to validate cache entry first */
} else if(msg->rep->security == sec_status_secure &&
!reply_an_ns_rrsets_secure(msg->rep) && must_validate) {
!reply_all_rrsets_secure(msg->rep) && must_validate) {
verbose(VERB_ALGO, "Serve expired: secure entry"
" changed status");
goto bail_out; /* rrset changed, re-verify */
@@ -1097,18 +1097,6 @@ mesh_state_make_unique(struct mesh_state* mstate)
mstate->unique = mstate;
}
/** pop a reply from the reply list, if there are any. */
static struct mesh_reply*
mesh_reply_list_pop_first(struct mesh_state* mstate)
{
if(mstate->reply_list) {
struct mesh_reply* r = mstate->reply_list;
mstate->reply_list = r->next;
return r;
}
return NULL;
}
void
mesh_state_cleanup(struct mesh_state* mstate)
{
@@ -1124,30 +1112,15 @@ mesh_state_cleanup(struct mesh_state* mstate)
}
/* drop unsent replies */
if(!mstate->replies_sent) {
struct mesh_reply* rep;
struct mesh_reply* rep = mstate->reply_list;
struct mesh_cb* cb;
/* Pop items from the list, that means there is no iterator.
* And then items can be removed from the reply list, from
* like comm_point_drop_reply and comm_point_close calls.
* As the tcp_req_info and http2 code drops the entire
* connection. That could delete mesh_reply items previous and
* after the current state. The previous items are already
* popped. And the next items can be altered, like to when a
* connection has more replies on the reply list.
* The current item is also popped so the code needs to
* remove its references. */
while((rep = mesh_reply_list_pop_first(mstate)) != NULL) {
/* in tcp_req_info, the mstates linked are removed, but
* the reply_list is now NULL, so the remove-from-empty-list
* takes no time and also it does not do the mesh accounting */
mstate->reply_list = NULL;
for(; rep; rep=rep->next) {
infra_wait_limit_dec(mesh->env->infra_cache,
&rep->query_reply, mesh->env->cfg);
if(rep->query_reply.c->tcp_req_info)
tcp_req_info_remove_mesh_state(
rep->query_reply.c->tcp_req_info,
mstate);
else if(rep->query_reply.c->use_h2)
http2_stream_remove_mesh_state(rep->h2_stream);
else if(rep->query_reply.doq_stream)
doq_stream_remove_mesh_state(
rep->query_reply.doq_stream);
comm_point_drop_reply(&rep->query_reply);
log_assert(mesh->num_reply_addrs > 0);
mesh->num_reply_addrs--;
@@ -1511,6 +1484,12 @@ mesh_send_reply(struct mesh_state* m, int rcode, struct reply_info* rep,
struct timeval end_time;
struct timeval duration;
int secure;
/* briefly set the replylist to null in case the
* meshsendreply calls tcpreqinfo sendreply that
* comm_point_drops because of size, and then the
* null stops the mesh state remove and thus
* reply_list modification and accounting */
struct mesh_reply* rlist = m->reply_list;
/* rpz: apply actions */
rcode = mesh_is_udp(r) && mesh_is_rpz_respip_tcponly_action(m)
@@ -1567,7 +1546,9 @@ mesh_send_reply(struct mesh_state* m, int rcode, struct reply_info* rep,
sldns_buffer_write_at(r_buffer, 0, &r->qid, sizeof(uint16_t));
sldns_buffer_write_at(r_buffer, 12, r->qname,
m->s.qinfo.qname_len);
m->reply_list = NULL;
comm_point_send_reply(&r->query_reply);
m->reply_list = rlist;
} else if(rcode) {
m->s.qinfo.qname = r->qname;
m->s.qinfo.local_alias = r->local_alias;
@@ -1589,7 +1570,9 @@ mesh_send_reply(struct mesh_state* m, int rcode, struct reply_info* rep,
}
error_encode(r_buffer, rcode, &m->s.qinfo, r->qid,
r->qflags, &r->edns);
m->reply_list = NULL;
comm_point_send_reply(&r->query_reply);
m->reply_list = rlist;
} else {
size_t udp_size = r->edns.udp_size;
r->edns.edns_version = EDNS_ADVERTISED_VERSION;
@@ -1625,7 +1608,9 @@ mesh_send_reply(struct mesh_state* m, int rcode, struct reply_info* rep,
error_encode(r_buffer, LDNS_RCODE_SERVFAIL,
&m->s.qinfo, r->qid, r->qflags, &r->edns);
}
m->reply_list = NULL;
comm_point_send_reply(&r->query_reply);
m->reply_list = rlist;
}
infra_wait_limit_dec(m->s.env->infra_cache, &r->query_reply,
m->s.env->cfg);
@@ -1777,7 +1762,6 @@ void mesh_query_done(struct mesh_state* mstate)
struct reply_info* rep = (mstate->s.return_msg?
mstate->s.return_msg->rep:NULL);
struct timeval tv = {0, 0};
struct mesh_area* mesh = mstate->s.env->mesh;
int i = 0;
/* No need for the serve expired timer anymore; we are going to reply. */
if(mstate->s.serve_expired_data) {
@@ -1802,18 +1786,7 @@ void mesh_query_done(struct mesh_state* mstate)
&& (!rep || rep->security != sec_status_secure))
dns_error_reporting(&mstate->s, rep);
while((r = mesh_reply_list_pop_first(mstate)) != NULL) {
/* it was not detached (because it had a reply list), could be now */
if(!mstate->reply_list && !mstate->cb_list
&& mstate->super_set.count == 0) {
mesh->num_detached_states++;
}
/* if not replies any more in mstate, it is no longer a reply_state */
if(!mstate->reply_list && !mstate->cb_list) {
log_assert(mesh->num_reply_states > 0);
mesh->num_reply_states--;
}
for(r = mstate->reply_list; r; r = r->next) {
if(mesh_is_udp(r)) {
/* For UDP queries, the old replies are discarded.
* This stops a large volume of old replies from
@@ -1828,18 +1801,22 @@ void mesh_query_done(struct mesh_state* mstate)
((int)old.tv_sec)*1000+((int)old.tv_usec)/1000 >
mstate->s.env->cfg->discard_timeout) {
/* Drop the reply, it is too old */
/* briefly set the reply_list to NULL, so that the
* tcp req info cleanup routine that calls the mesh
* to deregister the meshstate for it is not done
* because the list is NULL and also accounting is not
* done there, but instead we do that here. */
struct mesh_reply* reply_list = mstate->reply_list;
verbose(VERB_ALGO, "drop reply, it is older than discard-timeout");
infra_wait_limit_dec(mstate->s.env->infra_cache,
&r->query_reply, mstate->s.env->cfg);
if(r->query_reply.c->tcp_req_info)
tcp_req_info_remove_mesh_state(
r->query_reply.c->tcp_req_info,
mstate);
else if(r->query_reply.c->use_h2)
mstate->reply_list = NULL;
if(r->query_reply.c->use_h2)
http2_stream_remove_mesh_state(r->h2_stream);
else if(r->query_reply.doq_stream)
doq_stream_remove_mesh_state(r->query_reply.doq_stream);
comm_point_drop_reply(&r->query_reply);
mstate->reply_list = reply_list;
log_assert(mstate->s.env->mesh->num_reply_addrs > 0);
mstate->s.env->mesh->num_reply_addrs--;
mstate->s.env->mesh->num_queries_discard_timeout++;
@@ -1864,17 +1841,22 @@ void mesh_query_done(struct mesh_state* mstate)
/* if this query is determined to be dropped during the
* mesh processing, this is the point to take that action. */
if(mstate->s.is_drop) {
/* briefly set the reply_list to NULL, so that the
* tcp req info cleanup routine that calls the mesh
* to deregister the meshstate for it is not done
* because the list is NULL and also accounting is not
* done there, but instead we do that here. */
struct mesh_reply* reply_list = mstate->reply_list;
infra_wait_limit_dec(mstate->s.env->infra_cache,
&r->query_reply, mstate->s.env->cfg);
if(r->query_reply.c->tcp_req_info) {
tcp_req_info_remove_mesh_state(
r->query_reply.c->tcp_req_info, mstate);
} else if(r->query_reply.c->use_h2) {
mstate->reply_list = NULL;
if(r->query_reply.c->use_h2) {
http2_stream_remove_mesh_state(r->h2_stream);
} else if(r->query_reply.doq_stream) {
doq_stream_remove_mesh_state(r->query_reply.doq_stream);
}
comm_point_drop_reply(&r->query_reply);
mstate->reply_list = reply_list;
log_assert(mstate->s.env->mesh->num_reply_addrs > 0);
mstate->s.env->mesh->num_reply_addrs--;
} else {
@@ -1915,6 +1897,18 @@ void mesh_query_done(struct mesh_state* mstate)
}
}
/* Mesh area accounting */
if(mstate->reply_list) {
mstate->reply_list = NULL;
if(!mstate->reply_list && !mstate->cb_list) {
/* was a reply state, not anymore */
log_assert(mstate->s.env->mesh->num_reply_states > 0);
mstate->s.env->mesh->num_reply_states--;
}
if(!mstate->reply_list && !mstate->cb_list &&
mstate->super_set.count == 0)
mstate->s.env->mesh->num_detached_states++;
}
mstate->replies_sent = 1;
while((c = mstate->cb_list) != NULL) {
@@ -2504,6 +2498,7 @@ void mesh_state_remove_reply(struct mesh_area* mesh, struct mesh_state* m,
}
}
static int
apply_respip_action(struct module_qstate* qstate,
const struct query_info* qinfo, struct respip_client_info* cinfo,
@@ -2636,18 +2631,7 @@ mesh_serve_expired_callback(void* arg)
if(verbosity >= VERB_ALGO)
log_dns_msg("Serve expired lookup", &qstate->qinfo, msg->rep);
while((r = mesh_reply_list_pop_first(mstate)) != NULL) {
/* it was not detached (because it had a reply list), could be now */
if(!mstate->reply_list && !mstate->cb_list
&& mstate->super_set.count == 0) {
mesh->num_detached_states++;
}
/* if not replies any more in mstate, it is no longer a reply_state */
if(!mstate->reply_list && !mstate->cb_list) {
log_assert(mesh->num_reply_states > 0);
mesh->num_reply_states--;
}
for(r = mstate->reply_list; r; r = r->next) {
if(mesh_is_udp(r)) {
struct timeval old;
timeval_subtract(&old, mstate->s.env->now_tv, &r->start_time);
@@ -2655,17 +2639,22 @@ mesh_serve_expired_callback(void* arg)
((int)old.tv_sec)*1000+((int)old.tv_usec)/1000 >
mstate->s.env->cfg->discard_timeout) {
/* Drop the reply, it is too old */
/* briefly set the reply_list to NULL, so that the
* tcp req info cleanup routine that calls the mesh
* to deregister the meshstate for it is not done
* because the list is NULL and also accounting is not
* done there, but instead we do that here. */
struct mesh_reply* reply_list = mstate->reply_list;
verbose(VERB_ALGO, "drop reply, it is older than discard-timeout");
infra_wait_limit_dec(mstate->s.env->infra_cache,
&r->query_reply, mstate->s.env->cfg);
if(r->query_reply.c->tcp_req_info)
tcp_req_info_remove_mesh_state(
r->query_reply.c->tcp_req_info, mstate);
else if(r->query_reply.c->use_h2)
mstate->reply_list = NULL;
if(r->query_reply.c->use_h2)
http2_stream_remove_mesh_state(r->h2_stream);
else if(r->query_reply.doq_stream)
doq_stream_remove_mesh_state(r->query_reply.doq_stream);
comm_point_drop_reply(&r->query_reply);
mstate->reply_list = reply_list;
log_assert(mstate->s.env->mesh->num_reply_addrs > 0);
mstate->s.env->mesh->num_reply_addrs--;
mstate->s.env->mesh->num_queries_discard_timeout++;
@@ -2703,7 +2692,8 @@ mesh_serve_expired_callback(void* arg)
if(r->query_reply.c->tcp_req_info)
tcp_req_info_remove_mesh_state(r->query_reply.c->tcp_req_info, mstate);
/* mesh_send_reply removed mesh state from http2_stream. */
/* mesh_send_reply decremented wait_limit. */
infra_wait_limit_dec(mstate->s.env->infra_cache,
&r->query_reply, mstate->s.env->cfg);
prev = r;
prev_buffer = r_buffer;
}
@@ -2722,6 +2712,18 @@ mesh_serve_expired_callback(void* arg)
}
}
/* Mesh area accounting */
if(mstate->reply_list) {
mstate->reply_list = NULL;
if(!mstate->reply_list && !mstate->cb_list) {
log_assert(mesh->num_reply_states > 0);
mesh->num_reply_states--;
if(mstate->super_set.count == 0) {
mesh->num_detached_states++;
}
}
}
while((c = mstate->cb_list) != NULL) {
/* take this cb off the list; so that the list can be
* changed, eg. by adds from the callback routine */
+3 -10
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;
@@ -786,13 +789,3 @@ size_t doq_table_quic_size_get(struct doq_table* ATTR_UNUSED(table))
return 0;
}
#endif
void tcp_read_again_cb(void* ATTR_UNUSED(arg))
{
/* nothing */
}
void tcp_more_read_again_cb(void* ATTR_UNUSED(arg))
{
/* nothing */
}
+84
View File
@@ -1337,6 +1337,89 @@ static void mesh_test(void)
free(c1);
}
#include "util/data/packed_rrset.h"
#include "sldns/sbuffer.h"
/** packed_rrset unit tests */
static void packed_rrset_test(void)
{
/* packed_rr_to_string assembles the dname, type, class, ttl and
* rdata of one rr into a buffer of 65535 bytes. Check that it
* refuses an rr that does not fit in there, also when the caller
* passes a dest_len that is larger than that, like the callers in
* daemon/cachedump.c and daemon/remote.c do. Without the check it
* writes past the end of the assembly buffer. */
uint8_t smalldname[] = "\003www\007example\003com";
uint8_t smallrdata[] = {0, 4, 1, 2, 3, 4};
uint8_t maxdname[LDNS_MAX_DOMAINLEN];
struct ub_packed_rrset_key rrk;
struct packed_rrset_data d;
uint8_t* rr_data[1];
size_t rr_len[1];
time_t rr_ttl[1];
size_t dest_len = 65535*4+2048; /* the size daemon/cachedump.c uses */
char* dest = (char*)malloc(dest_len);
int i;
unit_show_func("util/data/packed_rrset.c", "packed_rr_to_string");
if(!dest) fatal_exit("out of memory");
memset(&rrk, 0, sizeof(rrk));
memset(&d, 0, sizeof(d));
rrk.entry.data = &d;
rrk.rk.rrset_class = htons(LDNS_RR_CLASS_IN);
d.count = 1;
d.rr_len = rr_len;
d.rr_ttl = rr_ttl;
d.rr_data = rr_data;
rr_ttl[0] = 3600;
/* an ordinary rr is printed, also with the large dest_len */
rrk.rk.dname = smalldname;
rrk.rk.dname_len = sizeof(smalldname);
rrk.rk.type = htons(LDNS_RR_TYPE_A);
rr_data[0] = smallrdata;
rr_len[0] = sizeof(smallrdata);
unit_assert(packed_rr_to_string(&rrk, 0, 0, dest, dest_len) == 1);
unit_assert(strstr(dest, "1.2.3.4") != NULL);
/* a dname of the maximum length, 127 labels of one character */
for(i=0; i<127; i++) {
maxdname[i*2] = 1;
maxdname[i*2+1] = (uint8_t)'a';
}
maxdname[254] = 0;
rrk.rk.dname = maxdname;
rrk.rk.dname_len = sizeof(maxdname);
rrk.rk.type = htons(LDNS_RR_TYPE_TXT);
/* 255+2+2+4+65272 is exactly 65535, that still fits */
rr_len[0] = 65535 - 255 - 8;
rr_data[0] = (uint8_t*)calloc(1, rr_len[0]);
if(!rr_data[0]) fatal_exit("out of memory");
sldns_write_uint16(rr_data[0], (uint16_t)(rr_len[0]-2));
unit_assert(packed_rr_to_string(&rrk, 0, 0, dest, dest_len) == 1);
free(rr_data[0]);
/* one more byte of rdata does not fit and must be refused */
rr_len[0] = 65535 - 255 - 8 + 1;
rr_data[0] = (uint8_t*)calloc(1, rr_len[0]);
if(!rr_data[0]) fatal_exit("out of memory");
sldns_write_uint16(rr_data[0], (uint16_t)(rr_len[0]-2));
unit_assert(packed_rr_to_string(&rrk, 0, 0, dest, dest_len) == 0);
unit_assert(dest[0] == 0);
free(rr_data[0]);
/* the largest rdata an rr can hold, well over the buffer */
rr_len[0] = 2 + 65535;
rr_data[0] = (uint8_t*)calloc(1, rr_len[0]);
if(!rr_data[0]) fatal_exit("out of memory");
sldns_write_uint16(rr_data[0], 65535);
unit_assert(packed_rr_to_string(&rrk, 0, 0, dest, dest_len) == 0);
unit_assert(dest[0] == 0);
free(rr_data[0]);
free(dest);
}
void unit_show_func(const char* file, const char* func)
{
printf("test %s:%s\n", file, func);
@@ -1409,6 +1492,7 @@ main(int argc, char* argv[])
zonemd_test();
tcpreuse_test();
msgparse_test();
packed_rrset_test();
edns_ede_answer_encode_test();
localzone_test();
mesh_test();
+1 -143
View File
@@ -196,7 +196,7 @@ verifytest_rrset(struct module_env* env, struct val_env* ve,
setup_sigalg(dnskey, sigalg); /* check all algorithms in the dnskey */
/* ok to give null as qstate here, won't be used for answer section. */
sec = dnskeyset_verify_rrset(env, ve, rrset, dnskey, sigalg, &reason,
NULL, LDNS_SECTION_ANSWER, NULL, NULL, &verified, reasonbuf,
NULL, LDNS_SECTION_ANSWER, NULL, &verified, reasonbuf,
sizeof(reasonbuf));
if(vsig) {
printf("verify outcome is: %s %s\n", sec_status_to_string(sec),
@@ -510,146 +510,6 @@ nsec3_hash_test(const char* fname)
sldns_buffer_free(buf);
}
/** Test the rrset_canonicalize_to_buffer function to see if the
* size of canon_owner name is properly checked for. */
static void
canon_owner_buf_test(void)
{
struct regional* region;
sldns_buffer* buf;
struct ub_packed_rrset_key k;
struct packed_rrset_data d;
size_t rr_len[2];
time_t rr_ttl[2];
uint8_t* rr_data[2];
int ret;
unit_show_func("validator/val_sigcrypt.c",
"rrset_canonicalize_to_buffer");
region = regional_create();
if(!region)
fatal_exit("out of memory");
/* Purposefully a very small buffer, to overflow it */
buf = sldns_buffer_new(28);
if(!buf)
fatal_exit("out of memory");
/* An RRset to canonicalize. The buffer is made smaller, so
* it can fail on bounds checks. */
memset(&d, 0, sizeof(d));
d.ttl = 3600;
d.count = 1;
d.rrsig_count = 1;
d.rr_len = rr_len;
d.rr_ttl = rr_ttl;
d.rr_data = rr_data;
rr_len[0] = 18;
rr_len[1] = 36;
rr_ttl[0] = 3600;
rr_ttl[1] = 3600;
rr_data[0] = (uint8_t*)"\x00\x10\x0Fzzaaaaaaaaaaaaa";
rr_data[1] = (uint8_t*)"\x00\x24\x00\x06\x08\x3\x01\x02\x03\x04\x01\x02\x03\x04\x01\x02\x03\x04\x12\x34\x03zzz\x00zzaaaaaaaaaaa";
memset(&k, 0, sizeof(k));
k.rk.dname = (uint8_t*) "\x0f" "aaaaaaaaaaaaaaa" "\x00";
k.rk.dname_len = 17;
k.rk.type = htons(LDNS_RR_TYPE_TXT);
k.rk.rrset_class = htons(LDNS_RR_CLASS_IN);
k.entry.data = &d;
/* There should be no buffer overflow, assertion failure, here */
ret = rrset_canonicalize_to_buffer(region, buf, &k);
unit_assert(ret == 0);
regional_destroy(region);
sldns_buffer_free(buf);
}
/** Test if ds_digest_match_dnskey that calls ds_create_dnskey_digest,
* checks the buffer size. */
static void
dnskey_ds_digest_test(void)
{
struct regional* region;
sldns_buffer* buf;
struct module_env env;
struct ub_packed_rrset_key k1, k2;
struct packed_rrset_data d1, d2;
size_t rr_len1[1], rr_len2[1];
time_t rr_ttl1[1], rr_ttl2[1];
uint8_t* rr_rdata1[1], *rr_rdata2[1];
int ret;
unit_show_func("validator/val_sigcrypt.c", "ds_digest_match_dnskey");
region = regional_create();
if(!region)
fatal_exit("out of memory");
/* Purposefully a very small buffer, to overflow it */
buf = sldns_buffer_new(28);
if(!buf)
fatal_exit("out of memory");
memset(&env, 0, sizeof(env));
env.scratch = region;
env.scratch_buffer = buf;
/* A DNSKEY and DS RRset to match together. The buffer is made
* smaller, so it can fail on bounds checks. */
memset(&d1, 0, sizeof(d1));
d1.ttl = 3600;
d1.count = 1;
d1.rr_len = rr_len1;
d1.rr_ttl = rr_ttl1;
d1.rr_data = rr_rdata1;
rr_len1[0] = 38;
rr_ttl1[0] = 3600;
/* DS rdata has: keytag (2bytes), algorithm (1byte),
* digesttype (1byte), digest (remainder). */
rr_rdata1[0] = (uint8_t*)"\x00\x24"
"\x12\x34"
"\x08" /* RSASHA256 */
"\x02" /* SHA256 */
"0123456789abcdef0123456789abcdef"; /* 32 bytes */
;
memset(&k1, 0, sizeof(k1));
k1.rk.dname = (uint8_t*) "\x03" "foo" "\x00";
k1.rk.dname_len = 5;
k1.rk.type = htons(LDNS_RR_TYPE_DS);
k1.rk.rrset_class = htons(LDNS_RR_CLASS_IN);
k1.entry.data = &d1;
memset(&d2, 0, sizeof(d2));
d2.ttl = 3600;
d2.count = 1;
d2.rr_len = rr_len2;
d2.rr_ttl = rr_ttl2;
d2.rr_data = rr_rdata2;
rr_len2[0] = 38;
rr_ttl2[0] = 3600;
/* DNSKEY rdata has: flags (2bytes), protocol (1byte),
* algorithm (1byte), publickey (remainder). */
rr_rdata2[0] = (uint8_t*)"\x00\x24"
"\x01\x01" /* KSK */
"\x03" /* DNSSEC_KEYPROTO */
"\x08" /* RSASHA256 */
"0123456789abcdef0123456789abcdef"; /* 32 bytes of content */
;
memset(&k2, 0, sizeof(k2));
k2.rk.dname = (uint8_t*) "\x03" "foo" "\x00";
k2.rk.dname_len = 5;
k2.rk.type = htons(LDNS_RR_TYPE_DNSKEY);
k2.rk.rrset_class = htons(LDNS_RR_CLASS_IN);
k2.entry.data = &d2;
/* 36 byte rdata length for DNSKEY (38-2), and dname length of 5,
* exceeds the (small) buffer size. */
/* There should be no buffer overflow, assertion failure, here */
ret = ds_digest_match_dnskey(&env, &k2, 0, &k1, 0);
unit_assert(ret == 0);
regional_destroy(region);
sldns_buffer_free(buf);
}
#define xstr(s) str(s)
#define str(s) #s
@@ -864,6 +724,4 @@ verify_test(void)
#endif
nsectest();
nsec3_hash_test(SRCDIRSTR "/testdata/test_nsec3_hash.1");
dnskey_ds_digest_test();
canon_owner_buf_test();
}
-9
View File
@@ -85,15 +85,6 @@ for input in $PRE/testdata/*.rpl $PRE/testdata/*.crpl; do
fi
fi
# detect if ECDSA is needed
if echo $cleaninput | grep ecdsa >/dev/null 2>&1; then
if $PRE/testbound -e >/dev/null 2>&1; then
: # ECDSA is supported
else
continue
fi
fi
# detect if CLIENT_SUBNET is needed
if echo $cleaninput | grep subnet >/dev/null 2>&1; then
if $PRE/testbound -c >/dev/null 2>&1; then
+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-----
+31 -1
View File
@@ -283,9 +283,39 @@ www.example.com. IN A
SECTION ANSWER
ENTRY_END
; the zonefile was not updated with new contents, due to zonemd failure
; the zonefile was updated with new contents
STEP 70 CHECK_TEMPFILE example.com
FILE_BEGIN
example.com. 3600 IN SOA ns.example.com. hostmaster.example.com. 200154054 28800 7200 604800 3600
example.com. 3600 IN RRSIG SOA 8 2 3600 20201116135527 20201019135527 55566 example.com. gcFHT/Q4iDZ78CK6fyY2HZr8sRtgH2Rna9fEs06RW0gqMnfDntweoIaBamOZ7NlAP84aY2bZeanmEccmkHexByUpodCoKQ4NzVXctLr0TO4PVoFyfUfj62fjhM56SF8ioDxsoDQcPtYXcjNQjwfntWofMqHCMxrb9LzbgePzhOM=
example.com. 3600 IN NS ns.example.com.
example.com. 3600 IN RRSIG NS 8 2 3600 20201116135527 20201019135527 55566 example.com. X+V3XsbJbBi9OsHpjMkGCox8RLY/uXp/XX/O/flTrIre9fMDWm9ZGnewtuQFpLgGc6hUTi0eLsuRWRA5fZXEKUBhmoR2Ph01KgE1gvlL7v6zPWQwXVcBRUr3mOSbYdNNkHkXEjiDBGEhNkfqR216zNgw563eEGXOkLUFNIx5Zpg=
example.com. 3600 IN NSEC bar.example.com. NS SOA RRSIG NSEC DNSKEY ZONEMD
example.com. 3600 IN RRSIG NSEC 8 2 3600 20201116135527 20201019135527 55566 example.com. ufLrlOQprAqjnH85Rt3T0Mxd3ZB0mBeeNIr84eFJ8Rk6WiWEPm0Y1R7GRufNI24Mj7iqLcL4nJM6KK6B7dJqjqu73jw1acuYNnbsoV2BNDRXRFP2FNWTpctVdi+955f3FzgsmEJXfGiSUG0YXAEcZmdCPCn5ii2jk8mk7r6KKYo=
example.com. 3600 IN DNSKEY 256 3 8 AwEAAdug/L739i0mgN2nuK/bhxu3wFn5Ud9nK2+XUmZQlPUEZUC5YZvm1rfMmEWTGBn87fFxEu/kjFZHJ55JLzqsbbpVHLbmKCTT2gYR2FV2WDKROGKuYbVkJIXdKAjJ0ONuK507NinYvlWXIoxHn22KAWOd9wKgSTNHBlmGkX+ts3hh ;{id = 55566}
example.com. 3600 IN RRSIG DNSKEY 8 2 3600 20201116135527 20201019135527 55566 example.com. fsdnVg38PKQTH2mDOwkXL6Jre7JP7Gf8WI3CvIbmeYQUJtAlpcSbZkS3wInm3kKMxOuT55BWzndQzpfmpo91OqJjG27W0k9301NMLUwFprA6b9HK+iPAT0JpYPDPzcm1bQdarLzLS+eD/GPwmyVSX7Gze+08VfE8m8sOW2r7UjA=
example.com. 3600 IN ZONEMD 200154054 1 2 58F7620F93204BBB31B44F795B3409CC4ABD9EF5601DECC15675BD7751213152984EDDCE0626E6062E744B03B3E47711202FBB79E4A2EB8BC5CF46741B5CAE6F
example.com. 3600 IN RRSIG ZONEMD 8 2 3600 20201116135527 20201019135527 55566 example.com. orn8ZF/yqj9u4WrhiO6gtEcTaVsnZSWWZLfXhcIOiWSB8kKCxtZl5cG17dD3Du1NllUwMRqkp0KleLhIoUS9xeQ/0x05u+CYLrfQ62oAiD7q54ZQzpXJIH52aQzKV70ZnO03CZowhQBnetmIoKX6xLogKo8pt+BdQbo3oVHxV8Y=
bar.example.com. 3600 IN A 1.2.3.55
bar.example.com. 3600 IN RRSIG A 8 3 3600 20201116135527 20201019135527 55566 example.com. NYhmRicF4C9+YxpWeQrepy4ALM1CM0USoDuGi3W5Xtp4/+YpCJfSIdR9vlJaJ2WayYuZrz9Ai2ci7oWwE1Fn3oywGwCKvGo9m0c3mC2eEtphE19wrop6pWu6um4RiFhmzYS1voraA3PAdYzze9U4NHzlk0+sb5vNZW9dSZS30Ds=
bar.example.com. 3600 IN NSEC ding.example.com. A RRSIG NSEC
bar.example.com. 3600 IN RRSIG NSEC 8 3 3600 20201116135527 20201019135527 55566 example.com. VhsGuBx20DXQZNU8ITAMnasn6NVyEjN9xtB8msH5xJn80UCuaqvFBURzcPWN3aHnykEvGfdPF/9P3WvlON0cMikWkqSLy6Q9bpvgAq13HWYh+ZcDoqLtICaB7RkBQc+6aHAqZFyQbD8/m8Kxt5eVJtV6rEuf+yPX0+3aXHhsRg0=
ding.example.com. 3600 IN A 1.2.3.4
ding.example.com. 3600 IN RRSIG A 8 3 3600 20201116135527 20201019135527 55566 example.com. OERsruISkpd1s68ute8Xm8YXisBCTkkiDMt34K+0dVqvySOJq63d3qN18BeUxZxLyHDB1eR3nZZKqEdkTqrv2r98skhWhjnOECpFbu5gKjtN/KPexbbJ+rxC0QqciuWOC7M6YE0cvI17/RB9KhVRy5rqY2X4Gt2wk2CNeD1dAko=
ding.example.com. 3600 IN NSEC foo.example.com. A RRSIG NSEC
ding.example.com. 3600 IN RRSIG NSEC 8 3 3600 20201116135527 20201019135527 55566 example.com. nb1W2aaKrU5iAQiY8gMsoMOejID19JMTEwY2rRoe+KsvzMs0rE0ifEkqit4blXaU0tfy0foJ70uqdJFqBoGz1NcSwZ6GNk/iNfGvG3XpxZ/zqEe7kkIucqqei794G7z9psqV94yZ3WaT+IswPpWrSaWv1w41RtcWufPhe4fOAmU=
foo.example.com. 3600 IN A 1.2.3.4
foo.example.com. 3600 IN RRSIG A 8 3 3600 20201116135527 20201019135527 55566 example.com. ZcUngb2pUejwnsshbJN/Dfr+Bzu8fcZXyqLArQ+10Bw1IPHyfx7yyUJ43V5tTYVHPSEsJzTnaWj+olVrNhVZxq5e0pgzSYPfGln2FEItEvMIOn33j8yKTpPW2MLyuFF5ZkXhosG20EUwRMvMmRHRz9mIZfwWoMbSGPukmLh8zMA=
foo.example.com. 3600 IN NSEC ns.example.com. A RRSIG NSEC
foo.example.com. 3600 IN RRSIG NSEC 8 3 3600 20201116135527 20201019135527 55566 example.com. fUZEpkEULRWDntN5Z7Kr8M83Hjhf08ECMKRpo6IBoBc3ayenj+YMgWAvFXC825wjENPYYWNGag0d32U83zCZxqgv+8uXZd3B7QDpTbL41aWZdc++s5YWTkYjyOWwJ1XHOv4nL3qEnJBXVzo/E1gbSKhTFuG97i+7J1MFd9MsC5s=
ns.example.com. 3600 IN A 127.0.0.1
ns.example.com. 3600 IN RRSIG A 8 3 3600 20201116135527 20201019135527 55566 example.com. SiuxuPtN/ITd+Z20j8UNUHJWbLHirE8zQOWMv5fAZ1rPKpAidrZgUL8J417GdrTwkueU2ywAJ7EzFJSwNTa7o/wUnq7svmOR6Ze6UQsKuZFZGEfqPNDRp4YuF86LU5jChuo+f/IRpydHrxVwGxDPCR9KarDM+ewfW+yI5bZeZcg=
ns.example.com. 3600 IN NSEC www.example.com. A RRSIG NSEC
ns.example.com. 3600 IN RRSIG NSEC 8 3 3600 20201116135527 20201019135527 55566 example.com. 0upKNYjiow4NDJm3I1RbUddE9GGuFYEVKswww5BAc/6WHuukupncL30lskvcSKGpByDssP2Hi2CufyEtYeGWh6q1TxtOFRqFBX1p6Q5b3tBlCtvv4h31dQR9uqLvq+GkGS5MR+0LO5kWagIpZmnI8YY5plVdXEtNbp2Ar8zvz/A=
www.example.com. 3600 IN A 127.0.0.1
www.example.com. 3600 IN RRSIG A 8 3 3600 20201116135527 20201019135527 55566 example.com. AaIeICaPjV50TDrpbyOn94+hs8EYIMTmN4pYqj7e8GIGimqQIk5jgpwSx6SOoOF+uOqkf9GKHkQTn5YVGaeXwEQleg7mPTmMYKAOk06Y7MFUO1Vwt1Vt7Wo+Cpa3x2a1CmEkfFOi4WqP43VJnUtjjKmXoKRz3VUmqByyJYUAGbQ=
www.example.com. 3600 IN NSEC example.com. A RRSIG NSEC
www.example.com. 3600 IN RRSIG NSEC 8 3 3600 20201116135527 20201019135527 55566 example.com. meg/t6nIBqQZ0d5/dT7uu/3CuP4vE+HxqFQaj2fjUNceA/6C7QIQnqQ5Kyblg+XijDkQX0yvyFNHYdgF16UDgFT7tlNUCHk1SpF5BWzV4c4tBEhxASTz7UQo111O3Tyd6CldPzO/Se15Ud0/ZYltHEqWTfY5nJoXC/OJD9V2QOI=
FILE_END
SCENARIO_END
+6 -1
View File
@@ -107,7 +107,12 @@ check_expected_stats () {
# Check that the rest (unspecified) stats are all 0 (no surprises).
check_rest_stats () {
echo "> Checking rest stats"
grep -F -x -v -f $EXPECTED_STATS_FILE $FILTERED_STATS_FILE > $REST_STATS_FILE
grep -F -x -v -f $EXPECTED_STATS_FILE $FILTERED_STATS_FILE | \
grep -F -v -e "msg.cache.max_collisions" \
-e "rrset.cache.max_collisions" | \
> $REST_STATS_FILE
# It filtered out the 'max_collisions', since at random, a msg
# or rrset cache entry could have a cache hash collision. */
if test $DEBUG -ne 0; then
echo "Rest:"
cat $REST_STATS_FILE
+82
View File
@@ -0,0 +1,82 @@
; config options
server:
; This is the test key 29332 in the testdata.
trust-anchor: ". 3600 IN DS 29332 8 2 b75e26316631b6e37cbc977323a08769f86e36a10fee888676d35f61e2ff4181"
val-override-date: "20201020135527"
target-fetch-policy: "0 0 0 0 0"
qname-minimisation: no
fake-sha1: yes
trust-anchor-signaling: no
minimal-responses: no
log-servfail: yes
forward-zone:
name: "."
forward-addr: 10.5.5.5
CONFIG_END
SCENARIO_BEGIN Test nxdomain that gets unsigned response
; and the DS lookup that it makes gets an NSEC NXDOMAIN response.
; 10.5.5.5 forwarder
RANGE_BEGIN 0 100
ADDRESS 10.5.5.5
; unsigned NXDOMAIN response, from the first forwarder, here it is served
; from the upstream.
ENTRY_BEGIN
MATCH opcode qtype qname
ADJUST copy_id
REPLY QR NXDOMAIN
SECTION QUESTION
example.veryinvalid. IN TXT
SECTION AUTHORITY
. 3600 IN SOA ns.root. host.root. 1 3600 3600 3600 3600
ENTRY_END
; DNSKEY answer, using CSK for test simplicity.
ENTRY_BEGIN
MATCH opcode qtype qname
ADJUST copy_id
REPLY QR NOERROR
SECTION QUESTION
. IN DNSKEY
SECTION ANSWER
. IN DNSKEY 257 3 8 AwEAAb4WMOTBLTFvmBra5m6SK4VfViOzmvyUAU0qv861ZQXeEFvwlndqNU9rwRsMxrSWAYs5nHErKDn49usC/HyxxW1477iGFHhfgL4mjNreJm9zft2QFB1VLbRbEPYdDMLCn4co0qnG7/KG8W2i8Pym1L7f+aREwbLo+/716AS2PbaKMhfWLKLiq5wnBcUClQMNzCiwhqxDJp1oePqfkVdeUgXOtgi0dYRIKyQFhJ5VWJ22npoi/Gif0XLCADAlAwRLKc8o/yJkCxskzgpHpw5Cki1lclg0aq4ssOuPRQ+ne6IHYCz9D2mwzulblhLFamKdq7aHzNt4NlyxhpANVFiKLD8= ;{id = 29332 (ksk), size = 2048b}
. 3600 IN RRSIG DNSKEY 8 0 3600 20201116135527 20201019135527 29332 . ToK8hJrGa+kNu6y8FpRwZq2FjDPBAk5Ctchia3Vu9yTth2dR7BhK2ALTWVBwAQGwiwxXKoVK9QCxdQM0ti7CVb9x75bejkd2E6UGWVmqyTRPpn3D43qYARm87y3ZVKG7LlWHp8UOf21XLp1H7R+wuipIvBJ1XA+QGXThPdbV9EEz1kKGdprBfdpFkQdcAiuYYrOTa5cJ11z32mGiQ12fWjpb4UUbfcoDD9YOoa/S5a6h7jYBOfm75ZB8UCW3Z/SlsN8KIfYZsg5CZphpf38XH5uNLMmzpaWYhfamJZJve9Isx4eILNmdMLK4E8ESwDFCVNzMIqdf20VRg6Lh7nwQeA==
ENTRY_END
; answer for DS, from another forwarder, here returned from the test upstream.
ENTRY_BEGIN
MATCH opcode qtype qname
ADJUST copy_id
REPLY QR NXDOMAIN
SECTION QUESTION
veryinvalid. IN DS
SECTION AUTHORITY
versicherung. 3600 IN NSEC vet. NS DS RRSIG NSEC
versicherung. 3600 IN RRSIG NSEC 8 1 3600 20201116135527 20201019135527 29332 . AVAON9Y7AVwX9YWQK8JPcB6Wk/tEfQT7JrLiCRlBBQA0+mpVYYYtMyrm4aMjkhusqYcnpIZoLGOI/dxJjIwDgnMkd4EqY2oICea3I260f8z2v9e7zNobyUTjkoWsmLPc7VRLtEGKu1XyVpt7DX6ElGoSUhU4JsTx7wkkXU0SGAakL0bhK8K68B92NEVwgKX4D7+kVfpjc0aHaB3rAkhQCM/G0jEFp0RuhTX1aru6IuYrZmjW0dvQ2niec6NaYzvuGnbhMLlFLuXqSmI2B7uIFx894usd1cVWnSRg49bAkuiEv5q04ltRel1huJBGGiZlLEwanS5g53C5DHfq10OUrw==
. 3600 IN NSEC aaa. NS SOA RRSIG NSEC DNSKEY ZONEMD
. 3600 IN RRSIG NSEC 8 0 3600 20201116135527 20201019135527 29332 . E8r6rpFgFBUda2GnFSMzHZLtjy1dT+ZS0wPRE12RNwVK547bo2vByv9EFhOHS6sEIFqX+AmIJotuiEPKnCFUTr6FKscaxtw38dJRZ3wldqV6dmqUiRmz91crDCV5nSL45FIbkWKk1Q+tnXie3sZ4zwBc12kGg2BttMAQ0i4sbMbf6EUNYZGwYzSB0/VhXVJcl8gl+5lfpiVqfWNZI7vTEaHqrC2gBC3UK1cQE9lQOqhJ6H5ThA1FR9j/mZFM9sG5vQ2Mqlzl2iiN2Y6mCptDY1vwfff6AnT0YeDwJ/XwGisMZrSvTCYaiRndb8CUUmCr23AFy5OER1rmeFGkHX5+WQ==
. 3600 IN SOA ns.root. host.root. 1 3600 3600 3600 3600
. 3600 IN RRSIG SOA 8 0 3600 20201116135527 20201019135527 29332 . tVeReLMXPnl6rk4QX94xy9lCodQ+xc39lokbNkvbXnTURNCOAwtNiMMPlAAJ3/HTpIxo175gPfupACIveBtgajdp85jUIvLMOM5B6lX80+dUPBGZ4gHVjf+8EGnr7q2wnW2+KcJu0OhN2g+YqCV6aPi8pzuAp+AMsBYcMfXqEQq9Lxqv6TL50MUCJN3GPCyBIdjbs/A+ZB7D1EOO1YgdbsMHK/pWKYt4UfBFfekoA6joIGf4vBKKRTWnoo0BcrFob3AW1SyJkoxoqEsN3YAVL9jNkJCkU0/adLypHgDNayLgsWI5/o4Ng8LxN6tNxAilkMhcGY80T5g0uo+ukY7McA==
ENTRY_END
RANGE_END
STEP 1 QUERY
ENTRY_BEGIN
REPLY RD DO
SECTION QUESTION
example.veryinvalid. IN TXT
ENTRY_END
STEP 10 CHECK_ANSWER
ENTRY_BEGIN
MATCH all
REPLY QR RD RA DO SERVFAIL
SECTION QUESTION
example.veryinvalid. IN TXT
SECTION ANSWER
ENTRY_END
SCENARIO_END
-1
View File
@@ -11,7 +11,6 @@ server:
trust-anchor-signaling: no
iter-scrub-promiscuous: no
rrset-roundrobin: no
val-clean-additional: yes
stub-zone:
name: "."
-1
View File
@@ -12,7 +12,6 @@ server:
minimal-responses: no
iter-scrub-promiscuous: no
rrset-roundrobin: no
val-clean-additional: yes
stub-zone:
name: "."
+5 -7
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;
@@ -278,7 +279,7 @@ config_create(void)
cfg->val_sig_skew_min = 3600; /* at least daylight savings trouble */
cfg->val_sig_skew_max = 86400; /* at most timezone settings trouble */
cfg->val_max_restart = 5;
cfg->val_clean_additional = 0; /* off to protect against much data. */
cfg->val_clean_additional = 1;
cfg->val_log_level = 0;
cfg->val_log_squelch = 0;
cfg->val_permissive_mode = 0;
@@ -429,8 +430,6 @@ config_create(void)
cfg->iter_scrub_rrsig = 8;
cfg->iter_scrub_promiscuous = 1;
cfg->max_global_quota = 200;
cfg->val_validation_attempts = 32;
cfg->val_hash_attempts = 32;
return cfg;
error_exit:
config_delete(cfg);
@@ -459,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;
@@ -744,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)
@@ -787,8 +788,6 @@ int config_set_option(struct config_file* cfg, const char* opt,
else S_NUMBER_OR_ZERO("iter-scrub-rrsig:", iter_scrub_rrsig)
else S_YNO("iter-scrub-promiscuous:", iter_scrub_promiscuous)
else S_NUMBER_OR_ZERO("max-global-quota:", max_global_quota)
else S_NUMBER_OR_ZERO("val-validation-attempts:", val_validation_attempts)
else S_NUMBER_OR_ZERO("val-hash-attempts:", val_hash_attempts)
else S_YNO("serve-original-ttl:", serve_original_ttl)
else S_STR("val-nsec3-keysize-iterations:", val_nsec3_key_iterations)
else S_YNO("zonemd-permissive-mode:", zonemd_permissive_mode)
@@ -1269,8 +1268,6 @@ config_get_option(struct config_file* cfg, const char* opt,
else O_DEC(opt, "iter-scrub-rrsig", iter_scrub_rrsig)
else O_YNO(opt, "iter-scrub-promiscuous", iter_scrub_promiscuous)
else O_DEC(opt, "max-global-quota", max_global_quota)
else O_DEC(opt, "val-validation-attempts", val_validation_attempts)
else O_DEC(opt, "val-hash-attempts", val_hash_attempts)
else O_YNO(opt, "serve-original-ttl", serve_original_ttl)
else O_STR(opt, "val-nsec3-keysize-iterations",val_nsec3_key_iterations)
else O_YNO(opt, "zonemd-permissive-mode", zonemd_permissive_mode)
@@ -1290,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 -4
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;
@@ -798,10 +800,6 @@ struct config_file {
int iter_scrub_rrsig;
/** limit on upstream queries for an incoming query and subqueries. */
int max_global_quota;
/** limit on validator validation attempts. */
int val_validation_attempts;
/** limit on validator hash attempts. */
int val_hash_attempts;
/** Should the iterator scrub promiscuous NS rrsets, from positive
* answers. */
int iter_scrub_promiscuous;
+1 -2
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) }
@@ -608,8 +609,6 @@ iter-scrub-ns{COLON} { YDVAR(1, VAR_ITER_SCRUB_NS) }
iter-scrub-cname{COLON} { YDVAR(1, VAR_ITER_SCRUB_CNAME) }
iter-scrub-rrsig{COLON} { YDVAR(1, VAR_ITER_SCRUB_RRSIG) }
max-global-quota{COLON} { YDVAR(1, VAR_MAX_GLOBAL_QUOTA) }
val-validation-attempts{COLON} { YDVAR(1, VAR_VAL_VALIDATION_ATTEMPTS) }
val-hash-attempts{COLON} { YDVAR(1, VAR_VAL_HASH_ATTEMPTS) }
max-transfer-size{COLON} { YDVAR(1, VAR_MAX_TRANSFER_SIZE) }
max-transfer-time{COLON} { YDVAR(1, VAR_MAX_TRANSFER_TIME) }
iter-scrub-promiscuous{COLON} { YDVAR(1, VAR_ITER_SCRUB_PROMISCUOUS) }
+12 -21
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
@@ -218,7 +219,6 @@ extern struct config_parser_state* cfg_parser;
%token VAR_ITER_SCRUB_RRSIG
%token VAR_MAX_TRANSFER_SIZE VAR_MAX_TRANSFER_TIME
%token VAR_MAX_GLOBAL_QUOTA VAR_HARDEN_UNVERIFIED_GLUE VAR_LOG_TIME_ISO
%token VAR_VAL_VALIDATION_ATTEMPTS VAR_VAL_HASH_ATTEMPTS
%token VAR_ITER_SCRUB_PROMISCUOUS VAR_LOG_THREAD_ID
%%
@@ -344,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 |
@@ -361,8 +362,7 @@ content_server: server_num_threads | server_verbosity | server_port |
server_harden_unknown_additional | server_disable_edns_do |
server_log_destaddr | server_cookie_secret_file |
server_iter_scrub_ns | server_iter_scrub_cname | server_max_global_quota |
server_val_validation_attempts |
server_val_hash_attempts | server_iter_scrub_rrsig |
server_iter_scrub_rrsig |
server_harden_unverified_glue | server_log_time_iso | server_iter_scrub_promiscuous
;
stub_clause: stubstart contents_stub
@@ -4241,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));
@@ -4319,24 +4328,6 @@ server_iter_scrub_promiscuous: VAR_ITER_SCRUB_PROMISCUOUS STRING_ARG
free($2);
}
;
server_val_validation_attempts: VAR_VAL_VALIDATION_ATTEMPTS STRING_ARG
{
OUTYY(("P(server_val_validation_attempts:%s)\n", $2));
if(atoi($2) == 0 && strcmp($2, "0") != 0)
yyerror("number expected");
else cfg_parser->cfg->val_validation_attempts = atoi($2);
free($2);
}
;
server_val_hash_attempts: VAR_VAL_HASH_ATTEMPTS STRING_ARG
{
OUTYY(("P(server_val_hash_attempts:%s)\n", $2));
if(atoi($2) == 0 && strcmp($2, "0") != 0)
yyerror("number expected");
else cfg_parser->cfg->val_hash_attempts = atoi($2);
free($2);
}
;
ipsetstart: VAR_IPSET
{
OUTYY(("\nP(ipset:)\n"));
+7 -14
View File
@@ -192,34 +192,34 @@ pkt_dname_len(sldns_buffer* pkt)
while(1) {
/* read next label */
if(sldns_buffer_remaining(pkt) < 1)
goto fail;
return 0;
labellen = sldns_buffer_read_u8(pkt);
if(LABEL_IS_PTR(labellen)) {
/* compression ptr */
uint16_t ptr;
if(sldns_buffer_remaining(pkt) < 1)
goto fail;
return 0;
ptr = PTR_OFFSET(labellen, sldns_buffer_read_u8(pkt));
if(ptrcount++ > MAX_COMPRESS_PTRS)
goto fail; /* loop! */
return 0; /* loop! */
if(sldns_buffer_limit(pkt) <= ptr)
goto fail; /* out of bounds! */
return 0; /* out of bounds! */
if(!endpos)
endpos = sldns_buffer_position(pkt);
sldns_buffer_set_position(pkt, ptr);
} else {
/* label contents */
if(labellen > 0x3f)
goto fail; /* label too long */
return 0; /* label too long */
len += 1 + labellen;
if(len > LDNS_MAX_DOMAINLEN)
goto fail;
return 0;
if(labellen == 0) {
/* end of dname */
break;
}
if(sldns_buffer_remaining(pkt) < labellen)
goto fail;
return 0;
sldns_buffer_skip(pkt, (ssize_t)labellen);
}
}
@@ -227,13 +227,6 @@ pkt_dname_len(sldns_buffer* pkt)
sldns_buffer_set_position(pkt, endpos);
return len;
fail:
/* Restore the position on failure too: callers (rdata_copy) compute
* the consumed field length from the buffer position and must not
* see a partial walk of a name that failed to parse. */
if(endpos)
sldns_buffer_set_position(pkt, endpos);
return 0;
}
int
+5 -40
View File
@@ -248,7 +248,6 @@ rdata_copy(sldns_buffer* pkt, struct packed_rrset_data* data, uint8_t* to,
sldns_pkt_section section)
{
uint16_t pkt_len;
size_t tolen;
uint32_t ttl;
const sldns_rr_descriptor* desc;
@@ -294,13 +293,9 @@ rdata_copy(sldns_buffer* pkt, struct packed_rrset_data* data, uint8_t* to,
(rr->ttl_data - sldns_buffer_begin(pkt) + sizeof(uint32_t)));
/* insert decompressed size into rdata len stored in memory */
/* -2 because rdatalen bytes are not included. */
tolen = rr->size;
if(tolen < 2)
return 0;
pkt_len = htons(rr->size - 2);
memmove(to, &pkt_len, sizeof(uint16_t));
to += 2;
tolen -= 2;
/* read packet rdata len */
pkt_len = sldns_buffer_read_u16(pkt);
if(sldns_buffer_remaining(pkt) < pkt_len)
@@ -309,29 +304,16 @@ rdata_copy(sldns_buffer* pkt, struct packed_rrset_data* data, uint8_t* to,
if(pkt_len > 0 && desc && desc->_dname_count > 0) {
int count = (int)desc->_dname_count;
int rdf = 0;
size_t len, dlen;
size_t oldpos, newpos;
size_t len;
size_t oldpos;
/* decompress dnames. */
while(pkt_len > 0 && count) {
switch(desc->_wireformat[rdf]) {
case LDNS_RDF_TYPE_DNAME:
oldpos = sldns_buffer_position(pkt);
dlen = pkt_dname_len(pkt);
if(dlen == 0)
return 0; /* malformed */
if(dlen > tolen)
return 0; /* alloc mismatch */
newpos = sldns_buffer_position(pkt);
if(oldpos > newpos)
return 0; /* should have moved forward*/
sldns_buffer_set_position(pkt, oldpos);
dname_pkt_copy(pkt, to,
dname_pkt_copy(pkt, to,
sldns_buffer_current(pkt));
sldns_buffer_set_position(pkt, newpos);
to += dlen;
tolen -= dlen;
if(sldns_buffer_position(pkt)-oldpos > pkt_len)
return 0; /* malformed: walks diverged */
to += pkt_dname_len(pkt);
pkt_len -= sldns_buffer_position(pkt)-oldpos;
count--;
len = 0;
@@ -344,12 +326,9 @@ rdata_copy(sldns_buffer* pkt, struct packed_rrset_data* data, uint8_t* to,
break;
}
if(len) {
if(len > tolen)
return 0; /* alloc mismatch */
log_assert(len <= pkt_len);
memmove(to, sldns_buffer_current(pkt), len);
to += len;
tolen -= len;
sldns_buffer_skip(pkt, (ssize_t)len);
pkt_len -= len;
}
@@ -357,11 +336,8 @@ rdata_copy(sldns_buffer* pkt, struct packed_rrset_data* data, uint8_t* to,
}
}
/* copy remaining rdata */
if(pkt_len > 0) {
if(pkt_len > tolen)
return 0; /* alloc mismatch */
if(pkt_len > 0)
memmove(to, sldns_buffer_current(pkt), pkt_len);
}
return 1;
}
@@ -1139,17 +1115,6 @@ reply_all_rrsets_secure(struct reply_info* rep)
return 1;
}
int reply_an_ns_rrsets_secure(struct reply_info* rep)
{
size_t i;
for(i=0; i<rep->an_numrrsets+rep->ns_numrrsets; i++) {
if( ((struct packed_rrset_data*)rep->rrsets[i]->entry.data)
->security != sec_status_secure )
return 0;
}
return 1;
}
struct reply_info*
parse_reply_in_temp_region(sldns_buffer* pkt, struct regional* region,
struct query_info* qi)
-3
View File
@@ -494,9 +494,6 @@ int reply_check_cname_chain(struct query_info* qinfo, struct reply_info* rep);
*/
int reply_all_rrsets_secure(struct reply_info* rep);
/** Check status of answer and authority section RRs. */
int reply_an_ns_rrsets_secure(struct reply_info* rep);
/**
* Find answer rrset in reply, the one matching qinfo. Follows CNAMEs, so the
* result may have a different owner name.
+3 -1
View File
@@ -280,7 +280,9 @@ int packed_rr_to_string(struct ub_packed_rrset_key* rrset, size_t i,
size_t rlen = rrset->rk.dname_len + 2 + 2 + 4 + d->rr_len[i];
time_t adjust = 0;
log_assert(dest_len > 0 && dest);
if(rlen > dest_len) {
/* rlen is the length written into rr, dest_len bounds the output
* string; check both, callers can pass a dest_len over sizeof(rr). */
if(rlen > dest_len || rlen > sizeof(rr)) {
dest[0] = 0;
return 0;
}
+2 -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"
@@ -141,8 +142,6 @@ fptr_whitelist_comm_timer(void (*fptr)(void*))
#ifdef UB_ON_WINDOWS
else if(fptr == &wsvc_cron_cb) return 1;
#endif
else if(fptr == &tcp_read_again_cb) return 1;
else if(fptr == &tcp_more_read_again_cb) return 1;
else if(fptr == &auth_xfer_timer) return 1;
else if(fptr == &auth_xfer_probe_timer_callback) return 1;
else if(fptr == &auth_xfer_transfer_timer_callback) return 1;
@@ -193,6 +192,7 @@ fptr_whitelist_event(void (*fptr)(int, short, void *))
else if(fptr == &comm_point_doq_callback) return 1;
#endif
else if(fptr == &fast_reload_service_cb) return 1;
else if(fptr == &worker_auth_load_service_cb) return 1;
#ifdef USE_DNSTAP
else if(fptr == &dtio_output_cb) return 1;
else if(fptr == &dtio_cmd_cb) return 1;
+21
View File
@@ -72,6 +72,14 @@ static ub_thread_key_type logkey;
#ifndef THREADS_DISABLED
/** pthread mutex to protect FILE* */
static lock_basic_type log_lock;
#ifdef HAVE_PTHREAD
/* Guards the one-time initialization below. Without this, two threads
* calling log_init() for the first time concurrently (e.g. via
* ub_ctx_create() from a multi-threaded application) can both observe
* key_created==0 and both call lock_basic_init(&log_lock), a data race
* that reinitializes/corrupts an in-use mutex. */
static pthread_once_t log_lock_once = PTHREAD_ONCE_INIT;
#endif
#endif
/** the identity of this executable/process */
static const char* ident="unbound";
@@ -85,15 +93,28 @@ static int log_time_asc = 0;
/** print time in iso format */
static int log_time_iso = 0;
#if !defined(THREADS_DISABLED) && defined(HAVE_PTHREAD)
static void log_lock_init_once(void)
{
ub_thread_key_create(&logkey, NULL);
lock_basic_init(&log_lock);
key_created = 1;
}
#endif
void
log_init(const char* filename, int use_syslog, const char* chrootdir)
{
FILE *f;
#if !defined(THREADS_DISABLED) && defined(HAVE_PTHREAD)
(void)pthread_once(&log_lock_once, log_lock_init_once);
#else
if(!key_created) {
key_created = 1;
ub_thread_key_create(&logkey, NULL);
lock_basic_init(&log_lock);
}
#endif
lock_basic_lock(&log_lock);
if(logfile
#if defined(HAVE_SYSLOG_H) || defined(UB_ON_WINDOWS)
+360
View File
@@ -58,6 +58,9 @@
#include "sldns/wire2str.h"
#include "sldns/str2wire.h"
#include <fcntl.h>
#ifdef HAVE_POLL_H
#include <poll.h>
#endif
#ifdef HAVE_OPENSSL_SSL_H
#include <openssl/ssl.h>
#include <openssl/evp.h>
@@ -92,6 +95,9 @@ int RRSET_ROUNDROBIN = 1;
/** log tag queries with name instead of 'info' for filtering */
int LOG_TAG_QUERYREPLY = 0;
/** What number of loop iterations is too much for sock poll retries */
#define SOCK_POLL_LOOP_MAX 200
#ifdef HAVE_SSL
static struct tls_session_ticket_key {
unsigned char *key_name;
@@ -2083,3 +2089,357 @@ hex_pton(const char* src, uint8_t* target, size_t targsize)
}
return t-target;
}
int
sock_poll_timeout(int fd, int timeout, int pollin, int pollout, int* event)
{
int loopcount = 0;
/* Loop if the system call returns an errno to do so, like EINTR. */
log_assert(pollin || pollout);
while(1) {
struct pollfd p, *fds;
int nfds, ret;
if(++loopcount > SOCK_POLL_LOOP_MAX) {
log_err("sock_poll_timeout: loop");
if(event)
*event = 0;
return 0;
}
if(fd == -1) {
fds = NULL;
nfds = 0;
} else {
fds = &p;
nfds = 1;
memset(&p, 0, sizeof(p));
p.fd = fd;
#ifndef USE_WINSOCK
p.events = POLLERR
| POLLHUP
;
#endif
if(pollin)
p.events |= POLLIN;
if(pollout)
p.events |= POLLOUT;
}
#ifndef USE_WINSOCK
ret = poll(fds, nfds, timeout);
#else
if(fds == NULL) {
Sleep(timeout);
ret = 0;
} else {
ret = WSAPoll(fds, nfds, timeout);
}
#endif
if(ret == -1) {
#ifndef USE_WINSOCK
if(
errno == EINTR || errno == EAGAIN
# ifdef EWOULDBLOCK
|| errno == EWOULDBLOCK
# endif
) continue; /* Try again. */
#endif
/* For WSAPoll we only get errors here:
* o WSAENETDOWN
* o WSAEFAULT
* o WSAEINVAL
* o WSAENOBUFS
*/
log_err("poll: %s", sock_strerror(errno));
if(event)
*event = 0;
return 0;
} else if(ret == 0) {
/* Timeout */
if(event)
*event = 0;
return 1;
}
break;
}
if(event)
*event = 1;
return 1;
}
int
create_socketpair(int* pair, struct ub_randstate* rand)
{
#ifndef USE_WINSOCK
if(socketpair(AF_UNIX, SOCK_STREAM, 0, pair) == -1) {
log_err("socketpair: %s", strerror(errno));
return 0;
}
(void)rand;
#else
struct sockaddr_in addr, baddr, accaddr, connaddr;
socklen_t baddrlen, accaddrlen, connaddrlen;
uint8_t localhost[] = {127, 0, 0, 1};
uint8_t nonce[16], recvnonce[16];
size_t i;
int lst, pollin_event, bcount, loopcount;
int connect_poll_timeout = 200; /* msec to wait for connection */
ssize_t ret;
pair[0] = -1;
pair[1] = -1;
for(i=0; i<sizeof(nonce); i++) {
nonce[i] = ub_random_max(rand, 256);
}
lst = socket(AF_INET, SOCK_STREAM, 0);
if(lst == -1) {
log_err("create_socketpair: socket: %s", sock_strerror(errno));
return 0;
}
memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_port = 0;
memcpy(&addr.sin_addr, localhost, 4);
if(bind(lst, (struct sockaddr*)&addr, (socklen_t)sizeof(addr))
== -1) {
log_err("create socketpair: bind: %s", sock_strerror(errno));
sock_close(lst);
return 0;
}
if(listen(lst, 12) == -1) {
log_err("create socketpair: listen: %s", sock_strerror(errno));
sock_close(lst);
return 0;
}
pair[1] = socket(AF_INET, SOCK_STREAM, 0);
if(pair[1] == -1) {
log_err("create socketpair: socket: %s", sock_strerror(errno));
sock_close(lst);
return 0;
}
baddrlen = (socklen_t)sizeof(baddr);
if(getsockname(lst, (struct sockaddr*)&baddr, &baddrlen) == -1) {
log_err("create socketpair: getsockname: %s",
sock_strerror(errno));
sock_close(lst);
sock_close(pair[1]);
pair[1] = -1;
return 0;
}
if(baddrlen > (socklen_t)sizeof(baddr)) {
log_err("create socketpair: getsockname returned addr too big");
sock_close(lst);
sock_close(pair[1]);
pair[1] = -1;
return 0;
}
/* the socket is blocking */
if(connect(pair[1], (struct sockaddr*)&baddr, baddrlen) == -1) {
log_err("create socketpair: connect: %s",
sock_strerror(errno));
sock_close(lst);
sock_close(pair[1]);
pair[1] = -1;
return 0;
}
if(!sock_poll_timeout(lst, connect_poll_timeout, 1, 0, &pollin_event)) {
log_err("create socketpair: poll for accept failed: %s",
sock_strerror(errno));
sock_close(lst);
sock_close(pair[1]);
pair[1] = -1;
return 0;
}
if(!pollin_event) {
log_err("create socketpair: poll timeout for accept");
sock_close(lst);
sock_close(pair[1]);
pair[1] = -1;
return 0;
}
accaddrlen = (socklen_t)sizeof(accaddr);
pair[0] = accept(lst, (struct sockaddr*)&accaddr, &accaddrlen);
if(pair[0] == -1) {
log_err("create socketpair: accept: %s", sock_strerror(errno));
sock_close(lst);
sock_close(pair[1]);
pair[1] = -1;
return 0;
}
if(accaddrlen > (socklen_t)sizeof(accaddr)) {
log_err("create socketpair: accept returned addr too big");
sock_close(lst);
sock_close(pair[0]);
sock_close(pair[1]);
pair[0] = -1;
pair[1] = -1;
return 0;
}
if(accaddr.sin_family != AF_INET ||
memcmp(localhost, &accaddr.sin_addr, 4) != 0) {
log_err("create socketpair: accept from wrong address");
sock_close(lst);
sock_close(pair[0]);
sock_close(pair[1]);
pair[0] = -1;
pair[1] = -1;
return 0;
}
connaddrlen = (socklen_t)sizeof(connaddr);
if(getsockname(pair[1], (struct sockaddr*)&connaddr, &connaddrlen)
== -1) {
log_err("create socketpair: getsockname connectedaddr: %s",
sock_strerror(errno));
sock_close(lst);
sock_close(pair[0]);
sock_close(pair[1]);
pair[0] = -1;
pair[1] = -1;
return 0;
}
if(connaddrlen > (socklen_t)sizeof(connaddr)) {
log_err("create socketpair: getsockname connectedaddr returned addr too big");
sock_close(lst);
sock_close(pair[0]);
sock_close(pair[1]);
pair[0] = -1;
pair[1] = -1;
return 0;
}
if(connaddr.sin_family != AF_INET ||
memcmp(localhost, &connaddr.sin_addr, 4) != 0) {
log_err("create socketpair: getsockname connectedaddr returned wrong address");
sock_close(lst);
sock_close(pair[0]);
sock_close(pair[1]);
pair[0] = -1;
pair[1] = -1;
return 0;
}
if(accaddr.sin_port != connaddr.sin_port) {
log_err("create socketpair: accept from wrong port");
sock_close(lst);
sock_close(pair[0]);
sock_close(pair[1]);
pair[0] = -1;
pair[1] = -1;
return 0;
}
sock_close(lst);
loopcount = 0;
bcount = 0;
while(1) {
if(++loopcount > SOCK_POLL_LOOP_MAX) {
log_err("create socketpair: send failed due to loop");
sock_close(pair[0]);
sock_close(pair[1]);
pair[0] = -1;
pair[1] = -1;
return 0;
}
ret = send(pair[1], (void*)(nonce+bcount),
sizeof(nonce)-bcount, 0);
if(ret == -1) {
if(
#ifndef USE_WINSOCK
errno == EINTR || errno == EAGAIN
# ifdef EWOULDBLOCK
|| errno == EWOULDBLOCK
# endif
#else
WSAGetLastError() == WSAEINTR ||
WSAGetLastError() == WSAEINPROGRESS ||
WSAGetLastError() == WSAEWOULDBLOCK
#endif
)
continue; /* Try again. */
log_err("create socketpair: send: %s", sock_strerror(errno));
sock_close(pair[0]);
sock_close(pair[1]);
pair[0] = -1;
pair[1] = -1;
return 0;
} else if(ret+(ssize_t)bcount != sizeof(nonce)) {
bcount += ret;
if((size_t)bcount < sizeof(nonce))
continue;
}
break;
}
if(!sock_poll_timeout(pair[0], connect_poll_timeout, 1, 0, &pollin_event)) {
log_err("create socketpair: poll failed: %s",
sock_strerror(errno));
sock_close(pair[0]);
sock_close(pair[1]);
pair[0] = -1;
pair[1] = -1;
return 0;
}
if(!pollin_event) {
log_err("create socketpair: poll timeout for recv");
sock_close(pair[0]);
sock_close(pair[1]);
pair[0] = -1;
pair[1] = -1;
return 0;
}
loopcount = 0;
bcount = 0;
while(1) {
if(++loopcount > SOCK_POLL_LOOP_MAX) {
log_err("create socketpair: recv failed due to loop");
sock_close(pair[0]);
sock_close(pair[1]);
pair[0] = -1;
pair[1] = -1;
return 0;
}
ret = recv(pair[0], (void*)(recvnonce+bcount),
sizeof(nonce)-bcount, 0);
if(ret == -1) {
if(
#ifndef USE_WINSOCK
errno == EINTR || errno == EAGAIN
# ifdef EWOULDBLOCK
|| errno == EWOULDBLOCK
# endif
#else
WSAGetLastError() == WSAEINTR ||
WSAGetLastError() == WSAEINPROGRESS ||
WSAGetLastError() == WSAEWOULDBLOCK
#endif
)
continue; /* Try again. */
log_err("create socketpair: recv: %s", sock_strerror(errno));
sock_close(pair[0]);
sock_close(pair[1]);
pair[0] = -1;
pair[1] = -1;
return 0;
} else if(ret == 0) {
log_err("create socketpair: stream closed");
sock_close(pair[0]);
sock_close(pair[1]);
pair[0] = -1;
pair[1] = -1;
return 0;
} else if(ret+(ssize_t)bcount != sizeof(nonce)) {
bcount += ret;
if((size_t)bcount < sizeof(nonce))
continue;
}
break;
}
if(memcmp(nonce, recvnonce, sizeof(nonce)) != 0) {
log_err("create socketpair: recv wrong nonce");
sock_close(pair[0]);
sock_close(pair[1]);
pair[0] = -1;
pair[1] = -1;
return 0;
}
#endif
return 1;
}
+16
View File
@@ -604,4 +604,20 @@ ssize_t hex_ntop(uint8_t const *src, size_t srclength, char *target,
/** Convert hexadecimal data to binary. */
ssize_t hex_pton(const char* src, uint8_t* target, size_t targsize);
/**
* This routine polls a socket for readiness.
* @param fd: file descriptor, -1 uses no fd for a timer only.
* @param timeout: time in msec to wait. 0 means nonblocking test,
* -1 waits blocking for events.
* @param pollin: check for input event.
* @param pollout: check for output event.
* @param event: output variable, set to true if the event happens.
* It is false if there was an error or timeout.
* @return false is system call failure, also logged.
*/
int sock_poll_timeout(int fd, int timeout, int pollin, int pollout, int* event);
/** create a socketpair for bidirectional communication, false on failure */
int create_socketpair(int* pair, struct ub_randstate* rand);
#endif /* NET_HELP_H */
+1 -86
View File
@@ -122,10 +122,6 @@
#define NUM_UDP_PER_SELECT 1
#endif
/** The number of TCP queries over a TCP connection, per read indication
* from select. */
#define NUM_TCP_PER_SELECT 100
/** timeout in millisec to wait for write to unblock, packets dropped after.*/
#define SEND_BLOCKED_WAIT_TIMEOUT 200
/** max number of times to wait for write to unblock, packets dropped after.*/
@@ -3230,26 +3226,6 @@ static int http2_submit_settings(struct http2_session* h2_session)
}
#endif /* HAVE_NGHTTP2 */
/** Clear http2 stream mesh states */
static void http2_session_clear_meshstate(struct http2_session* h2_session)
{
#ifdef HAVE_NGHTTP2
/* Since the session gets closed, remove the mesh state references. */
struct http2_stream* h2_stream;
for(h2_stream = h2_session->first_stream; h2_stream;
h2_stream = h2_stream->next) {
if(h2_stream->mesh_state) {
mesh_state_remove_reply(h2_stream->mesh,
h2_stream->mesh_state, h2_session->c,
h2_stream, NULL);
h2_stream->mesh_state = NULL;
}
}
#else
(void)h2_session;
#endif /* HAVE_NGHTTP2 */
}
#ifdef HAVE_NGHTTP2
/** Delete http2 stream. After session delete or stream close callback */
static void http2_stream_delete(struct http2_session* h2_session,
@@ -4645,10 +4621,6 @@ comm_point_tcp_handle_write(int fd, struct comm_point* c)
static int
tcp_req_info_read_again(int fd, struct comm_point* c)
{
/* One event-loop visit drains at most this many pipelined queries;
* the rest is re-queued, so that other file descriptors get
* serviced in between. */
int budget = NUM_TCP_PER_SELECT;
while(c->tcp_req_info->read_again) {
int r;
c->tcp_req_info->read_again = 0;
@@ -4665,16 +4637,6 @@ tcp_req_info_read_again(int fd, struct comm_point* c)
}
return 0;
}
if(--budget <= 0 && c->tcp_req_info->read_again) {
/* Defer the rest of the drain to the next loop turn.
* This uses a zero delay timer. For TLS the undrained
* remainder sits in OpenSSL's user-space buffer. */
struct timeval tv;
memset(&tv, 0, sizeof(tv));
verbose(VERB_ALGO, "Defer tcp_req_info read again");
comm_timer_set(c->tcp_req_info->read_again_timer, &tv);
return 1;
}
}
return 1;
}
@@ -4688,7 +4650,6 @@ tcp_more_read_again(int fd, struct comm_point* c)
/* this continues until the read routines get EAGAIN or so,
* and thus does not call the callback, and the bool is 0 */
int* moreread = c->tcp_more_read_again;
int budget = NUM_TCP_PER_SELECT;
while(moreread && *moreread) {
*moreread = 0;
if(!comm_point_tcp_handle_read(fd, c, 0)) {
@@ -4701,30 +4662,6 @@ tcp_more_read_again(int fd, struct comm_point* c)
}
return;
}
if(--budget <= 0 && *moreread) {
/* Defer the rest of the drain to the next loop turn.
* This uses a zero delay timer. For TLS the undrained
* remainder sits in OpenSSL's user-space buffer. */
struct timeval tv;
memset(&tv, 0, sizeof(tv));
if(!c->tcp_more_read_again_timer) {
c->tcp_more_read_again_timer = comm_timer_create(c->ev->base, tcp_more_read_again_cb, c);
if(!c->tcp_more_read_again_timer) {
log_err("out of memory for tcp more read again timer");
reclaim_tcp_handler(c);
if(!c->tcp_do_close) {
fptr_ok(fptr_whitelist_comm_point(
c->callback));
(void)(*c->callback)(c, c->cb_arg,
NETEVENT_CLOSED, NULL);
}
return;
}
}
verbose(VERB_ALGO, "Defer more read again");
comm_timer_set(c->tcp_more_read_again_timer, &tv);
return;
}
}
}
@@ -4752,23 +4689,6 @@ tcp_more_write_again(int fd, struct comm_point* c)
}
}
void
tcp_read_again_cb(void* arg)
{
struct tcp_req_info* req = (struct tcp_req_info*)arg;
verbose(VERB_ALGO, "tcp_read_again_cb");
if(!tcp_req_info_read_again(req->cp->fd, req->cp))
return;
}
void
tcp_more_read_again_cb(void* arg)
{
struct comm_point* c = (struct comm_point*)arg;
verbose(VERB_ALGO, "tcp_more_read_again_cb");
tcp_more_read_again(c->fd, c);
}
void
comm_point_tcp_handle_callback(int fd, short event, void* arg)
{
@@ -6211,7 +6131,7 @@ comm_point_create_tcp_handler(struct comm_base *base,
c->pp2_enabled = parent->pp2_enabled;
c->pp2_header_state = pp2_header_none;
if(spoolbuf) {
c->tcp_req_info = tcp_req_info_create(base, spoolbuf);
c->tcp_req_info = tcp_req_info_create(spoolbuf);
if(!c->tcp_req_info) {
log_err("could not create tcp commpoint");
sldns_buffer_free(c->buffer);
@@ -6773,9 +6693,6 @@ comm_point_close(struct comm_point* c)
*c->tcp_more_read_again = 0;
if(c->tcp_more_write_again && *c->tcp_more_write_again)
*c->tcp_more_write_again = 0;
if(c->tcp_more_read_again_timer &&
comm_timer_is_set(c->tcp_more_read_again_timer))
comm_timer_disable(c->tcp_more_read_again_timer);
/* close fd after removing from event lists, or epoll.. is messed up */
if(c->fd != -1 && !c->do_not_close) {
@@ -6815,7 +6732,6 @@ comm_point_delete(struct comm_point* c)
free(c->tcp_handlers);
}
free(c->timeout);
comm_timer_delete(c->tcp_more_read_again_timer);
if(c->type == comm_tcp || c->type == comm_local || c->type == comm_http) {
sldns_buffer_free(c->buffer);
#ifdef USE_DNSCRYPT
@@ -6956,7 +6872,6 @@ comm_point_drop_reply(struct comm_reply* repinfo)
if(repinfo->c->type == comm_http) {
if(repinfo->c->h2_session) {
repinfo->c->h2_session->is_drop = 1;
http2_session_clear_meshstate(repinfo->c->h2_session);
if(!repinfo->c->h2_session->postpone_drop)
reclaim_http_handler(repinfo->c);
return;
-9
View File
@@ -384,9 +384,6 @@ struct comm_point {
* Or leave NULL if it is not used at all. */
int* tcp_more_write_again;
/** resume timer for tcp_more_read_again */
struct comm_timer* tcp_more_read_again_timer;
/** if set, read/write completes:
read/write state of tcp is toggled.
buffer reset/bytecount reset.
@@ -1136,12 +1133,6 @@ void doq_send_pkt(struct comm_point* c, struct doq_pkt_addr* paddr,
/** doq timer callback function. */
void doq_timer_cb(void* arg);
/** tcp read again callback function. For tcp req info listen. */
void tcp_read_again_cb(void* arg);
/** tcp more read again callback function. For outside network. */
void tcp_more_read_again_cb(void* arg);
/**
* This routine is published for checks and tests, and is only used internally.
* handle libevent callback for timer comm.
+9 -13
View File
@@ -1297,13 +1297,12 @@ void autr_write_file(struct module_env* env, struct trust_anchor* tp)
* @param tp: trust point to verify with
* @param rrset: DNSKEY rrset to verify.
* @param qstate: qstate with region.
* @param vq: validator query state.
* @return false on failure, true if verification successful.
*/
static int
verify_dnskey(struct module_env* env, struct val_env* ve,
struct trust_anchor* tp, struct ub_packed_rrset_key* rrset,
struct module_qstate* qstate, struct val_qstate* vq)
struct module_qstate* qstate)
{
char reasonbuf[256];
char* reason = NULL;
@@ -1311,7 +1310,7 @@ verify_dnskey(struct module_env* env, struct val_env* ve,
int downprot = env->cfg->harden_algo_downgrade;
enum sec_status sec = val_verify_DNSKEY_with_TA(env, ve, rrset,
tp->ds_rrset, tp->dnskey_rrset, downprot?sigalg:NULL, &reason,
NULL, qstate, vq, reasonbuf, sizeof(reasonbuf));
NULL, qstate, reasonbuf, sizeof(reasonbuf));
/* sigalg is ignored, it returns algorithms signalled to exist, but
* in 5011 there are no other rrsets to check. if downprot is
* enabled, then it checks that the DNSKEY is signed with all
@@ -1351,18 +1350,16 @@ min_expiry(struct module_env* env, struct packed_rrset_data* dd)
static int
rr_is_selfsigned_revoked(struct module_env* env, struct val_env* ve,
struct ub_packed_rrset_key* dnskey_rrset, size_t i,
struct module_qstate* qstate, struct val_qstate* vq)
struct module_qstate* qstate)
{
enum sec_status sec;
char* reason = NULL;
size_t num_tagmatches = 0;
verbose(VERB_ALGO, "seen REVOKE flag, check self-signed, rr %d",
(int)i);
/* no algorithm downgrade protection necessary, if it is selfsigned
* revoked it can be removed. */
sec = dnskey_verify_rrset(env, ve, dnskey_rrset, dnskey_rrset, i,
&reason, NULL, LDNS_SECTION_ANSWER, qstate, vq,
&num_tagmatches);
&reason, NULL, LDNS_SECTION_ANSWER, qstate);
return (sec == sec_status_secure);
}
@@ -1578,7 +1575,7 @@ init_events(struct trust_anchor* tp)
static void
check_contains_revoked(struct module_env* env, struct val_env* ve,
struct trust_anchor* tp, struct ub_packed_rrset_key* dnskey_rrset,
int* changed, struct module_qstate* qstate, struct val_qstate* vq)
int* changed, struct module_qstate* qstate)
{
struct packed_rrset_data* dd = (struct packed_rrset_data*)
dnskey_rrset->entry.data;
@@ -1598,8 +1595,7 @@ check_contains_revoked(struct module_env* env, struct val_env* ve,
}
if(!ta)
continue; /* key not found */
if(rr_is_selfsigned_revoked(env, ve, dnskey_rrset, i, qstate,
vq)) {
if(rr_is_selfsigned_revoked(env, ve, dnskey_rrset, i, qstate)) {
/* checked if there is an rrsig signed by this key. */
/* same keytag, but stored can be revoked already, so
* compare keytags, with +0 or +128(REVOKE flag) */
@@ -2213,7 +2209,7 @@ autr_tp_remove(struct module_env* env, struct trust_anchor* tp,
int autr_process_prime(struct module_env* env, struct val_env* ve,
struct trust_anchor* tp, struct ub_packed_rrset_key* dnskey_rrset,
struct module_qstate* qstate, struct val_qstate* vq)
struct module_qstate* qstate)
{
int changed = 0;
log_assert(tp && tp->autr);
@@ -2254,7 +2250,7 @@ int autr_process_prime(struct module_env* env, struct val_env* ve,
return 1; /* trust point exists */
}
/* check for revoked keys to remove immediately */
check_contains_revoked(env, ve, tp, dnskey_rrset, &changed, qstate, vq);
check_contains_revoked(env, ve, tp, dnskey_rrset, &changed, qstate);
if(changed) {
verbose(VERB_ALGO, "autotrust: revokedkeys, reassemble");
if(!autr_assemble(tp)) {
@@ -2270,7 +2266,7 @@ int autr_process_prime(struct module_env* env, struct val_env* ve,
}
}
/* verify the dnskey rrset and see if it is valid. */
if(!verify_dnskey(env, ve, tp, dnskey_rrset, qstate, vq)) {
if(!verify_dnskey(env, ve, tp, dnskey_rrset, qstate)) {
verbose(VERB_ALGO, "autotrust: dnskey did not verify.");
/* only increase failure count if this is not the first prime,
* this means there was a previous successful probe */
+1 -3
View File
@@ -50,7 +50,6 @@ struct module_env;
struct module_qstate;
struct val_env;
struct sldns_buffer;
struct val_qstate;
/** Autotrust anchor states */
typedef enum {
@@ -191,14 +190,13 @@ void autr_point_delete(struct trust_anchor* tp);
* @param dnskey_rrset: DNSKEY rrset probed (can be NULL if bad prime result).
* allocated in a region. Has not been validated yet.
* @param qstate: qstate with region.
* @param vq: validator query state.
* @return false if trust anchor was revoked completely.
* Otherwise logs errors to log, does not change return value.
* On errors, likely the trust point has been unchanged.
*/
int autr_process_prime(struct module_env* env, struct val_env* ve,
struct trust_anchor* tp, struct ub_packed_rrset_key* dnskey_rrset,
struct module_qstate* qstate, struct val_qstate* vq);
struct module_qstate* qstate);
/**
* Debug printout of rfc5011 tracked anchors
+5 -6
View File
@@ -177,8 +177,7 @@ static int
nsec_verify_rrset(struct module_env* env, struct val_env* ve,
struct ub_packed_rrset_key* nsec, struct key_entry_key* kkey,
char** reason, sldns_ede_code* reason_bogus,
struct module_qstate* qstate, struct val_qstate* vq, char* reasonbuf,
size_t reasonlen)
struct module_qstate* qstate, char* reasonbuf, size_t reasonlen)
{
struct packed_rrset_data* d = (struct packed_rrset_data*)
nsec->entry.data;
@@ -190,7 +189,7 @@ nsec_verify_rrset(struct module_env* env, struct val_env* ve,
if(d->security == sec_status_secure)
return 1;
d->security = val_verify_rrset_entry(env, ve, nsec, kkey, reason,
reason_bogus, LDNS_SECTION_AUTHORITY, qstate, vq, &verified,
reason_bogus, LDNS_SECTION_AUTHORITY, qstate, &verified,
reasonbuf, reasonlen);
if(d->security == sec_status_secure) {
rrset_update_sec_status(env->rrset_cache, nsec, *env->now);
@@ -204,7 +203,7 @@ val_nsec_prove_nodata_dsreply(struct module_env* env, struct val_env* ve,
struct query_info* qinfo, struct reply_info* rep,
struct key_entry_key* kkey, time_t* proof_ttl, char** reason,
sldns_ede_code* reason_bogus, struct module_qstate* qstate,
struct val_qstate* vq, char* reasonbuf, size_t reasonlen)
char* reasonbuf, size_t reasonlen)
{
struct ub_packed_rrset_key* nsec = reply_find_rrset_section_ns(
rep, qinfo->qname, qinfo->qname_len, LDNS_RR_TYPE_NSEC,
@@ -222,7 +221,7 @@ val_nsec_prove_nodata_dsreply(struct module_env* env, struct val_env* ve,
* 2) this is not a delegation point */
if(nsec) {
if(!nsec_verify_rrset(env, ve, nsec, kkey, reason,
reason_bogus, qstate, vq, reasonbuf, reasonlen)) {
reason_bogus, qstate, reasonbuf, reasonlen)) {
verbose(VERB_ALGO, "NSEC RRset for the "
"referral did not verify.");
return sec_status_bogus;
@@ -259,7 +258,7 @@ val_nsec_prove_nodata_dsreply(struct module_env* env, struct val_env* ve,
if(rep->rrsets[i]->rk.type != htons(LDNS_RR_TYPE_NSEC))
continue;
if(!nsec_verify_rrset(env, ve, rep->rrsets[i], kkey, reason,
reason_bogus, qstate, vq, reasonbuf, reasonlen)) {
reason_bogus, qstate, reasonbuf, reasonlen)) {
verbose(VERB_ALGO, "NSEC for empty non-terminal "
"did not verify.");
*reason = "NSEC for empty non-terminal "
+1 -4
View File
@@ -52,7 +52,6 @@ struct ub_packed_rrset_key;
struct reply_info;
struct query_info;
struct key_entry_key;
struct val_qstate;
/**
* Check DS absence.
@@ -69,7 +68,6 @@ struct val_qstate;
* @param reason: string explaining why bogus.
* @param reason_bogus: relevant EDE code for validation failure.
* @param qstate: qstate with region.
* @param vq: validator qstate.
* @param reasonbuf: buffer to use for fail reason string print.
* @param reasonlen: length of reasonbuf.
* @return security status.
@@ -82,8 +80,7 @@ enum sec_status val_nsec_prove_nodata_dsreply(struct module_env* env,
struct val_env* ve, struct query_info* qinfo,
struct reply_info* rep, struct key_entry_key* kkey,
time_t* proof_ttl, char** reason, sldns_ede_code* reason_bogus,
struct module_qstate* qstate, struct val_qstate* vq, char* reasonbuf,
size_t reasonlen);
struct module_qstate* qstate, char* reasonbuf, size_t reasonlen);
/**
* nsec typemap check, takes an NSEC-type bitmap as argument, checks for type.
+4 -6
View File
@@ -1521,8 +1521,7 @@ static int
list_is_secure(struct module_env* env, struct val_env* ve,
struct ub_packed_rrset_key** list, size_t num,
struct key_entry_key* kkey, char** reason, sldns_ede_code *reason_bogus,
struct module_qstate* qstate, struct val_qstate* vq, char* reasonbuf,
size_t reasonlen)
struct module_qstate* qstate, char* reasonbuf, size_t reasonlen)
{
struct packed_rrset_data* d;
size_t i;
@@ -1538,7 +1537,7 @@ list_is_secure(struct module_env* env, struct val_env* ve,
continue;
d->security = val_verify_rrset_entry(env, ve, list[i], kkey,
reason, reason_bogus, LDNS_SECTION_AUTHORITY, qstate,
vq, &verified, reasonbuf, reasonlen);
&verified, reasonbuf, reasonlen);
if(d->security != sec_status_secure) {
verbose(VERB_ALGO, "NSEC3 did not verify");
return 0;
@@ -1553,8 +1552,7 @@ nsec3_prove_nods(struct module_env* env, struct val_env* ve,
struct ub_packed_rrset_key** list, size_t num,
struct query_info* qinfo, struct key_entry_key* kkey, char** reason,
sldns_ede_code* reason_bogus, struct module_qstate* qstate,
struct val_qstate* vq, struct nsec3_cache_table* ct, char* reasonbuf,
size_t reasonlen)
struct nsec3_cache_table* ct, char* reasonbuf, size_t reasonlen)
{
struct nsec3_filter flt;
struct ce_response ce;
@@ -1570,7 +1568,7 @@ nsec3_prove_nods(struct module_env* env, struct val_env* ve,
return sec_status_bogus; /* no valid NSEC3s, bogus */
}
if(!list_is_secure(env, ve, list, num, kkey, reason, reason_bogus,
qstate, vq, reasonbuf, reasonlen)) {
qstate, reasonbuf, reasonlen)) {
*reason = "not all NSEC3 records secure";
return sec_status_bogus; /* not all NSEC3 records secure */
}
+1 -4
View File
@@ -78,7 +78,6 @@ struct reply_info;
struct query_info;
struct key_entry_key;
struct sldns_buffer;
struct val_qstate;
/**
* 0 1 2 3 4 5 6 7
@@ -216,7 +215,6 @@ nsec3_prove_wildcard(struct module_env* env, struct val_env* ve,
* @param reason: string for bogus result.
* @param reason_bogus: EDE (RFC8914) code paired with the reason of failure.
* @param qstate: qstate with region.
* @param vq: validator qstate.
* @param ct: cached hashes table.
* @param reasonbuf: buffer to use for fail reason string print.
* @param reasonlen: length of reasonbuf.
@@ -232,8 +230,7 @@ nsec3_prove_nods(struct module_env* env, struct val_env* ve,
struct ub_packed_rrset_key** list, size_t num,
struct query_info* qinfo, struct key_entry_key* kkey, char** reason,
sldns_ede_code* reason_bogus, struct module_qstate* qstate,
struct val_qstate* vq, struct nsec3_cache_table* ct, char* reasonbuf,
size_t reasonlen);
struct nsec3_cache_table* ct, char* reasonbuf, size_t reasonlen);
/**
* Prove NXDOMAIN or NODATA.
+8 -108
View File
@@ -82,8 +82,6 @@
/** Maximum number of RRSIG validations for an RRset. */
#define MAX_VALIDATE_RRSIGS 8
/** Maximum number of NSEC validations for a message. */
#define MAX_VALIDATE_NSECS 8
/** return number of rrs in an rrset */
static size_t
@@ -307,8 +305,6 @@ ds_create_dnskey_digest(struct module_env* env,
* digest = digest_algorithm( DNSKEY owner name | DNSKEY RDATA);
* DNSKEY RDATA = Flags | Protocol | Algorithm | Public Key. */
sldns_buffer_clear(b);
if(!sldns_buffer_available(b, dnskey_rrset->rk.dname_len + dnskey_len-2))
return 0; /* buffer too small */
sldns_buffer_write(b, dnskey_rrset->rk.dname,
dnskey_rrset->rk.dname_len);
query_dname_tolower(sldns_buffer_begin(b));
@@ -550,10 +546,8 @@ int algo_needs_missing(struct algo_needs* n)
* @param reason_bogus: EDE (RFC8914) code paired with the reason of failure.
* @param section: section of packet where this rrset comes from.
* @param qstate: qstate with region.
* @param vq: validator qstate with attempt counts.
* @param numverified: incremented when the number of RRSIG validations
* increases.
* @param num_tagmatches: incremented for tag matches.
* @return secure if any key signs *this* signature. bogus if no key signs it,
* unchecked on error, or indeterminate if all keys are not supported by
* the crypto library (openssl3+ only).
@@ -565,7 +559,7 @@ dnskeyset_verify_rrset_sig(struct module_env* env, struct val_env* ve,
struct rbtree_type** sortree,
char** reason, sldns_ede_code *reason_bogus,
sldns_pkt_section section, struct module_qstate* qstate,
struct val_qstate* vq, int* numverified, size_t* num_tagmatches)
int* numverified)
{
/* find matching keys and check them */
enum sec_status sec = sec_status_bogus;
@@ -584,14 +578,6 @@ dnskeyset_verify_rrset_sig(struct module_env* env, struct val_env* ve,
}
for(i=0; i<num; i++) {
if((*num_tagmatches)++ > MAX_TAG_MATCHES) {
*reason = "too many tag matches";
if(reason_bogus)
*reason_bogus = LDNS_EDE_DNSSEC_BOGUS;
verbose(VERB_ALGO, "verify sig: too many tag matches, "
"MAX_TAG_MATCHES (%d); bogus", MAX_TAG_MATCHES);
return sec_status_bogus;
}
/* see if key matches keytag and algo */
if(algo != dnskey_get_algo(dnskey, i) ||
tag != dnskey_calc_keytag(dnskey, i))
@@ -599,26 +585,6 @@ dnskeyset_verify_rrset_sig(struct module_env* env, struct val_env* ve,
numchecked ++;
(*numverified)++;
if(vq && vq->num_validation_attempts++ > env->cfg->val_validation_attempts) {
*reason = "too many validation attempts";
if(reason_bogus)
*reason_bogus = LDNS_EDE_DNSSEC_BOGUS;
verbose(VERB_ALGO, "verify sig: too many validation attempts, "
"val-validation-attempts (%d); bogus", env->cfg->val_validation_attempts);
return sec_status_bogus;
}
if(vq && (ntohs(rrset->rk.type) == LDNS_RR_TYPE_NSEC ||
ntohs(rrset->rk.type) == LDNS_RR_TYPE_NSEC3) &&
vq->num_nsec_attempts++ > MAX_VALIDATE_NSECS) {
*reason = "too many NSEC or NSEC3 validation attempts";
if(reason_bogus)
*reason_bogus = LDNS_EDE_DNSSEC_BOGUS;
verbose(VERB_ALGO, "verify sig: too many NSEC or NSEC3 validation attempts, "
"(%d); bogus", MAX_VALIDATE_NSECS);
vq->num_nsec_attempts_exceeded = 1;
return sec_status_bogus;
}
/* see if key verifies */
sec = dnskey_verify_rrset_sig(env->scratch,
env->scratch_buffer, ve, now, rrset, dnskey, i,
@@ -658,12 +624,11 @@ enum sec_status
dnskeyset_verify_rrset(struct module_env* env, struct val_env* ve,
struct ub_packed_rrset_key* rrset, struct ub_packed_rrset_key* dnskey,
uint8_t* sigalg, char** reason, sldns_ede_code *reason_bogus,
sldns_pkt_section section, struct module_qstate* qstate,
struct val_qstate* vq, int* verified, char* reasonbuf,
size_t reasonlen)
sldns_pkt_section section, struct module_qstate* qstate, int* verified,
char* reasonbuf, size_t reasonlen)
{
enum sec_status sec;
size_t i, num, num_tagmatches = 0;
size_t i, num;
rbtree_type* sortree = NULL;
/* make sure that for all DNSKEY algorithms there are valid sigs */
struct algo_needs needs;
@@ -691,19 +656,9 @@ dnskeyset_verify_rrset(struct module_env* env, struct val_env* ve,
}
}
for(i=0; i<num; i++) {
if(num_tagmatches > MAX_TAG_MATCHES) {
*reason = "too many tag matches";
if(reason_bogus)
*reason_bogus = LDNS_EDE_DNSSEC_BOGUS;
verbose(VERB_ALGO, "rrset failed to verify: too many tag matches, "
"MAX_TAG_MATCHES (%d)", MAX_TAG_MATCHES);
if(reason_bogus)
*reason_bogus = LDNS_EDE_DNSSEC_BOGUS;
return sec_status_bogus;
}
sec = dnskeyset_verify_rrset_sig(env, ve, *env->now, rrset,
dnskey, i, &sortree, reason, reason_bogus,
section, qstate, vq, verified, &num_tagmatches);
section, qstate, verified);
/* see which algorithm has been fixed up */
if(sec == sec_status_secure) {
if(!sigalg)
@@ -752,8 +707,7 @@ enum sec_status
dnskey_verify_rrset(struct module_env* env, struct val_env* ve,
struct ub_packed_rrset_key* rrset, struct ub_packed_rrset_key* dnskey,
size_t dnskey_idx, char** reason, sldns_ede_code *reason_bogus,
sldns_pkt_section section, struct module_qstate* qstate,
struct val_qstate* vq, size_t* num_tagmatches)
sldns_pkt_section section, struct module_qstate* qstate)
{
enum sec_status sec;
size_t i, num, numchecked = 0, numindeterminate = 0;
@@ -774,26 +728,9 @@ dnskey_verify_rrset(struct module_env* env, struct val_env* ve,
}
for(i=0; i<num; i++) {
/* see if sig matches keytag and algo */
if((*num_tagmatches)++ > MAX_TAG_MATCHES) {
*reason = "too many tag matches";
if(reason_bogus)
*reason_bogus = LDNS_EDE_DNSSEC_BOGUS;
verbose(VERB_ALGO, "rrset failed to verify: too many tag matches, "
"MAX_TAG_MATCHES (%d); bogus", MAX_TAG_MATCHES);
return sec_status_bogus;
}
if(algo != rrset_get_sig_algo(rrset, i) ||
tag != rrset_get_sig_keytag(rrset, i))
continue;
if(vq && vq->num_validation_attempts++ > env->cfg->val_validation_attempts) {
*reason = "too many validation attempts";
if(reason_bogus)
*reason_bogus = LDNS_EDE_DNSSEC_BOGUS;
verbose(VERB_ALGO, "rrset failed to verify: too many validation attempts, "
"val-validation-attempts (%d); bogus", env->cfg->val_validation_attempts);
return sec_status_bogus;
}
buf_canon = 0;
sec = dnskey_verify_rrset_sig(env->scratch,
env->scratch_buffer, ve, *env->now, rrset,
@@ -1371,32 +1308,15 @@ rrset_canonical(struct regional* region, sldns_buffer* buf,
}
sldns_buffer_clear(buf);
if(sldns_buffer_remaining(buf) < siglen || siglen < 18+1) {
verbose(VERB_ALGO, "verify: failed to canonicalize, "
"rrset too big");
return 0;
}
sldns_buffer_write(buf, sig, siglen);
/* canonicalize signer name */
canon_dname_tolower(sldns_buffer_begin(buf)+18,
sldns_buffer_current(buf));
if(sldns_buffer_remaining(buf) < k->rk.dname_len+2) {
/* Check if the first can_owner name can fit in the buffer.
* The length is k->rk.dname_len or k->rk.dname_len+2
* if it has '*.' in prefixed. Checks the upper bound,
* also realistically the rest of the rrtype, rrclass, origttl,
* rdata and so on has to be inserted, so that extra space has
* to be there. */
verbose(VERB_ALGO, "verify: failed to canonicalize, "
"rrset too big");
return 0;
}
RBTREE_FOR(walk, struct canon_rr*, (*sortree)) {
/* see if there is enough space left in the buffer */
if(sldns_buffer_remaining(buf) < can_owner_len + 2 + 2 + 4
+ d->rr_len[walk->rr_idx]) {
verbose(VERB_ALGO, "verify: failed to canonicalize, "
log_err("verify: failed to canonicalize, "
"rrset too big");
return 0;
}
@@ -1405,13 +1325,6 @@ rrset_canonical(struct regional* region, sldns_buffer* buf,
sldns_buffer_write(buf, can_owner, can_owner_len);
else insert_can_owner(buf, k, sig, &can_owner,
&can_owner_len);
/* Check again, if the rdata can fit in the buffer */
if(sldns_buffer_remaining(buf) < 2 + 2 + 4
+ d->rr_len[walk->rr_idx]) {
verbose(VERB_ALGO, "verify: failed to canonicalize, "
"rrset too big");
return 0;
}
sldns_buffer_write(buf, &k->rk.type, 2);
sldns_buffer_write(buf, &k->rk.rrset_class, 2);
sldns_buffer_write(buf, sig+4, 4);
@@ -1463,17 +1376,11 @@ rrset_canonicalize_to_buffer(struct regional* region, sldns_buffer* buf,
canonical_sort(k, d, sortree, rrs);
sldns_buffer_clear(buf);
if(sldns_buffer_remaining(buf) < k->rk.dname_len) {
/* Check if the first can_owner name can fit in the buffer. */
verbose(VERB_ALGO, "verify: failed to canonicalize, "
"rrset too big");
return 0;
}
RBTREE_FOR(walk, struct canon_rr*, sortree) {
/* see if there is enough space left in the buffer */
if(sldns_buffer_remaining(buf) < can_owner_len + 2 + 2 + 4
+ d->rr_len[walk->rr_idx]) {
verbose(VERB_ALGO, "verify: failed to canonicalize, "
log_err("verify: failed to canonicalize, "
"rrset too big");
return 0;
}
@@ -1486,13 +1393,6 @@ rrset_canonicalize_to_buffer(struct regional* region, sldns_buffer* buf,
query_dname_tolower(can_owner);
can_owner_len = k->rk.dname_len;
}
/* Check again, if the rdata can fit in the buffer */
if(sldns_buffer_remaining(buf) < 2 + 2 + 4
+ d->rr_len[walk->rr_idx]) {
verbose(VERB_ALGO, "verify: failed to canonicalize, "
"rrset too big");
return 0;
}
sldns_buffer_write(buf, &k->rk.type, 2);
sldns_buffer_write(buf, &k->rk.rrset_class, 2);
sldns_buffer_write_u32(buf, d->rr_ttl[walk->rr_idx]);
+3 -9
View File
@@ -53,7 +53,6 @@ struct ub_packed_rrset_key;
struct rbtree_type;
struct regional;
struct sldns_buffer;
struct val_qstate;
/** number of entries in algorithm needs array */
#define ALGO_NEEDS_MAX 256
@@ -263,7 +262,6 @@ uint16_t dnskey_get_flags(struct ub_packed_rrset_key* k, size_t idx);
* @param reason_bogus: EDE (RFC8914) code paired with the reason of failure.
* @param section: section of packet where this rrset comes from.
* @param qstate: qstate with region.
* @param vq: validator qstate with attempt counts.
* @param verified: if not NULL the number of RRSIG validations is returned.
* @param reasonbuf: buffer to use for fail reason string print.
* @param reasonlen: length of reasonbuf.
@@ -275,9 +273,8 @@ enum sec_status dnskeyset_verify_rrset(struct module_env* env,
struct val_env* ve, struct ub_packed_rrset_key* rrset,
struct ub_packed_rrset_key* dnskey, uint8_t* sigalg,
char** reason, sldns_ede_code *reason_bogus,
sldns_pkt_section section, struct module_qstate* qstate,
struct val_qstate* vq, int* verified, char* reasonbuf,
size_t reasonlen);
sldns_pkt_section section, struct module_qstate* qstate, int* verified,
char* reasonbuf, size_t reasonlen);
/**
* verify rrset against one specific dnskey (from rrset)
@@ -290,16 +287,13 @@ enum sec_status dnskeyset_verify_rrset(struct module_env* env,
* @param reason_bogus: EDE (RFC8914) code paired with the reason of failure.
* @param section: section of packet where this rrset comes from.
* @param qstate: qstate with region.
* @param vq: validator qstate with attempt counts.
* @param num_tagmatches: incremented to keep track of tag matches.
* @return secure if *this* key signs any of the signatures on rrset.
* unchecked on error or and bogus on bad signature.
*/
enum sec_status dnskey_verify_rrset(struct module_env* env, struct val_env* ve,
struct ub_packed_rrset_key* rrset, struct ub_packed_rrset_key* dnskey,
size_t dnskey_idx, char** reason, sldns_ede_code *reason_bogus,
sldns_pkt_section section, struct module_qstate* qstate,
struct val_qstate* vq, size_t* num_tagmatches);
sldns_pkt_section section, struct module_qstate* qstate);
/**
* verify rrset, with specific dnskey(from set), for a specific rrsig
+33 -86
View File
@@ -406,8 +406,7 @@ val_verify_rrset(struct module_env* env, struct val_env* ve,
struct ub_packed_rrset_key* rrset, struct ub_packed_rrset_key* keys,
uint8_t* sigalg, char** reason, sldns_ede_code *reason_bogus,
sldns_pkt_section section, struct module_qstate* qstate,
struct val_qstate* vq, int *verified, char* reasonbuf,
size_t reasonlen)
int *verified, char* reasonbuf, size_t reasonlen)
{
enum sec_status sec;
struct packed_rrset_data* d = (struct packed_rrset_data*)rrset->
@@ -432,8 +431,7 @@ val_verify_rrset(struct module_env* env, struct val_env* ve,
log_nametypeclass(VERB_ALGO, "verify rrset", rrset->rk.dname,
ntohs(rrset->rk.type), ntohs(rrset->rk.rrset_class));
sec = dnskeyset_verify_rrset(env, ve, rrset, keys, sigalg, reason,
reason_bogus, section, qstate, vq, verified, reasonbuf,
reasonlen);
reason_bogus, section, qstate, verified, reasonbuf, reasonlen);
verbose(VERB_ALGO, "verify result: %s", sec_status_to_string(sec));
regional_free_all(env->scratch);
@@ -477,8 +475,7 @@ val_verify_rrset_entry(struct module_env* env, struct val_env* ve,
struct ub_packed_rrset_key* rrset, struct key_entry_key* kkey,
char** reason, sldns_ede_code *reason_bogus,
sldns_pkt_section section, struct module_qstate* qstate,
struct val_qstate* vq, int* verified, char* reasonbuf,
size_t reasonlen)
int* verified, char* reasonbuf, size_t reasonlen)
{
/* temporary dnskey rrset-key */
struct ub_packed_rrset_key dnskey;
@@ -492,8 +489,7 @@ val_verify_rrset_entry(struct module_env* env, struct val_env* ve,
dnskey.entry.key = &dnskey;
dnskey.entry.data = kd->rrset_data;
sec = val_verify_rrset(env, ve, rrset, &dnskey, kd->algo, reason,
reason_bogus, section, qstate, vq, verified, reasonbuf,
reasonlen);
reason_bogus, section, qstate, verified, reasonbuf, reasonlen);
return sec;
}
@@ -503,20 +499,13 @@ verify_dnskeys_with_ds_rr(struct module_env* env, struct val_env* ve,
struct ub_packed_rrset_key* dnskey_rrset,
struct ub_packed_rrset_key* ds_rrset, size_t ds_idx, char** reason,
sldns_ede_code *reason_bogus, struct module_qstate* qstate,
struct val_qstate* vq, int *nonechecked, char* reasonbuf,
size_t reasonlen, size_t* num_tagmatches,
size_t* num_tagmatches_dnskeysig)
int *nonechecked, char* reasonbuf, size_t reasonlen)
{
enum sec_status sec = sec_status_bogus;
size_t i, num, numchecked = 0, numhashok = 0, numsizesupp = 0;
num = rrset_get_count(dnskey_rrset);
*nonechecked = 0;
for(i=0; i<num; i++) {
if((*num_tagmatches)++ > MAX_TAG_MATCHES) {
verbose(VERB_ALGO, "DS match attempt reached "
"MAX_TAG_MATCHES (%d); bogus", MAX_TAG_MATCHES);
return sec_status_bogus;
}
/* Skip DNSKEYs that don't match the basic criteria. */
if(ds_get_key_algo(ds_rrset, ds_idx)
!= dnskey_get_algo(dnskey_rrset, i)
@@ -529,15 +518,6 @@ verify_dnskeys_with_ds_rr(struct module_env* env, struct val_env* ve,
ds_get_key_algo(ds_rrset, ds_idx),
ds_get_keytag(ds_rrset, ds_idx));
if(vq && vq->num_hash_attempts++ > env->cfg->val_hash_attempts) {
*reason = "too many hash attempts";
if(reason_bogus)
*reason_bogus = LDNS_EDE_DNSSEC_BOGUS;
verbose(VERB_ALGO, "rrset failed to verify: too many hash attempts, "
"val-hash-attempts (%d); bogus", env->cfg->val_hash_attempts);
return sec_status_bogus;
}
/* Convert the candidate DNSKEY into a hash using the
* same DS hash algorithm. */
if(!ds_digest_match_dnskey(env, dnskey_rrset, i, ds_rrset,
@@ -561,14 +541,8 @@ verify_dnskeys_with_ds_rr(struct module_env* env, struct val_env* ve,
/* Otherwise, we have a match! Make sure that the DNSKEY
* verifies *with this key* */
if(*num_tagmatches_dnskeysig > MAX_TAG_MATCHES) {
verbose(VERB_ALGO, "DS that matched has too many DNSKEY to RRSIG tag matches "
"MAX_TAG_MATCHES (%d); bogus", MAX_TAG_MATCHES);
return sec_status_bogus;
}
sec = dnskey_verify_rrset(env, ve, dnskey_rrset, dnskey_rrset,
i, reason, reason_bogus, LDNS_SECTION_ANSWER, qstate,
vq, num_tagmatches_dnskeysig);
i, reason, reason_bogus, LDNS_SECTION_ANSWER, qstate);
if(sec == sec_status_secure) {
return sec;
}
@@ -612,14 +586,14 @@ val_verify_DNSKEY_with_DS(struct module_env* env, struct val_env* ve,
struct ub_packed_rrset_key* dnskey_rrset,
struct ub_packed_rrset_key* ds_rrset, uint8_t* sigalg, char** reason,
sldns_ede_code *reason_bogus, struct module_qstate* qstate,
struct val_qstate* vq, char* reasonbuf, size_t reasonlen)
char* reasonbuf, size_t reasonlen)
{
/* as long as this is false, we can consider this DS rrset to be
* equivalent to no DS rrset. */
int has_useful_ds = 0, digest_algo, alg, has_algo_refusal = 0,
nonechecked, has_checked_ds = 0;
struct algo_needs needs;
size_t i, num, num_tagmatches = 0, num_tagmatches_dnskeysig = 0;
size_t i, num;
enum sec_status sec;
if(dnskey_rrset->rk.dname_len != ds_rrset->rk.dname_len ||
@@ -641,13 +615,6 @@ val_verify_DNSKEY_with_DS(struct module_env* env, struct val_env* ve,
}
num = rrset_get_count(ds_rrset);
for(i=0; i<num; i++) {
if(num_tagmatches > MAX_TAG_MATCHES) {
verbose(VERB_ALGO, "DS verify attempt reached "
"MAX_TAG_MATCHES (%d); bogus", MAX_TAG_MATCHES);
*reason = "DS verify has too many tag matches";
return sec_status_bogus;
}
/* Check to see if we can understand this DS.
* And check it is the strongest digest */
if(!ds_digest_algo_is_supported(ds_rrset, i) ||
@@ -656,16 +623,9 @@ val_verify_DNSKEY_with_DS(struct module_env* env, struct val_env* ve,
continue;
}
if(num_tagmatches_dnskeysig > MAX_TAG_MATCHES) {
verbose(VERB_ALGO, "DS verify attempt reached "
"DNSKEY to RRSIG MAX_TAG_MATCHES (%d); bogus", MAX_TAG_MATCHES);
*reason = "DS verify has too many DNSKEY to RRSIG tag matches";
return sec_status_bogus;
}
sec = verify_dnskeys_with_ds_rr(env, ve, dnskey_rrset,
ds_rrset, i, reason, reason_bogus, qstate, vq,
&nonechecked, reasonbuf, reasonlen, &num_tagmatches,
&num_tagmatches_dnskeysig);
ds_rrset, i, reason, reason_bogus, qstate,
&nonechecked, reasonbuf, reasonlen);
if(sec == sec_status_insecure) {
/* DNSKEY too large unsupported or algo refused by
* crypto lib. */
@@ -727,12 +687,12 @@ val_verify_new_DNSKEYs(struct regional* region, struct module_env* env,
struct val_env* ve, struct ub_packed_rrset_key* dnskey_rrset,
struct ub_packed_rrset_key* ds_rrset, int downprot, char** reason,
sldns_ede_code *reason_bogus, struct module_qstate* qstate,
struct val_qstate* vq, char* reasonbuf, size_t reasonlen)
char* reasonbuf, size_t reasonlen)
{
uint8_t sigalg[ALGO_NEEDS_MAX+1];
enum sec_status sec = val_verify_DNSKEY_with_DS(env, ve,
dnskey_rrset, ds_rrset, downprot?sigalg:NULL, reason,
reason_bogus, qstate, vq, reasonbuf, reasonlen);
reason_bogus, qstate, reasonbuf, reasonlen);
if(sec == sec_status_secure) {
return key_entry_create_rrset(region,
@@ -758,14 +718,14 @@ val_verify_DNSKEY_with_TA(struct module_env* env, struct val_env* ve,
struct ub_packed_rrset_key* ta_ds,
struct ub_packed_rrset_key* ta_dnskey, uint8_t* sigalg, char** reason,
sldns_ede_code *reason_bogus, struct module_qstate* qstate,
struct val_qstate* vq, char* reasonbuf, size_t reasonlen)
char* reasonbuf, size_t reasonlen)
{
/* as long as this is false, we can consider this anchor to be
* equivalent to no anchor. */
int has_useful_ta = 0, digest_algo = 0, alg, has_algo_refusal = 0,
nonechecked, has_checked_ds = 0;
struct algo_needs needs;
size_t i, num, num_tagmatches = 0, num_tagmatches_dnskeysig = 0;
size_t i, num;
enum sec_status sec;
if(ta_ds && (dnskey_rrset->rk.dname_len != ta_ds->rk.dname_len ||
@@ -801,15 +761,6 @@ val_verify_DNSKEY_with_TA(struct module_env* env, struct val_env* ve,
if(ta_ds) {
num = rrset_get_count(ta_ds);
for(i=0; i<num; i++) {
if(num_tagmatches > MAX_TAG_MATCHES) {
verbose(VERB_ALGO, "anchor DS verify attempt reached "
"MAX_TAG_MATCHES (%d); bogus", MAX_TAG_MATCHES);
*reason = "anchor DS verify has too many tag matches";
if(reason_bogus)
*reason_bogus = LDNS_EDE_DNSSEC_BOGUS;
return sec_status_bogus;
}
/* Check to see if we can understand this DS.
* And check it is the strongest digest */
if(!ds_digest_algo_is_supported(ta_ds, i) ||
@@ -817,18 +768,9 @@ val_verify_DNSKEY_with_TA(struct module_env* env, struct val_env* ve,
ds_get_digest_algo(ta_ds, i) != digest_algo)
continue;
if(num_tagmatches_dnskeysig > MAX_TAG_MATCHES) {
verbose(VERB_ALGO, "anchor DS verify has too many DNSKEY to RRSIG tag matches "
"MAX_TAG_MATCHES (%d); bogus", MAX_TAG_MATCHES);
*reason = "anchor DS verify has too many DNSKEY to RRSIG tag matches";
if(reason_bogus)
*reason_bogus = LDNS_EDE_DNSSEC_BOGUS;
return sec_status_bogus;
}
sec = verify_dnskeys_with_ds_rr(env, ve, dnskey_rrset,
ta_ds, i, reason, reason_bogus, qstate, vq,
&nonechecked, reasonbuf, reasonlen, &num_tagmatches,
&num_tagmatches_dnskeysig);
ta_ds, i, reason, reason_bogus, qstate, &nonechecked,
reasonbuf, reasonlen);
if(sec == sec_status_insecure) {
has_algo_refusal = 1;
continue;
@@ -871,16 +813,8 @@ val_verify_DNSKEY_with_TA(struct module_env* env, struct val_env* ve,
/* we saw a useful TA */
has_useful_ta = 1;
if(num_tagmatches_dnskeysig > MAX_TAG_MATCHES) {
verbose(VERB_ALGO, "anchor DS that matched has too many DNSKEY to RRSIG tag matches "
"MAX_TAG_MATCHES (%d); bogus", MAX_TAG_MATCHES);
*reason = "anchor DS that matched has too many DNSKEY to RRSIG tag matches";
if(reason_bogus)
*reason_bogus = LDNS_EDE_DNSSEC_BOGUS;
return sec_status_bogus;
}
sec = dnskey_verify_rrset(env, ve, dnskey_rrset,
ta_dnskey, i, reason, reason_bogus, LDNS_SECTION_ANSWER, qstate, vq, &num_tagmatches_dnskeysig);
ta_dnskey, i, reason, reason_bogus, LDNS_SECTION_ANSWER, qstate);
if(sec == sec_status_secure) {
if(!sigalg || algo_needs_set_secure(&needs,
(uint8_t)dnskey_get_algo(ta_dnskey, i))) {
@@ -928,13 +862,12 @@ val_verify_new_DNSKEYs_with_ta(struct regional* region, struct module_env* env,
struct ub_packed_rrset_key* ta_ds_rrset,
struct ub_packed_rrset_key* ta_dnskey_rrset, int downprot,
char** reason, sldns_ede_code *reason_bogus,
struct module_qstate* qstate, struct val_qstate* vq, char* reasonbuf,
size_t reasonlen)
struct module_qstate* qstate, char* reasonbuf, size_t reasonlen)
{
uint8_t sigalg[ALGO_NEEDS_MAX+1];
enum sec_status sec = val_verify_DNSKEY_with_TA(env, ve,
dnskey_rrset, ta_ds_rrset, ta_dnskey_rrset,
downprot?sigalg:NULL, reason, reason_bogus, qstate, vq,
downprot?sigalg:NULL, reason, reason_bogus, qstate,
reasonbuf, reasonlen);
if(sec == sec_status_secure) {
@@ -1390,6 +1323,20 @@ int val_has_signed_nsecs(struct reply_info* rep, char** reason)
return 0;
}
void val_has_auth_nsecs(struct reply_info* rep, int* has_nsec, int* has_nsec3)
{
size_t i, num_nsec = 0, num_nsec3 = 0;
for(i=rep->an_numrrsets; i<rep->an_numrrsets+rep->ns_numrrsets; i++) {
if(rep->rrsets[i]->rk.type == htons(LDNS_RR_TYPE_NSEC))
num_nsec++;
else if(rep->rrsets[i]->rk.type == htons(LDNS_RR_TYPE_NSEC3))
num_nsec3++;
else continue;
}
*has_nsec = (num_nsec != 0);
*has_nsec3 = (num_nsec3 != 0);
}
struct dns_msg*
val_find_DS(struct module_env* env, uint8_t* nm, size_t nmlen, uint16_t c,
struct regional* region, uint8_t* topname)
+13 -16
View File
@@ -55,11 +55,6 @@ struct regional;
struct val_anchors;
struct rrset_cache;
struct sock_list;
struct val_qstate;
/** Maximum number of matches with key tag and algorithm, for DNSKEY to
* RRSIG and DS to DNSKEY. Since the number is O(N*N), there is a limit. */
#define MAX_TAG_MATCHES 256
/**
* Response classifications for the validator. The different types of proofs.
@@ -129,7 +124,6 @@ void val_find_signer(enum val_classification subtype,
* @param reason_bogus: EDE (RFC8914) code paired with the reason of failure.
* @param section: section of packet where this rrset comes from.
* @param qstate: qstate with region.
* @param vq: validator qstate with attempt counts.
* @param verified: if not NULL, the number of RRSIG validations is returned.
* @param reasonbuf: buffer to use for fail reason string print.
* @param reasonlen: length of reasonbuf.
@@ -139,8 +133,7 @@ enum sec_status val_verify_rrset_entry(struct module_env* env,
struct val_env* ve, struct ub_packed_rrset_key* rrset,
struct key_entry_key* kkey, char** reason, sldns_ede_code *reason_bogus,
sldns_pkt_section section, struct module_qstate* qstate,
struct val_qstate* vq, int* verified, char* reasonbuf,
size_t reasonlen);
int* verified, char* reasonbuf, size_t reasonlen);
/**
* Verify DNSKEYs with DS rrset. Like val_verify_new_DNSKEYs but
@@ -155,7 +148,6 @@ enum sec_status val_verify_rrset_entry(struct module_env* env,
* @param reason: reason of failure. Fixed string or alloced in scratch.
* @param reason_bogus: EDE (RFC8914) code paired with the reason of failure.
* @param qstate: qstate with region.
* @param vq: validator qstate with attempt counts.
* @param reasonbuf: buffer to use for fail reason string print.
* @param reasonlen: length of reasonbuf.
* @return: sec_status_secure if a DS matches.
@@ -166,7 +158,7 @@ enum sec_status val_verify_DNSKEY_with_DS(struct module_env* env,
struct val_env* ve, struct ub_packed_rrset_key* dnskey_rrset,
struct ub_packed_rrset_key* ds_rrset, uint8_t* sigalg, char** reason,
sldns_ede_code *reason_bogus, struct module_qstate* qstate,
struct val_qstate* vq, char* reasonbuf, size_t reasonlen);
char* reasonbuf, size_t reasonlen);
/**
* Verify DNSKEYs with DS and DNSKEY rrset. Like val_verify_DNSKEY_with_DS
@@ -182,7 +174,6 @@ enum sec_status val_verify_DNSKEY_with_DS(struct module_env* env,
* @param reason: reason of failure. Fixed string or alloced in scratch.
* @param reason_bogus: EDE (RFC8914) code paired with the reason of failure.
* @param qstate: qstate with region.
* @param vq: validator qstate with attempt counts.
* @param reasonbuf: buffer to use for fail reason string print.
* @param reasonlen: length of reasonbuf.
* @return: sec_status_secure if a DS matches.
@@ -194,7 +185,7 @@ enum sec_status val_verify_DNSKEY_with_TA(struct module_env* env,
struct ub_packed_rrset_key* ta_ds,
struct ub_packed_rrset_key* ta_dnskey, uint8_t* sigalg, char** reason,
sldns_ede_code *reason_bogus, struct module_qstate* qstate,
struct val_qstate* vq, char* reasonbuf, size_t reasonlen);
char* reasonbuf, size_t reasonlen);
/**
* Verify new DNSKEYs with DS rrset. The DS contains hash values that should
@@ -211,7 +202,6 @@ enum sec_status val_verify_DNSKEY_with_TA(struct module_env* env,
* @param reason: reason of failure. Fixed string or alloced in scratch.
* @param reason_bogus: EDE (RFC8914) code paired with the reason of failure.
* @param qstate: qstate with region.
* @param vq: validator qstate with attempt counts.
* @param reasonbuf: buffer to use for fail reason string print.
* @param reasonlen: length of reasonbuf.
* @return a KeyEntry. This will either contain the now trusted
@@ -229,7 +219,7 @@ struct key_entry_key* val_verify_new_DNSKEYs(struct regional* region,
struct ub_packed_rrset_key* dnskey_rrset,
struct ub_packed_rrset_key* ds_rrset, int downprot, char** reason,
sldns_ede_code *reason_bogus, struct module_qstate* qstate,
struct val_qstate* vq, char* reasonbuf, size_t reasonlen);
char* reasonbuf, size_t reasonlen);
/**
* Verify rrset with trust anchor: DS and DNSKEY rrset.
@@ -245,7 +235,6 @@ struct key_entry_key* val_verify_new_DNSKEYs(struct regional* region,
* @param reason: reason of failure. Fixed string or alloced in scratch.
* @param reason_bogus: EDE (RFC8914) code paired with the reason of failure.
* @param qstate: qstate with region.
* @param vq: validator qstate with attempt counts.
* @param reasonbuf: buffer to use for fail reason string print.
* @param reasonlen: length of reasonbuf.
* @return a KeyEntry. This will either contain the now trusted
@@ -264,7 +253,7 @@ struct key_entry_key* val_verify_new_DNSKEYs_with_ta(struct regional* region,
struct ub_packed_rrset_key* ta_ds_rrset,
struct ub_packed_rrset_key* ta_dnskey_rrset, int downprot,
char** reason, sldns_ede_code *reason_bogus, struct module_qstate* qstate,
struct val_qstate* vq, char* reasonbuf, size_t reasonlen);
char* reasonbuf, size_t reasonlen);
/**
* Determine if DS rrset is usable for validator or not.
@@ -421,6 +410,14 @@ void val_blacklist(struct sock_list** blacklist, struct regional* region,
*/
int val_has_signed_nsecs(struct reply_info* rep, char** reason);
/**
* See if there are NSECs, or NSEC3s in the authority section.
* @param rep: reply to check
* @param has_nsec: returned true if it has nsecs.
* @param has_nsec3: returned true if it has nsec3s.
*/
void val_has_auth_nsecs(struct reply_info* rep, int* has_nsec, int* has_nsec3);
/**
* Return algo number for favorite (best) algorithm that we support in DS.
* @param ds_rrset: the DSes in this rrset are inspected and best algo chosen.
+54 -75
View File
@@ -350,17 +350,13 @@ static void
val_restart(struct val_qstate* vq)
{
struct comm_timer* temp_timer;
int restart_count, num_validation_attempts, num_hash_attempts;
int restart_count;
if(!vq) return;
temp_timer = vq->suspend_timer;
restart_count = vq->restart_count+1;
num_validation_attempts = vq->num_validation_attempts;
num_hash_attempts = vq->num_hash_attempts;
memset(vq, 0, sizeof(*vq));
vq->suspend_timer = temp_timer;
vq->restart_count = restart_count;
vq->num_validation_attempts = num_validation_attempts;
vq->num_hash_attempts = num_hash_attempts;
vq->state = VAL_INIT_STATE;
}
@@ -459,24 +455,6 @@ already_validated(struct dns_msg* ret_msg)
return 0;
}
/** If it is possible to restart the validation state */
static int
val_can_restart(struct module_qstate* qstate, struct val_qstate* vq,
struct val_env* ve)
{
/* For validation failures that are limits exceeded on the amount
* of work that the DNSSEC validator is willing to do, the restart
* is not allowed. A restart would increase the amount of effort
* spent even further. */
if(vq->restart_count < ve->max_restart &&
vq->num_validation_attempts <= qstate->env->cfg->val_validation_attempts &&
vq->num_hash_attempts <= qstate->env->cfg->val_hash_attempts &&
!vq->num_nsec_attempts_exceeded)
return 1;
(void)qstate;
return 0;
}
/**
* Generate a request for DNS data.
*
@@ -785,8 +763,8 @@ validate_msg_signatures(struct module_qstate* qstate, struct val_qstate* vq,
/* Verify the answer rrset */
sec = val_verify_rrset_entry(env, ve, s, key_entry, &reason,
&reason_bogus, LDNS_SECTION_ANSWER, qstate, vq,
&verified, reasonbuf, sizeof(reasonbuf));
&reason_bogus, LDNS_SECTION_ANSWER, qstate, &verified,
reasonbuf, sizeof(reasonbuf));
/* If the (answer) rrset failed to validate, then this
* message is BAD. */
if(sec != sec_status_secure) {
@@ -830,7 +808,7 @@ validate_msg_signatures(struct module_qstate* qstate, struct val_qstate* vq,
continue;
s = chase_reply->rrsets[i];
sec = val_verify_rrset_entry(env, ve, s, key_entry, &reason,
&reason_bogus, LDNS_SECTION_AUTHORITY, qstate, vq,
&reason_bogus, LDNS_SECTION_AUTHORITY, qstate,
&verified, reasonbuf, sizeof(reasonbuf));
/* If anything in the authority section fails to be secure,
* we have a bad message. */
@@ -877,7 +855,7 @@ validate_msg_signatures(struct module_qstate* qstate, struct val_qstate* vq,
if(sname && query_dname_compare(sname, key_entry->name)==0)
(void)val_verify_rrset_entry(env, ve, s, key_entry,
&reason, NULL, LDNS_SECTION_ADDITIONAL, qstate,
vq, &verified, reasonbuf, sizeof(reasonbuf));
&verified, reasonbuf, sizeof(reasonbuf));
/* the additional section can fail to be secure,
* it is optional, check signature in case we need
* to clean the additional section later. */
@@ -1455,20 +1433,16 @@ validate_nameerror_response(struct module_env* env, struct val_env* ve,
* trusted DNSKEY rrset that signs this response must already have been
* completed.
*
* @param env: module env.
* @param chase_reply: answer to validate.
*/
static void
validate_referral_response(struct module_env* env, struct reply_info* chase_reply)
validate_referral_response(struct reply_info* chase_reply)
{
size_t i, count;
size_t i;
enum sec_status s;
/* message security equals lowest rrset security */
chase_reply->security = sec_status_secure;
if(env->cfg->val_clean_additional)
count = chase_reply->rrset_count;
else count = chase_reply->an_numrrsets+chase_reply->ns_numrrsets;
for(i=0; i<count; i++) {
for(i=0; i<chase_reply->rrset_count; i++) {
s = ((struct packed_rrset_data*)chase_reply->rrsets[i]
->entry.data)->security;
if(s < chase_reply->security)
@@ -2335,7 +2309,7 @@ processValidate(struct module_qstate* qstate, struct val_qstate* vq,
key_entry_get_reason_bogus(vq->key_entry));
errinf_ede(qstate, "while building chain of trust",
key_entry_get_reason_bogus(vq->key_entry));
if(!val_can_restart(qstate, vq, ve))
if(vq->restart_count >= ve->max_restart)
key_cache_insert(ve->kcache, vq->key_entry,
qstate->env->cfg->val_log_level >= 2);
return 1;
@@ -2483,7 +2457,7 @@ processValidate(struct module_qstate* qstate, struct val_qstate* vq,
case VAL_CLASS_REFERRAL:
verbose(VERB_ALGO, "Validating a referral response");
validate_referral_response(qstate->env, vq->chase_reply);
validate_referral_response(vq->chase_reply);
verbose(VERB_DETAIL, "validate(referral): %s",
sec_status_to_string(
vq->chase_reply->security));
@@ -2557,17 +2531,15 @@ processFinished(struct module_qstate* qstate, struct val_qstate* vq,
}
if(subtype == VAL_CLASS_REFERRAL) {
if(qstate->env->cfg->val_clean_additional) {
/* for a referral, move to next unchecked rrset and check it*/
vq->rrset_skip = val_next_unchecked(vq->orig_msg->rep,
vq->rrset_skip);
if(vq->rrset_skip < vq->orig_msg->rep->rrset_count) {
/* and restart for this rrset */
verbose(VERB_ALGO, "validator: go to next rrset");
vq->chase_reply->security = sec_status_unchecked;
vq->state = VAL_INIT_STATE;
return 1;
}
/* for a referral, move to next unchecked rrset and check it*/
vq->rrset_skip = val_next_unchecked(vq->orig_msg->rep,
vq->rrset_skip);
if(vq->rrset_skip < vq->orig_msg->rep->rrset_count) {
/* and restart for this rrset */
verbose(VERB_ALGO, "validator: go to next rrset");
vq->chase_reply->security = sec_status_unchecked;
vq->state = VAL_INIT_STATE;
return 1;
}
/* referral chase is done */
}
@@ -2612,7 +2584,7 @@ processFinished(struct module_qstate* qstate, struct val_qstate* vq,
struct msgreply_entry* e;
/* see if we can try again to fetch data */
if(val_can_restart(qstate, vq, ve)) {
if(vq->restart_count < ve->max_restart) {
verbose(VERB_ALGO, "validation failed, "
"blacklist and retry to fetch data");
val_blacklist(&qstate->blacklist, qstate->region,
@@ -2904,7 +2876,6 @@ val_operate(struct module_qstate* qstate, enum module_ev event, int id,
* (this rrset is allocated in the wrong region, not the qstate).
* @param ta: trust anchor.
* @param qstate: qstate that needs key.
* @param vq: validator qstate.
* @param id: module id.
* @param sub_qstate: the sub query state, that is the lookup that fetched
* the trust anchor data, it contains error information for the answer.
@@ -2915,8 +2886,8 @@ val_operate(struct module_qstate* qstate, enum module_ev event, int id,
*/
static struct key_entry_key*
primeResponseToKE(struct ub_packed_rrset_key* dnskey_rrset,
struct trust_anchor* ta, struct module_qstate* qstate,
struct val_qstate* vq, int id, struct module_qstate* sub_qstate)
struct trust_anchor* ta, struct module_qstate* qstate, int id,
struct module_qstate* sub_qstate)
{
struct val_env* ve = (struct val_env*)qstate->env->modinfo[id];
struct key_entry_key* kkey = NULL;
@@ -2956,8 +2927,7 @@ primeResponseToKE(struct ub_packed_rrset_key* dnskey_rrset,
/* attempt to verify with trust anchor DS and DNSKEY */
kkey = val_verify_new_DNSKEYs_with_ta(qstate->region, qstate->env, ve,
dnskey_rrset, ta->ds_rrset, ta->dnskey_rrset, downprot,
&reason, &reason_bogus, qstate, vq, reasonbuf,
sizeof(reasonbuf));
&reason, &reason_bogus, qstate, reasonbuf, sizeof(reasonbuf));
if(!kkey) {
log_err("out of memory: verifying prime TA");
return NULL;
@@ -3070,7 +3040,7 @@ ds_response_to_ke(struct module_qstate* qstate, struct val_qstate* vq,
* bogus, then we are done. */
sec = val_verify_rrset_entry(qstate->env, ve, ds,
vq->key_entry, &reason, &reason_bogus,
LDNS_SECTION_ANSWER, qstate, vq, &verified, reasonbuf,
LDNS_SECTION_ANSWER, qstate, &verified, reasonbuf,
sizeof(reasonbuf));
if(sec != sec_status_secure) {
verbose(VERB_DETAIL, "DS rrset in DS response did "
@@ -3121,7 +3091,7 @@ ds_response_to_ke(struct module_qstate* qstate, struct val_qstate* vq,
/* Try to prove absence of the DS with NSEC */
sec = val_nsec_prove_nodata_dsreply(
qstate->env, ve, qinfo, msg->rep, vq->key_entry,
&proof_ttl, &reason, &reason_bogus, qstate, vq,
&proof_ttl, &reason, &reason_bogus, qstate,
reasonbuf, sizeof(reasonbuf));
switch(sec) {
case sec_status_secure:
@@ -3146,6 +3116,7 @@ ds_response_to_ke(struct module_qstate* qstate, struct val_qstate* vq,
case sec_status_unchecked:
default:
/* NSEC proof did not work, try next */
verbose(VERB_ALGO, "NSEC proof did not prove insecure delegation, try NSEC3");
break;
}
@@ -3159,7 +3130,7 @@ ds_response_to_ke(struct module_qstate* qstate, struct val_qstate* vq,
sec = nsec3_prove_nods(qstate->env, ve,
msg->rep->rrsets + msg->rep->an_numrrsets,
msg->rep->ns_numrrsets, qinfo, vq->key_entry, &reason,
&reason_bogus, qstate, vq, &vq->nsec3_cache_table,
&reason_bogus, qstate, &vq->nsec3_cache_table,
reasonbuf, sizeof(reasonbuf));
switch(sec) {
case sec_status_insecure:
@@ -3181,6 +3152,25 @@ ds_response_to_ke(struct module_qstate* qstate, struct val_qstate* vq,
*ke = NULL;
return 0;
case sec_status_bogus:
/* It could be that the NSEC proof failed,
* and, then tried NSEC3. */
{
int has_nsec=0, has_nsec3=0;
val_has_auth_nsecs(msg->rep, &has_nsec,
&has_nsec3);
if(!has_nsec3 && has_nsec) {
/* The NSECs are the cause, mention that in the error message. */
verbose(VERB_DETAIL, "NSECs for the "
"referral did not prove no DS.");
errinf_ede(qstate, "NSECs for the referral did not prove no DS", LDNS_EDE_DNSSEC_BOGUS);
goto return_bogus;
}
if(!has_nsec3 && !has_nsec) {
verbose(VERB_DETAIL, "absence of NSECs and NSEC3s when attempting to prove no DS.");
errinf_ede(qstate, "no NSECs or NSEC3s when attempting to prove no DS", LDNS_EDE_DNSSEC_BOGUS);
goto return_bogus;
}
}
verbose(VERB_DETAIL, "NSEC3s for the "
"referral did not prove no DS.");
errinf_ede(qstate, reason, reason_bogus);
@@ -3227,7 +3217,7 @@ ds_response_to_ke(struct module_qstate* qstate, struct val_qstate* vq,
}
sec = val_verify_rrset_entry(qstate->env, ve, cname,
vq->key_entry, &reason, &reason_bogus,
LDNS_SECTION_ANSWER, qstate, vq, &verified, reasonbuf,
LDNS_SECTION_ANSWER, qstate, &verified, reasonbuf,
sizeof(reasonbuf));
if(sec == sec_status_secure) {
/* Check for wildcard expansion */
@@ -3348,7 +3338,6 @@ process_ds_response(struct module_qstate* qstate, struct val_qstate* vq,
uint8_t* olds = vq->empty_DS_name;
int ret;
*suspend = 0;
vq->num_nsec_attempts = 0;
vq->empty_DS_name = NULL;
if(sub_qstate && sub_qstate->rpz_applied) {
verbose(VERB_ALGO, "rpz was applied to the DS lookup, "
@@ -3360,8 +3349,6 @@ process_ds_response(struct module_qstate* qstate, struct val_qstate* vq,
}
ret = ds_response_to_ke(qstate, vq, id, rcode, msg, qinfo, &dske,
sub_qstate);
/* New NSEC attempt count for next message validation. */
vq->num_nsec_attempts = 0;
if(ret != 0) {
switch(ret) {
case 1:
@@ -3403,7 +3390,7 @@ process_ds_response(struct module_qstate* qstate, struct val_qstate* vq,
vq->chain_blacklist = NULL; /* fresh blacklist for next part*/
/* Keep the forState.state on FINDKEY. */
} else if(key_entry_isbad(dske)
&& val_can_restart(qstate, vq, ve)) {
&& vq->restart_count < ve->max_restart) {
vq->empty_DS_name = olds;
val_blacklist(&vq->chain_blacklist, qstate->region, origin, 1);
qstate->errinf = NULL;
@@ -3453,7 +3440,6 @@ process_dnskey_response(struct module_qstate* qstate, struct val_qstate* vq,
char* reason = NULL;
sldns_ede_code reason_bogus = LDNS_EDE_DNSSEC_BOGUS;
vq->num_nsec_attempts = 0;
if(sub_qstate && sub_qstate->rpz_applied) {
verbose(VERB_ALGO, "rpz was applied to the DNSKEY lookup, "
"make it insecure");
@@ -3473,7 +3459,7 @@ process_dnskey_response(struct module_qstate* qstate, struct val_qstate* vq,
verbose(VERB_DETAIL, "Missing DNSKEY RRset in response to "
"DNSKEY query.");
if(val_can_restart(qstate, vq, ve)) {
if(vq->restart_count < ve->max_restart) {
val_blacklist(&vq->chain_blacklist, qstate->region,
origin, 1);
qstate->errinf = NULL;
@@ -3510,9 +3496,7 @@ process_dnskey_response(struct module_qstate* qstate, struct val_qstate* vq,
downprot = qstate->env->cfg->harden_algo_downgrade;
vq->key_entry = val_verify_new_DNSKEYs(qstate->region, qstate->env,
ve, dnskey, vq->ds_rrset, downprot, &reason, &reason_bogus,
qstate, vq, reasonbuf, sizeof(reasonbuf));
/* New NSEC attempt count for next message validation. */
vq->num_nsec_attempts = 0;
qstate, reasonbuf, sizeof(reasonbuf));
if(!vq->key_entry) {
log_err("out of memory in verify new DNSKEYs");
@@ -3523,7 +3507,7 @@ process_dnskey_response(struct module_qstate* qstate, struct val_qstate* vq,
* state. */
if(!key_entry_isgood(vq->key_entry)) {
if(key_entry_isbad(vq->key_entry)) {
if(val_can_restart(qstate, vq, ve)) {
if(vq->restart_count < ve->max_restart) {
val_blacklist(&vq->chain_blacklist,
qstate->region, origin, 1);
qstate->errinf = NULL;
@@ -3575,7 +3559,6 @@ process_prime_response(struct module_qstate* qstate, struct val_qstate* vq,
struct trust_anchor* ta = anchor_find(qstate->env->anchors,
vq->trust_anchor_name, vq->trust_anchor_labs,
vq->trust_anchor_len, vq->qchase.qclass);
vq->num_nsec_attempts = 0;
if(!ta) {
/* trust anchor revoked, restart with less anchors */
vq->state = VAL_INIT_STATE;
@@ -3594,23 +3577,19 @@ process_prime_response(struct module_qstate* qstate, struct val_qstate* vq,
if(ta->autr) {
if(!autr_process_prime(qstate->env, ve, ta, dnskey_rrset,
qstate, vq)) {
/* New NSEC attempt count for next message validation. */
vq->num_nsec_attempts = 0;
qstate)) {
/* trust anchor revoked, restart with less anchors */
vq->state = VAL_INIT_STATE;
vq->trust_anchor_name = NULL;
return;
}
}
vq->key_entry = primeResponseToKE(dnskey_rrset, ta, qstate, vq, id,
vq->key_entry = primeResponseToKE(dnskey_rrset, ta, qstate, id,
sub_qstate);
lock_basic_unlock(&ta->lock);
/* New NSEC attempt count for next message validation. */
vq->num_nsec_attempts = 0;
if(vq->key_entry) {
if(key_entry_isbad(vq->key_entry)
&& val_can_restart(qstate, vq, ve)) {
&& vq->restart_count < ve->max_restart) {
val_blacklist(&vq->chain_blacklist, qstate->region,
origin, 1);
qstate->errinf = NULL;
-13
View File
@@ -231,19 +231,6 @@ struct val_qstate {
struct comm_timer* suspend_timer;
/** Number of suspends */
int suspend_count;
/** Number of DNSKEY RRSIG validation attempts. This is the number of
* cryptographic operations done for the mesh state. */
int num_validation_attempts;
/** Number of DS hash verification attempts. This is the number of
* hash operations done for the mesh state.
* It does not count NSEC3 hashes. */
int num_hash_attempts;
/** Number of NSEC validations. And NSEC3 too. This is reset per
* answer. */
int num_nsec_attempts;
/** The nsec attempts have been exceeded. */
int num_nsec_attempts_exceeded;
};
/**