Enhance --add-subnet to allow arbitary subnet addresses.
diff --git a/CHANGELOG b/CHANGELOG
index 3f4026d..bbc2834 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -4,6 +4,10 @@
 	    least, 0.0.0.0 accesses the local host, so could
 	    be targets for DNS rebinding. See RFC 5735 section 3 
 	    for details. Thanks to Stephen Röttger for the bug report.
+
+	    Enhance --add-subnet to allow arbitrary subnet addresses.
+            Thanks to Ed Barsley for the patch.
+	
 	    
 version 2.75
             Fix reversion on 2.74 which caused 100% CPU use when a 
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 @@
 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 cf1a782..f42acdb 100644
--- a/src/dnsmasq.h
+++ b/src/dnsmasq.h
@@ -541,6 +541,13 @@
   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;
@@ -935,9 +942,9 @@
   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..746cd11 100644
--- a/src/option.c
+++ b/src/option.c
@@ -445,7 +445,7 @@
   { 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,20 @@
 
 #define ret_err(x) do { strcpy(errstr, (x)); return 0; } while (0)
 
+static 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 +1599,7 @@
 	    li = match_suffix->next;
 	    free(match_suffix->suffix);
 	    free(match_suffix);
-	  }    
+	  }
 	break;
       }
 
@@ -1593,10 +1607,45 @@
       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 29e9e65..6a51b30 100644
--- a/src/rfc1035.c
+++ b/src/rfc1035.c
@@ -629,26 +629,47 @@
 #endif
 };
 
+static void *get_addrp(union mysockaddr *addr, const short family) 
+{
+#ifdef HAVE_IPV6
+  if (family == AF_INET6)
+    return &addr->in6.sin6_addr;
+#endif
+
+  return &addr->in.sin_addr;
+}
+
 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 +677,11 @@
   
   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)
@@ -2335,4 +2361,3 @@
   
   return len;
 }
-