diff --git a/.gitignore b/.gitignore index a877fda..0bb6d62 100644 --- a/.gitignore +++ b/.gitignore @@ -72,3 +72,5 @@ unbound-1.4.5.tar.gz /unbound-1.16.0.tar.gz /unbound-1.16.2.tar.gz /unbound-1.16.2.tar.gz.asc +/unbound-1.*.tar.gz +/unbound-1.*.tar.gz.asc diff --git a/sources b/sources index abff2db..7d4806d 100644 --- a/sources +++ b/sources @@ -1,2 +1,2 @@ -SHA512 (unbound-1.16.2.tar.gz) = 0ea65ea63265be677441bd2a28df12098ec5e86c3372240c2874f9bd13752b8b818da81ae6076cf02cbeba3d36e397698a4c2b50570be1a6a8e47f57a0251572 -SHA512 (unbound-1.16.2.tar.gz.asc) = bc5241c86f90be76886209c81d6f1c025d4774fa00d114180b99d43999f31b1b4c8d123717b8a79a60bc3acfcbe9f46678b80b3d961431c7bfd05ff48c69ef4f +SHA512 (unbound-1.24.2.tar.gz) = 655d63ec5305323e84d82691425d74d98c332d0028517bd729d191e5f968ce9481b49ec7447d4c4906dce7997a998a115db36e911a59d2d877da5840c2080261 +SHA512 (unbound-1.24.2.tar.gz.asc) = 66a3e569a606cc3ed7dac9b411fba347da150728427619bdbf12ac57a5d7db1fc17963b1ba052a95d6c6fed67a6f0c1b5920318f6cd34e5091750626dd63fb21 diff --git a/unbound-1.16-CVE-2022-3204.patch b/unbound-1.16-CVE-2022-3204.patch deleted file mode 100644 index 2d2da91..0000000 --- a/unbound-1.16-CVE-2022-3204.patch +++ /dev/null @@ -1,218 +0,0 @@ -From 7af485f0fc9926425681ba0280ab6c2c8dd04530 Mon Sep 17 00:00:00 2001 -From: "W.C.A. Wijngaards" -Date: Wed, 21 Sep 2022 11:10:38 +0200 -Subject: [PATCH] - Patch for CVE-2022-3204 Non-Responsive Delegation Attack. - ---- - unbound-1.16.2/iterator/iter_delegpt.c | 3 +++ - unbound-1.16.2/iterator/iter_delegpt.h | 2 ++ - unbound-1.16.2/iterator/iter_utils.c | 3 +++ - unbound-1.16.2/iterator/iter_utils.h | 9 +++++++ - unbound-1.16.2/iterator/iterator.c | 36 +++++++++++++++++++++++++- - unbound-1.16.2/services/cache/dns.c | 3 +++ - unbound-1.16.2/services/mesh.c | 7 +++++ - unbound-1.16.2/services/mesh.h | 11 ++++++++ - 8 files changed, 73 insertions(+), 1 deletion(-) - -diff --git a/unbound-1.16.2/iterator/iter_delegpt.c b/unbound-1.16.2/iterator/iter_delegpt.c -index 4bffa1b..fd07aaa 100644 ---- a/unbound-1.16.2/iterator/iter_delegpt.c -+++ b/unbound-1.16.2/iterator/iter_delegpt.c -@@ -78,6 +78,7 @@ struct delegpt* delegpt_copy(struct delegpt* dp, struct regional* region) - if(!delegpt_add_ns(copy, region, ns->name, ns->lame, - ns->tls_auth_name, ns->port)) - return NULL; -+ copy->nslist->cache_lookup_count = ns->cache_lookup_count; - copy->nslist->resolved = ns->resolved; - copy->nslist->got4 = ns->got4; - copy->nslist->got6 = ns->got6; -@@ -121,6 +122,7 @@ delegpt_add_ns(struct delegpt* dp, struct regional* region, uint8_t* name, - ns->namelen = len; - dp->nslist = ns; - ns->name = regional_alloc_init(region, name, ns->namelen); -+ ns->cache_lookup_count = 0; - ns->resolved = 0; - ns->got4 = 0; - ns->got6 = 0; -@@ -620,6 +622,7 @@ int delegpt_add_ns_mlc(struct delegpt* dp, uint8_t* name, uint8_t lame, - } - ns->next = dp->nslist; - dp->nslist = ns; -+ ns->cache_lookup_count = 0; - ns->resolved = 0; - ns->got4 = 0; - ns->got6 = 0; -diff --git a/unbound-1.16.2/iterator/iter_delegpt.h b/unbound-1.16.2/iterator/iter_delegpt.h -index 62c8edc..586597a 100644 ---- a/unbound-1.16.2/iterator/iter_delegpt.h -+++ b/unbound-1.16.2/iterator/iter_delegpt.h -@@ -101,6 +101,8 @@ struct delegpt_ns { - uint8_t* name; - /** length of name */ - size_t namelen; -+ /** number of cache lookups for the name */ -+ int cache_lookup_count; - /** - * If the name has been resolved. false if not queried for yet. - * true if the A, AAAA queries have been generated. -diff --git a/unbound-1.16.2/iterator/iter_utils.c b/unbound-1.16.2/iterator/iter_utils.c -index 3e13e59..56b184a 100644 ---- a/unbound-1.16.2/iterator/iter_utils.c -+++ b/unbound-1.16.2/iterator/iter_utils.c -@@ -1209,6 +1209,9 @@ int iter_lookup_parent_glue_from_cache(struct module_env* env, - struct delegpt_ns* ns; - size_t num = delegpt_count_targets(dp); - for(ns = dp->nslist; ns; ns = ns->next) { -+ if(ns->cache_lookup_count > ITERATOR_NAME_CACHELOOKUP_MAX_PSIDE) -+ continue; -+ ns->cache_lookup_count++; - /* get cached parentside A */ - akey = rrset_cache_lookup(env->rrset_cache, ns->name, - ns->namelen, LDNS_RR_TYPE_A, qinfo->qclass, -diff --git a/unbound-1.16.2/iterator/iter_utils.h b/unbound-1.16.2/iterator/iter_utils.h -index 8583fde..850be96 100644 ---- a/unbound-1.16.2/iterator/iter_utils.h -+++ b/unbound-1.16.2/iterator/iter_utils.h -@@ -62,6 +62,15 @@ struct ub_packed_rrset_key; - struct module_stack; - struct outside_network; - -+/* max number of lookups in the cache for target nameserver names. -+ * This stops, for large delegations, N*N lookups in the cache. */ -+#define ITERATOR_NAME_CACHELOOKUP_MAX 3 -+/* max number of lookups in the cache for parentside glue for nameserver names -+ * This stops, for larger delegations, N*N lookups in the cache. -+ * It is a little larger than the nonpside max, so it allows a couple extra -+ * lookups of parent side glue. */ -+#define ITERATOR_NAME_CACHELOOKUP_MAX_PSIDE 5 -+ - /** - * Process config options and set iterator module state. - * Sets default values if no config is found. -diff --git a/unbound-1.16.2/iterator/iterator.c b/unbound-1.16.2/iterator/iterator.c -index 25e5cfe..da9b799 100644 ---- a/unbound-1.16.2/iterator/iterator.c -+++ b/unbound-1.16.2/iterator/iterator.c -@@ -1218,6 +1218,15 @@ generate_dnskey_prefetch(struct module_qstate* qstate, - (qstate->query_flags&BIT_RD) && !(qstate->query_flags&BIT_CD)){ - return; - } -+ /* we do not generate this prefetch when the query list is full, -+ * the query is fetched, if needed, when the validator wants it. -+ * At that time the validator waits for it, after spawning it. -+ * This means there is one state that uses cpu and a socket, the -+ * spawned while this one waits, and not several at the same time, -+ * if we had created the lookup here. And this helps to keep -+ * the total load down, but the query still succeeds to resolve. */ -+ if(mesh_jostle_exceeded(qstate->env->mesh)) -+ return; - - /* if the DNSKEY is in the cache this lookup will stop quickly */ - log_nametypeclass(VERB_ALGO, "schedule dnskey prefetch", -@@ -1911,6 +1920,14 @@ query_for_targets(struct module_qstate* qstate, struct iter_qstate* iq, - return 0; - } - query_count++; -+ /* If the mesh query list is full, exit the loop here. -+ * This makes the routine spawn one query at a time, -+ * and this means there is no query state load -+ * increase, because the spawned state uses cpu and a -+ * socket while this state waits for that spawned -+ * state. Next time we can look up further targets */ -+ if(mesh_jostle_exceeded(qstate->env->mesh)) -+ break; - } - /* Send the A request. */ - if(ie->supports_ipv4 && -@@ -1925,6 +1942,9 @@ query_for_targets(struct module_qstate* qstate, struct iter_qstate* iq, - return 0; - } - query_count++; -+ /* If the mesh query list is full, exit the loop. */ -+ if(mesh_jostle_exceeded(qstate->env->mesh)) -+ break; - } - - /* mark this target as in progress. */ -@@ -2085,6 +2105,15 @@ processLastResort(struct module_qstate* qstate, struct iter_qstate* iq, - } - ns->done_pside6 = 1; - query_count++; -+ if(mesh_jostle_exceeded(qstate->env->mesh)) { -+ /* Wait for the lookup; do not spawn multiple -+ * lookups at a time. */ -+ verbose(VERB_ALGO, "try parent-side glue lookup"); -+ iq->num_target_queries += query_count; -+ target_count_increase(iq, query_count); -+ qstate->ext_state[id] = module_wait_subquery; -+ return 0; -+ } - } - if(ie->supports_ipv4 && !ns->done_pside4) { - /* Send the A request. */ -@@ -2560,7 +2589,12 @@ processQueryTargets(struct module_qstate* qstate, struct iter_qstate* iq, - if(iq->depth < ie->max_dependency_depth - && iq->num_target_queries == 0 - && (!iq->target_count || iq->target_count[TARGET_COUNT_NX]==0) -- && iq->sent_count < TARGET_FETCH_STOP) { -+ && iq->sent_count < TARGET_FETCH_STOP -+ /* if the mesh query list is full, then do not waste cpu -+ * and sockets to fetch promiscuous targets. They can be -+ * looked up when needed. */ -+ && !mesh_jostle_exceeded(qstate->env->mesh) -+ ) { - tf_policy = ie->target_fetch_policy[iq->depth]; - } - -diff --git a/unbound-1.16.2/services/cache/dns.c b/unbound-1.16.2/services/cache/dns.c -index 6bca8d8..b6e5697 100644 ---- a/unbound-1.16.2/services/cache/dns.c -+++ b/unbound-1.16.2/services/cache/dns.c -@@ -404,6 +404,9 @@ cache_fill_missing(struct module_env* env, uint16_t qclass, - struct ub_packed_rrset_key* akey; - time_t now = *env->now; - for(ns = dp->nslist; ns; ns = ns->next) { -+ if(ns->cache_lookup_count > ITERATOR_NAME_CACHELOOKUP_MAX) -+ continue; -+ ns->cache_lookup_count++; - akey = rrset_cache_lookup(env->rrset_cache, ns->name, - ns->namelen, LDNS_RR_TYPE_A, qclass, 0, now, 0); - if(akey) { -diff --git a/unbound-1.16.2/services/mesh.c b/unbound-1.16.2/services/mesh.c -index 30bcf7c..2a41194 100644 ---- a/unbound-1.16.2/services/mesh.c -+++ b/unbound-1.16.2/services/mesh.c -@@ -2240,3 +2240,10 @@ mesh_serve_expired_callback(void* arg) - mesh_do_callback(mstate, LDNS_RCODE_NOERROR, msg->rep, c, &tv); - } - } -+ -+int mesh_jostle_exceeded(struct mesh_area* mesh) -+{ -+ if(mesh->all.count < mesh->max_reply_states) -+ return 0; -+ return 1; -+} -diff --git a/unbound-1.16.2/services/mesh.h b/unbound-1.16.2/services/mesh.h -index 3be9b63..25121a6 100644 ---- a/unbound-1.16.2/services/mesh.h -+++ b/unbound-1.16.2/services/mesh.h -@@ -685,4 +685,15 @@ struct dns_msg* - mesh_serve_expired_lookup(struct module_qstate* qstate, - struct query_info* lookup_qinfo); - -+/** -+ * See if the mesh has space for more queries. You can allocate queries -+ * anyway, but this checks for the allocated space. -+ * @param mesh: mesh area. -+ * @return true if the query list is full. -+ * It checks the number of all queries, not just number of reply states, -+ * that have a client address. So that spawned queries count too, -+ * that were created by the iterator, or other modules. -+ */ -+int mesh_jostle_exceeded(struct mesh_area* mesh); -+ - #endif /* SERVICES_MESH_H */ --- -2.37.3 - diff --git a/unbound-1.16-CVE-2023-50387-CVE-2023-50868.patch b/unbound-1.16-CVE-2023-50387-CVE-2023-50868.patch deleted file mode 100644 index b2e1ca5..0000000 --- a/unbound-1.16-CVE-2023-50387-CVE-2023-50868.patch +++ /dev/null @@ -1,2304 +0,0 @@ -commit cc3259547f5f1c4a8206e169cf579d08f95d5178 -Author: Tomas Korbar -Date: Wed Feb 14 13:37:21 2024 +0100 - - Fix CVE-2023-50387 and CVE-2023-50868 - -diff --git a/unbound-1.16.2/services/authzone.c b/unbound-1.16.2/services/authzone.c -index b9e0b11..04527c3 100644 ---- a/unbound-1.16.2/services/authzone.c -+++ b/unbound-1.16.2/services/authzone.c -@@ -7766,6 +7766,7 @@ static int zonemd_dnssec_verify_rrset(struct auth_zone* z, - enum sec_status sec; - struct val_env* ve; - int m; -+ int verified = 0; - m = modstack_find(mods, "validator"); - if(m == -1) { - auth_zone_log(z->name, VERB_ALGO, "zonemd dnssec verify: have " -@@ -7789,7 +7790,7 @@ static int zonemd_dnssec_verify_rrset(struct auth_zone* z, - "zonemd: verify %s RRset with DNSKEY", typestr); - } - sec = dnskeyset_verify_rrset(env, ve, &pk, dnskey, sigalg, why_bogus, NULL, -- LDNS_SECTION_ANSWER, NULL); -+ LDNS_SECTION_ANSWER, NULL, &verified); - if(sec == sec_status_secure) { - return 1; - } -diff --git a/unbound-1.16.2/services/cache/dns.c b/unbound-1.16.2/services/cache/dns.c -index b6e5697..0d75595 100644 ---- a/unbound-1.16.2/services/cache/dns.c -+++ b/unbound-1.16.2/services/cache/dns.c -@@ -695,6 +695,24 @@ tomsg(struct module_env* env, struct query_info* q, struct reply_info* r, - return msg; - } - -+struct dns_msg* -+dns_msg_deepcopy_region(struct dns_msg* origin, struct regional* region) -+{ -+ size_t i; -+ struct dns_msg* res = NULL; -+ res = gen_dns_msg(region, &origin->qinfo, origin->rep->rrset_count); -+ if(!res) return NULL; -+ *res->rep = *origin->rep; -+ for(i=0; irep->rrset_count; i++) { -+ res->rep->rrsets[i] = packed_rrset_copy_region( -+ origin->rep->rrsets[i], region, 0); -+ if(!res->rep->rrsets[i]) { -+ return NULL; -+ } -+ } -+ return res; -+} -+ - /** synthesize RRset-only response from cached RRset item */ - static struct dns_msg* - rrset_msg(struct ub_packed_rrset_key* rrset, struct regional* region, -diff --git a/unbound-1.16.2/services/cache/dns.h b/unbound-1.16.2/services/cache/dns.h -index 147f992..c2bf23c 100644 ---- a/unbound-1.16.2/services/cache/dns.h -+++ b/unbound-1.16.2/services/cache/dns.h -@@ -164,6 +164,15 @@ struct dns_msg* tomsg(struct module_env* env, struct query_info* q, - struct reply_info* r, struct regional* region, time_t now, - int allow_expired, struct regional* scratch); - -+/** -+ * Deep copy a dns_msg to a region. -+ * @param origin: the dns_msg to copy. -+ * @param region: the region to copy all the data to. -+ * @return the new dns_msg or NULL on malloc error. -+ */ -+struct dns_msg* dns_msg_deepcopy_region(struct dns_msg* origin, -+ struct regional* region); -+ - /** - * Find cached message - * @param env: module environment with the DNS cache. -diff --git a/unbound-1.16.2/testcode/unitverify.c b/unbound-1.16.2/testcode/unitverify.c -index ff069a1..395b4c2 100644 ---- a/unbound-1.16.2/testcode/unitverify.c -+++ b/unbound-1.16.2/testcode/unitverify.c -@@ -180,6 +180,7 @@ verifytest_rrset(struct module_env* env, struct val_env* ve, - enum sec_status sec; - char* reason = NULL; - uint8_t sigalg[ALGO_NEEDS_MAX+1]; -+ int verified = 0; - if(vsig) { - log_nametypeclass(VERB_QUERY, "verify of rrset", - rrset->rk.dname, ntohs(rrset->rk.type), -@@ -188,7 +189,7 @@ verifytest_rrset(struct module_env* env, struct val_env* ve, - setup_sigalg(dnskey, sigalg); /* check all algorithms in the dnskey */ - /* ok to give null as qstate here, won't be used for answer section. */ - sec = dnskeyset_verify_rrset(env, ve, rrset, dnskey, sigalg, &reason, NULL, -- LDNS_SECTION_ANSWER, NULL); -+ LDNS_SECTION_ANSWER, NULL, &verified); - if(vsig) { - printf("verify outcome is: %s %s\n", sec_status_to_string(sec), - reason?reason:""); -@@ -442,9 +443,9 @@ nsec3_hash_test_entry(struct entry* e, rbtree_type* ct, - - ret = nsec3_hash_name(ct, region, buf, nsec3, 0, qname, - qinfo.qname_len, &hash); -- if(ret != 1) { -+ if(ret < 1) { - printf("Bad nsec3_hash_name retcode %d\n", ret); -- unit_assert(ret == 1); -+ unit_assert(ret == 1 || ret == 2); - } - unit_assert(hash->dname && hash->hash && hash->hash_len && - hash->b32 && hash->b32_len); -diff --git a/unbound-1.16.2/testdata/val_any.rpl b/unbound-1.16.2/testdata/val_any.rpl -index 4ce1951..90263af 100644 ---- a/unbound-1.16.2/testdata/val_any.rpl -+++ b/unbound-1.16.2/testdata/val_any.rpl -@@ -161,6 +161,9 @@ SECTION QUESTION - example.com. IN ANY - ENTRY_END - -+; Allow validation resuming for the RRSIGs -+STEP 2 TIME_PASSES ELAPSE 0.05 -+ - ; recursion happens here. - STEP 10 CHECK_ANSWER - ENTRY_BEGIN -diff --git a/unbound-1.16.2/testdata/val_any_dname.rpl b/unbound-1.16.2/testdata/val_any_dname.rpl -index 6ab3cde..dd65e97 100644 ---- a/unbound-1.16.2/testdata/val_any_dname.rpl -+++ b/unbound-1.16.2/testdata/val_any_dname.rpl -@@ -163,6 +163,9 @@ SECTION QUESTION - example.com. IN ANY - ENTRY_END - -+; Allow validation resuming for the RRSIGs -+STEP 2 TIME_PASSES ELAPSE 0.05 -+ - ; recursion happens here. - STEP 10 CHECK_ANSWER - ENTRY_BEGIN -diff --git a/unbound-1.16.2/testdata/val_nx_nsec3_collision.rpl b/unbound-1.16.2/testdata/val_nx_nsec3_collision.rpl -index 8ff7e4b..87a55f5 100644 ---- a/unbound-1.16.2/testdata/val_nx_nsec3_collision.rpl -+++ b/unbound-1.16.2/testdata/val_nx_nsec3_collision.rpl -@@ -156,6 +156,9 @@ SECTION QUESTION - www.example.com. IN A - ENTRY_END - -+; Allow validation resuming for NSEC3 hash calculations -+STEP 2 TIME_PASSES ELAPSE 0.05 -+ - ; recursion happens here. - STEP 10 CHECK_ANSWER - ENTRY_BEGIN -diff --git a/unbound-1.16.2/util/fptr_wlist.c b/unbound-1.16.2/util/fptr_wlist.c -index 05a22d4..5b198b2 100644 ---- a/unbound-1.16.2/util/fptr_wlist.c -+++ b/unbound-1.16.2/util/fptr_wlist.c -@@ -131,6 +131,7 @@ fptr_whitelist_comm_timer(void (*fptr)(void*)) - else if(fptr == &pending_udp_timer_delay_cb) return 1; - else if(fptr == &worker_stat_timer_cb) return 1; - else if(fptr == &worker_probe_timer_cb) return 1; -+ else if(fptr == &validate_suspend_timer_cb) return 1; - #ifdef UB_ON_WINDOWS - else if(fptr == &wsvc_cron_cb) return 1; - #endif -diff --git a/unbound-1.16.2/validator/val_nsec.c b/unbound-1.16.2/validator/val_nsec.c -index 876bfab..5871db9 100644 ---- a/unbound-1.16.2/validator/val_nsec.c -+++ b/unbound-1.16.2/validator/val_nsec.c -@@ -180,6 +180,7 @@ nsec_verify_rrset(struct module_env* env, struct val_env* ve, - { - struct packed_rrset_data* d = (struct packed_rrset_data*) - nsec->entry.data; -+ int verified = 0; - if(!d) return 0; - if(d->security == sec_status_secure) - return 1; -@@ -187,7 +188,7 @@ nsec_verify_rrset(struct module_env* env, struct val_env* ve, - if(d->security == sec_status_secure) - return 1; - d->security = val_verify_rrset_entry(env, ve, nsec, kkey, reason, -- NULL, LDNS_SECTION_AUTHORITY, qstate); -+ NULL, LDNS_SECTION_AUTHORITY, qstate, &verified); - if(d->security == sec_status_secure) { - rrset_update_sec_status(env->rrset_cache, nsec, *env->now); - return 1; -diff --git a/unbound-1.16.2/validator/val_nsec3.c b/unbound-1.16.2/validator/val_nsec3.c -index a2b3794..95d1e4d 100644 ---- a/unbound-1.16.2/validator/val_nsec3.c -+++ b/unbound-1.16.2/validator/val_nsec3.c -@@ -57,6 +57,19 @@ - /* we include nsec.h for the bitmap_has_type function */ - #include "validator/val_nsec.h" - #include "sldns/sbuffer.h" -+#include "util/config_file.h" -+ -+/** -+ * Max number of NSEC3 calculations at once, suspend query for later. -+ * 8 is low enough and allows for cases where multiple proofs are needed. -+ */ -+#define MAX_NSEC3_CALCULATIONS 8 -+/** -+ * When all allowed NSEC3 calculations at once resulted in error treat as -+ * bogus. NSEC3 hash errors are not cached and this helps breaks loops with -+ * erroneous data. -+ */ -+#define MAX_NSEC3_ERRORS -1 - - /** - * This function we get from ldns-compat or from base system -@@ -532,6 +545,17 @@ nsec3_hash_cmp(const void* c1, const void* c2) - return memcmp(s1, s2, s1len); - } - -+int -+nsec3_cache_table_init(struct nsec3_cache_table* ct, struct regional* region) -+{ -+ if(ct->ct) return 1; -+ ct->ct = (rbtree_type*)regional_alloc(region, sizeof(*ct->ct)); -+ if(!ct->ct) return 0; -+ ct->region = region; -+ rbtree_init(ct->ct, &nsec3_hash_cmp); -+ return 1; -+} -+ - size_t - nsec3_get_hashed(sldns_buffer* buf, uint8_t* nm, size_t nmlen, int algo, - size_t iter, uint8_t* salt, size_t saltlen, uint8_t* res, size_t max) -@@ -646,7 +670,7 @@ nsec3_hash_name(rbtree_type* table, struct regional* region, sldns_buffer* buf, - c = (struct nsec3_cached_hash*)rbtree_search(table, &looki); - if(c) { - *hash = c; -- return 1; -+ return 2; - } - /* create a new entry */ - c = (struct nsec3_cached_hash*)regional_alloc(region, sizeof(*c)); -@@ -658,10 +682,10 @@ nsec3_hash_name(rbtree_type* table, struct regional* region, sldns_buffer* buf, - c->dname_len = dname_len; - r = nsec3_calc_hash(region, buf, c); - if(r != 1) -- return r; -+ return r; /* returns -1 or 0 */ - r = nsec3_calc_b32(region, buf, c); - if(r != 1) -- return r; -+ return r; /* returns 0 */ - #ifdef UNBOUND_DEBUG - n = - #else -@@ -704,6 +728,7 @@ nsec3_hash_matches_owner(struct nsec3_filter* flt, - struct nsec3_cached_hash* hash, struct ub_packed_rrset_key* s) - { - uint8_t* nm = s->rk.dname; -+ if(!hash) return 0; /* please clang */ - /* compare, does hash of name based on params in this NSEC3 - * match the owner name of this NSEC3? - * name must be: base32 . zone name -@@ -730,34 +755,50 @@ nsec3_hash_matches_owner(struct nsec3_filter* flt, - * @param nmlen: length of name. - * @param rrset: nsec3 that matches is returned here. - * @param rr: rr number in nsec3 rrset that matches. -+ * @param calculations: current hash calculations. - * @return true if a matching NSEC3 is found, false if not. - */ - static int - find_matching_nsec3(struct module_env* env, struct nsec3_filter* flt, -- rbtree_type* ct, uint8_t* nm, size_t nmlen, -- struct ub_packed_rrset_key** rrset, int* rr) -+ struct nsec3_cache_table* ct, uint8_t* nm, size_t nmlen, -+ struct ub_packed_rrset_key** rrset, int* rr, -+ int* calculations) - { - size_t i_rs; - int i_rr; - struct ub_packed_rrset_key* s; - struct nsec3_cached_hash* hash = NULL; - int r; -+ int calc_errors = 0; - - /* this loop skips other-zone and unknown NSEC3s, also non-NSEC3 RRs */ - for(s=filter_first(flt, &i_rs, &i_rr); s; - s=filter_next(flt, &i_rs, &i_rr)) { -+ /* check if we are allowed more calculations */ -+ if(*calculations >= MAX_NSEC3_CALCULATIONS) { -+ if(calc_errors == *calculations) { -+ *calculations = MAX_NSEC3_ERRORS; -+ } -+ break; -+ } - /* get name hashed for this NSEC3 RR */ -- r = nsec3_hash_name(ct, env->scratch, env->scratch_buffer, -+ r = nsec3_hash_name(ct->ct, ct->region, env->scratch_buffer, - s, i_rr, nm, nmlen, &hash); - if(r == 0) { - log_err("nsec3: malloc failure"); - break; /* alloc failure */ -- } else if(r != 1) -- continue; /* malformed NSEC3 */ -- else if(nsec3_hash_matches_owner(flt, hash, s)) { -- *rrset = s; /* rrset with this name */ -- *rr = i_rr; /* matches hash with these parameters */ -- return 1; -+ } else if(r < 0) { -+ /* malformed NSEC3 */ -+ calc_errors++; -+ (*calculations)++; -+ continue; -+ } else { -+ if(r == 1) (*calculations)++; -+ if(nsec3_hash_matches_owner(flt, hash, s)) { -+ *rrset = s; /* rrset with this name */ -+ *rr = i_rr; /* matches hash with these parameters */ -+ return 1; -+ } - } - } - *rrset = NULL; -@@ -775,6 +816,7 @@ nsec3_covers(uint8_t* zone, struct nsec3_cached_hash* hash, - if(!nsec3_get_nextowner(rrset, rr, &next, &nextlen)) - return 0; /* malformed RR proves nothing */ - -+ if(!hash) return 0; /* please clang */ - /* check the owner name is a hashed value . apex - * base32 encoded values must have equal length. - * hash_value and next hash value must have equal length. */ -@@ -823,35 +865,51 @@ nsec3_covers(uint8_t* zone, struct nsec3_cached_hash* hash, - * @param nmlen: length of name. - * @param rrset: covering NSEC3 rrset is returned here. - * @param rr: rr of cover is returned here. -+ * @param calculations: current hash calculations. - * @return true if a covering NSEC3 is found, false if not. - */ - static int - find_covering_nsec3(struct module_env* env, struct nsec3_filter* flt, -- rbtree_type* ct, uint8_t* nm, size_t nmlen, -- struct ub_packed_rrset_key** rrset, int* rr) -+ struct nsec3_cache_table* ct, uint8_t* nm, size_t nmlen, -+ struct ub_packed_rrset_key** rrset, int* rr, -+ int* calculations) - { - size_t i_rs; - int i_rr; - struct ub_packed_rrset_key* s; - struct nsec3_cached_hash* hash = NULL; - int r; -+ int calc_errors = 0; - - /* this loop skips other-zone and unknown NSEC3s, also non-NSEC3 RRs */ - for(s=filter_first(flt, &i_rs, &i_rr); s; - s=filter_next(flt, &i_rs, &i_rr)) { -+ /* check if we are allowed more calculations */ -+ if(*calculations >= MAX_NSEC3_CALCULATIONS) { -+ if(calc_errors == *calculations) { -+ *calculations = MAX_NSEC3_ERRORS; -+ } -+ break; -+ } - /* get name hashed for this NSEC3 RR */ -- r = nsec3_hash_name(ct, env->scratch, env->scratch_buffer, -+ r = nsec3_hash_name(ct->ct, ct->region, env->scratch_buffer, - s, i_rr, nm, nmlen, &hash); - if(r == 0) { - log_err("nsec3: malloc failure"); - break; /* alloc failure */ -- } else if(r != 1) -- continue; /* malformed NSEC3 */ -- else if(nsec3_covers(flt->zone, hash, s, i_rr, -- env->scratch_buffer)) { -- *rrset = s; /* rrset with this name */ -- *rr = i_rr; /* covers hash with these parameters */ -- return 1; -+ } else if(r < 0) { -+ /* malformed NSEC3 */ -+ calc_errors++; -+ (*calculations)++; -+ continue; -+ } else { -+ if(r == 1) (*calculations)++; -+ if(nsec3_covers(flt->zone, hash, s, i_rr, -+ env->scratch_buffer)) { -+ *rrset = s; /* rrset with this name */ -+ *rr = i_rr; /* covers hash with these parameters */ -+ return 1; -+ } - } - } - *rrset = NULL; -@@ -869,11 +927,13 @@ find_covering_nsec3(struct module_env* env, struct nsec3_filter* flt, - * @param ct: cached hashes table. - * @param qinfo: query that is verified for. - * @param ce: closest encloser information is returned in here. -+ * @param calculations: current hash calculations. - * @return true if a closest encloser candidate is found, false if not. - */ - static int --nsec3_find_closest_encloser(struct module_env* env, struct nsec3_filter* flt, -- rbtree_type* ct, struct query_info* qinfo, struct ce_response* ce) -+nsec3_find_closest_encloser(struct module_env* env, struct nsec3_filter* flt, -+ struct nsec3_cache_table* ct, struct query_info* qinfo, -+ struct ce_response* ce, int* calculations) - { - uint8_t* nm = qinfo->qname; - size_t nmlen = qinfo->qname_len; -@@ -888,8 +948,12 @@ nsec3_find_closest_encloser(struct module_env* env, struct nsec3_filter* flt, - * may be the case. */ - - while(dname_subdomain_c(nm, flt->zone)) { -+ if(*calculations >= MAX_NSEC3_CALCULATIONS || -+ *calculations == MAX_NSEC3_ERRORS) { -+ return 0; -+ } - if(find_matching_nsec3(env, flt, ct, nm, nmlen, -- &ce->ce_rrset, &ce->ce_rr)) { -+ &ce->ce_rrset, &ce->ce_rr, calculations)) { - ce->ce = nm; - ce->ce_len = nmlen; - return 1; -@@ -933,22 +997,38 @@ next_closer(uint8_t* qname, size_t qnamelen, uint8_t* ce, - * If set true, and the return value is true, then you can be - * certain that the ce.nc_rrset and ce.nc_rr are set properly. - * @param ce: closest encloser information is returned in here. -+ * @param calculations: pointer to the current NSEC3 hash calculations. - * @return bogus if no closest encloser could be proven. - * secure if a closest encloser could be proven, ce is set. - * insecure if the closest-encloser candidate turns out to prove - * that an insecure delegation exists above the qname. -+ * unchecked if no more hash calculations are allowed at this point. - */ - static enum sec_status --nsec3_prove_closest_encloser(struct module_env* env, struct nsec3_filter* flt, -- rbtree_type* ct, struct query_info* qinfo, int prove_does_not_exist, -- struct ce_response* ce) -+nsec3_prove_closest_encloser(struct module_env* env, struct nsec3_filter* flt, -+ struct nsec3_cache_table* ct, struct query_info* qinfo, -+ int prove_does_not_exist, struct ce_response* ce, int* calculations) - { - uint8_t* nc; - size_t nc_len; - /* robust: clean out ce, in case it gets abused later */ - memset(ce, 0, sizeof(*ce)); - -- if(!nsec3_find_closest_encloser(env, flt, ct, qinfo, ce)) { -+ if(!nsec3_find_closest_encloser(env, flt, ct, qinfo, ce, calculations)) { -+ if(*calculations == MAX_NSEC3_ERRORS) { -+ verbose(VERB_ALGO, "nsec3 proveClosestEncloser: could " -+ "not find a candidate for the closest " -+ "encloser; all attempted hash calculations " -+ "were erroneous; bogus"); -+ return sec_status_bogus; -+ } else if(*calculations >= MAX_NSEC3_CALCULATIONS) { -+ verbose(VERB_ALGO, "nsec3 proveClosestEncloser: could " -+ "not find a candidate for the closest " -+ "encloser; reached MAX_NSEC3_CALCULATIONS " -+ "(%d); unchecked still", -+ MAX_NSEC3_CALCULATIONS); -+ return sec_status_unchecked; -+ } - verbose(VERB_ALGO, "nsec3 proveClosestEncloser: could " - "not find a candidate for the closest encloser."); - return sec_status_bogus; -@@ -989,9 +1069,23 @@ nsec3_prove_closest_encloser(struct module_env* env, struct nsec3_filter* flt, - /* Otherwise, we need to show that the next closer name is covered. */ - next_closer(qinfo->qname, qinfo->qname_len, ce->ce, &nc, &nc_len); - if(!find_covering_nsec3(env, flt, ct, nc, nc_len, -- &ce->nc_rrset, &ce->nc_rr)) { -+ &ce->nc_rrset, &ce->nc_rr, calculations)) { -+ if(*calculations == MAX_NSEC3_ERRORS) { -+ verbose(VERB_ALGO, "nsec3: Could not find proof that the " -+ "candidate encloser was the closest encloser; " -+ "all attempted hash calculations were " -+ "erroneous; bogus"); -+ return sec_status_bogus; -+ } else if(*calculations >= MAX_NSEC3_CALCULATIONS) { -+ verbose(VERB_ALGO, "nsec3: Could not find proof that the " -+ "candidate encloser was the closest encloser; " -+ "reached MAX_NSEC3_CALCULATIONS (%d); " -+ "unchecked still", -+ MAX_NSEC3_CALCULATIONS); -+ return sec_status_unchecked; -+ } - verbose(VERB_ALGO, "nsec3: Could not find proof that the " -- "candidate encloser was the closest encloser"); -+ "candidate encloser was the closest encloser"); - return sec_status_bogus; - } - return sec_status_secure; -@@ -1019,8 +1113,8 @@ nsec3_ce_wildcard(struct regional* region, uint8_t* ce, size_t celen, - - /** Do the name error proof */ - static enum sec_status --nsec3_do_prove_nameerror(struct module_env* env, struct nsec3_filter* flt, -- rbtree_type* ct, struct query_info* qinfo) -+nsec3_do_prove_nameerror(struct module_env* env, struct nsec3_filter* flt, -+ struct nsec3_cache_table* ct, struct query_info* qinfo, int* calc) - { - struct ce_response ce; - uint8_t* wc; -@@ -1032,11 +1126,15 @@ nsec3_do_prove_nameerror(struct module_env* env, struct nsec3_filter* flt, - /* First locate and prove the closest encloser to qname. We will - * use the variant that fails if the closest encloser turns out - * to be qname. */ -- sec = nsec3_prove_closest_encloser(env, flt, ct, qinfo, 1, &ce); -+ sec = nsec3_prove_closest_encloser(env, flt, ct, qinfo, 1, &ce, calc); - if(sec != sec_status_secure) { - if(sec == sec_status_bogus) - verbose(VERB_ALGO, "nsec3 nameerror proof: failed " - "to prove a closest encloser"); -+ else if(sec == sec_status_unchecked) -+ verbose(VERB_ALGO, "nsec3 nameerror proof: will " -+ "continue proving closest encloser after " -+ "suspend"); - else verbose(VERB_ALGO, "nsec3 nameerror proof: closest " - "nsec3 is an insecure delegation"); - return sec; -@@ -1046,9 +1144,27 @@ nsec3_do_prove_nameerror(struct module_env* env, struct nsec3_filter* flt, - /* At this point, we know that qname does not exist. Now we need - * to prove that the wildcard does not exist. */ - log_assert(ce.ce); -- wc = nsec3_ce_wildcard(env->scratch, ce.ce, ce.ce_len, &wclen); -- if(!wc || !find_covering_nsec3(env, flt, ct, wc, wclen, -- &wc_rrset, &wc_rr)) { -+ wc = nsec3_ce_wildcard(ct->region, ce.ce, ce.ce_len, &wclen); -+ if(!wc) { -+ verbose(VERB_ALGO, "nsec3 nameerror proof: could not prove " -+ "that the applicable wildcard did not exist."); -+ return sec_status_bogus; -+ } -+ if(!find_covering_nsec3(env, flt, ct, wc, wclen, &wc_rrset, &wc_rr, calc)) { -+ if(*calc == MAX_NSEC3_ERRORS) { -+ verbose(VERB_ALGO, "nsec3 nameerror proof: could not prove " -+ "that the applicable wildcard did not exist; " -+ "all attempted hash calculations were " -+ "erroneous; bogus"); -+ return sec_status_bogus; -+ } else if(*calc >= MAX_NSEC3_CALCULATIONS) { -+ verbose(VERB_ALGO, "nsec3 nameerror proof: could not prove " -+ "that the applicable wildcard did not exist; " -+ "reached MAX_NSEC3_CALCULATIONS (%d); " -+ "unchecked still", -+ MAX_NSEC3_CALCULATIONS); -+ return sec_status_unchecked; -+ } - verbose(VERB_ALGO, "nsec3 nameerror proof: could not prove " - "that the applicable wildcard did not exist."); - return sec_status_bogus; -@@ -1064,14 +1180,13 @@ nsec3_do_prove_nameerror(struct module_env* env, struct nsec3_filter* flt, - enum sec_status - nsec3_prove_nameerror(struct module_env* env, struct val_env* ve, - struct ub_packed_rrset_key** list, size_t num, -- struct query_info* qinfo, struct key_entry_key* kkey) -+ struct query_info* qinfo, struct key_entry_key* kkey, -+ struct nsec3_cache_table* ct, int* calc) - { -- rbtree_type ct; - struct nsec3_filter flt; - - if(!list || num == 0 || !kkey || !key_entry_isgood(kkey)) - return sec_status_bogus; /* no valid NSEC3s, bogus */ -- rbtree_init(&ct, &nsec3_hash_cmp); /* init names-to-hash cache */ - filter_init(&flt, list, num, qinfo); /* init RR iterator */ - if(!flt.zone) - return sec_status_bogus; /* no RRs */ -@@ -1079,7 +1194,7 @@ nsec3_prove_nameerror(struct module_env* env, struct val_env* ve, - return sec_status_insecure; /* iteration count too high */ - log_nametypeclass(VERB_ALGO, "start nsec3 nameerror proof, zone", - flt.zone, 0, 0); -- return nsec3_do_prove_nameerror(env, &flt, &ct, qinfo); -+ return nsec3_do_prove_nameerror(env, &flt, ct, qinfo, calc); - } - - /* -@@ -1089,8 +1204,9 @@ nsec3_prove_nameerror(struct module_env* env, struct val_env* ve, - - /** Do the nodata proof */ - static enum sec_status --nsec3_do_prove_nodata(struct module_env* env, struct nsec3_filter* flt, -- rbtree_type* ct, struct query_info* qinfo) -+nsec3_do_prove_nodata(struct module_env* env, struct nsec3_filter* flt, -+ struct nsec3_cache_table* ct, struct query_info* qinfo, -+ int* calc) - { - struct ce_response ce; - uint8_t* wc; -@@ -1100,7 +1216,7 @@ nsec3_do_prove_nodata(struct module_env* env, struct nsec3_filter* flt, - enum sec_status sec; - - if(find_matching_nsec3(env, flt, ct, qinfo->qname, qinfo->qname_len, -- &rrset, &rr)) { -+ &rrset, &rr, calc)) { - /* cases 1 and 2 */ - if(nsec3_has_type(rrset, rr, qinfo->qtype)) { - verbose(VERB_ALGO, "proveNodata: Matching NSEC3 " -@@ -1144,11 +1260,23 @@ nsec3_do_prove_nodata(struct module_env* env, struct nsec3_filter* flt, - } - return sec_status_secure; - } -+ if(*calc == MAX_NSEC3_ERRORS) { -+ verbose(VERB_ALGO, "proveNodata: all attempted hash " -+ "calculations were erroneous while finding a matching " -+ "NSEC3, bogus"); -+ return sec_status_bogus; -+ } else if(*calc >= MAX_NSEC3_CALCULATIONS) { -+ verbose(VERB_ALGO, "proveNodata: reached " -+ "MAX_NSEC3_CALCULATIONS (%d) while finding a " -+ "matching NSEC3; unchecked still", -+ MAX_NSEC3_CALCULATIONS); -+ return sec_status_unchecked; -+ } - - /* For cases 3 - 5, we need the proven closest encloser, and it - * can't match qname. Although, at this point, we know that it - * won't since we just checked that. */ -- sec = nsec3_prove_closest_encloser(env, flt, ct, qinfo, 1, &ce); -+ sec = nsec3_prove_closest_encloser(env, flt, ct, qinfo, 1, &ce, calc); - if(sec == sec_status_bogus) { - verbose(VERB_ALGO, "proveNodata: did not match qname, " - "nor found a proven closest encloser."); -@@ -1157,14 +1285,17 @@ nsec3_do_prove_nodata(struct module_env* env, struct nsec3_filter* flt, - verbose(VERB_ALGO, "proveNodata: closest nsec3 is insecure " - "delegation."); - return sec_status_insecure; -+ } else if(sec==sec_status_unchecked) { -+ return sec_status_unchecked; - } - - /* Case 3: removed */ - - /* Case 4: */ - log_assert(ce.ce); -- wc = nsec3_ce_wildcard(env->scratch, ce.ce, ce.ce_len, &wclen); -- if(wc && find_matching_nsec3(env, flt, ct, wc, wclen, &rrset, &rr)) { -+ wc = nsec3_ce_wildcard(ct->region, ce.ce, ce.ce_len, &wclen); -+ if(wc && find_matching_nsec3(env, flt, ct, wc, wclen, &rrset, &rr, -+ calc)) { - /* found wildcard */ - if(nsec3_has_type(rrset, rr, qinfo->qtype)) { - verbose(VERB_ALGO, "nsec3 nodata proof: matching " -@@ -1195,6 +1326,18 @@ nsec3_do_prove_nodata(struct module_env* env, struct nsec3_filter* flt, - } - return sec_status_secure; - } -+ if(*calc == MAX_NSEC3_ERRORS) { -+ verbose(VERB_ALGO, "nsec3 nodata proof: all attempted hash " -+ "calculations were erroneous while matching " -+ "wildcard, bogus"); -+ return sec_status_bogus; -+ } else if(*calc >= MAX_NSEC3_CALCULATIONS) { -+ verbose(VERB_ALGO, "nsec3 nodata proof: reached " -+ "MAX_NSEC3_CALCULATIONS (%d) while matching " -+ "wildcard, unchecked still", -+ MAX_NSEC3_CALCULATIONS); -+ return sec_status_unchecked; -+ } - - /* Case 5: */ - /* Due to forwarders, cnames, and other collating effects, we -@@ -1223,28 +1366,27 @@ nsec3_do_prove_nodata(struct module_env* env, struct nsec3_filter* flt, - enum sec_status - nsec3_prove_nodata(struct module_env* env, struct val_env* ve, - struct ub_packed_rrset_key** list, size_t num, -- struct query_info* qinfo, struct key_entry_key* kkey) -+ struct query_info* qinfo, struct key_entry_key* kkey, -+ struct nsec3_cache_table* ct, int* calc) - { -- rbtree_type ct; - struct nsec3_filter flt; - - if(!list || num == 0 || !kkey || !key_entry_isgood(kkey)) - return sec_status_bogus; /* no valid NSEC3s, bogus */ -- rbtree_init(&ct, &nsec3_hash_cmp); /* init names-to-hash cache */ - filter_init(&flt, list, num, qinfo); /* init RR iterator */ - if(!flt.zone) - return sec_status_bogus; /* no RRs */ - if(nsec3_iteration_count_high(ve, &flt, kkey)) - return sec_status_insecure; /* iteration count too high */ -- return nsec3_do_prove_nodata(env, &flt, &ct, qinfo); -+ return nsec3_do_prove_nodata(env, &flt, ct, qinfo, calc); - } - - enum sec_status - nsec3_prove_wildcard(struct module_env* env, struct val_env* ve, - struct ub_packed_rrset_key** list, size_t num, -- struct query_info* qinfo, struct key_entry_key* kkey, uint8_t* wc) -+ struct query_info* qinfo, struct key_entry_key* kkey, uint8_t* wc, -+ struct nsec3_cache_table* ct, int* calc) - { -- rbtree_type ct; - struct nsec3_filter flt; - struct ce_response ce; - uint8_t* nc; -@@ -1254,7 +1396,6 @@ nsec3_prove_wildcard(struct module_env* env, struct val_env* ve, - - if(!list || num == 0 || !kkey || !key_entry_isgood(kkey)) - return sec_status_bogus; /* no valid NSEC3s, bogus */ -- rbtree_init(&ct, &nsec3_hash_cmp); /* init names-to-hash cache */ - filter_init(&flt, list, num, qinfo); /* init RR iterator */ - if(!flt.zone) - return sec_status_bogus; /* no RRs */ -@@ -1272,8 +1413,22 @@ nsec3_prove_wildcard(struct module_env* env, struct val_env* ve, - /* Now we still need to prove that the original data did not exist. - * Otherwise, we need to show that the next closer name is covered. */ - next_closer(qinfo->qname, qinfo->qname_len, ce.ce, &nc, &nc_len); -- if(!find_covering_nsec3(env, &flt, &ct, nc, nc_len, -- &ce.nc_rrset, &ce.nc_rr)) { -+ if(!find_covering_nsec3(env, &flt, ct, nc, nc_len, -+ &ce.nc_rrset, &ce.nc_rr, calc)) { -+ if(*calc == MAX_NSEC3_ERRORS) { -+ verbose(VERB_ALGO, "proveWildcard: did not find a " -+ "covering NSEC3 that covered the next closer " -+ "name; all attempted hash calculations were " -+ "erroneous; bogus"); -+ return sec_status_bogus; -+ } else if(*calc >= MAX_NSEC3_CALCULATIONS) { -+ verbose(VERB_ALGO, "proveWildcard: did not find a " -+ "covering NSEC3 that covered the next closer " -+ "name; reached MAX_NSEC3_CALCULATIONS " -+ "(%d); unchecked still", -+ MAX_NSEC3_CALCULATIONS); -+ return sec_status_unchecked; -+ } - verbose(VERB_ALGO, "proveWildcard: did not find a covering " - "NSEC3 that covered the next closer name."); - return sec_status_bogus; -@@ -1294,6 +1449,7 @@ list_is_secure(struct module_env* env, struct val_env* ve, - { - struct packed_rrset_data* d; - size_t i; -+ int verified = 0; - for(i=0; ientry.data; - if(list[i]->rk.type != htons(LDNS_RR_TYPE_NSEC3)) -@@ -1304,7 +1460,8 @@ list_is_secure(struct module_env* env, struct val_env* ve, - if(d->security == sec_status_secure) - continue; - d->security = val_verify_rrset_entry(env, ve, list[i], kkey, -- reason, reason_bogus, LDNS_SECTION_AUTHORITY, qstate); -+ reason, reason_bogus, LDNS_SECTION_AUTHORITY, qstate, -+ &verified); - if(d->security != sec_status_secure) { - verbose(VERB_ALGO, "NSEC3 did not verify"); - return 0; -@@ -1318,13 +1475,16 @@ enum sec_status - nsec3_prove_nods(struct module_env* env, struct val_env* ve, - struct ub_packed_rrset_key** list, size_t num, - struct query_info* qinfo, struct key_entry_key* kkey, char** reason, -- sldns_ede_code* reason_bogus, struct module_qstate* qstate) -+ sldns_ede_code* reason_bogus, struct module_qstate* qstate, -+ struct nsec3_cache_table* ct) - { -- rbtree_type ct; - struct nsec3_filter flt; - struct ce_response ce; - struct ub_packed_rrset_key* rrset; - int rr; -+ int calc = 0; -+ enum sec_status sec; -+ - log_assert(qinfo->qtype == LDNS_RR_TYPE_DS); - - if(!list || num == 0 || !kkey || !key_entry_isgood(kkey)) { -@@ -1335,7 +1495,6 @@ nsec3_prove_nods(struct module_env* env, struct val_env* ve, - *reason = "not all NSEC3 records secure"; - return sec_status_bogus; /* not all NSEC3 records secure */ - } -- rbtree_init(&ct, &nsec3_hash_cmp); /* init names-to-hash cache */ - filter_init(&flt, list, num, qinfo); /* init RR iterator */ - if(!flt.zone) { - *reason = "no NSEC3 records"; -@@ -1346,8 +1505,8 @@ nsec3_prove_nods(struct module_env* env, struct val_env* ve, - - /* Look for a matching NSEC3 to qname -- this is the normal - * NODATA case. */ -- if(find_matching_nsec3(env, &flt, &ct, qinfo->qname, qinfo->qname_len, -- &rrset, &rr)) { -+ if(find_matching_nsec3(env, &flt, ct, qinfo->qname, qinfo->qname_len, -+ &rrset, &rr, &calc)) { - /* If the matching NSEC3 has the SOA bit set, it is from - * the wrong zone (the child instead of the parent). If - * it has the DS bit set, then we were lied to. */ -@@ -1370,10 +1529,24 @@ nsec3_prove_nods(struct module_env* env, struct val_env* ve, - /* Otherwise, this proves no DS. */ - return sec_status_secure; - } -+ if(calc == MAX_NSEC3_ERRORS) { -+ verbose(VERB_ALGO, "nsec3 provenods: all attempted hash " -+ "calculations were erroneous while finding a matching " -+ "NSEC3, bogus"); -+ return sec_status_bogus; -+ } else if(calc >= MAX_NSEC3_CALCULATIONS) { -+ verbose(VERB_ALGO, "nsec3 provenods: reached " -+ "MAX_NSEC3_CALCULATIONS (%d) while finding a " -+ "matching NSEC3, unchecked still", -+ MAX_NSEC3_CALCULATIONS); -+ return sec_status_unchecked; -+ } - - /* Otherwise, we are probably in the opt-out case. */ -- if(nsec3_prove_closest_encloser(env, &flt, &ct, qinfo, 1, &ce) -- != sec_status_secure) { -+ sec = nsec3_prove_closest_encloser(env, &flt, ct, qinfo, 1, &ce, &calc); -+ if(sec == sec_status_unchecked) { -+ return sec_status_unchecked; -+ } else if(sec != sec_status_secure) { - /* an insecure delegation *above* the qname does not prove - * anything about this qname exactly, and bogus is bogus */ - verbose(VERB_ALGO, "nsec3 provenods: did not match qname, " -@@ -1407,17 +1580,16 @@ nsec3_prove_nods(struct module_env* env, struct val_env* ve, - - enum sec_status - nsec3_prove_nxornodata(struct module_env* env, struct val_env* ve, -- struct ub_packed_rrset_key** list, size_t num, -- struct query_info* qinfo, struct key_entry_key* kkey, int* nodata) -+ struct ub_packed_rrset_key** list, size_t num, -+ struct query_info* qinfo, struct key_entry_key* kkey, int* nodata, -+ struct nsec3_cache_table* ct, int* calc) - { - enum sec_status sec, secnx; -- rbtree_type ct; - struct nsec3_filter flt; - *nodata = 0; - - if(!list || num == 0 || !kkey || !key_entry_isgood(kkey)) - return sec_status_bogus; /* no valid NSEC3s, bogus */ -- rbtree_init(&ct, &nsec3_hash_cmp); /* init names-to-hash cache */ - filter_init(&flt, list, num, qinfo); /* init RR iterator */ - if(!flt.zone) - return sec_status_bogus; /* no RRs */ -@@ -1427,16 +1599,20 @@ nsec3_prove_nxornodata(struct module_env* env, struct val_env* ve, - /* try nxdomain and nodata after another, while keeping the - * hash cache intact */ - -- secnx = nsec3_do_prove_nameerror(env, &flt, &ct, qinfo); -+ secnx = nsec3_do_prove_nameerror(env, &flt, ct, qinfo, calc); - if(secnx==sec_status_secure) - return sec_status_secure; -- sec = nsec3_do_prove_nodata(env, &flt, &ct, qinfo); -+ else if(secnx == sec_status_unchecked) -+ return sec_status_unchecked; -+ sec = nsec3_do_prove_nodata(env, &flt, ct, qinfo, calc); - if(sec==sec_status_secure) { - *nodata = 1; - } else if(sec == sec_status_insecure) { - *nodata = 1; - } else if(secnx == sec_status_insecure) { - sec = sec_status_insecure; -+ } else if(sec == sec_status_unchecked) { -+ return sec_status_unchecked; - } - return sec; - } -diff --git a/unbound-1.16.2/validator/val_nsec3.h b/unbound-1.16.2/validator/val_nsec3.h -index 7676fc8..8ca9129 100644 ---- a/unbound-1.16.2/validator/val_nsec3.h -+++ b/unbound-1.16.2/validator/val_nsec3.h -@@ -98,6 +98,15 @@ struct sldns_buffer; - /** The SHA1 hash algorithm for NSEC3 */ - #define NSEC3_HASH_SHA1 0x01 - -+/** -+* Cache table for NSEC3 hashes. -+* It keeps a *pointer* to the region its items are allocated. -+*/ -+struct nsec3_cache_table { -+ rbtree_type* ct; -+ struct regional* region; -+}; -+ - /** - * Determine if the set of NSEC3 records provided with a response prove NAME - * ERROR. This means that the NSEC3s prove a) the closest encloser exists, -@@ -110,14 +119,18 @@ struct sldns_buffer; - * @param num: number of RRsets in the array to examine. - * @param qinfo: query that is verified for. - * @param kkey: key entry that signed the NSEC3s. -+ * @param ct: cached hashes table. -+ * @param calc: current hash calculations. - * @return: - * sec_status SECURE of the Name Error is proven by the NSEC3 RRs, -- * BOGUS if not, INSECURE if all of the NSEC3s could be validly ignored. -+ * BOGUS if not, INSECURE if all of the NSEC3s could be validly ignored, -+ * UNCHECKED if no more hash calculations are allowed at this point. - */ - enum sec_status - nsec3_prove_nameerror(struct module_env* env, struct val_env* ve, - struct ub_packed_rrset_key** list, size_t num, -- struct query_info* qinfo, struct key_entry_key* kkey); -+ struct query_info* qinfo, struct key_entry_key* kkey, -+ struct nsec3_cache_table* ct, int* calc); - - /** - * Determine if the NSEC3s provided in a response prove the NOERROR/NODATA -@@ -144,15 +157,18 @@ nsec3_prove_nameerror(struct module_env* env, struct val_env* ve, - * @param num: number of RRsets in the array to examine. - * @param qinfo: query that is verified for. - * @param kkey: key entry that signed the NSEC3s. -+ * @param ct: cached hashes table. -+ * @param calc: current hash calculations. - * @return: - * sec_status SECURE of the proposition is proven by the NSEC3 RRs, -- * BOGUS if not, INSECURE if all of the NSEC3s could be validly ignored. -+ * BOGUS if not, INSECURE if all of the NSEC3s could be validly ignored, -+ * UNCHECKED if no more hash calculations are allowed at this point. - */ - enum sec_status - nsec3_prove_nodata(struct module_env* env, struct val_env* ve, - struct ub_packed_rrset_key** list, size_t num, -- struct query_info* qinfo, struct key_entry_key* kkey); -- -+ struct query_info* qinfo, struct key_entry_key* kkey, -+ struct nsec3_cache_table* ct, int* calc); - - /** - * Prove that a positive wildcard match was appropriate (no direct match -@@ -166,14 +182,18 @@ nsec3_prove_nodata(struct module_env* env, struct val_env* ve, - * @param kkey: key entry that signed the NSEC3s. - * @param wc: The purported wildcard that matched. This is the wildcard name - * as *.wildcard.name., with the *. label already removed. -+ * @param ct: cached hashes table. -+ * @param calc: current hash calculations. - * @return: - * sec_status SECURE of the proposition is proven by the NSEC3 RRs, -- * BOGUS if not, INSECURE if all of the NSEC3s could be validly ignored. -+ * BOGUS if not, INSECURE if all of the NSEC3s could be validly ignored, -+ * UNCHECKED if no more hash calculations are allowed at this point. - */ - enum sec_status - nsec3_prove_wildcard(struct module_env* env, struct val_env* ve, - struct ub_packed_rrset_key** list, size_t num, -- struct query_info* qinfo, struct key_entry_key* kkey, uint8_t* wc); -+ struct query_info* qinfo, struct key_entry_key* kkey, uint8_t* wc, -+ struct nsec3_cache_table* ct, int* calc); - - /** - * Prove that a DS response either had no DS, or wasn't a delegation point. -@@ -189,17 +209,20 @@ nsec3_prove_wildcard(struct module_env* env, struct val_env* ve, - * @param reason: string for bogus result. - * @param reason_bogus: EDE (RFC8914) code paired with the reason of failure. - * @param qstate: qstate with region. -+ * @param ct: cached hashes table. - * @return: - * sec_status SECURE of the proposition is proven by the NSEC3 RRs, - * BOGUS if not, INSECURE if all of the NSEC3s could be validly ignored. - * or if there was no DS in an insecure (i.e., opt-in) way, -- * INDETERMINATE if it was clear that this wasn't a delegation point. -+ * INDETERMINATE if it was clear that this wasn't a delegation point, -+ * UNCHECKED if no more hash calculations are allowed at this point. - */ - enum sec_status - nsec3_prove_nods(struct module_env* env, struct val_env* ve, - struct ub_packed_rrset_key** list, size_t num, - struct query_info* qinfo, struct key_entry_key* kkey, char** reason, -- sldns_ede_code* reason_bogus, struct module_qstate* qstate); -+ sldns_ede_code* reason_bogus, struct module_qstate* qstate, -+ struct nsec3_cache_table* ct); - - /** - * Prove NXDOMAIN or NODATA. -@@ -212,14 +235,18 @@ nsec3_prove_nods(struct module_env* env, struct val_env* ve, - * @param kkey: key entry that signed the NSEC3s. - * @param nodata: if return value is secure, this indicates if nodata or - * nxdomain was proven. -+ * @param ct: cached hashes table. -+ * @param calc: current hash calculations. - * @return: - * sec_status SECURE of the proposition is proven by the NSEC3 RRs, -- * BOGUS if not, INSECURE if all of the NSEC3s could be validly ignored. -+ * BOGUS if not, INSECURE if all of the NSEC3s could be validly ignored, -+ * UNCHECKED if no more hash calculations are allowed at this point. - */ - enum sec_status - nsec3_prove_nxornodata(struct module_env* env, struct val_env* ve, - struct ub_packed_rrset_key** list, size_t num, -- struct query_info* qinfo, struct key_entry_key* kkey, int* nodata); -+ struct query_info* qinfo, struct key_entry_key* kkey, int* nodata, -+ struct nsec3_cache_table* ct, int* calc); - - /** - * The NSEC3 hash result storage. -@@ -256,6 +283,14 @@ struct nsec3_cached_hash { - */ - int nsec3_hash_cmp(const void* c1, const void* c2); - -+/** -+ * Initialise the NSEC3 cache table. -+ * @param ct: the nsec3 cache table. -+ * @param region: the region where allocations for the table will happen. -+ * @return true on success, false on malloc error. -+ */ -+int nsec3_cache_table_init(struct nsec3_cache_table* ct, struct regional* region); -+ - /** - * Obtain the hash of an owner name. - * Used internally by the nsec3 proof functions in this file. -@@ -272,7 +307,8 @@ int nsec3_hash_cmp(const void* c1, const void* c2); - * @param dname_len: the length of the name. - * @param hash: the hash node is returned on success. - * @return: -- * 1 on success, either from cache or newly hashed hash is returned. -+ * 2 on success, hash from cache is returned. -+ * 1 on success, newly computed hash is returned. - * 0 on a malloc failure. - * -1 if the NSEC3 rr was badly formatted (i.e. formerr). - */ -diff --git a/unbound-1.16.2/validator/val_sigcrypt.c b/unbound-1.16.2/validator/val_sigcrypt.c -index 5ab21e2..8600a68 100644 ---- a/unbound-1.16.2/validator/val_sigcrypt.c -+++ b/unbound-1.16.2/validator/val_sigcrypt.c -@@ -78,6 +78,9 @@ - #include - #endif - -+/** Maximum number of RRSIG validations for an RRset. */ -+#define MAX_VALIDATE_RRSIGS 8 -+ - /** return number of rrs in an rrset */ - static size_t - rrset_get_count(struct ub_packed_rrset_key* rrset) -@@ -541,6 +544,8 @@ int algo_needs_missing(struct algo_needs* n) - * @param reason_bogus: EDE (RFC8914) code paired with the reason of failure. - * @param section: section of packet where this rrset comes from. - * @param qstate: qstate with region. -+ * @param numverified: incremented when the number of RRSIG validations -+ * increases. - * @return secure if any key signs *this* signature. bogus if no key signs it, - * unchecked on error, or indeterminate if all keys are not supported by - * the crypto library (openssl3+ only). -@@ -551,7 +556,8 @@ dnskeyset_verify_rrset_sig(struct module_env* env, struct val_env* ve, - struct ub_packed_rrset_key* dnskey, size_t sig_idx, - struct rbtree_type** sortree, - char** reason, sldns_ede_code *reason_bogus, -- sldns_pkt_section section, struct module_qstate* qstate) -+ sldns_pkt_section section, struct module_qstate* qstate, -+ int* numverified) - { - /* find matching keys and check them */ - enum sec_status sec = sec_status_bogus; -@@ -575,6 +581,7 @@ dnskeyset_verify_rrset_sig(struct module_env* env, struct val_env* ve, - tag != dnskey_calc_keytag(dnskey, i)) - continue; - numchecked ++; -+ (*numverified)++; - - /* see if key verifies */ - sec = dnskey_verify_rrset_sig(env->scratch, -@@ -585,6 +592,13 @@ dnskeyset_verify_rrset_sig(struct module_env* env, struct val_env* ve, - return sec; - else if(sec == sec_status_indeterminate) - numindeterminate ++; -+ if(*numverified > MAX_VALIDATE_RRSIGS) { -+ *reason = "too many RRSIG validations"; -+ if(reason_bogus) -+ *reason_bogus = LDNS_EDE_DNSSEC_BOGUS; -+ verbose(VERB_ALGO, "verify sig: too many RRSIG validations"); -+ return sec_status_bogus; -+ } - } - if(numchecked == 0) { - *reason = "signatures from unknown keys"; -@@ -608,7 +622,7 @@ enum sec_status - dnskeyset_verify_rrset(struct module_env* env, struct val_env* ve, - struct ub_packed_rrset_key* rrset, struct ub_packed_rrset_key* dnskey, - uint8_t* sigalg, char** reason, sldns_ede_code *reason_bogus, -- sldns_pkt_section section, struct module_qstate* qstate) -+ sldns_pkt_section section, struct module_qstate* qstate, int* verified) - { - enum sec_status sec; - size_t i, num; -@@ -616,6 +630,7 @@ dnskeyset_verify_rrset(struct module_env* env, struct val_env* ve, - /* make sure that for all DNSKEY algorithms there are valid sigs */ - struct algo_needs needs; - int alg; -+ *verified = 0; - - num = rrset_get_sigcount(rrset); - if(num == 0) { -@@ -640,7 +655,7 @@ dnskeyset_verify_rrset(struct module_env* env, struct val_env* ve, - for(i=0; inow, rrset, - dnskey, i, &sortree, reason, reason_bogus, -- section, qstate); -+ section, qstate, verified); - /* see which algorithm has been fixed up */ - if(sec == sec_status_secure) { - if(!sigalg) -@@ -652,6 +667,13 @@ dnskeyset_verify_rrset(struct module_env* env, struct val_env* ve, - algo_needs_set_bogus(&needs, - (uint8_t)rrset_get_sig_algo(rrset, i)); - } -+ if(*verified > MAX_VALIDATE_RRSIGS) { -+ verbose(VERB_QUERY, "rrset failed to verify, too many RRSIG validations"); -+ *reason = "too many RRSIG validations"; -+ if(reason_bogus) -+ *reason_bogus = LDNS_EDE_DNSSEC_BOGUS; -+ return sec_status_bogus; -+ } - } - if(sigalg && (alg=algo_needs_missing(&needs)) != 0) { - verbose(VERB_ALGO, "rrset failed to verify: " -@@ -690,6 +712,7 @@ dnskey_verify_rrset(struct module_env* env, struct val_env* ve, - int buf_canon = 0; - uint16_t tag = dnskey_calc_keytag(dnskey, dnskey_idx); - int algo = dnskey_get_algo(dnskey, dnskey_idx); -+ int numverified = 0; - - num = rrset_get_sigcount(rrset); - if(num == 0) { -@@ -713,8 +736,16 @@ dnskey_verify_rrset(struct module_env* env, struct val_env* ve, - if(sec == sec_status_secure) - return sec; - numchecked ++; -+ numverified ++; - if(sec == sec_status_indeterminate) - numindeterminate ++; -+ if(numverified > MAX_VALIDATE_RRSIGS) { -+ verbose(VERB_QUERY, "rrset failed to verify, too many RRSIG validations"); -+ *reason = "too many RRSIG validations"; -+ if(reason_bogus) -+ *reason_bogus = LDNS_EDE_DNSSEC_BOGUS; -+ return sec_status_bogus; -+ } - } - verbose(VERB_ALGO, "rrset failed to verify: all signatures are bogus"); - if(!numchecked) { -diff --git a/unbound-1.16.2/validator/val_sigcrypt.h b/unbound-1.16.2/validator/val_sigcrypt.h -index 7f52b71..1a3d8fc 100644 ---- a/unbound-1.16.2/validator/val_sigcrypt.h -+++ b/unbound-1.16.2/validator/val_sigcrypt.h -@@ -260,6 +260,7 @@ uint16_t dnskey_get_flags(struct ub_packed_rrset_key* k, size_t idx); - * @param reason_bogus: EDE (RFC8914) code paired with the reason of failure. - * @param section: section of packet where this rrset comes from. - * @param qstate: qstate with region. -+ * @param verified: if not NULL the number of RRSIG validations is returned. - * @return SECURE if one key in the set verifies one rrsig. - * UNCHECKED on allocation errors, unsupported algorithms, malformed data, - * and BOGUS on verification failures (no keys match any signatures). -@@ -268,7 +269,7 @@ enum sec_status dnskeyset_verify_rrset(struct module_env* env, - struct val_env* ve, struct ub_packed_rrset_key* rrset, - struct ub_packed_rrset_key* dnskey, uint8_t* sigalg, - char** reason, sldns_ede_code *reason_bogus, -- sldns_pkt_section section, struct module_qstate* qstate); -+ sldns_pkt_section section, struct module_qstate* qstate, int* verified); - - - /** -diff --git a/unbound-1.16.2/validator/val_utils.c b/unbound-1.16.2/validator/val_utils.c -index e2319ee..cb37ea0 100644 ---- a/unbound-1.16.2/validator/val_utils.c -+++ b/unbound-1.16.2/validator/val_utils.c -@@ -58,6 +58,10 @@ - #include "sldns/wire2str.h" - #include "sldns/parseutil.h" - -+/** Maximum allowed digest match failures per DS, for DNSKEYs with the same -+ * properties */ -+#define MAX_DS_MATCH_FAILURES 4 -+ - enum val_classification - val_classify_response(uint16_t query_flags, struct query_info* origqinf, - struct query_info* qinf, struct reply_info* rep, size_t skip) -@@ -336,7 +340,8 @@ static enum sec_status - val_verify_rrset(struct module_env* env, struct val_env* ve, - struct ub_packed_rrset_key* rrset, struct ub_packed_rrset_key* keys, - uint8_t* sigalg, char** reason, sldns_ede_code *reason_bogus, -- sldns_pkt_section section, struct module_qstate* qstate) -+ sldns_pkt_section section, struct module_qstate* qstate, -+ int *verified) - { - enum sec_status sec; - struct packed_rrset_data* d = (struct packed_rrset_data*)rrset-> -@@ -346,6 +351,7 @@ val_verify_rrset(struct module_env* env, struct val_env* ve, - log_nametypeclass(VERB_ALGO, "verify rrset cached", - rrset->rk.dname, ntohs(rrset->rk.type), - ntohs(rrset->rk.rrset_class)); -+ *verified = 0; - return d->security; - } - /* check in the cache if verification has already been done */ -@@ -354,12 +360,13 @@ val_verify_rrset(struct module_env* env, struct val_env* ve, - log_nametypeclass(VERB_ALGO, "verify rrset from cache", - rrset->rk.dname, ntohs(rrset->rk.type), - ntohs(rrset->rk.rrset_class)); -+ *verified = 0; - return d->security; - } - log_nametypeclass(VERB_ALGO, "verify rrset", rrset->rk.dname, - ntohs(rrset->rk.type), ntohs(rrset->rk.rrset_class)); - sec = dnskeyset_verify_rrset(env, ve, rrset, keys, sigalg, reason, -- reason_bogus, section, qstate); -+ reason_bogus, section, qstate, verified); - verbose(VERB_ALGO, "verify result: %s", sec_status_to_string(sec)); - regional_free_all(env->scratch); - -@@ -393,7 +400,8 @@ enum sec_status - val_verify_rrset_entry(struct module_env* env, struct val_env* ve, - struct ub_packed_rrset_key* rrset, struct key_entry_key* kkey, - char** reason, sldns_ede_code *reason_bogus, -- sldns_pkt_section section, struct module_qstate* qstate) -+ sldns_pkt_section section, struct module_qstate* qstate, -+ int* verified) - { - /* temporary dnskey rrset-key */ - struct ub_packed_rrset_key dnskey; -@@ -407,7 +415,7 @@ val_verify_rrset_entry(struct module_env* env, struct val_env* ve, - dnskey.entry.key = &dnskey; - dnskey.entry.data = kd->rrset_data; - sec = val_verify_rrset(env, ve, rrset, &dnskey, kd->algo, reason, -- reason_bogus, section, qstate); -+ reason_bogus, section, qstate, verified); - return sec; - } - -@@ -439,6 +447,12 @@ verify_dnskeys_with_ds_rr(struct module_env* env, struct val_env* ve, - if(!ds_digest_match_dnskey(env, dnskey_rrset, i, ds_rrset, - ds_idx)) { - verbose(VERB_ALGO, "DS match attempt failed"); -+ if(numchecked > numhashok + MAX_DS_MATCH_FAILURES) { -+ verbose(VERB_ALGO, "DS match attempt reached " -+ "MAX_DS_MATCH_FAILURES (%d); bogus", -+ MAX_DS_MATCH_FAILURES); -+ return sec_status_bogus; -+ } - continue; - } - numhashok++; -diff --git a/unbound-1.16.2/validator/val_utils.h b/unbound-1.16.2/validator/val_utils.h -index 83e3d0a..e8cdcef 100644 ---- a/unbound-1.16.2/validator/val_utils.h -+++ b/unbound-1.16.2/validator/val_utils.h -@@ -124,12 +124,14 @@ void val_find_signer(enum val_classification subtype, - * @param reason_bogus: EDE (RFC8914) code paired with the reason of failure. - * @param section: section of packet where this rrset comes from. - * @param qstate: qstate with region. -+ * @param verified: if not NULL, the number of RRSIG validations is returned. - * @return security status of verification. - */ - enum sec_status val_verify_rrset_entry(struct module_env* env, - struct val_env* ve, struct ub_packed_rrset_key* rrset, - struct key_entry_key* kkey, char** reason, sldns_ede_code *reason_bogus, -- sldns_pkt_section section, struct module_qstate* qstate); -+ sldns_pkt_section section, struct module_qstate* qstate, -+ int* verified); - - /** - * Verify DNSKEYs with DS rrset. Like val_verify_new_DNSKEYs but -diff --git a/unbound-1.16.2/validator/validator.c b/unbound-1.16.2/validator/validator.c -index 1723afe..01303a8 100644 ---- a/unbound-1.16.2/validator/validator.c -+++ b/unbound-1.16.2/validator/validator.c -@@ -64,10 +64,15 @@ - #include "sldns/wire2str.h" - #include "sldns/str2wire.h" - -+/** Max number of RRSIGs to validate at once, suspend query for later. */ -+#define MAX_VALIDATE_AT_ONCE 8 -+/** Max number of validation suspends allowed, error out otherwise. */ -+#define MAX_VALIDATION_SUSPENDS 16 -+ - /* forward decl for cache response and normal super inform calls of a DS */ - static void process_ds_response(struct module_qstate* qstate, - struct val_qstate* vq, int id, int rcode, struct dns_msg* msg, -- struct query_info* qinfo, struct sock_list* origin); -+ struct query_info* qinfo, struct sock_list* origin, int* suspend); - - - /* Updates the suplied EDE (RFC8914) code selectively so we don't loose -@@ -281,6 +286,21 @@ val_new(struct module_qstate* qstate, int id) - return val_new_getmsg(qstate, vq); - } - -+/** reset validator query state for query restart */ -+static void -+val_restart(struct val_qstate* vq) -+{ -+ struct comm_timer* temp_timer; -+ int restart_count; -+ if(!vq) return; -+ temp_timer = vq->suspend_timer; -+ restart_count = vq->restart_count+1; -+ memset(vq, 0, sizeof(*vq)); -+ vq->suspend_timer = temp_timer; -+ vq->restart_count = restart_count; -+ vq->state = VAL_INIT_STATE; -+} -+ - /** - * Exit validation with an error status - * -@@ -587,30 +607,42 @@ prime_trust_anchor(struct module_qstate* qstate, struct val_qstate* vq, - * completed. - * - * @param qstate: query state. -+ * @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(). -+ * @param suspend: returned true if the task takes too long and needs to -+ * suspend to continue the effort later. - * @return false if any of the rrsets in the an or ns sections of the message - * fail to verify. The message is then set to bogus. - */ - static int --validate_msg_signatures(struct module_qstate* qstate, struct module_env* env, -- struct val_env* ve, struct query_info* qchase, -- struct reply_info* chase_reply, struct key_entry_key* key_entry) -+validate_msg_signatures(struct module_qstate* qstate, struct val_qstate* vq, -+ struct module_env* env, struct val_env* ve, struct query_info* qchase, -+ struct reply_info* chase_reply, struct key_entry_key* key_entry, -+ int* suspend) - { - uint8_t* sname; - size_t i, slen; - struct ub_packed_rrset_key* s; - enum sec_status sec; -- int dname_seen = 0; -+ int dname_seen = 0, num_verifies = 0, verified, have_state = 0; - char* reason = NULL; - sldns_ede_code reason_bogus = LDNS_EDE_DNSSEC_BOGUS; -+ *suspend = 0; -+ if(vq->msg_signatures_state) { -+ /* Pick up the state, and reset it, may not be needed now. */ -+ vq->msg_signatures_state = 0; -+ have_state = 1; -+ } - - /* validate the ANSWER section */ - for(i=0; ian_numrrsets; i++) { -+ if(have_state && i <= vq->msg_signatures_index) -+ continue; - s = chase_reply->rrsets[i]; - /* Skip the CNAME following a (validated) DNAME. - * Because of the normalization routines in the iterator, -@@ -629,7 +661,7 @@ validate_msg_signatures(struct module_qstate* qstate, struct module_env* env, - - /* Verify the answer rrset */ - sec = val_verify_rrset_entry(env, ve, s, key_entry, &reason, -- &reason_bogus, LDNS_SECTION_ANSWER, qstate); -+ &reason_bogus, LDNS_SECTION_ANSWER, qstate, &verified); - /* If the (answer) rrset failed to validate, then this - * message is BAD. */ - if(sec != sec_status_secure) { -@@ -654,14 +686,33 @@ validate_msg_signatures(struct module_qstate* qstate, struct module_env* env, - 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? -+ chase_reply->an_numrrsets+chase_reply->ns_numrrsets: -+ chase_reply->rrset_count)) { -+ /* If the number of RRSIGs exceeds the maximum in -+ * one go, suspend. Only suspend if there is a next -+ * rrset to verify, i+1msg_signatures_state = 1; -+ vq->msg_signatures_index = i; -+ verbose(VERB_ALGO, "msg signature validation " -+ "suspended"); -+ return 0; -+ } - } - - /* validate the AUTHORITY section */ - for(i=chase_reply->an_numrrsets; ian_numrrsets+ - chase_reply->ns_numrrsets; i++) { -+ if(have_state && i <= vq->msg_signatures_index) -+ continue; - s = chase_reply->rrsets[i]; - sec = val_verify_rrset_entry(env, ve, s, key_entry, &reason, -- &reason_bogus, LDNS_SECTION_AUTHORITY, qstate); -+ &reason_bogus, LDNS_SECTION_AUTHORITY, qstate, -+ &verified); - /* If anything in the authority section fails to be secure, - * we have a bad message. */ - if(sec != sec_status_secure) { -@@ -675,6 +726,18 @@ validate_msg_signatures(struct module_qstate* qstate, struct module_env* env, - update_reason_bogus(chase_reply, reason_bogus); - return 0; - } -+ num_verifies += verified; -+ if(num_verifies > MAX_VALIDATE_AT_ONCE && -+ i+1 < (env->cfg->val_clean_additional? -+ chase_reply->an_numrrsets+chase_reply->ns_numrrsets: -+ chase_reply->rrset_count)) { -+ *suspend = 1; -+ vq->msg_signatures_state = 1; -+ vq->msg_signatures_index = i; -+ verbose(VERB_ALGO, "msg signature validation " -+ "suspended"); -+ return 0; -+ } - } - - /* If set, the validator should clean the additional section of -@@ -684,22 +747,103 @@ validate_msg_signatures(struct module_qstate* qstate, struct module_env* env, - /* attempt to validate the ADDITIONAL section rrsets */ - for(i=chase_reply->an_numrrsets+chase_reply->ns_numrrsets; - irrset_count; i++) { -+ if(have_state && i <= vq->msg_signatures_index) -+ continue; - s = chase_reply->rrsets[i]; - /* only validate rrs that have signatures with the key */ - /* leave others unchecked, those get removed later on too */ - val_find_rrset_signer(s, &sname, &slen); - -+ verified = 0; - if(sname && query_dname_compare(sname, key_entry->name)==0) - (void)val_verify_rrset_entry(env, ve, s, key_entry, -- &reason, NULL, LDNS_SECTION_ADDITIONAL, qstate); -+ &reason, NULL, LDNS_SECTION_ADDITIONAL, qstate, -+ &verified); - /* the additional section can fail to be secure, - * it is optional, check signature in case we need - * to clean the additional section later. */ -+ num_verifies += verified; -+ if(num_verifies > MAX_VALIDATE_AT_ONCE && -+ i+1 < chase_reply->rrset_count) { -+ *suspend = 1; -+ vq->msg_signatures_state = 1; -+ vq->msg_signatures_index = i; -+ verbose(VERB_ALGO, "msg signature validation " -+ "suspended"); -+ return 0; -+ } - } - - return 1; - } - -+void -+validate_suspend_timer_cb(void* arg) -+{ -+ struct module_qstate* qstate = (struct module_qstate*)arg; -+ verbose(VERB_ALGO, "validate_suspend timer, continue"); -+ mesh_run(qstate->env->mesh, qstate->mesh_info, module_event_pass, -+ NULL); -+} -+ -+/** Setup timer to continue validation of msg signatures later */ -+static int -+validate_suspend_setup_timer(struct module_qstate* qstate, -+ struct val_qstate* vq, int id, enum val_state resume_state) -+{ -+ struct timeval tv; -+ int usec, slack, base; -+ if(vq->suspend_count >= MAX_VALIDATION_SUSPENDS) { -+ verbose(VERB_ALGO, "validate_suspend timer: " -+ "reached MAX_VALIDATION_SUSPENDS (%d); error out", -+ MAX_VALIDATION_SUSPENDS); -+ errinf(qstate, "max validation suspends reached, " -+ "too many RRSIG validations"); -+ return 0; -+ } -+ verbose(VERB_ALGO, "validate_suspend timer, set for suspend"); -+ vq->state = resume_state; -+ qstate->ext_state[id] = module_wait_reply; -+ if(!vq->suspend_timer) { -+ vq->suspend_timer = comm_timer_create( -+ qstate->env->worker_base, -+ validate_suspend_timer_cb, qstate); -+ if(!vq->suspend_timer) { -+ log_err("validate_suspend_setup_timer: " -+ "out of memory for comm_timer_create"); -+ return 0; -+ } -+ } -+ /* The timer is activated later, after other events in the event -+ * loop have been processed. The query state can also be deleted, -+ * when the list is full and query states are dropped. */ -+ /* Extend wait time if there are a lot of queries or if this one -+ * is taking long, to keep around cpu time for ordinary queries. */ -+ usec = 50000; /* 50 msec */ -+ slack = 0; -+ if(qstate->env->mesh->all.count >= qstate->env->mesh->max_reply_states) -+ slack += 3; -+ else if(qstate->env->mesh->all.count >= qstate->env->mesh->max_reply_states/2) -+ slack += 2; -+ else if(qstate->env->mesh->all.count >= qstate->env->mesh->max_reply_states/4) -+ slack += 1; -+ if(vq->suspend_count > 3) -+ slack += 3; -+ else if(vq->suspend_count > 0) -+ slack += vq->suspend_count; -+ if(slack != 0 && slack <= 12 /* No numeric overflow. */) { -+ usec = usec << slack; -+ } -+ /* Spread such timeouts within 90%-100% of the original timer. */ -+ base = usec * 9/10; -+ usec = base + ub_random_max(qstate->env->rnd, usec-base); -+ tv.tv_usec = (usec % 1000000); -+ tv.tv_sec = (usec / 1000000); -+ vq->suspend_count ++; -+ comm_timer_set(vq->suspend_timer, &tv); -+ return 1; -+} -+ - /** - * Detect wrong truncated response (say from BIND 9.6.1 that is forwarding - * and saw the NS record without signatures from a referral). -@@ -798,11 +942,17 @@ remove_spurious_authority(struct reply_info* chase_reply, - * @param chase_reply: answer to that query to validate. - * @param kkey: the key entry, which is trusted, and which matches - * the signer of the answer. The key entry isgood(). -+ * @param qstate: query state for the region. -+ * @param vq: validator state for the nsec3 cache table. -+ * @param nsec3_calculations: current nsec3 hash calculations. -+ * @param suspend: returned true if the task takes too long and needs to -+ * suspend to continue the effort later. - */ - static void - validate_positive_response(struct module_env* env, struct val_env* ve, - struct query_info* qchase, struct reply_info* chase_reply, -- struct key_entry_key* kkey) -+ struct key_entry_key* kkey, struct module_qstate* qstate, -+ struct val_qstate* vq, int* nsec3_calculations, int* suspend) - { - uint8_t* wc = NULL; - size_t wl; -@@ -811,6 +961,7 @@ validate_positive_response(struct module_env* env, struct val_env* ve, - int nsec3s_seen = 0; - size_t i; - struct ub_packed_rrset_key* s; -+ *suspend = 0; - - /* validate the ANSWER section - this will be the answer itself */ - for(i=0; ian_numrrsets; i++) { -@@ -862,17 +1013,23 @@ validate_positive_response(struct module_env* env, struct val_env* ve, - /* If this was a positive wildcard response that we haven't already - * proven, and we have NSEC3 records, try to prove it using the NSEC3 - * records. */ -- if(wc != NULL && !wc_NSEC_ok && nsec3s_seen) { -- enum sec_status sec = nsec3_prove_wildcard(env, ve, -+ if(wc != NULL && !wc_NSEC_ok && nsec3s_seen && -+ nsec3_cache_table_init(&vq->nsec3_cache_table, qstate->region)) { -+ enum sec_status sec = nsec3_prove_wildcard(env, ve, - chase_reply->rrsets+chase_reply->an_numrrsets, -- chase_reply->ns_numrrsets, qchase, kkey, wc); -+ chase_reply->ns_numrrsets, qchase, kkey, wc, -+ &vq->nsec3_cache_table, nsec3_calculations); - if(sec == sec_status_insecure) { - verbose(VERB_ALGO, "Positive wildcard response is " - "insecure"); - chase_reply->security = sec_status_insecure; - return; -- } else if(sec == sec_status_secure) -+ } else if(sec == sec_status_secure) { - wc_NSEC_ok = 1; -+ } else if(sec == sec_status_unchecked) { -+ *suspend = 1; -+ return; -+ } - } - - /* If after all this, we still haven't proven the positive wildcard -@@ -904,11 +1061,17 @@ validate_positive_response(struct module_env* env, struct val_env* ve, - * @param chase_reply: answer to that query to validate. - * @param kkey: the key entry, which is trusted, and which matches - * the signer of the answer. The key entry isgood(). -+ * @param qstate: query state for the region. -+ * @param vq: validator state for the nsec3 cache table. -+ * @param nsec3_calculations: current nsec3 hash calculations. -+ * @param suspend: returned true if the task takes too long and needs to -+ * suspend to continue the effort later. - */ - static void - validate_nodata_response(struct module_env* env, struct val_env* ve, - struct query_info* qchase, struct reply_info* chase_reply, -- struct key_entry_key* kkey) -+ struct key_entry_key* kkey, struct module_qstate* qstate, -+ struct val_qstate* vq, int* nsec3_calculations, int* suspend) - { - /* Since we are here, there must be nothing in the ANSWER section to - * validate. */ -@@ -925,6 +1088,7 @@ validate_nodata_response(struct module_env* env, struct val_env* ve, - int nsec3s_seen = 0; /* nsec3s seen */ - struct ub_packed_rrset_key* s; - size_t i; -+ *suspend = 0; - - for(i=chase_reply->an_numrrsets; ian_numrrsets+ - chase_reply->ns_numrrsets; i++) { -@@ -963,16 +1127,23 @@ validate_nodata_response(struct module_env* env, struct val_env* ve, - } - } - -- if(!has_valid_nsec && nsec3s_seen) { -+ if(!has_valid_nsec && nsec3s_seen && -+ nsec3_cache_table_init(&vq->nsec3_cache_table, qstate->region)) { - enum sec_status sec = nsec3_prove_nodata(env, ve, - chase_reply->rrsets+chase_reply->an_numrrsets, -- chase_reply->ns_numrrsets, qchase, kkey); -+ chase_reply->ns_numrrsets, qchase, kkey, -+ &vq->nsec3_cache_table, nsec3_calculations); - if(sec == sec_status_insecure) { - verbose(VERB_ALGO, "NODATA response is insecure"); - chase_reply->security = sec_status_insecure; - return; -- } else if(sec == sec_status_secure) -+ } else if(sec == sec_status_secure) { - has_valid_nsec = 1; -+ } else if(sec == sec_status_unchecked) { -+ /* check is incomplete; suspend */ -+ *suspend = 1; -+ return; -+ } - } - - if(!has_valid_nsec) { -@@ -1004,11 +1175,18 @@ validate_nodata_response(struct module_env* env, struct val_env* ve, - * @param kkey: the key entry, which is trusted, and which matches - * the signer of the answer. The key entry isgood(). - * @param rcode: adjusted RCODE, in case of RCODE/proof mismatch leniency. -+ * @param qstate: query state for the region. -+ * @param vq: validator state for the nsec3 cache table. -+ * @param nsec3_calculations: current nsec3 hash calculations. -+ * @param suspend: returned true if the task takes too long and needs to -+ * suspend to continue the effort later. - */ - static void - validate_nameerror_response(struct module_env* env, struct val_env* ve, - struct query_info* qchase, struct reply_info* chase_reply, -- struct key_entry_key* kkey, int* rcode) -+ struct key_entry_key* kkey, int* rcode, -+ struct module_qstate* qstate, struct val_qstate* vq, -+ int* nsec3_calculations, int* suspend) - { - int has_valid_nsec = 0; - int has_valid_wnsec = 0; -@@ -1018,6 +1196,7 @@ validate_nameerror_response(struct module_env* env, struct val_env* ve, - uint8_t* ce; - int ce_labs = 0; - int prev_ce_labs = 0; -+ *suspend = 0; - - for(i=chase_reply->an_numrrsets; ian_numrrsets+ - chase_reply->ns_numrrsets; i++) { -@@ -1047,13 +1226,18 @@ validate_nameerror_response(struct module_env* env, struct val_env* ve, - nsec3s_seen = 1; - } - -- if((!has_valid_nsec || !has_valid_wnsec) && nsec3s_seen) { -+ if((!has_valid_nsec || !has_valid_wnsec) && nsec3s_seen && -+ nsec3_cache_table_init(&vq->nsec3_cache_table, qstate->region)) { - /* use NSEC3 proof, both answer and auth rrsets, in case - * NSEC3s end up in the answer (due to qtype=NSEC3 or so) */ - chase_reply->security = nsec3_prove_nameerror(env, ve, - chase_reply->rrsets, chase_reply->an_numrrsets+ -- chase_reply->ns_numrrsets, qchase, kkey); -- if(chase_reply->security != sec_status_secure) { -+ chase_reply->ns_numrrsets, qchase, kkey, -+ &vq->nsec3_cache_table, nsec3_calculations); -+ if(chase_reply->security == sec_status_unchecked) { -+ *suspend = 1; -+ return; -+ } else if(chase_reply->security != sec_status_secure) { - verbose(VERB_QUERY, "NameError response failed nsec, " - "nsec3 proof was %s", sec_status_to_string( - chase_reply->security)); -@@ -1065,26 +1249,34 @@ validate_nameerror_response(struct module_env* env, struct val_env* ve, - - /* If the message fails to prove either condition, it is bogus. */ - if(!has_valid_nsec) { -+ validate_nodata_response(env, ve, qchase, chase_reply, kkey, -+ qstate, vq, nsec3_calculations, suspend); -+ if(*suspend) return; - verbose(VERB_QUERY, "NameError response has failed to prove: " - "qname does not exist"); -- chase_reply->security = sec_status_bogus; -- update_reason_bogus(chase_reply, LDNS_EDE_DNSSEC_BOGUS); - /* Be lenient with RCODE in NSEC NameError responses */ -- validate_nodata_response(env, ve, qchase, chase_reply, kkey); -- if (chase_reply->security == sec_status_secure) -+ if(chase_reply->security == sec_status_secure) { - *rcode = LDNS_RCODE_NOERROR; -+ } else { -+ chase_reply->security = sec_status_bogus; -+ update_reason_bogus(chase_reply, LDNS_EDE_DNSSEC_BOGUS); -+ } - return; - } - - if(!has_valid_wnsec) { -+ validate_nodata_response(env, ve, qchase, chase_reply, kkey, -+ qstate, vq, nsec3_calculations, suspend); -+ if(*suspend) return; - verbose(VERB_QUERY, "NameError response has failed to prove: " - "covering wildcard does not exist"); -- chase_reply->security = sec_status_bogus; -- update_reason_bogus(chase_reply, LDNS_EDE_DNSSEC_BOGUS); - /* Be lenient with RCODE in NSEC NameError responses */ -- validate_nodata_response(env, ve, qchase, chase_reply, kkey); -- if (chase_reply->security == sec_status_secure) -+ if (chase_reply->security == sec_status_secure) { - *rcode = LDNS_RCODE_NOERROR; -+ } else { -+ chase_reply->security = sec_status_bogus; -+ update_reason_bogus(chase_reply, LDNS_EDE_DNSSEC_BOGUS); -+ } - return; - } - -@@ -1144,11 +1336,17 @@ validate_referral_response(struct reply_info* chase_reply) - * @param chase_reply: answer to that query to validate. - * @param kkey: the key entry, which is trusted, and which matches - * the signer of the answer. The key entry isgood(). -+ * @param qstate: query state for the region. -+ * @param vq: validator state for the nsec3 cache table. -+ * @param nsec3_calculations: current nsec3 hash calculations. -+ * @param suspend: returned true if the task takes too long and needs to -+ * suspend to continue the effort later. - */ - static void - validate_any_response(struct module_env* env, struct val_env* ve, - struct query_info* qchase, struct reply_info* chase_reply, -- struct key_entry_key* kkey) -+ struct key_entry_key* kkey, struct module_qstate* qstate, -+ struct val_qstate* vq, int* nsec3_calculations, int* suspend) - { - /* all answer and auth rrsets already verified */ - /* but check if a wildcard response is given, then check NSEC/NSEC3 -@@ -1159,6 +1357,7 @@ validate_any_response(struct module_env* env, struct val_env* ve, - int nsec3s_seen = 0; - size_t i; - struct ub_packed_rrset_key* s; -+ *suspend = 0; - - if(qchase->qtype != LDNS_RR_TYPE_ANY) { - log_err("internal error: ANY validation called for non-ANY"); -@@ -1213,19 +1412,25 @@ validate_any_response(struct module_env* env, struct val_env* ve, - /* If this was a positive wildcard response that we haven't already - * proven, and we have NSEC3 records, try to prove it using the NSEC3 - * records. */ -- if(wc != NULL && !wc_NSEC_ok && nsec3s_seen) { -+ if(wc != NULL && !wc_NSEC_ok && nsec3s_seen && -+ nsec3_cache_table_init(&vq->nsec3_cache_table, qstate->region)) { - /* look both in answer and auth section for NSEC3s */ -- enum sec_status sec = nsec3_prove_wildcard(env, ve, -+ enum sec_status sec = nsec3_prove_wildcard(env, ve, - chase_reply->rrsets, -- chase_reply->an_numrrsets+chase_reply->ns_numrrsets, -- qchase, kkey, wc); -+ chase_reply->an_numrrsets+chase_reply->ns_numrrsets, -+ qchase, kkey, wc, &vq->nsec3_cache_table, -+ nsec3_calculations); - if(sec == sec_status_insecure) { - verbose(VERB_ALGO, "Positive ANY wildcard response is " - "insecure"); - chase_reply->security = sec_status_insecure; - return; -- } else if(sec == sec_status_secure) -+ } else if(sec == sec_status_secure) { - wc_NSEC_ok = 1; -+ } else if(sec == sec_status_unchecked) { -+ *suspend = 1; -+ return; -+ } - } - - /* If after all this, we still haven't proven the positive wildcard -@@ -1258,11 +1463,17 @@ validate_any_response(struct module_env* env, struct val_env* ve, - * @param chase_reply: answer to that query to validate. - * @param kkey: the key entry, which is trusted, and which matches - * the signer of the answer. The key entry isgood(). -+ * @param qstate: query state for the region. -+ * @param vq: validator state for the nsec3 cache table. -+ * @param nsec3_calculations: current nsec3 hash calculations. -+ * @param suspend: returned true if the task takes too long and needs to -+ * suspend to continue the effort later. - */ - static void - validate_cname_response(struct module_env* env, struct val_env* ve, - struct query_info* qchase, struct reply_info* chase_reply, -- struct key_entry_key* kkey) -+ struct key_entry_key* kkey, struct module_qstate* qstate, -+ struct val_qstate* vq, int* nsec3_calculations, int* suspend) - { - uint8_t* wc = NULL; - size_t wl; -@@ -1270,6 +1481,7 @@ validate_cname_response(struct module_env* env, struct val_env* ve, - int nsec3s_seen = 0; - size_t i; - struct ub_packed_rrset_key* s; -+ *suspend = 0; - - /* validate the ANSWER section - this will be the CNAME (+DNAME) */ - for(i=0; ian_numrrsets; i++) { -@@ -1334,17 +1546,23 @@ validate_cname_response(struct module_env* env, struct val_env* ve, - /* If this was a positive wildcard response that we haven't already - * proven, and we have NSEC3 records, try to prove it using the NSEC3 - * records. */ -- if(wc != NULL && !wc_NSEC_ok && nsec3s_seen) { -- enum sec_status sec = nsec3_prove_wildcard(env, ve, -+ if(wc != NULL && !wc_NSEC_ok && nsec3s_seen && -+ nsec3_cache_table_init(&vq->nsec3_cache_table, qstate->region)) { -+ enum sec_status sec = nsec3_prove_wildcard(env, ve, - chase_reply->rrsets+chase_reply->an_numrrsets, -- chase_reply->ns_numrrsets, qchase, kkey, wc); -+ chase_reply->ns_numrrsets, qchase, kkey, wc, -+ &vq->nsec3_cache_table, nsec3_calculations); - if(sec == sec_status_insecure) { - verbose(VERB_ALGO, "wildcard CNAME response is " - "insecure"); - chase_reply->security = sec_status_insecure; - return; -- } else if(sec == sec_status_secure) -+ } else if(sec == sec_status_secure) { - wc_NSEC_ok = 1; -+ } else if(sec == sec_status_unchecked) { -+ *suspend = 1; -+ return; -+ } - } - - /* If after all this, we still haven't proven the positive wildcard -@@ -1375,11 +1593,17 @@ validate_cname_response(struct module_env* env, struct val_env* ve, - * @param chase_reply: answer to that query to validate. - * @param kkey: the key entry, which is trusted, and which matches - * the signer of the answer. The key entry isgood(). -+ * @param qstate: query state for the region. -+ * @param vq: validator state for the nsec3 cache table. -+ * @param nsec3_calculations: current nsec3 hash calculations. -+ * @param suspend: returned true if the task takes too long and needs to -+ * suspend to continue the effort later. - */ - static void - validate_cname_noanswer_response(struct module_env* env, struct val_env* ve, - struct query_info* qchase, struct reply_info* chase_reply, -- struct key_entry_key* kkey) -+ struct key_entry_key* kkey, struct module_qstate* qstate, -+ struct val_qstate* vq, int* nsec3_calculations, int* suspend) - { - int nodata_valid_nsec = 0; /* If true, then NODATA has been proven.*/ - uint8_t* ce = NULL; /* for wildcard nodata responses. This is the -@@ -1393,6 +1617,7 @@ validate_cname_noanswer_response(struct module_env* env, struct val_env* ve, - uint8_t* nsec_ce; /* Used to find the NSEC with the longest ce */ - int ce_labs = 0; - int prev_ce_labs = 0; -+ *suspend = 0; - - /* the AUTHORITY section */ - for(i=chase_reply->an_numrrsets; ian_numrrsets+ -@@ -1458,11 +1683,13 @@ validate_cname_noanswer_response(struct module_env* env, struct val_env* ve, - update_reason_bogus(chase_reply, LDNS_EDE_DNSSEC_BOGUS); - return; - } -- if(!nodata_valid_nsec && !nxdomain_valid_nsec && nsec3s_seen) { -+ if(!nodata_valid_nsec && !nxdomain_valid_nsec && nsec3s_seen && -+ nsec3_cache_table_init(&vq->nsec3_cache_table, qstate->region)) { - int nodata; - enum sec_status sec = nsec3_prove_nxornodata(env, ve, - chase_reply->rrsets+chase_reply->an_numrrsets, -- chase_reply->ns_numrrsets, qchase, kkey, &nodata); -+ chase_reply->ns_numrrsets, qchase, kkey, &nodata, -+ &vq->nsec3_cache_table, nsec3_calculations); - if(sec == sec_status_insecure) { - verbose(VERB_ALGO, "CNAMEchain to noanswer response " - "is insecure"); -@@ -1472,6 +1699,9 @@ validate_cname_noanswer_response(struct module_env* env, struct val_env* ve, - if(nodata) - nodata_valid_nsec = 1; - else nxdomain_valid_nsec = 1; -+ } else if(sec == sec_status_unchecked) { -+ *suspend = 1; -+ return; - } - } - -@@ -1822,13 +2052,37 @@ processFindKey(struct module_qstate* qstate, struct val_qstate* vq, int id) - * Uses negative cache for NSEC3 lookup of DS responses. */ - /* only if cache not blacklisted, of course */ - struct dns_msg* msg; -- if(!qstate->blacklist && !vq->chain_blacklist && -+ int suspend; -+ if(vq->sub_ds_msg) { -+ /* We have a suspended DS reply from a sub-query; -+ * process it. */ -+ verbose(VERB_ALGO, "Process suspended sub DS response"); -+ msg = vq->sub_ds_msg; -+ process_ds_response(qstate, vq, id, LDNS_RCODE_NOERROR, -+ msg, &msg->qinfo, NULL, &suspend); -+ if(suspend) { -+ /* we'll come back here later to continue */ -+ if(!validate_suspend_setup_timer(qstate, vq, -+ id, VAL_FINDKEY_STATE)) -+ return val_error(qstate, id); -+ return 0; -+ } -+ vq->sub_ds_msg = NULL; -+ return 1; /* continue processing ds-response results */ -+ } else if(!qstate->blacklist && !vq->chain_blacklist && - (msg=val_find_DS(qstate->env, target_key_name, - target_key_len, vq->qchase.qclass, qstate->region, - vq->key_entry->name)) ) { - verbose(VERB_ALGO, "Process cached DS response"); - process_ds_response(qstate, vq, id, LDNS_RCODE_NOERROR, -- msg, &msg->qinfo, NULL); -+ msg, &msg->qinfo, NULL, &suspend); -+ if(suspend) { -+ /* we'll come back here later to continue */ -+ if(!validate_suspend_setup_timer(qstate, vq, -+ id, VAL_FINDKEY_STATE)) -+ return val_error(qstate, id); -+ return 0; -+ } - return 1; /* continue processing ds-response results */ - } - if(!generate_request(qstate, id, target_key_name, -@@ -1871,7 +2125,7 @@ processValidate(struct module_qstate* qstate, struct val_qstate* vq, - struct val_env* ve, int id) - { - enum val_classification subtype; -- int rcode; -+ int rcode, suspend, nsec3_calculations = 0; - - if(!vq->key_entry) { - verbose(VERB_ALGO, "validate: no key entry, failed"); -@@ -1926,8 +2180,14 @@ processValidate(struct module_qstate* qstate, struct val_qstate* vq, - - /* check signatures in the message; - * answer and authority must be valid, additional is only checked. */ -- if(!validate_msg_signatures(qstate, qstate->env, ve, &vq->qchase, -- vq->chase_reply, vq->key_entry)) { -+ if(!validate_msg_signatures(qstate, vq, qstate->env, ve, &vq->qchase, -+ vq->chase_reply, vq->key_entry, &suspend)) { -+ if(suspend) { -+ if(!validate_suspend_setup_timer(qstate, vq, -+ id, VAL_VALIDATE_STATE)) -+ return val_error(qstate, id); -+ return 0; -+ } - /* workaround bad recursor out there that truncates (even - * with EDNS4k) to 512 by removing RRSIG from auth section - * for positive replies*/ -@@ -1956,7 +2216,14 @@ processValidate(struct module_qstate* qstate, struct val_qstate* vq, - case VAL_CLASS_POSITIVE: - verbose(VERB_ALGO, "Validating a positive response"); - validate_positive_response(qstate->env, ve, -- &vq->qchase, vq->chase_reply, vq->key_entry); -+ &vq->qchase, vq->chase_reply, vq->key_entry, -+ qstate, vq, &nsec3_calculations, &suspend); -+ if(suspend) { -+ if(!validate_suspend_setup_timer(qstate, -+ vq, id, VAL_VALIDATE_STATE)) -+ return val_error(qstate, id); -+ return 0; -+ } - verbose(VERB_DETAIL, "validate(positive): %s", - sec_status_to_string( - vq->chase_reply->security)); -@@ -1965,7 +2232,14 @@ processValidate(struct module_qstate* qstate, struct val_qstate* vq, - case VAL_CLASS_NODATA: - verbose(VERB_ALGO, "Validating a nodata response"); - validate_nodata_response(qstate->env, ve, -- &vq->qchase, vq->chase_reply, vq->key_entry); -+ &vq->qchase, vq->chase_reply, vq->key_entry, -+ qstate, vq, &nsec3_calculations, &suspend); -+ if(suspend) { -+ if(!validate_suspend_setup_timer(qstate, -+ vq, id, VAL_VALIDATE_STATE)) -+ return val_error(qstate, id); -+ return 0; -+ } - verbose(VERB_DETAIL, "validate(nodata): %s", - sec_status_to_string( - vq->chase_reply->security)); -@@ -1975,7 +2249,14 @@ processValidate(struct module_qstate* qstate, struct val_qstate* vq, - rcode = (int)FLAGS_GET_RCODE(vq->orig_msg->rep->flags); - verbose(VERB_ALGO, "Validating a nxdomain response"); - validate_nameerror_response(qstate->env, ve, -- &vq->qchase, vq->chase_reply, vq->key_entry, &rcode); -+ &vq->qchase, vq->chase_reply, vq->key_entry, &rcode, -+ qstate, vq, &nsec3_calculations, &suspend); -+ if(suspend) { -+ if(!validate_suspend_setup_timer(qstate, -+ vq, id, VAL_VALIDATE_STATE)) -+ return val_error(qstate, id); -+ return 0; -+ } - verbose(VERB_DETAIL, "validate(nxdomain): %s", - sec_status_to_string( - vq->chase_reply->security)); -@@ -1986,7 +2267,14 @@ processValidate(struct module_qstate* qstate, struct val_qstate* vq, - case VAL_CLASS_CNAME: - verbose(VERB_ALGO, "Validating a cname response"); - validate_cname_response(qstate->env, ve, -- &vq->qchase, vq->chase_reply, vq->key_entry); -+ &vq->qchase, vq->chase_reply, vq->key_entry, -+ qstate, vq, &nsec3_calculations, &suspend); -+ if(suspend) { -+ if(!validate_suspend_setup_timer(qstate, -+ vq, id, VAL_VALIDATE_STATE)) -+ return val_error(qstate, id); -+ return 0; -+ } - verbose(VERB_DETAIL, "validate(cname): %s", - sec_status_to_string( - vq->chase_reply->security)); -@@ -1996,7 +2284,14 @@ processValidate(struct module_qstate* qstate, struct val_qstate* vq, - verbose(VERB_ALGO, "Validating a cname noanswer " - "response"); - validate_cname_noanswer_response(qstate->env, ve, -- &vq->qchase, vq->chase_reply, vq->key_entry); -+ &vq->qchase, vq->chase_reply, vq->key_entry, -+ qstate, vq, &nsec3_calculations, &suspend); -+ if(suspend) { -+ if(!validate_suspend_setup_timer(qstate, -+ vq, id, VAL_VALIDATE_STATE)) -+ return val_error(qstate, id); -+ return 0; -+ } - verbose(VERB_DETAIL, "validate(cname_noanswer): %s", - sec_status_to_string( - vq->chase_reply->security)); -@@ -2013,8 +2308,15 @@ processValidate(struct module_qstate* qstate, struct val_qstate* vq, - case VAL_CLASS_ANY: - verbose(VERB_ALGO, "Validating a positive ANY " - "response"); -- validate_any_response(qstate->env, ve, &vq->qchase, -- vq->chase_reply, vq->key_entry); -+ validate_any_response(qstate->env, ve, &vq->qchase, -+ vq->chase_reply, vq->key_entry, qstate, vq, -+ &nsec3_calculations, &suspend); -+ if(suspend) { -+ if(!validate_suspend_setup_timer(qstate, -+ vq, id, VAL_VALIDATE_STATE)) -+ return val_error(qstate, id); -+ return 0; -+ } - verbose(VERB_DETAIL, "validate(positive_any): %s", - sec_status_to_string( - vq->chase_reply->security)); -@@ -2123,16 +2425,13 @@ processFinished(struct module_qstate* qstate, struct val_qstate* vq, - if(vq->orig_msg->rep->security == sec_status_bogus) { - /* see if we can try again to fetch data */ - if(vq->restart_count < ve->max_restart) { -- int restart_count = vq->restart_count+1; - verbose(VERB_ALGO, "validation failed, " - "blacklist and retry to fetch data"); - val_blacklist(&qstate->blacklist, qstate->region, - qstate->reply_origin, 0); - qstate->reply_origin = NULL; - qstate->errinf = NULL; -- memset(vq, 0, sizeof(*vq)); -- vq->restart_count = restart_count; -- vq->state = VAL_INIT_STATE; -+ val_restart(vq); - verbose(VERB_ALGO, "pass back to next module"); - qstate->ext_state[id] = module_restart_next; - return 0; -@@ -2440,7 +2739,10 @@ primeResponseToKE(struct ub_packed_rrset_key* dnskey_rrset, - * DS response indicated an end to secure space, is_good if the DS - * validated. It returns ke=NULL if the DS response indicated that the - * request wasn't a delegation point. -- * @return 0 on servfail error (malloc failure). -+ * @return -+ * 0 on success, -+ * 1 on servfail error (malloc failure), -+ * 2 on NSEC3 suspend. - */ - static int - ds_response_to_ke(struct module_qstate* qstate, struct val_qstate* vq, -@@ -2451,6 +2753,7 @@ ds_response_to_ke(struct module_qstate* qstate, struct val_qstate* vq, - char* reason = NULL; - sldns_ede_code reason_bogus = LDNS_EDE_DNSSEC_BOGUS; - enum val_classification subtype; -+ int verified; - if(rcode != LDNS_RCODE_NOERROR) { - char rc[16]; - rc[0]=0; -@@ -2479,7 +2782,7 @@ ds_response_to_ke(struct module_qstate* qstate, struct val_qstate* vq, - /* Verify only returns BOGUS or SECURE. If the rrset is - * bogus, then we are done. */ - sec = val_verify_rrset_entry(qstate->env, ve, ds, -- vq->key_entry, &reason, &reason_bogus, LDNS_SECTION_ANSWER, qstate); -+ vq->key_entry, &reason, &reason_bogus, LDNS_SECTION_ANSWER, qstate, &verified); - if(sec != sec_status_secure) { - verbose(VERB_DETAIL, "DS rrset in DS response did " - "not verify"); -@@ -2499,7 +2802,7 @@ ds_response_to_ke(struct module_qstate* qstate, struct val_qstate* vq, - *ke = key_entry_create_null(qstate->region, - qinfo->qname, qinfo->qname_len, qinfo->qclass, - ub_packed_rrset_ttl(ds), *qstate->env->now); -- return (*ke) != NULL; -+ return (*ke) == NULL; - } - - /* Otherwise, we return the positive response. */ -@@ -2507,7 +2810,7 @@ ds_response_to_ke(struct module_qstate* qstate, struct val_qstate* vq, - *ke = key_entry_create_rrset(qstate->region, - qinfo->qname, qinfo->qname_len, qinfo->qclass, ds, - NULL, *qstate->env->now); -- return (*ke) != NULL; -+ return (*ke) == NULL; - } else if(subtype == VAL_CLASS_NODATA || - subtype == VAL_CLASS_NAMEERROR) { - /* NODATA means that the qname exists, but that there was -@@ -2539,12 +2842,12 @@ ds_response_to_ke(struct module_qstate* qstate, struct val_qstate* vq, - qinfo->qname, qinfo->qname_len, - qinfo->qclass, proof_ttl, - *qstate->env->now); -- return (*ke) != NULL; -+ return (*ke) == NULL; - case sec_status_insecure: - verbose(VERB_DETAIL, "NSEC RRset for the " - "referral proved not a delegation point"); - *ke = NULL; -- return 1; -+ return 0; - case sec_status_bogus: - verbose(VERB_DETAIL, "NSEC RRset for the " - "referral did not prove no DS."); -@@ -2556,10 +2859,17 @@ ds_response_to_ke(struct module_qstate* qstate, struct val_qstate* vq, - break; - } - -+ if(!nsec3_cache_table_init(&vq->nsec3_cache_table, qstate->region)) { -+ log_err("malloc failure in ds_response_to_ke for " -+ "NSEC3 cache"); -+ reason = "malloc failure"; -+ errinf_ede(qstate, reason, 0); -+ goto return_bogus; -+ } - sec = nsec3_prove_nods(qstate->env, ve, - msg->rep->rrsets + msg->rep->an_numrrsets, - msg->rep->ns_numrrsets, qinfo, vq->key_entry, &reason, -- &reason_bogus, qstate); -+ &reason_bogus, qstate, &vq->nsec3_cache_table); - switch(sec) { - case sec_status_insecure: - /* case insecure also continues to unsigned -@@ -2572,18 +2882,19 @@ ds_response_to_ke(struct module_qstate* qstate, struct val_qstate* vq, - qinfo->qname, qinfo->qname_len, - qinfo->qclass, proof_ttl, - *qstate->env->now); -- return (*ke) != NULL; -+ return (*ke) == NULL; - case sec_status_indeterminate: - verbose(VERB_DETAIL, "NSEC3s for the " - "referral proved no delegation"); - *ke = NULL; -- return 1; -+ return 0; - case sec_status_bogus: - verbose(VERB_DETAIL, "NSEC3s for the " - "referral did not prove no DS."); - errinf_ede(qstate, reason, reason_bogus); - goto return_bogus; - case sec_status_unchecked: -+ return 2; - default: - /* NSEC3 proof did not work */ - break; -@@ -2620,13 +2931,13 @@ ds_response_to_ke(struct module_qstate* qstate, struct val_qstate* vq, - goto return_bogus; - } - sec = val_verify_rrset_entry(qstate->env, ve, cname, -- vq->key_entry, &reason, NULL, LDNS_SECTION_ANSWER, qstate); -+ vq->key_entry, &reason, NULL, LDNS_SECTION_ANSWER, qstate, &verified); - if(sec == sec_status_secure) { - verbose(VERB_ALGO, "CNAME validated, " - "proof that DS does not exist"); - /* and that it is not a referral point */ - *ke = NULL; -- return 1; -+ return 0; - } - errinf(qstate, "CNAME in DS response was not secure."); - errinf(qstate, reason); -@@ -2649,7 +2960,7 @@ return_bogus: - *ke = key_entry_create_bad(qstate->region, qinfo->qname, - qinfo->qname_len, qinfo->qclass, - BOGUS_KEY_TTL, *qstate->env->now); -- return (*ke) != NULL; -+ return (*ke) == NULL; - } - - /** -@@ -2670,17 +2981,31 @@ return_bogus: - static void - process_ds_response(struct module_qstate* qstate, struct val_qstate* vq, - int id, int rcode, struct dns_msg* msg, struct query_info* qinfo, -- struct sock_list* origin) -+ struct sock_list* origin, int* suspend) - { - struct val_env* ve = (struct val_env*)qstate->env->modinfo[id]; - struct key_entry_key* dske = NULL; - uint8_t* olds = vq->empty_DS_name; -+ int ret; -+ *suspend = 0; - vq->empty_DS_name = NULL; -- if(!ds_response_to_ke(qstate, vq, id, rcode, msg, qinfo, &dske)) { -+ ret = ds_response_to_ke(qstate, vq, id, rcode, msg, qinfo, &dske); -+ if(ret != 0) { -+ switch(ret) { -+ case 1: - log_err("malloc failure in process_ds_response"); - vq->key_entry = NULL; /* make it error */ - vq->state = VAL_VALIDATE_STATE; - return; -+ case 2: -+ *suspend = 1; -+ return; -+ default: -+ log_err("unhandled error value for ds_response_to_ke"); -+ vq->key_entry = NULL; /* make it error */ -+ vq->state = VAL_VALIDATE_STATE; -+ return; -+ } - } - if(dske == NULL) { - vq->empty_DS_name = regional_alloc_init(qstate->region, -@@ -2927,9 +3252,26 @@ val_inform_super(struct module_qstate* qstate, int id, - return; - } - if(qstate->qinfo.qtype == LDNS_RR_TYPE_DS) { -+ int suspend; - process_ds_response(super, vq, id, qstate->return_rcode, -- qstate->return_msg, &qstate->qinfo, -- qstate->reply_origin); -+ qstate->return_msg, &qstate->qinfo, -+ qstate->reply_origin, &suspend); -+ /* If NSEC3 was needed during validation, NULL the NSEC3 cache; -+ * it will be re-initiated if needed later on. -+ * Validation (and the cache table) are happening/allocated in -+ * the super qstate whilst the RRs are allocated (and pointed -+ * to) in this sub qstate. */ -+ if(vq->nsec3_cache_table.ct) { -+ vq->nsec3_cache_table.ct = NULL; -+ } -+ if(suspend) { -+ /* deep copy the return_msg to vq->sub_ds_msg; it will -+ * be resumed later in the super state with the caveat -+ * that the initial calculations will be re-caclulated -+ * and re-suspended there before continuing. */ -+ vq->sub_ds_msg = dns_msg_deepcopy_region( -+ qstate->return_msg, super->region); -+ } - return; - } else if(qstate->qinfo.qtype == LDNS_RR_TYPE_DNSKEY) { - process_dnskey_response(super, vq, id, qstate->return_rcode, -@@ -2943,8 +3285,15 @@ val_inform_super(struct module_qstate* qstate, int id, - void - val_clear(struct module_qstate* qstate, int id) - { -+ struct val_qstate* vq; - if(!qstate) - return; -+ vq = (struct val_qstate*)qstate->minfo[id]; -+ if(vq) { -+ if(vq->suspend_timer) { -+ comm_timer_delete(vq->suspend_timer); -+ } -+ } - /* everything is allocated in the region, so assign NULL */ - qstate->minfo[id] = NULL; - } -diff --git a/unbound-1.16.2/validator/validator.h b/unbound-1.16.2/validator/validator.h -index 694e4c8..72f44b1 100644 ---- a/unbound-1.16.2/validator/validator.h -+++ b/unbound-1.16.2/validator/validator.h -@@ -45,11 +45,13 @@ - #include "util/module.h" - #include "util/data/msgreply.h" - #include "validator/val_utils.h" -+#include "validator/val_nsec3.h" - struct val_anchors; - struct key_cache; - struct key_entry_key; - struct val_neg_cache; - struct config_strlist; -+struct comm_timer; - - /** - * This is the TTL to use when a trust anchor fails to prime. A trust anchor -@@ -215,6 +217,19 @@ struct val_qstate { - - /** true if this state is waiting to prime a trust anchor */ - int wait_prime_ta; -+ -+ /** State to continue with RRSIG validation in a message later */ -+ int msg_signatures_state; -+ /** The rrset index for the msg signatures to continue from */ -+ size_t msg_signatures_index; -+ /** Cache table for NSEC3 hashes */ -+ struct nsec3_cache_table nsec3_cache_table; -+ /** DS message from sub if it got suspended from NSEC3 calculations */ -+ struct dns_msg* sub_ds_msg; -+ /** The timer to resume processing msg signatures */ -+ struct comm_timer* suspend_timer; -+ /** Number of suspends */ -+ int suspend_count; - }; - - /** -@@ -262,4 +277,7 @@ void val_clear(struct module_qstate* qstate, int id); - */ - size_t val_get_mem(struct module_env* env, int id); - -+/** Timer callback for msg signatures continue timer */ -+void validate_suspend_timer_cb(void* arg); -+ - #endif /* VALIDATOR_VALIDATOR_H */ diff --git a/unbound-1.16-control-key-perms.patch b/unbound-1.16-control-key-perms.patch deleted file mode 100644 index de3597c..0000000 --- a/unbound-1.16-control-key-perms.patch +++ /dev/null @@ -1,14 +0,0 @@ -diff --git a/unbound-1.16.2/smallapp/unbound-control-setup.sh.in b/unbound-1.16.2/smallapp/unbound-control-setup.sh.in -index 4a358f6bd..c2a79a242 100644 ---- a/unbound-1.16.2/smallapp/unbound-control-setup.sh.in -+++ b/unbound-1.16.2/smallapp/unbound-control-setup.sh.in -@@ -204,7 +204,8 @@ fi - # remove unused permissions - chmod o-rw \ - "$SVR_BASE.pem" \ -- "$SVR_BASE.key" \ -+ "$SVR_BASE.key" -+chmod g+r,o-rw \ - "$CTL_BASE.pem" \ - "$CTL_BASE.key" - diff --git a/unbound-1.16-control-t-flag.patch b/unbound-1.16-control-t-flag.patch deleted file mode 100644 index 581db24..0000000 --- a/unbound-1.16-control-t-flag.patch +++ /dev/null @@ -1,129 +0,0 @@ -commit 6d1e61173bbf44dae458c361be63217f7e9e5599 -Author: W.C.A. Wijngaards -Date: Thu Mar 28 09:58:03 2024 +0100 - - - Fix #1034: DoT forward-zone via unbound-control. - -diff --git a/unbound-1.16.2/daemon/remote.c b/unbound-1.16.2/daemon/remote.c -index 5d79eafd..cbce1198 100644 ---- a/unbound-1.16.2/daemon/remote.c -+++ b/unbound-1.16.2/daemon/remote.c -@@ -2097,7 +2097,7 @@ do_forward(RES* ssl, struct worker* worker, char* args) - - 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 +2112,8 @@ parse_fs_args(RES* ssl, char* args, uint8_t** nm, struct delegpt** dp, - *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,11 +2146,13 @@ 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)) -+ if(!parse_fs_args(ssl, args, &nm, &dp, &insecure, NULL, &tls)) - return; -+ if(tls) -+ dp->ssl_upstream = 1; - if(insecure && worker->env.anchors) { - if(!anchors_add_insecure(worker->env.anchors, LDNS_RR_CLASS_IN, - nm)) { -@@ -2174,7 +2178,7 @@ do_forward_remove(RES* ssl, struct worker* worker, char* args) - struct iter_forwards* fwd = worker->env.fwds; - int insecure = 0; - uint8_t* nm = NULL; -- if(!parse_fs_args(ssl, args, &nm, NULL, &insecure, NULL)) -+ if(!parse_fs_args(ssl, args, &nm, NULL, &insecure, NULL, NULL)) - return; - if(insecure && worker->env.anchors) - anchors_delete_insecure(worker->env.anchors, LDNS_RR_CLASS_IN, -@@ -2189,11 +2193,13 @@ 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)) -+ if(!parse_fs_args(ssl, args, &nm, &dp, &insecure, &prime, &tls)) - return; -+ if(tls) -+ dp->ssl_upstream = 1; - if(insecure && worker->env.anchors) { - if(!anchors_add_insecure(worker->env.anchors, LDNS_RR_CLASS_IN, - nm)) { -@@ -2232,7 +2238,7 @@ do_stub_remove(RES* ssl, struct worker* worker, char* args) - struct iter_forwards* fwd = worker->env.fwds; - int insecure = 0; - uint8_t* nm = NULL; -- if(!parse_fs_args(ssl, args, &nm, NULL, &insecure, NULL)) -+ if(!parse_fs_args(ssl, args, &nm, NULL, &insecure, NULL, NULL)) - return; - if(insecure && worker->env.anchors) - anchors_delete_insecure(worker->env.anchors, LDNS_RR_CLASS_IN, -diff --git a/unbound-1.16.2/doc/unbound-control.8.in b/unbound-1.16.2/doc/unbound-control.8.in -index 7823de3a..642b4c94 100644 ---- a/unbound-1.16.2/doc/unbound-control.8.in -+++ b/unbound-1.16.2/doc/unbound-control.8.in -@@ -239,22 +239,24 @@ still be bogus, use \fBflush_zone\fR to remove it), does not affect the config f - .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 -diff --git a/unbound-1.16.2/smallapp/unbound-control.c b/unbound-1.16.2/smallapp/unbound-control.c -index c4f73006..57b0787d 100644 ---- a/unbound-1.16.2/smallapp/unbound-control.c -+++ b/unbound-1.16.2/smallapp/unbound-control.c -@@ -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"); diff --git a/unbound-1.21-CVE-2024-8508.patch b/unbound-1.21-CVE-2024-8508.patch deleted file mode 100644 index 36b3cdd..0000000 --- a/unbound-1.21-CVE-2024-8508.patch +++ /dev/null @@ -1,249 +0,0 @@ -From 34de24d58bb5aa6fe3551512fc17cac08f65d93e Mon Sep 17 00:00:00 2001 -From: Yorgos Thessalonikefs -Date: Thu, 3 Oct 2024 14:46:57 +0200 -Subject: [PATCH] - Fix CVE-2024-8508, unbounded name compression could lead to - denial of service. - ---- - unbound-1.16.2/util/data/msgencode.c | 77 +++++++++++++++++----------- - 1 file changed, 46 insertions(+), 31 deletions(-) - -diff --git a/unbound-1.16.2/util/data/msgencode.c b/unbound-1.16.2/util/data/msgencode.c -index fe21cfb..f9e95e6 100644 ---- a/unbound-1.16.2/util/data/msgencode.c -+++ b/unbound-1.16.2/util/data/msgencode.c -@@ -62,6 +62,10 @@ - #define RETVAL_TRUNC -4 - /** return code that means all is peachy keen. Equal to DNS rcode NOERROR */ - #define RETVAL_OK 0 -+/** Max compressions we are willing to perform; more than that will result -+ * in semi-compressed messages, or truncated even on TCP for huge messages, to -+ * avoid locking the CPU for long */ -+#define MAX_COMPRESSION_PER_MESSAGE 120 - - /** - * Data structure to help domain name compression in outgoing messages. -@@ -284,15 +288,17 @@ write_compressed_dname(sldns_buffer* pkt, uint8_t* dname, int labs, - - /** compress owner name of RR, return RETVAL_OUTMEM RETVAL_TRUNC */ - static int --compress_owner(struct ub_packed_rrset_key* key, sldns_buffer* pkt, -- struct regional* region, struct compress_tree_node** tree, -- size_t owner_pos, uint16_t* owner_ptr, int owner_labs) -+compress_owner(struct ub_packed_rrset_key* key, sldns_buffer* pkt, -+ struct regional* region, struct compress_tree_node** tree, -+ size_t owner_pos, uint16_t* owner_ptr, int owner_labs, -+ size_t* compress_count) - { - struct compress_tree_node* p; - struct compress_tree_node** insertpt = NULL; - if(!*owner_ptr) { - /* compress first time dname */ -- if((p = compress_tree_lookup(tree, key->rk.dname, -+ if(*compress_count < MAX_COMPRESSION_PER_MESSAGE && -+ (p = compress_tree_lookup(tree, key->rk.dname, - owner_labs, &insertpt))) { - if(p->labs == owner_labs) - /* avoid ptr chains, since some software is -@@ -301,6 +307,7 @@ compress_owner(struct ub_packed_rrset_key* key, sldns_buffer* pkt, - if(!write_compressed_dname(pkt, key->rk.dname, - owner_labs, p)) - return RETVAL_TRUNC; -+ (*compress_count)++; - /* check if typeclass+4 ttl + rdatalen is available */ - if(sldns_buffer_remaining(pkt) < 4+4+2) - return RETVAL_TRUNC; -@@ -313,7 +320,8 @@ compress_owner(struct ub_packed_rrset_key* key, sldns_buffer* pkt, - if(owner_pos <= PTR_MAX_OFFSET) - *owner_ptr = htons(PTR_CREATE(owner_pos)); - } -- if(!compress_tree_store(key->rk.dname, owner_labs, -+ if(*compress_count < MAX_COMPRESSION_PER_MESSAGE && -+ !compress_tree_store(key->rk.dname, owner_labs, - owner_pos, region, p, insertpt)) - return RETVAL_OUTMEM; - } else { -@@ -333,20 +341,24 @@ compress_owner(struct ub_packed_rrset_key* key, sldns_buffer* pkt, - - /** compress any domain name to the packet, return RETVAL_* */ - static int --compress_any_dname(uint8_t* dname, sldns_buffer* pkt, int labs, -- struct regional* region, struct compress_tree_node** tree) -+compress_any_dname(uint8_t* dname, sldns_buffer* pkt, int labs, -+ struct regional* region, struct compress_tree_node** tree, -+ size_t* compress_count) - { - struct compress_tree_node* p; - struct compress_tree_node** insertpt = NULL; - size_t pos = sldns_buffer_position(pkt); -- if((p = compress_tree_lookup(tree, dname, labs, &insertpt))) { -+ if(*compress_count < MAX_COMPRESSION_PER_MESSAGE && -+ (p = compress_tree_lookup(tree, dname, labs, &insertpt))) { - if(!write_compressed_dname(pkt, dname, labs, p)) - return RETVAL_TRUNC; -+ (*compress_count)++; - } else { - if(!dname_buffer_write(pkt, dname)) - return RETVAL_TRUNC; - } -- if(!compress_tree_store(dname, labs, pos, region, p, insertpt)) -+ if(*compress_count < MAX_COMPRESSION_PER_MESSAGE && -+ !compress_tree_store(dname, labs, pos, region, p, insertpt)) - return RETVAL_OUTMEM; - return RETVAL_OK; - } -@@ -364,9 +376,9 @@ type_rdata_compressable(struct ub_packed_rrset_key* key) - - /** compress domain names in rdata, return RETVAL_* */ - static int --compress_rdata(sldns_buffer* pkt, uint8_t* rdata, size_t todolen, -- struct regional* region, struct compress_tree_node** tree, -- const sldns_rr_descriptor* desc) -+compress_rdata(sldns_buffer* pkt, uint8_t* rdata, size_t todolen, -+ struct regional* region, struct compress_tree_node** tree, -+ const sldns_rr_descriptor* desc, size_t* compress_count) - { - int labs, r, rdf = 0; - size_t dname_len, len, pos = sldns_buffer_position(pkt); -@@ -380,8 +392,8 @@ compress_rdata(sldns_buffer* pkt, uint8_t* rdata, size_t todolen, - switch(desc->_wireformat[rdf]) { - case LDNS_RDF_TYPE_DNAME: - labs = dname_count_size_labels(rdata, &dname_len); -- if((r=compress_any_dname(rdata, pkt, labs, region, -- tree)) != RETVAL_OK) -+ if((r=compress_any_dname(rdata, pkt, labs, region, -+ tree, compress_count)) != RETVAL_OK) - return r; - rdata += dname_len; - todolen -= dname_len; -@@ -449,7 +461,8 @@ static int - packed_rrset_encode(struct ub_packed_rrset_key* key, sldns_buffer* pkt, - uint16_t* num_rrs, time_t timenow, struct regional* region, - int do_data, int do_sig, struct compress_tree_node** tree, -- sldns_pkt_section s, uint16_t qtype, int dnssec, size_t rr_offset) -+ sldns_pkt_section s, uint16_t qtype, int dnssec, size_t rr_offset, -+ size_t* compress_count) - { - size_t i, j, owner_pos; - int r, owner_labs; -@@ -477,9 +490,9 @@ packed_rrset_encode(struct ub_packed_rrset_key* key, sldns_buffer* pkt, - for(i=0; icount; i++) { - /* rrset roundrobin */ - j = (i + rr_offset) % data->count; -- if((r=compress_owner(key, pkt, region, tree, -- owner_pos, &owner_ptr, owner_labs)) -- != RETVAL_OK) -+ if((r=compress_owner(key, pkt, region, tree, -+ owner_pos, &owner_ptr, owner_labs, -+ compress_count)) != RETVAL_OK) - return r; - sldns_buffer_write(pkt, &key->rk.type, 2); - sldns_buffer_write(pkt, &key->rk.rrset_class, 2); -@@ -489,8 +502,8 @@ packed_rrset_encode(struct ub_packed_rrset_key* key, sldns_buffer* pkt, - else sldns_buffer_write_u32(pkt, data->rr_ttl[j]-adjust); - if(c) { - if((r=compress_rdata(pkt, data->rr_data[j], -- data->rr_len[j], region, tree, c)) -- != RETVAL_OK) -+ data->rr_len[j], region, tree, c, -+ compress_count)) != RETVAL_OK) - return r; - } else { - if(sldns_buffer_remaining(pkt) < data->rr_len[j]) -@@ -510,9 +523,9 @@ packed_rrset_encode(struct ub_packed_rrset_key* key, sldns_buffer* pkt, - return RETVAL_TRUNC; - sldns_buffer_write(pkt, &owner_ptr, 2); - } else { -- if((r=compress_any_dname(key->rk.dname, -- pkt, owner_labs, region, tree)) -- != RETVAL_OK) -+ if((r=compress_any_dname(key->rk.dname, -+ pkt, owner_labs, region, tree, -+ compress_count)) != RETVAL_OK) - return r; - if(sldns_buffer_remaining(pkt) < - 4+4+data->rr_len[i]) -@@ -544,7 +557,8 @@ static int - insert_section(struct reply_info* rep, size_t num_rrsets, uint16_t* num_rrs, - sldns_buffer* pkt, size_t rrsets_before, time_t timenow, - struct regional* region, struct compress_tree_node** tree, -- sldns_pkt_section s, uint16_t qtype, int dnssec, size_t rr_offset) -+ sldns_pkt_section s, uint16_t qtype, int dnssec, size_t rr_offset, -+ size_t* compress_count) - { - int r; - size_t i, setstart; -@@ -560,7 +574,7 @@ insert_section(struct reply_info* rep, size_t num_rrsets, uint16_t* num_rrs, - setstart = sldns_buffer_position(pkt); - if((r=packed_rrset_encode(rep->rrsets[rrsets_before+i], - pkt, num_rrs, timenow, region, 1, 1, tree, -- s, qtype, dnssec, rr_offset)) -+ s, qtype, dnssec, rr_offset, compress_count)) - != RETVAL_OK) { - /* Bad, but if due to size must set TC bit */ - /* trim off the rrset neatly. */ -@@ -573,7 +587,7 @@ insert_section(struct reply_info* rep, size_t num_rrsets, uint16_t* num_rrs, - setstart = sldns_buffer_position(pkt); - if((r=packed_rrset_encode(rep->rrsets[rrsets_before+i], - pkt, num_rrs, timenow, region, 1, 0, tree, -- s, qtype, dnssec, rr_offset)) -+ s, qtype, dnssec, rr_offset, compress_count)) - != RETVAL_OK) { - sldns_buffer_set_position(pkt, setstart); - return r; -@@ -584,7 +598,7 @@ insert_section(struct reply_info* rep, size_t num_rrsets, uint16_t* num_rrs, - setstart = sldns_buffer_position(pkt); - if((r=packed_rrset_encode(rep->rrsets[rrsets_before+i], - pkt, num_rrs, timenow, region, 0, 1, tree, -- s, qtype, dnssec, rr_offset)) -+ s, qtype, dnssec, rr_offset, compress_count)) - != RETVAL_OK) { - sldns_buffer_set_position(pkt, setstart); - return r; -@@ -677,6 +691,7 @@ reply_info_encode(struct query_info* qinfo, struct reply_info* rep, - struct compress_tree_node* tree = 0; - int r; - size_t rr_offset; -+ size_t compress_count=0; - - sldns_buffer_clear(buffer); - if(udpsize < sldns_buffer_limit(buffer)) -@@ -723,7 +738,7 @@ reply_info_encode(struct query_info* qinfo, struct reply_info* rep, - arep.rrsets = &qinfo->local_alias->rrset; - if((r=insert_section(&arep, 1, &ancount, buffer, 0, - timezero, region, &tree, LDNS_SECTION_ANSWER, -- qinfo->qtype, dnssec, rr_offset)) != RETVAL_OK) { -+ qinfo->qtype, dnssec, rr_offset, &compress_count)) != RETVAL_OK) { - if(r == RETVAL_TRUNC) { - /* create truncated message */ - sldns_buffer_write_u16_at(buffer, 6, ancount); -@@ -738,7 +753,7 @@ reply_info_encode(struct query_info* qinfo, struct reply_info* rep, - /* insert answer section */ - if((r=insert_section(rep, rep->an_numrrsets, &ancount, buffer, - 0, timenow, region, &tree, LDNS_SECTION_ANSWER, qinfo->qtype, -- dnssec, rr_offset)) != RETVAL_OK) { -+ dnssec, rr_offset, &compress_count)) != RETVAL_OK) { - if(r == RETVAL_TRUNC) { - /* create truncated message */ - sldns_buffer_write_u16_at(buffer, 6, ancount); -@@ -756,7 +771,7 @@ reply_info_encode(struct query_info* qinfo, struct reply_info* rep, - if((r=insert_section(rep, rep->ns_numrrsets, &nscount, buffer, - rep->an_numrrsets, timenow, region, &tree, - LDNS_SECTION_AUTHORITY, qinfo->qtype, -- dnssec, rr_offset)) != RETVAL_OK) { -+ dnssec, rr_offset, &compress_count)) != RETVAL_OK) { - if(r == RETVAL_TRUNC) { - /* create truncated message */ - sldns_buffer_write_u16_at(buffer, 8, nscount); -@@ -773,7 +788,7 @@ reply_info_encode(struct query_info* qinfo, struct reply_info* rep, - if((r=insert_section(rep, rep->ar_numrrsets, &arcount, buffer, - rep->an_numrrsets + rep->ns_numrrsets, timenow, region, - &tree, LDNS_SECTION_ADDITIONAL, qinfo->qtype, -- dnssec, rr_offset)) != RETVAL_OK) { -+ dnssec, rr_offset, &compress_count)) != RETVAL_OK) { - if(r == RETVAL_TRUNC) { - /* no need to set TC bit, this is the additional */ - sldns_buffer_write_u16_at(buffer, 10, arcount); --- -2.47.0 - diff --git a/unbound-1.23.1-CVE-2025-5994.patch b/unbound-1.23.1-CVE-2025-5994.patch deleted file mode 100644 index a6e034d..0000000 --- a/unbound-1.23.1-CVE-2025-5994.patch +++ /dev/null @@ -1,2255 +0,0 @@ -diff --git a/unbound-1.16.2/edns-subnet/addrtree.c b/unbound-1.16.2/edns-subnet/addrtree.c -index 180a022..ebe71b9 100644 ---- a/unbound-1.16.2/edns-subnet/addrtree.c -+++ b/unbound-1.16.2/edns-subnet/addrtree.c -@@ -97,6 +97,7 @@ node_create(struct addrtree *tree, void *elem, addrlen_t scope, - tree->node_count++; - node->scope = scope; - node->ttl = ttl; -+ node->only_match_scope_zero = 0; - node->edge[0] = NULL; - node->edge[1] = NULL; - node->parent_edge = NULL; -@@ -155,6 +156,7 @@ clean_node(struct addrtree *tree, struct addrnode *node) - if (!node->elem) return; - tree->size_bytes -= tree->sizefunc(node->elem); - tree->delfunc(tree->env, node->elem); -+ node->only_match_scope_zero = 0; - node->elem = NULL; - } - -@@ -358,7 +360,7 @@ issub(const addrkey_t *s1, addrlen_t l1, - void - addrtree_insert(struct addrtree *tree, const addrkey_t *addr, - addrlen_t sourcemask, addrlen_t scope, void *elem, time_t ttl, -- time_t now) -+ time_t now, int only_match_scope_zero) - { - struct addrnode *newnode, *node; - struct addredge *edge; -@@ -381,6 +383,7 @@ addrtree_insert(struct addrtree *tree, const addrkey_t *addr, - /* update this node's scope and data */ - clean_node(tree, node); - node->ttl = ttl; -+ node->only_match_scope_zero = only_match_scope_zero; - node->elem = elem; - node->scope = scope; - tree->size_bytes += tree->sizefunc(elem); -@@ -447,6 +450,7 @@ addrtree_insert(struct addrtree *tree, const addrkey_t *addr, - newnode->elem = elem; - newnode->scope = scope; - newnode->ttl = ttl; -+ newnode->only_match_scope_zero = only_match_scope_zero; - } - - tree->size_bytes += node_size(tree, newnode); -@@ -483,7 +487,8 @@ addrtree_find(struct addrtree *tree, const addrkey_t *addr, - /* Current node more specific then question. */ - log_assert(depth <= sourcemask); - /* does this node have data? if yes, see if we have a match */ -- if (node->elem && node->ttl >= now) { -+ if (node->elem && node->ttl >= now && -+ !(sourcemask != 0 && node->only_match_scope_zero)) { - /* saved at wrong depth */; - log_assert(node->scope >= depth); - if (depth == node->scope || -diff --git a/unbound-1.16.2/edns-subnet/addrtree.h b/unbound-1.16.2/edns-subnet/addrtree.h -index 1aea54e..0bc1837 100644 ---- a/unbound-1.16.2/edns-subnet/addrtree.h -+++ b/unbound-1.16.2/edns-subnet/addrtree.h -@@ -95,6 +95,10 @@ struct addrnode { - time_t ttl; - /** Number of significant bits in address. */ - addrlen_t scope; -+ /** Only use the element for queries for subnet/0. Set if the query -+ * for /0 was answered with scope 0. For query /x answer scope 0, -+ * they can match anything and this is false. */ -+ int only_match_scope_zero; - /** A node can have 0-2 edges, set to NULL for unused */ - struct addredge *edge[2]; - /** edge between this node and parent */ -@@ -157,11 +161,12 @@ void addrtree_delete(struct addrtree *tree); - * @param scope: Number of significant bits in addr. - * @param elem: data to store in the tree. - * @param ttl: elem is valid up to this time, seconds. -+ * @param only_match_scope_zero: set for when query /0 has scope /0 answer. - * @param now: Current time in seconds. - */ - void addrtree_insert(struct addrtree *tree, const addrkey_t *addr, - addrlen_t sourcemask, addrlen_t scope, void *elem, time_t ttl, -- time_t now); -+ time_t now, int only_match_scope_zero); - - /** - * Find a node containing an element in the tree. -diff --git a/unbound-1.16.2/edns-subnet/subnetmod.c b/unbound-1.16.2/edns-subnet/subnetmod.c -index 7544611..53d3048 100644 ---- a/unbound-1.16.2/edns-subnet/subnetmod.c -+++ b/unbound-1.16.2/edns-subnet/subnetmod.c -@@ -51,10 +51,12 @@ - #include "services/cache/dns.h" - #include "util/module.h" - #include "util/regional.h" -+#include "util/fptr_wlist.h" - #include "util/storage/slabhash.h" - #include "util/config_file.h" - #include "util/data/msgreply.h" - #include "sldns/sbuffer.h" -+#include "sldns/wire2str.h" - #include "iterator/iter_utils.h" - - /** externally called */ -@@ -151,10 +153,12 @@ int ecs_whitelist_check(struct query_info* qinfo, - - /* 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) -+ && sq->ecs_client_in.subnet_validdata) { - qstate->no_cache_store = 0; - } - -+ sq->subnet_sent_no_subnet = 0; - if(sq->ecs_server_out.subnet_validdata && ((sq->subnet_downstream && - qstate->env->cfg->client_subnet_always_forward) || - ecs_is_whitelisted(sn_env->whitelist, -@@ -165,6 +169,14 @@ int ecs_whitelist_check(struct query_info* qinfo, - * set. */ - if(!edns_opt_list_find(qstate->edns_opts_back_out, - qstate->env->cfg->client_subnet_opcode)) { -+ /* if the client is not wanting an EDNS subnet option, -+ * omit it and store that we omitted it but actually -+ * are doing EDNS subnet to the server. */ -+ if(sq->ecs_server_out.subnet_source_mask == 0) { -+ sq->subnet_sent_no_subnet = 1; -+ sq->subnet_sent = 0; -+ return 1; -+ } - subnet_ecs_opt_list_append(&sq->ecs_server_out, - &qstate->edns_opts_back_out, qstate, region); - } -@@ -331,6 +343,7 @@ update_cache(struct module_qstate *qstate, int id) - 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; - - /* We already calculated hash upon lookup (lookup_and_reply) if we were - * allowed to look in the ECS cache */ -@@ -392,9 +405,12 @@ update_cache(struct module_qstate *qstate, int id) - reply_info_set_ttls(rep, *qstate->env->now); - 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; - addrtree_insert(tree, (addrkey_t*)edns->subnet_addr, - edns->subnet_source_mask, sq->max_scope, rep, -- rep->ttl, *qstate->env->now); -+ rep->ttl, *qstate->env->now, only_match_scope_zero); - - lock_rw_unlock(&lru_entry->lock); - if (need_to_insert) { -@@ -475,6 +491,69 @@ common_prefix(uint8_t *a, uint8_t *b, uint8_t net) - return !memcmp(a, b, n) && ((net % 8) == 0 || a[n] == b[n]); - } - -+/** -+ * Create sub request that looks up the query. -+ * @param qstate: query state -+ * @param sq: subnet qstate -+ * @return false on failure. -+ */ -+static int -+generate_sub_request(struct module_qstate *qstate, struct subnet_qstate* sq) -+{ -+ struct module_qstate* subq = NULL; -+ uint16_t qflags = 0; /* OPCODE QUERY, no flags */ -+ int prime = 0; -+ int valrec = 0; -+ struct query_info qinf; -+ qinf.qname = qstate->qinfo.qname; -+ qinf.qname_len = qstate->qinfo.qname_len; -+ qinf.qtype = qstate->qinfo.qtype; -+ qinf.qclass = qstate->qinfo.qclass; -+ qinf.local_alias = NULL; -+ -+ qflags |= BIT_RD; -+ if((qstate->query_flags & BIT_CD)!=0) { -+ qflags |= BIT_CD; -+ valrec = 1; -+ } -+ -+ fptr_ok(fptr_whitelist_modenv_attach_sub(qstate->env->attach_sub)); -+ if(!(*qstate->env->attach_sub)(qstate, &qinf, qflags, prime, valrec, -+ &subq)) { -+ return 0; -+ } -+ if(subq) { -+ /* It is possible to access the subquery module state. */ -+ if(sq->ecs_client_in.subnet_source_mask == 0 && -+ edns_opt_list_find(qstate->edns_opts_front_in, -+ qstate->env->cfg->client_subnet_opcode)) { -+ subq->no_cache_store = 1; -+ } -+ } -+ return 1; -+} -+ -+/** -+ * Perform the query without subnet -+ * @param qstate: query state -+ * @param sq: subnet qstate -+ * @return module state -+ */ -+static enum module_ext_state -+generate_lookup_without_subnet(struct module_qstate *qstate, -+ struct subnet_qstate* sq) -+{ -+ verbose(VERB_ALGO, "subnetcache: make subquery to look up without subnet"); -+ if(!generate_sub_request(qstate, sq)) { -+ verbose(VERB_ALGO, "Could not generate sub query"); -+ qstate->return_rcode = LDNS_RCODE_FORMERR; -+ qstate->return_msg = NULL; -+ return module_finished; -+ } -+ sq->wait_subquery = 1; -+ return module_wait_subquery; -+} -+ - static enum module_ext_state - eval_response(struct module_qstate *qstate, int id, struct subnet_qstate *sq) - { -@@ -495,7 +574,7 @@ eval_response(struct module_qstate *qstate, int id, struct subnet_qstate *sq) - } - - /* We have not asked for subnet data */ -- if (!sq->subnet_sent) { -+ if (!sq->subnet_sent && !sq->subnet_sent_no_subnet) { - if (s_in->subnet_validdata) - verbose(VERB_QUERY, "subnetcache: received spurious data"); - if (sq->subnet_downstream) /* Copy back to client */ -@@ -504,22 +583,27 @@ eval_response(struct module_qstate *qstate, int id, struct subnet_qstate *sq) - } - - /* subnet sent but nothing came back */ -- if (!s_in->subnet_validdata) { -+ if (!s_in->subnet_validdata && !sq->subnet_sent_no_subnet) { - /* The authority indicated no support for edns subnet. As a - * consequence the answer ended up in the regular cache. It - * is still useful to put it in the edns subnet cache for - * when a client explicitly asks for subnet specific answer. */ - verbose(VERB_QUERY, "subnetcache: Authority indicates no support"); -- if(!sq->started_no_cache_store) { -- lock_rw_wrlock(&sne->biglock); -- update_cache(qstate, id); -- lock_rw_unlock(&sne->biglock); -- } -- if (sq->subnet_downstream) -- cp_edns_bad_response(c_out, c_in); -- return module_finished; -+ return generate_lookup_without_subnet(qstate, sq); - } -- -+ -+ /* Purposefully there was no sent subnet, and there is consequently -+ * no subnet in the answer. If there was, use the subnet in the answer -+ * anyway. But if there is not, treat it as a prefix 0 answer. */ -+ if(sq->subnet_sent_no_subnet && !s_in->subnet_validdata) { -+ /* Fill in 0.0.0.0/0 scope 0, or ::0/0 scope 0, for caching. */ -+ s_in->subnet_addr_fam = s_out->subnet_addr_fam; -+ s_in->subnet_source_mask = 0; -+ s_in->subnet_scope_mask = 0; -+ memset(s_in->subnet_addr, 0, INET6_SIZE); -+ s_in->subnet_validdata = 1; -+ } -+ - /* Being here means we have asked for and got a subnet specific - * answer. Also, the answer from the authority is not yet cached - * anywhere. */ -@@ -530,13 +614,14 @@ eval_response(struct module_qstate *qstate, int id, struct subnet_qstate *sq) - !common_prefix(s_out->subnet_addr, s_in->subnet_addr, - s_out->subnet_source_mask)) - { -- /* we can not accept, restart query without option */ -+ /* we can not accept, perform query without option */ - verbose(VERB_QUERY, "subnetcache: forged data"); - s_out->subnet_validdata = 0; - (void)edns_opt_list_remove(&qstate->edns_opts_back_out, - qstate->env->cfg->client_subnet_opcode); - sq->subnet_sent = 0; -- return module_restart_next; -+ sq->subnet_sent_no_subnet = 0; -+ return generate_lookup_without_subnet(qstate, sq); - } - - lock_rw_wrlock(&sne->biglock); -@@ -656,6 +741,7 @@ ecs_query_response(struct module_qstate* qstate, struct dns_msg* response, - edns_opt_list_remove(&qstate->edns_opts_back_out, - qstate->env->cfg->client_subnet_opcode); - sq->subnet_sent = 0; -+ sq->subnet_sent_no_subnet = 0; - memset(&sq->ecs_server_out, 0, sizeof(sq->ecs_server_out)); - } else if (!sq->track_max_scope && - FLAGS_GET_RCODE(response->rep->flags) == LDNS_RCODE_NOERROR && -@@ -674,6 +760,24 @@ ecs_query_response(struct module_qstate* qstate, struct dns_msg* response, - return 1; - } - -+/** verbose print edns subnet option in pretty print */ -+static void -+subnet_log_print(const char* s, struct edns_option* ecs_opt) -+{ -+ if(verbosity >= VERB_ALGO) { -+ char buf[256]; -+ char* str = buf; -+ size_t str_len = sizeof(buf); -+ if(!ecs_opt) { -+ verbose(VERB_ALGO, "%s (null)", s); -+ return; -+ } -+ (void)sldns_wire2str_edns_subnet_print(&str, &str_len, -+ ecs_opt->opt_data, ecs_opt->opt_len); -+ verbose(VERB_ALGO, "%s %s", s, buf); -+ } -+} -+ - int - ecs_edns_back_parsed(struct module_qstate* qstate, int id, - void* ATTR_UNUSED(cbargs)) -@@ -688,6 +792,7 @@ ecs_edns_back_parsed(struct module_qstate* qstate, int id, - qstate->env->cfg->client_subnet_opcode)) && - parse_subnet_option(ecs_opt, &sq->ecs_server_in) && - sq->subnet_sent && sq->ecs_server_in.subnet_validdata) { -+ subnet_log_print("answer has edns subnet", ecs_opt); - /* Only skip global cache store if we sent an ECS option - * and received one back. Answers from non-whitelisted - * servers will end up in global cache. Answers for -@@ -698,6 +803,12 @@ ecs_edns_back_parsed(struct module_qstate* qstate, int id, - sq->ecs_server_in.subnet_scope_mask > - sq->max_scope)) - sq->max_scope = sq->ecs_server_in.subnet_scope_mask; -+ } else if(sq->subnet_sent_no_subnet) { -+ /* The answer can be stored as scope 0, not in global cache. */ -+ qstate->no_cache_store = 1; -+ } else if(sq->subnet_sent) { -+ /* Need another query to be able to store in global cache. */ -+ qstate->no_cache_store = 1; - } - - return 1; -@@ -715,6 +826,32 @@ subnetmod_operate(struct module_qstate *qstate, enum module_ev event, - strmodulevent(event)); - log_query_info(VERB_QUERY, "subnetcache operate: query", &qstate->qinfo); - -+ if(sq && sq->wait_subquery_done) { -+ /* The subquery lookup returned. */ -+ if(sq->ecs_client_in.subnet_source_mask == 0 && -+ edns_opt_list_find(qstate->edns_opts_front_in, -+ qstate->env->cfg->client_subnet_opcode)) { -+ if(!sq->started_no_cache_store && -+ qstate->return_msg) { -+ lock_rw_wrlock(&sne->biglock); -+ update_cache(qstate, id); -+ lock_rw_unlock(&sne->biglock); -+ } -+ if (sq->subnet_downstream) -+ cp_edns_bad_response(&sq->ecs_client_out, -+ &sq->ecs_client_in); -+ /* It is a scope zero lookup, append edns subnet -+ * option to the querier. */ -+ subnet_ecs_opt_list_append(&sq->ecs_client_out, -+ &qstate->edns_opts_front_out, qstate, -+ qstate->region); -+ } -+ sq->wait_subquery_done = 0; -+ qstate->ext_state[id] = module_finished; -+ qstate->no_cache_store = sq->started_no_cache_store; -+ qstate->no_cache_lookup = sq->started_no_cache_lookup; -+ return; -+ } - if((event == module_event_new || event == module_event_pass) && - sq == NULL) { - struct edns_option* ecs_opt; -@@ -725,6 +862,8 @@ subnetmod_operate(struct module_qstate *qstate, enum module_ev event, - } - - sq = (struct subnet_qstate*)qstate->minfo[id]; -+ if(sq->wait_subquery) -+ return; /* Wait for that subquery to return */ - - if((ecs_opt = edns_opt_list_find( - qstate->edns_opts_front_in, -@@ -736,6 +875,7 @@ subnetmod_operate(struct module_qstate *qstate, enum module_ev event, - qstate->ext_state[id] = module_finished; - return; - } -+ subnet_log_print("query has edns subnet", ecs_opt); - sq->subnet_downstream = 1; - } - else if(qstate->mesh_info->reply_list) { -@@ -748,6 +888,14 @@ subnetmod_operate(struct module_qstate *qstate, enum module_ev event, - /* No clients are interested in result or we could not - * parse it, we don't do client subnet */ - sq->ecs_server_out.subnet_validdata = 0; -+ if(edns_opt_list_find(qstate->edns_opts_front_in, -+ qstate->env->cfg->client_subnet_opcode)) { -+ /* aggregated this deaggregated state */ -+ qstate->ext_state[id] = -+ generate_lookup_without_subnet( -+ qstate, sq); -+ return; -+ } - verbose(VERB_ALGO, "subnetcache: pass to next module"); - qstate->ext_state[id] = module_wait_module; - return; -@@ -775,10 +923,25 @@ subnetmod_operate(struct module_qstate *qstate, enum module_ev event, - subnet_ecs_opt_list_append(&sq->ecs_client_out, - &qstate->edns_opts_front_out, qstate, - qstate->region); -+ if(verbosity >= VERB_ALGO) { -+ subnet_log_print("reply has edns subnet", -+ edns_opt_list_find( -+ qstate->edns_opts_front_out, -+ qstate->env->cfg-> -+ client_subnet_opcode)); -+ } - return; - } - lock_rw_unlock(&sne->biglock); - } -+ if(sq->ecs_client_in.subnet_source_mask == 0 && -+ edns_opt_list_find(qstate->edns_opts_front_in, -+ qstate->env->cfg->client_subnet_opcode)) { -+ /* client asked for resolution without edns subnet */ -+ qstate->ext_state[id] = generate_lookup_without_subnet( -+ qstate, sq); -+ return; -+ } - - sq->ecs_server_out.subnet_addr_fam = - sq->ecs_client_in.subnet_addr_fam; -@@ -815,6 +978,8 @@ subnetmod_operate(struct module_qstate *qstate, enum module_ev event, - qstate->ext_state[id] = module_wait_module; - return; - } -+ if(sq && sq->wait_subquery) -+ return; /* Wait for that subquery to return */ - /* Query handed back by next module, we have a 'final' answer */ - if(sq && event == module_event_moddone) { - qstate->ext_state[id] = eval_response(qstate, id, sq); -@@ -823,6 +988,13 @@ subnetmod_operate(struct module_qstate *qstate, enum module_ev event, - subnet_ecs_opt_list_append(&sq->ecs_client_out, - &qstate->edns_opts_front_out, qstate, - qstate->region); -+ if(verbosity >= VERB_ALGO) { -+ subnet_log_print("reply has edns subnet", -+ edns_opt_list_find( -+ qstate->edns_opts_front_out, -+ qstate->env->cfg-> -+ client_subnet_opcode)); -+ } - } - qstate->no_cache_store = sq->started_no_cache_store; - qstate->no_cache_lookup = sq->started_no_cache_lookup; -@@ -856,10 +1028,27 @@ subnetmod_clear(struct module_qstate *ATTR_UNUSED(qstate), - } - - void --subnetmod_inform_super(struct module_qstate *ATTR_UNUSED(qstate), -- int ATTR_UNUSED(id), struct module_qstate *ATTR_UNUSED(super)) -+subnetmod_inform_super(struct module_qstate *qstate, int id, -+ struct module_qstate *super) - { -- /* Not used */ -+ struct subnet_qstate* super_sq = -+ (struct subnet_qstate*)super->minfo[id]; -+ log_query_info(VERB_ALGO, "subnetcache inform_super: query", -+ &super->qinfo); -+ super_sq->wait_subquery = 0; -+ super_sq->wait_subquery_done = 1; -+ if(qstate->return_rcode != LDNS_RCODE_NOERROR || -+ !qstate->return_msg) { -+ super->return_msg = NULL; -+ super->return_rcode = LDNS_RCODE_SERVFAIL; -+ return; -+ } -+ super->return_rcode = LDNS_RCODE_NOERROR; -+ super->return_msg = dns_copy_msg(qstate->return_msg, super->region); -+ if(!super->return_msg) { -+ log_err("subnetcache: copy response, out of memory"); -+ super->return_rcode = LDNS_RCODE_SERVFAIL; -+ } - } - - size_t -diff --git a/unbound-1.16.2/edns-subnet/subnetmod.h b/unbound-1.16.2/edns-subnet/subnetmod.h -index f0bcaad..3893820 100644 ---- a/unbound-1.16.2/edns-subnet/subnetmod.h -+++ b/unbound-1.16.2/edns-subnet/subnetmod.h -@@ -85,6 +85,13 @@ struct subnet_qstate { - struct ecs_data ecs_server_out; - int subnet_downstream; - int subnet_sent; -+ /** -+ * If there was no subnet sent because the client used source prefix -+ * length 0 for omitting the information. Then the answer is cached -+ * like subnet was a /0 scope. Like the subnet_sent flag, but when -+ * the EDNS subnet option is omitted because the client asked. -+ */ -+ int subnet_sent_no_subnet; - /** keep track of longest received scope, set after receiving CNAME for - * incoming QNAME. */ - int track_max_scope; -@@ -95,6 +102,10 @@ struct subnet_qstate { - int started_no_cache_store; - /** has the subnet module been started with no_cache_lookup? */ - int started_no_cache_lookup; -+ /** Wait for subquery that has been started for nonsubnet lookup. */ -+ int wait_subquery; -+ /** The subquery waited for is done. */ -+ int wait_subquery_done; - }; - - void subnet_data_delete(void* d, void* ATTR_UNUSED(arg)); -diff --git a/unbound-1.16.2/iterator/iterator.c b/unbound-1.16.2/iterator/iterator.c -index da9b799..3cacd5d 100644 ---- a/unbound-1.16.2/iterator/iterator.c -+++ b/unbound-1.16.2/iterator/iterator.c -@@ -3982,10 +3982,10 @@ process_response(struct module_qstate* qstate, struct iter_qstate* iq, - /* like packet got dropped */ - goto handle_it; - } -- if(!inplace_cb_edns_back_parsed_call(qstate->env, qstate)) { -- log_err("unable to call edns_back_parsed callback"); -- goto handle_it; -- } -+ } -+ if(!inplace_cb_edns_back_parsed_call(qstate->env, qstate)) { -+ log_err("unable to call edns_back_parsed callback"); -+ goto handle_it; - } - - /* remove CD-bit, we asked for in case we handle validation ourself */ -diff --git a/unbound-1.16.2/testcode/unitecs.c b/unbound-1.16.2/testcode/unitecs.c -index b240bfc..68d6907 100644 ---- a/unbound-1.16.2/testcode/unitecs.c -+++ b/unbound-1.16.2/testcode/unitecs.c -@@ -173,7 +173,7 @@ static void consistency_test(void) - for (i = 0; i < 1000; i++) { - l = randomkey(&k, 128); - elem = (struct reply_info *) calloc(1, sizeof(struct reply_info)); -- addrtree_insert(t, k, l, 64, elem, timenow + 10, timenow); -+ addrtree_insert(t, k, l, 64, elem, timenow + 10, timenow, 0); - /* This should always hold because no items ever expire. They - * could be overwritten, though. */ - unit_assert( count <= t->node_count ); -@@ -189,7 +189,7 @@ static void consistency_test(void) - for (i = 0; i < 1000; i++) { - l = randomkey(&k, 128); - elem = (struct reply_info *) calloc(1, sizeof(struct reply_info)); -- addrtree_insert(t, k, l, 64, elem, i + 10, i); -+ addrtree_insert(t, k, l, 64, elem, i + 10, i, 0); - free(k); - unit_assert( !addrtree_inconsistent(t) ); - } -@@ -201,7 +201,7 @@ static void consistency_test(void) - for (i = 0; i < 1000; i++) { - l = randomkey(&k, 128); - elem = (struct reply_info *) calloc(1, sizeof(struct reply_info)); -- addrtree_insert(t, k, l, 64, elem, i + 10, i); -+ addrtree_insert(t, k, l, 64, elem, i + 10, i, 0); - unit_assert( t->node_count <= 27); - free(k); - unit_assert( !addrtree_inconsistent(t) ); -diff --git a/unbound-1.16.2/testdata/subnet_noecs_mult.rpl b/unbound-1.16.2/testdata/subnet_noecs_mult.rpl -new file mode 100644 -index 0000000..3e2acef ---- /dev/null -+++ b/unbound-1.16.2/testdata/subnet_noecs_mult.rpl -@@ -0,0 +1,334 @@ -+# config -+server: -+ send-client-subnet: 1.2.3.4 -+ max-client-subnet-ipv4: 17 -+ module-config: "subnetcache iterator" -+ qname-minimisation: no -+ minimal-responses: yes -+ target-fetch-policy: "0 0 0 0 0" -+ -+stub-zone: -+ name: "." -+ stub-addr: 193.0.14.129 # K.ROOT-SERVERS.NET. -+CONFIG_END -+ -+SCENARIO_BEGIN Test subnet with no edns subnet from server multiple times -+; Multiple queries are sent to a server that does not reply with the -+; edns-subnet option. -+ -+; K.ROOT-SERVERS.NET. -+RANGE_BEGIN 0 100 -+ ADDRESS 193.0.14.129 -+ENTRY_BEGIN -+MATCH opcode qtype qname -+ADJUST copy_id -+REPLY QR NOERROR -+SECTION QUESTION -+. IN NS -+SECTION ANSWER -+. IN NS K.ROOT-SERVERS.NET. -+SECTION ADDITIONAL -+K.ROOT-SERVERS.NET. IN A 193.0.14.129 -+ENTRY_END -+ -+ENTRY_BEGIN -+MATCH opcode subdomain -+ADJUST copy_id copy_query -+REPLY QR NOERROR -+SECTION QUESTION -+com. IN NS -+SECTION AUTHORITY -+com. IN NS a.gtld-servers.net. -+SECTION ADDITIONAL -+a.gtld-servers.net. IN A 192.5.6.30 -+ENTRY_END -+RANGE_END -+ -+; a.gtld-servers.net. -+RANGE_BEGIN 0 100 -+ ADDRESS 192.5.6.30 -+ -+ENTRY_BEGIN -+MATCH opcode subdomain -+ADJUST copy_id copy_query -+REPLY QR NOERROR -+SECTION QUESTION -+example.com. IN NS -+SECTION AUTHORITY -+example.com. IN NS ns.example.com. -+SECTION ADDITIONAL -+ns.example.com. IN A 1.2.3.4 -+ENTRY_END -+RANGE_END -+ -+; ns.example.com. -+RANGE_BEGIN 50 52 -+ ADDRESS 1.2.3.4 -+ -+ENTRY_BEGIN -+MATCH opcode qtype qname -+ADJUST copy_id -+REPLY QR AA NOERROR -+SECTION QUESTION -+example.com. IN NS -+SECTION ANSWER -+example.com. IN NS ns.example.com. -+ENTRY_END -+ -+ENTRY_BEGIN -+MATCH opcode qtype qname -+ADJUST copy_id -+REPLY QR AA NOERROR -+SECTION QUESTION -+ns.example.com. IN A -+SECTION ANSWER -+ns.example.com. IN A 1.2.3.4 -+ENTRY_END -+ -+ENTRY_BEGIN -+MATCH opcode qtype qname -+ADJUST copy_id -+REPLY QR AA NOERROR -+SECTION QUESTION -+ns.example.com. IN AAAA -+SECTION ANSWER -+SECTION AUTHORITY -+example.com. IN SOA ns.example.com. host.example.com. 4 86400 3600 86400 3600 -+ENTRY_END -+ -+ENTRY_BEGIN -+MATCH opcode qtype qname ednsdata -+ADJUST copy_id -+REPLY QR AA NOERROR -+SECTION QUESTION -+www.example.com. IN A -+SECTION ANSWER -+SECTION ADDITIONAL -+ ; Match this subnet option -+ HEX_EDNSDATA_BEGIN -+ ; client is 127.0.0.1 -+ 00 08 ; OPC -+ 00 07 ; option length -+ 00 01 ; Family -+ 11 00 ; source mask, scopemask -+ 7f 00 00 ; address -+ HEX_EDNSDATA_END -+ ; This is the response, without the subnet option -+ HEX_ANSWER_BEGIN; -+ 00 00 84 00 00 01 00 01 ; ID 0 QR AA NOERROR -+ 00 00 00 01 03 77 77 77 ; www.example.com A (DO) -+ 07 65 78 61 6d 70 6c 65 -+ 03 63 6f 6d 00 00 01 00 -+ 01 -+ C0 0C 00 01 00 01 00 00 0E 10 ; www.example.com. A IN 3600 -+ 00 04 0A 14 1E 2C ; rdata 10.20.30.44 -+ 00 00 29 10 00 00 00 -+ 80 00 00 00 -+ HEX_ANSWER_END -+ENTRY_END -+ -+ENTRY_BEGIN -+MATCH opcode qtype qname ednsdata -+ADJUST copy_id -+REPLY QR AA NOERROR -+SECTION QUESTION -+www.example.com. IN A -+SECTION ANSWER -+SECTION ADDITIONAL -+ ; Match this subnet option -+ HEX_EDNSDATA_BEGIN -+ ; client is 127.2.0.1 -+ 00 08 ; OPC -+ 00 07 ; option length -+ 00 01 ; Family -+ 11 00 ; source mask, scopemask -+ 7f 02 00 ; address -+ HEX_EDNSDATA_END -+ ; This is the response, without the subnet option -+ HEX_ANSWER_BEGIN; -+ 00 00 84 00 00 01 00 01 ; ID 0 QR AA NOERROR -+ 00 00 00 01 03 77 77 77 ; www.example.com A (DO) -+ 07 65 78 61 6d 70 6c 65 -+ 03 63 6f 6d 00 00 01 00 -+ 01 -+ C0 0C 00 01 00 01 00 00 0E 10 ; www.example.com. A IN 3600 -+ 00 04 0A 14 1E 2C ; rdata 10.20.30.44 -+ 00 00 29 10 00 00 00 -+ 80 00 00 00 -+ HEX_ANSWER_END -+ENTRY_END -+ -+ENTRY_BEGIN -+MATCH opcode qtype qname ednsdata -+ADJUST copy_id -+REPLY QR AA NOERROR -+SECTION QUESTION -+www.example.com. IN A -+SECTION ANSWER -+SECTION ADDITIONAL -+ ; Match this subnet option -+ HEX_EDNSDATA_BEGIN -+ ; client is 127.3.0.1 -+ 00 08 ; OPC -+ 00 07 ; option length -+ 00 01 ; Family -+ 11 00 ; source mask, scopemask -+ 7f 03 00 ; address -+ HEX_EDNSDATA_END -+ ; This is the response, without the subnet option -+ HEX_ANSWER_BEGIN; -+ 00 00 84 00 00 01 00 01 ; ID 0 QR AA NOERROR -+ 00 00 00 01 03 77 77 77 ; www.example.com A (DO) -+ 07 65 78 61 6d 70 6c 65 -+ 03 63 6f 6d 00 00 01 00 -+ 01 -+ C0 0C 00 01 00 01 00 00 0E 10 ; www.example.com. A IN 3600 -+ 00 04 0A 14 1E 2C ; rdata 10.20.30.44 -+ 00 00 29 10 00 00 00 -+ 80 00 00 00 -+ HEX_ANSWER_END -+ENTRY_END -+ -+; The answer for a query without subnet -+;ENTRY_BEGIN -+;MATCH opcode qtype qname -+;ADJUST copy_id -+;REPLY QR AA NOERROR -+;SECTION QUESTION -+;www.example.com. IN A -+;SECTION ANSWER -+;www.example.com. IN A 10.20.30.40 -+;ENTRY_END -+RANGE_END -+ -+; ns.example.com. -+RANGE_BEGIN 53 57 -+ ADDRESS 1.2.3.4 -+; The answer for a query without subnet -+ENTRY_BEGIN -+MATCH opcode qtype qname -+ADJUST copy_id -+REPLY QR AA NOERROR -+SECTION QUESTION -+www.example.com. IN A -+SECTION ANSWER -+www.example.com. IN A 10.20.30.40 -+ENTRY_END -+RANGE_END -+ -+STEP 10 QUERY -+ENTRY_BEGIN -+ HEX_ANSWER_BEGIN; -+ 00 00 01 00 00 01 00 00 ; ID 0 -+ 00 00 00 01 03 77 77 77 ; www.example.com A? (DO) -+ 07 65 78 61 6d 70 6c 65 -+ 03 63 6f 6d 00 00 01 00 -+ 01 00 00 29 10 00 00 00 -+ 80 00 00 0b -+ -+ 00 08 00 07 ; OPC, optlen -+ 00 01 11 00 ; ip4, scope 17, source 0 -+ 7f 00 00 ; 127.0.0.0/17 -+ HEX_ANSWER_END -+ENTRY_END -+ -+STEP 20 QUERY -+ENTRY_BEGIN -+ HEX_ANSWER_BEGIN; -+ 00 00 01 00 00 01 00 00 ; ID 0 -+ 00 00 00 01 03 77 77 77 ; www.example.com A? (DO) -+ 07 65 78 61 6d 70 6c 65 -+ 03 63 6f 6d 00 00 01 00 -+ 01 00 00 29 10 00 00 00 -+ 80 00 00 0b -+ -+ 00 08 00 07 ; OPC, optlen -+ 00 01 11 00 ; ip4, scope 17, source 0 -+ 7f 02 00 ; 127.2.0.0/17 -+ HEX_ANSWER_END -+ENTRY_END -+ -+STEP 30 QUERY -+ENTRY_BEGIN -+ HEX_ANSWER_BEGIN; -+ 00 00 01 00 00 01 00 00 ; ID 0 -+ 00 00 00 01 03 77 77 77 ; www.example.com A? (DO) -+ 07 65 78 61 6d 70 6c 65 -+ 03 63 6f 6d 00 00 01 00 -+ 01 00 00 29 10 00 00 00 -+ 80 00 00 0b -+ -+ 00 08 00 07 ; OPC, optlen -+ 00 01 11 00 ; ip4, scope 17, source 0 -+ 7f 03 00 ; 127.3.0.0/17 -+ HEX_ANSWER_END -+ENTRY_END -+ -+; recursion happens here. -+; The upstream server RANGE starts responding at STEP 50. -+STEP 50 TRAFFIC -+ -+; The upstream server now responds for the nonsubnet response. -+STEP 55 TRAFFIC -+ -+STEP 60 CHECK_ANSWER -+ENTRY_BEGIN -+MATCH all ednsdata -+REPLY QR RD RA DO NOERROR -+SECTION QUESTION -+www.example.com. IN A -+SECTION ANSWER -+www.example.com. IN A 10.20.30.40 -+;www.example.com. IN A 10.20.30.44 -+SECTION ADDITIONAL -+; HEX_EDNSDATA_BEGIN -+; ; client is 127.3.0.1 -+; 00 08 ; OPC -+; 00 07 ; option length -+; 00 01 ; Family -+; 11 00 ; source mask, scopemask -+; 7f 03 00 ; address -+; HEX_EDNSDATA_END -+ENTRY_END -+ -+STEP 70 CHECK_ANSWER -+ENTRY_BEGIN -+MATCH all ednsdata -+REPLY QR RD RA DO NOERROR -+SECTION QUESTION -+www.example.com. IN A -+SECTION ANSWER -+www.example.com. IN A 10.20.30.40 -+;www.example.com. IN A 10.20.30.44 -+SECTION ADDITIONAL -+; HEX_EDNSDATA_BEGIN -+; ; client is 127.2.0.1 -+; 00 08 ; OPC -+; 00 07 ; option length -+; 00 01 ; Family -+; 11 00 ; source mask, scopemask -+; 7f 02 00 ; address -+; HEX_EDNSDATA_END -+ENTRY_END -+ -+STEP 80 CHECK_ANSWER -+ENTRY_BEGIN -+MATCH all ednsdata -+REPLY QR RD RA DO NOERROR -+SECTION QUESTION -+www.example.com. IN A -+SECTION ANSWER -+www.example.com. IN A 10.20.30.40 -+;www.example.com. IN A 10.20.30.44 -+SECTION ADDITIONAL -+; HEX_EDNSDATA_BEGIN -+; ; client is 127.0.0.1 -+; 00 08 ; OPC -+; 00 07 ; option length -+; 00 01 ; Family -+; 11 00 ; source mask, scopemask -+; 7f 00 00 ; address -+; HEX_EDNSDATA_END -+ENTRY_END -+ -+SCENARIO_END -diff --git a/unbound-1.16.2/testdata/subnet_noecs_refused.rpl b/unbound-1.16.2/testdata/subnet_noecs_refused.rpl -new file mode 100644 -index 0000000..39fbe85 ---- /dev/null -+++ b/unbound-1.16.2/testdata/subnet_noecs_refused.rpl -@@ -0,0 +1,159 @@ -+# config -+server: -+ send-client-subnet: 1.2.3.4 -+ max-client-subnet-ipv4: 17 -+ module-config: "subnetcache iterator" -+ qname-minimisation: no -+ minimal-responses: yes -+ target-fetch-policy: "0 0 0 0 0" -+ -+stub-zone: -+ name: "." -+ stub-addr: 193.0.14.129 # K.ROOT-SERVERS.NET. -+CONFIG_END -+ -+SCENARIO_BEGIN Test subnet with no edns subnet support but it is refused -+; The query is sent to a server that does not reply with the edns-subnet -+; option. The upstream server sends rcode refused. That results in a -+; NULL return_msg. -+ -+; K.ROOT-SERVERS.NET. -+RANGE_BEGIN 0 100 -+ ADDRESS 193.0.14.129 -+ENTRY_BEGIN -+MATCH opcode qtype qname -+ADJUST copy_id -+REPLY QR NOERROR -+SECTION QUESTION -+. IN NS -+SECTION ANSWER -+. IN NS K.ROOT-SERVERS.NET. -+SECTION ADDITIONAL -+K.ROOT-SERVERS.NET. IN A 193.0.14.129 -+ENTRY_END -+ -+ENTRY_BEGIN -+MATCH opcode subdomain -+ADJUST copy_id copy_query -+REPLY QR NOERROR -+SECTION QUESTION -+com. IN NS -+SECTION AUTHORITY -+com. IN NS a.gtld-servers.net. -+SECTION ADDITIONAL -+a.gtld-servers.net. IN A 192.5.6.30 -+ENTRY_END -+RANGE_END -+ -+; a.gtld-servers.net. -+RANGE_BEGIN 0 100 -+ ADDRESS 192.5.6.30 -+ -+ENTRY_BEGIN -+MATCH opcode subdomain -+ADJUST copy_id copy_query -+REPLY QR NOERROR -+SECTION QUESTION -+example.com. IN NS -+SECTION AUTHORITY -+example.com. IN NS ns.example.com. -+SECTION ADDITIONAL -+ns.example.com. IN A 1.2.3.4 -+ENTRY_END -+RANGE_END -+ -+; ns.example.com. -+RANGE_BEGIN 0 100 -+ ADDRESS 1.2.3.4 -+ -+ENTRY_BEGIN -+MATCH opcode qtype qname -+ADJUST copy_id -+REPLY QR AA NOERROR -+SECTION QUESTION -+example.com. IN NS -+SECTION ANSWER -+example.com. IN NS ns.example.com. -+ENTRY_END -+ -+ENTRY_BEGIN -+MATCH opcode qtype qname -+ADJUST copy_id -+REPLY QR AA NOERROR -+SECTION QUESTION -+ns.example.com. IN A -+SECTION ANSWER -+ns.example.com. IN A 1.2.3.4 -+ENTRY_END -+ -+ENTRY_BEGIN -+MATCH opcode qtype qname -+ADJUST copy_id -+REPLY QR AA NOERROR -+SECTION QUESTION -+ns.example.com. IN AAAA -+SECTION ANSWER -+SECTION AUTHORITY -+example.com. IN SOA ns.example.com. host.example.com. 4 86400 3600 86400 3600 -+ENTRY_END -+ -+; This matches the no EDNS subnet info queries that are made for the -+; fallback without subnet. The answer is refused. -+ENTRY_BEGIN -+MATCH opcode qtype qname ednsdata -+ADJUST copy_id -+REPLY QR AA REFUSED -+SECTION QUESTION -+www.example.com. IN A -+SECTION ANSWER -+;www.example.com. IN A 10.20.30.40 -+ENTRY_END -+ -+; This matches the initial query with edns subnet in the query, -+; the answer has no edns subnet in the reply. -+ENTRY_BEGIN -+MATCH opcode qtype qname -+ADJUST copy_id -+REPLY QR AA NOERROR -+SECTION QUESTION -+www.example.com. IN A -+SECTION ANSWER -+www.example.com. IN A 10.20.30.40 -+ENTRY_END -+RANGE_END -+ -+STEP 1 QUERY -+;ENTRY_BEGIN -+;REPLY RD DO -+;SECTION QUESTION -+;www.example.com. IN A -+; but send this query with subnet scope zero in the query, because that -+; makes the reply possibly get stored in the cache. -+; -+; query with subnet 0.0.0.0/0. -+ENTRY_BEGIN -+HEX_ANSWER_BEGIN -+ 00 00 01 00 00 01 00 00 ;ID 0 -+ 00 00 00 01 03 77 77 77 ; www.example.com A? (DO) -+ 07 65 78 61 6d 70 6c 65 -+ 03 63 6f 6d 00 00 01 00 -+ 01 00 00 29 10 00 00 00 -+ 80 00 00 08 -+ -+ 00 08 00 04 ; OPC, optlen -+ 00 01 00 00 ; ip4, scope 0, source 0 -+ ;0.0.0.0/0 -+HEX_ANSWER_END -+ENTRY_END -+ -+; recursion happens here. -+STEP 10 CHECK_ANSWER -+ENTRY_BEGIN -+MATCH all ednsdata -+REPLY QR RD RA DO SERVFAIL -+SECTION QUESTION -+www.example.com. IN A -+SECTION ANSWER -+;www.example.com. IN A 10.20.30.40 -+ENTRY_END -+SCENARIO_END -diff --git a/unbound-1.16.2/testdata/subnet_noecs_support.rpl b/unbound-1.16.2/testdata/subnet_noecs_support.rpl -new file mode 100644 -index 0000000..0c9826c ---- /dev/null -+++ b/unbound-1.16.2/testdata/subnet_noecs_support.rpl -@@ -0,0 +1,127 @@ -+# config -+server: -+ send-client-subnet: 1.2.3.4 -+ max-client-subnet-ipv4: 17 -+ module-config: "subnetcache iterator" -+ qname-minimisation: no -+ minimal-responses: yes -+ target-fetch-policy: "0 0 0 0 0" -+ -+stub-zone: -+ name: "." -+ stub-addr: 193.0.14.129 # K.ROOT-SERVERS.NET. -+CONFIG_END -+ -+SCENARIO_BEGIN Test subnet with no edns subnet support from the server -+; The query is sent to a server that does not reply with the edns-subnet -+; option. -+ -+; K.ROOT-SERVERS.NET. -+RANGE_BEGIN 0 100 -+ ADDRESS 193.0.14.129 -+ENTRY_BEGIN -+MATCH opcode qtype qname -+ADJUST copy_id -+REPLY QR NOERROR -+SECTION QUESTION -+. IN NS -+SECTION ANSWER -+. IN NS K.ROOT-SERVERS.NET. -+SECTION ADDITIONAL -+K.ROOT-SERVERS.NET. IN A 193.0.14.129 -+ENTRY_END -+ -+ENTRY_BEGIN -+MATCH opcode subdomain -+ADJUST copy_id copy_query -+REPLY QR NOERROR -+SECTION QUESTION -+com. IN NS -+SECTION AUTHORITY -+com. IN NS a.gtld-servers.net. -+SECTION ADDITIONAL -+a.gtld-servers.net. IN A 192.5.6.30 -+ENTRY_END -+RANGE_END -+ -+; a.gtld-servers.net. -+RANGE_BEGIN 0 100 -+ ADDRESS 192.5.6.30 -+ -+ENTRY_BEGIN -+MATCH opcode subdomain -+ADJUST copy_id copy_query -+REPLY QR NOERROR -+SECTION QUESTION -+example.com. IN NS -+SECTION AUTHORITY -+example.com. IN NS ns.example.com. -+SECTION ADDITIONAL -+ns.example.com. IN A 1.2.3.4 -+ENTRY_END -+RANGE_END -+ -+; ns.example.com. -+RANGE_BEGIN 0 100 -+ ADDRESS 1.2.3.4 -+ -+ENTRY_BEGIN -+MATCH opcode qtype qname -+ADJUST copy_id -+REPLY QR AA NOERROR -+SECTION QUESTION -+example.com. IN NS -+SECTION ANSWER -+example.com. IN NS ns.example.com. -+ENTRY_END -+ -+ENTRY_BEGIN -+MATCH opcode qtype qname -+ADJUST copy_id -+REPLY QR AA NOERROR -+SECTION QUESTION -+ns.example.com. IN A -+SECTION ANSWER -+ns.example.com. IN A 1.2.3.4 -+ENTRY_END -+ -+ENTRY_BEGIN -+MATCH opcode qtype qname -+ADJUST copy_id -+REPLY QR AA NOERROR -+SECTION QUESTION -+ns.example.com. IN AAAA -+SECTION ANSWER -+SECTION AUTHORITY -+example.com. IN SOA ns.example.com. host.example.com. 4 86400 3600 86400 3600 -+ENTRY_END -+ -+ENTRY_BEGIN -+MATCH opcode qtype qname -+ADJUST copy_id -+REPLY QR AA NOERROR -+SECTION QUESTION -+www.example.com. IN A -+SECTION ANSWER -+www.example.com. IN A 10.20.30.40 -+ENTRY_END -+RANGE_END -+ -+STEP 1 QUERY -+ENTRY_BEGIN -+REPLY RD DO -+SECTION QUESTION -+www.example.com. IN A -+ENTRY_END -+ -+; recursion happens here. -+STEP 10 CHECK_ANSWER -+ENTRY_BEGIN -+MATCH all ednsdata -+REPLY QR RD RA DO NOERROR -+SECTION QUESTION -+www.example.com. IN A -+SECTION ANSWER -+www.example.com. IN A 10.20.30.40 -+ENTRY_END -+SCENARIO_END -diff --git a/unbound-1.16.2/testdata/subnet_prezero.rpl b/unbound-1.16.2/testdata/subnet_prezero.rpl -new file mode 100644 -index 0000000..22cdfff ---- /dev/null -+++ b/unbound-1.16.2/testdata/subnet_prezero.rpl -@@ -0,0 +1,155 @@ -+; subnet unit test -+server: -+ trust-anchor-signaling: no -+ send-client-subnet: 1.2.3.4 -+ send-client-subnet: 1.2.3.5 -+ target-fetch-policy: "0 0 0 0 0" -+ module-config: "subnetcache validator iterator" -+ qname-minimisation: no -+ minimal-responses: no -+ -+stub-zone: -+ name: "example.com" -+ stub-addr: 1.2.3.4 -+CONFIG_END -+ -+SCENARIO_BEGIN Test subnetcache source prefix zero from client. -+; In RFC7871 section-7.1.2 (para. 2). -+; It says that the recursor must send no EDNS subnet or its own address -+; in the EDNS subnet to the upstream server. And use that answer for the -+; source prefix length zero query. That type of query is for privacy. -+; The authority server is then going to use the resolver's IP, if any, to -+; tailor the answer to the query source address. -+ -+; ns.example.com -+RANGE_BEGIN 0 100 -+ ADDRESS 1.2.3.4 -+ -+; reply with 0.0.0.0/0 in reply -+; For the test the answers for 0.0.0.0/0 queries are SERVFAIL, the normal -+; answers are NOERROR. -+ENTRY_BEGIN -+MATCH opcode qtype qname ednsdata -+ADJUST copy_id -+REPLY QR AA DO SERVFAIL -+SECTION QUESTION -+www.example.com. IN A -+SECTION ANSWER -+www.example.com. IN CNAME star.c10r.example.com. -+SECTION ADDITIONAL -+HEX_EDNSDATA_BEGIN -+ 00 08 00 04 ; OPCODE=subnet, optlen -+ 00 01 00 00 ; ip4, scope 0, source 0 -+ ; 0.0.0.0/0 -+HEX_EDNSDATA_END -+ENTRY_END -+ -+; reply without subnet -+ENTRY_BEGIN -+MATCH opcode qtype qname -+ADJUST copy_id -+REPLY QR AA DO NOERROR -+SECTION QUESTION -+www.example.com. IN A -+SECTION ANSWER -+www.example.com. IN CNAME star.c10r.example.com. -+ENTRY_END -+ -+; delegation answer for c10r.example.com, with subnet /0 -+ENTRY_BEGIN -+MATCH opcode subdomain ednsdata -+ADJUST copy_id copy_query -+REPLY QR DO SERVFAIL -+SECTION QUESTION -+c10r.example.com. IN NS -+SECTION AUTHORITY -+c10r.example.com. IN NS ns.c10r.example.com. -+SECTION ADDITIONAL -+ns.c10r.example.com. IN A 1.2.3.5 -+HEX_EDNSDATA_BEGIN -+ 00 08 00 04 ; OPCODE=subnet, optlen -+ 00 01 00 00 ; ip4, scope 0, source 0 -+ ; 0.0.0.0/0 -+HEX_EDNSDATA_END -+ENTRY_END -+ -+; delegation answer for c10r.example.com, without subnet -+ENTRY_BEGIN -+MATCH opcode subdomain -+ADJUST copy_id copy_query -+REPLY QR DO NOERROR -+SECTION QUESTION -+c10r.example.com. IN NS -+SECTION AUTHORITY -+c10r.example.com. IN NS ns.c10r.example.com. -+SECTION ADDITIONAL -+ns.c10r.example.com. IN A 1.2.3.5 -+ENTRY_END -+RANGE_END -+ -+; ns.c10r.example.com -+RANGE_BEGIN 0 100 -+ ADDRESS 1.2.3.5 -+ -+; reply with 0.0.0.0/0 in reply -+ENTRY_BEGIN -+MATCH opcode qtype qname ednsdata -+ADJUST copy_id -+REPLY QR AA DO SERVFAIL -+SECTION QUESTION -+star.c10r.example.com. IN A -+SECTION ANSWER -+star.c10r.example.com. IN A 1.2.3.6 -+SECTION ADDITIONAL -+HEX_EDNSDATA_BEGIN -+ 00 08 00 04 ; OPCODE=subnet, optlen -+ 00 01 00 00 ; ip4, scope 0, source 0 -+ ; 0.0.0.0/0 -+HEX_EDNSDATA_END -+ENTRY_END -+ -+; reply without subnet -+ENTRY_BEGIN -+MATCH opcode qtype qname -+ADJUST copy_id -+REPLY QR AA DO NOERROR -+SECTION QUESTION -+star.c10r.example.com. IN A -+SECTION ANSWER -+star.c10r.example.com. IN A 1.2.3.6 -+ENTRY_END -+RANGE_END -+ -+; ask for www.example.com -+; server answers with CNAME to a delegation, that then -+; returns a /24 answer. -+STEP 1 QUERY -+ENTRY_BEGIN -+REPLY RD DO -+SECTION QUESTION -+www.example.com. IN A -+SECTION ADDITIONAL -+HEX_EDNSDATA_BEGIN -+ 00 08 00 04 ; OPCODE=subnet, optlen -+ 00 01 00 00 ; ip4, scope 0, source 0 -+ ; 0.0.0.0/0 -+HEX_EDNSDATA_END -+ENTRY_END -+ -+STEP 10 CHECK_ANSWER -+ENTRY_BEGIN -+MATCH all ednsdata -+REPLY QR RD RA DO NOERROR -+SECTION QUESTION -+www.example.com. IN A -+SECTION ANSWER -+www.example.com. IN CNAME star.c10r.example.com. -+star.c10r.example.com. IN A 1.2.3.6 -+SECTION ADDITIONAL -+HEX_EDNSDATA_BEGIN -+ 00 08 00 04 ; OPCODE=subnet, optlen -+ 00 01 00 00 ; ip4, scope 0, source 0 -+ ; 0.0.0.0/0 -+HEX_EDNSDATA_END -+ENTRY_END -+SCENARIO_END -diff --git a/unbound-1.16.2/testdata/subnet_scopezero.rpl b/unbound-1.16.2/testdata/subnet_scopezero.rpl -new file mode 100644 -index 0000000..e006514 ---- /dev/null -+++ b/unbound-1.16.2/testdata/subnet_scopezero.rpl -@@ -0,0 +1,439 @@ -+; scope of 0, if the query also had scope of 0, do not answer this -+; to everyone, but only for scope 0 queries. Otherwise can answer cached. -+ -+server: -+ target-fetch-policy: "0 0 0 0 0" -+ send-client-subnet: 1.2.3.4 -+ module-config: "subnetcache validator iterator" -+ verbosity: 4 -+ qname-minimisation: no -+ -+stub-zone: -+ name: "." -+ stub-addr: 193.0.14.129 -+ -+stub-zone: -+ name: "example.com" -+ stub-addr: 1.2.3.4 -+CONFIG_END -+ -+SCENARIO_BEGIN Test subnet cache with scope zero queries and responses. -+ -+; the upstream server. -+RANGE_BEGIN 0 100 -+ ADDRESS 193.0.14.129 -+ -+ENTRY_BEGIN -+MATCH opcode qtype qname ednsdata -+ADJUST copy_id -+REPLY QR NOERROR -+SECTION QUESTION -+. IN NS -+SECTION ANSWER -+. IN NS K.ROOT-SERVERS.NET. -+SECTION ADDITIONAL -+HEX_EDNSDATA_BEGIN -+ ;; we expect to receive empty -+HEX_EDNSDATA_END -+K.ROOT-SERVERS.NET. IN A 193.0.14.129 -+ENTRY_END -+RANGE_END -+ -+RANGE_BEGIN 0 11 -+ ADDRESS 1.2.3.4 -+ENTRY_BEGIN -+MATCH opcode qtype qname -+ADJUST copy_id -+;copy_ednsdata_assume_clientsubnet -+REPLY QR NOERROR -+SECTION QUESTION -+www.example.com. IN A -+SECTION ANSWER -+www.example.com. IN A 10.20.30.40 -+SECTION AUTHORITY -+SECTION ADDITIONAL -+HEX_EDNSDATA_BEGIN -+ ; client is 127.0.0.1 -+ 00 08 ; OPC -+ 00 07 ; option length -+ 00 01 ; Family -+ 18 11 ; source mask, scopemask -+ 7f 00 00 ; address -+HEX_EDNSDATA_END -+ENTRY_END -+RANGE_END -+ -+RANGE_BEGIN 20 31 -+ ADDRESS 1.2.3.4 -+ENTRY_BEGIN -+MATCH opcode qtype qname -+ADJUST copy_id -+;copy_ednsdata_assume_clientsubnet -+REPLY QR NOERROR -+SECTION QUESTION -+www.example.com. IN A -+SECTION ANSWER -+www.example.com. IN A 10.20.30.41 -+SECTION AUTHORITY -+SECTION ADDITIONAL -+HEX_EDNSDATA_BEGIN -+ ; client is 127.0.0.1 -+ 00 08 ; OPC -+ 00 07 ; option length -+ 00 01 ; Family -+ 18 11 ; source mask, scopemask -+ 7f 01 00 ; address -+HEX_EDNSDATA_END -+ENTRY_END -+RANGE_END -+ -+RANGE_BEGIN 40 51 -+ ADDRESS 1.2.3.4 -+ENTRY_BEGIN -+MATCH opcode qtype qname -+ADJUST copy_id -+;copy_ednsdata_assume_clientsubnet -+REPLY QR NOERROR -+SECTION QUESTION -+www.example.com. IN A -+SECTION ANSWER -+www.example.com. IN A 10.20.30.42 -+SECTION AUTHORITY -+SECTION ADDITIONAL -+HEX_EDNSDATA_BEGIN -+ 00 08 ; OPC -+ 00 04 ; option length -+ 00 01 ; Family -+ 00 00 ; source mask, scopemask -+ ; address 0.0.0.0/0 scope 0 -+HEX_EDNSDATA_END -+ENTRY_END -+RANGE_END -+ -+RANGE_BEGIN 120 131 -+ ADDRESS 1.2.3.4 -+ENTRY_BEGIN -+MATCH opcode qtype qname -+ADJUST copy_id -+;copy_ednsdata_assume_clientsubnet -+REPLY QR NOERROR -+SECTION QUESTION -+www.example.com. IN A -+SECTION ANSWER -+www.example.com. IN A 10.20.30.43 -+SECTION AUTHORITY -+SECTION ADDITIONAL -+HEX_EDNSDATA_BEGIN -+ 00 08 ; OPC -+ 00 07 ; option length -+ 00 01 ; Family -+ 18 00 ; source mask, scopemask -+ 7f 02 00 ; address 127.2.0.0/24 scope 0 -+HEX_EDNSDATA_END -+ENTRY_END -+RANGE_END -+ -+; query for 127.0.0.0/24 -+STEP 1 QUERY -+ENTRY_BEGIN -+HEX_ANSWER_BEGIN -+ 00 00 01 00 00 01 00 00 ;ID 0 -+ 00 00 00 01 03 77 77 77 ; www.example.com A? (DO) -+ 07 65 78 61 6d 70 6c 65 -+ 03 63 6f 6d 00 00 01 00 -+ 01 00 00 29 10 00 00 00 -+ 80 00 00 0b -+ -+ 00 08 00 07 ; OPC, optlen -+ 00 01 18 00 ; ip4, scope 24, source 0 -+ 7f 00 00 ;127.0.0.0/24 -+HEX_ANSWER_END -+ENTRY_END -+ -+; answer is 10.20.30.40 for 127.0.0.0/24 scope 17 -+STEP 10 CHECK_ANSWER -+ENTRY_BEGIN -+MATCH all ednsdata -+REPLY QR RD RA NOERROR -+SECTION QUESTION -+www.example.com. IN A -+SECTION ANSWER -+www.example.com. IN A 10.20.30.40 -+SECTION AUTHORITY -+SECTION ADDITIONAL -+HEX_EDNSDATA_BEGIN -+ ; client is 127.0.0.1 -+ 00 08 ; OPC -+ 00 07 ; option length -+ 00 01 ; Family -+ 18 11 ; source mask, scopemask -+ 7f 00 00 ; address -+HEX_EDNSDATA_END -+ENTRY_END -+ -+; query for 127.1.0.0/24 -+STEP 20 QUERY -+ENTRY_BEGIN -+HEX_ANSWER_BEGIN -+ 00 00 01 00 00 01 00 00 ;ID 0 -+ 00 00 00 01 03 77 77 77 ; www.example.com A? (DO) -+ 07 65 78 61 6d 70 6c 65 -+ 03 63 6f 6d 00 00 01 00 -+ 01 00 00 29 10 00 00 00 -+ 80 00 00 0b -+ -+ 00 08 00 07 ; OPC, optlen -+ 00 01 18 00 ; ip4, scope 24, source 0 -+ 7f 01 00 ;127.1.0.0/24 -+HEX_ANSWER_END -+ENTRY_END -+ -+; answer is 10.20.30.41 for 127.1.0.0/24 scope 17 -+STEP 30 CHECK_ANSWER -+ENTRY_BEGIN -+MATCH all ednsdata -+REPLY QR RD RA NOERROR -+SECTION QUESTION -+www.example.com. IN A -+SECTION ANSWER -+www.example.com. IN A 10.20.30.41 -+SECTION AUTHORITY -+SECTION ADDITIONAL -+HEX_EDNSDATA_BEGIN -+ ; client is 127.1.0.1 -+ 00 08 ; OPC -+ 00 07 ; option length -+ 00 01 ; Family -+ 18 11 ; source mask, scopemask -+ 7f 01 00 ; address -+HEX_EDNSDATA_END -+ENTRY_END -+ -+; query for 0.0.0.0/0 -+STEP 40 QUERY -+ENTRY_BEGIN -+HEX_ANSWER_BEGIN -+ 00 00 01 00 00 01 00 00 ;ID 0 -+ 00 00 00 01 03 77 77 77 ; www.example.com A? (DO) -+ 07 65 78 61 6d 70 6c 65 -+ 03 63 6f 6d 00 00 01 00 -+ 01 00 00 29 10 00 00 00 -+ 80 00 00 08 -+ -+ 00 08 00 04 ; OPC, optlen -+ 00 01 00 00 ; ip4, scope 0, source 0 -+ ;0.0.0.0/0 -+HEX_ANSWER_END -+ENTRY_END -+ -+; answer is 10.20.30.42 for 0.0.0.0/0 scope 0 -+STEP 50 CHECK_ANSWER -+ENTRY_BEGIN -+MATCH all ednsdata -+REPLY QR RD RA NOERROR -+SECTION QUESTION -+www.example.com. IN A -+SECTION ANSWER -+www.example.com. IN A 10.20.30.42 -+SECTION AUTHORITY -+SECTION ADDITIONAL -+HEX_EDNSDATA_BEGIN -+ 00 08 ; OPC -+ 00 04 ; option length -+ 00 01 ; Family -+ 00 00 ; source mask, scopemask -+ ; address -+HEX_EDNSDATA_END -+ENTRY_END -+ -+; query for 127.0.0.0/24, again, it should be in cache. -+; and not from the scope 0 answer. -+STEP 60 QUERY -+ENTRY_BEGIN -+HEX_ANSWER_BEGIN -+ 00 00 01 00 00 01 00 00 ;ID 0 -+ 00 00 00 01 03 77 77 77 ; www.example.com A? (DO) -+ 07 65 78 61 6d 70 6c 65 -+ 03 63 6f 6d 00 00 01 00 -+ 01 00 00 29 10 00 00 00 -+ 80 00 00 0b -+ -+ 00 08 00 07 ; OPC, optlen -+ 00 01 18 00 ; ip4, scope 24, source 0 -+ 7f 00 00 ;127.0.0.0/24 -+HEX_ANSWER_END -+ENTRY_END -+ -+; answer should be 10.20.30.40 for 127.0.0.0/24 scope 17 -+STEP 70 CHECK_ANSWER -+ENTRY_BEGIN -+MATCH all ednsdata -+REPLY QR RD RA NOERROR -+SECTION QUESTION -+www.example.com. IN A -+SECTION ANSWER -+www.example.com. IN A 10.20.30.40 -+SECTION AUTHORITY -+SECTION ADDITIONAL -+HEX_EDNSDATA_BEGIN -+ ; client is 127.0.0.1 -+ 00 08 ; OPC -+ 00 07 ; option length -+ 00 01 ; Family -+ 18 11 ; source mask, scopemask -+ 7f 00 00 ; address -+HEX_EDNSDATA_END -+ENTRY_END -+ -+; query for 127.1.0.0/24, again, it should be in cache. -+STEP 80 QUERY -+ENTRY_BEGIN -+HEX_ANSWER_BEGIN -+ 00 00 01 00 00 01 00 00 ;ID 0 -+ 00 00 00 01 03 77 77 77 ; www.example.com A? (DO) -+ 07 65 78 61 6d 70 6c 65 -+ 03 63 6f 6d 00 00 01 00 -+ 01 00 00 29 10 00 00 00 -+ 80 00 00 0b -+ -+ 00 08 00 07 ; OPC, optlen -+ 00 01 18 00 ; ip4, scope 24, source 0 -+ 7f 01 00 ;127.1.0.0/24 -+HEX_ANSWER_END -+ENTRY_END -+ -+; answer should be 10.20.30.41 for 127.1.0.0/24 scope 17 -+STEP 90 CHECK_ANSWER -+ENTRY_BEGIN -+MATCH all ednsdata -+REPLY QR RD RA NOERROR -+SECTION QUESTION -+www.example.com. IN A -+SECTION ANSWER -+www.example.com. IN A 10.20.30.41 -+SECTION AUTHORITY -+SECTION ADDITIONAL -+HEX_EDNSDATA_BEGIN -+ ; client is 127.1.0.1 -+ 00 08 ; OPC -+ 00 07 ; option length -+ 00 01 ; Family -+ 18 11 ; source mask, scopemask -+ 7f 01 00 ; address -+HEX_EDNSDATA_END -+ENTRY_END -+ -+; query for 0.0.0.0/0, again. -+STEP 100 QUERY -+ENTRY_BEGIN -+HEX_ANSWER_BEGIN -+ 00 00 01 00 00 01 00 00 ;ID 0 -+ 00 00 00 01 03 77 77 77 ; www.example.com A? (DO) -+ 07 65 78 61 6d 70 6c 65 -+ 03 63 6f 6d 00 00 01 00 -+ 01 00 00 29 10 00 00 00 -+ 80 00 00 08 -+ -+ 00 08 00 04 ; OPC, optlen -+ 00 01 00 00 ; ip4, scope 0, source 0 -+ ;0.0.0.0/0 -+HEX_ANSWER_END -+ENTRY_END -+ -+; answer should be 10.20.30.42 for 0.0.0.0/0 scope 0 -+STEP 110 CHECK_ANSWER -+ENTRY_BEGIN -+MATCH all ednsdata -+REPLY QR RD RA NOERROR -+SECTION QUESTION -+www.example.com. IN A -+SECTION ANSWER -+www.example.com. IN A 10.20.30.42 -+SECTION AUTHORITY -+SECTION ADDITIONAL -+HEX_EDNSDATA_BEGIN -+ 00 08 ; OPC -+ 00 04 ; option length -+ 00 01 ; Family -+ 00 00 ; source mask, scopemask -+ ; address -+HEX_EDNSDATA_END -+ENTRY_END -+ -+; now a query for a /24 that gets an answer for a /0. -+STEP 120 QUERY -+ENTRY_BEGIN -+HEX_ANSWER_BEGIN -+ 00 00 01 00 00 01 00 00 ;ID 0 -+ 00 00 00 01 03 77 77 77 ; www.example.com A? (DO) -+ 07 65 78 61 6d 70 6c 65 -+ 03 63 6f 6d 00 00 01 00 -+ 01 00 00 29 10 00 00 00 -+ 80 00 00 0b -+ -+ 00 08 00 07 ; OPC, optlen -+ 00 01 18 00 ; ip4, scope 24, source 0 -+ 7f 02 00 ;127.2.0.0/24 -+HEX_ANSWER_END -+ENTRY_END -+ -+; answer should be 10.20.30.43 for 127.2.0.0/24 scope 0 -+STEP 130 CHECK_ANSWER -+ENTRY_BEGIN -+MATCH all ednsdata -+REPLY QR RD RA NOERROR -+SECTION QUESTION -+www.example.com. IN A -+SECTION ANSWER -+www.example.com. IN A 10.20.30.43 -+SECTION AUTHORITY -+SECTION ADDITIONAL -+HEX_EDNSDATA_BEGIN -+ ; client is 127.2.0.1 -+ 00 08 ; OPC -+ 00 07 ; option length -+ 00 01 ; Family -+ 18 00 ; source mask, scopemask -+ 7f 02 00 ; address -+HEX_EDNSDATA_END -+ENTRY_END -+ -+; the scope 0 answer is now used to answer queries from -+; query for 127.0.0.0/24 -+STEP 140 QUERY -+ENTRY_BEGIN -+HEX_ANSWER_BEGIN -+ 00 00 01 00 00 01 00 00 ;ID 0 -+ 00 00 00 01 03 77 77 77 ; www.example.com A? (DO) -+ 07 65 78 61 6d 70 6c 65 -+ 03 63 6f 6d 00 00 01 00 -+ 01 00 00 29 10 00 00 00 -+ 80 00 00 0b -+ -+ 00 08 00 07 ; OPC, optlen -+ 00 01 18 00 ; ip4, scope 24, source 0 -+ 7f 00 00 ;127.0.0.0/24 -+HEX_ANSWER_END -+ENTRY_END -+ -+STEP 150 CHECK_ANSWER -+ENTRY_BEGIN -+MATCH all ednsdata -+REPLY QR RD RA NOERROR -+SECTION QUESTION -+www.example.com. IN A -+SECTION ANSWER -+www.example.com. IN A 10.20.30.43 -+SECTION AUTHORITY -+SECTION ADDITIONAL -+HEX_EDNSDATA_BEGIN -+ ; client is 127.0.0.1 -+ 00 08 ; OPC -+ 00 07 ; option length -+ 00 01 ; Family -+ 18 00 ; source mask, scopemask -+ 7f 00 00 ; address -+HEX_EDNSDATA_END -+ENTRY_END -+ -+SCENARIO_END -diff --git a/unbound-1.16.2/testdata/subnet_scopezero_noedns.rpl b/unbound-1.16.2/testdata/subnet_scopezero_noedns.rpl -new file mode 100644 -index 0000000..25df0dd ---- /dev/null -+++ b/unbound-1.16.2/testdata/subnet_scopezero_noedns.rpl -@@ -0,0 +1,441 @@ -+; scope of 0, if the query also had scope of 0, do not answer this -+; to everyone, but only for scope 0 queries. Otherwise can answer cached. -+ -+server: -+ target-fetch-policy: "0 0 0 0 0" -+ send-client-subnet: 1.2.3.4 -+ module-config: "subnetcache validator iterator" -+ verbosity: 4 -+ qname-minimisation: no -+ -+stub-zone: -+ name: "." -+ stub-addr: 193.0.14.129 -+ -+stub-zone: -+ name: "example.com" -+ stub-addr: 1.2.3.4 -+CONFIG_END -+ -+SCENARIO_BEGIN Test subnet cache with scope zero response without EDNS. -+ -+; the upstream server. -+RANGE_BEGIN 0 100 -+ ADDRESS 193.0.14.129 -+ -+ENTRY_BEGIN -+MATCH opcode qtype qname ednsdata -+ADJUST copy_id -+REPLY QR NOERROR -+SECTION QUESTION -+. IN NS -+SECTION ANSWER -+. IN NS K.ROOT-SERVERS.NET. -+SECTION ADDITIONAL -+HEX_EDNSDATA_BEGIN -+ ;; we expect to receive empty -+HEX_EDNSDATA_END -+K.ROOT-SERVERS.NET. IN A 193.0.14.129 -+ENTRY_END -+RANGE_END -+ -+RANGE_BEGIN 0 11 -+ ADDRESS 1.2.3.4 -+ENTRY_BEGIN -+MATCH opcode qtype qname -+ADJUST copy_id -+;copy_ednsdata_assume_clientsubnet -+REPLY QR NOERROR -+SECTION QUESTION -+www.example.com. IN A -+SECTION ANSWER -+www.example.com. IN A 10.20.30.40 -+SECTION AUTHORITY -+SECTION ADDITIONAL -+HEX_EDNSDATA_BEGIN -+ ; client is 127.0.0.1 -+ 00 08 ; OPC -+ 00 07 ; option length -+ 00 01 ; Family -+ 18 11 ; source mask, scopemask -+ 7f 00 00 ; address -+HEX_EDNSDATA_END -+ENTRY_END -+RANGE_END -+ -+RANGE_BEGIN 20 31 -+ ADDRESS 1.2.3.4 -+ENTRY_BEGIN -+MATCH opcode qtype qname -+ADJUST copy_id -+;copy_ednsdata_assume_clientsubnet -+REPLY QR NOERROR -+SECTION QUESTION -+www.example.com. IN A -+SECTION ANSWER -+www.example.com. IN A 10.20.30.41 -+SECTION AUTHORITY -+SECTION ADDITIONAL -+HEX_EDNSDATA_BEGIN -+ ; client is 127.0.0.1 -+ 00 08 ; OPC -+ 00 07 ; option length -+ 00 01 ; Family -+ 18 11 ; source mask, scopemask -+ 7f 01 00 ; address -+HEX_EDNSDATA_END -+ENTRY_END -+RANGE_END -+ -+RANGE_BEGIN 40 51 -+ ADDRESS 1.2.3.4 -+ENTRY_BEGIN -+MATCH opcode qtype qname -+ADJUST copy_id -+;copy_ednsdata_assume_clientsubnet -+REPLY QR NOERROR -+SECTION QUESTION -+www.example.com. IN A -+SECTION ANSWER -+www.example.com. IN A 10.20.30.42 -+SECTION AUTHORITY -+SECTION ADDITIONAL -+;no EDNS in this answer. Tests if the back_parsed callback -+;is called to process the lack of edns contents. -+;HEX_EDNSDATA_BEGIN -+ ;00 08 ; OPC -+ ;00 04 ; option length -+ ;00 01 ; Family -+ ;00 00 ; source mask, scopemask -+ ; ; address 0.0.0.0/0 scope 0 -+;HEX_EDNSDATA_END -+ENTRY_END -+RANGE_END -+ -+RANGE_BEGIN 120 131 -+ ADDRESS 1.2.3.4 -+ENTRY_BEGIN -+MATCH opcode qtype qname -+ADJUST copy_id -+;copy_ednsdata_assume_clientsubnet -+REPLY QR NOERROR -+SECTION QUESTION -+www.example.com. IN A -+SECTION ANSWER -+www.example.com. IN A 10.20.30.43 -+SECTION AUTHORITY -+SECTION ADDITIONAL -+HEX_EDNSDATA_BEGIN -+ 00 08 ; OPC -+ 00 07 ; option length -+ 00 01 ; Family -+ 18 00 ; source mask, scopemask -+ 7f 02 00 ; address 127.2.0.0/24 scope 0 -+HEX_EDNSDATA_END -+ENTRY_END -+RANGE_END -+ -+; query for 127.0.0.0/24 -+STEP 1 QUERY -+ENTRY_BEGIN -+HEX_ANSWER_BEGIN -+ 00 00 01 00 00 01 00 00 ;ID 0 -+ 00 00 00 01 03 77 77 77 ; www.example.com A? (DO) -+ 07 65 78 61 6d 70 6c 65 -+ 03 63 6f 6d 00 00 01 00 -+ 01 00 00 29 10 00 00 00 -+ 80 00 00 0b -+ -+ 00 08 00 07 ; OPC, optlen -+ 00 01 18 00 ; ip4, scope 24, source 0 -+ 7f 00 00 ;127.0.0.0/24 -+HEX_ANSWER_END -+ENTRY_END -+ -+; answer is 10.20.30.40 for 127.0.0.0/24 scope 17 -+STEP 10 CHECK_ANSWER -+ENTRY_BEGIN -+MATCH all ednsdata -+REPLY QR RD RA NOERROR -+SECTION QUESTION -+www.example.com. IN A -+SECTION ANSWER -+www.example.com. IN A 10.20.30.40 -+SECTION AUTHORITY -+SECTION ADDITIONAL -+HEX_EDNSDATA_BEGIN -+ ; client is 127.0.0.1 -+ 00 08 ; OPC -+ 00 07 ; option length -+ 00 01 ; Family -+ 18 11 ; source mask, scopemask -+ 7f 00 00 ; address -+HEX_EDNSDATA_END -+ENTRY_END -+ -+; query for 127.1.0.0/24 -+STEP 20 QUERY -+ENTRY_BEGIN -+HEX_ANSWER_BEGIN -+ 00 00 01 00 00 01 00 00 ;ID 0 -+ 00 00 00 01 03 77 77 77 ; www.example.com A? (DO) -+ 07 65 78 61 6d 70 6c 65 -+ 03 63 6f 6d 00 00 01 00 -+ 01 00 00 29 10 00 00 00 -+ 80 00 00 0b -+ -+ 00 08 00 07 ; OPC, optlen -+ 00 01 18 00 ; ip4, scope 24, source 0 -+ 7f 01 00 ;127.1.0.0/24 -+HEX_ANSWER_END -+ENTRY_END -+ -+; answer is 10.20.30.41 for 127.1.0.0/24 scope 17 -+STEP 30 CHECK_ANSWER -+ENTRY_BEGIN -+MATCH all ednsdata -+REPLY QR RD RA NOERROR -+SECTION QUESTION -+www.example.com. IN A -+SECTION ANSWER -+www.example.com. IN A 10.20.30.41 -+SECTION AUTHORITY -+SECTION ADDITIONAL -+HEX_EDNSDATA_BEGIN -+ ; client is 127.1.0.1 -+ 00 08 ; OPC -+ 00 07 ; option length -+ 00 01 ; Family -+ 18 11 ; source mask, scopemask -+ 7f 01 00 ; address -+HEX_EDNSDATA_END -+ENTRY_END -+ -+; query for 0.0.0.0/0 -+STEP 40 QUERY -+ENTRY_BEGIN -+HEX_ANSWER_BEGIN -+ 00 00 01 00 00 01 00 00 ;ID 0 -+ 00 00 00 01 03 77 77 77 ; www.example.com A? (DO) -+ 07 65 78 61 6d 70 6c 65 -+ 03 63 6f 6d 00 00 01 00 -+ 01 00 00 29 10 00 00 00 -+ 80 00 00 08 -+ -+ 00 08 00 04 ; OPC, optlen -+ 00 01 00 00 ; ip4, scope 0, source 0 -+ ;0.0.0.0/0 -+HEX_ANSWER_END -+ENTRY_END -+ -+; answer is 10.20.30.42 for 0.0.0.0/0 scope 0 -+STEP 50 CHECK_ANSWER -+ENTRY_BEGIN -+MATCH all ednsdata -+REPLY QR RD RA NOERROR -+SECTION QUESTION -+www.example.com. IN A -+SECTION ANSWER -+www.example.com. IN A 10.20.30.42 -+SECTION AUTHORITY -+SECTION ADDITIONAL -+HEX_EDNSDATA_BEGIN -+ 00 08 ; OPC -+ 00 04 ; option length -+ 00 01 ; Family -+ 00 00 ; source mask, scopemask -+ ; address -+HEX_EDNSDATA_END -+ENTRY_END -+ -+; query for 127.0.0.0/24, again, it should be in cache. -+; and not from the scope 0 answer. -+STEP 60 QUERY -+ENTRY_BEGIN -+HEX_ANSWER_BEGIN -+ 00 00 01 00 00 01 00 00 ;ID 0 -+ 00 00 00 01 03 77 77 77 ; www.example.com A? (DO) -+ 07 65 78 61 6d 70 6c 65 -+ 03 63 6f 6d 00 00 01 00 -+ 01 00 00 29 10 00 00 00 -+ 80 00 00 0b -+ -+ 00 08 00 07 ; OPC, optlen -+ 00 01 18 00 ; ip4, scope 24, source 0 -+ 7f 00 00 ;127.0.0.0/24 -+HEX_ANSWER_END -+ENTRY_END -+ -+; answer should be 10.20.30.40 for 127.0.0.0/24 scope 17 -+STEP 70 CHECK_ANSWER -+ENTRY_BEGIN -+MATCH all ednsdata -+REPLY QR RD RA NOERROR -+SECTION QUESTION -+www.example.com. IN A -+SECTION ANSWER -+www.example.com. IN A 10.20.30.40 -+SECTION AUTHORITY -+SECTION ADDITIONAL -+HEX_EDNSDATA_BEGIN -+ ; client is 127.0.0.1 -+ 00 08 ; OPC -+ 00 07 ; option length -+ 00 01 ; Family -+ 18 11 ; source mask, scopemask -+ 7f 00 00 ; address -+HEX_EDNSDATA_END -+ENTRY_END -+ -+; query for 127.1.0.0/24, again, it should be in cache. -+STEP 80 QUERY -+ENTRY_BEGIN -+HEX_ANSWER_BEGIN -+ 00 00 01 00 00 01 00 00 ;ID 0 -+ 00 00 00 01 03 77 77 77 ; www.example.com A? (DO) -+ 07 65 78 61 6d 70 6c 65 -+ 03 63 6f 6d 00 00 01 00 -+ 01 00 00 29 10 00 00 00 -+ 80 00 00 0b -+ -+ 00 08 00 07 ; OPC, optlen -+ 00 01 18 00 ; ip4, scope 24, source 0 -+ 7f 01 00 ;127.1.0.0/24 -+HEX_ANSWER_END -+ENTRY_END -+ -+; answer should be 10.20.30.41 for 127.1.0.0/24 scope 17 -+STEP 90 CHECK_ANSWER -+ENTRY_BEGIN -+MATCH all ednsdata -+REPLY QR RD RA NOERROR -+SECTION QUESTION -+www.example.com. IN A -+SECTION ANSWER -+www.example.com. IN A 10.20.30.41 -+SECTION AUTHORITY -+SECTION ADDITIONAL -+HEX_EDNSDATA_BEGIN -+ ; client is 127.1.0.1 -+ 00 08 ; OPC -+ 00 07 ; option length -+ 00 01 ; Family -+ 18 11 ; source mask, scopemask -+ 7f 01 00 ; address -+HEX_EDNSDATA_END -+ENTRY_END -+ -+; query for 0.0.0.0/0, again. -+STEP 100 QUERY -+ENTRY_BEGIN -+HEX_ANSWER_BEGIN -+ 00 00 01 00 00 01 00 00 ;ID 0 -+ 00 00 00 01 03 77 77 77 ; www.example.com A? (DO) -+ 07 65 78 61 6d 70 6c 65 -+ 03 63 6f 6d 00 00 01 00 -+ 01 00 00 29 10 00 00 00 -+ 80 00 00 08 -+ -+ 00 08 00 04 ; OPC, optlen -+ 00 01 00 00 ; ip4, scope 0, source 0 -+ ;0.0.0.0/0 -+HEX_ANSWER_END -+ENTRY_END -+ -+; answer should be 10.20.30.42 for 0.0.0.0/0 scope 0 -+STEP 110 CHECK_ANSWER -+ENTRY_BEGIN -+MATCH all ednsdata -+REPLY QR RD RA NOERROR -+SECTION QUESTION -+www.example.com. IN A -+SECTION ANSWER -+www.example.com. IN A 10.20.30.42 -+SECTION AUTHORITY -+SECTION ADDITIONAL -+HEX_EDNSDATA_BEGIN -+ 00 08 ; OPC -+ 00 04 ; option length -+ 00 01 ; Family -+ 00 00 ; source mask, scopemask -+ ; address -+HEX_EDNSDATA_END -+ENTRY_END -+ -+; now a query for a /24 that gets an answer for a /0. -+STEP 120 QUERY -+ENTRY_BEGIN -+HEX_ANSWER_BEGIN -+ 00 00 01 00 00 01 00 00 ;ID 0 -+ 00 00 00 01 03 77 77 77 ; www.example.com A? (DO) -+ 07 65 78 61 6d 70 6c 65 -+ 03 63 6f 6d 00 00 01 00 -+ 01 00 00 29 10 00 00 00 -+ 80 00 00 0b -+ -+ 00 08 00 07 ; OPC, optlen -+ 00 01 18 00 ; ip4, scope 24, source 0 -+ 7f 02 00 ;127.2.0.0/24 -+HEX_ANSWER_END -+ENTRY_END -+ -+; answer should be 10.20.30.43 for 127.2.0.0/24 scope 0 -+STEP 130 CHECK_ANSWER -+ENTRY_BEGIN -+MATCH all ednsdata -+REPLY QR RD RA NOERROR -+SECTION QUESTION -+www.example.com. IN A -+SECTION ANSWER -+www.example.com. IN A 10.20.30.43 -+SECTION AUTHORITY -+SECTION ADDITIONAL -+HEX_EDNSDATA_BEGIN -+ ; client is 127.2.0.1 -+ 00 08 ; OPC -+ 00 07 ; option length -+ 00 01 ; Family -+ 18 00 ; source mask, scopemask -+ 7f 02 00 ; address -+HEX_EDNSDATA_END -+ENTRY_END -+ -+; the scope 0 answer is now used to answer queries from -+; query for 127.0.0.0/24 -+STEP 140 QUERY -+ENTRY_BEGIN -+HEX_ANSWER_BEGIN -+ 00 00 01 00 00 01 00 00 ;ID 0 -+ 00 00 00 01 03 77 77 77 ; www.example.com A? (DO) -+ 07 65 78 61 6d 70 6c 65 -+ 03 63 6f 6d 00 00 01 00 -+ 01 00 00 29 10 00 00 00 -+ 80 00 00 0b -+ -+ 00 08 00 07 ; OPC, optlen -+ 00 01 18 00 ; ip4, scope 24, source 0 -+ 7f 00 00 ;127.0.0.0/24 -+HEX_ANSWER_END -+ENTRY_END -+ -+STEP 150 CHECK_ANSWER -+ENTRY_BEGIN -+MATCH all ednsdata -+REPLY QR RD RA NOERROR -+SECTION QUESTION -+www.example.com. IN A -+SECTION ANSWER -+www.example.com. IN A 10.20.30.43 -+SECTION AUTHORITY -+SECTION ADDITIONAL -+HEX_EDNSDATA_BEGIN -+ ; client is 127.0.0.1 -+ 00 08 ; OPC -+ 00 07 ; option length -+ 00 01 ; Family -+ 18 00 ; source mask, scopemask -+ 7f 00 00 ; address -+HEX_EDNSDATA_END -+ENTRY_END -+ -+SCENARIO_END diff --git a/unbound-fedora-config.patch b/unbound-fedora-config.patch new file mode 100644 index 0000000..6fc5649 --- /dev/null +++ b/unbound-fedora-config.patch @@ -0,0 +1,113 @@ +From a8be97bada623287cdd911c7a4549bbceef9fea0 Mon Sep 17 00:00:00 2001 +From: Tomas Korbar +Date: Tue, 4 Feb 2025 09:48:12 +0100 +Subject: [PATCH] Customize unbound.conf for Fedora defaults + +Set some Fedora/RHEL specific changes to example configuration file. By +patching upstream provided config file we would not need to manually +update external copy in source RPM. +--- + unbound-1.24.2/doc/example.conf.in | 33 ++++++++++++++++++++++++++++-- + 1 file changed, 31 insertions(+), 2 deletions(-) + +diff --git a/unbound-1.24.2/doc/example.conf.in b/unbound-1.24.2/doc/example.conf.in +index fda565c..cf10b85 100644 +--- a/unbound-1.24.2/doc/example.conf.in ++++ b/unbound-1.24.2/doc/example.conf.in +@@ -51,11 +51,19 @@ server: + # specify 0.0.0.0 and ::0 to bind to all available interfaces. + # specify every interface[@port] on a new 'interface:' labelled line. + # The listen interfaces are not changed on reload, only on restart. ++ # interface: 0.0.0.0 ++ # interface: ::0 + # interface: 192.0.2.153 + # interface: 192.0.2.154 + # interface: 192.0.2.154@5003 + # interface: 2001:DB8::5 + # interface: eth0@5003 ++ # ++ # for dns over tls and raw dns over port 80 ++ # interface: 0.0.0.0@443 ++ # interface: ::0@443 ++ # interface: 0.0.0.0@80 ++ # interface: ::0@80 + + # enable this feature to copy the source address of queries to reply. + # Socket options are not supported on all platforms. experimental. +@@ -295,6 +303,8 @@ server: + # nat64-prefix: 64:ff9b::0/96 + + # Enable UDP, "yes" or "no". ++ # NOTE: if setting up an Unbound on tls443 for public use, you might want to ++ # disable UDP to avoid being used in DNS amplification attacks. + # do-udp: yes + + # Enable TCP, "yes" or "no". +@@ -330,6 +340,9 @@ server: + # can be dropped. Default is 0, disabled. In seconds, such as 3. + # sock-queue-timeout: 0 + ++ # Fedora note: do not activate this - not compiled in because ++ # it causes frequent unbound crashes. Also, socket activation ++ # is bad when you have things like dnsmasq also running with libvirt. + # Use systemd socket activation for UDP, TCP, and control sockets. + # use-systemd: no + +@@ -919,6 +932,8 @@ server: + # you need to do the reverse notation yourself. + # local-data-ptr: "192.0.2.3 www.example.com" + ++ include: /etc/unbound/local.d/*.conf ++ + # tag a localzone with a list of tag names (in "" with spaces between) + # local-zone-tag: "example.com" "tag2 tag3" + +@@ -929,8 +944,8 @@ server: + # the TLS stream, and over HTTPS using HTTP/2 as specified in RFC8484. + # Give the certificate to use and private key. + # default is "" (disabled). requires restart to take effect. +- # tls-service-key: "path/to/privatekeyfile.key" +- # tls-service-pem: "path/to/publiccertfile.pem" ++ # tls-service-key: "/etc/unbound/unbound_server.key" ++ # tls-service-pem: "/etc/unbound/unbound_server.pem" + # tls-port: 853 + # https-port: 443 + # quic-port: 853 +@@ -1184,6 +1199,12 @@ remote-control: + # unbound-control certificate file. + # control-cert-file: "@UNBOUND_RUN_DIR@/unbound_control.pem" + ++# Default Fedora settings ++include: "@UNBOUND_SHARE_DIR@/fedora-defaults.conf" ++ ++# Stub and Forward zones ++include: "@sysconfdir@/unbound/conf.d/*.conf" ++ + # Stub zones. + # Create entries like below, to make all queries for 'example.com' and + # 'example.org' go to the given list of nameservers. list zero or more +@@ -1204,6 +1225,10 @@ remote-control: + # name: "example.org" + # stub-host: ns.example.com. + ++# You can now also dynamically create and delete stub-zone's using ++# unbound-control stub_add domain.com 1.2.3.4 5.6.7.8 ++# unbound-control stub_remove domain.com 1.2.3.4 5.6.7.8 ++ + # Forward zones + # Create entries like below, to make all queries for 'example.com' and + # 'example.org' go to the given list of servers. These servers have to handle +@@ -1221,6 +1246,10 @@ remote-control: + # forward-zone: + # name: "example.org" + # forward-host: fwd.example.com ++# ++# You can now also dynamically create and delete forward-zone's using ++# unbound-control forward_add domain.com 1.2.3.4 5.6.7.8 ++# unbound-control forward_remove domain.com 1.2.3.4 5.6.7.8 + + # Authority zones + # The data for these zones is kept locally, from a file or downloaded. +-- +2.52.0 + diff --git a/unbound.spec b/unbound.spec index a717cc5..3f541d7 100644 --- a/unbound.spec +++ b/unbound.spec @@ -29,8 +29,8 @@ Summary: Validating, recursive, and caching DNS(SEC) resolver Name: unbound -Version: 1.16.2 -Release: 24%{?extra_version:.%{extra_version}}%{?dist} +Version: 1.24.2 +Release: 1%{?extra_version:.%{extra_version}}%{?dist} License: BSD Url: https://nlnetlabs.nl/projects/unbound/ Source: https://nlnetlabs.nl/downloads/%{name}/%{name}-%{version}%{?extra_version}.tar.gz @@ -60,31 +60,14 @@ Source25: unbound.sysusers Source26: unbound-as112-networks.conf Source27: tmpfiles-unbound-libs.conf -# https://github.com/NLnetLabs/unbound/commit/137719522a8ea5b380fbb6206d2466f402f5b554 -Patch1: unbound-1.16-CVE-2022-3204.patch -# https://nlnetlabs.nl/downloads/unbound/patch_CVE-2023-50387_CVE-2023-50868.diff -Patch4: unbound-1.16-CVE-2023-50387-CVE-2023-50868.patch -# https://github.com/NLnetLabs/unbound/commit/6d1e61173 -Patch5: unbound-1.16-control-t-flag.patch -# https://github.com/NLnetLabs/unbound/commit/b7c61d7cc256d6a174e6179622c7fa968272c259 -Patch6: unbound-1.21-CVE-2024-8508.patch -# https://github.com/NLnetLabs/unbound/commit/b48958c983f60af40358cca168c403e57bde30d2 -Patch7: unbound-1.16-control-key-perms.patch -# The patch for CVE-2025-5994 requires certain changes fixing bugs in subnet module -# that is why we have to backport these commits. They have their respective tests -# backported with them. -# https://github.com/NLnetLabs/unbound/commit/0f08cc6d5577ad4747749c55229e16df8711ee32 -# https://github.com/NLnetLabs/unbound/commit/6d0812b56731af130e8bc7e1572388934beb9b3b -# https://github.com/NLnetLabs/unbound/commit/be626f7c5330dc414a582a04b537ea79d5c452fb -# https://github.com/NLnetLabs/unbound/commit/5bf82f246481098a6473f296b21fc1229d276c0f -# https://github.com/NLnetLabs/unbound/commit/a1150078f29e14b36c8e4d9d05a263a5e6abbc5b -Patch8: unbound-1.23.1-CVE-2025-5994.patch +# Downstream configuration changes +Patch1: unbound-fedora-config.patch BuildRequires: gcc, make BuildRequires: flex, openssl-devel BuildRequires: libevent-devel expat-devel BuildRequires: pkgconfig -%if 0%{?fedora} +%if 0%{?fedora} || 0%{?rhel} >= 10 BuildRequires: gnupg2 %endif %if 0%{with_python2} @@ -192,7 +175,7 @@ Unbound dracut module allowing use of Unbound for name resolution in initramfs. %prep -%if 0%{?fedora} +%if 0%{?fedora} || 0%{?rhel} >= 10 %gpgverify -k 19 -s 18 -d 0 %endif %global pkgname %{name}-%{version}%{?extra_version} @@ -213,7 +196,7 @@ pushd %{pkgname} %autopatch -p2 # only for snapshots -autoreconf -iv +autoreconf -fiv # copy common doc files - after here, since it may be patched cp -pr doc pythonmod libunbound ../