From add20fbb15ba3c06d19e57a31face0bf926de5e5 Mon Sep 17 00:00:00 2001 From: eabdullin Date: Mon, 15 Sep 2025 12:54:33 +0000 Subject: [PATCH] import CS unbound-1.16.2-21.el9 --- SOURCES/unbound-1.16-control-key-perms.patch | 14 + SOURCES/unbound-1.21-CVE-2024-8508.patch | 249 ++ SOURCES/unbound-1.23.1-CVE-2025-5994.patch | 2255 ++++++++++++++++++ SPECS/unbound.spec | 42 +- 4 files changed, 2558 insertions(+), 2 deletions(-) create mode 100644 SOURCES/unbound-1.16-control-key-perms.patch create mode 100644 SOURCES/unbound-1.21-CVE-2024-8508.patch create mode 100644 SOURCES/unbound-1.23.1-CVE-2025-5994.patch diff --git a/SOURCES/unbound-1.16-control-key-perms.patch b/SOURCES/unbound-1.16-control-key-perms.patch new file mode 100644 index 0000000..de3597c --- /dev/null +++ b/SOURCES/unbound-1.16-control-key-perms.patch @@ -0,0 +1,14 @@ +diff --git a/unbound-1.16.2/smallapp/unbound-control-setup.sh.in b/unbound-1.16.2/smallapp/unbound-control-setup.sh.in +index 4a358f6bd..c2a79a242 100644 +--- a/unbound-1.16.2/smallapp/unbound-control-setup.sh.in ++++ b/unbound-1.16.2/smallapp/unbound-control-setup.sh.in +@@ -204,7 +204,8 @@ fi + # remove unused permissions + chmod o-rw \ + "$SVR_BASE.pem" \ +- "$SVR_BASE.key" \ ++ "$SVR_BASE.key" ++chmod g+r,o-rw \ + "$CTL_BASE.pem" \ + "$CTL_BASE.key" + diff --git a/SOURCES/unbound-1.21-CVE-2024-8508.patch b/SOURCES/unbound-1.21-CVE-2024-8508.patch new file mode 100644 index 0000000..36b3cdd --- /dev/null +++ b/SOURCES/unbound-1.21-CVE-2024-8508.patch @@ -0,0 +1,249 @@ +From 34de24d58bb5aa6fe3551512fc17cac08f65d93e Mon Sep 17 00:00:00 2001 +From: Yorgos Thessalonikefs +Date: Thu, 3 Oct 2024 14:46:57 +0200 +Subject: [PATCH] - Fix CVE-2024-8508, unbounded name compression could lead to + denial of service. + +--- + unbound-1.16.2/util/data/msgencode.c | 77 +++++++++++++++++----------- + 1 file changed, 46 insertions(+), 31 deletions(-) + +diff --git a/unbound-1.16.2/util/data/msgencode.c b/unbound-1.16.2/util/data/msgencode.c +index fe21cfb..f9e95e6 100644 +--- a/unbound-1.16.2/util/data/msgencode.c ++++ b/unbound-1.16.2/util/data/msgencode.c +@@ -62,6 +62,10 @@ + #define RETVAL_TRUNC -4 + /** return code that means all is peachy keen. Equal to DNS rcode NOERROR */ + #define RETVAL_OK 0 ++/** Max compressions we are willing to perform; more than that will result ++ * in semi-compressed messages, or truncated even on TCP for huge messages, to ++ * avoid locking the CPU for long */ ++#define MAX_COMPRESSION_PER_MESSAGE 120 + + /** + * Data structure to help domain name compression in outgoing messages. +@@ -284,15 +288,17 @@ write_compressed_dname(sldns_buffer* pkt, uint8_t* dname, int labs, + + /** compress owner name of RR, return RETVAL_OUTMEM RETVAL_TRUNC */ + static int +-compress_owner(struct ub_packed_rrset_key* key, sldns_buffer* pkt, +- struct regional* region, struct compress_tree_node** tree, +- size_t owner_pos, uint16_t* owner_ptr, int owner_labs) ++compress_owner(struct ub_packed_rrset_key* key, sldns_buffer* pkt, ++ struct regional* region, struct compress_tree_node** tree, ++ size_t owner_pos, uint16_t* owner_ptr, int owner_labs, ++ size_t* compress_count) + { + struct compress_tree_node* p; + struct compress_tree_node** insertpt = NULL; + if(!*owner_ptr) { + /* compress first time dname */ +- if((p = compress_tree_lookup(tree, key->rk.dname, ++ if(*compress_count < MAX_COMPRESSION_PER_MESSAGE && ++ (p = compress_tree_lookup(tree, key->rk.dname, + owner_labs, &insertpt))) { + if(p->labs == owner_labs) + /* avoid ptr chains, since some software is +@@ -301,6 +307,7 @@ compress_owner(struct ub_packed_rrset_key* key, sldns_buffer* pkt, + if(!write_compressed_dname(pkt, key->rk.dname, + owner_labs, p)) + return RETVAL_TRUNC; ++ (*compress_count)++; + /* check if typeclass+4 ttl + rdatalen is available */ + if(sldns_buffer_remaining(pkt) < 4+4+2) + return RETVAL_TRUNC; +@@ -313,7 +320,8 @@ compress_owner(struct ub_packed_rrset_key* key, sldns_buffer* pkt, + if(owner_pos <= PTR_MAX_OFFSET) + *owner_ptr = htons(PTR_CREATE(owner_pos)); + } +- if(!compress_tree_store(key->rk.dname, owner_labs, ++ if(*compress_count < MAX_COMPRESSION_PER_MESSAGE && ++ !compress_tree_store(key->rk.dname, owner_labs, + owner_pos, region, p, insertpt)) + return RETVAL_OUTMEM; + } else { +@@ -333,20 +341,24 @@ compress_owner(struct ub_packed_rrset_key* key, sldns_buffer* pkt, + + /** compress any domain name to the packet, return RETVAL_* */ + static int +-compress_any_dname(uint8_t* dname, sldns_buffer* pkt, int labs, +- struct regional* region, struct compress_tree_node** tree) ++compress_any_dname(uint8_t* dname, sldns_buffer* pkt, int labs, ++ struct regional* region, struct compress_tree_node** tree, ++ size_t* compress_count) + { + struct compress_tree_node* p; + struct compress_tree_node** insertpt = NULL; + size_t pos = sldns_buffer_position(pkt); +- if((p = compress_tree_lookup(tree, dname, labs, &insertpt))) { ++ if(*compress_count < MAX_COMPRESSION_PER_MESSAGE && ++ (p = compress_tree_lookup(tree, dname, labs, &insertpt))) { + if(!write_compressed_dname(pkt, dname, labs, p)) + return RETVAL_TRUNC; ++ (*compress_count)++; + } else { + if(!dname_buffer_write(pkt, dname)) + return RETVAL_TRUNC; + } +- if(!compress_tree_store(dname, labs, pos, region, p, insertpt)) ++ if(*compress_count < MAX_COMPRESSION_PER_MESSAGE && ++ !compress_tree_store(dname, labs, pos, region, p, insertpt)) + return RETVAL_OUTMEM; + return RETVAL_OK; + } +@@ -364,9 +376,9 @@ type_rdata_compressable(struct ub_packed_rrset_key* key) + + /** compress domain names in rdata, return RETVAL_* */ + static int +-compress_rdata(sldns_buffer* pkt, uint8_t* rdata, size_t todolen, +- struct regional* region, struct compress_tree_node** tree, +- const sldns_rr_descriptor* desc) ++compress_rdata(sldns_buffer* pkt, uint8_t* rdata, size_t todolen, ++ struct regional* region, struct compress_tree_node** tree, ++ const sldns_rr_descriptor* desc, size_t* compress_count) + { + int labs, r, rdf = 0; + size_t dname_len, len, pos = sldns_buffer_position(pkt); +@@ -380,8 +392,8 @@ compress_rdata(sldns_buffer* pkt, uint8_t* rdata, size_t todolen, + switch(desc->_wireformat[rdf]) { + case LDNS_RDF_TYPE_DNAME: + labs = dname_count_size_labels(rdata, &dname_len); +- if((r=compress_any_dname(rdata, pkt, labs, region, +- tree)) != RETVAL_OK) ++ if((r=compress_any_dname(rdata, pkt, labs, region, ++ tree, compress_count)) != RETVAL_OK) + return r; + rdata += dname_len; + todolen -= dname_len; +@@ -449,7 +461,8 @@ static int + packed_rrset_encode(struct ub_packed_rrset_key* key, sldns_buffer* pkt, + uint16_t* num_rrs, time_t timenow, struct regional* region, + int do_data, int do_sig, struct compress_tree_node** tree, +- sldns_pkt_section s, uint16_t qtype, int dnssec, size_t rr_offset) ++ sldns_pkt_section s, uint16_t qtype, int dnssec, size_t rr_offset, ++ size_t* compress_count) + { + size_t i, j, owner_pos; + int r, owner_labs; +@@ -477,9 +490,9 @@ packed_rrset_encode(struct ub_packed_rrset_key* key, sldns_buffer* pkt, + for(i=0; icount; i++) { + /* rrset roundrobin */ + j = (i + rr_offset) % data->count; +- if((r=compress_owner(key, pkt, region, tree, +- owner_pos, &owner_ptr, owner_labs)) +- != RETVAL_OK) ++ if((r=compress_owner(key, pkt, region, tree, ++ owner_pos, &owner_ptr, owner_labs, ++ compress_count)) != RETVAL_OK) + return r; + sldns_buffer_write(pkt, &key->rk.type, 2); + sldns_buffer_write(pkt, &key->rk.rrset_class, 2); +@@ -489,8 +502,8 @@ packed_rrset_encode(struct ub_packed_rrset_key* key, sldns_buffer* pkt, + else sldns_buffer_write_u32(pkt, data->rr_ttl[j]-adjust); + if(c) { + if((r=compress_rdata(pkt, data->rr_data[j], +- data->rr_len[j], region, tree, c)) +- != RETVAL_OK) ++ data->rr_len[j], region, tree, c, ++ compress_count)) != RETVAL_OK) + return r; + } else { + if(sldns_buffer_remaining(pkt) < data->rr_len[j]) +@@ -510,9 +523,9 @@ packed_rrset_encode(struct ub_packed_rrset_key* key, sldns_buffer* pkt, + return RETVAL_TRUNC; + sldns_buffer_write(pkt, &owner_ptr, 2); + } else { +- if((r=compress_any_dname(key->rk.dname, +- pkt, owner_labs, region, tree)) +- != RETVAL_OK) ++ if((r=compress_any_dname(key->rk.dname, ++ pkt, owner_labs, region, tree, ++ compress_count)) != RETVAL_OK) + return r; + if(sldns_buffer_remaining(pkt) < + 4+4+data->rr_len[i]) +@@ -544,7 +557,8 @@ static int + insert_section(struct reply_info* rep, size_t num_rrsets, uint16_t* num_rrs, + sldns_buffer* pkt, size_t rrsets_before, time_t timenow, + struct regional* region, struct compress_tree_node** tree, +- sldns_pkt_section s, uint16_t qtype, int dnssec, size_t rr_offset) ++ sldns_pkt_section s, uint16_t qtype, int dnssec, size_t rr_offset, ++ size_t* compress_count) + { + int r; + size_t i, setstart; +@@ -560,7 +574,7 @@ insert_section(struct reply_info* rep, size_t num_rrsets, uint16_t* num_rrs, + setstart = sldns_buffer_position(pkt); + if((r=packed_rrset_encode(rep->rrsets[rrsets_before+i], + pkt, num_rrs, timenow, region, 1, 1, tree, +- s, qtype, dnssec, rr_offset)) ++ s, qtype, dnssec, rr_offset, compress_count)) + != RETVAL_OK) { + /* Bad, but if due to size must set TC bit */ + /* trim off the rrset neatly. */ +@@ -573,7 +587,7 @@ insert_section(struct reply_info* rep, size_t num_rrsets, uint16_t* num_rrs, + setstart = sldns_buffer_position(pkt); + if((r=packed_rrset_encode(rep->rrsets[rrsets_before+i], + pkt, num_rrs, timenow, region, 1, 0, tree, +- s, qtype, dnssec, rr_offset)) ++ s, qtype, dnssec, rr_offset, compress_count)) + != RETVAL_OK) { + sldns_buffer_set_position(pkt, setstart); + return r; +@@ -584,7 +598,7 @@ insert_section(struct reply_info* rep, size_t num_rrsets, uint16_t* num_rrs, + setstart = sldns_buffer_position(pkt); + if((r=packed_rrset_encode(rep->rrsets[rrsets_before+i], + pkt, num_rrs, timenow, region, 0, 1, tree, +- s, qtype, dnssec, rr_offset)) ++ s, qtype, dnssec, rr_offset, compress_count)) + != RETVAL_OK) { + sldns_buffer_set_position(pkt, setstart); + return r; +@@ -677,6 +691,7 @@ reply_info_encode(struct query_info* qinfo, struct reply_info* rep, + struct compress_tree_node* tree = 0; + int r; + size_t rr_offset; ++ size_t compress_count=0; + + sldns_buffer_clear(buffer); + if(udpsize < sldns_buffer_limit(buffer)) +@@ -723,7 +738,7 @@ reply_info_encode(struct query_info* qinfo, struct reply_info* rep, + arep.rrsets = &qinfo->local_alias->rrset; + if((r=insert_section(&arep, 1, &ancount, buffer, 0, + timezero, region, &tree, LDNS_SECTION_ANSWER, +- qinfo->qtype, dnssec, rr_offset)) != RETVAL_OK) { ++ qinfo->qtype, dnssec, rr_offset, &compress_count)) != RETVAL_OK) { + if(r == RETVAL_TRUNC) { + /* create truncated message */ + sldns_buffer_write_u16_at(buffer, 6, ancount); +@@ -738,7 +753,7 @@ reply_info_encode(struct query_info* qinfo, struct reply_info* rep, + /* insert answer section */ + if((r=insert_section(rep, rep->an_numrrsets, &ancount, buffer, + 0, timenow, region, &tree, LDNS_SECTION_ANSWER, qinfo->qtype, +- dnssec, rr_offset)) != RETVAL_OK) { ++ dnssec, rr_offset, &compress_count)) != RETVAL_OK) { + if(r == RETVAL_TRUNC) { + /* create truncated message */ + sldns_buffer_write_u16_at(buffer, 6, ancount); +@@ -756,7 +771,7 @@ reply_info_encode(struct query_info* qinfo, struct reply_info* rep, + if((r=insert_section(rep, rep->ns_numrrsets, &nscount, buffer, + rep->an_numrrsets, timenow, region, &tree, + LDNS_SECTION_AUTHORITY, qinfo->qtype, +- dnssec, rr_offset)) != RETVAL_OK) { ++ dnssec, rr_offset, &compress_count)) != RETVAL_OK) { + if(r == RETVAL_TRUNC) { + /* create truncated message */ + sldns_buffer_write_u16_at(buffer, 8, nscount); +@@ -773,7 +788,7 @@ reply_info_encode(struct query_info* qinfo, struct reply_info* rep, + if((r=insert_section(rep, rep->ar_numrrsets, &arcount, buffer, + rep->an_numrrsets + rep->ns_numrrsets, timenow, region, + &tree, LDNS_SECTION_ADDITIONAL, qinfo->qtype, +- dnssec, rr_offset)) != RETVAL_OK) { ++ dnssec, rr_offset, &compress_count)) != RETVAL_OK) { + if(r == RETVAL_TRUNC) { + /* no need to set TC bit, this is the additional */ + sldns_buffer_write_u16_at(buffer, 10, arcount); +-- +2.47.0 + diff --git a/SOURCES/unbound-1.23.1-CVE-2025-5994.patch b/SOURCES/unbound-1.23.1-CVE-2025-5994.patch new file mode 100644 index 0000000..a6e034d --- /dev/null +++ b/SOURCES/unbound-1.23.1-CVE-2025-5994.patch @@ -0,0 +1,2255 @@ +diff --git a/unbound-1.16.2/edns-subnet/addrtree.c b/unbound-1.16.2/edns-subnet/addrtree.c +index 180a022..ebe71b9 100644 +--- a/unbound-1.16.2/edns-subnet/addrtree.c ++++ b/unbound-1.16.2/edns-subnet/addrtree.c +@@ -97,6 +97,7 @@ node_create(struct addrtree *tree, void *elem, addrlen_t scope, + tree->node_count++; + node->scope = scope; + node->ttl = ttl; ++ node->only_match_scope_zero = 0; + node->edge[0] = NULL; + node->edge[1] = NULL; + node->parent_edge = NULL; +@@ -155,6 +156,7 @@ clean_node(struct addrtree *tree, struct addrnode *node) + if (!node->elem) return; + tree->size_bytes -= tree->sizefunc(node->elem); + tree->delfunc(tree->env, node->elem); ++ node->only_match_scope_zero = 0; + node->elem = NULL; + } + +@@ -358,7 +360,7 @@ issub(const addrkey_t *s1, addrlen_t l1, + void + addrtree_insert(struct addrtree *tree, const addrkey_t *addr, + addrlen_t sourcemask, addrlen_t scope, void *elem, time_t ttl, +- time_t now) ++ time_t now, int only_match_scope_zero) + { + struct addrnode *newnode, *node; + struct addredge *edge; +@@ -381,6 +383,7 @@ addrtree_insert(struct addrtree *tree, const addrkey_t *addr, + /* update this node's scope and data */ + clean_node(tree, node); + node->ttl = ttl; ++ node->only_match_scope_zero = only_match_scope_zero; + node->elem = elem; + node->scope = scope; + tree->size_bytes += tree->sizefunc(elem); +@@ -447,6 +450,7 @@ addrtree_insert(struct addrtree *tree, const addrkey_t *addr, + newnode->elem = elem; + newnode->scope = scope; + newnode->ttl = ttl; ++ newnode->only_match_scope_zero = only_match_scope_zero; + } + + tree->size_bytes += node_size(tree, newnode); +@@ -483,7 +487,8 @@ addrtree_find(struct addrtree *tree, const addrkey_t *addr, + /* Current node more specific then question. */ + log_assert(depth <= sourcemask); + /* does this node have data? if yes, see if we have a match */ +- if (node->elem && node->ttl >= now) { ++ if (node->elem && node->ttl >= now && ++ !(sourcemask != 0 && node->only_match_scope_zero)) { + /* saved at wrong depth */; + log_assert(node->scope >= depth); + if (depth == node->scope || +diff --git a/unbound-1.16.2/edns-subnet/addrtree.h b/unbound-1.16.2/edns-subnet/addrtree.h +index 1aea54e..0bc1837 100644 +--- a/unbound-1.16.2/edns-subnet/addrtree.h ++++ b/unbound-1.16.2/edns-subnet/addrtree.h +@@ -95,6 +95,10 @@ struct addrnode { + time_t ttl; + /** Number of significant bits in address. */ + addrlen_t scope; ++ /** Only use the element for queries for subnet/0. Set if the query ++ * for /0 was answered with scope 0. For query /x answer scope 0, ++ * they can match anything and this is false. */ ++ int only_match_scope_zero; + /** A node can have 0-2 edges, set to NULL for unused */ + struct addredge *edge[2]; + /** edge between this node and parent */ +@@ -157,11 +161,12 @@ void addrtree_delete(struct addrtree *tree); + * @param scope: Number of significant bits in addr. + * @param elem: data to store in the tree. + * @param ttl: elem is valid up to this time, seconds. ++ * @param only_match_scope_zero: set for when query /0 has scope /0 answer. + * @param now: Current time in seconds. + */ + void addrtree_insert(struct addrtree *tree, const addrkey_t *addr, + addrlen_t sourcemask, addrlen_t scope, void *elem, time_t ttl, +- time_t now); ++ time_t now, int only_match_scope_zero); + + /** + * Find a node containing an element in the tree. +diff --git a/unbound-1.16.2/edns-subnet/subnetmod.c b/unbound-1.16.2/edns-subnet/subnetmod.c +index 7544611..53d3048 100644 +--- a/unbound-1.16.2/edns-subnet/subnetmod.c ++++ b/unbound-1.16.2/edns-subnet/subnetmod.c +@@ -51,10 +51,12 @@ + #include "services/cache/dns.h" + #include "util/module.h" + #include "util/regional.h" ++#include "util/fptr_wlist.h" + #include "util/storage/slabhash.h" + #include "util/config_file.h" + #include "util/data/msgreply.h" + #include "sldns/sbuffer.h" ++#include "sldns/wire2str.h" + #include "iterator/iter_utils.h" + + /** externally called */ +@@ -151,10 +153,12 @@ int ecs_whitelist_check(struct query_info* qinfo, + + /* Cache by default, might be disabled after parsing EDNS option + * received from nameserver. */ +- if(!iter_stub_fwd_no_cache(qstate, &qstate->qinfo, NULL, NULL)) { ++ if(!iter_stub_fwd_no_cache(qstate, &qstate->qinfo, NULL, NULL) ++ && sq->ecs_client_in.subnet_validdata) { + qstate->no_cache_store = 0; + } + ++ sq->subnet_sent_no_subnet = 0; + if(sq->ecs_server_out.subnet_validdata && ((sq->subnet_downstream && + qstate->env->cfg->client_subnet_always_forward) || + ecs_is_whitelisted(sn_env->whitelist, +@@ -165,6 +169,14 @@ int ecs_whitelist_check(struct query_info* qinfo, + * set. */ + if(!edns_opt_list_find(qstate->edns_opts_back_out, + qstate->env->cfg->client_subnet_opcode)) { ++ /* if the client is not wanting an EDNS subnet option, ++ * omit it and store that we omitted it but actually ++ * are doing EDNS subnet to the server. */ ++ if(sq->ecs_server_out.subnet_source_mask == 0) { ++ sq->subnet_sent_no_subnet = 1; ++ sq->subnet_sent = 0; ++ return 1; ++ } + subnet_ecs_opt_list_append(&sq->ecs_server_out, + &qstate->edns_opts_back_out, qstate, region); + } +@@ -331,6 +343,7 @@ update_cache(struct module_qstate *qstate, int id) + struct slabhash *subnet_msg_cache = sne->subnet_msg_cache; + struct ecs_data *edns = &sq->ecs_client_in; + size_t i; ++ int only_match_scope_zero; + + /* We already calculated hash upon lookup (lookup_and_reply) if we were + * allowed to look in the ECS cache */ +@@ -392,9 +405,12 @@ update_cache(struct module_qstate *qstate, int id) + reply_info_set_ttls(rep, *qstate->env->now); + rep->flags |= (BIT_RA | BIT_QR); /* fix flags to be sensible for */ + rep->flags &= ~(BIT_AA | BIT_CD);/* a reply based on the cache */ ++ if(edns->subnet_source_mask == 0 && edns->subnet_scope_mask == 0) ++ only_match_scope_zero = 1; ++ else only_match_scope_zero = 0; + addrtree_insert(tree, (addrkey_t*)edns->subnet_addr, + edns->subnet_source_mask, sq->max_scope, rep, +- rep->ttl, *qstate->env->now); ++ rep->ttl, *qstate->env->now, only_match_scope_zero); + + lock_rw_unlock(&lru_entry->lock); + if (need_to_insert) { +@@ -475,6 +491,69 @@ common_prefix(uint8_t *a, uint8_t *b, uint8_t net) + return !memcmp(a, b, n) && ((net % 8) == 0 || a[n] == b[n]); + } + ++/** ++ * Create sub request that looks up the query. ++ * @param qstate: query state ++ * @param sq: subnet qstate ++ * @return false on failure. ++ */ ++static int ++generate_sub_request(struct module_qstate *qstate, struct subnet_qstate* sq) ++{ ++ struct module_qstate* subq = NULL; ++ uint16_t qflags = 0; /* OPCODE QUERY, no flags */ ++ int prime = 0; ++ int valrec = 0; ++ struct query_info qinf; ++ qinf.qname = qstate->qinfo.qname; ++ qinf.qname_len = qstate->qinfo.qname_len; ++ qinf.qtype = qstate->qinfo.qtype; ++ qinf.qclass = qstate->qinfo.qclass; ++ qinf.local_alias = NULL; ++ ++ qflags |= BIT_RD; ++ if((qstate->query_flags & BIT_CD)!=0) { ++ qflags |= BIT_CD; ++ valrec = 1; ++ } ++ ++ fptr_ok(fptr_whitelist_modenv_attach_sub(qstate->env->attach_sub)); ++ if(!(*qstate->env->attach_sub)(qstate, &qinf, qflags, prime, valrec, ++ &subq)) { ++ return 0; ++ } ++ if(subq) { ++ /* It is possible to access the subquery module state. */ ++ if(sq->ecs_client_in.subnet_source_mask == 0 && ++ edns_opt_list_find(qstate->edns_opts_front_in, ++ qstate->env->cfg->client_subnet_opcode)) { ++ subq->no_cache_store = 1; ++ } ++ } ++ return 1; ++} ++ ++/** ++ * Perform the query without subnet ++ * @param qstate: query state ++ * @param sq: subnet qstate ++ * @return module state ++ */ ++static enum module_ext_state ++generate_lookup_without_subnet(struct module_qstate *qstate, ++ struct subnet_qstate* sq) ++{ ++ verbose(VERB_ALGO, "subnetcache: make subquery to look up without subnet"); ++ if(!generate_sub_request(qstate, sq)) { ++ verbose(VERB_ALGO, "Could not generate sub query"); ++ qstate->return_rcode = LDNS_RCODE_FORMERR; ++ qstate->return_msg = NULL; ++ return module_finished; ++ } ++ sq->wait_subquery = 1; ++ return module_wait_subquery; ++} ++ + static enum module_ext_state + eval_response(struct module_qstate *qstate, int id, struct subnet_qstate *sq) + { +@@ -495,7 +574,7 @@ eval_response(struct module_qstate *qstate, int id, struct subnet_qstate *sq) + } + + /* We have not asked for subnet data */ +- if (!sq->subnet_sent) { ++ if (!sq->subnet_sent && !sq->subnet_sent_no_subnet) { + if (s_in->subnet_validdata) + verbose(VERB_QUERY, "subnetcache: received spurious data"); + if (sq->subnet_downstream) /* Copy back to client */ +@@ -504,22 +583,27 @@ eval_response(struct module_qstate *qstate, int id, struct subnet_qstate *sq) + } + + /* subnet sent but nothing came back */ +- if (!s_in->subnet_validdata) { ++ if (!s_in->subnet_validdata && !sq->subnet_sent_no_subnet) { + /* The authority indicated no support for edns subnet. As a + * consequence the answer ended up in the regular cache. It + * is still useful to put it in the edns subnet cache for + * when a client explicitly asks for subnet specific answer. */ + verbose(VERB_QUERY, "subnetcache: Authority indicates no support"); +- if(!sq->started_no_cache_store) { +- lock_rw_wrlock(&sne->biglock); +- update_cache(qstate, id); +- lock_rw_unlock(&sne->biglock); +- } +- if (sq->subnet_downstream) +- cp_edns_bad_response(c_out, c_in); +- return module_finished; ++ return generate_lookup_without_subnet(qstate, sq); + } +- ++ ++ /* Purposefully there was no sent subnet, and there is consequently ++ * no subnet in the answer. If there was, use the subnet in the answer ++ * anyway. But if there is not, treat it as a prefix 0 answer. */ ++ if(sq->subnet_sent_no_subnet && !s_in->subnet_validdata) { ++ /* Fill in 0.0.0.0/0 scope 0, or ::0/0 scope 0, for caching. */ ++ s_in->subnet_addr_fam = s_out->subnet_addr_fam; ++ s_in->subnet_source_mask = 0; ++ s_in->subnet_scope_mask = 0; ++ memset(s_in->subnet_addr, 0, INET6_SIZE); ++ s_in->subnet_validdata = 1; ++ } ++ + /* Being here means we have asked for and got a subnet specific + * answer. Also, the answer from the authority is not yet cached + * anywhere. */ +@@ -530,13 +614,14 @@ eval_response(struct module_qstate *qstate, int id, struct subnet_qstate *sq) + !common_prefix(s_out->subnet_addr, s_in->subnet_addr, + s_out->subnet_source_mask)) + { +- /* we can not accept, restart query without option */ ++ /* we can not accept, perform query without option */ + verbose(VERB_QUERY, "subnetcache: forged data"); + s_out->subnet_validdata = 0; + (void)edns_opt_list_remove(&qstate->edns_opts_back_out, + qstate->env->cfg->client_subnet_opcode); + sq->subnet_sent = 0; +- return module_restart_next; ++ sq->subnet_sent_no_subnet = 0; ++ return generate_lookup_without_subnet(qstate, sq); + } + + lock_rw_wrlock(&sne->biglock); +@@ -656,6 +741,7 @@ ecs_query_response(struct module_qstate* qstate, struct dns_msg* response, + edns_opt_list_remove(&qstate->edns_opts_back_out, + qstate->env->cfg->client_subnet_opcode); + sq->subnet_sent = 0; ++ sq->subnet_sent_no_subnet = 0; + memset(&sq->ecs_server_out, 0, sizeof(sq->ecs_server_out)); + } else if (!sq->track_max_scope && + FLAGS_GET_RCODE(response->rep->flags) == LDNS_RCODE_NOERROR && +@@ -674,6 +760,24 @@ ecs_query_response(struct module_qstate* qstate, struct dns_msg* response, + return 1; + } + ++/** verbose print edns subnet option in pretty print */ ++static void ++subnet_log_print(const char* s, struct edns_option* ecs_opt) ++{ ++ if(verbosity >= VERB_ALGO) { ++ char buf[256]; ++ char* str = buf; ++ size_t str_len = sizeof(buf); ++ if(!ecs_opt) { ++ verbose(VERB_ALGO, "%s (null)", s); ++ return; ++ } ++ (void)sldns_wire2str_edns_subnet_print(&str, &str_len, ++ ecs_opt->opt_data, ecs_opt->opt_len); ++ verbose(VERB_ALGO, "%s %s", s, buf); ++ } ++} ++ + int + ecs_edns_back_parsed(struct module_qstate* qstate, int id, + void* ATTR_UNUSED(cbargs)) +@@ -688,6 +792,7 @@ ecs_edns_back_parsed(struct module_qstate* qstate, int id, + qstate->env->cfg->client_subnet_opcode)) && + parse_subnet_option(ecs_opt, &sq->ecs_server_in) && + sq->subnet_sent && sq->ecs_server_in.subnet_validdata) { ++ subnet_log_print("answer has edns subnet", ecs_opt); + /* Only skip global cache store if we sent an ECS option + * and received one back. Answers from non-whitelisted + * servers will end up in global cache. Answers for +@@ -698,6 +803,12 @@ ecs_edns_back_parsed(struct module_qstate* qstate, int id, + sq->ecs_server_in.subnet_scope_mask > + sq->max_scope)) + sq->max_scope = sq->ecs_server_in.subnet_scope_mask; ++ } else if(sq->subnet_sent_no_subnet) { ++ /* The answer can be stored as scope 0, not in global cache. */ ++ qstate->no_cache_store = 1; ++ } else if(sq->subnet_sent) { ++ /* Need another query to be able to store in global cache. */ ++ qstate->no_cache_store = 1; + } + + return 1; +@@ -715,6 +826,32 @@ subnetmod_operate(struct module_qstate *qstate, enum module_ev event, + strmodulevent(event)); + log_query_info(VERB_QUERY, "subnetcache operate: query", &qstate->qinfo); + ++ if(sq && sq->wait_subquery_done) { ++ /* The subquery lookup returned. */ ++ if(sq->ecs_client_in.subnet_source_mask == 0 && ++ edns_opt_list_find(qstate->edns_opts_front_in, ++ qstate->env->cfg->client_subnet_opcode)) { ++ if(!sq->started_no_cache_store && ++ qstate->return_msg) { ++ lock_rw_wrlock(&sne->biglock); ++ update_cache(qstate, id); ++ lock_rw_unlock(&sne->biglock); ++ } ++ if (sq->subnet_downstream) ++ cp_edns_bad_response(&sq->ecs_client_out, ++ &sq->ecs_client_in); ++ /* It is a scope zero lookup, append edns subnet ++ * option to the querier. */ ++ subnet_ecs_opt_list_append(&sq->ecs_client_out, ++ &qstate->edns_opts_front_out, qstate, ++ qstate->region); ++ } ++ sq->wait_subquery_done = 0; ++ qstate->ext_state[id] = module_finished; ++ qstate->no_cache_store = sq->started_no_cache_store; ++ qstate->no_cache_lookup = sq->started_no_cache_lookup; ++ return; ++ } + if((event == module_event_new || event == module_event_pass) && + sq == NULL) { + struct edns_option* ecs_opt; +@@ -725,6 +862,8 @@ subnetmod_operate(struct module_qstate *qstate, enum module_ev event, + } + + sq = (struct subnet_qstate*)qstate->minfo[id]; ++ if(sq->wait_subquery) ++ return; /* Wait for that subquery to return */ + + if((ecs_opt = edns_opt_list_find( + qstate->edns_opts_front_in, +@@ -736,6 +875,7 @@ subnetmod_operate(struct module_qstate *qstate, enum module_ev event, + qstate->ext_state[id] = module_finished; + return; + } ++ subnet_log_print("query has edns subnet", ecs_opt); + sq->subnet_downstream = 1; + } + else if(qstate->mesh_info->reply_list) { +@@ -748,6 +888,14 @@ subnetmod_operate(struct module_qstate *qstate, enum module_ev event, + /* No clients are interested in result or we could not + * parse it, we don't do client subnet */ + sq->ecs_server_out.subnet_validdata = 0; ++ if(edns_opt_list_find(qstate->edns_opts_front_in, ++ qstate->env->cfg->client_subnet_opcode)) { ++ /* aggregated this deaggregated state */ ++ qstate->ext_state[id] = ++ generate_lookup_without_subnet( ++ qstate, sq); ++ return; ++ } + verbose(VERB_ALGO, "subnetcache: pass to next module"); + qstate->ext_state[id] = module_wait_module; + return; +@@ -775,10 +923,25 @@ subnetmod_operate(struct module_qstate *qstate, enum module_ev event, + subnet_ecs_opt_list_append(&sq->ecs_client_out, + &qstate->edns_opts_front_out, qstate, + qstate->region); ++ if(verbosity >= VERB_ALGO) { ++ subnet_log_print("reply has edns subnet", ++ edns_opt_list_find( ++ qstate->edns_opts_front_out, ++ qstate->env->cfg-> ++ client_subnet_opcode)); ++ } + return; + } + lock_rw_unlock(&sne->biglock); + } ++ if(sq->ecs_client_in.subnet_source_mask == 0 && ++ edns_opt_list_find(qstate->edns_opts_front_in, ++ qstate->env->cfg->client_subnet_opcode)) { ++ /* client asked for resolution without edns subnet */ ++ qstate->ext_state[id] = generate_lookup_without_subnet( ++ qstate, sq); ++ return; ++ } + + sq->ecs_server_out.subnet_addr_fam = + sq->ecs_client_in.subnet_addr_fam; +@@ -815,6 +978,8 @@ subnetmod_operate(struct module_qstate *qstate, enum module_ev event, + qstate->ext_state[id] = module_wait_module; + return; + } ++ if(sq && sq->wait_subquery) ++ return; /* Wait for that subquery to return */ + /* Query handed back by next module, we have a 'final' answer */ + if(sq && event == module_event_moddone) { + qstate->ext_state[id] = eval_response(qstate, id, sq); +@@ -823,6 +988,13 @@ subnetmod_operate(struct module_qstate *qstate, enum module_ev event, + subnet_ecs_opt_list_append(&sq->ecs_client_out, + &qstate->edns_opts_front_out, qstate, + qstate->region); ++ if(verbosity >= VERB_ALGO) { ++ subnet_log_print("reply has edns subnet", ++ edns_opt_list_find( ++ qstate->edns_opts_front_out, ++ qstate->env->cfg-> ++ client_subnet_opcode)); ++ } + } + qstate->no_cache_store = sq->started_no_cache_store; + qstate->no_cache_lookup = sq->started_no_cache_lookup; +@@ -856,10 +1028,27 @@ subnetmod_clear(struct module_qstate *ATTR_UNUSED(qstate), + } + + void +-subnetmod_inform_super(struct module_qstate *ATTR_UNUSED(qstate), +- int ATTR_UNUSED(id), struct module_qstate *ATTR_UNUSED(super)) ++subnetmod_inform_super(struct module_qstate *qstate, int id, ++ struct module_qstate *super) + { +- /* Not used */ ++ struct subnet_qstate* super_sq = ++ (struct subnet_qstate*)super->minfo[id]; ++ log_query_info(VERB_ALGO, "subnetcache inform_super: query", ++ &super->qinfo); ++ super_sq->wait_subquery = 0; ++ super_sq->wait_subquery_done = 1; ++ if(qstate->return_rcode != LDNS_RCODE_NOERROR || ++ !qstate->return_msg) { ++ super->return_msg = NULL; ++ super->return_rcode = LDNS_RCODE_SERVFAIL; ++ return; ++ } ++ super->return_rcode = LDNS_RCODE_NOERROR; ++ super->return_msg = dns_copy_msg(qstate->return_msg, super->region); ++ if(!super->return_msg) { ++ log_err("subnetcache: copy response, out of memory"); ++ super->return_rcode = LDNS_RCODE_SERVFAIL; ++ } + } + + size_t +diff --git a/unbound-1.16.2/edns-subnet/subnetmod.h b/unbound-1.16.2/edns-subnet/subnetmod.h +index f0bcaad..3893820 100644 +--- a/unbound-1.16.2/edns-subnet/subnetmod.h ++++ b/unbound-1.16.2/edns-subnet/subnetmod.h +@@ -85,6 +85,13 @@ struct subnet_qstate { + struct ecs_data ecs_server_out; + int subnet_downstream; + int subnet_sent; ++ /** ++ * If there was no subnet sent because the client used source prefix ++ * length 0 for omitting the information. Then the answer is cached ++ * like subnet was a /0 scope. Like the subnet_sent flag, but when ++ * the EDNS subnet option is omitted because the client asked. ++ */ ++ int subnet_sent_no_subnet; + /** keep track of longest received scope, set after receiving CNAME for + * incoming QNAME. */ + int track_max_scope; +@@ -95,6 +102,10 @@ struct subnet_qstate { + int started_no_cache_store; + /** has the subnet module been started with no_cache_lookup? */ + int started_no_cache_lookup; ++ /** Wait for subquery that has been started for nonsubnet lookup. */ ++ int wait_subquery; ++ /** The subquery waited for is done. */ ++ int wait_subquery_done; + }; + + void subnet_data_delete(void* d, void* ATTR_UNUSED(arg)); +diff --git a/unbound-1.16.2/iterator/iterator.c b/unbound-1.16.2/iterator/iterator.c +index da9b799..3cacd5d 100644 +--- a/unbound-1.16.2/iterator/iterator.c ++++ b/unbound-1.16.2/iterator/iterator.c +@@ -3982,10 +3982,10 @@ process_response(struct module_qstate* qstate, struct iter_qstate* iq, + /* like packet got dropped */ + goto handle_it; + } +- if(!inplace_cb_edns_back_parsed_call(qstate->env, qstate)) { +- log_err("unable to call edns_back_parsed callback"); +- goto handle_it; +- } ++ } ++ if(!inplace_cb_edns_back_parsed_call(qstate->env, qstate)) { ++ log_err("unable to call edns_back_parsed callback"); ++ goto handle_it; + } + + /* remove CD-bit, we asked for in case we handle validation ourself */ +diff --git a/unbound-1.16.2/testcode/unitecs.c b/unbound-1.16.2/testcode/unitecs.c +index b240bfc..68d6907 100644 +--- a/unbound-1.16.2/testcode/unitecs.c ++++ b/unbound-1.16.2/testcode/unitecs.c +@@ -173,7 +173,7 @@ static void consistency_test(void) + for (i = 0; i < 1000; i++) { + l = randomkey(&k, 128); + elem = (struct reply_info *) calloc(1, sizeof(struct reply_info)); +- addrtree_insert(t, k, l, 64, elem, timenow + 10, timenow); ++ addrtree_insert(t, k, l, 64, elem, timenow + 10, timenow, 0); + /* This should always hold because no items ever expire. They + * could be overwritten, though. */ + unit_assert( count <= t->node_count ); +@@ -189,7 +189,7 @@ static void consistency_test(void) + for (i = 0; i < 1000; i++) { + l = randomkey(&k, 128); + elem = (struct reply_info *) calloc(1, sizeof(struct reply_info)); +- addrtree_insert(t, k, l, 64, elem, i + 10, i); ++ addrtree_insert(t, k, l, 64, elem, i + 10, i, 0); + free(k); + unit_assert( !addrtree_inconsistent(t) ); + } +@@ -201,7 +201,7 @@ static void consistency_test(void) + for (i = 0; i < 1000; i++) { + l = randomkey(&k, 128); + elem = (struct reply_info *) calloc(1, sizeof(struct reply_info)); +- addrtree_insert(t, k, l, 64, elem, i + 10, i); ++ addrtree_insert(t, k, l, 64, elem, i + 10, i, 0); + unit_assert( t->node_count <= 27); + free(k); + unit_assert( !addrtree_inconsistent(t) ); +diff --git a/unbound-1.16.2/testdata/subnet_noecs_mult.rpl b/unbound-1.16.2/testdata/subnet_noecs_mult.rpl +new file mode 100644 +index 0000000..3e2acef +--- /dev/null ++++ b/unbound-1.16.2/testdata/subnet_noecs_mult.rpl +@@ -0,0 +1,334 @@ ++# config ++server: ++ send-client-subnet: 1.2.3.4 ++ max-client-subnet-ipv4: 17 ++ module-config: "subnetcache iterator" ++ qname-minimisation: no ++ minimal-responses: yes ++ target-fetch-policy: "0 0 0 0 0" ++ ++stub-zone: ++ name: "." ++ stub-addr: 193.0.14.129 # K.ROOT-SERVERS.NET. ++CONFIG_END ++ ++SCENARIO_BEGIN Test subnet with no edns subnet from server multiple times ++; Multiple queries are sent to a server that does not reply with the ++; edns-subnet option. ++ ++; K.ROOT-SERVERS.NET. ++RANGE_BEGIN 0 100 ++ ADDRESS 193.0.14.129 ++ENTRY_BEGIN ++MATCH opcode qtype qname ++ADJUST copy_id ++REPLY QR NOERROR ++SECTION QUESTION ++. IN NS ++SECTION ANSWER ++. IN NS K.ROOT-SERVERS.NET. ++SECTION ADDITIONAL ++K.ROOT-SERVERS.NET. IN A 193.0.14.129 ++ENTRY_END ++ ++ENTRY_BEGIN ++MATCH opcode subdomain ++ADJUST copy_id copy_query ++REPLY QR NOERROR ++SECTION QUESTION ++com. IN NS ++SECTION AUTHORITY ++com. IN NS a.gtld-servers.net. ++SECTION ADDITIONAL ++a.gtld-servers.net. IN A 192.5.6.30 ++ENTRY_END ++RANGE_END ++ ++; a.gtld-servers.net. ++RANGE_BEGIN 0 100 ++ ADDRESS 192.5.6.30 ++ ++ENTRY_BEGIN ++MATCH opcode subdomain ++ADJUST copy_id copy_query ++REPLY QR NOERROR ++SECTION QUESTION ++example.com. IN NS ++SECTION AUTHORITY ++example.com. IN NS ns.example.com. ++SECTION ADDITIONAL ++ns.example.com. IN A 1.2.3.4 ++ENTRY_END ++RANGE_END ++ ++; ns.example.com. ++RANGE_BEGIN 50 52 ++ ADDRESS 1.2.3.4 ++ ++ENTRY_BEGIN ++MATCH opcode qtype qname ++ADJUST copy_id ++REPLY QR AA NOERROR ++SECTION QUESTION ++example.com. IN NS ++SECTION ANSWER ++example.com. IN NS ns.example.com. ++ENTRY_END ++ ++ENTRY_BEGIN ++MATCH opcode qtype qname ++ADJUST copy_id ++REPLY QR AA NOERROR ++SECTION QUESTION ++ns.example.com. IN A ++SECTION ANSWER ++ns.example.com. IN A 1.2.3.4 ++ENTRY_END ++ ++ENTRY_BEGIN ++MATCH opcode qtype qname ++ADJUST copy_id ++REPLY QR AA NOERROR ++SECTION QUESTION ++ns.example.com. IN AAAA ++SECTION ANSWER ++SECTION AUTHORITY ++example.com. IN SOA ns.example.com. host.example.com. 4 86400 3600 86400 3600 ++ENTRY_END ++ ++ENTRY_BEGIN ++MATCH opcode qtype qname ednsdata ++ADJUST copy_id ++REPLY QR AA NOERROR ++SECTION QUESTION ++www.example.com. IN A ++SECTION ANSWER ++SECTION ADDITIONAL ++ ; Match this subnet option ++ HEX_EDNSDATA_BEGIN ++ ; client is 127.0.0.1 ++ 00 08 ; OPC ++ 00 07 ; option length ++ 00 01 ; Family ++ 11 00 ; source mask, scopemask ++ 7f 00 00 ; address ++ HEX_EDNSDATA_END ++ ; This is the response, without the subnet option ++ HEX_ANSWER_BEGIN; ++ 00 00 84 00 00 01 00 01 ; ID 0 QR AA NOERROR ++ 00 00 00 01 03 77 77 77 ; www.example.com A (DO) ++ 07 65 78 61 6d 70 6c 65 ++ 03 63 6f 6d 00 00 01 00 ++ 01 ++ C0 0C 00 01 00 01 00 00 0E 10 ; www.example.com. A IN 3600 ++ 00 04 0A 14 1E 2C ; rdata 10.20.30.44 ++ 00 00 29 10 00 00 00 ++ 80 00 00 00 ++ HEX_ANSWER_END ++ENTRY_END ++ ++ENTRY_BEGIN ++MATCH opcode qtype qname ednsdata ++ADJUST copy_id ++REPLY QR AA NOERROR ++SECTION QUESTION ++www.example.com. IN A ++SECTION ANSWER ++SECTION ADDITIONAL ++ ; Match this subnet option ++ HEX_EDNSDATA_BEGIN ++ ; client is 127.2.0.1 ++ 00 08 ; OPC ++ 00 07 ; option length ++ 00 01 ; Family ++ 11 00 ; source mask, scopemask ++ 7f 02 00 ; address ++ HEX_EDNSDATA_END ++ ; This is the response, without the subnet option ++ HEX_ANSWER_BEGIN; ++ 00 00 84 00 00 01 00 01 ; ID 0 QR AA NOERROR ++ 00 00 00 01 03 77 77 77 ; www.example.com A (DO) ++ 07 65 78 61 6d 70 6c 65 ++ 03 63 6f 6d 00 00 01 00 ++ 01 ++ C0 0C 00 01 00 01 00 00 0E 10 ; www.example.com. A IN 3600 ++ 00 04 0A 14 1E 2C ; rdata 10.20.30.44 ++ 00 00 29 10 00 00 00 ++ 80 00 00 00 ++ HEX_ANSWER_END ++ENTRY_END ++ ++ENTRY_BEGIN ++MATCH opcode qtype qname ednsdata ++ADJUST copy_id ++REPLY QR AA NOERROR ++SECTION QUESTION ++www.example.com. IN A ++SECTION ANSWER ++SECTION ADDITIONAL ++ ; Match this subnet option ++ HEX_EDNSDATA_BEGIN ++ ; client is 127.3.0.1 ++ 00 08 ; OPC ++ 00 07 ; option length ++ 00 01 ; Family ++ 11 00 ; source mask, scopemask ++ 7f 03 00 ; address ++ HEX_EDNSDATA_END ++ ; This is the response, without the subnet option ++ HEX_ANSWER_BEGIN; ++ 00 00 84 00 00 01 00 01 ; ID 0 QR AA NOERROR ++ 00 00 00 01 03 77 77 77 ; www.example.com A (DO) ++ 07 65 78 61 6d 70 6c 65 ++ 03 63 6f 6d 00 00 01 00 ++ 01 ++ C0 0C 00 01 00 01 00 00 0E 10 ; www.example.com. A IN 3600 ++ 00 04 0A 14 1E 2C ; rdata 10.20.30.44 ++ 00 00 29 10 00 00 00 ++ 80 00 00 00 ++ HEX_ANSWER_END ++ENTRY_END ++ ++; The answer for a query without subnet ++;ENTRY_BEGIN ++;MATCH opcode qtype qname ++;ADJUST copy_id ++;REPLY QR AA NOERROR ++;SECTION QUESTION ++;www.example.com. IN A ++;SECTION ANSWER ++;www.example.com. IN A 10.20.30.40 ++;ENTRY_END ++RANGE_END ++ ++; ns.example.com. ++RANGE_BEGIN 53 57 ++ ADDRESS 1.2.3.4 ++; The answer for a query without subnet ++ENTRY_BEGIN ++MATCH opcode qtype qname ++ADJUST copy_id ++REPLY QR AA NOERROR ++SECTION QUESTION ++www.example.com. IN A ++SECTION ANSWER ++www.example.com. IN A 10.20.30.40 ++ENTRY_END ++RANGE_END ++ ++STEP 10 QUERY ++ENTRY_BEGIN ++ HEX_ANSWER_BEGIN; ++ 00 00 01 00 00 01 00 00 ; ID 0 ++ 00 00 00 01 03 77 77 77 ; www.example.com A? (DO) ++ 07 65 78 61 6d 70 6c 65 ++ 03 63 6f 6d 00 00 01 00 ++ 01 00 00 29 10 00 00 00 ++ 80 00 00 0b ++ ++ 00 08 00 07 ; OPC, optlen ++ 00 01 11 00 ; ip4, scope 17, source 0 ++ 7f 00 00 ; 127.0.0.0/17 ++ HEX_ANSWER_END ++ENTRY_END ++ ++STEP 20 QUERY ++ENTRY_BEGIN ++ HEX_ANSWER_BEGIN; ++ 00 00 01 00 00 01 00 00 ; ID 0 ++ 00 00 00 01 03 77 77 77 ; www.example.com A? (DO) ++ 07 65 78 61 6d 70 6c 65 ++ 03 63 6f 6d 00 00 01 00 ++ 01 00 00 29 10 00 00 00 ++ 80 00 00 0b ++ ++ 00 08 00 07 ; OPC, optlen ++ 00 01 11 00 ; ip4, scope 17, source 0 ++ 7f 02 00 ; 127.2.0.0/17 ++ HEX_ANSWER_END ++ENTRY_END ++ ++STEP 30 QUERY ++ENTRY_BEGIN ++ HEX_ANSWER_BEGIN; ++ 00 00 01 00 00 01 00 00 ; ID 0 ++ 00 00 00 01 03 77 77 77 ; www.example.com A? (DO) ++ 07 65 78 61 6d 70 6c 65 ++ 03 63 6f 6d 00 00 01 00 ++ 01 00 00 29 10 00 00 00 ++ 80 00 00 0b ++ ++ 00 08 00 07 ; OPC, optlen ++ 00 01 11 00 ; ip4, scope 17, source 0 ++ 7f 03 00 ; 127.3.0.0/17 ++ HEX_ANSWER_END ++ENTRY_END ++ ++; recursion happens here. ++; The upstream server RANGE starts responding at STEP 50. ++STEP 50 TRAFFIC ++ ++; The upstream server now responds for the nonsubnet response. ++STEP 55 TRAFFIC ++ ++STEP 60 CHECK_ANSWER ++ENTRY_BEGIN ++MATCH all ednsdata ++REPLY QR RD RA DO NOERROR ++SECTION QUESTION ++www.example.com. IN A ++SECTION ANSWER ++www.example.com. IN A 10.20.30.40 ++;www.example.com. IN A 10.20.30.44 ++SECTION ADDITIONAL ++; HEX_EDNSDATA_BEGIN ++; ; client is 127.3.0.1 ++; 00 08 ; OPC ++; 00 07 ; option length ++; 00 01 ; Family ++; 11 00 ; source mask, scopemask ++; 7f 03 00 ; address ++; HEX_EDNSDATA_END ++ENTRY_END ++ ++STEP 70 CHECK_ANSWER ++ENTRY_BEGIN ++MATCH all ednsdata ++REPLY QR RD RA DO NOERROR ++SECTION QUESTION ++www.example.com. IN A ++SECTION ANSWER ++www.example.com. IN A 10.20.30.40 ++;www.example.com. IN A 10.20.30.44 ++SECTION ADDITIONAL ++; HEX_EDNSDATA_BEGIN ++; ; client is 127.2.0.1 ++; 00 08 ; OPC ++; 00 07 ; option length ++; 00 01 ; Family ++; 11 00 ; source mask, scopemask ++; 7f 02 00 ; address ++; HEX_EDNSDATA_END ++ENTRY_END ++ ++STEP 80 CHECK_ANSWER ++ENTRY_BEGIN ++MATCH all ednsdata ++REPLY QR RD RA DO NOERROR ++SECTION QUESTION ++www.example.com. IN A ++SECTION ANSWER ++www.example.com. IN A 10.20.30.40 ++;www.example.com. IN A 10.20.30.44 ++SECTION ADDITIONAL ++; HEX_EDNSDATA_BEGIN ++; ; client is 127.0.0.1 ++; 00 08 ; OPC ++; 00 07 ; option length ++; 00 01 ; Family ++; 11 00 ; source mask, scopemask ++; 7f 00 00 ; address ++; HEX_EDNSDATA_END ++ENTRY_END ++ ++SCENARIO_END +diff --git a/unbound-1.16.2/testdata/subnet_noecs_refused.rpl b/unbound-1.16.2/testdata/subnet_noecs_refused.rpl +new file mode 100644 +index 0000000..39fbe85 +--- /dev/null ++++ b/unbound-1.16.2/testdata/subnet_noecs_refused.rpl +@@ -0,0 +1,159 @@ ++# config ++server: ++ send-client-subnet: 1.2.3.4 ++ max-client-subnet-ipv4: 17 ++ module-config: "subnetcache iterator" ++ qname-minimisation: no ++ minimal-responses: yes ++ target-fetch-policy: "0 0 0 0 0" ++ ++stub-zone: ++ name: "." ++ stub-addr: 193.0.14.129 # K.ROOT-SERVERS.NET. ++CONFIG_END ++ ++SCENARIO_BEGIN Test subnet with no edns subnet support but it is refused ++; The query is sent to a server that does not reply with the edns-subnet ++; option. The upstream server sends rcode refused. That results in a ++; NULL return_msg. ++ ++; K.ROOT-SERVERS.NET. ++RANGE_BEGIN 0 100 ++ ADDRESS 193.0.14.129 ++ENTRY_BEGIN ++MATCH opcode qtype qname ++ADJUST copy_id ++REPLY QR NOERROR ++SECTION QUESTION ++. IN NS ++SECTION ANSWER ++. IN NS K.ROOT-SERVERS.NET. ++SECTION ADDITIONAL ++K.ROOT-SERVERS.NET. IN A 193.0.14.129 ++ENTRY_END ++ ++ENTRY_BEGIN ++MATCH opcode subdomain ++ADJUST copy_id copy_query ++REPLY QR NOERROR ++SECTION QUESTION ++com. IN NS ++SECTION AUTHORITY ++com. IN NS a.gtld-servers.net. ++SECTION ADDITIONAL ++a.gtld-servers.net. IN A 192.5.6.30 ++ENTRY_END ++RANGE_END ++ ++; a.gtld-servers.net. ++RANGE_BEGIN 0 100 ++ ADDRESS 192.5.6.30 ++ ++ENTRY_BEGIN ++MATCH opcode subdomain ++ADJUST copy_id copy_query ++REPLY QR NOERROR ++SECTION QUESTION ++example.com. IN NS ++SECTION AUTHORITY ++example.com. IN NS ns.example.com. ++SECTION ADDITIONAL ++ns.example.com. IN A 1.2.3.4 ++ENTRY_END ++RANGE_END ++ ++; ns.example.com. ++RANGE_BEGIN 0 100 ++ ADDRESS 1.2.3.4 ++ ++ENTRY_BEGIN ++MATCH opcode qtype qname ++ADJUST copy_id ++REPLY QR AA NOERROR ++SECTION QUESTION ++example.com. IN NS ++SECTION ANSWER ++example.com. IN NS ns.example.com. ++ENTRY_END ++ ++ENTRY_BEGIN ++MATCH opcode qtype qname ++ADJUST copy_id ++REPLY QR AA NOERROR ++SECTION QUESTION ++ns.example.com. IN A ++SECTION ANSWER ++ns.example.com. IN A 1.2.3.4 ++ENTRY_END ++ ++ENTRY_BEGIN ++MATCH opcode qtype qname ++ADJUST copy_id ++REPLY QR AA NOERROR ++SECTION QUESTION ++ns.example.com. IN AAAA ++SECTION ANSWER ++SECTION AUTHORITY ++example.com. IN SOA ns.example.com. host.example.com. 4 86400 3600 86400 3600 ++ENTRY_END ++ ++; This matches the no EDNS subnet info queries that are made for the ++; fallback without subnet. The answer is refused. ++ENTRY_BEGIN ++MATCH opcode qtype qname ednsdata ++ADJUST copy_id ++REPLY QR AA REFUSED ++SECTION QUESTION ++www.example.com. IN A ++SECTION ANSWER ++;www.example.com. IN A 10.20.30.40 ++ENTRY_END ++ ++; This matches the initial query with edns subnet in the query, ++; the answer has no edns subnet in the reply. ++ENTRY_BEGIN ++MATCH opcode qtype qname ++ADJUST copy_id ++REPLY QR AA NOERROR ++SECTION QUESTION ++www.example.com. IN A ++SECTION ANSWER ++www.example.com. IN A 10.20.30.40 ++ENTRY_END ++RANGE_END ++ ++STEP 1 QUERY ++;ENTRY_BEGIN ++;REPLY RD DO ++;SECTION QUESTION ++;www.example.com. IN A ++; but send this query with subnet scope zero in the query, because that ++; makes the reply possibly get stored in the cache. ++; ++; query with subnet 0.0.0.0/0. ++ENTRY_BEGIN ++HEX_ANSWER_BEGIN ++ 00 00 01 00 00 01 00 00 ;ID 0 ++ 00 00 00 01 03 77 77 77 ; www.example.com A? (DO) ++ 07 65 78 61 6d 70 6c 65 ++ 03 63 6f 6d 00 00 01 00 ++ 01 00 00 29 10 00 00 00 ++ 80 00 00 08 ++ ++ 00 08 00 04 ; OPC, optlen ++ 00 01 00 00 ; ip4, scope 0, source 0 ++ ;0.0.0.0/0 ++HEX_ANSWER_END ++ENTRY_END ++ ++; recursion happens here. ++STEP 10 CHECK_ANSWER ++ENTRY_BEGIN ++MATCH all ednsdata ++REPLY QR RD RA DO SERVFAIL ++SECTION QUESTION ++www.example.com. IN A ++SECTION ANSWER ++;www.example.com. IN A 10.20.30.40 ++ENTRY_END ++SCENARIO_END +diff --git a/unbound-1.16.2/testdata/subnet_noecs_support.rpl b/unbound-1.16.2/testdata/subnet_noecs_support.rpl +new file mode 100644 +index 0000000..0c9826c +--- /dev/null ++++ b/unbound-1.16.2/testdata/subnet_noecs_support.rpl +@@ -0,0 +1,127 @@ ++# config ++server: ++ send-client-subnet: 1.2.3.4 ++ max-client-subnet-ipv4: 17 ++ module-config: "subnetcache iterator" ++ qname-minimisation: no ++ minimal-responses: yes ++ target-fetch-policy: "0 0 0 0 0" ++ ++stub-zone: ++ name: "." ++ stub-addr: 193.0.14.129 # K.ROOT-SERVERS.NET. ++CONFIG_END ++ ++SCENARIO_BEGIN Test subnet with no edns subnet support from the server ++; The query is sent to a server that does not reply with the edns-subnet ++; option. ++ ++; K.ROOT-SERVERS.NET. ++RANGE_BEGIN 0 100 ++ ADDRESS 193.0.14.129 ++ENTRY_BEGIN ++MATCH opcode qtype qname ++ADJUST copy_id ++REPLY QR NOERROR ++SECTION QUESTION ++. IN NS ++SECTION ANSWER ++. IN NS K.ROOT-SERVERS.NET. ++SECTION ADDITIONAL ++K.ROOT-SERVERS.NET. IN A 193.0.14.129 ++ENTRY_END ++ ++ENTRY_BEGIN ++MATCH opcode subdomain ++ADJUST copy_id copy_query ++REPLY QR NOERROR ++SECTION QUESTION ++com. IN NS ++SECTION AUTHORITY ++com. IN NS a.gtld-servers.net. ++SECTION ADDITIONAL ++a.gtld-servers.net. IN A 192.5.6.30 ++ENTRY_END ++RANGE_END ++ ++; a.gtld-servers.net. ++RANGE_BEGIN 0 100 ++ ADDRESS 192.5.6.30 ++ ++ENTRY_BEGIN ++MATCH opcode subdomain ++ADJUST copy_id copy_query ++REPLY QR NOERROR ++SECTION QUESTION ++example.com. IN NS ++SECTION AUTHORITY ++example.com. IN NS ns.example.com. ++SECTION ADDITIONAL ++ns.example.com. IN A 1.2.3.4 ++ENTRY_END ++RANGE_END ++ ++; ns.example.com. ++RANGE_BEGIN 0 100 ++ ADDRESS 1.2.3.4 ++ ++ENTRY_BEGIN ++MATCH opcode qtype qname ++ADJUST copy_id ++REPLY QR AA NOERROR ++SECTION QUESTION ++example.com. IN NS ++SECTION ANSWER ++example.com. IN NS ns.example.com. ++ENTRY_END ++ ++ENTRY_BEGIN ++MATCH opcode qtype qname ++ADJUST copy_id ++REPLY QR AA NOERROR ++SECTION QUESTION ++ns.example.com. IN A ++SECTION ANSWER ++ns.example.com. IN A 1.2.3.4 ++ENTRY_END ++ ++ENTRY_BEGIN ++MATCH opcode qtype qname ++ADJUST copy_id ++REPLY QR AA NOERROR ++SECTION QUESTION ++ns.example.com. IN AAAA ++SECTION ANSWER ++SECTION AUTHORITY ++example.com. IN SOA ns.example.com. host.example.com. 4 86400 3600 86400 3600 ++ENTRY_END ++ ++ENTRY_BEGIN ++MATCH opcode qtype qname ++ADJUST copy_id ++REPLY QR AA NOERROR ++SECTION QUESTION ++www.example.com. IN A ++SECTION ANSWER ++www.example.com. IN A 10.20.30.40 ++ENTRY_END ++RANGE_END ++ ++STEP 1 QUERY ++ENTRY_BEGIN ++REPLY RD DO ++SECTION QUESTION ++www.example.com. IN A ++ENTRY_END ++ ++; recursion happens here. ++STEP 10 CHECK_ANSWER ++ENTRY_BEGIN ++MATCH all ednsdata ++REPLY QR RD RA DO NOERROR ++SECTION QUESTION ++www.example.com. IN A ++SECTION ANSWER ++www.example.com. IN A 10.20.30.40 ++ENTRY_END ++SCENARIO_END +diff --git a/unbound-1.16.2/testdata/subnet_prezero.rpl b/unbound-1.16.2/testdata/subnet_prezero.rpl +new file mode 100644 +index 0000000..22cdfff +--- /dev/null ++++ b/unbound-1.16.2/testdata/subnet_prezero.rpl +@@ -0,0 +1,155 @@ ++; subnet unit test ++server: ++ trust-anchor-signaling: no ++ send-client-subnet: 1.2.3.4 ++ send-client-subnet: 1.2.3.5 ++ target-fetch-policy: "0 0 0 0 0" ++ module-config: "subnetcache validator iterator" ++ qname-minimisation: no ++ minimal-responses: no ++ ++stub-zone: ++ name: "example.com" ++ stub-addr: 1.2.3.4 ++CONFIG_END ++ ++SCENARIO_BEGIN Test subnetcache source prefix zero from client. ++; In RFC7871 section-7.1.2 (para. 2). ++; It says that the recursor must send no EDNS subnet or its own address ++; in the EDNS subnet to the upstream server. And use that answer for the ++; source prefix length zero query. That type of query is for privacy. ++; The authority server is then going to use the resolver's IP, if any, to ++; tailor the answer to the query source address. ++ ++; ns.example.com ++RANGE_BEGIN 0 100 ++ ADDRESS 1.2.3.4 ++ ++; reply with 0.0.0.0/0 in reply ++; For the test the answers for 0.0.0.0/0 queries are SERVFAIL, the normal ++; answers are NOERROR. ++ENTRY_BEGIN ++MATCH opcode qtype qname ednsdata ++ADJUST copy_id ++REPLY QR AA DO SERVFAIL ++SECTION QUESTION ++www.example.com. IN A ++SECTION ANSWER ++www.example.com. IN CNAME star.c10r.example.com. ++SECTION ADDITIONAL ++HEX_EDNSDATA_BEGIN ++ 00 08 00 04 ; OPCODE=subnet, optlen ++ 00 01 00 00 ; ip4, scope 0, source 0 ++ ; 0.0.0.0/0 ++HEX_EDNSDATA_END ++ENTRY_END ++ ++; reply without subnet ++ENTRY_BEGIN ++MATCH opcode qtype qname ++ADJUST copy_id ++REPLY QR AA DO NOERROR ++SECTION QUESTION ++www.example.com. IN A ++SECTION ANSWER ++www.example.com. IN CNAME star.c10r.example.com. ++ENTRY_END ++ ++; delegation answer for c10r.example.com, with subnet /0 ++ENTRY_BEGIN ++MATCH opcode subdomain ednsdata ++ADJUST copy_id copy_query ++REPLY QR DO SERVFAIL ++SECTION QUESTION ++c10r.example.com. IN NS ++SECTION AUTHORITY ++c10r.example.com. IN NS ns.c10r.example.com. ++SECTION ADDITIONAL ++ns.c10r.example.com. IN A 1.2.3.5 ++HEX_EDNSDATA_BEGIN ++ 00 08 00 04 ; OPCODE=subnet, optlen ++ 00 01 00 00 ; ip4, scope 0, source 0 ++ ; 0.0.0.0/0 ++HEX_EDNSDATA_END ++ENTRY_END ++ ++; delegation answer for c10r.example.com, without subnet ++ENTRY_BEGIN ++MATCH opcode subdomain ++ADJUST copy_id copy_query ++REPLY QR DO NOERROR ++SECTION QUESTION ++c10r.example.com. IN NS ++SECTION AUTHORITY ++c10r.example.com. IN NS ns.c10r.example.com. ++SECTION ADDITIONAL ++ns.c10r.example.com. IN A 1.2.3.5 ++ENTRY_END ++RANGE_END ++ ++; ns.c10r.example.com ++RANGE_BEGIN 0 100 ++ ADDRESS 1.2.3.5 ++ ++; reply with 0.0.0.0/0 in reply ++ENTRY_BEGIN ++MATCH opcode qtype qname ednsdata ++ADJUST copy_id ++REPLY QR AA DO SERVFAIL ++SECTION QUESTION ++star.c10r.example.com. IN A ++SECTION ANSWER ++star.c10r.example.com. IN A 1.2.3.6 ++SECTION ADDITIONAL ++HEX_EDNSDATA_BEGIN ++ 00 08 00 04 ; OPCODE=subnet, optlen ++ 00 01 00 00 ; ip4, scope 0, source 0 ++ ; 0.0.0.0/0 ++HEX_EDNSDATA_END ++ENTRY_END ++ ++; reply without subnet ++ENTRY_BEGIN ++MATCH opcode qtype qname ++ADJUST copy_id ++REPLY QR AA DO NOERROR ++SECTION QUESTION ++star.c10r.example.com. IN A ++SECTION ANSWER ++star.c10r.example.com. IN A 1.2.3.6 ++ENTRY_END ++RANGE_END ++ ++; ask for www.example.com ++; server answers with CNAME to a delegation, that then ++; returns a /24 answer. ++STEP 1 QUERY ++ENTRY_BEGIN ++REPLY RD DO ++SECTION QUESTION ++www.example.com. IN A ++SECTION ADDITIONAL ++HEX_EDNSDATA_BEGIN ++ 00 08 00 04 ; OPCODE=subnet, optlen ++ 00 01 00 00 ; ip4, scope 0, source 0 ++ ; 0.0.0.0/0 ++HEX_EDNSDATA_END ++ENTRY_END ++ ++STEP 10 CHECK_ANSWER ++ENTRY_BEGIN ++MATCH all ednsdata ++REPLY QR RD RA DO NOERROR ++SECTION QUESTION ++www.example.com. IN A ++SECTION ANSWER ++www.example.com. IN CNAME star.c10r.example.com. ++star.c10r.example.com. IN A 1.2.3.6 ++SECTION ADDITIONAL ++HEX_EDNSDATA_BEGIN ++ 00 08 00 04 ; OPCODE=subnet, optlen ++ 00 01 00 00 ; ip4, scope 0, source 0 ++ ; 0.0.0.0/0 ++HEX_EDNSDATA_END ++ENTRY_END ++SCENARIO_END +diff --git a/unbound-1.16.2/testdata/subnet_scopezero.rpl b/unbound-1.16.2/testdata/subnet_scopezero.rpl +new file mode 100644 +index 0000000..e006514 +--- /dev/null ++++ b/unbound-1.16.2/testdata/subnet_scopezero.rpl +@@ -0,0 +1,439 @@ ++; scope of 0, if the query also had scope of 0, do not answer this ++; to everyone, but only for scope 0 queries. Otherwise can answer cached. ++ ++server: ++ target-fetch-policy: "0 0 0 0 0" ++ send-client-subnet: 1.2.3.4 ++ module-config: "subnetcache validator iterator" ++ verbosity: 4 ++ qname-minimisation: no ++ ++stub-zone: ++ name: "." ++ stub-addr: 193.0.14.129 ++ ++stub-zone: ++ name: "example.com" ++ stub-addr: 1.2.3.4 ++CONFIG_END ++ ++SCENARIO_BEGIN Test subnet cache with scope zero queries and responses. ++ ++; the upstream server. ++RANGE_BEGIN 0 100 ++ ADDRESS 193.0.14.129 ++ ++ENTRY_BEGIN ++MATCH opcode qtype qname ednsdata ++ADJUST copy_id ++REPLY QR NOERROR ++SECTION QUESTION ++. IN NS ++SECTION ANSWER ++. IN NS K.ROOT-SERVERS.NET. ++SECTION ADDITIONAL ++HEX_EDNSDATA_BEGIN ++ ;; we expect to receive empty ++HEX_EDNSDATA_END ++K.ROOT-SERVERS.NET. IN A 193.0.14.129 ++ENTRY_END ++RANGE_END ++ ++RANGE_BEGIN 0 11 ++ ADDRESS 1.2.3.4 ++ENTRY_BEGIN ++MATCH opcode qtype qname ++ADJUST copy_id ++;copy_ednsdata_assume_clientsubnet ++REPLY QR NOERROR ++SECTION QUESTION ++www.example.com. IN A ++SECTION ANSWER ++www.example.com. IN A 10.20.30.40 ++SECTION AUTHORITY ++SECTION ADDITIONAL ++HEX_EDNSDATA_BEGIN ++ ; client is 127.0.0.1 ++ 00 08 ; OPC ++ 00 07 ; option length ++ 00 01 ; Family ++ 18 11 ; source mask, scopemask ++ 7f 00 00 ; address ++HEX_EDNSDATA_END ++ENTRY_END ++RANGE_END ++ ++RANGE_BEGIN 20 31 ++ ADDRESS 1.2.3.4 ++ENTRY_BEGIN ++MATCH opcode qtype qname ++ADJUST copy_id ++;copy_ednsdata_assume_clientsubnet ++REPLY QR NOERROR ++SECTION QUESTION ++www.example.com. IN A ++SECTION ANSWER ++www.example.com. IN A 10.20.30.41 ++SECTION AUTHORITY ++SECTION ADDITIONAL ++HEX_EDNSDATA_BEGIN ++ ; client is 127.0.0.1 ++ 00 08 ; OPC ++ 00 07 ; option length ++ 00 01 ; Family ++ 18 11 ; source mask, scopemask ++ 7f 01 00 ; address ++HEX_EDNSDATA_END ++ENTRY_END ++RANGE_END ++ ++RANGE_BEGIN 40 51 ++ ADDRESS 1.2.3.4 ++ENTRY_BEGIN ++MATCH opcode qtype qname ++ADJUST copy_id ++;copy_ednsdata_assume_clientsubnet ++REPLY QR NOERROR ++SECTION QUESTION ++www.example.com. IN A ++SECTION ANSWER ++www.example.com. IN A 10.20.30.42 ++SECTION AUTHORITY ++SECTION ADDITIONAL ++HEX_EDNSDATA_BEGIN ++ 00 08 ; OPC ++ 00 04 ; option length ++ 00 01 ; Family ++ 00 00 ; source mask, scopemask ++ ; address 0.0.0.0/0 scope 0 ++HEX_EDNSDATA_END ++ENTRY_END ++RANGE_END ++ ++RANGE_BEGIN 120 131 ++ ADDRESS 1.2.3.4 ++ENTRY_BEGIN ++MATCH opcode qtype qname ++ADJUST copy_id ++;copy_ednsdata_assume_clientsubnet ++REPLY QR NOERROR ++SECTION QUESTION ++www.example.com. IN A ++SECTION ANSWER ++www.example.com. IN A 10.20.30.43 ++SECTION AUTHORITY ++SECTION ADDITIONAL ++HEX_EDNSDATA_BEGIN ++ 00 08 ; OPC ++ 00 07 ; option length ++ 00 01 ; Family ++ 18 00 ; source mask, scopemask ++ 7f 02 00 ; address 127.2.0.0/24 scope 0 ++HEX_EDNSDATA_END ++ENTRY_END ++RANGE_END ++ ++; query for 127.0.0.0/24 ++STEP 1 QUERY ++ENTRY_BEGIN ++HEX_ANSWER_BEGIN ++ 00 00 01 00 00 01 00 00 ;ID 0 ++ 00 00 00 01 03 77 77 77 ; www.example.com A? (DO) ++ 07 65 78 61 6d 70 6c 65 ++ 03 63 6f 6d 00 00 01 00 ++ 01 00 00 29 10 00 00 00 ++ 80 00 00 0b ++ ++ 00 08 00 07 ; OPC, optlen ++ 00 01 18 00 ; ip4, scope 24, source 0 ++ 7f 00 00 ;127.0.0.0/24 ++HEX_ANSWER_END ++ENTRY_END ++ ++; answer is 10.20.30.40 for 127.0.0.0/24 scope 17 ++STEP 10 CHECK_ANSWER ++ENTRY_BEGIN ++MATCH all ednsdata ++REPLY QR RD RA NOERROR ++SECTION QUESTION ++www.example.com. IN A ++SECTION ANSWER ++www.example.com. IN A 10.20.30.40 ++SECTION AUTHORITY ++SECTION ADDITIONAL ++HEX_EDNSDATA_BEGIN ++ ; client is 127.0.0.1 ++ 00 08 ; OPC ++ 00 07 ; option length ++ 00 01 ; Family ++ 18 11 ; source mask, scopemask ++ 7f 00 00 ; address ++HEX_EDNSDATA_END ++ENTRY_END ++ ++; query for 127.1.0.0/24 ++STEP 20 QUERY ++ENTRY_BEGIN ++HEX_ANSWER_BEGIN ++ 00 00 01 00 00 01 00 00 ;ID 0 ++ 00 00 00 01 03 77 77 77 ; www.example.com A? (DO) ++ 07 65 78 61 6d 70 6c 65 ++ 03 63 6f 6d 00 00 01 00 ++ 01 00 00 29 10 00 00 00 ++ 80 00 00 0b ++ ++ 00 08 00 07 ; OPC, optlen ++ 00 01 18 00 ; ip4, scope 24, source 0 ++ 7f 01 00 ;127.1.0.0/24 ++HEX_ANSWER_END ++ENTRY_END ++ ++; answer is 10.20.30.41 for 127.1.0.0/24 scope 17 ++STEP 30 CHECK_ANSWER ++ENTRY_BEGIN ++MATCH all ednsdata ++REPLY QR RD RA NOERROR ++SECTION QUESTION ++www.example.com. IN A ++SECTION ANSWER ++www.example.com. IN A 10.20.30.41 ++SECTION AUTHORITY ++SECTION ADDITIONAL ++HEX_EDNSDATA_BEGIN ++ ; client is 127.1.0.1 ++ 00 08 ; OPC ++ 00 07 ; option length ++ 00 01 ; Family ++ 18 11 ; source mask, scopemask ++ 7f 01 00 ; address ++HEX_EDNSDATA_END ++ENTRY_END ++ ++; query for 0.0.0.0/0 ++STEP 40 QUERY ++ENTRY_BEGIN ++HEX_ANSWER_BEGIN ++ 00 00 01 00 00 01 00 00 ;ID 0 ++ 00 00 00 01 03 77 77 77 ; www.example.com A? (DO) ++ 07 65 78 61 6d 70 6c 65 ++ 03 63 6f 6d 00 00 01 00 ++ 01 00 00 29 10 00 00 00 ++ 80 00 00 08 ++ ++ 00 08 00 04 ; OPC, optlen ++ 00 01 00 00 ; ip4, scope 0, source 0 ++ ;0.0.0.0/0 ++HEX_ANSWER_END ++ENTRY_END ++ ++; answer is 10.20.30.42 for 0.0.0.0/0 scope 0 ++STEP 50 CHECK_ANSWER ++ENTRY_BEGIN ++MATCH all ednsdata ++REPLY QR RD RA NOERROR ++SECTION QUESTION ++www.example.com. IN A ++SECTION ANSWER ++www.example.com. IN A 10.20.30.42 ++SECTION AUTHORITY ++SECTION ADDITIONAL ++HEX_EDNSDATA_BEGIN ++ 00 08 ; OPC ++ 00 04 ; option length ++ 00 01 ; Family ++ 00 00 ; source mask, scopemask ++ ; address ++HEX_EDNSDATA_END ++ENTRY_END ++ ++; query for 127.0.0.0/24, again, it should be in cache. ++; and not from the scope 0 answer. ++STEP 60 QUERY ++ENTRY_BEGIN ++HEX_ANSWER_BEGIN ++ 00 00 01 00 00 01 00 00 ;ID 0 ++ 00 00 00 01 03 77 77 77 ; www.example.com A? (DO) ++ 07 65 78 61 6d 70 6c 65 ++ 03 63 6f 6d 00 00 01 00 ++ 01 00 00 29 10 00 00 00 ++ 80 00 00 0b ++ ++ 00 08 00 07 ; OPC, optlen ++ 00 01 18 00 ; ip4, scope 24, source 0 ++ 7f 00 00 ;127.0.0.0/24 ++HEX_ANSWER_END ++ENTRY_END ++ ++; answer should be 10.20.30.40 for 127.0.0.0/24 scope 17 ++STEP 70 CHECK_ANSWER ++ENTRY_BEGIN ++MATCH all ednsdata ++REPLY QR RD RA NOERROR ++SECTION QUESTION ++www.example.com. IN A ++SECTION ANSWER ++www.example.com. IN A 10.20.30.40 ++SECTION AUTHORITY ++SECTION ADDITIONAL ++HEX_EDNSDATA_BEGIN ++ ; client is 127.0.0.1 ++ 00 08 ; OPC ++ 00 07 ; option length ++ 00 01 ; Family ++ 18 11 ; source mask, scopemask ++ 7f 00 00 ; address ++HEX_EDNSDATA_END ++ENTRY_END ++ ++; query for 127.1.0.0/24, again, it should be in cache. ++STEP 80 QUERY ++ENTRY_BEGIN ++HEX_ANSWER_BEGIN ++ 00 00 01 00 00 01 00 00 ;ID 0 ++ 00 00 00 01 03 77 77 77 ; www.example.com A? (DO) ++ 07 65 78 61 6d 70 6c 65 ++ 03 63 6f 6d 00 00 01 00 ++ 01 00 00 29 10 00 00 00 ++ 80 00 00 0b ++ ++ 00 08 00 07 ; OPC, optlen ++ 00 01 18 00 ; ip4, scope 24, source 0 ++ 7f 01 00 ;127.1.0.0/24 ++HEX_ANSWER_END ++ENTRY_END ++ ++; answer should be 10.20.30.41 for 127.1.0.0/24 scope 17 ++STEP 90 CHECK_ANSWER ++ENTRY_BEGIN ++MATCH all ednsdata ++REPLY QR RD RA NOERROR ++SECTION QUESTION ++www.example.com. IN A ++SECTION ANSWER ++www.example.com. IN A 10.20.30.41 ++SECTION AUTHORITY ++SECTION ADDITIONAL ++HEX_EDNSDATA_BEGIN ++ ; client is 127.1.0.1 ++ 00 08 ; OPC ++ 00 07 ; option length ++ 00 01 ; Family ++ 18 11 ; source mask, scopemask ++ 7f 01 00 ; address ++HEX_EDNSDATA_END ++ENTRY_END ++ ++; query for 0.0.0.0/0, again. ++STEP 100 QUERY ++ENTRY_BEGIN ++HEX_ANSWER_BEGIN ++ 00 00 01 00 00 01 00 00 ;ID 0 ++ 00 00 00 01 03 77 77 77 ; www.example.com A? (DO) ++ 07 65 78 61 6d 70 6c 65 ++ 03 63 6f 6d 00 00 01 00 ++ 01 00 00 29 10 00 00 00 ++ 80 00 00 08 ++ ++ 00 08 00 04 ; OPC, optlen ++ 00 01 00 00 ; ip4, scope 0, source 0 ++ ;0.0.0.0/0 ++HEX_ANSWER_END ++ENTRY_END ++ ++; answer should be 10.20.30.42 for 0.0.0.0/0 scope 0 ++STEP 110 CHECK_ANSWER ++ENTRY_BEGIN ++MATCH all ednsdata ++REPLY QR RD RA NOERROR ++SECTION QUESTION ++www.example.com. IN A ++SECTION ANSWER ++www.example.com. IN A 10.20.30.42 ++SECTION AUTHORITY ++SECTION ADDITIONAL ++HEX_EDNSDATA_BEGIN ++ 00 08 ; OPC ++ 00 04 ; option length ++ 00 01 ; Family ++ 00 00 ; source mask, scopemask ++ ; address ++HEX_EDNSDATA_END ++ENTRY_END ++ ++; now a query for a /24 that gets an answer for a /0. ++STEP 120 QUERY ++ENTRY_BEGIN ++HEX_ANSWER_BEGIN ++ 00 00 01 00 00 01 00 00 ;ID 0 ++ 00 00 00 01 03 77 77 77 ; www.example.com A? (DO) ++ 07 65 78 61 6d 70 6c 65 ++ 03 63 6f 6d 00 00 01 00 ++ 01 00 00 29 10 00 00 00 ++ 80 00 00 0b ++ ++ 00 08 00 07 ; OPC, optlen ++ 00 01 18 00 ; ip4, scope 24, source 0 ++ 7f 02 00 ;127.2.0.0/24 ++HEX_ANSWER_END ++ENTRY_END ++ ++; answer should be 10.20.30.43 for 127.2.0.0/24 scope 0 ++STEP 130 CHECK_ANSWER ++ENTRY_BEGIN ++MATCH all ednsdata ++REPLY QR RD RA NOERROR ++SECTION QUESTION ++www.example.com. IN A ++SECTION ANSWER ++www.example.com. IN A 10.20.30.43 ++SECTION AUTHORITY ++SECTION ADDITIONAL ++HEX_EDNSDATA_BEGIN ++ ; client is 127.2.0.1 ++ 00 08 ; OPC ++ 00 07 ; option length ++ 00 01 ; Family ++ 18 00 ; source mask, scopemask ++ 7f 02 00 ; address ++HEX_EDNSDATA_END ++ENTRY_END ++ ++; the scope 0 answer is now used to answer queries from ++; query for 127.0.0.0/24 ++STEP 140 QUERY ++ENTRY_BEGIN ++HEX_ANSWER_BEGIN ++ 00 00 01 00 00 01 00 00 ;ID 0 ++ 00 00 00 01 03 77 77 77 ; www.example.com A? (DO) ++ 07 65 78 61 6d 70 6c 65 ++ 03 63 6f 6d 00 00 01 00 ++ 01 00 00 29 10 00 00 00 ++ 80 00 00 0b ++ ++ 00 08 00 07 ; OPC, optlen ++ 00 01 18 00 ; ip4, scope 24, source 0 ++ 7f 00 00 ;127.0.0.0/24 ++HEX_ANSWER_END ++ENTRY_END ++ ++STEP 150 CHECK_ANSWER ++ENTRY_BEGIN ++MATCH all ednsdata ++REPLY QR RD RA NOERROR ++SECTION QUESTION ++www.example.com. IN A ++SECTION ANSWER ++www.example.com. IN A 10.20.30.43 ++SECTION AUTHORITY ++SECTION ADDITIONAL ++HEX_EDNSDATA_BEGIN ++ ; client is 127.0.0.1 ++ 00 08 ; OPC ++ 00 07 ; option length ++ 00 01 ; Family ++ 18 00 ; source mask, scopemask ++ 7f 00 00 ; address ++HEX_EDNSDATA_END ++ENTRY_END ++ ++SCENARIO_END +diff --git a/unbound-1.16.2/testdata/subnet_scopezero_noedns.rpl b/unbound-1.16.2/testdata/subnet_scopezero_noedns.rpl +new file mode 100644 +index 0000000..25df0dd +--- /dev/null ++++ b/unbound-1.16.2/testdata/subnet_scopezero_noedns.rpl +@@ -0,0 +1,441 @@ ++; scope of 0, if the query also had scope of 0, do not answer this ++; to everyone, but only for scope 0 queries. Otherwise can answer cached. ++ ++server: ++ target-fetch-policy: "0 0 0 0 0" ++ send-client-subnet: 1.2.3.4 ++ module-config: "subnetcache validator iterator" ++ verbosity: 4 ++ qname-minimisation: no ++ ++stub-zone: ++ name: "." ++ stub-addr: 193.0.14.129 ++ ++stub-zone: ++ name: "example.com" ++ stub-addr: 1.2.3.4 ++CONFIG_END ++ ++SCENARIO_BEGIN Test subnet cache with scope zero response without EDNS. ++ ++; the upstream server. ++RANGE_BEGIN 0 100 ++ ADDRESS 193.0.14.129 ++ ++ENTRY_BEGIN ++MATCH opcode qtype qname ednsdata ++ADJUST copy_id ++REPLY QR NOERROR ++SECTION QUESTION ++. IN NS ++SECTION ANSWER ++. IN NS K.ROOT-SERVERS.NET. ++SECTION ADDITIONAL ++HEX_EDNSDATA_BEGIN ++ ;; we expect to receive empty ++HEX_EDNSDATA_END ++K.ROOT-SERVERS.NET. IN A 193.0.14.129 ++ENTRY_END ++RANGE_END ++ ++RANGE_BEGIN 0 11 ++ ADDRESS 1.2.3.4 ++ENTRY_BEGIN ++MATCH opcode qtype qname ++ADJUST copy_id ++;copy_ednsdata_assume_clientsubnet ++REPLY QR NOERROR ++SECTION QUESTION ++www.example.com. IN A ++SECTION ANSWER ++www.example.com. IN A 10.20.30.40 ++SECTION AUTHORITY ++SECTION ADDITIONAL ++HEX_EDNSDATA_BEGIN ++ ; client is 127.0.0.1 ++ 00 08 ; OPC ++ 00 07 ; option length ++ 00 01 ; Family ++ 18 11 ; source mask, scopemask ++ 7f 00 00 ; address ++HEX_EDNSDATA_END ++ENTRY_END ++RANGE_END ++ ++RANGE_BEGIN 20 31 ++ ADDRESS 1.2.3.4 ++ENTRY_BEGIN ++MATCH opcode qtype qname ++ADJUST copy_id ++;copy_ednsdata_assume_clientsubnet ++REPLY QR NOERROR ++SECTION QUESTION ++www.example.com. IN A ++SECTION ANSWER ++www.example.com. IN A 10.20.30.41 ++SECTION AUTHORITY ++SECTION ADDITIONAL ++HEX_EDNSDATA_BEGIN ++ ; client is 127.0.0.1 ++ 00 08 ; OPC ++ 00 07 ; option length ++ 00 01 ; Family ++ 18 11 ; source mask, scopemask ++ 7f 01 00 ; address ++HEX_EDNSDATA_END ++ENTRY_END ++RANGE_END ++ ++RANGE_BEGIN 40 51 ++ ADDRESS 1.2.3.4 ++ENTRY_BEGIN ++MATCH opcode qtype qname ++ADJUST copy_id ++;copy_ednsdata_assume_clientsubnet ++REPLY QR NOERROR ++SECTION QUESTION ++www.example.com. IN A ++SECTION ANSWER ++www.example.com. IN A 10.20.30.42 ++SECTION AUTHORITY ++SECTION ADDITIONAL ++;no EDNS in this answer. Tests if the back_parsed callback ++;is called to process the lack of edns contents. ++;HEX_EDNSDATA_BEGIN ++ ;00 08 ; OPC ++ ;00 04 ; option length ++ ;00 01 ; Family ++ ;00 00 ; source mask, scopemask ++ ; ; address 0.0.0.0/0 scope 0 ++;HEX_EDNSDATA_END ++ENTRY_END ++RANGE_END ++ ++RANGE_BEGIN 120 131 ++ ADDRESS 1.2.3.4 ++ENTRY_BEGIN ++MATCH opcode qtype qname ++ADJUST copy_id ++;copy_ednsdata_assume_clientsubnet ++REPLY QR NOERROR ++SECTION QUESTION ++www.example.com. IN A ++SECTION ANSWER ++www.example.com. IN A 10.20.30.43 ++SECTION AUTHORITY ++SECTION ADDITIONAL ++HEX_EDNSDATA_BEGIN ++ 00 08 ; OPC ++ 00 07 ; option length ++ 00 01 ; Family ++ 18 00 ; source mask, scopemask ++ 7f 02 00 ; address 127.2.0.0/24 scope 0 ++HEX_EDNSDATA_END ++ENTRY_END ++RANGE_END ++ ++; query for 127.0.0.0/24 ++STEP 1 QUERY ++ENTRY_BEGIN ++HEX_ANSWER_BEGIN ++ 00 00 01 00 00 01 00 00 ;ID 0 ++ 00 00 00 01 03 77 77 77 ; www.example.com A? (DO) ++ 07 65 78 61 6d 70 6c 65 ++ 03 63 6f 6d 00 00 01 00 ++ 01 00 00 29 10 00 00 00 ++ 80 00 00 0b ++ ++ 00 08 00 07 ; OPC, optlen ++ 00 01 18 00 ; ip4, scope 24, source 0 ++ 7f 00 00 ;127.0.0.0/24 ++HEX_ANSWER_END ++ENTRY_END ++ ++; answer is 10.20.30.40 for 127.0.0.0/24 scope 17 ++STEP 10 CHECK_ANSWER ++ENTRY_BEGIN ++MATCH all ednsdata ++REPLY QR RD RA NOERROR ++SECTION QUESTION ++www.example.com. IN A ++SECTION ANSWER ++www.example.com. IN A 10.20.30.40 ++SECTION AUTHORITY ++SECTION ADDITIONAL ++HEX_EDNSDATA_BEGIN ++ ; client is 127.0.0.1 ++ 00 08 ; OPC ++ 00 07 ; option length ++ 00 01 ; Family ++ 18 11 ; source mask, scopemask ++ 7f 00 00 ; address ++HEX_EDNSDATA_END ++ENTRY_END ++ ++; query for 127.1.0.0/24 ++STEP 20 QUERY ++ENTRY_BEGIN ++HEX_ANSWER_BEGIN ++ 00 00 01 00 00 01 00 00 ;ID 0 ++ 00 00 00 01 03 77 77 77 ; www.example.com A? (DO) ++ 07 65 78 61 6d 70 6c 65 ++ 03 63 6f 6d 00 00 01 00 ++ 01 00 00 29 10 00 00 00 ++ 80 00 00 0b ++ ++ 00 08 00 07 ; OPC, optlen ++ 00 01 18 00 ; ip4, scope 24, source 0 ++ 7f 01 00 ;127.1.0.0/24 ++HEX_ANSWER_END ++ENTRY_END ++ ++; answer is 10.20.30.41 for 127.1.0.0/24 scope 17 ++STEP 30 CHECK_ANSWER ++ENTRY_BEGIN ++MATCH all ednsdata ++REPLY QR RD RA NOERROR ++SECTION QUESTION ++www.example.com. IN A ++SECTION ANSWER ++www.example.com. IN A 10.20.30.41 ++SECTION AUTHORITY ++SECTION ADDITIONAL ++HEX_EDNSDATA_BEGIN ++ ; client is 127.1.0.1 ++ 00 08 ; OPC ++ 00 07 ; option length ++ 00 01 ; Family ++ 18 11 ; source mask, scopemask ++ 7f 01 00 ; address ++HEX_EDNSDATA_END ++ENTRY_END ++ ++; query for 0.0.0.0/0 ++STEP 40 QUERY ++ENTRY_BEGIN ++HEX_ANSWER_BEGIN ++ 00 00 01 00 00 01 00 00 ;ID 0 ++ 00 00 00 01 03 77 77 77 ; www.example.com A? (DO) ++ 07 65 78 61 6d 70 6c 65 ++ 03 63 6f 6d 00 00 01 00 ++ 01 00 00 29 10 00 00 00 ++ 80 00 00 08 ++ ++ 00 08 00 04 ; OPC, optlen ++ 00 01 00 00 ; ip4, scope 0, source 0 ++ ;0.0.0.0/0 ++HEX_ANSWER_END ++ENTRY_END ++ ++; answer is 10.20.30.42 for 0.0.0.0/0 scope 0 ++STEP 50 CHECK_ANSWER ++ENTRY_BEGIN ++MATCH all ednsdata ++REPLY QR RD RA NOERROR ++SECTION QUESTION ++www.example.com. IN A ++SECTION ANSWER ++www.example.com. IN A 10.20.30.42 ++SECTION AUTHORITY ++SECTION ADDITIONAL ++HEX_EDNSDATA_BEGIN ++ 00 08 ; OPC ++ 00 04 ; option length ++ 00 01 ; Family ++ 00 00 ; source mask, scopemask ++ ; address ++HEX_EDNSDATA_END ++ENTRY_END ++ ++; query for 127.0.0.0/24, again, it should be in cache. ++; and not from the scope 0 answer. ++STEP 60 QUERY ++ENTRY_BEGIN ++HEX_ANSWER_BEGIN ++ 00 00 01 00 00 01 00 00 ;ID 0 ++ 00 00 00 01 03 77 77 77 ; www.example.com A? (DO) ++ 07 65 78 61 6d 70 6c 65 ++ 03 63 6f 6d 00 00 01 00 ++ 01 00 00 29 10 00 00 00 ++ 80 00 00 0b ++ ++ 00 08 00 07 ; OPC, optlen ++ 00 01 18 00 ; ip4, scope 24, source 0 ++ 7f 00 00 ;127.0.0.0/24 ++HEX_ANSWER_END ++ENTRY_END ++ ++; answer should be 10.20.30.40 for 127.0.0.0/24 scope 17 ++STEP 70 CHECK_ANSWER ++ENTRY_BEGIN ++MATCH all ednsdata ++REPLY QR RD RA NOERROR ++SECTION QUESTION ++www.example.com. IN A ++SECTION ANSWER ++www.example.com. IN A 10.20.30.40 ++SECTION AUTHORITY ++SECTION ADDITIONAL ++HEX_EDNSDATA_BEGIN ++ ; client is 127.0.0.1 ++ 00 08 ; OPC ++ 00 07 ; option length ++ 00 01 ; Family ++ 18 11 ; source mask, scopemask ++ 7f 00 00 ; address ++HEX_EDNSDATA_END ++ENTRY_END ++ ++; query for 127.1.0.0/24, again, it should be in cache. ++STEP 80 QUERY ++ENTRY_BEGIN ++HEX_ANSWER_BEGIN ++ 00 00 01 00 00 01 00 00 ;ID 0 ++ 00 00 00 01 03 77 77 77 ; www.example.com A? (DO) ++ 07 65 78 61 6d 70 6c 65 ++ 03 63 6f 6d 00 00 01 00 ++ 01 00 00 29 10 00 00 00 ++ 80 00 00 0b ++ ++ 00 08 00 07 ; OPC, optlen ++ 00 01 18 00 ; ip4, scope 24, source 0 ++ 7f 01 00 ;127.1.0.0/24 ++HEX_ANSWER_END ++ENTRY_END ++ ++; answer should be 10.20.30.41 for 127.1.0.0/24 scope 17 ++STEP 90 CHECK_ANSWER ++ENTRY_BEGIN ++MATCH all ednsdata ++REPLY QR RD RA NOERROR ++SECTION QUESTION ++www.example.com. IN A ++SECTION ANSWER ++www.example.com. IN A 10.20.30.41 ++SECTION AUTHORITY ++SECTION ADDITIONAL ++HEX_EDNSDATA_BEGIN ++ ; client is 127.1.0.1 ++ 00 08 ; OPC ++ 00 07 ; option length ++ 00 01 ; Family ++ 18 11 ; source mask, scopemask ++ 7f 01 00 ; address ++HEX_EDNSDATA_END ++ENTRY_END ++ ++; query for 0.0.0.0/0, again. ++STEP 100 QUERY ++ENTRY_BEGIN ++HEX_ANSWER_BEGIN ++ 00 00 01 00 00 01 00 00 ;ID 0 ++ 00 00 00 01 03 77 77 77 ; www.example.com A? (DO) ++ 07 65 78 61 6d 70 6c 65 ++ 03 63 6f 6d 00 00 01 00 ++ 01 00 00 29 10 00 00 00 ++ 80 00 00 08 ++ ++ 00 08 00 04 ; OPC, optlen ++ 00 01 00 00 ; ip4, scope 0, source 0 ++ ;0.0.0.0/0 ++HEX_ANSWER_END ++ENTRY_END ++ ++; answer should be 10.20.30.42 for 0.0.0.0/0 scope 0 ++STEP 110 CHECK_ANSWER ++ENTRY_BEGIN ++MATCH all ednsdata ++REPLY QR RD RA NOERROR ++SECTION QUESTION ++www.example.com. IN A ++SECTION ANSWER ++www.example.com. IN A 10.20.30.42 ++SECTION AUTHORITY ++SECTION ADDITIONAL ++HEX_EDNSDATA_BEGIN ++ 00 08 ; OPC ++ 00 04 ; option length ++ 00 01 ; Family ++ 00 00 ; source mask, scopemask ++ ; address ++HEX_EDNSDATA_END ++ENTRY_END ++ ++; now a query for a /24 that gets an answer for a /0. ++STEP 120 QUERY ++ENTRY_BEGIN ++HEX_ANSWER_BEGIN ++ 00 00 01 00 00 01 00 00 ;ID 0 ++ 00 00 00 01 03 77 77 77 ; www.example.com A? (DO) ++ 07 65 78 61 6d 70 6c 65 ++ 03 63 6f 6d 00 00 01 00 ++ 01 00 00 29 10 00 00 00 ++ 80 00 00 0b ++ ++ 00 08 00 07 ; OPC, optlen ++ 00 01 18 00 ; ip4, scope 24, source 0 ++ 7f 02 00 ;127.2.0.0/24 ++HEX_ANSWER_END ++ENTRY_END ++ ++; answer should be 10.20.30.43 for 127.2.0.0/24 scope 0 ++STEP 130 CHECK_ANSWER ++ENTRY_BEGIN ++MATCH all ednsdata ++REPLY QR RD RA NOERROR ++SECTION QUESTION ++www.example.com. IN A ++SECTION ANSWER ++www.example.com. IN A 10.20.30.43 ++SECTION AUTHORITY ++SECTION ADDITIONAL ++HEX_EDNSDATA_BEGIN ++ ; client is 127.2.0.1 ++ 00 08 ; OPC ++ 00 07 ; option length ++ 00 01 ; Family ++ 18 00 ; source mask, scopemask ++ 7f 02 00 ; address ++HEX_EDNSDATA_END ++ENTRY_END ++ ++; the scope 0 answer is now used to answer queries from ++; query for 127.0.0.0/24 ++STEP 140 QUERY ++ENTRY_BEGIN ++HEX_ANSWER_BEGIN ++ 00 00 01 00 00 01 00 00 ;ID 0 ++ 00 00 00 01 03 77 77 77 ; www.example.com A? (DO) ++ 07 65 78 61 6d 70 6c 65 ++ 03 63 6f 6d 00 00 01 00 ++ 01 00 00 29 10 00 00 00 ++ 80 00 00 0b ++ ++ 00 08 00 07 ; OPC, optlen ++ 00 01 18 00 ; ip4, scope 24, source 0 ++ 7f 00 00 ;127.0.0.0/24 ++HEX_ANSWER_END ++ENTRY_END ++ ++STEP 150 CHECK_ANSWER ++ENTRY_BEGIN ++MATCH all ednsdata ++REPLY QR RD RA NOERROR ++SECTION QUESTION ++www.example.com. IN A ++SECTION ANSWER ++www.example.com. IN A 10.20.30.43 ++SECTION AUTHORITY ++SECTION ADDITIONAL ++HEX_EDNSDATA_BEGIN ++ ; client is 127.0.0.1 ++ 00 08 ; OPC ++ 00 07 ; option length ++ 00 01 ; Family ++ 18 00 ; source mask, scopemask ++ 7f 00 00 ; address ++HEX_EDNSDATA_END ++ENTRY_END ++ ++SCENARIO_END diff --git a/SPECS/unbound.spec b/SPECS/unbound.spec index 34e7f66..7ac5e05 100644 --- a/SPECS/unbound.spec +++ b/SPECS/unbound.spec @@ -30,7 +30,7 @@ Summary: Validating, recursive, and caching DNS(SEC) resolver Name: unbound Version: 1.16.2 -Release: 17%{?extra_version:.%{extra_version}}%{?dist} +Release: 21%{?extra_version:.%{extra_version}}%{?dist} License: BSD Url: https://nlnetlabs.nl/projects/unbound/ Source: https://nlnetlabs.nl/downloads/%{name}/%{name}-%{version}%{?extra_version}.tar.gz @@ -65,6 +65,19 @@ Patch1: unbound-1.16-CVE-2022-3204.patch Patch4: unbound-1.16-CVE-2023-50387-CVE-2023-50868.patch # https://github.com/NLnetLabs/unbound/commit/6d1e61173 Patch5: unbound-1.16-control-t-flag.patch +# https://github.com/NLnetLabs/unbound/commit/b7c61d7cc256d6a174e6179622c7fa968272c259 +Patch6: unbound-1.21-CVE-2024-8508.patch +# https://github.com/NLnetLabs/unbound/commit/b48958c983f60af40358cca168c403e57bde30d2 +Patch7: unbound-1.16-control-key-perms.patch +# The patch for CVE-2025-5994 requires certain changes fixing bugs in subnet module +# that is why we have to backport these commits. They have their respective tests +# backported with them. +# https://github.com/NLnetLabs/unbound/commit/0f08cc6d5577ad4747749c55229e16df8711ee32 +# https://github.com/NLnetLabs/unbound/commit/6d0812b56731af130e8bc7e1572388934beb9b3b +# https://github.com/NLnetLabs/unbound/commit/be626f7c5330dc414a582a04b537ea79d5c452fb +# https://github.com/NLnetLabs/unbound/commit/5bf82f246481098a6473f296b21fc1229d276c0f +# https://github.com/NLnetLabs/unbound/commit/a1150078f29e14b36c8e4d9d05a263a5e6abbc5b +Patch8: unbound-1.23.1-CVE-2025-5994.patch BuildRequires: gcc, make BuildRequires: flex, openssl-devel @@ -381,6 +394,16 @@ fi %postun libs %systemd_postun_with_restart unbound-anchor.timer +# this trigger ensures that if user changed their config +# prior the move of root auth-zone to separate file in 1.16.2-18, we do not +# force the change of root auth-zone on them +%triggerpostun -- unbound < 1.16.2-18 +if [ -f %{_sysconfdir}/%{name}/unbound.conf.rpmnew ] \ + && [ -L %{_sysconfdir}/%{name}/conf.d/unbound-local-root.conf ] \ + && [ "$(readlink -f %{_sysconfdir}/%{name}/conf.d/unbound-local-root.conf)" == "%{_sysconfdir}/%{name}/unbound-local-root.conf" ]; then + rm -f %{_sysconfdir}/%{name}/conf.d/unbound-local-root.conf +fi + %check pushd %{dir_primary} #pushd pythonmod @@ -421,7 +444,7 @@ popd %ghost %attr(0640,root,unbound) %{_sysconfdir}/%{name}/unbound_control.pem %ghost %attr(0640,root,unbound) %{_sysconfdir}/%{name}/unbound_control.key %ghost %attr(0640,root,unbound) %{_sysconfdir}/%{name}/unbound_server.pem -%ghost %attr(0640,root,unbound) %{_sysconfdir}/%{name}/unbound_server.key +%ghost %attr(0600,root,unbound) %{_sysconfdir}/%{name}/unbound_server.key %{_sbindir}/unbound %{_sbindir}/unbound-checkconf %{_sbindir}/unbound-control @@ -486,6 +509,21 @@ popd %{_prefix}/lib/dracut/modules.d/99unbound %changelog +* Mon Jul 28 2025 Tomas Korbar - 1.16.2-21 +- Fix RebirthDay Attack (CVE-2025-5994) +- Resolves: RHEL-104129 + +* Wed Jul 16 2025 Tomas Korbar - 1.16.2-20 +- Fix verification of unbound-control key files +- Resolves: RHEL-65396 + +* Tue Jun 24 2025 Tomas Korbar - 1.16.2-19 +- Fix regression on update introduced by local-root symlink +- Resolves: RHEL-92255 + +* Wed May 14 2025 Petr Menšík - 1.16.2-18 +- Prevent unbounded name compression (CVE-2024-8508) + * Mon Feb 10 2025 Tomas Korbar - 1.16.2-17 - Add as112 networks config file - Resolves: RHEL-78696