dnsmasq/SOURCES/dnsmasq-2.85-forward-retries.patch

208 lines
6.6 KiB
Diff

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