Fast Reload Option (#1042)

* - fast-reload, add unbound-control fast_reload

* - fast-reload, make a thread to service the unbound-control command.

* - fast-reload, communication sockets for information transfer.

* - fast-reload, fix compile for unbound-dnstap-socket.

* - fast-reload, set nonblocking communication to keep the server thread
  responding to DNS requests.

* - fast-reload, poll routine to test for readiness, timeout fails connection.

* - fast-reload, detect loop in sock_poll_timeout routine.

* - fast-reload, send done and exited notification.

* - fast-reload, defines for constants in ipc.

* - fast-reload, ipc socket recv and send resists partial reads and writes and
  can continue byte by byte. Also it can continue after an interrupt.

* - fast-reload, send exit command to thread when done.

* - fast-reload, output strings for client on string list.

* - fast-reload, add newline to terminal output.

* - fast-reload, send client string to remote client.

* - fast-reload, better debug output.

* - fast-reload, print queue structure, for output to the remote client.

* - fast-reload, move print items to print queue from fast_reload_thread struct.

* - fast-reload, keep list of pending print queue items in daemon struct.

* - fast-reload, comment explains in_list for printq to print remainder.

* - fast-reload, unit test testdata/fast_reload_thread.tdir that tests the
  thread output.

* - fast-reload, fix test link for fast_reload_printq_list_delete function.

* - fast-reload, reread config file from disk.

* - fast-reload, unshare forwards, making the structure locked, with an rwlock.

* - fast-reload, for nonthreaded, the unbound-control commands forward,
  forward_add and forward_delete should be distributed to other processes,
  but when threaded, they should not be distributed to other threads because
  the structure is not thread specific any more.

* - fast-reload, unshared stub hints, making the structure locked, with an rwlock.

* - fast-reload, helpful comments for hints lookup function return value.

* - fast-reload, fix bug in fast reload printout, the strlist appendlist routine,
  and printout time statistics after the reload is done.

* - fast-reload, keep track of reloadtime and deletestime and print them.

* - fast-reload, keep track of constructtime and print it.

* - fast-reload, construct new items.

* - fast-reload, better comment.

* - fast-reload, reload the config and swap trees for forwards and stub hints.

* - fast-reload, in forwards_swap_tree set protection of trees with locks.

* - fast-reload, in hints_swap_tree also swap the node count of the trees.

* - fast-reload, reload ipc to stop and start threads.

* - fast-reload, unused forward declarations removed.

* - fast-reload, unit test that fast reload works with forwards and stubs.

* - fast-reload, fix clang analyzer warnings.

* - fast-reload, small documentation entry in unbound-control -h output.

* - fast-reload, printout memory use by fast reload, in bytes.

* - fast-reload, compile without threads.

* - fast-reload, document fast_reload in man page.

* - fast-reload, print ok when done successfully.

* - fast-reload, option for fast-reload commandline, +v verbosity option,
  with timing and memory use output.

* - fast-reload, option for fast-reload commandline, +p does not pause threads.

* - fast-reload, option for fast-reload commandline, +d drops mesh queries.

* - fast-reload, fix to poll every thread with nopause to make certain that
  resources are not held by the threads and can be deleted.

* - fast-reload, fix to use atomic store for config variables with nopause.

* - fast-reload, reload views.

* - fast-reload, when tag defines are different, it drops the queries.

* - fast-reload, fix tag define check.

* - fast-reload, document that tag change causes drop of queries.

* - fast-reload, fix space in documentation man page.

* - fast-reload, copy respip client information to query state, put views tree
  in module env for lookup.

* - fast-reload, nicer respip view comparison.

* - fast-reload, respip global set is in module env.

* - fast-reload, document that respip_client_info acl info is copied.

* - fast-reload, reload the respip_set.

* - fast-reload, document no pause and pick up of use_response_ip boolean.

* - fast-reload, fix test compile.

* - fast-reload, reload local zones.

* Update locking management for iter_fwd and iter_hints methods. (#1054)

fast reload, move most of the locking management to iter_fwd and
iter_hints methods. The caller still has the ability to handle its
own locking, if desired, for atomic operations on sets of different
structs.

Co-authored-by: Wouter Wijngaards <wcawijngaards@users.noreply.github.com>

* - fast-reload, reload access-control.

* - fast-reload, reload access control interface, such as interface-action.

* - fast-reload, reload tcp-connection-limit.

* - fast-reload, improve comments on acl_list and tcl_list swap tree.

* - fast-reload, fixup references to old tcp connection limits in open tcp
  connections.

* - fast-reload, fixup to clean tcp connection also for different linked order.

* - fast-reload, if no tcp connection limits existed, no need to remove
  references for that.

* - fast-reload, document more options that work and do not work.

* - fast-reload, reload auth_zone and rpz data.

* - fast-reload, fix auth_zones_get_mem.

* - fast-reload, fix compilation of testbound for the new comm_timer_get_mem
  reference in remote control.

* - fast-reload, change use_rpz with reload.

* - fast-reload, list changes in auth zones and stop zonemd callbacks for
  deleted auth zones.

* - fast-reload, note xtree is not swapped, and why it is not swapped.

* - fast-reload, for added auth zones, pick up zone transfer and zonemd tasks.

* - fast-reload, unlock xfr when done with transfer pick up.

* - fast-reload, unlock z when picking up the xfr for it during transfer task
  pick up.

* - fast-reload, pick up task changes for added, deleted and modified auth zones.

* - fast-reload, remove xfr of auth zone deletion without tasks.

* - fast-reload, pick up zone transfer config.

* - fast-reload, the main worker thread picks up the transfer tasks and also
  performs setup of the xfer struct.

* - fast-reload, keep writelock on newzone when auth zone changes.

* - fast-reload, change cachedb_enabled setting.

* - fast-reload, pick up edns-strings config.

* - fast-reload, note that settings are not updated.

* - fast-reload, pick up dnstap config.

* - fast-reload, dnstap options that need to be loaded without +p.

* - fast-reload, fix auth zone reload

* - fast-reload, remove debug for auth zone test.

* - fast-reload, fix auth zone reload with zone transfer.

* - fast-reload, fix auth zone reload lock order.

* - fast-reload, remove debug from fast reload test.

* - fast-reload, remove unused function.

* - fast-reload, fix the worker trust anchor probe timer lock acquisition in
  the probe answer callback routine for trust anchor probes.

* - fast-reload, reload trust anchors.

* - fast-reload, fix trust anchor reload lock on autr global data and test
  for trust anchor reload.

* - fast-reload, adjust cache sizes.

* - fast-reload, reload cache sizes when changed.

* - fast-reload, reload validator env changes.

* - fast-reload, reload mesh changes.

* - fast-reload, check for incompatible changes.

* - fast-reload, improve error text for incompatible change.

* - fast-reload, fix check config option compatibility.

* - fast-reload, improve error text for nopause change.

* - fast-reload, fix spelling of incompatible options.

* - fast-reload, reload target-fetch-policy, outbound-msg-retry, max-sent-count
  and max-query-restarts.

* - fast-reload, check nopause config change for target-fetch-policy.

* - fast-reload, reload do-not-query-address, private-address and capt-exempt.

* - fast-reload, check nopause config change for do-not-query-address,
  private-address and capt-exempt.

* - fast-reload, check fast reload not possible due to interface and
  outgoing-interface changes.

* - fast-reload, reload nat64 settings.

* - fast-reload, reload settings stored in the infra structure.

* - fast-reload, fix modstack lookup and remove outgoing-range check.

* - fast-reload, more explanation for config parse failure.

* - fast-reload, reload worker outside network changes.

* - fast-reload, detect incompatible changes in network settings.

* fast-reload, commit test files.

* - fast-reload, fix warnings for call types in windows compile.

* - fast-reload, fix warnings and comm_point_internal for tcp wouldblock calls.

* - fast-reload, extend lock checks for repeat thread ids.

* - fast-reload, additional test cases, cache change and tag changes.

* - fast-reload, fix documentation for auth_zone_verify_zonemd_with_key.

* - fast-reload, fix copy_cfg type casts and memory leak on config parse failure.

* - fast-reload, fix use of WSAPoll.

* Review comments for the fast reload feature (#1259)

* - fast-reload review, respip set can be null from a view.

* - fast-reload review, typos.

* - fast-reload review, keep clang static analyzer happy.

* - fast-reload review, don't forget to copy tag_actions.

* - fast-reload review, less indentation.

* - fast-reload review, don't leak respip_actions when reloading.

* - fast-reload review, protect NULL pointer dereference in get_mem
  functions.

* - fast-reload review, add fast_reload_most_options.tdir to test most
  options with high verbosity when fast reloading.

* - fast-reload review, don't skip new line on long error printouts.

* - fast-reload review, typo.

* - fast-reload review, use new_z for consistency.

* - fast-reload review, nit for unlock ordering to make eye comparison
  with the lock counterpart easier.

* - fast-reload review, in case of error the sockets are already closed.

* - fast-reload review, identation.

* - fast-reload review, add static keywords.

* - fast-reload review, update unbound-control usage text.

* - fast-reload review, updates to the man page.

* - fast-reload, the fast-reload command is experimental.

* - fast-reload, fix compile of doqclient for fast reload functions.

* Changelog comment for #1042
- Merge #1042: Fast Reload. The unbound-control fast_reload is added.
  It reads changed config in a thread, then only briefly pauses the
  service threads, that keep running. DNS service is only interrupted
  briefly, less than a second.

---------

Co-authored-by: Yorgos Thessalonikefs <yorgos@nlnetlabs.nl>
This commit is contained in:
Wouter Wijngaards 2025-03-31 15:25:24 +02:00 committed by GitHub
parent 914cef75f9
commit 218f5cfc92
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
90 changed files with 7471 additions and 251 deletions

View file

@ -881,7 +881,7 @@ view.lo view.o: $(srcdir)/services/view.c config.h $(srcdir)/services/view.h $(s
$(srcdir)/util/locks.h $(srcdir)/util/log.h $(srcdir)/services/localzone.h $(srcdir)/util/storage/dnstree.h \
$(srcdir)/util/module.h $(srcdir)/util/storage/lruhash.h $(srcdir)/util/data/msgreply.h \
$(srcdir)/util/data/packed_rrset.h $(srcdir)/util/data/msgparse.h $(srcdir)/sldns/pkthdr.h \
$(srcdir)/sldns/rrdef.h $(srcdir)/sldns/sbuffer.h $(srcdir)/util/config_file.h
$(srcdir)/sldns/rrdef.h $(srcdir)/sldns/sbuffer.h $(srcdir)/util/config_file.h $(srcdir)/respip/respip.h
rpz.lo rpz.o: $(srcdir)/services/rpz.c config.h $(srcdir)/services/rpz.h $(srcdir)/services/localzone.h \
$(srcdir)/util/rbtree.h $(srcdir)/util/locks.h $(srcdir)/util/log.h $(srcdir)/util/storage/dnstree.h \
$(srcdir)/util/module.h $(srcdir)/util/storage/lruhash.h $(srcdir)/util/data/msgreply.h \
@ -977,7 +977,7 @@ fptr_wlist.lo fptr_wlist.o: $(srcdir)/util/fptr_wlist.c config.h $(srcdir)/util/
$(srcdir)/validator/val_nsec3.h $(srcdir)/validator/val_sigcrypt.h $(srcdir)/validator/val_kentry.h \
$(srcdir)/validator/val_neg.h $(srcdir)/validator/autotrust.h $(srcdir)/libunbound/libworker.h \
$(srcdir)/libunbound/context.h $(srcdir)/util/alloc.h $(srcdir)/libunbound/unbound-event.h \
$(srcdir)/libunbound/worker.h
$(srcdir)/libunbound/worker.h $(srcdir)/daemon/remote.h
locks.lo locks.o: $(srcdir)/util/locks.c config.h $(srcdir)/util/locks.h $(srcdir)/util/log.h
log.lo log.o: $(srcdir)/util/log.c config.h $(srcdir)/util/log.h $(srcdir)/util/locks.h $(srcdir)/sldns/sbuffer.h
mini_event.lo mini_event.o: $(srcdir)/util/mini_event.c config.h $(srcdir)/util/mini_event.h $(srcdir)/util/rbtree.h \
@ -1313,7 +1313,10 @@ remote.lo remote.o: $(srcdir)/daemon/remote.c config.h $(srcdir)/daemon/remote.h
$(srcdir)/validator/val_anchor.h $(srcdir)/iterator/iterator.h $(srcdir)/services/outbound_list.h \
$(srcdir)/iterator/iter_fwd.h $(srcdir)/iterator/iter_hints.h $(srcdir)/iterator/iter_delegpt.h \
$(srcdir)/services/outside_network.h $(srcdir)/sldns/str2wire.h $(srcdir)/sldns/parseutil.h \
$(srcdir)/sldns/wire2str.h $(srcdir)/util/edns.h
$(srcdir)/sldns/wire2str.h $(srcdir)/util/edns.h \
$(srcdir)/util/locks.h $(srcdir)/util/ub_event.h \
$(srcdir)/util/tcp_conn_limit.h $(srcdir)/util/edns.h $(srcdir)/validator/val_neg.h \
$(srcdir)/iterator/iter_utils.h $(srcdir)/iterator/iter_donotq.h $(srcdir)/iterator/iter_priv.h
stats.lo stats.o: $(srcdir)/daemon/stats.c config.h $(srcdir)/daemon/stats.h $(srcdir)/util/timehist.h \
$(srcdir)/libunbound/unbound.h $(srcdir)/daemon/worker.h $(srcdir)/libunbound/worker.h $(srcdir)/sldns/sbuffer.h \
$(srcdir)/util/data/packed_rrset.h $(srcdir)/util/storage/lruhash.h $(srcdir)/util/locks.h $(srcdir)/util/log.h \

View file

@ -663,6 +663,9 @@
/* Define to 1 if you have the <stdarg.h> header file. */
#undef HAVE_STDARG_H
/* Define to 1 if you have the <stdatomic.h> header file. */
#undef HAVE_STDATOMIC_H
/* Define to 1 if you have the <stdbool.h> header file. */
#undef HAVE_STDBOOL_H

8
configure vendored
View file

@ -16100,6 +16100,14 @@ then :
fi
ac_fn_c_check_header_compile "$LINENO" "stdatomic.h" "ac_cv_header_stdatomic_h" "$ac_includes_default
"
if test "x$ac_cv_header_stdatomic_h" = xyes
then :
printf "%s\n" "#define HAVE_STDATOMIC_H 1" >>confdefs.h
fi
# check for types.
# Using own tests for int64* because autoconf builtin only give 32bit.

View file

@ -524,6 +524,7 @@ AC_CHECK_HEADERS([netioapi.h],,, [AC_INCLUDES_DEFAULT
# Check for Linux timestamping headers
AC_CHECK_HEADERS([linux/net_tstamp.h],,, [AC_INCLUDES_DEFAULT])
AC_CHECK_HEADERS([stdatomic.h],,, [AC_INCLUDES_DEFAULT])
# check for types.
# Using own tests for int64* because autoconf builtin only give 32bit.

View file

@ -816,3 +816,14 @@ log_acl_action(const char* action, struct sockaddr_storage* addr,
(int)port);
}
}
void acl_list_swap_tree(struct acl_list* acl, struct acl_list* data)
{
/* swap tree and region */
rbtree_type oldtree = acl->tree;
struct regional* oldregion = acl->region;
acl->tree = data->tree;
acl->region = data->region;
data->tree = oldtree;
data->region = oldregion;
}

View file

@ -201,4 +201,12 @@ const char* acl_access_to_str(enum acl_access acl);
void log_acl_action(const char* action, struct sockaddr_storage* addr,
socklen_t addrlen, enum acl_access acl, struct acl_addr* acladdr);
/**
* Swap internal tree with preallocated entries.
* @param acl: the acl structure.
* @param data: the data structure used to take elements from. This contains
* the old elements on return.
*/
void acl_list_swap_tree(struct acl_list* acl, struct acl_list* data);
#endif /* DAEMON_ACL_LIST_H */

View file

@ -323,8 +323,7 @@ daemon_init(void)
return daemon;
}
static int setup_acl_for_ports(struct acl_list* list,
struct listen_port* port_list)
int setup_acl_for_ports(struct acl_list* list, struct listen_port* port_list)
{
struct acl_addr* acl_node;
for(; port_list; port_list=port_list->next) {
@ -717,16 +716,16 @@ daemon_fork(struct daemon* daemon)
#endif
log_assert(daemon);
if(!(daemon->views = views_create()))
if(!(daemon->env->views = views_create()))
fatal_exit("Could not create views: out of memory");
/* create individual views and their localzone/data trees */
if(!views_apply_cfg(daemon->views, daemon->cfg))
if(!views_apply_cfg(daemon->env->views, daemon->cfg))
fatal_exit("Could not set up views");
if(!acl_list_apply_cfg(daemon->acl, daemon->cfg, daemon->views))
if(!acl_list_apply_cfg(daemon->acl, daemon->cfg, daemon->env->views))
fatal_exit("Could not setup access control list");
if(!acl_interface_apply_cfg(daemon->acl_interface, daemon->cfg,
daemon->views))
daemon->env->views))
fatal_exit("Could not setup interface control list");
if(!tcl_list_apply_cfg(daemon->tcl, daemon->cfg))
fatal_exit("Could not setup TCP connection limits");
@ -762,15 +761,15 @@ daemon_fork(struct daemon* daemon)
fatal_exit("Could not set root or stub hints");
/* process raw response-ip configuration data */
if(!(daemon->respip_set = respip_set_create()))
if(!(daemon->env->respip_set = respip_set_create()))
fatal_exit("Could not create response IP set");
if(!respip_global_apply_cfg(daemon->respip_set, daemon->cfg))
if(!respip_global_apply_cfg(daemon->env->respip_set, daemon->cfg))
fatal_exit("Could not set up response IP set");
if(!respip_views_apply_cfg(daemon->views, daemon->cfg,
if(!respip_views_apply_cfg(daemon->env->views, daemon->cfg,
&have_view_respip_cfg))
fatal_exit("Could not set up per-view response IP sets");
daemon->use_response_ip = !respip_set_is_empty(daemon->respip_set) ||
have_view_respip_cfg;
daemon->use_response_ip = !respip_set_is_empty(
daemon->env->respip_set) || have_view_respip_cfg;
/* setup modules */
daemon_setup_modules(daemon);
@ -886,14 +885,18 @@ daemon_cleanup(struct daemon* daemon)
daemon->env->hints = NULL;
local_zones_delete(daemon->local_zones);
daemon->local_zones = NULL;
respip_set_delete(daemon->respip_set);
daemon->respip_set = NULL;
views_delete(daemon->views);
daemon->views = NULL;
respip_set_delete(daemon->env->respip_set);
daemon->env->respip_set = NULL;
views_delete(daemon->env->views);
daemon->env->views = NULL;
if(daemon->env->auth_zones)
auth_zones_cleanup(daemon->env->auth_zones);
/* key cache is cleared by module deinit during next daemon_fork() */
daemon_remote_clear(daemon->rc);
if(daemon->fast_reload_thread)
fast_reload_thread_stop(daemon->fast_reload_thread);
if(daemon->fast_reload_printq_list)
fast_reload_printq_list_delete(daemon->fast_reload_printq_list);
for(i=0; i<daemon->num; i++)
worker_delete(daemon->workers[i]);
free(daemon->workers);
@ -951,6 +954,7 @@ daemon_delete(struct daemon* daemon)
listen_desetup_locks();
free(daemon->chroot);
free(daemon->pidfile);
free(daemon->cfgfile);
free(daemon->env);
#ifdef HAVE_SSL
listen_sslctx_delete_ticket_keys();

View file

@ -60,6 +60,8 @@ struct respip_set;
struct shm_main_info;
struct doq_table;
struct cookie_secrets;
struct fast_reload_thread;
struct fast_reload_printq;
#include "dnstap/dnstap_config.h"
#ifdef USE_DNSTAP
@ -137,15 +139,11 @@ struct daemon {
struct timeval time_last_stat;
/** time when daemon started */
struct timeval time_boot;
/** views structure containing view tree */
struct views* views;
#ifdef USE_DNSTAP
/** the dnstap environment master value, copied and changed by threads*/
struct dt_env* dtenv;
#endif
struct shm_main_info* shm_info;
/** response-ip set with associated actions and tags. */
struct respip_set* respip_set;
/** some response-ip tags or actions are configured if true */
int use_response_ip;
/** some RPZ policies are configured */
@ -160,6 +158,17 @@ struct daemon {
int reuse_cache;
/** the EDNS cookie secrets from the cookie-secret-file */
struct cookie_secrets* cookie_secrets;
/** the fast reload thread, or NULL */
struct fast_reload_thread* fast_reload_thread;
/** the fast reload printq list */
struct fast_reload_printq* fast_reload_printq_list;
/** the fast reload option to drop mesh queries, true if so. */
int fast_reload_drop_mesh;
/** for fast reload, if the tcl, tcp connection limits, has
* changes for workers */
int fast_reload_tcl_has_changes;
/** config file name */
char* cfgfile;
};
/**
@ -212,4 +221,12 @@ void daemon_delete(struct daemon* daemon);
*/
void daemon_apply_cfg(struct daemon* daemon, struct config_file* cfg);
/**
* Setup acl list to have entries for the port list.
* @param list: the acl interface
* @param port_list: list of open ports, or none.
* @return false on failure
*/
int setup_acl_for_ports(struct acl_list* list, struct listen_port* port_list);
#endif /* DAEMON_H */

File diff suppressed because it is too large Load diff

View file

@ -48,6 +48,7 @@
#ifdef HAVE_OPENSSL_SSL_H
#include <openssl/ssl.h>
#endif
#include "util/locks.h"
struct config_file;
struct listen_list;
struct listen_port;
@ -55,6 +56,7 @@ struct worker;
struct comm_reply;
struct comm_point;
struct daemon_remote;
struct config_strlist_head;
/** number of milliseconds timeout on incoming remote control handshake */
#define REMOTE_CONTROL_TCP_TIMEOUT 120000
@ -118,6 +120,137 @@ struct remote_stream {
};
typedef struct remote_stream RES;
/**
* Notification status. This is exchanged between the fast reload thread
* and the server thread, over the commpair sockets.
*/
enum fast_reload_notification {
/** nothing, not used */
fast_reload_notification_none = 0,
/** the fast reload thread is done */
fast_reload_notification_done = 1,
/** the fast reload thread is done but with an error, it failed */
fast_reload_notification_done_error = 2,
/** the fast reload thread is told to exit by the server thread.
* Sent on server quit while the reload is running. */
fast_reload_notification_exit = 3,
/** the fast reload thread has exited, after being told to exit */
fast_reload_notification_exited = 4,
/** the fast reload thread has information to print out */
fast_reload_notification_printout = 5,
/** stop as part of the reload the thread and other threads */
fast_reload_notification_reload_stop = 6,
/** ack the stop as part of the reload, and also ack start */
fast_reload_notification_reload_ack = 7,
/** resume from stop as part of the reload */
fast_reload_notification_reload_start = 8,
/** the fast reload thread wants the mainthread to poll workers,
* after the reload, sent when nopause is used */
fast_reload_notification_reload_nopause_poll = 9
};
/**
* Fast reload printout queue. Contains a list of strings, that need to be
* printed over the file descriptor.
*/
struct fast_reload_printq {
/** if this item is in a list, the previous and next */
struct fast_reload_printq *prev, *next;
/** if this item is in a list, it is true. */
int in_list;
/** list of strings to printout */
struct config_strlist_head* to_print;
/** the current item to print. It is malloced. NULL if none. */
char* client_item;
/** The length, strlen, of the client_item, that has to be sent. */
int client_len;
/** The number of bytes sent of client_item. */
int client_byte_count;
/** the comm point for the client connection, the remote control
* client. */
struct comm_point* client_cp;
/** the remote control connection to print output to. */
struct remote_stream remote;
/** the worker that the event is added in */
struct worker* worker;
};
/**
* Fast reload auth zone change. Keeps track if an auth zone was removed,
* added or changed. This is needed because workers can have events for
* dealing with auth zones, like transfers, and those have to be removed
* too, not just the auth zone structure from the tree. */
struct fast_reload_auth_change {
/** next in the list of auth zone changes. */
struct fast_reload_auth_change* next;
/** the zone in the old config */
struct auth_zone* old_z;
/** the zone in the new config */
struct auth_zone* new_z;
/** if the zone was deleted */
int is_deleted;
/** if the zone was added */
int is_added;
/** if the zone has been changed */
int is_changed;
};
/**
* Fast reload thread structure
*/
struct fast_reload_thread {
/** the thread number for the dtio thread,
* must be first to cast thread arg to int* in checklock code. */
int threadnum;
/** communication socket pair, that sends commands */
int commpair[2];
/** thread id, of the io thread */
ub_thread_type tid;
/** if the io processing has started */
int started;
/** if the thread has to quit */
int need_to_quit;
/** verbosity of the fast_reload command, the number of +v options */
int fr_verb;
/** option to not pause threads during reload */
int fr_nopause;
/** option to drop mesh queries */
int fr_drop_mesh;
/** the event that listens on the remote service worker to the
* commpair, it receives content from the fast reload thread. */
void* service_event;
/** if the event that listens on the remote service worker has
* been added to the comm base. */
int service_event_is_added;
/** the service event can read a cmd, nonblocking, so it can
* save the partial read cmd here */
uint32_t service_read_cmd;
/** the number of bytes in service_read_cmd */
int service_read_cmd_count;
/** the worker that the service_event is added in */
struct worker* worker;
/** the printout of output to the remote client. */
struct fast_reload_printq *printq;
/** lock on fr_output, to stop race when both remote control thread
* and fast reload thread use fr_output list. */
lock_basic_type fr_output_lock;
/** list of strings, that the fast reload thread produces that have
* to be printed. The remote control thread can pick them up with
* the lock. */
struct config_strlist_head* fr_output;
/** communication socket pair, to respond to the reload request */
int commreload[2];
/** the list of auth zone changes. */
struct fast_reload_auth_change* auth_zone_change_list;
/** the old tree of auth zones, to lookup. */
struct auth_zones* old_auth_zones;
};
/**
* Create new remote control state for the daemon.
* @param cfg: config file with key file settings.
@ -203,4 +336,38 @@ int ssl_printf(RES* ssl, const char* format, ...)
int ssl_read_line(RES* ssl, char* buf, size_t max);
#endif /* HAVE_SSL */
/**
* Start fast reload thread
* @param ssl: the RES connection to print to.
* @param worker: the remote servicing worker.
* @param s: the rc_state that is servicing the remote control connection to
* the remote control client. It needs to be moved away to stay connected
* while the fast reload is running.
* @param fr_verb: verbosity to print output at. 0 is nothing, 1 is some
* and 2 is more detail.
* @param fr_nopause: option to not pause threads during reload.
* @param fr_drop_mesh: option to drop mesh queries.
*/
void fast_reload_thread_start(RES* ssl, struct worker* worker,
struct rc_state* s, int fr_verb, int fr_nopause, int fr_drop_mesh);
/**
* Stop fast reload thread
* @param fast_reload_thread: the thread struct.
*/
void fast_reload_thread_stop(struct fast_reload_thread* fast_reload_thread);
/** fast reload thread commands to remote service thread event callback */
void fast_reload_service_cb(int fd, short bits, void* arg);
/** fast reload callback for the remote control client connection */
int fast_reload_client_callback(struct comm_point* c, void* arg, int err,
struct comm_reply* rep);
/** fast reload printq delete list */
void fast_reload_printq_list_delete(struct fast_reload_printq* list);
/** Pick up per worker changes after a fast reload. */
void fast_reload_worker_pickup_changes(struct worker* worker);
#endif /* DAEMON_REMOTE_H */

View file

@ -710,6 +710,9 @@ perform_setup(struct daemon* daemon, struct config_file* cfg, int debug_mode,
* it would succeed on SIGHUP as well */
if(!cfg->use_syslog)
log_init(cfg->logfile, cfg->use_syslog, cfg->chrootdir);
daemon->cfgfile = strdup(*cfgfile);
if(!daemon->cfgfile)
fatal_exit("out of memory in daemon cfgfile strdup");
}
/**

View file

@ -371,6 +371,84 @@ worker_check_request(sldns_buffer* pkt, struct worker* worker,
return;
}
/**
* Send fast-reload acknowledgement to the mainthread in one byte.
* This signals that this worker has received the previous command.
* The worker is waiting if that is after a reload_stop command.
* Or the worker has briefly processed the event itself, and in doing so
* released data pointers to old config, after a reload_poll command.
*/
static void
worker_send_reload_ack(struct worker* worker)
{
/* If this is clipped to 8 bits because thread_num>255, then that
* is not a problem, the receiver counts the number of bytes received.
* The number is informative only. */
uint8_t c = (uint8_t)worker->thread_num;
ssize_t ret;
while(1) {
ret = send(worker->daemon->fast_reload_thread->commreload[1],
(void*)&c, 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("worker reload ack reply: send failed: %s",
sock_strerror(errno));
break;
}
break;
}
}
/** stop and wait to resume the worker */
static void
worker_stop_and_wait(struct worker* worker)
{
uint8_t* buf = NULL;
uint32_t len = 0, cmd;
worker_send_reload_ack(worker);
/* wait for reload */
if(!tube_read_msg(worker->cmd, &buf, &len, 0)) {
log_err("worker reload read reply failed");
return;
}
if(len != sizeof(uint32_t)) {
log_err("worker reload reply, bad control msg length %d",
(int)len);
free(buf);
return;
}
cmd = sldns_read_uint32(buf);
free(buf);
if(cmd == worker_cmd_quit) {
/* quit anyway */
verbose(VERB_ALGO, "reload reply, control cmd quit");
comm_base_exit(worker->base);
return;
}
if(cmd != worker_cmd_reload_start) {
log_err("worker reload reply, wrong reply command");
}
if(worker->daemon->fast_reload_drop_mesh) {
verbose(VERB_ALGO, "worker: drop mesh queries after reload");
mesh_delete_all(worker->env.mesh);
}
fast_reload_worker_pickup_changes(worker);
worker_send_reload_ack(worker);
verbose(VERB_ALGO, "worker resume after reload");
}
void
worker_handle_control_cmd(struct tube* ATTR_UNUSED(tube), uint8_t* msg,
size_t len, int error, void* arg)
@ -406,6 +484,15 @@ worker_handle_control_cmd(struct tube* ATTR_UNUSED(tube), uint8_t* msg,
verbose(VERB_ALGO, "got control cmd remote");
daemon_remote_exec(worker);
break;
case worker_cmd_reload_stop:
verbose(VERB_ALGO, "got control cmd reload_stop");
worker_stop_and_wait(worker);
break;
case worker_cmd_reload_poll:
verbose(VERB_ALGO, "got control cmd reload_poll");
fast_reload_worker_pickup_changes(worker);
worker_send_reload_ack(worker);
break;
default:
log_err("bad command %d", (int)cmd);
break;
@ -600,7 +687,8 @@ apply_respip_action(struct worker* worker, const struct query_info* qinfo,
return 1;
if(!respip_rewrite_reply(qinfo, cinfo, rep, encode_repp, &actinfo,
alias_rrset, 0, worker->scratchpad, az, NULL))
alias_rrset, 0, worker->scratchpad, az, NULL,
worker->env.views, worker->env.respip_set))
return 0;
/* xxx_deny actions mean dropping the reply, unless the original reply
@ -761,7 +849,8 @@ answer_from_cache(struct worker* worker, struct query_info* qinfo,
} else if(partial_rep &&
!respip_merge_cname(partial_rep, qinfo, rep, cinfo,
must_validate, &encode_rep, worker->scratchpad,
worker->env.auth_zones)) {
worker->env.auth_zones, worker->env.views,
worker->env.respip_set)) {
goto bail_out;
}
if(encode_rep != rep) {
@ -1813,7 +1902,7 @@ worker_handle_request(struct comm_point* c, void* arg, int error,
cinfo_tmp.tag_datas = acladdr->tag_datas;
cinfo_tmp.tag_datas_size = acladdr->tag_datas_size;
cinfo_tmp.view = acladdr->view;
cinfo_tmp.respip_set = worker->daemon->respip_set;
cinfo_tmp.view_name = NULL;
cinfo = &cinfo_tmp;
}

View file

@ -72,7 +72,13 @@ enum worker_commands {
/** obtain statistics without statsclear */
worker_cmd_stats_noreset,
/** execute remote control command */
worker_cmd_remote
worker_cmd_remote,
/** for fast-reload, perform stop */
worker_cmd_reload_stop,
/** for fast-reload, start again */
worker_cmd_reload_start,
/** for fast-reload, poll to make sure worker has released data */
worker_cmd_reload_poll
};
/**

View file

@ -192,8 +192,11 @@ static void
dt_apply_identity(struct dt_env *env, struct config_file *cfg)
{
char buf[MAXHOSTNAMELEN+1];
if (!cfg->dnstap_send_identity)
if (!cfg->dnstap_send_identity) {
free(env->identity);
env->identity = NULL;
return;
}
free(env->identity);
if (cfg->dnstap_identity == NULL || cfg->dnstap_identity[0] == 0) {
if (gethostname(buf, MAXHOSTNAMELEN) == 0) {
@ -215,8 +218,11 @@ dt_apply_identity(struct dt_env *env, struct config_file *cfg)
static void
dt_apply_version(struct dt_env *env, struct config_file *cfg)
{
if (!cfg->dnstap_send_version)
if (!cfg->dnstap_send_version) {
free(env->version);
env->version = NULL;
return;
}
free(env->version);
if (cfg->dnstap_version == NULL || cfg->dnstap_version[0] == 0)
env->version = strdup(PACKAGE_STRING);
@ -230,13 +236,8 @@ dt_apply_version(struct dt_env *env, struct config_file *cfg)
}
void
dt_apply_cfg(struct dt_env *env, struct config_file *cfg)
dt_apply_logcfg(struct dt_env *env, struct config_file *cfg)
{
if (!cfg->dnstap)
return;
dt_apply_identity(env, cfg);
dt_apply_version(env, cfg);
if ((env->log_resolver_query_messages = (unsigned int)
cfg->dnstap_log_resolver_query_messages))
{
@ -275,6 +276,17 @@ dt_apply_cfg(struct dt_env *env, struct config_file *cfg)
lock_basic_unlock(&env->sample_lock);
}
void
dt_apply_cfg(struct dt_env *env, struct config_file *cfg)
{
if (!cfg->dnstap)
return;
dt_apply_identity(env, cfg);
dt_apply_version(env, cfg);
dt_apply_logcfg(env, cfg);
}
int
dt_init(struct dt_env *env, struct comm_base* base)
{

View file

@ -106,6 +106,13 @@ dt_create(struct config_file* cfg);
void
dt_apply_cfg(struct dt_env *env, struct config_file *cfg);
/**
* Apply config settings for log enable for message types.
* @param env: dnstap environment object.
* @param cfg: new config settings.
*/
void dt_apply_logcfg(struct dt_env *env, struct config_file *cfg);
/**
* Initialize per-worker state in dnstap environment object.
* @param env: dnstap environment object to initialize, created with dt_create().

View file

@ -1787,6 +1787,20 @@ void remote_get_opt_ssl(char* ATTR_UNUSED(str), void* ATTR_UNUSED(arg))
log_assert(0);
}
void fast_reload_service_cb(int ATTR_UNUSED(fd), short ATTR_UNUSED(ev),
void* ATTR_UNUSED(arg))
{
log_assert(0);
}
int fast_reload_client_callback(struct comm_point* ATTR_UNUSED(c),
void* ATTR_UNUSED(arg), int ATTR_UNUSED(error),
struct comm_reply* ATTR_UNUSED(repinfo))
{
log_assert(0);
return 0;
}
#ifdef HAVE_NGTCP2
void doq_client_event_cb(int ATTR_UNUSED(fd), short ATTR_UNUSED(ev),
void* ATTR_UNUSED(arg))

View file

@ -1,5 +1,9 @@
31 March 2025: Wouter
- iana portlist update.
- Merge #1042: Fast Reload. The unbound-control fast_reload is added.
It reads changed config in a thread, then only briefly pauses the
service threads, that keep running. DNS service is only interrupted
briefly, less than a second.
27 March 2025: Wouter
- Fix unit test dname log printout typecast.

View file

@ -60,6 +60,93 @@ Reload the server but try to keep the RRset and message cache if
That means the caches sizes and the number of threads must not change between
reloads.
.TP
.B fast_reload \fR[\fI+dpv\fR]
Reload the server, but keep downtime to a minimum, so that user queries
keep seeing service. This needs the code compiled with threads. The config
is loaded in a thread, and prepared, then it briefly pauses the existing
server and updates config options. The intent is that the pause does not
impact the service of user queries. The cache is kept. Also user queries
worked on are kept and continue, but with the new config options.
This command is experimental at this time.
Not all options are changed, but it changes like forwards, stubs and
local zones. Also access-control and interface-action and similar options,
also tcp-connection-limits, views. It can reload some define-tag changes.
It does not work with interface, outgoing-interface changes, also not with
remote-control, outgoing-port-permit, outgoing-port-avoid, msg-buffer-size,
slabs options and statistics-interval changes.
.IP
The fast reload also works on the options: insecure-lan-zones, domain-insecure,
trust-anchor-file, trust-anchor, trusted-key-file, auto-trust-anchor-file,
auth-zone and its options, rpz and its options, edns-strings, respip_set,
view and its options, access-control options, tcp-connection-limit,
log-identity, infra-cache-numhosts, msg-cache-size, rrset-cache-size,
key-cache-size, ratelimit-size, neg-cache-size, num-queries-per-thread,
jostle-timeout, use-caps-for-id, unwanted-reply-threshold, tls-use-sni,
outgoing-tcp-mss, ip-dscp, max-reuse-tcp-queries, tcp-reuse-timeout,
tcp-auth-query-timeout, delay-close.
.IP
For dnstap, the options can be changed: dnstap-log-resolver-query-messages,
dnstap-log-resolver-response-messages, dnstap-log-client-query-messages,
dnstap-log-client-response-messages, dnstap-log-forwarder-query-messages
and dnstap-log-forwarder-response-messages. It does not work with
these options: dnstap-enable, dnstap-bidirectional, dnstap-socket-path,
dnstap-ip, dnstap-tls, dnstap-tls-server-name, dnstap-tls-cert-bundle,
dnstap-tls-client-key-file and dnstap-tls-client-cert-file. The options
dnstap-send-identity, dnstap-send-version, dnstap-identity, and
dnstap-version can be loaded when '+p' is not used.
.IP
The '+v' option makes the output verbose which includes the time it took to do
the reload.
With '+vv' it is more verbose which includes the amount of memory that was
allocated temporarily to perform the reload; this amount of memory can be big
if the config has large contents.
In the timing output the 'reload' time is the time during which the server was
paused.
.IP
The '+p' option makes the reload not pause threads, they keep running.
Locks are acquired, but items are updated in sequence, so it is possible
for threads to see an inconsistent state with some options from the old
and some options from the new config, such as cache TTL parameters from the
old config and forwards from the new config. The stubs and forwards are
updated at the same time, so that they are viewed consistently, either old
or new values together. The option makes the reload time take eg. 3
microseconds instead of 0.3 milliseconds during which the worker threads are
interrupted. So, the interruption is much shorter, at the expense of some
inconsistency. After the reload itself, every worker thread is briefly
contacted to make them release resources, this makes the delete timing
a little longer, and takes up time from the remote control servicing
worker thread.
.IP
With the nopause option, the reload does not work to reload some options,
that fast reload works on without the nopause option: val-bogus-ttl,
val-date-override, val-sig-key-min, val-sig-skew-max, val-max-restart,
val-nsec3-keysize-iterations, target-fetch-policy, outbound-msg-retry,
max-sent-count, max-query-restarts, do-not-query-address,
do-not-query-localhost, private-address, private-domain, caps-exempt,
nat64-prefix, do-nat64, infra-host-ttl, infra-keep-probing, ratelimit,
ip-ratelimit, ip-ratelimit-cookie, wait-limit-netblock,
wait-limit-cookie-netblock, ratelimit-below-domain, ratelimit-for-domain.
.IP
The '+d' option makes the reload drop queries that the worker threads are
working on. This is like flush_requestlist. Without it the queries are kept
so that users keep getting answers for those queries that are currently
processed. The drop makes it so that queries during the life time of the
query processing see only old, or only new config options.
.IP
When there are changes to the config tags, from \fBdefine\-tag\fR config,
then the '+d' option is implicitly turned on with a warning printout, and
queries are dropped.
This is to stop references to the old tag information, by the old
queries. If the number of tags is increased in the newly loaded config, by
adding tags at the end, then the implicit '+d' option is not needed.
.IP
For response ip, that is actions associated with IP addresses, and perhaps
intersected with access control tag and action information, those settings
are stored with a query when it comes in based on its source IP address.
The old information is kept with the query until the queries are done.
This is gone when those queries are resolved and finished, or it is possible
to flush the requestlist with '+d'.
.TP
.B verbosity \fInumber
Change verbosity value for logging. Same values as \fBverbosity\fR keyword in
\fIunbound.conf\fR(5). This new setting lasts until the server is issued

View file

@ -624,3 +624,19 @@ forwards_delete_stub_hole(struct iter_forwards* fwd, uint16_t c,
fwd_init_parents(fwd);
if(!nolock) { lock_rw_unlock(&fwd->lock); }
}
void
forwards_swap_tree(struct iter_forwards* fwd, struct iter_forwards* data)
{
rbtree_type* oldtree = fwd->tree;
if(oldtree) {
lock_unprotect(&fwd->lock, oldtree);
}
if(data->tree) {
lock_unprotect(&data->lock, data->tree);
}
fwd->tree = data->tree;
data->tree = oldtree;
lock_protect(&fwd->lock, fwd->tree, sizeof(*fwd->tree));
lock_protect(&data->lock, data->tree, sizeof(*data->tree));
}

View file

@ -234,4 +234,13 @@ int forwards_add_stub_hole(struct iter_forwards* fwd, uint16_t c,
void forwards_delete_stub_hole(struct iter_forwards* fwd, uint16_t c,
uint8_t* nm, int nolock);
/**
* Swap internal tree with preallocated entries. Caller should manage
* the locks.
* @param fwd: the forward data structure.
* @param data: the data structure used to take elements from. This contains
* the old elements on return.
*/
void forwards_swap_tree(struct iter_forwards* fwd, struct iter_forwards* data);
#endif /* ITERATOR_ITER_FWD_H */

View file

@ -611,3 +611,14 @@ hints_delete_stub(struct iter_hints* hints, uint16_t c, uint8_t* nm,
name_tree_init_parents(&hints->tree);
if(!nolock) { lock_rw_unlock(&hints->lock); }
}
void
hints_swap_tree(struct iter_hints* hints, struct iter_hints* data)
{
rbnode_type* oldroot = hints->tree.root;
size_t oldcount = hints->tree.count;
hints->tree.root = data->tree.root;
hints->tree.count = data->tree.count;
data->tree.root = oldroot;
data->tree.count = oldcount;
}

View file

@ -198,4 +198,13 @@ int hints_add_stub(struct iter_hints* hints, uint16_t c, struct delegpt* dp,
void hints_delete_stub(struct iter_hints* hints, uint16_t c,
uint8_t* nm, int nolock);
/**
* Swap internal tree with preallocated entries. Caller should manage
* the locks.
* @param hints: the hints data structure.
* @param data: the data structure used to take elements from. This contains
* the old elements on return.
*/
void hints_swap_tree(struct iter_hints* hints, struct iter_hints* data);
#endif /* ITERATOR_ITER_HINTS_H */

View file

@ -77,41 +77,73 @@
static const char DEFAULT_NAT64_PREFIX[] = "64:ff9b::/96";
/** fillup fetch policy array */
static void
fetch_fill(struct iter_env* ie, const char* str)
static int
fetch_fill(int* target_fetch_policy, int max_dependency_depth, const char* str)
{
char* s = (char*)str, *e;
int i;
for(i=0; i<ie->max_dependency_depth+1; i++) {
ie->target_fetch_policy[i] = strtol(s, &e, 10);
if(s == e)
fatal_exit("cannot parse fetch policy number %s", s);
for(i=0; i<max_dependency_depth+1; i++) {
target_fetch_policy[i] = strtol(s, &e, 10);
if(s == e) {
log_err("cannot parse fetch policy number %s", s);
return 0;
}
s = e;
}
return 1;
}
/** Read config string that represents the target fetch policy */
static int
read_fetch_policy(struct iter_env* ie, const char* str)
int
read_fetch_policy(int** target_fetch_policy, int* max_dependency_depth,
const char* str)
{
int count = cfg_count_numbers(str);
if(count < 1) {
log_err("Cannot parse target fetch policy: \"%s\"", str);
return 0;
}
ie->max_dependency_depth = count - 1;
ie->target_fetch_policy = (int*)calloc(
(size_t)ie->max_dependency_depth+1, sizeof(int));
if(!ie->target_fetch_policy) {
*max_dependency_depth = count - 1;
*target_fetch_policy = (int*)calloc(
(size_t)(*max_dependency_depth)+1, sizeof(int));
if(!*target_fetch_policy) {
log_err("alloc fetch policy: out of memory");
return 0;
}
fetch_fill(ie, str);
if(!fetch_fill(*target_fetch_policy, *max_dependency_depth, str))
return 0;
return 1;
}
/** apply config caps whitelist items to name tree */
static int
struct rbtree_type*
caps_white_create(void)
{
struct rbtree_type* caps_white = rbtree_create(name_tree_compare);
if(!caps_white)
log_err("out of memory");
return caps_white;
}
/** delete caps_whitelist element */
static void
caps_free(struct rbnode_type* n, void* ATTR_UNUSED(d))
{
if(n) {
free(((struct name_tree_node*)n)->name);
free(n);
}
}
void
caps_white_delete(struct rbtree_type* caps_white)
{
if(!caps_white)
return;
traverse_postorder(caps_white, caps_free, NULL);
free(caps_white);
}
int
caps_white_apply_cfg(rbtree_type* ntree, struct config_file* cfg)
{
struct config_strlist* p;
@ -145,12 +177,41 @@ caps_white_apply_cfg(rbtree_type* ntree, struct config_file* cfg)
}
int
iter_apply_cfg(struct iter_env* iter_env, struct config_file* cfg)
nat64_apply_cfg(struct iter_nat64* nat64, struct config_file* cfg)
{
const char *nat64_prefix;
nat64_prefix = cfg->nat64_prefix;
if(!nat64_prefix)
nat64_prefix = cfg->dns64_prefix;
if(!nat64_prefix)
nat64_prefix = DEFAULT_NAT64_PREFIX;
if(!netblockstrtoaddr(nat64_prefix, 0, &nat64->nat64_prefix_addr,
&nat64->nat64_prefix_addrlen, &nat64->nat64_prefix_net)) {
log_err("cannot parse nat64-prefix netblock: %s", nat64_prefix);
return 0;
}
if(!addr_is_ip6(&nat64->nat64_prefix_addr,
nat64->nat64_prefix_addrlen)) {
log_err("nat64-prefix is not IPv6: %s", cfg->nat64_prefix);
return 0;
}
if(!prefixnet_is_nat64(nat64->nat64_prefix_net)) {
log_err("nat64-prefix length it not 32, 40, 48, 56, 64 or 96: %s",
nat64_prefix);
return 0;
}
nat64->use_nat64 = cfg->do_nat64;
return 1;
}
int
iter_apply_cfg(struct iter_env* iter_env, struct config_file* cfg)
{
int i;
/* target fetch policy */
if(!read_fetch_policy(iter_env, cfg->target_fetch_policy))
if(!read_fetch_policy(&iter_env->target_fetch_policy,
&iter_env->max_dependency_depth, cfg->target_fetch_policy))
return 0;
for(i=0; i<iter_env->max_dependency_depth+1; i++)
verbose(VERB_QUERY, "target fetch policy for level %d is %d",
@ -170,7 +231,7 @@ iter_apply_cfg(struct iter_env* iter_env, struct config_file* cfg)
}
if(cfg->caps_whitelist) {
if(!iter_env->caps_white)
iter_env->caps_white = rbtree_create(name_tree_compare);
iter_env->caps_white = caps_white_create();
if(!iter_env->caps_white || !caps_white_apply_cfg(
iter_env->caps_white, cfg)) {
log_err("Could not set capsforid whitelist");
@ -179,31 +240,13 @@ iter_apply_cfg(struct iter_env* iter_env, struct config_file* cfg)
}
nat64_prefix = cfg->nat64_prefix;
if(!nat64_prefix)
nat64_prefix = cfg->dns64_prefix;
if(!nat64_prefix)
nat64_prefix = DEFAULT_NAT64_PREFIX;
if(!netblockstrtoaddr(nat64_prefix, 0, &iter_env->nat64_prefix_addr,
&iter_env->nat64_prefix_addrlen,
&iter_env->nat64_prefix_net)) {
log_err("cannot parse nat64-prefix netblock: %s", nat64_prefix);
return 0;
}
if(!addr_is_ip6(&iter_env->nat64_prefix_addr,
iter_env->nat64_prefix_addrlen)) {
log_err("nat64-prefix is not IPv6: %s", cfg->nat64_prefix);
return 0;
}
if(!prefixnet_is_nat64(iter_env->nat64_prefix_net)) {
log_err("nat64-prefix length it not 32, 40, 48, 56, 64 or 96: %s",
nat64_prefix);
if(!nat64_apply_cfg(&iter_env->nat64, cfg)) {
log_err("Could not setup nat64");
return 0;
}
iter_env->supports_ipv6 = cfg->do_ip6;
iter_env->supports_ipv4 = cfg->do_ip4;
iter_env->use_nat64 = cfg->do_nat64;
iter_env->outbound_msg_retry = cfg->outbound_msg_retry;
iter_env->max_sent_count = cfg->max_sent_count;
iter_env->max_query_restarts = cfg->max_query_restarts;
@ -270,7 +313,7 @@ iter_filter_unsuitable(struct iter_env* iter_env, struct module_env* env,
if(!iter_env->supports_ipv6 && addr_is_ip6(&a->addr, a->addrlen)) {
return -1; /* there is no ip6 available */
}
if(!iter_env->supports_ipv4 && !iter_env->use_nat64 &&
if(!iter_env->supports_ipv4 && !iter_env->nat64.use_nat64 &&
!addr_is_ip6(&a->addr, a->addrlen)) {
return -1; /* there is no ip4 available */
}

View file

@ -61,6 +61,7 @@ struct sock_list;
struct ub_packed_rrset_key;
struct module_stack;
struct outside_network;
struct iter_nat64;
/* max number of lookups in the cache for target nameserver names.
* This stops, for large delegations, N*N lookups in the cache. */
@ -430,6 +431,43 @@ int iter_stub_fwd_no_cache(struct module_qstate *qstate,
void iterator_set_ip46_support(struct module_stack* mods,
struct module_env* env, struct outside_network* outnet);
/**
* Read config string that represents the target fetch policy.
* @param target_fetch_policy: alloced on return.
* @param max_dependency_depth: set on return.
* @param str: the config string
* @return false on failure.
*/
int read_fetch_policy(int** target_fetch_policy, int* max_dependency_depth,
const char* str);
/**
* Create caps exempt data structure.
* @return NULL on failure.
*/
struct rbtree_type* caps_white_create(void);
/**
* Delete caps exempt data structure.
* @param caps_white: caps exempt tree.
*/
void caps_white_delete(struct rbtree_type* caps_white);
/**
* Apply config caps whitelist items to name tree
* @param ntree: caps exempt tree.
* @param cfg: config with options.
*/
int caps_white_apply_cfg(struct rbtree_type* ntree, struct config_file* cfg);
/**
* Apply config for nat64
* @param nat64: the nat64 state.
* @param cfg: config with options.
* @return false on failure.
*/
int nat64_apply_cfg(struct iter_nat64* nat64, struct config_file* cfg);
/**
* Limit NSEC and NSEC3 TTL in response, RFC9077
* @param msg: dns message, the SOA record ttl is used to restrict ttls

View file

@ -107,16 +107,6 @@ iter_init(struct module_env* env, int id)
return 1;
}
/** delete caps_whitelist element */
static void
caps_free(struct rbnode_type* n, void* ATTR_UNUSED(d))
{
if(n) {
free(((struct name_tree_node*)n)->name);
free(n);
}
}
void
iter_deinit(struct module_env* env, int id)
{
@ -128,10 +118,7 @@ iter_deinit(struct module_env* env, int id)
free(iter_env->target_fetch_policy);
priv_delete(iter_env->priv);
donotq_delete(iter_env->donotq);
if(iter_env->caps_white) {
traverse_postorder(iter_env->caps_white, caps_free, NULL);
free(iter_env->caps_white);
}
caps_white_delete(iter_env->caps_white);
free(iter_env);
env->modinfo[id] = NULL;
}
@ -260,7 +247,7 @@ error_supers(struct module_qstate* qstate, int id, struct module_qstate* super)
log_err("out of memory adding missing");
}
delegpt_mark_neg(dpns, qstate->qinfo.qtype);
if((dpns->got4 == 2 || (!ie->supports_ipv4 && !ie->use_nat64)) &&
if((dpns->got4 == 2 || (!ie->supports_ipv4 && !ie->nat64.use_nat64)) &&
(dpns->got6 == 2 || !ie->supports_ipv6)) {
dpns->resolved = 1; /* mark as failed */
target_count_increase_nx(super_iq, 1);
@ -1725,7 +1712,7 @@ processInitRequest(struct module_qstate* qstate, struct iter_qstate* iq,
*/
if(iter_dp_is_useless(&qstate->qinfo, qstate->query_flags,
iq->dp, ie->supports_ipv4, ie->supports_ipv6,
ie->use_nat64)) {
ie->nat64.use_nat64)) {
int have_dp = 0;
if(!can_have_last_resort(qstate->env, iq->dp->name, iq->dp->namelen, iq->qchase.qclass, &have_dp, &iq->dp, qstate->region)) {
if(have_dp) {
@ -2089,7 +2076,7 @@ query_for_targets(struct module_qstate* qstate, struct iter_qstate* iq,
if(mesh_jostle_exceeded(qstate->env->mesh)) {
/* If no ip4 query is possible, that makes
* this ns resolved. */
if(!((ie->supports_ipv4 || ie->use_nat64) &&
if(!((ie->supports_ipv4 || ie->nat64.use_nat64) &&
((ns->lame && !ns->done_pside4) ||
(!ns->lame && !ns->got4)))) {
ns->resolved = 1;
@ -2098,7 +2085,7 @@ query_for_targets(struct module_qstate* qstate, struct iter_qstate* iq,
}
}
/* Send the A request. */
if((ie->supports_ipv4 || ie->use_nat64) &&
if((ie->supports_ipv4 || ie->nat64.use_nat64) &&
((ns->lame && !ns->done_pside4) ||
(!ns->lame && !ns->got4))) {
if(!generate_target_query(qstate, iq, id,
@ -2270,14 +2257,14 @@ processLastResort(struct module_qstate* qstate, struct iter_qstate* iq,
/* if this nameserver is at a delegation point, but that
* delegation point is a stub and we cannot go higher, skip*/
if( ((ie->supports_ipv6 && !ns->done_pside6) ||
((ie->supports_ipv4 || ie->use_nat64) && !ns->done_pside4)) &&
((ie->supports_ipv4 || ie->nat64.use_nat64) && !ns->done_pside4)) &&
!can_have_last_resort(qstate->env, ns->name, ns->namelen,
iq->qchase.qclass, NULL, NULL, NULL)) {
log_nametypeclass(VERB_ALGO, "cannot pside lookup ns "
"because it is also a stub/forward,",
ns->name, LDNS_RR_TYPE_NS, iq->qchase.qclass);
if(ie->supports_ipv6) ns->done_pside6 = 1;
if(ie->supports_ipv4 || ie->use_nat64) ns->done_pside4 = 1;
if(ie->supports_ipv4 || ie->nat64.use_nat64) ns->done_pside4 = 1;
continue;
}
/* query for parent-side A and AAAA for nameservers */
@ -2302,7 +2289,7 @@ processLastResort(struct module_qstate* qstate, struct iter_qstate* iq,
return 0;
}
}
if((ie->supports_ipv4 || ie->use_nat64) && !ns->done_pside4) {
if((ie->supports_ipv4 || ie->nat64.use_nat64) && !ns->done_pside4) {
/* Send the A request. */
if(!generate_parentside_target_query(qstate, iq, id,
ns->name, ns->namelen,
@ -2571,7 +2558,7 @@ processQueryTargets(struct module_qstate* qstate, struct iter_qstate* iq,
}
if(!ie->supports_ipv6)
delegpt_no_ipv6(iq->dp);
if(!ie->supports_ipv4 && !ie->use_nat64)
if(!ie->supports_ipv4 && !ie->nat64.use_nat64)
delegpt_no_ipv4(iq->dp);
delegpt_log(VERB_ALGO, iq->dp);
@ -3070,9 +3057,9 @@ processQueryTargets(struct module_qstate* qstate, struct iter_qstate* iq,
real_addr = target->addr;
real_addrlen = target->addrlen;
if(ie->use_nat64 && target->addr.ss_family == AF_INET) {
addr_to_nat64(&target->addr, &ie->nat64_prefix_addr,
ie->nat64_prefix_addrlen, ie->nat64_prefix_net,
if(ie->nat64.use_nat64 && target->addr.ss_family == AF_INET) {
addr_to_nat64(&target->addr, &ie->nat64.nat64_prefix_addr,
ie->nat64.nat64_prefix_addrlen, ie->nat64.nat64_prefix_net,
&real_addr, &real_addrlen);
log_name_addr(VERB_QUERY, "applied NAT64:",
iq->dp->name, &real_addr, real_addrlen);
@ -3882,7 +3869,7 @@ processTargetResponse(struct module_qstate* qstate, int id,
} else {
verbose(VERB_ALGO, "iterator TargetResponse failed");
delegpt_mark_neg(dpns, qstate->qinfo.qtype);
if((dpns->got4 == 2 || (!ie->supports_ipv4 && !ie->use_nat64)) &&
if((dpns->got4 == 2 || (!ie->supports_ipv4 && !ie->nat64.use_nat64)) &&
(dpns->got6 == 2 || !ie->supports_ipv6)) {
dpns->resolved = 1; /* fail the target */
/* do not count cached answers */

View file

@ -46,8 +46,6 @@
#include "util/data/msgreply.h"
#include "util/module.h"
struct delegpt;
struct iter_hints;
struct iter_forwards;
struct iter_donotq;
struct iter_prep_list;
struct iter_priv;
@ -108,15 +106,9 @@ extern int BLACKLIST_PENALTY;
#define EMPTY_NODATA_RETRY_COUNT 2
/**
* Global state for the iterator.
* Iterator global state for nat64.
*/
struct iter_env {
/** A flag to indicate whether or not we have an IPv6 route */
int supports_ipv6;
/** A flag to indicate whether or not we have an IPv4 route */
int supports_ipv4;
struct iter_nat64 {
/** A flag to locally apply NAT64 to make IPv4 addrs into IPv6 */
int use_nat64;
@ -128,6 +120,20 @@ struct iter_env {
/** CIDR mask length of NAT64 prefix */
int nat64_prefix_net;
};
/**
* Global state for the iterator.
*/
struct iter_env {
/** A flag to indicate whether or not we have an IPv6 route */
int supports_ipv6;
/** A flag to indicate whether or not we have an IPv4 route */
int supports_ipv4;
/** State for nat64 */
struct iter_nat64 nat64;
/** A set of inetaddrs that should never be queried. */
struct iter_donotq* donotq;

View file

@ -1059,6 +1059,20 @@ void dtio_mainfdcallback(int ATTR_UNUSED(fd), short ATTR_UNUSED(ev),
}
#endif
void fast_reload_service_cb(int ATTR_UNUSED(fd), short ATTR_UNUSED(ev),
void* ATTR_UNUSED(arg))
{
log_assert(0);
}
int fast_reload_client_callback(struct comm_point* ATTR_UNUSED(c),
void* ATTR_UNUSED(arg), int ATTR_UNUSED(error),
struct comm_reply* ATTR_UNUSED(repinfo))
{
log_assert(0);
return 0;
}
#ifdef HAVE_NGTCP2
void doq_client_event_cb(int ATTR_UNUSED(fd), short ATTR_UNUSED(ev),
void* ATTR_UNUSED(arg))

View file

@ -105,6 +105,7 @@ respip_sockaddr_find_or_create(struct respip_set* set, struct sockaddr_storage*
socklen_t addrlen, int net, int create, const char* ipstr)
{
struct resp_addr* node;
log_assert(set);
node = (struct resp_addr*)addr_tree_find(&set->ip_tree, addr, addrlen, net);
if(!node && create) {
node = regional_alloc_zero(set->region, sizeof(*node));
@ -128,6 +129,7 @@ void
respip_sockaddr_delete(struct respip_set* set, struct resp_addr* node)
{
struct resp_addr* prev;
log_assert(set);
prev = (struct resp_addr*)rbtree_previous((struct rbnode_type*)node);
lock_rw_destroy(&node->lock);
(void)rbtree_delete(&set->ip_tree, node);
@ -146,6 +148,7 @@ respip_find_or_create(struct respip_set* set, const char* ipstr, int create)
struct sockaddr_storage addr;
int net;
socklen_t addrlen;
log_assert(set);
if(!netblockstrtoaddr(ipstr, 0, &addr, &addrlen, &net)) {
log_err("cannot parse netblock: '%s'", ipstr);
@ -160,6 +163,7 @@ respip_tag_cfg(struct respip_set* set, const char* ipstr,
const uint8_t* taglist, size_t taglen)
{
struct resp_addr* node;
log_assert(set);
if(!(node=respip_find_or_create(set, ipstr, 1)))
return 0;
@ -183,6 +187,7 @@ respip_action_cfg(struct respip_set* set, const char* ipstr,
{
struct resp_addr* node;
enum respip_action action;
log_assert(set);
if(!(node=respip_find_or_create(set, ipstr, 1)))
return 0;
@ -325,6 +330,7 @@ static int
respip_data_cfg(struct respip_set* set, const char* ipstr, const char* rrstr)
{
struct resp_addr* node;
log_assert(set);
node=respip_find_or_create(set, ipstr, 0);
if(!node || node->action == respip_none) {
@ -344,6 +350,7 @@ respip_set_apply_cfg(struct respip_set* set, char* const* tagname, int num_tags,
struct config_strbytelist* p;
struct config_str2list* pa;
struct config_str2list* pd;
log_assert(set);
set->tagname = tagname;
set->num_tags = num_tags;
@ -609,6 +616,7 @@ respip_addr_lookup(const struct reply_info *rep, struct respip_set* rs,
struct resp_addr* ra;
struct sockaddr_storage ss;
socklen_t addrlen;
log_assert(rs);
lock_rw_rdlock(&rs->lock);
for(i=0; i<rep->an_numrrsets; i++) {
@ -867,7 +875,8 @@ respip_rewrite_reply(const struct query_info* qinfo,
const struct respip_client_info* cinfo, const struct reply_info* rep,
struct reply_info** new_repp, struct respip_action_info* actinfo,
struct ub_packed_rrset_key** alias_rrset, int search_only,
struct regional* region, struct auth_zones* az, int* rpz_passthru)
struct regional* region, struct auth_zones* az, int* rpz_passthru,
struct views* views, struct respip_set* ipset)
{
const uint8_t* ctaglist;
size_t ctaglen;
@ -876,7 +885,6 @@ respip_rewrite_reply(const struct query_info* qinfo,
struct config_strlist** tag_datas;
size_t tag_datas_size;
struct view* view = NULL;
struct respip_set* ipset = NULL;
size_t rrset_id = 0, rr_id = 0;
enum respip_action action = respip_none;
int tag = -1;
@ -899,8 +907,20 @@ respip_rewrite_reply(const struct query_info* qinfo,
tag_actions_size = cinfo->tag_actions_size;
tag_datas = cinfo->tag_datas;
tag_datas_size = cinfo->tag_datas_size;
view = cinfo->view;
ipset = cinfo->respip_set;
if(cinfo->view) {
view = cinfo->view;
lock_rw_rdlock(&view->lock);
} else if(cinfo->view_name) {
view = views_find_view(views, cinfo->view_name, 0);
if(!view) {
/* If the view no longer exists, the rewrite can not
* be processed further. */
verbose(VERB_ALGO, "respip: failed because view %s no "
"longer exists", cinfo->view_name);
return 0;
}
/* The view is rdlocked by views_find_view. */
}
log_assert(ipset);
@ -915,7 +935,6 @@ respip_rewrite_reply(const struct query_info* qinfo,
* Note also that we assume 'view' is valid in this function, which
* should be safe (see unbound bug #1191) */
if(view) {
lock_rw_rdlock(&view->lock);
if(view->respip_set) {
if((raddr = respip_addr_lookup(rep,
view->respip_set, &rrset_id, &rr_id))) {
@ -1101,7 +1120,8 @@ respip_operate(struct module_qstate* qstate, enum module_ev event, int id,
qstate->client_info, qstate->return_msg->rep,
&new_rep, &actinfo, &alias_rrset, 0,
qstate->region, qstate->env->auth_zones,
&qstate->rpz_passthru)) {
&qstate->rpz_passthru, qstate->env->views,
qstate->env->respip_set)) {
goto servfail;
}
if(actinfo.action != respip_none) {
@ -1149,7 +1169,8 @@ respip_merge_cname(struct reply_info* base_rep,
const struct query_info* qinfo, const struct reply_info* tgt_rep,
const struct respip_client_info* cinfo, int must_validate,
struct reply_info** new_repp, struct regional* region,
struct auth_zones* az)
struct auth_zones* az, struct views* views,
struct respip_set* respip_set)
{
struct reply_info* new_rep;
struct reply_info* tmp_rep = NULL; /* just a placeholder */
@ -1176,7 +1197,7 @@ respip_merge_cname(struct reply_info* base_rep,
/* see if the target reply would be subject to a response-ip action. */
if(!respip_rewrite_reply(qinfo, cinfo, tgt_rep, &tmp_rep, &actinfo,
&alias_rrset, 1, region, az, NULL))
&alias_rrset, 1, region, az, NULL, views, respip_set))
return 0;
if(actinfo.action != respip_none) {
log_info("CNAME target of redirect response-ip action would "
@ -1229,7 +1250,8 @@ respip_inform_super(struct module_qstate* qstate, int id,
if(!respip_merge_cname(super->return_msg->rep, &qstate->qinfo,
qstate->return_msg->rep, super->client_info,
super->env->need_to_validate, &new_rep, super->region,
qstate->env->auth_zones))
qstate->env->auth_zones, qstate->env->views,
qstate->env->respip_set))
goto fail;
super->return_msg->rep = new_rep;
return;
@ -1326,3 +1348,35 @@ respip_inform_print(struct respip_action_info* respip_actinfo, uint8_t* qname,
(actionstr) ? actionstr : "inform", srcip, port);
log_nametypeclass(NO_VERBOSE, txt, qname, qtype, qclass);
}
size_t respip_set_get_mem(struct respip_set* set)
{
size_t m;
if(!set) return 0;
m = sizeof(*set);
lock_rw_rdlock(&set->lock);
m += regional_get_mem(set->region);
lock_rw_unlock(&set->lock);
return m;
}
void
respip_set_swap_tree(struct respip_set* respip_set,
struct respip_set* data)
{
rbnode_type* oldroot = respip_set->ip_tree.root;
size_t oldcount = respip_set->ip_tree.count;
struct regional* oldregion = respip_set->region;
char* const* oldtagname = respip_set->tagname;
int oldnum_tags = respip_set->num_tags;
respip_set->ip_tree.root = data->ip_tree.root;
respip_set->ip_tree.count = data->ip_tree.count;
respip_set->region = data->region;
respip_set->tagname = data->tagname;
respip_set->num_tags = data->num_tags;
data->ip_tree.root = oldroot;
data->ip_tree.count = oldcount;
data->region = oldregion;
data->tagname = oldtagname;
data->num_tags = oldnum_tags;
}

View file

@ -23,7 +23,8 @@
struct respip_set {
struct regional* region;
struct rbtree_type ip_tree;
lock_rw_type lock; /* lock on the respip tree */
lock_rw_type lock; /* lock on the respip tree. It is ordered
after views and before hints, stubs and local zones. */
char* const* tagname; /* shallow copy of tag names, for logging */
int num_tags; /* number of tagname entries */
};
@ -59,7 +60,6 @@ struct respip_addr_info;
* This is essentially a subset of acl_addr (except for respip_set) but
* defined as a separate structure to avoid dependency on the daemon-specific
* structure.
* respip_set is supposed to refer to the response-ip set for the global view.
*/
struct respip_client_info {
uint8_t* taglist;
@ -68,8 +68,12 @@ struct respip_client_info {
size_t tag_actions_size;
struct config_strlist** tag_datas;
size_t tag_datas_size;
/** The view for the action, during cache callback that is by
* pointer. */
struct view* view;
struct respip_set* respip_set;
/** If from module query state, the view pointer is NULL, but the
* name is stored in reference to the view. */
char* view_name;
};
/**
@ -149,13 +153,16 @@ int respip_views_apply_cfg(struct views* vs, struct config_file* cfg,
* on error.
* @param region: allocator to build *new_repp.
* @param az: auth zones containing RPZ information.
* @param views: views tree to lookup view used.
* @param respip_set: the respip set for the global view.
* @return 1 on success, 0 on error.
*/
int respip_merge_cname(struct reply_info* base_rep,
const struct query_info* qinfo, const struct reply_info* tgt_rep,
const struct respip_client_info* cinfo, int must_validate,
struct reply_info** new_repp, struct regional* region,
struct auth_zones* az);
struct auth_zones* az, struct views* views,
struct respip_set* respip_set);
/**
* See if any IP-based action should apply to any IP address of AAAA/A answer
@ -178,6 +185,8 @@ int respip_merge_cname(struct reply_info* base_rep,
* @param region: allocator to build *new_repp.
* @param rpz_passthru: keeps track of query state can have passthru that
* stops further rpz processing. Or NULL for cached answer processing.
* @param views: views tree to lookup view used.
* @param ipset: the respip set for the global view.
* @return 1 on success, 0 on error.
*/
int respip_rewrite_reply(const struct query_info* qinfo,
@ -186,7 +195,7 @@ int respip_rewrite_reply(const struct query_info* qinfo,
struct respip_action_info* actinfo,
struct ub_packed_rrset_key** alias_rrset,
int search_only, struct regional* region, struct auth_zones* az,
int* rpz_passthru);
int* rpz_passthru, struct views* views, struct respip_set* ipset);
/**
* Get the response-ip function block.
@ -302,4 +311,18 @@ respip_sockaddr_delete(struct respip_set* set, struct resp_addr* node);
struct ub_packed_rrset_key*
respip_copy_rrset(const struct ub_packed_rrset_key* key, struct regional* region);
/** Get memory usage of respip set tree. The routine locks and unlocks the
* set for reading. */
size_t respip_set_get_mem(struct respip_set* set);
/**
* Swap internal tree with preallocated entries. Caller should manage
* the locks.
* @param respip_set: response ip tree
* @param data: preallocated information.
*/
void respip_set_swap_tree(struct respip_set* respip_set,
struct respip_set* data);
#endif /* RESPIP_RESPIP_H */

View file

@ -2317,9 +2317,6 @@ auth_free_masters(struct auth_master* list)
}
}
/** delete auth xfer structure
* @param xfr: delete this xfer and its tasks.
*/
void
auth_xfer_delete(struct auth_xfer* xfr)
{
@ -7006,6 +7003,18 @@ xfr_set_timeout(struct auth_xfer* xfr, struct module_env* env,
comm_timer_set(xfr->task_nextprobe->timer, &tv);
}
void auth_xfer_pickup_initial_zone(struct auth_xfer* x, struct module_env* env)
{
/* set lease_time, because we now have timestamp in env,
* (not earlier during startup and apply_cfg), and this
* notes the start time when the data was acquired */
if(x->have_zone)
x->lease_time = *env->now;
if(x->task_nextprobe && x->task_nextprobe->worker == NULL) {
xfr_set_timeout(x, env, 0, 1);
}
}
/** initial pick up of worker timeouts, ties events to worker event loop */
void
auth_xfer_pickup_initial(struct auth_zones* az, struct module_env* env)
@ -7014,14 +7023,7 @@ auth_xfer_pickup_initial(struct auth_zones* az, struct module_env* env)
lock_rw_wrlock(&az->lock);
RBTREE_FOR(x, struct auth_xfer*, &az->xtree) {
lock_basic_lock(&x->lock);
/* set lease_time, because we now have timestamp in env,
* (not earlier during startup and apply_cfg), and this
* notes the start time when the data was acquired */
if(x->have_zone)
x->lease_time = *env->now;
if(x->task_nextprobe && x->task_nextprobe->worker == NULL) {
xfr_set_timeout(x, env, 0, 1);
}
auth_xfer_pickup_initial_zone(x, env);
lock_basic_unlock(&x->lock);
}
lock_rw_unlock(&az->lock);
@ -8580,3 +8582,161 @@ void auth_zones_pickup_zonemd_verify(struct auth_zones* az,
}
lock_rw_unlock(&az->lock);
}
/** Get memory usage of auth rrset */
static size_t
auth_rrset_get_mem(struct auth_rrset* rrset)
{
size_t m = sizeof(*rrset) + packed_rrset_sizeof(rrset->data);
return m;
}
/** Get memory usage of auth data */
static size_t
auth_data_get_mem(struct auth_data* node)
{
size_t m = sizeof(*node) + node->namelen;
struct auth_rrset* rrset;
for(rrset = node->rrsets; rrset; rrset = rrset->next) {
m += auth_rrset_get_mem(rrset);
}
return m;
}
/** Get memory usage of auth zone */
static size_t
auth_zone_get_mem(struct auth_zone* z)
{
size_t m = sizeof(*z) + z->namelen;
struct auth_data* node;
if(z->zonefile)
m += strlen(z->zonefile)+1;
RBTREE_FOR(node, struct auth_data*, &z->data) {
m += auth_data_get_mem(node);
}
if(z->rpz)
m += rpz_get_mem(z->rpz);
return m;
}
/** Get memory usage of list of auth addr */
static size_t
auth_addrs_get_mem(struct auth_addr* list)
{
size_t m = 0;
struct auth_addr* a;
for(a = list; a; a = a->next) {
m += sizeof(*a);
}
return m;
}
/** Get memory usage of list of primaries for auth xfer */
static size_t
auth_primaries_get_mem(struct auth_master* list)
{
size_t m = 0;
struct auth_master* n;
for(n = list; n; n = n->next) {
m += sizeof(*n);
m += auth_addrs_get_mem(n->list);
if(n->host)
m += strlen(n->host)+1;
if(n->file)
m += strlen(n->file)+1;
}
return m;
}
/** Get memory usage or list of auth chunks */
static size_t
auth_chunks_get_mem(struct auth_chunk* list)
{
size_t m = 0;
struct auth_chunk* chunk;
for(chunk = list; chunk; chunk = chunk->next) {
m += sizeof(*chunk) + chunk->len;
}
return m;
}
/** Get memory usage of auth xfer */
static size_t
auth_xfer_get_mem(struct auth_xfer* xfr)
{
size_t m = sizeof(*xfr) + xfr->namelen;
/* auth_nextprobe */
m += comm_timer_get_mem(xfr->task_nextprobe->timer);
/* auth_probe */
m += auth_primaries_get_mem(xfr->task_probe->masters);
m += comm_point_get_mem(xfr->task_probe->cp);
m += comm_timer_get_mem(xfr->task_probe->timer);
/* auth_transfer */
m += auth_chunks_get_mem(xfr->task_transfer->chunks_first);
m += auth_primaries_get_mem(xfr->task_transfer->masters);
m += comm_point_get_mem(xfr->task_transfer->cp);
m += comm_timer_get_mem(xfr->task_transfer->timer);
/* allow_notify_list */
m += auth_primaries_get_mem(xfr->allow_notify_list);
return m;
}
/** Get memory usage of auth zones ztree */
static size_t
az_ztree_get_mem(struct auth_zones* az)
{
size_t m = 0;
struct auth_zone* z;
RBTREE_FOR(z, struct auth_zone*, &az->ztree) {
lock_rw_rdlock(&z->lock);
m += auth_zone_get_mem(z);
lock_rw_unlock(&z->lock);
}
return m;
}
/** Get memory usage of auth zones xtree */
static size_t
az_xtree_get_mem(struct auth_zones* az)
{
size_t m = 0;
struct auth_xfer* xfr;
RBTREE_FOR(xfr, struct auth_xfer*, &az->xtree) {
lock_basic_lock(&xfr->lock);
m += auth_xfer_get_mem(xfr);
lock_basic_unlock(&xfr->lock);
}
return m;
}
size_t auth_zones_get_mem(struct auth_zones* zones)
{
size_t m;
if(!zones) return 0;
m = sizeof(*zones);
lock_rw_rdlock(&zones->rpz_lock);
lock_rw_rdlock(&zones->lock);
m += az_ztree_get_mem(zones);
m += az_xtree_get_mem(zones);
lock_rw_unlock(&zones->lock);
lock_rw_unlock(&zones->rpz_lock);
return m;
}
void xfr_disown_tasks(struct auth_xfer* xfr, struct worker* worker)
{
if(xfr->task_nextprobe->worker == worker) {
xfr_nextprobe_disown(xfr);
}
if(xfr->task_probe->worker == worker) {
xfr_probe_disown(xfr);
}
if(xfr->task_transfer->worker == worker) {
xfr_transfer_disown(xfr);
}
}

View file

@ -70,7 +70,8 @@ struct auth_chunk;
* Authoritative zones, shared.
*/
struct auth_zones {
/** lock on the authzone trees */
/** lock on the authzone trees. It is locked after views, respip,
* local_zones and before fwds and stubs. */
lock_rw_type lock;
/** rbtree of struct auth_zone */
rbtree_type ztree;
@ -207,7 +208,9 @@ struct auth_xfer {
* one of the tasks.
* Once it has the task assigned to it, the worker can access the
* other elements of the task structure without a lock, because that
* is necessary for the eventloop and callbacks from that. */
* is necessary for the eventloop and callbacks from that.
* The auth_zone->lock is locked before this lock.
*/
lock_basic_type lock;
/** zone name, in uncompressed wireformat */
@ -783,4 +786,33 @@ void auth_zonemd_dnskey_lookup_callback(void* arg, int rcode,
void auth_zones_pickup_zonemd_verify(struct auth_zones* az,
struct module_env* env);
/** Get memory usage for auth zones. The routine locks and unlocks
* for reading. */
size_t auth_zones_get_mem(struct auth_zones* zones);
/**
* Initial pick up of the auth zone nextprobe timeout and that turns
* into further zone transfer work, if any. Also sets the lease time.
* @param x: xfer structure, locked by caller.
* @param env: environment of the worker that picks up the task.
*/
void auth_xfer_pickup_initial_zone(struct auth_xfer* x,
struct module_env* env);
/**
* Delete auth xfer structure
* @param xfr: delete this xfer and its tasks.
*/
void auth_xfer_delete(struct auth_xfer* xfr);
/**
* Disown tasks from the xfr that belong to this worker.
* Only tasks for the worker in question, the comm point and timer
* delete functions need to run in the thread of that worker to be
* able to delete the callback from the event base.
* @param xfr: xfr structure
* @param worker: the worker for which to stop tasks.
*/
void xfr_disown_tasks(struct auth_xfer* xfr, struct worker* worker);
#endif /* SERVICES_AUTHZONE_H */

View file

@ -161,7 +161,7 @@ rate_deldatafunc(void* d, void* ATTR_UNUSED(arg))
/** find or create element in domainlimit tree */
static struct domain_limit_data* domain_limit_findcreate(
struct infra_cache* infra, char* name)
struct rbtree_type* domain_limits, char* name)
{
uint8_t* nm;
int labs;
@ -177,8 +177,8 @@ static struct domain_limit_data* domain_limit_findcreate(
labs = dname_count_labels(nm);
/* can we find it? */
d = (struct domain_limit_data*)name_tree_find(&infra->domain_limits,
nm, nmlen, labs, LDNS_RR_CLASS_IN);
d = (struct domain_limit_data*)name_tree_find(domain_limits, nm,
nmlen, labs, LDNS_RR_CLASS_IN);
if(d) {
free(nm);
return d;
@ -197,8 +197,8 @@ static struct domain_limit_data* domain_limit_findcreate(
d->node.dclass = LDNS_RR_CLASS_IN;
d->lim = -1;
d->below = -1;
if(!name_tree_insert(&infra->domain_limits, &d->node, nm, nmlen,
labs, LDNS_RR_CLASS_IN)) {
if(!name_tree_insert(domain_limits, &d->node, nm, nmlen, labs,
LDNS_RR_CLASS_IN)) {
log_err("duplicate element in domainlimit tree");
free(nm);
free(d);
@ -208,19 +208,19 @@ static struct domain_limit_data* domain_limit_findcreate(
}
/** insert rate limit configuration into lookup tree */
static int infra_ratelimit_cfg_insert(struct infra_cache* infra,
static int infra_ratelimit_cfg_insert(struct rbtree_type* domain_limits,
struct config_file* cfg)
{
struct config_str2list* p;
struct domain_limit_data* d;
for(p = cfg->ratelimit_for_domain; p; p = p->next) {
d = domain_limit_findcreate(infra, p->str);
d = domain_limit_findcreate(domain_limits, p->str);
if(!d)
return 0;
d->lim = atoi(p->str2);
}
for(p = cfg->ratelimit_below_domain; p; p = p->next) {
d = domain_limit_findcreate(infra, p->str);
d = domain_limit_findcreate(domain_limits, p->str);
if(!d)
return 0;
d->below = atoi(p->str2);
@ -228,24 +228,21 @@ static int infra_ratelimit_cfg_insert(struct infra_cache* infra,
return 1;
}
/** setup domain limits tree (0 on failure) */
static int
setup_domain_limits(struct infra_cache* infra, struct config_file* cfg)
int
setup_domain_limits(struct rbtree_type* domain_limits, struct config_file* cfg)
{
name_tree_init(&infra->domain_limits);
if(!infra_ratelimit_cfg_insert(infra, cfg)) {
name_tree_init(domain_limits);
if(!infra_ratelimit_cfg_insert(domain_limits, cfg)) {
return 0;
}
name_tree_init_parents(&infra->domain_limits);
name_tree_init_parents(domain_limits);
return 1;
}
/** find or create element in wait limit netblock tree */
static struct wait_limit_netblock_info*
wait_limit_netblock_findcreate(struct infra_cache* infra, char* str,
int cookie)
wait_limit_netblock_findcreate(struct rbtree_type* tree, char* str)
{
rbtree_type* tree;
struct sockaddr_storage addr;
int net;
socklen_t addrlen;
@ -257,10 +254,6 @@ wait_limit_netblock_findcreate(struct infra_cache* infra, char* str,
}
/* can we find it? */
if(cookie)
tree = &infra->wait_limits_cookie_netblock;
else
tree = &infra->wait_limits_netblock;
d = (struct wait_limit_netblock_info*)addr_tree_find(tree, &addr,
addrlen, net);
if(d)
@ -282,19 +275,21 @@ wait_limit_netblock_findcreate(struct infra_cache* infra, char* str,
/** insert wait limit information into lookup tree */
static int
infra_wait_limit_netblock_insert(struct infra_cache* infra,
struct config_file* cfg)
infra_wait_limit_netblock_insert(rbtree_type* wait_limits_netblock,
rbtree_type* wait_limits_cookie_netblock, struct config_file* cfg)
{
struct config_str2list* p;
struct wait_limit_netblock_info* d;
for(p = cfg->wait_limit_netblock; p; p = p->next) {
d = wait_limit_netblock_findcreate(infra, p->str, 0);
d = wait_limit_netblock_findcreate(wait_limits_netblock,
p->str);
if(!d)
return 0;
d->limit = atoi(p->str2);
}
for(p = cfg->wait_limit_cookie_netblock; p; p = p->next) {
d = wait_limit_netblock_findcreate(infra, p->str, 1);
d = wait_limit_netblock_findcreate(wait_limits_cookie_netblock,
p->str);
if(!d)
return 0;
d->limit = atoi(p->str2);
@ -302,16 +297,17 @@ infra_wait_limit_netblock_insert(struct infra_cache* infra,
return 1;
}
/** setup wait limits tree (0 on failure) */
static int
setup_wait_limits(struct infra_cache* infra, struct config_file* cfg)
int
setup_wait_limits(rbtree_type* wait_limits_netblock,
rbtree_type* wait_limits_cookie_netblock, struct config_file* cfg)
{
addr_tree_init(&infra->wait_limits_netblock);
addr_tree_init(&infra->wait_limits_cookie_netblock);
if(!infra_wait_limit_netblock_insert(infra, cfg))
addr_tree_init(wait_limits_netblock);
addr_tree_init(wait_limits_cookie_netblock);
if(!infra_wait_limit_netblock_insert(wait_limits_netblock,
wait_limits_cookie_netblock, cfg))
return 0;
addr_tree_init_parents(&infra->wait_limits_netblock);
addr_tree_init_parents(&infra->wait_limits_cookie_netblock);
addr_tree_init_parents(wait_limits_netblock);
addr_tree_init_parents(wait_limits_cookie_netblock);
return 1;
}
@ -344,11 +340,12 @@ infra_create(struct config_file* cfg)
return NULL;
}
/* insert config data into ratelimits */
if(!setup_domain_limits(infra, cfg)) {
if(!setup_domain_limits(&infra->domain_limits, cfg)) {
infra_delete(infra);
return NULL;
}
if(!setup_wait_limits(infra, cfg)) {
if(!setup_wait_limits(&infra->wait_limits_netblock,
&infra->wait_limits_cookie_netblock, cfg)) {
infra_delete(infra);
return NULL;
}
@ -373,12 +370,29 @@ static void domain_limit_free(rbnode_type* n, void* ATTR_UNUSED(arg))
}
}
void
domain_limits_free(struct rbtree_type* domain_limits)
{
if(!domain_limits)
return;
traverse_postorder(domain_limits, domain_limit_free, NULL);
}
/** delete wait_limit_netblock_info entries */
static void wait_limit_netblock_del(rbnode_type* n, void* ATTR_UNUSED(arg))
{
free(n);
}
void
wait_limits_free(struct rbtree_type* wait_limits_tree)
{
if(!wait_limits_tree)
return;
traverse_postorder(wait_limits_tree, wait_limit_netblock_del,
NULL);
}
void
infra_delete(struct infra_cache* infra)
{
@ -386,12 +400,10 @@ infra_delete(struct infra_cache* infra)
return;
slabhash_delete(infra->hosts);
slabhash_delete(infra->domain_rates);
traverse_postorder(&infra->domain_limits, domain_limit_free, NULL);
domain_limits_free(&infra->domain_limits);
slabhash_delete(infra->client_ip_rates);
traverse_postorder(&infra->wait_limits_netblock,
wait_limit_netblock_del, NULL);
traverse_postorder(&infra->wait_limits_cookie_netblock,
wait_limit_netblock_del, NULL);
wait_limits_free(&infra->wait_limits_netblock);
wait_limits_free(&infra->wait_limits_cookie_netblock);
free(infra);
}
@ -422,7 +434,7 @@ infra_adjust(struct infra_cache* infra, struct config_file* cfg)
/* reapply domain limits */
traverse_postorder(&infra->domain_limits, domain_limit_free,
NULL);
if(!setup_domain_limits(infra, cfg)) {
if(!setup_domain_limits(&infra->domain_limits, cfg)) {
infra_delete(infra);
return NULL;
}

View file

@ -515,6 +515,21 @@ void infra_wait_limit_inc(struct infra_cache* infra, struct comm_reply* rep,
void infra_wait_limit_dec(struct infra_cache* infra, struct comm_reply* rep,
struct config_file* cfg);
/** setup wait limits tree (0 on failure) */
int setup_wait_limits(struct rbtree_type* wait_limits_netblock,
struct rbtree_type* wait_limits_cookie_netblock,
struct config_file* cfg);
/** Free the wait limits and wait cookie limits tree. */
void wait_limits_free(struct rbtree_type* wait_limits_tree);
/** setup domain limits tree (0 on failure) */
int setup_domain_limits(struct rbtree_type* domain_limits,
struct config_file* cfg);
/** Free the domain limits tree. */
void domain_limits_free(struct rbtree_type* domain_limits);
/** exported for unit test */
int still_useful_timeout();

View file

@ -2220,3 +2220,35 @@ void local_zones_del_data(struct local_zones* zones,
lock_rw_unlock(&z->lock);
}
/** Get memory usage for local_zone */
static size_t
local_zone_get_mem(struct local_zone* z)
{
size_t m = sizeof(*z);
lock_rw_rdlock(&z->lock);
m += z->namelen + z->taglen + regional_get_mem(z->region);
lock_rw_unlock(&z->lock);
return m;
}
size_t local_zones_get_mem(struct local_zones* zones)
{
struct local_zone* z;
size_t m;
if(!zones) return 0;
m = sizeof(*zones);
lock_rw_rdlock(&zones->lock);
RBTREE_FOR(z, struct local_zone*, &zones->ztree) {
m += local_zone_get_mem(z);
}
lock_rw_unlock(&zones->lock);
return m;
}
void local_zones_swap_tree(struct local_zones* zones, struct local_zones* data)
{
rbtree_type oldtree = zones->ztree;
zones->ztree = data->ztree;
data->ztree = oldtree;
}

View file

@ -642,6 +642,20 @@ local_zone_enter_rr(struct local_zone* z, uint8_t* nm, size_t nmlen,
struct local_data*
local_zone_find_data(struct local_zone* z, uint8_t* nm, size_t nmlen, int nmlabs);
/** Get memory usage for local_zones tree. The routine locks and unlocks
* the tree for reading. */
size_t local_zones_get_mem(struct local_zones* zones);
/**
* Swap internal tree with preallocated entries. Caller should manage
* the locks.
* @param zones: the local zones structure.
* @param data: the data structure used to take elements from. This contains
* the old elements on return.
*/
void local_zones_swap_tree(struct local_zones* zones,
struct local_zones* data);
/** Enter a new zone; returns with WRlock
* Made public for unit testing
* @param zones: the local zones tree

View file

@ -77,6 +77,20 @@
#include <netdb.h>
#endif
/** Compare two views by name */
static int
view_name_compare(const char* v_a, const char* v_b)
{
if(v_a == NULL && v_b == NULL)
return 0;
/* The NULL name is smaller than if the name is set. */
if(v_a == NULL)
return -1;
if(v_b == NULL)
return 1;
return strcmp(v_a, v_b);
}
/**
* Compare two response-ip client info entries for the purpose of mesh state
* compare. It returns 0 if ci_a and ci_b are considered equal; otherwise
@ -132,12 +146,14 @@ client_info_compare(const struct respip_client_info* ci_a,
}
if(ci_a->tag_datas != ci_b->tag_datas)
return ci_a->tag_datas < ci_b->tag_datas ? -1 : 1;
if(ci_a->view != ci_b->view)
return ci_a->view < ci_b->view ? -1 : 1;
/* For the unbound daemon these should be non-NULL and identical,
* but we check that just in case. */
if(ci_a->respip_set != ci_b->respip_set)
return ci_a->respip_set < ci_b->respip_set ? -1 : 1;
if(ci_a->view || ci_a->view_name || ci_b->view || ci_b->view_name) {
/* Compare the views by name. */
cmp = view_name_compare(
(ci_a->view?ci_a->view->name:ci_a->view_name),
(ci_b->view?ci_b->view->name:ci_b->view_name));
if(cmp != 0)
return cmp;
}
return 0;
}
@ -870,6 +886,72 @@ void mesh_report_reply(struct mesh_area* mesh, struct outbound_entry* e,
mesh_run(mesh, e->qstate->mesh_info, event, e);
}
/** copy strlist to region */
static struct config_strlist*
cfg_region_strlist_copy(struct regional* region, struct config_strlist* list)
{
struct config_strlist* result = NULL, *last = NULL, *s = list;
while(s) {
struct config_strlist* n = regional_alloc_zero(region,
sizeof(*n));
if(!n)
return NULL;
n->str = regional_strdup(region, s->str);
if(!n->str)
return NULL;
if(last)
last->next = n;
else result = n;
last = n;
s = s->next;
}
return result;
}
/** Copy the client info to the query region. */
static struct respip_client_info*
mesh_copy_client_info(struct regional* region, struct respip_client_info* cinfo)
{
size_t i;
struct respip_client_info* client_info;
client_info = regional_alloc_init(region, cinfo, sizeof(*cinfo));
if(!client_info)
return NULL;
/* Copy the client_info so that if the configuration changes,
* then the data stays valid. */
client_info->taglist = regional_alloc_init(region, cinfo->taglist,
cinfo->taglen);
if(!client_info->taglist)
return NULL;
client_info->tag_actions = regional_alloc_init(region, cinfo->tag_actions,
cinfo->tag_actions_size);
if(!client_info->tag_actions)
return NULL;
client_info->tag_datas = regional_alloc_zero(region,
sizeof(struct config_strlist*)*cinfo->tag_datas_size);
if(!client_info->tag_datas)
return NULL;
for(i=0; i<cinfo->tag_datas_size; i++) {
if(cinfo->tag_datas[i]) {
client_info->tag_datas[i] = cfg_region_strlist_copy(
region, cinfo->tag_datas[i]);
if(!client_info->tag_datas[i])
return NULL;
}
}
if(cinfo->view) {
/* Do not copy the view pointer but store a name instead.
* The name is looked up later when done, this means that
* the view tree can be changed, by reloads. */
client_info->view = NULL;
client_info->view_name = regional_strdup(region,
cinfo->view->name);
if(!client_info->view_name)
return NULL;
}
return client_info;
}
struct mesh_state*
mesh_state_create(struct module_env* env, struct query_info* qinfo,
struct respip_client_info* cinfo, uint16_t qflags, int prime,
@ -910,8 +992,7 @@ mesh_state_create(struct module_env* env, struct query_info* qinfo,
return NULL;
}
if(cinfo) {
mstate->s.client_info = regional_alloc_init(region, cinfo,
sizeof(*cinfo));
mstate->s.client_info = mesh_copy_client_info(region, cinfo);
if(!mstate->s.client_info) {
alloc_reg_release(env->alloc, region);
return NULL;
@ -1686,6 +1767,25 @@ struct mesh_state* mesh_area_find(struct mesh_area* mesh,
return result;
}
/** remove mesh state callback */
int mesh_state_del_cb(struct mesh_state* s, mesh_cb_func_type cb, void* cb_arg)
{
struct mesh_cb* r, *prev = NULL;
r = s->cb_list;
while(r) {
if(r->cb == cb && r->cb_arg == cb_arg) {
/* Delete this entry. */
/* It was allocated in the s.region, so no free. */
if(prev) prev->next = r->next;
else s->cb_list = r->next;
return 1;
}
prev = r;
r = r->next;
}
return 0;
}
int mesh_state_add_cb(struct mesh_state* s, struct edns_data* edns,
sldns_buffer* buf, mesh_cb_func_type cb, void* cb_arg,
uint16_t qid, uint16_t qflags)
@ -2151,7 +2251,8 @@ apply_respip_action(struct module_qstate* qstate,
return 1;
if(!respip_rewrite_reply(qinfo, cinfo, rep, encode_repp, actinfo,
alias_rrset, 0, qstate->region, az, NULL))
alias_rrset, 0, qstate->region, az, NULL, qstate->env->views,
qstate->env->respip_set))
return 0;
/* xxx_deny actions mean dropping the reply, unless the original reply
@ -2226,7 +2327,8 @@ mesh_serve_expired_callback(void* arg)
} else if(partial_rep &&
!respip_merge_cname(partial_rep, &qstate->qinfo, msg->rep,
qstate->client_info, must_validate, &encode_rep, qstate->region,
qstate->env->auth_zones)) {
qstate->env->auth_zones, qstate->env->views,
qstate->env->respip_set)) {
return;
}
if(!encode_rep || alias_rrset) {
@ -2380,3 +2482,25 @@ int mesh_jostle_exceeded(struct mesh_area* mesh)
return 0;
return 1;
}
void mesh_remove_callback(struct mesh_area* mesh, struct query_info* qinfo,
uint16_t qflags, mesh_cb_func_type cb, void* cb_arg)
{
struct mesh_state* s = NULL;
s = mesh_area_find(mesh, NULL, qinfo, qflags&(BIT_RD|BIT_CD), 0, 0);
if(!s) return;
if(!mesh_state_del_cb(s, cb, cb_arg)) return;
/* It was in the list and removed. */
log_assert(mesh->num_reply_addrs > 0);
mesh->num_reply_addrs--;
if(!s->reply_list && !s->cb_list) {
/* was a reply state, not anymore */
log_assert(mesh->num_reply_states > 0);
mesh->num_reply_states--;
}
if(!s->reply_list && !s->cb_list &&
s->super_set.count == 0) {
mesh->num_detached_states++;
}
}

View file

@ -706,4 +706,17 @@ int mesh_jostle_exceeded(struct mesh_area* mesh);
*/
void mesh_respond_serve_expired(struct mesh_state* mstate);
/**
* Remove callback from mesh. Removes the callback from the state.
* The state itself is left to run. Searches for the pointer values.
*
* @param mesh: the mesh.
* @param qinfo: query from client.
* @param qflags: flags from client query.
* @param cb: callback function.
* @param cb_arg: callback user arg.
*/
void mesh_remove_callback(struct mesh_area* mesh, struct query_info* qinfo,
uint16_t qflags, mesh_cb_func_type cb, void* cb_arg);
#endif /* SERVICES_MESH_H */

View file

@ -2792,3 +2792,31 @@ void rpz_disable(struct rpz* r)
return;
r->disabled = 1;
}
/** Get memory usage for clientip_synthesized_rrset. Ignores memory usage
* of locks. */
static size_t
rpz_clientip_synthesized_set_get_mem(struct clientip_synthesized_rrset* set)
{
size_t m = sizeof(*set);
lock_rw_rdlock(&set->lock);
m += regional_get_mem(set->region);
lock_rw_unlock(&set->lock);
return m;
}
size_t rpz_get_mem(struct rpz* r)
{
size_t m = sizeof(*r);
if(r->taglist)
m += r->taglistlen;
if(r->log_name)
m += strlen(r->log_name) + 1;
m += regional_get_mem(r->region);
m += local_zones_get_mem(r->local_zones);
m += local_zones_get_mem(r->nsdname_zones);
m += respip_set_get_mem(r->respip_set);
m += rpz_clientip_synthesized_set_get_mem(r->client_set);
m += rpz_clientip_synthesized_set_get_mem(r->ns_set);
return m;
}

View file

@ -269,4 +269,11 @@ void rpz_enable(struct rpz* r);
*/
void rpz_disable(struct rpz* r);
/**
* Get memory usage of rpz. Caller must manage locks.
* @param r: RPZ struct.
* @return memory usage.
*/
size_t rpz_get_mem(struct rpz* r);
#endif /* SERVICES_RPZ_H */

View file

@ -43,6 +43,7 @@
#include "services/view.h"
#include "services/localzone.h"
#include "util/config_file.h"
#include "respip/respip.h"
int
view_cmp(const void* v1, const void* v2)
@ -66,11 +67,6 @@ views_create(void)
return v;
}
/* \noop (ignore this comment for doxygen)
* This prototype is defined in in respip.h, but we want to avoid
* unnecessary dependencies */
void respip_set_delete(struct respip_set *set);
void
view_delete(struct view* v)
{
@ -247,3 +243,38 @@ void views_print(struct views* v)
/* TODO implement print */
(void)v;
}
size_t views_get_mem(struct views* vs)
{
struct view* v;
size_t m;
if(!vs) return 0;
m = sizeof(struct views);
lock_rw_rdlock(&vs->lock);
RBTREE_FOR(v, struct view*, &vs->vtree) {
m += view_get_mem(v);
}
lock_rw_unlock(&vs->lock);
return m;
}
size_t view_get_mem(struct view* v)
{
size_t m = sizeof(*v);
lock_rw_rdlock(&v->lock);
m += getmem_str(v->name);
m += local_zones_get_mem(v->local_zones);
m += respip_set_get_mem(v->respip_set);
lock_rw_unlock(&v->lock);
return m;
}
void views_swap_tree(struct views* vs, struct views* data)
{
rbnode_type* oldroot = vs->vtree.root;
size_t oldcount = vs->vtree.count;
vs->vtree.root = data->vtree.root;
vs->vtree.count = data->vtree.count;
data->vtree.root = oldroot;
data->vtree.count = oldcount;
}

View file

@ -54,7 +54,8 @@ struct respip_set;
* Views storage, shared.
*/
struct views {
/** lock on the view tree */
/** lock on the view tree. When locking order, the views lock
* is before the forwards,hints,anchors,localzones lock. */
lock_rw_type lock;
/** rbtree of struct view */
rbtree_type vtree;
@ -135,4 +136,27 @@ void views_print(struct views* v);
*/
struct view* views_find_view(struct views* vs, const char* name, int write);
/**
* Calculate memory usage of views.
* @param vs: the views tree. The routine locks and unlocks the structure
* for reading.
* @return memory in bytes.
*/
size_t views_get_mem(struct views* vs);
/**
* Calculate memory usage of view.
* @param v: the view. The routine locks and unlocks the structure for reading.
* @return memory in bytes.
*/
size_t view_get_mem(struct view* v);
/**
* Swap internal tree with preallocated entries. Caller should manage
* the locks.
* @param vs: views tree
* @param data: preallocated information.
*/
void views_swap_tree(struct views* vs, struct views* data);
#endif /* SERVICES_VIEW_H */

View file

@ -109,6 +109,16 @@ usage(void)
printf(" That means the caches sizes and\n");
printf(" the number of threads must not\n");
printf(" change between reloads.\n");
printf(" fast_reload [+dpv] reloads the server but only briefly stops\n");
printf(" server processing, keeps cache, and changes\n");
printf(" most options; check unbound-control(8).\n");
printf(" +d drops running queries to keep consistency\n");
printf(" on changed options while reloading.\n");
printf(" +p does not pause threads for even faster\n");
printf(" reload but less options are supported\n");
printf(" ; check unbound-control(8).\n");
printf(" +v verbose output, it will include duration needed.\n");
printf(" +vv more verbose output, it will include memory needed.\n");
printf(" stats print statistics\n");
printf(" stats_noreset peek at statistics\n");
#ifdef HAVE_SHMGET

View file

@ -256,6 +256,20 @@ void dtio_mainfdcallback(int ATTR_UNUSED(fd), short ATTR_UNUSED(ev),
}
#endif
void fast_reload_service_cb(int ATTR_UNUSED(fd), short ATTR_UNUSED(ev),
void* ATTR_UNUSED(arg))
{
log_assert(0);
}
int fast_reload_client_callback(struct comm_point* ATTR_UNUSED(c),
void* ATTR_UNUSED(arg), int ATTR_UNUSED(error),
struct comm_reply* ATTR_UNUSED(repinfo))
{
log_assert(0);
return 0;
}
#ifdef HAVE_NGTCP2
void doq_client_event_cb(int ATTR_UNUSED(fd), short ATTR_UNUSED(ev),
void* ATTR_UNUSED(arg))

View file

@ -64,6 +64,9 @@ static int key_deleted = 0;
static ub_thread_key_type thr_debug_key;
/** the list of threads, so all threads can be examined. NULL if unused. */
static struct thr_check* thread_infos[THRDEBUG_MAX_THREADS];
/** stored maximum lock number for threads, when a thread is restarted the
* number is kept track of, because the new locks get new id numbers. */
static int thread_lockcount[THRDEBUG_MAX_THREADS];
/** do we check locking order */
int check_locking_order = 1;
/** the pid of this runset, reasonably unique. */
@ -698,10 +701,20 @@ open_lockorder(struct thr_check* thr)
char buf[24];
time_t t;
snprintf(buf, sizeof(buf), "%s.%d", output_name, thr->num);
thr->order_info = fopen(buf, "w");
if(!thr->order_info)
fatal_exit("could not open %s: %s", buf, strerror(errno));
thr->locks_created = 0;
thr->locks_created = thread_lockcount[thr->num];
if(thr->locks_created == 0) {
thr->order_info = fopen(buf, "w");
if(!thr->order_info)
fatal_exit("could not open %s: %s", buf, strerror(errno));
} else {
/* There is already a file to append on with the previous
* thread information. */
thr->order_info = fopen(buf, "a");
if(!thr->order_info)
fatal_exit("could not open for append %s: %s", buf, strerror(errno));
return;
}
t = time(NULL);
/* write: <time_stamp> <runpid> <thread_num> */
if(fwrite(&t, sizeof(t), 1, thr->order_info) != 1 ||
@ -728,6 +741,7 @@ static void* checklock_main(void* arg)
if(check_locking_order)
open_lockorder(thr);
ret = thr->func(thr->arg);
thread_lockcount[thr->num] = thr->locks_created;
thread_infos[thr->num] = NULL;
if(check_locking_order)
fclose(thr->order_info);

View file

@ -2699,3 +2699,17 @@ void dtio_mainfdcallback(int ATTR_UNUSED(fd), short ATTR_UNUSED(ev),
log_assert(0);
}
#endif
void fast_reload_service_cb(int ATTR_UNUSED(fd), short ATTR_UNUSED(ev),
void* ATTR_UNUSED(arg))
{
log_assert(0);
}
int fast_reload_client_callback(struct comm_point* ATTR_UNUSED(c),
void* ATTR_UNUSED(arg), int ATTR_UNUSED(error),
struct comm_reply* ATTR_UNUSED(repinfo))
{
log_assert(0);
return 0;
}

View file

@ -1484,6 +1484,11 @@ size_t comm_point_get_mem(struct comm_point* ATTR_UNUSED(c))
return 0;
}
size_t comm_timer_get_mem(struct comm_timer* ATTR_UNUSED(timer))
{
return 0;
}
size_t serviced_get_mem(struct serviced_query* ATTR_UNUSED(c))
{
return 0;
@ -1994,4 +1999,24 @@ void http2_stream_remove_mesh_state(struct http2_stream* ATTR_UNUSED(h2_stream))
{
}
void fast_reload_service_cb(int ATTR_UNUSED(fd), short ATTR_UNUSED(event),
void* ATTR_UNUSED(arg))
{
log_assert(0);
}
void fast_reload_thread_stop(
struct fast_reload_thread* ATTR_UNUSED(fast_reload_thread))
{
/* nothing */
}
int fast_reload_client_callback(struct comm_point* ATTR_UNUSED(c),
void* ATTR_UNUSED(arg), int ATTR_UNUSED(error),
struct comm_reply* ATTR_UNUSED(repinfo))
{
log_assert(0);
return 0;
}
/*********** End of Dummy routines ***********/

View file

@ -601,6 +601,17 @@ void listen_desetup_locks(void)
/* nothing */
}
void fast_reload_printq_list_delete(
struct fast_reload_printq* ATTR_UNUSED(list))
{
/* nothing */
}
void fast_reload_worker_pickup_changes(struct worker* ATTR_UNUSED(worker))
{
/* nothing */
}
#ifdef HAVE_NGTCP2
void* quic_sslctx_create(char* ATTR_UNUSED(key), char* ATTR_UNUSED(pem),
char* ATTR_UNUSED(verifypem))

View file

@ -0,0 +1,2 @@
@ SOA ns root 1 3600 300 7200 3600
www A 1.2.3.4

View file

@ -0,0 +1,2 @@
@ SOA ns root 1 3600 300 7200 3600
www A 1.2.3.5

View file

@ -0,0 +1,107 @@
server:
verbosity: 4
num-threads: 1
interface: 127.0.0.1
port: @PORT@
use-syslog: no
directory: ""
pidfile: "unbound.pid"
chroot: ""
username: ""
do-not-query-localhost: no
trust-anchor: "ta1.example.com DS 55566 8 2 9c148338951ce1c3b5cd3da532f3d90dfcf92595148022f2c2fd98e5deee90af"
trust-anchor: "ta2.example.com DS 55566 8 2 9c148338951ce1c3b5cd3da532f3d90dfcf92595148022f2c2fd98e5deee90af"
trust-anchor: "ta3.example.com DS 55566 8 2 9c148338951ce1c3b5cd3da532f3d90dfcf92595148022f2c2fd98e5deee90af"
domain-insecure: "insec1.ta1.example.com"
domain-insecure: "insec2.ta1.example.com"
domain-insecure: "insec3.ta1.example.com"
forward-zone:
name: "."
forward-addr: "127.0.0.1@12345"
remote-control:
control-enable: yes
control-interface: @CONTROL_PATH@/controlpipe.@CONTROL_PID@
control-use-cert: no
forward-zone:
name: "example1.org"
forward-addr: "127.0.0.1@@NS1_PORT@"
forward-zone:
name: "example2.org"
forward-addr: "127.0.0.1@@NS1_PORT@"
forward-zone:
name: "example3.org"
forward-addr: "127.0.0.1@@NS1_PORT@"
forward-zone:
name: "example4.org"
forward-addr: "127.0.0.1@@NS2_PORT@"
forward-zone:
name: "example5.org"
forward-addr: "127.0.0.1@@NS2_PORT@"
forward-zone:
name: "example6.org"
forward-addr: "127.0.0.1@@NS2_PORT@"
stub-zone:
name: "stub1.org"
stub-addr: "127.0.0.1@@NS1_PORT@"
stub-prime: no
stub-zone:
name: "stub2.org"
stub-addr: "127.0.0.1@@NS1_PORT@"
stub-prime: no
stub-zone:
name: "stub3.org"
stub-addr: "127.0.0.1@@NS1_PORT@"
stub-prime: no
stub-zone:
name: "stub4.org"
stub-addr: "127.0.0.1@@NS2_PORT@"
stub-prime: no
stub-zone:
name: "stub5.org"
stub-addr: "127.0.0.1@@NS2_PORT@"
stub-prime: no
stub-zone:
name: "stub6.org"
stub-addr: "127.0.0.1@@NS2_PORT@"
stub-prime: no
auth-zone:
name: "auth1.org"
zonefile: "auth1.zone"
auth-zone:
name: "auth2.org"
zonefile: "auth1.zone"
auth-zone:
name: "auth3.org"
zonefile: "auth1.zone"
auth-zone:
name: "auth5.org"
zonefile: "auth5.zone"
primary: 127.0.0.1@@NS1_PORT@
auth-zone:
name: "auth6.org"
zonefile: "auth6.zone"
primary: 127.0.0.1@@NS1_PORT@
auth-zone:
name: "auth7.org"
zonefile: "auth7.zone"
primary: 127.0.0.1@@NS1_PORT@

View file

@ -0,0 +1,108 @@
server:
verbosity: 4
num-threads: 1
interface: 127.0.0.1
port: @PORT@
use-syslog: no
directory: ""
pidfile: "unbound.pid"
chroot: ""
username: ""
do-not-query-localhost: no
trust-anchor: "ta1.example.com DS 55566 8 2 9c148338951ce1c3b5cd3da532f3d90dfcf92595148022f2c2fd98e5deee90af"
trust-anchor: "ta3.example.com DS 55567 8 2 9c148338951ce1c3b5cd3da532f3d90dfcf92595148022f2c2fd98e5deee90af"
trust-anchor: "ta4.example.com DS 55566 8 2 9c148338951ce1c3b5cd3da532f3d90dfcf92595148022f2c2fd98e5deee90af"
domain-insecure: "insec1.ta1.example.com"
domain-insecure: "insec3.ta1.example.com"
domain-insecure: "insec4.ta1.example.com"
forward-zone:
name: "."
# No addresses makes the server return SERVFAIL for deleted zones.
#forward-addr: "127.0.0.1@12345"
remote-control:
control-enable: yes
control-interface: @CONTROL_PATH@/controlpipe.@CONTROL_PID@
control-use-cert: no
forward-zone:
name: "example1.org"
forward-addr: "127.0.0.1@@NS2_PORT@"
forward-zone:
name: "example2.org"
forward-addr: "127.0.0.1@@NS1_PORT@"
forward-zone:
name: "example3.org"
forward-addr: "127.0.0.1@@NS2_PORT@"
forward-zone:
name: "example4.org"
forward-addr: "127.0.0.1@@NS1_PORT@"
forward-zone:
name: "example5.org"
forward-addr: "127.0.0.1@@NS2_PORT@"
forward-zone:
name: "example6.org"
forward-addr: "127.0.0.1@@NS1_PORT@"
stub-zone:
name: "stub1.org"
stub-addr: "127.0.0.1@@NS2_PORT@"
stub-prime: no
stub-zone:
name: "stub2.org"
stub-addr: "127.0.0.1@@NS1_PORT@"
stub-prime: no
stub-zone:
name: "stub3.org"
stub-addr: "127.0.0.1@@NS2_PORT@"
stub-prime: no
stub-zone:
name: "stub4.org"
stub-addr: "127.0.0.1@@NS1_PORT@"
stub-prime: no
stub-zone:
name: "stub5.org"
stub-addr: "127.0.0.1@@NS2_PORT@"
stub-prime: no
stub-zone:
name: "stub6.org"
stub-addr: "127.0.0.1@@NS1_PORT@"
stub-prime: no
auth-zone:
name: "auth1.org"
zonefile: "auth1.zone"
auth-zone:
name: "auth3.org"
zonefile: "auth2.zone"
auth-zone:
name: "auth4.org"
zonefile: "auth2.zone"
auth-zone:
name: "auth5.org"
zonefile: "auth5.zone"
primary: 127.0.0.1@@NS1_PORT@
auth-zone:
name: "auth7.org"
zonefile: "auth7.zone"
primary: 127.0.0.1@@NS2_PORT@
auth-zone:
name: "auth8.org"
zonefile: "auth8.zone"
primary: 127.0.0.1@@NS1_PORT@

View file

@ -0,0 +1,16 @@
BaseName: fast_reload_fwd
Version: 1.0
Description: Test fast reload change of forwards and stubs.
CreationDate: Thu Jan 22 11:55:55 CET 2024
Maintainer: dr. W.C.A. Wijngaards
Category:
Component:
CmdDepends:
Depends:
Help:
Pre: fast_reload_fwd.pre
Post: fast_reload_fwd.post
Test: fast_reload_fwd.test
AuxFiles:
Passed:
Failure:

View file

@ -0,0 +1,339 @@
; match A records and return a reply indicating it is this server.
ENTRY_BEGIN
MATCH opcode qtype qname
ADJUST copy_id
REPLY QR AA NOERROR
SECTION QUESTION
www.example1.org. IN A
SECTION ANSWER
www.example1.org. IN A 1.2.3.1
ENTRY_END
ENTRY_BEGIN
MATCH opcode qtype qname
ADJUST copy_id
REPLY QR AA NOERROR
SECTION QUESTION
www.example2.org. IN A
SECTION ANSWER
www.example2.org. IN A 1.2.3.1
ENTRY_END
ENTRY_BEGIN
MATCH opcode qtype qname
ADJUST copy_id
REPLY QR AA NOERROR
SECTION QUESTION
www.example3.org. IN A
SECTION ANSWER
www.example3.org. IN A 1.2.3.1
ENTRY_END
ENTRY_BEGIN
MATCH opcode qtype qname
ADJUST copy_id
REPLY QR AA NOERROR
SECTION QUESTION
www.example4.org. IN A
SECTION ANSWER
www.example4.org. IN A 1.2.3.1
ENTRY_END
ENTRY_BEGIN
MATCH opcode qtype qname
ADJUST copy_id
REPLY QR AA NOERROR
SECTION QUESTION
www.example5.org. IN A
SECTION ANSWER
www.example5.org. IN A 1.2.3.1
ENTRY_END
ENTRY_BEGIN
MATCH opcode qtype qname
ADJUST copy_id
REPLY QR AA NOERROR
SECTION QUESTION
www.example6.org. IN A
SECTION ANSWER
www.example6.org. IN A 1.2.3.1
ENTRY_END
ENTRY_BEGIN
MATCH opcode qtype qname
ADJUST copy_id
REPLY QR AA NOERROR
SECTION QUESTION
www2.example1.org. IN A
SECTION ANSWER
www2.example1.org. IN A 1.2.3.1
ENTRY_END
ENTRY_BEGIN
MATCH opcode qtype qname
ADJUST copy_id
REPLY QR AA NOERROR
SECTION QUESTION
www2.example2.org. IN A
SECTION ANSWER
www2.example2.org. IN A 1.2.3.1
ENTRY_END
ENTRY_BEGIN
MATCH opcode qtype qname
ADJUST copy_id
REPLY QR AA NOERROR
SECTION QUESTION
www2.example3.org. IN A
SECTION ANSWER
www2.example3.org. IN A 1.2.3.1
ENTRY_END
ENTRY_BEGIN
MATCH opcode qtype qname
ADJUST copy_id
REPLY QR AA NOERROR
SECTION QUESTION
www2.example4.org. IN A
SECTION ANSWER
www2.example4.org. IN A 1.2.3.1
ENTRY_END
ENTRY_BEGIN
MATCH opcode qtype qname
ADJUST copy_id
REPLY QR AA NOERROR
SECTION QUESTION
www2.example5.org. IN A
SECTION ANSWER
www2.example5.org. IN A 1.2.3.1
ENTRY_END
ENTRY_BEGIN
MATCH opcode qtype qname
ADJUST copy_id
REPLY QR AA NOERROR
SECTION QUESTION
www2.example6.org. IN A
SECTION ANSWER
www2.example6.org. IN A 1.2.3.1
ENTRY_END
ENTRY_BEGIN
MATCH opcode qtype qname
ADJUST copy_id
REPLY QR AA NOERROR
SECTION QUESTION
www.stub1.org. IN A
SECTION ANSWER
www.stub1.org. IN A 1.2.3.1
ENTRY_END
ENTRY_BEGIN
MATCH opcode qtype qname
ADJUST copy_id
REPLY QR AA NOERROR
SECTION QUESTION
www.stub2.org. IN A
SECTION ANSWER
www.stub2.org. IN A 1.2.3.1
ENTRY_END
ENTRY_BEGIN
MATCH opcode qtype qname
ADJUST copy_id
REPLY QR AA NOERROR
SECTION QUESTION
www.stub3.org. IN A
SECTION ANSWER
www.stub3.org. IN A 1.2.3.1
ENTRY_END
ENTRY_BEGIN
MATCH opcode qtype qname
ADJUST copy_id
REPLY QR AA NOERROR
SECTION QUESTION
www.stub4.org. IN A
SECTION ANSWER
www.stub4.org. IN A 1.2.3.1
ENTRY_END
ENTRY_BEGIN
MATCH opcode qtype qname
ADJUST copy_id
REPLY QR AA NOERROR
SECTION QUESTION
www.stub5.org. IN A
SECTION ANSWER
www.stub5.org. IN A 1.2.3.1
ENTRY_END
ENTRY_BEGIN
MATCH opcode qtype qname
ADJUST copy_id
REPLY QR AA NOERROR
SECTION QUESTION
www.stub6.org. IN A
SECTION ANSWER
www.stub6.org. IN A 1.2.3.1
ENTRY_END
ENTRY_BEGIN
MATCH opcode qtype qname
ADJUST copy_id
REPLY QR AA NOERROR
SECTION QUESTION
www2.stub1.org. IN A
SECTION ANSWER
www2.stub1.org. IN A 1.2.3.1
ENTRY_END
ENTRY_BEGIN
MATCH opcode qtype qname
ADJUST copy_id
REPLY QR AA NOERROR
SECTION QUESTION
www2.stub2.org. IN A
SECTION ANSWER
www2.stub2.org. IN A 1.2.3.1
ENTRY_END
ENTRY_BEGIN
MATCH opcode qtype qname
ADJUST copy_id
REPLY QR AA NOERROR
SECTION QUESTION
www2.stub3.org. IN A
SECTION ANSWER
www2.stub3.org. IN A 1.2.3.1
ENTRY_END
ENTRY_BEGIN
MATCH opcode qtype qname
ADJUST copy_id
REPLY QR AA NOERROR
SECTION QUESTION
www2.stub4.org. IN A
SECTION ANSWER
www2.stub4.org. IN A 1.2.3.1
ENTRY_END
ENTRY_BEGIN
MATCH opcode qtype qname
ADJUST copy_id
REPLY QR AA NOERROR
SECTION QUESTION
www2.stub5.org. IN A
SECTION ANSWER
www2.stub5.org. IN A 1.2.3.1
ENTRY_END
ENTRY_BEGIN
MATCH opcode qtype qname
ADJUST copy_id
REPLY QR AA NOERROR
SECTION QUESTION
www2.stub6.org. IN A
SECTION ANSWER
www2.stub6.org. IN A 1.2.3.1
ENTRY_END
ENTRY_BEGIN
MATCH opcode qtype qname
ADJUST copy_id
REPLY QR AA NOERROR
SECTION QUESTION
auth5.org. IN SOA
SECTION ANSWER
auth5.org. SOA ns root 1 3600 300 7200 3600
ENTRY_END
ENTRY_BEGIN
MATCH opcode qtype qname
ADJUST copy_id
REPLY QR AA NOERROR
SECTION QUESTION
auth5.org. IN AXFR
SECTION ANSWER
auth5.org. SOA ns root 1 3600 300 7200 3600
www.auth5.org. A 1.2.3.4
auth5.org. SOA ns root 1 3600 300 7200 3600
ENTRY_END
ENTRY_BEGIN
MATCH opcode qtype qname
ADJUST copy_id
REPLY QR AA NOERROR
SECTION QUESTION
auth6.org. IN SOA
SECTION ANSWER
auth6.org. SOA ns root 1 3600 300 7200 3600
ENTRY_END
ENTRY_BEGIN
MATCH opcode qtype qname
ADJUST copy_id
REPLY QR AA NOERROR
SECTION QUESTION
auth6.org. IN AXFR
SECTION ANSWER
auth6.org. SOA ns root 1 3600 300 7200 3600
www.auth6.org. A 1.2.3.4
auth6.org. SOA ns root 1 3600 300 7200 3600
ENTRY_END
ENTRY_BEGIN
MATCH opcode qtype qname
ADJUST copy_id
REPLY QR AA NOERROR
SECTION QUESTION
auth7.org. IN SOA
SECTION ANSWER
auth7.org. SOA ns root 1 3600 300 7200 3600
ENTRY_END
ENTRY_BEGIN
MATCH opcode qtype qname
ADJUST copy_id
REPLY QR AA NOERROR
SECTION QUESTION
auth7.org. IN AXFR
SECTION ANSWER
auth7.org. SOA ns root 1 3600 300 7200 3600
www.auth7.org. A 1.2.3.4
auth7.org. SOA ns root 1 3600 300 7200 3600
ENTRY_END
ENTRY_BEGIN
MATCH opcode qtype qname
ADJUST copy_id
REPLY QR AA NOERROR
SECTION QUESTION
auth8.org. IN SOA
SECTION ANSWER
auth8.org. SOA ns root 1 3600 300 7200 3600
ENTRY_END
ENTRY_BEGIN
MATCH opcode qtype qname
ADJUST copy_id
REPLY QR AA NOERROR
SECTION QUESTION
auth8.org. IN AXFR
SECTION ANSWER
auth8.org. SOA ns root 1 3600 300 7200 3600
www.auth8.org. A 1.2.3.4
auth8.org. SOA ns root 1 3600 300 7200 3600
ENTRY_END
; match anything and return a reply
ENTRY_BEGIN
MATCH opcode
ADJUST copy_id copy_query
REPLY QR AA NOERROR
SECTION QUESTION
example.org. IN SOA
SECTION AUTHORITY
example.org. IN SOA ns1.example.org. hostmaster.example.org. 1 3600 900 86400 3600
ENTRY_END

View file

@ -0,0 +1,285 @@
; match A records and return a reply indicating it is this server.
ENTRY_BEGIN
MATCH opcode qtype qname
ADJUST copy_id
REPLY QR AA NOERROR
SECTION QUESTION
www.example1.org. IN A
SECTION ANSWER
www.example1.org. IN A 1.2.3.2
ENTRY_END
ENTRY_BEGIN
MATCH opcode qtype qname
ADJUST copy_id
REPLY QR AA NOERROR
SECTION QUESTION
www.example2.org. IN A
SECTION ANSWER
www.example2.org. IN A 1.2.3.2
ENTRY_END
ENTRY_BEGIN
MATCH opcode qtype qname
ADJUST copy_id
REPLY QR AA NOERROR
SECTION QUESTION
www.example3.org. IN A
SECTION ANSWER
www.example3.org. IN A 1.2.3.2
ENTRY_END
ENTRY_BEGIN
MATCH opcode qtype qname
ADJUST copy_id
REPLY QR AA NOERROR
SECTION QUESTION
www.example4.org. IN A
SECTION ANSWER
www.example4.org. IN A 1.2.3.2
ENTRY_END
ENTRY_BEGIN
MATCH opcode qtype qname
ADJUST copy_id
REPLY QR AA NOERROR
SECTION QUESTION
www.example5.org. IN A
SECTION ANSWER
www.example5.org. IN A 1.2.3.2
ENTRY_END
ENTRY_BEGIN
MATCH opcode qtype qname
ADJUST copy_id
REPLY QR AA NOERROR
SECTION QUESTION
www.example6.org. IN A
SECTION ANSWER
www.example6.org. IN A 1.2.3.2
ENTRY_END
ENTRY_BEGIN
MATCH opcode qtype qname
ADJUST copy_id
REPLY QR AA NOERROR
SECTION QUESTION
www2.example1.org. IN A
SECTION ANSWER
www2.example1.org. IN A 1.2.3.2
ENTRY_END
ENTRY_BEGIN
MATCH opcode qtype qname
ADJUST copy_id
REPLY QR AA NOERROR
SECTION QUESTION
www2.example2.org. IN A
SECTION ANSWER
www2.example2.org. IN A 1.2.3.2
ENTRY_END
ENTRY_BEGIN
MATCH opcode qtype qname
ADJUST copy_id
REPLY QR AA NOERROR
SECTION QUESTION
www2.example3.org. IN A
SECTION ANSWER
www2.example3.org. IN A 1.2.3.2
ENTRY_END
ENTRY_BEGIN
MATCH opcode qtype qname
ADJUST copy_id
REPLY QR AA NOERROR
SECTION QUESTION
www2.example4.org. IN A
SECTION ANSWER
www2.example4.org. IN A 1.2.3.2
ENTRY_END
ENTRY_BEGIN
MATCH opcode qtype qname
ADJUST copy_id
REPLY QR AA NOERROR
SECTION QUESTION
www2.example5.org. IN A
SECTION ANSWER
www2.example5.org. IN A 1.2.3.2
ENTRY_END
ENTRY_BEGIN
MATCH opcode qtype qname
ADJUST copy_id
REPLY QR AA NOERROR
SECTION QUESTION
www2.example6.org. IN A
SECTION ANSWER
www2.example6.org. IN A 1.2.3.2
ENTRY_END
ENTRY_BEGIN
MATCH opcode qtype qname
ADJUST copy_id
REPLY QR AA NOERROR
SECTION QUESTION
www.stub1.org. IN A
SECTION ANSWER
www.stub1.org. IN A 1.2.3.2
ENTRY_END
ENTRY_BEGIN
MATCH opcode qtype qname
ADJUST copy_id
REPLY QR AA NOERROR
SECTION QUESTION
www.stub2.org. IN A
SECTION ANSWER
www.stub2.org. IN A 1.2.3.2
ENTRY_END
ENTRY_BEGIN
MATCH opcode qtype qname
ADJUST copy_id
REPLY QR AA NOERROR
SECTION QUESTION
www.stub3.org. IN A
SECTION ANSWER
www.stub3.org. IN A 1.2.3.2
ENTRY_END
ENTRY_BEGIN
MATCH opcode qtype qname
ADJUST copy_id
REPLY QR AA NOERROR
SECTION QUESTION
www.stub4.org. IN A
SECTION ANSWER
www.stub4.org. IN A 1.2.3.2
ENTRY_END
ENTRY_BEGIN
MATCH opcode qtype qname
ADJUST copy_id
REPLY QR AA NOERROR
SECTION QUESTION
www.stub5.org. IN A
SECTION ANSWER
www.stub5.org. IN A 1.2.3.2
ENTRY_END
ENTRY_BEGIN
MATCH opcode qtype qname
ADJUST copy_id
REPLY QR AA NOERROR
SECTION QUESTION
www.stub6.org. IN A
SECTION ANSWER
www.stub6.org. IN A 1.2.3.2
ENTRY_END
ENTRY_BEGIN
MATCH opcode qtype qname
ADJUST copy_id
REPLY QR AA NOERROR
SECTION QUESTION
www2.stub1.org. IN A
SECTION ANSWER
www2.stub1.org. IN A 1.2.3.2
ENTRY_END
ENTRY_BEGIN
MATCH opcode qtype qname
ADJUST copy_id
REPLY QR AA NOERROR
SECTION QUESTION
www2.stub2.org. IN A
SECTION ANSWER
www2.stub2.org. IN A 1.2.3.2
ENTRY_END
ENTRY_BEGIN
MATCH opcode qtype qname
ADJUST copy_id
REPLY QR AA NOERROR
SECTION QUESTION
www2.stub3.org. IN A
SECTION ANSWER
www2.stub3.org. IN A 1.2.3.2
ENTRY_END
ENTRY_BEGIN
MATCH opcode qtype qname
ADJUST copy_id
REPLY QR AA NOERROR
SECTION QUESTION
www2.stub4.org. IN A
SECTION ANSWER
www2.stub4.org. IN A 1.2.3.2
ENTRY_END
ENTRY_BEGIN
MATCH opcode qtype qname
ADJUST copy_id
REPLY QR AA NOERROR
SECTION QUESTION
www2.stub5.org. IN A
SECTION ANSWER
www2.stub5.org. IN A 1.2.3.2
ENTRY_END
ENTRY_BEGIN
MATCH opcode qtype qname
ADJUST copy_id
REPLY QR AA NOERROR
SECTION QUESTION
www2.stub6.org. IN A
SECTION ANSWER
www2.stub6.org. IN A 1.2.3.2
ENTRY_END
ENTRY_BEGIN
MATCH opcode qtype qname
ADJUST copy_id
REPLY QR AA NOERROR
SECTION QUESTION
auth7.org. IN SOA
SECTION ANSWER
auth7.org. SOA ns root 2 3600 300 7200 3600
ENTRY_END
ENTRY_BEGIN
MATCH opcode qtype qname
ADJUST copy_id
REPLY QR AA NOERROR
SECTION QUESTION
auth7.org. IN AXFR
SECTION ANSWER
auth7.org. SOA ns root 2 3600 300 7200 3600
www.auth7.org. A 1.2.3.5
auth7.org. SOA ns root 2 3600 300 7200 3600
ENTRY_END
ENTRY_BEGIN
MATCH opcode qtype qname
ADJUST copy_id
REPLY QR AA NOERROR
SECTION QUESTION
auth7.org. IN IXFR
SECTION ANSWER
auth7.org. SOA ns root 2 3600 300 7200 3600
www.auth7.org. A 1.2.3.5
auth7.org. SOA ns root 2 3600 300 7200 3600
ENTRY_END
; match anything and return a reply
ENTRY_BEGIN
MATCH opcode
ADJUST copy_id copy_query
REPLY QR AA NOERROR
SECTION QUESTION
example.org. IN SOA
SECTION AUTHORITY
example.org. IN SOA ns1.example.org. hostmaster.example.org. 1 3600 900 86400 3600
ENTRY_END

View file

@ -0,0 +1,25 @@
# #-- fast_reload_fwd.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
PRE="../.."
. ../common.sh
kill_pid $NS1_PID
kill_pid $NS2_PID
if test -f unbound.pid; then
kill_pid $UNBOUND_PID
fi
rm -f $CONTROL_PATH/controlpipe.$CONTROL_PID
echo
echo "> ns1.log"
cat ns1.log
echo
echo "> ns2.log"
cat ns2.log
echo
echo "> unbound.log"
cat unbound.log
exit 0

View file

@ -0,0 +1,56 @@
# #-- fast_reload_fwd.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
# if no threads; exit
if grep -e "define HAVE_PTHREAD 1" -e "define HAVE_SOLARIS_THREADS 1" -e "define HAVE_WINDOWS_THREADS 1" $PRE/config.h; then
echo "have threads"
else
skip_test "no threads"
fi
if grep -e "define ENABLE_LOCK_CHECKS 1" $PRE/config.h; then
get_make
echo "> (cd $PRE ; $MAKE lock-verify)"
(cd $PRE ; $MAKE lock-verify)
fi
get_random_port 3
UNBOUND_PORT=$RND_PORT
NS1_PORT=$(($RND_PORT + 1))
NS2_PORT=$(($RND_PORT + 2))
echo "UNBOUND_PORT=$UNBOUND_PORT" >> .tpkg.var.test
echo "NS1_PORT=$NS1_PORT" >> .tpkg.var.test
echo "NS2_PORT=$NS2_PORT" >> .tpkg.var.test
# make config files
CONTROL_PATH=/tmp
CONTROL_PID=$$
sed -e 's/@PORT\@/'$UNBOUND_PORT'/' -e 's/@NS1_PORT\@/'$NS1_PORT'/' -e 's/@NS2_PORT\@/'$NS2_PORT'/' -e 's?@CONTROL_PATH\@?'$CONTROL_PATH'?' -e 's/@CONTROL_PID@/'$CONTROL_PID'/' < fast_reload_fwd.conf > ub.conf
sed -e 's/@PORT\@/'$UNBOUND_PORT'/' -e 's/@NS1_PORT\@/'$NS1_PORT'/' -e 's/@NS2_PORT\@/'$NS2_PORT'/' -e 's?@CONTROL_PATH\@?'$CONTROL_PATH'?' -e 's/@CONTROL_PID@/'$CONTROL_PID'/' < fast_reload_fwd.conf2 > ub.conf2
# start forwarders
get_ldns_testns
$LDNS_TESTNS -p $NS1_PORT fast_reload_fwd.ns1 >ns1.log 2>&1 &
NS1_PID=$!
echo "NS1_PID=$NS1_PID" >> .tpkg.var.test
$LDNS_TESTNS -p $NS2_PORT fast_reload_fwd.ns2 >ns2.log 2>&1 &
NS2_PID=$!
echo "NS2_PID=$NS2_PID" >> .tpkg.var.test
# start unbound in the background
PRE="../.."
$PRE/unbound -d -c ub.conf >unbound.log 2>&1 &
UNBOUND_PID=$!
echo "UNBOUND_PID=$UNBOUND_PID" >> .tpkg.var.test
echo "CONTROL_PATH=$CONTROL_PATH" >> .tpkg.var.test
echo "CONTROL_PID=$CONTROL_PID" >> .tpkg.var.test
cat .tpkg.var.test
wait_ldns_testns_up ns1.log
wait_ldns_testns_up ns2.log
wait_unbound_up unbound.log

View file

@ -0,0 +1,320 @@
# #-- fast_reload_fwd.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="../.."
. ../common.sh
echo "> unbound-control status"
$PRE/unbound-control -c ub.conf status
if test $? -ne 0; then
echo "wrong exit value."
exit 1
else
echo "exit value: OK"
fi
# test that the forwards and stubs point to the right upstream.
for x in example1.org example2.org example3.org stub1.org stub2.org stub3.org; do
echo ""
echo "dig www.$x [upstream is NS1]"
dig @127.0.0.1 -p $UNBOUND_PORT www.$x A 2>&1 | tee outfile
if grep "1.2.3.1" outfile; then
echo "response OK"
else
echo "www.$x got the wrong answer"
exit 1
fi
done
for x in example4.org example5.org example6.org stub4.org stub5.org stub6.org; do
echo ""
echo "dig www.$x [upstream is NS2]"
dig @127.0.0.1 -p $UNBOUND_PORT www.$x A 2>&1 | tee outfile
if grep "1.2.3.2" outfile; then
echo "response OK"
else
echo "www.$x got the wrong answer"
exit 1
fi
done
for x in auth1.org auth2.org auth3.org auth5.org auth6.org auth7.org; do
echo ""
echo "dig www.$x [auth is 1.2.3.4]"
dig @127.0.0.1 -p $UNBOUND_PORT www.$x A 2>&1 | tee outfile
if grep "1.2.3.4" outfile; then
echo "response OK"
else
echo "www.$x got the wrong answer"
exit 1
fi
done
echo ""
echo "> list_insecure"
$PRE/unbound-control -c ub.conf list_insecure 2>&1 | tee output
if test $? -ne 0; then
echo "wrong exit value."
exit 1
fi
if grep "insec1.ta1.example.com" output >/dev/null; then :; else
echo "wrong output"
exit 1
fi
if grep "insec2.ta1.example.com" output >/dev/null; then :; else
echo "wrong output"
exit 1
fi
if grep "insec3.ta1.example.com" output >/dev/null; then :; else
echo "wrong output"
exit 1
fi
echo ""
echo "> trustanchor.unbound"
dig @127.0.0.1 -p $UNBOUND_PORT trustanchor.unbound CH TXT 2>&1 | tee outfile
if grep "ta1.example.com. 55566" outfile >/dev/null; then :; else
echo "wrong output ta1"
exit 1
fi
if grep "ta2.example.com. 55566" outfile >/dev/null; then :; else
echo "wrong output"
exit 1
fi
if grep "ta3.example.com. 55566" outfile >/dev/null; then :; else
echo "wrong output"
exit 1
fi
echo ""
echo "> replace config file ub.conf"
mv ub.conf ub.conf.orig
mv ub.conf2 ub.conf
echo ""
echo "> unbound-control fast_reload"
$PRE/unbound-control -c ub.conf fast_reload +vv 2>&1 | tee output
if test $? -ne 0; then
echo "wrong exit value."
exit 1
else
echo "exit value: OK"
fi
# for the previous digs to www.x the cached value should remain the same
# but for new lookups, to www2.x the new upstream should be used.
for x in example1.org example2.org example3.org stub1.org stub2.org stub3.org; do
echo ""
echo "dig www.$x [upstream is NS1]"
dig @127.0.0.1 -p $UNBOUND_PORT www.$x A 2>&1 | tee outfile
if grep "1.2.3.1" outfile; then
echo "response OK"
else
echo "www.$x got the wrong answer"
exit 1
fi
done
for x in example4.org example5.org example6.org stub4.org stub5.org stub6.org; do
echo ""
echo "dig www.$x [upstream is NS2]"
dig @127.0.0.1 -p $UNBOUND_PORT www.$x A 2>&1 | tee outfile
if grep "1.2.3.2" outfile; then
echo "response OK"
else
echo "www.$x got the wrong answer"
exit 1
fi
done
# new lookups for www2 go to the upstream.
for x in example2.org example4.org example6.org stub2.org stub4.org stub6.org; do
echo ""
echo "dig www2.$x [upstream is NS1]"
dig @127.0.0.1 -p $UNBOUND_PORT www2.$x A 2>&1 | tee outfile
if grep "1.2.3.1" outfile; then
echo "response OK"
else
echo "www2.$x got the wrong answer"
exit 1
fi
done
for x in example1.org example3.org example5.org stub1.org stub3.org stub5.org; do
echo ""
echo "dig www2.$x [upstream is NS2]"
dig @127.0.0.1 -p $UNBOUND_PORT www2.$x A 2>&1 | tee outfile
if grep "1.2.3.2" outfile; then
echo "response OK"
else
echo "www2.$x got the wrong answer"
exit 1
fi
done
# auth is unchanged, or at ns1.
for x in auth1.org auth5.org auth8.org; do
echo ""
echo "dig www.$x [auth is 1.2.3.4]"
dig @127.0.0.1 -p $UNBOUND_PORT www.$x A 2>&1 | tee outfile
if grep "1.2.3.4" outfile; then
echo "response OK"
else
echo "www.$x got the wrong answer"
exit 1
fi
done
# deleted auth
for x in auth2.org auth6.org; do
echo ""
echo "dig www.$x [auth is deleted]"
dig @127.0.0.1 -p $UNBOUND_PORT www.$x A 2>&1 | tee outfile
if grep "SERVFAIL" outfile; then
echo "response OK"
else
echo "www.$x got the wrong answer"
exit 1
fi
done
# changed and added auth
for x in auth3.org auth4.org auth7.org; do
echo ""
echo "dig www.$x [auth is 1.2.3.5]"
dig @127.0.0.1 -p $UNBOUND_PORT www.$x A 2>&1 | tee outfile
if grep "1.2.3.5" outfile; then
echo "response OK"
else
echo "www.$x got the wrong answer"
exit 1
fi
done
echo ""
echo "> list_insecure"
$PRE/unbound-control -c ub.conf list_insecure 2>&1 | tee output
if test $? -ne 0; then
echo "wrong exit value."
exit 1
fi
if grep "insec1.ta1.example.com" output >/dev/null; then :; else
echo "wrong output"
exit 1
fi
if grep "insec2.ta1.example.com" output >/dev/null; then
echo "wrong output"
exit 1
fi
if grep "insec3.ta1.example.com" output >/dev/null; then :; else
echo "wrong output"
exit 1
fi
if grep "insec4.ta1.example.com" output >/dev/null; then :; else
echo "wrong output"
exit 1
fi
echo ""
echo "> trustanchor.unbound"
dig @127.0.0.1 -p $UNBOUND_PORT trustanchor.unbound CH TXT 2>&1 | tee outfile
if grep "ta1.example.com. 55566" outfile >/dev/null; then :; else
echo "wrong output"
exit 1
fi
if grep "ta2.example.com. 55566" outfile >/dev/null; then
echo "wrong output"
exit 1
fi
if grep "ta3.example.com. 55566" outfile >/dev/null; then
echo "wrong output"
exit 1
fi
if grep "ta3.example.com. 55567" outfile >/dev/null; then :; else
echo "wrong output"
exit 1
fi
if grep "ta4.example.com. 55566" outfile >/dev/null; then :; else
echo "wrong output"
exit 1
fi
echo ""
echo "> test change: add tag1 tag2"
cp ub.conf ub.conf.orig2
echo "server:" >> ub.conf
echo ' define-tag: "tag1 tag2"' >> ub.conf
echo "> unbound-control fast_reload"
$PRE/unbound-control -c ub.conf fast_reload +vv 2>&1 | tee output
if test $? -ne 0; then
echo "wrong exit value."
exit 1
else
echo "exit value: OK"
fi
echo ""
echo "> test change: change to tag2 tag3"
cp ub.conf.orig2 ub.conf
echo "server:" >> ub.conf
echo ' define-tag: "tag2 tag3"' >> ub.conf
echo "> unbound-control fast_reload"
$PRE/unbound-control -c ub.conf fast_reload +vv 2>&1 | tee output
if test $? -ne 0; then
echo "wrong exit value."
exit 1
else
echo "exit value: OK"
fi
if grep "tags have changed" output; then
echo "output OK"
else
echo "wrong output"
exit 1
fi
echo ""
echo "> test change: change cache size"
cp ub.conf.orig2 ub.conf
echo "server:" >> ub.conf
echo " msg-cache-size: 10m" >> ub.conf
echo " rrset-cache-size: 5m" >> ub.conf
echo "> unbound-control fast_reload"
$PRE/unbound-control -c ub.conf fast_reload +vv 2>&1 | tee output
if test $? -ne 0; then
echo "wrong exit value."
exit 1
else
echo "exit value: OK"
fi
echo ""
echo "> test change: change nothing, +p too"
$PRE/unbound-control -c ub.conf fast_reload +vv +p 2>&1 | tee output
if test $? -ne 0; then
echo "wrong exit value."
exit 1
else
echo "exit value: OK"
fi
echo ""
echo "> stop unbound"
kill_pid $UNBOUND_PID
if test -f unbound.pid; then sleep 1; fi
if test -f unbound.pid; then sleep 1; fi
if test -f unbound.pid; then sleep 1; fi
if test -f unbound.pid; then echo "unbound.pid still there"; fi
# check the locks.
function locktest() {
if test -x $PRE/lock-verify -a -f ublocktrace.0; then
$PRE/lock-verify ublocktrace.*
if test $? -ne 0; then
echo "lock-verify error"
exit 1
fi
fi
}
locktest
exit 0

View file

@ -0,0 +1,3 @@
$ORIGIN auth.nlnetlabs.nl.
$TTL 60
@ IN SOA a b 1 2 3 4 5

View file

@ -0,0 +1,143 @@
# Try to define values for options that don't have "default" options that would
# trigger fast-reload functionality.
server:
verbosity: 4
num-threads: 4
interface: 127.0.0.1
interface: lo
port: @PORT@
interface-action: lo allow
use-syslog: no
directory: ""
pidfile: "unbound.pid"
chroot: ""
username: ""
do-not-query-localhost: no
module-config: "respip validator iterator"
outgoing-interface: 127.0.0.1
outgoing-port-avoid: "3200-3208"
define-tag: "tag1 tag2 tag3"
do-nat64: yes
nat64-prefix: 64:ff9b::0/96
dns64-prefix: 64:ff9b::0/96
dns64-ignore-aaaa: "ignore-aaaa.nlnetlabs.nl"
edns-tcp-keepalive: yes
response-ip: 192.0.2.0 always_refuse
access-control: 127.0.0.0/8 allow
access-control: ::1 allow
access-control-tag: 192.0.2.0/24 "tag2 tag3"
interface-tag: lo "tag2 tag3"
access-control-tag-action: 192.0.2.0/24 tag3 always_refuse
interface-tag-action: lo tag3 always_refuse
access-control-tag-data: 192.0.2.0/24 tag2 "A 127.0.0.1"
interface-tag-data: lo tag2 "A 127.0.0.1"
access-control-view: 192.0.2.0/24 viewname
interface-view: lo viewname
nsid: "ascii_something"
http-user-agent: "httpuseragent"
caps-exempt: "nlnetlabs.nl"
private-address: 10.0.0.0/8
private-address: 172.16.0.0/12
private-address: 192.168.0.0/16
private-address: 169.254.0.0/16
private-address: fd00::/8
private-address: fe80::/10
private-address: ::ffff:0:0/96
private-domain: "nlnetlabs.nl"
unwanted-reply-threshold: 10000000
do-not-query-address: 1.1.1.1
do-not-query-address: 8.8.8.8
do-not-query-address: 9.9.9.9
do-not-query-localhost: no
trust-anchor: "jelte.nlnetlabs.nl. DS 42860 5 1 14D739EB566D2B1A5E216A0BA4D17FA9B038BE4A"
domain-insecure: "nlnetlabs.nl"
serve-expired: yes
serve-expired-client-timeout: 1800
val-log-level: 2
local-zone: refuse.nlnetlabs.nl. refuse
local-zone: override.nlnetlabs.nl. deny
local-zone: tag.nlnetlabs.nl. transparent
local-data: "data.nlnetlabs.nl. TXT localdata"
local-data-ptr: "192.0.2.3 reverse.nlnetlabs.nl."
local-zone-tag: "tag.nlnetlabs.nl" "tag2 tag3"
local-zone-override: "override.nlnetlabs.nl" 192.0.2.0/24 refuse
ratelimit: 100
ratelimit-below-domain: ratelimit.nlnetlabs.nl 1000
ip-ratelimit: 100
tcp-connection-limit: 192.0.2.0/24 12
answer-cookie: yes
cookie-secret: "000102030405060708090a0b0c0d0e0f"
ede: yes
ede-serve-expired: yes
remote-control:
control-enable: yes
control-interface: @CONTROL_PATH@/controlpipe.@CONTROL_PID@
control-use-cert: no
stub-zone:
name: "stub.nlnetlabs.nl"
stub-addr: 192.0.2.68
stub-prime: no
stub-first: no
stub-tcp-upstream: no
stub-tls-upstream: no
stub-no-cache: no
forward-zone:
name: "forward.nlnetlabs.nl"
forward-addr: 192.0.2.68
forward-first: no
forward-tcp-upstream: no
forward-tls-upstream: no
forward-no-cache: no
auth-zone:
name: "auth.nlnetlabs.nl"
for-downstream: yes
for-upstream: yes
zonemd-check: no
zonemd-reject-absence: no
zonefile: "auth.nlnetlabs.nl.zone"
view:
name: "viewname"
local-zone: "view.nlnetlabs.nl" redirect
local-data: "view.nlnetlabs.nl A 192.0.2.3"
local-data-ptr: "192.0.2.3 view.nlnetlabs.nl"
view-first: no
rpz:
name: "rpz.nlnetlabs.nl"
zonefile: "rpz.nlnetlabs.nl.zone"
rpz-action-override: cname
rpz-cname-override: www.example.org
rpz-log: yes
rpz-log-name: "example policy"
rpz-signal-nxdomain-ra: no
for-downstream: no
tags: "tag3"

View file

@ -0,0 +1,16 @@
BaseName: fast_reload_most_options
Version: 1.0
Description: Test fast reload on high verbosity with most options.
CreationDate: Fri 28 Feb 2025 15:55:15 CET
Maintainer: Yorgos Thessalonikefs
Category:
Component:
CmdDepends:
Depends:
Help:
Pre: fast_reload_most_options.pre
Post: fast_reload_most_options.post
Test: fast_reload_most_options.test
AuxFiles:
Passed:
Failure:

View file

@ -0,0 +1,11 @@
# #-- fast_reload_most_options.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
rm -f $CONTROL_PATH/controlpipe.$CONTROL_PID
cat unbound.log

View file

@ -0,0 +1,33 @@
# #-- fast_reload_most_options.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
# if no threads; exit
if grep -e "define HAVE_PTHREAD 1" -e "define HAVE_SOLARIS_THREADS 1" -e "define HAVE_WINDOWS_THREADS 1" $PRE/config.h; then
echo "have threads"
else
skip_test "no threads"
fi
get_random_port 1
UNBOUND_PORT=$RND_PORT
echo "UNBOUND_PORT=$UNBOUND_PORT" >> .tpkg.var.test
# make config file
CONTROL_PATH=/tmp
CONTROL_PID=$$
sed -e 's/@PORT\@/'$UNBOUND_PORT'/' -e 's?@CONTROL_PATH\@?'$CONTROL_PATH'?' -e 's/@CONTROL_PID@/'$CONTROL_PID'/' < fast_reload_most_options.conf > ub.conf
# start unbound in the background
PRE="../.."
$PRE/unbound -d -c ub.conf >unbound.log 2>&1 &
UNBOUND_PID=$!
echo "UNBOUND_PID=$UNBOUND_PID" >> .tpkg.var.test
echo "CONTROL_PATH=$CONTROL_PATH" >> .tpkg.var.test
echo "CONTROL_PID=$CONTROL_PID" >> .tpkg.var.test
cat .tpkg.var.test
wait_unbound_up unbound.log

View file

@ -0,0 +1,42 @@
# #-- fast_reload_most_options.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="../.."
. ../common.sh
echo "> unbound-control status"
$PRE/unbound-control -c ub.conf status
if test $? -ne 0; then
echo "wrong exit value."
exit 1
else
echo "exit value: OK"
fi
for i in {1..10}
do
echo "> unbound-control fast_reload +vvdp ($i)"
$PRE/unbound-control -c ub.conf fast_reload +vvdp 2>&1 | tee output
if test $? -ne 0; then
echo "wrong exit value."
exit 1
else
echo "exit value: OK"
fi
wait_logfile unbound.log "start fast reload thread" 60
wait_logfile unbound.log "stop fast reload thread" 60
wait_logfile unbound.log "joined with fastreload thread" 60
if grep "ok" output; then
echo "OK"
else
echo "output not correct"
exit 1
fi
done
exit 0

View file

@ -0,0 +1,5 @@
$ORIGIN rpz.nlnetlabs.nl.
$TTL 60
@ IN SOA a b 1 2 3 4 5
nxdomain.nlnetlabs.nl IN CNAME .
rpzdata.nlnetlabs.nl IN A 0.0.0.0

View file

@ -0,0 +1,20 @@
server:
verbosity: 4
num-threads: 1
interface: 127.0.0.1
port: @PORT@
use-syslog: no
directory: ""
pidfile: "unbound.pid"
chroot: ""
username: ""
do-not-query-localhost: no
forward-zone:
name: "."
forward-addr: "127.0.0.1@12345"
remote-control:
control-enable: yes
control-interface: @CONTROL_PATH@/controlpipe.@CONTROL_PID@
control-use-cert: no

View file

@ -0,0 +1,16 @@
BaseName: fast_reload_thread
Version: 1.0
Description: Test fast reload thread output.
CreationDate: Thu Jan 4 09:25:55 CET 2024
Maintainer: dr. W.C.A. Wijngaards
Category:
Component:
CmdDepends:
Depends:
Help:
Pre: fast_reload_thread.pre
Post: fast_reload_thread.post
Test: fast_reload_thread.test
AuxFiles:
Passed:
Failure:

View file

@ -0,0 +1,11 @@
# #-- fast_reload_thread.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
rm -f $CONTROL_PATH/controlpipe.$CONTROL_PID
cat unbound.log

View file

@ -0,0 +1,34 @@
# #-- fast_reload_thread.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
# if no threads; exit
if grep -e "define HAVE_PTHREAD 1" -e "define HAVE_SOLARIS_THREADS 1" -e "define HAVE_WINDOWS_THREADS 1" $PRE/config.h; then
echo "have threads"
else
skip_test "no threads"
fi
get_random_port 1
UNBOUND_PORT=$RND_PORT
echo "UNBOUND_PORT=$UNBOUND_PORT" >> .tpkg.var.test
# make config file
CONTROL_PATH=/tmp
CONTROL_PID=$$
sed -e 's/@PORT\@/'$UNBOUND_PORT'/' -e 's?@CONTROL_PATH\@?'$CONTROL_PATH'?' -e 's/@CONTROL_PID@/'$CONTROL_PID'/' < fast_reload_thread.conf > ub.conf
# start unbound in the background
PRE="../.."
$PRE/unbound -d -c ub.conf >unbound.log 2>&1 &
UNBOUND_PID=$!
echo "UNBOUND_PID=$UNBOUND_PID" >> .tpkg.var.test
echo "CONTROL_PATH=$CONTROL_PATH" >> .tpkg.var.test
echo "CONTROL_PID=$CONTROL_PID" >> .tpkg.var.test
cat .tpkg.var.test
wait_unbound_up unbound.log

View file

@ -0,0 +1,38 @@
# #-- fast_reload_thread.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="../.."
. ../common.sh
echo "> unbound-control status"
$PRE/unbound-control -c ub.conf status
if test $? -ne 0; then
echo "wrong exit value."
exit 1
else
echo "exit value: OK"
fi
echo "> unbound-control fast_reload"
$PRE/unbound-control -c ub.conf fast_reload 2>&1 | tee output
if test $? -ne 0; then
echo "wrong exit value."
exit 1
else
echo "exit value: OK"
fi
wait_logfile unbound.log "start fast reload thread" 60
wait_logfile unbound.log "stop fast reload thread" 60
wait_logfile unbound.log "joined with fastreload thread" 60
if grep "ok" output; then
echo "OK"
else
echo "output not correct"
exit 1
fi
exit 0

View file

@ -1731,6 +1731,7 @@ config_delete(struct config_file* cfg)
config_del_strarray(cfg->tagname, cfg->num_tags);
config_del_strbytelist(cfg->local_zone_tags);
config_del_strbytelist(cfg->respip_tags);
config_deldblstrlist(cfg->respip_actions);
config_deldblstrlist(cfg->acl_view);
config_del_strbytelist(cfg->acl_tags);
config_deltrplstrlist(cfg->acl_tag_actions);
@ -2834,6 +2835,13 @@ if_is_dnscrypt(const char* ifname, int default_port, int dnscrypt_port)
#endif
}
size_t
getmem_str(char* str)
{
if(!str) return 0;
return strlen(str)+1;
}
int
if_is_quic(const char* ifname, int default_port, int quic_port)
{

View file

@ -1454,4 +1454,7 @@ int cfg_has_quic(struct config_file* cfg);
#define LINUX_IP_LOCAL_PORT_RANGE_PATH "/proc/sys/net/ipv4/ip_local_port_range"
#endif
/** get memory for string */
size_t getmem_str(char* str);
#endif /* UTIL_CONFIG_FILE_H */

View file

@ -131,6 +131,29 @@ edns_string_addr_lookup(rbtree_type* tree, struct sockaddr_storage* addr,
return (struct edns_string_addr*)addr_tree_lookup(tree, addr, addrlen);
}
size_t
edns_strings_get_mem(struct edns_strings* edns_strings)
{
if(!edns_strings) return 0;
return regional_get_mem(edns_strings->region) + sizeof(*edns_strings);
}
void
edns_strings_swap_tree(struct edns_strings* edns_strings,
struct edns_strings* data)
{
rbtree_type tree = edns_strings->client_strings;
uint16_t opcode = edns_strings->client_string_opcode;
struct regional* region = edns_strings->region;
edns_strings->client_strings = data->client_strings;
edns_strings->client_string_opcode = data->client_string_opcode;
edns_strings->region = data->region;
data->client_strings = tree;
data->client_string_opcode = opcode;
data->region = region;
}
uint8_t*
edns_cookie_server_hash(const uint8_t* in, const uint8_t* secret, int v4,
uint8_t* hash)

View file

@ -141,6 +141,22 @@ struct edns_string_addr*
edns_string_addr_lookup(rbtree_type* tree, struct sockaddr_storage* addr,
socklen_t addrlen);
/**
* Get memory usage of edns strings.
* @param edns_strings: the edns strings
* @return memory usage
*/
size_t edns_strings_get_mem(struct edns_strings* edns_strings);
/**
* Swap internal tree with preallocated entries.
* @param edns_strings: the edns strings structure.
* @param data: the data structure used to take elements from. This contains
* the old elements on return.
*/
void edns_strings_swap_tree(struct edns_strings* edns_strings,
struct edns_strings* data);
/**
* Compute the interoperable DNS cookie (RFC9018) hash.
* @param in: buffer input for the hash generation. It needs to be:

View file

@ -74,6 +74,7 @@
#include "libunbound/worker.h"
#include "util/tube.h"
#include "util/config_file.h"
#include "daemon/remote.h"
#ifdef UB_ON_WINDOWS
#include "winrc/win_svc.h"
#endif
@ -121,6 +122,7 @@ fptr_whitelist_comm_point_raw(comm_point_callback_type *fptr)
else if(fptr == &tube_handle_write) return 1;
else if(fptr == &remote_accept_callback) return 1;
else if(fptr == &remote_control_callback) return 1;
else if(fptr == &fast_reload_client_callback) return 1;
return 0;
}
@ -188,6 +190,7 @@ fptr_whitelist_event(void (*fptr)(int, short, void *))
#ifdef HAVE_NGTCP2
else if(fptr == &comm_point_doq_callback) return 1;
#endif
else if(fptr == &fast_reload_service_cb) return 1;
#ifdef USE_DNSTAP
else if(fptr == &dtio_output_cb) return 1;
else if(fptr == &dtio_cmd_cb) return 1;

View file

@ -177,6 +177,7 @@ struct val_anchors;
struct val_neg_cache;
struct iter_forwards;
struct iter_hints;
struct views;
struct respip_set;
struct respip_client_info;
struct respip_addr_info;
@ -524,6 +525,10 @@ struct module_env {
* data structure.
*/
struct iter_hints* hints;
/** views structure containing view tree */
struct views* views;
/** response-ip set with associated actions and tags. */
struct respip_set* respip_set;
/** module specific data. indexed by module id. */
void* modinfo[MAX_MODULE];

View file

@ -314,6 +314,11 @@ struct ub_event_base* comm_base_internal(struct comm_base* b)
return b->eb->base;
}
struct ub_event* comm_point_internal(struct comm_point* c)
{
return c->ev->ev;
}
/** see if errno for udp has to be logged or not uses globals */
static int
udp_send_errno_needs_log(struct sockaddr* addr, socklen_t addrlen)
@ -6917,8 +6922,9 @@ comm_timer_is_set(struct comm_timer* timer)
}
size_t
comm_timer_get_mem(struct comm_timer* ATTR_UNUSED(timer))
comm_timer_get_mem(struct comm_timer* timer)
{
if(!timer) return 0;
return sizeof(struct internal_timer);
}

View file

@ -547,6 +547,14 @@ void comm_base_set_slow_accept_handlers(struct comm_base* b,
*/
struct ub_event_base* comm_base_internal(struct comm_base* b);
/**
* Access internal event structure. It is for use with
* ub_winsock_tcp_wouldblock on windows.
* @param c: comm point.
* @return event.
*/
struct ub_event* comm_point_internal(struct comm_point* c);
/**
* Create an UDP comm point. Calls malloc.
* setups the structure with the parameters you provide.

View file

@ -562,6 +562,36 @@ lruhash_update_space_used(struct lruhash* table, void* cb_arg, int diff_size)
}
}
void lruhash_update_space_max(struct lruhash* table, void* cb_arg, size_t max)
{
struct lruhash_entry *reclaimlist = NULL;
fptr_ok(fptr_whitelist_hash_sizefunc(table->sizefunc));
fptr_ok(fptr_whitelist_hash_delkeyfunc(table->delkeyfunc));
fptr_ok(fptr_whitelist_hash_deldatafunc(table->deldatafunc));
fptr_ok(fptr_whitelist_hash_markdelfunc(table->markdelfunc));
if(cb_arg == NULL) cb_arg = table->cb_arg;
/* update space max */
lock_quick_lock(&table->lock);
table->space_max = max;
if(table->space_used > table->space_max)
reclaim_space(table, &reclaimlist);
lock_quick_unlock(&table->lock);
/* finish reclaim if any (outside of critical region) */
while(reclaimlist) {
struct lruhash_entry* n = reclaimlist->overflow_next;
void* d = reclaimlist->data;
(*table->delkeyfunc)(reclaimlist->key, cb_arg);
(*table->deldatafunc)(d, cb_arg);
reclaimlist = n;
}
}
void
lruhash_traverse(struct lruhash* h, int wr,
void (*func)(struct lruhash_entry*, void*), void* arg)

View file

@ -314,6 +314,16 @@ void lruhash_setmarkdel(struct lruhash* table, lruhash_markdelfunc_type md);
void lruhash_update_space_used(struct lruhash* table, void* cb_override,
int diff_size);
/**
* Update the max space for the hashtable.
*
* @param table: hash table.
* @param cb_override: if not NULL overrides the cb_arg for deletefunc.
* @param max: the new max.
*/
void lruhash_update_space_max(struct lruhash* table, void* cb_override,
size_t max);
/************************* getdns functions ************************/
/*** these are used by getdns only and not by unbound. ***/

View file

@ -267,3 +267,12 @@ void get_slabhash_stats(struct slabhash* sh, long long* num, long long* collisio
if (collisions != NULL)
*collisions = max_collisions;
}
void slabhash_adjust_size(struct slabhash* sl, size_t max)
{
size_t space_max = max / sl->size;
size_t i;
for(i=0; i<sl->size; i++) {
lruhash_update_space_max(sl->array[i], NULL, space_max);
}
}

View file

@ -221,6 +221,13 @@ size_t count_slabhash_entries(struct slabhash* table);
void get_slabhash_stats(struct slabhash* table,
long long* entries_count, long long* max_collisions);
/**
* Adjust size of slabhash memory max
* @param table: slabbed hash table
* @param max: new max memory
*/
void slabhash_adjust_size(struct slabhash* table, size_t max);
/* --- test representation --- */
/** test structure contains test key */
struct slabhash_testkey {

View file

@ -192,3 +192,14 @@ tcl_list_get_mem(struct tcl_list* tcl)
if(!tcl) return 0;
return sizeof(*tcl) + regional_get_mem(tcl->region);
}
void tcl_list_swap_tree(struct tcl_list* tcl, struct tcl_list* data)
{
/* swap tree and region */
rbtree_type oldtree = tcl->tree;
struct regional* oldregion = tcl->region;
tcl->tree = data->tree;
tcl->region = data->region;
data->tree = oldtree;
data->region = oldregion;
}

View file

@ -127,4 +127,13 @@ tcl_addr_lookup(struct tcl_list* tcl, struct sockaddr_storage* addr,
*/
size_t tcl_list_get_mem(struct tcl_list* tcl);
/**
* Swap internal tree with preallocated entries. Caller should manage
* tcl_addr item locks.
* @param tcl: the tcp connection list structure.
* @param data: the data structure used to take elements from. This contains
* the old elements on return.
*/
void tcl_list_swap_tree(struct tcl_list* tcl, struct tcl_list* data);
#endif /* DAEMON_TCP_CONN_LIMIT_H */

View file

@ -2035,25 +2035,40 @@ wait_probe_time(struct val_anchors* anchors)
return 0;
}
/** reset worker timer */
/** reset worker timer, at the time from wait_probe_time. */
static void
reset_worker_timer(struct module_env* env)
reset_worker_timer_at(struct module_env* env, time_t next)
{
struct timeval tv;
#ifndef S_SPLINT_S
time_t next = (time_t)wait_probe_time(env->anchors);
/* in case this is libunbound, no timer */
if(!env->probe_timer)
return;
if(next > *env->now)
tv.tv_sec = (time_t)(next - *env->now);
else tv.tv_sec = 0;
#else
(void)next;
#endif
tv.tv_usec = 0;
comm_timer_set(env->probe_timer, &tv);
verbose(VERB_ALGO, "scheduled next probe in " ARG_LL "d sec", (long long)tv.tv_sec);
}
/** reset worker timer. This routine manages the locks on acquiring the
* next time for the timer. */
static void
reset_worker_timer(struct module_env* env)
{
time_t next;
if(!env->anchors)
return;
lock_basic_lock(&env->anchors->lock);
next = wait_probe_time(env->anchors);
lock_basic_unlock(&env->anchors->lock);
reset_worker_timer_at(env, next);
}
/** set next probe for trust anchor */
static int
set_next_probe(struct module_env* env, struct trust_anchor* tp,
@ -2092,7 +2107,7 @@ set_next_probe(struct module_env* env, struct trust_anchor* tp,
verbose(VERB_ALGO, "next probe set in %d seconds",
(int)tp->autr->next_probe_time - (int)*env->now);
if(mold != mnew) {
reset_worker_timer(env);
reset_worker_timer_at(env, mnew);
}
return 1;
}
@ -2147,7 +2162,7 @@ autr_tp_remove(struct module_env* env, struct trust_anchor* tp,
autr_point_delete(del_tp);
}
if(mold != mnew) {
reset_worker_timer(env);
reset_worker_timer_at(env, mnew);
}
}

View file

@ -1173,17 +1173,53 @@ anchors_lookup(struct val_anchors* anchors,
return result;
}
/** Get memory usage of assembled key rrset */
static size_t
assembled_rrset_get_mem(struct ub_packed_rrset_key* pkey)
{
size_t s;
if(!pkey)
return 0;
s = sizeof(*pkey) + pkey->rk.dname_len;
if(pkey->entry.data) {
struct packed_rrset_data* pd = (struct packed_rrset_data*)
pkey->entry.data;
s += sizeof(*pd) + pd->count * (sizeof(size_t)+sizeof(time_t)+
sizeof(uint8_t*));
}
return s;
}
size_t
anchors_get_mem(struct val_anchors* anchors)
{
struct trust_anchor *ta;
size_t s = sizeof(*anchors);
if(!anchors)
return 0;
struct ta_key *k;
size_t s;
if(!anchors) return 0;
s = sizeof(*anchors);
lock_basic_lock(&anchors->lock);
RBTREE_FOR(ta, struct trust_anchor*, anchors->tree) {
lock_basic_lock(&ta->lock);
s += sizeof(*ta) + ta->namelen;
/* keys and so on */
for(k = ta->keylist; k; k = k->next) {
s += sizeof(*k) + k->len;
}
s += assembled_rrset_get_mem(ta->ds_rrset);
s += assembled_rrset_get_mem(ta->dnskey_rrset);
if(ta->autr) {
struct autr_ta* p;
s += sizeof(*ta->autr);
if(ta->autr->file)
s += strlen(ta->autr->file);
for(p = ta->autr->keys; p; p=p->next) {
s += sizeof(*p) + p->rr_len;
}
}
lock_basic_unlock(&ta->lock);
}
lock_basic_unlock(&anchors->lock);
return s;
}
@ -1346,3 +1382,22 @@ anchors_find_any_noninsecure(struct val_anchors* anchors)
lock_basic_unlock(&anchors->lock);
return NULL;
}
void
anchors_swap_tree(struct val_anchors* anchors, struct val_anchors* data)
{
rbtree_type* oldtree;
rbtree_type oldprobe;
if(!anchors || !data)
return; /* If anchors is NULL, there is no validation. */
oldtree = anchors->tree;
oldprobe = anchors->autr->probe;
anchors->tree = data->tree;
anchors->autr->probe = data->autr->probe;
data->tree = oldtree;
data->autr->probe = oldprobe;
}

View file

@ -58,7 +58,7 @@ struct sldns_buffer;
* on a trust anchor and look it up again to delete it.
*/
struct val_anchors {
/** lock on trees */
/** lock on trees. It is locked in order after stubs. */
lock_basic_type lock;
/**
* Anchors are store in this tree. Sort order is chosen, so that
@ -248,4 +248,12 @@ int anchor_has_keytag(struct val_anchors* anchors, uint8_t* name, int namelabs,
*/
struct trust_anchor* anchors_find_any_noninsecure(struct val_anchors* anchors);
/**
* Swap internal tree with preallocated entries.
* @param anchors: anchor storage.
* @param data: the data structure used to take elements from. This contains
* the old elements on return.
*/
void anchors_swap_tree(struct val_anchors* anchors, struct val_anchors* data);
#endif /* VALIDATOR_VAL_ANCHOR_H */

View file

@ -1554,3 +1554,12 @@ val_neg_getmsg(struct val_neg_cache* neg, struct query_info* qinfo,
lock_basic_unlock(&neg->lock);
return msg;
}
void
val_neg_adjust_size(struct val_neg_cache* neg, size_t max)
{
lock_basic_lock(&neg->lock);
neg->max = max;
neg_make_space(neg, 0);
lock_basic_unlock(&neg->lock);
}

View file

@ -299,4 +299,11 @@ struct val_neg_zone* neg_create_zone(struct val_neg_cache* neg,
*/
void val_neg_zone_take_inuse(struct val_neg_zone* zone);
/**
* Adjust the size of the negative cache.
* @param neg: negative cache
* @param max: new size for max mem.
*/
void val_neg_adjust_size(struct val_neg_cache* neg, size_t max);
#endif /* VALIDATOR_VAL_NEG_H */

View file

@ -91,50 +91,98 @@ update_reason_bogus(struct reply_info* rep, sldns_ede_code reason_bogus)
/** fill up nsec3 key iterations config entry */
static int
fill_nsec3_iter(struct val_env* ve, char* s, int c)
fill_nsec3_iter(size_t** keysize, size_t** maxiter, char* s, int c)
{
char* e;
int i;
free(ve->nsec3_keysize);
free(ve->nsec3_maxiter);
ve->nsec3_keysize = (size_t*)calloc((size_t)c, sizeof(size_t));
ve->nsec3_maxiter = (size_t*)calloc((size_t)c, sizeof(size_t));
if(!ve->nsec3_keysize || !ve->nsec3_maxiter) {
*keysize = (size_t*)calloc((size_t)c, sizeof(size_t));
*maxiter = (size_t*)calloc((size_t)c, sizeof(size_t));
if(!*keysize || !*maxiter) {
free(*keysize);
*keysize = NULL;
free(*maxiter);
*maxiter = NULL;
log_err("out of memory");
return 0;
}
for(i=0; i<c; i++) {
ve->nsec3_keysize[i] = (size_t)strtol(s, &e, 10);
(*keysize)[i] = (size_t)strtol(s, &e, 10);
if(s == e) {
log_err("cannot parse: %s", s);
free(*keysize);
*keysize = NULL;
free(*maxiter);
*maxiter = NULL;
return 0;
}
s = e;
ve->nsec3_maxiter[i] = (size_t)strtol(s, &e, 10);
(*maxiter)[i] = (size_t)strtol(s, &e, 10);
if(s == e) {
log_err("cannot parse: %s", s);
free(*keysize);
*keysize = NULL;
free(*maxiter);
*maxiter = NULL;
return 0;
}
s = e;
if(i>0 && ve->nsec3_keysize[i-1] >= ve->nsec3_keysize[i]) {
if(i>0 && (*keysize)[i-1] >= (*keysize)[i]) {
log_err("nsec3 key iterations not ascending: %d %d",
(int)ve->nsec3_keysize[i-1],
(int)ve->nsec3_keysize[i]);
(int)(*keysize)[i-1], (int)(*keysize)[i]);
free(*keysize);
*keysize = NULL;
free(*maxiter);
*maxiter = NULL;
return 0;
}
verbose(VERB_ALGO, "validator nsec3cfg keysz %d mxiter %d",
(int)ve->nsec3_keysize[i], (int)ve->nsec3_maxiter[i]);
(int)(*keysize)[i], (int)(*maxiter)[i]);
}
return 1;
}
int
val_env_parse_key_iter(char* val_nsec3_key_iterations, size_t** keysize,
size_t** maxiter, int* keyiter_count)
{
int c;
c = cfg_count_numbers(val_nsec3_key_iterations);
if(c < 1 || (c&1)) {
log_err("validator: unparsable or odd nsec3 key "
"iterations: %s", val_nsec3_key_iterations);
return 0;
}
*keyiter_count = c/2;
if(!fill_nsec3_iter(keysize, maxiter, val_nsec3_key_iterations, c/2)) {
log_err("validator: cannot apply nsec3 key iterations");
return 0;
}
return 1;
}
void
val_env_apply_cfg(struct val_env* val_env, struct config_file* cfg,
size_t* keysize, size_t* maxiter, int keyiter_count)
{
free(val_env->nsec3_keysize);
free(val_env->nsec3_maxiter);
val_env->nsec3_keysize = keysize;
val_env->nsec3_maxiter = maxiter;
val_env->nsec3_keyiter_count = keyiter_count;
val_env->bogus_ttl = (uint32_t)cfg->bogus_ttl;
val_env->date_override = cfg->val_date_override;
val_env->skew_min = cfg->val_sig_skew_min;
val_env->skew_max = cfg->val_sig_skew_max;
val_env->max_restart = cfg->val_max_restart;
}
/** apply config settings to validator */
static int
val_apply_cfg(struct module_env* env, struct val_env* val_env,
struct config_file* cfg)
{
int c;
val_env->bogus_ttl = (uint32_t)cfg->bogus_ttl;
size_t* keysize=NULL, *maxiter=NULL;
int keyiter_count = 0;
if(!env->anchors)
env->anchors = anchors_create();
if(!env->anchors) {
@ -154,21 +202,11 @@ val_apply_cfg(struct module_env* env, struct val_env* val_env,
log_err("validator: error in trustanchors config");
return 0;
}
val_env->date_override = cfg->val_date_override;
val_env->skew_min = cfg->val_sig_skew_min;
val_env->skew_max = cfg->val_sig_skew_max;
val_env->max_restart = cfg->val_max_restart;
c = cfg_count_numbers(cfg->val_nsec3_key_iterations);
if(c < 1 || (c&1)) {
log_err("validator: unparsable or odd nsec3 key "
"iterations: %s", cfg->val_nsec3_key_iterations);
return 0;
}
val_env->nsec3_keyiter_count = c/2;
if(!fill_nsec3_iter(val_env, cfg->val_nsec3_key_iterations, c/2)) {
log_err("validator: cannot apply nsec3 key iterations");
if(!val_env_parse_key_iter(cfg->val_nsec3_key_iterations,
&keysize, &maxiter, &keyiter_count)) {
return 0;
}
val_env_apply_cfg(val_env, cfg, keysize, maxiter, keyiter_count);
if (env->neg_cache)
val_env->neg_cache = env->neg_cache;
if(!val_env->neg_cache)

View file

@ -52,6 +52,7 @@ struct key_entry_key;
struct val_neg_cache;
struct config_strlist;
struct comm_timer;
struct config_file;
/**
* This is the TTL to use when a trust anchor fails to prime. A trust anchor
@ -280,4 +281,26 @@ size_t val_get_mem(struct module_env* env, int id);
/** Timer callback for msg signatures continue timer */
void validate_suspend_timer_cb(void* arg);
/**
* Parse the val_nsec3_key_iterations string.
* @param val_nsec3_key_iterations: the string with nsec3 iterations config.
* @param keysize: returns malloced key size array on success.
* @param maxiter: returns malloced max iterations array on success.
* @param keyiter_count: returns size of keysize and maxiter arrays.
* @return false if it does not parse correctly.
*/
int val_env_parse_key_iter(char* val_nsec3_key_iterations, size_t** keysize,
size_t** maxiter, int* keyiter_count);
/**
* Apply config to validator env
* @param val_env: validator env.
* @param cfg: config
* @param keysize: nsec3 key size array.
* @param maxiter: nsec3 max iterations array.
* @param keyiter_count: size of keysize and maxiter arrays.
*/
void val_env_apply_cfg(struct val_env* val_env, struct config_file* cfg,
size_t* keysize, size_t* maxiter, int keyiter_count);
#endif /* VALIDATOR_VALIDATOR_H */