From: Stuart Henderson Subject: unbound 1.20.0 To: tech Date: Wed, 22 May 2024 11:44:17 +0100 Lots of churn in autoconf, fair bit in the code too. Diff is ordered so the autoconf stuff is at the bottom. doc/Changelog | 187 cachedb/cachedb.c | 133 cachedb/cachedb.h | 16 daemon/cachedump.c | 23 daemon/daemon.c | 26 daemon/remote.c | 128 daemon/unbound.c | 39 daemon/worker.c | 35 edns-subnet/subnetmod.c | 40 iterator/iter_fwd.c | 119 iterator/iter_fwd.h | 60 iterator/iter_hints.c | 95 iterator/iter_hints.h | 54 iterator/iter_utils.c | 62 iterator/iter_utils.h | 6 iterator/iterator.c | 295 libunbound/context.c | 8 libunbound/libunbound.c | 5 libunbound/libworker.c | 21 services/authzone.c | 10 services/listen_dnsport.c | 51 services/listen_dnsport.h | 8 services/localzone.c | 6 services/mesh.c | 106 services/mesh.h | 6 services/rpz.c | 342 - services/rpz.h | 8 services/view.h | 3 services/cache/dns.c | 52 services/cache/infra.c | 170 services/cache/infra.h | 28 services/cache/rrset.c | 87 services/cache/rrset.h | 31 smallapp/unbound-anchor.c | 70 smallapp/unbound-checkconf.c | 13 smallapp/unbound-control.c | 5 testcode/checklocks.c | 24 testcode/fake_event.c | 72 testcode/replay.c | 14 testcode/replay.h | 9 testcode/testbound.c | 6 util/config_file.c | 39 util/config_file.h | 19 util/configlexer.lex | 7 util/configparser.y | 83 util/module.c | 11 util/module.h | 18 util/net_help.c | 36 util/netevent.c | 8 util/netevent.h | 2 util/data/msgparse.h | 2 util/data/msgreply.c | 33 util/storage/lookup3.c | 2 util/storage/lruhash.c | 34 util/storage/lruhash.h | 11 util/storage/slabhash.c | 7 util/storage/slabhash.h | 12 validator/val_utils.c | 40 validator/validator.c | 36 doc/README | 2 doc/example.conf.in | 28 doc/libunbound.3.in | 4 doc/unbound-anchor.8.in | 2 doc/unbound-checkconf.8.in | 6 doc/unbound-control.8.in | 8 doc/unbound-host.1.in | 2 doc/unbound.8.in | 4 doc/unbound.conf.5.in | 72 doc/unbound.doxygen | 2939 ++++++--- Makefile.in | 16 ax_pthread.m4 | 444 + config.h.in | 105 configure |13556 +++++++++++++++++++++++++------------------ configure.ac | 16 74 files changed, 12786 insertions(+), 7291 deletions(-) Index: doc/Changelog =================================================================== RCS file: /cvs/src/usr.sbin/unbound/doc/Changelog,v diff -u -p -r1.51 Changelog --- doc/Changelog 13 Apr 2024 12:24:57 -0000 1.51 +++ doc/Changelog 22 May 2024 10:33:17 -0000 @@ -1,15 +1,194 @@ +1 May 2024: Wouter + - Fix for the DNSBomb vulnerability CVE-2024-33655. Thanks to Xiang Li + from the Network and Information Security Lab of Tsinghua University + for reporting it. + - Set version number to 1.20.0 for release. + +29 April 2024: Yorgos + - Cleanup unnecessary strdup calls for EDE strings. + +29 April 2024: Wouter + - Fix doxygen comment for errinf_to_str_bogus. + +26 April 2024: Wouter + - Fix cachedb with serve-expired-client-timeout disabled. The edns + subnet module deletes global cache and cachedb cache when it + stores a result, and serve-expired is enabled, so that the global + reply, that is older than the ecs reply, does not return after + the ecs reply expires. + - Add unit tests for cachedb and subnet cache expired data. + - Man page entry for unbound-checkconf -q. + +26 April 2024: Yorgos + - Fix #876: [FR] can unbound-checkconf be silenced when configuration + is valid? + +25 April 2024: Wouter + - Fix configure flto check error, by finding grep for it. + - Merge #1041: Stub and Forward unshare. This has one structure + for them and fixes #1038: fatal error: Could not initialize + thread / error: reading root hints. + - Fix to disable fragmentation on systems with IP_DONTFRAG, + with a nonzero value for the socket option argument. + - Fix doc unit test for out of directory build. + +24 April 2024: Wouter + - Fix ci workflow for macos for moved install locations. + +23 April 2024: Yorgos + - Merge #1053: Remove child delegations from cache when grandchild + delegations are returned from parent. + +22 April 2024: Wouter + - Add checklock feature verbose_locking to trace locks and unlocks. + - Fix edns subnet to sort rrset references when storing messages + in the cache. This fixes a race condition in the rrset locks. + +15 April 2024: Wouter + - Fix #1048: Update ax_pkg_swig.m4 and ax_pthread.m4. + - Fix configure, autoconf for #1048. + +15 April 2024: Yorgos + - Merge #1049 from Petr Menšík: Py_NoSiteFlag is not needed since + Python 3.8 + +12 April 2024: Wouter + - Fix cachedb for serve-expired with serve-expired-client-timeout. + - Fixup unit test for cachedb server expired client timeout with + a check if response if from upstream or from cachedb. + - Fixup cachedb to not refetch when serve-expired-client-timeout is + used. + +10 April 2024: Wouter + - Implement cachedb-check-when-serve-expired: yes option, default + is enabled. When serve expired is enabled with cachedb, it first + checks cachedb before serving the expired response. + - Fixup compile without cachedb. + - Add test for cachedb serve expired. + - Extended test for cachedb serve expired. + - Fix makefile dependencies for fake_event.c. + - Fix cachedb for serve-expired with serve-expired-reply-ttl. + - Fix to not reply serve expired unless enabled for cachedb. + +9 April 2024: Yorgos + - Merge #1043 from xiaoxiaoafeifei: Add loongarch support; updates + config.guess(2024-01-01) and config.sub(2024-01-01), verified + with upstream. + +8 April 2024: Yorgos + - Fix #595: unbound-anchor cannot deal with full disk; it will now + first write out to a temp file before replacing the original one, + like Unbound already does for auto-trust-anchor-file. + +5 April 2024: Wouter + - Fix comment syntax for view function views_find_view. + +5 April 2024: Yorgos + - Merge #1027: Introduce 'cache-min-negative-ttl' option. + +3 April 2024: Wouter + - Fix #1040: fix heap-buffer-overflow issue in function cfg_mark_ports + of file util/config_file.c. + - For #1040: adjust error text and disallow negative ports in other + parts of cfg_mark_ports. + +3 April 2024: Yorgos + - Fix #1035: Potential Bug while parsing port from the "stub-host" + string; also affected forward-zones and remote-control host + directives. + - Fix #369: dnstap showing extra responses; for client responses + right from the cache when replying with expired data or + prefetching. + +28 March 2024: Wouter + - Fix #1034: DoT forward-zone via unbound-control. + - Fix for crypto related failures to have a better error string. + +27 March 2024: Wouter + - Fix name of unit test for subnet cache response. + - Fix #1032: The size of subnet_msg_cache calculation mistake cause + memory usage increased beyond expectations. + - Fix for #1032, add safeguard to make table space positive. + - Fix comment in lruhash space function. + - Fix to add unit test for lruhash space that exercises the routines. + - Fix that when the server truncates the pidfile, it does not follow + symbolic links. + - Fix that the server does not chown the pidfile. + +25 March 2024: Yorgos + - Merge #831 from Pierre4012: Improve Windows NSIS installer + script (setup.nsi). + - For #831: Format text, use exclamation icon and explicit label + names. + +19 March 2024: Wouter + - Fix rpz so that rpz CNAME can apply after rpz CNAME. And fix that + clientip and nsip can give a CNAME. + - Fix localdata and rpz localdata to match CNAME only if no direct + type match is available. + +18 March 2024: Wouter + - Fix that rpz CNAME content is limited to the max number of cnames. + - Fix rpz, it follows iterator CNAMEs for nsip and nsdname and sets + the reply query_info values, that is better for debug logging. + - Fix rpz that copies the cname override completely to the temp + region, so there are no references to the rpz region. + - Add rpz unit test for nsip action override. + - Fix rpz for qtype CNAME after nameserver trigger. + +15 March 2024: Yorgos + - Merge #1030: Persist the openssl and expat directories for repeated + Windows builds. + +15 March 2024: Wouter + - Fix that addrinfo is not kept around but copied and freed, so that + log-destaddr uses a copy of the information, much like NSD does. + +13 March 2024: Wouter + - Fix #1029: rpz trigger clientip and action rpz-passthru not working + as expected. + - Fix rpz that the rpz override is taken in case of clientip triggers. + Fix that the clientip passthru action is logged. Fix that the + clientip localdata action is logged. Fix rpz override action cname + for the clientip trigger. + - Fix to unify codepath for local alias for rpz cname action override. + - Fix rpz for cname override action after nsdname and nsip triggers. + +12 March 2024: Yorgos + - Merge #1028: Clearer documentation for tcp-idle-timeout and + edns-tcp-keepalive-timeout. + +11 March 2024: Wouter + - Fix #1021 Inconsistent Behavior with Changing rpz-cname-override + and doing a unbound-control reload. + 8 March 2024: Wouter - Fix unbound-control-setup.cmd to use 3072 bits so that certificates - are long enough for newer OpenSSL versions. - - Fix TTL of synthesized CNAME when a DNAME is used from cache. + are long enough for newer OpenSSL versions. This fix is included + in 1.19.3rc2. + - Fix TTL of synthesized CNAME when a DNAME is used from cache. This + fix is included in 1.19.3rc2. + - Remove unused portion from iter_dname_ttl unit test. + - Fix validator classification of qtype DNAME for positive and + redirection answers, and fix validator signature routine for dealing + with the synthesized CNAME for a DNAME without previously + encountering it and also for when the qtype is DNAME. + - Fix qname minimisation for reply with a DNAME for qtype CNAME that + answers it. + - Fix doc test so it ignores but outputs unsupported doxygen options. - Fix unbound-control-setup.cmd to have CA v3 basicConstraints, - like unbound-control-setup.sh has. + like unbound-control-setup.sh has. This fix is included in 1.19.3rc2. + +8 March 2024: Yorgos + - Update doc/unbound.doxygen with 'doxygen -u'. Fixes option + deprecation warnings and updates with newer defaults. 7 March 2024: Wouter - Version set to 1.19.3 for release. After 1.19.2 point release with security fix for CVE-2024-1931, Denial of service when trimming EDE text on positive replies. The code repo includes the fix and - is for version 1.19.3. + is for version 1.19.3. The code repo continues for version 1.19.4, + but 1.19.3 includes the fixes in 1.19.3rc2 as well. 5 March 2024: Wouter - Fix for #1022: Fix ede prohibited in access control refused answers. Index: cachedb/cachedb.c =================================================================== RCS file: /cvs/src/usr.sbin/unbound/cachedb/cachedb.c,v diff -u -p -r1.18 cachedb.c --- cachedb/cachedb.c 12 Apr 2024 15:45:24 -0000 1.18 +++ cachedb/cachedb.c 22 May 2024 10:33:17 -0000 @@ -50,6 +50,8 @@ #include "util/data/msgreply.h" #include "util/data/msgencode.h" #include "services/cache/dns.h" +#include "services/mesh.h" +#include "services/modstack.h" #include "validator/val_neg.h" #include "validator/val_secalgo.h" #include "iterator/iter_utils.h" @@ -265,15 +267,6 @@ cachedb_init(struct module_env* env, int return 0; } cachedb_env->enabled = 1; - if(env->cfg->serve_expired && env->cfg->serve_expired_reply_ttl) - log_warn( - "cachedb: serve-expired-reply-ttl is set but not working for data " - "originating from the external cache; 0 TTL is used for those."); - if(env->cfg->serve_expired && env->cfg->serve_expired_client_timeout) - log_warn( - "cachedb: serve-expired-client-timeout is set but not working for " - "data originating from the external cache; expired data are used " - "in the reply without first trying to refresh the data."); return 1; } @@ -511,9 +504,38 @@ adjust_msg_ttl(struct dns_msg* msg, time } } +/* Set the TTL of the given RRset to fixed value. */ +static void +packed_rrset_ttl_set(struct packed_rrset_data* data, time_t ttl) +{ + size_t i; + size_t total = data->count + data->rrsig_count; + data->ttl = ttl; + for(i=0; irr_ttl[i] = ttl; + } + data->ttl_add = 0; +} + +/* Set the TTL of a DNS message and its RRs by to a fixed value. */ +static void +set_msg_ttl(struct dns_msg* msg, time_t ttl) +{ + size_t i; + msg->rep->ttl = ttl; + msg->rep->prefetch_ttl = PREFETCH_TTL_CALC(msg->rep->ttl); + msg->rep->serve_expired_ttl = msg->rep->ttl + SERVE_EXPIRED_TTL; + + for(i=0; irep->rrset_count; i++) { + packed_rrset_ttl_set((struct packed_rrset_data*)msg-> + rep->rrsets[i]->entry.data, ttl); + } +} + /** convert dns message in buffer to return_msg */ static int -parse_data(struct module_qstate* qstate, struct sldns_buffer* buf) +parse_data(struct module_qstate* qstate, struct sldns_buffer* buf, + int* msg_expired) { struct msg_parse* prs; struct edns_data edns; @@ -583,6 +605,7 @@ parse_data(struct module_qstate* qstate, adjust = *qstate->env->now - (time_t)timestamp; if(qstate->return_msg->rep->ttl < adjust) { verbose(VERB_ALGO, "cachedb msg expired"); + *msg_expired = 1; /* If serve-expired is enabled, we still use an expired message * setting the TTL to 0. */ if(!qstate->env->cfg->serve_expired || @@ -605,6 +628,7 @@ parse_data(struct module_qstate* qstate, * 'now' should be redundant given how these values were calculated, * but we check it just in case as does good_expiry_and_qinfo(). */ if(qstate->env->cfg->serve_expired && + !qstate->env->cfg->serve_expired_client_timeout && (adjust == -1 || (time_t)expiry < *qstate->env->now)) { qstate->need_refetch = 1; } @@ -617,7 +641,8 @@ parse_data(struct module_qstate* qstate, * return true if lookup was successful. */ static int -cachedb_extcache_lookup(struct module_qstate* qstate, struct cachedb_env* ie) +cachedb_extcache_lookup(struct module_qstate* qstate, struct cachedb_env* ie, + int* msg_expired) { char key[(CACHEDB_HASHSIZE/8)*2+1]; calc_hash(qstate, key, sizeof(key)); @@ -634,7 +659,7 @@ cachedb_extcache_lookup(struct module_qs } /* parse dns message into return_msg */ - if( !parse_data(qstate, qstate->env->scratch_buffer) ) { + if( !parse_data(qstate, qstate->env->scratch_buffer, msg_expired) ) { return 0; } return 1; @@ -666,6 +691,7 @@ cachedb_extcache_store(struct module_qst static int cachedb_intcache_lookup(struct module_qstate* qstate, struct cachedb_env* cde) { + uint8_t dpname_storage[LDNS_MAX_DOMAINLEN+1]; uint8_t* dpname=NULL; size_t dpnamelen=0; struct dns_msg* msg; @@ -674,7 +700,7 @@ cachedb_intcache_lookup(struct module_qs return 0; } if(iter_stub_fwd_no_cache(qstate, &qstate->qinfo, - &dpname, &dpnamelen)) + &dpname, &dpnamelen, dpname_storage, sizeof(dpname_storage))) return 0; /* no cache for these queries */ msg = dns_cache_lookup(qstate->env, qstate->qinfo.qname, qstate->qinfo.qname_len, qstate->qinfo.qtype, @@ -705,17 +731,39 @@ cachedb_intcache_lookup(struct module_qs * Store query into the internal cache of unbound. */ static void -cachedb_intcache_store(struct module_qstate* qstate) +cachedb_intcache_store(struct module_qstate* qstate, int msg_expired) { uint32_t store_flags = qstate->query_flags; + int serve_expired = qstate->env->cfg->serve_expired; if(qstate->env->cfg->serve_expired) store_flags |= DNSCACHE_STORE_ZEROTTL; if(!qstate->return_msg) return; + if(serve_expired && msg_expired) { + /* Set TTLs to a value such that value + *env->now is + * going to be now-3 seconds. Making it expired + * in the cache. */ + set_msg_ttl(qstate->return_msg, (time_t)-3); + } (void)dns_cache_store(qstate->env, &qstate->qinfo, qstate->return_msg->rep, 0, qstate->prefetch_leeway, 0, qstate->region, store_flags, qstate->qstarttime); + if(serve_expired && msg_expired) { + if(qstate->env->cfg->serve_expired_client_timeout) { + /* No expired response from the query state, the + * query resolution needs to continue and it can + * pick up the expired result after the timer out + * of cache. */ + return; + } + /* set TTLs to zero again */ + adjust_msg_ttl(qstate->return_msg, -1); + /* Send serve expired responses based on the cachedb + * returned message, that was just stored in the cache. + * It can then continue to work on this query. */ + mesh_respond_serve_expired(qstate->mesh_info); + } } /** @@ -731,6 +779,7 @@ cachedb_handle_query(struct module_qstat struct cachedb_qstate* ATTR_UNUSED(iq), struct cachedb_env* ie, int id) { + int msg_expired = 0; qstate->is_cachedb_answer = 0; /* check if we are enabled, and skip if so */ if(!ie->enabled) { @@ -765,20 +814,28 @@ cachedb_handle_query(struct module_qstat } /* ask backend cache to see if we have data */ - if(cachedb_extcache_lookup(qstate, ie)) { + if(cachedb_extcache_lookup(qstate, ie, &msg_expired)) { if(verbosity >= VERB_ALGO) log_dns_msg(ie->backend->name, &qstate->return_msg->qinfo, qstate->return_msg->rep); /* store this result in internal cache */ - cachedb_intcache_store(qstate); + cachedb_intcache_store(qstate, msg_expired); /* In case we have expired data but there is a client timer for expired * answers, pass execution to next module in order to try updating the * data first. * TODO: this needs revisit. The expired data stored from cachedb has * 0 TTL which is picked up by iterator later when looking in the cache. - * Document that ext cachedb does not work properly with - * serve_stale_reply_ttl yet. */ + */ + if(qstate->env->cfg->serve_expired && msg_expired) { + qstate->return_msg = NULL; + qstate->ext_state[id] = module_wait_module; + /* The expired reply is sent with + * mesh_respond_serve_expired, and so + * the need_refetch is not used. */ + qstate->need_refetch = 0; + return; + } if(qstate->need_refetch && qstate->serve_expired_data && qstate->serve_expired_data->timer) { qstate->return_msg = NULL; @@ -791,6 +848,14 @@ cachedb_handle_query(struct module_qstat return; } + if(qstate->serve_expired_data && + qstate->env->cfg->cachedb_check_when_serve_expired && + !qstate->env->cfg->serve_expired_client_timeout) { + /* Reply with expired data if any to client, because cachedb + * also has no useful, current data */ + mesh_respond_serve_expired(qstate->mesh_info); + } + /* no cache fetches */ /* pass request to next module */ qstate->ext_state[id] = module_wait_module; @@ -922,5 +987,37 @@ struct module_func_block* cachedb_get_funcblock(void) { return &cachedb_block; +} + +int +cachedb_is_enabled(struct module_stack* mods, struct module_env* env) +{ + struct cachedb_env* ie; + int id = modstack_find(mods, "cachedb"); + if(id == -1) + return 0; + ie = (struct cachedb_env*)env->modinfo[id]; + if(ie && ie->enabled) + return 1; + return 0; +} + +void cachedb_msg_remove(struct module_qstate* qstate) +{ + char key[(CACHEDB_HASHSIZE/8)*2+1]; + int id = modstack_find(qstate->env->modstack, "cachedb"); + struct cachedb_env* ie = (struct cachedb_env*)qstate->env->modinfo[id]; + + log_query_info(VERB_ALGO, "cachedb msg remove", &qstate->qinfo); + calc_hash(qstate, key, sizeof(key)); + sldns_buffer_clear(qstate->env->scratch_buffer); + sldns_buffer_write_u32(qstate->env->scratch_buffer, 0); + sldns_buffer_flip(qstate->env->scratch_buffer); + + /* call backend */ + (*ie->backend->store)(qstate->env, ie, key, + sldns_buffer_begin(qstate->env->scratch_buffer), + sldns_buffer_limit(qstate->env->scratch_buffer), + 0); } #endif /* USE_CACHEDB */ Index: cachedb/cachedb.h =================================================================== RCS file: /cvs/src/usr.sbin/unbound/cachedb/cachedb.h,v diff -u -p -r1.3 cachedb.h --- cachedb/cachedb.h 24 Aug 2020 09:41:52 -0000 1.3 +++ cachedb/cachedb.h 22 May 2024 10:33:17 -0000 @@ -41,6 +41,7 @@ */ #include "util/module.h" struct cachedb_backend; +struct module_stack; /** * The global variable environment contents for the cachedb @@ -110,3 +111,18 @@ size_t cachedb_get_mem(struct module_env */ struct module_func_block* cachedb_get_funcblock(void); +/** + * See if the cachedb is enabled. + * @param mods: module stack. It finds the cachedb module environment. + * @param env: module environment. + * @return true if exists and enabled. + */ +int cachedb_is_enabled(struct module_stack* mods, struct module_env* env); + +/** + * Remove a message from the global cache. Because edns subnet has a more + * specific entry, and if not removed when everything expires, the global + * entry is used, instead of a fresh lookup of the edns subnet entry. + * @param qstate: query state. + */ +void cachedb_msg_remove(struct module_qstate* qstate); Index: daemon/cachedump.c =================================================================== RCS file: /cvs/src/usr.sbin/unbound/daemon/cachedump.c,v diff -u -p -r1.10 cachedump.c --- daemon/cachedump.c 5 Sep 2023 11:12:10 -0000 1.10 +++ daemon/cachedump.c 22 May 2024 10:33:17 -0000 @@ -839,6 +839,7 @@ int print_deleg_lookup(RES* ssl, struct char b[260]; struct query_info qinfo; struct iter_hints_stub* stub; + int nolock = 0; regional_free_all(region); qinfo.qname = nm; qinfo.qname_len = nmlen; @@ -850,13 +851,16 @@ int print_deleg_lookup(RES* ssl, struct if(!ssl_printf(ssl, "The following name servers are used for lookup " "of %s\n", b)) return 0; - - dp = forwards_lookup(worker->env.fwds, nm, qinfo.qclass); + + dp = forwards_lookup(worker->env.fwds, nm, qinfo.qclass, nolock); if(dp) { - if(!ssl_printf(ssl, "forwarding request:\n")) + if(!ssl_printf(ssl, "forwarding request:\n")) { + lock_rw_unlock(&worker->env.fwds->lock); return 0; + } print_dp_main(ssl, dp, NULL); print_dp_details(ssl, worker, dp); + lock_rw_unlock(&worker->env.fwds->lock); return 1; } @@ -892,21 +896,26 @@ int print_deleg_lookup(RES* ssl, struct return 0; continue; } - } + } stub = hints_lookup_stub(worker->env.hints, nm, qinfo.qclass, - dp); + dp, nolock); if(stub) { if(stub->noprime) { if(!ssl_printf(ssl, "The noprime stub servers " - "are used:\n")) + "are used:\n")) { + lock_rw_unlock(&worker->env.hints->lock); return 0; + } } else { if(!ssl_printf(ssl, "The stub is primed " - "with servers:\n")) + "with servers:\n")) { + lock_rw_unlock(&worker->env.hints->lock); return 0; + } } print_dp_main(ssl, stub->dp, NULL); print_dp_details(ssl, worker, stub->dp); + lock_rw_unlock(&worker->env.hints->lock); } else { print_dp_main(ssl, dp, msg); print_dp_details(ssl, worker, dp); Index: daemon/daemon.c =================================================================== RCS file: /cvs/src/usr.sbin/unbound/daemon/daemon.c,v diff -u -p -r1.24 daemon.c --- daemon/daemon.c 6 Sep 2023 09:08:04 -0000 1.24 +++ daemon/daemon.c 22 May 2024 10:33:17 -0000 @@ -91,6 +91,8 @@ #include "util/net_help.h" #include "sldns/keyraw.h" #include "respip/respip.h" +#include "iterator/iter_fwd.h" +#include "iterator/iter_hints.h" #include #ifdef HAVE_SYSTEMD @@ -99,6 +101,9 @@ #ifdef HAVE_NETDB_H #include #endif +#ifdef USE_CACHEDB +#include "cachedb/cachedb.h" +#endif /** How many quit requests happened. */ static int sig_record_quit = 0; @@ -260,6 +265,7 @@ daemon_init(void) free(daemon); return NULL; } + daemon->env->modstack = &daemon->mods; /* init edns_known_options */ if(!edns_known_options_init(daemon->env)) { free(daemon->env); @@ -321,17 +327,15 @@ static int setup_acl_for_ports(struct ac struct listen_port* port_list) { struct acl_addr* acl_node; - struct addrinfo* addr; for(; port_list; port_list=port_list->next) { if(!port_list->socket) { /* This is mainly for testbound where port_list is * empty. */ continue; } - addr = port_list->socket->addr; if(!(acl_node = acl_interface_insert(list, - (struct sockaddr_storage*)addr->ai_addr, - (socklen_t)addr->ai_addrlen, + (struct sockaddr_storage*)port_list->socket->addr, + port_list->socket->addrlen, acl_refuse))) { return 0; } @@ -716,6 +720,12 @@ daemon_fork(struct daemon* daemon) fatal_exit("Could not create local zones: out of memory"); if(!local_zones_apply_cfg(daemon->local_zones, daemon->cfg)) fatal_exit("Could not set up local zones"); + if(!(daemon->env->fwds = forwards_create()) || + !forwards_apply_cfg(daemon->env->fwds, daemon->cfg)) + fatal_exit("Could not set forward zones"); + if(!(daemon->env->hints = hints_create()) || + !hints_apply_cfg(daemon->env->hints, daemon->cfg)) + fatal_exit("Could not set root or stub hints"); /* process raw response-ip configuration data */ if(!(daemon->respip_set = respip_set_create())) @@ -740,6 +750,10 @@ daemon_fork(struct daemon* daemon) if(!edns_strings_apply_cfg(daemon->env->edns_strings, daemon->cfg)) fatal_exit("Could not set up EDNS strings"); +#ifdef USE_CACHEDB + daemon->env->cachedb_enabled = cachedb_is_enabled(&daemon->mods, + daemon->env); +#endif /* response-ip-xxx options don't work as expected without the respip * module. To avoid run-time operational surprise we reject such * configuration. */ @@ -832,6 +846,10 @@ daemon_cleanup(struct daemon* daemon) slabhash_clear(daemon->env->msg_cache); } daemon->old_num = daemon->num; /* save the current num */ + forwards_delete(daemon->env->fwds); + daemon->env->fwds = NULL; + hints_delete(daemon->env->hints); + daemon->env->hints = NULL; local_zones_delete(daemon->local_zones); daemon->local_zones = NULL; respip_set_delete(daemon->respip_set); Index: daemon/remote.c =================================================================== RCS file: /cvs/src/usr.sbin/unbound/daemon/remote.c,v diff -u -p -r1.38 remote.c --- daemon/remote.c 13 Apr 2024 12:24:57 -0000 1.38 +++ daemon/remote.c 22 May 2024 10:33:17 -0000 @@ -1992,12 +1992,19 @@ static int print_root_fwds(RES* ssl, struct iter_forwards* fwds, uint8_t* root) { struct delegpt* dp; - dp = forwards_lookup(fwds, root, LDNS_RR_CLASS_IN); - if(!dp) + int nolock = 0; + dp = forwards_lookup(fwds, root, LDNS_RR_CLASS_IN, nolock); + if(!dp) { return ssl_printf(ssl, "off (using root hints)\n"); + } /* if dp is returned it must be the root */ log_assert(query_dname_compare(dp->name, root)==0); - return ssl_print_name_dp(ssl, NULL, root, LDNS_RR_CLASS_IN, dp); + if(!ssl_print_name_dp(ssl, NULL, root, LDNS_RR_CLASS_IN, dp)) { + lock_rw_unlock(&fwds->lock); + return 0; + } + lock_rw_unlock(&fwds->lock); + return 1; } /** parse args into delegpt */ @@ -2069,6 +2076,7 @@ do_forward(RES* ssl, struct worker* work { struct iter_forwards* fwd = worker->env.fwds; uint8_t* root = (uint8_t*)"\000"; + int nolock = 0; if(!fwd) { (void)ssl_printf(ssl, "error: structure not allocated\n"); return; @@ -2082,12 +2090,12 @@ do_forward(RES* ssl, struct worker* work /* delete all the existing queries first */ mesh_delete_all(worker->env.mesh); if(strcmp(args, "off") == 0) { - forwards_delete_zone(fwd, LDNS_RR_CLASS_IN, root); + forwards_delete_zone(fwd, LDNS_RR_CLASS_IN, root, nolock); } else { struct delegpt* dp; if(!(dp = parse_delegpt(ssl, args, root))) return; - if(!forwards_add_zone(fwd, LDNS_RR_CLASS_IN, dp)) { + if(!forwards_add_zone(fwd, LDNS_RR_CLASS_IN, dp, nolock)) { (void)ssl_printf(ssl, "error out of memory\n"); return; } @@ -2097,7 +2105,7 @@ do_forward(RES* ssl, struct worker* work static int parse_fs_args(RES* ssl, char* args, uint8_t** nm, struct delegpt** dp, - int* insecure, int* prime) + int* insecure, int* prime, int* tls) { char* zonename; char* rest; @@ -2112,6 +2120,8 @@ parse_fs_args(RES* ssl, char* args, uint *insecure = 1; else if(*args == 'p' && prime) *prime = 1; + else if(*args == 't' && tls) + *tls = 1; else { (void)ssl_printf(ssl, "error: unknown option %s\n", args); return 0; @@ -2144,25 +2154,33 @@ static void do_forward_add(RES* ssl, struct worker* worker, char* args) { struct iter_forwards* fwd = worker->env.fwds; - int insecure = 0; + int insecure = 0, tls = 0; uint8_t* nm = NULL; struct delegpt* dp = NULL; - if(!parse_fs_args(ssl, args, &nm, &dp, &insecure, NULL)) + int nolock = 1; + if(!parse_fs_args(ssl, args, &nm, &dp, &insecure, NULL, &tls)) return; + if(tls) + dp->ssl_upstream = 1; + /* prelock forwarders for atomic operation with anchors */ + lock_rw_wrlock(&fwd->lock); if(insecure && worker->env.anchors) { if(!anchors_add_insecure(worker->env.anchors, LDNS_RR_CLASS_IN, nm)) { + lock_rw_unlock(&fwd->lock); (void)ssl_printf(ssl, "error out of memory\n"); delegpt_free_mlc(dp); free(nm); return; } } - if(!forwards_add_zone(fwd, LDNS_RR_CLASS_IN, dp)) { + if(!forwards_add_zone(fwd, LDNS_RR_CLASS_IN, dp, nolock)) { + lock_rw_unlock(&fwd->lock); (void)ssl_printf(ssl, "error out of memory\n"); free(nm); return; } + lock_rw_unlock(&fwd->lock); free(nm); send_ok(ssl); } @@ -2174,12 +2192,16 @@ do_forward_remove(RES* ssl, struct worke struct iter_forwards* fwd = worker->env.fwds; int insecure = 0; uint8_t* nm = NULL; - if(!parse_fs_args(ssl, args, &nm, NULL, &insecure, NULL)) + int nolock = 1; + if(!parse_fs_args(ssl, args, &nm, NULL, &insecure, NULL, NULL)) return; + /* prelock forwarders for atomic operation with anchors */ + lock_rw_wrlock(&fwd->lock); if(insecure && worker->env.anchors) anchors_delete_insecure(worker->env.anchors, LDNS_RR_CLASS_IN, nm); - forwards_delete_zone(fwd, LDNS_RR_CLASS_IN, nm); + forwards_delete_zone(fwd, LDNS_RR_CLASS_IN, nm, nolock); + lock_rw_unlock(&fwd->lock); free(nm); send_ok(ssl); } @@ -2189,38 +2211,53 @@ static void do_stub_add(RES* ssl, struct worker* worker, char* args) { struct iter_forwards* fwd = worker->env.fwds; - int insecure = 0, prime = 0; + int insecure = 0, prime = 0, tls = 0; uint8_t* nm = NULL; struct delegpt* dp = NULL; - if(!parse_fs_args(ssl, args, &nm, &dp, &insecure, &prime)) + int nolock = 1; + if(!parse_fs_args(ssl, args, &nm, &dp, &insecure, &prime, &tls)) return; + if(tls) + dp->ssl_upstream = 1; + /* prelock forwarders and hints for atomic operation with anchors */ + lock_rw_wrlock(&fwd->lock); + lock_rw_wrlock(&worker->env.hints->lock); if(insecure && worker->env.anchors) { if(!anchors_add_insecure(worker->env.anchors, LDNS_RR_CLASS_IN, nm)) { + lock_rw_unlock(&fwd->lock); + lock_rw_unlock(&worker->env.hints->lock); (void)ssl_printf(ssl, "error out of memory\n"); delegpt_free_mlc(dp); free(nm); return; } } - if(!forwards_add_stub_hole(fwd, LDNS_RR_CLASS_IN, nm)) { + if(!forwards_add_stub_hole(fwd, LDNS_RR_CLASS_IN, nm, nolock)) { if(insecure && worker->env.anchors) anchors_delete_insecure(worker->env.anchors, LDNS_RR_CLASS_IN, nm); + lock_rw_unlock(&fwd->lock); + lock_rw_unlock(&worker->env.hints->lock); (void)ssl_printf(ssl, "error out of memory\n"); delegpt_free_mlc(dp); free(nm); return; } - if(!hints_add_stub(worker->env.hints, LDNS_RR_CLASS_IN, dp, !prime)) { + if(!hints_add_stub(worker->env.hints, LDNS_RR_CLASS_IN, dp, !prime, + nolock)) { (void)ssl_printf(ssl, "error out of memory\n"); - forwards_delete_stub_hole(fwd, LDNS_RR_CLASS_IN, nm); + forwards_delete_stub_hole(fwd, LDNS_RR_CLASS_IN, nm, nolock); if(insecure && worker->env.anchors) anchors_delete_insecure(worker->env.anchors, LDNS_RR_CLASS_IN, nm); + lock_rw_unlock(&fwd->lock); + lock_rw_unlock(&worker->env.hints->lock); free(nm); return; } + lock_rw_unlock(&fwd->lock); + lock_rw_unlock(&worker->env.hints->lock); free(nm); send_ok(ssl); } @@ -2232,13 +2269,19 @@ do_stub_remove(RES* ssl, struct worker* struct iter_forwards* fwd = worker->env.fwds; int insecure = 0; uint8_t* nm = NULL; - if(!parse_fs_args(ssl, args, &nm, NULL, &insecure, NULL)) + int nolock = 1; + if(!parse_fs_args(ssl, args, &nm, NULL, &insecure, NULL, NULL)) return; + /* prelock forwarders and hints for atomic operation with anchors */ + lock_rw_wrlock(&fwd->lock); + lock_rw_wrlock(&worker->env.hints->lock); if(insecure && worker->env.anchors) anchors_delete_insecure(worker->env.anchors, LDNS_RR_CLASS_IN, nm); - forwards_delete_stub_hole(fwd, LDNS_RR_CLASS_IN, nm); - hints_delete_stub(worker->env.hints, LDNS_RR_CLASS_IN, nm); + forwards_delete_stub_hole(fwd, LDNS_RR_CLASS_IN, nm, nolock); + hints_delete_stub(worker->env.hints, LDNS_RR_CLASS_IN, nm, nolock); + lock_rw_unlock(&fwd->lock); + lock_rw_unlock(&worker->env.hints->lock); free(nm); send_ok(ssl); } @@ -2667,6 +2710,7 @@ do_list_forwards(RES* ssl, struct worker struct iter_forward_zone* z; struct trust_anchor* a; int insecure; + lock_rw_rdlock(&fwds->lock); RBTREE_FOR(z, struct iter_forward_zone*, fwds->tree) { if(!z->dp) continue; /* skip empty marker for stub */ @@ -2681,9 +2725,12 @@ do_list_forwards(RES* ssl, struct worker } if(!ssl_print_name_dp(ssl, (insecure?"forward +i":"forward"), - z->name, z->dclass, z->dp)) + z->name, z->dclass, z->dp)) { + lock_rw_unlock(&fwds->lock); return; + } } + lock_rw_unlock(&fwds->lock); } /** do the list_stubs command */ @@ -2694,6 +2741,7 @@ do_list_stubs(RES* ssl, struct worker* w struct trust_anchor* a; int insecure; char str[32]; + lock_rw_rdlock(&worker->env.hints->lock); RBTREE_FOR(z, struct iter_hints_stub*, &worker->env.hints->tree) { /* see if it is insecure */ @@ -2709,9 +2757,12 @@ do_list_stubs(RES* ssl, struct worker* w snprintf(str, sizeof(str), "stub %sprime%s", (z->noprime?"no":""), (insecure?" +i":"")); if(!ssl_print_name_dp(ssl, str, z->node.name, - z->node.dclass, z->dp)) + z->node.dclass, z->dp)) { + lock_rw_unlock(&worker->env.hints->lock); return; + } } + lock_rw_unlock(&worker->env.hints->lock); } /** do the list_auth_zones command */ @@ -3071,26 +3122,6 @@ execute_cmd(struct daemon_remote* rc, RE } else if(cmdcmp(p, "auth_zone_transfer", 18)) { do_auth_zone_transfer(ssl, worker, skipwhite(p+18)); return; - } else if(cmdcmp(p, "stub_add", 8)) { - /* must always distribute this cmd */ - if(rc) distribute_cmd(rc, ssl, cmd); - do_stub_add(ssl, worker, skipwhite(p+8)); - return; - } else if(cmdcmp(p, "stub_remove", 11)) { - /* must always distribute this cmd */ - if(rc) distribute_cmd(rc, ssl, cmd); - do_stub_remove(ssl, worker, skipwhite(p+11)); - return; - } else if(cmdcmp(p, "forward_add", 11)) { - /* must always distribute this cmd */ - if(rc) distribute_cmd(rc, ssl, cmd); - do_forward_add(ssl, worker, skipwhite(p+11)); - return; - } else if(cmdcmp(p, "forward_remove", 14)) { - /* must always distribute this cmd */ - if(rc) distribute_cmd(rc, ssl, cmd); - do_forward_remove(ssl, worker, skipwhite(p+14)); - return; } else if(cmdcmp(p, "insecure_add", 12)) { /* must always distribute this cmd */ if(rc) distribute_cmd(rc, ssl, cmd); @@ -3101,11 +3132,6 @@ execute_cmd(struct daemon_remote* rc, RE if(rc) distribute_cmd(rc, ssl, cmd); do_insecure_remove(ssl, worker, skipwhite(p+15)); return; - } else if(cmdcmp(p, "forward", 7)) { - /* must always distribute this cmd */ - if(rc) distribute_cmd(rc, ssl, cmd); - do_forward(ssl, worker, skipwhite(p+7)); - return; } else if(cmdcmp(p, "flush_stats", 11)) { /* must always distribute this cmd */ if(rc) distribute_cmd(rc, ssl, cmd); @@ -3147,6 +3173,16 @@ execute_cmd(struct daemon_remote* rc, RE do_data_add(ssl, worker->daemon->local_zones, skipwhite(p+10)); } else if(cmdcmp(p, "local_datas", 11)) { do_datas_add(ssl, worker->daemon->local_zones); + } else if(cmdcmp(p, "forward_add", 11)) { + do_forward_add(ssl, worker, skipwhite(p+11)); + } else if(cmdcmp(p, "forward_remove", 14)) { + do_forward_remove(ssl, worker, skipwhite(p+14)); + } else if(cmdcmp(p, "forward", 7)) { + do_forward(ssl, worker, skipwhite(p+7)); + } else if(cmdcmp(p, "stub_add", 8)) { + do_stub_add(ssl, worker, skipwhite(p+8)); + } else if(cmdcmp(p, "stub_remove", 11)) { + do_stub_remove(ssl, worker, skipwhite(p+11)); } else if(cmdcmp(p, "view_local_zone_remove", 22)) { do_view_zone_remove(ssl, worker, skipwhite(p+22)); } else if(cmdcmp(p, "view_local_zone", 15)) { Index: daemon/unbound.c =================================================================== RCS file: /cvs/src/usr.sbin/unbound/daemon/unbound.c,v diff -u -p -r1.31 unbound.c --- daemon/unbound.c 23 Feb 2022 12:04:05 -0000 1.31 +++ daemon/unbound.c 22 May 2024 10:33:17 -0000 @@ -366,9 +366,8 @@ readpid (const char* file) /** write pid to file. * @param pidfile: file name of pid file. * @param pid: pid to write to file. - * @return false on failure */ -static int +static void writepid (const char* pidfile, pid_t pid) { int fd; @@ -383,7 +382,7 @@ writepid (const char* pidfile, pid_t pid , 0644)) == -1) { log_err("cannot open pidfile %s: %s", pidfile, strerror(errno)); - return 0; + return; } while(count < strlen(pidbuf)) { ssize_t r = write(fd, pidbuf+count, strlen(pidbuf)-count); @@ -393,17 +392,16 @@ writepid (const char* pidfile, pid_t pid log_err("cannot write to pidfile %s: %s", pidfile, strerror(errno)); close(fd); - return 0; + return; } else if(r == 0) { log_err("cannot write any bytes to pidfile %s: " "write returns 0 bytes written", pidfile); close(fd); - return 0; + return; } count += r; } close(fd); - return 1; } /** @@ -545,7 +543,15 @@ perform_setup(struct daemon* daemon, str cfg, 1); if(!daemon->pidfile) fatal_exit("pidfile alloc: out of memory"); - checkoldpid(daemon->pidfile, pidinchroot); + /* Check old pid if there is no username configured. + * With a username, the assumption is that the privilege + * drop makes a pidfile not removed when the server stopped + * last time. The server does not chown the pidfile for it, + * because that creates privilege escape problems, with the + * pidfile writable by unprivileged users, but used by + * privileged users. */ + if(cfg->username && cfg->username[0]) + checkoldpid(daemon->pidfile, pidinchroot); } #endif @@ -557,18 +563,7 @@ perform_setup(struct daemon* daemon, str /* write new pidfile (while still root, so can be outside chroot) */ #ifdef HAVE_KILL if(cfg->pidfile && cfg->pidfile[0] && need_pidfile) { - if(writepid(daemon->pidfile, getpid())) { - if(cfg->username && cfg->username[0] && cfg_uid != (uid_t)-1 && - pidinchroot) { -# ifdef HAVE_CHOWN - if(chown(daemon->pidfile, cfg_uid, cfg_gid) == -1) { - verbose(VERB_QUERY, "cannot chown %u.%u %s: %s", - (unsigned)cfg_uid, (unsigned)cfg_gid, - daemon->pidfile, strerror(errno)); - } -# endif /* HAVE_CHOWN */ - } - } + writepid(daemon->pidfile, getpid()); } #else (void)daemon; @@ -746,7 +741,11 @@ run_daemon(const char* cfgfile, int cmdl if(daemon->pidfile) { int fd; /* truncate pidfile */ - fd = open(daemon->pidfile, O_WRONLY | O_TRUNC, 0644); + fd = open(daemon->pidfile, O_WRONLY | O_TRUNC +#ifdef O_NOFOLLOW + | O_NOFOLLOW +#endif + , 0644); if(fd != -1) close(fd); /* delete pidfile */ Index: daemon/worker.c =================================================================== RCS file: /cvs/src/usr.sbin/unbound/daemon/worker.c,v diff -u -p -r1.39 worker.c --- daemon/worker.c 13 Apr 2024 12:24:57 -0000 1.39 +++ daemon/worker.c 22 May 2024 10:33:17 -0000 @@ -659,7 +659,12 @@ answer_from_cache(struct worker* worker, if(rep->ttl < timenow) { /* Check if we need to serve expired now */ if(worker->env.cfg->serve_expired && - !worker->env.cfg->serve_expired_client_timeout) { + !worker->env.cfg->serve_expired_client_timeout +#ifdef USE_CACHEDB + && !(worker->env.cachedb_enabled && + worker->env.cfg->cachedb_check_when_serve_expired) +#endif + ) { if(worker->env.cfg->serve_expired_ttl && rep->serve_expired_ttl < timenow) return 0; @@ -1454,8 +1459,8 @@ worker_handle_request(struct comm_point* */ if(worker->dtenv.log_client_query_messages) { log_addr(VERB_ALGO, "request from client", &repinfo->client_addr, repinfo->client_addrlen); - log_addr(VERB_ALGO, "to local addr", (void*)repinfo->c->socket->addr->ai_addr, repinfo->c->socket->addr->ai_addrlen); - dt_msg_send_client_query(&worker->dtenv, &repinfo->client_addr, (void*)repinfo->c->socket->addr->ai_addr, c->type, c->ssl, c->buffer, + log_addr(VERB_ALGO, "to local addr", (void*)repinfo->c->socket->addr, repinfo->c->socket->addrlen); + dt_msg_send_client_query(&worker->dtenv, &repinfo->client_addr, (void*)repinfo->c->socket->addr, c->type, c->ssl, c->buffer, ((worker->env.cfg->sock_queue_timeout && timeval_isset(&c->recv_tv))?&c->recv_tv:NULL)); } #endif @@ -1943,10 +1948,10 @@ send_reply_rc: /* * sending src (client)/dst (local service) addresses over DNSTAP from send_reply code label (when we serviced local zone for ex.) */ - if(worker->dtenv.log_client_response_messages) { - log_addr(VERB_ALGO, "from local addr", (void*)repinfo->c->socket->addr->ai_addr, repinfo->c->socket->addr->ai_addrlen); + if(worker->dtenv.log_client_response_messages && rc !=0) { + log_addr(VERB_ALGO, "from local addr", (void*)repinfo->c->socket->addr, repinfo->c->socket->addrlen); log_addr(VERB_ALGO, "response to client", &repinfo->client_addr, repinfo->client_addrlen); - dt_msg_send_client_response(&worker->dtenv, &repinfo->client_addr, (void*)repinfo->c->socket->addr->ai_addr, c->type, c->ssl, c->buffer); + dt_msg_send_client_response(&worker->dtenv, &repinfo->client_addr, (void*)repinfo->c->socket->addr, c->type, c->ssl, c->buffer); } #endif if(worker->env.cfg->log_replies) @@ -1961,13 +1966,13 @@ send_reply_rc: log_reply_info(NO_VERBOSE, &qinfo, &repinfo->client_addr, repinfo->client_addrlen, tv, 1, c->buffer, - (worker->env.cfg->log_destaddr?(void*)repinfo->c->socket->addr->ai_addr:NULL), + (worker->env.cfg->log_destaddr?(void*)repinfo->c->socket->addr:NULL), c->type); } else { log_reply_info(NO_VERBOSE, &qinfo, &repinfo->client_addr, repinfo->client_addrlen, tv, 1, c->buffer, - (worker->env.cfg->log_destaddr?(void*)repinfo->c->socket->addr->ai_addr:NULL), + (worker->env.cfg->log_destaddr?(void*)repinfo->c->socket->addr:NULL), c->type); } } @@ -2261,18 +2266,6 @@ worker_init(struct worker* worker, struc worker_delete(worker); return 0; } - if(!(worker->env.fwds = forwards_create()) || - !forwards_apply_cfg(worker->env.fwds, cfg)) { - log_err("Could not set forward zones"); - worker_delete(worker); - return 0; - } - if(!(worker->env.hints = hints_create()) || - !hints_apply_cfg(worker->env.hints, cfg)) { - log_err("Could not set root or stub hints"); - worker_delete(worker); - return 0; - } /* one probe timer per process -- if we have 5011 anchors */ if(autr_get_num_anchors(worker->env.anchors) > 0 #ifndef THREADS_DISABLED @@ -2345,8 +2338,6 @@ worker_delete(struct worker* worker) outside_network_quit_prepare(worker->back); mesh_delete(worker->env.mesh); sldns_buffer_free(worker->env.scratch_buffer); - forwards_delete(worker->env.fwds); - hints_delete(worker->env.hints); listen_delete(worker->front); outside_network_delete(worker->back); comm_signal_delete(worker->comsig); Index: edns-subnet/subnetmod.c =================================================================== RCS file: /cvs/src/usr.sbin/unbound/edns-subnet/subnetmod.c,v diff -u -p -r1.15 subnetmod.c --- edns-subnet/subnetmod.c 12 Apr 2024 15:45:24 -0000 1.15 +++ edns-subnet/subnetmod.c 22 May 2024 10:33:17 -0000 @@ -57,6 +57,9 @@ #include "sldns/sbuffer.h" #include "sldns/wire2str.h" #include "iterator/iter_utils.h" +#ifdef USE_CACHEDB +#include "cachedb/cachedb.h" +#endif /** externally called */ void @@ -152,7 +155,7 @@ int ecs_whitelist_check(struct query_inf /* Cache by default, might be disabled after parsing EDNS option * received from nameserver. */ - if(!iter_stub_fwd_no_cache(qstate, &qstate->qinfo, NULL, NULL)) { + if(!iter_stub_fwd_no_cache(qstate, &qstate->qinfo, NULL, NULL, NULL, 0)) { qstate->no_cache_store = 0; } @@ -310,9 +313,18 @@ delfunc(void *envptr, void *elemptr) { static size_t sizefunc(void *elemptr) { struct reply_info *elem = (struct reply_info *)elemptr; - return sizeof (struct reply_info) - sizeof (struct rrset_ref) + size_t s = sizeof (struct reply_info) - sizeof (struct rrset_ref) + elem->rrset_count * sizeof (struct rrset_ref) + elem->rrset_count * sizeof (struct ub_packed_rrset_key *); + size_t i; + for (i = 0; i < elem->rrset_count; i++) { + struct ub_packed_rrset_key *key = elem->rrsets[i]; + struct packed_rrset_data *data = key->entry.data; + s += ub_rrset_sizefunc(key, data); + } + if(elem->reason_bogus_str) + s += strlen(elem->reason_bogus_str)+1; + return s; } /** @@ -352,7 +364,7 @@ update_cache(struct module_qstate *qstat struct slabhash *subnet_msg_cache = sne->subnet_msg_cache; struct ecs_data *edns = &sq->ecs_client_in; size_t i; - int only_match_scope_zero; + int only_match_scope_zero, diff_size; /* We already calculated hash upon lookup (lookup_and_reply) if we were * allowed to look in the ECS cache */ @@ -412,19 +424,25 @@ update_cache(struct module_qstate *qstat rep->ref[i].id = rep->rrsets[i]->id; } reply_info_set_ttls(rep, *qstate->env->now); + reply_info_sortref(rep); rep->flags |= (BIT_RA | BIT_QR); /* fix flags to be sensible for */ rep->flags &= ~(BIT_AA | BIT_CD);/* a reply based on the cache */ if(edns->subnet_source_mask == 0 && edns->subnet_scope_mask == 0) only_match_scope_zero = 1; else only_match_scope_zero = 0; + diff_size = (int)tree->size_bytes; addrtree_insert(tree, (addrkey_t*)edns->subnet_addr, edns->subnet_source_mask, sq->max_scope, rep, rep->ttl, *qstate->env->now, only_match_scope_zero); + diff_size = (int)tree->size_bytes - diff_size; lock_rw_unlock(&lru_entry->lock); if (need_to_insert) { slabhash_insert(subnet_msg_cache, h, lru_entry, lru_entry->data, NULL); + } else { + slabhash_update_space_used(subnet_msg_cache, h, NULL, + diff_size); } } @@ -587,7 +605,21 @@ eval_response(struct module_qstate *qsta } sne->num_msg_nocache++; lock_rw_unlock(&sne->biglock); - + + /* If there is an expired answer in the global cache, remove that, + * because expired answers would otherwise resurface once the ecs data + * expires, giving once in a while global data responses for ecs + * domains, with serve expired enabled. */ + if(qstate->env->cfg->serve_expired) { + msg_cache_remove(qstate->env, qstate->qinfo.qname, + qstate->qinfo.qname_len, qstate->qinfo.qtype, + qstate->qinfo.qclass, 0); +#ifdef USE_CACHEDB + if(qstate->env->cachedb_enabled) + cachedb_msg_remove(qstate); +#endif + } + if (sq->subnet_downstream) { /* Client wants to see the answer, echo option back * and adjust the scope. */ Index: iterator/iter_fwd.c =================================================================== RCS file: /cvs/src/usr.sbin/unbound/iterator/iter_fwd.c,v diff -u -p -r1.8 iter_fwd.c --- iterator/iter_fwd.c 13 Apr 2024 12:24:57 -0000 1.8 +++ iterator/iter_fwd.c 22 May 2024 10:33:17 -0000 @@ -71,6 +71,7 @@ forwards_create(void) sizeof(struct iter_forwards)); if(!fwd) return NULL; + lock_rw_init(&fwd->lock); return fwd; } @@ -100,6 +101,7 @@ forwards_delete(struct iter_forwards* fw { if(!fwd) return; + lock_rw_destroy(&fwd->lock); fwd_del_tree(fwd); free(fwd); } @@ -332,45 +334,64 @@ make_stub_holes(struct iter_forwards* fw int forwards_apply_cfg(struct iter_forwards* fwd, struct config_file* cfg) { + if(fwd->tree) { + lock_unprotect(&fwd->lock, fwd->tree); + } fwd_del_tree(fwd); fwd->tree = rbtree_create(fwd_cmp); if(!fwd->tree) return 0; + lock_protect(&fwd->lock, fwd->tree, sizeof(*fwd->tree)); + lock_rw_wrlock(&fwd->lock); /* read forward zones */ - if(!read_forwards(fwd, cfg)) + if(!read_forwards(fwd, cfg)) { + lock_rw_unlock(&fwd->lock); return 0; - if(!make_stub_holes(fwd, cfg)) + } + if(!make_stub_holes(fwd, cfg)) { + lock_rw_unlock(&fwd->lock); return 0; + } fwd_init_parents(fwd); + lock_rw_unlock(&fwd->lock); return 1; } struct delegpt* -forwards_find(struct iter_forwards* fwd, uint8_t* qname, uint16_t qclass) +forwards_find(struct iter_forwards* fwd, uint8_t* qname, uint16_t qclass, + int nolock) { - rbnode_type* res = NULL; + struct iter_forward_zone* res; struct iter_forward_zone key; + int has_dp; key.node.key = &key; key.dclass = qclass; key.name = qname; key.namelabs = dname_count_size_labels(qname, &key.namelen); - res = rbtree_search(fwd->tree, &key); - if(res) return ((struct iter_forward_zone*)res)->dp; - return NULL; + /* lock_() calls are macros that could be nothing, surround in {} */ + if(!nolock) { lock_rw_rdlock(&fwd->lock); } + res = (struct iter_forward_zone*)rbtree_search(fwd->tree, &key); + has_dp = res && res->dp; + if(!has_dp && !nolock) { lock_rw_unlock(&fwd->lock); } + return has_dp?res->dp:NULL; } struct delegpt* -forwards_lookup(struct iter_forwards* fwd, uint8_t* qname, uint16_t qclass) +forwards_lookup(struct iter_forwards* fwd, uint8_t* qname, uint16_t qclass, + int nolock) { /* lookup the forward zone in the tree */ rbnode_type* res = NULL; struct iter_forward_zone *result; struct iter_forward_zone key; + int has_dp; key.node.key = &key; key.dclass = qclass; key.name = qname; key.namelabs = dname_count_size_labels(qname, &key.namelen); + /* lock_() calls are macros that could be nothing, surround in {} */ + if(!nolock) { lock_rw_rdlock(&fwd->lock); } if(rbtree_find_less_equal(fwd->tree, &key, &res)) { /* exact */ result = (struct iter_forward_zone*)res; @@ -378,8 +399,10 @@ forwards_lookup(struct iter_forwards* fw /* smaller element (or no element) */ int m; result = (struct iter_forward_zone*)res; - if(!result || result->dclass != qclass) + if(!result || result->dclass != qclass) { + if(!nolock) { lock_rw_unlock(&fwd->lock); } return NULL; + } /* count number of labels matched */ (void)dname_lab_cmp(result->name, result->namelabs, key.name, key.namelabs, &m); @@ -389,20 +412,22 @@ forwards_lookup(struct iter_forwards* fw result = result->parent; } } - if(result) - return result->dp; - return NULL; + has_dp = result && result->dp; + if(!has_dp && !nolock) { lock_rw_unlock(&fwd->lock); } + return has_dp?result->dp:NULL; } struct delegpt* -forwards_lookup_root(struct iter_forwards* fwd, uint16_t qclass) +forwards_lookup_root(struct iter_forwards* fwd, uint16_t qclass, int nolock) { uint8_t root = 0; - return forwards_lookup(fwd, &root, qclass); + return forwards_lookup(fwd, &root, qclass, nolock); } -int -forwards_next_root(struct iter_forwards* fwd, uint16_t* dclass) +/* Finds next root item in forwards lookup tree. + * Caller needs to handle locking of the forwards structure. */ +static int +next_root_locked(struct iter_forwards* fwd, uint16_t* dclass) { struct iter_forward_zone key; rbnode_type* n; @@ -419,7 +444,7 @@ forwards_next_root(struct iter_forwards* } /* root not first item? search for higher items */ *dclass = p->dclass + 1; - return forwards_next_root(fwd, dclass); + return next_root_locked(fwd, dclass); } /* find class n in tree, we may get a direct hit, or if we don't * this is the last item of the previous class so rbtree_next() takes @@ -447,10 +472,21 @@ forwards_next_root(struct iter_forwards* } /* not a root node, return next higher item */ *dclass = p->dclass+1; - return forwards_next_root(fwd, dclass); + return next_root_locked(fwd, dclass); } } +int +forwards_next_root(struct iter_forwards* fwd, uint16_t* dclass, int nolock) +{ + int ret; + /* lock_() calls are macros that could be nothing, surround in {} */ + if(!nolock) { lock_rw_rdlock(&fwd->lock); } + ret = next_root_locked(fwd, dclass); + if(!nolock) { lock_rw_unlock(&fwd->lock); } + return ret; +} + size_t forwards_get_mem(struct iter_forwards* fwd) { @@ -458,10 +494,12 @@ forwards_get_mem(struct iter_forwards* f size_t s; if(!fwd) return 0; + lock_rw_rdlock(&fwd->lock); s = sizeof(*fwd) + sizeof(*fwd->tree); RBTREE_FOR(p, struct iter_forward_zone*, fwd->tree) { s += sizeof(*p) + p->namelen + delegpt_get_mem(p->dp); } + lock_rw_unlock(&fwd->lock); return s; } @@ -477,49 +515,78 @@ fwd_zone_find(struct iter_forwards* fwd, } int -forwards_add_zone(struct iter_forwards* fwd, uint16_t c, struct delegpt* dp) +forwards_add_zone(struct iter_forwards* fwd, uint16_t c, struct delegpt* dp, + int nolock) { struct iter_forward_zone *z; + /* lock_() calls are macros that could be nothing, surround in {} */ + if(!nolock) { lock_rw_wrlock(&fwd->lock); } if((z=fwd_zone_find(fwd, c, dp->name)) != NULL) { (void)rbtree_delete(fwd->tree, &z->node); fwd_zone_free(z); } - if(!forwards_insert(fwd, c, dp)) + if(!forwards_insert(fwd, c, dp)) { + if(!nolock) { lock_rw_unlock(&fwd->lock); } return 0; + } fwd_init_parents(fwd); + if(!nolock) { lock_rw_unlock(&fwd->lock); } return 1; } void -forwards_delete_zone(struct iter_forwards* fwd, uint16_t c, uint8_t* nm) +forwards_delete_zone(struct iter_forwards* fwd, uint16_t c, uint8_t* nm, + int nolock) { struct iter_forward_zone *z; - if(!(z=fwd_zone_find(fwd, c, nm))) + /* lock_() calls are macros that could be nothing, surround in {} */ + if(!nolock) { lock_rw_wrlock(&fwd->lock); } + if(!(z=fwd_zone_find(fwd, c, nm))) { + if(!nolock) { lock_rw_unlock(&fwd->lock); } return; /* nothing to do */ + } (void)rbtree_delete(fwd->tree, &z->node); fwd_zone_free(z); fwd_init_parents(fwd); + if(!nolock) { lock_rw_unlock(&fwd->lock); } } int -forwards_add_stub_hole(struct iter_forwards* fwd, uint16_t c, uint8_t* nm) +forwards_add_stub_hole(struct iter_forwards* fwd, uint16_t c, uint8_t* nm, + int nolock) { + /* lock_() calls are macros that could be nothing, surround in {} */ + if(!nolock) { lock_rw_wrlock(&fwd->lock); } + if(fwd_zone_find(fwd, c, nm) != NULL) { + if(!nolock) { lock_rw_unlock(&fwd->lock); } + return 1; /* already a stub zone there */ + } if(!fwd_add_stub_hole(fwd, c, nm)) { + if(!nolock) { lock_rw_unlock(&fwd->lock); } return 0; } fwd_init_parents(fwd); + if(!nolock) { lock_rw_unlock(&fwd->lock); } return 1; } void -forwards_delete_stub_hole(struct iter_forwards* fwd, uint16_t c, uint8_t* nm) +forwards_delete_stub_hole(struct iter_forwards* fwd, uint16_t c, + uint8_t* nm, int nolock) { struct iter_forward_zone *z; - if(!(z=fwd_zone_find(fwd, c, nm))) + /* lock_() calls are macros that could be nothing, surround in {} */ + if(!nolock) { lock_rw_wrlock(&fwd->lock); } + if(!(z=fwd_zone_find(fwd, c, nm))) { + if(!nolock) { lock_rw_unlock(&fwd->lock); } return; /* nothing to do */ - if(z->dp != NULL) + } + if(z->dp != NULL) { + if(!nolock) { lock_rw_unlock(&fwd->lock); } return; /* not a stub hole */ + } (void)rbtree_delete(fwd->tree, &z->node); fwd_zone_free(z); fwd_init_parents(fwd); + if(!nolock) { lock_rw_unlock(&fwd->lock); } } Index: iterator/iter_fwd.h =================================================================== RCS file: /cvs/src/usr.sbin/unbound/iterator/iter_fwd.h,v diff -u -p -r1.2 iter_fwd.h --- iterator/iter_fwd.h 17 Feb 2017 18:53:32 -0000 1.2 +++ iterator/iter_fwd.h 22 May 2024 10:33:17 -0000 @@ -43,6 +43,7 @@ #ifndef ITERATOR_ITER_FWD_H #define ITERATOR_ITER_FWD_H #include "util/rbtree.h" +#include "util/locks.h" struct config_file; struct delegpt; @@ -50,6 +51,11 @@ struct delegpt; * Iterator forward zones structure */ struct iter_forwards { + /** lock on the forwards tree. + * When grabbing both this lock and the anchors.lock, this lock + * is grabbed first. When grabbing both this lock and the hints.lock + * this lock is grabbed first. */ + lock_rw_type lock; /** * Zones are stored in this tree. Sort order is specially chosen. * first sorted on qclass. Then on dname in nsec-like order, so that @@ -106,47 +112,65 @@ int forwards_apply_cfg(struct iter_forwa /** * Find forward zone exactly by name + * The return value is contents of the forwards structure. + * Caller should lock and unlock a readlock on the forwards structure if nolock + * is set. + * Otherwise caller should unlock the readlock on the forwards structure if a + * value was returned. * @param fwd: forward storage. * @param qname: The qname of the query. * @param qclass: The qclass of the query. + * @param nolock: Skip locking, locking is handled by the caller. * @return: A delegation point or null. */ struct delegpt* forwards_find(struct iter_forwards* fwd, uint8_t* qname, - uint16_t qclass); + uint16_t qclass, int nolock); /** * Find forward zone information * For this qname/qclass find forward zone information, returns delegation * point with server names and addresses, or NULL if no forwarding is needed. + * The return value is contents of the forwards structure. + * Caller should lock and unlock a readlock on the forwards structure if nolock + * is set. + * Otherwise caller should unlock the readlock on the forwards structure if a + * value was returned. * * @param fwd: forward storage. * @param qname: The qname of the query. * @param qclass: The qclass of the query. + * @param nolock: Skip locking, locking is handled by the caller. * @return: A delegation point if the query has to be forwarded to that list, * otherwise null. */ -struct delegpt* forwards_lookup(struct iter_forwards* fwd, - uint8_t* qname, uint16_t qclass); +struct delegpt* forwards_lookup(struct iter_forwards* fwd, + uint8_t* qname, uint16_t qclass, int nolock); /** * Same as forwards_lookup, but for the root only * @param fwd: forward storage. * @param qclass: The qclass of the query. + * @param nolock: Skip locking, locking is handled by the caller. * @return: A delegation point if root forward exists, otherwise null. */ -struct delegpt* forwards_lookup_root(struct iter_forwards* fwd, - uint16_t qclass); +struct delegpt* forwards_lookup_root(struct iter_forwards* fwd, + uint16_t qclass, int nolock); /** * Find next root item in forwards lookup tree. + * Handles its own locking unless nolock is set. In that case the caller + * should lock and unlock a readlock on the forwards structure. * @param fwd: the forward storage * @param qclass: class to look at next, or higher. + * @param nolock: Skip locking, locking is handled by the caller. * @return false if none found, or if true stored in qclass. */ -int forwards_next_root(struct iter_forwards* fwd, uint16_t* qclass); +int forwards_next_root(struct iter_forwards* fwd, uint16_t* qclass, + int nolock); /** * Get memory in use by forward storage + * Locks and unlocks the structure. * @param fwd: forward storage. * @return bytes in use */ @@ -158,42 +182,56 @@ int fwd_cmp(const void* k1, const void* /** * Add zone to forward structure. For external use since it recalcs * the tree parents. + * Handles its own locking unless nolock is set. In that case the caller + * should lock and unlock a writelock on the forwards structure. * @param fwd: the forward data structure * @param c: class of zone * @param dp: delegation point with name and target nameservers for new * forward zone. malloced. + * @param nolock: Skip locking, locking is handled by the caller. * @return false on failure (out of memory); */ -int forwards_add_zone(struct iter_forwards* fwd, uint16_t c, - struct delegpt* dp); +int forwards_add_zone(struct iter_forwards* fwd, uint16_t c, + struct delegpt* dp, int nolock); /** * Remove zone from forward structure. For external use since it * recalcs the tree parents. + * Handles its own locking unless nolock is set. In that case the caller + * should lock and unlock a writelock on the forwards structure. * @param fwd: the forward data structure * @param c: class of zone * @param nm: name of zone (in uncompressed wireformat). + * @param nolock: Skip locking, locking is handled by the caller. */ -void forwards_delete_zone(struct iter_forwards* fwd, uint16_t c, uint8_t* nm); +void forwards_delete_zone(struct iter_forwards* fwd, uint16_t c, + uint8_t* nm, int nolock); /** * Add stub hole (empty entry in forward table, that makes resolution skip * a forward-zone because the stub zone should override the forward zone). * Does not add one if not necessary. + * Handles its own locking unless nolock is set. In that case the caller + * should lock and unlock a writelock on the forwards structure. * @param fwd: the forward data structure * @param c: class of zone * @param nm: name of zone (in uncompressed wireformat). + * @param nolock: Skip locking, locking is handled by the caller. * @return false on failure (out of memory); */ -int forwards_add_stub_hole(struct iter_forwards* fwd, uint16_t c, uint8_t* nm); +int forwards_add_stub_hole(struct iter_forwards* fwd, uint16_t c, + uint8_t* nm, int nolock); /** * Remove stub hole, if one exists. + * Handles its own locking unless nolock is set. In that case the caller + * should lock and unlock a writelock on the forwards structure. * @param fwd: the forward data structure * @param c: class of zone * @param nm: name of zone (in uncompressed wireformat). + * @param nolock: Skip locking, locking is handled by the caller. */ void forwards_delete_stub_hole(struct iter_forwards* fwd, uint16_t c, - uint8_t* nm); + uint8_t* nm, int nolock); #endif /* ITERATOR_ITER_FWD_H */ Index: iterator/iter_hints.c =================================================================== RCS file: /cvs/src/usr.sbin/unbound/iterator/iter_hints.c,v diff -u -p -r1.17 iter_hints.c --- iterator/iter_hints.c 13 Apr 2024 12:24:57 -0000 1.17 +++ iterator/iter_hints.c 22 May 2024 10:33:17 -0000 @@ -57,6 +57,8 @@ hints_create(void) sizeof(struct iter_hints)); if(!hints) return NULL; + lock_rw_init(&hints->lock); + lock_protect(&hints->lock, &hints->tree, sizeof(hints->tree)); return hints; } @@ -83,6 +85,7 @@ hints_delete(struct iter_hints* hints) { if(!hints) return; + lock_rw_destroy(&hints->lock); hints_del_tree(hints); free(hints); } @@ -438,47 +441,70 @@ read_root_hints_list(struct iter_hints* int hints_apply_cfg(struct iter_hints* hints, struct config_file* cfg) { + int nolock = 1; + lock_rw_wrlock(&hints->lock); hints_del_tree(hints); name_tree_init(&hints->tree); - + /* read root hints */ - if(!read_root_hints_list(hints, cfg)) + if(!read_root_hints_list(hints, cfg)) { + lock_rw_unlock(&hints->lock); return 0; + } /* read stub hints */ - if(!read_stubs(hints, cfg)) + if(!read_stubs(hints, cfg)) { + lock_rw_unlock(&hints->lock); return 0; + } /* use fallback compiletime root hints */ - if(!hints_lookup_root(hints, LDNS_RR_CLASS_IN)) { + if(!hints_find_root(hints, LDNS_RR_CLASS_IN, nolock)) { struct delegpt* dp = compile_time_root_prime(cfg->do_ip4, cfg->do_ip6); verbose(VERB_ALGO, "no config, using builtin root hints."); - if(!dp) + if(!dp) { + lock_rw_unlock(&hints->lock); return 0; - if(!hints_insert(hints, LDNS_RR_CLASS_IN, dp, 0)) + } + if(!hints_insert(hints, LDNS_RR_CLASS_IN, dp, 0)) { + lock_rw_unlock(&hints->lock); return 0; + } } name_tree_init_parents(&hints->tree); + lock_rw_unlock(&hints->lock); return 1; } -struct delegpt* -hints_lookup_root(struct iter_hints* hints, uint16_t qclass) +struct delegpt* +hints_find(struct iter_hints* hints, uint8_t* qname, uint16_t qclass, + int nolock) { - uint8_t rootlab = 0; struct iter_hints_stub *stub; + size_t len; + int has_dp; + int labs = dname_count_size_labels(qname, &len); + /* lock_() calls are macros that could be nothing, surround in {} */ + if(!nolock) { lock_rw_rdlock(&hints->lock); } stub = (struct iter_hints_stub*)name_tree_find(&hints->tree, - &rootlab, 1, 1, qclass); - if(!stub) - return NULL; - return stub->dp; + qname, len, labs, qclass); + has_dp = stub && stub->dp; + if(!has_dp && !nolock) { lock_rw_unlock(&hints->lock); } + return has_dp?stub->dp:NULL; +} + +struct delegpt* +hints_find_root(struct iter_hints* hints, uint16_t qclass, int nolock) +{ + uint8_t rootlab = 0; + return hints_find(hints, &rootlab, qclass, nolock); } struct iter_hints_stub* -hints_lookup_stub(struct iter_hints* hints, uint8_t* qname, - uint16_t qclass, struct delegpt* cache_dp) +hints_lookup_stub(struct iter_hints* hints, uint8_t* qname, + uint16_t qclass, struct delegpt* cache_dp, int nolock) { size_t len; int labs; @@ -486,14 +512,20 @@ hints_lookup_stub(struct iter_hints* hin /* first lookup the stub */ labs = dname_count_size_labels(qname, &len); + /* lock_() calls are macros that could be nothing, surround in {} */ + if(!nolock) { lock_rw_rdlock(&hints->lock); } r = (struct iter_hints_stub*)name_tree_lookup(&hints->tree, qname, len, labs, qclass); - if(!r) return NULL; + if(!r) { + if(!nolock) { lock_rw_unlock(&hints->lock); } + return NULL; + } /* If there is no cache (root prime situation) */ if(cache_dp == NULL) { if(r->dp->namelabs != 1) return r; /* no cache dp, use any non-root stub */ + if(!nolock) { lock_rw_unlock(&hints->lock); } return NULL; } @@ -510,12 +542,18 @@ hints_lookup_stub(struct iter_hints* hin if(dname_strict_subdomain(r->dp->name, r->dp->namelabs, cache_dp->name, cache_dp->namelabs)) return r; /* need to prime this stub */ + if(!nolock) { lock_rw_unlock(&hints->lock); } return NULL; } -int hints_next_root(struct iter_hints* hints, uint16_t* qclass) +int hints_next_root(struct iter_hints* hints, uint16_t* qclass, int nolock) { - return name_tree_next_root(&hints->tree, qclass); + int ret; + /* lock_() calls are macros that could be nothing, surround in {} */ + if(!nolock) { lock_rw_rdlock(&hints->lock); } + ret = name_tree_next_root(&hints->tree, qclass); + if(!nolock) { lock_rw_unlock(&hints->lock); } + return ret; } size_t @@ -524,39 +562,52 @@ hints_get_mem(struct iter_hints* hints) size_t s; struct iter_hints_stub* p; if(!hints) return 0; + lock_rw_rdlock(&hints->lock); s = sizeof(*hints); RBTREE_FOR(p, struct iter_hints_stub*, &hints->tree) { s += sizeof(*p) + delegpt_get_mem(p->dp); } + lock_rw_unlock(&hints->lock); return s; } int hints_add_stub(struct iter_hints* hints, uint16_t c, struct delegpt* dp, - int noprime) + int noprime, int nolock) { struct iter_hints_stub *z; + /* lock_() calls are macros that could be nothing, surround in {} */ + if(!nolock) { lock_rw_wrlock(&hints->lock); } if((z=(struct iter_hints_stub*)name_tree_find(&hints->tree, dp->name, dp->namelen, dp->namelabs, c)) != NULL) { (void)rbtree_delete(&hints->tree, &z->node); hints_stub_free(z); } - if(!hints_insert(hints, c, dp, noprime)) + if(!hints_insert(hints, c, dp, noprime)) { + if(!nolock) { lock_rw_unlock(&hints->lock); } return 0; + } name_tree_init_parents(&hints->tree); + if(!nolock) { lock_rw_unlock(&hints->lock); } return 1; } void -hints_delete_stub(struct iter_hints* hints, uint16_t c, uint8_t* nm) +hints_delete_stub(struct iter_hints* hints, uint16_t c, uint8_t* nm, + int nolock) { struct iter_hints_stub *z; size_t len; int labs = dname_count_size_labels(nm, &len); + /* lock_() calls are macros that could be nothing, surround in {} */ + if(!nolock) { lock_rw_wrlock(&hints->lock); } if(!(z=(struct iter_hints_stub*)name_tree_find(&hints->tree, - nm, len, labs, c))) + nm, len, labs, c))) { + if(!nolock) { lock_rw_unlock(&hints->lock); } return; /* nothing to do */ + } (void)rbtree_delete(&hints->tree, &z->node); hints_stub_free(z); name_tree_init_parents(&hints->tree); + if(!nolock) { lock_rw_unlock(&hints->lock); } } Index: iterator/iter_hints.h =================================================================== RCS file: /cvs/src/usr.sbin/unbound/iterator/iter_hints.h,v diff -u -p -r1.2 iter_hints.h --- iterator/iter_hints.h 17 Feb 2017 18:53:32 -0000 1.2 +++ iterator/iter_hints.h 22 May 2024 10:33:17 -0000 @@ -43,6 +43,7 @@ #ifndef ITERATOR_ITER_HINTS_H #define ITERATOR_ITER_HINTS_H #include "util/storage/dnstree.h" +#include "util/locks.h" struct iter_env; struct config_file; struct delegpt; @@ -51,6 +52,10 @@ struct delegpt; * Iterator hints structure */ struct iter_hints { + /** lock on the forwards tree. + * When grabbing both this lock and the anchors.lock, this lock + * is grabbed first. */ + lock_rw_type lock; /** * Hints are stored in this tree. Sort order is specially chosen. * first sorted on qclass. Then on dname in nsec-like order, so that @@ -95,42 +100,70 @@ void hints_delete(struct iter_hints* hin int hints_apply_cfg(struct iter_hints* hints, struct config_file* cfg); /** - * Find root hints for the given class. + * Find hints for the given class. + * The return value is contents of the hints structure. + * Caller should lock and unlock a readlock on the hints structure if nolock + * is set. + * Otherwise caller should unlock the readlock on the hints structure if a + * value was returned. * @param hints: hint storage. + * @param qname: the qname that generated the delegation point. * @param qclass: class for which root hints are requested. host order. + * @param nolock: Skip locking, locking is handled by the caller. * @return: NULL if no hints, or a ptr to stored hints. */ -struct delegpt* hints_lookup_root(struct iter_hints* hints, uint16_t qclass); +struct delegpt* hints_find(struct iter_hints* hints, uint8_t* qname, + uint16_t qclass, int nolock); + +/** + * Same as hints_lookup, but for the root only. + * @param hints: hint storage. + * @param qclass: class for which root hints are requested. host order. + * @param nolock: Skip locking, locking is handled by the caller. + * @return: NULL if no hints, or a ptr to stored hints. + */ +struct delegpt* hints_find_root(struct iter_hints* hints, + uint16_t qclass, int nolock); /** * Find next root hints (to cycle through all root hints). + * Handles its own locking unless nolock is set. In that case the caller + * should lock and unlock a readlock on the hints structure. * @param hints: hint storage * @param qclass: class for which root hints are sought. * 0 means give the first available root hints class. * x means, give class x or a higher class if any. * returns the found class in this variable. + * @param nolock: Skip locking, locking is handled by the caller. * @return true if a root hint class is found. * false if not root hint class is found (qclass may have been changed). */ -int hints_next_root(struct iter_hints* hints, uint16_t* qclass); +int hints_next_root(struct iter_hints* hints, uint16_t* qclass, int nolock); /** * Given a qname/qclass combination, and the delegation point from the cache * for this qname/qclass, determine if this combination indicates that a * stub hint exists and must be primed. + * The return value is contents of the hints structure. + * Caller should lock and unlock a readlock on the hints structure if nolock + * is set. + * Otherwise caller should unlock the readlock on the hints structure if a + * value was returned. * * @param hints: hint storage. * @param qname: The qname that generated the delegation point. * @param qclass: The qclass that generated the delegation point. * @param dp: The cache generated delegation point. + * @param nolock: Skip locking, locking is handled by the caller. * @return: A priming delegation point if there is a stub hint that must * be primed, otherwise null. */ -struct iter_hints_stub* hints_lookup_stub(struct iter_hints* hints, - uint8_t* qname, uint16_t qclass, struct delegpt* dp); +struct iter_hints_stub* hints_lookup_stub(struct iter_hints* hints, + uint8_t* qname, uint16_t qclass, struct delegpt* dp, int nolock); /** * Get memory in use by hints + * Locks and unlocks the structure. * @param hints: hint storage. * @return bytes in use */ @@ -139,23 +172,30 @@ size_t hints_get_mem(struct iter_hints* /** * Add stub to hints structure. For external use since it recalcs * the tree parents. + * Handles its own locking unless nolock is set. In that case the caller + * should lock and unlock a writelock on the hints structure. * @param hints: the hints data structure * @param c: class of zone * @param dp: delegation point with name and target nameservers for new * hints stub. malloced. * @param noprime: set noprime option to true or false on new hint stub. + * @param nolock: Skip locking, locking is handled by the caller. * @return false on failure (out of memory); */ int hints_add_stub(struct iter_hints* hints, uint16_t c, struct delegpt* dp, - int noprime); + int noprime, int nolock); /** * Remove stub from hints structure. For external use since it * recalcs the tree parents. + * Handles its own locking unless nolock is set. In that case the caller + * should lock and unlock a writelock on the hints structure. * @param hints: the hints data structure * @param c: class of stub zone * @param nm: name of stub zone (in uncompressed wireformat). + * @param nolock: Skip locking, locking is handled by the caller. */ -void hints_delete_stub(struct iter_hints* hints, uint16_t c, uint8_t* nm); +void hints_delete_stub(struct iter_hints* hints, uint16_t c, + uint8_t* nm, int nolock); #endif /* ITERATOR_ITER_HINTS_H */ Index: iterator/iter_utils.c =================================================================== RCS file: /cvs/src/usr.sbin/unbound/iterator/iter_utils.c,v diff -u -p -r1.21 iter_utils.c --- iterator/iter_utils.c 5 Sep 2023 11:12:10 -0000 1.21 +++ iterator/iter_utils.c 22 May 2024 10:33:17 -0000 @@ -1284,8 +1284,17 @@ iter_get_next_root(struct iter_hints* hi uint16_t* c) { uint16_t c1 = *c, c2 = *c; - int r1 = hints_next_root(hints, &c1); - int r2 = forwards_next_root(fwd, &c2); + int r1, r2; + int nolock = 1; + + /* prelock both forwards and hints for atomic read. */ + lock_rw_rdlock(&fwd->lock); + lock_rw_rdlock(&hints->lock); + r1 = hints_next_root(hints, &c1, nolock); + r2 = forwards_next_root(fwd, &c2, nolock); + lock_rw_unlock(&fwd->lock); + lock_rw_unlock(&hints->lock); + if(!r1 && !r2) /* got none, end of list */ return 0; else if(!r1) /* got one, return that */ @@ -1450,15 +1459,21 @@ int iter_dp_cangodown(struct query_info* int iter_stub_fwd_no_cache(struct module_qstate *qstate, struct query_info *qinf, - uint8_t** retdpname, size_t* retdpnamelen) + uint8_t** retdpname, size_t* retdpnamelen, uint8_t* dpname_storage, + size_t dpname_storage_len) { struct iter_hints_stub *stub; struct delegpt *dp; + int nolock = 1; /* Check for stub. */ + /* Lock both forwards and hints for atomic read. */ + lock_rw_rdlock(&qstate->env->fwds->lock); + lock_rw_rdlock(&qstate->env->hints->lock); stub = hints_lookup_stub(qstate->env->hints, qinf->qname, - qinf->qclass, NULL); - dp = forwards_lookup(qstate->env->fwds, qinf->qname, qinf->qclass); + qinf->qclass, NULL, nolock); + dp = forwards_lookup(qstate->env->fwds, qinf->qname, qinf->qclass, + nolock); /* see if forward or stub is more pertinent */ if(stub && stub->dp && dp) { @@ -1472,7 +1487,9 @@ iter_stub_fwd_no_cache(struct module_qst /* check stub */ if (stub != NULL && stub->dp != NULL) { - if(stub->dp->no_cache) { + int stub_no_cache = stub->dp->no_cache; + lock_rw_unlock(&qstate->env->fwds->lock); + if(stub_no_cache) { char qname[255+1]; char dpname[255+1]; dname_str(qinf->qname, qname); @@ -1480,15 +1497,27 @@ iter_stub_fwd_no_cache(struct module_qst verbose(VERB_ALGO, "stub for %s %s has no_cache", qname, dpname); } if(retdpname) { - *retdpname = stub->dp->name; + if(stub->dp->namelen > dpname_storage_len) { + verbose(VERB_ALGO, "no cache stub dpname too long"); + lock_rw_unlock(&qstate->env->hints->lock); + *retdpname = NULL; + *retdpnamelen = 0; + return stub_no_cache; + } + memmove(dpname_storage, stub->dp->name, + stub->dp->namelen); + *retdpname = dpname_storage; *retdpnamelen = stub->dp->namelen; } - return (stub->dp->no_cache); + lock_rw_unlock(&qstate->env->hints->lock); + return stub_no_cache; } /* Check for forward. */ if (dp) { - if(dp->no_cache) { + int dp_no_cache = dp->no_cache; + lock_rw_unlock(&qstate->env->hints->lock); + if(dp_no_cache) { char qname[255+1]; char dpname[255+1]; dname_str(qinf->qname, qname); @@ -1496,11 +1525,22 @@ iter_stub_fwd_no_cache(struct module_qst verbose(VERB_ALGO, "forward for %s %s has no_cache", qname, dpname); } if(retdpname) { - *retdpname = dp->name; + if(dp->namelen > dpname_storage_len) { + verbose(VERB_ALGO, "no cache dpname too long"); + lock_rw_unlock(&qstate->env->fwds->lock); + *retdpname = NULL; + *retdpnamelen = 0; + return dp_no_cache; + } + memmove(dpname_storage, dp->name, dp->namelen); + *retdpname = dpname_storage; *retdpnamelen = dp->namelen; } - return (dp->no_cache); + lock_rw_unlock(&qstate->env->fwds->lock); + return dp_no_cache; } + lock_rw_unlock(&qstate->env->fwds->lock); + lock_rw_unlock(&qstate->env->hints->lock); if(retdpname) { *retdpname = NULL; *retdpnamelen = 0; Index: iterator/iter_utils.h =================================================================== RCS file: /cvs/src/usr.sbin/unbound/iterator/iter_utils.h,v diff -u -p -r1.15 iter_utils.h --- iterator/iter_utils.h 5 Sep 2023 11:12:10 -0000 1.15 +++ iterator/iter_utils.h 22 May 2024 10:33:17 -0000 @@ -407,10 +407,14 @@ int iter_dp_cangodown(struct query_info* * Used for NXDOMAIN checks, above that it is an nxdomain from a * different server and zone. You can pass NULL to not get it. * @param retdpnamelen: returns the length of the dpname. + * @param dpname_storage: this is where the dpname buf is stored, if any. + * So that caller can manage the buffer. + * @param dpname_storage_len: size of dpname_storage buffer. * @return true if no_cache is set in stub or fwd. */ int iter_stub_fwd_no_cache(struct module_qstate *qstate, - struct query_info *qinf, uint8_t** retdpname, size_t* retdpnamelen); + struct query_info *qinf, uint8_t** retdpname, size_t* retdpnamelen, + uint8_t* dpname_storage, size_t dpname_storage_len); /** * Set support for IP4 and IP6 depending on outgoing interfaces Index: iterator/iterator.c =================================================================== RCS file: /cvs/src/usr.sbin/unbound/iterator/iterator.c,v diff -u -p -r1.36 iterator.c --- iterator/iterator.c 13 Apr 2024 12:24:57 -0000 1.36 +++ iterator/iterator.c 22 May 2024 10:33:17 -0000 @@ -52,6 +52,7 @@ #include "iterator/iter_priv.h" #include "validator/val_neg.h" #include "services/cache/dns.h" +#include "services/cache/rrset.h" #include "services/cache/infra.h" #include "services/authzone.h" #include "util/module.h" @@ -678,30 +679,40 @@ errinf_reply(struct module_qstate* qstat /** see if last resort is possible - does config allow queries to parent */ static int -can_have_last_resort(struct module_env* env, uint8_t* nm, size_t nmlen, - uint16_t qclass, struct delegpt** retdp) +can_have_last_resort(struct module_env* env, uint8_t* nm, size_t ATTR_UNUSED(nmlen), + uint16_t qclass, int* have_dp, struct delegpt** retdp, + struct regional* region) { - struct delegpt* fwddp; - struct iter_hints_stub* stub; - int labs = dname_count_labels(nm); + struct delegpt* dp = NULL; + int nolock = 0; /* do not process a last resort (the parent side) if a stub * or forward is configured, because we do not want to go 'above' * the configured servers */ - if(!dname_is_root(nm) && (stub = (struct iter_hints_stub*) - name_tree_find(&env->hints->tree, nm, nmlen, labs, qclass)) && + if(!dname_is_root(nm) && + (dp = hints_find(env->hints, nm, qclass, nolock)) && /* has_parent side is turned off for stub_first, where we * are allowed to go to the parent */ - stub->dp->has_parent_side_NS) { - if(retdp) *retdp = stub->dp; + dp->has_parent_side_NS) { + if(retdp) *retdp = delegpt_copy(dp, region); + lock_rw_unlock(&env->hints->lock); + if(have_dp) *have_dp = 1; return 0; } - if((fwddp = forwards_find(env->fwds, nm, qclass)) && + if(dp) { + lock_rw_unlock(&env->hints->lock); + dp = NULL; + } + if((dp = forwards_find(env->fwds, nm, qclass, nolock)) && /* has_parent_side is turned off for forward_first, where * we are allowed to go to the parent */ - fwddp->has_parent_side_NS) { - if(retdp) *retdp = fwddp; + dp->has_parent_side_NS) { + if(retdp) *retdp = delegpt_copy(dp, region); + lock_rw_unlock(&env->fwds->lock); + if(have_dp) *have_dp = 1; return 0; } + /* lock_() calls are macros that could be nothing, surround in {} */ + if(dp) { lock_rw_unlock(&env->fwds->lock); } return 1; } @@ -877,10 +888,11 @@ prime_root(struct module_qstate* qstate, { struct delegpt* dp; struct module_qstate* subq; + int nolock = 0; verbose(VERB_DETAIL, "priming . %s NS", sldns_lookup_by_id(sldns_rr_classes, (int)qclass)? sldns_lookup_by_id(sldns_rr_classes, (int)qclass)->name:"??"); - dp = hints_lookup_root(qstate->env->hints, qclass); + dp = hints_find_root(qstate->env->hints, qclass, nolock); if(!dp) { verbose(VERB_ALGO, "Cannot prime due to lack of hints"); return 0; @@ -890,6 +902,7 @@ prime_root(struct module_qstate* qstate, if(!generate_sub_request((uint8_t*)"\000", 1, LDNS_RR_TYPE_NS, qclass, qstate, id, iq, QUERYTARGETS_STATE, PRIME_RESP_STATE, &subq, 0, 0)) { + lock_rw_unlock(&qstate->env->hints->lock); verbose(VERB_ALGO, "could not prime root"); return 0; } @@ -900,6 +913,7 @@ prime_root(struct module_qstate* qstate, * copy dp, it is now part of the root prime query. * dp was part of in the fixed hints structure. */ subiq->dp = delegpt_copy(dp, subq->region); + lock_rw_unlock(&qstate->env->hints->lock); if(!subiq->dp) { log_err("out of memory priming root, copydp"); fptr_ok(fptr_whitelist_modenv_kill_sub( @@ -911,6 +925,8 @@ prime_root(struct module_qstate* qstate, subiq->num_target_queries = 0; subiq->dnssec_expected = iter_indicates_dnssec( qstate->env, subiq->dp, NULL, subq->qinfo.qclass); + } else { + lock_rw_unlock(&qstate->env->hints->lock); } /* this module stops, our submodule starts, and does the query. */ @@ -941,18 +957,21 @@ prime_stub(struct module_qstate* qstate, struct iter_hints_stub* stub; struct delegpt* stub_dp; struct module_qstate* subq; + int nolock = 0; if(!qname) return 0; - stub = hints_lookup_stub(qstate->env->hints, qname, qclass, iq->dp); + stub = hints_lookup_stub(qstate->env->hints, qname, qclass, iq->dp, + nolock); /* The stub (if there is one) does not need priming. */ - if(!stub) - return 0; + if(!stub) return 0; stub_dp = stub->dp; /* if we have an auth_zone dp, and stub is equal, don't prime stub * yet, unless we want to fallback and avoid the auth_zone */ if(!iq->auth_zone_avoid && iq->dp && iq->dp->auth_dp && - query_dname_compare(iq->dp->name, stub_dp->name) == 0) + query_dname_compare(iq->dp->name, stub_dp->name) == 0) { + lock_rw_unlock(&qstate->env->hints->lock); return 0; + } /* is it a noprime stub (always use) */ if(stub->noprime) { @@ -961,13 +980,14 @@ prime_stub(struct module_qstate* qstate, /* copy the dp out of the fixed hints structure, so that * it can be changed when servicing this query */ iq->dp = delegpt_copy(stub_dp, qstate->region); + lock_rw_unlock(&qstate->env->hints->lock); if(!iq->dp) { log_err("out of memory priming stub"); errinf(qstate, "malloc failure, priming stub"); (void)error_response(qstate, id, LDNS_RCODE_SERVFAIL); return 1; /* return 1 to make module stop, with error */ } - log_nametypeclass(VERB_DETAIL, "use stub", stub_dp->name, + log_nametypeclass(VERB_DETAIL, "use stub", iq->dp->name, LDNS_RR_TYPE_NS, qclass); return r; } @@ -981,6 +1001,7 @@ prime_stub(struct module_qstate* qstate, if(!generate_sub_request(stub_dp->name, stub_dp->namelen, LDNS_RR_TYPE_NS, qclass, qstate, id, iq, QUERYTARGETS_STATE, PRIME_RESP_STATE, &subq, 0, 0)) { + lock_rw_unlock(&qstate->env->hints->lock); verbose(VERB_ALGO, "could not prime stub"); errinf(qstate, "could not generate lookup for stub prime"); (void)error_response(qstate, id, LDNS_RCODE_SERVFAIL); @@ -993,6 +1014,7 @@ prime_stub(struct module_qstate* qstate, /* Set the initial delegation point to the hint. */ /* make copy to avoid use of stub dp by different qs/threads */ subiq->dp = delegpt_copy(stub_dp, subq->region); + lock_rw_unlock(&qstate->env->hints->lock); if(!subiq->dp) { log_err("out of memory priming stub, copydp"); fptr_ok(fptr_whitelist_modenv_kill_sub( @@ -1009,6 +1031,8 @@ prime_stub(struct module_qstate* qstate, subiq->wait_priming_stub = 1; subiq->dnssec_expected = iter_indicates_dnssec( qstate->env, subiq->dp, NULL, subq->qinfo.qclass); + } else { + lock_rw_unlock(&qstate->env->hints->lock); } /* this module stops, our submodule starts, and does the query. */ @@ -1181,7 +1205,7 @@ generate_ns_check(struct module_qstate* if(iq->depth == ie->max_dependency_depth) return; if(!can_have_last_resort(qstate->env, iq->dp->name, iq->dp->namelen, - iq->qchase.qclass, NULL)) + iq->qchase.qclass, NULL, NULL, NULL)) return; /* is this query the same as the nscheck? */ if(qstate->qinfo.qtype == LDNS_RR_TYPE_NS && @@ -1294,6 +1318,7 @@ forward_request(struct module_qstate* qs struct delegpt* dp; uint8_t* delname = iq->qchase.qname; size_t delnamelen = iq->qchase.qname_len; + int nolock = 0; if(iq->refetch_glue && iq->dp) { delname = iq->dp->name; delnamelen = iq->dp->namelen; @@ -1302,12 +1327,13 @@ forward_request(struct module_qstate* qs if( (iq->qchase.qtype == LDNS_RR_TYPE_DS || iq->refetch_glue) && !dname_is_root(iq->qchase.qname)) dname_remove_label(&delname, &delnamelen); - dp = forwards_lookup(qstate->env->fwds, delname, iq->qchase.qclass); - if(!dp) - return 0; + dp = forwards_lookup(qstate->env->fwds, delname, iq->qchase.qclass, + nolock); + if(!dp) return 0; /* send recursion desired to forward addr */ iq->chase_flags |= BIT_RD; iq->dp = delegpt_copy(dp, qstate->region); + lock_rw_unlock(&qstate->env->fwds->lock); /* iq->dp checked by caller */ verbose(VERB_ALGO, "forwarding request"); return 1; @@ -1335,6 +1361,7 @@ static int processInitRequest(struct module_qstate* qstate, struct iter_qstate* iq, struct iter_env* ie, int id) { + uint8_t dpname_storage[LDNS_MAX_DOMAINLEN+1]; uint8_t* delname, *dpname=NULL; size_t delnamelen, dpnamelen=0; struct dns_msg* msg = NULL; @@ -1381,7 +1408,7 @@ processInitRequest(struct module_qstate* if (iq->refetch_glue && iq->dp && !can_have_last_resort(qstate->env, iq->dp->name, - iq->dp->namelen, iq->qchase.qclass, NULL)) { + iq->dp->namelen, iq->qchase.qclass, NULL, NULL, NULL)) { iq->refetch_glue = 0; } @@ -1389,8 +1416,61 @@ processInitRequest(struct module_qstate* /* This either results in a query restart (CNAME cache response), a * terminating response (ANSWER), or a cache miss (null). */ - - if (iter_stub_fwd_no_cache(qstate, &iq->qchase, &dpname, &dpnamelen)) { + + /* Check RPZ for override */ + if(qstate->env->auth_zones) { + /* apply rpz qname triggers, like after cname */ + struct dns_msg* forged_response = + rpz_callback_from_iterator_cname(qstate, iq); + if(forged_response) { + uint8_t* sname = 0; + size_t slen = 0; + int count = 0; + while(forged_response && reply_find_rrset_section_an( + forged_response->rep, iq->qchase.qname, + iq->qchase.qname_len, LDNS_RR_TYPE_CNAME, + iq->qchase.qclass) && + iq->qchase.qtype != LDNS_RR_TYPE_CNAME && + count++ < ie->max_query_restarts) { + /* another cname to follow */ + if(!handle_cname_response(qstate, iq, forged_response, + &sname, &slen)) { + errinf(qstate, "malloc failure, CNAME info"); + return error_response(qstate, id, LDNS_RCODE_SERVFAIL); + } + iq->qchase.qname = sname; + iq->qchase.qname_len = slen; + forged_response = + rpz_callback_from_iterator_cname(qstate, iq); + } + if(forged_response != NULL) { + qstate->ext_state[id] = module_finished; + qstate->return_rcode = LDNS_RCODE_NOERROR; + qstate->return_msg = forged_response; + iq->response = forged_response; + next_state(iq, FINISHED_STATE); + if(!iter_prepend(iq, qstate->return_msg, qstate->region)) { + log_err("rpz: after cached cname, prepend rrsets: out of memory"); + return error_response(qstate, id, LDNS_RCODE_SERVFAIL); + } + qstate->return_msg->qinfo = qstate->qinfo; + return 0; + } + /* Follow the CNAME response */ + iq->dp = NULL; + iq->refetch_glue = 0; + iq->query_restart_count++; + iq->sent_count = 0; + iq->dp_target_count = 0; + sock_list_insert(&qstate->reply_origin, NULL, 0, qstate->region); + if(qstate->env->cfg->qname_minimisation) + iq->minimisation_state = INIT_MINIMISE_STATE; + return next_state(iq, INIT_REQUEST_STATE); + } + } + + if (iter_stub_fwd_no_cache(qstate, &iq->qchase, &dpname, &dpnamelen, + dpname_storage, sizeof(dpname_storage))) { /* Asked to not query cache. */ verbose(VERB_ALGO, "no-cache set, going to the network"); qstate->no_cache_lookup = 1; @@ -1449,39 +1529,6 @@ processInitRequest(struct module_qstate* } iq->qchase.qname = sname; iq->qchase.qname_len = slen; - if(qstate->env->auth_zones) { - /* apply rpz qname triggers after cname */ - struct dns_msg* forged_response = - rpz_callback_from_iterator_cname(qstate, iq); - while(forged_response && reply_find_rrset_section_an( - forged_response->rep, iq->qchase.qname, - iq->qchase.qname_len, LDNS_RR_TYPE_CNAME, - iq->qchase.qclass)) { - /* another cname to follow */ - if(!handle_cname_response(qstate, iq, forged_response, - &sname, &slen)) { - errinf(qstate, "malloc failure, CNAME info"); - return error_response(qstate, id, LDNS_RCODE_SERVFAIL); - } - iq->qchase.qname = sname; - iq->qchase.qname_len = slen; - forged_response = - rpz_callback_from_iterator_cname(qstate, iq); - } - if(forged_response != NULL) { - qstate->ext_state[id] = module_finished; - qstate->return_rcode = LDNS_RCODE_NOERROR; - qstate->return_msg = forged_response; - iq->response = forged_response; - next_state(iq, FINISHED_STATE); - if(!iter_prepend(iq, qstate->return_msg, qstate->region)) { - log_err("rpz: after cached cname, prepend rrsets: out of memory"); - return error_response(qstate, id, LDNS_RCODE_SERVFAIL); - } - qstate->return_msg->qinfo = qstate->qinfo; - return 0; - } - } /* This *is* a query restart, even if it is a cheap * one. */ iq->dp = NULL; @@ -1494,7 +1541,6 @@ processInitRequest(struct module_qstate* iq->minimisation_state = INIT_MINIMISE_STATE; return next_state(iq, INIT_REQUEST_STATE); } - /* if from cache, NULL, else insert 'cache IP' len=0 */ if(qstate->reply_origin) sock_list_insert(&qstate->reply_origin, NULL, 0, qstate->region); @@ -1555,7 +1601,7 @@ processInitRequest(struct module_qstate* } if(iq->qchase.qtype == LDNS_RR_TYPE_DS || iq->refetch_glue || (iq->qchase.qtype == LDNS_RR_TYPE_NS && qstate->prefetch_leeway - && can_have_last_resort(qstate->env, delname, delnamelen, iq->qchase.qclass, NULL))) { + && can_have_last_resort(qstate->env, delname, delnamelen, iq->qchase.qclass, NULL, NULL, NULL))) { /* remove first label from delname, root goes to hints, * but only to fetch glue, not for qtype=DS. */ /* also when prefetching an NS record, fetch it again from @@ -1584,6 +1630,7 @@ processInitRequest(struct module_qstate* * root priming situation. */ if(iq->dp == NULL) { int r; + int nolock = 0; /* if under auth zone, no prime needed */ if(!auth_zone_delegpt(qstate, iq, delname, delnamelen)) return error_response(qstate, id, @@ -1597,12 +1644,13 @@ processInitRequest(struct module_qstate* break; /* got noprime-stub-zone, continue */ else if(r) return 0; /* stub prime request made */ - if(forwards_lookup_root(qstate->env->fwds, - iq->qchase.qclass)) { + if(forwards_lookup_root(qstate->env->fwds, + iq->qchase.qclass, nolock)) { + lock_rw_unlock(&qstate->env->fwds->lock); /* forward zone root, no root prime needed */ /* fill in some dp - safety belt */ - iq->dp = hints_lookup_root(qstate->env->hints, - iq->qchase.qclass); + iq->dp = hints_find_root(qstate->env->hints, + iq->qchase.qclass, nolock); if(!iq->dp) { log_err("internal error: no hints dp"); errinf(qstate, "no hints for this class"); @@ -1610,6 +1658,7 @@ processInitRequest(struct module_qstate* LDNS_RCODE_SERVFAIL); } iq->dp = delegpt_copy(iq->dp, qstate->region); + lock_rw_unlock(&qstate->env->hints->lock); if(!iq->dp) { log_err("out of memory in safety belt"); errinf(qstate, "malloc failure, in safety belt"); @@ -1649,15 +1698,13 @@ processInitRequest(struct module_qstate* if(iter_dp_is_useless(&qstate->qinfo, qstate->query_flags, iq->dp, ie->supports_ipv4, ie->supports_ipv6, ie->use_nat64)) { - struct delegpt* retdp = NULL; - if(!can_have_last_resort(qstate->env, iq->dp->name, iq->dp->namelen, iq->qchase.qclass, &retdp)) { - if(retdp) { + 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) { verbose(VERB_QUERY, "cache has stub " "or fwd but no addresses, " "fallback to config"); - iq->dp = delegpt_copy(retdp, - qstate->region); - if(!iq->dp) { + if(have_dp && !iq->dp) { log_err("out of memory in " "stub/fwd fallback"); errinf(qstate, "malloc failure, for fallback to config"); @@ -1677,10 +1724,11 @@ processInitRequest(struct module_qstate* } if(dname_is_root(iq->dp->name)) { /* use safety belt */ + int nolock = 0; verbose(VERB_QUERY, "Cache has root NS but " "no addresses. Fallback to the safety belt."); - iq->dp = hints_lookup_root(qstate->env->hints, - iq->qchase.qclass); + iq->dp = hints_find_root(qstate->env->hints, + iq->qchase.qclass, nolock); /* note deleg_msg is from previous lookup, * but RD is on, so it is not used */ if(!iq->dp) { @@ -1689,6 +1737,7 @@ processInitRequest(struct module_qstate* LDNS_RCODE_REFUSED); } iq->dp = delegpt_copy(iq->dp, qstate->region); + lock_rw_unlock(&qstate->env->hints->lock); if(!iq->dp) { log_err("out of memory in safety belt"); errinf(qstate, "malloc failure, in safety belt, for root"); @@ -1744,6 +1793,7 @@ processInitRequest2(struct module_qstate delnamelen = iq->qchase.qname_len; if(iq->refetch_glue) { struct iter_hints_stub* stub; + int nolock = 0; if(!iq->dp) { log_err("internal or malloc fail: no dp for refetch"); errinf(qstate, "malloc failure, no delegation info"); @@ -1753,12 +1803,14 @@ processInitRequest2(struct module_qstate * this is above stub without stub-first. */ stub = hints_lookup_stub( qstate->env->hints, iq->qchase.qname, iq->qchase.qclass, - iq->dp); + iq->dp, nolock); if(!stub || !stub->dp->has_parent_side_NS || dname_subdomain_c(iq->dp->name, stub->dp->name)) { delname = iq->dp->name; delnamelen = iq->dp->namelen; } + /* lock_() calls are macros that could be nothing, surround in {} */ + if(stub) { lock_rw_unlock(&qstate->env->hints->lock); } } if(iq->qchase.qtype == LDNS_RR_TYPE_DS || iq->refetch_glue) { if(!dname_is_root(delname)) @@ -2062,7 +2114,7 @@ processLastResort(struct module_qstate* log_assert(iq->dp); if(!can_have_last_resort(qstate->env, iq->dp->name, iq->dp->namelen, - iq->qchase.qclass, NULL)) { + iq->qchase.qclass, NULL, NULL, NULL)) { /* fail -- no more targets, no more hope of targets, no hope * of a response. */ errinf(qstate, "all the configured stub or forward servers failed,"); @@ -2072,21 +2124,24 @@ processLastResort(struct module_qstate* return error_response_cache(qstate, id, LDNS_RCODE_SERVFAIL); } if(!iq->dp->has_parent_side_NS && dname_is_root(iq->dp->name)) { - struct delegpt* p = hints_lookup_root(qstate->env->hints, - iq->qchase.qclass); - if(p) { + struct delegpt* dp; + int nolock = 0; + dp = hints_find_root(qstate->env->hints, + iq->qchase.qclass, nolock); + if(dp) { struct delegpt_addr* a; iq->chase_flags &= ~BIT_RD; /* go to authorities */ - for(ns = p->nslist; ns; ns=ns->next) { + for(ns = dp->nslist; ns; ns=ns->next) { (void)delegpt_add_ns(iq->dp, qstate->region, ns->name, ns->lame, ns->tls_auth_name, ns->port); } - for(a = p->target_list; a; a=a->next_target) { + for(a = dp->target_list; a; a=a->next_target) { (void)delegpt_add_addr(iq->dp, qstate->region, &a->addr, a->addrlen, a->bogus, a->lame, a->tls_auth_name, -1, NULL); } + lock_rw_unlock(&qstate->env->hints->lock); } iq->dp->has_parent_side_NS = 1; } else if(!iq->dp->has_parent_side_NS) { @@ -2164,7 +2219,7 @@ processLastResort(struct module_qstate* if( ((ie->supports_ipv6 && !ns->done_pside6) || ((ie->supports_ipv4 || ie->use_nat64) && !ns->done_pside4)) && !can_have_last_resort(qstate->env, ns->name, ns->namelen, - iq->qchase.qclass, NULL)) { + 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); @@ -2746,8 +2801,51 @@ processQueryTargets(struct module_qstate delegpt_add_unused_targets(iq->dp); if(qstate->env->auth_zones) { - /* apply rpz triggers at query time */ + uint8_t* sname = NULL; + size_t snamelen = 0; + /* apply rpz triggers at query time; nameserver IP and dname */ + struct dns_msg* forged_response_after_cname; struct dns_msg* forged_response = rpz_callback_from_iterator_module(qstate, iq); + int count = 0; + while(forged_response && reply_find_rrset_section_an( + forged_response->rep, iq->qchase.qname, + iq->qchase.qname_len, LDNS_RR_TYPE_CNAME, + iq->qchase.qclass) && + iq->qchase.qtype != LDNS_RR_TYPE_CNAME && + count++ < ie->max_query_restarts) { + /* another cname to follow */ + if(!handle_cname_response(qstate, iq, forged_response, + &sname, &snamelen)) { + errinf(qstate, "malloc failure, CNAME info"); + return error_response(qstate, id, LDNS_RCODE_SERVFAIL); + } + iq->qchase.qname = sname; + iq->qchase.qname_len = snamelen; + forged_response_after_cname = + rpz_callback_from_iterator_cname(qstate, iq); + if(forged_response_after_cname) { + forged_response = forged_response_after_cname; + } else { + /* Follow the CNAME with a query restart */ + iq->deleg_msg = NULL; + iq->dp = NULL; + iq->dsns_point = NULL; + iq->auth_zone_response = 0; + iq->refetch_glue = 0; + iq->query_restart_count++; + iq->sent_count = 0; + iq->dp_target_count = 0; + if(qstate->env->cfg->qname_minimisation) + iq->minimisation_state = INIT_MINIMISE_STATE; + outbound_list_clear(&iq->outlist); + iq->num_current_queries = 0; + fptr_ok(fptr_whitelist_modenv_detach_subs( + qstate->env->detach_subs)); + (*qstate->env->detach_subs)(qstate); + iq->num_target_queries = 0; + return next_state(iq, INIT_REQUEST_STATE); + } + } if(forged_response != NULL) { qstate->ext_state[id] = module_finished; qstate->return_rcode = LDNS_RCODE_NOERROR; @@ -3082,7 +3180,8 @@ processQueryResponse(struct module_qstat /* DNAME to a subdomain loop; do not recurse */ type = RESPONSE_TYPE_ANSWER; } - } else if(type == RESPONSE_TYPE_CNAME && + } + if(type == RESPONSE_TYPE_CNAME && iq->qchase.qtype == LDNS_RR_TYPE_CNAME && iq->minimisation_state == MINIMISE_STATE && query_dname_compare(iq->qchase.qname, iq->qinfo_out.qname) == 0) { @@ -3193,6 +3292,7 @@ processQueryResponse(struct module_qstat } return final_state(iq); } else if(type == RESPONSE_TYPE_REFERRAL) { + struct delegpt* old_dp = NULL; /* REFERRAL type responses get a reset of the * delegation point, and back to the QUERYTARGETS_STATE. */ verbose(VERB_DETAIL, "query response was REFERRAL"); @@ -3244,6 +3344,8 @@ processQueryResponse(struct module_qstat /* Reset the event state, setting the current delegation * point to the referral. */ iq->deleg_msg = iq->response; + /* Keep current delegation point for label comparison */ + old_dp = iq->dp; iq->dp = delegpt_from_message(iq->response, qstate->region); if (qstate->env->cfg->qname_minimisation) iq->minimisation_state = INIT_MINIMISE_STATE; @@ -3251,6 +3353,20 @@ processQueryResponse(struct module_qstat errinf(qstate, "malloc failure, for delegation point"); return error_response(qstate, id, LDNS_RCODE_SERVFAIL); } + if(old_dp->namelabs + 1 < iq->dp->namelabs) { + /* We got a grandchild delegation (more than one label + * difference) than expected. Check for in-between + * delegations in the cache and remove them. + * They could prove problematic when they expire + * and rrset_expired_above() encounters them during + * delegation cache lookups. */ + uint8_t* qname = iq->dp->name; + size_t qnamelen = iq->dp->namelen; + rrset_cache_remove_above(qstate->env->rrset_cache, + &qname, &qnamelen, LDNS_RR_TYPE_NS, + iq->qchase.qclass, *qstate->env->now, + old_dp->name, old_dp->namelen); + } if(!cache_fill_missing(qstate->env, iq->qchase.qclass, qstate->region, iq->dp)) { errinf(qstate, "malloc failure, copy extra info into delegation point"); @@ -3341,10 +3457,13 @@ processQueryResponse(struct module_qstat /* apply rpz qname triggers after cname */ struct dns_msg* forged_response = rpz_callback_from_iterator_cname(qstate, iq); + int count = 0; while(forged_response && reply_find_rrset_section_an( forged_response->rep, iq->qchase.qname, iq->qchase.qname_len, LDNS_RR_TYPE_CNAME, - iq->qchase.qclass)) { + iq->qchase.qclass) && + iq->qchase.qtype != LDNS_RR_TYPE_CNAME && + count++ < ie->max_query_restarts) { /* another cname to follow */ if(!handle_cname_response(qstate, iq, forged_response, &sname, &snamelen)) { @@ -3926,17 +4045,9 @@ processFinished(struct module_qstate* qs !qstate->env->cfg->val_log_squelch) { char* err_str = errinf_to_str_misc(qstate); if(err_str) { - size_t err_str_len = strlen(err_str); verbose(VERB_ALGO, "iterator EDE: %s", err_str); - /* allocate space and store the error - * string */ - iq->response->rep->reason_bogus_str = regional_alloc( - qstate->region, - sizeof(char) * (err_str_len+1)); - memcpy(iq->response->rep->reason_bogus_str, - err_str, err_str_len+1); + iq->response->rep->reason_bogus_str = err_str; } - free(err_str); } /* we have finished processing this query */ Index: libunbound/context.c =================================================================== RCS file: /cvs/src/usr.sbin/unbound/libunbound/context.c,v diff -u -p -r1.13 context.c --- libunbound/context.c 5 Sep 2023 11:12:10 -0000 1.13 +++ libunbound/context.c 22 May 2024 10:33:17 -0000 @@ -53,6 +53,8 @@ #include "util/storage/slabhash.h" #include "util/edns.h" #include "sldns/sbuffer.h" +#include "iterator/iter_fwd.h" +#include "iterator/iter_hints.h" int context_finalize(struct ub_ctx* ctx) @@ -84,6 +86,12 @@ context_finalize(struct ub_ctx* ctx) return UB_INITFAIL; if(!auth_zones_apply_cfg(ctx->env->auth_zones, cfg, 1, &is_rpz, ctx->env, &ctx->mods)) + return UB_INITFAIL; + if(!(ctx->env->fwds = forwards_create()) || + !forwards_apply_cfg(ctx->env->fwds, cfg)) + return UB_INITFAIL; + if(!(ctx->env->hints = hints_create()) || + !hints_apply_cfg(ctx->env->hints, cfg)) return UB_INITFAIL; if(!edns_strings_apply_cfg(ctx->env->edns_strings, cfg)) return UB_INITFAIL; Index: libunbound/libunbound.c =================================================================== RCS file: /cvs/src/usr.sbin/unbound/libunbound/libunbound.c,v diff -u -p -r1.20 libunbound.c --- libunbound/libunbound.c 5 Sep 2023 11:12:10 -0000 1.20 +++ libunbound/libunbound.c 22 May 2024 10:33:17 -0000 @@ -66,6 +66,8 @@ #include "services/authzone.h" #include "services/listen_dnsport.h" #include "sldns/sbuffer.h" +#include "iterator/iter_fwd.h" +#include "iterator/iter_hints.h" #ifdef HAVE_PTHREAD #include #endif @@ -171,6 +173,7 @@ static struct ub_ctx* ub_ctx_create_nopi ctx->env->worker = NULL; ctx->env->need_to_validate = 0; modstack_init(&ctx->mods); + ctx->env->modstack = &ctx->mods; rbtree_init(&ctx->queries, &context_query_cmp); return ctx; } @@ -379,6 +382,8 @@ ub_ctx_delete(struct ub_ctx* ctx) config_delete(ctx->env->cfg); edns_known_options_delete(ctx->env); edns_strings_delete(ctx->env->edns_strings); + forwards_delete(ctx->env->fwds); + hints_delete(ctx->env->hints); auth_zones_delete(ctx->env->auth_zones); free(ctx->env); } Index: libunbound/libworker.c =================================================================== RCS file: /cvs/src/usr.sbin/unbound/libunbound/libworker.c,v diff -u -p -r1.31 libworker.c --- libunbound/libworker.c 12 Apr 2024 15:45:24 -0000 1.31 +++ libunbound/libworker.c 22 May 2024 10:33:17 -0000 @@ -70,8 +70,6 @@ #include "util/data/msgreply.h" #include "util/data/msgencode.h" #include "util/tube.h" -#include "iterator/iter_fwd.h" -#include "iterator/iter_hints.h" #include "sldns/sbuffer.h" #include "sldns/str2wire.h" #ifdef USE_DNSTAP @@ -100,8 +98,6 @@ libworker_delete_env(struct libworker* w !w->is_bg || w->is_bg_thread); sldns_buffer_free(w->env->scratch_buffer); regional_destroy(w->env->scratch); - forwards_delete(w->env->fwds); - hints_delete(w->env->hints); ub_randfree(w->env->rnd); free(w->env); } @@ -159,30 +155,19 @@ libworker_setup(struct ub_ctx* ctx, int } w->env->scratch = regional_create_custom(cfg->msg_buffer_size); w->env->scratch_buffer = sldns_buffer_new(cfg->msg_buffer_size); - w->env->fwds = forwards_create(); - if(w->env->fwds && !forwards_apply_cfg(w->env->fwds, cfg)) { - forwards_delete(w->env->fwds); - w->env->fwds = NULL; - } - w->env->hints = hints_create(); - if(w->env->hints && !hints_apply_cfg(w->env->hints, cfg)) { - hints_delete(w->env->hints); - w->env->hints = NULL; - } #ifdef HAVE_SSL w->sslctx = connect_sslctx_create(NULL, NULL, cfg->tls_cert_bundle, cfg->tls_win_cert); if(!w->sslctx) { /* to make the setup fail after unlock */ - hints_delete(w->env->hints); - w->env->hints = NULL; + sldns_buffer_free(w->env->scratch_buffer); + w->env->scratch_buffer = NULL; } #endif if(!w->is_bg || w->is_bg_thread) { lock_basic_unlock(&ctx->cfglock); } - if(!w->env->scratch || !w->env->scratch_buffer || !w->env->fwds || - !w->env->hints) { + if(!w->env->scratch || !w->env->scratch_buffer) { libworker_delete(w); return NULL; } Index: services/authzone.c =================================================================== RCS file: /cvs/src/usr.sbin/unbound/services/authzone.c,v diff -u -p -r1.26 authzone.c --- services/authzone.c 13 Apr 2024 12:24:57 -0000 1.26 +++ services/authzone.c 22 May 2024 10:33:17 -0000 @@ -2152,6 +2152,16 @@ auth_zones_cfg(struct auth_zones* az, st if(az->rpz_first) az->rpz_first->rpz_az_prev = z; az->rpz_first = z; + } else if(c->isrpz && z->rpz) { + if(!rpz_config(z->rpz, c)) { + log_err("Could not change rpz config"); + if(x) { + lock_basic_unlock(&x->lock); + } + lock_rw_unlock(&z->lock); + lock_rw_unlock(&az->rpz_lock); + return 0; + } } if(c->isrpz) { lock_rw_unlock(&az->rpz_lock); Index: services/listen_dnsport.c =================================================================== RCS file: /cvs/src/usr.sbin/unbound/services/listen_dnsport.c,v diff -u -p -r1.35 listen_dnsport.c --- services/listen_dnsport.c 12 Apr 2024 15:45:24 -0000 1.35 +++ services/listen_dnsport.c 22 May 2024 10:33:17 -0000 @@ -140,9 +140,11 @@ void verbose_print_unbound_socket(struct unbound_socket* ub_sock) { if(verbosity >= VERB_ALGO) { + char buf[256]; log_info("listing of unbound_socket structure:"); - verbose_print_addr(ub_sock->addr); - log_info("s is: %d, fam is: %s, acl: %s", ub_sock->s, + addr_to_str((void*)ub_sock->addr, ub_sock->addrlen, buf, + sizeof(buf)); + log_info("%s s is: %d, fam is: %s, acl: %s", buf, ub_sock->s, ub_sock->fam == AF_INET?"AF_INET":"AF_INET6", ub_sock->acl?"yes":"no"); } @@ -610,7 +612,9 @@ create_udp_sock(int family, int socktype # elif defined(IP_DONTFRAG) && !defined(__APPLE__) /* the IP_DONTFRAG option if defined in the 11.0 OSX headers, * but does not work on that version, so we exclude it */ - int off = 0; + /* a nonzero value disables fragmentation, according to + * docs.oracle.com for ip(4). */ + int off = 1; if (setsockopt(s, IPPROTO_IP, IP_DONTFRAG, &off, (socklen_t)sizeof(off)) < 0) { log_err("setsockopt(..., IP_DONTFRAG, ...) failed: %s", @@ -1047,7 +1051,22 @@ make_sock(int stype, const char* ifname, } } - ub_sock->addr = res; + if(!res->ai_addr) { + log_err("getaddrinfo returned no address"); + freeaddrinfo(res); + sock_close(s); + return -1; + } + ub_sock->addr = memdup(res->ai_addr, res->ai_addrlen); + ub_sock->addrlen = res->ai_addrlen; + if(!ub_sock->addr) { + log_err("out of memory: allocate listening address"); + freeaddrinfo(res); + sock_close(s); + return -1; + } + freeaddrinfo(res); + ub_sock->s = s; ub_sock->fam = hints->ai_family; ub_sock->acl = NULL; @@ -1277,8 +1296,7 @@ ports_create_if(const char* ifname, int if((s = make_sock_port(SOCK_DGRAM, ifname, port, hints, 1, &noip6, rcv, snd, reuseport, transparent, tcp_mss, nodelay, freebind, use_systemd, dscp, ub_sock)) == -1) { - if(ub_sock->addr) - freeaddrinfo(ub_sock->addr); + free(ub_sock->addr); free(ub_sock); if(noip6) { log_warn("IPv6 protocol not available"); @@ -1289,8 +1307,7 @@ ports_create_if(const char* ifname, int /* getting source addr packet info is highly non-portable */ if(!set_recvpktinfo(s, hints->ai_family)) { sock_close(s); - if(ub_sock->addr) - freeaddrinfo(ub_sock->addr); + free(ub_sock->addr); free(ub_sock); return 0; } @@ -1301,8 +1318,7 @@ ports_create_if(const char* ifname, int ?listen_type_udpancil_dnscrypt:listen_type_udpancil, is_pp2, ub_sock)) { sock_close(s); - if(ub_sock->addr) - freeaddrinfo(ub_sock->addr); + free(ub_sock->addr); free(ub_sock); return 0; } @@ -1314,8 +1330,7 @@ ports_create_if(const char* ifname, int if((s = make_sock_port(SOCK_DGRAM, ifname, port, hints, 1, &noip6, rcv, snd, reuseport, transparent, tcp_mss, nodelay, freebind, use_systemd, dscp, ub_sock)) == -1) { - if(ub_sock->addr) - freeaddrinfo(ub_sock->addr); + free(ub_sock->addr); free(ub_sock); if(noip6) { log_warn("IPv6 protocol not available"); @@ -1332,8 +1347,7 @@ ports_create_if(const char* ifname, int listen_type_udpancil:listen_type_udp), is_pp2, ub_sock)) { sock_close(s); - if(ub_sock->addr) - freeaddrinfo(ub_sock->addr); + free(ub_sock->addr); free(ub_sock); return 0; } @@ -1356,8 +1370,7 @@ ports_create_if(const char* ifname, int if((s = make_sock_port(SOCK_STREAM, ifname, port, hints, 1, &noip6, 0, 0, reuseport, transparent, tcp_mss, nodelay, freebind, use_systemd, dscp, ub_sock)) == -1) { - if(ub_sock->addr) - freeaddrinfo(ub_sock->addr); + free(ub_sock->addr); free(ub_sock); if(noip6) { /*log_warn("IPv6 protocol not available");*/ @@ -1369,8 +1382,7 @@ ports_create_if(const char* ifname, int verbose(VERB_ALGO, "setup TCP for SSL service"); if(!port_insert(list, s, port_type, is_pp2, ub_sock)) { sock_close(s); - if(ub_sock->addr) - freeaddrinfo(ub_sock->addr); + free(ub_sock->addr); free(ub_sock); return 0; } @@ -1952,8 +1964,7 @@ void listening_ports_free(struct listen_ } /* rc_ports don't have ub_socket */ if(list->socket) { - if(list->socket->addr) - freeaddrinfo(list->socket->addr); + free(list->socket->addr); free(list->socket); } free(list); Index: services/listen_dnsport.h =================================================================== RCS file: /cvs/src/usr.sbin/unbound/services/listen_dnsport.h,v diff -u -p -r1.19 listen_dnsport.h --- services/listen_dnsport.h 20 Oct 2022 08:26:14 -0000 1.19 +++ services/listen_dnsport.h 22 May 2024 10:33:17 -0000 @@ -107,11 +107,13 @@ enum listen_type { * socket properties (just like NSD nsd_socket structure definition) */ struct unbound_socket { - /** socket-address structure */ - struct addrinfo* addr; + /** the address of the socket */ + struct sockaddr* addr; + /** length of the address */ + socklen_t addrlen; /** socket descriptor returned by socket() syscall */ int s; - /** address family (AF_INET/IF_INET6) */ + /** address family (AF_INET/AF_INET6) */ int fam; /** ACL on the socket (listening interface) */ struct acl_addr* acl; Index: services/localzone.c =================================================================== RCS file: /cvs/src/usr.sbin/unbound/services/localzone.c,v diff -u -p -r1.21 localzone.c --- services/localzone.c 13 Apr 2024 12:24:57 -0000 1.21 +++ services/localzone.c 22 May 2024 10:33:17 -0000 @@ -330,14 +330,16 @@ get_rr_nameclass(const char* str, uint8_ static struct local_rrset* local_data_find_type(struct local_data* data, uint16_t type, int alias_ok) { - struct local_rrset* p; + struct local_rrset* p, *cname = NULL; type = htons(type); for(p = data->rrsets; p; p = p->next) { if(p->rrset->rk.type == type) return p; if(alias_ok && p->rrset->rk.type == htons(LDNS_RR_TYPE_CNAME)) - return p; + cname = p; } + if(alias_ok) + return cname; return NULL; } Index: services/mesh.c =================================================================== RCS file: /cvs/src/usr.sbin/unbound/services/mesh.c,v diff -u -p -r1.28 mesh.c --- services/mesh.c 13 Apr 2024 12:24:57 -0000 1.28 +++ services/mesh.c 22 May 2024 10:33:17 -0000 @@ -47,6 +47,7 @@ #include "services/outbound_list.h" #include "services/cache/dns.h" #include "services/cache/rrset.h" +#include "services/cache/infra.h" #include "util/log.h" #include "util/net_help.h" #include "util/module.h" @@ -385,7 +386,7 @@ mesh_serve_expired_init(struct mesh_stat &mesh_serve_expired_lookup; /* In case this timer already popped, start it again */ - if(!mstate->s.serve_expired_data->timer) { + if(!mstate->s.serve_expired_data->timer && timeout != -1) { mstate->s.serve_expired_data->timer = comm_timer_create( mstate->s.env->worker_base, mesh_serve_expired_callback, mstate); if(!mstate->s.serve_expired_data->timer) @@ -415,6 +416,14 @@ void mesh_new_client(struct mesh_area* m if(rep->c->tcp_req_info) { r_buffer = rep->c->tcp_req_info->spool_buffer; } + if(!infra_wait_limit_allowed(mesh->env->infra_cache, rep, + edns->cookie_valid, mesh->env->cfg)) { + verbose(VERB_ALGO, "Too many queries waiting from the IP. " + "dropping incoming query."); + comm_point_drop_reply(rep); + mesh->stats_dropped++; + return; + } if(!unique) s = mesh_area_find(mesh, cinfo, qinfo, qflags&(BIT_RD|BIT_CD), 0, 0); /* does this create a new reply state? */ @@ -511,6 +520,19 @@ void mesh_new_client(struct mesh_area* m log_err("mesh_new_client: out of memory initializing serve expired"); goto servfail_mem; } +#ifdef USE_CACHEDB + if(!timeout && mesh->env->cfg->serve_expired && + !mesh->env->cfg->serve_expired_client_timeout && + (mesh->env->cachedb_enabled && + mesh->env->cfg->cachedb_check_when_serve_expired)) { + if(!mesh_serve_expired_init(s, -1)) { + log_err("mesh_new_client: out of memory initializing serve expired"); + goto servfail_mem; + } + } +#endif + infra_wait_limit_inc(mesh->env->infra_cache, rep, *mesh->env->now, + mesh->env->cfg); /* update statistics */ if(was_detached) { log_assert(mesh->num_detached_states > 0); @@ -616,6 +638,18 @@ mesh_new_callback(struct mesh_area* mesh mesh_state_delete(&s->s); return 0; } +#ifdef USE_CACHEDB + if(!timeout && mesh->env->cfg->serve_expired && + !mesh->env->cfg->serve_expired_client_timeout && + (mesh->env->cachedb_enabled && + mesh->env->cfg->cachedb_check_when_serve_expired)) { + if(!mesh_serve_expired_init(s, -1)) { + if(added) + mesh_state_delete(&s->s); + return 0; + } + } +#endif /* update statistics */ if(was_detached) { log_assert(mesh->num_detached_states > 0); @@ -930,6 +964,8 @@ mesh_state_cleanup(struct mesh_state* ms * takes no time and also it does not do the mesh accounting */ mstate->reply_list = NULL; for(; rep; rep=rep->next) { + infra_wait_limit_dec(mesh->env->infra_cache, + &rep->query_reply, mesh->env->cfg); comm_point_drop_reply(&rep->query_reply); log_assert(mesh->num_reply_addrs > 0); mesh->num_reply_addrs--; @@ -1179,7 +1215,7 @@ mesh_do_callback(struct mesh_state* m, i rcode = LDNS_RCODE_SERVFAIL; if(!rcode && rep && (rep->security == sec_status_bogus || rep->security == sec_status_secure_sentinel_fail)) { - if(!(reason = errinf_to_str_bogus(&m->s))) + if(!(reason = errinf_to_str_bogus(&m->s, NULL))) rcode = LDNS_RCODE_SERVFAIL; } /* send the reply */ @@ -1413,6 +1449,8 @@ mesh_send_reply(struct mesh_state* m, in comm_point_send_reply(&r->query_reply); m->reply_list = rlist; } + infra_wait_limit_dec(m->s.env->infra_cache, &r->query_reply, + m->s.env->cfg); /* account */ log_assert(m->s.env->mesh->num_reply_addrs > 0); m->s.env->mesh->num_reply_addrs--; @@ -1436,7 +1474,7 @@ mesh_send_reply(struct mesh_state* m, in log_reply_info(NO_VERBOSE, &m->s.qinfo, &r->query_reply.client_addr, r->query_reply.client_addrlen, duration, 0, r_buffer, - (m->s.env->cfg->log_destaddr?(void*)r->query_reply.c->socket->addr->ai_addr:NULL), + (m->s.env->cfg->log_destaddr?(void*)r->query_reply.c->socket->addr:NULL), r->query_reply.c->type); } } @@ -1464,12 +1502,32 @@ void mesh_query_done(struct mesh_state* && mstate->s.env->cfg->log_servfail && !mstate->s.env->cfg->val_log_squelch) { char* err = errinf_to_str_servfail(&mstate->s); - if(err) - log_err("%s", err); - free(err); + if(err) { log_err("%s", err); } } } for(r = mstate->reply_list; r; r = r->next) { + struct timeval old; + timeval_subtract(&old, mstate->s.env->now_tv, &r->start_time); + if(mstate->s.env->cfg->discard_timeout != 0 && + ((int)old.tv_sec)*1000+((int)old.tv_usec)/1000 > + mstate->s.env->cfg->discard_timeout) { + /* Drop the reply, it is too old */ + /* briefly set the reply_list to NULL, so that the + * tcp req info cleanup routine that calls the mesh + * to deregister the meshstate for it is not done + * because the list is NULL and also accounting is not + * done there, but instead we do that here. */ + struct mesh_reply* reply_list = mstate->reply_list; + verbose(VERB_ALGO, "drop reply, it is older than discard-timeout"); + infra_wait_limit_dec(mstate->s.env->infra_cache, + &r->query_reply, mstate->s.env->cfg); + mstate->reply_list = NULL; + comm_point_drop_reply(&r->query_reply); + mstate->reply_list = reply_list; + mstate->s.env->mesh->stats_dropped++; + continue; + } + i++; tv = r->start_time; @@ -1493,6 +1551,8 @@ void mesh_query_done(struct mesh_state* * because the list is NULL and also accounting is not * done there, but instead we do that here. */ struct mesh_reply* reply_list = mstate->reply_list; + infra_wait_limit_dec(mstate->s.env->infra_cache, + &r->query_reply, mstate->s.env->cfg); mstate->reply_list = NULL; comm_point_drop_reply(&r->query_reply); mstate->reply_list = reply_list; @@ -2025,6 +2085,8 @@ void mesh_state_remove_reply(struct mesh /* delete it, but allocated in m region */ log_assert(mesh->num_reply_addrs > 0); mesh->num_reply_addrs--; + infra_wait_limit_dec(mesh->env->infra_cache, + &n->query_reply, mesh->env->cfg); /* prev = prev; */ n = n->next; @@ -2165,6 +2227,28 @@ mesh_serve_expired_callback(void* arg) log_dns_msg("Serve expired lookup", &qstate->qinfo, msg->rep); for(r = mstate->reply_list; r; r = r->next) { + struct timeval old; + timeval_subtract(&old, mstate->s.env->now_tv, &r->start_time); + if(mstate->s.env->cfg->discard_timeout != 0 && + ((int)old.tv_sec)*1000+((int)old.tv_usec)/1000 > + mstate->s.env->cfg->discard_timeout) { + /* Drop the reply, it is too old */ + /* briefly set the reply_list to NULL, so that the + * tcp req info cleanup routine that calls the mesh + * to deregister the meshstate for it is not done + * because the list is NULL and also accounting is not + * done there, but instead we do that here. */ + struct mesh_reply* reply_list = mstate->reply_list; + verbose(VERB_ALGO, "drop reply, it is older than discard-timeout"); + infra_wait_limit_dec(mstate->s.env->infra_cache, + &r->query_reply, mstate->s.env->cfg); + mstate->reply_list = NULL; + comm_point_drop_reply(&r->query_reply); + mstate->reply_list = reply_list; + mstate->s.env->mesh->stats_dropped++; + continue; + } + i++; tv = r->start_time; @@ -2192,6 +2276,8 @@ mesh_serve_expired_callback(void* arg) r, r_buffer, prev, prev_buffer); if(r->query_reply.c->tcp_req_info) tcp_req_info_remove_mesh_state(r->query_reply.c->tcp_req_info, mstate); + infra_wait_limit_dec(mstate->s.env->infra_cache, + &r->query_reply, mstate->s.env->cfg); prev = r; prev_buffer = r_buffer; } @@ -2236,6 +2322,14 @@ mesh_serve_expired_callback(void* arg) qstate->env->mesh->num_detached_states++; mesh_do_callback(mstate, LDNS_RCODE_NOERROR, msg->rep, c, &tv); } +} + +void +mesh_respond_serve_expired(struct mesh_state* mstate) +{ + if(!mstate->s.serve_expired_data) + mesh_serve_expired_init(mstate, -1); + mesh_serve_expired_callback(mstate); } int mesh_jostle_exceeded(struct mesh_area* mesh) Index: services/mesh.h =================================================================== RCS file: /cvs/src/usr.sbin/unbound/services/mesh.h,v diff -u -p -r1.11 mesh.h --- services/mesh.h 5 Sep 2023 11:12:10 -0000 1.11 +++ services/mesh.h 22 May 2024 10:33:17 -0000 @@ -690,4 +690,10 @@ mesh_serve_expired_lookup(struct module_ */ int mesh_jostle_exceeded(struct mesh_area* mesh); +/** + * Give the serve expired responses. + * @param mstate: mesh state for query that has serve_expired_data. + */ +void mesh_respond_serve_expired(struct mesh_state* mstate); + #endif /* SERVICES_MESH_H */ Index: services/rpz.c =================================================================== RCS file: /cvs/src/usr.sbin/unbound/services/rpz.c,v diff -u -p -r1.1.1.11 rpz.c --- services/rpz.c 12 Apr 2024 15:44:27 -0000 1.1.1.11 +++ services/rpz.c 22 May 2024 10:33:17 -0000 @@ -478,43 +478,30 @@ new_cname_override(struct regional* regi return rrset; } -struct rpz* -rpz_create(struct config_auth* p) +/** delete the cname override */ +static void +delete_cname_override(struct rpz* r) { - struct rpz* r = calloc(1, sizeof(*r)); - if(!r) - goto err; - - r->region = regional_create_custom(sizeof(struct regional)); - if(!r->region) { - goto err; - } - - if(!(r->local_zones = local_zones_create())){ - goto err; - } - - r->nsdname_zones = local_zones_create(); - if(r->local_zones == NULL){ - goto err; - } - - if(!(r->respip_set = respip_set_create())) { - goto err; - } - - r->client_set = rpz_clientip_synthesized_set_create(); - if(r->client_set == NULL) { - goto err; + if(r->cname_override) { + /* The cname override is what is allocated in the region. */ + regional_free_all(r->region); + r->cname_override = NULL; } +} - r->ns_set = rpz_clientip_synthesized_set_create(); - if(r->ns_set == NULL) { - goto err; +/** Apply rpz config elements to the rpz structure, false on failure. */ +static int +rpz_apply_cfg_elements(struct rpz* r, struct config_auth* p) +{ + if(p->rpz_taglist && p->rpz_taglistlen) { + r->taglistlen = p->rpz_taglistlen; + r->taglist = memdup(p->rpz_taglist, r->taglistlen); + if(!r->taglist) { + log_err("malloc failure on RPZ taglist alloc"); + return 0; + } } - r->taglistlen = p->rpz_taglistlen; - r->taglist = memdup(p->rpz_taglist, r->taglistlen); if(p->rpz_action_override) { r->action_override = rpz_config_to_action(p->rpz_action_override); } @@ -528,17 +515,17 @@ rpz_create(struct config_auth* p) if(!p->rpz_cname) { log_err("rpz: override with cname action found, but no " "rpz-cname-override configured"); - goto err; + return 0; } if(sldns_str2wire_dname_buf(p->rpz_cname, nm, &nmlen) != 0) { log_err("rpz: cannot parse cname override: %s", p->rpz_cname); - goto err; + return 0; } r->cname_override = new_cname_override(r->region, nm, nmlen); if(!r->cname_override) { - goto err; + return 0; } } r->log = p->rpz_log; @@ -546,9 +533,49 @@ rpz_create(struct config_auth* p) if(p->rpz_log_name) { if(!(r->log_name = strdup(p->rpz_log_name))) { log_err("malloc failure on RPZ log_name strdup"); - goto err; + return 0; } } + return 1; +} + +struct rpz* +rpz_create(struct config_auth* p) +{ + struct rpz* r = calloc(1, sizeof(*r)); + if(!r) + goto err; + + r->region = regional_create_custom(sizeof(struct regional)); + if(!r->region) { + goto err; + } + + if(!(r->local_zones = local_zones_create())){ + goto err; + } + + r->nsdname_zones = local_zones_create(); + if(r->local_zones == NULL){ + goto err; + } + + if(!(r->respip_set = respip_set_create())) { + goto err; + } + + r->client_set = rpz_clientip_synthesized_set_create(); + if(r->client_set == NULL) { + goto err; + } + + r->ns_set = rpz_clientip_synthesized_set_create(); + if(r->ns_set == NULL) { + goto err; + } + + if(!rpz_apply_cfg_elements(r, p)) + goto err; return r; err: if(r) { @@ -571,6 +598,32 @@ err: return NULL; } +int +rpz_config(struct rpz* r, struct config_auth* p) +{ + /* If the zonefile changes, it is read later, after which + * rpz_clear and rpz_finish_config is called. */ + + /* free taglist, if any */ + if(r->taglist) { + free(r->taglist); + r->taglist = NULL; + r->taglistlen = 0; + } + + /* free logname, if any */ + if(r->log_name) { + free(r->log_name); + r->log_name = NULL; + } + + delete_cname_override(r); + + if(!rpz_apply_cfg_elements(r, p)) + return 0; + return 1; +} + /** * Remove RPZ zone name from dname * Copy dname to newdname, without the originlen number of trailing bytes @@ -1191,16 +1244,20 @@ rpz_find_zone(struct local_zones* zones, /** Find entry for RR type in the list of rrsets for the clientip. */ static struct local_rrset* rpz_find_synthesized_rrset(uint16_t qtype, - struct clientip_synthesized_rr* data) + struct clientip_synthesized_rr* data, int alias_ok) { - struct local_rrset* cursor = data->data; + struct local_rrset* cursor = data->data, *cname = NULL; while( cursor != NULL) { struct packed_rrset_key* packed_rrset = &cursor->rrset->rk; if(htons(qtype) == packed_rrset->type) { return cursor; } + if(ntohs(packed_rrset->type) == LDNS_RR_TYPE_CNAME && alias_ok) + cname = cursor; cursor = cursor->next; } + if(alias_ok) + return cname; return NULL; } @@ -1386,7 +1443,7 @@ static int rpz_remove_clientip_rr(struct struct local_rrset* rrset; struct packed_rrset_data* d; size_t index; - rrset = rpz_find_synthesized_rrset(rr_type, node); + rrset = rpz_find_synthesized_rrset(rr_type, node, 0); if(rrset == NULL) return 0; /* type not found, ignore */ d = (struct packed_rrset_data*)rrset->rrset->entry.data; @@ -1789,7 +1846,7 @@ rpz_apply_clientip_localdata_action(stru } /* check query type / rr type */ - rrset = rpz_find_synthesized_rrset(qinfo->qtype, raddr); + rrset = rpz_find_synthesized_rrset(qinfo->qtype, raddr, 1); if(rrset == NULL) { verbose(VERB_ALGO, "rpz: unable to find local-data for query"); rrset_count = 0; @@ -1823,6 +1880,28 @@ nodata: rrset_count, rcode, rsoa); } +/** Apply the cname override action, during worker request callback. + * false on failure. */ +static int +rpz_apply_cname_override_action(struct rpz* r, + struct query_info* qinfo, struct regional* temp) +{ + if(!r) + return 0; + qinfo->local_alias = regional_alloc_zero(temp, + sizeof(struct local_rrset)); + if(qinfo->local_alias == NULL) + return 0; /* out of memory */ + qinfo->local_alias->rrset = respip_copy_rrset(r->cname_override, temp); + if(qinfo->local_alias->rrset == NULL) { + qinfo->local_alias = NULL; + return 0; /* out of memory */ + } + qinfo->local_alias->rrset->rk.dname = qinfo->qname; + qinfo->local_alias->rrset->rk.dname_len = qinfo->qname_len; + return 1; +} + /** add additional section SOA record to the reply. * Since this gets fed into the normal iterator answer creation, it * gets minimal-responses applied to it, that can remove the additional SOA @@ -1933,6 +2012,7 @@ rpz_synthesize_localdata_from_rrset(stru msg = rpz_dns_msg_new(ms->region); if(msg == NULL) { return NULL; } + msg->qinfo = *qi; new_reply_info = construct_reply_info_base(ms->region, LDNS_RCODE_NOERROR | BIT_QR | BIT_AA | BIT_RA, 1, /* qd */ @@ -1975,40 +2055,42 @@ rpz_synthesize_localdata_from_rrset(stru static inline struct dns_msg* rpz_synthesize_nsip_localdata(struct rpz* r, struct module_qstate* ms, - struct clientip_synthesized_rr* data, struct auth_zone* az) + struct query_info* qi, struct clientip_synthesized_rr* data, + struct auth_zone* az) { - struct query_info* qi = &ms->qinfo; struct local_rrset* rrset; - rrset = rpz_find_synthesized_rrset(qi->qtype, data); + rrset = rpz_find_synthesized_rrset(qi->qtype, data, 1); if(rrset == NULL) { verbose(VERB_ALGO, "rpz: nsip: no matching local data found"); return NULL; } - return rpz_synthesize_localdata_from_rrset(r, ms, &ms->qinfo, rrset, az); + return rpz_synthesize_localdata_from_rrset(r, ms, qi, rrset, az); } /* copy'n'paste from localzone.c */ static struct local_rrset* local_data_find_type(struct local_data* data, uint16_t type, int alias_ok) { - struct local_rrset* p; + struct local_rrset* p, *cname = NULL; type = htons(type); for(p = data->rrsets; p; p = p->next) { if(p->rrset->rk.type == type) return p; if(alias_ok && p->rrset->rk.type == htons(LDNS_RR_TYPE_CNAME)) - return p; + cname = p; } + if(alias_ok) + return cname; return NULL; } /* based on localzone.c:local_data_answer() */ static inline struct dns_msg* rpz_synthesize_nsdname_localdata(struct rpz* r, struct module_qstate* ms, - struct local_zone* z, struct matched_delegation_point const* match, - struct auth_zone* az) + struct query_info* qi, struct local_zone* z, + struct matched_delegation_point const* match, struct auth_zone* az) { struct local_data key; struct local_data* ld; @@ -2029,13 +2111,13 @@ rpz_synthesize_nsdname_localdata(struct return NULL; } - rrset = local_data_find_type(ld, ms->qinfo.qtype, 1); + rrset = local_data_find_type(ld, qi->qtype, 1); if(rrset == NULL) { verbose(VERB_ALGO, "rpz: nsdname: no matching local data found"); return NULL; } - return rpz_synthesize_localdata_from_rrset(r, ms, &ms->qinfo, rrset, az); + return rpz_synthesize_localdata_from_rrset(r, ms, qi, rrset, az); } /* like local_data_answer for qname triggers after a cname */ @@ -2052,17 +2134,70 @@ rpz_synthesize_qname_localdata_msg(struc key.namelabs = dname_count_labels(qinfo->qname); ld = (struct local_data*)rbtree_search(&z->data, &key.node); if(ld == NULL) { - verbose(VERB_ALGO, "rpz: qname after cname: name not found"); + verbose(VERB_ALGO, "rpz: qname: name not found"); return NULL; } rrset = local_data_find_type(ld, qinfo->qtype, 1); if(rrset == NULL) { - verbose(VERB_ALGO, "rpz: qname after cname: type not found"); + verbose(VERB_ALGO, "rpz: qname: type not found"); return NULL; } return rpz_synthesize_localdata_from_rrset(r, ms, qinfo, rrset, az); } +/** Synthesize a CNAME message for RPZ action override */ +static struct dns_msg* +rpz_synthesize_cname_override_msg(struct rpz* r, struct module_qstate* ms, + struct query_info* qinfo) +{ + struct dns_msg* msg = NULL; + struct reply_info* new_reply_info; + struct ub_packed_rrset_key* rp; + + msg = rpz_dns_msg_new(ms->region); + if(msg == NULL) { return NULL; } + + msg->qinfo = *qinfo; + new_reply_info = construct_reply_info_base(ms->region, + LDNS_RCODE_NOERROR | BIT_QR | BIT_AA | BIT_RA, + 1, /* qd */ + 0, /* ttl */ + 0, /* prettl */ + 0, /* expttl */ + 1, /* an */ + 0, /* ns */ + 0, /* ar */ + 1, /* total */ + sec_status_insecure, + LDNS_EDE_NONE); + if(new_reply_info == NULL) { + log_err("out of memory"); + return NULL; + } + new_reply_info->authoritative = 1; + + rp = respip_copy_rrset(r->cname_override, ms->region); + if(rp == NULL) { + log_err("out of memory"); + return NULL; + } + rp->rk.dname = qinfo->qname; + rp->rk.dname_len = qinfo->qname_len; + /* this rrset is from the rpz data, or synthesized. + * It is not actually from the network, so we flag it with this + * flags as a fake RRset. If later the cache is used to look up + * rrsets, then the fake ones are not returned (if you look without + * the flag). For like CNAME lookups from the iterator or A, AAAA + * lookups for nameserver targets, it would use the without flag + * actual data. So that the actual network data and fake data + * are kept track of separately. */ + rp->rk.flags |= PACKED_RRSET_RPZ; + new_reply_info->rrsets[0] = rp; + + msg->rep = new_reply_info; + return msg; +} + static int rpz_synthesize_qname_localdata(struct module_env* env, struct rpz* r, struct local_zone* z, enum localzone_type lzt, struct query_info* qinfo, @@ -2072,17 +2207,8 @@ rpz_synthesize_qname_localdata(struct mo struct local_data* ld = NULL; int ret = 0; if(r->action_override == RPZ_CNAME_OVERRIDE_ACTION) { - qinfo->local_alias = regional_alloc_zero(temp, sizeof(struct local_rrset)); - if(qinfo->local_alias == NULL) { - return 0; /* out of memory */ - } - qinfo->local_alias->rrset = regional_alloc_init(temp, r->cname_override, - sizeof(*r->cname_override)); - if(qinfo->local_alias->rrset == NULL) { - return 0; /* out of memory */ - } - qinfo->local_alias->rrset->rk.dname = qinfo->qname; - qinfo->local_alias->rrset->rk.dname_len = qinfo->qname_len; + if(!rpz_apply_cname_override_action(r, qinfo, temp)) + return 0; if(r->log) { log_rpz_apply("qname", z->name, NULL, RPZ_CNAME_OVERRIDE_ACTION, qinfo, repinfo, NULL, r->log_name); @@ -2134,8 +2260,9 @@ rpz_delegation_point_ipbased_trigger_loo } static struct dns_msg* -rpz_apply_nsip_trigger(struct module_qstate* ms, struct rpz* r, - struct clientip_synthesized_rr* raddr, struct auth_zone* az) +rpz_apply_nsip_trigger(struct module_qstate* ms, struct query_info* qchase, + struct rpz* r, struct clientip_synthesized_rr* raddr, + struct auth_zone* az) { enum rpz_action action = raddr->action; struct dns_msg* ret = NULL; @@ -2148,16 +2275,16 @@ rpz_apply_nsip_trigger(struct module_qst if(action == RPZ_LOCAL_DATA_ACTION && raddr->data == NULL) { verbose(VERB_ALGO, "rpz: bug: nsip local data action but no local data"); - ret = rpz_synthesize_nodata(r, ms, &ms->qinfo, az); + ret = rpz_synthesize_nodata(r, ms, qchase, az); goto done; } switch(action) { case RPZ_NXDOMAIN_ACTION: - ret = rpz_synthesize_nxdomain(r, ms, &ms->qinfo, az); + ret = rpz_synthesize_nxdomain(r, ms, qchase, az); break; case RPZ_NODATA_ACTION: - ret = rpz_synthesize_nodata(r, ms, &ms->qinfo, az); + ret = rpz_synthesize_nodata(r, ms, qchase, az); break; case RPZ_TCP_ONLY_ACTION: /* basically a passthru here but the tcp-only will be @@ -2166,17 +2293,20 @@ rpz_apply_nsip_trigger(struct module_qst ret = NULL; break; case RPZ_DROP_ACTION: - ret = rpz_synthesize_nodata(r, ms, &ms->qinfo, az); + ret = rpz_synthesize_nodata(r, ms, qchase, az); ms->is_drop = 1; break; case RPZ_LOCAL_DATA_ACTION: - ret = rpz_synthesize_nsip_localdata(r, ms, raddr, az); - if(ret == NULL) { ret = rpz_synthesize_nodata(r, ms, &ms->qinfo, az); } + ret = rpz_synthesize_nsip_localdata(r, ms, qchase, raddr, az); + if(ret == NULL) { ret = rpz_synthesize_nodata(r, ms, qchase, az); } break; case RPZ_PASSTHRU_ACTION: ret = NULL; ms->rpz_passthru = 1; break; + case RPZ_CNAME_OVERRIDE_ACTION: + ret = rpz_synthesize_cname_override_msg(r, ms, qchase); + break; default: verbose(VERB_ALGO, "rpz: nsip: bug: unhandled or invalid action: '%s'", rpz_action_to_string(action)); @@ -2194,9 +2324,9 @@ done: } static struct dns_msg* -rpz_apply_nsdname_trigger(struct module_qstate* ms, struct rpz* r, - struct local_zone* z, struct matched_delegation_point const* match, - struct auth_zone* az) +rpz_apply_nsdname_trigger(struct module_qstate* ms, struct query_info* qchase, + struct rpz* r, struct local_zone* z, + struct matched_delegation_point const* match, struct auth_zone* az) { struct dns_msg* ret = NULL; enum rpz_action action = localzone_type_to_rpz_action(z->type); @@ -2209,10 +2339,10 @@ rpz_apply_nsdname_trigger(struct module_ switch(action) { case RPZ_NXDOMAIN_ACTION: - ret = rpz_synthesize_nxdomain(r, ms, &ms->qinfo, az); + ret = rpz_synthesize_nxdomain(r, ms, qchase, az); break; case RPZ_NODATA_ACTION: - ret = rpz_synthesize_nodata(r, ms, &ms->qinfo, az); + ret = rpz_synthesize_nodata(r, ms, qchase, az); break; case RPZ_TCP_ONLY_ACTION: /* basically a passthru here but the tcp-only will be @@ -2221,19 +2351,22 @@ rpz_apply_nsdname_trigger(struct module_ ret = NULL; break; case RPZ_DROP_ACTION: - ret = rpz_synthesize_nodata(r, ms, &ms->qinfo, az); + ret = rpz_synthesize_nodata(r, ms, qchase, az); ms->is_drop = 1; break; case RPZ_LOCAL_DATA_ACTION: - ret = rpz_synthesize_nsdname_localdata(r, ms, z, match, az); - if(ret == NULL) { ret = rpz_synthesize_nodata(r, ms, &ms->qinfo, az); } + ret = rpz_synthesize_nsdname_localdata(r, ms, qchase, z, match, az); + if(ret == NULL) { ret = rpz_synthesize_nodata(r, ms, qchase, az); } break; case RPZ_PASSTHRU_ACTION: ret = NULL; ms->rpz_passthru = 1; break; + case RPZ_CNAME_OVERRIDE_ACTION: + ret = rpz_synthesize_cname_override_msg(r, ms, qchase); + break; default: - verbose(VERB_ALGO, "rpz: nsip: bug: unhandled or invalid action: '%s'", + verbose(VERB_ALGO, "rpz: nsdname: bug: unhandled or invalid action: '%s'", rpz_action_to_string(action)); ret = NULL; } @@ -2324,7 +2457,7 @@ rpz_callback_from_iterator_module(struct /* the nsdname has precedence over the nsip triggers */ z = rpz_delegation_point_zone_lookup(is->dp, r->nsdname_zones, - ms->qinfo.qclass, &match); + is->qchase.qclass, &match); if(z != NULL) { lock_rw_unlock(&a->lock); break; @@ -2347,9 +2480,9 @@ rpz_callback_from_iterator_module(struct if(z) { lock_rw_unlock(&z->lock); } - return rpz_apply_nsip_trigger(ms, r, raddr, a); + return rpz_apply_nsip_trigger(ms, &is->qchase, r, raddr, a); } - return rpz_apply_nsdname_trigger(ms, r, z, &match, a); + return rpz_apply_nsdname_trigger(ms, &is->qchase, r, z, &match, a); } struct dns_msg* rpz_callback_from_iterator_cname(struct module_qstate* ms, @@ -2412,10 +2545,10 @@ struct dns_msg* rpz_callback_from_iterat dname_str(is->qchase.qname, nm); dname_str(z->name, zn); if(strcmp(zn, nm) != 0) - verbose(VERB_ALGO, "rpz: qname trigger after cname %s on %s, with action=%s", + verbose(VERB_ALGO, "rpz: qname trigger %s on %s, with action=%s", zn, nm, rpz_action_to_string(localzone_type_to_rpz_action(lzt))); else - verbose(VERB_ALGO, "rpz: qname trigger after cname %s, with action=%s", + verbose(VERB_ALGO, "rpz: qname trigger %s, with action=%s", nm, rpz_action_to_string(localzone_type_to_rpz_action(lzt))); } switch(localzone_type_to_rpz_action(lzt)) { @@ -2444,7 +2577,7 @@ struct dns_msg* rpz_callback_from_iterat ms->rpz_passthru = 1; break; default: - verbose(VERB_ALGO, "rpz: qname trigger after cname: bug: unhandled or invalid action: '%s'", + verbose(VERB_ALGO, "rpz: qname trigger: bug: unhandled or invalid action: '%s'", rpz_action_to_string(localzone_type_to_rpz_action(lzt))); ret = NULL; } @@ -2472,8 +2605,21 @@ rpz_apply_maybe_clientip_trigger(struct az, qinfo, repinfo, taglist, taglen, stats, z_out, a_out, r_out); client_action = ((node == NULL) ? RPZ_INVALID_ACTION : node->action); + if(node != NULL && *r_out && + (*r_out)->action_override != RPZ_NO_OVERRIDE_ACTION) { + client_action = (*r_out)->action_override; + } if(client_action == RPZ_PASSTHRU_ACTION) { + if(*r_out && (*r_out)->log) + log_rpz_apply( + (node?"clientip":"qname"), + ((*z_out)?(*z_out)->name:NULL), + (node?&node->node:NULL), + client_action, qinfo, repinfo, NULL, + (*r_out)->log_name); *passthru = 1; + ret = 0; + goto done; } if(*z_out == NULL || (client_action != RPZ_INVALID_ACTION && client_action != RPZ_PASSTHRU_ACTION)) { @@ -2488,14 +2634,15 @@ rpz_apply_maybe_clientip_trigger(struct if(client_action == RPZ_LOCAL_DATA_ACTION) { rpz_apply_clientip_localdata_action(node, env, qinfo, edns, repinfo, buf, temp, *a_out); + ret = 1; + } else if(client_action == RPZ_CNAME_OVERRIDE_ACTION) { + if(!rpz_apply_cname_override_action(*r_out, qinfo, + temp)) { + ret = 0; + goto done; + } + ret = 0; } else { - if(*r_out && (*r_out)->log) - log_rpz_apply( - (node?"clientip":"qname"), - ((*z_out)?(*z_out)->name:NULL), - (node?&node->node:NULL), - client_action, qinfo, repinfo, NULL, - (*r_out)->log_name); local_zones_zone_answer(*z_out /*likely NULL, no zone*/, env, qinfo, edns, repinfo, buf, temp, 0 /* no local data used */, rpz_action_to_localzone_type(client_action)); @@ -2503,8 +2650,15 @@ rpz_apply_maybe_clientip_trigger(struct LDNS_RCODE_WIRE(sldns_buffer_begin(buf)) == LDNS_RCODE_NXDOMAIN) LDNS_RA_CLR(sldns_buffer_begin(buf)); + ret = 1; } - ret = 1; + if(*r_out && (*r_out)->log) + log_rpz_apply( + (node?"clientip":"qname"), + ((*z_out)?(*z_out)->name:NULL), + (node?&node->node:NULL), + client_action, qinfo, repinfo, NULL, + (*r_out)->log_name); goto done; } ret = -1; Index: services/rpz.h =================================================================== RCS file: /cvs/src/usr.sbin/unbound/services/rpz.h,v diff -u -p -r1.1.1.6 rpz.h --- services/rpz.h 5 Sep 2023 11:07:45 -0000 1.1.1.6 +++ services/rpz.h 22 May 2024 10:33:17 -0000 @@ -226,6 +226,14 @@ int rpz_clear(struct rpz* r); struct rpz* rpz_create(struct config_auth* p); /** + * Change config on rpz, after reload. + * @param r: the rpz structure. + * @param p: the config that was read. + * @return false on failure. + */ +int rpz_config(struct rpz* r, struct config_auth* p); + +/** * String for RPZ action enum * @param a: RPZ action to get string for * @return: string for RPZ action Index: services/view.h =================================================================== RCS file: /cvs/src/usr.sbin/unbound/services/view.h,v diff -u -p -r1.3 view.h --- services/view.h 7 Feb 2018 00:24:33 -0000 1.3 +++ services/view.h 22 May 2024 10:33:17 -0000 @@ -126,7 +126,8 @@ void view_delete(struct view* v); */ void views_print(struct views* v); -/* Find a view by name. +/** + * Find a view by name. * @param vs: views * @param name: name of the view we are looking for * @param write: 1 for obtaining write lock on found view, 0 for read lock Index: services/cache/dns.c =================================================================== RCS file: /cvs/src/usr.sbin/unbound/services/cache/dns.c,v diff -u -p -r1.23 dns.c --- services/cache/dns.c 13 Apr 2024 12:24:57 -0000 1.23 +++ services/cache/dns.c 22 May 2024 10:33:17 -0000 @@ -193,46 +193,6 @@ dns_cache_store_msg(struct module_env* e slabhash_insert(env->msg_cache, hash, &e->entry, rep, env->alloc); } -/** see if an rrset is expired above the qname, return upper qname. */ -static int -rrset_expired_above(struct module_env* env, uint8_t** qname, size_t* qnamelen, - uint16_t searchtype, uint16_t qclass, time_t now, uint8_t* expiretop, - size_t expiretoplen) -{ - struct ub_packed_rrset_key *rrset; - uint8_t lablen; - - while(*qnamelen > 0) { - /* look one label higher */ - lablen = **qname; - *qname += lablen + 1; - *qnamelen -= lablen + 1; - if(*qnamelen <= 0) - break; - - /* looks up with a time of 0, to see expired entries */ - if((rrset = rrset_cache_lookup(env->rrset_cache, *qname, - *qnamelen, searchtype, qclass, 0, 0, 0))) { - struct packed_rrset_data* data = - (struct packed_rrset_data*)rrset->entry.data; - if(now > data->ttl) { - /* it is expired, this is not wanted */ - lock_rw_unlock(&rrset->entry.lock); - log_nametypeclass(VERB_ALGO, "this rrset is expired", *qname, searchtype, qclass); - return 1; - } - /* it is not expired, continue looking */ - lock_rw_unlock(&rrset->entry.lock); - } - - /* do not look above the expiretop. */ - if(expiretop && *qnamelen == expiretoplen && - query_dname_compare(*qname, expiretop)==0) - break; - } - return 0; -} - /** find closest NS or DNAME and returns the rrset (locked) */ static struct ub_packed_rrset_key* find_closest_of_type(struct module_env* env, uint8_t* qname, size_t qnamelen, @@ -266,12 +226,12 @@ find_closest_of_type(struct module_env* /* check for expiry, but we have to let go of the rrset * for the lock ordering */ lock_rw_unlock(&rrset->entry.lock); - /* the expired_above function always takes off one - * label (if qnamelen>0) and returns the final qname - * where it searched, so we can continue from there - * turning the O N*N search into O N. */ - if(!rrset_expired_above(env, &qname, &qnamelen, - searchtype, qclass, now, expiretop, + /* the rrset_cache_expired_above function always takes + * off one label (if qnamelen>0) and returns the final + * qname where it searched, so we can continue from + * there turning the O N*N search into O N. */ + if(!rrset_cache_expired_above(env->rrset_cache, &qname, + &qnamelen, searchtype, qclass, now, expiretop, expiretoplen)) { /* we want to return rrset, but it may be * gone from cache, if so, just loop like Index: services/cache/infra.c =================================================================== RCS file: /cvs/src/usr.sbin/unbound/services/cache/infra.c,v diff -u -p -r1.15 infra.c --- services/cache/infra.c 5 Sep 2023 11:12:10 -0000 1.15 +++ services/cache/infra.c 22 May 2024 10:33:17 -0000 @@ -234,6 +234,81 @@ setup_domain_limits(struct infra_cache* 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) +{ + rbtree_type* tree; + struct sockaddr_storage addr; + int net; + socklen_t addrlen; + struct wait_limit_netblock_info* d; + + if(!netblockstrtoaddr(str, 0, &addr, &addrlen, &net)) { + log_err("cannot parse wait limit netblock '%s'", str); + return 0; + } + + /* 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) + return d; + + /* create it */ + d = (struct wait_limit_netblock_info*)calloc(1, sizeof(*d)); + if(!d) + return NULL; + d->limit = -1; + if(!addr_tree_insert(tree, &d->node, &addr, addrlen, net)) { + log_err("duplicate element in domainlimit tree"); + free(d); + return NULL; + } + return d; +} + + +/** insert wait limit information into lookup tree */ +static int +infra_wait_limit_netblock_insert(struct infra_cache* infra, + 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); + 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); + if(!d) + return 0; + d->limit = atoi(p->str2); + } + return 1; +} + +/** setup wait limits tree (0 on failure) */ +static int +setup_wait_limits(struct infra_cache* infra, 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)) + return 0; + addr_tree_init_parents(&infra->wait_limits_netblock); + addr_tree_init_parents(&infra->wait_limits_cookie_netblock); + return 1; +} + struct infra_cache* infra_create(struct config_file* cfg) { @@ -267,6 +342,10 @@ infra_create(struct config_file* cfg) infra_delete(infra); return NULL; } + if(!setup_wait_limits(infra, cfg)) { + infra_delete(infra); + return NULL; + } infra_ip_ratelimit = cfg->ip_ratelimit; infra->client_ip_rates = slabhash_create(cfg->ip_ratelimit_slabs, INFRA_HOST_STARTSIZE, cfg->ip_ratelimit_size, &ip_rate_sizefunc, @@ -287,6 +366,12 @@ static void domain_limit_free(rbnode_typ } } +/** delete wait_limit_netblock_info entries */ +static void wait_limit_netblock_del(rbnode_type* n, void* ATTR_UNUSED(arg)) +{ + free(n); +} + void infra_delete(struct infra_cache* infra) { @@ -296,6 +381,10 @@ infra_delete(struct infra_cache* infra) slabhash_delete(infra->domain_rates); traverse_postorder(&infra->domain_limits, domain_limit_free, NULL); 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); free(infra); } @@ -880,7 +969,8 @@ static void infra_create_ratedata(struct /** create rate data item for ip address */ static void infra_ip_create_ratedata(struct infra_cache* infra, - struct sockaddr_storage* addr, socklen_t addrlen, time_t timenow) + struct sockaddr_storage* addr, socklen_t addrlen, time_t timenow, + int mesh_wait) { hashvalue_type h = hash_addr(addr, addrlen, 0); struct ip_rate_key* k = (struct ip_rate_key*)calloc(1, sizeof(*k)); @@ -898,6 +988,7 @@ static void infra_ip_create_ratedata(str k->entry.data = d; d->qps[0] = 1; d->timestamp[0] = timenow; + d->mesh_wait = mesh_wait; slabhash_insert(infra->client_ip_rates, h, &k->entry, d, NULL); } @@ -1121,6 +1212,81 @@ int infra_ip_ratelimit_inc(struct infra_ } /* create */ - infra_ip_create_ratedata(infra, addr, addrlen, timenow); + infra_ip_create_ratedata(infra, addr, addrlen, timenow, 0); return 1; +} + +int infra_wait_limit_allowed(struct infra_cache* infra, struct comm_reply* rep, + int cookie_valid, struct config_file* cfg) +{ + struct lruhash_entry* entry; + if(cfg->wait_limit == 0) + return 1; + + entry = infra_find_ip_ratedata(infra, &rep->client_addr, + rep->client_addrlen, 0); + if(entry) { + rbtree_type* tree; + struct wait_limit_netblock_info* w; + struct rate_data* d = (struct rate_data*)entry->data; + int mesh_wait = d->mesh_wait; + lock_rw_unlock(&entry->lock); + + /* have the wait amount, check how much is allowed */ + if(cookie_valid) + tree = &infra->wait_limits_cookie_netblock; + else tree = &infra->wait_limits_netblock; + w = (struct wait_limit_netblock_info*)addr_tree_lookup(tree, + &rep->client_addr, rep->client_addrlen); + if(w) { + if(w->limit != -1 && mesh_wait > w->limit) + return 0; + } else { + /* if there is no IP netblock specific information, + * use the configured value. */ + if(mesh_wait > (cookie_valid?cfg->wait_limit_cookie: + cfg->wait_limit)) + return 0; + } + } + return 1; +} + +void infra_wait_limit_inc(struct infra_cache* infra, struct comm_reply* rep, + time_t timenow, struct config_file* cfg) +{ + struct lruhash_entry* entry; + if(cfg->wait_limit == 0) + return; + + /* Find it */ + entry = infra_find_ip_ratedata(infra, &rep->client_addr, + rep->client_addrlen, 1); + if(entry) { + struct rate_data* d = (struct rate_data*)entry->data; + d->mesh_wait++; + lock_rw_unlock(&entry->lock); + return; + } + + /* Create it */ + infra_ip_create_ratedata(infra, &rep->client_addr, + rep->client_addrlen, timenow, 1); +} + +void infra_wait_limit_dec(struct infra_cache* infra, struct comm_reply* rep, + struct config_file* cfg) +{ + struct lruhash_entry* entry; + if(cfg->wait_limit == 0) + return; + + entry = infra_find_ip_ratedata(infra, &rep->client_addr, + rep->client_addrlen, 1); + if(entry) { + struct rate_data* d = (struct rate_data*)entry->data; + if(d->mesh_wait > 0) + d->mesh_wait--; + lock_rw_unlock(&entry->lock); + } } Index: services/cache/infra.h =================================================================== RCS file: /cvs/src/usr.sbin/unbound/services/cache/infra.h,v diff -u -p -r1.8 infra.h --- services/cache/infra.h 5 Sep 2023 11:12:10 -0000 1.8 +++ services/cache/infra.h 22 May 2024 10:33:17 -0000 @@ -122,6 +122,10 @@ struct infra_cache { rbtree_type domain_limits; /** hash table with query rates per client ip: ip_rate_key, ip_rate_data */ struct slabhash* client_ip_rates; + /** tree of addr_tree_node, with wait_limit_netblock_info information */ + rbtree_type wait_limits_netblock; + /** tree of addr_tree_node, with wait_limit_netblock_info information */ + rbtree_type wait_limits_cookie_netblock; }; /** ratelimit, unless overridden by domain_limits, 0 is off */ @@ -184,10 +188,22 @@ struct rate_data { /** what the timestamp is of the qps array members, counter is * valid for that timestamp. Usually now and now-1. */ time_t timestamp[RATE_WINDOW]; + /** the number of queries waiting in the mesh */ + int mesh_wait; }; #define ip_rate_data rate_data +/** + * Data to store the configuration per netblock for the wait limit + */ +struct wait_limit_netblock_info { + /** The addr tree node, this must be first. */ + struct addr_tree_node node; + /** the limit on the amount */ + int limit; +}; + /** infra host cache default hash lookup size */ #define INFRA_HOST_STARTSIZE 32 /** bytes per zonename reserved in the hostcache, dnamelen(zonename.com.) */ @@ -473,5 +489,17 @@ void ip_rate_delkeyfunc(void* d, void* a /* delete data */ #define ip_rate_deldatafunc rate_deldatafunc + +/** See if the IP address can have another reply in the wait limit */ +int infra_wait_limit_allowed(struct infra_cache* infra, struct comm_reply* rep, + int cookie_valid, struct config_file* cfg); + +/** Increment number of waiting replies for IP */ +void infra_wait_limit_inc(struct infra_cache* infra, struct comm_reply* rep, + time_t timenow, struct config_file* cfg); + +/** Decrement number of waiting replies for IP */ +void infra_wait_limit_dec(struct infra_cache* infra, struct comm_reply* rep, + struct config_file* cfg); #endif /* SERVICES_CACHE_INFRA_H */ Index: services/cache/rrset.c =================================================================== RCS file: /cvs/src/usr.sbin/unbound/services/cache/rrset.c,v diff -u -p -r1.7 rrset.c --- services/cache/rrset.c 12 Mar 2021 19:45:27 -0000 1.7 +++ services/cache/rrset.c 22 May 2024 10:33:17 -0000 @@ -46,6 +46,7 @@ #include "util/data/packed_rrset.h" #include "util/data/msgreply.h" #include "util/data/msgparse.h" +#include "util/data/dname.h" #include "util/regional.h" #include "util/alloc.h" #include "util/net_help.h" @@ -127,6 +128,9 @@ need_to_update_rrset(void* nd, void* cd, { struct packed_rrset_data* newd = (struct packed_rrset_data*)nd; struct packed_rrset_data* cached = (struct packed_rrset_data*)cd; + /* o if new data is expired, current data is better */ + if( newd->ttl < timenow && cached->ttl >= timenow) + return 0; /* o store if rrset has been validated * everything better than bogus data * secure is preferred */ @@ -438,6 +442,89 @@ rrset_check_sec_status(struct rrset_cach updata->trust = cachedata->trust; } lock_rw_unlock(&e->lock); +} + +void +rrset_cache_remove_above(struct rrset_cache* r, uint8_t** qname, size_t* + qnamelen, uint16_t searchtype, uint16_t qclass, time_t now, uint8_t* + qnametop, size_t qnametoplen) +{ + struct ub_packed_rrset_key *rrset; + uint8_t lablen; + + while(*qnamelen > 0) { + /* look one label higher */ + lablen = **qname; + *qname += lablen + 1; + *qnamelen -= lablen + 1; + if(*qnamelen <= 0) + return; + + /* stop at qnametop */ + if(qnametop && *qnamelen == qnametoplen && + query_dname_compare(*qname, qnametop)==0) + return; + + if(verbosity >= VERB_ALGO) { + /* looks up with a time of 0, to see expired entries */ + if((rrset = rrset_cache_lookup(r, *qname, + *qnamelen, searchtype, qclass, 0, 0, 0))) { + struct packed_rrset_data* data = + (struct packed_rrset_data*)rrset->entry.data; + int expired = (now > data->ttl); + lock_rw_unlock(&rrset->entry.lock); + if(expired) + log_nametypeclass(verbosity, "this " + "(grand)parent rrset will be " + "removed (expired)", + *qname, searchtype, qclass); + else log_nametypeclass(verbosity, "this " + "(grand)parent rrset will be " + "removed", + *qname, searchtype, qclass); + } + } + rrset_cache_remove(r, *qname, *qnamelen, searchtype, qclass, 0); + } +} + +int +rrset_cache_expired_above(struct rrset_cache* r, uint8_t** qname, size_t* + qnamelen, uint16_t searchtype, uint16_t qclass, time_t now, uint8_t* + qnametop, size_t qnametoplen) +{ + struct ub_packed_rrset_key *rrset; + uint8_t lablen; + + while(*qnamelen > 0) { + /* look one label higher */ + lablen = **qname; + *qname += lablen + 1; + *qnamelen -= lablen + 1; + if(*qnamelen <= 0) + break; + + /* looks up with a time of 0, to see expired entries */ + if((rrset = rrset_cache_lookup(r, *qname, + *qnamelen, searchtype, qclass, 0, 0, 0))) { + struct packed_rrset_data* data = + (struct packed_rrset_data*)rrset->entry.data; + if(now > data->ttl) { + /* it is expired, this is not wanted */ + lock_rw_unlock(&rrset->entry.lock); + log_nametypeclass(VERB_ALGO, "this rrset is expired", *qname, searchtype, qclass); + return 1; + } + /* it is not expired, continue looking */ + lock_rw_unlock(&rrset->entry.lock); + } + + /* do not look above the qnametop. */ + if(qnametop && *qnamelen == qnametoplen && + query_dname_compare(*qname, qnametop)==0) + break; + } + return 0; } void rrset_cache_remove(struct rrset_cache* r, uint8_t* nm, size_t nmlen, Index: services/cache/rrset.h =================================================================== RCS file: /cvs/src/usr.sbin/unbound/services/cache/rrset.h,v diff -u -p -r1.4 rrset.h --- services/cache/rrset.h 23 Feb 2022 12:04:06 -0000 1.4 +++ services/cache/rrset.h 22 May 2024 10:33:17 -0000 @@ -232,6 +232,37 @@ void rrset_check_sec_status(struct rrset struct ub_packed_rrset_key* rrset, time_t now); /** + * Removes rrsets above the qname, returns upper qname. + * @param r: the rrset cache. + * @param qname: the start qname, also used as the output. + * @param qnamelen: length of qname, updated when it returns. + * @param searchtype: qtype to search for. + * @param qclass: qclass to search for. + * @param now: current time. + * @param qnametop: the top qname to stop removal (it is not removed). + * @param qnametoplen: length of qnametop. + */ +void rrset_cache_remove_above(struct rrset_cache* r, uint8_t** qname, + size_t* qnamelen, uint16_t searchtype, uint16_t qclass, time_t now, + uint8_t* qnametop, size_t qnametoplen); + +/** + * Sees if an rrset is expired above the qname, returns upper qname. + * @param r: the rrset cache. + * @param qname: the start qname, also used as the output. + * @param qnamelen: length of qname, updated when it returns. + * @param searchtype: qtype to search for. + * @param qclass: qclass to search for. + * @param now: current time. + * @param qnametop: the top qname, don't look farther than that. + * @param qnametoplen: length of qnametop. + * @return true if there is an expired rrset above, false otherwise. + */ +int rrset_cache_expired_above(struct rrset_cache* r, uint8_t** qname, + size_t* qnamelen, uint16_t searchtype, uint16_t qclass, time_t now, + uint8_t* qnametop, size_t qnametoplen); + +/** * Remove an rrset from the cache, by name and type and flags * @param r: rrset cache * @param nm: name of rrset Index: smallapp/unbound-anchor.c =================================================================== RCS file: /cvs/src/usr.sbin/unbound/smallapp/unbound-anchor.c,v diff -u -p -r1.19 unbound-anchor.c --- smallapp/unbound-anchor.c 5 Sep 2023 11:12:10 -0000 1.19 +++ smallapp/unbound-anchor.c 22 May 2024 10:33:17 -0000 @@ -1839,15 +1839,49 @@ verify_p7sig(BIO* data, BIO* p7s, STACK_ return secure; } +/** open a temp file */ +static FILE* +tempfile_open(char* tempf, size_t tempflen, const char* fname, const char* mode) +{ + snprintf(tempf, tempflen, "%s~", fname); + return fopen(tempf, mode); +} + +/** close an open temp file and replace the original with it */ +static void +tempfile_close(FILE* fd, const char* tempf, const char* fname) +{ + fflush(fd); +#ifdef HAVE_FSYNC + fsync(fileno(fd)); +#else + FlushFileBuffers((HANDLE)_get_osfhandle(_fileno(fd))); +#endif + if(fclose(fd) != 0) { + printf("could not complete write: %s: %s\n", + tempf, strerror(errno)); + unlink(tempf); + return; + } + /* success; overwrite actual file */ +#ifdef USE_WINSOCK + (void)unlink(fname); /* windows does not replace file with rename() */ +#endif + if(rename(tempf, fname) < 0) { + printf("rename(%s to %s): %s", tempf, fname, strerror(errno)); + } +} + /** write unsigned root anchor file, a 5011 revoked tp */ static void write_unsigned_root(const char* root_anchor_file) { FILE* out; time_t now = time(NULL); - out = fopen(root_anchor_file, "w"); + char tempf[2048]; + out = tempfile_open(tempf, sizeof(tempf), root_anchor_file, "w"); if(!out) { - if(verb) printf("%s: %s\n", root_anchor_file, strerror(errno)); + if(verb) printf("%s: %s\n", tempf, strerror(errno)); return; } if(fprintf(out, "; autotrust trust anchor file\n" @@ -1862,13 +1896,7 @@ write_unsigned_root(const char* root_anc root_anchor_file); if(verb && errno != 0) printf("%s\n", strerror(errno)); } - fflush(out); -#ifdef HAVE_FSYNC - fsync(fileno(out)); -#else - FlushFileBuffers((HANDLE)_get_osfhandle(_fileno(out))); -#endif - fclose(out); + tempfile_close(out, tempf, root_anchor_file); } /** write root anchor file */ @@ -1878,29 +1906,24 @@ write_root_anchor(const char* root_ancho char* pp = NULL; int len; FILE* out; + char tempf[2048]; (void)BIO_seek(ds, 0); len = BIO_get_mem_data(ds, &pp); if(!len || !pp) { if(verb) printf("out of memory\n"); return; } - out = fopen(root_anchor_file, "w"); + out = tempfile_open(tempf, sizeof(tempf), root_anchor_file, "w"); if(!out) { - if(verb) printf("%s: %s\n", root_anchor_file, strerror(errno)); + if(verb) printf("%s: %s\n", tempf, strerror(errno)); return; } if(fwrite(pp, (size_t)len, 1, out) != 1) { if(verb) printf("failed to write all data to %s\n", - root_anchor_file); + tempf); if(verb && errno != 0) printf("%s\n", strerror(errno)); } - fflush(out); -#ifdef HAVE_FSYNC - fsync(fileno(out)); -#else - FlushFileBuffers((HANDLE)_get_osfhandle(_fileno(out))); -#endif - fclose(out); + tempfile_close(out, tempf, root_anchor_file); } /** Perform the verification and update of the trustanchor file */ @@ -2044,18 +2067,19 @@ try_read_anchor(const char* file) static void write_builtin_anchor(const char* file) { + char tempf[2048]; const char* builtin_root_anchor = get_builtin_ds(); - FILE* out = fopen(file, "w"); + FILE* out = tempfile_open(tempf, sizeof(tempf), file, "w"); if(!out) { printf("could not write builtin anchor, to file %s: %s\n", - file, strerror(errno)); + tempf, strerror(errno)); return; } if(!fwrite(builtin_root_anchor, strlen(builtin_root_anchor), 1, out)) { printf("could not complete write builtin anchor, to file %s: %s\n", - file, strerror(errno)); + tempf, strerror(errno)); } - fclose(out); + tempfile_close(out, tempf, file); } /** Index: smallapp/unbound-checkconf.c =================================================================== RCS file: /cvs/src/usr.sbin/unbound/smallapp/unbound-checkconf.c,v diff -u -p -r1.22 unbound-checkconf.c --- smallapp/unbound-checkconf.c 12 Apr 2024 15:45:24 -0000 1.22 +++ smallapp/unbound-checkconf.c 22 May 2024 10:33:17 -0000 @@ -88,6 +88,7 @@ usage(void) printf("file if omitted %s is used.\n", CONFIGFILE); printf("-o option print value of option to stdout.\n"); printf("-f output full pathname with chroot applied, eg. with -o pidfile.\n"); + printf("-q quiet (suppress output on success).\n"); printf("-h show this usage help.\n"); printf("Version %s\n", PACKAGE_VERSION); printf("BSD licensed, see LICENSE in source package for details.\n"); @@ -969,7 +970,7 @@ check_auth(struct config_file* cfg) /** check config file */ static void -checkconf(const char* cfgfile, const char* opt, int final) +checkconf(const char* cfgfile, const char* opt, int final, int quiet) { char oldwd[4096]; struct config_file* cfg = config_create(); @@ -1002,7 +1003,7 @@ checkconf(const char* cfgfile, const cha check_fwd(cfg); check_hints(cfg); check_auth(cfg); - printf("unbound-checkconf: no errors in %s\n", cfgfile); + if(!quiet) { printf("unbound-checkconf: no errors in %s\n", cfgfile); } config_delete(cfg); } @@ -1016,6 +1017,7 @@ int main(int argc, char* argv[]) { int c; int final = 0; + int quiet = 0; const char* f; const char* opt = NULL; const char* cfgfile = CONFIGFILE; @@ -1028,7 +1030,7 @@ int main(int argc, char* argv[]) cfgfile = CONFIGFILE; #endif /* USE_WINSOCK */ /* parse the options */ - while( (c=getopt(argc, argv, "fho:")) != -1) { + while( (c=getopt(argc, argv, "fhqo:")) != -1) { switch(c) { case 'f': final = 1; @@ -1036,6 +1038,9 @@ int main(int argc, char* argv[]) case 'o': opt = optarg; break; + case 'q': + quiet = 1; + break; case '?': case 'h': default: @@ -1053,7 +1058,7 @@ int main(int argc, char* argv[]) if (pledge("stdio rpath dns getpw", NULL) == -1) fatal_exit("Could not pledge"); - checkconf(f, opt, final); + checkconf(f, opt, final, quiet); checklock_stop(); return 0; } Index: smallapp/unbound-control.c =================================================================== RCS file: /cvs/src/usr.sbin/unbound/smallapp/unbound-control.c,v diff -u -p -r1.26 unbound-control.c --- smallapp/unbound-control.c 5 Sep 2023 11:12:10 -0000 1.26 +++ smallapp/unbound-control.c 22 May 2024 10:33:17 -0000 @@ -150,12 +150,13 @@ usage(void) printf(" list_local_data list local-data RRs in use\n"); printf(" insecure_add zone add domain-insecure zone\n"); printf(" insecure_remove zone remove domain-insecure zone\n"); - printf(" forward_add [+i] zone addr.. add forward-zone with servers\n"); + printf(" forward_add [+it] zone addr.. add forward-zone with servers\n"); printf(" forward_remove [+i] zone remove forward zone\n"); - printf(" stub_add [+ip] zone addr.. add stub-zone with servers\n"); + printf(" stub_add [+ipt] zone addr.. add stub-zone with servers\n"); printf(" stub_remove [+i] zone remove stub zone\n"); printf(" +i also do dnssec insecure point\n"); printf(" +p set stub to use priming\n"); + printf(" +t set to use tls upstream\n"); printf(" forward [off | addr ...] without arg show forward setup\n"); printf(" or off to turn off root forwarding\n"); printf(" or give list of ip addresses\n"); Index: testcode/checklocks.c =================================================================== RCS file: /cvs/src/usr.sbin/unbound/testcode/checklocks.c,v diff -u -p -r1.1.1.2 checklocks.c --- testcode/checklocks.c 4 Dec 2018 12:04:14 -0000 1.1.1.2 +++ testcode/checklocks.c 22 May 2024 10:33:17 -0000 @@ -68,6 +68,17 @@ static struct thr_check* thread_infos[TH int check_locking_order = 1; /** the pid of this runset, reasonably unique. */ static pid_t check_lock_pid; +/** + * Should checklocks print a trace of the lock and unlock calls. + * It uses fprintf for that because the log function uses a lock and that + * would loop otherwise. + */ +static int verbose_locking = 0; +/** + * Assume lock 0 0 (create_thread, create_instance), is the log lock and + * do not print for that. Otherwise the output is full of log lock accesses. + */ +static int verbose_locking_not_loglock = 1; /** print all possible debug info on the state of the system */ static void total_debug_info(void); @@ -508,6 +519,9 @@ checklock_rdlock(enum check_lock_type ty if(key_deleted) return; + if(verbose_locking && !(verbose_locking_not_loglock && + lock->create_thread == 0 && lock->create_instance == 0)) + fprintf(stderr, "checklock_rdlock lock %d %d %s:%d at %s:%d\n", lock->create_thread, lock->create_instance, lock->create_file, lock->create_line, file, line); log_assert(type == check_lock_rwlock); checklock_lockit(type, lock, func, file, line, try_rd, timed_rd, &lock->u.rwlock, 0, 0); @@ -528,6 +542,9 @@ checklock_wrlock(enum check_lock_type ty if(key_deleted) return; log_assert(type == check_lock_rwlock); + if(verbose_locking && !(verbose_locking_not_loglock && + lock->create_thread == 0 && lock->create_instance == 0)) + fprintf(stderr, "checklock_wrlock lock %d %d %s:%d at %s:%d\n", lock->create_thread, lock->create_instance, lock->create_file, lock->create_line, file, line); checklock_lockit(type, lock, func, file, line, try_wr, timed_wr, &lock->u.rwlock, 0, 1); } @@ -565,6 +582,9 @@ checklock_lock(enum check_lock_type type if(key_deleted) return; log_assert(type != check_lock_rwlock); + if(verbose_locking && !(verbose_locking_not_loglock && + lock->create_thread == 0 && lock->create_instance == 0)) + fprintf(stderr, "checklock_lock lock %d %d %s:%d at %s:%d\n", lock->create_thread, lock->create_instance, lock->create_file, lock->create_line, file, line); switch(type) { case check_lock_mutex: checklock_lockit(type, lock, func, file, line, @@ -601,6 +621,10 @@ checklock_unlock(enum check_lock_type ty } if(lock->hold_count <= 0) lock_error(lock, func, file, line, "too many unlocks"); + + if(verbose_locking && !(verbose_locking_not_loglock && + lock->create_thread == 0 && lock->create_instance == 0)) + fprintf(stderr, "checklock_unlock lock %d %d %s:%d at %s:%d\n", lock->create_thread, lock->create_instance, lock->create_file, lock->create_line, file, line); /* store this point as last touched by */ lock->holder = thr; Index: testcode/fake_event.c =================================================================== RCS file: /cvs/src/usr.sbin/unbound/testcode/fake_event.c,v diff -u -p -r1.1.1.14 fake_event.c --- testcode/fake_event.c 12 Apr 2024 15:44:27 -0000 1.1.1.14 +++ testcode/fake_event.c 22 May 2024 10:33:17 -0000 @@ -52,6 +52,7 @@ #include "util/data/msgreply.h" #include "util/data/msgencode.h" #include "util/data/dname.h" +#include "util/storage/slabhash.h" #include "util/edns.h" #include "util/config_file.h" #include "services/listen_dnsport.h" @@ -65,6 +66,7 @@ #include "sldns/wire2str.h" #include "sldns/str2wire.h" #include "daemon/remote.h" +#include "daemon/daemon.h" #include "util/timeval_func.h" #include struct worker; @@ -154,6 +156,8 @@ repevt_string(enum replay_event_type t) case repevt_assign: return "ASSIGN"; case repevt_traffic: return "TRAFFIC"; case repevt_infra_rtt: return "INFRA_RTT"; + case repevt_flush_message: return "FLUSH_MESSAGE"; + case repevt_expire_message: return "EXPIRE_MESSAGE"; default: return "UNKNOWN"; } } @@ -691,6 +695,66 @@ do_infra_rtt(struct replay_runtime* runt free(dp); } +/** Flush message from message cache. */ +static void +do_flush_message(struct replay_runtime* runtime) +{ + struct replay_moment* now = runtime->now; + uint8_t rr[1024]; + size_t rr_len = sizeof(rr), dname_len = 0; + hashvalue_type h; + struct query_info k; + + if(sldns_str2wire_rr_question_buf(now->string, rr, &rr_len, + &dname_len, NULL, 0, NULL, 0) != 0) + fatal_exit("could not parse '%s'", now->string); + + log_info("remove message %s", now->string); + k.qname = rr; + k.qname_len = dname_len; + k.qtype = sldns_wirerr_get_type(rr, rr_len, dname_len); + k.qclass = sldns_wirerr_get_class(rr, rr_len, dname_len); + k.local_alias = NULL; + h = query_info_hash(&k, 0); + slabhash_remove(runtime->daemon->env->msg_cache, h, &k); +} + +/** Expire message from message cache. */ +static void +do_expire_message(struct replay_runtime* runtime) +{ + struct replay_moment* now = runtime->now; + uint8_t rr[1024]; + size_t rr_len = sizeof(rr), dname_len = 0; + hashvalue_type h; + struct query_info k; + struct lruhash_entry* e; + + if(sldns_str2wire_rr_question_buf(now->string, rr, &rr_len, + &dname_len, NULL, 0, NULL, 0) != 0) + fatal_exit("could not parse '%s'", now->string); + + log_info("expire message %s", now->string); + k.qname = rr; + k.qname_len = dname_len; + k.qtype = sldns_wirerr_get_type(rr, rr_len, dname_len); + k.qclass = sldns_wirerr_get_class(rr, rr_len, dname_len); + k.local_alias = NULL; + h = query_info_hash(&k, 0); + + e = slabhash_lookup(runtime->daemon->env->msg_cache, h, &k, 0); + if(e) { + struct msgreply_entry* msg = (struct msgreply_entry*)e->key; + struct reply_info* rep = (struct reply_info*)msg->entry.data; + time_t expired = runtime->now_secs; + expired -= 3; + rep->ttl = expired; + rep->prefetch_ttl = expired; + rep->serve_expired_ttl = expired; + lock_rw_unlock(&msg->entry.lock); + } +} + /** perform exponential backoff on the timeout */ static void expon_timeout_backoff(struct replay_runtime* runtime) @@ -794,6 +858,14 @@ do_moment_and_advance(struct replay_runt break; case repevt_infra_rtt: do_infra_rtt(runtime); + advance_moment(runtime); + break; + case repevt_flush_message: + do_flush_message(runtime); + advance_moment(runtime); + break; + case repevt_expire_message: + do_expire_message(runtime); advance_moment(runtime); break; default: Index: testcode/replay.c =================================================================== RCS file: /cvs/src/usr.sbin/unbound/testcode/replay.c,v diff -u -p -r1.1.1.6 replay.c --- testcode/replay.c 5 Sep 2023 11:07:49 -0000 1.1.1.6 +++ testcode/replay.c 22 May 2024 10:33:17 -0000 @@ -348,6 +348,20 @@ replay_moment_read(char* remain, FILE* i mom->string = strdup(m); if(!mom->string) fatal_exit("out of memory"); if(!mom->variable) fatal_exit("out of memory"); + } else if(parse_keyword(&remain, "FLUSH_MESSAGE")) { + mom->evt_type = repevt_flush_message; + while(isspace((unsigned char)*remain)) + remain++; + strip_end_white(remain); + mom->string = strdup(remain); + if(!mom->string) fatal_exit("out of memory"); + } else if(parse_keyword(&remain, "EXPIRE_MESSAGE")) { + mom->evt_type = repevt_expire_message; + while(isspace((unsigned char)*remain)) + remain++; + strip_end_white(remain); + mom->string = strdup(remain); + if(!mom->string) fatal_exit("out of memory"); } else { log_err("%d: unknown event type %s", pstate->lineno, remain); free(mom); Index: testcode/replay.h =================================================================== RCS file: /cvs/src/usr.sbin/unbound/testcode/replay.h,v diff -u -p -r1.1.1.4 replay.h --- testcode/replay.h 13 Aug 2021 19:55:25 -0000 1.1.1.4 +++ testcode/replay.h 22 May 2024 10:33:17 -0000 @@ -85,6 +85,8 @@ * The file contents is macro expanded before match. * o CHECK_TEMPFILE [fname] - followed by FILE_BEGIN [to match] FILE_END * o INFRA_RTT [ip] [dp] [rtt] - update infra cache entry with rtt. + * o FLUSH_MESSAGE name type class - flushes entry in message cache. + * o EXPIRE_MESSAGE name type class - expires entry in message cache. * o ERROR * ; following entry starts on the next line, ENTRY_BEGIN. * ; more STEP items @@ -148,6 +150,7 @@ struct fake_timer; struct replay_var; struct infra_cache; struct sldns_buffer; +struct daemon; /** * A replay scenario. @@ -212,6 +215,10 @@ struct replay_moment { repevt_assign, /** store infra rtt cache entry: addr and string (int) */ repevt_infra_rtt, + /** flush message cache entry */ + repevt_flush_message, + /** expire message cache entry */ + repevt_expire_message, /** cause traffic to flow */ repevt_traffic } @@ -297,6 +304,8 @@ struct replay_runtime { /** ref the infra cache (was passed to outside_network_create) */ struct infra_cache* infra; + /** the daemon structure passed in worker call to remote accept open */ + struct daemon* daemon; /** the current time in seconds */ time_t now_secs; Index: testcode/testbound.c =================================================================== RCS file: /cvs/src/usr.sbin/unbound/testcode/testbound.c,v diff -u -p -r1.1.1.9 testbound.c --- testcode/testbound.c 23 Feb 2022 11:57:27 -0000 1.1.1.9 +++ testcode/testbound.c 22 May 2024 10:33:17 -0000 @@ -48,6 +48,7 @@ #include "testcode/fake_event.h" #include "daemon/remote.h" #include "libunbound/worker.h" +#include "daemon/worker.h" #include "util/config_file.h" #include "sldns/keyraw.h" #ifdef UB_ON_WINDOWS @@ -532,9 +533,10 @@ void daemon_remote_clear(struct daemon_r } int daemon_remote_open_accept(struct daemon_remote* ATTR_UNUSED(rc), - struct listen_port* ATTR_UNUSED(ports), - struct worker* ATTR_UNUSED(worker)) + struct listen_port* ATTR_UNUSED(ports), struct worker* worker) { + struct replay_runtime* runtime = (struct replay_runtime*)worker->base; + runtime->daemon = worker->daemon; return 1; } Index: util/config_file.c =================================================================== RCS file: /cvs/src/usr.sbin/unbound/util/config_file.c,v diff -u -p -r1.34 config_file.c --- util/config_file.c 13 Apr 2024 12:24:57 -0000 1.34 +++ util/config_file.c 22 May 2024 10:33:17 -0000 @@ -174,6 +174,7 @@ config_create(void) cfg->min_ttl = 0; cfg->max_ttl = 3600 * 24; cfg->max_negative_ttl = 3600; + cfg->min_negative_ttl = 0; cfg->prefetch = 0; cfg->prefetch_key = 0; cfg->deny_any = 0; @@ -308,6 +309,11 @@ config_create(void) cfg->minimal_responses = 1; cfg->rrset_roundrobin = 1; cfg->unknown_server_time_limit = 376; + cfg->discard_timeout = 1900; /* msec */ + cfg->wait_limit = 1000; + cfg->wait_limit_cookie = 10000; + cfg->wait_limit_netblock = NULL; + cfg->wait_limit_cookie_netblock = NULL; cfg->max_udp_size = 1232; /* value taken from edns_buffer_size */ if(!(cfg->server_key_file = strdup(RUN_DIR"/unbound_server.key"))) goto error_exit; @@ -384,6 +390,7 @@ config_create(void) if(!(cfg->cachedb_backend = strdup("testframe"))) goto error_exit; if(!(cfg->cachedb_secret = strdup("default"))) goto error_exit; cfg->cachedb_no_store = 0; + cfg->cachedb_check_when_serve_expired = 1; #ifdef USE_REDIS if(!(cfg->redis_server_host = strdup("127.0.0.1"))) goto error_exit; cfg->redis_server_path = NULL; @@ -615,6 +622,8 @@ int config_set_option(struct config_file { IS_NUMBER_OR_ZERO; cfg->max_ttl = atoi(val); MAX_TTL=(time_t)cfg->max_ttl;} else if(strcmp(opt, "cache-max-negative-ttl:") == 0) { IS_NUMBER_OR_ZERO; cfg->max_negative_ttl = atoi(val); MAX_NEG_TTL=(time_t)cfg->max_negative_ttl;} + else if(strcmp(opt, "cache-min-negative-ttl:") == 0) + { IS_NUMBER_OR_ZERO; cfg->min_negative_ttl = atoi(val); MIN_NEG_TTL=(time_t)cfg->min_negative_ttl;} else if(strcmp(opt, "cache-min-ttl:") == 0) { IS_NUMBER_OR_ZERO; cfg->min_ttl = atoi(val); MIN_TTL=(time_t)cfg->min_ttl;} else if(strcmp(opt, "infra-cache-min-rtt:") == 0) { @@ -722,6 +731,9 @@ int config_set_option(struct config_file else S_YNO("minimal-responses:", minimal_responses) else S_YNO("rrset-roundrobin:", rrset_roundrobin) else S_NUMBER_OR_ZERO("unknown-server-time-limit:", unknown_server_time_limit) + else S_NUMBER_OR_ZERO("discard-timeout:", discard_timeout) + else S_NUMBER_OR_ZERO("wait-limit:", wait_limit) + else S_NUMBER_OR_ZERO("wait-limit-cookie:", wait_limit_cookie) else S_STRLIST("local-data:", local_data) else S_YNO("unblock-lan-zones:", unblock_lan_zones) else S_YNO("insecure-lan-zones:", insecure_lan_zones) @@ -827,6 +839,7 @@ int config_set_option(struct config_file #endif #ifdef USE_CACHEDB else S_YNO("cachedb-no-store:", cachedb_no_store) + else S_YNO("cachedb-check-when-serve-expired:", cachedb_check_when_serve_expired) #endif /* USE_CACHEDB */ else if(strcmp(opt, "define-tag:") ==0) { return config_add_tag(cfg, val); @@ -1065,6 +1078,7 @@ config_get_option(struct config_file* cf else O_YNO(opt, "deny-any", deny_any) else O_DEC(opt, "cache-max-ttl", max_ttl) else O_DEC(opt, "cache-max-negative-ttl", max_negative_ttl) + else O_DEC(opt, "cache-min-negative-ttl", min_negative_ttl) else O_DEC(opt, "cache-min-ttl", min_ttl) else O_DEC(opt, "infra-host-ttl", host_ttl) else O_DEC(opt, "infra-cache-slabs", infra_cache_slabs) @@ -1201,6 +1215,11 @@ config_get_option(struct config_file* cf else O_YNO(opt, "minimal-responses", minimal_responses) else O_YNO(opt, "rrset-roundrobin", rrset_roundrobin) else O_DEC(opt, "unknown-server-time-limit", unknown_server_time_limit) + else O_DEC(opt, "discard-timeout", discard_timeout) + else O_DEC(opt, "wait-limit", wait_limit) + else O_DEC(opt, "wait-limit-cookie", wait_limit_cookie) + else O_LS2(opt, "wait-limit-netblock", wait_limit_netblock) + else O_LS2(opt, "wait-limit-cookie-netblock", wait_limit_cookie_netblock) #ifdef CLIENT_SUBNET else O_LST(opt, "send-client-subnet", client_subnet) else O_LST(opt, "client-subnet-zone", client_subnet_zone) @@ -1318,6 +1337,7 @@ config_get_option(struct config_file* cf else O_STR(opt, "backend", cachedb_backend) else O_STR(opt, "secret-seed", cachedb_secret) else O_YNO(opt, "cachedb-no-store", cachedb_no_store) + else O_YNO(opt, "cachedb-check-when-serve-expired", cachedb_check_when_serve_expired) #ifdef USE_REDIS else O_STR(opt, "redis-server-host", redis_server_host) else O_DEC(opt, "redis-server-port", redis_server_port) @@ -1671,6 +1691,8 @@ config_delete(struct config_file* cfg) config_deltrplstrlist(cfg->interface_tag_actions); config_deltrplstrlist(cfg->interface_tag_datas); config_delstrlist(cfg->control_ifs.first); + config_deldblstrlist(cfg->wait_limit_netblock); + config_deldblstrlist(cfg->wait_limit_cookie_netblock); free(cfg->server_key_file); free(cfg->server_cert_file); free(cfg->control_key_file); @@ -1761,6 +1783,10 @@ cfg_mark_ports(const char* str, int allo #endif if(!mid) { int port = atoi(str); + if(port < 0) { + log_err("port number is negative: %d", port); + return 0; + } if(port == 0 && strcmp(str, "0") != 0) { log_err("cannot parse port number '%s'", str); return 0; @@ -1770,6 +1796,10 @@ cfg_mark_ports(const char* str, int allo } else { int i, low, high = atoi(mid+1); char buf[16]; + if(high < 0) { + log_err("port number is negative: %d", high); + return 0; + } if(high == 0 && strcmp(mid+1, "0") != 0) { log_err("cannot parse port number '%s'", mid+1); return 0; @@ -1782,10 +1812,18 @@ cfg_mark_ports(const char* str, int allo memcpy(buf, str, (size_t)(mid-str)); buf[mid-str] = 0; low = atoi(buf); + if(low < 0) { + log_err("port number is negative: %d", low); + return 0; + } if(low == 0 && strcmp(buf, "0") != 0) { log_err("cannot parse port number '%s'", buf); return 0; } + if(high > num) { + /* Stop very high values from taking a long time. */ + high = num; + } for(i=low; i<=high; i++) { if(i < num) avail[i] = (allow?i:0); @@ -2310,6 +2348,7 @@ config_apply(struct config_file* config) SERVE_EXPIRED_REPLY_TTL = (time_t)config->serve_expired_reply_ttl; SERVE_ORIGINAL_TTL = config->serve_original_ttl; MAX_NEG_TTL = (time_t)config->max_negative_ttl; + MIN_NEG_TTL = (time_t)config->min_negative_ttl; RTT_MIN_TIMEOUT = config->infra_cache_min_rtt; RTT_MAX_TIMEOUT = config->infra_cache_max_rtt; EDNS_ADVERTISED_SIZE = (uint16_t)config->edns_buffer_size; Index: util/config_file.h =================================================================== RCS file: /cvs/src/usr.sbin/unbound/util/config_file.h,v diff -u -p -r1.32 config_file.h --- util/config_file.h 13 Apr 2024 12:24:57 -0000 1.32 +++ util/config_file.h 22 May 2024 10:33:17 -0000 @@ -315,6 +315,8 @@ struct config_file { int min_ttl; /** the number of seconds maximal negative TTL for SOA in auth */ int max_negative_ttl; + /** the number of seconds minimal negative TTL for SOA in auth */ + int min_negative_ttl; /** if prefetching of messages should be performed. */ int prefetch; /** if prefetching of DNSKEYs should be performed. */ @@ -535,6 +537,21 @@ struct config_file { /* wait time for unknown server in msec */ int unknown_server_time_limit; + /** Wait time to drop recursion replies */ + int discard_timeout; + + /** Wait limit for number of replies per IP address */ + int wait_limit; + + /** Wait limit for number of replies per IP address with cookie */ + int wait_limit_cookie; + + /** wait limit per netblock */ + struct config_str2list* wait_limit_netblock; + + /** wait limit with cookie per netblock */ + struct config_str2list* wait_limit_cookie_netblock; + /* maximum UDP response size */ size_t max_udp_size; @@ -705,6 +722,8 @@ struct config_file { char* cachedb_secret; /** cachedb that does not store, but only reads from database, if on */ int cachedb_no_store; + /** cachedb check before serving serve-expired response */ + int cachedb_check_when_serve_expired; #ifdef USE_REDIS /** redis server's IP address or host name */ char* redis_server_host; Index: util/configlexer.lex =================================================================== RCS file: /cvs/src/usr.sbin/unbound/util/configlexer.lex,v diff -u -p -r1.29 configlexer.lex --- util/configlexer.lex 13 Apr 2024 12:24:57 -0000 1.29 +++ util/configlexer.lex 22 May 2024 10:33:17 -0000 @@ -297,6 +297,7 @@ rrset-cache-size{COLON} { YDVAR(1, VAR_ rrset-cache-slabs{COLON} { YDVAR(1, VAR_RRSET_CACHE_SLABS) } cache-max-ttl{COLON} { YDVAR(1, VAR_CACHE_MAX_TTL) } cache-max-negative-ttl{COLON} { YDVAR(1, VAR_CACHE_MAX_NEGATIVE_TTL) } +cache-min-negative-ttl{COLON} { YDVAR(1, VAR_CACHE_MIN_NEGATIVE_TTL) } cache-min-ttl{COLON} { YDVAR(1, VAR_CACHE_MIN_TTL) } infra-host-ttl{COLON} { YDVAR(1, VAR_INFRA_HOST_TTL) } infra-lame-ttl{COLON} { YDVAR(1, VAR_INFRA_LAME_TTL) } @@ -463,6 +464,11 @@ domain-insecure{COLON} { YDVAR(1, VAR_D minimal-responses{COLON} { YDVAR(1, VAR_MINIMAL_RESPONSES) } rrset-roundrobin{COLON} { YDVAR(1, VAR_RRSET_ROUNDROBIN) } unknown-server-time-limit{COLON} { YDVAR(1, VAR_UNKNOWN_SERVER_TIME_LIMIT) } +discard-timeout{COLON} { YDVAR(1, VAR_DISCARD_TIMEOUT) } +wait-limit{COLON} { YDVAR(1, VAR_WAIT_LIMIT) } +wait-limit-cookie{COLON} { YDVAR(1, VAR_WAIT_LIMIT_COOKIE) } +wait-limit-netblock{COLON} { YDVAR(1, VAR_WAIT_LIMIT_NETBLOCK) } +wait-limit-cookie-netblock{COLON} { YDVAR(1, VAR_WAIT_LIMIT_COOKIE_NETBLOCK) } max-udp-size{COLON} { YDVAR(1, VAR_MAX_UDP_SIZE) } dns64-prefix{COLON} { YDVAR(1, VAR_DNS64_PREFIX) } dns64-synthall{COLON} { YDVAR(1, VAR_DNS64_SYNTHALL) } @@ -560,6 +566,7 @@ cachedb{COLON} { YDVAR(0, VAR_CACHEDB) backend{COLON} { YDVAR(1, VAR_CACHEDB_BACKEND) } secret-seed{COLON} { YDVAR(1, VAR_CACHEDB_SECRETSEED) } cachedb-no-store{COLON} { YDVAR(1, VAR_CACHEDB_NO_STORE) } +cachedb-check-when-serve-expired{COLON} { YDVAR(1, VAR_CACHEDB_CHECK_WHEN_SERVE_EXPIRED) } redis-server-host{COLON} { YDVAR(1, VAR_CACHEDB_REDISHOST) } redis-server-port{COLON} { YDVAR(1, VAR_CACHEDB_REDISPORT) } redis-server-path{COLON} { YDVAR(1, VAR_CACHEDB_REDISPATH) } Index: util/configparser.y =================================================================== RCS file: /cvs/src/usr.sbin/unbound/util/configparser.y,v diff -u -p -r1.29 configparser.y --- util/configparser.y 13 Apr 2024 12:24:57 -0000 1.29 +++ util/configparser.y 22 May 2024 10:33:17 -0000 @@ -153,6 +153,7 @@ extern struct config_parser_state* cfg_p %token VAR_MIN_CLIENT_SUBNET_IPV4 VAR_MIN_CLIENT_SUBNET_IPV6 %token VAR_MAX_ECS_TREE_SIZE_IPV4 VAR_MAX_ECS_TREE_SIZE_IPV6 %token VAR_CAPS_WHITELIST VAR_CACHE_MAX_NEGATIVE_TTL VAR_PERMIT_SMALL_HOLDDOWN +%token VAR_CACHE_MIN_NEGATIVE_TTL %token VAR_QNAME_MINIMISATION VAR_QNAME_MINIMISATION_STRICT VAR_IP_FREEBIND %token VAR_DEFINE_TAG VAR_LOCAL_ZONE_TAG VAR_ACCESS_CONTROL_TAG %token VAR_LOCAL_ZONE_OVERRIDE VAR_ACCESS_CONTROL_TAG_ACTION @@ -188,6 +189,8 @@ extern struct config_parser_state* cfg_p %token VAR_ANSWER_COOKIE VAR_COOKIE_SECRET VAR_IP_RATELIMIT_COOKIE %token VAR_FORWARD_NO_CACHE VAR_STUB_NO_CACHE VAR_LOG_SERVFAIL VAR_DENY_ANY %token VAR_UNKNOWN_SERVER_TIME_LIMIT VAR_LOG_TAG_QUERYREPLY +%token VAR_DISCARD_TIMEOUT VAR_WAIT_LIMIT VAR_WAIT_LIMIT_COOKIE +%token VAR_WAIT_LIMIT_NETBLOCK VAR_WAIT_LIMIT_COOKIE_NETBLOCK %token VAR_STREAM_WAIT_SIZE VAR_TLS_CIPHERS VAR_TLS_CIPHERSUITES VAR_TLS_USE_SNI %token VAR_IPSET VAR_IPSET_NAME_V4 VAR_IPSET_NAME_V6 %token VAR_TLS_SESSION_TICKET_KEYS VAR_RPZ VAR_TAGS VAR_RPZ_ACTION_OVERRIDE @@ -200,7 +203,7 @@ extern struct config_parser_state* cfg_p %token VAR_INTERFACE_TAG_ACTION VAR_INTERFACE_TAG_DATA %token VAR_PROXY_PROTOCOL_PORT VAR_STATISTICS_INHIBIT_ZERO %token VAR_HARDEN_UNKNOWN_ADDITIONAL VAR_DISABLE_EDNS_DO VAR_CACHEDB_NO_STORE -%token VAR_LOG_DESTADDR +%token VAR_LOG_DESTADDR VAR_CACHEDB_CHECK_WHEN_SERVE_EXPIRED %% toplevelvars: /* empty */ | toplevelvars toplevelvar ; @@ -298,6 +301,7 @@ content_server: server_num_threads | ser server_min_client_subnet_ipv4 | server_min_client_subnet_ipv6 | server_max_ecs_tree_size_ipv4 | server_max_ecs_tree_size_ipv6 | server_caps_whitelist | server_cache_max_negative_ttl | + server_cache_min_negative_ttl | server_permit_small_holddown | server_qname_minimisation | server_ip_freebind | server_define_tag | server_local_zone_tag | server_disable_dnssec_lame_check | server_access_control_tag | @@ -325,6 +329,8 @@ content_server: server_num_threads | ser server_fast_server_permil | server_fast_server_num | server_tls_win_cert | server_tcp_connection_limit | server_log_servfail | server_deny_any | server_unknown_server_time_limit | server_log_tag_queryreply | + server_discard_timeout | server_wait_limit | server_wait_limit_cookie | + server_wait_limit_netblock | server_wait_limit_cookie_netblock | server_stream_wait_size | server_tls_ciphers | server_tls_ciphersuites | server_tls_session_ticket_keys | server_answer_cookie | server_cookie_secret | server_ip_ratelimit_cookie | @@ -2014,6 +2020,15 @@ server_cache_max_negative_ttl: VAR_CACHE free($2); } ; +server_cache_min_negative_ttl: VAR_CACHE_MIN_NEGATIVE_TTL STRING_ARG + { + OUTYY(("P(server_cache_min_negative_ttl:%s)\n", $2)); + if(atoi($2) == 0 && strcmp($2, "0") != 0) + yyerror("number expected"); + else cfg_parser->cfg->min_negative_ttl = atoi($2); + free($2); + } + ; server_cache_min_ttl: VAR_CACHE_MIN_TTL STRING_ARG { OUTYY(("P(server_cache_min_ttl:%s)\n", $2)); @@ -2366,6 +2381,57 @@ server_unknown_server_time_limit: VAR_UN free($2); } ; +server_discard_timeout: VAR_DISCARD_TIMEOUT STRING_ARG + { + OUTYY(("P(server_discard_timeout:%s)\n", $2)); + cfg_parser->cfg->discard_timeout = atoi($2); + free($2); + } + ; +server_wait_limit: VAR_WAIT_LIMIT STRING_ARG + { + OUTYY(("P(server_wait_limit:%s)\n", $2)); + cfg_parser->cfg->wait_limit = atoi($2); + free($2); + } + ; +server_wait_limit_cookie: VAR_WAIT_LIMIT_COOKIE STRING_ARG + { + OUTYY(("P(server_wait_limit_cookie:%s)\n", $2)); + cfg_parser->cfg->wait_limit_cookie = atoi($2); + free($2); + } + ; +server_wait_limit_netblock: VAR_WAIT_LIMIT_NETBLOCK STRING_ARG STRING_ARG + { + OUTYY(("P(server_wait_limit_netblock:%s %s)\n", $2, $3)); + if(atoi($3) == 0 && strcmp($3, "0") != 0) { + yyerror("number expected"); + free($2); + free($3); + } else { + if(!cfg_str2list_insert(&cfg_parser->cfg-> + wait_limit_netblock, $2, $3)) + fatal_exit("out of memory adding " + "wait-limit-netblock"); + } + } + ; +server_wait_limit_cookie_netblock: VAR_WAIT_LIMIT_COOKIE_NETBLOCK STRING_ARG STRING_ARG + { + OUTYY(("P(server_wait_limit_cookie_netblock:%s %s)\n", $2, $3)); + if(atoi($3) == 0 && strcmp($3, "0") != 0) { + yyerror("number expected"); + free($2); + free($3); + } else { + if(!cfg_str2list_insert(&cfg_parser->cfg-> + wait_limit_cookie_netblock, $2, $3)) + fatal_exit("out of memory adding " + "wait-limit-cookie-netblock"); + } + } + ; server_max_udp_size: VAR_MAX_UDP_SIZE STRING_ARG { OUTYY(("P(server_max_udp_size:%s)\n", $2)); @@ -3723,7 +3789,7 @@ contents_cachedb: contents_cachedb conte content_cachedb: cachedb_backend_name | cachedb_secret_seed | redis_server_host | redis_server_port | redis_timeout | redis_expire_records | redis_server_path | redis_server_password | - cachedb_no_store | redis_logical_db + cachedb_no_store | redis_logical_db | cachedb_check_when_serve_expired ; cachedb_backend_name: VAR_CACHEDB_BACKEND STRING_ARG { @@ -3756,6 +3822,19 @@ cachedb_no_store: VAR_CACHEDB_NO_STORE S if(strcmp($2, "yes") != 0 && strcmp($2, "no") != 0) yyerror("expected yes or no."); else cfg_parser->cfg->cachedb_no_store = (strcmp($2, "yes")==0); + #else + OUTYY(("P(Compiled without cachedb, ignoring)\n")); + #endif + free($2); + } + ; +cachedb_check_when_serve_expired: VAR_CACHEDB_CHECK_WHEN_SERVE_EXPIRED STRING_ARG + { + #ifdef USE_CACHEDB + OUTYY(("P(cachedb_check_when_serve_expired:%s)\n", $2)); + if(strcmp($2, "yes") != 0 && strcmp($2, "no") != 0) + yyerror("expected yes or no."); + else cfg_parser->cfg->cachedb_check_when_serve_expired = (strcmp($2, "yes")==0); #else OUTYY(("P(Compiled without cachedb, ignoring)\n")); #endif Index: util/module.c =================================================================== RCS file: /cvs/src/usr.sbin/unbound/util/module.c,v diff -u -p -r1.9 module.c --- util/module.c 12 Apr 2024 15:45:24 -0000 1.9 +++ util/module.c 22 May 2024 10:33:17 -0000 @@ -129,7 +129,7 @@ void errinf_origin(struct module_qstate* } } -char* errinf_to_str_bogus(struct module_qstate* qstate) +char* errinf_to_str_bogus(struct module_qstate* qstate, struct regional* region) { char buf[20480]; char* p = buf; @@ -148,7 +148,10 @@ char* errinf_to_str_bogus(struct module_ snprintf(p, left, " %s", s->str); left -= strlen(p); p += strlen(p); } - p = strdup(buf); + if(region) + p = regional_strdup(region, buf); + else + p = strdup(buf); if(!p) log_err("malloc failure in errinf_to_str"); return p; @@ -188,7 +191,7 @@ char* errinf_to_str_servfail(struct modu snprintf(p, left, " %s", s->str); left -= strlen(p); p += strlen(p); } - p = strdup(buf); + p = regional_strdup(qstate->region, buf); if(!p) log_err("malloc failure in errinf_to_str"); return p; @@ -206,7 +209,7 @@ char* errinf_to_str_misc(struct module_q snprintf(p, left, "%s%s", (s==qstate->errinf?"":" "), s->str); left -= strlen(p); p += strlen(p); } - p = strdup(buf); + p = regional_strdup(qstate->region, buf); if(!p) log_err("malloc failure in errinf_to_str"); return p; Index: util/module.h =================================================================== RCS file: /cvs/src/usr.sbin/unbound/util/module.h,v diff -u -p -r1.17 module.h --- util/module.h 12 Apr 2024 15:45:24 -0000 1.17 +++ util/module.h 22 May 2024 10:33:17 -0000 @@ -180,6 +180,7 @@ struct iter_hints; struct respip_set; struct respip_client_info; struct respip_addr_info; +struct module_stack; /** Maximum number of modules in operation */ #define MAX_MODULE 16 @@ -511,10 +512,10 @@ struct module_env { /** auth zones */ struct auth_zones* auth_zones; /** Mapping of forwarding zones to targets. - * iterator forwarder information. per-thread, created by worker */ + * iterator forwarder information. */ struct iter_forwards* fwds; /** - * iterator forwarder information. per-thread, created by worker. + * iterator stub information. * The hints -- these aren't stored in the cache because they don't * expire. The hints are always used to "prime" the cache. Note * that both root hints and stub zone "hints" are stored in this @@ -537,6 +538,12 @@ struct module_env { /** EDNS client string information */ struct edns_strings* edns_strings; + /** module stack */ + struct module_stack* modstack; +#ifdef USE_CACHEDB + /** the cachedb enabled value, copied and stored here. */ + int cachedb_enabled; +#endif /* Make every mesh state unique, do not aggregate mesh states. */ int unique_mesh; }; @@ -824,10 +831,11 @@ void errinf_dname(struct module_qstate* /** * Create error info in string. For validation failures. * @param qstate: query state. + * @param region: the region for the result or NULL for malloced result. * @return string or NULL on malloc failure (already logged). - * This string is malloced and has to be freed by caller. + * This string is malloced if region is NULL and has to be freed by caller. */ -char* errinf_to_str_bogus(struct module_qstate* qstate); +char* errinf_to_str_bogus(struct module_qstate* qstate, struct regional* region); /** * Check the sldns_ede_code of the qstate->errinf. @@ -840,7 +848,6 @@ sldns_ede_code errinf_to_reason_bogus(st * Create error info in string. For other servfails. * @param qstate: query state. * @return string or NULL on malloc failure (already logged). - * This string is malloced and has to be freed by caller. */ char* errinf_to_str_servfail(struct module_qstate* qstate); @@ -848,7 +855,6 @@ char* errinf_to_str_servfail(struct modu * Create error info in string. For misc failures that are not servfail. * @param qstate: query state. * @return string or NULL on malloc failure (already logged). - * This string is malloced and has to be freed by caller. */ char* errinf_to_str_misc(struct module_qstate* qstate); Index: util/net_help.c =================================================================== RCS file: /cvs/src/usr.sbin/unbound/util/net_help.c,v diff -u -p -r1.31 net_help.c --- util/net_help.c 12 Apr 2024 15:45:24 -0000 1.31 +++ util/net_help.c 22 May 2024 10:33:17 -0000 @@ -77,6 +77,8 @@ /** max length of an IP address (the address portion) that we allow */ #define MAX_ADDR_STRLEN 128 /* characters */ +/** max length of a hostname (with port and tls name) that we allow */ +#define MAX_HOST_STRLEN (LDNS_MAX_DOMAINLEN * 3) /* characters */ /** default value for EDNS ADVERTISED size */ uint16_t EDNS_ADVERTISED_SIZE = 4096; @@ -486,28 +488,38 @@ uint8_t* authextstrtodname(char* str, in *port = UNBOUND_DNS_PORT; *auth_name = NULL; if((s=strchr(str, '@'))) { + char buf[MAX_HOST_STRLEN]; + size_t len = (size_t)(s-str); char* hash = strchr(s+1, '#'); if(hash) { *auth_name = hash+1; } else { *auth_name = NULL; } + if(len >= MAX_HOST_STRLEN) { + return NULL; + } + (void)strlcpy(buf, str, sizeof(buf)); + buf[len] = 0; *port = atoi(s+1); if(*port == 0) { if(!hash && strcmp(s+1,"0")!=0) - return 0; + return NULL; if(hash && strncmp(s+1,"0#",2)!=0) - return 0; + return NULL; } - *s = 0; - dname = sldns_str2wire_dname(str, &dname_len); - *s = '@'; + dname = sldns_str2wire_dname(buf, &dname_len); } else if((s=strchr(str, '#'))) { + char buf[MAX_HOST_STRLEN]; + size_t len = (size_t)(s-str); + if(len >= MAX_HOST_STRLEN) { + return NULL; + } + (void)strlcpy(buf, str, sizeof(buf)); + buf[len] = 0; *port = UNBOUND_DNS_OVER_TLS_PORT; *auth_name = s+1; - *s = 0; - dname = sldns_str2wire_dname(str, &dname_len); - *s = '#'; + dname = sldns_str2wire_dname(buf, &dname_len); } else { dname = sldns_str2wire_dname(str, &dname_len); } @@ -1026,11 +1038,11 @@ static void log_crypto_err_io_code_arg(c } else { if(print_errno) { if(errno == 0) - log_err("str: syscall error with errno %s", - strerror(errno)); - else log_err("str: %s", strerror(errno)); + log_err("%s: syscall error with errno %s", + str, strerror(errno)); + else log_err("%s: %s", str, strerror(errno)); } else { - log_err("str: %s", inf); + log_err("%s: %s", str, inf); } } } Index: util/netevent.c =================================================================== RCS file: /cvs/src/usr.sbin/unbound/util/netevent.c,v diff -u -p -r1.37 netevent.c --- util/netevent.c 13 Apr 2024 12:24:57 -0000 1.37 +++ util/netevent.c 22 May 2024 10:33:17 -0000 @@ -4772,9 +4772,9 @@ comm_point_send_reply(struct comm_reply * sending src (client)/dst (local service) addresses over DNSTAP from udp callback */ if(repinfo->c->dtenv != NULL && repinfo->c->dtenv->log_client_response_messages) { - log_addr(VERB_ALGO, "from local addr", (void*)repinfo->c->socket->addr->ai_addr, repinfo->c->socket->addr->ai_addrlen); + log_addr(VERB_ALGO, "from local addr", (void*)repinfo->c->socket->addr, repinfo->c->socket->addrlen); log_addr(VERB_ALGO, "response to client", &repinfo->client_addr, repinfo->client_addrlen); - dt_msg_send_client_response(repinfo->c->dtenv, &repinfo->client_addr, (void*)repinfo->c->socket->addr->ai_addr, repinfo->c->type, repinfo->c->ssl, repinfo->c->buffer); + dt_msg_send_client_response(repinfo->c->dtenv, &repinfo->client_addr, (void*)repinfo->c->socket->addr, repinfo->c->type, repinfo->c->ssl, repinfo->c->buffer); } #endif } else { @@ -4783,9 +4783,9 @@ comm_point_send_reply(struct comm_reply * sending src (client)/dst (local service) addresses over DNSTAP from TCP callback */ if(repinfo->c->tcp_parent->dtenv != NULL && repinfo->c->tcp_parent->dtenv->log_client_response_messages) { - log_addr(VERB_ALGO, "from local addr", (void*)repinfo->c->socket->addr->ai_addr, repinfo->c->socket->addr->ai_addrlen); + log_addr(VERB_ALGO, "from local addr", (void*)repinfo->c->socket->addr, repinfo->c->socket->addrlen); log_addr(VERB_ALGO, "response to client", &repinfo->client_addr, repinfo->client_addrlen); - dt_msg_send_client_response(repinfo->c->tcp_parent->dtenv, &repinfo->client_addr, (void*)repinfo->c->socket->addr->ai_addr, repinfo->c->type, repinfo->c->ssl, + dt_msg_send_client_response(repinfo->c->tcp_parent->dtenv, &repinfo->client_addr, (void*)repinfo->c->socket->addr, repinfo->c->type, repinfo->c->ssl, ( repinfo->c->tcp_req_info? repinfo->c->tcp_req_info->spool_buffer: repinfo->c->buffer )); } #endif Index: util/netevent.h =================================================================== RCS file: /cvs/src/usr.sbin/unbound/util/netevent.h,v diff -u -p -r1.21 netevent.h --- util/netevent.h 5 Sep 2023 11:12:10 -0000 1.21 +++ util/netevent.h 22 May 2024 10:33:17 -0000 @@ -181,6 +181,8 @@ struct comm_point { /** if the event is added or not */ int event_added; + /** Reference to struct that is part of the listening ports, + * where for listening ports information is kept about the address. */ struct unbound_socket* socket; /** file descriptor for communication point */ Index: util/data/msgparse.h =================================================================== RCS file: /cvs/src/usr.sbin/unbound/util/data/msgparse.h,v diff -u -p -r1.10 msgparse.h --- util/data/msgparse.h 12 Apr 2024 15:45:24 -0000 1.10 +++ util/data/msgparse.h 22 May 2024 10:33:17 -0000 @@ -82,6 +82,8 @@ extern time_t MAX_TTL; extern time_t MIN_TTL; /** Maximum Negative TTL that is allowed */ extern time_t MAX_NEG_TTL; +/** Minimum Negative TTL that is allowed */ +extern time_t MIN_NEG_TTL; /** If we serve expired entries and prefetch them */ extern int SERVE_EXPIRED; /** Time to serve records after expiration */ Index: util/data/msgreply.c =================================================================== RCS file: /cvs/src/usr.sbin/unbound/util/data/msgreply.c,v diff -u -p -r1.24 msgreply.c --- util/data/msgreply.c 13 Apr 2024 12:24:57 -0000 1.24 +++ util/data/msgreply.c 22 May 2024 10:33:17 -0000 @@ -61,6 +61,8 @@ time_t MAX_TTL = 3600 * 24 * 10; /* ten time_t MIN_TTL = 0; /** MAX Negative TTL, for SOA records in authority section */ time_t MAX_NEG_TTL = 3600; /* one hour */ +/** MIN Negative TTL, for SOA records in authority section */ +time_t MIN_NEG_TTL = 0; /** If we serve expired entries and prefetch them */ int SERVE_EXPIRED = 0; /** Time to serve records after expiration */ @@ -223,18 +225,25 @@ rdata_copy(sldns_buffer* pkt, struct pac if(type == LDNS_RR_TYPE_SOA && section == LDNS_SECTION_AUTHORITY) { /* negative response. see if TTL of SOA record larger than the * minimum-ttl in the rdata of the SOA record */ - if(*rr_ttl > soa_find_minttl(rr)) - *rr_ttl = soa_find_minttl(rr); - } - if(!SERVE_ORIGINAL_TTL && (*rr_ttl < MIN_TTL)) - *rr_ttl = MIN_TTL; - if(!SERVE_ORIGINAL_TTL && (*rr_ttl > MAX_TTL)) - *rr_ttl = MAX_TTL; - if(type == LDNS_RR_TYPE_SOA && section == LDNS_SECTION_AUTHORITY) { - /* max neg ttl overrides the min and max ttl of everything - * else, it is for a more specific record */ - if(*rr_ttl > MAX_NEG_TTL) - *rr_ttl = MAX_NEG_TTL; + if(*rr_ttl > soa_find_minttl(rr)) *rr_ttl = soa_find_minttl(rr); + if(!SERVE_ORIGINAL_TTL) { + /* If MIN_NEG_TTL is configured skip setting MIN_TTL */ + if(MIN_NEG_TTL <= 0 && *rr_ttl < MIN_TTL) { + *rr_ttl = MIN_TTL; + } + if(*rr_ttl > MAX_TTL) *rr_ttl = MAX_TTL; + } + /* MAX_NEG_TTL overrides the min and max ttl of everything + * else; it is for a more specific record */ + if(*rr_ttl > MAX_NEG_TTL) *rr_ttl = MAX_NEG_TTL; + /* MIN_NEG_TTL overrides the min and max ttl of everything + * else if configured; it is for a more specific record */ + if(MIN_NEG_TTL > 0 && *rr_ttl < MIN_NEG_TTL) { + *rr_ttl = MIN_NEG_TTL; + } + } else if(!SERVE_ORIGINAL_TTL) { + if(*rr_ttl < MIN_TTL) *rr_ttl = MIN_TTL; + if(*rr_ttl > MAX_TTL) *rr_ttl = MAX_TTL; } if(*rr_ttl < data->ttl) data->ttl = *rr_ttl; Index: util/storage/lookup3.c =================================================================== RCS file: /cvs/src/usr.sbin/unbound/util/storage/lookup3.c,v diff -u -p -r1.6 lookup3.c --- util/storage/lookup3.c 13 Aug 2021 19:58:47 -0000 1.6 +++ util/storage/lookup3.c 22 May 2024 10:33:17 -0000 @@ -89,7 +89,7 @@ on 1 byte), but shoehorning those bytes # if (defined(__BYTE_ORDER) && defined(__LITTLE_ENDIAN) && \ __BYTE_ORDER == __LITTLE_ENDIAN) || \ (defined(i386) || defined(__i386__) || defined(__i486__) || \ - defined(__i586__) || defined(__i686__) || defined(vax) || defined(MIPSEL) || defined(__x86)) + defined(__i586__) || defined(__i686__) || defined(vax) || defined(MIPSEL) || defined(__x86) || defined(__loongarch__)) # define HASH_LITTLE_ENDIAN 1 # define HASH_BIG_ENDIAN 0 # elif (defined(__BYTE_ORDER) && defined(__BIG_ENDIAN) && \ Index: util/storage/lruhash.c =================================================================== RCS file: /cvs/src/usr.sbin/unbound/util/storage/lruhash.c,v diff -u -p -r1.5 lruhash.c --- util/storage/lruhash.c 5 Sep 2023 11:12:11 -0000 1.5 +++ util/storage/lruhash.c 22 May 2024 10:33:17 -0000 @@ -528,6 +528,40 @@ lruhash_setmarkdel(struct lruhash* table lock_quick_unlock(&table->lock); } +void +lruhash_update_space_used(struct lruhash* table, void* cb_arg, int diff_size) +{ + 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 used */ + lock_quick_lock(&table->lock); + + if((int)table->space_used + diff_size < 0) + table->space_used = 0; + else table->space_used = (size_t)((int)table->space_used + diff_size); + + 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) Index: util/storage/lruhash.h =================================================================== RCS file: /cvs/src/usr.sbin/unbound/util/storage/lruhash.h,v diff -u -p -r1.4 lruhash.h --- util/storage/lruhash.h 5 Sep 2023 11:12:11 -0000 1.4 +++ util/storage/lruhash.h 22 May 2024 10:33:17 -0000 @@ -303,6 +303,17 @@ void lru_touch(struct lruhash* table, st */ void lruhash_setmarkdel(struct lruhash* table, lruhash_markdelfunc_type md); +/** + * Update the size of an element in the hashtable. + * + * @param table: hash table. + * @param cb_override: if not NULL overrides the cb_arg for deletefunc. + * @param diff_size: difference in size to the hash table storage. + * This is newsize - oldsize, a positive number uses more space. + */ +void lruhash_update_space_used(struct lruhash* table, void* cb_override, + int diff_size); + /************************* getdns functions ************************/ /*** these are used by getdns only and not by unbound. ***/ Index: util/storage/slabhash.c =================================================================== RCS file: /cvs/src/usr.sbin/unbound/util/storage/slabhash.c,v diff -u -p -r1.4 slabhash.c --- util/storage/slabhash.c 5 Sep 2023 11:12:11 -0000 1.4 +++ util/storage/slabhash.c 22 May 2024 10:33:17 -0000 @@ -166,6 +166,13 @@ int slabhash_is_size(struct slabhash* sl return 0; } +void slabhash_update_space_used(struct slabhash* sl, hashvalue_type hash, + void* cb_arg, int diff_size) +{ + lruhash_update_space_used(sl->array[slab_idx(sl, hash)], cb_arg, + diff_size); +} + size_t slabhash_get_mem(struct slabhash* sl) { size_t i, total = sizeof(*sl); Index: util/storage/slabhash.h =================================================================== RCS file: /cvs/src/usr.sbin/unbound/util/storage/slabhash.h,v diff -u -p -r1.5 slabhash.h --- util/storage/slabhash.h 5 Sep 2023 11:12:11 -0000 1.5 +++ util/storage/slabhash.h 22 May 2024 10:33:17 -0000 @@ -162,6 +162,18 @@ size_t slabhash_get_size(struct slabhash int slabhash_is_size(struct slabhash* table, size_t size, size_t slabs); /** + * Update the size of an element in the hashtable, uses + * lruhash_update_space_used. + * + * @param table: hash table. + * @param hash: hash value. User calculates the hash. + * @param cb_override: if not NULL overrides the cb_arg for deletefunc. + * @param diff_size: difference in size to the hash table storage. + */ +void slabhash_update_space_used(struct slabhash* table, hashvalue_type hash, + void* cb_override, int diff_size); + +/** * Retrieve slab hash current memory use. * @param table: hash table. * @return memory in use. Index: validator/val_utils.c =================================================================== RCS file: /cvs/src/usr.sbin/unbound/validator/val_utils.c,v diff -u -p -r1.15 val_utils.c --- validator/val_utils.c 13 Apr 2024 12:24:57 -0000 1.15 +++ validator/val_utils.c 22 May 2024 10:33:17 -0000 @@ -118,7 +118,30 @@ val_classify_response(uint16_t query_fla * ANY responses are validated differently. */ if(rcode == LDNS_RCODE_NOERROR && qinf->qtype == LDNS_RR_TYPE_ANY) return VAL_CLASS_ANY; - + + /* For the query type DNAME, the name matters. Equal name is the + * answer looked for, but a subdomain redirects the query. */ + if(qinf->qtype == LDNS_RR_TYPE_DNAME) { + for(i=skip; ian_numrrsets; i++) { + if(rcode == LDNS_RCODE_NOERROR && + ntohs(rep->rrsets[i]->rk.type) + == LDNS_RR_TYPE_DNAME && + query_dname_compare(qinf->qname, + rep->rrsets[i]->rk.dname) == 0) { + /* type is DNAME and name is equal, it is + * the answer. For the query name a subdomain + * of the rrset.dname it would redirect. */ + return VAL_CLASS_POSITIVE; + } + if(ntohs(rep->rrsets[i]->rk.type) + == LDNS_RR_TYPE_CNAME) + return VAL_CLASS_CNAME; + } + log_dns_msg("validator: error. failed to classify response message: ", + qinf, rep); + return VAL_CLASS_UNKNOWN; + } + /* Note that DNAMEs will be ignored here, unless qtype=DNAME. Unless * qtype=CNAME, this will yield a CNAME response. */ for(i=skip; ian_numrrsets; i++) { @@ -231,6 +254,21 @@ val_find_signer(enum val_classification rep->rrsets[i]->rk.dname) == 0) { val_find_rrset_signer(rep->rrsets[i], signer_name, signer_len); + /* If there was no signer, and the query + * was for type CNAME, and this is a CNAME, + * and the previous is a DNAME, then this + * is the synthesized CNAME, use the signer + * of the DNAME record. */ + if(*signer_name == NULL && + qinf->qtype == LDNS_RR_TYPE_CNAME && + ntohs(rep->rrsets[i]->rk.type) == + LDNS_RR_TYPE_CNAME && i > skip && + ntohs(rep->rrsets[i-1]->rk.type) == + LDNS_RR_TYPE_DNAME && + dname_strict_subdomain_c(rep->rrsets[i]->rk.dname, rep->rrsets[i-1]->rk.dname)) { + val_find_rrset_signer(rep->rrsets[i-1], + signer_name, signer_len); + } return; } } Index: validator/validator.c =================================================================== RCS file: /cvs/src/usr.sbin/unbound/validator/validator.c,v diff -u -p -r1.23 validator.c --- validator/validator.c 13 Apr 2024 12:24:57 -0000 1.23 +++ validator/validator.c 22 May 2024 10:33:17 -0000 @@ -621,7 +621,6 @@ prime_trust_anchor(struct module_qstate* * @param vq: validator query state. * @param env: module env for verify. * @param ve: validator env for verify. - * @param qchase: query that was made. * @param chase_reply: answer to validate. * @param key_entry: the key entry, which is trusted, and which matches * the signer of the answer. The key entry isgood(). @@ -632,7 +631,7 @@ prime_trust_anchor(struct module_qstate* */ static int validate_msg_signatures(struct module_qstate* qstate, struct val_qstate* vq, - struct module_env* env, struct val_env* ve, struct query_info* qchase, + struct module_env* env, struct val_env* ve, struct reply_info* chase_reply, struct key_entry_key* key_entry, int* suspend) { @@ -640,7 +639,7 @@ validate_msg_signatures(struct module_qs size_t i, slen; struct ub_packed_rrset_key* s; enum sec_status sec; - int dname_seen = 0, num_verifies = 0, verified, have_state = 0; + int num_verifies = 0, verified, have_state = 0; char* reason = NULL; sldns_ede_code reason_bogus = LDNS_EDE_DNSSEC_BOGUS; *suspend = 0; @@ -658,9 +657,13 @@ validate_msg_signatures(struct module_qs /* Skip the CNAME following a (validated) DNAME. * Because of the normalization routines in the iterator, * there will always be an unsigned CNAME following a DNAME - * (unless qtype=DNAME). */ - if(dname_seen && ntohs(s->rk.type) == LDNS_RR_TYPE_CNAME) { - dname_seen = 0; + * (unless qtype=DNAME in the answer part). */ + if(i>0 && ntohs(chase_reply->rrsets[i-1]->rk.type) == + LDNS_RR_TYPE_DNAME && + ntohs(s->rk.type) == LDNS_RR_TYPE_CNAME && + ((struct packed_rrset_data*)chase_reply->rrsets[i-1]->entry.data)->security == sec_status_secure && + dname_strict_subdomain_c(s->rk.dname, chase_reply->rrsets[i-1]->rk.dname) + ) { /* CNAME was synthesized by our own iterator */ /* since the DNAME verified, mark the CNAME as secure */ ((struct packed_rrset_data*)s->entry.data)->security = @@ -691,12 +694,6 @@ validate_msg_signatures(struct module_qs return 0; } - /* Notice a DNAME that should be followed by an unsigned - * CNAME. */ - if(qchase->qtype != LDNS_RR_TYPE_DNAME && - ntohs(s->rk.type) == LDNS_RR_TYPE_DNAME) { - dname_seen = 1; - } num_verifies += verified; if(num_verifies > MAX_VALIDATE_AT_ONCE && i+1 < (env->cfg->val_clean_additional? @@ -2186,7 +2183,7 @@ processValidate(struct module_qstate* qs /* check signatures in the message; * answer and authority must be valid, additional is only checked. */ - if(!validate_msg_signatures(qstate, vq, qstate->env, ve, &vq->qchase, + if(!validate_msg_signatures(qstate, vq, qstate->env, ve, vq->chase_reply, vq->key_entry, &suspend)) { if(suspend) { if(!validate_suspend_setup_timer(qstate, vq, @@ -2456,19 +2453,12 @@ processFinished(struct module_qstate* qs log_query_info(NO_VERBOSE, "validation failure", &qstate->qinfo); else { - char* err_str = errinf_to_str_bogus(qstate); + char* err_str = errinf_to_str_bogus(qstate, + qstate->region); if(err_str) { - size_t err_str_len = strlen(err_str); log_info("%s", err_str); - /* allocate space and store the error - * string */ - vq->orig_msg->rep->reason_bogus_str = regional_alloc( - qstate->region, - sizeof(char) * (err_str_len+1)); - memcpy(vq->orig_msg->rep->reason_bogus_str, - err_str, err_str_len+1); + vq->orig_msg->rep->reason_bogus_str = err_str; } - free(err_str); } } /* Index: doc/README =================================================================== RCS file: /cvs/src/usr.sbin/unbound/doc/README,v diff -u -p -r1.37 README --- doc/README 13 Apr 2024 12:24:57 -0000 1.37 +++ doc/README 22 May 2024 10:33:17 -0000 @@ -1,4 +1,4 @@ -README for Unbound 1.19.3 +README for Unbound 1.20.0 Copyright 2007 NLnet Labs http://unbound.net Index: doc/example.conf.in =================================================================== RCS file: /cvs/src/usr.sbin/unbound/doc/example.conf.in,v diff -u -p -r1.42 example.conf.in --- doc/example.conf.in 13 Apr 2024 12:24:57 -0000 1.42 +++ doc/example.conf.in 22 May 2024 10:33:17 -0000 @@ -1,7 +1,7 @@ # # Example configuration file. # -# See unbound.conf(5) man page, version 1.19.3. +# See unbound.conf(5) man page, version 1.20.0. # # this is a comment. @@ -191,6 +191,21 @@ server: # are behind a slow satellite link, to eg. 1128. # unknown-server-time-limit: 376 + # msec before recursion replies are dropped. The work item continues. + # discard-timeout: 1900 + + # Max number of replies waiting for recursion per IP address. + # wait-limit: 1000 + + # Max replies waiting for recursion for IP address with cookie. + # wait-limit-cookie: 10000 + + # Apart from the default, the wait limit can be set for a netblock. + # wait-limit-netblock: 192.0.2.0/24 50000 + + # Apart from the default, the wait limit with cookie can be adjusted. + # wait-limit-cookie-netblock: 192.0.2.0/24 50000 + # the amount of memory to use for the RRset cache. # plain value in bytes or you can append k, m or G. default is "4Mb". # rrset-cache-size: 4m @@ -211,6 +226,11 @@ server: # the time to live (TTL) value cap for negative responses in the cache # cache-max-negative-ttl: 3600 + # the time to live (TTL) value lower bound, in seconds. Default 0. + # For negative responses in the cache. If disabled, default, + # cache-min-tll applies if configured. + # cache-min-negative-ttl: 0 + # the time to live (TTL) value for cached roundtrip times, lameness and # EDNS version information for hosts. In seconds. # infra-host-ttl: 900 @@ -283,7 +303,8 @@ server: # Enable EDNS TCP keepalive option. # edns-tcp-keepalive: no - # Timeout for EDNS TCP keepalive, in msec. + # Timeout for EDNS TCP keepalive, in msec. Overrides tcp-idle-timeout + # if edns-tcp-keepalive is set. # edns-tcp-keepalive-timeout: 120000 # UDP queries that have waited in the socket buffer for a long time @@ -1247,6 +1268,9 @@ remote-control: # secret-seed: "default" # # if the backend should be read from, but not written to. # cachedb-no-store: no +# # if the cachedb should be checked before a serve-expired response is +# # given, when serve-expired is enabled. +# cachedb-check-when-serve-expired: yes # # # For "redis" backend: # # (to enable, use --with-libhiredis to configure before compiling) Index: doc/libunbound.3.in =================================================================== RCS file: /cvs/src/usr.sbin/unbound/doc/libunbound.3.in,v diff -u -p -r1.40 libunbound.3.in --- doc/libunbound.3.in 13 Apr 2024 12:24:57 -0000 1.40 +++ doc/libunbound.3.in 22 May 2024 10:33:17 -0000 @@ -1,4 +1,4 @@ -.TH "libunbound" "3" "Mar 14, 2024" "NLnet Labs" "unbound 1.19.3" +.TH "libunbound" "3" "May 8, 2024" "NLnet Labs" "unbound 1.20.0" .\" .\" libunbound.3 -- unbound library functions manual .\" @@ -44,7 +44,7 @@ .B ub_ctx_zone_remove, .B ub_ctx_data_add, .B ub_ctx_data_remove -\- Unbound DNS validating resolver 1.19.3 functions. +\- Unbound DNS validating resolver 1.20.0 functions. .SH "SYNOPSIS" .B #include .LP Index: doc/unbound-anchor.8.in =================================================================== RCS file: /cvs/src/usr.sbin/unbound/doc/unbound-anchor.8.in,v diff -u -p -r1.39 unbound-anchor.8.in --- doc/unbound-anchor.8.in 13 Apr 2024 12:24:57 -0000 1.39 +++ doc/unbound-anchor.8.in 22 May 2024 10:33:17 -0000 @@ -1,4 +1,4 @@ -.TH "unbound-anchor" "8" "Mar 14, 2024" "NLnet Labs" "unbound 1.19.3" +.TH "unbound-anchor" "8" "May 8, 2024" "NLnet Labs" "unbound 1.20.0" .\" .\" unbound-anchor.8 -- unbound anchor maintenance utility manual .\" Index: doc/unbound-checkconf.8.in =================================================================== RCS file: /cvs/src/usr.sbin/unbound/doc/unbound-checkconf.8.in,v diff -u -p -r1.39 unbound-checkconf.8.in --- doc/unbound-checkconf.8.in 13 Apr 2024 12:24:57 -0000 1.39 +++ doc/unbound-checkconf.8.in 22 May 2024 10:33:17 -0000 @@ -1,4 +1,4 @@ -.TH "unbound-checkconf" "8" "Mar 14, 2024" "NLnet Labs" "unbound 1.19.3" +.TH "unbound-checkconf" "8" "May 8, 2024" "NLnet Labs" "unbound 1.20.0" .\" .\" unbound-checkconf.8 -- unbound configuration checker manual .\" @@ -14,6 +14,7 @@ unbound\-checkconf .B unbound\-checkconf .RB [ \-h ] .RB [ \-f ] +.RB [ \-q ] .RB [ \-o .IR option ] .RI [ cfgfile ] @@ -36,6 +37,9 @@ Print full pathname, with chroot applied .B \-o\fI option If given, after checking the config file the value of this option is printed to stdout. For "" (disabled) options an empty line is printed. +.TP +.B \-q +Make the operation quiet, suppress output on success. .TP .I cfgfile The config file to read with settings for Unbound. It is checked. Index: doc/unbound-control.8.in =================================================================== RCS file: /cvs/src/usr.sbin/unbound/doc/unbound-control.8.in,v diff -u -p -r1.41 unbound-control.8.in --- doc/unbound-control.8.in 13 Apr 2024 12:24:57 -0000 1.41 +++ doc/unbound-control.8.in 22 May 2024 10:33:17 -0000 @@ -1,4 +1,4 @@ -.TH "unbound-control" "8" "Mar 14, 2024" "NLnet Labs" "unbound 1.19.3" +.TH "unbound-control" "8" "May 8, 2024" "NLnet Labs" "unbound 1.20.0" .\" .\" unbound-control.8 -- unbound remote control manual .\" @@ -239,22 +239,24 @@ still be bogus, use \fBflush_zone\fR to .B insecure_remove \fIzone Removes domain\-insecure for the given zone. .TP -.B forward_add \fR[\fI+i\fR] \fIzone addr ... +.B forward_add \fR[\fI+it\fR] \fIzone addr ... Add a new forward zone to running Unbound. With +i option also adds a \fIdomain\-insecure\fR for the zone (so it can resolve insecurely if you have a DNSSEC root trust anchor configured for other names). The addr can be IP4, IP6 or nameserver names, like \fIforward-zone\fR config in unbound.conf. +The +t option sets it to use tls upstream, like \fIforward\-tls\-upstream\fR: yes. .TP .B forward_remove \fR[\fI+i\fR] \fIzone Remove a forward zone from running Unbound. The +i also removes a \fIdomain\-insecure\fR for the zone. .TP -.B stub_add \fR[\fI+ip\fR] \fIzone addr ... +.B stub_add \fR[\fI+ipt\fR] \fIzone addr ... Add a new stub zone to running Unbound. With +i option also adds a \fIdomain\-insecure\fR for the zone. With +p the stub zone is set to prime, without it it is set to notprime. The addr can be IP4, IP6 or nameserver names, like the \fIstub-zone\fR config in unbound.conf. +The +t option sets it to use tls upstream, like \fIstub\-tls\-upstream\fR: yes. .TP .B stub_remove \fR[\fI+i\fR] \fIzone Remove a stub zone from running Unbound. The +i also removes a Index: doc/unbound-host.1.in =================================================================== RCS file: /cvs/src/usr.sbin/unbound/doc/unbound-host.1.in,v diff -u -p -r1.41 unbound-host.1.in --- doc/unbound-host.1.in 13 Apr 2024 12:24:57 -0000 1.41 +++ doc/unbound-host.1.in 22 May 2024 10:33:17 -0000 @@ -1,4 +1,4 @@ -.TH "unbound\-host" "1" "Mar 14, 2024" "NLnet Labs" "unbound 1.19.3" +.TH "unbound\-host" "1" "May 8, 2024" "NLnet Labs" "unbound 1.20.0" .\" .\" unbound-host.1 -- unbound DNS lookup utility .\" Index: doc/unbound.8.in =================================================================== RCS file: /cvs/src/usr.sbin/unbound/doc/unbound.8.in,v diff -u -p -r1.42 unbound.8.in --- doc/unbound.8.in 13 Apr 2024 12:24:57 -0000 1.42 +++ doc/unbound.8.in 22 May 2024 10:33:17 -0000 @@ -1,4 +1,4 @@ -.TH "unbound" "8" "Mar 14, 2024" "NLnet Labs" "unbound 1.19.3" +.TH "unbound" "8" "May 8, 2024" "NLnet Labs" "unbound 1.20.0" .\" .\" unbound.8 -- unbound manual .\" @@ -9,7 +9,7 @@ .\" .SH "NAME" .B unbound -\- Unbound DNS validating resolver 1.19.3. +\- Unbound DNS validating resolver 1.20.0. .SH "SYNOPSIS" .B unbound .RB [ \-h ] Index: doc/unbound.conf.5.in =================================================================== RCS file: /cvs/src/usr.sbin/unbound/doc/unbound.conf.5.in,v diff -u -p -r1.46 unbound.conf.5.in --- doc/unbound.conf.5.in 13 Apr 2024 12:24:57 -0000 1.46 +++ doc/unbound.conf.5.in 22 May 2024 10:33:17 -0000 @@ -1,4 +1,4 @@ -.TH "unbound.conf" "5" "Mar 14, 2024" "NLnet Labs" "unbound 1.19.3" +.TH "unbound.conf" "5" "May 8, 2024" "NLnet Labs" "unbound 1.20.0" .\" .\" unbound.conf.5 -- unbound.conf manual .\" @@ -266,6 +266,36 @@ Increase this if you are behind a slow s That would then avoid re\-querying every initial query because it times out. Default is 376 msec. .TP +.B discard\-timeout: \fI +The wait time in msec where recursion requests are dropped. This is +to stop a large number of replies from accumulating. They receive +no reply, the work item continues to recurse. It is nice to be a bit +larger than serve\-expired\-client\-timeout if that is enabled. +A value of 1900 msec is suggested. The value 0 disables it. +Default 1900 msec. +.TP +.B wait\-limit: \fI +The number of replies that can wait for recursion, for an IP address. +This makes a ratelimit per IP address of waiting replies for recursion. +It stops very large amounts of queries waiting to be returned to one +destination. The value 0 disables wait limits. Default is 1000. +.TP +.B wait\-limit\-cookie: \fI +The number of replies that can wait for recursion, for an IP address +that sent the query with a valid DNS cookie. Since the cookie validates +the client address, the limit can be higher. Default is 10000. +.TP +.B wait\-limit\-netblock: \fI +The wait limit for the netblock. If not given the wait\-limit value is +used. The most specific netblock is used to determine the limit. Useful for +overriding the default for a specific, group or individual, server. +The value -1 disables wait limits for the netblock. +.TP +.B wait\-limit\-cookie\-netblock: \fI +The wait limit for the netblock, when the query has a DNS cookie. +If not given, the wait\-limit\-cookie value is used. +The value -1 disables wait limits for the netblock. +.TP .B so\-rcvbuf: \fI If not 0, then set the SO_RCVBUF socket option to get more buffer space on UDP port 53 incoming queries. So that short spikes on busy @@ -352,6 +382,15 @@ Time to live maximum for negative respon authority section that is limited in time. Default is 3600. This applies to nxdomain and nodata answers. .TP +.B cache\-min\-negative\-ttl: \fI +Time to live minimum for negative responses, these have a SOA in the +authority section that is limited in time. +Default is 0 (disabled). +If this is disabled and \fBcache-min-ttl\fR is configured, it will take effect +instead. +In that case you can set this to 1 to honor the upstream TTL. +This applies to nxdomain and nodata answers. +.TP .B infra\-host\-ttl: \fI Time to live for entries in the host cache. The host cache contains roundtrip timing, lameness and EDNS support information. Default is 900. @@ -436,6 +475,8 @@ configured value if the number of free b total number configured, and finally to 0 if the number of free buffers falls below 20% of the total number configured. A minimum timeout of 200 milliseconds is observed regardless of the option value used. +It will be overriden by \fBedns\-tcp\-keepalive\-timeout\fR if +\fBedns\-tcp\-keepalive\fR is enabled. .TP .B tcp-reuse-timeout: \fI\fR The period Unbound will keep TCP persistent connections open to @@ -454,20 +495,11 @@ This option defaults to 3000 millisecond Enable or disable EDNS TCP Keepalive. Default is no. .TP .B edns-tcp-keepalive-timeout: \fI\fR -The period Unbound will wait for a query on a TCP connection when -EDNS TCP Keepalive is active. If this timeout expires Unbound closes -the connection. If the client supports the EDNS TCP Keepalive option, +Overrides \fBtcp\-idle\-timeout\fR when \fBedns\-tcp\-keepalive\fR is enabled. +If the client supports the EDNS TCP Keepalive option, Unbound sends the timeout value to the client to encourage it to close the connection before the server times out. This option defaults to 120000 milliseconds. -When the number of free incoming TCP buffers falls below 50% of -the total number configured, the advertised timeout is progressively -reduced to 1% of the configured value, then to 0.2% of the configured -value if the number of free buffers falls below 35% of the total number -configured, and finally to 0 if the number of free buffers falls below -20% of the total number configured. -A minimum actual timeout of 200 milliseconds is observed regardless of the -advertised timeout. .TP .B sock\-queue\-timeout: \fI\fR UDP queries that have waited in the socket buffer for a long time can be @@ -2593,11 +2625,7 @@ If Unbound cannot even find an answer in query as usual, and stores the answer in the backend. .P This module interacts with the \fBserve\-expired\-*\fR options and will reply -with expired data if Unbound is configured for that. Currently the use -of \fBserve\-expired\-client\-timeout:\fR and -\fBserve\-expired\-reply\-ttl:\fR is not consistent for data originating from -the external cache as these will result in a reply with 0 TTL without trying to -update the data first, ignoring the configured values. +with expired data if Unbound is configured for that. .P If Unbound was built with \fB\-\-with\-libhiredis\fR @@ -2653,6 +2681,16 @@ This option defaults to "default". If the backend should be read from, but not written to. This makes this instance not store dns messages in the backend. But if data is available it is retrieved. The default is no. +.TP +.B cachedb-check-when-serve-expired: \fI\fR +If enabled, the cachedb is checked before an expired response is returned. +When \fBserve\-expired\fR is enabled, without \fBserve\-expired\-client\-timeout\fR, it then +does not immediately respond with an expired response from cache, but instead +first checks the cachedb for valid contents, and if so returns it. If the +cachedb also has no valid contents, the serve expired response is sent. +If also \fBserve\-expired\-client\-timeout\fR is enabled, the expired response +is delayed until the timeout expires. Unless the lookup succeeds within the +timeout. The default is yes. .P The following .B cachedb Index: doc/unbound.doxygen =================================================================== RCS file: /cvs/src/usr.sbin/unbound/doc/unbound.doxygen,v diff -u -p -r1.7 unbound.doxygen --- doc/unbound.doxygen 23 Feb 2022 12:04:05 -0000 1.7 +++ doc/unbound.doxygen 22 May 2024 10:33:17 -0000 @@ -1,96 +1,151 @@ -# Doxyfile 1.7.1 +# Doxyfile 1.9.7 # This file describes the settings to be used by the documentation system -# doxygen (www.doxygen.org) for a project +# doxygen (www.doxygen.org) for a project. # -# All text after a hash (#) is considered a comment and will be ignored +# All text after a double hash (##) is considered a comment and is placed in +# front of the TAG it is preceding. +# +# All text after a single hash (#) is considered a comment and will be ignored. # The format is: -# TAG = value [value, ...] -# For lists items can also be appended using: -# TAG += value [value, ...] -# Values that contain spaces should be placed between quotes (" ") +# TAG = value [value, ...] +# For lists, items can also be appended using: +# TAG += value [value, ...] +# Values that contain spaces should be placed between quotes (\" \"). +# +# Note: +# +# Use doxygen to compare the used configuration file with the template +# configuration file: +# doxygen -x [configFile] +# Use doxygen to compare the used configuration file with the template +# configuration file without replacing the environment variables or CMake type +# replacement variables: +# doxygen -x_noenv [configFile] #--------------------------------------------------------------------------- # Project related configuration options #--------------------------------------------------------------------------- -# This tag specifies the encoding used for all characters in the config file -# that follow. The default is UTF-8 which is also the encoding used for all +# This tag specifies the encoding used for all characters in the configuration +# file that follow. The default is UTF-8 which is also the encoding used for all # text before the first occurrence of this tag. Doxygen uses libiconv (or the # iconv built into libc) for the transcoding. See -# http://www.gnu.org/software/libiconv for the list of possible encodings. +# https://www.gnu.org/software/libiconv/ for the list of possible encodings. +# The default value is: UTF-8. DOXYFILE_ENCODING = UTF-8 -# The PROJECT_NAME tag is a single word (or a sequence of words surrounded -# by quotes) that should identify the project. +# The PROJECT_NAME tag is a single word (or a sequence of words surrounded by +# double-quotes, unless you are using Doxywizard) that should identify the +# project for which the documentation is generated. This name is used in the +# title of most generated pages and in a few other places. +# The default value is: My Project. PROJECT_NAME = unbound -# The PROJECT_NUMBER tag can be used to enter a project or revision number. -# This could be handy for archiving the generated documentation or -# if some version control system is used. +# The PROJECT_NUMBER tag can be used to enter a project or revision number. This +# could be handy for archiving the generated documentation or if some version +# control system is used. PROJECT_NUMBER = 0.1 -# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) -# base path where the generated documentation will be put. -# If a relative path is entered, it will be relative to the location -# where doxygen was started. If left blank the current directory will be used. +# Using the PROJECT_BRIEF tag one can provide an optional one line description +# for a project that appears at the top of each page and should give viewer a +# quick idea about the purpose of the project. Keep the description short. + +PROJECT_BRIEF = + +# With the PROJECT_LOGO tag one can specify a logo or an icon that is included +# in the documentation. The maximum height of the logo should not exceed 55 +# pixels and the maximum width should not exceed 200 pixels. Doxygen will copy +# the logo to the output directory. + +PROJECT_LOGO = + +# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) path +# into which the generated documentation will be written. If a relative path is +# entered, it will be relative to the location where doxygen was started. If +# left blank the current directory will be used. OUTPUT_DIRECTORY = doc -# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create -# 4096 sub-directories (in 2 levels) under the output directory of each output -# format and will distribute the generated files over these directories. -# Enabling this option can be useful when feeding doxygen a huge amount of -# source files, where putting all generated files in the same directory would -# otherwise cause performance problems for the file system. +# If the CREATE_SUBDIRS tag is set to YES then doxygen will create up to 4096 +# sub-directories (in 2 levels) under the output directory of each output format +# and will distribute the generated files over these directories. Enabling this +# option can be useful when feeding doxygen a huge amount of source files, where +# putting all generated files in the same directory would otherwise causes +# performance problems for the file system. Adapt CREATE_SUBDIRS_LEVEL to +# control the number of sub-directories. +# The default value is: NO. CREATE_SUBDIRS = NO +# Controls the number of sub-directories that will be created when +# CREATE_SUBDIRS tag is set to YES. Level 0 represents 16 directories, and every +# level increment doubles the number of directories, resulting in 4096 +# directories at level 8 which is the default and also the maximum value. The +# sub-directories are organized in 2 levels, the first level always has a fixed +# number of 16 directories. +# Minimum value: 0, maximum value: 8, default value: 8. +# This tag requires that the tag CREATE_SUBDIRS is set to YES. + +CREATE_SUBDIRS_LEVEL = 8 + +# If the ALLOW_UNICODE_NAMES tag is set to YES, doxygen will allow non-ASCII +# characters to appear in the names of generated files. If set to NO, non-ASCII +# characters will be escaped, for example _xE3_x81_x84 will be used for Unicode +# U+3044. +# The default value is: NO. + +ALLOW_UNICODE_NAMES = NO + # The OUTPUT_LANGUAGE tag is used to specify the language in which all # documentation generated by doxygen is written. Doxygen will use this # information to generate all constant output in the proper language. -# The default language is English, other supported languages are: -# Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese-Traditional, -# Croatian, Czech, Danish, Dutch, Esperanto, Farsi, Finnish, French, German, -# Greek, Hungarian, Italian, Japanese, Japanese-en (Japanese with English -# messages), Korean, Korean-en, Lithuanian, Norwegian, Macedonian, Persian, -# Polish, Portuguese, Romanian, Russian, Serbian, Serbian-Cyrilic, Slovak, -# Slovene, Spanish, Swedish, Ukrainian, and Vietnamese. +# Possible values are: Afrikaans, Arabic, Armenian, Brazilian, Bulgarian, +# Catalan, Chinese, Chinese-Traditional, Croatian, Czech, Danish, Dutch, English +# (United States), Esperanto, Farsi (Persian), Finnish, French, German, Greek, +# Hindi, Hungarian, Indonesian, Italian, Japanese, Japanese-en (Japanese with +# English messages), Korean, Korean-en (Korean with English messages), Latvian, +# Lithuanian, Macedonian, Norwegian, Persian (Farsi), Polish, Portuguese, +# Romanian, Russian, Serbian, Serbian-Cyrillic, Slovak, Slovene, Spanish, +# Swedish, Turkish, Ukrainian and Vietnamese. +# The default value is: English. OUTPUT_LANGUAGE = English -# If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will -# include brief member descriptions after the members that are listed in -# the file and class documentation (similar to JavaDoc). -# Set to NO to disable this. +# If the BRIEF_MEMBER_DESC tag is set to YES, doxygen will include brief member +# descriptions after the members that are listed in the file and class +# documentation (similar to Javadoc). Set to NO to disable this. +# The default value is: YES. BRIEF_MEMBER_DESC = YES -# If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend -# the brief description of a member or function before the detailed description. -# Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the +# If the REPEAT_BRIEF tag is set to YES, doxygen will prepend the brief +# description of a member or function before the detailed description +# +# Note: If both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the # brief descriptions will be completely suppressed. +# The default value is: YES. REPEAT_BRIEF = YES -# This tag implements a quasi-intelligent brief description abbreviator -# that is used to form the text in various listings. Each string -# in this list, if found as the leading text of the brief description, will be -# stripped from the text and the result after processing the whole list, is -# used as the annotated text. Otherwise, the brief description is used as-is. -# If left blank, the following values are used ("$name" is automatically -# replaced with the name of the entity): "The $name class" "The $name widget" -# "The $name file" "is" "provides" "specifies" "contains" -# "represents" "a" "an" "the" +# This tag implements a quasi-intelligent brief description abbreviator that is +# used to form the text in various listings. Each string in this list, if found +# as the leading text of the brief description, will be stripped from the text +# and the result, after processing the whole list, is used as the annotated +# text. Otherwise, the brief description is used as-is. If left blank, the +# following values are used ($name is automatically replaced with the name of +# the entity):The $name class, The $name widget, The $name file, is, provides, +# specifies, contains, represents, a, an and the. ABBREVIATE_BRIEF = # If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then -# Doxygen will generate a detailed section even if there is only a brief +# doxygen will generate a detailed section even if there is only a brief # description. +# The default value is: NO. ALWAYS_DETAILED_SEC = NO @@ -98,521 +153,844 @@ ALWAYS_DETAILED_SEC = NO # inherited members of a class in the documentation of that class as if those # members were ordinary class members. Constructors, destructors and assignment # operators of the base classes will not be shown. +# The default value is: NO. INLINE_INHERITED_MEMB = NO -# If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full -# path before files name in the file list and in the header files. If set -# to NO the shortest path that makes the file name unique will be used. +# If the FULL_PATH_NAMES tag is set to YES, doxygen will prepend the full path +# before files name in the file list and in the header files. If set to NO the +# shortest path that makes the file name unique will be used +# The default value is: YES. FULL_PATH_NAMES = YES -# If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag -# can be used to strip a user-defined part of the path. Stripping is -# only done if one of the specified strings matches the left-hand part of -# the path. The tag can be used to show relative paths in the file list. -# If left blank the directory from which doxygen is run is used as the -# path to strip. +# The STRIP_FROM_PATH tag can be used to strip a user-defined part of the path. +# Stripping is only done if one of the specified strings matches the left-hand +# part of the path. The tag can be used to show relative paths in the file list. +# If left blank the directory from which doxygen is run is used as the path to +# strip. +# +# Note that you can specify absolute paths here, but also relative paths, which +# will be relative from the directory where doxygen is started. +# This tag requires that the tag FULL_PATH_NAMES is set to YES. STRIP_FROM_PATH = -# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of -# the path mentioned in the documentation of a class, which tells -# the reader which header file to include in order to use a class. -# If left blank only the name of the header file containing the class -# definition is used. Otherwise one should specify the include paths that -# are normally passed to the compiler using the -I flag. +# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of the +# path mentioned in the documentation of a class, which tells the reader which +# header file to include in order to use a class. If left blank only the name of +# the header file containing the class definition is used. Otherwise one should +# specify the list of include paths that are normally passed to the compiler +# using the -I flag. STRIP_FROM_INC_PATH = -# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter -# (but less readable) file names. This can be useful is your file systems -# doesn't support long names like on DOS, Mac, or CD-ROM. +# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter (but +# less readable) file names. This can be useful is your file systems doesn't +# support long names like on DOS, Mac, or CD-ROM. +# The default value is: NO. SHORT_NAMES = NO -# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen -# will interpret the first line (until the first dot) of a JavaDoc-style -# comment as the brief description. If set to NO, the JavaDoc -# comments will behave just like regular Qt-style comments -# (thus requiring an explicit @brief command for a brief description.) +# If the JAVADOC_AUTOBRIEF tag is set to YES then doxygen will interpret the +# first line (until the first dot) of a Javadoc-style comment as the brief +# description. If set to NO, the Javadoc-style will behave just like regular Qt- +# style comments (thus requiring an explicit @brief command for a brief +# description.) +# The default value is: NO. JAVADOC_AUTOBRIEF = YES -# If the QT_AUTOBRIEF tag is set to YES then Doxygen will -# interpret the first line (until the first dot) of a Qt-style -# comment as the brief description. If set to NO, the comments -# will behave just like regular Qt-style comments (thus requiring -# an explicit \brief command for a brief description.) +# If the JAVADOC_BANNER tag is set to YES then doxygen will interpret a line +# such as +# /*************** +# as being the beginning of a Javadoc-style comment "banner". If set to NO, the +# Javadoc-style will behave just like regular comments and it will not be +# interpreted by doxygen. +# The default value is: NO. + +JAVADOC_BANNER = NO + +# If the QT_AUTOBRIEF tag is set to YES then doxygen will interpret the first +# line (until the first dot) of a Qt-style comment as the brief description. If +# set to NO, the Qt-style will behave just like regular Qt-style comments (thus +# requiring an explicit \brief command for a brief description.) +# The default value is: NO. QT_AUTOBRIEF = NO -# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen -# treat a multi-line C++ special comment block (i.e. a block of //! or /// -# comments) as a brief description. This used to be the default behaviour. -# The new default is to treat a multi-line C++ comment block as a detailed -# description. Set this tag to YES if you prefer the old behaviour instead. +# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make doxygen treat a +# multi-line C++ special comment block (i.e. a block of //! or /// comments) as +# a brief description. This used to be the default behavior. The new default is +# to treat a multi-line C++ comment block as a detailed description. Set this +# tag to YES if you prefer the old behavior instead. +# +# Note that setting this tag to YES also means that rational rose comments are +# not recognized any more. +# The default value is: NO. MULTILINE_CPP_IS_BRIEF = NO -# If the INHERIT_DOCS tag is set to YES (the default) then an undocumented -# member inherits the documentation from any documented member that it -# re-implements. +# By default Python docstrings are displayed as preformatted text and doxygen's +# special commands cannot be used. By setting PYTHON_DOCSTRING to NO the +# doxygen's special commands can be used and the contents of the docstring +# documentation blocks is shown as doxygen documentation. +# The default value is: YES. + +PYTHON_DOCSTRING = YES + +# If the INHERIT_DOCS tag is set to YES then an undocumented member inherits the +# documentation from any documented member that it re-implements. +# The default value is: YES. INHERIT_DOCS = YES -# If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce -# a new page for each member. If set to NO, the documentation of a member will -# be part of the file/class/namespace that contains it. +# If the SEPARATE_MEMBER_PAGES tag is set to YES then doxygen will produce a new +# page for each member. If set to NO, the documentation of a member will be part +# of the file/class/namespace that contains it. +# The default value is: NO. SEPARATE_MEMBER_PAGES = NO -# The TAB_SIZE tag can be used to set the number of spaces in a tab. -# Doxygen uses this value to replace tabs by spaces in code fragments. +# The TAB_SIZE tag can be used to set the number of spaces in a tab. Doxygen +# uses this value to replace tabs by spaces in code fragments. +# Minimum value: 1, maximum value: 16, default value: 4. TAB_SIZE = 8 -# This tag can be used to specify a number of aliases that acts -# as commands in the documentation. An alias has the form "name=value". -# For example adding "sideeffect=\par Side Effects:\n" will allow you to -# put the command \sideeffect (or @sideeffect) in the documentation, which -# will result in a user-defined paragraph with heading "Side Effects:". -# You can put \n's in the value part of an alias to insert newlines. +# This tag can be used to specify a number of aliases that act as commands in +# the documentation. An alias has the form: +# name=value +# For example adding +# "sideeffect=@par Side Effects:^^" +# will allow you to put the command \sideeffect (or @sideeffect) in the +# documentation, which will result in a user-defined paragraph with heading +# "Side Effects:". Note that you cannot put \n's in the value part of an alias +# to insert newlines (in the resulting output). You can put ^^ in the value part +# of an alias to insert a newline as if a physical newline was in the original +# file. When you need a literal { or } or , in the value part of an alias you +# have to escape them by means of a backslash (\), this can lead to conflicts +# with the commands \{ and \} for these it is advised to use the version @{ and +# @} or use a double escape (\\{ and \\}) ALIASES = -# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C -# sources only. Doxygen will then generate output that is more tailored for C. -# For instance, some of the names that are used will be different. The list -# of all members will be omitted, etc. +# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources +# only. Doxygen will then generate output that is more tailored for C. For +# instance, some of the names that are used will be different. The list of all +# members will be omitted, etc. +# The default value is: NO. OPTIMIZE_OUTPUT_FOR_C = YES -# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java -# sources only. Doxygen will then generate output that is more tailored for -# Java. For instance, namespaces will be presented as packages, qualified -# scopes will look different, etc. +# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java or +# Python sources only. Doxygen will then generate output that is more tailored +# for that language. For instance, namespaces will be presented as packages, +# qualified scopes will look different, etc. +# The default value is: NO. OPTIMIZE_OUTPUT_JAVA = NO # Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran -# sources only. Doxygen will then generate output that is more tailored for -# Fortran. +# sources. Doxygen will then generate output that is tailored for Fortran. +# The default value is: NO. OPTIMIZE_FOR_FORTRAN = NO # Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL -# sources. Doxygen will then generate output that is tailored for -# VHDL. +# sources. Doxygen will then generate output that is tailored for VHDL. +# The default value is: NO. OPTIMIZE_OUTPUT_VHDL = NO +# Set the OPTIMIZE_OUTPUT_SLICE tag to YES if your project consists of Slice +# sources only. Doxygen will then generate output that is more tailored for that +# language. For instance, namespaces will be presented as modules, types will be +# separated into more groups, etc. +# The default value is: NO. + +OPTIMIZE_OUTPUT_SLICE = NO + # Doxygen selects the parser to use depending on the extension of the files it -# parses. With this tag you can assign which parser to use for a given extension. -# Doxygen has a built-in mapping, but you can override or extend it using this -# tag. The format is ext=language, where ext is a file extension, and language -# is one of the parsers supported by doxygen: IDL, Java, Javascript, CSharp, C, -# C++, D, PHP, Objective-C, Python, Fortran, VHDL, C, C++. For instance to make -# doxygen treat .inc files as Fortran files (default is PHP), and .f files as C -# (default is Fortran), use: inc=Fortran f=C. Note that for custom extensions -# you also need to set FILE_PATTERNS otherwise the files are not read by doxygen. +# parses. With this tag you can assign which parser to use for a given +# extension. Doxygen has a built-in mapping, but you can override or extend it +# using this tag. The format is ext=language, where ext is a file extension, and +# language is one of the parsers supported by doxygen: IDL, Java, JavaScript, +# Csharp (C#), C, C++, Lex, D, PHP, md (Markdown), Objective-C, Python, Slice, +# VHDL, Fortran (fixed format Fortran: FortranFixed, free formatted Fortran: +# FortranFree, unknown formatted Fortran: Fortran. In the later case the parser +# tries to guess whether the code is fixed or free formatted code, this is the +# default for Fortran type files). For instance to make doxygen treat .inc files +# as Fortran files (default is PHP), and .f files as C (default is Fortran), +# use: inc=Fortran f=C. +# +# Note: For files without extension you can use no_extension as a placeholder. +# +# Note that for custom extensions you also need to set FILE_PATTERNS otherwise +# the files are not read by doxygen. When specifying no_extension you should add +# * to the FILE_PATTERNS. +# +# Note see also the list of default file extension mappings. EXTENSION_MAPPING = +# If the MARKDOWN_SUPPORT tag is enabled then doxygen pre-processes all comments +# according to the Markdown format, which allows for more readable +# documentation. See https://daringfireball.net/projects/markdown/ for details. +# The output of markdown processing is further processed by doxygen, so you can +# mix doxygen, HTML, and XML commands with Markdown formatting. Disable only in +# case of backward compatibilities issues. +# The default value is: YES. + +MARKDOWN_SUPPORT = YES + +# When the TOC_INCLUDE_HEADINGS tag is set to a non-zero value, all headings up +# to that level are automatically included in the table of contents, even if +# they do not have an id attribute. +# Note: This feature currently applies only to Markdown headings. +# Minimum value: 0, maximum value: 99, default value: 5. +# This tag requires that the tag MARKDOWN_SUPPORT is set to YES. + +TOC_INCLUDE_HEADINGS = 5 + +# The MARKDOWN_ID_STYLE tag can be used to specify the algorithm used to +# generate identifiers for the Markdown headings. Note: Every identifier is +# unique. +# Possible values are: DOXYGEN Use a fixed 'autotoc_md' string followed by a +# sequence number starting at 0. and GITHUB Use the lower case version of title +# with any whitespace replaced by '-' and punctations characters removed.. +# The default value is: DOXYGEN. +# This tag requires that the tag MARKDOWN_SUPPORT is set to YES. + +MARKDOWN_ID_STYLE = DOXYGEN + +# When enabled doxygen tries to link words that correspond to documented +# classes, or namespaces to their corresponding documentation. Such a link can +# be prevented in individual cases by putting a % sign in front of the word or +# globally by setting AUTOLINK_SUPPORT to NO. +# The default value is: YES. + +AUTOLINK_SUPPORT = YES + # If you use STL classes (i.e. std::string, std::vector, etc.) but do not want -# to include (a tag file for) the STL sources as input, then you should -# set this tag to YES in order to let doxygen match functions declarations and -# definitions whose arguments contain STL classes (e.g. func(std::string); v.s. -# func(std::string) {}). This also make the inheritance and collaboration +# to include (a tag file for) the STL sources as input, then you should set this +# tag to YES in order to let doxygen match functions declarations and +# definitions whose arguments contain STL classes (e.g. func(std::string); +# versus func(std::string) {}). This also make the inheritance and collaboration # diagrams that involve STL classes more complete and accurate. +# The default value is: NO. BUILTIN_STL_SUPPORT = NO # If you use Microsoft's C++/CLI language, you should set this option to YES to # enable parsing support. +# The default value is: NO. CPP_CLI_SUPPORT = NO -# Set the SIP_SUPPORT tag to YES if your project consists of sip sources only. -# Doxygen will parse them like normal C++ but will assume all classes use public -# instead of private inheritance when no explicit protection keyword is present. +# Set the SIP_SUPPORT tag to YES if your project consists of sip (see: +# https://www.riverbankcomputing.com/software/sip/intro) sources only. Doxygen +# will parse them like normal C++ but will assume all classes use public instead +# of private inheritance when no explicit protection keyword is present. +# The default value is: NO. SIP_SUPPORT = NO -# For Microsoft's IDL there are propget and propput attributes to indicate getter -# and setter methods for a property. Setting this option to YES (the default) -# will make doxygen to replace the get and set methods by a property in the -# documentation. This will only work if the methods are indeed getting or -# setting a simple type. If this is not the case, or you want to show the -# methods anyway, you should set this option to NO. +# For Microsoft's IDL there are propget and propput attributes to indicate +# getter and setter methods for a property. Setting this option to YES will make +# doxygen to replace the get and set methods by a property in the documentation. +# This will only work if the methods are indeed getting or setting a simple +# type. If this is not the case, or you want to show the methods anyway, you +# should set this option to NO. +# The default value is: YES. IDL_PROPERTY_SUPPORT = YES # If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC -# tag is set to YES, then doxygen will reuse the documentation of the first +# tag is set to YES then doxygen will reuse the documentation of the first # member in the group (if any) for the other members of the group. By default # all members of a group must be documented explicitly. +# The default value is: NO. DISTRIBUTE_GROUP_DOC = NO -# Set the SUBGROUPING tag to YES (the default) to allow class member groups of -# the same type (for instance a group of public functions) to be put as a -# subgroup of that type (e.g. under the Public Functions section). Set it to -# NO to prevent subgrouping. Alternatively, this can be done per class using -# the \nosubgrouping command. +# If one adds a struct or class to a group and this option is enabled, then also +# any nested class or struct is added to the same group. By default this option +# is disabled and one has to add nested compounds explicitly via \ingroup. +# The default value is: NO. + +GROUP_NESTED_COMPOUNDS = NO + +# Set the SUBGROUPING tag to YES to allow class member groups of the same type +# (for instance a group of public functions) to be put as a subgroup of that +# type (e.g. under the Public Functions section). Set it to NO to prevent +# subgrouping. Alternatively, this can be done per class using the +# \nosubgrouping command. +# The default value is: YES. SUBGROUPING = YES -# When TYPEDEF_HIDES_STRUCT is enabled, a typedef of a struct, union, or enum -# is documented as struct, union, or enum with the name of the typedef. So +# When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and unions +# are shown inside the group in which they are included (e.g. using \ingroup) +# instead of on a separate page (for HTML and Man pages) or section (for LaTeX +# and RTF). +# +# Note that this feature does not work in combination with +# SEPARATE_MEMBER_PAGES. +# The default value is: NO. + +INLINE_GROUPED_CLASSES = NO + +# When the INLINE_SIMPLE_STRUCTS tag is set to YES, structs, classes, and unions +# with only public data fields or simple typedef fields will be shown inline in +# the documentation of the scope in which they are defined (i.e. file, +# namespace, or group documentation), provided this scope is documented. If set +# to NO, structs, classes, and unions are shown on a separate page (for HTML and +# Man pages) or section (for LaTeX and RTF). +# The default value is: NO. + +INLINE_SIMPLE_STRUCTS = NO + +# When TYPEDEF_HIDES_STRUCT tag is enabled, a typedef of a struct, union, or +# enum is documented as struct, union, or enum with the name of the typedef. So # typedef struct TypeS {} TypeT, will appear in the documentation as a struct # with name TypeT. When disabled the typedef will appear as a member of a file, -# namespace, or class. And the struct will be named TypeS. This can typically -# be useful for C code in case the coding convention dictates that all compound +# namespace, or class. And the struct will be named TypeS. This can typically be +# useful for C code in case the coding convention dictates that all compound # types are typedef'ed and only the typedef is referenced, never the tag name. +# The default value is: NO. TYPEDEF_HIDES_STRUCT = NO -# The SYMBOL_CACHE_SIZE determines the size of the internal cache use to -# determine which symbols to keep in memory and which to flush to disk. -# When the cache is full, less often used symbols will be written to disk. -# For small to medium size projects (<1000 input files) the default value is -# probably good enough. For larger projects a too small cache size can cause -# doxygen to be busy swapping symbols to and from disk most of the time -# causing a significant performance penalty. -# If the system has enough physical memory increasing the cache will improve the -# performance by keeping more symbols in memory. Note that the value works on -# a logarithmic scale so increasing the size by one will roughly double the -# memory usage. The cache size is given by this formula: -# 2^(16+SYMBOL_CACHE_SIZE). The valid range is 0..9, the default is 0, -# corresponding to a cache size of 2^16 = 65536 symbols +# The size of the symbol lookup cache can be set using LOOKUP_CACHE_SIZE. This +# cache is used to resolve symbols given their name and scope. Since this can be +# an expensive process and often the same symbol appears multiple times in the +# code, doxygen keeps a cache of pre-resolved symbols. If the cache is too small +# doxygen will become slower. If the cache is too large, memory is wasted. The +# cache size is given by this formula: 2^(16+LOOKUP_CACHE_SIZE). The valid range +# is 0..9, the default is 0, corresponding to a cache size of 2^16=65536 +# symbols. At the end of a run doxygen will report the cache usage and suggest +# the optimal cache size from a speed point of view. +# Minimum value: 0, maximum value: 9, default value: 0. + +LOOKUP_CACHE_SIZE = 0 + +# The NUM_PROC_THREADS specifies the number of threads doxygen is allowed to use +# during processing. When set to 0 doxygen will based this on the number of +# cores available in the system. You can set it explicitly to a value larger +# than 0 to get more control over the balance between CPU load and processing +# speed. At this moment only the input processing can be done using multiple +# threads. Since this is still an experimental feature the default is set to 1, +# which effectively disables parallel processing. Please report any issues you +# encounter. Generating dot graphs in parallel is controlled by the +# DOT_NUM_THREADS setting. +# Minimum value: 0, maximum value: 32, default value: 1. + +NUM_PROC_THREADS = 1 + +# If the TIMESTAMP tag is set different from NO then each generated page will +# contain the date or date and time when the page was generated. Setting this to +# NO can help when comparing the output of multiple runs. +# Possible values are: YES, NO, DATETIME and DATE. +# The default value is: NO. -#SYMBOL_CACHE_SIZE = 0 +TIMESTAMP = YES #--------------------------------------------------------------------------- # Build related configuration options #--------------------------------------------------------------------------- -# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in -# documentation are documented, even if no documentation was available. -# Private class members and static file members will be hidden unless -# the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES +# If the EXTRACT_ALL tag is set to YES, doxygen will assume all entities in +# documentation are documented, even if no documentation was available. Private +# class members and static file members will be hidden unless the +# EXTRACT_PRIVATE respectively EXTRACT_STATIC tags are set to YES. +# Note: This will also disable the warnings about undocumented members that are +# normally produced when WARNINGS is set to YES. +# The default value is: NO. EXTRACT_ALL = NO -# If the EXTRACT_PRIVATE tag is set to YES all private members of a class -# will be included in the documentation. +# If the EXTRACT_PRIVATE tag is set to YES, all private members of a class will +# be included in the documentation. +# The default value is: NO. EXTRACT_PRIVATE = YES -# If the EXTRACT_STATIC tag is set to YES all static members of a file -# will be included in the documentation. +# If the EXTRACT_PRIV_VIRTUAL tag is set to YES, documented private virtual +# methods of a class will be included in the documentation. +# The default value is: NO. + +EXTRACT_PRIV_VIRTUAL = NO + +# If the EXTRACT_PACKAGE tag is set to YES, all members with package or internal +# scope will be included in the documentation. +# The default value is: NO. + +EXTRACT_PACKAGE = NO + +# If the EXTRACT_STATIC tag is set to YES, all static members of a file will be +# included in the documentation. +# The default value is: NO. EXTRACT_STATIC = YES -# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) -# defined locally in source files will be included in the documentation. -# If set to NO only classes defined in header files are included. +# If the EXTRACT_LOCAL_CLASSES tag is set to YES, classes (and structs) defined +# locally in source files will be included in the documentation. If set to NO, +# only classes defined in header files are included. Does not have any effect +# for Java sources. +# The default value is: YES. EXTRACT_LOCAL_CLASSES = YES -# This flag is only useful for Objective-C code. When set to YES local -# methods, which are defined in the implementation section but not in -# the interface are included in the documentation. -# If set to NO (the default) only methods in the interface are included. +# This flag is only useful for Objective-C code. If set to YES, local methods, +# which are defined in the implementation section but not in the interface are +# included in the documentation. If set to NO, only methods in the interface are +# included. +# The default value is: NO. EXTRACT_LOCAL_METHODS = YES # If this flag is set to YES, the members of anonymous namespaces will be # extracted and appear in the documentation as a namespace called -# 'anonymous_namespace{file}', where file will be replaced with the base -# name of the file that contains the anonymous namespace. By default -# anonymous namespace are hidden. +# 'anonymous_namespace{file}', where file will be replaced with the base name of +# the file that contains the anonymous namespace. By default anonymous namespace +# are hidden. +# The default value is: NO. EXTRACT_ANON_NSPACES = NO -# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all -# undocumented members of documented classes, files or namespaces. -# If set to NO (the default) these members will be included in the -# various overviews, but no documentation section is generated. -# This option has no effect if EXTRACT_ALL is enabled. +# If this flag is set to YES, the name of an unnamed parameter in a declaration +# will be determined by the corresponding definition. By default unnamed +# parameters remain unnamed in the output. +# The default value is: YES. + +RESOLVE_UNNAMED_PARAMS = YES + +# If the HIDE_UNDOC_MEMBERS tag is set to YES, doxygen will hide all +# undocumented members inside documented classes or files. If set to NO these +# members will be included in the various overviews, but no documentation +# section is generated. This option has no effect if EXTRACT_ALL is enabled. +# The default value is: NO. HIDE_UNDOC_MEMBERS = NO -# If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all -# undocumented classes that are normally visible in the class hierarchy. -# If set to NO (the default) these classes will be included in the various -# overviews. This option has no effect if EXTRACT_ALL is enabled. +# If the HIDE_UNDOC_CLASSES tag is set to YES, doxygen will hide all +# undocumented classes that are normally visible in the class hierarchy. If set +# to NO, these classes will be included in the various overviews. This option +# will also hide undocumented C++ concepts if enabled. This option has no effect +# if EXTRACT_ALL is enabled. +# The default value is: NO. HIDE_UNDOC_CLASSES = NO -# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all -# friend (class|struct|union) declarations. -# If set to NO (the default) these declarations will be included in the +# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, doxygen will hide all friend +# declarations. If set to NO, these declarations will be included in the # documentation. +# The default value is: NO. HIDE_FRIEND_COMPOUNDS = NO -# If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any -# documentation blocks found inside the body of a function. -# If set to NO (the default) these blocks will be appended to the -# function's detailed documentation block. +# If the HIDE_IN_BODY_DOCS tag is set to YES, doxygen will hide any +# documentation blocks found inside the body of a function. If set to NO, these +# blocks will be appended to the function's detailed documentation block. +# The default value is: NO. HIDE_IN_BODY_DOCS = NO -# The INTERNAL_DOCS tag determines if documentation -# that is typed after a \internal command is included. If the tag is set -# to NO (the default) then the documentation will be excluded. -# Set it to YES to include the internal documentation. +# The INTERNAL_DOCS tag determines if documentation that is typed after a +# \internal command is included. If the tag is set to NO then the documentation +# will be excluded. Set it to YES to include the internal documentation. +# The default value is: NO. INTERNAL_DOCS = NO -# If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate -# file names in lower-case letters. If set to YES upper-case letters are also -# allowed. This is useful if you have classes or files whose names only differ -# in case and if your file system supports case sensitive file names. Windows -# and Mac users are advised to set this option to NO. +# With the correct setting of option CASE_SENSE_NAMES doxygen will better be +# able to match the capabilities of the underlying filesystem. In case the +# filesystem is case sensitive (i.e. it supports files in the same directory +# whose names only differ in casing), the option must be set to YES to properly +# deal with such files in case they appear in the input. For filesystems that +# are not case sensitive the option should be set to NO to properly deal with +# output files written for symbols that only differ in casing, such as for two +# classes, one named CLASS and the other named Class, and to also support +# references to files without having to specify the exact matching casing. On +# Windows (including Cygwin) and MacOS, users should typically set this option +# to NO, whereas on Linux or other Unix flavors it should typically be set to +# YES. +# Possible values are: SYSTEM, NO and YES. +# The default value is: SYSTEM. CASE_SENSE_NAMES = YES -# If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen -# will show members with their full class and namespace scopes in the -# documentation. If set to YES the scope will be hidden. +# If the HIDE_SCOPE_NAMES tag is set to NO then doxygen will show members with +# their full class and namespace scopes in the documentation. If set to YES, the +# scope will be hidden. +# The default value is: NO. HIDE_SCOPE_NAMES = NO -# If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen -# will put a list of the files that are included by a file in the documentation -# of that file. +# If the HIDE_COMPOUND_REFERENCE tag is set to NO (default) then doxygen will +# append additional text to a page's title, such as Class Reference. If set to +# YES the compound reference will be hidden. +# The default value is: NO. + +HIDE_COMPOUND_REFERENCE= NO + +# If the SHOW_HEADERFILE tag is set to YES then the documentation for a class +# will show which file needs to be included to use the class. +# The default value is: YES. + +SHOW_HEADERFILE = YES + +# If the SHOW_INCLUDE_FILES tag is set to YES then doxygen will put a list of +# the files that are included by a file in the documentation of that file. +# The default value is: YES. SHOW_INCLUDE_FILES = YES -# If the FORCE_LOCAL_INCLUDES tag is set to YES then Doxygen -# will list include files with double quotes in the documentation -# rather than with sharp brackets. +# If the SHOW_GROUPED_MEMB_INC tag is set to YES then Doxygen will add for each +# grouped member an include statement to the documentation, telling the reader +# which file to include in order to use the member. +# The default value is: NO. + +SHOW_GROUPED_MEMB_INC = NO + +# If the FORCE_LOCAL_INCLUDES tag is set to YES then doxygen will list include +# files with double quotes in the documentation rather than with sharp brackets. +# The default value is: NO. FORCE_LOCAL_INCLUDES = NO -# If the INLINE_INFO tag is set to YES (the default) then a tag [inline] -# is inserted in the documentation for inline members. +# If the INLINE_INFO tag is set to YES then a tag [inline] is inserted in the +# documentation for inline members. +# The default value is: YES. INLINE_INFO = YES -# If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen -# will sort the (detailed) documentation of file and class members -# alphabetically by member name. If set to NO the members will appear in -# declaration order. +# If the SORT_MEMBER_DOCS tag is set to YES then doxygen will sort the +# (detailed) documentation of file and class members alphabetically by member +# name. If set to NO, the members will appear in declaration order. +# The default value is: YES. SORT_MEMBER_DOCS = NO -# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the -# brief documentation of file, namespace and class members alphabetically -# by member name. If set to NO (the default) the members will appear in -# declaration order. +# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the brief +# descriptions of file, namespace and class members alphabetically by member +# name. If set to NO, the members will appear in declaration order. Note that +# this will also influence the order of the classes in the class list. +# The default value is: NO. SORT_BRIEF_DOCS = NO -# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen -# will sort the (brief and detailed) documentation of class members so that -# constructors and destructors are listed first. If set to NO (the default) -# the constructors will appear in the respective orders defined by -# SORT_MEMBER_DOCS and SORT_BRIEF_DOCS. -# This tag will be ignored for brief docs if SORT_BRIEF_DOCS is set to NO -# and ignored for detailed docs if SORT_MEMBER_DOCS is set to NO. +# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen will sort the +# (brief and detailed) documentation of class members so that constructors and +# destructors are listed first. If set to NO the constructors will appear in the +# respective orders defined by SORT_BRIEF_DOCS and SORT_MEMBER_DOCS. +# Note: If SORT_BRIEF_DOCS is set to NO this option is ignored for sorting brief +# member documentation. +# Note: If SORT_MEMBER_DOCS is set to NO this option is ignored for sorting +# detailed member documentation. +# The default value is: NO. SORT_MEMBERS_CTORS_1ST = NO -# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the -# hierarchy of group names into alphabetical order. If set to NO (the default) -# the group names will appear in their defined order. +# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the hierarchy +# of group names into alphabetical order. If set to NO the group names will +# appear in their defined order. +# The default value is: NO. SORT_GROUP_NAMES = NO -# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be -# sorted by fully-qualified names, including namespaces. If set to -# NO (the default), the class list will be sorted only by class name, -# not including the namespace part. +# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be sorted by +# fully-qualified names, including namespaces. If set to NO, the class list will +# be sorted only by class name, not including the namespace part. # Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. -# Note: This option applies only to the class list, not to the -# alphabetical list. +# Note: This option applies only to the class list, not to the alphabetical +# list. +# The default value is: NO. SORT_BY_SCOPE_NAME = NO -# The GENERATE_TODOLIST tag can be used to enable (YES) or -# disable (NO) the todo list. This list is created by putting \todo -# commands in the documentation. +# If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to do proper +# type resolution of all parameters of a function it will reject a match between +# the prototype and the implementation of a member function even if there is +# only one candidate or it is obvious which candidate to choose by doing a +# simple string match. By disabling STRICT_PROTO_MATCHING doxygen will still +# accept a match between prototype and implementation in such cases. +# The default value is: NO. + +STRICT_PROTO_MATCHING = NO + +# The GENERATE_TODOLIST tag can be used to enable (YES) or disable (NO) the todo +# list. This list is created by putting \todo commands in the documentation. +# The default value is: YES. GENERATE_TODOLIST = YES -# The GENERATE_TESTLIST tag can be used to enable (YES) or -# disable (NO) the test list. This list is created by putting \test -# commands in the documentation. +# The GENERATE_TESTLIST tag can be used to enable (YES) or disable (NO) the test +# list. This list is created by putting \test commands in the documentation. +# The default value is: YES. GENERATE_TESTLIST = YES -# The GENERATE_BUGLIST tag can be used to enable (YES) or -# disable (NO) the bug list. This list is created by putting \bug -# commands in the documentation. +# The GENERATE_BUGLIST tag can be used to enable (YES) or disable (NO) the bug +# list. This list is created by putting \bug commands in the documentation. +# The default value is: YES. GENERATE_BUGLIST = YES -# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or -# disable (NO) the deprecated list. This list is created by putting -# \deprecated commands in the documentation. +# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or disable (NO) +# the deprecated list. This list is created by putting \deprecated commands in +# the documentation. +# The default value is: YES. GENERATE_DEPRECATEDLIST= YES -# The ENABLED_SECTIONS tag can be used to enable conditional -# documentation sections, marked by \if sectionname ... \endif. +# The ENABLED_SECTIONS tag can be used to enable conditional documentation +# sections, marked by \if ... \endif and \cond +# ... \endcond blocks. ENABLED_SECTIONS = -# The MAX_INITIALIZER_LINES tag determines the maximum number of lines -# the initial value of a variable or define consists of for it to appear in -# the documentation. If the initializer consists of more lines than specified -# here it will be hidden. Use a value of 0 to hide initializers completely. -# The appearance of the initializer of individual variables and defines in the -# documentation can be controlled using \showinitializer or \hideinitializer -# command in the documentation regardless of this setting. +# The MAX_INITIALIZER_LINES tag determines the maximum number of lines that the +# initial value of a variable or macro / define can have for it to appear in the +# documentation. If the initializer consists of more lines than specified here +# it will be hidden. Use a value of 0 to hide initializers completely. The +# appearance of the value of individual variables and macros / defines can be +# controlled using \showinitializer or \hideinitializer command in the +# documentation regardless of this setting. +# Minimum value: 0, maximum value: 10000, default value: 30. MAX_INITIALIZER_LINES = 30 -# Set the SHOW_USED_FILES tag to NO to disable the list of files generated -# at the bottom of the documentation of classes and structs. If set to YES the +# Set the SHOW_USED_FILES tag to NO to disable the list of files generated at +# the bottom of the documentation of classes and structs. If set to YES, the # list will mention the files that were used to generate the documentation. +# The default value is: YES. SHOW_USED_FILES = YES -# If the sources in your project are distributed over multiple directories -# then setting the SHOW_DIRECTORIES tag to YES will show the directory hierarchy -# in the documentation. The default is NO. - -#SHOW_DIRECTORIES = YES - -# Set the SHOW_FILES tag to NO to disable the generation of the Files page. -# This will remove the Files entry from the Quick Index and from the -# Folder Tree View (if specified). The default is YES. +# Set the SHOW_FILES tag to NO to disable the generation of the Files page. This +# will remove the Files entry from the Quick Index and from the Folder Tree View +# (if specified). +# The default value is: YES. SHOW_FILES = YES -# Set the SHOW_NAMESPACES tag to NO to disable the generation of the -# Namespaces page. -# This will remove the Namespaces entry from the Quick Index -# and from the Folder Tree View (if specified). The default is YES. +# Set the SHOW_NAMESPACES tag to NO to disable the generation of the Namespaces +# page. This will remove the Namespaces entry from the Quick Index and from the +# Folder Tree View (if specified). +# The default value is: YES. SHOW_NAMESPACES = YES # The FILE_VERSION_FILTER tag can be used to specify a program or script that # doxygen should invoke to get the current version for each file (typically from # the version control system). Doxygen will invoke the program by executing (via -# popen()) the command , where is the value of -# the FILE_VERSION_FILTER tag, and is the name of an input file -# provided by doxygen. Whatever the program writes to standard output -# is used as the file version. See the manual for examples. +# popen()) the command command input-file, where command is the value of the +# FILE_VERSION_FILTER tag, and input-file is the name of an input file provided +# by doxygen. Whatever the program writes to standard output is used as the file +# version. For an example see the documentation. FILE_VERSION_FILTER = # The LAYOUT_FILE tag can be used to specify a layout file which will be parsed # by doxygen. The layout file controls the global structure of the generated -# output files in an output format independent way. The create the layout file -# that represents doxygen's defaults, run doxygen with the -l option. -# You can optionally specify a file name after the option, if omitted -# DoxygenLayout.xml will be used as the name of the layout file. +# output files in an output format independent way. To create the layout file +# that represents doxygen's defaults, run doxygen with the -l option. You can +# optionally specify a file name after the option, if omitted DoxygenLayout.xml +# will be used as the name of the layout file. See also section "Changing the +# layout of pages" for information. +# +# Note that if you run doxygen from a directory containing a file called +# DoxygenLayout.xml, doxygen will parse it automatically even if the LAYOUT_FILE +# tag is left empty. LAYOUT_FILE = +# The CITE_BIB_FILES tag can be used to specify one or more bib files containing +# the reference definitions. This must be a list of .bib files. The .bib +# extension is automatically appended if omitted. This requires the bibtex tool +# to be installed. See also https://en.wikipedia.org/wiki/BibTeX for more info. +# For LaTeX the style of the bibliography can be controlled using +# LATEX_BIB_STYLE. To use this feature you need bibtex and perl available in the +# search path. See also \cite for info how to create references. + +CITE_BIB_FILES = + #--------------------------------------------------------------------------- -# configuration options related to warning and progress messages +# Configuration options related to warning and progress messages #--------------------------------------------------------------------------- -# The QUIET tag can be used to turn on/off the messages that are generated -# by doxygen. Possible values are YES and NO. If left blank NO is used. +# The QUIET tag can be used to turn on/off the messages that are generated to +# standard output by doxygen. If QUIET is set to YES this implies that the +# messages are off. +# The default value is: NO. QUIET = YES # The WARNINGS tag can be used to turn on/off the warning messages that are -# generated by doxygen. Possible values are YES and NO. If left blank -# NO is used. +# generated to standard error (stderr) by doxygen. If WARNINGS is set to YES +# this implies that the warnings are on. +# +# Tip: Turn warnings on while writing the documentation. +# The default value is: YES. WARNINGS = YES -# If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings -# for undocumented members. If EXTRACT_ALL is set to YES then this flag will -# automatically be disabled. +# If the WARN_IF_UNDOCUMENTED tag is set to YES then doxygen will generate +# warnings for undocumented members. If EXTRACT_ALL is set to YES then this flag +# will automatically be disabled. +# The default value is: YES. WARN_IF_UNDOCUMENTED = NO -# If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for -# potential errors in the documentation, such as not documenting some -# parameters in a documented function, or documenting parameters that -# don't exist or using markup commands wrongly. +# If the WARN_IF_DOC_ERROR tag is set to YES, doxygen will generate warnings for +# potential errors in the documentation, such as documenting some parameters in +# a documented function twice, or documenting parameters that don't exist or +# using markup commands wrongly. +# The default value is: YES. WARN_IF_DOC_ERROR = YES -# This WARN_NO_PARAMDOC option can be abled to get warnings for -# functions that are documented, but have no documentation for their parameters -# or return value. If set to NO (the default) doxygen will only warn about -# wrong or incomplete parameter documentation, but not about the absence of -# documentation. +# If WARN_IF_INCOMPLETE_DOC is set to YES, doxygen will warn about incomplete +# function parameter documentation. If set to NO, doxygen will accept that some +# parameters have no documentation without warning. +# The default value is: YES. + +WARN_IF_INCOMPLETE_DOC = YES + +# This WARN_NO_PARAMDOC option can be enabled to get warnings for functions that +# are documented, but have no documentation for their parameters or return +# value. If set to NO, doxygen will only warn about wrong parameter +# documentation, but not about the absence of documentation. If EXTRACT_ALL is +# set to YES then this flag will automatically be disabled. See also +# WARN_IF_INCOMPLETE_DOC +# The default value is: NO. WARN_NO_PARAMDOC = YES -# The WARN_FORMAT tag determines the format of the warning messages that -# doxygen can produce. The string should contain the $file, $line, and $text -# tags, which will be replaced by the file and line number from which the -# warning originated and the warning text. Optionally the format may contain -# $version, which will be replaced by the version of the file (if it could -# be obtained via FILE_VERSION_FILTER) +# If WARN_IF_UNDOC_ENUM_VAL option is set to YES, doxygen will warn about +# undocumented enumeration values. If set to NO, doxygen will accept +# undocumented enumeration values. If EXTRACT_ALL is set to YES then this flag +# will automatically be disabled. +# The default value is: NO. + +WARN_IF_UNDOC_ENUM_VAL = NO + +# If the WARN_AS_ERROR tag is set to YES then doxygen will immediately stop when +# a warning is encountered. If the WARN_AS_ERROR tag is set to FAIL_ON_WARNINGS +# then doxygen will continue running as if WARN_AS_ERROR tag is set to NO, but +# at the end of the doxygen process doxygen will return with a non-zero status. +# If the WARN_AS_ERROR tag is set to FAIL_ON_WARNINGS_PRINT then doxygen behaves +# like FAIL_ON_WARNINGS but in case no WARN_LOGFILE is defined doxygen will not +# write the warning messages in between other messages but write them at the end +# of a run, in case a WARN_LOGFILE is defined the warning messages will be +# besides being in the defined file also be shown at the end of a run, unless +# the WARN_LOGFILE is defined as - i.e. standard output (stdout) in that case +# the behavior will remain as with the setting FAIL_ON_WARNINGS. +# Possible values are: NO, YES, FAIL_ON_WARNINGS and FAIL_ON_WARNINGS_PRINT. +# The default value is: NO. + +WARN_AS_ERROR = NO + +# The WARN_FORMAT tag determines the format of the warning messages that doxygen +# can produce. The string should contain the $file, $line, and $text tags, which +# will be replaced by the file and line number from which the warning originated +# and the warning text. Optionally the format may contain $version, which will +# be replaced by the version of the file (if it could be obtained via +# FILE_VERSION_FILTER) +# See also: WARN_LINE_FORMAT +# The default value is: $file:$line: $text. WARN_FORMAT = "$file:$line: $text" -# The WARN_LOGFILE tag can be used to specify a file to which warning -# and error messages should be written. If left blank the output is written -# to stderr. +# In the $text part of the WARN_FORMAT command it is possible that a reference +# to a more specific place is given. To make it easier to jump to this place +# (outside of doxygen) the user can define a custom "cut" / "paste" string. +# Example: +# WARN_LINE_FORMAT = "'vi $file +$line'" +# See also: WARN_FORMAT +# The default value is: at line $line of file $file. + +WARN_LINE_FORMAT = "at line $line of file $file" + +# The WARN_LOGFILE tag can be used to specify a file to which warning and error +# messages should be written. If left blank the output is written to standard +# error (stderr). In case the file specified cannot be opened for writing the +# warning and error messages are written to standard error. When as file - is +# specified the warning and error messages are written to standard output +# (stdout). WARN_LOGFILE = #--------------------------------------------------------------------------- -# configuration options related to the input files +# Configuration options related to the input files #--------------------------------------------------------------------------- -# The INPUT tag can be used to specify the files and/or directories that contain -# documented source files. You may enter file names like "myfile.cpp" or -# directories like "/usr/src/myproject". Separate the files or directories -# with spaces. +# The INPUT tag is used to specify the files and/or directories that contain +# documented source files. You may enter file names like myfile.cpp or +# directories like /usr/src/myproject. Separate the files or directories with +# spaces. See also FILE_PATTERNS and EXTENSION_MAPPING +# Note: If this tag is empty the current directory is searched. INPUT = . # This tag can be used to specify the character encoding of the source files -# that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is -# also the default input encoding. Doxygen uses libiconv (or the iconv built -# into libc) for the transcoding. See http://www.gnu.org/software/libiconv for -# the list of possible encodings. +# that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses +# libiconv (or the iconv built into libc) for the transcoding. See the libiconv +# documentation (see: +# https://www.gnu.org/software/libiconv/) for the list of possible encodings. +# See also: INPUT_FILE_ENCODING +# The default value is: UTF-8. INPUT_ENCODING = UTF-8 +# This tag can be used to specify the character encoding of the source files +# that doxygen parses The INPUT_FILE_ENCODING tag can be used to specify +# character encoding on a per file pattern basis. Doxygen will compare the file +# name with each pattern and apply the encoding instead of the default +# INPUT_ENCODING) if there is a match. The character encodings are a list of the +# form: pattern=encoding (like *.php=ISO-8859-1). See cfg_input_encoding +# "INPUT_ENCODING" for further information on supported encodings. + +INPUT_FILE_ENCODING = + # If the value of the INPUT tag contains directories, you can use the -# FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp -# and *.h) to filter out the source-files in the directories. If left -# blank the following patterns are tested: -# *.c *.cc *.cxx *.cpp *.c++ *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh *.hxx -# *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.py *.f90 +# FILE_PATTERNS tag to specify one or more wildcard patterns (like *.cpp and +# *.h) to filter out the source-files in the directories. +# +# Note that for custom extensions or not directly supported extensions you also +# need to set EXTENSION_MAPPING for the extension otherwise the files are not +# read by doxygen. +# +# Note the list of default checked file patterns might differ from the list of +# default file extension mappings. +# +# If left blank the following patterns are tested:*.c, *.cc, *.cxx, *.cpp, +# *.c++, *.java, *.ii, *.ixx, *.ipp, *.i++, *.inl, *.idl, *.ddl, *.odl, *.h, +# *.hh, *.hxx, *.hpp, *.h++, *.l, *.cs, *.d, *.php, *.php4, *.php5, *.phtml, +# *.inc, *.m, *.markdown, *.md, *.mm, *.dox (to be provided as doxygen C +# comment), *.py, *.pyw, *.f90, *.f95, *.f03, *.f08, *.f18, *.f, *.for, *.vhd, +# *.vhdl, *.ucf, *.qsf and *.ice. FILE_PATTERNS = -# The RECURSIVE tag can be used to turn specify whether or not subdirectories -# should be searched for input files as well. Possible values are YES and NO. -# If left blank NO is used. +# The RECURSIVE tag can be used to specify whether or not subdirectories should +# be searched for input files as well. +# The default value is: NO. RECURSIVE = YES -# The EXCLUDE tag can be used to specify files and/or directories that should +# The EXCLUDE tag can be used to specify files and/or directories that should be # excluded from the INPUT source files. This way you can easily exclude a # subdirectory from a directory tree whose root is specified with the INPUT tag. +# +# Note that relative paths are relative to the directory from which doxygen is +# run. EXCLUDE = ./build \ ./compat \ - ./contrib \ + ./contrib \ util/configparser.c \ util/configparser.h \ util/configlexer.c \ @@ -623,27 +1001,29 @@ EXCLUDE = ./build \ pythonmod/interface.h \ pythonmod/ubmodule-msg.py \ pythonmod/ubmodule-tst.py \ - unboundmodule.py \ + unboundmodule.py \ libunbound/python/unbound.py \ libunbound/python/libunbound_wrap.c \ libunbound/python/doc \ libunbound/python/examples \ ./ldns-src \ - README.md \ - doc/control_proto_spec.txt \ - doc/requirements.txt + README.md \ + doc/control_proto_spec.txt \ + doc/requirements.txt -# The EXCLUDE_SYMLINKS tag can be used select whether or not files or -# directories that are symbolic links (a Unix filesystem feature) are excluded +# The EXCLUDE_SYMLINKS tag can be used to select whether or not files or +# directories that are symbolic links (a Unix file system feature) are excluded # from the input. +# The default value is: NO. EXCLUDE_SYMLINKS = NO # If the value of the INPUT tag contains directories, you can use the # EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude -# certain files from those directories. Note that the wildcards are matched -# against the file with absolute path, so to exclude all test directories -# for example use the pattern */test/* +# certain files from those directories. +# +# Note that the wildcards are matched against the file with absolute path, so to +# exclude all test directories for example use the pattern */test/* EXCLUDE_PATTERNS = @@ -651,694 +1031,1273 @@ EXCLUDE_PATTERNS = # (namespaces, classes, functions, etc.) that should be excluded from the # output. The symbol name can be a fully qualified name, a word, or if the # wildcard * is used, a substring. Examples: ANamespace, AClass, -# AClass::ANamespace, ANamespace::*Test +# ANamespace::AClass, ANamespace::*Test EXCLUDE_SYMBOLS = -# The EXAMPLE_PATH tag can be used to specify one or more files or -# directories that contain example code fragments that are included (see -# the \include command). +# The EXAMPLE_PATH tag can be used to specify one or more files or directories +# that contain example code fragments that are included (see the \include +# command). EXAMPLE_PATH = # If the value of the EXAMPLE_PATH tag contains directories, you can use the -# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp -# and *.h) to filter out the source-files in the directories. If left -# blank all files are included. +# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp and +# *.h) to filter out the source-files in the directories. If left blank all +# files are included. EXAMPLE_PATTERNS = # If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be -# searched for input files to be used with the \include or \dontinclude -# commands irrespective of the value of the RECURSIVE tag. -# Possible values are YES and NO. If left blank NO is used. +# searched for input files to be used with the \include or \dontinclude commands +# irrespective of the value of the RECURSIVE tag. +# The default value is: NO. EXAMPLE_RECURSIVE = NO -# The IMAGE_PATH tag can be used to specify one or more files or -# directories that contain image that are included in the documentation (see -# the \image command). +# The IMAGE_PATH tag can be used to specify one or more files or directories +# that contain images that are to be included in the documentation (see the +# \image command). IMAGE_PATH = # The INPUT_FILTER tag can be used to specify a program that doxygen should # invoke to filter for each input file. Doxygen will invoke the filter program -# by executing (via popen()) the command , where -# is the value of the INPUT_FILTER tag, and is the name of an -# input file. Doxygen will then use the output that the filter program writes -# to standard output. -# If FILTER_PATTERNS is specified, this tag will be -# ignored. +# by executing (via popen()) the command: +# +# +# +# where is the value of the INPUT_FILTER tag, and is the +# name of an input file. Doxygen will then use the output that the filter +# program writes to standard output. If FILTER_PATTERNS is specified, this tag +# will be ignored. +# +# Note that the filter must not add or remove lines; it is applied before the +# code is scanned, but not when the output code is generated. If lines are added +# or removed, the anchors will not be placed correctly. +# +# Note that doxygen will use the data processed and written to standard output +# for further processing, therefore nothing else, like debug statements or used +# commands (so in case of a Windows batch file always use @echo OFF), should be +# written to standard output. +# +# Note that for custom extensions or not directly supported extensions you also +# need to set EXTENSION_MAPPING for the extension otherwise the files are not +# properly processed by doxygen. INPUT_FILTER = # The FILTER_PATTERNS tag can be used to specify filters on a per file pattern -# basis. -# Doxygen will compare the file name with each pattern and apply the -# filter if there is a match. -# The filters are a list of the form: -# pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further -# info on how filters are used. If FILTER_PATTERNS is empty, INPUT_FILTER -# is applied to all files. +# basis. Doxygen will compare the file name with each pattern and apply the +# filter if there is a match. The filters are a list of the form: pattern=filter +# (like *.cpp=my_cpp_filter). See INPUT_FILTER for further information on how +# filters are used. If the FILTER_PATTERNS tag is empty or if none of the +# patterns match the file name, INPUT_FILTER is applied. +# +# Note that for custom extensions or not directly supported extensions you also +# need to set EXTENSION_MAPPING for the extension otherwise the files are not +# properly processed by doxygen. FILTER_PATTERNS = # If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using -# INPUT_FILTER) will be used to filter the input files when producing source -# files to browse (i.e. when SOURCE_BROWSER is set to YES). +# INPUT_FILTER) will also be used to filter the input files that are used for +# producing the source files to browse (i.e. when SOURCE_BROWSER is set to YES). +# The default value is: NO. FILTER_SOURCE_FILES = NO +# The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file +# pattern. A pattern will override the setting for FILTER_PATTERN (if any) and +# it is also possible to disable source filtering for a specific pattern using +# *.ext= (so without naming a filter). +# This tag requires that the tag FILTER_SOURCE_FILES is set to YES. + +FILTER_SOURCE_PATTERNS = + +# If the USE_MDFILE_AS_MAINPAGE tag refers to the name of a markdown file that +# is part of the input, its contents will be placed on the main page +# (index.html). This can be useful if you have a project on for instance GitHub +# and want to reuse the introduction page also for the doxygen output. + +USE_MDFILE_AS_MAINPAGE = + +# The Fortran standard specifies that for fixed formatted Fortran code all +# characters from position 72 are to be considered as comment. A common +# extension is to allow longer lines before the automatic comment starts. The +# setting FORTRAN_COMMENT_AFTER will also make it possible that longer lines can +# be processed before the automatic comment starts. +# Minimum value: 7, maximum value: 10000, default value: 72. + +FORTRAN_COMMENT_AFTER = 72 + #--------------------------------------------------------------------------- -# configuration options related to source browsing +# Configuration options related to source browsing #--------------------------------------------------------------------------- -# If the SOURCE_BROWSER tag is set to YES then a list of source files will -# be generated. Documented entities will be cross-referenced with these sources. -# Note: To get rid of all source code in the generated output, make sure also -# VERBATIM_HEADERS is set to NO. +# If the SOURCE_BROWSER tag is set to YES then a list of source files will be +# generated. Documented entities will be cross-referenced with these sources. +# +# Note: To get rid of all source code in the generated output, make sure that +# also VERBATIM_HEADERS is set to NO. +# The default value is: NO. SOURCE_BROWSER = NO -# Setting the INLINE_SOURCES tag to YES will include the body -# of functions and classes directly in the documentation. +# Setting the INLINE_SOURCES tag to YES will include the body of functions, +# classes and enums directly into the documentation. +# The default value is: NO. INLINE_SOURCES = NO -# Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct -# doxygen to hide any special comment blocks from generated source code -# fragments. Normal C and C++ comments will always remain visible. +# Setting the STRIP_CODE_COMMENTS tag to YES will instruct doxygen to hide any +# special comment blocks from generated source code fragments. Normal C, C++ and +# Fortran comments will always remain visible. +# The default value is: YES. STRIP_CODE_COMMENTS = YES -# If the REFERENCED_BY_RELATION tag is set to YES -# then for each documented function all documented -# functions referencing it will be listed. +# If the REFERENCED_BY_RELATION tag is set to YES then for each documented +# entity all documented functions referencing it will be listed. +# The default value is: NO. REFERENCED_BY_RELATION = YES -# If the REFERENCES_RELATION tag is set to YES -# then for each documented function all documented entities -# called/used by that function will be listed. +# If the REFERENCES_RELATION tag is set to YES then for each documented function +# all documented entities called/used by that function will be listed. +# The default value is: NO. REFERENCES_RELATION = YES -# If the REFERENCES_LINK_SOURCE tag is set to YES (the default) -# and SOURCE_BROWSER tag is set to YES, then the hyperlinks from -# functions in REFERENCES_RELATION and REFERENCED_BY_RELATION lists will -# link to the source code. -# Otherwise they will link to the documentation. +# If the REFERENCES_LINK_SOURCE tag is set to YES and SOURCE_BROWSER tag is set +# to YES then the hyperlinks from functions in REFERENCES_RELATION and +# REFERENCED_BY_RELATION lists will link to the source code. Otherwise they will +# link to the documentation. +# The default value is: YES. REFERENCES_LINK_SOURCE = YES -# If the USE_HTAGS tag is set to YES then the references to source code -# will point to the HTML generated by the htags(1) tool instead of doxygen -# built-in source browser. The htags tool is part of GNU's global source -# tagging system (see http://www.gnu.org/software/global/global.html). You -# will need version 4.8.6 or higher. +# If SOURCE_TOOLTIPS is enabled (the default) then hovering a hyperlink in the +# source code will show a tooltip with additional information such as prototype, +# brief description and links to the definition and documentation. Since this +# will make the HTML file larger and loading of large files a bit slower, you +# can opt to disable this feature. +# The default value is: YES. +# This tag requires that the tag SOURCE_BROWSER is set to YES. + +SOURCE_TOOLTIPS = YES + +# If the USE_HTAGS tag is set to YES then the references to source code will +# point to the HTML generated by the htags(1) tool instead of doxygen built-in +# source browser. The htags tool is part of GNU's global source tagging system +# (see https://www.gnu.org/software/global/global.html). You will need version +# 4.8.6 or higher. +# +# To use it do the following: +# - Install the latest version of global +# - Enable SOURCE_BROWSER and USE_HTAGS in the configuration file +# - Make sure the INPUT points to the root of the source tree +# - Run doxygen as normal +# +# Doxygen will invoke htags (and that will in turn invoke gtags), so these +# tools must be available from the command line (i.e. in the search path). +# +# The result: instead of the source browser generated by doxygen, the links to +# source code will now point to the output of htags. +# The default value is: NO. +# This tag requires that the tag SOURCE_BROWSER is set to YES. USE_HTAGS = NO -# If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen -# will generate a verbatim copy of the header file for each class for -# which an include is specified. Set to NO to disable this. +# If the VERBATIM_HEADERS tag is set the YES then doxygen will generate a +# verbatim copy of the header file for each class for which an include is +# specified. Set to NO to disable this. +# See also: Section \class. +# The default value is: YES. VERBATIM_HEADERS = NO -#--------------------------------------------------------------------------- -# configuration options related to the alphabetical class index -#--------------------------------------------------------------------------- - -# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index -# of all compounds will be generated. Enable this if the project -# contains a lot of classes, structs, unions or interfaces. +# If the CLANG_ASSISTED_PARSING tag is set to YES then doxygen will use the +# clang parser (see: +# http://clang.llvm.org/) for more accurate parsing at the cost of reduced +# performance. This can be particularly helpful with template rich C++ code for +# which doxygen's built-in parser lacks the necessary type information. +# Note: The availability of this option depends on whether or not doxygen was +# generated with the -Duse_libclang=ON option for CMake. +# The default value is: NO. + +CLANG_ASSISTED_PARSING = NO + +# If the CLANG_ASSISTED_PARSING tag is set to YES and the CLANG_ADD_INC_PATHS +# tag is set to YES then doxygen will add the directory of each input to the +# include path. +# The default value is: YES. +# This tag requires that the tag CLANG_ASSISTED_PARSING is set to YES. + +CLANG_ADD_INC_PATHS = YES + +# If clang assisted parsing is enabled you can provide the compiler with command +# line options that you would normally use when invoking the compiler. Note that +# the include paths will already be set by doxygen for the files and directories +# specified with INPUT and INCLUDE_PATH. +# This tag requires that the tag CLANG_ASSISTED_PARSING is set to YES. + +CLANG_OPTIONS = + +# If clang assisted parsing is enabled you can provide the clang parser with the +# path to the directory containing a file called compile_commands.json. This +# file is the compilation database (see: +# http://clang.llvm.org/docs/HowToSetupToolingForLLVM.html) containing the +# options used when the source files were built. This is equivalent to +# specifying the -p option to a clang tool, such as clang-check. These options +# will then be passed to the parser. Any options specified with CLANG_OPTIONS +# will be added as well. +# Note: The availability of this option depends on whether or not doxygen was +# generated with the -Duse_libclang=ON option for CMake. + +CLANG_DATABASE_PATH = + +#--------------------------------------------------------------------------- +# Configuration options related to the alphabetical class index +#--------------------------------------------------------------------------- + +# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index of all +# compounds will be generated. Enable this if the project contains a lot of +# classes, structs, unions or interfaces. +# The default value is: YES. ALPHABETICAL_INDEX = YES -# If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then -# the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns -# in which this list will be split (can be a number in the range [1..20]) - -#COLS_IN_ALPHA_INDEX = 5 - -# In case all classes in a project start with a common prefix, all -# classes will be put under the same header in the alphabetical index. -# The IGNORE_PREFIX tag can be used to specify one or more prefixes that -# should be ignored while generating the index headers. +# The IGNORE_PREFIX tag can be used to specify a prefix (or a list of prefixes) +# that should be ignored while generating the index headers. The IGNORE_PREFIX +# tag works for classes, function and member names. The entity will be placed in +# the alphabetical list under the first letter of the entity name that remains +# after removing the prefix. +# This tag requires that the tag ALPHABETICAL_INDEX is set to YES. IGNORE_PREFIX = #--------------------------------------------------------------------------- -# configuration options related to the HTML output +# Configuration options related to the HTML output #--------------------------------------------------------------------------- -# If the GENERATE_HTML tag is set to YES (the default) Doxygen will -# generate HTML output. +# If the GENERATE_HTML tag is set to YES, doxygen will generate HTML output +# The default value is: YES. GENERATE_HTML = YES -# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. -# If a relative path is entered the value of OUTPUT_DIRECTORY will be -# put in front of it. If left blank `html' will be used as the default path. +# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. If a +# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of +# it. +# The default directory is: html. +# This tag requires that the tag GENERATE_HTML is set to YES. HTML_OUTPUT = html -# The HTML_FILE_EXTENSION tag can be used to specify the file extension for -# each generated HTML page (for example: .htm,.php,.asp). If it is left blank -# doxygen will generate files with .html extension. +# The HTML_FILE_EXTENSION tag can be used to specify the file extension for each +# generated HTML page (for example: .htm, .php, .asp). +# The default value is: .html. +# This tag requires that the tag GENERATE_HTML is set to YES. HTML_FILE_EXTENSION = .html -# The HTML_HEADER tag can be used to specify a personal HTML header for -# each generated HTML page. If it is left blank doxygen will generate a +# The HTML_HEADER tag can be used to specify a user-defined HTML header file for +# each generated HTML page. If the tag is left blank doxygen will generate a # standard header. +# +# To get valid HTML the header file that includes any scripts and style sheets +# that doxygen needs, which is dependent on the configuration options used (e.g. +# the setting GENERATE_TREEVIEW). It is highly recommended to start with a +# default header using +# doxygen -w html new_header.html new_footer.html new_stylesheet.css +# YourConfigFile +# and then modify the file new_header.html. See also section "Doxygen usage" +# for information on how to generate the default header that doxygen normally +# uses. +# Note: The header is subject to change so you typically have to regenerate the +# default header when upgrading to a newer version of doxygen. For a description +# of the possible markers and block names see the documentation. +# This tag requires that the tag GENERATE_HTML is set to YES. HTML_HEADER = -# The HTML_FOOTER tag can be used to specify a personal HTML footer for -# each generated HTML page. If it is left blank doxygen will generate a -# standard footer. +# The HTML_FOOTER tag can be used to specify a user-defined HTML footer for each +# generated HTML page. If the tag is left blank doxygen will generate a standard +# footer. See HTML_HEADER for more information on how to generate a default +# footer and what special commands can be used inside the footer. See also +# section "Doxygen usage" for information on how to generate the default footer +# that doxygen normally uses. +# This tag requires that the tag GENERATE_HTML is set to YES. HTML_FOOTER = -# If the HTML_TIMESTAMP tag is set to YES then the generated HTML -# documentation will contain the timesstamp. +# The HTML_STYLESHEET tag can be used to specify a user-defined cascading style +# sheet that is used by each HTML page. It can be used to fine-tune the look of +# the HTML output. If left blank doxygen will generate a default style sheet. +# See also section "Doxygen usage" for information on how to generate the style +# sheet that doxygen normally uses. +# Note: It is recommended to use HTML_EXTRA_STYLESHEET instead of this tag, as +# it is more robust and this tag (HTML_STYLESHEET) will in the future become +# obsolete. +# This tag requires that the tag GENERATE_HTML is set to YES. -HTML_TIMESTAMP = NO +HTML_STYLESHEET = -# The HTML_STYLESHEET tag can be used to specify a user-defined cascading -# style sheet that is used by each HTML page. It can be used to -# fine-tune the look of the HTML output. If the tag is left blank doxygen -# will generate a default style sheet. Note that doxygen will try to copy -# the style sheet file to the HTML output directory, so don't put your own -# stylesheet in the HTML output directory as well, or it will be erased! +# The HTML_EXTRA_STYLESHEET tag can be used to specify additional user-defined +# cascading style sheets that are included after the standard style sheets +# created by doxygen. Using this option one can overrule certain style aspects. +# This is preferred over using HTML_STYLESHEET since it does not replace the +# standard style sheet and is therefore more robust against future updates. +# Doxygen will copy the style sheet files to the output directory. +# Note: The order of the extra style sheet files is of importance (e.g. the last +# style sheet in the list overrules the setting of the previous ones in the +# list). +# Note: Since the styling of scrollbars can currently not be overruled in +# Webkit/Chromium, the styling will be left out of the default doxygen.css if +# one or more extra stylesheets have been specified. So if scrollbar +# customization is desired it has to be added explicitly. For an example see the +# documentation. +# This tag requires that the tag GENERATE_HTML is set to YES. -HTML_STYLESHEET = +HTML_EXTRA_STYLESHEET = -# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. -# Doxygen will adjust the colors in the stylesheet and background images -# according to this color. Hue is specified as an angle on a colorwheel, -# see http://en.wikipedia.org/wiki/Hue for more information. -# For instance the value 0 represents red, 60 is yellow, 120 is green, -# 180 is cyan, 240 is blue, 300 purple, and 360 is red again. -# The allowed range is 0 to 359. - -#HTML_COLORSTYLE_HUE = 220 - -# The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of -# the colors in the HTML output. For a value of 0 the output will use -# grayscales only. A value of 255 will produce the most vivid colors. - -#HTML_COLORSTYLE_SAT = 100 - -# The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to -# the luminance component of the colors in the HTML output. Values below -# 100 gradually make the output lighter, whereas values above 100 make -# the output darker. The value divided by 100 is the actual gamma applied, -# so 80 represents a gamma of 0.8, The value 220 represents a gamma of 2.2, -# and 100 does not change the gamma. - -#HTML_COLORSTYLE_GAMMA = 80 - -# If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML -# page will contain the date and time when the page was generated. Setting -# this to NO can help when comparing the output of multiple runs. - -HTML_TIMESTAMP = YES - -# If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes, -# files or namespaces will be aligned in HTML using tables. If set to -# NO a bullet list will be used. +# The HTML_EXTRA_FILES tag can be used to specify one or more extra images or +# other source files which should be copied to the HTML output directory. Note +# that these files will be copied to the base HTML output directory. Use the +# $relpath^ marker in the HTML_HEADER and/or HTML_FOOTER files to load these +# files. In the HTML_STYLESHEET file, use the file name only. Also note that the +# files will be copied as-is; there are no commands or markers available. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_EXTRA_FILES = + +# The HTML_COLORSTYLE tag can be used to specify if the generated HTML output +# should be rendered with a dark or light theme. +# Possible values are: LIGHT always generate light mode output, DARK always +# generate dark mode output, AUTO_LIGHT automatically set the mode according to +# the user preference, use light mode if no preference is set (the default), +# AUTO_DARK automatically set the mode according to the user preference, use +# dark mode if no preference is set and TOGGLE allow to user to switch between +# light and dark mode via a button. +# The default value is: AUTO_LIGHT. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_COLORSTYLE = AUTO_LIGHT + +# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. Doxygen +# will adjust the colors in the style sheet and background images according to +# this color. Hue is specified as an angle on a color-wheel, see +# https://en.wikipedia.org/wiki/Hue for more information. For instance the value +# 0 represents red, 60 is yellow, 120 is green, 180 is cyan, 240 is blue, 300 +# purple, and 360 is red again. +# Minimum value: 0, maximum value: 359, default value: 220. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_COLORSTYLE_HUE = 220 + +# The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of the colors +# in the HTML output. For a value of 0 the output will use gray-scales only. A +# value of 255 will produce the most vivid colors. +# Minimum value: 0, maximum value: 255, default value: 100. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_COLORSTYLE_SAT = 100 + +# The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to the +# luminance component of the colors in the HTML output. Values below 100 +# gradually make the output lighter, whereas values above 100 make the output +# darker. The value divided by 100 is the actual gamma applied, so 80 represents +# a gamma of 0.8, The value 220 represents a gamma of 2.2, and 100 does not +# change the gamma. +# Minimum value: 40, maximum value: 240, default value: 80. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_COLORSTYLE_GAMMA = 80 + +# If the HTML_DYNAMIC_MENUS tag is set to YES then the generated HTML +# documentation will contain a main index with vertical navigation menus that +# are dynamically created via JavaScript. If disabled, the navigation index will +# consists of multiple levels of tabs that are statically embedded in every HTML +# page. Disable this option to support browsers that do not have JavaScript, +# like the Qt help browser. +# The default value is: YES. +# This tag requires that the tag GENERATE_HTML is set to YES. -#HTML_ALIGN_MEMBERS = YES +HTML_DYNAMIC_MENUS = YES # If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML # documentation will contain sections that can be hidden and shown after the -# page has loaded. For this to work a browser that supports -# JavaScript and DHTML is required (for instance Mozilla 1.0+, Firefox -# Netscape 6.0+, Internet explorer 5.0+, Konqueror, or Safari). +# page has loaded. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. HTML_DYNAMIC_SECTIONS = NO -# If the GENERATE_DOCSET tag is set to YES, additional index files -# will be generated that can be used as input for Apple's Xcode 3 -# integrated development environment, introduced with OSX 10.5 (Leopard). -# To create a documentation set, doxygen will generate a Makefile in the -# HTML output directory. Running make will produce the docset in that -# directory and running "make install" will install the docset in -# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find -# it at startup. -# See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html -# for more information. +# With HTML_INDEX_NUM_ENTRIES one can control the preferred number of entries +# shown in the various tree structured indices initially; the user can expand +# and collapse entries dynamically later on. Doxygen will expand the tree to +# such a level that at most the specified number of entries are visible (unless +# a fully collapsed tree already exceeds this amount). So setting the number of +# entries 1 will produce a full collapsed tree by default. 0 is a special value +# representing an infinite number of entries and will result in a full expanded +# tree by default. +# Minimum value: 0, maximum value: 9999, default value: 100. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_INDEX_NUM_ENTRIES = 100 + +# If the GENERATE_DOCSET tag is set to YES, additional index files will be +# generated that can be used as input for Apple's Xcode 3 integrated development +# environment (see: +# https://developer.apple.com/xcode/), introduced with OSX 10.5 (Leopard). To +# create a documentation set, doxygen will generate a Makefile in the HTML +# output directory. Running make will produce the docset in that directory and +# running make install will install the docset in +# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find it at +# startup. See https://developer.apple.com/library/archive/featuredarticles/Doxy +# genXcode/_index.html for more information. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. GENERATE_DOCSET = NO -# When GENERATE_DOCSET tag is set to YES, this tag determines the name of the -# feed. A documentation feed provides an umbrella under which multiple -# documentation sets from a single provider (such as a company or product suite) -# can be grouped. +# This tag determines the name of the docset feed. A documentation feed provides +# an umbrella under which multiple documentation sets from a single provider +# (such as a company or product suite) can be grouped. +# The default value is: Doxygen generated docs. +# This tag requires that the tag GENERATE_DOCSET is set to YES. DOCSET_FEEDNAME = "Doxygen generated docs" -# When GENERATE_DOCSET tag is set to YES, this tag specifies a string that -# should uniquely identify the documentation set bundle. This should be a -# reverse domain-name style string, e.g. com.mycompany.MyDocSet. Doxygen -# will append .docset to the name. +# This tag determines the URL of the docset feed. A documentation feed provides +# an umbrella under which multiple documentation sets from a single provider +# (such as a company or product suite) can be grouped. +# This tag requires that the tag GENERATE_DOCSET is set to YES. + +DOCSET_FEEDURL = + +# This tag specifies a string that should uniquely identify the documentation +# set bundle. This should be a reverse domain-name style string, e.g. +# com.mycompany.MyDocSet. Doxygen will append .docset to the name. +# The default value is: org.doxygen.Project. +# This tag requires that the tag GENERATE_DOCSET is set to YES. DOCSET_BUNDLE_ID = org.doxygen.Project -# When GENERATE_PUBLISHER_ID tag specifies a string that should uniquely identify +# The DOCSET_PUBLISHER_ID tag specifies a string that should uniquely identify # the documentation publisher. This should be a reverse domain-name style # string, e.g. com.mycompany.MyDocSet.documentation. +# The default value is: org.doxygen.Publisher. +# This tag requires that the tag GENERATE_DOCSET is set to YES. -#DOCSET_PUBLISHER_ID = org.doxygen.Publisher - -# The GENERATE_PUBLISHER_NAME tag identifies the documentation publisher. - -#DOCSET_PUBLISHER_NAME = Publisher +DOCSET_PUBLISHER_ID = org.doxygen.Publisher -# If the GENERATE_HTMLHELP tag is set to YES, additional index files -# will be generated that can be used as input for tools like the -# Microsoft HTML help workshop to generate a compiled HTML help file (.chm) -# of the generated HTML documentation. +# The DOCSET_PUBLISHER_NAME tag identifies the documentation publisher. +# The default value is: Publisher. +# This tag requires that the tag GENERATE_DOCSET is set to YES. + +DOCSET_PUBLISHER_NAME = Publisher + +# If the GENERATE_HTMLHELP tag is set to YES then doxygen generates three +# additional HTML index files: index.hhp, index.hhc, and index.hhk. The +# index.hhp is a project file that can be read by Microsoft's HTML Help Workshop +# on Windows. In the beginning of 2021 Microsoft took the original page, with +# a.o. the download links, offline the HTML help workshop was already many years +# in maintenance mode). You can download the HTML help workshop from the web +# archives at Installation executable (see: +# http://web.archive.org/web/20160201063255/http://download.microsoft.com/downlo +# ad/0/A/9/0A939EF6-E31C-430F-A3DF-DFAE7960D564/htmlhelp.exe). +# +# The HTML Help Workshop contains a compiler that can convert all HTML output +# generated by doxygen into a single compiled HTML file (.chm). Compiled HTML +# files are now used as the Windows 98 help format, and will replace the old +# Windows help format (.hlp) on all Windows platforms in the future. Compressed +# HTML files also contain an index, a table of contents, and you can search for +# words in the documentation. The HTML workshop also contains a viewer for +# compressed HTML files. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. GENERATE_HTMLHELP = NO -# If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can -# be used to specify the file name of the resulting .chm file. You -# can add a path in front of the file if the result should not be +# The CHM_FILE tag can be used to specify the file name of the resulting .chm +# file. You can add a path in front of the file if the result should not be # written to the html output directory. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. CHM_FILE = -# If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can -# be used to specify the location (absolute path including file name) of -# the HTML help compiler (hhc.exe). If non-empty doxygen will try to run -# the HTML help compiler on the generated index.hhp. +# The HHC_LOCATION tag can be used to specify the location (absolute path +# including file name) of the HTML help compiler (hhc.exe). If non-empty, +# doxygen will try to run the HTML help compiler on the generated index.hhp. +# The file has to be specified with full path. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. HHC_LOCATION = -# If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag -# controls if a separate .chi index file is generated (YES) or that -# it should be included in the master .chm file (NO). +# The GENERATE_CHI flag controls if a separate .chi index file is generated +# (YES) or that it should be included in the main .chm file (NO). +# The default value is: NO. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. GENERATE_CHI = NO -# If the GENERATE_HTMLHELP tag is set to YES, the CHM_INDEX_ENCODING -# is used to encode HtmlHelp index (hhk), content (hhc) and project file -# content. +# The CHM_INDEX_ENCODING is used to encode HtmlHelp index (hhk), content (hhc) +# and project file content. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. CHM_INDEX_ENCODING = -# If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag -# controls whether a binary table of contents is generated (YES) or a -# normal table of contents (NO) in the .chm file. +# The BINARY_TOC flag controls whether a binary table of contents is generated +# (YES) or a normal table of contents (NO) in the .chm file. Furthermore it +# enables the Previous and Next buttons. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. BINARY_TOC = NO -# The TOC_EXPAND flag can be set to YES to add extra items for group members -# to the contents of the HTML help documentation and to the tree view. +# The TOC_EXPAND flag can be set to YES to add extra items for group members to +# the table of contents of the HTML help documentation and to the tree view. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. TOC_EXPAND = NO +# The SITEMAP_URL tag is used to specify the full URL of the place where the +# generated documentation will be placed on the server by the user during the +# deployment of the documentation. The generated sitemap is called sitemap.xml +# and placed on the directory specified by HTML_OUTPUT. In case no SITEMAP_URL +# is specified no sitemap is generated. For information about the sitemap +# protocol see https://www.sitemaps.org +# This tag requires that the tag GENERATE_HTML is set to YES. + +SITEMAP_URL = + # If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and -# QHP_VIRTUAL_FOLDER are set, an additional index file will be generated -# that can be used as input for Qt's qhelpgenerator to generate a -# Qt Compressed Help (.qch) of the generated HTML documentation. +# QHP_VIRTUAL_FOLDER are set, an additional index file will be generated that +# can be used as input for Qt's qhelpgenerator to generate a Qt Compressed Help +# (.qch) of the generated HTML documentation. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. GENERATE_QHP = NO -# If the QHG_LOCATION tag is specified, the QCH_FILE tag can -# be used to specify the file name of the resulting .qch file. -# The path specified is relative to the HTML output folder. +# If the QHG_LOCATION tag is specified, the QCH_FILE tag can be used to specify +# the file name of the resulting .qch file. The path specified is relative to +# the HTML output folder. +# This tag requires that the tag GENERATE_QHP is set to YES. QCH_FILE = -# The QHP_NAMESPACE tag specifies the namespace to use when generating -# Qt Help Project output. For more information please see -# http://doc.trolltech.com/qthelpproject.html#namespace +# The QHP_NAMESPACE tag specifies the namespace to use when generating Qt Help +# Project output. For more information please see Qt Help Project / Namespace +# (see: +# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#namespace). +# The default value is: org.doxygen.Project. +# This tag requires that the tag GENERATE_QHP is set to YES. QHP_NAMESPACE = org.doxygen.Project -# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating -# Qt Help Project output. For more information please see -# http://doc.trolltech.com/qthelpproject.html#virtual-folders +# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating Qt +# Help Project output. For more information please see Qt Help Project / Virtual +# Folders (see: +# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#virtual-folders). +# The default value is: doc. +# This tag requires that the tag GENERATE_QHP is set to YES. QHP_VIRTUAL_FOLDER = doc -# If QHP_CUST_FILTER_NAME is set, it specifies the name of a custom filter to -# add. For more information please see -# http://doc.trolltech.com/qthelpproject.html#custom-filters +# If the QHP_CUST_FILTER_NAME tag is set, it specifies the name of a custom +# filter to add. For more information please see Qt Help Project / Custom +# Filters (see: +# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#custom-filters). +# This tag requires that the tag GENERATE_QHP is set to YES. QHP_CUST_FILTER_NAME = -# The QHP_CUST_FILT_ATTRS tag specifies the list of the attributes of the -# custom filter to add. For more information please see -# -# Qt Help Project / Custom Filters. +# The QHP_CUST_FILTER_ATTRS tag specifies the list of the attributes of the +# custom filter to add. For more information please see Qt Help Project / Custom +# Filters (see: +# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#custom-filters). +# This tag requires that the tag GENERATE_QHP is set to YES. QHP_CUST_FILTER_ATTRS = # The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this -# project's -# filter section matches. -# -# Qt Help Project / Filter Attributes. +# project's filter section matches. Qt Help Project / Filter Attributes (see: +# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#filter-attributes). +# This tag requires that the tag GENERATE_QHP is set to YES. QHP_SECT_FILTER_ATTRS = -# If the GENERATE_QHP tag is set to YES, the QHG_LOCATION tag can -# be used to specify the location of Qt's qhelpgenerator. -# If non-empty doxygen will try to run qhelpgenerator on the generated -# .qhp file. +# The QHG_LOCATION tag can be used to specify the location (absolute path +# including file name) of Qt's qhelpgenerator. If non-empty doxygen will try to +# run qhelpgenerator on the generated .qhp file. +# This tag requires that the tag GENERATE_QHP is set to YES. QHG_LOCATION = -# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files -# will be generated, which together with the HTML files, form an Eclipse help -# plugin. To install this plugin and make it available under the help contents -# menu in Eclipse, the contents of the directory containing the HTML and XML -# files needs to be copied into the plugins directory of eclipse. The name of -# the directory within the plugins directory should be the same as -# the ECLIPSE_DOC_ID value. After copying Eclipse needs to be restarted before -# the help appears. +# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files will be +# generated, together with the HTML files, they form an Eclipse help plugin. To +# install this plugin and make it available under the help contents menu in +# Eclipse, the contents of the directory containing the HTML and XML files needs +# to be copied into the plugins directory of eclipse. The name of the directory +# within the plugins directory should be the same as the ECLIPSE_DOC_ID value. +# After copying Eclipse needs to be restarted before the help appears. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. GENERATE_ECLIPSEHELP = NO -# A unique identifier for the eclipse help plugin. When installing the plugin -# the directory name containing the HTML and XML files should also have -# this name. +# A unique identifier for the Eclipse help plugin. When installing the plugin +# the directory name containing the HTML and XML files should also have this +# name. Each documentation set should have its own identifier. +# The default value is: org.doxygen.Project. +# This tag requires that the tag GENERATE_ECLIPSEHELP is set to YES. ECLIPSE_DOC_ID = org.doxygen.Project -# The DISABLE_INDEX tag can be used to turn on/off the condensed index at -# top of each HTML page. The value NO (the default) enables the index and -# the value YES disables it. +# If you want full control over the layout of the generated HTML pages it might +# be necessary to disable the index and replace it with your own. The +# DISABLE_INDEX tag can be used to turn on/off the condensed index (tabs) at top +# of each HTML page. A value of NO enables the index and the value YES disables +# it. Since the tabs in the index contain the same information as the navigation +# tree, you can set this option to YES if you also set GENERATE_TREEVIEW to YES. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. DISABLE_INDEX = NO -# This tag can be used to set the number of enum values (range [1..20]) -# that doxygen will group on one line in the generated HTML documentation. - -ENUM_VALUES_PER_LINE = 4 - # The GENERATE_TREEVIEW tag is used to specify whether a tree-like index -# structure should be generated to display hierarchical information. -# If the tag value is set to YES, a side panel will be generated -# containing a tree-like index structure (just like the one that -# is generated for HTML Help). For this to work a browser that supports -# JavaScript, DHTML, CSS and frames is required (i.e. any modern browser). -# Windows users are probably better off using the HTML help feature. +# structure should be generated to display hierarchical information. If the tag +# value is set to YES, a side panel will be generated containing a tree-like +# index structure (just like the one that is generated for HTML Help). For this +# to work a browser that supports JavaScript, DHTML, CSS and frames is required +# (i.e. any modern browser). Windows users are probably better off using the +# HTML help feature. Via custom style sheets (see HTML_EXTRA_STYLESHEET) one can +# further fine tune the look of the index (see "Fine-tuning the output"). As an +# example, the default style sheet generated by doxygen has an example that +# shows how to put an image at the root of the tree instead of the PROJECT_NAME. +# Since the tree basically has the same information as the tab index, you could +# consider setting DISABLE_INDEX to YES when enabling this option. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. GENERATE_TREEVIEW = NO -# By enabling USE_INLINE_TREES, doxygen will generate the Groups, Directories, -# and Class Hierarchy pages using a tree view instead of an ordered list. +# When both GENERATE_TREEVIEW and DISABLE_INDEX are set to YES, then the +# FULL_SIDEBAR option determines if the side bar is limited to only the treeview +# area (value NO) or if it should extend to the full height of the window (value +# YES). Setting this to YES gives a layout similar to +# https://docs.readthedocs.io with more room for contents, but less room for the +# project logo, title, and description. If either GENERATE_TREEVIEW or +# DISABLE_INDEX is set to NO, this option has no effect. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. -#USE_INLINE_TREES = NO +FULL_SIDEBAR = NO -# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be -# used to set the initial width (in pixels) of the frame in which the tree -# is shown. +# The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values that +# doxygen will group on one line in the generated HTML documentation. +# +# Note that a value of 0 will completely suppress the enum values from appearing +# in the overview section. +# Minimum value: 0, maximum value: 20, default value: 4. +# This tag requires that the tag GENERATE_HTML is set to YES. -TREEVIEW_WIDTH = 250 +ENUM_VALUES_PER_LINE = 4 -# When the EXT_LINKS_IN_WINDOW option is set to YES doxygen will open -# links to external symbols imported via tag files in a separate window. +# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be used +# to set the initial width (in pixels) of the frame in which the tree is shown. +# Minimum value: 0, maximum value: 1500, default value: 250. +# This tag requires that the tag GENERATE_HTML is set to YES. -#EXT_LINKS_IN_WINDOW = NO +TREEVIEW_WIDTH = 250 -# Use this tag to change the font size of Latex formulas included -# as images in the HTML documentation. The default is 10. Note that -# when you change the font size after a successful doxygen run you need -# to manually remove any form_*.png images from the HTML output directory -# to force them to be regenerated. +# If the EXT_LINKS_IN_WINDOW option is set to YES, doxygen will open links to +# external symbols imported via tag files in a separate window. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +EXT_LINKS_IN_WINDOW = NO + +# If the OBFUSCATE_EMAILS tag is set to YES, doxygen will obfuscate email +# addresses. +# The default value is: YES. +# This tag requires that the tag GENERATE_HTML is set to YES. + +OBFUSCATE_EMAILS = YES + +# If the HTML_FORMULA_FORMAT option is set to svg, doxygen will use the pdf2svg +# tool (see https://github.com/dawbarton/pdf2svg) or inkscape (see +# https://inkscape.org) to generate formulas as SVG images instead of PNGs for +# the HTML output. These images will generally look nicer at scaled resolutions. +# Possible values are: png (the default) and svg (looks nicer but requires the +# pdf2svg or inkscape tool). +# The default value is: png. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_FORMULA_FORMAT = png + +# Use this tag to change the font size of LaTeX formulas included as images in +# the HTML documentation. When you change the font size after a successful +# doxygen run you need to manually remove any form_*.png images from the HTML +# output directory to force them to be regenerated. +# Minimum value: 8, maximum value: 50, default value: 10. +# This tag requires that the tag GENERATE_HTML is set to YES. FORMULA_FONTSIZE = 10 -# Use the FORMULA_TRANSPARENT tag to determine whether or not the images -# generated for formulas are transparent PNGs. Transparent PNGs are -# not supported properly for IE 6.0, but are supported on all modern browsers. -# Note that when changing this option you need to delete any form_*.png files -# in the HTML output before the changes have effect. - -#FORMULA_TRANSPARENT = YES - -# When the SEARCHENGINE tag is enabled doxygen will generate a search box -# for the HTML output. The underlying search engine uses javascript -# and DHTML and should work on any modern browser. Note that when using -# HTML help (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets -# (GENERATE_DOCSET) there is already a search function so this one should -# typically be disabled. For large projects the javascript based search engine -# can be slow, then enabling SERVER_BASED_SEARCH may provide a better solution. +# The FORMULA_MACROFILE can contain LaTeX \newcommand and \renewcommand commands +# to create new LaTeX commands to be used in formulas as building blocks. See +# the section "Including formulas" for details. + +FORMULA_MACROFILE = + +# Enable the USE_MATHJAX option to render LaTeX formulas using MathJax (see +# https://www.mathjax.org) which uses client side JavaScript for the rendering +# instead of using pre-rendered bitmaps. Use this if you do not have LaTeX +# installed or if you want to formulas look prettier in the HTML output. When +# enabled you may also need to install MathJax separately and configure the path +# to it using the MATHJAX_RELPATH option. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +USE_MATHJAX = NO + +# With MATHJAX_VERSION it is possible to specify the MathJax version to be used. +# Note that the different versions of MathJax have different requirements with +# regards to the different settings, so it is possible that also other MathJax +# settings have to be changed when switching between the different MathJax +# versions. +# Possible values are: MathJax_2 and MathJax_3. +# The default value is: MathJax_2. +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_VERSION = MathJax_2 + +# When MathJax is enabled you can set the default output format to be used for +# the MathJax output. For more details about the output format see MathJax +# version 2 (see: +# http://docs.mathjax.org/en/v2.7-latest/output.html) and MathJax version 3 +# (see: +# http://docs.mathjax.org/en/latest/web/components/output.html). +# Possible values are: HTML-CSS (which is slower, but has the best +# compatibility. This is the name for Mathjax version 2, for MathJax version 3 +# this will be translated into chtml), NativeMML (i.e. MathML. Only supported +# for NathJax 2. For MathJax version 3 chtml will be used instead.), chtml (This +# is the name for Mathjax version 3, for MathJax version 2 this will be +# translated into HTML-CSS) and SVG. +# The default value is: HTML-CSS. +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_FORMAT = HTML-CSS + +# When MathJax is enabled you need to specify the location relative to the HTML +# output directory using the MATHJAX_RELPATH option. The destination directory +# should contain the MathJax.js script. For instance, if the mathjax directory +# is located at the same level as the HTML output directory, then +# MATHJAX_RELPATH should be ../mathjax. The default value points to the MathJax +# Content Delivery Network so you can quickly see the result without installing +# MathJax. However, it is strongly recommended to install a local copy of +# MathJax from https://www.mathjax.org before deployment. The default value is: +# - in case of MathJax version 2: https://cdn.jsdelivr.net/npm/mathjax@2 +# - in case of MathJax version 3: https://cdn.jsdelivr.net/npm/mathjax@3 +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_RELPATH = + +# The MATHJAX_EXTENSIONS tag can be used to specify one or more MathJax +# extension names that should be enabled during MathJax rendering. For example +# for MathJax version 2 (see +# https://docs.mathjax.org/en/v2.7-latest/tex.html#tex-and-latex-extensions): +# MATHJAX_EXTENSIONS = TeX/AMSmath TeX/AMSsymbols +# For example for MathJax version 3 (see +# http://docs.mathjax.org/en/latest/input/tex/extensions/index.html): +# MATHJAX_EXTENSIONS = ams +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_EXTENSIONS = + +# The MATHJAX_CODEFILE tag can be used to specify a file with javascript pieces +# of code that will be used on startup of the MathJax code. See the MathJax site +# (see: +# http://docs.mathjax.org/en/v2.7-latest/output.html) for more details. For an +# example see the documentation. +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_CODEFILE = + +# When the SEARCHENGINE tag is enabled doxygen will generate a search box for +# the HTML output. The underlying search engine uses javascript and DHTML and +# should work on any modern browser. Note that when using HTML help +# (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets (GENERATE_DOCSET) +# there is already a search function so this one should typically be disabled. +# For large projects the javascript based search engine can be slow, then +# enabling SERVER_BASED_SEARCH may provide a better solution. It is possible to +# search using the keyboard; to jump to the search box use + S +# (what the is depends on the OS and browser, but it is typically +# , /