From: "Jason A. Donenfeld" <ja...@zx2c4.com> The matching logic is the same as for --address. extract_address is responsible for calling out to add_to_ipset. --- src/dnsmasq.h | 10 +++++++++- src/forward.c | 20 ++++++++++++++++++- src/option.c | 64 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/rfc1035.c | 24 +++++++++++++++++++++- 4 files changed, 115 insertions(+), 3 deletions(-)
diff --git a/src/dnsmasq.h b/src/dnsmasq.h index 832e04a..9732487 100644 --- a/src/dnsmasq.h +++ b/src/dnsmasq.h @@ -430,6 +430,12 @@ struct server { struct server *next; }; +struct ipsets { + char **sets; + char *domain; + struct ipsets *next; +}; + struct irec { union mysockaddr addr; struct in_addr netmask; /* only valid for IPv4 */ @@ -779,6 +785,7 @@ extern struct daemon { struct iname *if_names, *if_addrs, *if_except, *dhcp_except, *auth_peers; struct bogus_addr *bogus_addr; struct server *servers; + struct ipsets *ipsets; int log_fac; /* log facility */ char *log_file; /* optional log file */ int max_logs; /* queue limit */ @@ -903,7 +910,8 @@ size_t setup_reply(struct dns_header *header, size_t qlen, struct all_addr *addrp, unsigned int flags, unsigned long local_ttl); int extract_addresses(struct dns_header *header, size_t qlen, char *namebuff, - time_t now, int is_sign, int checkrebind, int checking_disabled); + time_t now, char **ipsets, int is_sign, int checkrebind, + int checking_disabled); size_t answer_request(struct dns_header *header, char *limit, size_t qlen, struct in_addr local_addr, struct in_addr local_netmask, time_t now); int check_for_bogus_wildcard(struct dns_header *header, size_t qlen, char *name, diff --git a/src/forward.c b/src/forward.c index fb0b4c4..f5e95a1 100644 --- a/src/forward.c +++ b/src/forward.c @@ -439,9 +439,27 @@ static size_t process_reply(struct dns_header *header, time_t now, struct server *server, size_t n, int check_rebind, int checking_disabled) { unsigned char *pheader, *sizep; + char **sets = 0; int munged = 0, is_sign; size_t plen; +#ifdef HAVE_IPSET + /* Similar algorithm to search_servers. */ + struct ipsets *ipset_pos; + unsigned int namelen = strlen(daemon->namebuff); + unsigned int matchlen = 0; + for (ipset_pos = daemon->ipsets; ipset_pos; ipset_pos = ipset_pos->next) { + unsigned int domainlen = strlen(ipset_pos->domain); + char *matchstart = daemon->namebuff + namelen - domainlen; + if (namelen >= domainlen && hostname_isequal(matchstart, ipset_pos->domain) && + (domainlen == 0 || namelen == domainlen || *(matchstart - 1) == '.' ) && + domainlen >= matchlen) { + matchlen = domainlen; + sets = ipset_pos->sets; + } + } +#endif + /* If upstream is advertising a larger UDP packet size than we allow, trim it so that we don't get overlarge requests for the client. We can't do this for signed packets. */ @@ -494,7 +512,7 @@ static size_t process_reply(struct dns_header *header, time_t now, SET_RCODE(header, NOERROR); } - if (extract_addresses(header, n, daemon->namebuff, now, is_sign, check_rebind, checking_disabled)) + if (extract_addresses(header, n, daemon->namebuff, now, sets, is_sign, check_rebind, checking_disabled)) { my_syslog(LOG_WARNING, _("possible DNS-rebind attack detected: %s"), daemon->namebuff); munged = 1; diff --git a/src/option.c b/src/option.c index 3fc3e03..899f4a1 100644 --- a/src/option.c +++ b/src/option.c @@ -127,6 +127,7 @@ struct myoption { #define LOPT_AUTHSOA 316 #define LOPT_AUTHSFS 317 #define LOPT_AUTHPEER 318 +#define LOPT_IPSET 319 #ifdef HAVE_GETOPT_LONG static const struct option opts[] = @@ -259,6 +260,7 @@ static const struct myoption opts[] = { "auth-soa", 1, 0, LOPT_AUTHSOA }, { "auth-sec-servers", 1, 0, LOPT_AUTHSFS }, { "auth-peer", 1, 0, LOPT_AUTHPEER }, + { "ipset", 1, 0, LOPT_IPSET }, { NULL, 0, 0, 0 } }; @@ -397,6 +399,7 @@ static struct { { LOPT_AUTHSOA, ARG_ONE, "<serial>[,...]", gettext_noop("Set authoritive zone information"), NULL }, { LOPT_AUTHSFS, ARG_DUP, "<NS>[,<NS>...]", gettext_noop("Secondary authoritative nameservers for forward domains"), NULL }, { LOPT_AUTHPEER, ARG_DUP, "<ipaddr>[,<ipaddr>...]", gettext_noop("Peers which are allowed to do zone transfer"), NULL }, + { LOPT_IPSET, ARG_DUP, "/<domain>/<ipset>[,<ipset>...]", gettext_noop("Specify ipsets to which matching domains should be added"), NULL }, { 0, 0, NULL, NULL, NULL } }; @@ -2021,6 +2024,67 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma daemon->servers = newlist; break; } + case LOPT_IPSET: +#ifndef HAVE_IPSET + ret_err(_("recompile with HAVE_IPSET defined to enable ipset directives")); + break; +#else + { + struct ipsets ipsets_head; + struct ipsets *ipsets = &ipsets_head; + int size; + char *end; + char **sets, **sets_pos; + memset(ipsets, 0, sizeof(struct ipsets)); + unhide_metas(arg); + if (arg && *arg == '/') { + arg++; + while ((end = split_chr(arg, '/'))) { + char *domain = NULL; + /* elide leading dots - they are implied in the search algorithm */ + while (*arg == '.') + arg++; + /* # matches everything and becomes a zero length domain string */ + if (strcmp(arg, "#") == 0 || !*arg) + domain = ""; + else if (strlen(arg) != 0 && !(domain = canonicalise_opt(arg))) + option = '?'; + ipsets->next = opt_malloc(sizeof(struct ipsets)); + ipsets = ipsets->next; + memset(ipsets, 0, sizeof(struct ipsets)); + ipsets->domain = domain; + arg = end; + } + } else { + ipsets->next = opt_malloc(sizeof(struct ipsets)); + ipsets = ipsets->next; + memset(ipsets, 0, sizeof(struct ipsets)); + ipsets->domain = ""; + } + if (!arg || !*arg) { + option = '?'; + break; + } + size = 2; + for (end = arg; *end; ++end) { + if (*end == ',') + ++size; + } + sets = sets_pos = opt_malloc(sizeof(char *) * size); + do { + end = split(arg); + *sets_pos++ = opt_string_alloc(arg); + arg = end; + } while (end); + *sets_pos = 0; + for (ipsets = &ipsets_head; ipsets->next; ipsets = ipsets->next) + ipsets->next->sets = sets; + ipsets->next = daemon->ipsets; + daemon->ipsets = ipsets_head.next; + + break; + } +#endif case 'c': /* --cache-size */ { diff --git a/src/rfc1035.c b/src/rfc1035.c index 721cd61..6676bdf 100644 --- a/src/rfc1035.c +++ b/src/rfc1035.c @@ -777,9 +777,14 @@ static int find_soa(struct dns_header *header, size_t qlen, char *name) expired and cleaned out that way. Return 1 if we reject an address because it look like part of dns-rebinding attack. */ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t now, - int is_sign, int check_rebind, int checking_disabled) + char **ipsets, int is_sign, int check_rebind, int checking_disabled) { unsigned char *p, *p1, *endrr, *namep; +#ifdef HAVE_IPSET + char **ipsets_cur; +#else + (void)ipsets; /* unused */ +#endif int i, j, qtype, qclass, aqtype, aqclass, ardlen, res, searched_soa = 0; unsigned long ttl = 0; struct all_addr addr; @@ -966,6 +971,23 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t (flags & F_IPV4) && private_net(addr.addr.addr4, !option_bool(OPT_LOCAL_REBIND))) return 1; +#ifdef HAVE_IPSET + if (ipsets && (flags & F_IPV4 +#ifdef HAVE_IPV6 + || flags & F_IPV6 +#endif + )) { + ipsets_cur = ipsets; + while (*ipsets_cur) + add_to_ipset(*ipsets_cur++, &addr, +#ifdef HAVE_IPV6 + flags & F_IPV4 ? AF_INET : AF_INET6, +#else + AF_INET, +#endif + 0); + } +#endif newc = cache_insert(name, &addr, now, attl, flags | F_FORWARD); if (newc && cpp) -- 1.8.1.2 _______________________________________________ Dnsmasq-discuss mailing list Dnsmasq-discuss@lists.thekelleys.org.uk http://lists.thekelleys.org.uk/mailman/listinfo/dnsmasq-discuss