6d0937
From 3ae073ab82183075a93e71ed889e0ee4fdb780e6 Mon Sep 17 00:00:00 2001
6d0937
From: Simon Kelley <simon@thekelleys.org.uk>
6d0937
Date: Wed, 17 Feb 2021 23:56:32 +0000
6d0937
Subject: [PATCH] Fix problem with DNS retries in 2.83/2.84.
6d0937
6d0937
The new logic in 2.83/2.84 which merges distinct requests for the
6d0937
same domain causes problems with clients which do retries as distinct
6d0937
requests (differing IDs and/or source ports.) The retries just get
6d0937
piggy-backed on the first, failed, request.
6d0937
6d0937
The logic is now changed so that distinct requests for repeated
6d0937
queries still get merged into a single ID/source port, but they now
6d0937
always trigger a re-try upstream.
6d0937
6d0937
Thanks to Nicholas Mu for his analysis.
6d0937
6d0937
(cherry picked from commit 141a26f979b4bc959d8e866a295e24f8cf456920)
6d0937
6d0937
Simplify preceding fix.
6d0937
6d0937
Remove distinction between retry with same QID/SP and
6d0937
retry for same query with different QID/SP. If the
6d0937
QID/SP are the same as an existing one, simply retry,
6d0937
if a new QID/SP is seen, add to the list to be replied to.
6d0937
6d0937
(cherry picked from commit 305cb79c5754d5554729b18a2c06fe7ce699687a)
6d0937
---
6d0937
 src/forward.c | 105 ++++++++++++++++++++++----------------------------
6d0937
 1 file changed, 45 insertions(+), 60 deletions(-)
6d0937
6d0937
diff --git a/src/forward.c b/src/forward.c
6d0937
index 637f220..1e9c509 100644
6d0937
--- a/src/forward.c
6d0937
+++ b/src/forward.c
6d0937
@@ -17,9 +17,6 @@
6d0937
 #include "dnsmasq.h"
6d0937
 
6d0937
 static struct frec *lookup_frec(unsigned short id, int fd, int family, void *hash);
6d0937
-static struct frec *lookup_frec_by_sender(unsigned short id,
6d0937
-					  union mysockaddr *addr,
6d0937
-					  void *hash);
6d0937
 static struct frec *lookup_frec_by_query(void *hash, unsigned int flags);
6d0937
 
6d0937
 static unsigned short get_id(void);
6d0937
@@ -260,8 +257,48 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr,
6d0937
     fwd_flags |= FREC_DO_QUESTION;
6d0937
 #endif
6d0937
   
6d0937
-  /* may be no servers available. */
6d0937
-  if (forward || (forward = lookup_frec_by_sender(ntohs(header->id), udpaddr, hash)))
6d0937
+  /* Check for retry on existing query */
6d0937
+  if (!forward && (forward = lookup_frec_by_query(hash, fwd_flags)))
6d0937
+    {
6d0937
+      struct frec_src *src;
6d0937
+
6d0937
+      for (src = &forward->frec_src; src; src = src->next)
6d0937
+	if (src->orig_id == ntohs(header->id) && 
6d0937
+	    sockaddr_isequal(&src->source, udpaddr))
6d0937
+	  break;
6d0937
+
6d0937
+      /* Existing query, but from new source, just add this 
6d0937
+	 client to the list that will get the reply.*/
6d0937
+      if (!src)
6d0937
+	{
6d0937
+	  /* Note whine_malloc() zeros memory. */
6d0937
+	  if (!daemon->free_frec_src &&
6d0937
+	      daemon->frec_src_count < daemon->ftabsize &&
6d0937
+	      (daemon->free_frec_src = whine_malloc(sizeof(struct frec_src))))
6d0937
+	    {
6d0937
+	      daemon->frec_src_count++;
6d0937
+	      daemon->free_frec_src->next = NULL;
6d0937
+	    }
6d0937
+	  
6d0937
+	  /* If we've been spammed with many duplicates, just drop the query. */
6d0937
+	  if (!daemon->free_frec_src)
6d0937
+	    return 0;
6d0937
+	  
6d0937
+	  src = daemon->free_frec_src;
6d0937
+	  daemon->free_frec_src = src->next;
6d0937
+	  src->next = forward->frec_src.next;
6d0937
+	  forward->frec_src.next = src;
6d0937
+	  src->orig_id = ntohs(header->id);
6d0937
+	  src->source = *udpaddr;
6d0937
+	  src->dest = *dst_addr;
6d0937
+	  src->log_id = daemon->log_id;
6d0937
+	  src->iface = dst_iface;
6d0937
+	  src->fd = udpfd;
6d0937
+	}
6d0937
+    }
6d0937
+
6d0937
+  /* retry existing query */
6d0937
+  if (forward)
6d0937
     {
6d0937
       /* If we didn't get an answer advertising a maximal packet in EDNS,
6d0937
 	 fall back to 1280, which should work everywhere on IPv6.
6d0937
@@ -336,42 +373,8 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr,
6d0937
     }
6d0937
   else 
6d0937
     {
6d0937
-      /* Query from new source, but the same query may be in progress
6d0937
-	 from another source. If so, just add this client to the
6d0937
-	 list that will get the reply.
6d0937
-	 
6d0937
-	 Note that is the EDNS client subnet option is in use, we can't do this,
6d0937
-	 as the clients (and therefore query EDNS options) will be different
6d0937
-	 for each query. The EDNS subnet code has checks to avoid
6d0937
-	 attacks in this case. */
6d0937
-      if (!option_bool(OPT_CLIENT_SUBNET) && (forward = lookup_frec_by_query(hash, fwd_flags)))
6d0937
-	{
6d0937
-	  if (!daemon->free_frec_src &&
6d0937
-	      daemon->frec_src_count < daemon->ftabsize &&
6d0937
-	      (daemon->free_frec_src = whine_malloc(sizeof(struct frec_src))))
6d0937
-	    {
6d0937
-	      daemon->frec_src_count++;
6d0937
-	      daemon->free_frec_src->next = NULL;
6d0937
-	    }
6d0937
-	  
6d0937
-	  /* If we've been spammed with many duplicates, just drop the query. */
6d0937
-	  if (daemon->free_frec_src)
6d0937
-	    {
6d0937
-	      struct frec_src *new = daemon->free_frec_src;
6d0937
-	      daemon->free_frec_src = new->next;
6d0937
-	      new->next = forward->frec_src.next;
6d0937
-	      forward->frec_src.next = new;
6d0937
-	      new->orig_id = ntohs(header->id);
6d0937
-	      new->source = *udpaddr;
6d0937
-	      new->dest = *dst_addr;
6d0937
-	      new->log_id = daemon->log_id;
6d0937
-	      new->iface = dst_iface;
6d0937
-	      new->fd = udpfd;
6d0937
-	    }
6d0937
-	  
6d0937
-	  return 1;
6d0937
-	}
6d0937
-	
6d0937
+      /* new query */
6d0937
+
6d0937
       if (gotname)
6d0937
 	flags = search_servers(now, &addrp, gotname, daemon->namebuff, &type, &domain, &norebind);
6d0937
       
6d0937
@@ -380,6 +383,7 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr,
6d0937
 #endif
6d0937
       type &= ~SERV_DO_DNSSEC;      
6d0937
       
6d0937
+      /* may be no servers available. */
6d0937
       if (daemon->servers && !flags)
6d0937
 	forward = get_new_frec(now, NULL, 0);
6d0937
       /* table full - flags == 0, return REFUSED */
6d0937
@@ -2249,25 +2253,6 @@ static struct frec *lookup_frec(unsigned short id, int fd, int family, void *has
6d0937
   return NULL;
6d0937
 }
6d0937
 
6d0937
-static struct frec *lookup_frec_by_sender(unsigned short id,
6d0937
-					  union mysockaddr *addr,
6d0937
-					  void *hash)
6d0937
-{
6d0937
-  struct frec *f;
6d0937
-  struct frec_src *src;
6d0937
-
6d0937
-  for (f = daemon->frec_list; f; f = f->next)
6d0937
-    if (f->sentto &&
6d0937
-	!(f->flags & (FREC_DNSKEY_QUERY | FREC_DS_QUERY)) &&
6d0937
-	memcmp(hash, f->hash, HASH_SIZE) == 0)
6d0937
-      for (src = &f->frec_src; src; src = src->next)
6d0937
-	if (src->orig_id == id && 
6d0937
-	    sockaddr_isequal(&src->source, addr))
6d0937
-	  return f;
6d0937
-  
6d0937
-  return NULL;
6d0937
-}
6d0937
-
6d0937
 static struct frec *lookup_frec_by_query(void *hash, unsigned int flags)
6d0937
 {
6d0937
   struct frec *f;
6d0937
-- 
6d0937
2.26.2
6d0937