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;
}
-