208 lines
6.6 KiB
Diff
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
|
|
|