From 094bfaeb4ff69cae99387bc2ea07ff57632c89f5 Mon Sep 17 00:00:00 2001 From: Mathias Kresin Date: Sun, 24 Jul 2016 14:15:22 +0100 Subject: [PATCH] auth-zone: allow to exclude ip addresses from answer. --- man/dnsmasq.8 | 6 ++++- src/auth.c | 63 ++++++++++++++++++++++++++++++++------------------- src/dnsmasq.h | 1 + src/option.c | 21 +++++++++++++++-- 4 files changed, 65 insertions(+), 26 deletions(-) diff --git a/man/dnsmasq.8 b/man/dnsmasq.8 index ac8d921..8910947 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=[,[/][,[/].....]] +.B --auth-zone=[,[/][,[/].....][,exclude:[/]].....] 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 3c5c37f..f1ca2f5 100644 --- a/src/auth.c +++ b/src/auth.c @@ -18,36 +18,53 @@ #ifdef HAVE_AUTH +static struct addrlist *find_addrlist(struct addrlist *list, int flag, struct all_addr *addr_u) +{ + 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 - list->prefixlen)); + + if (is_same_net(addr, list->addr.addr.addr4, netmask)) + return list; + } +#ifdef HAVE_IPV6 + 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) { - struct addrlist *subnet; + if (!zone->subnet) + return NULL; + + return find_addrlist(zone->subnet, flag, addr_u); +} - for (subnet = zone->subnet; subnet; subnet = subnet->next) - { - if (!(subnet->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)); - - if (is_same_net(addr, subnet->addr.addr.addr4, netmask)) - return subnet; - } -#ifdef HAVE_IPV6 - else if (is_same_net6(&(addr_u->addr.addr6), &subnet->addr.addr.addr6, subnet->prefixlen)) - return subnet; -#endif - - } - return NULL; +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)) + return 0; + + /* No subnets specified, no filter */ if (!zone->subnet) return 1; diff --git a/src/dnsmasq.h b/src/dnsmasq.h index 2bda5d0..27385a9 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..6cedef3 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,6 +1914,7 @@ 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 all_addr addr; @@ -1923,6 +1925,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 +1968,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;