I have a (rather odd, and perhaps ill-advised) network setup in which
names in a particular domain (e.g. example.com) are split across three
sites, and I need three dnsmasq servers to be mutually dependent in the
following hierarchy to resolve names for that domain:

          master
           / \
          /   \
      area1   area2

If a client sends a query for x.example.com to area1 that area1 can't
answer, or if another client sends a query for y.example.com to area2
that area2 can't answer, both servers will forward the query to master,
which is configured (with --server) to be the sole upstream DNS server
for example.com on both area1 and area2. If master can't answer a query
for example.com, it is configured to forward the query to area1 and
area2. Clearly, master shouldn't forward queries that originate from
area1 back to area1: this would lead to an infinite forwarding loop.

The attached patch implements a new option, --dont-mirror-queries. When
enabled, this option prevents dnsmasq from forwarding a request to an
upstream server if its IP address matches that of the sender of the
query. I suppose this could be considered a dynamic, per-query version
of the --dns-loop-detect option that is only capable of detecting 1-hop
loops.

Kurt H Maier <[email protected]> was the brains of this operation, helping
me figure out the part of forward.c that needed patching.

Cheers,
Chris
From bc963171d1a5d46566c3835e66145532b8269ac8 Mon Sep 17 00:00:00 2001
From: Chris Novakovic <[email protected]>
Date: Fri, 29 Jan 2016 13:04:02 +0000
Subject: Added --dont-mirror-queries option

This commit adds the --dont-mirror-queries option. When enabled, this
option prevents dnsmasq from forwarding an incoming DNS query to an
upstream server if its IP address matches the IP address from which the
query originated.
---
 man/dnsmasq.8 |  6 ++++++
 src/dnsmasq.h |  3 ++-
 src/forward.c | 27 ++++++++++++++++++++++++++-
 src/option.c  |  3 +++
 4 files changed, 37 insertions(+), 2 deletions(-)

diff --git a/man/dnsmasq.8 b/man/dnsmasq.8
index b6fe6b4..d361c42 100644
--- a/man/dnsmasq.8
+++ b/man/dnsmasq.8
@@ -370,6 +370,12 @@ the upstream server through which it was sent is disabled and this event is logg
 set of upstream servers changes, the test is re-run on all of them, including ones which
 were previously disabled.
 .TP
+.B --dont-mirror-queries
+Don't forward DNS queries to upstream DNS servers whose IP address
+matches that of the original sender of the query. This option makes
+it possible for two dnsmasq DNS servers to be mutually dependent on
+each other, without the risk of inducing forwarding loops.
+.TP
 .B --stop-dns-rebind
 Reject (and log) addresses from upstream nameservers which are in the
 private IP ranges. This blocks an attack where a browser behind a
diff --git a/src/dnsmasq.h b/src/dnsmasq.h
index 9d4d6b3..5487da2 100644
--- a/src/dnsmasq.h
+++ b/src/dnsmasq.h
@@ -237,7 +237,8 @@ struct event_desc {
 #define OPT_TFTP_NO_FAIL   52
 #define OPT_SCRIPT_ARP     53
 #define OPT_MAC_B64        54
-#define OPT_LAST           55
+#define OPT_DONT_MIRROR    55
+#define OPT_LAST           56
 
 /* extra flags for my_syslog, we use a couple of facilities since they are known 
    not to occupy the same bits as priorities, no matter how syslog.h is set up. */
diff --git a/src/forward.c b/src/forward.c
index 9b464d3..a437b4c 100644
--- a/src/forward.c
+++ b/src/forward.c
@@ -440,8 +440,33 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr,
 	  /* only send to servers dealing with our domain.
 	     domain may be NULL, in which case server->domain 
 	     must be NULL also. */
+      
+      /* If --dont-mirror-queries is enabled, don't forward the query to this upstream
+       * server if it has the same IP address as the sender of the original query */
+      int is_mirrored_query = 0;
+      if (option_bool(OPT_DONT_MIRROR))
+        {
+          char sender_ip[ADDRSTRLEN], upstream_ip[ADDRSTRLEN];
+          
+          if (udpaddr->sa.sa_family == AF_INET)
+            inet_ntop(AF_INET, &udpaddr->in.sin_addr, sender_ip, ADDRSTRLEN);
+#ifdef HAVE_IPV6
+          else
+            inet_ntop(AF_INET6, &udpaddr->in6.sin6_addr, sender_ip, ADDRSTRLEN);
+#endif
+          
+          if (start->addr.sa.sa_family == AF_INET)
+            inet_ntop(AF_INET, &start->addr.in.sin_addr, upstream_ip, ADDRSTRLEN);
+#ifdef HAVE_IPV6
+          else
+            inet_ntop(AF_INET6, &start->addr.in6.sin6_addr, upstream_ip, ADDRSTRLEN);
+#endif
+          
+          is_mirrored_query = (strcmp(sender_ip, upstream_ip) == 0);
+        }
 	  
-	  if (type == (start->flags & SERV_TYPE) &&
+	  if (is_mirrored_query == 0 &&
+          type == (start->flags & SERV_TYPE) &&
 	      (type != SERV_HAS_DOMAIN || hostname_isequal(domain, start->domain)) &&
 	      !(start->flags & (SERV_LITERAL_ADDRESS | SERV_LOOP)))
 	    {
diff --git a/src/option.c b/src/option.c
index ac35e7c..ba2e0eb 100644
--- a/src/option.c
+++ b/src/option.c
@@ -157,6 +157,7 @@ struct myoption {
 #define LOPT_MAXPORT       345
 #define LOPT_CPE_ID        346
 #define LOPT_SCRIPT_ARP    347
+#define LOPT_DONT_MIRROR   348
 
 #ifdef HAVE_GETOPT_LONG
 static const struct option opts[] =  
@@ -319,6 +320,7 @@ static const struct myoption opts[] =
     { "quiet-ra", 0, 0, LOPT_QUIET_RA },
     { "dns-loop-detect", 0, 0, LOPT_LOOP_DETECT },
     { "script-arp", 0, 0, LOPT_SCRIPT_ARP },
+    { "dont-mirror-queries", 0, 0, LOPT_DONT_MIRROR },
     { NULL, 0, 0, 0 }
   };
 
@@ -488,6 +490,7 @@ static struct {
   { LOPT_LOCAL_SERVICE, OPT_LOCAL_SERVICE, NULL, gettext_noop("Accept queries only from directly-connected networks"), NULL },
   { LOPT_LOOP_DETECT, OPT_LOOP_DETECT, NULL, gettext_noop("Detect and remove DNS forwarding loops"), NULL },
   { LOPT_IGNORE_ADDR, ARG_DUP, "<ipaddr>", gettext_noop("Ignore DNS responses containing ipaddr."), NULL }, 
+  { LOPT_DONT_MIRROR, OPT_DONT_MIRROR, NULL, gettext_noop("Don't forward DNS queries to requestor."), NULL },
   { 0, 0, NULL, NULL, NULL }
 }; 
 
-- 
1.8.4

_______________________________________________
Dnsmasq-discuss mailing list
[email protected]
http://lists.thekelleys.org.uk/mailman/listinfo/dnsmasq-discuss

Reply via email to