diff --git a/backport-001-CVE-2024-43168.patch b/backport-001-CVE-2024-43168.patch deleted file mode 100644 index 02dbd7a2d9e32be18e08034965263f3231d2fc5a..0000000000000000000000000000000000000000 --- a/backport-001-CVE-2024-43168.patch +++ /dev/null @@ -1,28 +0,0 @@ -From 193401e7543a1e561dd634a3eaae932fa462a2b9 Mon Sep 17 00:00:00 2001 -From: zhailiangliang -Date: Wed, 3 Apr 2024 15:40:58 +0800 -Subject: [PATCH] fix heap-buffer-overflow issue in function cfg_mark_ports of - file util/config_file.c - ---- - util/config_file.c | 4 ++++ - 1 file changed, 4 insertions(+) - -diff --git a/util/config_file.c b/util/config_file.c -index 26185da0..e7b2f195 100644 ---- a/util/config_file.c -+++ b/util/config_file.c -@@ -1761,6 +1761,10 @@ cfg_mark_ports(const char* str, int allow, int* avail, int num) - #endif - if(!mid) { - int port = atoi(str); -+ if(port < 0) { -+ log_err("Prevent out-of-bounds access to array avail"); -+ return 0; -+ } - if(port == 0 && strcmp(str, "0") != 0) { - log_err("cannot parse port number '%s'", str); - return 0; --- -2.33.0 - diff --git a/backport-002-CVE-2024-43168.patch b/backport-002-CVE-2024-43168.patch deleted file mode 100644 index 681c498f044100e2c0ca944fa0e6fd2b02ec7d0f..0000000000000000000000000000000000000000 --- a/backport-002-CVE-2024-43168.patch +++ /dev/null @@ -1,56 +0,0 @@ -From dfff8d23cf4145c58e5c1e99d4159d3a91a70ab7 Mon Sep 17 00:00:00 2001 -From: "W.C.A. Wijngaards" -Date: Wed, 3 Apr 2024 10:16:18 +0200 -Subject: [PATCH] - For #1040: adjust error text and disallow negative ports in - other parts of cfg_mark_ports. - ---- - util/config_file.c | 14 +++++++++++++- - 1 files changed, 13 insertions(+), 1 deletion(-) - -diff --git a/util/config_file.c b/util/config_file.c -index e7b2f195..74554286 100644 ---- a/util/config_file.c -+++ b/util/config_file.c -@@ -1762,7 +1762,7 @@ cfg_mark_ports(const char* str, int allow, int* avail, int num) - if(!mid) { - int port = atoi(str); - if(port < 0) { -- log_err("Prevent out-of-bounds access to array avail"); -+ log_err("port number is negative: %d", port); - return 0; - } - if(port == 0 && strcmp(str, "0") != 0) { -@@ -1774,6 +1774,10 @@ cfg_mark_ports(const char* str, int allow, int* avail, int num) - } else { - int i, low, high = atoi(mid+1); - char buf[16]; -+ if(high < 0) { -+ log_err("port number is negative: %d", high); -+ return 0; -+ } - if(high == 0 && strcmp(mid+1, "0") != 0) { - log_err("cannot parse port number '%s'", mid+1); - return 0; -@@ -1786,10 +1790,18 @@ cfg_mark_ports(const char* str, int allow, int* avail, int num) - memcpy(buf, str, (size_t)(mid-str)); - buf[mid-str] = 0; - low = atoi(buf); -+ if(low < 0) { -+ log_err("port number is negative: %d", low); -+ return 0; -+ } - if(low == 0 && strcmp(buf, "0") != 0) { - log_err("cannot parse port number '%s'", buf); - return 0; - } -+ if(high > num) { -+ /* Stop very high values from taking a long time. */ -+ high = num; -+ } - for(i=low; i<=high; i++) { - if(i < num) - avail[i] = (allow?i:0); --- -2.33.0 - diff --git a/backport-003-CVE-2024-43168.patch b/backport-003-CVE-2024-43168.patch deleted file mode 100644 index 3d863281252c49e79d1be3bcd2ee08f712852406..0000000000000000000000000000000000000000 --- a/backport-003-CVE-2024-43168.patch +++ /dev/null @@ -1,135 +0,0 @@ -From 4497e8a154f53cd5947a6ee5aa65cf99be57152e Mon Sep 17 00:00:00 2001 -From: zhailiangliang -Date: Tue, 7 May 2024 11:35:52 +0000 -Subject: [PATCH] Fix potential overflow bug while parsing port in function - cfg_mark_ports - ---- - util/config_file.c | 76 ++++++++++++++++++++++++++++++---------------- - 1 file changed, 50 insertions(+), 26 deletions(-) - -diff --git a/util/config_file.c b/util/config_file.c -index 2b67d4c1..4a3b7d77 100644 ---- a/util/config_file.c -+++ b/util/config_file.c -@@ -42,6 +42,7 @@ - #include "config.h" - #include - #include -+#include - #ifdef HAVE_TIME_H - #include - #endif -@@ -1772,6 +1773,38 @@ init_outgoing_availports(int* a, int num) - } - } - -+static int -+extract_port_from_str(const char* str, int max_port) { -+ char* endptr; -+ if (str == NULL || *str == '\0') { -+ log_err("str: '%s' is invalid", str); -+ return -1; -+ } -+ -+ long int value = strtol(str, &endptr, 10); -+ if ((endptr == str) || (*endptr != '\0')) { -+ log_err("cannot parse port number '%s'", str); -+ return -1; -+ } -+ -+ if (errno == ERANGE) { -+ log_err("overflow occurred when parsing '%s'", str); -+ return -1; -+ } -+ -+ if (value == 0 && strcmp(str, "0") != 0) { -+ log_err("cannot parse port number '%s'", str); -+ return -1; -+ } -+ -+ if (value < 0 || value >= max_port) { -+ log_err(" '%s' is out of bounds [0, %d)", str, max_port); -+ return -1; -+ } -+ -+ return (int)value; -+} -+ - int - cfg_mark_ports(const char* str, int allow, int* avail, int num) - { -@@ -1782,53 +1815,44 @@ cfg_mark_ports(const char* str, int allow, int* avail, int num) - "options"); - #endif - if(!mid) { -- int port = atoi(str); -- if(port < 0) { -- log_err("port number is negative: %d", port); -+ int port = extract_port_from_str(str, num); -+ if (port < 0) { -+ log_err("Failed to parse the port number"); - return 0; - } -- if(port == 0 && strcmp(str, "0") != 0) { -- log_err("cannot parse port number '%s'", str); -- return 0; -- } -- if(port < num) -- avail[port] = (allow?port:0); -+ avail[port] = (allow?port:0); - } else { -- int i, low, high = atoi(mid+1); - char buf[16]; -- if(high < 0) { -- log_err("port number is negative: %d", high); -- return 0; -- } -- if(high == 0 && strcmp(mid+1, "0") != 0) { -- log_err("cannot parse port number '%s'", mid+1); -+ int i, low; -+ int high = extract_port_from_str(mid+1, num); -+ if (high < 0) { -+ log_err("Failed to parse the port number"); - return 0; - } -+ - if( (int)(mid-str)+1 >= (int)sizeof(buf) ) { - log_err("cannot parse port number '%s'", str); - return 0; - } -+ - if(mid > str) - memcpy(buf, str, (size_t)(mid-str)); - buf[mid-str] = 0; -- low = atoi(buf); -- if(low < 0) { -- log_err("port number is negative: %d", low); -+ low = extract_port_from_str(buf, num); -+ if (low < 0) { -+ log_err("Failed to parse the port number"); - return 0; - } -- if(low == 0 && strcmp(buf, "0") != 0) { -- log_err("cannot parse port number '%s'", buf); -+ -+ if (low > high) { -+ log_err("Low value is greater than high value"); - return 0; - } -- if(high > num) { -- /* Stop very high values from taking a long time. */ -- high = num; -- } -+ - for(i=low; i<=high; i++) { - if(i < num) - avail[i] = (allow?i:0); - } -- return 1; - } - return 1; - } --- -2.33.0 - diff --git a/backport-004-CVE-2024-43168.patch b/backport-004-CVE-2024-43168.patch deleted file mode 100644 index 5d70552cc96730189650ee158e2dc86b664ecb77..0000000000000000000000000000000000000000 --- a/backport-004-CVE-2024-43168.patch +++ /dev/null @@ -1,44 +0,0 @@ -From c085a53268940dfbb907cbaa7a690740b6c8210c Mon Sep 17 00:00:00 2001 -From: "W.C.A. Wijngaards" -Date: Tue, 7 May 2024 14:05:21 +0200 -Subject: [PATCH] - Fix for #1062: declaration before statement, avoid print of - null, and redundant check for array size. And changelog note for merge of - #1062. - ---- - util/config_file.c | 8 +++++--- - 1 files changed, 6 insertions(+), 3 deletions(-) - -diff --git a/util/config_file.c b/util/config_file.c -index 4a3b7d77..2ac6c468 100644 ---- a/util/config_file.c -+++ b/util/config_file.c -@@ -1776,12 +1776,13 @@ init_outgoing_availports(int* a, int num) - static int - extract_port_from_str(const char* str, int max_port) { - char* endptr; -+ long int value; - if (str == NULL || *str == '\0') { -- log_err("str: '%s' is invalid", str); -+ log_err("str: '%s' is invalid", (str?str:"NULL")); - return -1; - } - -- long int value = strtol(str, &endptr, 10); -+ value = strtol(str, &endptr, 10); - if ((endptr == str) || (*endptr != '\0')) { - log_err("cannot parse port number '%s'", str); - return -1; -@@ -1820,7 +1821,8 @@ cfg_mark_ports(const char* str, int allow, int* avail, int num) - log_err("Failed to parse the port number"); - return 0; - } -- avail[port] = (allow?port:0); -+ if(port < num) -+ avail[port] = (allow?port:0); - } else { - char buf[16]; - int i, low; --- -2.33.0 - diff --git a/backport-CVE-2023-50387_CVE-2023-50868.patch b/backport-CVE-2023-50387_CVE-2023-50868.patch deleted file mode 100644 index 8e0ff38d0cc0a2b9fdfea5d83bdc2767f9b4c9fc..0000000000000000000000000000000000000000 --- a/backport-CVE-2023-50387_CVE-2023-50868.patch +++ /dev/null @@ -1,2329 +0,0 @@ -From 882903f2fa800c4cb6f5e225b728e2887bb7b9ae Mon Sep 17 00:00:00 2001 -From: "W.C.A. Wijngaards" -Date: Tue, 13 Feb 2024 13:02:08 +0100 -Subject: [PATCH] - Fix CVE-2023-50387, CVE-2023-50868 - -CVE-2023-50387: -DNSSEC verification complexity can be exploited to exhaust CPU resources and stall DNS resolvers. -CVE-2023-50868: -NSEC3 closest encloser proof can exhaust CPU. - ---- - services/authzone.c | 3 +- - services/cache/dns.c | 18 + - services/cache/dns.h | 9 + - testcode/unitverify.c | 7 +- - testdata/val_any.rpl | 3 + - testdata/val_any_dname.rpl | 3 + - testdata/val_nx_nsec3_collision.rpl | 3 + - util/fptr_wlist.c | 1 + - validator/val_nsec.c | 3 +- - validator/val_nsec3.c | 316 ++++++++++++++---- - validator/val_nsec3.h | 60 +++- - validator/val_sigcrypt.c | 37 +- - validator/val_sigcrypt.h | 3 +- - validator/val_utils.c | 22 +- - validator/val_utils.h | 4 +- - validator/validator.c | 500 ++++++++++++++++++++++++---- - validator/validator.h | 18 + - 17 files changed, 839 insertions(+), 171 deletions(-) - -diff --git a/services/authzone.c b/services/authzone.c -index 3898767c..4c63b2e0 100644 ---- a/services/authzone.c -+++ b/services/authzone.c -@@ -7767,6 +7767,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 " -@@ -7790,7 +7791,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/services/cache/dns.c b/services/cache/dns.c -index 6fc9919e..1ced1143 100644 ---- a/services/cache/dns.c -+++ b/services/cache/dns.c -@@ -703,6 +703,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/services/cache/dns.h b/services/cache/dns.h -index 147f992c..c2bf23c6 100644 ---- a/services/cache/dns.h -+++ b/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/testcode/unitverify.c b/testcode/unitverify.c -index ff069a1b..395b4c25 100644 ---- a/testcode/unitverify.c -+++ b/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/testdata/val_any.rpl b/testdata/val_any.rpl -index 4ce19513..90263af8 100644 ---- a/testdata/val_any.rpl -+++ b/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/testdata/val_any_dname.rpl b/testdata/val_any_dname.rpl -index 6ab3cded..dd65e97b 100644 ---- a/testdata/val_any_dname.rpl -+++ b/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/testdata/val_nx_nsec3_collision.rpl b/testdata/val_nx_nsec3_collision.rpl -index 8ff7e4b0..87a55f56 100644 ---- a/testdata/val_nx_nsec3_collision.rpl -+++ b/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/util/fptr_wlist.c b/util/fptr_wlist.c -index dc8ab669..00b73253 100644 ---- a/util/fptr_wlist.c -+++ b/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/validator/val_nsec.c b/validator/val_nsec.c -index 876bfab6..5871db90 100644 ---- a/validator/val_nsec.c -+++ b/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/validator/val_nsec3.c b/validator/val_nsec3.c -index a2b3794f..95d1e4d7 100644 ---- a/validator/val_nsec3.c -+++ b/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/validator/val_nsec3.h b/validator/val_nsec3.h -index 7676fc8b..8ca91293 100644 ---- a/validator/val_nsec3.h -+++ b/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/validator/val_sigcrypt.c b/validator/val_sigcrypt.c -index 5ab21e20..8600a682 100644 ---- a/validator/val_sigcrypt.c -+++ b/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/validator/val_sigcrypt.h b/validator/val_sigcrypt.h -index 7f52b71e..1a3d8fcb 100644 ---- a/validator/val_sigcrypt.h -+++ b/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/validator/val_utils.c b/validator/val_utils.c -index e2319ee2..cb37ea00 100644 ---- a/validator/val_utils.c -+++ b/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/validator/val_utils.h b/validator/val_utils.h -index 83e3d0ad..e8cdcefa 100644 ---- a/validator/val_utils.h -+++ b/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/validator/validator.c b/validator/validator.c -index 1723afef..f1f7be34 100644 ---- a/validator/validator.c -+++ b/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,14 @@ 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 +2961,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 +2982,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 +3253,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 +3286,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/validator/validator.h b/validator/validator.h -index 694e4c89..72f44b16 100644 ---- a/validator/validator.h -+++ b/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/backport-CVE-2024-33655.patch b/backport-CVE-2024-33655.patch deleted file mode 100644 index 92387ce4bce3df3fb04a5cb56d2c6ad0d8dffc84..0000000000000000000000000000000000000000 --- a/backport-CVE-2024-33655.patch +++ /dev/null @@ -1,798 +0,0 @@ -From c3206f4568f60c486be6d165b1f2b5b254fea3de Mon Sep 17 00:00:00 2001 -From: "W.C.A. Wijngaards" -Date: Wed, 1 May 2024 10:10:58 +0200 -Subject: [PATCH] - Fix for the DNSBomb vulnerability CVE-2024-33655. Thanks to - Xiang Li from the Network and Information Security Lab of Tsinghua - University for reporting it. - ---- - doc/Changelog | 5 + - doc/example.conf.in | 15 ++ - doc/unbound.conf.5.in | 30 ++++ - services/cache/infra.c | 170 +++++++++++++++++- - services/cache/infra.h | 28 +++ - services/mesh.c | 65 +++++++ - testdata/cachedb_expired_client_timeout.crpl | 1 + - testdata/cachedb_subnet_expired.crpl | 1 + - .../doh_downstream.tdir/doh_downstream.conf | 1 + - .../doh_downstream_notls.conf | 1 + - .../doh_downstream_post.conf | 1 + - .../fwd_three_service.conf | 1 + - testdata/iter_ghost_timewindow.rpl | 1 + - .../ssl_req_order.tdir/ssl_req_order.conf | 1 + - .../tcp_req_order.tdir/tcp_req_order.conf | 1 + - testdata/tcp_sigpipe.tdir/tcp_sigpipe.conf | 3 +- - util/config_file.c | 15 ++ - util/config_file.h | 15 ++ - util/configlexer.lex | 5 + - util/configparser.y | 55 ++++++ - 20 files changed, 412 insertions(+), 3 deletions(-) - -diff --git a/doc/Changelog b/doc/Changelog -index dab04633d..24fbb42c7 100644 ---- a/doc/Changelog -+++ b/doc/Changelog -@@ -1,3 +1,8 @@ -+1 May 2024: Wouter -+ - Fix for the DNSBomb vulnerability CVE-2024-33655. Thanks to Xiang Li -+ from the Network and Information Security Lab of Tsinghua University -+ for reporting it. -+ - 5 January 2023: Wouter - - Tag for 1.17.1 release. - -diff --git a/doc/example.conf.in b/doc/example.conf.in -index ea211e97d..e0aec8ec7 100644 ---- a/doc/example.conf.in -+++ b/doc/example.conf.in -@@ -191,6 +191,21 @@ server: - # are behind a slow satellite link, to eg. 1128. - # unknown-server-time-limit: 376 - -+ # msec before recursion replies are dropped. The work item continues. -+ # discard-timeout: 1900 -+ -+ # Max number of replies waiting for recursion per IP address. -+ # wait-limit: 1000 -+ -+ # Max replies waiting for recursion for IP address with cookie. -+ # wait-limit-cookie: 10000 -+ -+ # Apart from the default, the wait limit can be set for a netblock. -+ # wait-limit-netblock: 192.0.2.0/24 50000 -+ -+ # Apart from the default, the wait limit with cookie can be adjusted. -+ # wait-limit-cookie-netblock: 192.0.2.0/24 50000 -+ - # the amount of memory to use for the RRset cache. - # plain value in bytes or you can append k, m or G. default is "4Mb". - # rrset-cache-size: 4m -diff --git a/doc/unbound.conf.5.in b/doc/unbound.conf.5.in -index 167f4a5de..6d310c116 100644 ---- a/doc/unbound.conf.5.in -+++ b/doc/unbound.conf.5.in -@@ -302,6 +302,36 @@ Increase this if you are behind a slow satellite link, to eg. 1128. - That would then avoid re\-querying every initial query because it times out. - Default is 376 msec. - .TP -+.B discard\-timeout: \fI -+The wait time in msec where recursion requests are dropped. This is -+to stop a large number of replies from accumulating. They receive -+no reply, the work item continues to recurse. It is nice to be a bit -+larger than serve\-expired\-client\-timeout if that is enabled. -+A value of 1900 msec is suggested. The value 0 disables it. -+Default 1900 msec. -+.TP -+.B wait\-limit: \fI -+The number of replies that can wait for recursion, for an IP address. -+This makes a ratelimit per IP address of waiting replies for recursion. -+It stops very large amounts of queries waiting to be returned to one -+destination. The value 0 disables wait limits. Default is 1000. -+.TP -+.B wait\-limit\-cookie: \fI -+The number of replies that can wait for recursion, for an IP address -+that sent the query with a valid DNS cookie. Since the cookie validates -+the client address, the limit can be higher. Default is 10000. -+.TP -+.B wait\-limit\-netblock: \fI -+The wait limit for the netblock. If not given the wait\-limit value is -+used. The most specific netblock is used to determine the limit. Useful for -+overriding the default for a specific, group or individual, server. -+The value -1 disables wait limits for the netblock. -+.TP -+.B wait\-limit\-cookie\-netblock: \fI -+The wait limit for the netblock, when the query has a DNS cookie. -+If not given, the wait\-limit\-cookie value is used. -+The value -1 disables wait limits for the netblock. -+.TP - .B so\-rcvbuf: \fI - If not 0, then set the SO_RCVBUF socket option to get more buffer - space on UDP port 53 incoming queries. So that short spikes on busy -diff --git a/services/cache/infra.c b/services/cache/infra.c -index 31462d13a..457685ab5 100644 ---- a/services/cache/infra.c -+++ b/services/cache/infra.c -@@ -234,6 +234,81 @@ setup_domain_limits(struct infra_cache* infra, struct config_file* cfg) - return 1; - } - -+/** find or create element in wait limit netblock tree */ -+static struct wait_limit_netblock_info* -+wait_limit_netblock_findcreate(struct infra_cache* infra, char* str, -+ int cookie) -+{ -+ rbtree_type* tree; -+ struct sockaddr_storage addr; -+ int net; -+ socklen_t addrlen; -+ struct wait_limit_netblock_info* d; -+ -+ if(!netblockstrtoaddr(str, 0, &addr, &addrlen, &net)) { -+ log_err("cannot parse wait limit netblock '%s'", str); -+ return 0; -+ } -+ -+ /* can we find it? */ -+ if(cookie) -+ tree = &infra->wait_limits_cookie_netblock; -+ else -+ tree = &infra->wait_limits_netblock; -+ d = (struct wait_limit_netblock_info*)addr_tree_find(tree, &addr, -+ addrlen, net); -+ if(d) -+ return d; -+ -+ /* create it */ -+ d = (struct wait_limit_netblock_info*)calloc(1, sizeof(*d)); -+ if(!d) -+ return NULL; -+ d->limit = -1; -+ if(!addr_tree_insert(tree, &d->node, &addr, addrlen, net)) { -+ log_err("duplicate element in domainlimit tree"); -+ free(d); -+ return NULL; -+ } -+ return d; -+} -+ -+ -+/** insert wait limit information into lookup tree */ -+static int -+infra_wait_limit_netblock_insert(struct infra_cache* infra, -+ struct config_file* cfg) -+{ -+ struct config_str2list* p; -+ struct wait_limit_netblock_info* d; -+ for(p = cfg->wait_limit_netblock; p; p = p->next) { -+ d = wait_limit_netblock_findcreate(infra, p->str, 0); -+ if(!d) -+ return 0; -+ d->limit = atoi(p->str2); -+ } -+ for(p = cfg->wait_limit_cookie_netblock; p; p = p->next) { -+ d = wait_limit_netblock_findcreate(infra, p->str, 1); -+ if(!d) -+ return 0; -+ d->limit = atoi(p->str2); -+ } -+ return 1; -+} -+ -+/** setup wait limits tree (0 on failure) */ -+static int -+setup_wait_limits(struct infra_cache* infra, struct config_file* cfg) -+{ -+ addr_tree_init(&infra->wait_limits_netblock); -+ addr_tree_init(&infra->wait_limits_cookie_netblock); -+ if(!infra_wait_limit_netblock_insert(infra, cfg)) -+ return 0; -+ addr_tree_init_parents(&infra->wait_limits_netblock); -+ addr_tree_init_parents(&infra->wait_limits_cookie_netblock); -+ return 1; -+} -+ - struct infra_cache* - infra_create(struct config_file* cfg) - { -@@ -267,6 +342,10 @@ infra_create(struct config_file* cfg) - infra_delete(infra); - return NULL; - } -+ if(!setup_wait_limits(infra, cfg)) { -+ infra_delete(infra); -+ return NULL; -+ } - infra_ip_ratelimit = cfg->ip_ratelimit; - infra->client_ip_rates = slabhash_create(cfg->ip_ratelimit_slabs, - INFRA_HOST_STARTSIZE, cfg->ip_ratelimit_size, &ip_rate_sizefunc, -@@ -287,6 +366,12 @@ static void domain_limit_free(rbnode_type* n, void* ATTR_UNUSED(arg)) - } - } - -+/** delete wait_limit_netblock_info entries */ -+static void wait_limit_netblock_del(rbnode_type* n, void* ATTR_UNUSED(arg)) -+{ -+ free(n); -+} -+ - void - infra_delete(struct infra_cache* infra) - { -@@ -296,6 +381,10 @@ infra_delete(struct infra_cache* infra) - slabhash_delete(infra->domain_rates); - traverse_postorder(&infra->domain_limits, domain_limit_free, NULL); - slabhash_delete(infra->client_ip_rates); -+ traverse_postorder(&infra->wait_limits_netblock, -+ wait_limit_netblock_del, NULL); -+ traverse_postorder(&infra->wait_limits_cookie_netblock, -+ wait_limit_netblock_del, NULL); - free(infra); - } - -@@ -880,7 +969,8 @@ static void infra_create_ratedata(struct infra_cache* infra, - - /** create rate data item for ip address */ - static void infra_ip_create_ratedata(struct infra_cache* infra, -- struct sockaddr_storage* addr, socklen_t addrlen, time_t timenow) -+ struct sockaddr_storage* addr, socklen_t addrlen, time_t timenow, -+ int mesh_wait) - { - hashvalue_type h = hash_addr(addr, addrlen, 0); - struct ip_rate_key* k = (struct ip_rate_key*)calloc(1, sizeof(*k)); -@@ -898,6 +988,7 @@ static void infra_ip_create_ratedata(struct infra_cache* infra, - k->entry.data = d; - d->qps[0] = 1; - d->timestamp[0] = timenow; -+ d->mesh_wait = mesh_wait; - slabhash_insert(infra->client_ip_rates, h, &k->entry, d, NULL); - } - -@@ -1121,6 +1212,81 @@ int infra_ip_ratelimit_inc(struct infra_cache* infra, - } - - /* create */ -- infra_ip_create_ratedata(infra, addr, addrlen, timenow); -+ infra_ip_create_ratedata(infra, addr, addrlen, timenow, 0); - return 1; - } -+ -+int infra_wait_limit_allowed(struct infra_cache* infra, struct comm_reply* rep, -+ int cookie_valid, struct config_file* cfg) -+{ -+ struct lruhash_entry* entry; -+ if(cfg->wait_limit == 0) -+ return 1; -+ -+ entry = infra_find_ip_ratedata(infra, &rep->client_addr, -+ rep->client_addrlen, 0); -+ if(entry) { -+ rbtree_type* tree; -+ struct wait_limit_netblock_info* w; -+ struct rate_data* d = (struct rate_data*)entry->data; -+ int mesh_wait = d->mesh_wait; -+ lock_rw_unlock(&entry->lock); -+ -+ /* have the wait amount, check how much is allowed */ -+ if(cookie_valid) -+ tree = &infra->wait_limits_cookie_netblock; -+ else tree = &infra->wait_limits_netblock; -+ w = (struct wait_limit_netblock_info*)addr_tree_lookup(tree, -+ &rep->client_addr, rep->client_addrlen); -+ if(w) { -+ if(w->limit != -1 && mesh_wait > w->limit) -+ return 0; -+ } else { -+ /* if there is no IP netblock specific information, -+ * use the configured value. */ -+ if(mesh_wait > (cookie_valid?cfg->wait_limit_cookie: -+ cfg->wait_limit)) -+ return 0; -+ } -+ } -+ return 1; -+} -+ -+void infra_wait_limit_inc(struct infra_cache* infra, struct comm_reply* rep, -+ time_t timenow, struct config_file* cfg) -+{ -+ struct lruhash_entry* entry; -+ if(cfg->wait_limit == 0) -+ return; -+ -+ /* Find it */ -+ entry = infra_find_ip_ratedata(infra, &rep->client_addr, -+ rep->client_addrlen, 1); -+ if(entry) { -+ struct rate_data* d = (struct rate_data*)entry->data; -+ d->mesh_wait++; -+ lock_rw_unlock(&entry->lock); -+ return; -+ } -+ -+ /* Create it */ -+ infra_ip_create_ratedata(infra, &rep->client_addr, -+ rep->client_addrlen, timenow, 1); -+} -+ -+void infra_wait_limit_dec(struct infra_cache* infra, struct comm_reply* rep, -+ struct config_file* cfg) -+{ -+ struct lruhash_entry* entry; -+ if(cfg->wait_limit == 0) -+ return; -+ -+ entry = infra_find_ip_ratedata(infra, &rep->client_addr, -+ rep->client_addrlen, 1); -+ if(entry) { -+ struct rate_data* d = (struct rate_data*)entry->data; -+ if(d->mesh_wait > 0) -+ d->mesh_wait--; -+ lock_rw_unlock(&entry->lock); -+ } -+} -diff --git a/services/cache/infra.h b/services/cache/infra.h -index 525073bf3..ee6f384de 100644 ---- a/services/cache/infra.h -+++ b/services/cache/infra.h -@@ -122,6 +122,10 @@ struct infra_cache { - rbtree_type domain_limits; - /** hash table with query rates per client ip: ip_rate_key, ip_rate_data */ - struct slabhash* client_ip_rates; -+ /** tree of addr_tree_node, with wait_limit_netblock_info information */ -+ rbtree_type wait_limits_netblock; -+ /** tree of addr_tree_node, with wait_limit_netblock_info information */ -+ rbtree_type wait_limits_cookie_netblock; - }; - - /** ratelimit, unless overridden by domain_limits, 0 is off */ -@@ -184,10 +188,22 @@ struct rate_data { - /** what the timestamp is of the qps array members, counter is - * valid for that timestamp. Usually now and now-1. */ - time_t timestamp[RATE_WINDOW]; -+ /** the number of queries waiting in the mesh */ -+ int mesh_wait; - }; - - #define ip_rate_data rate_data - -+/** -+ * Data to store the configuration per netblock for the wait limit -+ */ -+struct wait_limit_netblock_info { -+ /** The addr tree node, this must be first. */ -+ struct addr_tree_node node; -+ /** the limit on the amount */ -+ int limit; -+}; -+ - /** infra host cache default hash lookup size */ - #define INFRA_HOST_STARTSIZE 32 - /** bytes per zonename reserved in the hostcache, dnamelen(zonename.com.) */ -@@ -474,4 +490,16 @@ void ip_rate_delkeyfunc(void* d, void* arg); - /* delete data */ - #define ip_rate_deldatafunc rate_deldatafunc - -+/** See if the IP address can have another reply in the wait limit */ -+int infra_wait_limit_allowed(struct infra_cache* infra, struct comm_reply* rep, -+ int cookie_valid, struct config_file* cfg); -+ -+/** Increment number of waiting replies for IP */ -+void infra_wait_limit_inc(struct infra_cache* infra, struct comm_reply* rep, -+ time_t timenow, struct config_file* cfg); -+ -+/** Decrement number of waiting replies for IP */ -+void infra_wait_limit_dec(struct infra_cache* infra, struct comm_reply* rep, -+ struct config_file* cfg); -+ - #endif /* SERVICES_CACHE_INFRA_H */ -diff --git a/services/mesh.c b/services/mesh.c -index 55afd065f..e886c4b92 100644 ---- a/services/mesh.c -+++ b/services/mesh.c -@@ -47,6 +47,7 @@ - #include "services/outbound_list.h" - #include "services/cache/dns.h" - #include "services/cache/rrset.h" -+#include "services/cache/infra.h" - #include "util/log.h" - #include "util/net_help.h" - #include "util/module.h" -@@ -415,6 +416,14 @@ void mesh_new_client(struct mesh_area* mesh, struct query_info* qinfo, - if(rep->c->tcp_req_info) { - r_buffer = rep->c->tcp_req_info->spool_buffer; - } -+ if(!infra_wait_limit_allowed(mesh->env->infra_cache, rep, -+ edns->cookie_valid, mesh->env->cfg)) { -+ verbose(VERB_ALGO, "Too many queries waiting from the IP. " -+ "dropping incoming query."); -+ comm_point_drop_reply(rep); -+ mesh->stats_dropped++; -+ return; -+ } - if(!unique) - s = mesh_area_find(mesh, cinfo, qinfo, qflags&(BIT_RD|BIT_CD), 0, 0); - /* does this create a new reply state? */ -@@ -522,6 +531,8 @@ void mesh_new_client(struct mesh_area* mesh, struct query_info* qinfo, - log_err("mesh_new_client: out of memory initializing serve expired"); - goto servfail_mem; - } -+ infra_wait_limit_inc(mesh->env->infra_cache, rep, *mesh->env->now, -+ mesh->env->cfg); - /* update statistics */ - if(was_detached) { - log_assert(mesh->num_detached_states > 0); -@@ -953,6 +964,8 @@ mesh_state_cleanup(struct mesh_state* mstate) - * takes no time and also it does not do the mesh accounting */ - mstate->reply_list = NULL; - for(; rep; rep=rep->next) { -+ infra_wait_limit_dec(mesh->env->infra_cache, -+ &rep->query_reply, mesh->env->cfg); - comm_point_drop_reply(&rep->query_reply); - log_assert(mesh->num_reply_addrs > 0); - mesh->num_reply_addrs--; -@@ -1436,6 +1449,8 @@ mesh_send_reply(struct mesh_state* m, int rcode, struct reply_info* rep, - comm_point_send_reply(&r->query_reply); - m->reply_list = rlist; - } -+ infra_wait_limit_dec(m->s.env->infra_cache, &r->query_reply, -+ m->s.env->cfg); - /* account */ - log_assert(m->s.env->mesh->num_reply_addrs > 0); - m->s.env->mesh->num_reply_addrs--; -@@ -1491,6 +1506,28 @@ void mesh_query_done(struct mesh_state* mstate) - } - } - for(r = mstate->reply_list; r; r = r->next) { -+ struct timeval old; -+ timeval_subtract(&old, mstate->s.env->now_tv, &r->start_time); -+ if(mstate->s.env->cfg->discard_timeout != 0 && -+ ((int)old.tv_sec)*1000+((int)old.tv_usec)/1000 > -+ mstate->s.env->cfg->discard_timeout) { -+ /* Drop the reply, it is too old */ -+ /* briefly set the reply_list to NULL, so that the -+ * tcp req info cleanup routine that calls the mesh -+ * to deregister the meshstate for it is not done -+ * because the list is NULL and also accounting is not -+ * done there, but instead we do that here. */ -+ struct mesh_reply* reply_list = mstate->reply_list; -+ verbose(VERB_ALGO, "drop reply, it is older than discard-timeout"); -+ infra_wait_limit_dec(mstate->s.env->infra_cache, -+ &r->query_reply, mstate->s.env->cfg); -+ mstate->reply_list = NULL; -+ comm_point_drop_reply(&r->query_reply); -+ mstate->reply_list = reply_list; -+ mstate->s.env->mesh->stats_dropped++; -+ continue; -+ } -+ - tv = r->start_time; - - /* if a response-ip address block has been stored the -@@ -1514,6 +1551,8 @@ void mesh_query_done(struct mesh_state* mstate) - * because the list is NULL and also accounting is not - * done there, but instead we do that here. */ - struct mesh_reply* reply_list = mstate->reply_list; -+ infra_wait_limit_dec(mstate->s.env->infra_cache, -+ &r->query_reply, mstate->s.env->cfg); - mstate->reply_list = NULL; - comm_point_drop_reply(&r->query_reply); - mstate->reply_list = reply_list; -@@ -2046,6 +2085,8 @@ void mesh_state_remove_reply(struct mesh_area* mesh, struct mesh_state* m, - /* delete it, but allocated in m region */ - log_assert(mesh->num_reply_addrs > 0); - mesh->num_reply_addrs--; -+ infra_wait_limit_dec(mesh->env->infra_cache, -+ &n->query_reply, mesh->env->cfg); - - /* prev = prev; */ - n = n->next; -@@ -2186,6 +2227,28 @@ mesh_serve_expired_callback(void* arg) - log_dns_msg("Serve expired lookup", &qstate->qinfo, msg->rep); - - for(r = mstate->reply_list; r; r = r->next) { -+ struct timeval old; -+ timeval_subtract(&old, mstate->s.env->now_tv, &r->start_time); -+ if(mstate->s.env->cfg->discard_timeout != 0 && -+ ((int)old.tv_sec)*1000+((int)old.tv_usec)/1000 > -+ mstate->s.env->cfg->discard_timeout) { -+ /* Drop the reply, it is too old */ -+ /* briefly set the reply_list to NULL, so that the -+ * tcp req info cleanup routine that calls the mesh -+ * to deregister the meshstate for it is not done -+ * because the list is NULL and also accounting is not -+ * done there, but instead we do that here. */ -+ struct mesh_reply* reply_list = mstate->reply_list; -+ verbose(VERB_ALGO, "drop reply, it is older than discard-timeout"); -+ infra_wait_limit_dec(mstate->s.env->infra_cache, -+ &r->query_reply, mstate->s.env->cfg); -+ mstate->reply_list = NULL; -+ comm_point_drop_reply(&r->query_reply); -+ mstate->reply_list = reply_list; -+ mstate->s.env->mesh->stats_dropped++; -+ continue; -+ } -+ - tv = r->start_time; - - /* If address info is returned, it means the action should be an -@@ -2213,6 +2276,8 @@ mesh_serve_expired_callback(void* arg) - r, r_buffer, prev, prev_buffer); - if(r->query_reply.c->tcp_req_info) - tcp_req_info_remove_mesh_state(r->query_reply.c->tcp_req_info, mstate); -+ infra_wait_limit_dec(mstate->s.env->infra_cache, -+ &r->query_reply, mstate->s.env->cfg); - prev = r; - prev_buffer = r_buffer; - -diff --git a/testdata/doh_downstream.tdir/doh_downstream.conf b/testdata/doh_downstream.tdir/doh_downstream.conf -index f0857bb58..222c2159d 100644 ---- a/testdata/doh_downstream.tdir/doh_downstream.conf -+++ b/testdata/doh_downstream.tdir/doh_downstream.conf -@@ -11,6 +11,7 @@ server: - chroot: "" - username: "" - do-not-query-localhost: no -+ discard-timeout: 3000 # testns uses sleep=2 - http-query-buffer-size: 1G - http-response-buffer-size: 1G - http-max-streams: 200 -diff --git a/testdata/doh_downstream_notls.tdir/doh_downstream_notls.conf b/testdata/doh_downstream_notls.tdir/doh_downstream_notls.conf -index bdca45645..161c35559 100644 ---- a/testdata/doh_downstream_notls.tdir/doh_downstream_notls.conf -+++ b/testdata/doh_downstream_notls.tdir/doh_downstream_notls.conf -@@ -11,6 +11,7 @@ server: - chroot: "" - username: "" - do-not-query-localhost: no -+ discard-timeout: 3000 # testns uses sleep=2 - http-query-buffer-size: 1G - http-response-buffer-size: 1G - http-max-streams: 200 -diff --git a/testdata/doh_downstream_post.tdir/doh_downstream_post.conf b/testdata/doh_downstream_post.tdir/doh_downstream_post.conf -index f0857bb58..222c2159d 100644 ---- a/testdata/doh_downstream_post.tdir/doh_downstream_post.conf -+++ b/testdata/doh_downstream_post.tdir/doh_downstream_post.conf -@@ -11,6 +11,7 @@ server: - chroot: "" - username: "" - do-not-query-localhost: no -+ discard-timeout: 3000 # testns uses sleep=2 - http-query-buffer-size: 1G - http-response-buffer-size: 1G - http-max-streams: 200 -diff --git a/testdata/fwd_three_service.tdir/fwd_three_service.conf b/testdata/fwd_three_service.tdir/fwd_three_service.conf -index 05fafe015..d6c9a205f 100644 ---- a/testdata/fwd_three_service.tdir/fwd_three_service.conf -+++ b/testdata/fwd_three_service.tdir/fwd_three_service.conf -@@ -11,6 +11,7 @@ server: - num-queries-per-thread: 1024 - use-syslog: no - do-not-query-localhost: no -+ discard-timeout: 3000 # testns uses sleep=2 - forward-zone: - name: "." - forward-addr: "127.0.0.1@@TOPORT@" -diff --git a/testdata/iter_ghost_timewindow.rpl b/testdata/iter_ghost_timewindow.rpl -index 566be82a9..9e304628c 100644 ---- a/testdata/iter_ghost_timewindow.rpl -+++ b/testdata/iter_ghost_timewindow.rpl -@@ -3,6 +3,7 @@ server: - target-fetch-policy: "0 0 0 0 0" - qname-minimisation: "no" - minimal-responses: no -+ discard-timeout: 86400 - - stub-zone: - name: "." -diff --git a/testdata/ssl_req_order.tdir/ssl_req_order.conf b/testdata/ssl_req_order.tdir/ssl_req_order.conf -index 3b2e2b1b4..ec39d3ab2 100644 ---- a/testdata/ssl_req_order.tdir/ssl_req_order.conf -+++ b/testdata/ssl_req_order.tdir/ssl_req_order.conf -@@ -9,6 +9,7 @@ server: - chroot: "" - username: "" - do-not-query-localhost: no -+ discard-timeout: 3000 # testns uses sleep=2 - ssl-port: @PORT@ - ssl-service-key: "unbound_server.key" - ssl-service-pem: "unbound_server.pem" -diff --git a/testdata/tcp_req_order.tdir/tcp_req_order.conf b/testdata/tcp_req_order.tdir/tcp_req_order.conf -index 40d6f55c8..b2804e8e2 100644 ---- a/testdata/tcp_req_order.tdir/tcp_req_order.conf -+++ b/testdata/tcp_req_order.tdir/tcp_req_order.conf -@@ -9,6 +9,7 @@ server: - chroot: "" - username: "" - do-not-query-localhost: no -+ discard-timeout: 3000 # testns uses sleep=2 - - local-zone: "example.net" static - local-data: "www1.example.net. IN A 1.2.3.1" -diff --git a/testdata/tcp_sigpipe.tdir/tcp_sigpipe.conf b/testdata/tcp_sigpipe.tdir/tcp_sigpipe.conf -index 384f16b07..4f1ff9b08 100644 ---- a/testdata/tcp_sigpipe.tdir/tcp_sigpipe.conf -+++ b/testdata/tcp_sigpipe.tdir/tcp_sigpipe.conf -@@ -1,5 +1,5 @@ - server: -- verbosity: 2 -+ verbosity: 4 - # num-threads: 1 - interface: 127.0.0.1 - port: @PORT@ -@@ -9,6 +9,7 @@ server: - chroot: "" - username: "" - do-not-query-localhost: no -+ discard-timeout: 3000 # testns uses sleep=2 - - forward-zone: - name: "." -diff --git a/util/config_file.c b/util/config_file.c -index e4fe3ee9f..2b67d4c19 100644 ---- a/util/config_file.c -+++ b/util/config_file.c -@@ -309,6 +309,11 @@ config_create(void) - cfg->minimal_responses = 1; - cfg->rrset_roundrobin = 1; - cfg->unknown_server_time_limit = 376; -+ cfg->discard_timeout = 1900; /* msec */ -+ cfg->wait_limit = 1000; -+ cfg->wait_limit_cookie = 10000; -+ cfg->wait_limit_netblock = NULL; -+ cfg->wait_limit_cookie_netblock = NULL; - cfg->max_udp_size = 4096; - if(!(cfg->server_key_file = strdup(RUN_DIR"/unbound_server.key"))) - goto error_exit; -@@ -726,6 +731,9 @@ int config_set_option(struct config_file* cfg, const char* opt, - else S_YNO("minimal-responses:", minimal_responses) - else S_YNO("rrset-roundrobin:", rrset_roundrobin) - else S_NUMBER_OR_ZERO("unknown-server-time-limit:", unknown_server_time_limit) -+ else S_NUMBER_OR_ZERO("discard-timeout:", discard_timeout) -+ else S_NUMBER_OR_ZERO("wait-limit:", wait_limit) -+ else S_NUMBER_OR_ZERO("wait-limit-cookie:", wait_limit_cookie) - else S_STRLIST("local-data:", local_data) - else S_YNO("unblock-lan-zones:", unblock_lan_zones) - else S_YNO("insecure-lan-zones:", insecure_lan_zones) -@@ -1207,6 +1215,11 @@ config_get_option(struct config_file* cfg, const char* opt, - else O_YNO(opt, "minimal-responses", minimal_responses) - else O_YNO(opt, "rrset-roundrobin", rrset_roundrobin) - else O_DEC(opt, "unknown-server-time-limit", unknown_server_time_limit) -+ else O_DEC(opt, "discard-timeout", discard_timeout) -+ else O_DEC(opt, "wait-limit", wait_limit) -+ else O_DEC(opt, "wait-limit-cookie", wait_limit_cookie) -+ else O_LS2(opt, "wait-limit-netblock", wait_limit_netblock) -+ else O_LS2(opt, "wait-limit-cookie-netblock", wait_limit_cookie_netblock) - #ifdef CLIENT_SUBNET - else O_LST(opt, "send-client-subnet", client_subnet) - else O_LST(opt, "client-subnet-zone", client_subnet_zone) -@@ -1678,6 +1691,8 @@ config_delete(struct config_file* cfg) - config_deltrplstrlist(cfg->interface_tag_actions); - config_deltrplstrlist(cfg->interface_tag_datas); - config_delstrlist(cfg->control_ifs.first); -+ config_deldblstrlist(cfg->wait_limit_netblock); -+ config_deldblstrlist(cfg->wait_limit_cookie_netblock); - free(cfg->server_key_file); - free(cfg->server_cert_file); - free(cfg->control_key_file); -diff --git a/util/config_file.h b/util/config_file.h -index 42836796e..d3a2e268c 100644 ---- a/util/config_file.h -+++ b/util/config_file.h -@@ -537,6 +537,21 @@ struct config_file { - /* wait time for unknown server in msec */ - int unknown_server_time_limit; - -+ /** Wait time to drop recursion replies */ -+ int discard_timeout; -+ -+ /** Wait limit for number of replies per IP address */ -+ int wait_limit; -+ -+ /** Wait limit for number of replies per IP address with cookie */ -+ int wait_limit_cookie; -+ -+ /** wait limit per netblock */ -+ struct config_str2list* wait_limit_netblock; -+ -+ /** wait limit with cookie per netblock */ -+ struct config_str2list* wait_limit_cookie_netblock; -+ - /* maximum UDP response size */ - size_t max_udp_size; - -diff --git a/util/configlexer.lex b/util/configlexer.lex -index 51bf06601..7ae1b8c38 100644 ---- a/util/configlexer.lex -+++ b/util/configlexer.lex -@@ -464,6 +464,11 @@ domain-insecure{COLON} { YDVAR(1, VAR_DOMAIN_INSECURE) } - minimal-responses{COLON} { YDVAR(1, VAR_MINIMAL_RESPONSES) } - rrset-roundrobin{COLON} { YDVAR(1, VAR_RRSET_ROUNDROBIN) } - unknown-server-time-limit{COLON} { YDVAR(1, VAR_UNKNOWN_SERVER_TIME_LIMIT) } -+discard-timeout{COLON} { YDVAR(1, VAR_DISCARD_TIMEOUT) } -+wait-limit{COLON} { YDVAR(1, VAR_WAIT_LIMIT) } -+wait-limit-cookie{COLON} { YDVAR(1, VAR_WAIT_LIMIT_COOKIE) } -+wait-limit-netblock{COLON} { YDVAR(1, VAR_WAIT_LIMIT_NETBLOCK) } -+wait-limit-cookie-netblock{COLON} { YDVAR(1, VAR_WAIT_LIMIT_COOKIE_NETBLOCK) } - max-udp-size{COLON} { YDVAR(1, VAR_MAX_UDP_SIZE) } - dns64-prefix{COLON} { YDVAR(1, VAR_DNS64_PREFIX) } - dns64-synthall{COLON} { YDVAR(1, VAR_DNS64_SYNTHALL) } -diff --git a/util/configparser.y b/util/configparser.y -index 2adbf92cc..0feeb61b1 100644 ---- a/util/configparser.y -+++ b/util/configparser.y -@@ -189,6 +189,8 @@ extern struct config_parser_state* cfg_parser; - %token VAR_ANSWER_COOKIE VAR_COOKIE_SECRET - %token VAR_FORWARD_NO_CACHE VAR_STUB_NO_CACHE VAR_LOG_SERVFAIL VAR_DENY_ANY - %token VAR_UNKNOWN_SERVER_TIME_LIMIT VAR_LOG_TAG_QUERYREPLY -+%token VAR_DISCARD_TIMEOUT VAR_WAIT_LIMIT VAR_WAIT_LIMIT_COOKIE -+%token VAR_WAIT_LIMIT_NETBLOCK VAR_WAIT_LIMIT_COOKIE_NETBLOCK - %token VAR_STREAM_WAIT_SIZE VAR_TLS_CIPHERS VAR_TLS_CIPHERSUITES VAR_TLS_USE_SNI - %token VAR_IPSET VAR_IPSET_NAME_V4 VAR_IPSET_NAME_V6 - %token VAR_TLS_SESSION_TICKET_KEYS VAR_RPZ VAR_TAGS VAR_RPZ_ACTION_OVERRIDE -@@ -327,6 +329,8 @@ content_server: server_num_threads | server_verbosity | server_port | - server_fast_server_permil | server_fast_server_num | server_tls_win_cert | - server_tcp_connection_limit | server_log_servfail | server_deny_any | - server_unknown_server_time_limit | server_log_tag_queryreply | -+ server_discard_timeout | server_wait_limit | server_wait_limit_cookie | -+ server_wait_limit_netblock | server_wait_limit_cookie_netblock | - server_stream_wait_size | server_tls_ciphers | - server_tls_ciphersuites | server_tls_session_ticket_keys | - server_answer_cookie | server_cookie_secret | -@@ -2377,6 +2381,57 @@ server_unknown_server_time_limit: VAR_UNKNOWN_SERVER_TIME_LIMIT STRING_ARG - free($2); - } - ; -+server_discard_timeout: VAR_DISCARD_TIMEOUT STRING_ARG -+ { -+ OUTYY(("P(server_discard_timeout:%s)\n", $2)); -+ cfg_parser->cfg->discard_timeout = atoi($2); -+ free($2); -+ } -+ ; -+server_wait_limit: VAR_WAIT_LIMIT STRING_ARG -+ { -+ OUTYY(("P(server_wait_limit:%s)\n", $2)); -+ cfg_parser->cfg->wait_limit = atoi($2); -+ free($2); -+ } -+ ; -+server_wait_limit_cookie: VAR_WAIT_LIMIT_COOKIE STRING_ARG -+ { -+ OUTYY(("P(server_wait_limit_cookie:%s)\n", $2)); -+ cfg_parser->cfg->wait_limit_cookie = atoi($2); -+ free($2); -+ } -+ ; -+server_wait_limit_netblock: VAR_WAIT_LIMIT_NETBLOCK STRING_ARG STRING_ARG -+ { -+ OUTYY(("P(server_wait_limit_netblock:%s %s)\n", $2, $3)); -+ if(atoi($3) == 0 && strcmp($3, "0") != 0) { -+ yyerror("number expected"); -+ free($2); -+ free($3); -+ } else { -+ if(!cfg_str2list_insert(&cfg_parser->cfg-> -+ wait_limit_netblock, $2, $3)) -+ fatal_exit("out of memory adding " -+ "wait-limit-netblock"); -+ } -+ } -+ ; -+server_wait_limit_cookie_netblock: VAR_WAIT_LIMIT_COOKIE_NETBLOCK STRING_ARG STRING_ARG -+ { -+ OUTYY(("P(server_wait_limit_cookie_netblock:%s %s)\n", $2, $3)); -+ if(atoi($3) == 0 && strcmp($3, "0") != 0) { -+ yyerror("number expected"); -+ free($2); -+ free($3); -+ } else { -+ if(!cfg_str2list_insert(&cfg_parser->cfg-> -+ wait_limit_cookie_netblock, $2, $3)) -+ fatal_exit("out of memory adding " -+ "wait-limit-cookie-netblock"); -+ } -+ } -+ ; - server_max_udp_size: VAR_MAX_UDP_SIZE STRING_ARG - { - OUTYY(("P(server_max_udp_size:%s)\n", $2)); diff --git a/backport-CVE-2024-43167.patch b/backport-CVE-2024-43167.patch deleted file mode 100644 index a8efd96e0749b9900b2fa3509bbb699e9f113efa..0000000000000000000000000000000000000000 --- a/backport-CVE-2024-43167.patch +++ /dev/null @@ -1,45 +0,0 @@ -From 8e43e2574c4e02f79c562a061581cdcefe136912 Mon Sep 17 00:00:00 2001 -From: zhailiangliang -Date: Tue, 21 May 2024 08:40:16 +0000 -Subject: [PATCH] fix null pointer dereference issue in function ub_ctx_set_fwd - of file libunbound/libunbound.c - ---- - libunbound/libunbound.c | 7 +++++-- - 1 file changed, 5 insertions(+), 2 deletions(-) - -diff --git a/libunbound/libunbound.c b/libunbound/libunbound.c -index 17057ec6..3c895514 100644 ---- a/libunbound/libunbound.c -+++ b/libunbound/libunbound.c -@@ -981,7 +981,8 @@ ub_ctx_set_fwd(struct ub_ctx* ctx, const char* addr) - if(!addr) { - /* disable fwd mode - the root stub should be first. */ - if(ctx->env->cfg->forwards && -- strcmp(ctx->env->cfg->forwards->name, ".") == 0) { -+ (ctx->env->cfg->forwards->name && -+ strcmp(ctx->env->cfg->forwards->name, ".") == 0)) { - s = ctx->env->cfg->forwards; - ctx->env->cfg->forwards = s->next; - s->next = NULL; -@@ -1001,7 +1002,8 @@ ub_ctx_set_fwd(struct ub_ctx* ctx, const char* addr) - /* it parses, add root stub in front of list */ - lock_basic_lock(&ctx->cfglock); - if(!ctx->env->cfg->forwards || -- strcmp(ctx->env->cfg->forwards->name, ".") != 0) { -+ (ctx->env->cfg->forwards->name && -+ strcmp(ctx->env->cfg->forwards->name, ".") != 0)) { - s = calloc(1, sizeof(*s)); - if(!s) { - lock_basic_unlock(&ctx->cfglock); -@@ -1019,6 +1021,7 @@ ub_ctx_set_fwd(struct ub_ctx* ctx, const char* addr) - ctx->env->cfg->forwards = s; - } else { - log_assert(ctx->env->cfg->forwards); -+ log_assert(ctx->env->cfg->forwards->name); - s = ctx->env->cfg->forwards; - } - dupl = strdup(addr); --- -2.33.0 - diff --git a/backport-CVE-2024-8508.patch b/backport-CVE-2024-8508.patch deleted file mode 100644 index 42d4cf86ca61ed5778c13ccceb7209af7066de0b..0000000000000000000000000000000000000000 --- a/backport-CVE-2024-8508.patch +++ /dev/null @@ -1,246 +0,0 @@ -From b7c61d7cc256d6a174e6179622c7fa968272c259 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. - ---- - util/data/msgencode.c | 77 ++++++++++++++++++++++++++----------------- - 1 file changed, 46 insertions(+), 31 deletions(-) - -diff --git a/util/data/msgencode.c b/util/data/msgencode.c -index 898ff8412..6d116fb52 100644 ---- a/util/data/msgencode.c -+++ b/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); diff --git a/backport-pre-CVE-2024-33655-Downstream-DNS-Cookies-a-la-RFC7873-and-RFC9018.patch b/backport-pre-CVE-2024-33655-Downstream-DNS-Cookies-a-la-RFC7873-and-RFC9018.patch deleted file mode 100644 index 472918a58216ab68f511270b9070a06f4639f4a1..0000000000000000000000000000000000000000 --- a/backport-pre-CVE-2024-33655-Downstream-DNS-Cookies-a-la-RFC7873-and-RFC9018.patch +++ /dev/null @@ -1,886 +0,0 @@ -From 75f3fbdd6563dd87c93964e48a3fb7e6c520d74e Mon Sep 17 00:00:00 2001 -From: Willem Toorop -Date: Wed, 28 Sep 2022 10:28:19 +0200 -Subject: [PATCH] Downstream DNS Cookies a la RFC7873 and RFC9018 - -Create server cookies for clients that send client cookies. -Needs to be turned on in the config file with: - - answer-cookie: yes - -A cookie-secret can be configured for anycast setups. -Also adds an access control list that will allow queries with -either a valid cookie or over a stateful transport. ---- - Makefile.in | 8 +- - daemon/acl_list.c | 2 + - daemon/acl_list.h | 4 +- - daemon/worker.c | 44 ++++++++++- - doc/unbound.conf.5.in | 23 +++++- - libunbound/libworker.c | 2 + - services/authzone.c | 4 + - sldns/rrdef.h | 4 + - testcode/fake_event.c | 2 + - util/config_file.c | 23 ++++++ - util/config_file.h | 7 ++ - util/configlexer.lex | 2 + - util/configparser.y | 35 ++++++++- - util/data/msgparse.c | 151 ++++++++++++++++++++++++++++++++++++- - util/data/msgparse.h | 14 +++- - util/siphash.c | 165 +++++++++++++++++++++++++++++++++++++++++ - validator/autotrust.c | 2 + - 17 files changed, 473 insertions(+), 19 deletions(-) - create mode 100644 util/siphash.c - -diff --git a/Makefile.in b/Makefile.in -index 3189731ad..718f47f5c 100644 ---- a/Makefile.in -+++ b/Makefile.in -@@ -128,7 +128,7 @@ util/config_file.c util/configlexer.c util/configparser.c \ - util/shm_side/shm_main.c services/authzone.c \ - util/fptr_wlist.c util/locks.c util/log.c util/mini_event.c util/module.c \ - util/netevent.c util/net_help.c util/random.c util/rbtree.c util/regional.c \ --util/rtt.c util/edns.c util/storage/dnstree.c util/storage/lookup3.c \ -+util/rtt.c util/siphash.c util/edns.c util/storage/dnstree.c util/storage/lookup3.c \ - util/storage/lruhash.c util/storage/slabhash.c util/tcp_conn_limit.c \ - util/timehist.c util/tube.c util/proxy_protocol.c \ - util/ub_event.c util/ub_event_pluggable.c util/winsock_event.c \ -@@ -145,7 +145,7 @@ as112.lo msgparse.lo msgreply.lo packed_rrset.lo iterator.lo iter_delegpt.lo \ - iter_donotq.lo iter_fwd.lo iter_hints.lo iter_priv.lo iter_resptype.lo \ - iter_scrub.lo iter_utils.lo localzone.lo mesh.lo modstack.lo view.lo \ - outbound_list.lo alloc.lo config_file.lo configlexer.lo configparser.lo \ --fptr_wlist.lo edns.lo locks.lo log.lo mini_event.lo module.lo net_help.lo \ -+fptr_wlist.lo siphash.lo edns.lo locks.lo log.lo mini_event.lo module.lo net_help.lo \ - random.lo rbtree.lo regional.lo rtt.lo dnstree.lo lookup3.lo lruhash.lo \ - slabhash.lo tcp_conn_limit.lo timehist.lo tube.lo winsock_event.lo \ - autotrust.lo val_anchor.lo rpz.lo proxy_protocol.lo \ -@@ -915,7 +915,8 @@ config_file.lo config_file.o: $(srcdir)/util/config_file.c config.h $(srcdir)/ut - configlexer.lo configlexer.o: util/configlexer.c config.h $(srcdir)/util/configyyrename.h \ - $(srcdir)/util/config_file.h util/configparser.h - configparser.lo configparser.o: util/configparser.c config.h $(srcdir)/util/configyyrename.h \ -- $(srcdir)/util/config_file.h $(srcdir)/util/net_help.h $(srcdir)/util/log.h -+ $(srcdir)/util/config_file.h $(srcdir)/util/net_help.h $(srcdir)/util/log.h $(srcdir)/sldns/str2wire.h \ -+ $(srcdir)/sldns/rrdef.h - shm_main.lo shm_main.o: $(srcdir)/util/shm_side/shm_main.c config.h $(srcdir)/util/shm_side/shm_main.h \ - $(srcdir)/libunbound/unbound.h $(srcdir)/daemon/daemon.h $(srcdir)/util/locks.h $(srcdir)/util/log.h \ - $(srcdir)/util/alloc.h $(srcdir)/services/modstack.h \ -@@ -1004,6 +1005,7 @@ rtt.lo rtt.o: $(srcdir)/util/rtt.c config.h $(srcdir)/util/rtt.h $(srcdir)/itera - $(srcdir)/services/outbound_list.h $(srcdir)/util/data/msgreply.h $(srcdir)/util/storage/lruhash.h \ - $(srcdir)/util/locks.h $(srcdir)/util/log.h $(srcdir)/util/data/packed_rrset.h $(srcdir)/util/module.h \ - $(srcdir)/util/data/msgparse.h $(srcdir)/sldns/pkthdr.h $(srcdir)/sldns/rrdef.h -+siphash.lo siphash.o: $(srcdir)/util/siphash.c - edns.lo edns.o: $(srcdir)/util/edns.c config.h $(srcdir)/util/edns.h $(srcdir)/util/storage/dnstree.h \ - $(srcdir)/util/rbtree.h $(srcdir)/util/config_file.h $(srcdir)/util/netevent.h $(srcdir)/dnscrypt/dnscrypt.h \ - $(srcdir)/util/net_help.h $(srcdir)/util/log.h $(srcdir)/util/regional.h \ -diff --git a/daemon/acl_list.c b/daemon/acl_list.c -index 8e8e1fc9b..e6d0470a1 100644 ---- a/daemon/acl_list.c -+++ b/daemon/acl_list.c -@@ -109,6 +109,8 @@ parse_acl_access(const char* str, enum acl_access* control) - *control = acl_allow_snoop; - else if(strcmp(str, "allow_setrd") == 0) - *control = acl_allow_setrd; -+ else if (strcmp(str, "allow_cookie") == 0) -+ *control = acl_allow_cookie; - else { - log_err("access control type %s unknown", str); - return 0; -diff --git a/daemon/acl_list.h b/daemon/acl_list.h -index c717179ba..8aece11bf 100644 ---- a/daemon/acl_list.h -+++ b/daemon/acl_list.h -@@ -65,7 +65,9 @@ enum acl_access { - /** allow full access for all queries, recursion and cache snooping */ - acl_allow_snoop, - /** allow full access for recursion queries and set RD flag regardless of request */ -- acl_allow_setrd -+ acl_allow_setrd, -+ /** allow full access if valid cookie present or stateful transport */ -+ acl_allow_cookie - }; - - /** -diff --git a/daemon/worker.c b/daemon/worker.c -index c2a94be79..1abf20a7b 100644 ---- a/daemon/worker.c -+++ b/daemon/worker.c -@@ -1432,8 +1432,10 @@ worker_handle_request(struct comm_point* c, void* arg, int error, - } - goto send_reply; - } -- if((ret=parse_edns_from_query_pkt(c->buffer, &edns, worker->env.cfg, c, -- worker->scratchpad)) != 0) { -+ if((ret=parse_edns_from_query_pkt( -+ c->buffer, &edns, worker->env.cfg, c, repinfo, -+ (worker->env.now ? *worker->env.now : time(NULL)), -+ worker->scratchpad)) != 0) { - struct edns_data reply_edns; - verbose(VERB_ALGO, "worker parse edns: formerror."); - log_addr(VERB_CLIENT, "from", &repinfo->client_addr, -@@ -1466,6 +1468,44 @@ worker_handle_request(struct comm_point* c, void* arg, int error, - edns.udp_size = NORMAL_UDP_SIZE; - } - } -+ /* "if, else if" sequence below deals with downstream DNS Cookies */ -+ if (acl != acl_allow_cookie) -+ ; /* pass; No cookie downstream processing whatsoever */ -+ -+ else if (edns.cookie_valid) -+ ; /* pass; Valid cookie is good! */ -+ -+ else if (c->type != comm_udp) -+ ; /* pass; Stateful transport */ -+ -+ else if (edns.cookie_present) { -+ /* Cookie present, but not valid: Cookie was bad! */ -+ extended_error_encode(c->buffer, -+ LDNS_EXT_RCODE_BADCOOKIE, &qinfo, -+ *(uint16_t*)(void *) -+ sldns_buffer_begin(c->buffer), -+ sldns_buffer_read_u16_at(c->buffer, 2), -+ 0, &edns); -+ regional_free_all(worker->scratchpad); -+ goto send_reply; -+ } else { -+ /* Cookie requered, but no cookie present on UDP */ -+ verbose(VERB_ALGO, "worker request: " -+ "need cookie or stateful transport"); -+ log_addr(VERB_ALGO, "from", -+ &repinfo->remote_addr, repinfo->remote_addrlen); -+ EDNS_OPT_LIST_APPEND_EDE(&edns.opt_list_out, -+ worker->scratchpad, LDNS_EDE_OTHER, -+ "DNS Cookie needed for UDP replies"); -+ error_encode(c->buffer, -+ (LDNS_RCODE_REFUSED|BIT_TC), &qinfo, -+ *(uint16_t*)(void *) -+ sldns_buffer_begin(c->buffer), -+ sldns_buffer_read_u16_at(c->buffer, 2), -+ &edns); -+ regional_free_all(worker->scratchpad); -+ goto send_reply; -+ } - if(edns.udp_size > worker->daemon->cfg->max_udp_size && - c->type == comm_udp) { - verbose(VERB_QUERY, -diff --git a/doc/unbound.conf.5.in b/doc/unbound.conf.5.in -index 73575d93a..e187452de 100644 ---- a/doc/unbound.conf.5.in -+++ b/doc/unbound.conf.5.in -@@ -673,9 +673,9 @@ This option is experimental at this time. - .B access\-control: \fI - The netblock is given as an IP4 or IP6 address with /size appended for a - classless network block. The action can be \fIdeny\fR, \fIrefuse\fR, --\fIallow\fR, \fIallow_setrd\fR, \fIallow_snoop\fR, \fIdeny_non_local\fR or --\fIrefuse_non_local\fR. --The most specific netblock match is used, if none match \fIrefuse\fR is used. -+\fIallow\fR, \fIallow_setrd\fR, \fIallow_snoop\fR, \fIallow_cookie\fR, -+\fIdeny_non_local\fR or \fIrefuse_non_local\fR. -+The most specific netblock match is used, if none match \fIdeny\fR is used. - The order of the access\-control statements therefore does not matter. - .IP - The action \fIdeny\fR stops queries from hosts from that netblock. -@@ -710,6 +710,14 @@ the cache contents (for malicious acts). However, nonrecursive queries can - also be a valuable debugging tool (when you want to examine the cache - contents). In that case use \fIallow_snoop\fR for your administration host. - .IP -+When the \fBanswer\-cookie\fR option is enabled, the \fIallow_cookie\fR action -+will allow access to UDP queries that contain a valid Server Cookie as -+specified in RFC 7873 and RFC9018. UDP queries containing only a Client Cookie -+and no Server Cookie, will receive a BADCOOKIE response including a Server -+Cookie, allow clients to retry with that Server Cookie. The \fIallow_cookie\fR -+will also accept requests over statefull transports, regardless of the precence -+of a Cookie and regardless the \fBanswer\-cookie\fR setting. -+.IP - By default only localhost is \fIallow\fRed, the rest is \fIrefuse\fRd. - The default is \fIrefuse\fRd, because that is protocol\-friendly. The DNS - protocol is not designed to handle dropped packets due to policy, and -@@ -1824,6 +1832,15 @@ Set the number of servers that should be used for fast server selection. Only - use the fastest specified number of servers with the fast\-server\-permil - option, that turns this on or off. The default is to use the fastest 3 servers. - .TP 5 -+.B answer\-cookie: \fI -+Enable to answer to requests containig DNS Cookies as specified in RFC7873 and -+RFC9018. Default is no. -+.TP 5 -+.B cookie\-secret: \fI<128 bit hex string> -+Server's in an Anycast deployment need to be able to verify each other's -+Server Cookies. For this they need to share the secret used to construct -+and verify the Server Cookies. -+Default is a 128 bits random secret generated at startup time. - .B edns\-client\-string: \fI - Include an EDNS0 option containing configured ascii string in queries with - destination address matching the configured IP netblock. This configuration -diff --git a/libunbound/libworker.c b/libunbound/libworker.c -index 11bf5f9db..d5fabe6fb 100644 ---- a/libunbound/libworker.c -+++ b/libunbound/libworker.c -@@ -604,6 +604,8 @@ setup_qinfo_edns(struct libworker* w, struct ctx_query* q, - edns->opt_list_out = NULL; - edns->opt_list_inplace_cb_out = NULL; - edns->padding_block_size = 0; -+ edns->cookie_present = 0; -+ edns->cookie_valid = 0; - if(sldns_buffer_capacity(w->back->udp_buff) < 65535) - edns->udp_size = (uint16_t)sldns_buffer_capacity( - w->back->udp_buff); -diff --git a/services/authzone.c b/services/authzone.c -index 6de1e4319..079a7eaf1 100644 ---- a/services/authzone.c -+++ b/services/authzone.c -@@ -5419,6 +5419,8 @@ xfr_transfer_lookup_host(struct auth_xfer* xfr, struct module_env* env) - edns.opt_list_out = NULL; - edns.opt_list_inplace_cb_out = NULL; - edns.padding_block_size = 0; -+ edns.cookie_present = 0; -+ edns.cookie_valid = 0; - if(sldns_buffer_capacity(buf) < 65535) - edns.udp_size = (uint16_t)sldns_buffer_capacity(buf); - else edns.udp_size = 65535; -@@ -6612,6 +6614,8 @@ xfr_probe_lookup_host(struct auth_xfer* xfr, struct module_env* env) - edns.opt_list_out = NULL; - edns.opt_list_inplace_cb_out = NULL; - edns.padding_block_size = 0; -+ edns.cookie_present = 0; -+ edns.cookie_valid = 0; - if(sldns_buffer_capacity(buf) < 65535) - edns.udp_size = (uint16_t)sldns_buffer_capacity(buf); - else edns.udp_size = 65535; -diff --git a/sldns/rrdef.h b/sldns/rrdef.h -index 999c22307..d5b0585fd 100644 ---- a/sldns/rrdef.h -+++ b/sldns/rrdef.h -@@ -433,6 +433,7 @@ enum sldns_enum_edns_option - LDNS_EDNS_DHU = 6, /* RFC6975 */ - LDNS_EDNS_N3U = 7, /* RFC6975 */ - LDNS_EDNS_CLIENT_SUBNET = 8, /* RFC7871 */ -+ LDNS_EDNS_COOKIE = 10, /* RFC7873 */ - LDNS_EDNS_KEEPALIVE = 11, /* draft-ietf-dnsop-edns-tcp-keepalive*/ - LDNS_EDNS_PADDING = 12, /* RFC7830 */ - LDNS_EDNS_EDE = 15, /* RFC8914 */ -@@ -482,6 +483,9 @@ typedef enum sldns_enum_ede_code sldns_ede_code; - #define LDNS_TSIG_ERROR_BADNAME 20 - #define LDNS_TSIG_ERROR_BADALG 21 - -+/** DNS Cookie extended rcode */ -+#define LDNS_EXT_RCODE_BADCOOKIE 23 -+ - /** - * Contains all information about resource record types. - * -diff --git a/testcode/fake_event.c b/testcode/fake_event.c -index 03e1c04f3..c93ceaa4c 100644 ---- a/testcode/fake_event.c -+++ b/testcode/fake_event.c -@@ -1263,6 +1263,8 @@ struct serviced_query* outnet_serviced_query(struct outside_network* outnet, - if(dnssec) - edns.bits = EDNS_DO; - edns.padding_block_size = 0; -+ edns.cookie_present = 0; -+ edns.cookie_valid = 0; - edns.opt_list_in = NULL; - edns.opt_list_out = per_upstream_opt_list; - edns.opt_list_inplace_cb_out = NULL; -diff --git a/util/config_file.c b/util/config_file.c -index 158169c42..a92342761 100644 ---- a/util/config_file.c -+++ b/util/config_file.c -@@ -55,6 +55,7 @@ - #include "util/regional.h" - #include "util/fptr_wlist.h" - #include "util/data/dname.h" -+#include "util/random.h" - #include "util/rtt.h" - #include "services/cache/infra.h" - #include "sldns/wire2str.h" -@@ -87,6 +88,9 @@ struct config_parser_state* cfg_parser = 0; - /** init ports possible for use */ - static void init_outgoing_availports(int* array, int num); - -+/** init cookie with random data */ -+static void init_cookie_secret(uint8_t* cookie_secret,size_t cookie_secret_len); -+ - struct config_file* - config_create(void) - { -@@ -364,6 +368,10 @@ config_create(void) - cfg->ipsecmod_whitelist = NULL; - cfg->ipsecmod_strict = 0; - #endif -+ cfg->do_answer_cookie = 0; -+ memset(cfg->cookie_secret, 0, sizeof(cfg->cookie_secret)); -+ cfg->cookie_secret_len = 16; -+ init_cookie_secret(cfg->cookie_secret, cfg->cookie_secret_len); - #ifdef USE_CACHEDB - if(!(cfg->cachedb_backend = strdup("testframe"))) goto error_exit; - if(!(cfg->cachedb_secret = strdup("default"))) goto error_exit; -@@ -1660,6 +1668,21 @@ config_delete(struct config_file* cfg) - free(cfg); - } - -+static void -+init_cookie_secret(uint8_t* cookie_secret, size_t cookie_secret_len) -+{ -+ struct ub_randstate *rand = ub_initstate(NULL); -+ -+ if (!rand) -+ fatal_exit("could not init random generator"); -+ while (cookie_secret_len) { -+ *cookie_secret++ = (uint8_t)ub_random(rand); -+ cookie_secret_len--; -+ } -+ ub_randfree(rand); -+} -+ -+ - static void - init_outgoing_availports(int* a, int num) - { -diff --git a/util/config_file.h b/util/config_file.h -index bbc6d4ac1..3db4676b9 100644 ---- a/util/config_file.h -+++ b/util/config_file.h -@@ -688,6 +688,13 @@ struct config_file { - int redis_expire_records; - #endif - #endif -+ /** Downstream DNS Cookies */ -+ /** do answer with server cookie when request contained cookie option */ -+ int do_answer_cookie; -+ /** cookie secret */ -+ uint8_t cookie_secret[40]; -+ /** cookie secret length */ -+ size_t cookie_secret_len; - - /* ipset module */ - #ifdef USE_IPSET -diff --git a/util/configlexer.lex b/util/configlexer.lex -index fc9aa7266..4fdb2cde0 100644 ---- a/util/configlexer.lex -+++ b/util/configlexer.lex -@@ -558,6 +558,8 @@ name-v4{COLON} { YDVAR(1, VAR_IPSET_NAME_V4) } - name-v6{COLON} { YDVAR(1, VAR_IPSET_NAME_V6) } - udp-upstream-without-downstream{COLON} { YDVAR(1, VAR_UDP_UPSTREAM_WITHOUT_DOWNSTREAM) } - tcp-connection-limit{COLON} { YDVAR(2, VAR_TCP_CONNECTION_LIMIT) } -+answer-cookie{COLON} { YDVAR(1, VAR_ANSWER_COOKIE ) } -+cookie-secret{COLON} { YDVAR(1, VAR_COOKIE_SECRET) } - edns-client-string{COLON} { YDVAR(2, VAR_EDNS_CLIENT_STRING) } - edns-client-string-opcode{COLON} { YDVAR(1, VAR_EDNS_CLIENT_STRING_OPCODE) } - nsid{COLON} { YDVAR(1, VAR_NSID ) } -diff --git a/util/configparser.y b/util/configparser.y -index 8f3672f5d..980201460 100644 ---- a/util/configparser.y -+++ b/util/configparser.y -@@ -42,11 +42,13 @@ - #include - #include - #include -+#include - #include - - #include "util/configyyrename.h" - #include "util/config_file.h" - #include "util/net_help.h" -+#include "sldns/str2wire.h" - - int ub_c_lex(void); - void ub_c_error(const char *message); -@@ -181,6 +183,7 @@ extern struct config_parser_state* cfg_parser; - %token VAR_FALLBACK_ENABLED VAR_TLS_ADDITIONAL_PORT VAR_LOW_RTT VAR_LOW_RTT_PERMIL - %token VAR_FAST_SERVER_PERMIL VAR_FAST_SERVER_NUM - %token VAR_ALLOW_NOTIFY VAR_TLS_WIN_CERT VAR_TCP_CONNECTION_LIMIT -+%token VAR_ANSWER_COOKIE VAR_COOKIE_SECRET - %token VAR_FORWARD_NO_CACHE VAR_STUB_NO_CACHE VAR_LOG_SERVFAIL VAR_DENY_ANY - %token VAR_UNKNOWN_SERVER_TIME_LIMIT VAR_LOG_TAG_QUERYREPLY - %token VAR_STREAM_WAIT_SIZE VAR_TLS_CIPHERS VAR_TLS_CIPHERSUITES VAR_TLS_USE_SNI -@@ -316,6 +319,7 @@ content_server: server_num_threads | server_verbosity | server_port | - server_unknown_server_time_limit | server_log_tag_queryreply | - server_stream_wait_size | server_tls_ciphers | - server_tls_ciphersuites | server_tls_session_ticket_keys | -+ server_answer_cookie | server_cookie_secret | - server_tls_use_sni | server_edns_client_string | - server_edns_client_string_opcode | server_nsid | - server_zonemd_permissive_mode | server_max_reuse_tcp_queries | -@@ -3695,6 +3699,30 @@ server_tcp_connection_limit: VAR_TCP_CONNECTION_LIMIT STRING_ARG STRING_ARG - } - } - ; -+server_answer_cookie: VAR_ANSWER_COOKIE STRING_ARG -+ { -+ OUTYY(("P(server_answer_cookie:%s)\n", $2)); -+ if(strcmp($2, "yes") != 0 && strcmp($2, "no") != 0) -+ yyerror("expected yes or no."); -+ else cfg_parser->cfg->do_answer_cookie = (strcmp($2, "yes")==0); -+ free($2); -+ } -+ ; -+server_cookie_secret: VAR_COOKIE_SECRET STRING_ARG -+ { -+ uint8_t secret[32]; -+ size_t secret_len = sizeof(secret); -+ -+ OUTYY(("P(server_cookie_secret:%s)\n", $2)); -+ if (sldns_str2wire_hex_buf($2, secret, &secret_len) -+ || ( secret_len != 16)) -+ yyerror("expected 128 bit hex string"); -+ else { -+ cfg_parser->cfg->cookie_secret_len = secret_len; -+ memcpy(cfg_parser->cfg->cookie_secret, secret, sizeof(secret)); -+ } -+ free($2); -+ } - ipsetstart: VAR_IPSET - { - OUTYY(("\nP(ipset:)\n")); -@@ -3764,10 +3792,11 @@ validate_acl_action(const char* action) - strcmp(action, "refuse_non_local")!=0 && - strcmp(action, "allow_setrd")!=0 && - strcmp(action, "allow")!=0 && -- strcmp(action, "allow_snoop")!=0) -+ strcmp(action, "allow_snoop")!=0 && -+ strcmp(action, "allow_cookie")!=0) - { - yyerror("expected deny, refuse, deny_non_local, " -- "refuse_non_local, allow, allow_setrd or " -- "allow_snoop as access control action"); -+ "refuse_non_local, allow, allow_setrd, " -+ "allow_snoop or allow_cookie as access control action"); - } - } -diff --git a/util/data/msgparse.c b/util/data/msgparse.c -index 5bb69d6ed..3059d555c 100644 ---- a/util/data/msgparse.c -+++ b/util/data/msgparse.c -@@ -951,11 +951,67 @@ edns_opt_list_append_keepalive(struct edns_option** list, int msec, - data, region); - } - -+int siphash(const uint8_t *in, const size_t inlen, -+ const uint8_t *k, uint8_t *out, const size_t outlen); -+ -+/** RFC 1982 comparison, uses unsigned integers, and tries to avoid -+ * compiler optimization (eg. by avoiding a-b<0 comparisons), -+ * this routine matches compare_serial(), for SOA serial number checks */ -+static int -+compare_1982(uint32_t a, uint32_t b) -+{ -+ /* for 32 bit values */ -+ const uint32_t cutoff = ((uint32_t) 1 << (32 - 1)); -+ -+ if (a == b) { -+ return 0; -+ } else if ((a < b && b - a < cutoff) || (a > b && a - b > cutoff)) { -+ return -1; -+ } else { -+ return 1; -+ } -+} -+ -+/** if we know that b is larger than a, return the difference between them, -+ * that is the distance between them. in RFC1982 arith */ -+static uint32_t -+subtract_1982(uint32_t a, uint32_t b) -+{ -+ /* for 32 bit values */ -+ const uint32_t cutoff = ((uint32_t) 1 << (32 - 1)); -+ -+ if(a == b) -+ return 0; -+ if(a < b && b - a < cutoff) { -+ return b-a; -+ } -+ if(a > b && a - b > cutoff) { -+ return ((uint32_t)0xffffffff) - (a-b-1); -+ } -+ /* wrong case, b smaller than a */ -+ return 0; -+} -+ -+ -+static uint8_t * -+cookie_hash(uint8_t *hash, uint8_t *buf, -+ struct sockaddr_storage *addr, uint8_t *secret) -+{ -+ if (addr->ss_family == AF_INET6) { -+ memcpy(buf+16, &((struct sockaddr_in6 *)addr)->sin6_addr, 16); -+ siphash(buf, 32, secret, hash, 8); -+ } else { -+ memcpy(buf+16, &((struct sockaddr_in *)addr)->sin_addr, 4); -+ siphash(buf, 20, secret, hash, 8); -+ } -+ return hash; -+} -+ - /** parse EDNS options from EDNS wireformat rdata */ - static int - parse_edns_options_from_query(uint8_t* rdata_ptr, size_t rdata_len, - struct edns_data* edns, struct config_file* cfg, struct comm_point* c, -- struct regional* region) -+ struct comm_reply* repinfo, uint32_t now, struct regional* region) - { - /* To respond with a Keepalive option, the client connection must have - * received one message with a TCP Keepalive EDNS option, and that -@@ -979,6 +1035,10 @@ parse_edns_options_from_query(uint8_t* rdata_ptr, size_t rdata_len, - while(rdata_len >= 4) { - uint16_t opt_code = sldns_read_uint16(rdata_ptr); - uint16_t opt_len = sldns_read_uint16(rdata_ptr+2); -+ uint8_t server_cookie[40], hash[8]; -+ uint32_t cookie_time, subt_1982; -+ int comp_1982; -+ - rdata_ptr += 4; - rdata_len -= 4; - if(opt_len > rdata_len) -@@ -1041,6 +1101,86 @@ parse_edns_options_from_query(uint8_t* rdata_ptr, size_t rdata_len, - edns->padding_block_size = cfg->pad_responses_block_size; - break; - -+ case LDNS_EDNS_COOKIE: -+ if(!cfg || !cfg->do_answer_cookie) -+ break; -+ if(opt_len != 8 && (opt_len < 16 || opt_len > 40)) { -+ verbose(VERB_ALGO, "worker request: " -+ "badly formatted cookie"); -+ return LDNS_RCODE_FORMERR; -+ } -+ edns->cookie_present = 1; -+ -+ /* Copy client cookie, version and timestamp for -+ * validation and creation purposes. -+ */ -+ memcpy(server_cookie, rdata_ptr, 16); -+ -+ /* In the "if, if else" block below, we validate a -+ * RFC9018 cookie. If it doesn't match the recipe, or -+ * if it doesn't validate, or if the cookie is too old -+ * (< 30 min), a new cookie is generated. -+ */ -+ if (opt_len != 24) -+ ; /* RFC9018 cookies are 24 bytes long */ -+ -+ else if (cfg->cookie_secret_len != 16) -+ ; /* RFC9018 cookies have 16 byte secrets */ -+ -+ else if (rdata_ptr[8] != 1) -+ ; /* RFC9018 cookies are cookie version 1 */ -+ -+ else if ((comp_1982 = compare_1982(now, -+ (cookie_time = sldns_read_uint32(rdata_ptr + 12)))) > 0 -+ && (subt_1982 = subtract_1982(cookie_time, now)) > 3600) -+ ; /* Cookie is older than 1 hour -+ * (see RFC9018 Section 4.3.) -+ */ -+ -+ else if (comp_1982 <= 0 -+ && subtract_1982(now, cookie_time) > 300) -+ ; /* Cookie time is more than 5 minutes in the -+ * future. (see RFC9018 Section 4.3.) -+ */ -+ -+ else if (memcmp( cookie_hash( hash, server_cookie -+ , &repinfo->remote_addr -+ , cfg->cookie_secret) -+ , rdata_ptr + 16 , 8 ) == 0) { -+ -+ /* Cookie is valid! */ -+ edns->cookie_valid = 1; -+ if (comp_1982 > 0 && subt_1982 > 1800) -+ ; /* But older than 30 minutes, -+ * so create a new one anyway */ -+ -+ else if (!edns_opt_list_append( /* Reuse cookie */ -+ &edns->opt_list_out, LDNS_EDNS_COOKIE, opt_len, -+ rdata_ptr, region)) { -+ log_err("out of memory"); -+ return LDNS_RCODE_SERVFAIL; -+ } else -+ /* Cookie to be reused added to -+ * outgoing options. Done! -+ */ -+ break; -+ } -+ /* Add a new server cookie to outgoing cookies */ -+ server_cookie[ 8] = 1; /* Version */ -+ server_cookie[ 9] = 0; /* Reserved */ -+ server_cookie[10] = 0; /* Reserved */ -+ server_cookie[11] = 0; /* Reserved */ -+ sldns_write_uint32(server_cookie + 12, now); -+ cookie_hash( hash, server_cookie -+ , &repinfo->remote_addr, cfg->cookie_secret); -+ memcpy(server_cookie + 16, hash, 8); -+ if (!edns_opt_list_append( &edns->opt_list_out -+ , LDNS_EDNS_COOKIE -+ , 24, server_cookie, region)) { -+ log_err("out of memory"); -+ return LDNS_RCODE_SERVFAIL; -+ } -+ break; - default: - break; - } -@@ -1115,6 +1255,8 @@ parse_extract_edns_from_response_msg(struct msg_parse* msg, - edns->opt_list_out = NULL; - edns->opt_list_inplace_cb_out = NULL; - edns->padding_block_size = 0; -+ edns->cookie_present = 0; -+ edns->cookie_valid = 0; - - /* take the options */ - rdata_len = found->rr_first->size-2; -@@ -1170,7 +1312,8 @@ skip_pkt_rrs(sldns_buffer* pkt, int num) - - int - parse_edns_from_query_pkt(sldns_buffer* pkt, struct edns_data* edns, -- struct config_file* cfg, struct comm_point* c, struct regional* region) -+ struct config_file* cfg, struct comm_point* c, -+ struct comm_reply* repinfo, time_t now, struct regional* region) - { - size_t rdata_len; - uint8_t* rdata_ptr; -@@ -1206,6 +1349,8 @@ parse_edns_from_query_pkt(sldns_buffer* pkt, struct edns_data* edns, - edns->opt_list_out = NULL; - edns->opt_list_inplace_cb_out = NULL; - edns->padding_block_size = 0; -+ edns->cookie_present = 0; -+ edns->cookie_valid = 0; - - /* take the options */ - rdata_len = sldns_buffer_read_u16(pkt); -@@ -1214,7 +1359,7 @@ parse_edns_from_query_pkt(sldns_buffer* pkt, struct edns_data* edns, - rdata_ptr = sldns_buffer_current(pkt); - /* ignore rrsigs */ - return parse_edns_options_from_query(rdata_ptr, rdata_len, edns, cfg, -- c, region); -+ c, repinfo, now, region); - } - - void -diff --git a/util/data/msgparse.h b/util/data/msgparse.h -index 0c458e6e8..aebeb2a88 100644 ---- a/util/data/msgparse.h -+++ b/util/data/msgparse.h -@@ -72,6 +72,7 @@ struct regional; - struct edns_option; - struct config_file; - struct comm_point; -+struct comm_reply; - - /** number of buckets in parse rrset hash table. Must be power of 2. */ - #define PARSE_TABLE_SIZE 32 -@@ -217,8 +218,6 @@ struct rr_parse { - * region. - */ - struct edns_data { -- /** if EDNS OPT record was present */ -- int edns_present; - /** Extended RCODE */ - uint8_t ext_rcode; - /** The EDNS version number */ -@@ -238,7 +237,13 @@ struct edns_data { - struct edns_option* opt_list_inplace_cb_out; - /** block size to pad */ - uint16_t padding_block_size; --}; -+ /** if EDNS OPT record was present */ -+ unsigned int edns_present : 1; -+ /** if a cookie was present */ -+ unsigned int cookie_present : 1; -+ /** if the cookie validated */ -+ unsigned int cookie_valid : 1; -+}; - - /** - * EDNS option -@@ -315,7 +320,8 @@ int skip_pkt_rrs(struct sldns_buffer* pkt, int num); - * RCODE formerr if OPT is badly formatted and so on. - */ - int parse_edns_from_query_pkt(struct sldns_buffer* pkt, struct edns_data* edns, -- struct config_file* cfg, struct comm_point* c, struct regional* region); -+ struct config_file* cfg, struct comm_point* c, -+ struct comm_reply* repinfo, time_t now, struct regional* region); - - /** - * Calculate hash value for rrset in packet. -diff --git a/util/siphash.c b/util/siphash.c -new file mode 100644 -index 000000000..d69f4b579 ---- /dev/null -+++ b/util/siphash.c -@@ -0,0 +1,165 @@ -+/* -+ SipHash reference C implementation -+ -+ Copyright (c) 2012-2016 Jean-Philippe Aumasson -+ -+ Copyright (c) 2012-2014 Daniel J. Bernstein -+ -+ To the extent possible under law, the author(s) have dedicated all copyright -+ and related and neighboring rights to this software to the public domain -+ worldwide. This software is distributed without any warranty. -+ -+ You should have received a copy of the CC0 Public Domain Dedication along -+ with -+ this software. If not, see -+ . -+ */ -+#include -+#include -+#include -+#include -+ -+/* default: SipHash-2-4 */ -+#define cROUNDS 2 -+#define dROUNDS 4 -+ -+#define ROTL(x, b) (uint64_t)(((x) << (b)) | ((x) >> (64 - (b)))) -+ -+#define U32TO8_LE(p, v) \ -+ (p)[0] = (uint8_t)((v)); \ -+ (p)[1] = (uint8_t)((v) >> 8); \ -+ (p)[2] = (uint8_t)((v) >> 16); \ -+ (p)[3] = (uint8_t)((v) >> 24); -+ -+#define U64TO8_LE(p, v) \ -+ U32TO8_LE((p), (uint32_t)((v))); \ -+ U32TO8_LE((p) + 4, (uint32_t)((v) >> 32)); -+ -+#define U8TO64_LE(p) \ -+ (((uint64_t)((p)[0])) | ((uint64_t)((p)[1]) << 8) | \ -+ ((uint64_t)((p)[2]) << 16) | ((uint64_t)((p)[3]) << 24) | \ -+ ((uint64_t)((p)[4]) << 32) | ((uint64_t)((p)[5]) << 40) | \ -+ ((uint64_t)((p)[6]) << 48) | ((uint64_t)((p)[7]) << 56)) -+ -+#define SIPROUND \ -+ do { \ -+ v0 += v1; \ -+ v1 = ROTL(v1, 13); \ -+ v1 ^= v0; \ -+ v0 = ROTL(v0, 32); \ -+ v2 += v3; \ -+ v3 = ROTL(v3, 16); \ -+ v3 ^= v2; \ -+ v0 += v3; \ -+ v3 = ROTL(v3, 21); \ -+ v3 ^= v0; \ -+ v2 += v1; \ -+ v1 = ROTL(v1, 17); \ -+ v1 ^= v2; \ -+ v2 = ROTL(v2, 32); \ -+ } while (0) -+ -+#ifdef DEBUG -+#define TRACE \ -+ do { \ -+ printf("(%3d) v0 %08x %08x\n", (int)inlen, (uint32_t)(v0 >> 32), \ -+ (uint32_t)v0); \ -+ printf("(%3d) v1 %08x %08x\n", (int)inlen, (uint32_t)(v1 >> 32), \ -+ (uint32_t)v1); \ -+ printf("(%3d) v2 %08x %08x\n", (int)inlen, (uint32_t)(v2 >> 32), \ -+ (uint32_t)v2); \ -+ printf("(%3d) v3 %08x %08x\n", (int)inlen, (uint32_t)(v3 >> 32), \ -+ (uint32_t)v3); \ -+ } while (0) -+#else -+#define TRACE -+#endif -+ -+int siphash(const uint8_t *in, const size_t inlen, const uint8_t *k, -+ uint8_t *out, const size_t outlen) { -+ -+ assert((outlen == 8) || (outlen == 16)); -+ uint64_t v0 = 0x736f6d6570736575ULL; -+ uint64_t v1 = 0x646f72616e646f6dULL; -+ uint64_t v2 = 0x6c7967656e657261ULL; -+ uint64_t v3 = 0x7465646279746573ULL; -+ uint64_t k0 = U8TO64_LE(k); -+ uint64_t k1 = U8TO64_LE(k + 8); -+ uint64_t m; -+ int i; -+ const uint8_t *end = in + inlen - (inlen % sizeof(uint64_t)); -+ const int left = inlen & 7; -+ uint64_t b = ((uint64_t)inlen) << 56; -+ v3 ^= k1; -+ v2 ^= k0; -+ v1 ^= k1; -+ v0 ^= k0; -+ -+ if (outlen == 16) -+ v1 ^= 0xee; -+ -+ for (; in != end; in += 8) { -+ m = U8TO64_LE(in); -+ v3 ^= m; -+ -+ TRACE; -+ for (i = 0; i < cROUNDS; ++i) -+ SIPROUND; -+ -+ v0 ^= m; -+ } -+ -+ switch (left) { -+ case 7: -+ b |= ((uint64_t)in[6]) << 48; -+ case 6: -+ b |= ((uint64_t)in[5]) << 40; -+ case 5: -+ b |= ((uint64_t)in[4]) << 32; -+ case 4: -+ b |= ((uint64_t)in[3]) << 24; -+ case 3: -+ b |= ((uint64_t)in[2]) << 16; -+ case 2: -+ b |= ((uint64_t)in[1]) << 8; -+ case 1: -+ b |= ((uint64_t)in[0]); -+ break; -+ case 0: -+ break; -+ } -+ -+ v3 ^= b; -+ -+ TRACE; -+ for (i = 0; i < cROUNDS; ++i) -+ SIPROUND; -+ -+ v0 ^= b; -+ -+ if (outlen == 16) -+ v2 ^= 0xee; -+ else -+ v2 ^= 0xff; -+ -+ TRACE; -+ for (i = 0; i < dROUNDS; ++i) -+ SIPROUND; -+ -+ b = v0 ^ v1 ^ v2 ^ v3; -+ U64TO8_LE(out, b); -+ -+ if (outlen == 8) -+ return 0; -+ -+ v1 ^= 0xdd; -+ -+ TRACE; -+ for (i = 0; i < dROUNDS; ++i) -+ SIPROUND; -+ -+ b = v0 ^ v1 ^ v2 ^ v3; -+ U64TO8_LE(out + 8, b); -+ -+ return 0; -+} -diff --git a/validator/autotrust.c b/validator/autotrust.c -index 3cdf9ceae..3011a0ace 100644 ---- a/validator/autotrust.c -+++ b/validator/autotrust.c -@@ -2376,6 +2376,8 @@ probe_anchor(struct module_env* env, struct trust_anchor* tp) - edns.opt_list_out = NULL; - edns.opt_list_inplace_cb_out = NULL; - edns.padding_block_size = 0; -+ edns.cookie_present = 0; -+ edns.cookie_valid = 0; - if(sldns_buffer_capacity(buf) < 65535) - edns.udp_size = (uint16_t)sldns_buffer_capacity(buf); - else edns.udp_size = 65535; diff --git a/backport-pre-CVE-2024-33655-Fix-out-of-bounds-read-in-parse_edns_options_from_query.patch b/backport-pre-CVE-2024-33655-Fix-out-of-bounds-read-in-parse_edns_options_from_query.patch deleted file mode 100644 index 2ad2c2331a774f093153382f55dd601f5905793f..0000000000000000000000000000000000000000 --- a/backport-pre-CVE-2024-33655-Fix-out-of-bounds-read-in-parse_edns_options_from_query.patch +++ /dev/null @@ -1,30 +0,0 @@ -From 1c85901cc4c0f7693183f4b280604619e56cba00 Mon Sep 17 00:00:00 2001 -From: "W.C.A. Wijngaards" -Date: Wed, 16 Aug 2023 16:58:49 +0200 -Subject: [PATCH] - Fix out of bounds read in parse_edns_options_from_query, it - would read 8 bytes after a client option of length 8, and then ignore them - to recreate a 24 byte response. The fixup does not read out of bounds, - and puts zeroes in the buffer at that point, that then are ignored. - ---- - util/data/msgparse.c | 7 ++++++- - 1 file changed, 6 insertions(+), 1 deletion(-) - -diff --git a/util/data/msgparse.c b/util/data/msgparse.c -index 40189d613..b5414c6d0 100644 ---- a/util/data/msgparse.c -+++ b/util/data/msgparse.c -@@ -1049,7 +1049,12 @@ parse_edns_options_from_query(uint8_t* rdata_ptr, size_t rdata_len, - /* Copy client cookie, version and timestamp for - * validation and creation purposes. - */ -- memmove(server_cookie, rdata_ptr, 16); -+ if(opt_len >= 16) { -+ memmove(server_cookie, rdata_ptr, 16); -+ } else { -+ memset(server_cookie, 0, 16); -+ memmove(server_cookie, rdata_ptr, opt_len); -+ } - - /* In the "if, if else" block below, we validate a - * RFC9018 cookie. If it doesn't match the recipe, or diff --git a/backport-pre-CVE-2024-33655-Fix-possibly-unaligned-memory-access-in-parse_edns_options_from_query.patch b/backport-pre-CVE-2024-33655-Fix-possibly-unaligned-memory-access-in-parse_edns_options_from_query.patch deleted file mode 100644 index fd49490d46d1266e3262d431df8f97b16c4fcb39..0000000000000000000000000000000000000000 --- a/backport-pre-CVE-2024-33655-Fix-possibly-unaligned-memory-access-in-parse_edns_options_from_query.patch +++ /dev/null @@ -1,22 +0,0 @@ -From 2b1028bdad6da4d42f0571d0182322c3554e0bcc Mon Sep 17 00:00:00 2001 -From: "W.C.A. Wijngaards" -Date: Wed, 16 Aug 2023 10:06:06 +0200 -Subject: [PATCH] - Fix possibly unaligned memory access. - ---- - util/data/msgparse.c | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/util/data/msgparse.c b/util/data/msgparse.c -index 8cef37a17..40189d613 100644 ---- a/util/data/msgparse.c -+++ b/util/data/msgparse.c -@@ -1049,7 +1049,7 @@ parse_edns_options_from_query(uint8_t* rdata_ptr, size_t rdata_len, - /* Copy client cookie, version and timestamp for - * validation and creation purposes. - */ -- memcpy(server_cookie, rdata_ptr, 16); -+ memmove(server_cookie, rdata_ptr, 16); - - /* In the "if, if else" block below, we validate a - * RFC9018 cookie. If it doesn't match the recipe, or diff --git a/backport-pre-CVE-2024-33655-extended_error_encode-for-extended-errors.patch b/backport-pre-CVE-2024-33655-extended_error_encode-for-extended-errors.patch deleted file mode 100644 index e0bee0fb906680198f389731b4ea26a9f9cbf26b..0000000000000000000000000000000000000000 --- a/backport-pre-CVE-2024-33655-extended_error_encode-for-extended-errors.patch +++ /dev/null @@ -1,130 +0,0 @@ -From 71f23ef354fae12e99963fe43200d38dfe796222 Mon Sep 17 00:00:00 2001 -From: Willem Toorop -Date: Wed, 28 Sep 2022 09:57:56 +0200 -Subject: [PATCH] extended_error_encode() for extended errors - ---- - daemon/worker.c | 14 ++------------ - util/data/msgencode.c | 18 ++++++++++++++---- - util/data/msgencode.h | 19 ++++++++++++++++++- - 3 files changed, 34 insertions(+), 17 deletions(-) - -diff --git a/daemon/worker.c b/daemon/worker.c -index 29c1961ed..c2a94be79 100644 ---- a/daemon/worker.c -+++ b/daemon/worker.c -@@ -1439,8 +1439,6 @@ worker_handle_request(struct comm_point* c, void* arg, int error, - repinfo->client_addrlen); - memset(&reply_edns, 0, sizeof(reply_edns)); - reply_edns.edns_present = 1; -- reply_edns.udp_size = EDNS_ADVERTISED_SIZE; -- LDNS_RCODE_SET(sldns_buffer_begin(c->buffer), ret); - error_encode(c->buffer, ret, &qinfo, - *(uint16_t*)(void *)sldns_buffer_begin(c->buffer), - sldns_buffer_read_u16_at(c->buffer, 2), &reply_edns); -@@ -1449,23 +1447,15 @@ worker_handle_request(struct comm_point* c, void* arg, int error, - } - if(edns.edns_present) { - if(edns.edns_version != 0) { -- edns.ext_rcode = (uint8_t)(EDNS_RCODE_BADVERS>>4); -- edns.edns_version = EDNS_ADVERTISED_VERSION; -- edns.udp_size = EDNS_ADVERTISED_SIZE; -- edns.bits &= EDNS_DO; - edns.opt_list_in = NULL; - edns.opt_list_out = NULL; - edns.opt_list_inplace_cb_out = NULL; -- edns.padding_block_size = 0; - verbose(VERB_ALGO, "query with bad edns version."); - log_addr(VERB_CLIENT, "from", &repinfo->client_addr, - repinfo->client_addrlen); -- error_encode(c->buffer, EDNS_RCODE_BADVERS&0xf, &qinfo, -+ extended_error_encode(c->buffer, EDNS_RCODE_BADVERS, &qinfo, - *(uint16_t*)(void *)sldns_buffer_begin(c->buffer), -- sldns_buffer_read_u16_at(c->buffer, 2), NULL); -- if(sldns_buffer_capacity(c->buffer) >= -- sldns_buffer_limit(c->buffer)+calc_edns_field_size(&edns)) -- attach_edns_record(c->buffer, &edns); -+ sldns_buffer_read_u16_at(c->buffer, 2), 0, &edns); - regional_free_all(worker->scratchpad); - goto send_reply; - } -diff --git a/util/data/msgencode.c b/util/data/msgencode.c -index fe21cfb86..d832ddc91 100644 ---- a/util/data/msgencode.c -+++ b/util/data/msgencode.c -@@ -959,14 +959,15 @@ qinfo_query_encode(sldns_buffer* pkt, struct query_info* qinfo) - } - - void --error_encode(sldns_buffer* buf, int r, struct query_info* qinfo, -- uint16_t qid, uint16_t qflags, struct edns_data* edns) -+extended_error_encode(sldns_buffer* buf, uint16_t rcode, struct query_info* qinfo, -+ uint16_t qid, uint16_t qflags, uint16_t xflags, struct edns_data* edns) - { - uint16_t flags; - - sldns_buffer_clear(buf); - sldns_buffer_write(buf, &qid, sizeof(uint16_t)); -- flags = (uint16_t)(BIT_QR | BIT_RA | r); /* QR and retcode*/ -+ flags = (uint16_t)(BIT_QR | BIT_RA | (rcode & 0xF)); /* QR and retcode*/ -+ flags |= xflags; - flags |= (qflags & (BIT_RD|BIT_CD)); /* copy RD and CD bit */ - sldns_buffer_write_u16(buf, flags); - if(qinfo) flags = 1; -@@ -993,7 +994,7 @@ error_encode(sldns_buffer* buf, int r, struct query_info* qinfo, - struct edns_data es = *edns; - es.edns_version = EDNS_ADVERTISED_VERSION; - es.udp_size = EDNS_ADVERTISED_SIZE; -- es.ext_rcode = 0; -+ es.ext_rcode = (uint8_t)(rcode >> 4); - es.bits &= EDNS_DO; - if(sldns_buffer_limit(buf) + calc_edns_field_size(&es) > - edns->udp_size) -@@ -1001,3 +1002,12 @@ error_encode(sldns_buffer* buf, int r, struct query_info* qinfo, - attach_edns_record(buf, &es); - } - } -+ -+void -+error_encode(sldns_buffer* buf, int r, struct query_info* qinfo, -+ uint16_t qid, uint16_t qflags, struct edns_data* edns) -+{ -+ extended_error_encode(buf, (r & 0x000F), qinfo, qid, qflags -+ , (r & 0xFFF0), edns); -+} -+ -diff --git a/util/data/msgencode.h b/util/data/msgencode.h -index 30dc515cb..1b37ca870 100644 ---- a/util/data/msgencode.h -+++ b/util/data/msgencode.h -@@ -120,7 +120,7 @@ void attach_edns_record(struct sldns_buffer* pkt, struct edns_data* edns); - * Encode an error. With QR and RA set. - * - * @param pkt: where to store the packet. -- * @param r: RCODE value to encode. -+ * @param r: RCODE value to encode (may contain extra flags). - * @param qinfo: if not NULL, the query is included. - * @param qid: query ID to set in packet. network order. - * @param qflags: original query flags (to copy RD and CD bits). host order. -@@ -130,4 +130,21 @@ void attach_edns_record(struct sldns_buffer* pkt, struct edns_data* edns); - void error_encode(struct sldns_buffer* pkt, int r, struct query_info* qinfo, - uint16_t qid, uint16_t qflags, struct edns_data* edns); - -+/** -+ * Encode an extended error. With QR and RA set. -+ * -+ * @param pkt: where to store the packet. -+ * @param rcode: Extended RCODE value to encode. -+ * @param qinfo: if not NULL, the query is included. -+ * @param qid: query ID to set in packet. network order. -+ * @param qflags: original query flags (to copy RD and CD bits). host order. -+ * @param xflags: extra flags to set (such as for example BIT_AA and/or BIT_TC) -+ * @param edns: if not NULL, this is the query edns info, -+ * and an edns reply is attached. Only attached if EDNS record fits reply. -+ * Without edns extended errors (i.e. > 15 )will not be conveyed. -+ */ -+void extended_error_encode(struct sldns_buffer* pkt, uint16_t rcode, -+ struct query_info* qinfo, uint16_t qid, uint16_t qflags, -+ uint16_t xflags, struct edns_data* edns); -+ - #endif /* UTIL_DATA_MSGENCODE_H */ diff --git a/backport-unbound-config.patch b/backport-unbound-config.patch new file mode 100644 index 0000000000000000000000000000000000000000..9fe78c04178acb723b204536599c0e9fdac09446 --- /dev/null +++ b/backport-unbound-config.patch @@ -0,0 +1,483 @@ +From aa201e383210d02c0396d0a1375d217551c0e2bd Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Petr=20Men=C5=A1=C3=ADk?= +Date: Fri, 15 Nov 2024 08:57:14 +0100 +Subject: [PATCH] Customize unbound.conf for defaults + +Set some specific changes to example configuration file. By +patching upstream provided config file we would not need to manually +update external copy in source RPM. +--- + doc/example.conf.in | 152 ++++++++++++++++++++++++++++++-------------- + 1 file changed, 104 insertions(+), 48 deletions(-) + +diff --git a/doc/example.conf.in b/doc/example.conf.in +index 59090c6..33c6209 100644 +--- a/doc/example.conf.in ++++ b/doc/example.conf.in +@@ -17,11 +17,12 @@ server: + # whitespace is not necessary, but looks cleaner. + + # verbosity number, 0 is least verbose. 1 is default. +- # verbosity: 1 ++ verbosity: 1 + + # print statistics to the log (for every thread) every N seconds. + # Set to "" or 0 to disable. Default is disabled. +- # statistics-interval: 0 ++ # Needs to be disabled for munin plugin ++ statistics-interval: 0 + + # enable shm for stats, default no. if you enable also enable + # statistics-interval, every time it also writes stats to the +@@ -32,11 +33,13 @@ server: + # shm-key: 11777 + + # enable cumulative statistics, without clearing them after printing. +- # statistics-cumulative: no ++ # Needs to be disabled for munin plugin ++ statistics-cumulative: no + + # enable extended statistics (query types, answer codes, status) +- # printed from unbound-control. Default off, because of speed. +- # extended-statistics: no ++ # printed from unbound-control. default off, because of speed. ++ # Needs to be enabled for munin plugin ++ extended-statistics: yes + + # Inhibits selected extended statistics (qtype, qclass, qopcode, rcode, + # rpz-actions) from printing if their value is 0. +@@ -44,22 +47,35 @@ server: + # statistics-inhibit-zero: yes + + # number of threads to create. 1 disables threading. +- # num-threads: 1 ++ num-threads: 4 + + # specify the interfaces to answer queries from by ip-address. + # The default is to listen to localhost (127.0.0.1 and ::1). + # 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. +- # interface-automatic: no ++ # interface-automatic: yes ++ # ++ # NOTE: Enable this option when specifying interface 0.0.0.0 or ::0 ++ # NOTE: Disabled per Fedora policy not to listen to * on default install ++ # NOTE: If deploying on non-default port, eg 80/443, this needs to be disabled ++ interface-automatic: no + + # instead of the default port, open additional ports separated by + # spaces when interface-automatic is enabled, by listing them here. +@@ -94,7 +110,8 @@ server: + + # permit Unbound to use this port number or port range for + # making outgoing queries, using an outgoing interface. +- # outgoing-port-permit: 32768 ++ # Only ephemeral ports are allowed by SElinux ++ outgoing-port-permit: 32768-60999 + + # deny Unbound the use this of port number or port range for + # making outgoing queries, using an outgoing interface. +@@ -103,7 +120,9 @@ server: + # IANA-assigned port numbers. + # If multiple outgoing-port-permit and outgoing-port-avoid options + # are present, they are processed in order. +- # outgoing-port-avoid: "3200-3208" ++ # Our SElinux policy does not allow non-ephemeral ports to be used ++ outgoing-port-avoid: 0-32767 ++ outgoing-port-avoid: 61000-65535 + + # number of outgoing simultaneous tcp buffers to hold per thread. + # outgoing-num-tcp: 10 +@@ -121,12 +140,12 @@ server: + + # use SO_REUSEPORT to distribute queries over threads. + # at extreme load it could be better to turn it off to distribute even. +- # so-reuseport: yes ++ so-reuseport: yes + + # use IP_TRANSPARENT so the interface: addresses can be non-local + # and you can config non-existing IPs that are going to work later on + # (uses IP_BINDANY on FreeBSD). +- # ip-transparent: no ++ ip-transparent: yes + + # use IP_FREEBIND so the interface: addresses can be non-local + # and you can bind to nonexisting IPs and interfaces that are down. +@@ -285,6 +304,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". +@@ -310,7 +331,7 @@ server: + # tcp-idle-timeout: 30000 + + # Enable EDNS TCP keepalive option. +- # edns-tcp-keepalive: no ++ edns-tcp-keepalive: yes + + # Timeout for EDNS TCP keepalive, in msec. Overrides tcp-idle-timeout + # if edns-tcp-keepalive is set. +@@ -320,6 +341,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 + +@@ -433,6 +457,7 @@ server: + # + # If you give "" no chroot is performed. The path must not end in a /. + # chroot: "@UNBOUND_CHROOT_DIR@" ++ chroot: "" + + # if given, user privileges are dropped (after binding port), + # and the given username is assumed. Default is user "unbound". +@@ -444,7 +469,7 @@ server: + # is not changed. + # If you give a server: directory: dir before include: file statements + # then those includes can be relative to the working directory. +- # directory: "@UNBOUND_RUN_DIR@" ++ directory: "/etc/unbound" + + # the log file, "" means log to stderr. + # Use of this option sets use-syslog to "no". +@@ -459,7 +484,7 @@ server: + # log-identity: "" + + # print UTC timestamp in ascii to logfile, default is epoch in seconds. +- # log-time-ascii: no ++ log-time-ascii: yes + + # log timestamp in ISO8601 format if also log-time-ascii is enabled. + # (y-m-dTh:m:s.msec[+-]tzhours:tzminutes) +@@ -532,13 +557,13 @@ server: + # harden-short-bufsize: yes + + # Harden against unseemly large queries. +- # harden-large-queries: no ++ harden-large-queries: yes + + # Harden against out of zone rrsets, to avoid spoofing attempts. + # harden-glue: yes + + # Harden against unverified (outside-zone, including sibling zone) glue rrsets +- # harden-unverified-glue: no ++ harden-unverified-glue: yes + + # Harden against receiving dnssec-stripped data. If you turn it + # off, failing to validate dnskey data for a trustanchor will +@@ -553,7 +578,7 @@ server: + # infrastructure data. Validates the replies (if possible). + # Default off, because the lookups burden the server. Experimental + # implementation of draft-wijngaards-dnsext-resolver-side-mitigation. +- # harden-referral-path: no ++ harden-referral-path: yes + + # Harden against algorithm downgrade when multiple algorithms are + # advertised in the DS record. If no, allows the weakest algorithm +@@ -567,7 +592,7 @@ server: + # Sent minimum amount of information to upstream servers to enhance + # privacy. Only sent minimum required labels of the QNAME and set QTYPE + # to A when possible. +- # qname-minimisation: yes ++ qname-minimisation: yes + + # QNAME minimisation in strict mode. Do not fall-back to sending full + # QNAME to potentially broken nameservers. A lot of domains will not be +@@ -577,7 +602,7 @@ server: + + # Aggressive NSEC uses the DNSSEC NSEC chain to synthesize NXDOMAIN + # and other denials, using information from previous NXDOMAINs answers. +- # aggressive-nsec: yes ++ aggressive-nsec: yes + + # Use 0x20-encoded random bits in the query to foil spoof attempts. + # This feature is an experimental implementation of draft dns-0x20. +@@ -610,7 +635,7 @@ server: + # threshold, a warning is printed and a defensive action is taken, + # the cache is cleared to flush potential poison out of it. + # A suggested value is 10000000, the default is 0 (turned off). +- # unwanted-reply-threshold: 0 ++ unwanted-reply-threshold: 10000000 + + # Do not query the following addresses. No DNS queries are sent there. + # List one address per entry. List classless netblocks with /size, +@@ -622,20 +647,20 @@ server: + # do-not-query-localhost: yes + + # if yes, perform prefetching of almost expired message cache entries. +- # prefetch: no ++ prefetch: yes + + # if yes, perform key lookups adjacent to normal lookups. +- # prefetch-key: no ++ prefetch-key: yes + + # deny queries of type ANY with an empty response. +- # deny-any: no ++ deny-any: yes + + # if yes, Unbound rotates RRSet order in response. +- # rrset-roundrobin: yes ++ rrset-roundrobin: yes + + # if yes, Unbound doesn't insert authority/additional sections + # into response messages when those sections are not required. +- # minimal-responses: yes ++ minimal-responses: yes + + # true to disable DNSSEC lameness check in iterator. + # disable-dnssec-lame-check: no +@@ -645,7 +670,9 @@ server: + # most modules have to be listed at the beginning of the line, + # except cachedb(just before iterator), and python (at the beginning, + # or, just before the iterator). +- # module-config: "validator iterator" ++ # For redis cachedb use: ++ # "ipsecmod validator cachedb iterator" ++ module-config: "ipsecmod validator iterator" + + # File with trusted keys, kept uptodate using RFC5011 probes, + # initial file like trust-anchor-file, then it stores metadata. +@@ -659,10 +686,10 @@ server: + # auto-trust-anchor-file: "@UNBOUND_ROOTKEY_FILE@" + + # trust anchor signaling sends a RFC8145 key tag query after priming. +- # trust-anchor-signaling: yes ++ trust-anchor-signaling: yes + + # Root key trust anchor sentinel (draft-ietf-dnsop-kskroll-sentinel) +- # root-key-sentinel: yes ++ root-key-sentinel: yes + + # File with trusted keys for validation. Specify more than one file + # with several entries, one file per entry. +@@ -683,6 +710,9 @@ server: + # the trusted-keys { name flag proto algo "key"; }; clauses are read. + # you need external update procedures to track changes in keys. + # trusted-keys-file: "" ++ # ++ trusted-keys-file: /etc/unbound/keys.d/*.key ++ auto-trust-anchor-file: "/var/lib/unbound/root.key" + + # Ignore chain of trust. Domain is treated as insecure. + # domain-insecure: "example.com" +@@ -710,14 +740,15 @@ server: + # unsecure data. Useful to shield the users of this validator from + # potential bogus data in the additional section. All unsigned data + # in the additional section is removed from secure messages. +- # val-clean-additional: yes ++ val-clean-additional: yes + + # Turn permissive mode on to permit bogus messages. Thus, messages + # for which security checks failed will be returned to clients, + # instead of SERVFAIL. It still performs the security checks, which + # result in interesting log files and possibly the AD bit in + # replies if the message is found secure. The default is off. +- # val-permissive-mode: no ++ # NOTE: TURNING THIS ON DISABLES ALL DNSSEC SECURITY ++ val-permissive-mode: no + + # Ignore the CD flag in incoming queries and refuse them bogus data. + # Enable it if the only clients of Unbound are legacy servers (w2008) +@@ -731,11 +762,11 @@ server: + + # Serve expired responses from cache, with serve-expired-reply-ttl in + # the response, and then attempt to fetch the data afresh. +- # serve-expired: no ++ serve-expired: yes + # + # Limit serving of expired responses to configured seconds after + # expiration. 0 disables the limit. +- # serve-expired-ttl: 0 ++ serve-expired-ttl: 14400 + # + # Set the TTL of expired records to the serve-expired-ttl value after a + # failed attempt to retrieve the record from upstream. This makes sure +@@ -762,7 +793,7 @@ server: + + # Have the validator log failed validations for your diagnosis. + # 0: off. 1: A line per failed user query. 2: With reason and bad IP. +- # val-log-level: 0 ++ val-log-level: 1 + + # It is possible to configure NSEC3 maximum iteration counts per + # keysize. Keep this table very short, as linear search is done. +@@ -906,6 +937,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" + +@@ -916,8 +949,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 +@@ -926,6 +959,8 @@ server: + # tls-ciphers: "DHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-SHA256:DHE-RSA-AES128-SHA256:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA256" + # cipher setting for TLSv1.3 + # tls-ciphersuites: "TLS_AES_128_GCM_SHA256:TLS_AES_128_CCM_8_SHA256:TLS_AES_128_CCM_SHA256:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256" ++ # Fedora/RHEL: use system-wide crypto policies ++ tls-ciphers: "PROFILE=SYSTEM" + + # Pad responses to padded queries received over TLS + # pad-responses: yes +@@ -1070,11 +1105,12 @@ server: + + # Enable to attach Extended DNS Error codes (RFC8914) to responses. + # ede: no ++ ede: yes + + # Enable to attach an Extended DNS Error (RFC8914) Code 3 - Stale + # Answer as EDNS0 option to expired responses. + # Note that the ede option above needs to be enabled for this to work. +- # ede-serve-expired: no ++ ede-serve-expired: yes + + # Specific options for ipsecmod. Unbound needs to be configured with + # --enable-ipsecmod for these to take effect. +@@ -1083,12 +1120,14 @@ server: + # Enable or disable ipsecmod (it still needs to be defined in + # module-config above). Can be used when ipsecmod needs to be + # enabled/disabled via remote-control(below). +- # ipsecmod-enabled: yes +- # ++ # Fedora: module will be enabled on-demand by libreswan ++ ipsecmod-enabled: no ++ + # Path to executable external hook. It must be defined when ipsecmod is + # listed in module-config (above). + # ipsecmod-hook: "./my_executable" +- # ++ ipsecmod-hook:/usr/libexec/ipsec/_unbound-hook ++ + # When enabled Unbound will reply with SERVFAIL if the return value of + # the ipsecmod-hook is not 0. + # ipsecmod-strict: no +@@ -1121,7 +1160,7 @@ server: + # o and give a python-script to run. + python: + # Script file to load +- # python-script: "@UNBOUND_SHARE_DIR@/ubmodule-tst.py" ++ # python-script: "/etc/unbound/ubmodule-tst.py" + + # Dynamic library config section. To enable: + # o use --with-dynlibmodule to configure before compiling. +@@ -1132,7 +1171,7 @@ python: + # the module-config then you need one dynlib-file per instance. + dynlib: + # Script file to load +- # dynlib-file: "@UNBOUND_SHARE_DIR@/dynlib.so" ++ # dynlib-file: "/etc/unbound/dynlib.so" + + # Remote control config section. + remote-control: +@@ -1155,16 +1196,19 @@ remote-control: + # control-use-cert: "yes" + + # Unbound server key file. +- # server-key-file: "@UNBOUND_RUN_DIR@/unbound_server.key" ++ server-key-file: "/etc/unbound/unbound_server.key" + + # Unbound server certificate file. +- # server-cert-file: "@UNBOUND_RUN_DIR@/unbound_server.pem" ++ server-cert-file: "/etc/unbound/unbound_server.pem" + + # unbound-control key file. +- # control-key-file: "@UNBOUND_RUN_DIR@/unbound_control.key" ++ control-key-file: "/etc/unbound/unbound_control.key" + + # unbound-control certificate file. +- # control-cert-file: "@UNBOUND_RUN_DIR@/unbound_control.pem" ++ control-cert-file: "/etc/unbound/unbound_control.pem" ++ ++# Stub and Forward zones ++include: /etc/unbound/conf.d/*.conf + + # Stub zones. + # Create entries like below, to make all queries for 'example.com' and +@@ -1186,6 +1230,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 +@@ -1203,6 +1251,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. +@@ -1234,6 +1286,7 @@ remote-control: + # fallback-enabled: yes + # for-downstream: no + # for-upstream: yes ++ + # auth-zone: + # name: "example.org" + # for-downstream: yes +@@ -1259,6 +1312,9 @@ remote-control: + # name: "anotherview" + # local-zone: "example.com" refuse + ++# Fedora: DNSCrypt support not enabled since it requires linking to ++# another crypto library ++# + # DNSCrypt + # To enable, use --enable-dnscrypt to configure before compiling. + # Caveats: +@@ -1338,7 +1394,7 @@ remote-control: + # dnstap-enable: no + # # if set to yes frame streams will be used in bidirectional mode + # dnstap-bidirectional: yes +-# dnstap-socket-path: "@DNSTAP_SOCKET_PATH@" ++# dnstap-socket-path: "/etc/unbound/dnstap.sock" + # # if "" use the unix socket in dnstap-socket-path, otherwise, + # # set it to "IPaddress[@port]" of the destination. + # dnstap-ip: "" +-- +2.47.0 diff --git a/icannbundle.pem b/icannbundle.pem index d76ce0ba0ee9e89284f3e2e33119e6251ff6f10c..ceeef5b0b84927e00da8f886565bb618b1d40d6b 100644 --- a/icannbundle.pem +++ b/icannbundle.pem @@ -1,59 +1,3 @@ -Certificate: - Data: - Version: 3 (0x2) - Serial Number: 1 (0x1) - Signature Algorithm: sha256WithRSAEncryption - Issuer: O=ICANN, OU=ICANN Certification Authority, CN=ICANN Root CA, C=US - Validity - Not Before: Dec 23 04:19:12 2009 GMT - Not After : Dec 18 04:19:12 2029 GMT - Subject: O=ICANN, OU=ICANN Certification Authority, CN=ICANN Root CA, C=US - Subject Public Key Info: - Public Key Algorithm: rsaEncryption - RSA Public Key: (2048 bit) - Modulus (2048 bit): - 00:a0:db:70:b8:4f:34:da:9c:d4:d0:7e:bb:ea:15: - bc:e9:c9:11:2a:1f:61:2f:6a:b9:bd:3f:3d:76:a0: - 9a:0a:f7:ee:93:6e:6e:55:53:84:8c:f2:2c:f1:82: - 27:c8:0f:9a:cf:52:1b:54:da:28:d2:2c:30:8e:dd: - fb:92:20:33:2d:d6:c8:f1:0e:10:21:88:71:fa:84: - 22:4b:5d:47:56:16:7c:9b:9f:5d:c3:11:79:9c:14: - e2:ff:c0:74:ac:dd:39:d7:e0:38:d8:b0:73:aa:fb: - d1:db:84:af:52:22:a8:f6:d5:9b:94:f4:e6:5d:5e: - e8:3f:87:90:0b:c7:1a:77:f5:2e:d3:8f:1a:ce:02: - 1d:07:69:21:47:32:da:46:ae:00:4c:b6:a5:a2:9c: - 39:c1:c0:4a:f6:d3:1c:ae:d3:6d:bb:c7:18:f0:7e: - ed:f6:80:ce:d0:01:2e:89:de:12:ba:ee:11:cb:a6: - 7a:d7:0d:7c:f3:08:8d:72:9d:bf:55:75:13:70:bb: - 31:22:4a:cb:e8:c0:aa:a4:09:aa:36:68:40:60:74: - 9d:e7:19:81:43:22:52:fe:c9:2b:52:0f:41:13:36: - 09:72:65:95:cc:89:ae:6f:56:17:16:34:73:52:a3: - 04:ed:bd:88:82:8a:eb:d7:dc:82:52:9c:06:e1:52: - 85:41 - Exponent: 65537 (0x10001) - X509v3 extensions: - X509v3 Basic Constraints: critical - CA:TRUE - X509v3 Key Usage: critical - Digital Signature, Non Repudiation, Key Encipherment, Data Encipherment, Key Agreement, Certificate Sign, CRL Sign - X509v3 Subject Key Identifier: - BA:52:E9:49:83:24:86:52:2F:C7:99:CD:FC:8D:6B:69:08:4D:C0:50 - Signature Algorithm: sha256WithRSAEncryption - 0f:f1:e9:82:a2:0a:87:9f:2d:94:60:5a:b2:c0:4b:a1:2f:2b: - 3b:47:d5:0a:99:86:38:b2:ec:c6:3b:89:e4:6e:07:cf:14:c7: - c7:e8:cf:99:8f:aa:30:c3:19:70:b9:e6:6d:d6:3f:c8:68:26: - b2:a0:a5:37:42:ca:d8:62:80:d1:a2:5a:48:2e:1f:85:3f:0c: - 7b:c2:c7:94:11:5f:19:2a:95:ac:a0:3a:03:d8:91:5b:2e:0d: - 9c:7c:1f:2e:fc:e9:44:e1:16:26:73:1c:45:4a:65:c1:83:4c: - 90:f3:f2:28:42:df:db:c4:e7:04:12:18:62:43:5e:bc:1f:6c: - 84:e6:bc:49:32:df:61:d7:99:ee:e4:90:52:7b:0a:c2:91:8a: - 98:62:66:b1:c8:e0:b7:5a:b5:46:7c:76:71:54:8e:cc:a4:81: - 5c:19:db:d2:6f:66:b5:bb:2b:ae:6b:c9:74:04:a8:24:de:e8: - c5:d3:fc:2c:1c:d7:8f:db:6a:8d:c9:53:be:5d:50:73:ac:cf: - 1f:93:c0:52:50:5b:a2:4f:fe:ad:65:36:17:46:d1:2d:e5:a2: - 90:66:05:db:29:4e:5d:50:5d:e3:4f:da:a0:8f:f0:6b:e4:16: - 70:dd:7f:f3:77:7d:b9:4e:f9:ec:c3:33:02:d7:e9:63:2f:31: - e7:40:61:a4 -----BEGIN CERTIFICATE----- MIIDdzCCAl+gAwIBAgIBATANBgkqhkiG9w0BAQsFADBdMQ4wDAYDVQQKEwVJQ0FO TjEmMCQGA1UECxMdSUNBTk4gQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkxFjAUBgNV @@ -75,163 +19,3 @@ DQEBCwUAA4IBAQAP8emCogqHny2UYFqywEuhLys7R9UKmYY4suzGO4nkbgfPFMfH 0/wsHNeP22qNyVO+XVBzrM8fk8BSUFuiT/6tZTYXRtEt5aKQZgXbKU5dUF3jT9qg j/Br5BZw3X/zd325TvnswzMC1+ljLzHnQGGk -----END CERTIFICATE----- -Certificate: - Data: - Version: 3 (0x2) - Serial Number: 11 (0xb) - Signature Algorithm: sha256WithRSAEncryption - Issuer: O=ICANN, OU=ICANN Certification Authority, CN=ICANN Root CA, C=US - Validity - Not Before: Nov 8 23:39:47 2016 GMT - Not After : Nov 6 23:39:47 2026 GMT - Subject: O=ICANN, CN=ICANN EMAIL CA - Subject Public Key Info: - Public Key Algorithm: rsaEncryption - RSA Public Key: (2048 bit) - Modulus (2048 bit): - 00:d2:19:1e:22:69:33:f6:a4:d2:76:c5:80:11:75: - 8e:d0:e8:6f:bf:89:f8:2a:6a:da:8a:85:28:40:ba: - c5:23:5f:47:ed:72:e2:8e:d3:5c:c8:8a:3a:99:a9: - 57:2c:0a:2b:22:f3:54:7b:8b:f7:8c:21:a2:50:01: - 4f:8b:af:34:df:72:fc:78:31:d0:1d:eb:bc:9b:e6: - fa:c1:84:d0:05:07:8a:74:53:a5:60:9e:eb:75:9e: - a8:5d:32:c8:02:32:e4:bf:cb:97:9b:7a:fa:2c:f6: - 6a:1d:b8:57:ad:e3:03:22:93:d0:f4:4f:a8:b8:01: - db:82:33:98:b6:87:ed:3d:67:40:00:27:2e:d5:95: - d2:ad:36:46:14:c6:17:79:65:7f:65:f3:88:80:65: - 7c:22:67:08:23:3c:cf:a5:10:38:72:30:97:92:6f: - 20:4a:ba:24:4c:4a:c8:4a:a5:dc:2a:44:a1:29:78: - b4:9f:fe:84:ff:27:5b:3a:72:ea:31:c1:ad:06:22: - d6:44:a0:4a:57:32:9c:f2:46:47:d0:89:6e:20:23: - 2c:ea:b0:83:7e:c1:f3:ea:da:dd:e3:63:59:97:21: - fa:1b:11:39:27:cf:82:8b:56:15:d4:36:92:0c:a5: - 7e:80:e0:18:c9:50:08:42:0a:df:97:3c:9c:b8:0a: - 4d:b1 - Exponent: 65537 (0x10001) - X509v3 extensions: - X509v3 Basic Constraints: critical - CA:TRUE - X509v3 Key Usage: critical - Certificate Sign, CRL Sign - X509v3 Authority Key Identifier: - keyid:BA:52:E9:49:83:24:86:52:2F:C7:99:CD:FC:8D:6B:69:08:4D:C0:50 - - X509v3 Subject Key Identifier: - 7B:3F:BA:CE:A1:B3:A6:13:2E:5A:82:84:D4:D2:EA:A5:24:F1:CD:B4 - Signature Algorithm: sha256WithRSAEncryption - 0e:8a:c9:ea:6f:9c:e9:23:b6:9c:a6:a4:c2:d1:b1:ee:25:18: - 24:2b:79:d4:a8:f2:99:b9:5c:91:4d:e6:2b:32:2e:01:f5:87: - 95:64:fc:6d:f1:87:fa:24:b4:43:4b:49:f3:84:54:44:eb:af: - 41:ab:49:ab:c8:b7:32:6c:14:83:5b:d7:2c:41:f9:89:d5:c4: - 2b:9a:55:c5:b6:ad:17:d5:4d:bc:41:58:56:72:0d:db:b7:7d: - 57:c6:a2:9c:7e:6b:67:ae:26:f8:26:45:bb:c4:95:2e:ea:71: - e3:b4:7a:69:95:a4:8a:80:f8:59:dc:88:6e:e1:a7:fc:bb:8e: - b2:aa:a8:b6:1b:2f:2c:97:a5:12:d5:82:ae:a0:e8:a6:15:fd: - d1:e0:5d:e4:84:b1:76:db:0a:e2:ca:58:2e:d3:df:48:4e:46: - ac:c6:35:79:17:99:ce:e9:be:2c:e4:c2:50:ff:5b:96:15:cd: - 64:ac:1b:db:fe:d2:ac:43:61:c8:5f:ee:24:b6:a4:3b:d2:ff: - 0a:f4:0c:88:58:a1:9d:a4:c1:1f:6a:6c:67:90:98:e8:1f:5e: - 2d:55:60:91:26:2a:b1:66:80:e4:e6:0e:05:2c:75:a9:ca:0b: - e4:a0:8f:e1:47:a8:8f:61:5d:7c:ce:09:60:88:48:c3:46:bf: - be:7e:36:be ------BEGIN CERTIFICATE----- -MIIDZDCCAkygAwIBAgIBCzANBgkqhkiG9w0BAQsFADBdMQ4wDAYDVQQKEwVJQ0FO -TjEmMCQGA1UECxMdSUNBTk4gQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkxFjAUBgNV -BAMTDUlDQU5OIFJvb3QgQ0ExCzAJBgNVBAYTAlVTMB4XDTE2MTEwODIzMzk0N1oX -DTI2MTEwNjIzMzk0N1owKTEOMAwGA1UEChMFSUNBTk4xFzAVBgNVBAMTDklDQU5O -IEVNQUlMIENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA0hkeImkz -9qTSdsWAEXWO0Ohvv4n4KmraioUoQLrFI19H7XLijtNcyIo6malXLAorIvNUe4v3 -jCGiUAFPi68033L8eDHQHeu8m+b6wYTQBQeKdFOlYJ7rdZ6oXTLIAjLkv8uXm3r6 -LPZqHbhXreMDIpPQ9E+ouAHbgjOYtoftPWdAACcu1ZXSrTZGFMYXeWV/ZfOIgGV8 -ImcIIzzPpRA4cjCXkm8gSrokTErISqXcKkShKXi0n/6E/ydbOnLqMcGtBiLWRKBK -VzKc8kZH0IluICMs6rCDfsHz6trd42NZlyH6GxE5J8+Ci1YV1DaSDKV+gOAYyVAI -QgrflzycuApNsQIDAQABo2MwYTAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQE -AwIBBjAfBgNVHSMEGDAWgBS6UulJgySGUi/Hmc38jWtpCE3AUDAdBgNVHQ4EFgQU -ez+6zqGzphMuWoKE1NLqpSTxzbQwDQYJKoZIhvcNAQELBQADggEBAA6KyepvnOkj -tpympMLRse4lGCQredSo8pm5XJFN5isyLgH1h5Vk/G3xh/oktENLSfOEVETrr0Gr -SavItzJsFINb1yxB+YnVxCuaVcW2rRfVTbxBWFZyDdu3fVfGopx+a2euJvgmRbvE -lS7qceO0emmVpIqA+FnciG7hp/y7jrKqqLYbLyyXpRLVgq6g6KYV/dHgXeSEsXbb -CuLKWC7T30hORqzGNXkXmc7pvizkwlD/W5YVzWSsG9v+0qxDYchf7iS2pDvS/wr0 -DIhYoZ2kwR9qbGeQmOgfXi1VYJEmKrFmgOTmDgUsdanKC+Sgj+FHqI9hXXzOCWCI -SMNGv75+Nr4= ------END CERTIFICATE----- -Certificate: - Data: - Version: 3 (0x2) - Serial Number: 10 (0xa) - Signature Algorithm: sha256WithRSAEncryption - Issuer: O=ICANN, OU=ICANN Certification Authority, CN=ICANN Root CA, C=US - Validity - Not Before: Nov 8 23:38:16 2016 GMT - Not After : Nov 6 23:38:16 2026 GMT - Subject: O=ICANN, CN=ICANN SSL CA - Subject Public Key Info: - Public Key Algorithm: rsaEncryption - RSA Public Key: (2048 bit) - Modulus (2048 bit): - 00:dd:c6:ab:bf:7c:66:9d:b3:2b:96:00:14:c7:60: - 7a:8d:62:5b:26:4b:30:d7:b3:4c:82:69:c6:4d:4d: - 73:f3:d4:91:21:5d:ab:35:f0:c8:04:0e:f4:a3:35: - e2:e1:18:a9:98:12:03:58:f8:9f:eb:77:54:5b:89: - 81:26:c9:aa:c2:f4:c9:0c:82:57:2a:5e:05:e9:61: - 17:cc:19:18:71:eb:35:83:c1:86:9d:ec:f1:6b:ca: - dd:a1:96:0b:95:d4:e1:0f:9e:24:6f:dc:3c:d0:28: - 9e:f2:53:47:2b:a1:ad:32:03:c8:3f:0d:80:80:7d: - f0:02:d2:6e:5a:2c:44:21:9b:09:50:15:3f:a1:3d: - d3:c9:c8:24:e7:ea:4e:92:2f:94:90:2e:de:e7:68: - f6:c6:b3:90:1f:bc:c9:7b:a2:65:d7:11:e9:8b:f0: - 3a:5a:b7:17:07:df:69:e3:6e:b9:54:6a:8e:3a:aa: - 94:7f:2c:0a:a1:ad:ba:b7:d9:60:62:27:a7:71:40: - 3b:8e:b0:84:7b:b8:c8:67:ef:66:ba:3d:ac:c3:85: - e5:86:bb:a7:9c:fd:b6:e1:c0:10:53:3d:d4:7e:1b: - 09:e6:9f:22:5c:a7:27:09:7e:27:12:33:fa:df:9b: - 20:2f:14:f7:17:c0:e4:1e:07:91:1f:f9:9a:cd:a8: - e2:c5 - Exponent: 65537 (0x10001) - X509v3 extensions: - X509v3 Basic Constraints: critical - CA:TRUE - X509v3 Key Usage: critical - Certificate Sign, CRL Sign - X509v3 Authority Key Identifier: - keyid:BA:52:E9:49:83:24:86:52:2F:C7:99:CD:FC:8D:6B:69:08:4D:C0:50 - - X509v3 Subject Key Identifier: - 6E:77:A8:40:10:4A:D8:9C:0C:F2:B7:5A:3A:A5:2F:79:4A:61:14:D8 - Signature Algorithm: sha256WithRSAEncryption - 47:46:4f:c7:5f:46:e3:d1:dc:fc:2b:f8:fc:65:ce:36:b1:f4: - 5f:ee:14:75:a3:d9:5f:de:75:4b:fa:7b:88:9f:10:8c:2e:97: - cc:35:1b:ce:24:d3:36:60:95:d5:ae:11:b6:3f:8b:f4:12:69: - 85:b5:3b:2a:b6:ab:7a:81:85:c2:55:57:ed:d0:b5:e7:4f:54: - 37:51:24:c9:d5:07:3a:ef:b6:c5:1a:3e:14:29:a7:a6:f8:08: - 2a:0b:26:79:f9:62:85:4a:e5:ea:90:ca:71:38:16:91:4e:7e: - fd:e3:b3:f3:55:8f:5a:d0:86:cf:33:94:88:f1:90:99:cb:81: - e2:81:92:68:2f:c3:61:d5:52:8d:e6:9a:5b:00:83:42:27:88: - f6:d9:fa:d1:bc:bb:b0:bc:b5:14:0b:4e:1a:54:ef:fa:d6:9d: - c4:0c:fc:ed:15:ab:21:4b:45:b5:d9:3b:ed:3c:d5:1e:2e:7a: - 83:6f:24:45:d4:4c:b4:ef:60:43:18:d0:84:5d:16:7b:f5:50: - 80:b1:a9:c2:8f:3b:c8:90:08:fd:aa:17:13:19:38:19:d1:8e: - 85:7c:1e:57:16:8c:f9:8a:e8:29:25:38:cd:bb:55:8e:4a:6a: - 6f:e5:7d:fc:d7:55:d6:ae:38:07:96:c1:97:ff:e5:2b:4f:99: - 2d:70:f2:08 ------BEGIN CERTIFICATE----- -MIIDYjCCAkqgAwIBAgIBCjANBgkqhkiG9w0BAQsFADBdMQ4wDAYDVQQKEwVJQ0FO -TjEmMCQGA1UECxMdSUNBTk4gQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkxFjAUBgNV -BAMTDUlDQU5OIFJvb3QgQ0ExCzAJBgNVBAYTAlVTMB4XDTE2MTEwODIzMzgxNloX -DTI2MTEwNjIzMzgxNlowJzEOMAwGA1UEChMFSUNBTk4xFTATBgNVBAMTDElDQU5O -IFNTTCBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAN3Gq798Zp2z -K5YAFMdgeo1iWyZLMNezTIJpxk1Nc/PUkSFdqzXwyAQO9KM14uEYqZgSA1j4n+t3 -VFuJgSbJqsL0yQyCVypeBelhF8wZGHHrNYPBhp3s8WvK3aGWC5XU4Q+eJG/cPNAo -nvJTRyuhrTIDyD8NgIB98ALSblosRCGbCVAVP6E908nIJOfqTpIvlJAu3udo9saz -kB+8yXuiZdcR6YvwOlq3FwffaeNuuVRqjjqqlH8sCqGturfZYGInp3FAO46whHu4 -yGfvZro9rMOF5Ya7p5z9tuHAEFM91H4bCeafIlynJwl+JxIz+t+bIC8U9xfA5B4H -kR/5ms2o4sUCAwEAAaNjMGEwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMC -AQYwHwYDVR0jBBgwFoAUulLpSYMkhlIvx5nN/I1raQhNwFAwHQYDVR0OBBYEFG53 -qEAQSticDPK3WjqlL3lKYRTYMA0GCSqGSIb3DQEBCwUAA4IBAQBHRk/HX0bj0dz8 -K/j8Zc42sfRf7hR1o9lf3nVL+nuInxCMLpfMNRvOJNM2YJXVrhG2P4v0EmmFtTsq -tqt6gYXCVVft0LXnT1Q3USTJ1Qc677bFGj4UKaem+AgqCyZ5+WKFSuXqkMpxOBaR -Tn7947PzVY9a0IbPM5SI8ZCZy4HigZJoL8Nh1VKN5ppbAINCJ4j22frRvLuwvLUU -C04aVO/61p3EDPztFashS0W12TvtPNUeLnqDbyRF1Ey072BDGNCEXRZ79VCAsanC -jzvIkAj9qhcTGTgZ0Y6FfB5XFoz5iugpJTjNu1WOSmpv5X3811XWrjgHlsGX/+Ur -T5ktcPII ------END CERTIFICATE----- diff --git a/unbound-1.17.1.tar.gz b/unbound-1.22.0.tar.gz similarity index 47% rename from unbound-1.17.1.tar.gz rename to unbound-1.22.0.tar.gz index c98fca989ffdc2c9293d36dd67e9e971b179437a..d9a7e9346dca4bf5bea9715d68617a6e76631529 100644 Binary files a/unbound-1.17.1.tar.gz and b/unbound-1.22.0.tar.gz differ diff --git a/unbound.conf b/unbound.conf deleted file mode 100644 index 5d0063a603adb82f6b98b0b5c6d46d135020beec..0000000000000000000000000000000000000000 --- a/unbound.conf +++ /dev/null @@ -1,1335 +0,0 @@ -# -# Example configuration file. -# -# See unbound.conf(5) man page -# -# this is a comment. - -# Use this anywhere in the file to include other text into this file. -#include: "otherfile.conf" - -# Use this anywhere in the file to include other text, that explicitly starts a -# clause, into this file. Text after this directive needs to start a clause. -#include-toplevel: "otherfile.conf" - -# The server clause sets the main parameters. -server: - # whitespace is not necessary, but looks cleaner. - - # verbosity number, 0 is least verbose. 1 is default. - verbosity: 1 - - # print statistics to the log (for every thread) every N seconds. - # Set to "" or 0 to disable. Default is disabled. - # Needs to be disabled for munin plugin - statistics-interval: 0 - - # enable shm for stats, default no. if you enable also enable - # statistics-interval, every time it also writes stats to the - # shared memory segment keyed with shm-key. - # shm-enable: no - - # shm for stats uses this key, and key+1 for the shared mem segment. - # shm-key: 11777 - - # enable cumulative statistics, without clearing them after printing. - # Needs to be disabled for munin plugin - statistics-cumulative: no - - # enable extended statistics (query types, answer codes, status) - # printed from unbound-control. default off, because of speed. - # Needs to be enabled for munin plugin - extended-statistics: yes - - # Inhibits selected extended statistics (qtype, qclass, qopcode, rcode, - # rpz-actions) from printing if their value is 0. - # Default on. - # statistics-inhibit-zero: yes - - # number of threads to create. 1 disables threading. - num-threads: 4 - - # specify the interfaces to answer queries from by ip-address. - # The default is to listen to localhost (127.0.0.1 and ::1). - # 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 - # - # 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. - # interface-automatic: yes - # - # NOTE: Enable this option when specifying interface 0.0.0.0 or ::0 - # NOTE: Disabled per Fedora policy not to listen to * on default install - # NOTE: If deploying on non-default port, eg 80/443, this needs to be disabled - interface-automatic: no - - # instead of the default port, open additional ports separated by - # spaces when interface-automatic is enabled, by listing them here. - # interface-automatic-ports: "" - - # port to answer queries from - # port: 53 - - # specify the interfaces to send outgoing queries to authoritative - # server from by ip-address. If none, the default (all) interface - # is used. Specify every interface on a 'outgoing-interface:' line. - # outgoing-interface: 192.0.2.153 - # outgoing-interface: 2001:DB8::5 - # outgoing-interface: 2001:DB8::6 - - # Specify a netblock to use remainder 64 bits as random bits for - # upstream queries. Uses freebind option (Linux). - # outgoing-interface: 2001:DB8::/64 - # Also (Linux:) ip -6 addr add 2001:db8::/64 dev lo - # And: ip -6 route add local 2001:db8::/64 dev lo - # And set prefer-ip6: yes to use the ip6 randomness from a netblock. - # Set this to yes to prefer ipv6 upstream servers over ipv4. - # prefer-ip6: no - - # Prefer ipv4 upstream servers, even if ipv6 is available. - # prefer-ip4: no - - # number of ports to allocate per thread, determines the size of the - # port range that can be open simultaneously. About double the - # num-queries-per-thread, or, use as many as the OS will allow you. - # outgoing-range: 4096 - - # permit Unbound to use this port number or port range for - # making outgoing queries, using an outgoing interface. - # Only ephemeral ports are allowed by SElinux - outgoing-port-permit: 32768-60999 - - # deny Unbound the use this of port number or port range for - # making outgoing queries, using an outgoing interface. - # Use this to make sure Unbound does not grab a UDP port that some - # other server on this computer needs. The default is to avoid - # IANA-assigned port numbers. - # If multiple outgoing-port-permit and outgoing-port-avoid options - # are present, they are processed in order. - # Our SElinux policy does not allow non-ephemeral ports to be used - outgoing-port-avoid: 0-32767 - outgoing-port-avoid: 61000-65535 - - # number of outgoing simultaneous tcp buffers to hold per thread. - # outgoing-num-tcp: 10 - - # number of incoming simultaneous tcp buffers to hold per thread. - # incoming-num-tcp: 10 - - # buffer size for UDP port 53 incoming (SO_RCVBUF socket option). - # 0 is system default. Use 4m to catch query spikes for busy servers. - # so-rcvbuf: 0 - - # buffer size for UDP port 53 outgoing (SO_SNDBUF socket option). - # 0 is system default. Use 4m to handle spikes on very busy servers. - # so-sndbuf: 0 - - # use SO_REUSEPORT to distribute queries over threads. - # at extreme load it could be better to turn it off to distribute even. - so-reuseport: yes - - # use IP_TRANSPARENT so the interface: addresses can be non-local - # and you can config non-existing IPs that are going to work later on - # (uses IP_BINDANY on FreeBSD). - ip-transparent: yes - - # use IP_FREEBIND so the interface: addresses can be non-local - # and you can bind to nonexisting IPs and interfaces that are down. - # Linux only. On Linux you also have ip-transparent that is similar. - # ip-freebind: no - - # the value of the Differentiated Services Codepoint (DSCP) - # in the differentiated services field (DS) of the outgoing - # IP packets - # ip-dscp: 0 - - # EDNS reassembly buffer to advertise to UDP peers (the actual buffer - # is set with msg-buffer-size). - # edns-buffer-size: 1232 - - # Maximum UDP response size (not applied to TCP response). - # Suggested values are 512 to 4096. Default is 4096. 65536 disables it. - # 3072 causes +dnssec any isc.org queries to need TC=1. - # Helps mitigating DDOS - max-udp-size: 3072 - - # max memory to use for stream(tcp and tls) waiting result buffers. - # stream-wait-size: 4m - - # buffer size for handling DNS data. No messages larger than this - # size can be sent or received, by UDP or TCP. In bytes. - # msg-buffer-size: 65552 - - # the amount of memory to use for the message cache. - # plain value in bytes or you can append k, m or G. default is "4Mb". - # msg-cache-size: 4m - - # the number of slabs to use for the message cache. - # the number of slabs must be a power of 2. - # more slabs reduce lock contention, but fragment memory usage. - # msg-cache-slabs: 4 - - # the number of queries that a thread gets to service. - # num-queries-per-thread: 1024 - - # if very busy, 50% queries run to completion, 50% get timeout in msec - # jostle-timeout: 200 - - # msec to wait before close of port on timeout UDP. 0 disables. - # delay-close: 0 - - # perform connect for UDP sockets to mitigate ICMP side channel. - # udp-connect: yes - - # The number of retries, per upstream nameserver in a delegation, when - # a throwaway response (also timeouts) is received. - # outbound-msg-retry: 5 - - # Hard limit on the number of outgoing queries Unbound will make while - # resolving a name, making sure large NS sets do not loop. - # It resets on query restarts (e.g., CNAME) and referrals. - # max-sent-count: 32 - - # Hard limit on the number of times Unbound is allowed to restart a - # query upon encountering a CNAME record. - # max-query-restarts: 11 - - # msec for waiting for an unknown server to reply. Increase if you - # are behind a slow satellite link, to eg. 1128. - # unknown-server-time-limit: 376 - - # the amount of memory to use for the RRset cache. - # plain value in bytes or you can append k, m or G. default is "4Mb". - # rrset-cache-size: 4m - - # the number of slabs to use for the RRset cache. - # the number of slabs must be a power of 2. - # more slabs reduce lock contention, but fragment memory usage. - # rrset-cache-slabs: 4 - - # the time to live (TTL) value lower bound, in seconds. Default 0. - # If more than an hour could easily give trouble due to stale data. - # cache-min-ttl: 0 - - # the time to live (TTL) value cap for RRsets and messages in the - # cache. Items are not cached for longer. In seconds. - # cache-max-ttl: 86400 - - # the time to live (TTL) value cap for negative responses in the cache - # cache-max-negative-ttl: 3600 - - # the time to live (TTL) value for cached roundtrip times, lameness and - # EDNS version information for hosts. In seconds. - # infra-host-ttl: 900 - - # minimum wait time for responses, increase if uplink is long. In msec. - # infra-cache-min-rtt: 50 - - # maximum wait time for responses. In msec. - # infra-cache-max-rtt: 120000 - - # enable to make server probe down hosts more frequently. - # infra-keep-probing: no - - # the number of slabs to use for the Infrastructure cache. - # the number of slabs must be a power of 2. - # more slabs reduce lock contention, but fragment memory usage. - # infra-cache-slabs: 4 - - # the maximum number of hosts that are cached (roundtrip, EDNS, lame). - # infra-cache-numhosts: 10000 - - # define a number of tags here, use with local-zone, access-control, - # interface-*. - # repeat the define-tag statement to add additional tags. - # define-tag: "tag1 tag2 tag3" - - # Enable IPv4, "yes" or "no". - # do-ip4: yes - - # Enable IPv6, "yes" or "no". - # do-ip6: yes - - # 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". - # do-tcp: yes - - # upstream connections use TCP only (and no UDP), "yes" or "no" - # useful for tunneling scenarios, default no. - # tcp-upstream: no - - # upstream connections also use UDP (even if do-udp is no). - # useful if if you want UDP upstream, but don't provide UDP downstream. - # udp-upstream-without-downstream: no - - # Maximum segment size (MSS) of TCP socket on which the server - # responds to queries. Default is 0, system default MSS. - # tcp-mss: 0 - - # Maximum segment size (MSS) of TCP socket for outgoing queries. - # Default is 0, system default MSS. - # outgoing-tcp-mss: 0 - - # Idle TCP timeout, connection closed in milliseconds - # tcp-idle-timeout: 30000 - - # Enable EDNS TCP keepalive option. - edns-tcp-keepalive: yes - - # Timeout for EDNS TCP keepalive, in msec. - # edns-tcp-keepalive-timeout: 120000 - - # 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 - - # Detach from the terminal, run in background, "yes" or "no". - # Set the value to "no" when Unbound runs as systemd service. - # do-daemonize: yes - - # control which clients are allowed to make (recursive) queries - # to this server. Specify classless netblocks with /size and action. - # By default everything is refused, except for localhost. - # Choose deny (drop message), refuse (polite error reply), - # allow (recursive ok), allow_setrd (recursive ok, rd bit is forced on), - # allow_snoop (recursive and nonrecursive ok) - # deny_non_local (drop queries unless can be answered from local-data) - # refuse_non_local (like deny_non_local but polite error reply). - # access-control: 127.0.0.0/8 allow - # access-control: ::1 allow - # access-control: ::ffff:127.0.0.1 allow - - # tag access-control with list of tags (in "" with spaces between) - # Clients using this access control element use localzones that - # are tagged with one of these tags. - # access-control-tag: 192.0.2.0/24 "tag2 tag3" - - # set action for particular tag for given access control element. - # if you have multiple tag values, the tag used to lookup the action - # is the first tag match between access-control-tag and local-zone-tag - # where "first" comes from the order of the define-tag values. - # access-control-tag-action: 192.0.2.0/24 tag3 refuse - - # set redirect data for particular tag for access control element - # access-control-tag-data: 192.0.2.0/24 tag2 "A 127.0.0.1" - - # Set view for access control element - # access-control-view: 192.0.2.0/24 viewname - - # Similar to 'access-control:' but for interfaces. - # Control which listening interfaces are allowed to accept (recursive) - # queries for this server. - # The specified interfaces should be the same as the ones specified in - # 'interface:' followed by the action. - # The actions are the same as 'access-control:' above. - # By default all the interfaces configured are refused. - # Note: any 'access-control*:' setting overrides all 'interface-*:' - # settings for targeted clients. - # interface-action: 192.0.2.153 allow - # interface-action: 192.0.2.154 allow - # interface-action: 192.0.2.154@5003 allow - # interface-action: 2001:DB8::5 allow - # interface-action: eth0@5003 allow - - # Similar to 'access-control-tag:' but for interfaces. - # Tag interfaces with a list of tags (in "" with spaces between). - # Interfaces using these tags use localzones that are tagged with one - # of these tags. - # The specified interfaces should be the same as the ones specified in - # 'interface:' followed by the list of tags. - # Note: any 'access-control*:' setting overrides all 'interface-*:' - # settings for targeted clients. - # interface-tag: eth0@5003 "tag2 tag3" - - # Similar to 'access-control-tag-action:' but for interfaces. - # Set action for particular tag for a given interface element. - # If you have multiple tag values, the tag used to lookup the action - # is the first tag match between interface-tag and local-zone-tag - # where "first" comes from the order of the define-tag values. - # The specified interfaces should be the same as the ones specified in - # 'interface:' followed by the tag and action. - # Note: any 'access-control*:' setting overrides all 'interface-*:' - # settings for targeted clients. - # interface-tag-action: eth0@5003 tag3 refuse - - # Similar to 'access-control-tag-data:' but for interfaces. - # Set redirect data for a particular tag for an interface element. - # The specified interfaces should be the same as the ones specified in - # 'interface:' followed by the tag and the redirect data. - # Note: any 'access-control*:' setting overrides all 'interface-*:' - # settings for targeted clients. - # interface-tag-data: eth0@5003 tag2 "A 127.0.0.1" - - # Similar to 'access-control-view:' but for interfaces. - # Set view for an interface element. - # The specified interfaces should be the same as the ones specified in - # 'interface:' followed by the view name. - # Note: any 'access-control*:' setting overrides all 'interface-*:' - # settings for targeted clients. - # interface-view: eth0@5003 viewname - - # if given, a chroot(2) is done to the given directory. - # i.e. you can chroot to the working directory, for example, - # for extra security, but make sure all files are in that directory. - # - # If chroot is enabled, you should pass the configfile (from the - # commandline) as a full path from the original root. After the - # chroot has been performed the now defunct portion of the config - # file path is removed to be able to reread the config after a reload. - # - # All other file paths (working dir, logfile, roothints, and - # key files) can be specified in several ways: - # o as an absolute path relative to the new root. - # o as a relative path to the working directory. - # o as an absolute path relative to the original root. - # In the last case the path is adjusted to remove the unused portion. - # - # The pid file can be absolute and outside of the chroot, it is - # written just prior to performing the chroot and dropping permissions. - # - # Additionally, Unbound may need to access /dev/urandom (for entropy). - # How to do this is specific to your OS. - # - # If you give "" no chroot is performed. The path must not end in a /. - # chroot: "/var/lib/unbound" - chroot: "" - - # if given, user privileges are dropped (after binding port), - # and the given username is assumed. Default is user "unbound". - # If you give "" no privileges are dropped. - username: "unbound" - - # the working directory. The relative files in this config are - # relative to this directory. If you give "" the working directory - # is not changed. - # If you give a server: directory: dir before include: file statements - # then those includes can be relative to the working directory. - directory: "/etc/unbound" - - # the log file, "" means log to stderr. - # Use of this option sets use-syslog to "no". - # logfile: "" - - # Log to syslog(3) if yes. The log facility LOG_DAEMON is used to - # log to. If yes, it overrides the logfile. - # use-syslog: yes - - # Log identity to report. if empty, defaults to the name of argv[0] - # (usually "unbound"). - # log-identity: "" - - # print UTC timestamp in ascii to logfile, default is epoch in seconds. - log-time-ascii: yes - - # print one line with time, IP, name, type, class for every query. - # log-queries: no - - # print one line per reply, with time, IP, name, type, class, rcode, - # timetoresolve, fromcache and responsesize. - # log-replies: no - - # log with tag 'query' and 'reply' instead of 'info' for - # filtering log-queries and log-replies from the log. - # log-tag-queryreply: no - - # log the local-zone actions, like local-zone type inform is enabled - # also for the other local zone types. - # log-local-actions: no - - # print log lines that say why queries return SERVFAIL to clients. - # log-servfail: no - - # the pid file. Can be an absolute path outside of chroot/work dir. - pidfile: "/var/run/unbound/unbound.pid" - - # file to read root hints from. - # get one from https://www.internic.net/domain/named.cache - # root-hints: "" - - # enable to not answer id.server and hostname.bind queries. - # hide-identity: no - - # enable to not answer version.server and version.bind queries. - # hide-version: no - - # enable to not answer trustanchor.unbound queries. - # hide-trustanchor: no - - # enable to not set the User-Agent HTTP header. - # hide-http-user-agent: no - - # the identity to report. Leave "" or default to return hostname. - # identity: "" - - # the version to report. Leave "" or default to return package version. - # version: "" - - # NSID identity (hex string, or "ascii_somestring"). default disabled. - # nsid: "aabbccdd" - - # User-Agent HTTP header to use. Leave "" or default to use package name - # and version. - # http-user-agent: "" - - # the target fetch policy. - # series of integers describing the policy per dependency depth. - # The number of values in the list determines the maximum dependency - # depth the recursor will pursue before giving up. Each integer means: - # -1 : fetch all targets opportunistically, - # 0: fetch on demand, - # positive value: fetch that many targets opportunistically. - # Enclose the list of numbers between quotes (""). - # target-fetch-policy: "3 2 1 0 0" - - # Harden against very small EDNS buffer sizes. - # harden-short-bufsize: yes - - # Harden against unseemly large queries. - # harden-large-queries: no - - # Harden against out of zone rrsets, to avoid spoofing attempts. - harden-glue: yes - - # Harden against receiving dnssec-stripped data. If you turn it - # off, failing to validate dnskey data for a trustanchor will - # trigger insecure mode for that zone (like without a trustanchor). - # Default on, which insists on dnssec data for trust-anchored zones. - harden-dnssec-stripped: yes - - # Harden against queries that fall under dnssec-signed nxdomain names. - harden-below-nxdomain: yes - - # Harden the referral path by performing additional queries for - # infrastructure data. Validates the replies (if possible). - # Default off, because the lookups burden the server. Experimental - # implementation of draft-wijngaards-dnsext-resolver-side-mitigation. - harden-referral-path: yes - - # Harden against algorithm downgrade when multiple algorithms are - # advertised in the DS record. If no, allows the weakest algorithm - # to validate the zone. - # harden-algo-downgrade: no - - # Sent minimum amount of information to upstream servers to enhance - # privacy. Only sent minimum required labels of the QNAME and set QTYPE - # to A when possible. - qname-minimisation: yes - - # QNAME minimisation in strict mode. Do not fall-back to sending full - # QNAME to potentially broken nameservers. A lot of domains will not be - # resolvable when this option in enabled. - # This option only has effect when qname-minimisation is enabled. - # qname-minimisation-strict: no - - # Aggressive NSEC uses the DNSSEC NSEC chain to synthesize NXDOMAIN - # and other denials, using information from previous NXDOMAINs answers. - aggressive-nsec: yes - - # Use 0x20-encoded random bits in the query to foil spoof attempts. - # This feature is an experimental implementation of draft dns-0x20. - # use-caps-for-id: no - - # Domains (and domains in them) without support for dns-0x20 and - # the fallback fails because they keep sending different answers. - # caps-exempt: "licdn.com" - # caps-exempt: "senderbase.org" - - # Enforce privacy of these addresses. Strips them away from answers. - # It may cause DNSSEC validation to additionally mark it as bogus. - # Protects against 'DNS Rebinding' (uses browser as network proxy). - # Only 'private-domain' and 'local-data' names are allowed to have - # these private addresses. No default. - # private-address: 10.0.0.0/8 - # private-address: 172.16.0.0/12 - # private-address: 192.168.0.0/16 - # private-address: 169.254.0.0/16 - # private-address: fd00::/8 - # private-address: fe80::/10 - # private-address: ::ffff:0:0/96 - - # Allow the domain (and its subdomains) to contain private addresses. - # local-data statements are allowed to contain private addresses too. - # private-domain: "example.com" - - # If nonzero, unwanted replies are not only reported in statistics, - # but also a running total is kept per thread. If it reaches the - # threshold, a warning is printed and a defensive action is taken, - # the cache is cleared to flush potential poison out of it. - # A suggested value is 10000000, the default is 0 (turned off). - unwanted-reply-threshold: 10000000 - - # Do not query the following addresses. No DNS queries are sent there. - # List one address per entry. List classless netblocks with /size, - # do-not-query-address: 127.0.0.1/8 - # do-not-query-address: ::1 - - # if yes, the above default do-not-query-address entries are present. - # if no, localhost can be queried (for testing and debugging). - # do-not-query-localhost: yes - - # if yes, perform prefetching of almost expired message cache entries. - prefetch: yes - - # if yes, perform key lookups adjacent to normal lookups. - prefetch-key: yes - - # deny queries of type ANY with an empty response. - deny-any: yes - - # if yes, Unbound rotates RRSet order in response. - rrset-roundrobin: yes - - # if yes, Unbound doesn't insert authority/additional sections - # into response messages when those sections are not required. - minimal-responses: yes - - # true to disable DNSSEC lameness check in iterator. - # disable-dnssec-lame-check: no - - # module configuration of the server. A string with identifiers - # separated by spaces. Syntax: "[dns64] [validator] iterator" - # most modules have to be listed at the beginning of the line, - # except cachedb(just before iterator), and python (at the beginning, - # or, just before the iterator). - # For redis cachedb use: - # "ipsecmod validator cachedb iterator" - module-config: "ipsecmod validator iterator" - - # File with trusted keys, kept uptodate using RFC5011 probes, - # initial file like trust-anchor-file, then it stores metadata. - # Use several entries, one per domain name, to track multiple zones. - # - # If you want to perform DNSSEC validation, run unbound-anchor before - # you start Unbound (i.e. in the system boot scripts). - # And then enable the auto-trust-anchor-file config item. - # Please note usage of unbound-anchor root anchor is at your own risk - # and under the terms of our LICENSE (see that file in the source). - # auto-trust-anchor-file: "/var/lib/unbound/root.key" - - # trust anchor signaling sends a RFC8145 key tag query after priming. - trust-anchor-signaling: yes - - # Root key trust anchor sentinel (draft-ietf-dnsop-kskroll-sentinel) - root-key-sentinel: yes - - # File with trusted keys for validation. Specify more than one file - # with several entries, one file per entry. - # Zone file format, with DS and DNSKEY entries. - # Note this gets out of date, use auto-trust-anchor-file please. - # trust-anchor-file: "" - - # Trusted key for validation. DS or DNSKEY. specify the RR on a - # single line, surrounded by "". TTL is ignored. class is IN default. - # Note this gets out of date, use auto-trust-anchor-file please. - # (These examples are from August 2007 and may not be valid anymore). - # trust-anchor: "nlnetlabs.nl. DNSKEY 257 3 5 AQPzzTWMz8qSWIQlfRnPckx2BiVmkVN6LPupO3mbz7FhLSnm26n6iG9N Lby97Ji453aWZY3M5/xJBSOS2vWtco2t8C0+xeO1bc/d6ZTy32DHchpW 6rDH1vp86Ll+ha0tmwyy9QP7y2bVw5zSbFCrefk8qCUBgfHm9bHzMG1U BYtEIQ==" - # trust-anchor: "jelte.nlnetlabs.nl. DS 42860 5 1 14D739EB566D2B1A5E216A0BA4D17FA9B038BE4A" - - # File with trusted keys for validation. Specify more than one file - # with several entries, one file per entry. Like trust-anchor-file - # but has a different file format. Format is BIND-9 style format, - # the trusted-keys { name flag proto algo "key"; }; clauses are read. - # you need external update procedures to track changes in keys. - # trusted-keys-file: "" - # - trusted-keys-file: /etc/unbound/keys.d/*.key - auto-trust-anchor-file: "/var/lib/unbound/root.key" - - # Ignore chain of trust. Domain is treated as insecure. - # domain-insecure: "example.com" - - # Override the date for validation with a specific fixed date. - # Do not set this unless you are debugging signature inception - # and expiration. "" or "0" turns the feature off. -1 ignores date. - # val-override-date: "" - - # The time to live for bogus data, rrsets and messages. This avoids - # some of the revalidation, until the time interval expires. in secs. - # val-bogus-ttl: 60 - - # The signature inception and expiration dates are allowed to be off - # by 10% of the signature lifetime (expir-incep) from our local clock. - # This leeway is capped with a minimum and a maximum. In seconds. - # val-sig-skew-min: 3600 - # val-sig-skew-max: 86400 - - # The maximum number the validator should restart validation with - # another authority in case of failed validation. - # val-max-restart: 5 - - # Should additional section of secure message also be kept clean of - # unsecure data. Useful to shield the users of this validator from - # potential bogus data in the additional section. All unsigned data - # in the additional section is removed from secure messages. - val-clean-additional: yes - - # Turn permissive mode on to permit bogus messages. Thus, messages - # for which security checks failed will be returned to clients, - # instead of SERVFAIL. It still performs the security checks, which - # result in interesting log files and possibly the AD bit in - # replies if the message is found secure. The default is off. - # NOTE: TURNING THIS ON DISABLES ALL DNSSEC SECURITY - val-permissive-mode: no - - # Ignore the CD flag in incoming queries and refuse them bogus data. - # Enable it if the only clients of Unbound are legacy servers (w2008) - # that set CD but cannot validate themselves. - # ignore-cd-flag: no - - # Serve expired responses from cache, with serve-expired-reply-ttl in - # the response, and then attempt to fetch the data afresh. - serve-expired: yes - # - # Limit serving of expired responses to configured seconds after - # expiration. 0 disables the limit. - serve-expired-ttl: 14400 - # - # Set the TTL of expired records to the serve-expired-ttl value after a - # failed attempt to retrieve the record from upstream. This makes sure - # that the expired records will be served as long as there are queries - # for it. - # serve-expired-ttl-reset: no - # - # TTL value to use when replying with expired data. - # serve-expired-reply-ttl: 30 - # - # Time in milliseconds before replying to the client with expired data. - # This essentially enables the serve-stale behavior as specified in - # RFC 8767 that first tries to resolve before - # immediately responding with expired data. 0 disables this behavior. - # A recommended value is 1800. - # serve-expired-client-timeout: 0 - - # Return the original TTL as received from the upstream name server rather - # than the decrementing TTL as stored in the cache. Enabling this feature - # does not impact cache expiry, it only changes the TTL Unbound embeds in - # responses to queries. Note that enabling this feature implicitly disables - # enforcement of the configured minimum and maximum TTL. - # serve-original-ttl: no - - # Have the validator log failed validations for your diagnosis. - # 0: off. 1: A line per failed user query. 2: With reason and bad IP. - val-log-level: 1 - - # It is possible to configure NSEC3 maximum iteration counts per - # keysize. Keep this table very short, as linear search is done. - # A message with an NSEC3 with larger count is marked insecure. - # List in ascending order the keysize and count values. - # val-nsec3-keysize-iterations: "1024 150 2048 150 4096 150" - - # if enabled, ZONEMD verification failures do not block the zone. - # zonemd-permissive-mode: no - - # instruct the auto-trust-anchor-file probing to add anchors after ttl. - # add-holddown: 2592000 # 30 days - - # instruct the auto-trust-anchor-file probing to del anchors after ttl. - # del-holddown: 2592000 # 30 days - - # auto-trust-anchor-file probing removes missing anchors after ttl. - # If the value 0 is given, missing anchors are not removed. - # keep-missing: 31622400 # 366 days - - # debug option that allows very small holddown times for key rollover, - # otherwise the RFC mandates probe intervals must be at least 1 hour. - # permit-small-holddown: no - - # the amount of memory to use for the key cache. - # plain value in bytes or you can append k, m or G. default is "4Mb". - # key-cache-size: 4m - - # the number of slabs to use for the key cache. - # the number of slabs must be a power of 2. - # more slabs reduce lock contention, but fragment memory usage. - # key-cache-slabs: 4 - - # the amount of memory to use for the negative cache. - # plain value in bytes or you can append k, m or G. default is "1Mb". - # neg-cache-size: 1m - - # By default, for a number of zones a small default 'nothing here' - # reply is built-in. Query traffic is thus blocked. If you - # wish to serve such zone you can unblock them by uncommenting one - # of the nodefault statements below. - # You may also have to use domain-insecure: zone to make DNSSEC work, - # unless you have your own trust anchors for this zone. - # local-zone: "localhost." nodefault - # local-zone: "127.in-addr.arpa." nodefault - # local-zone: "1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.ip6.arpa." nodefault - # local-zone: "home.arpa." nodefault - # local-zone: "onion." nodefault - # local-zone: "test." nodefault - # local-zone: "invalid." nodefault - # local-zone: "10.in-addr.arpa." nodefault - # local-zone: "16.172.in-addr.arpa." nodefault - # local-zone: "17.172.in-addr.arpa." nodefault - # local-zone: "18.172.in-addr.arpa." nodefault - # local-zone: "19.172.in-addr.arpa." nodefault - # local-zone: "20.172.in-addr.arpa." nodefault - # local-zone: "21.172.in-addr.arpa." nodefault - # local-zone: "22.172.in-addr.arpa." nodefault - # local-zone: "23.172.in-addr.arpa." nodefault - # local-zone: "24.172.in-addr.arpa." nodefault - # local-zone: "25.172.in-addr.arpa." nodefault - # local-zone: "26.172.in-addr.arpa." nodefault - # local-zone: "27.172.in-addr.arpa." nodefault - # local-zone: "28.172.in-addr.arpa." nodefault - # local-zone: "29.172.in-addr.arpa." nodefault - # local-zone: "30.172.in-addr.arpa." nodefault - # local-zone: "31.172.in-addr.arpa." nodefault - # local-zone: "168.192.in-addr.arpa." nodefault - # local-zone: "0.in-addr.arpa." nodefault - # local-zone: "254.169.in-addr.arpa." nodefault - # local-zone: "2.0.192.in-addr.arpa." nodefault - # local-zone: "100.51.198.in-addr.arpa." nodefault - # local-zone: "113.0.203.in-addr.arpa." nodefault - # local-zone: "255.255.255.255.in-addr.arpa." nodefault - # local-zone: "0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.ip6.arpa." nodefault - # local-zone: "d.f.ip6.arpa." nodefault - # local-zone: "8.e.f.ip6.arpa." nodefault - # local-zone: "9.e.f.ip6.arpa." nodefault - # local-zone: "a.e.f.ip6.arpa." nodefault - # local-zone: "b.e.f.ip6.arpa." nodefault - # local-zone: "8.b.d.0.1.0.0.2.ip6.arpa." nodefault - # And for 64.100.in-addr.arpa. to 127.100.in-addr.arpa. - - # Add example.com into ipset - # local-zone: "example.com" ipset - - # If Unbound is running service for the local host then it is useful - # to perform lan-wide lookups to the upstream, and unblock the - # long list of local-zones above. If this Unbound is a dns server - # for a network of computers, disabled is better and stops information - # leakage of local lan information. - # unblock-lan-zones: no - - # The insecure-lan-zones option disables validation for - # these zones, as if they were all listed as domain-insecure. - # insecure-lan-zones: no - - # a number of locally served zones can be configured. - # local-zone: - # local-data: "" - # o deny serves local data (if any), else, drops queries. - # o refuse serves local data (if any), else, replies with error. - # o static serves local data, else, nxdomain or nodata answer. - # o transparent gives local data, but resolves normally for other names - # o redirect serves the zone data for any subdomain in the zone. - # o nodefault can be used to normally resolve AS112 zones. - # o typetransparent resolves normally for other types and other names - # o inform acts like transparent, but logs client IP address - # o inform_deny drops queries and logs client IP address - # o inform_redirect redirects queries and logs client IP address - # o always_transparent, always_refuse, always_nxdomain, always_nodata, - # always_deny resolve in that way but ignore local data for - # that name - # o always_null returns 0.0.0.0 or ::0 for any name in the zone. - # o noview breaks out of that view towards global local-zones. - # - # defaults are localhost address, reverse for 127.0.0.1 and ::1 - # and nxdomain for AS112 zones. If you configure one of these zones - # the default content is omitted, or you can omit it with 'nodefault'. - # - # If you configure local-data without specifying local-zone, by - # default a transparent local-zone is created for the data. - # - # You can add locally served data with - # local-zone: "local." static - # local-data: "mycomputer.local. IN A 192.0.2.51" - # local-data: 'mytext.local TXT "content of text record"' - # - # You can override certain queries with - # local-data: "adserver.example.com A 127.0.0.1" - # - # You can redirect a domain to a fixed address with - # (this makes example.com, www.example.com, etc, all go to 192.0.2.3) - # local-zone: "example.com" redirect - # local-data: "example.com A 192.0.2.3" - # - # Shorthand to make PTR records, "IPv4 name" or "IPv6 name". - # You can also add PTR records using local-data directly, but then - # 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" - - # add a netblock specific override to a localzone, with zone type - # local-zone-override: "example.com" 192.0.2.0/24 refuse - - # service clients over TLS (on the TCP sockets) with plain DNS inside - # 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: "/etc/unbound/unbound_server.key" - # tls-service-pem: "/etc/unbound/unbound_server.pem" - # tls-port: 853 - # https-port: 443 - - # cipher setting for TLSv1.2 - # tls-ciphers: "DHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-SHA256:DHE-RSA-AES128-SHA256:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA256" - # cipher setting for TLSv1.3 - # tls-ciphersuites: "TLS_AES_128_GCM_SHA256:TLS_AES_128_CCM_8_SHA256:TLS_AES_128_CCM_SHA256:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256" - # Fedora/RHEL: use system-wide crypto policies - tls-ciphers: "PROFILE=SYSTEM" - # TODO: ask system-wide crypto people what to use here - #tls-ciphersuites: "PROFILE=SYSTEM" # does not work - - # Pad responses to padded queries received over TLS - # pad-responses: yes - - # Padded responses will be padded to the closest multiple of this size. - # pad-responses-block-size: 468 - - # Use the SNI extension for TLS connections. Default is yes. - # Changing the value requires a reload. - # tls-use-sni: yes - - # Add the secret file for TLS Session Ticket. - # Secret file must be 80 bytes of random data. - # First key use to encrypt and decrypt TLS session tickets. - # Other keys use to decrypt only. - # requires restart to take effect. - # tls-session-ticket-keys: "path/to/secret_file1" - # tls-session-ticket-keys: "path/to/secret_file2" - - # request upstream over TLS (with plain DNS inside the TLS stream). - # Default is no. Can be turned on and off with unbound-control. - # tls-upstream: no - - # Certificates used to authenticate connections made upstream. - # tls-cert-bundle: "" - - # Add system certs to the cert bundle, from the Windows Cert Store - # tls-win-cert: no - # and on other systems, the default openssl certificates - # tls-system-cert: no - - # Pad queries over TLS upstreams - # pad-queries: yes - - # Padded queries will be padded to the closest multiple of this size. - # pad-queries-block-size: 128 - - # Also serve tls on these port numbers (eg. 443, ...), by listing - # tls-additional-port: portno for each of the port numbers. - - # HTTP endpoint to provide DNS-over-HTTPS service on. - # http-endpoint: "/dns-query" - - # HTTP/2 SETTINGS_MAX_CONCURRENT_STREAMS value to use. - # http-max-streams: 100 - - # Maximum number of bytes used for all HTTP/2 query buffers. - # http-query-buffer-size: 4m - - # Maximum number of bytes used for all HTTP/2 response buffers. - # http-response-buffer-size: 4m - - # Set TCP_NODELAY socket option on sockets used for DNS-over-HTTPS - # service. - # http-nodelay: yes - - # Disable TLS for DNS-over-HTTP downstream service. - # http-notls-downstream: no - - # The interfaces that use these listed port numbers will support and - # expect PROXYv2. For UDP and TCP/TLS interfaces. - # proxy-protocol-port: portno for each of the port numbers. - - # DNS64 prefix. Must be specified when DNS64 is use. - # Enable dns64 in module-config. Used to synthesize IPv6 from IPv4. - # dns64-prefix: 64:ff9b::0/96 - - # DNS64 ignore AAAA records for these domains and use A instead. - # dns64-ignore-aaaa: "example.com" - - # ratelimit for uncached, new queries, this limits recursion effort. - # ratelimiting is experimental, and may help against randomqueryflood. - # if 0(default) it is disabled, otherwise state qps allowed per zone. - # ratelimit: 0 - - # ratelimits are tracked in a cache, size in bytes of cache (or k,m). - # ratelimit-size: 4m - # ratelimit cache slabs, reduces lock contention if equal to cpucount. - # ratelimit-slabs: 4 - - # 0 blocks when ratelimited, otherwise let 1/xth traffic through - # ratelimit-factor: 10 - - # Aggressive rate limit when the limit is reached and until demand has - # decreased in a 2 second rate window. - # ratelimit-backoff: no - - # override the ratelimit for a specific domain name. - # give this setting multiple times to have multiple overrides. - # ratelimit-for-domain: example.com 1000 - # override the ratelimits for all domains below a domain name - # can give this multiple times, the name closest to the zone is used. - # ratelimit-below-domain: com 1000 - - # global query ratelimit for all ip addresses. - # feature is experimental. - # if 0(default) it is disabled, otherwise states qps allowed per ip address - # ip-ratelimit: 0 - - # ip ratelimits are tracked in a cache, size in bytes of cache (or k,m). - # ip-ratelimit-size: 4m - # ip ratelimit cache slabs, reduces lock contention if equal to cpucount. - # ip-ratelimit-slabs: 4 - - # 0 blocks when ip is ratelimited, otherwise let 1/xth traffic through - # ip-ratelimit-factor: 10 - - # Aggressive rate limit when the limit is reached and until demand has - # decreased in a 2 second rate window. - # ip-ratelimit-backoff: no - - # Limit the number of connections simultaneous from a netblock - # tcp-connection-limit: 192.0.2.0/24 12 - - # select from the fastest servers this many times out of 1000. 0 means - # the fast server select is disabled. prefetches are not sped up. - # fast-server-permil: 0 - # the number of servers that will be used in the fast server selection. - # fast-server-num: 3 - - # Enable to attach Extended DNS Error codes (RFC8914) to responses. - ede: yes - - # Enable to attach an Extended DNS Error (RFC8914) Code 3 - Stale - # Answer as EDNS0 option to expired responses. - # Note that the ede option above needs to be enabled for this to work. - ede-serve-expired: yes - - # Specific options for ipsecmod. Unbound needs to be configured with - # --enable-ipsecmod for these to take effect. - # - # Enable or disable ipsecmod (it still needs to be defined in - # module-config above). Can be used when ipsecmod needs to be - # enabled/disabled via remote-control(below). - # Fedora: module will be enabled on-demand by libreswan - ipsecmod-enabled: no - - # Path to executable external hook. It must be defined when ipsecmod is - # listed in module-config (above). - # ipsecmod-hook: "./my_executable" - ipsecmod-hook:/usr/libexec/ipsec/_unbound-hook - - # When enabled Unbound will reply with SERVFAIL if the return value of - # the ipsecmod-hook is not 0. - # ipsecmod-strict: no - # - # Maximum time to live (TTL) for cached A/AAAA records with IPSECKEY. - # ipsecmod-max-ttl: 3600 - # - # Reply with A/AAAA even if the relevant IPSECKEY is bogus. Mainly used for - # testing. - # ipsecmod-ignore-bogus: no - # - # Domains for which ipsecmod will be triggered. If not defined (default) - # all domains are treated as being allowed. - # ipsecmod-allow: "example.com" - # ipsecmod-allow: "nlnetlabs.nl" - - # Timeout for REUSE entries in milliseconds. - # tcp-reuse-timeout: 60000 - # Max number of queries on a reuse connection. - # max-reuse-tcp-queries: 200 - # Timeout in milliseconds for TCP queries to auth servers. - # tcp-auth-query-timeout: 3000 - -# Python config section. To enable: -# o use --with-pythonmodule to configure before compiling. -# o list python in the module-config string (above) to enable. -# It can be at the start, it gets validated results, or just before -# the iterator and process before DNSSEC validation. -# o and give a python-script to run. -python: - # Script file to load - # python-script: "/etc/unbound/ubmodule-tst.py" - -# Dynamic library config section. To enable: -# o use --with-dynlibmodule to configure before compiling. -# o list dynlib in the module-config string (above) to enable. -# It can be placed anywhere, the dynlib module is only a very thin wrapper -# to load modules dynamically. -# o and give a dynlib-file to run. If more than one dynlib entry is listed in -# the module-config then you need one dynlib-file per instance. -dynlib: - # Script file to load - # dynlib-file: "/etc/unbound/dynlib.so" - -# Remote control config section. -remote-control: - # Enable remote control with unbound-control(8) here. - # set up the keys and certificates with unbound-control-setup. - # Note: required for unbound-munin package - control-enable: no - - # Set to no and use an absolute path as control-interface to use - # a unix local named pipe for unbound-control. - # control-use-cert: yes - - # what interfaces are listened to for remote control. - # give 0.0.0.0 and ::0 to listen to all interfaces. - # set to an absolute path to use a unix local name pipe, certificates - # are not used for that, so key and cert files need not be present. - # control-interface: 127.0.0.1 - # control-interface: ::1 - - # port number for remote control operations. - # control-port: 8953 - - # Unbound server key file. - server-key-file: "/etc/unbound/unbound_server.key" - - # Unbound server certificate file. - server-cert-file: "/etc/unbound/unbound_server.pem" - - # unbound-control key file. - control-key-file: "/etc/unbound/unbound_control.key" - - # unbound-control certificate file. - control-cert-file: "/etc/unbound/unbound_control.pem" - -# Stub and Forward zones -include: /etc/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 -# nameservers by hostname or by ipaddress. If you set stub-prime to yes, -# the list is treated as priming hints (default is no). -# With stub-first yes, it attempts without the stub if it fails. -# Consider adding domain-insecure: name and local-zone: name nodefault -# to the server: section if the stub is a locally served zone. -# stub-zone: -# name: "example.com" -# stub-addr: 192.0.2.68 -# stub-prime: no -# stub-first: no -# stub-tcp-upstream: no -# stub-tls-upstream: no -# stub-no-cache: no -# stub-zone: -# 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 -# recursion to other nameservers. List zero or more nameservers by hostname -# or by ipaddress. Use an entry with name "." to forward all queries. -# If you enable forward-first, it attempts without the forward if it fails. -# forward-zone: -# name: "example.com" -# forward-addr: 192.0.2.68 -# forward-addr: 192.0.2.73@5355 # forward to port 5355. -# forward-first: no -# forward-tcp-upstream: no -# forward-tls-upstream: no -# forward-no-cache: no -# 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. -# The data can be served to downstream clients, or used instead of the -# upstream (which saves a lookup to the upstream). The first example -# has a copy of the root for local usage. The second serves example.org -# authoritatively. zonefile: reads from file (and writes to it if you also -# download it), master: fetches with AXFR and IXFR, or url to zonefile. -# With allow-notify: you can give additional (apart from masters) sources of -# notifies. -auth-zone: - name: "." - primary: 199.9.14.201 # b.root-servers.net - primary: 192.33.4.12 # c.root-servers.net - primary: 199.7.91.13 # d.root-servers.net - primary: 192.5.5.241 # f.root-servers.net - primary: 192.112.36.4 # g.root-servers.net - primary: 193.0.14.129 # k.root-servers.net - primary: 192.0.47.132 # xfr.cjr.dns.icann.org - primary: 192.0.32.132 # xfr.lax.dns.icann.org - primary: 2001:500:200::b # b.root-servers.net - primary: 2001:500:2::c # c.root-servers.net - primary: 2001:500:2d::d # d.root-servers.net - primary: 2001:500:2f::f # f.root-servers.net - primary: 2001:500:12::d0d # g.root-servers.net - primary: 2001:7fd::1 # k.root-servers.net - primary: 2620:0:2830:202::132 # xfr.cjr.dns.icann.org - primary: 2620:0:2d0:202::132 # xfr.lax.dns.icann.org - fallback-enabled: yes - for-downstream: no - for-upstream: yes - -# auth-zone: -# name: "example.org" -# for-downstream: yes -# for-upstream: yes -# zonemd-check: no -# zonemd-reject-absence: no -# zonefile: "example.org.zone" - -# Views -# Create named views. Name must be unique. Map views to requests using -# the access-control-view option. Views can contain zero or more local-zone -# and local-data options. Options from matching views will override global -# options. Global options will be used if no matching view is found. -# With view-first yes, it will try to answer using the global local-zone and -# local-data elements if there is no view specific match. -# view: -# name: "viewname" -# local-zone: "example.com" redirect -# local-data: "example.com A 192.0.2.3" -# local-data-ptr: "192.0.2.3 www.example.com" -# view-first: no -# view: -# name: "anotherview" -# local-zone: "example.com" refuse - -# Fedora: DNSCrypt support not enabled since it requires linking to -# another crypto library -# -# DNSCrypt -# o enable, use --enable-dnscrypt to configure before compiling. -# Caveats: -# 1. the keys/certs cannot be produced by Unbound. You can use dnscrypt-wrapper -# for this: https://github.com/cofyc/dnscrypt-wrapper/blob/master/README.md#usage -# 2. dnscrypt channel attaches to an interface. you MUST set interfaces to -# listen on `dnscrypt-port` with the follo0wing snippet: -# server: -# interface: 0.0.0.0@443 -# interface: ::0@443 -# -# Finally, `dnscrypt` config has its own section. -# dnscrypt: -# dnscrypt-enable: yes -# dnscrypt-port: 443 -# dnscrypt-provider: 2.dnscrypt-cert.example.com. -# dnscrypt-secret-key: /path/unbound-conf/keys1/1.key -# dnscrypt-secret-key: /path/unbound-conf/keys2/1.key -# dnscrypt-provider-cert: /path/unbound-conf/keys1/1.cert -# dnscrypt-provider-cert: /path/unbound-conf/keys2/1.cert - -# CacheDB -# External backend DB as auxiliary cache. -# To enable, use --enable-cachedb to configure before compiling. -# Specify the backend name -# (default is "testframe", which has no use other than for debugging and -# testing) and backend-specific options. The 'cachedb' module must be -# included in module-config, just before the iterator module. -# cachedb: -# backend: "testframe" -# # secret seed string to calculate hashed keys -# secret-seed: "default" -# -# # For "redis" backend: -# # (to enable, use --with-libhiredis to configure before compiling) -# # redis server's IP address or host name -# redis-server-host: 127.0.0.1 -# # redis server's TCP port -# redis-server-port: 6379 -# # timeout (in ms) for communication with the redis server -# redis-timeout: 100 -# # set timeout on redis records based on DNS response TTL -# redis-expire-records: no - -# IPSet -# Add specify domain into set via ipset. -# To enable: -# o use --enable-ipset to configure before compiling; -# o Unbound then needs to run as root user. -# ipset: -# # set name for ip v4 addresses -# name-v4: "list-v4" -# # set name for ip v6 addresses -# name-v6: "list-v6" -# - -# Dnstap logging support, if compiled in by using --enable-dnstap to configure. -# To enable, set the dnstap-enable to yes and also some of -# dnstap-log-..-messages to yes. And select an upstream log destination, by -# socket path, TCP or TLS destination. -# dnstap: -# dnstap-enable: no -# # if set to yes frame streams will be used in bidirectional mode -# dnstap-bidirectional: yes -# dnstap-socket-path: "/etc/unbound/dnstap.sock" -# # if "" use the unix socket in dnstap-socket-path, otherwise, -# # set it to "IPaddress[@port]" of the destination. -# dnstap-ip: "" -# # if set to yes if you want to use TLS to dnstap-ip, no for TCP. -# dnstap-tls: yes -# # name for authenticating the upstream server. or "" disabled. -# dnstap-tls-server-name: "" -# # if "", it uses the cert bundle from the main Unbound config. -# dnstap-tls-cert-bundle: "" -# # key file for client authentication, or "" disabled. -# dnstap-tls-client-key-file: "" -# # cert file for client authentication, or "" disabled. -# dnstap-tls-client-cert-file: "" -# dnstap-send-identity: no -# dnstap-send-version: no -# # if "" it uses the hostname. -# dnstap-identity: "" -# # if "" it uses the package version. -# dnstap-version: "" -# dnstap-log-resolver-query-messages: no -# dnstap-log-resolver-response-messages: no -# dnstap-log-client-query-messages: no -# dnstap-log-client-response-messages: no -# dnstap-log-forwarder-query-messages: no -# dnstap-log-forwarder-response-messages: no - -# Response Policy Zones -# RPZ policies. Applied in order of configuration. QNAME, Response IP -# Address, nsdname, nsip and clientip triggers are supported. Supported -# actions are: NXDOMAIN, NODATA, PASSTHRU, DROP, Local Data, tcp-only -# and drop. Policies can be loaded from a file, or using zone -# transfer, or using HTTP. The respip module needs to be added -# to the module-config, e.g.: module-config: "respip validator iterator". -# rpz: -# name: "rpz.example.com" -# zonefile: "rpz.example.com" -# primary: 192.0.2.0 -# allow-notify: 192.0.2.0/32 -# url: http://www.example.com/rpz.example.org.zone -# rpz-action-override: cname -# rpz-cname-override: www.example.org -# rpz-log: yes -# rpz-log-name: "example policy" -# rpz-signal-nxdomain-ra: no -# for-downstream: no -# tags: "example" diff --git a/unbound.service b/unbound.service index 7369e92456bac55328c9a74ce84de7b44381dc35..76be93caae780c145d5cee9edc891cdbd5818327 100644 --- a/unbound.service +++ b/unbound.service @@ -14,6 +14,7 @@ ExecStartPre=/usr/sbin/unbound-checkconf ExecStartPre=/bin/bash -c 'if [ ! "$DISABLE_UNBOUND_ANCHOR" == "yes" ]; then /usr/sbin/unbound-anchor -a /var/lib/unbound/root.key -c /etc/unbound/icannbundle.pem -f /etc/resolv.conf -R; else echo "Updates of root keys with unbound-anchor is disabled"; fi' ExecStart=/usr/sbin/unbound -d $UNBOUND_OPTIONS ExecReload=+/bin/kill -HUP $MAINPID +Restart=on-abnormal [Install] WantedBy=multi-user.target diff --git a/unbound.spec b/unbound.spec index 00fd0a34b167ac77ea2801e28797731b1fb10c1a..404338ed05913fc210b03aad42f3a26eece956ce 100644 --- a/unbound.spec +++ b/unbound.spec @@ -1,14 +1,13 @@ %{!?delete_la: %global delete_la find $RPM_BUILD_ROOT -type f -name "*.la" -delete} Name: unbound -Version: 1.17.1 -Release: 11 +Version: 1.22.0 +Release: 1 Summary: Unbound is a validating, recursive, caching DNS resolver License: BSD-3-Clause Url: https://nlnetlabs.nl/projects/unbound/about/ Source: https://nlnetlabs.nl/downloads/unbound/%{name}-%{version}.tar.gz Source1: unbound.service -Source2: unbound.conf Source3: root.key Source4: unbound-keygen.service Source5: tmpfiles-unbound.conf @@ -22,19 +21,8 @@ Source12: unbound-anchor.timer Source13: unbound-anchor.service Patch1: unbound-remove-buildin-key.patch -Patch2: backport-CVE-2023-50387_CVE-2023-50868.patch -Patch3: backport-pre-CVE-2024-33655-extended_error_encode-for-extended-errors.patch -Patch4: backport-pre-CVE-2024-33655-Downstream-DNS-Cookies-a-la-RFC7873-and-RFC9018.patch -Patch5: backport-pre-CVE-2024-33655-Fix-possibly-unaligned-memory-access-in-parse_edns_options_from_query.patch -Patch6: backport-pre-CVE-2024-33655-Fix-out-of-bounds-read-in-parse_edns_options_from_query.patch -Patch7: backport-CVE-2024-33655.patch -Patch8: backport-CVE-2024-43167.patch -Patch9: backport-001-CVE-2024-43168.patch -Patch10: backport-002-CVE-2024-43168.patch -Patch11: backport-003-CVE-2024-43168.patch -Patch12: backport-004-CVE-2024-43168.patch -Patch13: backport-CVE-2024-8508.patch Patch14: backport-check-before-use-daemon-shm_info.patch +Patch15: backport-unbound-config.patch BuildRequires: make flex swig pkgconfig systemd BuildRequires: libevent-devel expat-devel openssl-devel python3-devel @@ -118,6 +106,7 @@ popd --with-pidfile=%{_rundir}/%{name}/%{name}.pid \\\ --enable-sha2 --disable-gost --enable-ecdsa \\\ --with-rootkey-file=%{_sharedstatedir}/unbound/root.key \\\ + --with-username=unbound \\\ --enable-linux-ip-local-port-range pushd %{name}-%{version} @@ -146,7 +135,7 @@ install -m 0600 %{SOURCE10} $RPM_BUILD_ROOT%{_sharedstatedir}/unbound/root.key %endif install -p -m 0644 %{SOURCE1} $RPM_BUILD_ROOT%{_unitdir}/unbound.service -install -p -m 0755 %{SOURCE2} $RPM_BUILD_ROOT%{_sysconfdir}/unbound +install -p -m 0755 %{name}-%{version}/doc/example.conf %{buildroot}%{_sysconfdir}/unbound/unbound.conf install -p -m 0644 %{SOURCE4} $RPM_BUILD_ROOT%{_unitdir}/unbound-keygen.service install -p -m 0644 %{SOURCE9} $RPM_BUILD_ROOT%{_sysconfdir}/unbound install -p -m 0644 %{SOURCE11} $RPM_BUILD_ROOT%{_sysconfdir}/sysconfig/unbound @@ -271,6 +260,12 @@ popd %{_mandir}/man* %changelog +* Mon Mar 31 2025 gaihuiying - 1.22.0-1 +- Type:requirement +- CVE:NA +- SUG:NA +- DESC:update to 1.22.0 + * Thu Jan 23 2025 gaihuiying - 1.17.1-11 - Type:bugfix - CVE:NA