summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPatrick Vogelaar <patrick.vogelaar@belden.com>2025-11-02 22:33:37 +0100
committerAnuj Mittal <anuj.mittal@oss.qualcomm.com>2025-11-12 11:14:56 +0530
commitd9c8972cb71a85ce6dfcfce55477937ea954b1ce (patch)
treef3444f13c49b12040b29b00e4cd269dbd143927c
parent4084b10111b49944cbe2b9464a5a002b32bcefd0 (diff)
downloadmeta-openembedded-d9c8972cb71a85ce6dfcfce55477937ea954b1ce.tar.gz
unbound: patch CVE-2024-33655 and CVE-2025-11411
For CVE-2024-33655 applied patch [1] mentioned in [2]. For CVE-2025-11411 applied minimal patch [3] mentioned in [4]. (Slightly adjustments were required to apply properly) [1] https://nlnetlabs.nl/downloads/unbound/patch_CVE-2024-33655.diff [2] https://www.nlnetlabs.nl/downloads/unbound/CVE-2024-33655.txt [3] https://nlnetlabs.nl/downloads/unbound/patch_CVE-2025-11411.diff [4] https://www.nlnetlabs.nl/downloads/unbound/CVE-2025-11411.txt Signed-off-by: Patrick Vogelaar <patrick.vogelaar@belden.com> Signed-off-by: Anuj Mittal <anuj.mittal@oss.qualcomm.com>
-rw-r--r--meta-networking/recipes-support/unbound/unbound/CVE-2024-33655.patch792
-rw-r--r--meta-networking/recipes-support/unbound/unbound/CVE-2025-11411.patch48
-rw-r--r--meta-networking/recipes-support/unbound/unbound_1.19.3.bb2
3 files changed, 842 insertions, 0 deletions
diff --git a/meta-networking/recipes-support/unbound/unbound/CVE-2024-33655.patch b/meta-networking/recipes-support/unbound/unbound/CVE-2024-33655.patch
new file mode 100644
index 0000000000..9431b0e0c6
--- /dev/null
+++ b/meta-networking/recipes-support/unbound/unbound/CVE-2024-33655.patch
@@ -0,0 +1,792 @@
1From e386c19bfb79e42910704b7f152693aa996a5da5 Mon Sep 17 00:00:00 2001
2From: Patrick Vogelaar <patrick.vogelaar@belden.com>
3Date: Sun, 2 Nov 2025 21:23:18 +0100
4Subject: [PATCH] Fix CVE-2024-33655 (DNSBomb attack)
5
6This fixes CVE-2024-33655 by applying a patch [1] listed in [2]
7
8[1] https://nlnetlabs.nl/downloads/unbound/patch_CVE-2024-33655.diff
9[2] https://www.nlnetlabs.nl/downloads/unbound/CVE-2024-33655.txt
10
11CVE: CVE-2024-33655
12Upstream-Status: Backport [https://nlnetlabs.nl/downloads/unbound/patch_CVE-2024-33655.diff]
13
14Signed-off-by: Patrick Vogelaar <patrick.vogelaar@belden.com>
15---
16 doc/example.conf.in | 15 ++
17 doc/unbound.conf.5.in | 30 ++++
18 services/cache/infra.c | 170 +++++++++++++++++-
19 services/cache/infra.h | 28 +++
20 services/mesh.c | 65 +++++++
21 .../doh_downstream.tdir/doh_downstream.conf | 1 +
22 .../doh_downstream_notls.conf | 1 +
23 .../doh_downstream_post.conf | 1 +
24 .../fwd_three_service.conf | 1 +
25 testdata/iter_ghost_timewindow.rpl | 1 +
26 .../ssl_req_order.tdir/ssl_req_order.conf | 1 +
27 .../tcp_req_order.tdir/tcp_req_order.conf | 1 +
28 testdata/tcp_sigpipe.tdir/tcp_sigpipe.conf | 3 +-
29 util/config_file.c | 15 ++
30 util/config_file.h | 15 ++
31 util/configlexer.lex | 5 +
32 util/configparser.y | 55 ++++++
33 17 files changed, 405 insertions(+), 3 deletions(-)
34
35diff --git a/doc/example.conf.in b/doc/example.conf.in
36index 1ac155b7..67047980 100644
37--- a/doc/example.conf.in
38+++ b/doc/example.conf.in
39@@ -191,6 +191,21 @@ server:
40 # are behind a slow satellite link, to eg. 1128.
41 # unknown-server-time-limit: 376
42
43+ # msec before recursion replies are dropped. The work item continues.
44+ # discard-timeout: 1900
45+
46+ # Max number of replies waiting for recursion per IP address.
47+ # wait-limit: 1000
48+
49+ # Max replies waiting for recursion for IP address with cookie.
50+ # wait-limit-cookie: 10000
51+
52+ # Apart from the default, the wait limit can be set for a netblock.
53+ # wait-limit-netblock: 192.0.2.0/24 50000
54+
55+ # Apart from the default, the wait limit with cookie can be adjusted.
56+ # wait-limit-cookie-netblock: 192.0.2.0/24 50000
57+
58 # the amount of memory to use for the RRset cache.
59 # plain value in bytes or you can append k, m or G. default is "4Mb".
60 # rrset-cache-size: 4m
61diff --git a/doc/unbound.conf.5.in b/doc/unbound.conf.5.in
62index 84eddd94..cc5fd64d 100644
63--- a/doc/unbound.conf.5.in
64+++ b/doc/unbound.conf.5.in
65@@ -302,6 +302,36 @@ Increase this if you are behind a slow satellite link, to eg. 1128.
66 That would then avoid re\-querying every initial query because it times out.
67 Default is 376 msec.
68 .TP
69+.B discard\-timeout: \fI<msec>
70+The wait time in msec where recursion requests are dropped. This is
71+to stop a large number of replies from accumulating. They receive
72+no reply, the work item continues to recurse. It is nice to be a bit
73+larger than serve\-expired\-client\-timeout if that is enabled.
74+A value of 1900 msec is suggested. The value 0 disables it.
75+Default 1900 msec.
76+.TP
77+.B wait\-limit: \fI<number>
78+The number of replies that can wait for recursion, for an IP address.
79+This makes a ratelimit per IP address of waiting replies for recursion.
80+It stops very large amounts of queries waiting to be returned to one
81+destination. The value 0 disables wait limits. Default is 1000.
82+.TP
83+.B wait\-limit\-cookie: \fI<number>
84+The number of replies that can wait for recursion, for an IP address
85+that sent the query with a valid DNS cookie. Since the cookie validates
86+the client address, the limit can be higher. Default is 10000.
87+.TP
88+.B wait\-limit\-netblock: \fI<netblock> <number>
89+The wait limit for the netblock. If not given the wait\-limit value is
90+used. The most specific netblock is used to determine the limit. Useful for
91+overriding the default for a specific, group or individual, server.
92+The value -1 disables wait limits for the netblock.
93+.TP
94+.B wait\-limit\-cookie\-netblock: \fI<netblock> <number>
95+The wait limit for the netblock, when the query has a DNS cookie.
96+If not given, the wait\-limit\-cookie value is used.
97+The value -1 disables wait limits for the netblock.
98+.TP
99 .B so\-rcvbuf: \fI<number>
100 If not 0, then set the SO_RCVBUF socket option to get more buffer
101 space on UDP port 53 incoming queries. So that short spikes on busy
102diff --git a/services/cache/infra.c b/services/cache/infra.c
103index 31462d13..457685ab 100644
104--- a/services/cache/infra.c
105+++ b/services/cache/infra.c
106@@ -234,6 +234,81 @@ setup_domain_limits(struct infra_cache* infra, struct config_file* cfg)
107 return 1;
108 }
109
110+/** find or create element in wait limit netblock tree */
111+static struct wait_limit_netblock_info*
112+wait_limit_netblock_findcreate(struct infra_cache* infra, char* str,
113+ int cookie)
114+{
115+ rbtree_type* tree;
116+ struct sockaddr_storage addr;
117+ int net;
118+ socklen_t addrlen;
119+ struct wait_limit_netblock_info* d;
120+
121+ if(!netblockstrtoaddr(str, 0, &addr, &addrlen, &net)) {
122+ log_err("cannot parse wait limit netblock '%s'", str);
123+ return 0;
124+ }
125+
126+ /* can we find it? */
127+ if(cookie)
128+ tree = &infra->wait_limits_cookie_netblock;
129+ else
130+ tree = &infra->wait_limits_netblock;
131+ d = (struct wait_limit_netblock_info*)addr_tree_find(tree, &addr,
132+ addrlen, net);
133+ if(d)
134+ return d;
135+
136+ /* create it */
137+ d = (struct wait_limit_netblock_info*)calloc(1, sizeof(*d));
138+ if(!d)
139+ return NULL;
140+ d->limit = -1;
141+ if(!addr_tree_insert(tree, &d->node, &addr, addrlen, net)) {
142+ log_err("duplicate element in domainlimit tree");
143+ free(d);
144+ return NULL;
145+ }
146+ return d;
147+}
148+
149+
150+/** insert wait limit information into lookup tree */
151+static int
152+infra_wait_limit_netblock_insert(struct infra_cache* infra,
153+ struct config_file* cfg)
154+{
155+ struct config_str2list* p;
156+ struct wait_limit_netblock_info* d;
157+ for(p = cfg->wait_limit_netblock; p; p = p->next) {
158+ d = wait_limit_netblock_findcreate(infra, p->str, 0);
159+ if(!d)
160+ return 0;
161+ d->limit = atoi(p->str2);
162+ }
163+ for(p = cfg->wait_limit_cookie_netblock; p; p = p->next) {
164+ d = wait_limit_netblock_findcreate(infra, p->str, 1);
165+ if(!d)
166+ return 0;
167+ d->limit = atoi(p->str2);
168+ }
169+ return 1;
170+}
171+
172+/** setup wait limits tree (0 on failure) */
173+static int
174+setup_wait_limits(struct infra_cache* infra, struct config_file* cfg)
175+{
176+ addr_tree_init(&infra->wait_limits_netblock);
177+ addr_tree_init(&infra->wait_limits_cookie_netblock);
178+ if(!infra_wait_limit_netblock_insert(infra, cfg))
179+ return 0;
180+ addr_tree_init_parents(&infra->wait_limits_netblock);
181+ addr_tree_init_parents(&infra->wait_limits_cookie_netblock);
182+ return 1;
183+}
184+
185 struct infra_cache*
186 infra_create(struct config_file* cfg)
187 {
188@@ -267,6 +342,10 @@ infra_create(struct config_file* cfg)
189 infra_delete(infra);
190 return NULL;
191 }
192+ if(!setup_wait_limits(infra, cfg)) {
193+ infra_delete(infra);
194+ return NULL;
195+ }
196 infra_ip_ratelimit = cfg->ip_ratelimit;
197 infra->client_ip_rates = slabhash_create(cfg->ip_ratelimit_slabs,
198 INFRA_HOST_STARTSIZE, cfg->ip_ratelimit_size, &ip_rate_sizefunc,
199@@ -287,6 +366,12 @@ static void domain_limit_free(rbnode_type* n, void* ATTR_UNUSED(arg))
200 }
201 }
202
203+/** delete wait_limit_netblock_info entries */
204+static void wait_limit_netblock_del(rbnode_type* n, void* ATTR_UNUSED(arg))
205+{
206+ free(n);
207+}
208+
209 void
210 infra_delete(struct infra_cache* infra)
211 {
212@@ -296,6 +381,10 @@ infra_delete(struct infra_cache* infra)
213 slabhash_delete(infra->domain_rates);
214 traverse_postorder(&infra->domain_limits, domain_limit_free, NULL);
215 slabhash_delete(infra->client_ip_rates);
216+ traverse_postorder(&infra->wait_limits_netblock,
217+ wait_limit_netblock_del, NULL);
218+ traverse_postorder(&infra->wait_limits_cookie_netblock,
219+ wait_limit_netblock_del, NULL);
220 free(infra);
221 }
222
223@@ -880,7 +969,8 @@ static void infra_create_ratedata(struct infra_cache* infra,
224
225 /** create rate data item for ip address */
226 static void infra_ip_create_ratedata(struct infra_cache* infra,
227- struct sockaddr_storage* addr, socklen_t addrlen, time_t timenow)
228+ struct sockaddr_storage* addr, socklen_t addrlen, time_t timenow,
229+ int mesh_wait)
230 {
231 hashvalue_type h = hash_addr(addr, addrlen, 0);
232 struct ip_rate_key* k = (struct ip_rate_key*)calloc(1, sizeof(*k));
233@@ -898,6 +988,7 @@ static void infra_ip_create_ratedata(struct infra_cache* infra,
234 k->entry.data = d;
235 d->qps[0] = 1;
236 d->timestamp[0] = timenow;
237+ d->mesh_wait = mesh_wait;
238 slabhash_insert(infra->client_ip_rates, h, &k->entry, d, NULL);
239 }
240
241@@ -1121,6 +1212,81 @@ int infra_ip_ratelimit_inc(struct infra_cache* infra,
242 }
243
244 /* create */
245- infra_ip_create_ratedata(infra, addr, addrlen, timenow);
246+ infra_ip_create_ratedata(infra, addr, addrlen, timenow, 0);
247 return 1;
248 }
249+
250+int infra_wait_limit_allowed(struct infra_cache* infra, struct comm_reply* rep,
251+ int cookie_valid, struct config_file* cfg)
252+{
253+ struct lruhash_entry* entry;
254+ if(cfg->wait_limit == 0)
255+ return 1;
256+
257+ entry = infra_find_ip_ratedata(infra, &rep->client_addr,
258+ rep->client_addrlen, 0);
259+ if(entry) {
260+ rbtree_type* tree;
261+ struct wait_limit_netblock_info* w;
262+ struct rate_data* d = (struct rate_data*)entry->data;
263+ int mesh_wait = d->mesh_wait;
264+ lock_rw_unlock(&entry->lock);
265+
266+ /* have the wait amount, check how much is allowed */
267+ if(cookie_valid)
268+ tree = &infra->wait_limits_cookie_netblock;
269+ else tree = &infra->wait_limits_netblock;
270+ w = (struct wait_limit_netblock_info*)addr_tree_lookup(tree,
271+ &rep->client_addr, rep->client_addrlen);
272+ if(w) {
273+ if(w->limit != -1 && mesh_wait > w->limit)
274+ return 0;
275+ } else {
276+ /* if there is no IP netblock specific information,
277+ * use the configured value. */
278+ if(mesh_wait > (cookie_valid?cfg->wait_limit_cookie:
279+ cfg->wait_limit))
280+ return 0;
281+ }
282+ }
283+ return 1;
284+}
285+
286+void infra_wait_limit_inc(struct infra_cache* infra, struct comm_reply* rep,
287+ time_t timenow, struct config_file* cfg)
288+{
289+ struct lruhash_entry* entry;
290+ if(cfg->wait_limit == 0)
291+ return;
292+
293+ /* Find it */
294+ entry = infra_find_ip_ratedata(infra, &rep->client_addr,
295+ rep->client_addrlen, 1);
296+ if(entry) {
297+ struct rate_data* d = (struct rate_data*)entry->data;
298+ d->mesh_wait++;
299+ lock_rw_unlock(&entry->lock);
300+ return;
301+ }
302+
303+ /* Create it */
304+ infra_ip_create_ratedata(infra, &rep->client_addr,
305+ rep->client_addrlen, timenow, 1);
306+}
307+
308+void infra_wait_limit_dec(struct infra_cache* infra, struct comm_reply* rep,
309+ struct config_file* cfg)
310+{
311+ struct lruhash_entry* entry;
312+ if(cfg->wait_limit == 0)
313+ return;
314+
315+ entry = infra_find_ip_ratedata(infra, &rep->client_addr,
316+ rep->client_addrlen, 1);
317+ if(entry) {
318+ struct rate_data* d = (struct rate_data*)entry->data;
319+ if(d->mesh_wait > 0)
320+ d->mesh_wait--;
321+ lock_rw_unlock(&entry->lock);
322+ }
323+}
324diff --git a/services/cache/infra.h b/services/cache/infra.h
325index 525073bf..ee6f384d 100644
326--- a/services/cache/infra.h
327+++ b/services/cache/infra.h
328@@ -122,6 +122,10 @@ struct infra_cache {
329 rbtree_type domain_limits;
330 /** hash table with query rates per client ip: ip_rate_key, ip_rate_data */
331 struct slabhash* client_ip_rates;
332+ /** tree of addr_tree_node, with wait_limit_netblock_info information */
333+ rbtree_type wait_limits_netblock;
334+ /** tree of addr_tree_node, with wait_limit_netblock_info information */
335+ rbtree_type wait_limits_cookie_netblock;
336 };
337
338 /** ratelimit, unless overridden by domain_limits, 0 is off */
339@@ -184,10 +188,22 @@ struct rate_data {
340 /** what the timestamp is of the qps array members, counter is
341 * valid for that timestamp. Usually now and now-1. */
342 time_t timestamp[RATE_WINDOW];
343+ /** the number of queries waiting in the mesh */
344+ int mesh_wait;
345 };
346
347 #define ip_rate_data rate_data
348
349+/**
350+ * Data to store the configuration per netblock for the wait limit
351+ */
352+struct wait_limit_netblock_info {
353+ /** The addr tree node, this must be first. */
354+ struct addr_tree_node node;
355+ /** the limit on the amount */
356+ int limit;
357+};
358+
359 /** infra host cache default hash lookup size */
360 #define INFRA_HOST_STARTSIZE 32
361 /** bytes per zonename reserved in the hostcache, dnamelen(zonename.com.) */
362@@ -474,4 +490,16 @@ void ip_rate_delkeyfunc(void* d, void* arg);
363 /* delete data */
364 #define ip_rate_deldatafunc rate_deldatafunc
365
366+/** See if the IP address can have another reply in the wait limit */
367+int infra_wait_limit_allowed(struct infra_cache* infra, struct comm_reply* rep,
368+ int cookie_valid, struct config_file* cfg);
369+
370+/** Increment number of waiting replies for IP */
371+void infra_wait_limit_inc(struct infra_cache* infra, struct comm_reply* rep,
372+ time_t timenow, struct config_file* cfg);
373+
374+/** Decrement number of waiting replies for IP */
375+void infra_wait_limit_dec(struct infra_cache* infra, struct comm_reply* rep,
376+ struct config_file* cfg);
377+
378 #endif /* SERVICES_CACHE_INFRA_H */
379diff --git a/services/mesh.c b/services/mesh.c
380index 47cfb042..2b06957d 100644
381--- a/services/mesh.c
382+++ b/services/mesh.c
383@@ -47,6 +47,7 @@
384 #include "services/outbound_list.h"
385 #include "services/cache/dns.h"
386 #include "services/cache/rrset.h"
387+#include "services/cache/infra.h"
388 #include "util/log.h"
389 #include "util/net_help.h"
390 #include "util/module.h"
391@@ -415,6 +416,14 @@ void mesh_new_client(struct mesh_area* mesh, struct query_info* qinfo,
392 if(rep->c->tcp_req_info) {
393 r_buffer = rep->c->tcp_req_info->spool_buffer;
394 }
395+ if(!infra_wait_limit_allowed(mesh->env->infra_cache, rep,
396+ edns->cookie_valid, mesh->env->cfg)) {
397+ verbose(VERB_ALGO, "Too many queries waiting from the IP. "
398+ "dropping incoming query.");
399+ comm_point_drop_reply(rep);
400+ mesh->stats_dropped++;
401+ return;
402+ }
403 if(!unique)
404 s = mesh_area_find(mesh, cinfo, qinfo, qflags&(BIT_RD|BIT_CD), 0, 0);
405 /* does this create a new reply state? */
406@@ -511,6 +520,8 @@ void mesh_new_client(struct mesh_area* mesh, struct query_info* qinfo,
407 log_err("mesh_new_client: out of memory initializing serve expired");
408 goto servfail_mem;
409 }
410+ infra_wait_limit_inc(mesh->env->infra_cache, rep, *mesh->env->now,
411+ mesh->env->cfg);
412 /* update statistics */
413 if(was_detached) {
414 log_assert(mesh->num_detached_states > 0);
415@@ -930,6 +941,8 @@ mesh_state_cleanup(struct mesh_state* mstate)
416 * takes no time and also it does not do the mesh accounting */
417 mstate->reply_list = NULL;
418 for(; rep; rep=rep->next) {
419+ infra_wait_limit_dec(mesh->env->infra_cache,
420+ &rep->query_reply, mesh->env->cfg);
421 comm_point_drop_reply(&rep->query_reply);
422 log_assert(mesh->num_reply_addrs > 0);
423 mesh->num_reply_addrs--;
424@@ -1413,6 +1426,8 @@ mesh_send_reply(struct mesh_state* m, int rcode, struct reply_info* rep,
425 comm_point_send_reply(&r->query_reply);
426 m->reply_list = rlist;
427 }
428+ infra_wait_limit_dec(m->s.env->infra_cache, &r->query_reply,
429+ m->s.env->cfg);
430 /* account */
431 log_assert(m->s.env->mesh->num_reply_addrs > 0);
432 m->s.env->mesh->num_reply_addrs--;
433@@ -1470,6 +1485,28 @@ void mesh_query_done(struct mesh_state* mstate)
434 }
435 }
436 for(r = mstate->reply_list; r; r = r->next) {
437+ struct timeval old;
438+ timeval_subtract(&old, mstate->s.env->now_tv, &r->start_time);
439+ if(mstate->s.env->cfg->discard_timeout != 0 &&
440+ ((int)old.tv_sec)*1000+((int)old.tv_usec)/1000 >
441+ mstate->s.env->cfg->discard_timeout) {
442+ /* Drop the reply, it is too old */
443+ /* briefly set the reply_list to NULL, so that the
444+ * tcp req info cleanup routine that calls the mesh
445+ * to deregister the meshstate for it is not done
446+ * because the list is NULL and also accounting is not
447+ * done there, but instead we do that here. */
448+ struct mesh_reply* reply_list = mstate->reply_list;
449+ verbose(VERB_ALGO, "drop reply, it is older than discard-timeout");
450+ infra_wait_limit_dec(mstate->s.env->infra_cache,
451+ &r->query_reply, mstate->s.env->cfg);
452+ mstate->reply_list = NULL;
453+ comm_point_drop_reply(&r->query_reply);
454+ mstate->reply_list = reply_list;
455+ mstate->s.env->mesh->stats_dropped++;
456+ continue;
457+ }
458+
459 i++;
460 tv = r->start_time;
461
462@@ -1493,6 +1530,8 @@ void mesh_query_done(struct mesh_state* mstate)
463 * because the list is NULL and also accounting is not
464 * done there, but instead we do that here. */
465 struct mesh_reply* reply_list = mstate->reply_list;
466+ infra_wait_limit_dec(mstate->s.env->infra_cache,
467+ &r->query_reply, mstate->s.env->cfg);
468 mstate->reply_list = NULL;
469 comm_point_drop_reply(&r->query_reply);
470 mstate->reply_list = reply_list;
471@@ -2025,6 +2064,8 @@ void mesh_state_remove_reply(struct mesh_area* mesh, struct mesh_state* m,
472 /* delete it, but allocated in m region */
473 log_assert(mesh->num_reply_addrs > 0);
474 mesh->num_reply_addrs--;
475+ infra_wait_limit_dec(mesh->env->infra_cache,
476+ &n->query_reply, mesh->env->cfg);
477
478 /* prev = prev; */
479 n = n->next;
480@@ -2165,6 +2206,28 @@ mesh_serve_expired_callback(void* arg)
481 log_dns_msg("Serve expired lookup", &qstate->qinfo, msg->rep);
482
483 for(r = mstate->reply_list; r; r = r->next) {
484+ struct timeval old;
485+ timeval_subtract(&old, mstate->s.env->now_tv, &r->start_time);
486+ if(mstate->s.env->cfg->discard_timeout != 0 &&
487+ ((int)old.tv_sec)*1000+((int)old.tv_usec)/1000 >
488+ mstate->s.env->cfg->discard_timeout) {
489+ /* Drop the reply, it is too old */
490+ /* briefly set the reply_list to NULL, so that the
491+ * tcp req info cleanup routine that calls the mesh
492+ * to deregister the meshstate for it is not done
493+ * because the list is NULL and also accounting is not
494+ * done there, but instead we do that here. */
495+ struct mesh_reply* reply_list = mstate->reply_list;
496+ verbose(VERB_ALGO, "drop reply, it is older than discard-timeout");
497+ infra_wait_limit_dec(mstate->s.env->infra_cache,
498+ &r->query_reply, mstate->s.env->cfg);
499+ mstate->reply_list = NULL;
500+ comm_point_drop_reply(&r->query_reply);
501+ mstate->reply_list = reply_list;
502+ mstate->s.env->mesh->stats_dropped++;
503+ continue;
504+ }
505+
506 i++;
507 tv = r->start_time;
508
509@@ -2192,6 +2255,8 @@ mesh_serve_expired_callback(void* arg)
510 r, r_buffer, prev, prev_buffer);
511 if(r->query_reply.c->tcp_req_info)
512 tcp_req_info_remove_mesh_state(r->query_reply.c->tcp_req_info, mstate);
513+ infra_wait_limit_dec(mstate->s.env->infra_cache,
514+ &r->query_reply, mstate->s.env->cfg);
515 prev = r;
516 prev_buffer = r_buffer;
517 }
518diff --git a/testdata/doh_downstream.tdir/doh_downstream.conf b/testdata/doh_downstream.tdir/doh_downstream.conf
519index f0857bb5..222c2159 100644
520--- a/testdata/doh_downstream.tdir/doh_downstream.conf
521+++ b/testdata/doh_downstream.tdir/doh_downstream.conf
522@@ -11,6 +11,7 @@ server:
523 chroot: ""
524 username: ""
525 do-not-query-localhost: no
526+ discard-timeout: 3000 # testns uses sleep=2
527 http-query-buffer-size: 1G
528 http-response-buffer-size: 1G
529 http-max-streams: 200
530diff --git a/testdata/doh_downstream_notls.tdir/doh_downstream_notls.conf b/testdata/doh_downstream_notls.tdir/doh_downstream_notls.conf
531index bdca4564..161c3555 100644
532--- a/testdata/doh_downstream_notls.tdir/doh_downstream_notls.conf
533+++ b/testdata/doh_downstream_notls.tdir/doh_downstream_notls.conf
534@@ -11,6 +11,7 @@ server:
535 chroot: ""
536 username: ""
537 do-not-query-localhost: no
538+ discard-timeout: 3000 # testns uses sleep=2
539 http-query-buffer-size: 1G
540 http-response-buffer-size: 1G
541 http-max-streams: 200
542diff --git a/testdata/doh_downstream_post.tdir/doh_downstream_post.conf b/testdata/doh_downstream_post.tdir/doh_downstream_post.conf
543index f0857bb5..222c2159 100644
544--- a/testdata/doh_downstream_post.tdir/doh_downstream_post.conf
545+++ b/testdata/doh_downstream_post.tdir/doh_downstream_post.conf
546@@ -11,6 +11,7 @@ server:
547 chroot: ""
548 username: ""
549 do-not-query-localhost: no
550+ discard-timeout: 3000 # testns uses sleep=2
551 http-query-buffer-size: 1G
552 http-response-buffer-size: 1G
553 http-max-streams: 200
554diff --git a/testdata/fwd_three_service.tdir/fwd_three_service.conf b/testdata/fwd_three_service.tdir/fwd_three_service.conf
555index 05fafe01..d6c9a205 100644
556--- a/testdata/fwd_three_service.tdir/fwd_three_service.conf
557+++ b/testdata/fwd_three_service.tdir/fwd_three_service.conf
558@@ -11,6 +11,7 @@ server:
559 num-queries-per-thread: 1024
560 use-syslog: no
561 do-not-query-localhost: no
562+ discard-timeout: 3000 # testns uses sleep=2
563 forward-zone:
564 name: "."
565 forward-addr: "127.0.0.1@@TOPORT@"
566diff --git a/testdata/iter_ghost_timewindow.rpl b/testdata/iter_ghost_timewindow.rpl
567index 566be82a..9e304628 100644
568--- a/testdata/iter_ghost_timewindow.rpl
569+++ b/testdata/iter_ghost_timewindow.rpl
570@@ -3,6 +3,7 @@ server:
571 target-fetch-policy: "0 0 0 0 0"
572 qname-minimisation: "no"
573 minimal-responses: no
574+ discard-timeout: 86400
575
576 stub-zone:
577 name: "."
578diff --git a/testdata/ssl_req_order.tdir/ssl_req_order.conf b/testdata/ssl_req_order.tdir/ssl_req_order.conf
579index 3b2e2b1b..ec39d3ab 100644
580--- a/testdata/ssl_req_order.tdir/ssl_req_order.conf
581+++ b/testdata/ssl_req_order.tdir/ssl_req_order.conf
582@@ -9,6 +9,7 @@ server:
583 chroot: ""
584 username: ""
585 do-not-query-localhost: no
586+ discard-timeout: 3000 # testns uses sleep=2
587 ssl-port: @PORT@
588 ssl-service-key: "unbound_server.key"
589 ssl-service-pem: "unbound_server.pem"
590diff --git a/testdata/tcp_req_order.tdir/tcp_req_order.conf b/testdata/tcp_req_order.tdir/tcp_req_order.conf
591index 40d6f55c..b2804e8e 100644
592--- a/testdata/tcp_req_order.tdir/tcp_req_order.conf
593+++ b/testdata/tcp_req_order.tdir/tcp_req_order.conf
594@@ -9,6 +9,7 @@ server:
595 chroot: ""
596 username: ""
597 do-not-query-localhost: no
598+ discard-timeout: 3000 # testns uses sleep=2
599
600 local-zone: "example.net" static
601 local-data: "www1.example.net. IN A 1.2.3.1"
602diff --git a/testdata/tcp_sigpipe.tdir/tcp_sigpipe.conf b/testdata/tcp_sigpipe.tdir/tcp_sigpipe.conf
603index 384f16b0..4f1ff9b0 100644
604--- a/testdata/tcp_sigpipe.tdir/tcp_sigpipe.conf
605+++ b/testdata/tcp_sigpipe.tdir/tcp_sigpipe.conf
606@@ -1,5 +1,5 @@
607 server:
608- verbosity: 2
609+ verbosity: 4
610 # num-threads: 1
611 interface: 127.0.0.1
612 port: @PORT@
613@@ -9,6 +9,7 @@ server:
614 chroot: ""
615 username: ""
616 do-not-query-localhost: no
617+ discard-timeout: 3000 # testns uses sleep=2
618
619 forward-zone:
620 name: "."
621diff --git a/util/config_file.c b/util/config_file.c
622index 26185da0..147f41e8 100644
623--- a/util/config_file.c
624+++ b/util/config_file.c
625@@ -308,6 +308,11 @@ config_create(void)
626 cfg->minimal_responses = 1;
627 cfg->rrset_roundrobin = 1;
628 cfg->unknown_server_time_limit = 376;
629+ cfg->discard_timeout = 1900; /* msec */
630+ cfg->wait_limit = 1000;
631+ cfg->wait_limit_cookie = 10000;
632+ cfg->wait_limit_netblock = NULL;
633+ cfg->wait_limit_cookie_netblock = NULL;
634 cfg->max_udp_size = 1232; /* value taken from edns_buffer_size */
635 if(!(cfg->server_key_file = strdup(RUN_DIR"/unbound_server.key")))
636 goto error_exit;
637@@ -722,6 +727,9 @@ int config_set_option(struct config_file* cfg, const char* opt,
638 else S_YNO("minimal-responses:", minimal_responses)
639 else S_YNO("rrset-roundrobin:", rrset_roundrobin)
640 else S_NUMBER_OR_ZERO("unknown-server-time-limit:", unknown_server_time_limit)
641+ else S_NUMBER_OR_ZERO("discard-timeout:", discard_timeout)
642+ else S_NUMBER_OR_ZERO("wait-limit:", wait_limit)
643+ else S_NUMBER_OR_ZERO("wait-limit-cookie:", wait_limit_cookie)
644 else S_STRLIST("local-data:", local_data)
645 else S_YNO("unblock-lan-zones:", unblock_lan_zones)
646 else S_YNO("insecure-lan-zones:", insecure_lan_zones)
647@@ -1201,6 +1209,11 @@ config_get_option(struct config_file* cfg, const char* opt,
648 else O_YNO(opt, "minimal-responses", minimal_responses)
649 else O_YNO(opt, "rrset-roundrobin", rrset_roundrobin)
650 else O_DEC(opt, "unknown-server-time-limit", unknown_server_time_limit)
651+ else O_DEC(opt, "discard-timeout", discard_timeout)
652+ else O_DEC(opt, "wait-limit", wait_limit)
653+ else O_DEC(opt, "wait-limit-cookie", wait_limit_cookie)
654+ else O_LS2(opt, "wait-limit-netblock", wait_limit_netblock)
655+ else O_LS2(opt, "wait-limit-cookie-netblock", wait_limit_cookie_netblock)
656 #ifdef CLIENT_SUBNET
657 else O_LST(opt, "send-client-subnet", client_subnet)
658 else O_LST(opt, "client-subnet-zone", client_subnet_zone)
659@@ -1671,6 +1684,8 @@ config_delete(struct config_file* cfg)
660 config_deltrplstrlist(cfg->interface_tag_actions);
661 config_deltrplstrlist(cfg->interface_tag_datas);
662 config_delstrlist(cfg->control_ifs.first);
663+ config_deldblstrlist(cfg->wait_limit_netblock);
664+ config_deldblstrlist(cfg->wait_limit_cookie_netblock);
665 free(cfg->server_key_file);
666 free(cfg->server_cert_file);
667 free(cfg->control_key_file);
668diff --git a/util/config_file.h b/util/config_file.h
669index 49110983..7ded3c24 100644
670--- a/util/config_file.h
671+++ b/util/config_file.h
672@@ -535,6 +535,21 @@ struct config_file {
673 /* wait time for unknown server in msec */
674 int unknown_server_time_limit;
675
676+ /** Wait time to drop recursion replies */
677+ int discard_timeout;
678+
679+ /** Wait limit for number of replies per IP address */
680+ int wait_limit;
681+
682+ /** Wait limit for number of replies per IP address with cookie */
683+ int wait_limit_cookie;
684+
685+ /** wait limit per netblock */
686+ struct config_str2list* wait_limit_netblock;
687+
688+ /** wait limit with cookie per netblock */
689+ struct config_str2list* wait_limit_cookie_netblock;
690+
691 /* maximum UDP response size */
692 size_t max_udp_size;
693
694diff --git a/util/configlexer.lex b/util/configlexer.lex
695index e1ab76e2..7455f50c 100644
696--- a/util/configlexer.lex
697+++ b/util/configlexer.lex
698@@ -463,6 +463,11 @@ domain-insecure{COLON} { YDVAR(1, VAR_DOMAIN_INSECURE) }
699 minimal-responses{COLON} { YDVAR(1, VAR_MINIMAL_RESPONSES) }
700 rrset-roundrobin{COLON} { YDVAR(1, VAR_RRSET_ROUNDROBIN) }
701 unknown-server-time-limit{COLON} { YDVAR(1, VAR_UNKNOWN_SERVER_TIME_LIMIT) }
702+discard-timeout{COLON} { YDVAR(1, VAR_DISCARD_TIMEOUT) }
703+wait-limit{COLON} { YDVAR(1, VAR_WAIT_LIMIT) }
704+wait-limit-cookie{COLON} { YDVAR(1, VAR_WAIT_LIMIT_COOKIE) }
705+wait-limit-netblock{COLON} { YDVAR(1, VAR_WAIT_LIMIT_NETBLOCK) }
706+wait-limit-cookie-netblock{COLON} { YDVAR(1, VAR_WAIT_LIMIT_COOKIE_NETBLOCK) }
707 max-udp-size{COLON} { YDVAR(1, VAR_MAX_UDP_SIZE) }
708 dns64-prefix{COLON} { YDVAR(1, VAR_DNS64_PREFIX) }
709 dns64-synthall{COLON} { YDVAR(1, VAR_DNS64_SYNTHALL) }
710diff --git a/util/configparser.y b/util/configparser.y
711index 0e4cd596..7d95690e 100644
712--- a/util/configparser.y
713+++ b/util/configparser.y
714@@ -188,6 +188,8 @@ extern struct config_parser_state* cfg_parser;
715 %token VAR_ANSWER_COOKIE VAR_COOKIE_SECRET VAR_IP_RATELIMIT_COOKIE
716 %token VAR_FORWARD_NO_CACHE VAR_STUB_NO_CACHE VAR_LOG_SERVFAIL VAR_DENY_ANY
717 %token VAR_UNKNOWN_SERVER_TIME_LIMIT VAR_LOG_TAG_QUERYREPLY
718+%token VAR_DISCARD_TIMEOUT VAR_WAIT_LIMIT VAR_WAIT_LIMIT_COOKIE
719+%token VAR_WAIT_LIMIT_NETBLOCK VAR_WAIT_LIMIT_COOKIE_NETBLOCK
720 %token VAR_STREAM_WAIT_SIZE VAR_TLS_CIPHERS VAR_TLS_CIPHERSUITES VAR_TLS_USE_SNI
721 %token VAR_IPSET VAR_IPSET_NAME_V4 VAR_IPSET_NAME_V6
722 %token VAR_TLS_SESSION_TICKET_KEYS VAR_RPZ VAR_TAGS VAR_RPZ_ACTION_OVERRIDE
723@@ -325,6 +327,8 @@ content_server: server_num_threads | server_verbosity | server_port |
724 server_fast_server_permil | server_fast_server_num | server_tls_win_cert |
725 server_tcp_connection_limit | server_log_servfail | server_deny_any |
726 server_unknown_server_time_limit | server_log_tag_queryreply |
727+ server_discard_timeout | server_wait_limit | server_wait_limit_cookie |
728+ server_wait_limit_netblock | server_wait_limit_cookie_netblock |
729 server_stream_wait_size | server_tls_ciphers |
730 server_tls_ciphersuites | server_tls_session_ticket_keys |
731 server_answer_cookie | server_cookie_secret | server_ip_ratelimit_cookie |
732@@ -2366,6 +2370,57 @@ server_unknown_server_time_limit: VAR_UNKNOWN_SERVER_TIME_LIMIT STRING_ARG
733 free($2);
734 }
735 ;
736+server_discard_timeout: VAR_DISCARD_TIMEOUT STRING_ARG
737+ {
738+ OUTYY(("P(server_discard_timeout:%s)\n", $2));
739+ cfg_parser->cfg->discard_timeout = atoi($2);
740+ free($2);
741+ }
742+ ;
743+server_wait_limit: VAR_WAIT_LIMIT STRING_ARG
744+ {
745+ OUTYY(("P(server_wait_limit:%s)\n", $2));
746+ cfg_parser->cfg->wait_limit = atoi($2);
747+ free($2);
748+ }
749+ ;
750+server_wait_limit_cookie: VAR_WAIT_LIMIT_COOKIE STRING_ARG
751+ {
752+ OUTYY(("P(server_wait_limit_cookie:%s)\n", $2));
753+ cfg_parser->cfg->wait_limit_cookie = atoi($2);
754+ free($2);
755+ }
756+ ;
757+server_wait_limit_netblock: VAR_WAIT_LIMIT_NETBLOCK STRING_ARG STRING_ARG
758+ {
759+ OUTYY(("P(server_wait_limit_netblock:%s %s)\n", $2, $3));
760+ if(atoi($3) == 0 && strcmp($3, "0") != 0) {
761+ yyerror("number expected");
762+ free($2);
763+ free($3);
764+ } else {
765+ if(!cfg_str2list_insert(&cfg_parser->cfg->
766+ wait_limit_netblock, $2, $3))
767+ fatal_exit("out of memory adding "
768+ "wait-limit-netblock");
769+ }
770+ }
771+ ;
772+server_wait_limit_cookie_netblock: VAR_WAIT_LIMIT_COOKIE_NETBLOCK STRING_ARG STRING_ARG
773+ {
774+ OUTYY(("P(server_wait_limit_cookie_netblock:%s %s)\n", $2, $3));
775+ if(atoi($3) == 0 && strcmp($3, "0") != 0) {
776+ yyerror("number expected");
777+ free($2);
778+ free($3);
779+ } else {
780+ if(!cfg_str2list_insert(&cfg_parser->cfg->
781+ wait_limit_cookie_netblock, $2, $3))
782+ fatal_exit("out of memory adding "
783+ "wait-limit-cookie-netblock");
784+ }
785+ }
786+ ;
787 server_max_udp_size: VAR_MAX_UDP_SIZE STRING_ARG
788 {
789 OUTYY(("P(server_max_udp_size:%s)\n", $2));
790--
7912.34.1
792
diff --git a/meta-networking/recipes-support/unbound/unbound/CVE-2025-11411.patch b/meta-networking/recipes-support/unbound/unbound/CVE-2025-11411.patch
new file mode 100644
index 0000000000..a653090770
--- /dev/null
+++ b/meta-networking/recipes-support/unbound/unbound/CVE-2025-11411.patch
@@ -0,0 +1,48 @@
1From 98fac0b396e1e85a6345baa59fc178b1f51759b8 Mon Sep 17 00:00:00 2001
2From: Patrick Vogelaar <patrick.vogelaar@belden.com>
3Date: Wed, 29 Oct 2025 13:33:23 +0100
4Subject: [PATCH] Fix CVE-2025-11411 (possible domain hijacking attack)
5
6This fixes CVE-2025-11411 by applying the minimal patch [1] listed in [2]
7
8[1] https://nlnetlabs.nl/downloads/unbound/patch_CVE-2025-11411.diff
9[2] https://www.nlnetlabs.nl/downloads/unbound/CVE-2025-11411.txt
10
11CVE: CVE-2025-11411
12Upstream-Status: Backport [minimal backport of https://github.com/NLnetLabs/unbound/commit/a33f0638e1dacf2633cf2292078a674576bca852]
13
14Signed-off-by: Patrick Vogelaar <patrick.vogelaar@belden.com>
15---
16 iterator/iter_scrub.c | 16 ++++++++++++++++
17 1 file changed, 16 insertions(+)
18
19diff --git a/iterator/iter_scrub.c b/iterator/iter_scrub.c
20index 48867e50..5beaa048 100644
21--- a/iterator/iter_scrub.c
22+++ b/iterator/iter_scrub.c
23@@ -571,6 +571,22 @@ scrub_normalize(sldns_buffer* pkt, struct msg_parse* msg,
24 "RRset:", pkt, msg, prev, &rrset);
25 continue;
26 }
27+ /* If the NS set is a promiscuous NS set, scrub that
28+ * to remove potential for poisonous contents that
29+ * affects other names in the same zone. Remove
30+ * promiscuous NS sets in positive answers, that
31+ * thus have records in the answer section. Nodata
32+ * and nxdomain promiscuous NS sets have been removed
33+ * already. Since the NS rrset is scrubbed, its
34+ * address records are also not marked to be allowed
35+ * and are removed later. */
36+ if(FLAGS_GET_RCODE(msg->flags) == LDNS_RCODE_NOERROR &&
37+ msg->an_rrsets != 0 &&
38+ 1 /* env->cfg->iter_scrub_promiscuous */) {
39+ remove_rrset("normalize: removing promiscuous "
40+ "RRset:", pkt, msg, prev, &rrset);
41+ continue;
42+ }
43 if(nsset == NULL) {
44 nsset = rrset;
45 } else {
46--
472.34.1
48
diff --git a/meta-networking/recipes-support/unbound/unbound_1.19.3.bb b/meta-networking/recipes-support/unbound/unbound_1.19.3.bb
index 6f54038c6c..e48d76ea59 100644
--- a/meta-networking/recipes-support/unbound/unbound_1.19.3.bb
+++ b/meta-networking/recipes-support/unbound/unbound_1.19.3.bb
@@ -11,6 +11,8 @@ LIC_FILES_CHKSUM = "file://LICENSE;md5=5308494bc0590c0cb036afd781d78f06"
11 11
12SRC_URI = "git://github.com/NLnetLabs/unbound.git;protocol=https;branch=branch-1.19.3 \ 12SRC_URI = "git://github.com/NLnetLabs/unbound.git;protocol=https;branch=branch-1.19.3 \
13 file://CVE-2024-8508.patch \ 13 file://CVE-2024-8508.patch \
14 file://CVE-2024-33655.patch \
15 file://CVE-2025-11411.patch \
14 " 16 "
15SRCREV = "48b6c60a24e9a5d6d369a7a37c9fe2a767f26abd" 17SRCREV = "48b6c60a24e9a5d6d369a7a37c9fe2a767f26abd"
16 18