The matching logic is the same as for --address. extract_address is
responsible for calling out to add_to_ipset.
---
 src/dnsmasq.h | 12 ++++++++++-
 src/forward.c | 20 ++++++++++++++++++-
 src/option.c  | 64 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 src/rfc1035.c | 14 ++++++++++++-
 4 files changed, 107 insertions(+), 3 deletions(-)

diff --git a/src/dnsmasq.h b/src/dnsmasq.h
index f1dffe8..95bd464 100644
--- a/src/dnsmasq.h
+++ b/src/dnsmasq.h
@@ -430,6 +430,14 @@ struct server {
   struct server *next; 
 };
 
+#if defined(HAVE_IPSET) || defined(HAVE_OLD_IPSET)
+struct ipsets {
+  char **sets;
+  char *domain;
+  struct ipsets *next;
+};
+#endif
+
 struct irec {
   union mysockaddr addr;
   struct in_addr netmask; /* only valid for IPv4 */
@@ -779,6 +787,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 +912,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..ea0bdf4 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; 
 
+#if defined(HAVE_IPSET) || defined(HAVE_OLD_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..15fbd41 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:
+#if !defined(HAVE_IPSET) && !defined(HAVE_OLD_IPSET)
+      ret_err(_("recompile with HAVE_IPSET or HAVE_OLD_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 = 1;
+        for (end = arg; *end; ++end) {
+                if (*end == ',')
+                        ++size;
+        }
+        sets = sets_pos = opt_malloc(sizeof(char *) * size + 1);
+        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..ccdaec5 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;
+#if defined(HAVE_IPSET) || defined(HAVE_OLD_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,13 @@ 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;
+#if defined(HAVE_IPSET) || defined(HAVE_OLD_IPSET)
+                         if (ipsets && (flags & F_IPV4 || flags & F_IPV6)) {
+                               ipsets_cur = ipsets;
+                               while (*ipsets_cur)
+                                       add_to_ipset(*ipsets_cur++, &addr, 
flags & F_IPV4 ? AF_INET : AF_INET6, 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

Reply via email to