[Dnsmasq-discuss] [PATCH] --add-subnet custom values

Ed Bardsley ebardsley at google.com
Sat Jul 25 03:36:21 BST 2015


Hi folks -

I wrote up some changes to --add-subnet to, in addition to the current
behavior, let you specify things like --add-subnet=1.2.3.4/24 to use
that specific address instead of the requestor's subnet. I've found
this particularly useful for testing in cases where you can control an
intermediate resolver but not the client, as well as in cases where
it's appropriate to lie to geo-aware DNS servers. :)

Perhaps others will also find this useful.

 man/dnsmasq.8 | 32 +++++++++++++++++++++-----------
 src/dnsmasq.h | 13 ++++++++++---
 src/option.c  | 63
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-----
 src/rfc1035.c | 38 +++++++++++++++++++++++++++++++-------
 4 files changed, 120 insertions(+), 26 deletions(-)

Patch is attached.

Ed
-------------- next part --------------
diff --git a/man/dnsmasq.8 b/man/dnsmasq.8
index c8913b5..a23c898 100644
--- a/man/dnsmasq.8
+++ b/man/dnsmasq.8
@@ -604,17 +604,27 @@ experimental. Also note that exposing MAC addresses in this way may
 have security and privacy implications. The warning about caching
 given for --add-subnet applies to --add-mac too.
 .TP 
-.B --add-subnet[[=<IPv4 prefix length>],<IPv6 prefix length>]
-Add the subnet address of the requestor to the DNS queries which are
-forwarded upstream. The amount of the address forwarded depends on the
-prefix length parameter: 32 (128 for IPv6) forwards the whole address,
-zero forwards none of it but still marks the request so that no
-upstream nameserver will add client address information either. The
-default is zero for both IPv4 and IPv6. Note that upstream nameservers
-may be configured to return different results based on this
-information, but the dnsmasq cache does not take account. If a dnsmasq
-instance is configured such that different results may be encountered,
-caching should be disabled.
+.B --add-subnet[[=[<IPv4 address>/]<IPv4 prefix length>][,[<IPv6 address>/]<IPv6 prefix length>]]
+Add a subnet address to the DNS queries which are forwarded
+upstream. If an address is specified in the flag, it will be used,
+otherwise, the address of the requestor will be used. The amount of
+the address forwarded depends on the prefix length parameter: 32 (128
+for IPv6) forwards the whole address, zero forwards none of it but
+still marks the request so that no upstream nameserver will add client
+address information either. The default is zero for both IPv4 and
+IPv6. Note that upstream nameservers may be configured to return
+different results based on this information, but the dnsmasq cache
+does not take account. If a dnsmasq instance is configured such that
+different results may be encountered, caching should be disabled.
+
+For example,
+.B --add-subnet=24,96
+will add the /24 and /96 subnets of the requestor for IPv4 and IPv6 requestors, respectively.
+.B --add-subnet=1.2.3.4/24
+will add 1.2.3.0/24 for IPv4 requestors and ::/0 for IPv6 requestors.
+.B --add-subnet=1.2.3.4/24,1.2.3.4/24
+will add 1.2.3.0/24 for both IPv4 and IPv6 requestors.
+
 .TP
 .B \-c, --cache-size=<cachesize>
 Set the size of dnsmasq's cache. The default is 150 names. Setting the cache size to zero disables caching.
diff --git a/src/dnsmasq.h b/src/dnsmasq.h
index de146c6..06a1dc6 100644
--- a/src/dnsmasq.h
+++ b/src/dnsmasq.h
@@ -540,6 +540,13 @@ struct iname {
   struct iname *next;
 };
 
+/* subnet parameters from command line */
+struct mysubnet {
+  union mysockaddr addr;
+  int addr_used;
+  int mask;
+};
+
 /* resolv-file parms from command-line */
 struct resolvc {
   struct resolvc *next;
@@ -934,9 +941,9 @@ extern struct daemon {
   struct auth_zone *auth_zones;
   struct interface_name *int_names;
   char *mxtarget;
-  int addr4_netmask;
-  int addr6_netmask;
-  char *lease_file; 
+  struct mysubnet *add_subnet4;
+  struct mysubnet *add_subnet6;
+  char *lease_file;
   char *username, *groupname, *scriptuser;
   char *luascript;
   char *authserver, *hostmaster;
diff --git a/src/option.c b/src/option.c
index ecc2619..bafea5d 100644
--- a/src/option.c
+++ b/src/option.c
@@ -445,7 +445,7 @@ static struct {
   { LOPT_PXE_SERV, ARG_DUP, "<service>", gettext_noop("Boot service for PXE menu."), NULL },
   { LOPT_TEST, 0, NULL, gettext_noop("Check configuration syntax."), NULL },
   { LOPT_ADD_MAC, OPT_ADD_MAC, NULL, gettext_noop("Add requestor's MAC address to forwarded DNS queries."), NULL },
-  { LOPT_ADD_SBNET, ARG_ONE, "<v4 pref>[,<v6 pref>]", gettext_noop("Add requestor's IP subnet to forwarded DNS queries."), NULL },
+  { LOPT_ADD_SBNET, ARG_ONE, "<v4 pref>[,<v6 pref>]", gettext_noop("Add specified IP subnet to forwarded DNS queries."), NULL },
   { LOPT_DNSSEC, OPT_DNSSEC_PROXY, NULL, gettext_noop("Proxy DNSSEC validation results from upstream nameservers."), NULL },
   { LOPT_INCR_ADDR, OPT_CONSEC_ADDR, NULL, gettext_noop("Attempt to allocate sequential IP addresses to DHCP clients."), NULL },
   { LOPT_CONNTRACK, OPT_CONNTRACK, NULL, gettext_noop("Copy connection-track mark from queries to upstream connections."), NULL },
@@ -722,6 +722,25 @@ static void do_usage(void)
 
 #define ret_err(x) do { strcpy(errstr, (x)); return 0; } while (0)
 
+char *parse_mysockaddr(char *arg, union mysockaddr *addr) {
+  if (inet_pton(AF_INET, arg, &addr->in.sin_addr) > 0)
+    {
+      addr->sa.sa_family = AF_INET;
+    }
+#ifdef HAVE_IPV6
+  else if (inet_pton(AF_INET6, arg, &addr->in6.sin6_addr) > 0)
+    {
+      addr->sa.sa_family = AF_INET6;
+    }
+#endif
+  else
+    {
+      return _("bad address");
+    }
+
+  return NULL;
+}
+
 char *parse_server(char *arg, union mysockaddr *addr, union mysockaddr *source_addr, char *interface, int *flags)
 {
   int source_port = 0, serv_port = NAMESERVER_PORT;
@@ -1585,7 +1604,7 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
 	    li = match_suffix->next;
 	    free(match_suffix->suffix);
 	    free(match_suffix);
-	  }    
+	  }
 	break;
       }
 
@@ -1593,10 +1612,44 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
       set_option_bool(OPT_CLIENT_SUBNET);
       if (arg)
 	{
+          char *err, *end;
 	  comma = split(arg);
-	  if (!atoi_check(arg, &daemon->addr4_netmask) || 
-	      (comma && !atoi_check(comma, &daemon->addr6_netmask)))
-	     ret_err(gen_err);
+
+          struct mysubnet* new = opt_malloc(sizeof(struct mysubnet));
+          if ((end = split_chr(arg, '/'))) {
+            /* has subnet+len */
+            err = parse_mysockaddr(arg, &new->addr);
+            if (err)
+              ret_err(err);
+            if (!atoi_check(end, &new->mask))
+              ret_err(gen_err);
+            new->addr_used = 1;
+          } else {
+	    if (!atoi_check(arg, &new->mask))
+              ret_err(gen_err);
+          }
+          daemon->add_subnet4 = new;
+
+          new = opt_malloc(sizeof(struct mysubnet));
+          if (comma)
+            {
+              if ((end = split_chr(comma, '/')))
+                {
+                  /* has subnet+len */
+                  err = parse_mysockaddr(comma, &new->addr);
+                  if (err)
+                    ret_err(err);
+                  if (!atoi_check(end, &new->mask))
+                    ret_err(gen_err);
+                  new->addr_used = 1;
+                }
+              else
+                {
+                  if (!atoi_check(comma, &new->mask))
+                    ret_err(gen_err);
+                }
+            }
+          daemon->add_subnet6 = new;
 	}
       break;
 
diff --git a/src/rfc1035.c b/src/rfc1035.c
index 56647b0..1d0b7ef 100644
--- a/src/rfc1035.c
+++ b/src/rfc1035.c
@@ -629,26 +629,46 @@ struct subnet_opt {
 #endif
 };
 
+static void *get_addrp(union mysockaddr *addr, const short family) {
+  void *ret = &addr->in.sin_addr;
+#ifdef HAVE_IPV6
+  if (family == AF_INET6)
+    {
+      return &addr->in6.sin6_addr;
+    }
+#endif
+  return ret;
+}
+
 static size_t calc_subnet_opt(struct subnet_opt *opt, union mysockaddr *source)
 {
   /* http://tools.ietf.org/html/draft-vandergaast-edns-client-subnet-02 */
   
   int len;
   void *addrp;
+  int sa_family = source->sa.sa_family;
 
 #ifdef HAVE_IPV6
   if (source->sa.sa_family == AF_INET6)
     {
-      opt->family = htons(2);
-      opt->source_netmask = daemon->addr6_netmask;
-      addrp = &source->in6.sin6_addr;
+      opt->source_netmask = daemon->add_subnet6->mask;
+      if (daemon->add_subnet6->addr_used) {
+	sa_family = daemon->add_subnet6->addr.sa.sa_family;
+	addrp = get_addrp(&daemon->add_subnet6->addr, sa_family);
+      } else {
+	addrp = &source->in6.sin6_addr;
+      }
     }
   else
 #endif
     {
-      opt->family = htons(1);
-      opt->source_netmask = daemon->addr4_netmask;
-      addrp = &source->in.sin_addr;
+      opt->source_netmask = daemon->add_subnet4->mask;
+      if (daemon->add_subnet4->addr_used) {
+	sa_family = daemon->add_subnet4->addr.sa.sa_family;
+	addrp = get_addrp(&daemon->add_subnet4->addr, sa_family);
+      } else {
+	addrp = &source->in.sin_addr;
+      }
     }
   
   opt->scope_netmask = 0;
@@ -656,6 +676,11 @@ static size_t calc_subnet_opt(struct subnet_opt *opt, union mysockaddr *source)
   
   if (opt->source_netmask != 0)
     {
+#ifdef HAVE_IPV6
+      opt->family = htons(sa_family == AF_INET6 ? 2 : 1);
+#else
+      opt->family = htons(1);
+#endif
       len = ((opt->source_netmask - 1) >> 3) + 1;
       memcpy(opt->addr, addrp, len);
       if (opt->source_netmask & 7)
@@ -2334,4 +2359,3 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
   
   return len;
 }
-


More information about the Dnsmasq-discuss mailing list