}
}
+/** fast reload thread, check if config str3list has changed. */
+#define FR_CHECK_CHANGED_CFG_STR3LIST(desc, var, buff) do { \
+ fr_check_changed_cfg_str3list(cfg->var, newcfg->var, desc, buff,\
+ sizeof(buff)); \
+ } while(0);
+static void
+fr_check_changed_cfg_str3list(struct config_str3list* cmp1,
+ struct config_str3list* cmp2, const char* desc, char* str, size_t len)
+{
+ struct config_str3list* p1 = cmp1, *p2 = cmp2;
+ while(p1 && p2) {
+ if((!p1->str && p2->str) ||
+ (p1->str && !p2->str) ||
+ (p1->str && p2->str && strcmp(p1->str, p2->str) != 0)) {
+ /* The str3list is different. */
+ fr_add_incompatible_option(desc, str, len);
+ return;
+ }
+ if((!p1->str2 && p2->str2) ||
+ (p1->str2 && !p2->str2) ||
+ (p1->str2 && p2->str2 &&
+ strcmp(p1->str2, p2->str2) != 0)) {
+ /* The str3list is different. */
+ fr_add_incompatible_option(desc, str, len);
+ return;
+ }
+ if((!p1->str3 && p2->str3) ||
+ (p1->str3 && !p2->str3) ||
+ (p1->str3 && p2->str3 &&
+ strcmp(p1->str3, p2->str3) != 0)) {
+ /* The str3list is different. */
+ fr_add_incompatible_option(desc, str, len);
+ return;
+ }
+ p1 = p1->next;
+ p2 = p2->next;
+ }
+ if((!p1 && p2) || (p1 && !p2)) {
+ fr_add_incompatible_option(desc, str, len);
+ }
+}
+
+/** fast reload thread, check tag datas. */
+static int
+fr_check_tag_datas(struct fast_reload_thread* fr, struct config_file* newcfg)
+{
+ char changed_str[1024];
+ struct config_file* cfg = fr->worker->env.cfg;
+ changed_str[0]=0;
+
+ /* Check for tag_datas in acl_addr. */
+ FR_CHECK_CHANGED_CFG_STR3LIST("interface-tag-data", interface_tag_datas, changed_str);
+ FR_CHECK_CHANGED_CFG_STR3LIST("access-control-tag-data", acl_tag_datas, changed_str);
+
+ if(changed_str[0] != 0) {
+ if(fr->fr_drop_mesh)
+ return 1; /* already dropping queries */
+ fr->fr_drop_mesh = 1;
+ fr->worker->daemon->fast_reload_drop_mesh = fr->fr_drop_mesh;
+ if(!fr_output_printf(fr, "recursion referenced data has changed, with: '%s"
+ "', and the queries have to be dropped"
+ ", setting '+d'\n", changed_str))
+ return 0;
+ fr_send_notification(fr, fast_reload_notification_printout);
+ }
+ return 1;
+}
+
/** fast reload thread, check compatible config items */
static int
fr_check_compat_cfg(struct fast_reload_thread* fr, struct config_file* newcfg)
config_delete(newcfg);
return 0;
}
+ if(!fr_check_tag_datas(fr, newcfg)) {
+ config_delete(newcfg);
+ return 0;
+ }
if(!fr_check_compat_cfg(fr, newcfg)) {
config_delete(newcfg);
return 0;
not considered valid when an NSEC next owner name is not
under the signer zone name. Thanks to Qifan Zhang, Palo
Alto Networks, for the report.
+ - Fix mesh cycle detection for configuration with respip CNAME
+ loop and tagged clients. Thanks to Qifan Zhang, Palo Alto
+ Networks, for the report.
22 July 2026: Wouter
- Release tag for 1.25.2, with the security commits:
* clients. */
qstate->is_drop = 1;
} else if(alias_rrset) {
- if(!generate_cname_request(qstate, alias_rrset))
+ if(!generate_cname_request(qstate, alias_rrset)) {
+ errinf(qstate, "Could not generate CNAME request");
goto servfail;
+ }
next_state = module_wait_subquery;
}
qstate->return_msg->rep = new_rep;
servfail:
qstate->return_rcode = LDNS_RCODE_SERVFAIL;
qstate->return_msg = NULL;
+ qstate->ext_state[id] = module_finished;
}
int
return;
fail:
+ errinf(super, "CNAME lookup failed");
super->return_rcode = LDNS_RCODE_SERVFAIL;
super->return_msg = NULL;
return;
mesh_run(mesh, e->qstate->mesh_info, event, e);
}
-/** copy strlist to region */
-static struct config_strlist*
-cfg_region_strlist_copy(struct regional* region, struct config_strlist* list)
-{
- struct config_strlist* result = NULL, *last = NULL, *s = list;
- while(s) {
- struct config_strlist* n = regional_alloc_zero(region,
- sizeof(*n));
- if(!n)
- return NULL;
- n->str = regional_strdup(region, s->str);
- if(!n->str)
- return NULL;
- if(last)
- last->next = n;
- else result = n;
- last = n;
- s = s->next;
- }
- return result;
-}
-
struct respip_client_info*
mesh_copy_client_info(struct regional* region, struct respip_client_info* cinfo)
{
- size_t i;
struct respip_client_info* client_info;
client_info = regional_alloc_init(region, cinfo, sizeof(*cinfo));
if(!client_info)
if(!client_info->tag_actions)
return NULL;
}
- if(cinfo->tag_datas) {
- client_info->tag_datas = regional_alloc_zero(region,
- sizeof(struct config_strlist*)*cinfo->tag_datas_size);
- if(!client_info->tag_datas)
- return NULL;
- for(i=0; i<cinfo->tag_datas_size; i++) {
- if(cinfo->tag_datas[i]) {
- client_info->tag_datas[i] = cfg_region_strlist_copy(
- region, cinfo->tag_datas[i]);
- if(!client_info->tag_datas[i])
- return NULL;
- }
- }
- }
+ /* tag_datas is owned by the matched acl_addr in config_file; its
+ * lifetime is until config reload, which tears down all mesh states
+ * first. Keep the original pointer so client_info_compare()
+ * can recognise two states from the same ACL entry. */
+ /* fast reload insists on dropping the queries when interface-tag-data
+ * or access-control-tag-data are changed. */
+ /* client_info->tag_datas already copied by regional_alloc_init above */
if(cinfo->view) {
/* Do not copy the view pointer but store a name instead.
* The name is looked up later when done, this means that
enum module_ev ev, struct outbound_entry* e)
{
enum module_ext_state s;
+ int numrun = 0;
verbose(VERB_ALGO, "mesh_run: start");
while(mstate) {
+ if(numrun++ > MESH_MAX_RUN_ITER) {
+ /* These modules are too much to activate, stop them.*/
+ log_err("Too many module run iterations, deleting");
+ while(mstate) {
+ /* notify supers */
+ if(mstate->super_set.count > 0) {
+ verbose(VERB_ALGO, "notify supers of failure");
+ mstate->s.return_msg = NULL;
+ mstate->s.return_rcode = LDNS_RCODE_SERVFAIL;
+ mesh_walk_supers(mesh, mstate);
+ }
+ mesh_state_delete(&mstate->s);
+ if(mesh->run.count > 0) {
+ /* pop random element off the runnable tree */
+ mstate = (struct mesh_state*)mesh->run.root->key;
+ (void)rbtree_delete(&mesh->run, mstate);
+ } else mstate = NULL;
+ }
+ break;
+ }
/* run the module */
fptr_ok(fptr_whitelist_mod_operate(
mesh->mods.mod[mstate->s.curmod]->operate));
*/
#define MESH_MAX_ACTIVATION 10000
+/**
+ * Maximum number of mesh state run items. These are different modules
+ * activated during a mesh run. Any more is likely an infinite loop
+ * in the module. It is then terminated, and states are deleted.
+ */
+#define MESH_MAX_RUN_ITER 10000
+
/**
* Max number of references-to-references-to-references.. search size.
* Any more is treated like 'too large', and the creation of a new
--- /dev/null
+; config options
+; The island of trust is at test.
+server:
+ target-fetch-policy: "0 0 0 0 0"
+ qname-minimisation: "no"
+ fake-sha1: yes
+ trust-anchor-signaling: no
+ minimal-responses: no
+ iter-scrub-promiscuous: no
+ aggressive-nsec: no
+ local-zone: test. nodefault
+ log-servfail: yes
+ discard-timeout: 0
+ module-config: "respip iterator"
+ define-tag: "turqoise"
+ access-control-tag: 127.0.0.0/8 "turqoise"
+ access-control-tag-data: 127.0.0.0/8 "turqoise" "A 127.0.0.1"
+
+ ; These two CNAMEs form a loop.
+ response-ip: 192.0.2.1/32 redirect
+ response-ip-data: 192.0.2.1/32 "CNAME loop2.far.test."
+ response-ip: 192.0.2.2/32 redirect
+ response-ip-data: 192.0.2.2/32 "CNAME loop1.far.test."
+
+stub-zone:
+ name: "."
+ stub-addr: 193.0.14.129 # K.ROOT-SERVERS.NET.
+CONFIG_END
+
+SCENARIO_BEGIN Test respip CNAME loop that is tagged.
+
+; 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
+test. IN NS
+SECTION AUTHORITY
+test. IN NS ns.test.
+SECTION ADDITIONAL
+ns.test. IN A 1.2.3.5
+ENTRY_END
+RANGE_END
+
+; ns.test
+RANGE_BEGIN 0 100
+ ADDRESS 1.2.3.5
+ENTRY_BEGIN
+MATCH opcode qtype qname
+ADJUST copy_id
+REPLY QR AA NOERROR
+SECTION QUESTION
+test. IN NS
+SECTION ANSWER
+test. IN NS ns.test
+SECTION ADDITIONAL
+ns.test. IN A 1.2.3.5
+ENTRY_END
+
+ENTRY_BEGIN
+MATCH opcode qtype qname
+ADJUST copy_id
+REPLY QR AA NOERROR
+SECTION QUESTION
+ns.test. IN A
+SECTION ANSWER
+ns.test. IN A 1.2.3.5
+ENTRY_END
+
+ENTRY_BEGIN
+MATCH opcode qtype qname
+ADJUST copy_id
+REPLY QR AA NOERROR
+SECTION QUESTION
+ns.test. IN AAAA
+SECTION AUTHORITY
+test. 3600 IN SOA ns.test. host.test. 20201 3600 1800 604800 3600
+ENTRY_END
+
+ENTRY_BEGIN
+MATCH opcode subdomain
+ADJUST copy_id copy_query
+REPLY QR NOERROR
+SECTION QUESTION
+example.test. IN NS
+SECTION AUTHORITY
+example.test. IN NS ns.example.test.
+SECTION ADDITIONAL
+ns.example.test. IN A 1.2.3.4
+ENTRY_END
+
+ENTRY_BEGIN
+MATCH opcode subdomain
+ADJUST copy_id copy_query
+REPLY QR NOERROR
+SECTION QUESTION
+far.test. IN NS
+SECTION AUTHORITY
+far.test. IN NS ns.far.test.
+SECTION ADDITIONAL
+ns.far.test. IN A 1.2.3.6
+ENTRY_END
+RANGE_END
+
+; ns.example.test.
+RANGE_BEGIN 0 20
+ ADDRESS 1.2.3.4
+ENTRY_BEGIN
+MATCH opcode qtype qname
+ADJUST copy_id
+REPLY QR NOERROR
+SECTION QUESTION
+example.test. IN NS
+SECTION ANSWER
+example.test. IN NS ns.example.test.
+SECTION ADDITIONAL
+ns.example.test. 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.test. IN A
+SECTION ANSWER
+ns.example.test. 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.test. IN AAAA
+SECTION AUTHORITY
+example.test. 3600 IN SOA ns.example.test. host.example.test. 20301 3600 1800 604800 3600
+ENTRY_END
+
+; response to query of interest
+ENTRY_BEGIN
+MATCH opcode qtype qname
+ADJUST copy_id
+REPLY QR AA NOERROR
+SECTION QUESTION
+www.example.test. IN A
+SECTION ANSWER
+www.example.test. 1 IN A 192.0.2.1
+ENTRY_END
+RANGE_END
+
+; ns.example.test.
+RANGE_BEGIN 45 100
+ ADDRESS 1.2.3.4
+ENTRY_BEGIN
+MATCH opcode qtype qname
+ADJUST copy_id
+REPLY QR AA NOERROR
+SECTION QUESTION
+www.example.test. IN A
+SECTION ANSWER
+www.example.test. 1 IN A 192.0.2.1
+ENTRY_END
+RANGE_END
+
+; ns.far.test.
+RANGE_BEGIN 0 100
+ ADDRESS 1.2.3.6
+ENTRY_BEGIN
+MATCH opcode qtype qname
+ADJUST copy_id
+REPLY QR NOERROR
+SECTION QUESTION
+far.test. IN NS
+SECTION ANSWER
+far.test. IN NS ns.far.test.
+SECTION ADDITIONAL
+ns.far.test. IN A 1.2.3.6
+ENTRY_END
+
+ENTRY_BEGIN
+MATCH opcode qtype qname
+ADJUST copy_id
+REPLY QR AA NOERROR
+SECTION QUESTION
+ns.far.test. IN A
+SECTION ANSWER
+ns.far.test. IN A 1.2.3.6
+ENTRY_END
+
+ENTRY_BEGIN
+MATCH opcode qtype qname
+ADJUST copy_id
+REPLY QR AA NOERROR
+SECTION QUESTION
+ns.far.test. IN AAAA
+SECTION AUTHORITY
+far.test. 3600 IN SOA ns.far.test. host.far.test. 20301 3600 1800 604800 3600
+ENTRY_END
+
+; response to query of interest
+ENTRY_BEGIN
+MATCH opcode qtype qname
+ADJUST copy_id
+REPLY QR AA NOERROR
+SECTION QUESTION
+loop1.far.test. IN A
+SECTION ANSWER
+loop1.far.test. IN A 192.0.2.1
+ENTRY_END
+
+ENTRY_BEGIN
+MATCH opcode qtype qname
+ADJUST copy_id
+REPLY QR AA NOERROR
+SECTION QUESTION
+loop2.far.test. IN A
+SECTION ANSWER
+loop2.far.test. IN A 192.0.2.2
+ENTRY_END
+RANGE_END
+
+STEP 1 QUERY
+ENTRY_BEGIN
+REPLY RD DO
+SECTION QUESTION
+loop1.far.test. IN A
+ENTRY_END
+
+STEP 2 CHECK_ANSWER
+ENTRY_BEGIN
+MATCH all
+REPLY QR RD RA DO SERVFAIL
+SECTION QUESTION
+loop1.far.test. IN A
+SECTION ANSWER
+ENTRY_END
+
+SCENARIO_END