mirror of
https://github.com/NLnetLabs/unbound.git
synced 2026-08-17 21:25:50 +02:00
Compare commits
25
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
48d0aea60a | ||
|
|
17bbcac979 | ||
|
|
fd8ebbb19c | ||
|
|
2adbbea365 | ||
|
|
156c00a727 | ||
|
|
d753f95956 | ||
|
|
61d6c0e766 | ||
|
|
b4daa2d0fa | ||
|
|
425b701fb9 | ||
|
|
0a5cde80f1 | ||
|
|
58ede90fdb | ||
|
|
55ae8da032 | ||
|
|
6bd86df72e | ||
|
|
0efd3605cc | ||
|
|
a2f2f53ef9 | ||
|
|
b5a03093f6 | ||
|
|
8eba898135 | ||
|
|
56f66de89f | ||
|
|
2fbaee2255 | ||
|
|
a122490461 | ||
|
|
7826b4f306 | ||
|
|
380994219f | ||
|
|
153accb8de | ||
|
|
7bb1c62263 | ||
|
|
1578b6e180 |
+4
-2
@@ -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 \
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -62,6 +62,7 @@ struct doq_table;
|
||||
struct cookie_secrets;
|
||||
struct fast_reload_thread;
|
||||
struct fast_reload_printq;
|
||||
struct auth_load_general_info;
|
||||
struct shared_ports;
|
||||
|
||||
#include "dnstap/dnstap_config.h"
|
||||
@@ -191,6 +192,8 @@ struct daemon {
|
||||
int fast_reload_tcl_has_changes;
|
||||
/** config file name */
|
||||
char* cfgfile;
|
||||
/** Auth load threads, the number of active threads. */
|
||||
struct auth_load_general_info* auth_load_info;
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
+1
-369
@@ -115,9 +115,6 @@
|
||||
#ifdef HAVE_NETDB_H
|
||||
#include <netdb.h>
|
||||
#endif
|
||||
#ifdef HAVE_POLL_H
|
||||
#include <poll.h>
|
||||
#endif
|
||||
|
||||
/* just for portability */
|
||||
#ifdef SQ
|
||||
@@ -4466,92 +4463,6 @@ int remote_control_callback(struct comm_point* c, void* arg, int err,
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* This routine polls a socket for readiness.
|
||||
* @param fd: file descriptor, -1 uses no fd for a timer only.
|
||||
* @param timeout: time in msec to wait. 0 means nonblocking test,
|
||||
* -1 waits blocking for events.
|
||||
* @param pollin: check for input event.
|
||||
* @param pollout: check for output event.
|
||||
* @param event: output variable, set to true if the event happens.
|
||||
* It is false if there was an error or timeout.
|
||||
* @return false is system call failure, also logged.
|
||||
*/
|
||||
static int
|
||||
sock_poll_timeout(int fd, int timeout, int pollin, int pollout, int* event)
|
||||
{
|
||||
int loopcount = 0;
|
||||
/* Loop if the system call returns an errno to do so, like EINTR. */
|
||||
log_assert(pollin || pollout);
|
||||
while(1) {
|
||||
struct pollfd p, *fds;
|
||||
int nfds, ret;
|
||||
if(++loopcount > IPC_LOOP_MAX) {
|
||||
log_err("sock_poll_timeout: loop");
|
||||
if(event)
|
||||
*event = 0;
|
||||
return 0;
|
||||
}
|
||||
if(fd == -1) {
|
||||
fds = NULL;
|
||||
nfds = 0;
|
||||
} else {
|
||||
fds = &p;
|
||||
nfds = 1;
|
||||
memset(&p, 0, sizeof(p));
|
||||
p.fd = fd;
|
||||
#ifndef USE_WINSOCK
|
||||
p.events = POLLERR
|
||||
| POLLHUP
|
||||
;
|
||||
#endif
|
||||
if(pollin)
|
||||
p.events |= POLLIN;
|
||||
if(pollout)
|
||||
p.events |= POLLOUT;
|
||||
}
|
||||
#ifndef USE_WINSOCK
|
||||
ret = poll(fds, nfds, timeout);
|
||||
#else
|
||||
if(fds == NULL) {
|
||||
Sleep(timeout);
|
||||
ret = 0;
|
||||
} else {
|
||||
ret = WSAPoll(fds, nfds, timeout);
|
||||
}
|
||||
#endif
|
||||
if(ret == -1) {
|
||||
#ifndef USE_WINSOCK
|
||||
if(
|
||||
errno == EINTR || errno == EAGAIN
|
||||
# ifdef EWOULDBLOCK
|
||||
|| errno == EWOULDBLOCK
|
||||
# endif
|
||||
) continue; /* Try again. */
|
||||
#endif
|
||||
/* For WSAPoll we only get errors here:
|
||||
* o WSAENETDOWN
|
||||
* o WSAEFAULT
|
||||
* o WSAEINVAL
|
||||
* o WSAENOBUFS
|
||||
*/
|
||||
log_err("poll: %s", sock_strerror(errno));
|
||||
if(event)
|
||||
*event = 0;
|
||||
return 0;
|
||||
} else if(ret == 0) {
|
||||
/* Timeout */
|
||||
if(event)
|
||||
*event = 0;
|
||||
return 1;
|
||||
}
|
||||
break;
|
||||
}
|
||||
if(event)
|
||||
*event = 1;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/** fast reload convert fast reload notification status to string */
|
||||
static const char*
|
||||
fr_notification_to_string(enum fast_reload_notification status)
|
||||
@@ -6365,6 +6276,7 @@ fr_atomic_copy_cfg(struct config_file* oldcfg, struct config_file* cfg,
|
||||
COPY_VAR_ptr(acls);
|
||||
COPY_VAR_int(donotquery_localhost);
|
||||
COPY_VAR_ptr(tcp_connection_limits);
|
||||
COPY_VAR_int(auth_task_threads);
|
||||
COPY_VAR_int(harden_short_bufsize);
|
||||
COPY_VAR_int(harden_large_queries);
|
||||
COPY_VAR_int(harden_glue);
|
||||
@@ -7153,286 +7065,6 @@ done_error:
|
||||
}
|
||||
#endif /* !THREADS_DISABLED */
|
||||
|
||||
/** create a socketpair for bidirectional communication, false on failure */
|
||||
static int
|
||||
create_socketpair(int* pair, struct ub_randstate* rand)
|
||||
{
|
||||
#ifndef USE_WINSOCK
|
||||
if(socketpair(AF_UNIX, SOCK_STREAM, 0, pair) == -1) {
|
||||
log_err("socketpair: %s", strerror(errno));
|
||||
return 0;
|
||||
}
|
||||
(void)rand;
|
||||
#else
|
||||
struct sockaddr_in addr, baddr, accaddr, connaddr;
|
||||
socklen_t baddrlen, accaddrlen, connaddrlen;
|
||||
uint8_t localhost[] = {127, 0, 0, 1};
|
||||
uint8_t nonce[16], recvnonce[16];
|
||||
size_t i;
|
||||
int lst, pollin_event, bcount, loopcount;
|
||||
int connect_poll_timeout = 200; /* msec to wait for connection */
|
||||
ssize_t ret;
|
||||
pair[0] = -1;
|
||||
pair[1] = -1;
|
||||
for(i=0; i<sizeof(nonce); i++) {
|
||||
nonce[i] = ub_random_max(rand, 256);
|
||||
}
|
||||
lst = socket(AF_INET, SOCK_STREAM, 0);
|
||||
if(lst == -1) {
|
||||
log_err("create_socketpair: socket: %s", sock_strerror(errno));
|
||||
return 0;
|
||||
}
|
||||
memset(&addr, 0, sizeof(addr));
|
||||
addr.sin_family = AF_INET;
|
||||
addr.sin_port = 0;
|
||||
memcpy(&addr.sin_addr, localhost, 4);
|
||||
if(bind(lst, (struct sockaddr*)&addr, (socklen_t)sizeof(addr))
|
||||
== -1) {
|
||||
log_err("create socketpair: bind: %s", sock_strerror(errno));
|
||||
sock_close(lst);
|
||||
return 0;
|
||||
}
|
||||
if(listen(lst, 12) == -1) {
|
||||
log_err("create socketpair: listen: %s", sock_strerror(errno));
|
||||
sock_close(lst);
|
||||
return 0;
|
||||
}
|
||||
|
||||
pair[1] = socket(AF_INET, SOCK_STREAM, 0);
|
||||
if(pair[1] == -1) {
|
||||
log_err("create socketpair: socket: %s", sock_strerror(errno));
|
||||
sock_close(lst);
|
||||
return 0;
|
||||
}
|
||||
baddrlen = (socklen_t)sizeof(baddr);
|
||||
if(getsockname(lst, (struct sockaddr*)&baddr, &baddrlen) == -1) {
|
||||
log_err("create socketpair: getsockname: %s",
|
||||
sock_strerror(errno));
|
||||
sock_close(lst);
|
||||
sock_close(pair[1]);
|
||||
pair[1] = -1;
|
||||
return 0;
|
||||
}
|
||||
if(baddrlen > (socklen_t)sizeof(baddr)) {
|
||||
log_err("create socketpair: getsockname returned addr too big");
|
||||
sock_close(lst);
|
||||
sock_close(pair[1]);
|
||||
pair[1] = -1;
|
||||
return 0;
|
||||
}
|
||||
/* the socket is blocking */
|
||||
if(connect(pair[1], (struct sockaddr*)&baddr, baddrlen) == -1) {
|
||||
log_err("create socketpair: connect: %s",
|
||||
sock_strerror(errno));
|
||||
sock_close(lst);
|
||||
sock_close(pair[1]);
|
||||
pair[1] = -1;
|
||||
return 0;
|
||||
}
|
||||
if(!sock_poll_timeout(lst, connect_poll_timeout, 1, 0, &pollin_event)) {
|
||||
log_err("create socketpair: poll for accept failed: %s",
|
||||
sock_strerror(errno));
|
||||
sock_close(lst);
|
||||
sock_close(pair[1]);
|
||||
pair[1] = -1;
|
||||
return 0;
|
||||
}
|
||||
if(!pollin_event) {
|
||||
log_err("create socketpair: poll timeout for accept");
|
||||
sock_close(lst);
|
||||
sock_close(pair[1]);
|
||||
pair[1] = -1;
|
||||
return 0;
|
||||
}
|
||||
accaddrlen = (socklen_t)sizeof(accaddr);
|
||||
pair[0] = accept(lst, (struct sockaddr*)&accaddr, &accaddrlen);
|
||||
if(pair[0] == -1) {
|
||||
log_err("create socketpair: accept: %s", sock_strerror(errno));
|
||||
sock_close(lst);
|
||||
sock_close(pair[1]);
|
||||
pair[1] = -1;
|
||||
return 0;
|
||||
}
|
||||
if(accaddrlen > (socklen_t)sizeof(accaddr)) {
|
||||
log_err("create socketpair: accept returned addr too big");
|
||||
sock_close(lst);
|
||||
sock_close(pair[0]);
|
||||
sock_close(pair[1]);
|
||||
pair[0] = -1;
|
||||
pair[1] = -1;
|
||||
return 0;
|
||||
}
|
||||
if(accaddr.sin_family != AF_INET ||
|
||||
memcmp(localhost, &accaddr.sin_addr, 4) != 0) {
|
||||
log_err("create socketpair: accept from wrong address");
|
||||
sock_close(lst);
|
||||
sock_close(pair[0]);
|
||||
sock_close(pair[1]);
|
||||
pair[0] = -1;
|
||||
pair[1] = -1;
|
||||
return 0;
|
||||
}
|
||||
connaddrlen = (socklen_t)sizeof(connaddr);
|
||||
if(getsockname(pair[1], (struct sockaddr*)&connaddr, &connaddrlen)
|
||||
== -1) {
|
||||
log_err("create socketpair: getsockname connectedaddr: %s",
|
||||
sock_strerror(errno));
|
||||
sock_close(lst);
|
||||
sock_close(pair[0]);
|
||||
sock_close(pair[1]);
|
||||
pair[0] = -1;
|
||||
pair[1] = -1;
|
||||
return 0;
|
||||
}
|
||||
if(connaddrlen > (socklen_t)sizeof(connaddr)) {
|
||||
log_err("create socketpair: getsockname connectedaddr returned addr too big");
|
||||
sock_close(lst);
|
||||
sock_close(pair[0]);
|
||||
sock_close(pair[1]);
|
||||
pair[0] = -1;
|
||||
pair[1] = -1;
|
||||
return 0;
|
||||
}
|
||||
if(connaddr.sin_family != AF_INET ||
|
||||
memcmp(localhost, &connaddr.sin_addr, 4) != 0) {
|
||||
log_err("create socketpair: getsockname connectedaddr returned wrong address");
|
||||
sock_close(lst);
|
||||
sock_close(pair[0]);
|
||||
sock_close(pair[1]);
|
||||
pair[0] = -1;
|
||||
pair[1] = -1;
|
||||
return 0;
|
||||
}
|
||||
if(accaddr.sin_port != connaddr.sin_port) {
|
||||
log_err("create socketpair: accept from wrong port");
|
||||
sock_close(lst);
|
||||
sock_close(pair[0]);
|
||||
sock_close(pair[1]);
|
||||
pair[0] = -1;
|
||||
pair[1] = -1;
|
||||
return 0;
|
||||
}
|
||||
sock_close(lst);
|
||||
|
||||
loopcount = 0;
|
||||
bcount = 0;
|
||||
while(1) {
|
||||
if(++loopcount > IPC_LOOP_MAX) {
|
||||
log_err("create socketpair: send failed due to loop");
|
||||
sock_close(pair[0]);
|
||||
sock_close(pair[1]);
|
||||
pair[0] = -1;
|
||||
pair[1] = -1;
|
||||
return 0;
|
||||
}
|
||||
ret = send(pair[1], (void*)(nonce+bcount),
|
||||
sizeof(nonce)-bcount, 0);
|
||||
if(ret == -1) {
|
||||
if(
|
||||
#ifndef USE_WINSOCK
|
||||
errno == EINTR || errno == EAGAIN
|
||||
# ifdef EWOULDBLOCK
|
||||
|| errno == EWOULDBLOCK
|
||||
# endif
|
||||
#else
|
||||
WSAGetLastError() == WSAEINTR ||
|
||||
WSAGetLastError() == WSAEINPROGRESS ||
|
||||
WSAGetLastError() == WSAEWOULDBLOCK
|
||||
#endif
|
||||
)
|
||||
continue; /* Try again. */
|
||||
log_err("create socketpair: send: %s", sock_strerror(errno));
|
||||
sock_close(pair[0]);
|
||||
sock_close(pair[1]);
|
||||
pair[0] = -1;
|
||||
pair[1] = -1;
|
||||
return 0;
|
||||
} else if(ret+(ssize_t)bcount != sizeof(nonce)) {
|
||||
bcount += ret;
|
||||
if((size_t)bcount < sizeof(nonce))
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if(!sock_poll_timeout(pair[0], connect_poll_timeout, 1, 0, &pollin_event)) {
|
||||
log_err("create socketpair: poll failed: %s",
|
||||
sock_strerror(errno));
|
||||
sock_close(pair[0]);
|
||||
sock_close(pair[1]);
|
||||
pair[0] = -1;
|
||||
pair[1] = -1;
|
||||
return 0;
|
||||
}
|
||||
if(!pollin_event) {
|
||||
log_err("create socketpair: poll timeout for recv");
|
||||
sock_close(pair[0]);
|
||||
sock_close(pair[1]);
|
||||
pair[0] = -1;
|
||||
pair[1] = -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
loopcount = 0;
|
||||
bcount = 0;
|
||||
while(1) {
|
||||
if(++loopcount > IPC_LOOP_MAX) {
|
||||
log_err("create socketpair: recv failed due to loop");
|
||||
sock_close(pair[0]);
|
||||
sock_close(pair[1]);
|
||||
pair[0] = -1;
|
||||
pair[1] = -1;
|
||||
return 0;
|
||||
}
|
||||
ret = recv(pair[0], (void*)(recvnonce+bcount),
|
||||
sizeof(nonce)-bcount, 0);
|
||||
if(ret == -1) {
|
||||
if(
|
||||
#ifndef USE_WINSOCK
|
||||
errno == EINTR || errno == EAGAIN
|
||||
# ifdef EWOULDBLOCK
|
||||
|| errno == EWOULDBLOCK
|
||||
# endif
|
||||
#else
|
||||
WSAGetLastError() == WSAEINTR ||
|
||||
WSAGetLastError() == WSAEINPROGRESS ||
|
||||
WSAGetLastError() == WSAEWOULDBLOCK
|
||||
#endif
|
||||
)
|
||||
continue; /* Try again. */
|
||||
log_err("create socketpair: recv: %s", sock_strerror(errno));
|
||||
sock_close(pair[0]);
|
||||
sock_close(pair[1]);
|
||||
pair[0] = -1;
|
||||
pair[1] = -1;
|
||||
return 0;
|
||||
} else if(ret == 0) {
|
||||
log_err("create socketpair: stream closed");
|
||||
sock_close(pair[0]);
|
||||
sock_close(pair[1]);
|
||||
pair[0] = -1;
|
||||
pair[1] = -1;
|
||||
return 0;
|
||||
} else if(ret+(ssize_t)bcount != sizeof(nonce)) {
|
||||
bcount += ret;
|
||||
if((size_t)bcount < sizeof(nonce))
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if(memcmp(nonce, recvnonce, sizeof(nonce)) != 0) {
|
||||
log_err("create socketpair: recv wrong nonce");
|
||||
sock_close(pair[0]);
|
||||
sock_close(pair[1]);
|
||||
pair[0] = -1;
|
||||
pair[1] = -1;
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
return 1;
|
||||
}
|
||||
|
||||
/** fast reload thread. setup the thread info */
|
||||
static int
|
||||
fast_reload_thread_setup(struct worker* worker, int fr_verb, int fr_nopause,
|
||||
|
||||
@@ -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 */
|
||||
@@ -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);
|
||||
}
|
||||
@@ -0,0 +1,203 @@
|
||||
/*
|
||||
* services/authload.h - authoritative zone load thread
|
||||
*
|
||||
* Copyright (c) 2026, NLnet Labs. All rights reserved.
|
||||
*
|
||||
* This software is open source.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
*
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* Neither the name of the NLNET LABS nor the names of its contributors may
|
||||
* be used to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
|
||||
* TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/**
|
||||
* \file
|
||||
*
|
||||
* This file contains the auth load thread. This loads authority zone
|
||||
* and RPZ zone information in a thread, in a separate memory structure.
|
||||
* When it is done, the information is swapped over to the running server.
|
||||
*/
|
||||
|
||||
#ifndef SERVICES_AUTHLOAD_H
|
||||
#define SERVICES_AUTHLOAD_H
|
||||
#include "util/locks.h"
|
||||
struct worker;
|
||||
struct auth_xfer;
|
||||
struct module_env;
|
||||
struct auth_load_task;
|
||||
|
||||
/**
|
||||
* General information for auth load threads. The number of active threads.
|
||||
*/
|
||||
struct auth_load_general_info {
|
||||
/** lock on this structure */
|
||||
lock_basic_type lock;
|
||||
/** The number of active auth load threads. */
|
||||
int num_auth_load_threads;
|
||||
};
|
||||
|
||||
/**
|
||||
* The types of notifications that the auth load thread sends around.
|
||||
*/
|
||||
enum auth_load_notification_type {
|
||||
/** This is sent to make the auth load thread perform exit */
|
||||
auth_load_notification_exit
|
||||
};
|
||||
|
||||
/**
|
||||
* The auth load thread. The thread runs to load authority zone information
|
||||
* and RPZ information into memory. It loads into a copy. Then that is swapped
|
||||
* over to the running server. This keeps the server responsive while the
|
||||
* information is loaded.
|
||||
*/
|
||||
struct auth_load_thread {
|
||||
/** the thread number for the thread,
|
||||
* must be first to cast thread arg to int* in checklock code. */
|
||||
int threadnum;
|
||||
/** thread id, of the io thread */
|
||||
ub_thread_type tid;
|
||||
#ifdef HAVE_GETTID
|
||||
/** thread tid, the LWP id */
|
||||
pid_t thread_tid;
|
||||
/** if logging should include the LWP id */
|
||||
int thread_tid_log;
|
||||
#endif
|
||||
|
||||
/** communication socket pair, that sends commands */
|
||||
int commpair[2];
|
||||
/** if the thread has to quit */
|
||||
int need_to_quit;
|
||||
/** the event that listens on the worker to commpair,
|
||||
* it receives content from the auth load thread. */
|
||||
void* service_event;
|
||||
/** if the event that listens on the worker has
|
||||
* been added to the comm base. */
|
||||
int service_event_is_added;
|
||||
|
||||
/** the worker that the auth load is connected to */
|
||||
struct worker* worker;
|
||||
|
||||
/** The task that the thread is working on */
|
||||
struct auth_load_task* task;
|
||||
};
|
||||
|
||||
/**
|
||||
* The types of tasks that the auth load can perform.
|
||||
*/
|
||||
enum auth_load_task_type {
|
||||
AUTH_LOAD_TASK_TRANSFER,
|
||||
AUTH_LOAD_TASK_ZONEFILE_READ,
|
||||
AUTH_LOAD_TASK_ZONEFILE_WRITE,
|
||||
AUTH_LOAD_TASK_HTTPCHUNKS
|
||||
};
|
||||
|
||||
/**
|
||||
* The task for the auth load. The task can be to load a zone transfer, AXFR,
|
||||
* IXFR, from zonefile, and from a http read, from chunks.
|
||||
*/
|
||||
struct auth_load_task {
|
||||
/** The type of the task */
|
||||
enum auth_load_task_type task_type;
|
||||
/** The task is connected with this worker */
|
||||
struct worker* worker;
|
||||
|
||||
/** The zone name */
|
||||
uint8_t* name;
|
||||
/** The zone namelen */
|
||||
size_t namelen;
|
||||
/** The zone class */
|
||||
uint16_t dclass;
|
||||
|
||||
/** name of the host that the transfer comes from. */
|
||||
char* host;
|
||||
/** file part of the url that the transfer comes from, or NULL. */
|
||||
char* file;
|
||||
/** Set if the host is http transfer, if false it is AXFR or IXFR. */
|
||||
int on_http;
|
||||
/** Set if the transfer is doing IXFR */
|
||||
int on_ixfr;
|
||||
/** Set if the transfer is an IXFR but we detected an AXFR contents */
|
||||
int on_ixfr_is_axfr;
|
||||
|
||||
/** Set if the ixfr failed. (So that there can be backoff to AXFR). */
|
||||
int ixfr_fail;
|
||||
|
||||
/** current serial (from SOA), if we have no zone, 0
|
||||
* This is for checking the IXFR result. */
|
||||
uint32_t serial;
|
||||
|
||||
/** the data chunks, or NULL, to process. */
|
||||
struct auth_chunk* chunks_first;
|
||||
/** last data chunk */
|
||||
struct auth_chunk* chunks_last;
|
||||
/** size of data in data chunks. */
|
||||
size_t chunks_total;
|
||||
|
||||
/** time taken for the task */
|
||||
struct timeval time_taken;
|
||||
/** memory used for the task */
|
||||
size_t mem_used;
|
||||
};
|
||||
|
||||
/**
|
||||
* Add a new task to be performed by the auth load thread.
|
||||
* It starts a thread, or makes a wait list item.
|
||||
* @param xfr: zone transfer to start for.
|
||||
* @param worker: worker that is connected to the task.
|
||||
* @return false on failure.
|
||||
*/
|
||||
int auth_load_add_task_xfr(struct auth_xfer* xfr, struct worker* worker);
|
||||
|
||||
/** See if there is a quit signal, true if so. */
|
||||
int auth_load_thread_poll_for_quit(struct auth_load_thread* thr);
|
||||
|
||||
/**
|
||||
* Create auth load info structure.
|
||||
* @return NULL on failure.
|
||||
*/
|
||||
struct auth_load_general_info* auth_load_info_create(void);
|
||||
|
||||
/**
|
||||
* Delete auth load info structure.
|
||||
* @param auth_load_info: to delete.
|
||||
*/
|
||||
void auth_load_info_delete(struct auth_load_general_info* auth_load_info);
|
||||
|
||||
/**
|
||||
* Grab a new thread from the auth load count.
|
||||
* @param env: with auth_load_info with the active thread count.
|
||||
* and config file, with configured maximum.
|
||||
* @return false on failure, like too many active, true if successful.
|
||||
*/
|
||||
int auth_load_info_grab_thread(struct module_env* env);
|
||||
|
||||
/**
|
||||
* Release thread from auth load count. It is done.
|
||||
* @param env: with auth_load_info with the active thread count.
|
||||
*/
|
||||
void auth_load_info_release_thread(struct module_env* env);
|
||||
|
||||
#endif /* SERVICES_AUTHLOAD_H */
|
||||
+374
-121
@@ -60,6 +60,7 @@
|
||||
#include "services/outside_network.h"
|
||||
#include "services/listen_dnsport.h"
|
||||
#include "services/mesh.h"
|
||||
#include "services/authload.h"
|
||||
#include "sldns/rrdef.h"
|
||||
#include "sldns/pkthdr.h"
|
||||
#include "sldns/sbuffer.h"
|
||||
@@ -98,6 +99,10 @@
|
||||
#define NUM_TIMEOUTS_FALLBACK_IXFR 3
|
||||
/** number of IXFRs before an AXFR is performed, to consolidate RPZ memory. */
|
||||
#define NUM_IXFR_BEFORE_AXFR 5
|
||||
/** number of records before polling if the auth-load-thread has to quit.
|
||||
* It polls on the socket, once for this number of processed records, so
|
||||
* it can quit when the signal is sent over the socket. */
|
||||
#define NUM_RECORDS_BEFORE_SIGNAL_CHECK 10000
|
||||
|
||||
/** pick up nextprobe task to start waiting to perform transfer actions */
|
||||
static void xfr_set_timeout(struct auth_xfer* xfr, struct module_env* env,
|
||||
@@ -385,7 +390,7 @@ auth_data_delete(struct auth_data* n)
|
||||
}
|
||||
|
||||
/** helper traverse to delete zones */
|
||||
static void
|
||||
void
|
||||
auth_data_del(rbnode_type* n, void* ATTR_UNUSED(arg))
|
||||
{
|
||||
struct auth_data* z = (struct auth_data*)n->key;
|
||||
@@ -597,7 +602,7 @@ auth_zone_set_fallback(struct auth_zone* z, char* fallbackstr)
|
||||
}
|
||||
|
||||
/** create domain with the given name */
|
||||
static struct auth_data*
|
||||
struct auth_data*
|
||||
az_domain_create(struct auth_zone* z, uint8_t* nm, size_t nmlen)
|
||||
{
|
||||
struct auth_data* n = (struct auth_data*)malloc(sizeof(*n));
|
||||
@@ -4739,7 +4744,8 @@ chunkline_non_comment_RR(struct auth_chunk** chunk, size_t* chunk_pos,
|
||||
* failure and return a string in the scratch buffer (first RR string)
|
||||
* on failure. */
|
||||
static int
|
||||
http_zonefile_syntax_check(struct auth_xfer* xfr, sldns_buffer* buf)
|
||||
http_zonefile_syntax_check(uint8_t* name, size_t namelen, uint16_t dclass,
|
||||
struct auth_chunk* chunk_list, struct sldns_buffer* buf)
|
||||
{
|
||||
uint8_t rr[LDNS_RR_BUF_SIZE];
|
||||
size_t rr_len, dname_len = 0;
|
||||
@@ -4749,11 +4755,11 @@ http_zonefile_syntax_check(struct auth_xfer* xfr, sldns_buffer* buf)
|
||||
int e;
|
||||
memset(&pstate, 0, sizeof(pstate));
|
||||
pstate.default_ttl = 3600;
|
||||
if(xfr->namelen < sizeof(pstate.origin)) {
|
||||
pstate.origin_len = xfr->namelen;
|
||||
memmove(pstate.origin, xfr->name, xfr->namelen);
|
||||
if(namelen < sizeof(pstate.origin)) {
|
||||
pstate.origin_len = namelen;
|
||||
memmove(pstate.origin, name, namelen);
|
||||
}
|
||||
chunk = xfr->task_transfer->chunks_first;
|
||||
chunk = chunk_list;
|
||||
chunk_pos = 0;
|
||||
if(!chunkline_non_comment_RR(&chunk, &chunk_pos, buf, &pstate)) {
|
||||
return 0;
|
||||
@@ -4770,7 +4776,7 @@ http_zonefile_syntax_check(struct auth_xfer* xfr, sldns_buffer* buf)
|
||||
return 0;
|
||||
}
|
||||
/* check that class is correct */
|
||||
if(sldns_wirerr_get_class(rr, rr_len, dname_len) != xfr->dclass) {
|
||||
if(sldns_wirerr_get_class(rr, rr_len, dname_len) != dclass) {
|
||||
log_err("parse failure: first record in downloaded zonefile "
|
||||
"from wrong RR class");
|
||||
return 0;
|
||||
@@ -4792,7 +4798,7 @@ chunklist_sum(struct auth_chunk* list)
|
||||
|
||||
/** for http download, parse and add RR to zone */
|
||||
static int
|
||||
http_parse_add_rr(struct auth_xfer* xfr, struct auth_zone* z,
|
||||
http_parse_add_rr(const char* host, const char* file, struct auth_zone* z,
|
||||
sldns_buffer* buf, struct sldns_file_parse_state* pstate)
|
||||
{
|
||||
uint8_t rr[LDNS_RR_BUF_SIZE];
|
||||
@@ -4806,9 +4812,7 @@ http_parse_add_rr(struct auth_xfer* xfr, struct auth_zone* z,
|
||||
pstate->prev_rr_len?pstate->prev_rr:NULL, pstate->prev_rr_len);
|
||||
if(e != 0) {
|
||||
log_err("%s/%s parse failure RR[%d]: %s in '%s'",
|
||||
xfr->task_transfer->master->host,
|
||||
xfr->task_transfer->master->file,
|
||||
LDNS_WIREPARSE_OFFSET(e),
|
||||
host, file, LDNS_WIREPARSE_OFFSET(e),
|
||||
sldns_get_errorstr_parse(LDNS_WIREPARSE_ERROR(e)),
|
||||
line);
|
||||
return 0;
|
||||
@@ -4828,10 +4832,10 @@ http_parse_add_rr(struct auth_xfer* xfr, struct auth_zone* z,
|
||||
/** RR list iterator, returns RRs from answer section one by one from the
|
||||
* dns packets in the chunklist */
|
||||
static void
|
||||
chunk_rrlist_start(struct auth_xfer* xfr, struct auth_chunk** rr_chunk,
|
||||
chunk_rrlist_start(struct auth_chunk* chunk_list, struct auth_chunk** rr_chunk,
|
||||
int* rr_num, size_t* rr_pos)
|
||||
{
|
||||
*rr_chunk = xfr->task_transfer->chunks_first;
|
||||
*rr_chunk = chunk_list;
|
||||
*rr_num = 0;
|
||||
*rr_pos = 0;
|
||||
}
|
||||
@@ -4998,9 +5002,10 @@ ixfr_start_serial(struct auth_chunk* rr_chunk, int rr_num, size_t rr_pos,
|
||||
}
|
||||
|
||||
/** apply IXFR to zone in memory. z is locked. false on failure(mallocfail) */
|
||||
static int
|
||||
apply_ixfr(struct auth_xfer* xfr, struct auth_zone* z,
|
||||
struct sldns_buffer* scratch_buffer)
|
||||
int
|
||||
xfr_apply_ixfr(struct auth_chunk* chunk_list, uint32_t xfr_serial,
|
||||
struct auth_zone* z, struct sldns_buffer* scratch_buffer,
|
||||
struct auth_load_thread* thr)
|
||||
{
|
||||
struct auth_chunk* rr_chunk;
|
||||
int rr_num;
|
||||
@@ -5015,10 +5020,8 @@ apply_ixfr(struct auth_xfer* xfr, struct auth_zone* z,
|
||||
int delmode = 0;
|
||||
int softfail = 0;
|
||||
|
||||
xfr->num_ixfrs++;
|
||||
|
||||
/* start RR iterator over chunklist of packets */
|
||||
chunk_rrlist_start(xfr, &rr_chunk, &rr_num, &rr_pos);
|
||||
chunk_rrlist_start(chunk_list, &rr_chunk, &rr_num, &rr_pos);
|
||||
while(!chunk_rrlist_end(rr_chunk, rr_num)) {
|
||||
if(!chunk_rrlist_get_current(rr_chunk, rr_num, rr_pos,
|
||||
&rr_dname, &rr_type, &rr_class, &rr_ttl, &rr_rdlen,
|
||||
@@ -5049,7 +5052,7 @@ apply_ixfr(struct auth_xfer* xfr, struct auth_zone* z,
|
||||
if(!ixfr_start_serial(rr_chunk, rr_num, rr_pos,
|
||||
rr_dname, rr_type, rr_class, rr_ttl,
|
||||
rr_rdlen, rr_rdata, rr_nextpos,
|
||||
transfer_serial, xfr->serial)) {
|
||||
transfer_serial, xfr_serial)) {
|
||||
return 0;
|
||||
}
|
||||
} else if(transfer_serial == serial) {
|
||||
@@ -5070,7 +5073,8 @@ apply_ixfr(struct auth_xfer* xfr, struct auth_zone* z,
|
||||
* SOA 3 followed by add
|
||||
* SOA 3 end */
|
||||
/* ended by SOA record */
|
||||
xfr->serial = transfer_serial;
|
||||
/* xfr->serial is set by xfr_find_soa
|
||||
* after this function. */
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -5127,6 +5131,10 @@ apply_ixfr(struct auth_xfer* xfr, struct auth_zone* z,
|
||||
}
|
||||
|
||||
rr_counter++;
|
||||
if(thr && rr_counter % NUM_RECORDS_BEFORE_SIGNAL_CHECK == 0) {
|
||||
if(auth_load_thread_poll_for_quit(thr))
|
||||
return 0;
|
||||
}
|
||||
chunk_rrlist_gonext(&rr_chunk, &rr_num, &rr_pos, rr_nextpos);
|
||||
}
|
||||
if(softfail) {
|
||||
@@ -5136,10 +5144,21 @@ apply_ixfr(struct auth_xfer* xfr, struct auth_zone* z,
|
||||
return 1;
|
||||
}
|
||||
|
||||
/** apply AXFR to zone in memory. z is locked. false on failure(mallocfail) */
|
||||
/** apply IXFR to zone in memory. z is locked. false on failure(mallocfail) */
|
||||
static int
|
||||
apply_axfr(struct auth_xfer* xfr, struct auth_zone* z,
|
||||
apply_ixfr(struct auth_xfer* xfr, struct auth_zone* z,
|
||||
struct sldns_buffer* scratch_buffer)
|
||||
{
|
||||
xfr->num_ixfrs++;
|
||||
|
||||
return xfr_apply_ixfr(xfr->task_transfer->chunks_first, xfr->serial, z,
|
||||
scratch_buffer, NULL);
|
||||
}
|
||||
|
||||
/** apply AXFR to zone in memory. z is locked. false on failure(mallocfail) */
|
||||
int
|
||||
xfr_apply_axfr(struct auth_chunk* chunk_list, struct auth_zone* z,
|
||||
struct sldns_buffer* scratch_buffer, struct auth_load_thread* thr)
|
||||
{
|
||||
struct auth_chunk* rr_chunk;
|
||||
int rr_num;
|
||||
@@ -5147,21 +5166,16 @@ apply_axfr(struct auth_xfer* xfr, struct auth_zone* z,
|
||||
uint8_t* rr_dname, *rr_rdata;
|
||||
uint16_t rr_type, rr_class, rr_rdlen;
|
||||
uint32_t rr_ttl;
|
||||
uint32_t serial = 0;
|
||||
size_t rr_nextpos;
|
||||
size_t rr_counter = 0;
|
||||
int have_end_soa = 0;
|
||||
|
||||
auth_zone_clear_data(z);
|
||||
xfr->have_zone = 0;
|
||||
xfr->serial = 0;
|
||||
xfr->soa_zone_acquired = 0;
|
||||
xfr->num_ixfrs = 0;
|
||||
|
||||
/* insert all RRs in to the zone */
|
||||
/* insert the SOA only once, skip the last one */
|
||||
/* start RR iterator over chunklist of packets */
|
||||
chunk_rrlist_start(xfr, &rr_chunk, &rr_num, &rr_pos);
|
||||
chunk_rrlist_start(chunk_list, &rr_chunk, &rr_num, &rr_pos);
|
||||
while(!chunk_rrlist_end(rr_chunk, rr_num)) {
|
||||
if(!chunk_rrlist_get_current(rr_chunk, rr_num, rr_pos,
|
||||
&rr_dname, &rr_type, &rr_class, &rr_ttl, &rr_rdlen,
|
||||
@@ -5178,7 +5192,7 @@ apply_axfr(struct auth_xfer* xfr, struct auth_zone* z,
|
||||
break;
|
||||
}
|
||||
if(rr_rdlen < 22) return 0; /* bad SOA rdlen */
|
||||
serial = sldns_read_uint32(rr_rdata+rr_rdlen-20);
|
||||
/* serial is at sldns_read_uint32(rr_rdata+rr_rdlen-20); */
|
||||
}
|
||||
|
||||
/* add this RR */
|
||||
@@ -5190,22 +5204,75 @@ apply_axfr(struct auth_xfer* xfr, struct auth_zone* z,
|
||||
}
|
||||
|
||||
rr_counter++;
|
||||
if(thr && rr_counter % NUM_RECORDS_BEFORE_SIGNAL_CHECK == 0) {
|
||||
if(auth_load_thread_poll_for_quit(thr))
|
||||
return 0;
|
||||
}
|
||||
chunk_rrlist_gonext(&rr_chunk, &rr_num, &rr_pos, rr_nextpos);
|
||||
}
|
||||
if(!have_end_soa) {
|
||||
log_err("no end SOA record for AXFR");
|
||||
return 0;
|
||||
}
|
||||
/* xfr->serial and xfr->have_zone are set by xfr_find_soa
|
||||
* after this function. */
|
||||
return 1;
|
||||
}
|
||||
|
||||
xfr->serial = serial;
|
||||
xfr->have_zone = 1;
|
||||
/** apply AXFR to zone in memory. z is locked. false on failure(mallocfail) */
|
||||
static int
|
||||
apply_axfr(struct auth_xfer* xfr, struct auth_zone* z,
|
||||
struct sldns_buffer* scratch_buffer)
|
||||
{
|
||||
xfr->have_zone = 0;
|
||||
xfr->serial = 0;
|
||||
xfr->soa_zone_acquired = 0;
|
||||
xfr->num_ixfrs = 0;
|
||||
return xfr_apply_axfr(xfr->task_transfer->chunks_first, z,
|
||||
scratch_buffer, NULL);
|
||||
}
|
||||
|
||||
void
|
||||
xfr_http_preview(const char* file, struct auth_chunk* chunk_list)
|
||||
{
|
||||
if(verbosity >= VERB_ALGO)
|
||||
verbose(VERB_ALGO, "http download %s of size %d",
|
||||
file, (int)chunklist_sum(chunk_list));
|
||||
if(chunk_list && verbosity >= VERB_ALGO) {
|
||||
char preview[1024];
|
||||
if(chunk_list->len+1 > sizeof(preview)) {
|
||||
memmove(preview, chunk_list->data, sizeof(preview)-1);
|
||||
preview[sizeof(preview)-1]=0;
|
||||
} else {
|
||||
memmove(preview, chunk_list->data, chunk_list->len);
|
||||
preview[chunk_list->len]=0;
|
||||
}
|
||||
log_info("auth zone http downloaded content preview: %s",
|
||||
preview);
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
xfr_http_syntax_check(uint8_t* name, size_t namelen, uint16_t dclass,
|
||||
const char* host, const char* file, struct auth_chunk* chunk_list,
|
||||
struct sldns_buffer* scratch_buffer)
|
||||
{
|
||||
/* perhaps a little syntax check before we try to apply the data? */
|
||||
if(!http_zonefile_syntax_check(name, namelen, dclass, chunk_list,
|
||||
scratch_buffer)) {
|
||||
log_err("http download %s/%s does not contain a zonefile, "
|
||||
"but got '%s'", host, file,
|
||||
sldns_buffer_begin(scratch_buffer));
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
/** apply HTTP to zone in memory. z is locked. false on failure(mallocfail) */
|
||||
static int
|
||||
apply_http(struct auth_xfer* xfr, struct auth_zone* z,
|
||||
struct sldns_buffer* scratch_buffer)
|
||||
int
|
||||
xfr_apply_http(uint8_t* name, size_t namelen, const char* host,
|
||||
const char* file, struct auth_chunk* chunk_list, struct auth_zone* z,
|
||||
struct sldns_buffer* scratch_buffer, struct auth_load_thread* thr)
|
||||
{
|
||||
/* parse data in chunks */
|
||||
/* parse RR's and read into memory. ignore $INCLUDE from the
|
||||
@@ -5216,52 +5283,24 @@ apply_http(struct auth_xfer* xfr, struct auth_zone* z,
|
||||
int ret;
|
||||
memset(&pstate, 0, sizeof(pstate));
|
||||
pstate.default_ttl = 3600;
|
||||
if(xfr->namelen < sizeof(pstate.origin)) {
|
||||
pstate.origin_len = xfr->namelen;
|
||||
memmove(pstate.origin, xfr->name, xfr->namelen);
|
||||
}
|
||||
|
||||
if(verbosity >= VERB_ALGO)
|
||||
verbose(VERB_ALGO, "http download %s of size %d",
|
||||
xfr->task_transfer->master->file,
|
||||
(int)chunklist_sum(xfr->task_transfer->chunks_first));
|
||||
if(xfr->task_transfer->chunks_first && verbosity >= VERB_ALGO) {
|
||||
char preview[1024];
|
||||
if(xfr->task_transfer->chunks_first->len+1 > sizeof(preview)) {
|
||||
memmove(preview, xfr->task_transfer->chunks_first->data,
|
||||
sizeof(preview)-1);
|
||||
preview[sizeof(preview)-1]=0;
|
||||
} else {
|
||||
memmove(preview, xfr->task_transfer->chunks_first->data,
|
||||
xfr->task_transfer->chunks_first->len);
|
||||
preview[xfr->task_transfer->chunks_first->len]=0;
|
||||
}
|
||||
log_info("auth zone http downloaded content preview: %s",
|
||||
preview);
|
||||
}
|
||||
|
||||
/* perhaps a little syntax check before we try to apply the data? */
|
||||
if(!http_zonefile_syntax_check(xfr, scratch_buffer)) {
|
||||
log_err("http download %s/%s does not contain a zonefile, "
|
||||
"but got '%s'", xfr->task_transfer->master->host,
|
||||
xfr->task_transfer->master->file,
|
||||
sldns_buffer_begin(scratch_buffer));
|
||||
return 0;
|
||||
if(namelen < sizeof(pstate.origin)) {
|
||||
pstate.origin_len = namelen;
|
||||
memmove(pstate.origin, name, namelen);
|
||||
}
|
||||
|
||||
auth_zone_clear_data(z);
|
||||
xfr->have_zone = 0;
|
||||
xfr->serial = 0;
|
||||
xfr->soa_zone_acquired = 0;
|
||||
xfr->num_ixfrs = 0;
|
||||
|
||||
chunk = xfr->task_transfer->chunks_first;
|
||||
chunk = chunk_list;
|
||||
chunk_pos = 0;
|
||||
pstate.lineno = 0;
|
||||
while(chunkline_get_line_collated(&chunk, &chunk_pos, scratch_buffer)) {
|
||||
/* process this line */
|
||||
pstate.lineno++;
|
||||
chunkline_newline_removal(scratch_buffer);
|
||||
if(thr && pstate.lineno % NUM_RECORDS_BEFORE_SIGNAL_CHECK == 0) {
|
||||
if(auth_load_thread_poll_for_quit(thr))
|
||||
return 0;
|
||||
}
|
||||
if(chunkline_is_comment_line_or_empty(scratch_buffer)) {
|
||||
continue;
|
||||
}
|
||||
@@ -5269,8 +5308,7 @@ apply_http(struct auth_xfer* xfr, struct auth_zone* z,
|
||||
if((ret=http_parse_origin(scratch_buffer, &pstate))!=0) {
|
||||
if(ret == 2) {
|
||||
verbose(VERB_ALGO, "error parsing ORIGIN on line [%s:%d] %s",
|
||||
xfr->task_transfer->master->file,
|
||||
pstate.lineno,
|
||||
file, pstate.lineno,
|
||||
sldns_buffer_begin(scratch_buffer));
|
||||
return 0;
|
||||
}
|
||||
@@ -5279,17 +5317,15 @@ apply_http(struct auth_xfer* xfr, struct auth_zone* z,
|
||||
if((ret=http_parse_ttl(scratch_buffer, &pstate))!=0) {
|
||||
if(ret == 2) {
|
||||
verbose(VERB_ALGO, "error parsing TTL on line [%s:%d] %s",
|
||||
xfr->task_transfer->master->file,
|
||||
pstate.lineno,
|
||||
file, pstate.lineno,
|
||||
sldns_buffer_begin(scratch_buffer));
|
||||
return 0;
|
||||
}
|
||||
continue; /* $TTL has been handled */
|
||||
}
|
||||
if(!http_parse_add_rr(xfr, z, scratch_buffer, &pstate)) {
|
||||
if(!http_parse_add_rr(host, file, z, scratch_buffer, &pstate)) {
|
||||
verbose(VERB_ALGO, "error parsing line [%s:%d] %s",
|
||||
xfr->task_transfer->master->file,
|
||||
pstate.lineno,
|
||||
file, pstate.lineno,
|
||||
sldns_buffer_begin(scratch_buffer));
|
||||
return 0;
|
||||
}
|
||||
@@ -5297,6 +5333,28 @@ apply_http(struct auth_xfer* xfr, struct auth_zone* z,
|
||||
return 1;
|
||||
}
|
||||
|
||||
/** apply HTTP to zone in memory. z is locked. false on failure(mallocfail) */
|
||||
static int
|
||||
apply_http(struct auth_xfer* xfr, struct auth_zone* z,
|
||||
struct sldns_buffer* scratch_buffer)
|
||||
{
|
||||
xfr_http_preview(xfr->task_transfer->master->file,
|
||||
xfr->task_transfer->chunks_first);
|
||||
if(!xfr_http_syntax_check(xfr->name, xfr->namelen, xfr->dclass,
|
||||
xfr->task_transfer->master->host,
|
||||
xfr->task_transfer->master->file,
|
||||
xfr->task_transfer->chunks_first, scratch_buffer))
|
||||
return 0;
|
||||
xfr->have_zone = 0;
|
||||
xfr->serial = 0;
|
||||
xfr->soa_zone_acquired = 0;
|
||||
xfr->num_ixfrs = 0;
|
||||
return xfr_apply_http(xfr->name, xfr->namelen,
|
||||
xfr->task_transfer->master->host,
|
||||
xfr->task_transfer->master->file,
|
||||
xfr->task_transfer->chunks_first, z, scratch_buffer, NULL);
|
||||
}
|
||||
|
||||
/** write http chunks to zonefile to create downloaded file */
|
||||
static int
|
||||
auth_zone_write_chunks(struct auth_xfer* xfr, const char* fname)
|
||||
@@ -6289,50 +6347,13 @@ xfer_link_data(sldns_buffer* pkt, struct auth_xfer* xfr)
|
||||
return 1;
|
||||
}
|
||||
|
||||
/** task transfer. the list of data is complete. process it and if failed
|
||||
* move to next master, if succeeded, end the task transfer */
|
||||
/** task transfer, process the failure to process the zone transfer.
|
||||
* It continues with the task transfer, possibly. */
|
||||
static void
|
||||
process_list_end_transfer(struct auth_xfer* xfr, struct module_env* env)
|
||||
xfr_process_transfer_failed(struct auth_xfer* xfr, struct module_env* env,
|
||||
int ixfr_fail)
|
||||
{
|
||||
int ixfr_fail = 0;
|
||||
if(xfr_process_chunk_list(xfr, env, &ixfr_fail)) {
|
||||
/* it worked! */
|
||||
auth_chunks_delete(xfr->task_transfer);
|
||||
|
||||
/* we fetched the zone, move to wait task */
|
||||
xfr_transfer_disown(xfr);
|
||||
|
||||
if(xfr->notify_received && (!xfr->notify_has_serial ||
|
||||
(xfr->notify_has_serial &&
|
||||
xfr_serial_means_update(xfr, xfr->notify_serial)))) {
|
||||
uint32_t sr = xfr->notify_serial;
|
||||
int has_sr = xfr->notify_has_serial;
|
||||
/* we received a notify while probe/transfer was
|
||||
* in progress. start a new probe and transfer */
|
||||
xfr->notify_received = 0;
|
||||
xfr->notify_has_serial = 0;
|
||||
xfr->notify_serial = 0;
|
||||
if(!xfr_start_probe(xfr, env, NULL)) {
|
||||
/* if we couldn't start it, already in
|
||||
* progress; restore notify serial,
|
||||
* while xfr still locked */
|
||||
xfr->notify_received = 1;
|
||||
xfr->notify_has_serial = has_sr;
|
||||
xfr->notify_serial = sr;
|
||||
lock_basic_unlock(&xfr->lock);
|
||||
}
|
||||
return;
|
||||
} else {
|
||||
/* pick up the nextprobe task and wait (normail wait time) */
|
||||
if(xfr->task_nextprobe->worker == NULL)
|
||||
xfr_set_timeout(xfr, env, 0, 0);
|
||||
}
|
||||
lock_basic_unlock(&xfr->lock);
|
||||
return;
|
||||
}
|
||||
/* processing failed */
|
||||
/* when done, delete data from list */
|
||||
auth_chunks_delete(xfr->task_transfer);
|
||||
if(ixfr_fail) {
|
||||
xfr->task_transfer->ixfr_fail = 1;
|
||||
} else {
|
||||
@@ -6341,6 +6362,238 @@ process_list_end_transfer(struct auth_xfer* xfr, struct module_env* env)
|
||||
xfr_transfer_nexttarget_or_end(xfr, env);
|
||||
}
|
||||
|
||||
/** task transfer, process the success to process the zone transfer.
|
||||
* It continues with the task probe, possibly, due to notify. Or nextprobe. */
|
||||
static void
|
||||
xfr_process_transfer_success(struct auth_xfer* xfr, struct module_env* env)
|
||||
{
|
||||
/* we fetched the zone, move to wait task */
|
||||
xfr_transfer_disown(xfr);
|
||||
|
||||
if(xfr->notify_received && (!xfr->notify_has_serial ||
|
||||
(xfr->notify_has_serial &&
|
||||
xfr_serial_means_update(xfr, xfr->notify_serial)))) {
|
||||
uint32_t sr = xfr->notify_serial;
|
||||
int has_sr = xfr->notify_has_serial;
|
||||
/* we received a notify while probe/transfer was
|
||||
* in progress. start a new probe and transfer */
|
||||
xfr->notify_received = 0;
|
||||
xfr->notify_has_serial = 0;
|
||||
xfr->notify_serial = 0;
|
||||
if(!xfr_start_probe(xfr, env, NULL)) {
|
||||
/* if we couldn't start it, already in
|
||||
* progress; restore notify serial,
|
||||
* while xfr still locked */
|
||||
xfr->notify_received = 1;
|
||||
xfr->notify_has_serial = has_sr;
|
||||
xfr->notify_serial = sr;
|
||||
lock_basic_unlock(&xfr->lock);
|
||||
}
|
||||
return;
|
||||
} else {
|
||||
/* pick up the nextprobe task and wait (normail wait time) */
|
||||
if(xfr->task_nextprobe->worker == NULL)
|
||||
xfr_set_timeout(xfr, env, 0, 0);
|
||||
}
|
||||
lock_basic_unlock(&xfr->lock);
|
||||
}
|
||||
|
||||
/** task transfer. the list of data is complete. process it and if failed
|
||||
* move to next master, if succeeded, end the task transfer */
|
||||
static void
|
||||
process_list_end_transfer(struct auth_xfer* xfr, struct module_env* env)
|
||||
{
|
||||
int ixfr_fail = 0;
|
||||
if(env->cfg->auth_task_threads != 0 /* auth load enabled */) {
|
||||
if(auth_load_info_grab_thread(env)) {
|
||||
/* Create auth load thread task to process the data. */
|
||||
if(auth_load_add_task_xfr(xfr, env->worker)) {
|
||||
/* Task is created, wait for it to be done. The worker
|
||||
* is signalled with the result. */
|
||||
/* When it is done, the xfr_process_load_end_transfer
|
||||
* routine is called. */
|
||||
lock_basic_unlock(&xfr->lock);
|
||||
return;
|
||||
}
|
||||
auth_load_info_release_thread(env);
|
||||
} else {
|
||||
verbose(VERB_ALGO, "Auth load threads at capacity, not creating a new thread");
|
||||
}
|
||||
} else if(xfr_process_chunk_list(xfr, env, &ixfr_fail)) {
|
||||
/* it worked! */
|
||||
auth_chunks_delete(xfr->task_transfer);
|
||||
xfr_process_transfer_success(xfr, env);
|
||||
return;
|
||||
}
|
||||
/* when done, delete data from list */
|
||||
auth_chunks_delete(xfr->task_transfer);
|
||||
xfr_process_transfer_failed(xfr, env, ixfr_fail);
|
||||
}
|
||||
|
||||
/** deal with successful load end, starts holding xfr lock,
|
||||
* ends holding xfr lock. */
|
||||
static int
|
||||
xfr_process_loaded_transfer(struct auth_xfer* xfr, struct module_env* env,
|
||||
int* gone, struct timeval* time_taken, size_t mem_used,
|
||||
size_t chunks_total)
|
||||
{
|
||||
struct auth_zone* z = NULL;
|
||||
verbose(VERB_ALGO, "xfr_process_loaded_transfer");
|
||||
lock_basic_unlock(&xfr->lock);
|
||||
if(!xfr_process_reacquire_locks(xfr, env, &z)) {
|
||||
/* the zone is gone, ignore xfr results */
|
||||
*gone = 1;
|
||||
return 0;
|
||||
}
|
||||
/* holding xfr and z locks */
|
||||
|
||||
verbose(VERB_ALGO, "xfr_process_loaded_transfer: num_ixfrs");
|
||||
if(xfr->task_transfer->master->http) {
|
||||
xfr->num_ixfrs = 0;
|
||||
xfr->have_zone = 0;
|
||||
xfr->serial = 0;
|
||||
} else if(xfr->task_transfer->on_ixfr &&
|
||||
!xfr->task_transfer->on_ixfr_is_axfr) {
|
||||
xfr->num_ixfrs++;
|
||||
} else {
|
||||
/* AXFR */
|
||||
xfr->num_ixfrs = 0;
|
||||
xfr->have_zone = 0;
|
||||
xfr->serial = 0;
|
||||
}
|
||||
|
||||
verbose(VERB_ALGO, "xfr_process_loaded_transfer: find_soa");
|
||||
xfr->zone_expired = 0;
|
||||
z->zone_expired = 0;
|
||||
if(!xfr_find_soa(z, xfr)) {
|
||||
verbose(VERB_ALGO, "xfr from %s: no SOA in zone after update"
|
||||
" (or malformed RR)", xfr->task_transfer->master->host);
|
||||
return 0;
|
||||
}
|
||||
z->soa_zone_acquired = *env->now;
|
||||
xfr->soa_zone_acquired = *env->now;
|
||||
xfr->is_rpz = (z->rpz!=NULL);
|
||||
|
||||
/* release xfr lock while verifying zonemd because it may have
|
||||
* to spawn lookups in the state machines */
|
||||
lock_basic_unlock(&xfr->lock);
|
||||
verbose(VERB_ALGO, "xfr_process_loaded_transfer: verify_zonemd");
|
||||
/* holding z lock */
|
||||
auth_zone_verify_zonemd(z, env, &env->mesh->mods, NULL, 0, 0);
|
||||
if(z->zone_expired) {
|
||||
char zname[LDNS_MAX_DOMAINLEN];
|
||||
/* ZONEMD must have failed */
|
||||
/* reacquire locks, so we hold xfr lock on exit of routine,
|
||||
* and both xfr and z again after releasing xfr for potential
|
||||
* state machine mesh callbacks */
|
||||
lock_rw_unlock(&z->lock);
|
||||
if(!xfr_process_reacquire_locks(xfr, env, &z)) {
|
||||
*gone = 1;
|
||||
return 0;
|
||||
}
|
||||
dname_str(xfr->name, zname);
|
||||
verbose(VERB_ALGO, "xfr from %s: ZONEMD failed for %s, transfer is failed", xfr->task_transfer->master->host, zname);
|
||||
xfr->zone_expired = 1;
|
||||
lock_rw_unlock(&z->lock);
|
||||
return 0;
|
||||
}
|
||||
/* reacquire locks, so we hold xfr lock on exit of routine,
|
||||
* and both xfr and z again after releasing xfr for potential
|
||||
* state machine mesh callbacks */
|
||||
lock_rw_unlock(&z->lock);
|
||||
if(!xfr_process_reacquire_locks(xfr, env, &z)) {
|
||||
*gone = 1;
|
||||
return 0;
|
||||
}
|
||||
/* holding xfr and z locks */
|
||||
|
||||
verbose(VERB_ALGO, "xfr_process_loaded_transfer: lease");
|
||||
if(xfr->have_zone)
|
||||
xfr->lease_time = *env->now;
|
||||
/* unlock */
|
||||
lock_rw_unlock(&z->lock);
|
||||
|
||||
if(verbosity >= VERB_QUERY && xfr->have_zone) {
|
||||
char zname[LDNS_MAX_DOMAINLEN];
|
||||
dname_str(xfr->name, zname);
|
||||
verbose(VERB_QUERY, "auth zone %s updated to serial %u",
|
||||
zname, (unsigned)xfr->serial);
|
||||
if(verbosity >= 8) {
|
||||
char taskline[1024];
|
||||
if(xfr->task_transfer->master->http) {
|
||||
snprintf(taskline, sizeof(taskline),
|
||||
"http transfer from %s/%s of %lu "
|
||||
"bytes serial %u",
|
||||
xfr->task_transfer->master->host,
|
||||
xfr->task_transfer->master->file,
|
||||
(unsigned long)chunks_total,
|
||||
(unsigned)xfr->serial);
|
||||
} else if(xfr->task_transfer->on_ixfr &&
|
||||
!xfr->task_transfer->on_ixfr_is_axfr) {
|
||||
snprintf(taskline, sizeof(taskline),
|
||||
"IXFR transfer from %s of %lu "
|
||||
"bytes serial %u",
|
||||
xfr->task_transfer->master->host,
|
||||
(unsigned long)chunks_total,
|
||||
(unsigned)xfr->serial);
|
||||
} else {
|
||||
snprintf(taskline, sizeof(taskline),
|
||||
"AXFR transfer from %s of %lu "
|
||||
"bytes serial %u",
|
||||
xfr->task_transfer->master->host,
|
||||
(unsigned long)chunks_total,
|
||||
(unsigned)xfr->serial);
|
||||
}
|
||||
verbose(VERB_ALGO, "auth zone %s details %s thread "
|
||||
"time was %d.%6.6ds and used %lu bytes of "
|
||||
"memory", zname, taskline,
|
||||
(int)time_taken->tv_sec,
|
||||
(int)time_taken->tv_usec,
|
||||
(unsigned long)mem_used);
|
||||
}
|
||||
}
|
||||
verbose(VERB_ALGO, "xfr_process_loaded_transfer: write after update");
|
||||
/* see if we need to write to a zonefile */
|
||||
xfr_write_after_update(xfr, env);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
void xfr_process_load_end_transfer(struct auth_xfer* xfr,
|
||||
struct module_env* env, uint8_t status, int ixfr_fail,
|
||||
struct timeval* time_taken, size_t mem_used, size_t chunks_total,
|
||||
struct auth_chunk* chunk_list)
|
||||
{
|
||||
/* Chunks are put here for the auth zone write for the http case. */
|
||||
verbose(VERB_ALGO, "xfr_process_load_end_transfer");
|
||||
xfr->task_transfer->chunks_first = chunk_list;
|
||||
if(status) {
|
||||
int gone = 0;
|
||||
if(!xfr_process_loaded_transfer(xfr, env, &gone, time_taken,
|
||||
mem_used, chunks_total)) {
|
||||
status = 0;
|
||||
if(gone) {
|
||||
/* the zone is gone from the authzones. */
|
||||
lock_basic_unlock(&xfr->lock);
|
||||
auth_chunks_delete(xfr->task_transfer);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
verbose(VERB_ALGO, "xfr_process_load_end_transfer: chunks delete");
|
||||
auth_chunks_delete(xfr->task_transfer);
|
||||
|
||||
if(status) {
|
||||
/* it worked! */
|
||||
verbose(VERB_ALGO, "xfr_process_load_end_transfer: success");
|
||||
xfr_process_transfer_success(xfr, env);
|
||||
return;
|
||||
}
|
||||
/* The transfer failed */
|
||||
verbose(VERB_ALGO, "xfr_process_load_end_transfer: failed");
|
||||
xfr_process_transfer_failed(xfr, env, ixfr_fail);
|
||||
}
|
||||
|
||||
/** callback for the task_transfer timer */
|
||||
void
|
||||
auth_xfer_transfer_timer_callback(void* arg)
|
||||
@@ -8937,7 +9190,7 @@ auth_data_get_mem(struct auth_data* node)
|
||||
}
|
||||
|
||||
/** Get memory usage of auth zone */
|
||||
static size_t
|
||||
size_t
|
||||
auth_zone_get_mem(struct auth_zone* z)
|
||||
{
|
||||
size_t m = sizeof(*z) + z->namelen;
|
||||
|
||||
@@ -65,6 +65,7 @@ struct auth_probe;
|
||||
struct auth_transfer;
|
||||
struct auth_master;
|
||||
struct auth_chunk;
|
||||
struct auth_load_thread;
|
||||
|
||||
/**
|
||||
* Authoritative zones, shared.
|
||||
@@ -857,4 +858,42 @@ int chunkline_count_parens(struct sldns_buffer* buf, size_t start);
|
||||
/** Clear data in auth zone */
|
||||
void auth_zone_clear_data(struct auth_zone* z);
|
||||
|
||||
/** Get memory usage of auth zone */
|
||||
size_t auth_zone_get_mem(struct auth_zone* z);
|
||||
|
||||
/** create domain with the given name */
|
||||
struct auth_data* az_domain_create(struct auth_zone* z, uint8_t* nm,
|
||||
size_t nmlen);
|
||||
|
||||
/** helper traverse to delete zones */
|
||||
void auth_data_del(rbnode_type* n, void* arg);
|
||||
|
||||
/** Handle the end of an auth load task. */
|
||||
void xfr_process_load_end_transfer(struct auth_xfer* xfr,
|
||||
struct module_env* env, uint8_t status, int ixfr_fail,
|
||||
struct timeval* time_taken, size_t mem_used, size_t chunks_total,
|
||||
struct auth_chunk* chunk_list);
|
||||
|
||||
/** Log preview of http transfer */
|
||||
void xfr_http_preview(const char* file, struct auth_chunk* chunk_list);
|
||||
|
||||
/** Check syntax of first part of the http download */
|
||||
int xfr_http_syntax_check(uint8_t* name, size_t namelen, uint16_t dclass,
|
||||
const char* host, const char* file, struct auth_chunk* chunk_list,
|
||||
struct sldns_buffer* scratch_buffer);
|
||||
|
||||
/** Apply http transfer to auth_zone */
|
||||
int xfr_apply_http(uint8_t* name, size_t namelen, const char* host,
|
||||
const char* file, struct auth_chunk* chunk_list, struct auth_zone* z,
|
||||
struct sldns_buffer* scratch_buffer, struct auth_load_thread* thr);
|
||||
|
||||
/** Apply IXFR transfer to auth_zone */
|
||||
int xfr_apply_ixfr(struct auth_chunk* chunk_list, uint32_t xfr_serial,
|
||||
struct auth_zone* z, struct sldns_buffer* scratch_buffer,
|
||||
struct auth_load_thread* thr);
|
||||
|
||||
/** Apply AXFR transfer to auth_zone */
|
||||
int xfr_apply_axfr(struct auth_chunk* chunk_list, struct auth_zone* z,
|
||||
struct sldns_buffer* scratch_buffer, struct auth_load_thread* thr);
|
||||
|
||||
#endif /* SERVICES_AUTHZONE_H */
|
||||
|
||||
@@ -264,6 +264,9 @@ setup_config(FILE* in, int* lineno, int* pass_argc, char* pass_argv[])
|
||||
fprintf(cfg, " pidfile: \"\"\n");
|
||||
fprintf(cfg, " val-log-level: 2\n");
|
||||
fprintf(cfg, " log-servfail: yes\n");
|
||||
/* the extra thread needs pipe communication that is not available
|
||||
* from fake_event calls. So auth-task-threads: 0 disables them. */
|
||||
fprintf(cfg, " auth-task-threads: 0\n");
|
||||
fprintf(cfg, "remote-control: control-enable: no\n");
|
||||
while(fgets(line, MAX_LINE_LEN-1, in)) {
|
||||
parse = line;
|
||||
|
||||
+18
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
|
||||
@@ -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
@@ -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:
|
||||
@@ -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
@@ -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
|
||||
@@ -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
@@ -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
@@ -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-----
|
||||
@@ -243,6 +243,7 @@ config_create(void)
|
||||
cfg->views = NULL;
|
||||
cfg->acls = NULL;
|
||||
cfg->tcp_connection_limits = NULL;
|
||||
cfg->auth_task_threads = 0;
|
||||
cfg->harden_short_bufsize = 1;
|
||||
cfg->harden_large_queries = 0;
|
||||
cfg->harden_glue = 1;
|
||||
@@ -457,6 +458,7 @@ struct config_file* config_create_forlib(void)
|
||||
cfg->neg_cache_size = 100 * 1024;
|
||||
cfg->donotquery_localhost = 0; /* allow, so that you can ask a
|
||||
forward nameserver running on localhost */
|
||||
cfg->auth_task_threads = 0; /* no extra threads to spawn in library */
|
||||
cfg->val_log_level = 2; /* to fill why_bogus with */
|
||||
cfg->val_log_squelch = 1;
|
||||
cfg->minimal_responses = 0;
|
||||
@@ -742,6 +744,7 @@ int config_set_option(struct config_file* cfg, const char* opt,
|
||||
else S_SIZET_OR_ZERO("unwanted-reply-threshold:", unwanted_threshold)
|
||||
else S_STRLIST("private-address:", private_address)
|
||||
else S_STRLIST("private-domain:", private_domain)
|
||||
else S_NUMBER_OR_ZERO("auth-task-threads:", auth_task_threads)
|
||||
else S_YNO("do-not-query-localhost:", donotquery_localhost)
|
||||
else S_STRLIST("do-not-query-address:", donotqueryaddrs)
|
||||
else S_STRLIST("auto-trust-anchor-file:", auto_trust_anchor_file_list)
|
||||
@@ -1284,6 +1287,7 @@ config_get_option(struct config_file* cfg, const char* opt,
|
||||
else O_LST(opt, "root-hints", root_hints)
|
||||
else O_LS2(opt, "access-control", acls)
|
||||
else O_LS2(opt, "tcp-connection-limit", tcp_connection_limits)
|
||||
else O_DEC(opt, "auth-task-threads", auth_task_threads)
|
||||
else O_LST(opt, "do-not-query-address", donotqueryaddrs)
|
||||
else O_LST(opt, "private-address", private_address)
|
||||
else O_LST(opt, "private-domain", private_domain)
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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) }
|
||||
|
||||
@@ -193,6 +193,7 @@ extern struct config_parser_state* cfg_parser;
|
||||
%token VAR_FALLBACK_ENABLED VAR_TLS_ADDITIONAL_PORT VAR_LOW_RTT VAR_LOW_RTT_PERMIL
|
||||
%token VAR_FAST_SERVER_PERMIL VAR_FAST_SERVER_NUM
|
||||
%token VAR_ALLOW_NOTIFY VAR_TLS_WIN_CERT VAR_TCP_CONNECTION_LIMIT
|
||||
%token VAR_AUTH_TASK_THREADS
|
||||
%token VAR_ANSWER_COOKIE VAR_COOKIE_SECRET VAR_IP_RATELIMIT_COOKIE
|
||||
%token VAR_FORWARD_NO_CACHE VAR_STUB_NO_CACHE VAR_LOG_SERVFAIL VAR_DENY_ANY
|
||||
%token VAR_UNKNOWN_SERVER_TIME_LIMIT VAR_LOG_TAG_QUERYREPLY
|
||||
@@ -343,6 +344,7 @@ content_server: server_num_threads | server_verbosity | server_port |
|
||||
server_tls_cert_bundle | server_tls_additional_port | server_low_rtt |
|
||||
server_fast_server_permil | server_fast_server_num | server_tls_win_cert |
|
||||
server_tcp_connection_limit | server_log_servfail | server_deny_any |
|
||||
server_auth_task_threads |
|
||||
server_unknown_server_time_limit | server_log_tag_queryreply |
|
||||
server_discard_timeout | server_wait_limit | server_wait_limit_cookie |
|
||||
server_wait_limit_netblock | server_wait_limit_cookie_netblock |
|
||||
@@ -4239,6 +4241,15 @@ server_tcp_connection_limit: VAR_TCP_CONNECTION_LIMIT STRING_ARG STRING_ARG
|
||||
}
|
||||
}
|
||||
;
|
||||
server_auth_task_threads: VAR_AUTH_TASK_THREADS STRING_ARG
|
||||
{
|
||||
OUTYY(("P(server_auth_task_threads:%s)\n", $2));
|
||||
if(atoi($2) == 0 && strcmp($2, "0") != 0)
|
||||
yyerror("number expected");
|
||||
else cfg_parser->cfg->auth_task_threads = atoi($2);
|
||||
free($2);
|
||||
}
|
||||
;
|
||||
server_answer_cookie: VAR_ANSWER_COOKIE STRING_ARG
|
||||
{
|
||||
OUTYY(("P(server_answer_cookie:%s)\n", $2));
|
||||
|
||||
@@ -72,6 +72,7 @@
|
||||
#include "libunbound/libworker.h"
|
||||
#include "libunbound/context.h"
|
||||
#include "libunbound/worker.h"
|
||||
#include "libunbound/authload.h"
|
||||
#include "util/tube.h"
|
||||
#include "util/config_file.h"
|
||||
#include "daemon/remote.h"
|
||||
@@ -191,6 +192,7 @@ fptr_whitelist_event(void (*fptr)(int, short, void *))
|
||||
else if(fptr == &comm_point_doq_callback) return 1;
|
||||
#endif
|
||||
else if(fptr == &fast_reload_service_cb) return 1;
|
||||
else if(fptr == &worker_auth_load_service_cb) return 1;
|
||||
#ifdef USE_DNSTAP
|
||||
else if(fptr == &dtio_output_cb) return 1;
|
||||
else if(fptr == &dtio_cmd_cb) return 1;
|
||||
|
||||
+360
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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 */
|
||||
|
||||
Reference in New Issue
Block a user