Compare commits
No commits in common. "c8" and "c8-beta" have entirely different histories.
@ -1,432 +0,0 @@
|
||||
commit 244dba98c6d93c84a362177bb4ef629b6b3345d0
|
||||
Author: Tomas Korbar <tkorbar@redhat.com>
|
||||
Date: Tue Aug 19 11:03:38 2025 +0200
|
||||
|
||||
Backport of upstream commits
|
||||
|
||||
45d8a2435e8200e892b82b6a04c7ddfb07a4165a
|
||||
eb1fe15ca80b6bc43cd6bfdf309ec6c590aff811
|
||||
|
||||
diff --git a/src/cache.c b/src/cache.c
|
||||
index 8b1b560..612ad22 100644
|
||||
--- a/src/cache.c
|
||||
+++ b/src/cache.c
|
||||
@@ -77,17 +77,20 @@ static void cache_link(struct crec *crecp);
|
||||
static void rehash(int size);
|
||||
static void cache_hash(struct crec *crecp);
|
||||
|
||||
-static unsigned int next_uid(void)
|
||||
+void next_uid(struct crec *crecp)
|
||||
{
|
||||
static unsigned int uid = 0;
|
||||
|
||||
- uid++;
|
||||
-
|
||||
- /* uid == 0 used to indicate CNAME to interface name. */
|
||||
- if (uid == SRC_INTERFACE)
|
||||
- uid++;
|
||||
+ if (crecp->uid == UID_NONE)
|
||||
+ {
|
||||
+ uid++;
|
||||
|
||||
- return uid;
|
||||
+ /* uid == 0 used to indicate CNAME to interface name. */
|
||||
+ if (uid == UID_NONE)
|
||||
+ uid++;
|
||||
+
|
||||
+ crecp->uid = uid;
|
||||
+ }
|
||||
}
|
||||
|
||||
void cache_init(void)
|
||||
@@ -105,7 +108,7 @@ void cache_init(void)
|
||||
{
|
||||
cache_link(crecp);
|
||||
crecp->flags = 0;
|
||||
- crecp->uid = next_uid();
|
||||
+ crecp->uid = UID_NONE;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -204,7 +207,7 @@ static void cache_free(struct crec *crecp)
|
||||
{
|
||||
crecp->flags &= ~F_FORWARD;
|
||||
crecp->flags &= ~F_REVERSE;
|
||||
- crecp->uid = next_uid(); /* invalidate CNAMES pointing to this. */
|
||||
+ crecp->uid = UID_NONE; /* invalidate CNAMES pointing to this. */
|
||||
|
||||
if (cache_tail)
|
||||
cache_tail->next = crecp;
|
||||
@@ -322,7 +325,8 @@ static int is_expired(time_t now, struct crec *crecp)
|
||||
return 1;
|
||||
}
|
||||
|
||||
-static struct crec *cache_scan_free(char *name, struct all_addr *addr, time_t now, unsigned short flags)
|
||||
+static struct crec *cache_scan_free(char *name, struct all_addr *addr, time_t now, unsigned short flags,
|
||||
+ struct crec **target_crec, unsigned int *target_uid)
|
||||
{
|
||||
/* Scan and remove old entries.
|
||||
If (flags & F_FORWARD) then remove any forward entries for name and any expired
|
||||
@@ -335,7 +339,10 @@ static struct crec *cache_scan_free(char *name, struct all_addr *addr, time_t no
|
||||
to a cache entry if the name exists in the cache as a HOSTS or DHCP entry (these are never deleted)
|
||||
|
||||
We take advantage of the fact that hash chains have stuff in the order <reverse>,<other>,<immortal>
|
||||
- so that when we hit an entry which isn't reverse and is immortal, we're done. */
|
||||
+ so that when we hit an entry which isn't reverse and is immortal, we're done.
|
||||
+
|
||||
+ If we free a crec which is a CNAME target, return the entry and uid in target_crec and target_uid.
|
||||
+ This entry will get re-used with the same name, to preserve CNAMEs. */
|
||||
|
||||
struct crec *crecp, **up;
|
||||
|
||||
@@ -343,17 +350,6 @@ static struct crec *cache_scan_free(char *name, struct all_addr *addr, time_t no
|
||||
{
|
||||
for (up = hash_bucket(name), crecp = *up; crecp; crecp = crecp->hash_next)
|
||||
{
|
||||
- if (is_expired(now, crecp) || is_outdated_cname_pointer(crecp))
|
||||
- {
|
||||
- *up = crecp->hash_next;
|
||||
- if (!(crecp->flags & (F_HOSTS | F_DHCP | F_CONFIG)))
|
||||
- {
|
||||
- cache_unlink(crecp);
|
||||
- cache_free(crecp);
|
||||
- }
|
||||
- continue;
|
||||
- }
|
||||
-
|
||||
if ((crecp->flags & F_FORWARD) && hostname_isequal(cache_get_name(crecp), name))
|
||||
{
|
||||
/* Don't delete DNSSEC in favour of a CNAME, they can co-exist */
|
||||
@@ -363,6 +359,16 @@ static struct crec *cache_scan_free(char *name, struct all_addr *addr, time_t no
|
||||
if (crecp->flags & (F_HOSTS | F_DHCP | F_CONFIG))
|
||||
return crecp;
|
||||
*up = crecp->hash_next;
|
||||
+ /* If this record is for the name we're inserting and is the target
|
||||
+ of a CNAME record. Make the new record for the same name, in the same
|
||||
+ crec, with the same uid to avoid breaking the existing CNAME. */
|
||||
+ if (crecp->uid != UID_NONE)
|
||||
+ {
|
||||
+ if (target_crec)
|
||||
+ *target_crec = crecp;
|
||||
+ if (target_uid)
|
||||
+ *target_uid = crecp->uid;
|
||||
+ }
|
||||
cache_unlink(crecp);
|
||||
cache_free(crecp);
|
||||
continue;
|
||||
@@ -381,6 +387,18 @@ static struct crec *cache_scan_free(char *name, struct all_addr *addr, time_t no
|
||||
}
|
||||
#endif
|
||||
}
|
||||
+
|
||||
+ if (is_expired(now, crecp) || is_outdated_cname_pointer(crecp))
|
||||
+ {
|
||||
+ *up = crecp->hash_next;
|
||||
+ if (!(crecp->flags & (F_HOSTS | F_DHCP | F_CONFIG)))
|
||||
+ {
|
||||
+ cache_unlink(crecp);
|
||||
+ cache_free(crecp);
|
||||
+ }
|
||||
+ continue;
|
||||
+ }
|
||||
+
|
||||
up = &crecp->hash_next;
|
||||
}
|
||||
}
|
||||
@@ -447,11 +465,12 @@ void cache_start_insert(void)
|
||||
struct crec *cache_insert(char *name, struct all_addr *addr,
|
||||
time_t now, unsigned long ttl, unsigned short flags)
|
||||
{
|
||||
- struct crec *new;
|
||||
+ struct crec *new, *target_crec = NULL;
|
||||
union bigname *big_name = NULL;
|
||||
int freed_all = flags & F_REVERSE;
|
||||
int free_avail = 0;
|
||||
-
|
||||
+ unsigned int target_uid;
|
||||
+
|
||||
/* Don't log DNSSEC records here, done elsewhere */
|
||||
if (flags & (F_IPV4 | F_IPV6 | F_CNAME))
|
||||
{
|
||||
@@ -469,7 +488,7 @@ struct crec *cache_insert(char *name, struct all_addr *addr,
|
||||
|
||||
/* First remove any expired entries and entries for the name/address we
|
||||
are currently inserting. */
|
||||
- if ((new = cache_scan_free(name, addr, now, flags)))
|
||||
+ if ((new = cache_scan_free(name, addr, now, flags, &target_crec, &target_uid)))
|
||||
{
|
||||
/* We're trying to insert a record over one from
|
||||
/etc/hosts or DHCP, or other config. If the
|
||||
@@ -493,81 +512,89 @@ struct crec *cache_insert(char *name, struct all_addr *addr,
|
||||
}
|
||||
|
||||
/* Now get a cache entry from the end of the LRU list */
|
||||
- while (1) {
|
||||
- if (!(new = cache_tail)) /* no entries left - cache is too small, bail */
|
||||
- {
|
||||
- insert_error = 1;
|
||||
- return NULL;
|
||||
- }
|
||||
-
|
||||
- /* End of LRU list is still in use: if we didn't scan all the hash
|
||||
- chains for expired entries do that now. If we already tried that
|
||||
- then it's time to start spilling things. */
|
||||
-
|
||||
- if (new->flags & (F_FORWARD | F_REVERSE))
|
||||
- {
|
||||
- /* If free_avail set, we believe that an entry has been freed.
|
||||
- Bugs have been known to make this not true, resulting in
|
||||
- a tight loop here. If that happens, abandon the
|
||||
- insert. Once in this state, all inserts will probably fail. */
|
||||
- if (free_avail)
|
||||
- {
|
||||
- static int warned = 0;
|
||||
- if (!warned)
|
||||
- {
|
||||
- my_syslog(LOG_ERR, _("Internal error in cache."));
|
||||
- warned = 1;
|
||||
- }
|
||||
- insert_error = 1;
|
||||
- return NULL;
|
||||
- }
|
||||
-
|
||||
- if (freed_all)
|
||||
- {
|
||||
- struct all_addr free_addr = new->addr.addr;;
|
||||
+ if (!target_crec)
|
||||
+ while (1) {
|
||||
+ if (!(new = cache_tail)) /* no entries left - cache is too small, bail */
|
||||
+ {
|
||||
+ insert_error = 1;
|
||||
+ return NULL;
|
||||
+ }
|
||||
+
|
||||
+ /* Free entry at end of LRU list, use it. */
|
||||
+ if (!(new->flags & (F_FORWARD | F_REVERSE)))
|
||||
+ break;
|
||||
|
||||
+ /* End of LRU list is still in use: if we didn't scan all the hash
|
||||
+ chains for expired entries do that now. If we already tried that
|
||||
+ then it's time to start spilling things. */
|
||||
+
|
||||
+ /* If free_avail set, we believe that an entry has been freed.
|
||||
+ Bugs have been known to make this not true, resulting in
|
||||
+ a tight loop here. If that happens, abandon the
|
||||
+ insert. Once in this state, all inserts will probably fail. */
|
||||
+ if (free_avail)
|
||||
+ {
|
||||
+ static int warned = 0;
|
||||
+ if (!warned)
|
||||
+ {
|
||||
+ my_syslog(LOG_ERR, _("Internal error in cache."));
|
||||
+ warned = 1;
|
||||
+ }
|
||||
+ insert_error = 1;
|
||||
+ return NULL;
|
||||
+ }
|
||||
+
|
||||
+ if (freed_all)
|
||||
+ {
|
||||
+ struct all_addr free_addr = new->addr.addr;;
|
||||
+
|
||||
#ifdef HAVE_DNSSEC
|
||||
- /* For DNSSEC records, addr holds class. */
|
||||
- if (new->flags & (F_DS | F_DNSKEY))
|
||||
- free_addr.addr.dnssec.class = new->uid;
|
||||
+ /* For DNSSEC records, addr holds class. */
|
||||
+ if (new->flags & (F_DS | F_DNSKEY))
|
||||
+ free_addr.addr.dnssec.class = new->uid;
|
||||
#endif
|
||||
-
|
||||
- free_avail = 1; /* Must be free space now. */
|
||||
- cache_scan_free(cache_get_name(new), &free_addr, now, new->flags);
|
||||
- cache_live_freed++;
|
||||
- }
|
||||
- else
|
||||
- {
|
||||
- cache_scan_free(NULL, NULL, now, 0);
|
||||
- freed_all = 1;
|
||||
- }
|
||||
- continue;
|
||||
- }
|
||||
-
|
||||
- /* Check if we need to and can allocate extra memory for a long name.
|
||||
- If that fails, give up now, always succeed for DNSSEC records. */
|
||||
- if (name && (strlen(name) > SMALLDNAME-1))
|
||||
- {
|
||||
- if (big_free)
|
||||
- {
|
||||
- big_name = big_free;
|
||||
- big_free = big_free->next;
|
||||
- }
|
||||
- else if ((bignames_left == 0 && !(flags & (F_DS | F_DNSKEY))) ||
|
||||
- !(big_name = (union bigname *)whine_malloc(sizeof(union bigname))))
|
||||
- {
|
||||
- insert_error = 1;
|
||||
- return NULL;
|
||||
- }
|
||||
- else if (bignames_left != 0)
|
||||
- bignames_left--;
|
||||
-
|
||||
- }
|
||||
+
|
||||
+ free_avail = 1; /* Must be free space now. */
|
||||
+ cache_scan_free(cache_get_name(new), &free_addr, now, new->flags, NULL, NULL);
|
||||
+ cache_live_freed++;
|
||||
+ }
|
||||
+ else
|
||||
+ {
|
||||
+ cache_scan_free(NULL, NULL, now, 0, NULL, NULL);
|
||||
+ freed_all = 1;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ /* Check if we need to and can allocate extra memory for a long name.
|
||||
+ If that fails, give up now, always succeed for DNSSEC records. */
|
||||
+ if (name && (strlen(name) > SMALLDNAME-1))
|
||||
+ {
|
||||
+ if (big_free)
|
||||
+ {
|
||||
+ big_name = big_free;
|
||||
+ big_free = big_free->next;
|
||||
+ }
|
||||
+ else if ((bignames_left == 0 && !(flags & (F_DS | F_DNSKEY))) ||
|
||||
+ !(big_name = (union bigname *)whine_malloc(sizeof(union bigname))))
|
||||
+ {
|
||||
+ insert_error = 1;
|
||||
+ return NULL;
|
||||
+ }
|
||||
+ else if (bignames_left != 0)
|
||||
+ bignames_left--;
|
||||
+
|
||||
+ }
|
||||
|
||||
- /* Got the rest: finally grab entry. */
|
||||
- cache_unlink(new);
|
||||
- break;
|
||||
- }
|
||||
+ /* If we freed a cache entry for our name which was a CNAME target, use that.
|
||||
+ and preserve the uid, so that existing CNAMES are not broken. */
|
||||
+ if (target_crec)
|
||||
+ {
|
||||
+ new = target_crec;
|
||||
+ new->uid = target_uid;
|
||||
+ }
|
||||
+
|
||||
+ /* Got the rest: finally grab entry. */
|
||||
+ cache_unlink(new);
|
||||
|
||||
new->flags = flags;
|
||||
if (big_name)
|
||||
@@ -787,8 +814,9 @@ static void add_hosts_cname(struct crec *target)
|
||||
crec->ttd = a->ttl;
|
||||
crec->name.namep = a->alias;
|
||||
crec->addr.cname.target.cache = target;
|
||||
+ next_uid(target);
|
||||
crec->addr.cname.uid = target->uid;
|
||||
- crec->uid = next_uid();
|
||||
+ crec->uid = UID_NONE;
|
||||
cache_hash(crec);
|
||||
add_hosts_cname(crec); /* handle chains */
|
||||
}
|
||||
@@ -1071,7 +1099,7 @@ void cache_reload(void)
|
||||
cache->name.namep = a->alias;
|
||||
cache->addr.cname.target.int_name = intr;
|
||||
cache->addr.cname.uid = SRC_INTERFACE;
|
||||
- cache->uid = next_uid();
|
||||
+ cache->uid = UID_NONE;
|
||||
cache_hash(cache);
|
||||
add_hosts_cname(cache); /* handle chains */
|
||||
}
|
||||
@@ -1201,8 +1229,9 @@ static void add_dhcp_cname(struct crec *target, time_t ttd)
|
||||
aliasc->ttd = ttd;
|
||||
aliasc->name.namep = a->alias;
|
||||
aliasc->addr.cname.target.cache = target;
|
||||
+ next_uid(target);
|
||||
aliasc->addr.cname.uid = target->uid;
|
||||
- aliasc->uid = next_uid();
|
||||
+ aliasc->uid = UID_NONE;
|
||||
cache_hash(aliasc);
|
||||
add_dhcp_cname(aliasc, ttd);
|
||||
}
|
||||
@@ -1243,7 +1272,7 @@ void cache_add_dhcp_entry(char *host_name, int prot,
|
||||
}
|
||||
else if (!(crec->flags & F_DHCP))
|
||||
{
|
||||
- cache_scan_free(host_name, NULL, 0, crec->flags & (flags | F_CNAME | F_FORWARD));
|
||||
+ cache_scan_free(host_name, NULL, 0, crec->flags & (flags | F_CNAME | F_FORWARD), NULL, NULL);
|
||||
/* scan_free deletes all addresses associated with name */
|
||||
break;
|
||||
}
|
||||
@@ -1270,7 +1299,7 @@ void cache_add_dhcp_entry(char *host_name, int prot,
|
||||
if (crec->flags & F_NEG)
|
||||
{
|
||||
flags |= F_REVERSE;
|
||||
- cache_scan_free(NULL, (struct all_addr *)host_address, 0, flags);
|
||||
+ cache_scan_free(NULL, (struct all_addr *)host_address, 0, flags, NULL, NULL);
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -1290,7 +1319,7 @@ void cache_add_dhcp_entry(char *host_name, int prot,
|
||||
crec->ttd = ttd;
|
||||
crec->addr.addr = *host_address;
|
||||
crec->name.namep = host_name;
|
||||
- crec->uid = next_uid();
|
||||
+ crec->uid = UID_NONE;
|
||||
cache_hash(crec);
|
||||
|
||||
add_dhcp_cname(crec, ttd);
|
||||
diff --git a/src/dnsmasq.h b/src/dnsmasq.h
|
||||
index cf1c9c4..2d83a48 100644
|
||||
--- a/src/dnsmasq.h
|
||||
+++ b/src/dnsmasq.h
|
||||
@@ -471,8 +471,12 @@ struct crec {
|
||||
#define F_NOEXTRA (1u<<27)
|
||||
#define F_SERVFAIL (1u<<28)
|
||||
|
||||
+#define UID_NONE 0
|
||||
/* Values of uid in crecs with F_CONFIG bit set. */
|
||||
-#define SRC_INTERFACE 0
|
||||
+/* cname to uid SRC_INTERFACE are to interface names,
|
||||
+ so use UID_NONE for that to eliminate clashes with
|
||||
+ any other uid */
|
||||
+#define SRC_INTERFACE UID_NONE
|
||||
#define SRC_CONFIG 1
|
||||
#define SRC_HOSTS 2
|
||||
#define SRC_AH 3
|
||||
@@ -1147,6 +1151,7 @@ extern struct daemon {
|
||||
|
||||
/* cache.c */
|
||||
void cache_init(void);
|
||||
+void next_uid(struct crec *crecp);
|
||||
void log_query(unsigned int flags, char *name, struct all_addr *addr, char *arg);
|
||||
char *record_source(unsigned int index);
|
||||
char *querystr(char *desc, unsigned short type);
|
||||
diff --git a/src/rfc1035.c b/src/rfc1035.c
|
||||
index 48804b8..021661e 100644
|
||||
--- a/src/rfc1035.c
|
||||
+++ b/src/rfc1035.c
|
||||
@@ -739,6 +739,7 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t
|
||||
newc->addr.cname.uid = 1;
|
||||
if (cpp)
|
||||
{
|
||||
+ next_uid(newc);
|
||||
cpp->addr.cname.target.cache = newc;
|
||||
cpp->addr.cname.uid = newc->uid;
|
||||
}
|
||||
@@ -795,6 +796,7 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t
|
||||
newc = cache_insert(name, &addr, now, attl, flags | F_FORWARD | secflag);
|
||||
if (newc && cpp)
|
||||
{
|
||||
+ next_uid(newc);
|
||||
cpp->addr.cname.target.cache = newc;
|
||||
cpp->addr.cname.uid = newc->uid;
|
||||
}
|
||||
@@ -821,6 +823,7 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t
|
||||
newc = cache_insert(name, NULL, now, ttl ? ttl : cttl, F_FORWARD | F_NEG | flags | (secure ? F_DNSSECOK : 0));
|
||||
if (newc && cpp)
|
||||
{
|
||||
+ next_uid(newc);
|
||||
cpp->addr.cname.target.cache = newc;
|
||||
cpp->addr.cname.uid = newc->uid;
|
||||
}
|
||||
@ -1,207 +0,0 @@
|
||||
From b133ed74c5eb836f2a5add1f4de1c662b67f841a Mon Sep 17 00:00:00 2001
|
||||
From: =?UTF-8?q?Petr=20Men=C5=A1=C3=ADk?= <pemensik@redhat.com>
|
||||
Date: Mon, 11 Aug 2025 17:29:52 +0200
|
||||
Subject: [PATCH] Fix problem with DNS retries in 2.83/2.84.
|
||||
|
||||
Combination of upstream commits:
|
||||
- 141a26f979b4bc959d8e866a295e24f8cf456920
|
||||
- 305cb79c5754d5554729b18a2c06fe7ce699687a
|
||||
|
||||
And following commit
|
||||
|
||||
Subtly change behaviour on repeated DNS query.
|
||||
|
||||
This changes the behaviour introduced in
|
||||
141a26f979b4bc959d8e866a295e24f8cf456920
|
||||
|
||||
We re-introduce the distinction between a query
|
||||
which is retried from the same source, and one which is
|
||||
repeated from different sources.
|
||||
|
||||
In the later case, we still forward the query, to avoid
|
||||
problems when the reply to the first query is lost
|
||||
(see f8cf456920) but we suppress the behaviour
|
||||
that's used on a retry, when the query is sent to
|
||||
all available servers in parallel.
|
||||
|
||||
Retry -> all servers.
|
||||
Repeat -> next server.
|
||||
|
||||
This avoids a significant increase in upstream traffic on
|
||||
busy instances which see lots of queries for common names.
|
||||
|
||||
It does mean the clients which repeat queries from new source ports,
|
||||
rather than retrying them from the same source port, will see
|
||||
different behaviour, but it in fact restores the pre-2.83 behaviour,
|
||||
so it's not expected to be a practical problem.
|
||||
|
||||
(cherry picked from commit ea6b0b2665ed95baa41fe813547103f7f1ccb953)
|
||||
---
|
||||
src/forward.c | 114 ++++++++++++++++++++++----------------------------
|
||||
1 file changed, 51 insertions(+), 63 deletions(-)
|
||||
|
||||
diff --git a/src/forward.c b/src/forward.c
|
||||
index 64124b1..d27f230 100644
|
||||
--- a/src/forward.c
|
||||
+++ b/src/forward.c
|
||||
@@ -17,9 +17,6 @@
|
||||
#include "dnsmasq.h"
|
||||
|
||||
static struct frec *lookup_frec(unsigned short id, int fd, void *hash);
|
||||
-static struct frec *lookup_frec_by_sender(unsigned short id,
|
||||
- union mysockaddr *addr,
|
||||
- void *hash);
|
||||
static struct frec *lookup_frec_by_query(void *hash, unsigned int flags);
|
||||
|
||||
static unsigned short get_id(void);
|
||||
@@ -257,6 +254,7 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr,
|
||||
#endif
|
||||
unsigned int gotname = extract_request(header, plen, daemon->namebuff, NULL);
|
||||
unsigned char *oph = find_pseudoheader(header, plen, NULL, NULL, NULL, NULL);
|
||||
+ int old_src = 0;
|
||||
|
||||
(void)do_bit;
|
||||
|
||||
@@ -270,9 +268,53 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr,
|
||||
if (do_bit)
|
||||
fwd_flags |= FREC_DO_QUESTION;
|
||||
#endif
|
||||
-
|
||||
- /* may be no servers available. */
|
||||
- if (forward || (forward = lookup_frec_by_sender(ntohs(header->id), udpaddr, hash)))
|
||||
+
|
||||
+ /* Check for retry on existing query from same source */
|
||||
+ if (forward)
|
||||
+ old_src = 1;
|
||||
+ else if ((forward = lookup_frec_by_query(hash, fwd_flags)))
|
||||
+ {
|
||||
+ struct frec_src *src;
|
||||
+
|
||||
+ for (src = &forward->frec_src; src; src = src->next)
|
||||
+ if (src->orig_id == ntohs(header->id) &&
|
||||
+ sockaddr_isequal(&src->source, udpaddr))
|
||||
+ break;
|
||||
+
|
||||
+ if (src)
|
||||
+ old_src = 1;
|
||||
+ else
|
||||
+ {
|
||||
+ /* Existing query, but from new source, just add this
|
||||
+ client to the list that will get the reply.*/
|
||||
+
|
||||
+ /* Note whine_malloc() zeros memory. */
|
||||
+ if (!daemon->free_frec_src &&
|
||||
+ daemon->frec_src_count < daemon->ftabsize &&
|
||||
+ (daemon->free_frec_src = whine_malloc(sizeof(struct frec_src))))
|
||||
+ {
|
||||
+ daemon->frec_src_count++;
|
||||
+ daemon->free_frec_src->next = NULL;
|
||||
+ }
|
||||
+
|
||||
+ /* If we've been spammed with many duplicates, just drop the query. */
|
||||
+ if (daemon->free_frec_src)
|
||||
+ {
|
||||
+ src = daemon->free_frec_src;
|
||||
+ daemon->free_frec_src = src->next;
|
||||
+ src->next = forward->frec_src.next;
|
||||
+ forward->frec_src.next = src;
|
||||
+ src->orig_id = ntohs(header->id);
|
||||
+ src->source = *udpaddr;
|
||||
+ src->dest = *dst_addr;
|
||||
+ src->log_id = daemon->log_id;
|
||||
+ src->iface = dst_iface;
|
||||
+ src->fd = udpfd;
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ if (forward)
|
||||
{
|
||||
/* If we didn't get an answer advertising a maximal packet in EDNS,
|
||||
fall back to 1280, which should work everywhere on IPv6.
|
||||
@@ -317,10 +359,10 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr,
|
||||
}
|
||||
#endif
|
||||
|
||||
- /* retry on existing query, send to all available servers */
|
||||
+ /* retry on existing query, from original source. Send to all available servers */
|
||||
domain = forward->sentto->domain;
|
||||
forward->sentto->failed_queries++;
|
||||
- if (!option_bool(OPT_ORDER))
|
||||
+ if (!option_bool(OPT_ORDER) && old_src)
|
||||
{
|
||||
sd = forward->sentto->serv_domain;
|
||||
forward->forwardall = 1;
|
||||
@@ -338,42 +380,7 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr,
|
||||
}
|
||||
else
|
||||
{
|
||||
- /* Query from new source, but the same query may be in progress
|
||||
- from another source. If so, just add this client to the
|
||||
- list that will get the reply.
|
||||
-
|
||||
- Note that is the EDNS client subnet option is in use, we can't do this,
|
||||
- as the clients (and therefore query EDNS options) will be different
|
||||
- for each query. The EDNS subnet code has checks to avoid
|
||||
- attacks in this case. */
|
||||
- if (!option_bool(OPT_CLIENT_SUBNET) && (forward = lookup_frec_by_query(hash, fwd_flags)))
|
||||
- {
|
||||
- /* Note whine_malloc() zeros memory. */
|
||||
- if (!daemon->free_frec_src &&
|
||||
- daemon->frec_src_count < daemon->ftabsize &&
|
||||
- (daemon->free_frec_src = whine_malloc(sizeof(struct frec_src))))
|
||||
- {
|
||||
- daemon->frec_src_count++;
|
||||
- daemon->free_frec_src->next = NULL;
|
||||
- }
|
||||
-
|
||||
- /* If we've been spammed with many duplicates, just drop the query. */
|
||||
- if (daemon->free_frec_src)
|
||||
- {
|
||||
- struct frec_src *new = daemon->free_frec_src;
|
||||
- daemon->free_frec_src = new->next;
|
||||
- new->next = forward->frec_src.next;
|
||||
- forward->frec_src.next = new;
|
||||
- new->orig_id = ntohs(header->id);
|
||||
- new->source = *udpaddr;
|
||||
- new->dest = *dst_addr;
|
||||
- new->log_id = daemon->log_id;
|
||||
- new->iface = dst_iface;
|
||||
- new->fd = udpfd;
|
||||
- }
|
||||
-
|
||||
- return 1;
|
||||
- }
|
||||
+ /* new query */
|
||||
|
||||
if (gotname)
|
||||
flags = search_servers(now, &addrp, gotname, daemon->namebuff, &type, &domain, &norebind, &sd);
|
||||
@@ -2424,25 +2431,6 @@ static struct frec *lookup_frec(unsigned short id, int fd, void *hash)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
-static struct frec *lookup_frec_by_sender(unsigned short id,
|
||||
- union mysockaddr *addr,
|
||||
- void *hash)
|
||||
-{
|
||||
- struct frec *f;
|
||||
- struct frec_src *src;
|
||||
-
|
||||
- for (f = daemon->frec_list; f; f = f->next)
|
||||
- if (f->sentto &&
|
||||
- !(f->flags & (FREC_DNSKEY_QUERY | FREC_DS_QUERY)) &&
|
||||
- memcmp(hash, f->hash, HASH_SIZE) == 0)
|
||||
- for (src = &f->frec_src; src; src = src->next)
|
||||
- if (src->orig_id == id &&
|
||||
- sockaddr_isequal(&src->source, addr))
|
||||
- return f;
|
||||
-
|
||||
- return NULL;
|
||||
-}
|
||||
-
|
||||
static struct frec *lookup_frec_by_query(void *hash, unsigned int flags)
|
||||
{
|
||||
struct frec *f;
|
||||
--
|
||||
2.50.1
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -1,36 +0,0 @@
|
||||
From b8544d5802e56186eb144fbcdd18070b01dc9ab0 Mon Sep 17 00:00:00 2001
|
||||
From: Simon Kelley <simon@thekelleys.org.uk>
|
||||
Date: Fri, 10 Apr 2026 16:29:31 +0100
|
||||
Subject: [PATCH 1/5] Fix buffer overflow in struct bigname. CVE-2026-2291
|
||||
|
||||
All buffers capable of holding a domain name should be
|
||||
at least MAXDNAME*2 + 1 bytes long, where MAXDNAME is the maximum
|
||||
size of a domain name. The accounts for the trailing zero and the
|
||||
fact that some characters are escaped in the internal representation
|
||||
of a domain name in dnsmasq.
|
||||
|
||||
The declaration of struct bigname get this wrong, with the effect
|
||||
that a remote attacker capable of asking DNS queries or answering DNS
|
||||
queries can cause a large OOB write in the heap.
|
||||
|
||||
This was first spotted by Andrew S. Fasano.
|
||||
---
|
||||
src/dnsmasq.h | 2 +-
|
||||
1 file changed, 1 insertion(+), 1 deletion(-)
|
||||
|
||||
diff --git a/src/dnsmasq.h b/src/dnsmasq.h
|
||||
index e455c3f..be8cf2a 100644
|
||||
--- a/src/dnsmasq.h
|
||||
+++ b/src/dnsmasq.h
|
||||
@@ -467,7 +467,7 @@ struct interface_name {
|
||||
};
|
||||
|
||||
union bigname {
|
||||
- char name[MAXDNAME];
|
||||
+ char name[(2*MAXDNAME) + 1];
|
||||
union bigname *next; /* freelist */
|
||||
};
|
||||
|
||||
--
|
||||
2.54.0
|
||||
|
||||
@ -1,70 +0,0 @@
|
||||
From 09fe631edd6d95630efc11bec8c5017705e68a10 Mon Sep 17 00:00:00 2001
|
||||
From: Simon Kelley <simon@thekelleys.org.uk>
|
||||
Date: Fri, 10 Apr 2026 22:16:45 +0100
|
||||
Subject: [PATCH 2/5] Fix NSEC bitmap parsing infinite loop. CVE-2026-4890
|
||||
|
||||
Report from Royce M <royce@xchglabs.com>.
|
||||
|
||||
Location: dnssec.c:1290-1306, dnssec.c:1450-1463
|
||||
|
||||
The bitmap window iteration advances by p[1] instead of p[1]+2 (missing the 2-byte window header). With bitmap_length=0, both rdlen and p are
|
||||
unchanged, causing an infinite loop and dnsmasq stops responding to all queries.
|
||||
|
||||
The same code accesses p[2] after only checking rdlen >= 2 without verifying p[1] >= 1, causing OOB reads at 6 locations.
|
||||
|
||||
Both bugs are reachable before RRSIG validation (confirmed by the source comment at line 2125), so no valid DNSSEC signatures are needed.
|
||||
---
|
||||
src/dnssec.c | 14 +++++++-------
|
||||
1 file changed, 7 insertions(+), 7 deletions(-)
|
||||
|
||||
diff --git a/src/dnssec.c b/src/dnssec.c
|
||||
index ed2f53f..68f1b5d 100644
|
||||
--- a/src/dnssec.c
|
||||
+++ b/src/dnssec.c
|
||||
@@ -1270,10 +1270,10 @@ static int prove_non_existence_nsec(struct dns_header *header, size_t plen, unsi
|
||||
packet checked to be as long as rdlen implies in prove_non_existence() */
|
||||
|
||||
/* If we can prove that there's no NS record, return that information. */
|
||||
- if (nons && rdlen >= 2 && p[0] == 0 && (p[2] & (0x80 >> T_NS)) != 0)
|
||||
+ if (nons && rdlen >= 2 && p[0] == 0 && p[1] >= 1 && (p[2] & (0x80 >> T_NS)) != 0)
|
||||
*nons = 0;
|
||||
|
||||
- if (rdlen >= 2 && p[0] == 0)
|
||||
+ if (rdlen >= 2 && p[0] == 0 && p[1] >= 1)
|
||||
{
|
||||
/* A CNAME answer would also be valid, so if there's a CNAME is should
|
||||
have been returned. */
|
||||
@@ -1301,8 +1301,8 @@ static int prove_non_existence_nsec(struct dns_header *header, size_t plen, unsi
|
||||
break; /* finished checking */
|
||||
}
|
||||
|
||||
- rdlen -= p[1];
|
||||
- p += p[1];
|
||||
+ rdlen -= p[1] + 2;
|
||||
+ p += p[1] + 2;
|
||||
}
|
||||
|
||||
return 0;
|
||||
@@ -1429,7 +1429,7 @@ static int check_nsec3_coverage(struct dns_header *header, size_t plen, int dige
|
||||
p += hash_len; /* skip next-domain hash */
|
||||
rdlen -= p - psave;
|
||||
|
||||
- if (rdlen >= 2 && p[0] == 0)
|
||||
+ if (rdlen >= 2 && p[0] == 0 && p[1] >= 1)
|
||||
{
|
||||
/* If we can prove that there's no NS record, return that information. */
|
||||
if (nons && (p[2] & (0x80 >> T_NS)) != 0)
|
||||
@@ -1458,8 +1458,8 @@ static int check_nsec3_coverage(struct dns_header *header, size_t plen, int dige
|
||||
break; /* finished checking */
|
||||
}
|
||||
|
||||
- rdlen -= p[1];
|
||||
- p += p[1];
|
||||
+ rdlen -= p[1] + 2;
|
||||
+ p += p[1] + 2;
|
||||
}
|
||||
|
||||
return 1;
|
||||
--
|
||||
2.54.0
|
||||
|
||||
@ -1,39 +0,0 @@
|
||||
From 2efe6d3acaf840fa06d58b6fad21ad73d0865716 Mon Sep 17 00:00:00 2001
|
||||
From: Simon Kelley <simon@thekelleys.org.uk>
|
||||
Date: Wed, 25 Mar 2026 23:04:08 +0000
|
||||
Subject: [PATCH 3/5] Verify rdlen field in RRSIG packets. CVE-2026-4891
|
||||
|
||||
Bug report from Royce M <royce@xchglabs.com>
|
||||
|
||||
This avoids crafted packets which give a value for rdlen _less_
|
||||
then the space taken up by the fixed data and the signer's name
|
||||
and engender a negative calculated length for the signature.
|
||||
---
|
||||
src/dnssec.c | 10 +++++++---
|
||||
1 file changed, 7 insertions(+), 3 deletions(-)
|
||||
|
||||
diff --git a/src/dnssec.c b/src/dnssec.c
|
||||
index 68f1b5d..d32db5b 100644
|
||||
--- a/src/dnssec.c
|
||||
+++ b/src/dnssec.c
|
||||
@@ -546,10 +546,14 @@ static int validate_rrset(time_t now, struct dns_header *header, size_t plen, in
|
||||
|
||||
*ttl_out = ttl;
|
||||
}
|
||||
-
|
||||
+
|
||||
+ /* Don't trust rdlen not to be too small and give us a negative sig_len
|
||||
+ It has already been checked that it doesn't run us off the end
|
||||
+ of the packet. */
|
||||
+ if ((sig_len = rdlen - (p - psav)) <= 0)
|
||||
+ return STAT_BOGUS;
|
||||
+
|
||||
sig = p;
|
||||
- sig_len = rdlen - (p - psav);
|
||||
-
|
||||
nsigttl = htonl(orig_ttl);
|
||||
|
||||
hash->update(ctx, 18, psav);
|
||||
--
|
||||
2.54.0
|
||||
|
||||
@ -1,36 +0,0 @@
|
||||
From e0a5f7bef040d25631ffff9abaf8424091b768bc Mon Sep 17 00:00:00 2001
|
||||
From: Simon Kelley <simon@thekelleys.org.uk>
|
||||
Date: Wed, 25 Mar 2026 23:16:35 +0000
|
||||
Subject: [PATCH 4/5] Fix buffer overflow in helper.c with large CLIDs.
|
||||
CVE-2026-4892
|
||||
|
||||
Bug reported bt Royce M <royce@xchglabs.com>
|
||||
|
||||
Location: helper.c:265-270
|
||||
DHCPv6 CLIDs can be up to 65535 bytes. When --dhcp-script is configured,
|
||||
the helper hex-encodes raw CLID bytes via sprintf("%.2x") into daemon->packet (5131 bytes).
|
||||
A 1000-byte CLID writes ~3000 bytes. The helper process retains root privileges.
|
||||
|
||||
Note: log6_packet() correctly caps CLID to 100 bytes for logging, but the helper code path was missed.
|
||||
---
|
||||
src/helper.c | 4 ++--
|
||||
1 file changed, 2 insertions(+), 2 deletions(-)
|
||||
|
||||
diff --git a/src/helper.c b/src/helper.c
|
||||
index b9da225..3a31e61 100644
|
||||
--- a/src/helper.c
|
||||
+++ b/src/helper.c
|
||||
@@ -261,8 +261,8 @@ int create_helper(int event_fd, int err_fd, uid_t uid, gid_t gid, long max_fd)
|
||||
data.hostname_len + data.ed_len + data.clid_len, 1))
|
||||
continue;
|
||||
|
||||
- /* CLID into packet */
|
||||
- for (p = daemon->packet, i = 0; i < data.clid_len; i++)
|
||||
+ /* CLID into packet: limit to 100 bytes to avoid overflowing buffer. */
|
||||
+ for (p = daemon->packet, i = 0; i < data.clid_len && i < 100; i++)
|
||||
{
|
||||
p += sprintf(p, "%.2x", buf[i]);
|
||||
if (i != data.clid_len - 1)
|
||||
--
|
||||
2.54.0
|
||||
|
||||
@ -1,33 +0,0 @@
|
||||
From 4b8388d967e207b277c45e1fc0fb646767e5ca5d Mon Sep 17 00:00:00 2001
|
||||
From: Simon Kelley <simon@thekelleys.org.uk>
|
||||
Date: Wed, 25 Mar 2026 23:22:37 +0000
|
||||
Subject: [PATCH] Fix broken client subnet validation. CVE-2026-4893
|
||||
|
||||
Bug report from Royce M <royce@xchglabs.com>
|
||||
|
||||
Location: forward.c:713, edns0.c:421
|
||||
|
||||
With --add-subnet enabled, process_reply() passes the OPT record
|
||||
length (~23 bytes) instead of the packet length to check_source().
|
||||
All internal bounds checks fail, and the function always returns 1.
|
||||
ECS source validation per RFC 7871 Section 9.2 is completely bypassed.
|
||||
---
|
||||
src/forward.c | 2 +-
|
||||
1 file changed, 1 insertion(+), 1 deletion(-)
|
||||
|
||||
diff --git a/src/forward.c b/src/forward.c
|
||||
index d27f230..156770a 100644
|
||||
--- a/src/forward.c
|
||||
+++ b/src/forward.c
|
||||
@@ -629,7 +629,7 @@ static size_t process_reply(struct dns_header *header, time_t now, struct server
|
||||
|
||||
if ((pheader = find_pseudoheader(header, n, &plen, &sizep, &is_sign, NULL)))
|
||||
{
|
||||
- if (check_subnet && !check_source(header, plen, pheader, query_source))
|
||||
+ if (check_subnet && !check_source(header, n, pheader, query_source))
|
||||
{
|
||||
my_syslog(LOG_WARNING, _("discarding DNS reply: subnet option mismatch"));
|
||||
return 0;
|
||||
--
|
||||
2.54.0
|
||||
|
||||
@ -13,7 +13,7 @@
|
||||
|
||||
Name: dnsmasq
|
||||
Version: 2.79
|
||||
Release: 36%{?extraversion:.%{extraversion}}%{?dist}
|
||||
Release: 32%{?extraversion:.%{extraversion}}%{?dist}
|
||||
Summary: A lightweight DHCP/caching DNS server
|
||||
|
||||
License: GPLv2 or GPLv3
|
||||
@ -98,20 +98,6 @@ Patch43: dnsmasq-2.87-log-root-writeable.patch
|
||||
Patch44: dnsmasq-2.85-domain-blocklist-speedup.patch
|
||||
# http://thekelleys.org.uk/gitweb/?p=dnsmasq.git;a=commit;h=dd33e98da09c487a58b6cb6693b8628c0b234a3b
|
||||
Patch45: dnsmasq-2.80-synth-domain-RHEL-15216.patch
|
||||
# https://thekelleys.org.uk/gitweb/?p=dnsmasq.git;a=commit;h=214a046f47b9f7dd56f5eef3a8678ccbd6e973b7
|
||||
Patch46: dnsmasq-2.90-CVE-2023-50387-CVE-2023-50868.patch
|
||||
# http://thekelleys.org.uk/gitweb/?p=dnsmasq.git;a=commit;h=141a26f979b4bc959d8e866a295e24f8cf456920
|
||||
# http://thekelleys.org.uk/gitweb/?p=dnsmasq.git;a=commit;h=305cb79c5754d5554729b18a2c06fe7ce699687a
|
||||
# http://thekelleys.org.uk/gitweb/?p=dnsmasq.git;a=commit;h=ea6b0b2665ed95baa41fe813547103f7f1ccb953
|
||||
Patch47: dnsmasq-2.85-forward-retries.patch
|
||||
# http://thekelleys.org.uk/gitweb/?p=dnsmasq.git;a=commit;h=45d8a2435e8200e892b82b6a04c7ddfb07a4165a
|
||||
# http://thekelleys.org.uk/gitweb/?p=dnsmasq.git;a=commit;h=eb1fe15ca80b6bc43cd6bfdf309ec6c590aff811
|
||||
Patch48: dnsmasq-2.79-cname-collision.patch
|
||||
Patch49: dnsmasq-2.93-CVE-2026-2291.patch
|
||||
Patch50: dnsmasq-2.93-CVE-2026-4890.patch
|
||||
Patch51: dnsmasq-2.93-CVE-2026-4891.patch
|
||||
Patch52: dnsmasq-2.93-CVE-2026-4892.patch
|
||||
Patch53: dnsmasq-2.93-CVE-2026-4893.patch
|
||||
|
||||
# This is workaround to nettle bug #1549190
|
||||
# https://bugzilla.redhat.com/show_bug.cgi?id=1549190
|
||||
@ -190,14 +176,6 @@ server's leases.
|
||||
%patch43 -p1 -b .rh2156789
|
||||
%patch44 -p1 -b .rh2209031
|
||||
%patch45 -p1 -b .RHEL-15216
|
||||
%patch46 -p1 -b .CVE-2023-50387-CVE-2023-50868
|
||||
%patch47 -p1 -b .RHEL-6586
|
||||
%patch48 -p1 -b .RHEL-61943
|
||||
%patch49 -p1 -b .CVE-2026-2291
|
||||
%patch50 -p1 -b .CVE-2026-4890
|
||||
%patch51 -p1 -b .CVE-2026-4891
|
||||
%patch52 -p1 -b .CVE-2026-4892
|
||||
%patch53 -p1 -b .CVE-2026-4893
|
||||
|
||||
# use /var/lib/dnsmasq instead of /var/lib/misc
|
||||
for file in dnsmasq.conf.example man/dnsmasq.8 man/es/dnsmasq.8 src/config.h; do
|
||||
@ -297,25 +275,6 @@ install -Dpm 644 %{SOURCE2} %{buildroot}%{_sysusersdir}/dnsmasq.conf
|
||||
%{_mandir}/man1/dhcp_*
|
||||
|
||||
%changelog
|
||||
* Tue May 05 2026 Petr Menšík <pemensik@redhat.com> - 2.79-36
|
||||
- Prevent overflow in extract_name function (CVE-2026-2291)
|
||||
- Prevent DoS in DNSSEC validation (CVE-2026-4890)
|
||||
- Prevent out-of-bounds read in DNSSEC validation (CVE-2026-4891)
|
||||
- Prevent out-of-bounds write in DHCPv6 server (CVE-2026-4892)
|
||||
- Prevent source check avoidance by RFC 7871 client-subnet (CVE-2026-4893)
|
||||
|
||||
* Mon Aug 18 2025 Tomas Korbar <tkorbar@redhat.com> - 2.79-35
|
||||
- Fix dnsmasq caching of intertwined CNAMES
|
||||
- Resolves: RHEL-61943
|
||||
|
||||
* Mon Aug 11 2025 Petr Menšík <pemensik@redhat.com> - 2.79-34
|
||||
- Enable retries again (RHEL-6586)
|
||||
|
||||
* Mon Mar 18 2024 Tomas Korbar <tkorbar@redhat.com> - 2.79-33
|
||||
- Fix CVE 2023-50387 and CVE 2023-50868
|
||||
- Resolves: RHEL-25667
|
||||
- Resolves: RHEL-25629
|
||||
|
||||
* Wed Nov 01 2023 Petr Menšík <pemensik@redhat.com> - 2.79-32
|
||||
- Do not crash on invalid domain in --synth-domain option (RHEL-15216)
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user