Mathias Kresin
2016-06-01 16:07:15 UTC
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;
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
1.9.1