--local-service. Default protection from DNS amplification attacks.
diff --git a/CHANGELOG b/CHANGELOG
index 806fc8e..4c89fa9 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -67,7 +67,16 @@
 	    Add --servers-file. Allows dynamic update of upstream servers 
 	    full access to configuration. 
 
- 	    
+	    Add --local-service. Accept DNS queries only from hosts 
+            whose address is on a local subnet, ie a subnet for which 
+            an interface exists on the server. This option
+            only has effect is there are no --interface --except-interface,
+            --listen-address or --auth-server options. It is intended 
+            to be set as a default on installation, to allow
+            unconfigured installations to be useful but also safe from 
+	    being used for DNS amplification attacks.
+
+
 version 2.68
             Use random addresses for DHCPv6 temporary address
             allocations, instead of algorithmically determined stable
diff --git a/man/dnsmasq.8 b/man/dnsmasq.8
index 975ccd4..87730df 100644
--- a/man/dnsmasq.8
+++ b/man/dnsmasq.8
@@ -208,6 +208,14 @@
 the address dnsmasq is listening on. When an interface is specified,
 it may be qualified with "/4" or "/6" to specify only the IPv4 or IPv6
 addresses associated with the interface.
+.TP
+.B --local-service
+Accept DNS queries only from hosts whose address is on a local subnet,
+ie a subnet for which an interface exists on the server. This option
+only has effect is there are no --interface --except-interface,
+--listen-address or --auth-server options. It is intended to be set as
+a default on installation, to allow unconfigured installations to be
+useful but also safe from being used for DNS amplification attacks.
 .TP 
 .B \-2, --no-dhcp-interface=<interface name>
 Do not provide DHCP or TFTP on the specified interface, but do provide DNS service.
diff --git a/src/dnsmasq.h b/src/dnsmasq.h
index a00d95c..6a0391d 100644
--- a/src/dnsmasq.h
+++ b/src/dnsmasq.h
@@ -233,7 +233,8 @@
 #define OPT_DNSSEC_PERMISS 46
 #define OPT_DNSSEC_DEBUG   47
 #define OPT_DNSSEC_NO_SIGN 48 
-#define OPT_LAST           49
+#define OPT_LOCAL_SERVICE  49
+#define OPT_LAST           50
 
 /* extra flags for my_syslog, we use a couple of facilities since they are known 
    not to occupy the same bits as priorities, no matter how syslog.h is set up. */
@@ -966,6 +967,7 @@
   pid_t tcp_pids[MAX_PROCS];
   struct randfd randomsocks[RANDOM_SOCKS];
   int v6pktinfo; 
+  struct addrlist *interface_addrs; /* list of all addresses/prefix lengths associated with all local interfaces */
 
   /* DHCP state */
   int dhcpfd, helperfd, pxefd; 
diff --git a/src/forward.c b/src/forward.c
index 7916716..b396aa4 100644
--- a/src/forward.c
+++ b/src/forward.c
@@ -1081,6 +1081,37 @@
     source_addr.in6.sin6_flowinfo = 0;
 #endif
 
+  /* We can be configured to only accept queries from at-most-one-hop-away addresses. */
+  if (option_bool(OPT_LOCAL_SERVICE))
+    {
+      struct addrlist *addr;
+#ifdef HAVE_IPV6
+      if (listen->family == AF_INET6) 
+	{
+	  for (addr = daemon->interface_addrs; addr; addr = addr->next)
+	    if ((addr->flags & ADDRLIST_IPV6) &&
+		is_same_net6(&addr->addr.addr.addr6, &source_addr.in6.sin6_addr, addr->prefixlen))
+	      break;
+	}
+      else
+#endif
+	{
+	  struct in_addr netmask;
+	  for (addr = daemon->interface_addrs; addr; addr = addr->next)
+	    {
+	      netmask.s_addr = 0xffffffff << (32 - addr->prefixlen);
+	      if (!(addr->flags & ADDRLIST_IPV6) &&
+		  is_same_net(addr->addr.addr.addr4, source_addr.in.sin_addr, netmask))
+		break;
+	    }
+	}
+      if (!addr)
+	{
+	  my_syslog(LOG_WARNING, _("Ignoring query from non-local network"));
+	  return;
+	}
+    }
+		
   if (check_dst)
     {
       struct ifreq ifr;
@@ -1544,6 +1575,37 @@
   
   if (getpeername(confd, (struct sockaddr *)&peer_addr, &peer_len) == -1)
     return packet;
+  
+  /* We can be configured to only accept queries from at-most-one-hop-away addresses. */
+  if (option_bool(OPT_LOCAL_SERVICE))
+    {
+      struct addrlist *addr;
+#ifdef HAVE_IPV6
+      if (peer_addr.sa.sa_family == AF_INET6) 
+	{
+	  for (addr = daemon->interface_addrs; addr; addr = addr->next)
+	    if ((addr->flags & ADDRLIST_IPV6) &&
+		is_same_net6(&addr->addr.addr.addr6, &peer_addr.in6.sin6_addr, addr->prefixlen))
+	      break;
+	}
+      else
+#endif
+	{
+	  struct in_addr netmask;
+	  for (addr = daemon->interface_addrs; addr; addr = addr->next)
+	    {
+	      netmask.s_addr = 0xffffffff << (32 - addr->prefixlen);
+	      if (!(addr->flags & ADDRLIST_IPV6) && 
+		  is_same_net(addr->addr.addr.addr4, peer_addr.in.sin_addr, netmask))
+		break;
+	    }
+	}
+      if (!addr)
+	{
+	  my_syslog(LOG_WARNING, _("Ignoring query from non-local network"));
+	  return packet;
+	}
+    }
 
   while (1)
     {
diff --git a/src/network.c b/src/network.c
index a4380ae..3cc5a4d 100644
--- a/src/network.c
+++ b/src/network.c
@@ -268,7 +268,40 @@
   
   if (!label)
     label = ifr.ifr_name;
+ 
+  /* maintain a list of all addresses on all interfaces for --local-service option */
+  if (option_bool(OPT_LOCAL_SERVICE))
+    {
+      struct addrlist *al;
 
+      if (param->spare)
+	{
+	  al = param->spare;
+	  param->spare = al->next;
+	}
+      else
+	al = whine_malloc(sizeof(struct addrlist));
+      
+      if (al)
+	{
+	  al->next = daemon->interface_addrs;
+	  daemon->interface_addrs = al;
+	  al->prefixlen = prefixlen;
+	  
+	  if (addr->sa.sa_family == AF_INET)
+	    {
+	      al->addr.addr.addr4 = addr->in.sin_addr;
+	      al->flags = 0;
+	    }
+#ifdef HAVE_IPV6
+	  else
+	    {
+	      al->addr.addr.addr6 = addr->in6.sin6_addr;
+	      al->flags = ADDRLIST_IPV6;
+	    } 
+#endif
+	}
+    }
   
 #ifdef HAVE_IPV6
   if (addr->sa.sa_family != AF_INET6 || !IN6_IS_ADDR_LINKLOCAL(&addr->in6.sin6_addr))
@@ -565,6 +598,15 @@
       intname->addr = NULL;
     }
 
+  /* Remove list of addresses of local interfaces */
+  for (addr = daemon->interface_addrs; addr; addr = tmp)
+    {
+      tmp = addr->next;
+      addr->next = spare;
+      spare = addr;
+    }
+  daemon->interface_addrs = NULL;
+  
 #ifdef HAVE_AUTH
   /* remove addresses stored against auth_zone subnets, but not 
    ones configured as address literals */
diff --git a/src/option.c b/src/option.c
index b898231..e8ef5fa 100644
--- a/src/option.c
+++ b/src/option.c
@@ -144,6 +144,7 @@
 #define LOPT_REV_SERV     332
 #define LOPT_SERVERS_FILE 333
 #define LOPT_DNSSEC_CHECK 334
+#define LOPT_LOCAL_SERVICE 335
 
 #ifdef HAVE_GETOPT_LONG
 static const struct option opts[] =  
@@ -175,6 +176,7 @@
     { "domain-suffix", 1, 0, 's' },
     { "interface", 1, 0, 'i' },
     { "listen-address", 1, 0, 'a' },
+    { "local-service", 0, 0, LOPT_LOCAL_SERVICE },
     { "bogus-priv", 0, 0, 'b' },
     { "bogus-nxdomain", 1, 0, 'B' },
     { "selfmx", 0, 0, 'e' },
@@ -448,6 +450,7 @@
   { LOPT_QUIET_DHCP, OPT_QUIET_DHCP, NULL, gettext_noop("Do not log routine DHCP."), NULL },
   { LOPT_QUIET_DHCP6, OPT_QUIET_DHCP6, NULL, gettext_noop("Do not log routine DHCPv6."), NULL },
   { LOPT_QUIET_RA, OPT_QUIET_RA, NULL, gettext_noop("Do not log RA."), NULL },
+  { LOPT_LOCAL_SERVICE, OPT_LOCAL_SERVICE, NULL, gettext_noop("Accept queries only from directly-connected networks"), NULL },
   { 0, 0, NULL, NULL, NULL }
 }; 
 
@@ -4457,6 +4460,11 @@
   else if (option_bool(OPT_DHCP_FQDN))
     die(_("there must be a default domain when --dhcp-fqdn is set"), NULL, EC_BADCONF);
 
+  /* If there's access-control config, then ignore --local-service, it's intended
+     as a system default to keep otherwise unconfigured installations safe. */
+  if (daemon->if_names || daemon->if_except || daemon->if_addrs || daemon->authserver)
+    reset_option_bool(OPT_LOCAL_SERVICE); 
+
   if (testmode)
     {
       fprintf(stderr, "dnsmasq: %s.\n", _("syntax check OK"));