Discussion:
[Dnsmasq-discuss] [PATCH] auth-zone: allow to exclude ip addresses from answer
Mathias Kresin
2016-06-01 16:07:15 UTC
Permalink
It can be used, to ensure that answers contain only global routeable IP
addresses (by excluding loopback, RFC1918 and ULA addresses).

Signed-off-by: Mathias Kresin <***@kresin.me>

---

This feature was discussed > 1Y ago [1] but never implemented. It is supposed to work
exactly as described in the the linked e-mail thread.

[1] http://lists.thekelleys.org.uk/pipermail/dnsmasq-discuss/2015q1/009122.html.

man/dnsmasq.8 | 6 +++++-
src/auth.c | 43 ++++++++++++++++++++++++++++++-------------
src/dnsmasq.h | 1 +
src/option.c | 22 ++++++++++++++++++++--
4 files changed, 56 insertions(+), 16 deletions(-)

diff --git a/man/dnsmasq.8 b/man/dnsmasq.8
index 0521534..8a22179 100644
--- a/man/dnsmasq.8
+++ b/man/dnsmasq.8
@@ -739,7 +739,7 @@ a return code of SERVFAIL. Note that
setting this may affect DNS behaviour in bad ways, it is not an
extra-logging flag and should not be set in production.
.TP
-.B --auth-zone=<domain>[,<subnet>[/<prefix length>][,<subnet>[/<prefix length>].....]]
+.B --auth-zone=<domain>[,<subnet>[/<prefix length>][,<subnet>[/<prefix length>].....][,exclude:<subnet>[/<prefix length>]].....]
Define a DNS zone for which dnsmasq acts as authoritative server. Locally defined DNS records which are in the domain
will be served. If subnet(s) are given, A and AAAA records must be in one of the
specified subnets.
@@ -756,6 +756,10 @@ appear in the zone, but RFC1918 IPv4 addresses which should not.
Interface-name and address-literal subnet specifications may be used
freely in the same --auth-zone declaration.

+It's possible to exclude certain IP addresses from responses. It can be
+used, to make sure that answers contain only global routeable IP
+addresses (by excluding loopback, RFC1918 and ULA addresses).
+
The subnet(s) are also used to define in-addr.arpa and
ip6.arpa domains which are served for reverse-DNS queries. If not
specified, the prefix length defaults to 24 for IPv4 and 64 for IPv6.
diff --git a/src/auth.c b/src/auth.c
index 198572d..6c72995 100644
--- a/src/auth.c
+++ b/src/auth.c
@@ -18,36 +18,53 @@

#ifdef HAVE_AUTH

-static struct addrlist *find_subnet(struct auth_zone *zone, int flag, struct all_addr *addr_u)
+static struct addrlist *find_addrlist(struct addrlist *list, int flag, struct all_addr *addr_u)
{
- struct addrlist *subnet;
-
- for (subnet = zone->subnet; subnet; subnet = subnet->next)
- {
- if (!(subnet->flags & ADDRLIST_IPV6))
+ do {
+ if (!(list->flags & ADDRLIST_IPV6))
{
struct in_addr netmask, addr = addr_u->addr.addr4;

if (!(flag & F_IPV4))
continue;

- netmask.s_addr = htonl(~(in_addr_t)0 << (32 - subnet->prefixlen));
+ netmask.s_addr = htonl(~(in_addr_t)0 << (32 - list->prefixlen));

- if (is_same_net(addr, subnet->addr.addr.addr4, netmask))
- return subnet;
+ if (is_same_net(addr, list->addr.addr.addr4, netmask))
+ return list;
}
#ifdef HAVE_IPV6
- else if (is_same_net6(&(addr_u->addr.addr6), &subnet->addr.addr.addr6, subnet->prefixlen))
- return subnet;
+ else if (is_same_net6(&(addr_u->addr.addr6), &list->addr.addr.addr6, list->prefixlen))
+ return list;
#endif

- }
+ } while ((list = list->next));
+
return NULL;
}

+static struct addrlist *find_subnet(struct auth_zone *zone, int flag, struct all_addr *addr_u)
+{
+ if (!zone->subnet)
+ return NULL;
+
+ return find_addrlist(zone->subnet, flag, addr_u);
+}
+
+static struct addrlist *find_exclude(struct auth_zone *zone, int flag, struct all_addr *addr_u)
+{
+ if (!zone->exclude)
+ return NULL;
+
+ return find_addrlist(zone->exclude, flag, addr_u);
+}
+
static int filter_zone(struct auth_zone *zone, int flag, struct all_addr *addr_u)
{
- /* No zones specified, no filter */
+ if (find_exclude(zone, flag, addr_u) != NULL)
+ return 0;
+
+ /* No subnets specified, no filter */
if (!zone->subnet)
return 1;

diff --git a/src/dnsmasq.h b/src/dnsmasq.h
index 1896a64..c35f687 100644
--- a/src/dnsmasq.h
+++ b/src/dnsmasq.h
@@ -340,6 +340,7 @@ struct auth_zone {
struct auth_name_list *next;
} *interface_names;
struct addrlist *subnet;
+ struct addrlist *exclude;
struct auth_zone *next;
};

diff --git a/src/option.c b/src/option.c
index d8c57d6..dd831c0 100644
--- a/src/option.c
+++ b/src/option.c
@@ -1906,6 +1906,7 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
new = opt_malloc(sizeof(struct auth_zone));
new->domain = opt_string_alloc(arg);
new->subnet = NULL;
+ new->exclude = NULL;
new->interface_names = NULL;
new->next = daemon->auth_zones;
daemon->auth_zones = new;
@@ -1913,8 +1914,10 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
while ((arg = comma))
{
int prefixlen = 0;
+ int is_exclude = 0;
char *prefix;
struct addrlist *subnet = NULL;
+ struct addrlist *exclude = NULL;
struct all_addr addr;

comma = split(arg);
@@ -1923,6 +1926,12 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
if (prefix && !atoi_check(prefix, &prefixlen))
ret_err(gen_err);

+ if (strstr(arg, "exclude:") == arg)
+ {
+ is_exclude = 1;
+ arg = arg+8;
+ }
+
if (inet_pton(AF_INET, arg, &addr.addr.addr4))
{
subnet = opt_malloc(sizeof(struct addrlist));
@@ -1960,8 +1969,17 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
if (subnet)
{
subnet->addr = addr;
- subnet->next = new->subnet;
- new->subnet = subnet;
+
+ if (is_exclude)
+ {
+ subnet->next = new->exclude;
+ new->exclude = subnet;
+ }
+ else
+ {
+ subnet->next = new->subnet;
+ new->subnet = subnet;
+ }
}
}
break;
--
1.9.1
Simon Kelley
2016-07-24 13:16:48 UTC
Permalink
Patch applied, with a couple of trivial style changes.

Many thanks, looks useful.



Cheers,

Simon.
Post by Mathias Kresin
It can be used, to ensure that answers contain only global routeable IP
addresses (by excluding loopback, RFC1918 and ULA addresses).
---
This feature was discussed > 1Y ago [1] but never implemented. It is supposed to work
exactly as described in the the linked e-mail thread.
[1] http://lists.thekelleys.org.uk/pipermail/dnsmasq-discuss/2015q1/009122.html.
man/dnsmasq.8 | 6 +++++-
src/auth.c | 43 ++++++++++++++++++++++++++++++-------------
src/dnsmasq.h | 1 +
src/option.c | 22 ++++++++++++++++++++--
4 files changed, 56 insertions(+), 16 deletions(-)
diff --git a/man/dnsmasq.8 b/man/dnsmasq.8
index 0521534..8a22179 100644
--- a/man/dnsmasq.8
+++ b/man/dnsmasq.8
@@ -739,7 +739,7 @@ a return code of SERVFAIL. Note that
setting this may affect DNS behaviour in bad ways, it is not an
extra-logging flag and should not be set in production.
.TP
-.B --auth-zone=<domain>[,<subnet>[/<prefix length>][,<subnet>[/<prefix length>].....]]
+.B --auth-zone=<domain>[,<subnet>[/<prefix length>][,<subnet>[/<prefix length>].....][,exclude:<subnet>[/<prefix length>]].....]
Define a DNS zone for which dnsmasq acts as authoritative server. Locally defined DNS records which are in the domain
will be served. If subnet(s) are given, A and AAAA records must be in one of the
specified subnets.
@@ -756,6 +756,10 @@ appear in the zone, but RFC1918 IPv4 addresses which should not.
Interface-name and address-literal subnet specifications may be used
freely in the same --auth-zone declaration.
+It's possible to exclude certain IP addresses from responses. It can be
+used, to make sure that answers contain only global routeable IP
+addresses (by excluding loopback, RFC1918 and ULA addresses).
+
The subnet(s) are also used to define in-addr.arpa and
ip6.arpa domains which are served for reverse-DNS queries. If not
specified, the prefix length defaults to 24 for IPv4 and 64 for IPv6.
diff --git a/src/auth.c b/src/auth.c
index 198572d..6c72995 100644
--- a/src/auth.c
+++ b/src/auth.c
@@ -18,36 +18,53 @@
#ifdef HAVE_AUTH
-static struct addrlist *find_subnet(struct auth_zone *zone, int flag, struct all_addr *addr_u)
+static struct addrlist *find_addrlist(struct addrlist *list, int flag, struct all_addr *addr_u)
{
- struct addrlist *subnet;
-
- for (subnet = zone->subnet; subnet; subnet = subnet->next)
- {
- if (!(subnet->flags & ADDRLIST_IPV6))
+ do {
+ if (!(list->flags & ADDRLIST_IPV6))
{
struct in_addr netmask, addr = addr_u->addr.addr4;
if (!(flag & F_IPV4))
continue;
- netmask.s_addr = htonl(~(in_addr_t)0 << (32 - subnet->prefixlen));
+ netmask.s_addr = htonl(~(in_addr_t)0 << (32 - list->prefixlen));
- if (is_same_net(addr, subnet->addr.addr.addr4, netmask))
- return subnet;
+ if (is_same_net(addr, list->addr.addr.addr4, netmask))
+ return list;
}
#ifdef HAVE_IPV6
- else if (is_same_net6(&(addr_u->addr.addr6), &subnet->addr.addr.addr6, subnet->prefixlen))
- return subnet;
+ else if (is_same_net6(&(addr_u->addr.addr6), &list->addr.addr.addr6, list->prefixlen))
+ return list;
#endif
- }
+ } while ((list = list->next));
+
return NULL;
}
+static struct addrlist *find_subnet(struct auth_zone *zone, int flag, struct all_addr *addr_u)
+{
+ if (!zone->subnet)
+ return NULL;
+
+ return find_addrlist(zone->subnet, flag, addr_u);
+}
+
+static struct addrlist *find_exclude(struct auth_zone *zone, int flag, struct all_addr *addr_u)
+{
+ if (!zone->exclude)
+ return NULL;
+
+ return find_addrlist(zone->exclude, flag, addr_u);
+}
+
static int filter_zone(struct auth_zone *zone, int flag, struct all_addr *addr_u)
{
- /* No zones specified, no filter */
+ if (find_exclude(zone, flag, addr_u) != NULL)
+ return 0;
+
+ /* No subnets specified, no filter */
if (!zone->subnet)
return 1;
diff --giauth-zone: allow to exclude ip addresses from answert a/src/dnsmasq.h b/src/dnsmasq.h
index 1896a64..c35f687 100644
--- a/src/dnsmasq.h
+++ b/src/dnsmasq.h
@@ -340,6 +340,7 @@ struct auth_zone {
struct auth_name_list *next;
} *interface_names;
struct addrlist *subnet;
+ struct addrlist *exclude;
struct auth_zone *next;
};
diff --git a/src/option.c b/src/option.c
index d8c57d6..dd831c0 100644
--- a/src/option.c
+++ b/src/option.c
@@ -1906,6 +1906,7 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
new = opt_malloc(sizeof(struct auth_zone));
new->domain = opt_string_alloc(arg);
new->subnet = NULL;
+ new->exclude = NULL;
new->interface_names = NULL;
new->next = daemon->auth_zones;
daemon->auth_zones = new;
@@ -1913,8 +1914,10 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
while ((arg = comma))
{
int prefixlen = 0;
+ int is_exclude = 0;
char *prefix;
struct addrlist *subnet = NULL;
+ struct addrlist *exclude = NULL;
struct all_addr addr;
comma = split(arg);
@@ -1923,6 +1926,12 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
if (prefix && !atoi_check(prefix, &prefixlen))
ret_err(gen_err);
+ if (strstr(arg, "exclude:") == arg)
+ {
+ is_exclude = 1;
+ arg = arg+8;
+ }
+
if (inet_pton(AF_INET, arg, &addr.addr.addr4))
{
subnet = opt_malloc(sizeof(struct addrlist));
@@ -1960,8 +1969,17 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
if (subnet)
{
subnet->addr = addr;
- subnet->next = new->subnet;
- new->subnet = subnet;
+
+ if (is_exclude)
+ {
+ subnet->next = new->exclude;
+ new->exclude = subnet;
+ }
+ else
+ {
+ subnet->next = new->subnet;
+ new->subnet = subnet;
+ }
}
}
break;
Loading...