Add --ipset option.
diff --git a/src/config.h b/src/config.h
index cff278d..9c8e785 100644
--- a/src/config.h
+++ b/src/config.h
@@ -97,6 +97,10 @@
    a build-dependency on libnetfilter_conntrack, but the resulting binary will
    still run happily on a kernel without conntrack support.
 
+HAVE_IPSET
+    define this to include the ability to selectively add resolved ip addresses
+    to given ipsets.
+
 HAVE_AUTH
    define this to include the facility to act as an authoritative DNS
    server for one or more zones.
@@ -136,7 +140,7 @@
 /* #define HAVE_DBUS */
 /* #define HAVE_IDN */
 /* #define HAVE_CONNTRACK */
-
+/* #define HAVE_IPSET */
 
 
 /* Default locations for important system files. */
@@ -323,6 +327,10 @@
 #undef HAVE_AUTH
 #endif
 
+#ifndef HAVE_LINUX_NETWORK
+#undef HAVE_IPSET
+#endif
+
 /* Define a string indicating which options are in use.
    DNSMASQP_COMPILE_OPTS is only defined in dnsmasq.c */
 
@@ -381,6 +389,10 @@
 "no-"
 #endif
 "conntrack "
+#ifndef HAVE_IPSET
+"no-"
+#endif
+"ipset "
 #ifndef HAVE_AUTH
 "no-"
 #endif
diff --git a/src/dnsmasq.c b/src/dnsmasq.c
index e36835d..43b8cb1 100644
--- a/src/dnsmasq.c
+++ b/src/dnsmasq.c
@@ -213,6 +213,11 @@
 
 #endif
 
+#ifdef HAVE_IPSET
+  if (daemon->ipsets)
+    ipset_init();
+#endif
+
 #ifdef HAVE_LINUX_NETWORK
   netlink_init();
   
diff --git a/src/dnsmasq.h b/src/dnsmasq.h
index 21a309c..a24cf41 100644
--- a/src/dnsmasq.h
+++ b/src/dnsmasq.h
@@ -430,6 +430,12 @@
   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 @@
   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 @@
 		   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, 
@@ -1117,6 +1125,12 @@
 #  endif
 #endif
 
+/* ipset.c */
+#ifdef HAVE_IPSET
+void ipset_init(void);
+int add_to_ipset(const char *setname, const struct all_addr *ipaddr, int flags, int remove);
+#endif
+
 /* helper.c */
 #if defined(HAVE_SCRIPT)
 int create_helper(int event_fd, int err_fd, uid_t uid, gid_t gid, long max_fd);
diff --git a/src/forward.c b/src/forward.c
index fb0b4c4..9a1e15a 100644
--- a/src/forward.c
+++ b/src/forward.c
@@ -439,9 +439,28 @@
 			    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 +513,7 @@
 	  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/ipset.c b/src/ipset.c
new file mode 100644
index 0000000..c16cc85
--- /dev/null
+++ b/src/ipset.c
@@ -0,0 +1,205 @@
+/* ipset.c is Copyright (c) 2013 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; version 2 dated June, 1991, or
+   (at your option) version 3 dated 29 June, 2007.
+ 
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+     
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "dnsmasq.h"
+
+#ifdef HAVE_IPSET
+
+#include <string.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/utsname.h>
+#include <arpa/inet.h>
+#include <linux/version.h>
+#include <linux/netlink.h>
+#include <linux/netfilter/nfnetlink.h>
+#ifndef NFNL_SUBSYS_IPSET
+#define NFNL_SUBSYS_IPSET 6
+#define IPSET_ATTR_DATA 7
+#define IPSET_ATTR_IP 1
+#define IPSET_ATTR_IPADDR_IPV4 1
+#define IPSET_ATTR_IPADDR_IPV6 2
+#define IPSET_ATTR_PROTOCOL 1
+#define IPSET_ATTR_SETNAME 2
+#define IPSET_CMD_ADD 9
+#define IPSET_CMD_DEL 10
+#define IPSET_MAXNAMELEN 32
+#define IPSET_PROTOCOL 6
+#else
+#include <linux/netfilter/ipset/ip_set.h>
+#endif
+
+/* data structure size in here is fixed */
+#define BUFF_SZ 256
+
+#define NL_ALIGN(len) (((len)+3) & ~(3))
+static const struct sockaddr_nl snl = { .nl_family = AF_NETLINK };
+static int ipset_sock, old_kernel;
+static char *buffer;
+
+static inline void add_attr(struct nlmsghdr *nlh, uint16_t type, size_t len, const void *data)
+{
+  struct nlattr *attr = (void *)nlh + NL_ALIGN(nlh->nlmsg_len);
+  uint16_t payload_len = NL_ALIGN(sizeof(struct nlattr)) + len;
+  attr->nla_type = type;
+  attr->nla_len = payload_len;
+  memcpy((void *)attr + NL_ALIGN(sizeof(struct nlattr)), data, len);
+  nlh->nlmsg_len += NL_ALIGN(payload_len);
+}
+
+void ipset_init(void)
+{
+  struct utsname utsname;
+  int version;
+  char *split;
+  
+  if (uname(&utsname) < 0)
+    die(_("failed to find kernel version: %s"), NULL, EC_MISC);
+  
+  split = strtok(utsname.release, ".");
+  version = (split ? atoi(split) : 0);
+  split = strtok(NULL, ".");
+  version = version * 256 + (split ? atoi(split) : 0);
+  split = strtok(NULL, ".");
+  version = version * 256 + (split ? atoi(split) : 0);
+  old_kernel = (version < KERNEL_VERSION(2,6,32));
+  
+  if (old_kernel && (ipset_sock = socket(AF_INET, SOCK_RAW, IPPROTO_RAW)) != -1)
+    return;
+  
+  if (!old_kernel && 
+      (buffer = safe_malloc(BUFF_SZ)) &&
+      (ipset_sock = socket(AF_NETLINK, SOCK_RAW, NETLINK_NETFILTER)) != -1 &&
+      (bind(ipset_sock, (struct sockaddr *)&snl, sizeof(snl)) != -1))
+    return;
+  
+  die (_("failed to create IPset control socket: %s"), NULL, EC_MISC);
+}
+
+static int new_add_to_ipset(const char *setname, const struct all_addr *ipaddr, int af, int remove)
+{
+  struct nlmsghdr *nlh;
+  struct nfgenmsg *nfg;
+  struct nlattr *nested[2];
+  uint8_t proto;
+  int addrsz = INADDRSZ;
+  
+#ifdef HAVE_IPV6
+  if (af == AF_INET6)
+    addrsz = IN6ADDRSZ;
+#endif
+    
+  if (strlen(setname) >= IPSET_MAXNAMELEN) 
+    {
+      errno = ENAMETOOLONG;
+      return -1;
+    }
+  
+  memset(buffer, 0, sizeof(buffer));
+
+  nlh = (struct nlmsghdr *)buffer;
+  nlh->nlmsg_len = NL_ALIGN(sizeof(struct nlmsghdr));
+  nlh->nlmsg_type = (remove ? IPSET_CMD_DEL : IPSET_CMD_ADD) | (NFNL_SUBSYS_IPSET << 8);
+  nlh->nlmsg_flags = NLM_F_REQUEST;
+  
+  nfg = (struct nfgenmsg *)(buffer + nlh->nlmsg_len);
+  nlh->nlmsg_len += NL_ALIGN(sizeof(struct nfgenmsg));
+  nfg->nfgen_family = af;
+  nfg->version = NFNETLINK_V0;
+  nfg->res_id = htons(0);
+  
+  proto = IPSET_PROTOCOL;
+  add_attr(nlh, IPSET_ATTR_PROTOCOL, sizeof(proto), &proto);
+  add_attr(nlh, IPSET_ATTR_SETNAME, strlen(setname) + 1, setname);
+  nested[0] = (struct nlattr *)(buffer + NL_ALIGN(nlh->nlmsg_len));
+  nlh->nlmsg_len += NL_ALIGN(sizeof(struct nlattr));
+  nested[0]->nla_type = NLA_F_NESTED | IPSET_ATTR_DATA;
+  nested[1] = (struct nlattr *)(buffer + NL_ALIGN(nlh->nlmsg_len));
+  nlh->nlmsg_len += NL_ALIGN(sizeof(struct nlattr));
+  nested[1]->nla_type = NLA_F_NESTED | IPSET_ATTR_IP;
+  add_attr(nlh, 
+	   (af == AF_INET ? IPSET_ATTR_IPADDR_IPV4 : IPSET_ATTR_IPADDR_IPV6) | NLA_F_NET_BYTEORDER,
+	   addrsz, &ipaddr->addr);
+  nested[1]->nla_len = (void *)buffer + NL_ALIGN(nlh->nlmsg_len) - (void *)nested[1];
+  nested[0]->nla_len = (void *)buffer + NL_ALIGN(nlh->nlmsg_len) - (void *)nested[0];
+	
+  if (sendto(ipset_sock, buffer, nlh->nlmsg_len, 0, (struct sockaddr *)&snl, sizeof(snl)) < 0)
+    return -1;
+  
+  return 0;
+}
+
+
+static int old_add_to_ipset(const char *setname, const struct all_addr *ipaddr, int remove)
+{
+  socklen_t size;
+  struct ip_set_req_adt_get {
+    unsigned op;
+    unsigned version;
+    union {
+      char name[IPSET_MAXNAMELEN];
+      uint16_t index;
+    } set;
+    char typename[IPSET_MAXNAMELEN];
+  } req_adt_get;
+  struct ip_set_req_adt {
+    unsigned op;
+    uint16_t index;
+    uint32_t ip;
+  } req_adt;
+  
+  if (strlen(setname) >= sizeof(req_adt_get.set.name)) 
+    {
+      errno = ENAMETOOLONG;
+      return -1;
+    }
+  
+  req_adt_get.op = 0x10;
+  req_adt_get.version = 3;
+  strcpy(req_adt_get.set.name, setname);
+  size = sizeof(req_adt_get);
+  if (getsockopt(ipset_sock, SOL_IP, 83, &req_adt_get, &size) < 0)
+    return -1;
+  req_adt.op = remove ? 0x102 : 0x101;
+  req_adt.index = req_adt_get.set.index;
+  req_adt.ip = ntohl(ipaddr->addr.addr4.s_addr);
+  if (setsockopt(ipset_sock, SOL_IP, 83, &req_adt, sizeof(req_adt)) < 0)
+    return -1;
+  
+  return 0;
+}
+
+
+
+int add_to_ipset(const char *setname, const struct all_addr *ipaddr, int flags, int remove)
+{
+  int af = AF_INET;
+
+#ifdef HAVE_IPV6
+  if (flags & F_IPV6)
+    {
+      af = AF_INET6;
+      /* old method only supports IPv4 */
+      if (old_kernel)
+	return -1;
+    }
+#endif
+  
+  return old_kernel ? old_add_to_ipset(setname, ipaddr, remove) : new_add_to_ipset(setname, ipaddr, af, remove);
+}
+
+#endif
diff --git a/src/option.c b/src/option.c
index 3fc3e03..9315694 100644
--- a/src/option.c
+++ b/src/option.c
@@ -127,6 +127,7 @@
 #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 @@
     { "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 @@
   { 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,74 @@
 	daemon->servers = newlist;
 	break;
       }
+
+    case LOPT_IPSET: /* --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..8d55ffd 100644
--- a/src/rfc1035.c
+++ b/src/rfc1035.c
@@ -777,13 +777,18 @@
    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;
   int i, j, qtype, qclass, aqtype, aqclass, ardlen, res, searched_soa = 0;
   unsigned long ttl = 0;
   struct all_addr addr;
-
+#ifdef HAVE_IPSET
+  char **ipsets_cur;
+#else
+  (void)ipsets; /* unused */
+#endif
+  
   cache_start_insert();
 
   /* find_soa is needed for dns_doctor and logging side-effects, so don't call it lazily if there are any. */
@@ -966,6 +971,15 @@
 			      (flags & F_IPV4) &&
 			      private_net(addr.addr.addr4, !option_bool(OPT_LOCAL_REBIND)))
 			    return 1;
+
+#ifdef HAVE_IPSET
+			  if (ipsets && (flags & (F_IPV4 | F_IPV6)))
+			    {
+			      ipsets_cur = ipsets;
+			      while (*ipsets_cur)
+				add_to_ipset(*ipsets_cur++, &addr, flags, 0);
+			    }
+#endif
 			  
 			  newc = cache_insert(name, &addr, now, attl, flags | F_FORWARD);
 			  if (newc && cpp)