import of dnsmasq-2.58.tar.gz
diff --git a/src/bpf.c b/src/bpf.c
index 9a77426..cffcf67 100644
--- a/src/bpf.c
+++ b/src/bpf.c
@@ -35,6 +35,13 @@
 #include <net/if_dl.h>
 #include <netinet/if_ether.h>
 
+#ifndef SA_SIZE
+#define SA_SIZE(sa)                                             \
+    (  (!(sa) || ((struct sockaddr *)(sa))->sa_len == 0) ?      \
+        sizeof(long)            :                               \
+        1 + ( (((struct sockaddr *)(sa))->sa_len - 1) | (sizeof(long) - 1) ) )
+#endif
+
 int arp_enumerate(void *parm, int (*callback)())
 {
   int mib[6];
diff --git a/src/cache.c b/src/cache.c
index 77c1972..11ccc0d 100644
--- a/src/cache.c
+++ b/src/cache.c
@@ -922,6 +922,21 @@
 }
 
 #ifdef HAVE_DHCP
+struct in_addr a_record_from_hosts(char *name, time_t now)
+{
+  struct crec *crecp = NULL;
+  struct in_addr ret;
+  
+  while ((crecp = cache_find_by_name(crecp, name, now, F_IPV4)))
+    if (crecp->flags & F_HOSTS)
+      return *(struct in_addr *)&crecp->addr;
+
+  my_syslog(MS_DHCP | LOG_WARNING, _("No IPv4 address found for %s"), name);
+  
+  ret.s_addr = 0;
+  return ret;
+}
+
 void cache_unhash_dhcp(void)
 {
   struct crec *cache, **up;
diff --git a/src/config.h b/src/config.h
index 7394253..a988770 100644
--- a/src/config.h
+++ b/src/config.h
@@ -14,7 +14,7 @@
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
 
-#define VERSION "2.57"
+#define VERSION "2.58"
 
 #define FTABSIZ 150 /* max number of outstanding requests (default) */
 #define MAX_PROCS 20 /* max no children for TCP requests */
@@ -134,6 +134,12 @@
          included when internationalisation support is built, using the 
 	 *-i18n makefile targets, even if HAVE_IDN is not explicitly set.
 
+HAVE_CONNTRACK
+   define this to include code which propogates conntrack marks from
+   incoming DNS queries to the corresponding upstream queries. This adds
+   a build-dependency on libnetfilter_conntrack, but the resulting binary will
+   still run happily on a kernel without conntrack support.
+
 NOTES:
    For Linux you should define 
       HAVE_LINUX_NETWORK
@@ -159,6 +165,7 @@
 /* #define HAVE_BROKEN_RTC */
 /* #define HAVE_DBUS */
 /* #define HAVE_IDN */
+/* #define HAVE_CONNTRACK */
 
 /* Allow TFTP to be disabled with COPTS=-DNO_TFTP */
 #ifdef NO_TFTP
diff --git a/src/conntrack.c b/src/conntrack.c
new file mode 100644
index 0000000..4142c51
--- /dev/null
+++ b/src/conntrack.c
@@ -0,0 +1,90 @@
+/* dnsmasq is Copyright (c) 2000-2011 Simon Kelley
+
+   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_CONNTRACK
+
+#include <libnetfilter_conntrack/libnetfilter_conntrack.h>
+
+static int gotit = 0; /* yuck */
+
+static int callback(enum nf_conntrack_msg_type type, struct nf_conntrack *ct, void *data);
+
+int get_incoming_mark(union mysockaddr *peer_addr, struct all_addr *local_addr, int istcp, unsigned int *markp)
+{
+  struct nf_conntrack *ct;
+  struct nfct_handle *h;
+  
+  gotit = 0;
+  
+  if ((ct = nfct_new())) 
+    {
+      nfct_set_attr_u8(ct, ATTR_L4PROTO, istcp ? IPPROTO_TCP : IPPROTO_UDP);
+      nfct_set_attr_u16(ct, ATTR_PORT_DST, htons(daemon->port));
+      
+#ifdef HAVE_IPV6
+      if (peer_addr->sa.sa_family == AF_INET6)
+	{
+	  nfct_set_attr_u8(ct, ATTR_L3PROTO, AF_INET6);
+	  nfct_set_attr(ct, ATTR_IPV6_SRC, peer_addr->in6.sin6_addr.s6_addr);
+	  nfct_set_attr_u16(ct, ATTR_PORT_SRC, peer_addr->in6.sin6_port);
+	  nfct_set_attr(ct, ATTR_IPV6_DST, local_addr->addr.addr6.s6_addr);
+	}
+      else
+#endif
+	{
+	  nfct_set_attr_u8(ct, ATTR_L3PROTO, AF_INET);
+	  nfct_set_attr_u32(ct, ATTR_IPV4_SRC, peer_addr->in.sin_addr.s_addr);
+	  nfct_set_attr_u16(ct, ATTR_PORT_SRC, peer_addr->in.sin_port);
+	  nfct_set_attr_u32(ct, ATTR_IPV4_DST, local_addr->addr.addr4.s_addr);
+	}
+      
+      
+      if ((h = nfct_open(CONNTRACK, 0))) 
+	{
+	  nfct_callback_register(h, NFCT_T_ALL, callback, (void *)markp);  
+	  if (nfct_query(h, NFCT_Q_GET, ct) == -1)
+	    {
+	      static int warned = 0;
+	      if (!warned)
+		{
+		  my_syslog(LOG_ERR, _("Conntrack connection mark retrieval failed: %s"), strerror(errno));
+		  warned = 1;
+		}
+	    }
+	  nfct_close(h);  
+	}
+      nfct_destroy(ct);
+    }
+
+  return gotit;
+}
+
+static int callback(enum nf_conntrack_msg_type type, struct nf_conntrack *ct, void *data)
+{
+  unsigned int *ret = (unsigned int *)data;
+  *ret = nfct_get_attr_u32(ct, ATTR_MARK);
+  (void)type; /* eliminate warning */
+  gotit = 1;
+
+  return NFCT_CB_CONTINUE;
+}
+
+#endif
+  
+
+
diff --git a/src/dhcp.c b/src/dhcp.c
index 29ddf24..28a3dac 100644
--- a/src/dhcp.c
+++ b/src/dhcp.c
@@ -19,7 +19,6 @@
 #ifdef HAVE_DHCP
 
 struct iface_param {
-  struct in_addr relay, primary;
   struct dhcp_context *current;
   int ind;
 };
@@ -269,8 +268,6 @@
   for (context = daemon->dhcp; context; context = context->next)
     context->current = context;
   
-  parm.relay = mess->giaddr;
-  parm.primary = iface_addr;
   parm.current = NULL;
   parm.ind = iface_index;
 
@@ -299,7 +296,7 @@
     return;
   lease_prune(NULL, now); /* lose any expired leases */
   iov.iov_len = dhcp_reply(parm.current, ifr.ifr_name, iface_index, (size_t)sz, 
-			   now, unicast_dest, &is_inform, pxe_fd);
+			   now, unicast_dest, &is_inform, pxe_fd, iface_addr);
   lease_update_file(now);
   lease_update_dns();
     
@@ -448,38 +445,27 @@
  	context->netmask = netmask;
       }
       
-      if (context->netmask.s_addr)
+      if (context->netmask.s_addr != 0 &&
+	  is_same_net(local, context->start, context->netmask) &&
+	  is_same_net(local, context->end, context->netmask))
 	{
-	  if (is_same_net(local, context->start, context->netmask) &&
-	      is_same_net(local, context->end, context->netmask))
+	  /* link it onto the current chain if we've not seen it before */
+	  if (if_index == param->ind && context->current == context)
 	    {
-	      /* link it onto the current chain if we've not seen it before */
-	      if (if_index == param->ind && context->current == context)
-		{
-		  context->router = local;
-		  context->local = local;
-		  context->current = param->current;
-		  param->current = context;
-		}
-	      
-	      if (!(context->flags & CONTEXT_BRDCAST))
-		{
-		  if (is_same_net(broadcast, context->start, context->netmask))
-		    context->broadcast = broadcast;
-		  else 
-		    context->broadcast.s_addr  = context->start.s_addr | ~context->netmask.s_addr;
-		}
-	    }	
-	  else if (param->relay.s_addr && is_same_net(param->relay, context->start, context->netmask))
+	      context->router = local;
+	      context->local = local;
+	      context->current = param->current;
+	      param->current = context;
+	    }
+	  
+	  if (!(context->flags & CONTEXT_BRDCAST))
 	    {
-	      context->router = param->relay;
-	      context->local = param->primary;
-	      /* fill in missing broadcast addresses for relayed ranges */
-	      if (!(context->flags & CONTEXT_BRDCAST))
+	      if (is_same_net(broadcast, context->start, context->netmask))
+		context->broadcast = broadcast;
+	      else 
 		context->broadcast.s_addr  = context->start.s_addr | ~context->netmask.s_addr;
 	    }
-
-	}
+	}		
     }
 
   return 1;
@@ -505,7 +491,7 @@
       start = ntohl(tmp->start.s_addr);
       end = ntohl(tmp->end.s_addr);
 
-      if (!(tmp->flags & CONTEXT_STATIC) &&
+      if (!(tmp->flags & (CONTEXT_STATIC | CONTEXT_PROXY)) &&
 	  addr >= start &&
 	  addr <= end &&
 	  match_netid(tmp->filter, netids, 1))
@@ -540,7 +526,8 @@
       if (!tmp)
 	for (tmp = context; tmp; tmp = tmp->current)
 	  if (match_netid(tmp->filter, netids, 1) &&
-	      is_same_net(taddr, tmp->start, tmp->netmask))
+	      is_same_net(taddr, tmp->start, tmp->netmask) &&
+	      !(tmp->flags & CONTEXT_PROXY))
 	    break;
     }
   
@@ -626,16 +613,22 @@
   
   for (pass = 0; pass <= 1; pass++)
     for (c = context; c; c = c->current)
-      if (c->flags & CONTEXT_STATIC)
+      if (c->flags & (CONTEXT_STATIC | CONTEXT_PROXY))
 	continue;
       else if (!match_netid(c->filter, netids, pass))
 	continue;
       else
 	{
-	  /* pick a seed based on hwaddr then iterate until we find a free address. */
-	  start.s_addr = addr.s_addr = 
-	    htonl(ntohl(c->start.s_addr) + 
-		  ((j + c->addr_epoch) % (1 + ntohl(c->end.s_addr) - ntohl(c->start.s_addr))));
+	  if (option_bool(OPT_CONSEC_ADDR))
+	    /* seed is largest extant lease addr in this context */
+	    start = lease_find_max_addr(c);
+	  else
+	    /* pick a seed based on hwaddr */
+	    start.s_addr = htonl(ntohl(c->start.s_addr) + 
+				 ((j + c->addr_epoch) % (1 + ntohl(c->end.s_addr) - ntohl(c->start.s_addr))));
+
+	  /* iterate until we find a free address. */
+	  addr = start;
 	  
 	  do {
 	    /* eliminate addresses in use by the server. */
@@ -660,9 +653,6 @@
 		
 		*addrp = addr;
 
-		if (option_bool(OPT_NO_PING))
-		  return 1;
-		
 		/* check if we failed to ping addr sometime in the last
 		   PING_CACHE_TIME seconds. If so, assume the same situation still exists.
 		   This avoids problems when a stupid client bangs
@@ -672,33 +662,51 @@
 		for (count = 0, r = daemon->ping_results; r; r = r->next)
 		  if (difftime(now, r->time) >  (float)PING_CACHE_TIME)
 		    victim = r; /* old record */
-		  else if (++count == max || r->addr.s_addr == addr.s_addr)
-		    return 1;
-		    
-		if (icmp_ping(addr))
-		  /* address in use: perturb address selection so that we are
-		     less likely to try this address again. */
-		  c->addr_epoch++;
-		else
+		  else 
+		    {
+		      count++;
+		      if (r->addr.s_addr == addr.s_addr)
+			{
+			  /* consec-ip mode: we offered this address for another client
+			     (different hash) recently, don't offer it to this one. */
+			  if (option_bool(OPT_CONSEC_ADDR) && r->hash != j)
+			    break;
+			  
+			  return 1;
+			}
+		    }
+
+		if (!r) 
 		  {
-		    /* at this point victim may hold an expired record */
-		    if (!victim)
+		    if ((count < max) && !option_bool(OPT_NO_PING) && icmp_ping(addr))
 		      {
-			if ((victim = whine_malloc(sizeof(struct ping_result))))
+			/* address in use: perturb address selection so that we are
+			   less likely to try this address again. */
+			if (!option_bool(OPT_CONSEC_ADDR))
+			  c->addr_epoch++;
+		      }
+		    else
+		      {
+			/* at this point victim may hold an expired record */
+			if (!victim)
 			  {
-			    victim->next = daemon->ping_results;
-			    daemon->ping_results = victim;
+			    if ((victim = whine_malloc(sizeof(struct ping_result))))
+			      {
+				victim->next = daemon->ping_results;
+				daemon->ping_results = victim;
+			      }
 			  }
+			
+			/* record that this address is OK for 30s 
+			   without more ping checks */
+			if (victim)
+			  {
+			    victim->addr = addr;
+			    victim->time = now;
+			    victim->hash = j;
+			  }
+			return 1;
 		      }
-		    
-		    /* record that this address is OK for 30s 
-		       without more ping checks */
-		    if (victim)
-		      {
-			victim->addr = addr;
-			victim->time = now;
-		      }
-		    return 1;
 		  }
 	      }
 
@@ -709,6 +717,7 @@
 	    
 	  } while (addr.s_addr != start.s_addr);
 	}
+
   return 0;
 }
 
diff --git a/src/dns_protocol.h b/src/dns_protocol.h
index bc18e79..456a997 100644
--- a/src/dns_protocol.h
+++ b/src/dns_protocol.h
@@ -56,7 +56,7 @@
   u16 id;
   u8  hb3,hb4;
   u16 qdcount,ancount,nscount,arcount;
-} ;
+};
 
 #define HB3_QR       0x80
 #define HB3_OPCODE   0x78
diff --git a/src/dnsmasq.c b/src/dnsmasq.c
index 827b0dc..821b7f0 100644
--- a/src/dnsmasq.c
+++ b/src/dnsmasq.c
@@ -40,7 +40,7 @@
 #ifndef LOCALEDIR
 "no-"
 #endif
-"I18N "
+"i18n "
 #ifndef HAVE_DHCP
 "no-"
 #endif
@@ -52,6 +52,10 @@
 "no-"
 #endif
 "TFTP "
+#ifndef HAVE_CONNTRACK
+"no-"
+#endif
+"conntrack "
 #if !defined(LOCALEDIR) && !defined(HAVE_IDN)
 "no-"
 #endif 
@@ -148,6 +152,14 @@
     die(_("TFTP server not available: set HAVE_TFTP in src/config.h"), NULL, EC_BADCONF);
 #endif
 
+#ifdef HAVE_CONNTRACK
+  if (option_bool(OPT_CONNTRACK) && (daemon->query_port != 0 || daemon->osport))
+    die (_("Cannot use --conntrack AND --query-port"), NULL, EC_BADCONF); 
+#else
+  if (option_bool(OPT_CONNTRACK))
+    die(_("Conntrack support not available: set HAVE_CONNTRACK in src/config.h"), NULL, EC_BADCONF);
+#endif
+
 #ifdef HAVE_SOLARIS_NETWORK
   if (daemon->max_logs != 0)
     die(_("asychronous logging is not available under Solaris"), NULL, EC_BADCONF);
@@ -1147,9 +1159,6 @@
 	      unsigned char *buff;
 	      struct server *s; 
 	      int flags;
-	      struct in_addr dst_addr_4;
-	      
-	      dst_addr_4.s_addr = 0;
 	      
 #ifndef NO_FORK
 	      /* Arrange for SIGALARM after CHILD_LIFETIME seconds to
@@ -1168,10 +1177,7 @@
 	      if ((flags = fcntl(confd, F_GETFL, 0)) != -1)
 		fcntl(confd, F_SETFL, flags & ~O_NONBLOCK);
 	      
-	      if (listener->family == AF_INET)
-		dst_addr_4 = iface->addr.in.sin_addr;
-	      
-	      buff = tcp_request(confd, now, dst_addr_4, iface->netmask);
+	      buff = tcp_request(confd, now, &iface->addr, iface->netmask);
 	       
 	      shutdown(confd, SHUT_RDWR);
 	      close(confd);
diff --git a/src/dnsmasq.h b/src/dnsmasq.h
index a386a31..3bf7a44 100644
--- a/src/dnsmasq.h
+++ b/src/dnsmasq.h
@@ -202,7 +202,9 @@
 #define OPT_NO_REBIND      31
 #define OPT_ADD_MAC        32
 #define OPT_DNSSEC         33
-#define OPT_LAST           34
+#define OPT_CONSEC_ADDR    34
+#define OPT_CONNTRACK      35
+#define OPT_LAST           36
 
 /* 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. */
@@ -309,7 +311,6 @@
 #define F_RRNAME    (1u<<17)
 #define F_SERVER    (1u<<18)
 #define F_QUERY     (1u<<19)
-#define F_NSRR      (1u<<20)
 
 
 /* struct sockaddr is not large enough to hold any address,
@@ -517,9 +518,10 @@
 #define DHOPT_HEX              512
 #define DHOPT_VENDOR_MATCH    1024
 #define DHOPT_RFC3925         2048
+#define DHOPT_TAGOK           4096
 
 struct dhcp_boot {
-  char *file, *sname;
+  char *file, *sname, *tftp_sname;
   struct in_addr next_server;
   struct dhcp_netid *netid;
   struct dhcp_boot *next;
@@ -585,6 +587,7 @@
 struct ping_result {
   struct in_addr addr;
   time_t time;
+  unsigned int hash;
   struct ping_result *next;
 };
 
@@ -742,6 +745,7 @@
 			  time_t now, unsigned long ttl, unsigned short flags);
 void cache_reload(void);
 void cache_add_dhcp_entry(char *host_name, struct in_addr *host_address, time_t ttd);
+struct in_addr a_record_from_hosts(char *name, time_t now);
 void cache_unhash_dhcp(void);
 void dump_cache(time_t now);
 char *cache_get_name(struct crec *crecp);
@@ -813,7 +817,7 @@
 void reply_query(int fd, int family, time_t now);
 void receive_query(struct listener *listen, time_t now);
 unsigned char *tcp_request(int confd, time_t now,
-			   struct in_addr local_addr, struct in_addr netmask);
+			   union mysockaddr *local_addr, struct in_addr netmask);
 void server_gone(struct server *server);
 struct frec *get_new_frec(time_t now, int *wait);
 
@@ -875,6 +879,7 @@
 struct dhcp_lease *lease_find_by_client(unsigned char *hwaddr, int hw_len, int hw_type,  
 					unsigned char *clid, int clid_len);
 struct dhcp_lease *lease_find_by_addr(struct in_addr addr);
+struct in_addr lease_find_max_addr(struct dhcp_context *context);
 void lease_prune(struct dhcp_lease *target, time_t now);
 void lease_update_from_configs(void);
 int do_script_run(time_t now);
@@ -884,7 +889,7 @@
 /* rfc2131.c */
 #ifdef HAVE_DHCP
 size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
-		  size_t sz, time_t now, int unicast_dest, int *is_inform, int pxe_fd);
+		  size_t sz, time_t now, int unicast_dest, int *is_inform, int pxe_fd, struct in_addr fallback);
 unsigned char *extended_hwaddr(int hwtype, int hwlen, unsigned char *hwaddr, 
 			       int clid_len, unsigned char *clid, int *len_out);
 #endif
@@ -938,3 +943,9 @@
 void tftp_request(struct listener *listen, time_t now);
 void check_tftp_listeners(fd_set *rset, time_t now);
 #endif
+
+/* conntrack.c */
+#ifdef HAVE_CONNTRACK
+int get_incoming_mark(union mysockaddr *peer_addr, struct all_addr *local_addr,
+		      int istcp, unsigned int *markp);
+#endif
diff --git a/src/forward.c b/src/forward.c
index 92bc6b0..bdf6bfb 100644
--- a/src/forward.c
+++ b/src/forward.c
@@ -207,10 +207,10 @@
 	  }
       }
   
-  if (flags == 0 && !(qtype & F_NSRR) && 
+  if (flags == 0 && !(qtype & F_QUERY) && 
       option_bool(OPT_NODOTS_LOCAL) && !strchr(qdomain, '.') && namelen != 0)
-    /* don't forward simple names, make exception for NS queries and empty name. */
-    flags = F_NXDOMAIN;
+    /* don't forward A or AAAA queries for simple names, except the empty name */
+    flags = F_NOERR;
   
   if (flags == F_NXDOMAIN && check_for_local_domain(qdomain, now))
     flags = F_NOERR;
@@ -243,7 +243,7 @@
   unsigned int flags = 0;
   unsigned int gotname = extract_request(header, plen, daemon->namebuff, NULL);
   struct server *start = NULL;
-    
+  
   /* RFC 4035: sect 4.6 para 2 */
   header->hb4 &= ~HB4_AD;
   
@@ -366,6 +366,16 @@
 		      daemon->rfd_save = forward->rfd4;
 		      fd = forward->rfd4->fd;
 		    }
+
+#ifdef HAVE_CONNTRACK
+		  /* Copy connection mark of incoming query to outgoing connection. */
+		  if (option_bool(OPT_CONNTRACK))
+		    {
+		      unsigned int mark;
+		      if (get_incoming_mark(udpaddr, dst_addr, 0, &mark))
+			setsockopt(fd, SOL_SOCKET, SO_MARK, &mark, sizeof(unsigned int));
+		    }
+#endif
 		}
 	      
 	      if (sendto(fd, (char *)header, plen, 0,
@@ -797,7 +807,7 @@
    about resources for debug mode, when the fork is suppressed: that's
    done by the caller. */
 unsigned char *tcp_request(int confd, time_t now,
-			   struct in_addr local_addr, struct in_addr netmask)
+			   union mysockaddr *local_addr, struct in_addr netmask)
 {
   size_t size = 0;
   int norebind = 0;
@@ -809,7 +819,13 @@
   unsigned char *packet = whine_malloc(65536 + MAXDNAME + RRFIXEDSZ);
   struct dns_header *header;
   struct server *last_server;
+  struct in_addr dst_addr_4;
+  union mysockaddr peer_addr;
+  socklen_t peer_len = sizeof(union mysockaddr);
   
+  if (getpeername(confd, (struct sockaddr *)&peer_addr, &peer_len) == -1)
+    return packet;
+
   while (1)
     {
       if (!packet ||
@@ -831,29 +847,28 @@
       
       if ((gotname = extract_request(header, (unsigned int)size, daemon->namebuff, &qtype)))
 	{
-	  union mysockaddr peer_addr;
-	  socklen_t peer_len = sizeof(union mysockaddr);
+	  char types[20];
 	  
-	  if (getpeername(confd, (struct sockaddr *)&peer_addr, &peer_len) != -1)
-	    {
-	      char types[20];
-
-	      querystr(types, qtype);
-
-	      if (peer_addr.sa.sa_family == AF_INET) 
-		log_query(F_QUERY | F_IPV4 | F_FORWARD, daemon->namebuff, 
-			  (struct all_addr *)&peer_addr.in.sin_addr, types);
+	  querystr(types, qtype);
+	  
+	  if (peer_addr.sa.sa_family == AF_INET) 
+	    log_query(F_QUERY | F_IPV4 | F_FORWARD, daemon->namebuff, 
+		      (struct all_addr *)&peer_addr.in.sin_addr, types);
 #ifdef HAVE_IPV6
-	      else
-		log_query(F_QUERY | F_IPV6 | F_FORWARD, daemon->namebuff, 
-			  (struct all_addr *)&peer_addr.in6.sin6_addr, types);
+	  else
+	    log_query(F_QUERY | F_IPV6 | F_FORWARD, daemon->namebuff, 
+		      (struct all_addr *)&peer_addr.in6.sin6_addr, types);
 #endif
-	    }
 	}
       
+      if (local_addr->sa.sa_family == AF_INET)
+	dst_addr_4 = local_addr->in.sin_addr;
+      else
+	dst_addr_4.s_addr = 0;
+      
       /* m > 0 if answered from cache */
       m = answer_request(header, ((char *) header) + 65536, (unsigned int)size, 
-			 local_addr, netmask, now);
+			 dst_addr_4, netmask, now);
 
       /* Do this by steam now we're not in the select() loop */
       check_log_writer(NULL); 
@@ -866,14 +881,8 @@
 	  char *domain = NULL;
 	   
 	  if (option_bool(OPT_ADD_MAC))
-	    {
-	      union mysockaddr peer_addr;
-	      socklen_t peer_len = sizeof(union mysockaddr);
-	      
-	      if (getpeername(confd, (struct sockaddr *)&peer_addr, &peer_len) != -1)
-		size = add_mac(header, size, ((char *) header) + 65536, &peer_addr);
-	    }
-      
+	    size = add_mac(header, size, ((char *) header) + 65536, &peer_addr);
+	          
 	  if (gotname)
 	    flags = search_servers(now, &addrp, gotname, daemon->namebuff, &type, &domain, &norebind);
 	  
@@ -907,18 +916,38 @@
 		  if (type != (last_server->flags & SERV_TYPE) ||
 		      (type == SERV_HAS_DOMAIN && !hostname_isequal(domain, last_server->domain)))
 		    continue;
-		  
-		  if ((last_server->tcpfd == -1) &&
-		      (last_server->tcpfd = socket(last_server->addr.sa.sa_family, SOCK_STREAM, 0)) != -1 &&
-		      (!local_bind(last_server->tcpfd,  &last_server->source_addr, last_server->interface, 1) ||
-		       connect(last_server->tcpfd, &last_server->addr.sa, sa_len(&last_server->addr)) == -1))
+
+		  if (last_server->tcpfd == -1)
 		    {
-		      close(last_server->tcpfd);
-		      last_server->tcpfd = -1;
+		      if ((last_server->tcpfd = socket(last_server->addr.sa.sa_family, SOCK_STREAM, 0)) == -1)
+			continue;
+		      
+		      if ((!local_bind(last_server->tcpfd,  &last_server->source_addr, last_server->interface, 1) ||
+			   connect(last_server->tcpfd, &last_server->addr.sa, sa_len(&last_server->addr)) == -1))
+			{
+			  close(last_server->tcpfd);
+			  last_server->tcpfd = -1;
+			  continue;
+			}
+
+#ifdef HAVE_CONNTRACK
+		      /* Copy connection mark of incoming query to outgoing connection. */
+		      if (option_bool(OPT_CONNTRACK))
+			{
+			  unsigned int mark;
+			  struct all_addr local;
+#ifdef HAVE_IPV6		      
+			  if (local_addr->sa.sa_family == AF_INET6)
+			    local.addr.addr6 = local_addr->in6.sin6_addr;
+			  else
+#endif
+			    local.addr.addr4 = local_addr->in.sin_addr;
+			  
+			  if (get_incoming_mark(&peer_addr, &local, 1, &mark))
+			    setsockopt(last_server->tcpfd, SOL_SOCKET, SO_MARK, &mark, sizeof(unsigned int));
+			}
+#endif	
 		    }
-		  
-		  if (last_server->tcpfd == -1)	
-		    continue;
 
 		  c1 = size >> 8;
 		  c2 = size;
diff --git a/src/lease.c b/src/lease.c
index cfa7543..0945495 100644
--- a/src/lease.c
+++ b/src/lease.c
@@ -323,6 +323,21 @@
   return NULL;
 }
 
+/* Find largest assigned address in context */
+struct in_addr lease_find_max_addr(struct dhcp_context *context)
+{
+  struct dhcp_lease *lease;
+  struct in_addr addr = context->start;
+  
+  if (!(context->flags & (CONTEXT_STATIC | CONTEXT_PROXY)))
+    for (lease = leases; lease; lease = lease->next)
+      if (((unsigned)ntohl(lease->addr.s_addr)) > ((unsigned)ntohl(context->start.s_addr)) &&
+	  ((unsigned)ntohl(lease->addr.s_addr)) <= ((unsigned)ntohl(context->end.s_addr)) &&
+	  ((unsigned)ntohl(lease->addr.s_addr)) > ((unsigned)ntohl(addr.s_addr)))
+	addr = lease->addr;
+  
+  return addr;
+}
 
 struct dhcp_lease *lease_allocate(struct in_addr addr)
 {
diff --git a/src/log.c b/src/log.c
index baaae7c..08ce62b 100644
--- a/src/log.c
+++ b/src/log.c
@@ -154,6 +154,19 @@
    
   while (entries)
     {
+      /* The data in the payoad is written with a terminating zero character 
+	 and the length reflects this. For a stream connection we need to 
+	 send the zero as a record terminator, but this isn't done for a 
+	 datagram connection, so treat the length as one less than reality 
+	 to elide the zero. If we're logging to a file, turn the zero into 
+	 a newline, and leave the length alone. */
+      int len_adjust = 0;
+
+      if (log_to_file)
+	entries->payload[entries->offset + entries->length - 1] = '\n';
+      else if (connection_type == SOCK_DGRAM)
+	len_adjust = 1;
+
       /* Avoid duplicates over a fork() */
       if (entries->pid != getpid())
 	{
@@ -163,11 +176,11 @@
 
       connection_good = 1;
 
-      if ((rc = write(log_fd, entries->payload + entries->offset, entries->length)) != -1)
+      if ((rc = write(log_fd, entries->payload + entries->offset, entries->length - len_adjust)) != -1)
 	{
 	  entries->length -= rc;
 	  entries->offset += rc;
-	  if (entries->length == 0)
+	  if (entries->length == len_adjust)
 	    {
 	      free_entry();
 	      if (entries_lost != 0)
@@ -366,10 +379,6 @@
       entry->length = len > MAX_MESSAGE ? MAX_MESSAGE : len;
       entry->offset = 0;
       entry->pid = pid;
-
-      /* replace terminator with \n */
-      if (log_to_file)
-	entry->payload[entry->length - 1] = '\n';
     }
   
   /* almost always, logging won't block, so try and write this now,
diff --git a/src/netlink.c b/src/netlink.c
index f6da7db..df3585f 100644
--- a/src/netlink.c
+++ b/src/netlink.c
@@ -222,7 +222,7 @@
 		      }
 		    
 		    if (addrp)
-		      if (!((*callback)(addrp, ifa->ifa_index, ifa->ifa_index, parm)))
+		      if (!((*callback)(addrp, ifa->ifa_scope, ifa->ifa_index, parm)))
 			return 0;
 		  }
 #endif
diff --git a/src/network.c b/src/network.c
index 7b2e905..fe5557a 100644
--- a/src/network.c
+++ b/src/network.c
@@ -896,20 +896,38 @@
 	  source_addr.in.sin_port = htons(daemon->query_port);
 	}
 #ifdef HAVE_IPV6
-      else if (inet_pton(AF_INET6, token, &addr.in6.sin6_addr) > 0)
-	{
+      else 
+	{	
+	  int scope_index = 0;
+	  char *scope_id = strchr(token, '%');
+	  
+	  if (scope_id)
+	    {
+	      *(scope_id++) = 0;
+	      scope_index = if_nametoindex(scope_id);
+	    }
+	  
+	  if (inet_pton(AF_INET6, token, &addr.in6.sin6_addr) > 0)
+	    {
 #ifdef HAVE_SOCKADDR_SA_LEN
-	  source_addr.in6.sin6_len = addr.in6.sin6_len = sizeof(source_addr.in6);
+	      source_addr.in6.sin6_len = addr.in6.sin6_len = sizeof(source_addr.in6);
 #endif
-	  source_addr.in6.sin6_family = addr.in6.sin6_family = AF_INET6;
-	  addr.in6.sin6_port = htons(NAMESERVER_PORT);
-	  source_addr.in6.sin6_addr = in6addr_any;
-	  source_addr.in6.sin6_port = htons(daemon->query_port);
+	      source_addr.in6.sin6_family = addr.in6.sin6_family = AF_INET6;
+	      source_addr.in6.sin6_flowinfo = addr.in6.sin6_flowinfo = 0;
+	      addr.in6.sin6_port = htons(NAMESERVER_PORT);
+	      addr.in6.sin6_scope_id = scope_index;
+	      source_addr.in6.sin6_addr = in6addr_any;
+	      source_addr.in6.sin6_port = htons(daemon->query_port);
+	      source_addr.in6.sin6_scope_id = 0;
+	    }
+	  else
+	    continue;
 	}
-#endif /* IPV6 */
+#else /* IPV6 */
       else
 	continue;
-      
+#endif 
+
       if (old_servers)
 	{
 	  serv = old_servers;
diff --git a/src/option.c b/src/option.c
index 4cee0a2..7e8f47a 100644
--- a/src/option.c
+++ b/src/option.c
@@ -110,6 +110,8 @@
 #define LOPT_LOC_REBND 299
 #define LOPT_ADD_MAC   300
 #define LOPT_DNSSEC    301
+#define LOPT_INCR_ADDR 302
+#define LOPT_CONNTRACK 303
 
 #ifdef HAVE_GETOPT_LONG
 static const struct option opts[] =  
@@ -225,6 +227,8 @@
     { "rebind-localhost-ok", 0, 0,  LOPT_LOC_REBND },
     { "add-mac", 0, 0, LOPT_ADD_MAC },
     { "proxy-dnssec", 0, 0, LOPT_DNSSEC },
+    { "dhcp-sequential-ip", 0, 0,  LOPT_INCR_ADDR },
+    { "conntrack", 0, 0, LOPT_CONNTRACK },
     { NULL, 0, 0, 0 }
   };
 
@@ -345,8 +349,10 @@
   { LOPT_PXE_PROMT, ARG_DUP, "<prompt>,[<timeout>]", gettext_noop("Prompt to send to PXE clients."), NULL },
   { LOPT_PXE_SERV, ARG_DUP, "<service>", gettext_noop("Boot service for PXE menu."), NULL },
   { LOPT_TEST, 0, NULL, gettext_noop("Check configuration syntax."), NULL },
-  { LOPT_ADD_MAC, OPT_ADD_MAC, NULL, gettext_noop("Add requestor's MAC address to forwarded DNS queries"), NULL },
-  { LOPT_DNSSEC, OPT_DNSSEC, NULL, gettext_noop("Proxy DNSSEC validation results from upstream nameservers"), NULL },
+  { LOPT_ADD_MAC, OPT_ADD_MAC, NULL, gettext_noop("Add requestor's MAC address to forwarded DNS queries."), NULL },
+  { LOPT_DNSSEC, OPT_DNSSEC, NULL, gettext_noop("Proxy DNSSEC validation results from upstream nameservers."), NULL },
+  { LOPT_INCR_ADDR, OPT_CONSEC_ADDR, NULL, gettext_noop("Attempt to allocate sequential IP addresses to DHCP clients."), NULL },
+  { LOPT_CONNTRACK, OPT_CONNTRACK, NULL, gettext_noop("Copy connection-track mark from queries to upstream connections."), NULL },
   { 0, 0, NULL, NULL, NULL }
 }; 
 
@@ -1590,6 +1596,10 @@
 	  {
 	    int source_port = 0, serv_port = NAMESERVER_PORT;
 	    char *portno, *source;
+#ifdef HAVE_IPV6
+	    int scope_index = 0;
+	    char *scope_id;
+#endif
 	    
 	    if ((source = split_chr(arg, '@')) && /* is there a source. */
 		(portno = split_chr(source, '#')) &&
@@ -1600,6 +1610,10 @@
 		!atoi_check16(portno, &serv_port))
 	      problem = _("bad port");
 	    
+#ifdef HAVE_IPV6
+	    scope_id = split_chr(arg, '%');
+#endif
+
 	    if ((newlist->addr.in.sin_addr.s_addr = inet_addr(arg)) != (in_addr_t) -1)
 	      {
 		newlist->addr.in.sin_port = htons(serv_port);	
@@ -1627,16 +1641,22 @@
 #ifdef HAVE_IPV6
 	    else if (inet_pton(AF_INET6, arg, &newlist->addr.in6.sin6_addr) > 0)
 	      {
+		if (scope_id && (scope_index = if_nametoindex(scope_id)) == 0)
+		  problem = _("bad interface name");
+		
 		newlist->addr.in6.sin6_port = htons(serv_port);
+		newlist->addr.in6.sin6_scope_id = scope_index;
 		newlist->source_addr.in6.sin6_port = htons(source_port);
+		newlist->source_addr.in6.sin6_scope_id = 0;
 		newlist->addr.sa.sa_family = newlist->source_addr.sa.sa_family = AF_INET6;
+		newlist->addr.in6.sin6_flowinfo = newlist->source_addr.in6.sin6_flowinfo = 0;
 #ifdef HAVE_SOCKADDR_SA_LEN
 		newlist->addr.in6.sin6_len = newlist->source_addr.in6.sin6_len = sizeof(newlist->addr.in6);
 #endif
 		if (source)
 		  {
-		     newlist->flags |= SERV_HAS_SOURCE;
-		     if (inet_pton(AF_INET6, source, &newlist->source_addr.in6.sin6_addr) == 0)
+		    newlist->flags |= SERV_HAS_SOURCE;
+		    if (inet_pton(AF_INET6, source, &newlist->source_addr.in6.sin6_addr) == 0)
 		      {
 #if defined(SO_BINDTODEVICE)
 			newlist->source_addr.in6.sin6_addr = in6addr_any; 
@@ -1652,7 +1672,6 @@
 #endif
 	    else
 	      option = '?'; /* error */
-	    
 	  }
 	
 	serv = newlist;
@@ -1842,6 +1861,7 @@
 	new->netmask.s_addr = 0;
 	new->broadcast.s_addr = 0;
 	new->router.s_addr = 0;
+	new->local.s_addr = 0;
 	new->netid.net = NULL;
 	new->filter = NULL;
 	new->flags = 0;
@@ -2037,7 +2057,9 @@
 		  strcpy(newtag->net, arg+4);
 		  unhide_metas(newtag->net);
 		}
-	      else 
+	      else if (strstr(arg, "tag:") == arg)
+		problem = _("cannot match tags in --dhcp-host");
+	      else
 		{
 		  struct hwaddr_config *newhw = opt_malloc(sizeof(struct hwaddr_config));
 		  if ((newhw->hwaddr_len = parse_hex(a[j], newhw->hwaddr, DHCP_CHADDR_MAX, 
@@ -2218,7 +2240,7 @@
 	  option = '?';
 	else 
 	  {
-	    char *dhcp_file, *dhcp_sname = NULL;
+	    char *dhcp_file, *dhcp_sname = NULL, *tftp_sname = NULL;
 	    struct in_addr dhcp_next_server;
 	    comma = split(arg);
 	    dhcp_file = opt_string_alloc(arg);
@@ -2231,8 +2253,17 @@
 		if (comma)
 		  {
 		    unhide_metas(comma);
-		    if ((dhcp_next_server.s_addr = inet_addr(comma)) == (in_addr_t)-1)
-		      option = '?';
+		    if ((dhcp_next_server.s_addr = inet_addr(comma)) == (in_addr_t)-1) {
+
+		      /*
+		       * The user may have specified the tftp hostname here.
+		       * save it so that it can be resolved/looked up during
+		       * actual dhcp_reply().
+		       */	
+
+		      tftp_sname = opt_string_alloc(comma);
+		      dhcp_next_server.s_addr = 0;
+		    }
 		  }
 	      }
 	    if (option != '?')
@@ -2240,6 +2271,7 @@
 		struct dhcp_boot *new = opt_malloc(sizeof(struct dhcp_boot));
 		new->file = dhcp_file;
 		new->sname = dhcp_sname;
+		new->tftp_sname = tftp_sname;
 		new->next_server = dhcp_next_server;
 		new->netid = id;
 		new->next = daemon->boot_config;
diff --git a/src/rfc1035.c b/src/rfc1035.c
index 889c1f0..25cbeec 100644
--- a/src/rfc1035.c
+++ b/src/rfc1035.c
@@ -642,7 +642,6 @@
 static unsigned char *do_doctor(unsigned char *p, int count, struct dns_header *header, size_t qlen, char *name)
 {
   int i, qtype, qclass, rdlen;
-  unsigned long ttl;
 
   for (i = count; i != 0; i--)
     {
@@ -656,7 +655,7 @@
       
       GETSHORT(qtype, p); 
       GETSHORT(qclass, p);
-      GETLONG(ttl, p);
+      p += 4; /* ttl */
       GETSHORT(rdlen, p);
       
       if (qclass == C_IN && qtype == T_A)
@@ -1044,8 +1043,6 @@
 	return F_IPV6;
       if (qtype == T_ANY)
 	return  F_IPV4 | F_IPV6;
-      if (qtype == T_NS || qtype == T_SOA)
-	return F_QUERY | F_NSRR;
     }
   
   return F_QUERY;
@@ -1101,12 +1098,17 @@
   struct txt_record *txt;
   struct interface_name *intr;
   struct ptr_record *ptr;
-  
-  if ((crecp = cache_find_by_name(NULL, name, now, F_IPV4 | F_IPV6)) &&
+  struct naptr *naptr;
+
+  if ((crecp = cache_find_by_name(NULL, name, now, F_IPV4 | F_IPV6 | F_CNAME)) &&
       (crecp->flags & (F_HOSTS | F_DHCP)))
     return 1;
   
-  for (mx = daemon->mxnames; mx; mx = mx->next)
+  for (naptr = daemon->naptr; naptr; naptr = naptr->next)
+     if (hostname_isequal(name, naptr->name))
+      return 1;
+
+   for (mx = daemon->mxnames; mx; mx = mx->next)
     if (hostname_isequal(name, mx->name))
       return 1;
 
@@ -1309,11 +1311,11 @@
 
   if (find_pseudoheader(header, qlen, NULL, &pheader, &is_sign))
     { 
-      unsigned short udpsz, ext_rcode, flags;
+      unsigned short udpsz, flags;
       unsigned char *psave = pheader;
 
       GETSHORT(udpsz, pheader);
-      GETSHORT(ext_rcode, pheader);
+      pheader += 2; /* ext_rcode */
       GETSHORT(flags, pheader);
       
       sec_reqd = flags & 0x8000; /* do bit */ 
diff --git a/src/rfc2131.c b/src/rfc2131.c
index ed9af7f..b96b197 100644
--- a/src/rfc2131.c
+++ b/src/rfc2131.c
@@ -41,8 +41,8 @@
 		       int mac_len, char *interface, char *string, u32 xid);
 static unsigned char *option_find(struct dhcp_packet *mess, size_t size, int opt_type, int minsize);
 static unsigned char *option_find1(unsigned char *p, unsigned char *end, int opt, int minsize);
-static size_t dhcp_packet_size(struct dhcp_packet *mess, struct dhcp_netid *netid,
-			       unsigned char *agent_id, unsigned char *real_end);
+static void log_tags(struct dhcp_netid *netid, struct dhcp_packet *mess);
+static size_t dhcp_packet_size(struct dhcp_packet *mess, unsigned char *agent_id, unsigned char *real_end);
 static void clear_packet(struct dhcp_packet *mess, unsigned char *end);
 static void do_options(struct dhcp_context *context,
 		       struct dhcp_packet *mess,
@@ -51,11 +51,12 @@
 		       char *hostname, 
 		       char *domain, char *config_domain,
 		       struct dhcp_netid *netid,
-		       struct in_addr subnet_addr,
+		       struct in_addr subnet_addr, 
 		       unsigned char fqdn_flags,
 		       int null_term, int pxearch,
 		       unsigned char *uuid,
-		       int vendor_class_len);
+		       int vendor_class_len,
+		       time_t now);
 
 
 static void match_vendor_opts(unsigned char *opt, struct dhcp_opt *dopt); 
@@ -67,7 +68,7 @@
 
   
 size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
-		  size_t sz, time_t now, int unicast_dest, int *is_inform, int pxe)
+		  size_t sz, time_t now, int unicast_dest, int *is_inform, int pxe, struct in_addr fallback)
 {
   unsigned char *opt, *clid = NULL;
   struct dhcp_lease *ltmp, *lease = NULL;
@@ -85,7 +86,7 @@
   unsigned int time;
   struct dhcp_config *config;
   struct dhcp_netid *netid, *tagif_netid;
-  struct in_addr subnet_addr, fallback, override;
+  struct in_addr subnet_addr, override;
   unsigned short fuzz = 0;
   unsigned int mess_type = 0;
   unsigned char fqdn_flags = 0;
@@ -297,17 +298,43 @@
 		
       if (!context_new)
 	for (context_tmp = daemon->dhcp; context_tmp; context_tmp = context_tmp->next)
-	  if (context_tmp->netmask.s_addr  && 
-	      is_same_net(addr, context_tmp->start, context_tmp->netmask) &&
-	      is_same_net(addr, context_tmp->end, context_tmp->netmask))
-	    {
-	      context_tmp->current = context_new;
-	      context_new = context_tmp;
-	    }
+	  {
+	    struct in_addr netmask = context_tmp->netmask;
+
+	    /* guess the netmask for relayed networks */
+	    if (!(context_tmp->flags & CONTEXT_NETMASK) && context_tmp->netmask.s_addr == 0)
+	      {
+		if (IN_CLASSA(ntohl(context_tmp->start.s_addr)) && IN_CLASSA(ntohl(context_tmp->end.s_addr)))
+		  netmask.s_addr = htonl(0xff000000);
+		else if (IN_CLASSB(ntohl(context_tmp->start.s_addr)) && IN_CLASSB(ntohl(context_tmp->end.s_addr)))
+		  netmask.s_addr = htonl(0xffff0000);
+		else if (IN_CLASSC(ntohl(context_tmp->start.s_addr)) && IN_CLASSC(ntohl(context_tmp->end.s_addr)))
+		  netmask.s_addr = htonl(0xffffff00); 
+	      }
+	    
+	    /* This section fills in context mainly when a client which is on a remote (relayed)
+	       network renews a lease without using the relay, after dnsmasq has restarted. */
+	    if (netmask.s_addr != 0  && 
+		is_same_net(addr, context_tmp->start, netmask) &&
+		is_same_net(addr, context_tmp->end, netmask))
+	      {
+		context_tmp->netmask = netmask;
+		if (context_tmp->local.s_addr == 0)
+		  context_tmp->local = fallback;
+		if (context_tmp->router.s_addr == 0)
+		  context_tmp->router = mess->giaddr;
+	   
+		/* fill in missing broadcast addresses for relayed ranges */
+		if (!(context_tmp->flags & CONTEXT_BRDCAST) && context_tmp->broadcast.s_addr == 0 )
+		  context_tmp->broadcast.s_addr = context_tmp->start.s_addr | ~context_tmp->netmask.s_addr;
+		
+		context_tmp->current = context_new;
+		context_new = context_tmp;
+	      }
+	  }
       
       if (context_new || force)
-	context = context_new;
-      
+	context = context_new; 
     }
   
   if (!context)
@@ -318,9 +345,6 @@
       return 0;
     }
 
-  /* keep _a_ local address available. */
-  fallback = context->local;
-  
   if (option_bool(OPT_LOG_OPTS))
     {
       struct dhcp_context *context_tmp;
@@ -441,9 +465,10 @@
 	  else if (context->netid.net)
 	    {
 	      context->netid.next = netid;
-	      netid = &context->netid; 
-	      tagif_netid = run_tag_if(netid);
+	      tagif_netid = run_tag_if(&context->netid);
 	    }
+
+	  log_tags(tagif_netid, mess);
 	    
 	  if (!message && !nailed)
 	    {
@@ -474,13 +499,13 @@
 	      
 	      clear_packet(mess, end);
 	      do_options(context, mess, end, NULL, hostname, get_domain(mess->yiaddr), 
-			 domain, tagif_netid, subnet_addr, 0, 0, 0, NULL, 0);
+			 domain, netid, subnet_addr, 0, 0, 0, NULL, 0, now);
 	    }
 	}
       
       log_packet("BOOTP", logaddr, mess->chaddr, mess->hlen, iface_name, message, mess->xid);
       
-      return message ? 0 : dhcp_packet_size(mess, tagif_netid, agent_id, real_end);
+      return message ? 0 : dhcp_packet_size(mess, agent_id, real_end);
     }
       
   if ((opt = option_find(mess, sz, OPTION_CLIENT_FQDN, 4)))
@@ -795,7 +820,8 @@
 	  do_encap_opts(&opt71, OPTION_VENDOR_CLASS_OPT, DHOPT_VENDOR_MATCH, mess, end, 0);
 	  
 	  log_packet("PXE", &mess->yiaddr, emac, emac_len, iface_name, (char *)mess->file, mess->xid);
-	  return dhcp_packet_size(mess, tagif_netid, agent_id, real_end);	  
+	  log_tags(tagif_netid, mess);
+	  return dhcp_packet_size(mess, agent_id, real_end);	  
 	}
       
       if ((opt = option_find(mess, sz, OPTION_ARCH, 2)))
@@ -829,8 +855,10 @@
 		     and set discovery_control = 8 */
 		  if (boot)
 		    {
-		      if (boot->next_server.s_addr)
+		      if (boot->next_server.s_addr) 
 			mess->siaddr = boot->next_server;
+		      else if (boot->tftp_sname) 
+			mess->siaddr = a_record_from_hosts(boot->tftp_sname, now);
 		      
 		      if (boot->file)
 			strncpy((char *)mess->file, boot->file, sizeof(mess->file)-1);
@@ -844,7 +872,8 @@
 		  do_encap_opts(pxe_opts(pxearch, tagif_netid, context->local), OPTION_VENDOR_CLASS_OPT, DHOPT_VENDOR_MATCH, mess, end, 0);
 		  
 		  log_packet("PXE", NULL, emac, emac_len, iface_name, ignore ? "proxy-ignored" : "proxy", mess->xid);
-		  return ignore ? 0 : dhcp_packet_size(mess, tagif_netid, agent_id, real_end);	  
+		  log_tags(tagif_netid, mess);
+		  return ignore ? 0 : dhcp_packet_size(mess, agent_id, real_end);	  
 		}
 	    }
 	}
@@ -975,15 +1004,16 @@
       if (message || !(context = narrow_context(context, mess->yiaddr, tagif_netid)))
 	return 0;
 
-      log_packet("DHCPOFFER" , &mess->yiaddr, emac, emac_len, iface_name, NULL, mess->xid);
-
       if (context->netid.net)
 	{
 	  context->netid.next = netid;
-	  netid = &context->netid;
-	  tagif_netid = run_tag_if(netid);
+	  tagif_netid = run_tag_if(&context->netid);
 	}
-       
+
+      log_tags(tagif_netid, mess);
+      
+      log_packet("DHCPOFFER" , &mess->yiaddr, emac, emac_len, iface_name, NULL, mess->xid);
+      
       time = calc_time(context, config, option_find(mess, sz, OPTION_LEASE_TIME, 4));
       clear_packet(mess, end);
       option_put(mess, end, OPTION_MESSAGE_TYPE, 1, DHCPOFFER);
@@ -996,9 +1026,9 @@
 	  option_put(mess, end, OPTION_T2, 4, (time*7)/8);
 	}
       do_options(context, mess, end, req_options, offer_hostname, get_domain(mess->yiaddr), 
-		 domain, tagif_netid, subnet_addr, fqdn_flags, borken_opt, pxearch, uuid, vendor_class_len);
+		 domain, netid, subnet_addr, fqdn_flags, borken_opt, pxearch, uuid, vendor_class_len, now);
       
-      return dhcp_packet_size(mess, tagif_netid, agent_id, real_end);
+      return dhcp_packet_size(mess, agent_id, real_end);
       
     case DHCPREQUEST:
       if (ignore || have_config(config, CONFIG_DISABLE))
@@ -1029,12 +1059,30 @@
 		  
 		  if (!context)
 		    {
-		      /* In auth mode, a REQUEST sent to the wrong server
-			 should be faulted, so that the client establishes 
-			 communication with us, otherwise, silently ignore. */
-		      if (!option_bool(OPT_AUTHORITATIVE))
-			return 0;
-		      message = _("wrong server-ID");
+		      /* Handle very strange configs where clients have more than one route to the server.
+			 If a clients idea of its server-id matches any of our DHCP interfaces, we let it pass.
+			 Have to set override to make sure we echo back the correct server-id */
+		      struct irec *intr;
+		      
+		      enumerate_interfaces();
+
+		      for (intr = daemon->interfaces; intr; intr = intr->next)
+			if (intr->addr.sa.sa_family == AF_INET &&
+			    intr->addr.in.sin_addr.s_addr == option_addr(opt).s_addr &&
+			    intr->tftp_ok)
+			  break;
+
+		      if (intr)
+			override = intr->addr.in.sin_addr;
+		      else
+			{
+			  /* In auth mode, a REQUEST sent to the wrong server
+			     should be faulted, so that the client establishes 
+			     communication with us, otherwise, silently ignore. */
+			  if (!option_bool(OPT_AUTHORITATIVE))
+			    return 0;
+			  message = _("wrong server-ID");
+			}
 		    }
 		}
 
@@ -1173,9 +1221,10 @@
 	  if (context->netid.net)
 	    {
 	      context->netid.next = netid;
-	      netid = &context->netid;
-	      tagif_netid = run_tag_if(netid);
+	      tagif_netid = run_tag_if( &context->netid);
 	    }
+
+	  log_tags(tagif_netid, mess);
 	  
 #ifdef HAVE_SCRIPT
 	  if (do_classes && daemon->lease_change_command)
@@ -1278,10 +1327,10 @@
 	      option_put(mess, end, OPTION_T2, 4, ((time/8)*7) - fuzz);
 	    }
 	  do_options(context, mess, end, req_options, hostname, get_domain(mess->yiaddr), 
-		     domain, tagif_netid, subnet_addr, fqdn_flags, borken_opt, pxearch, uuid, vendor_class_len);
+		     domain, netid, subnet_addr, fqdn_flags, borken_opt, pxearch, uuid, vendor_class_len, now);
 	}
 
-      return dhcp_packet_size(mess, tagif_netid, agent_id, real_end); 
+      return dhcp_packet_size(mess, agent_id, real_end); 
       
     case DHCPINFORM:
       if (ignore || have_config(config, CONFIG_DISABLE))
@@ -1304,15 +1353,16 @@
       
       if (!hostname && (hostname = host_from_dns(mess->ciaddr)))
 	domain = get_domain(mess->ciaddr);
-
-      log_packet("DHCPACK", &mess->ciaddr, emac, emac_len, iface_name, hostname, mess->xid);
       
       if (context && context->netid.net)
 	{
 	  context->netid.next = netid;
-	  netid = &context->netid;
-	  tagif_netid = run_tag_if(netid);
+	  tagif_netid = run_tag_if( &context->netid);
 	}
+
+      log_tags(tagif_netid, mess);
+      
+      log_packet("DHCPACK", &mess->ciaddr, emac, emac_len, iface_name, hostname, mess->xid);
       
       if (lease)
 	{
@@ -1337,10 +1387,10 @@
 	}
 
       do_options(context, mess, end, req_options, hostname, get_domain(mess->ciaddr),
-		 domain, tagif_netid, subnet_addr, fqdn_flags, borken_opt, pxearch, uuid, vendor_class_len);
+		 domain, netid, subnet_addr, fqdn_flags, borken_opt, pxearch, uuid, vendor_class_len, now);
       
       *is_inform = 1; /* handle reply differently */
-      return dhcp_packet_size(mess, tagif_netid, agent_id, real_end); 
+      return dhcp_packet_size(mess, agent_id, real_end); 
     }
   
   return 0;
@@ -1432,7 +1482,7 @@
 {
   if (override.s_addr != 0)
     return override;
-  else if (context)
+  else if (context && context->local.s_addr != 0)
     return context->local;
   else
     return fallback;
@@ -1672,30 +1722,16 @@
   return NULL;
 }
 
-static size_t dhcp_packet_size(struct dhcp_packet *mess, struct dhcp_netid *netid,
-			       unsigned char *agent_id, unsigned char *real_end)
+static void log_tags(struct dhcp_netid *netid, struct dhcp_packet *mess)
 {
-  unsigned char *p = dhcp_skip_opts(&mess->options[0] + sizeof(u32));
-  unsigned char *overload;
-  size_t ret;
-  struct dhcp_netid_list *id_list;
-  struct dhcp_netid *n;
-
-  /* move agent_id back down to the end of the packet */
-  if (agent_id)
-    {
-      memmove(p, agent_id, real_end - agent_id);
-      p += real_end - agent_id;
-      memset(p, 0, real_end - p); /* in case of overlap */
-    }
-  
-  /* We do logging too */
   if (netid && option_bool(OPT_LOG_OPTS))
     {
       char *s = daemon->namebuff;
       for (*s = 0; netid; netid = netid->next)
 	{
 	  /* kill dupes. */
+	  struct dhcp_netid *n;
+	  
 	  for (n = netid->next; n; n = n->next)
 	    if (strcmp(netid->net, n->net) == 0)
 	      break;
@@ -1709,7 +1745,22 @@
 	}
       my_syslog(MS_DHCP | LOG_INFO, _("%u tags: %s"), ntohl(mess->xid), s);
     } 
-   
+} 
+
+static size_t dhcp_packet_size(struct dhcp_packet *mess, unsigned char *agent_id, unsigned char *real_end)
+{
+  unsigned char *p = dhcp_skip_opts(&mess->options[0] + sizeof(u32));
+  unsigned char *overload;
+  size_t ret;
+  
+  /* move agent_id back down to the end of the packet */
+  if (agent_id)
+    {
+      memmove(p, agent_id, real_end - agent_id);
+      p += real_end - agent_id;
+      memset(p, 0, real_end - p); /* in case of overlap */
+    }
+  
   /* add END options to the regions. */
   overload = find_overload(mess);
   
@@ -1734,12 +1785,6 @@
 
   *p++ = OPTION_END;
   
-  for (id_list = daemon->force_broadcast; id_list; id_list = id_list->next)
-    if ((!id_list->list) || match_netid(id_list->list, netid, 0))
-      break;
-  if (id_list)
-    mess->flags |= htons(0x8000); /* force broadcast */
-  
   if (option_bool(OPT_LOG_OPTS))
     {
       if (mess->siaddr.s_addr != 0)
@@ -1889,20 +1934,14 @@
   return 0;
 }
 
-static struct dhcp_opt *option_find2(struct dhcp_netid *netid, struct dhcp_opt *opts, int opt)
+static struct dhcp_opt *option_find2(int opt)
 {
-  struct dhcp_opt *tmp;  
-  for (tmp = opts; tmp; tmp = tmp->next)
-    if (tmp->opt == opt && !(tmp->flags & (DHOPT_ENCAPSULATE | DHOPT_VENDOR | DHOPT_RFC3925)))
-      if (match_netid(tmp->netid, netid, 0))
-	return tmp;
-
-  /* No match, look for one without a netid */
-  for (tmp = opts; tmp; tmp = tmp->next)
-    if (tmp->opt == opt && !(tmp->flags & (DHOPT_ENCAPSULATE | DHOPT_VENDOR | DHOPT_RFC3925)))
-      if (match_netid(tmp->netid, netid, 1))
-	return tmp;
-	      	      
+  struct dhcp_opt *opts;
+  
+  for (opts = daemon->dhcp_opts; opts; opts = opts->next)
+    if (opts->opt == opt && (opts->flags & DHOPT_TAGOK))
+      return opts;
+  
   return NULL;
 }
 
@@ -2157,7 +2196,8 @@
 		       unsigned char fqdn_flags,
 		       int null_term, int pxe_arch,
 		       unsigned char *uuid,
-		       int vendor_class_len)
+		       int vendor_class_len,
+		       time_t now)
 {
   struct dhcp_opt *opt, *config_opts = daemon->dhcp_opts;
   struct dhcp_boot *boot;
@@ -2166,7 +2206,53 @@
   unsigned char f0 = 0, s0 = 0;
   int done_file = 0, done_server = 0;
   int done_vendor_class = 0;
+  struct dhcp_netid *tagif;
+  struct dhcp_netid_list *id_list;
 
+  /* flag options which are valid with the current tag set (sans context tags) */
+  tagif = run_tag_if(netid);
+  for (opt = config_opts; opt; opt = opt->next)
+    {
+      opt->flags &= ~DHOPT_TAGOK;
+      if (!(opt->flags & (DHOPT_ENCAPSULATE | DHOPT_VENDOR | DHOPT_RFC3925)) &&
+	  match_netid(opt->netid, tagif, 0))
+	opt->flags |= DHOPT_TAGOK;
+    }
+
+  /* now flag options which are valid, including the context tags,
+     otherwise valid options are inhibited if we found a higher priotity one above */
+  if (context && context->netid.net)
+    {
+      context->netid.next = netid;
+      tagif = run_tag_if(&context->netid);
+      
+      for (opt = config_opts; opt; opt = opt->next)
+	if (!(opt->flags & (DHOPT_ENCAPSULATE | DHOPT_VENDOR | DHOPT_RFC3925 | DHOPT_TAGOK)) &&
+	    match_netid(opt->netid, tagif, 0))
+	  {
+	    struct dhcp_opt *tmp;  
+	    for (tmp = config_opts; tmp; tmp = tmp->next) 
+	      if (tmp->opt == opt->opt && opt->netid && (tmp->flags & DHOPT_TAGOK))
+		break;
+	    if (!tmp)
+	      opt->flags |= DHOPT_TAGOK;
+	  }      
+    }
+  
+  /* now flag untagged options which are not overridden by tagged ones */
+  for (opt = config_opts; opt; opt = opt->next)
+    if (!(opt->flags & (DHOPT_ENCAPSULATE | DHOPT_VENDOR | DHOPT_RFC3925 | DHOPT_TAGOK)) && !opt->netid)
+      {
+	struct dhcp_opt *tmp;  
+	for (tmp = config_opts; tmp; tmp = tmp->next) 
+	  if (tmp->opt == opt->opt && (tmp->flags & DHOPT_TAGOK))
+	    break;
+	if (!tmp)
+	  opt->flags |= DHOPT_TAGOK;
+	else if (!tmp->netid)
+	  my_syslog(MS_DHCP | LOG_WARNING, _("Ignoring duplicate dhcp-option %d"), tmp->opt); 
+      }
+	
   if (config_domain && (!domain || !hostname_isequal(domain, config_domain)))
     my_syslog(MS_DHCP | LOG_WARNING, _("Ignoring domain %s for DHCP host name %s"), config_domain, hostname);
   
@@ -2191,6 +2277,12 @@
 	}
     }
       
+  for (id_list = daemon->force_broadcast; id_list; id_list = id_list->next)
+    if ((!id_list->list) || match_netid(id_list->list, netid, 0))
+      break;
+  if (id_list)
+    mess->flags |= htons(0x8000); /* force broadcast */
+  
   if (context)
     mess->siaddr = context->local;
   
@@ -2200,7 +2292,7 @@
      provide an manual option to disable it.
      Some PXE ROMs have bugs (surprise!) and need zero-terminated 
      names, so we always send those.  */
-  if ((boot = find_boot(netid)))
+  if ((boot = find_boot(tagif)))
     {
       if (boot->sname)
 	{	  
@@ -2222,8 +2314,10 @@
 	    strncpy((char *)mess->file, boot->file, sizeof(mess->file)-1);
 	}
       
-      if (boot->next_server.s_addr)
+      if (boot->next_server.s_addr) 
 	mess->siaddr = boot->next_server;
+      else if (boot->tftp_sname)
+	mess->siaddr = a_record_from_hosts(boot->tftp_sname, now);
     }
   else
     /* Use the values of the relevant options if no dhcp-boot given and
@@ -2232,20 +2326,20 @@
        dhcp-optsfile. */
     {
       if ((!req_options || !in_list(req_options, OPTION_FILENAME)) &&
-	  (opt = option_find2(netid, config_opts, OPTION_FILENAME)) && !(opt->flags & DHOPT_FORCE))
+	  (opt = option_find2(OPTION_FILENAME)) && !(opt->flags & DHOPT_FORCE))
 	{
 	  strncpy((char *)mess->file, (char *)opt->val, sizeof(mess->file)-1);
 	  done_file = 1;
 	}
       
       if ((!req_options || !in_list(req_options, OPTION_SNAME)) &&
-	  (opt = option_find2(netid, config_opts, OPTION_SNAME)) && !(opt->flags & DHOPT_FORCE))
+	  (opt = option_find2(OPTION_SNAME)) && !(opt->flags & DHOPT_FORCE))
 	{
 	  strncpy((char *)mess->sname, (char *)opt->val, sizeof(mess->sname)-1);
 	  done_server = 1;
 	}
       
-      if ((opt = option_find2(netid, config_opts, OPTION_END)))
+      if ((opt = option_find2(OPTION_END)))
 	mess->siaddr.s_addr = ((struct in_addr *)opt->val)->s_addr;	
     }
         
@@ -2273,36 +2367,36 @@
   /* replies to DHCPINFORM may not have a valid context */
   if (context)
     {
-      if (!option_find2(netid, config_opts, OPTION_NETMASK))
+      if (!option_find2(OPTION_NETMASK))
 	option_put(mess, end, OPTION_NETMASK, INADDRSZ, ntohl(context->netmask.s_addr));
   
       /* May not have a "guessed" broadcast address if we got no packets via a relay
 	 from this net yet (ie just unicast renewals after a restart */
       if (context->broadcast.s_addr &&
-	  !option_find2(netid, config_opts, OPTION_BROADCAST))
+	  !option_find2(OPTION_BROADCAST))
 	option_put(mess, end, OPTION_BROADCAST, INADDRSZ, ntohl(context->broadcast.s_addr));
       
       /* Same comments as broadcast apply, and also may not be able to get a sensible
 	 default when using subnet select.  User must configure by steam in that case. */
       if (context->router.s_addr &&
 	  in_list(req_options, OPTION_ROUTER) &&
-	  !option_find2(netid, config_opts, OPTION_ROUTER))
+	  !option_find2(OPTION_ROUTER))
 	option_put(mess, end, OPTION_ROUTER, INADDRSZ, ntohl(context->router.s_addr));
       
       if (in_list(req_options, OPTION_DNSSERVER) &&
-	  !option_find2(netid, config_opts, OPTION_DNSSERVER))
+	  !option_find2(OPTION_DNSSERVER))
 	option_put(mess, end, OPTION_DNSSERVER, INADDRSZ, ntohl(context->local.s_addr));
     }
 
   if (domain && in_list(req_options, OPTION_DOMAINNAME) && 
-      !option_find2(netid, config_opts, OPTION_DOMAINNAME))
+      !option_find2(OPTION_DOMAINNAME))
     option_put_string(mess, end, OPTION_DOMAINNAME, domain, null_term);
  
   /* Note that we ignore attempts to set the fqdn using --dhc-option=81,<name> */
   if (hostname)
     {
       if (in_list(req_options, OPTION_HOSTNAME) &&
-	  !option_find2(netid, config_opts, OPTION_HOSTNAME))
+	  !option_find2(OPTION_HOSTNAME))
 	option_put_string(mess, end, OPTION_HOSTNAME, hostname, null_term);
       
       if (fqdn_flags != 0)
@@ -2351,6 +2445,10 @@
     {
       int optno = opt->opt;
 
+      /* netids match and not encapsulated? */
+      if (!(opt->flags & DHOPT_TAGOK))
+	continue;
+      
       /* was it asked for, or are we sending it anyway? */
       if (!(opt->flags & DHOPT_FORCE) && !in_list(req_options, optno))
 	continue;
@@ -2369,10 +2467,6 @@
       if (optno == OPTION_FILENAME && done_file)
 	continue;
       
-      /* netids match and not encapsulated? */
-      if (opt != option_find2(netid, config_opts, optno))
-	continue;
-      
       /* For the options we have default values on
 	 dhc-option=<optionno> means "don't include this option"
 	 not "include a zero-length option" */
@@ -2439,7 +2533,7 @@
 		continue;
 	      
 	      o->flags |= DHOPT_ENCAP_DONE;
-	      if (match_netid(o->netid, netid, 1) &&
+	      if (match_netid(o->netid, tagif, 1) &&
 		  ((o->flags & DHOPT_FORCE) || in_list(req_options, outer)))
 		{
 		  o->flags |= DHOPT_ENCAP_MATCH;
@@ -2473,12 +2567,12 @@
 	}
     }      
 
-  force_encap = prune_vendor_opts(netid);
+  force_encap = prune_vendor_opts(tagif);
   
   if (context && pxe_arch != -1)
     {
       pxe_misc(mess, end, uuid);
-      config_opts = pxe_opts(pxe_arch, netid, context->local);
+      config_opts = pxe_opts(pxe_arch, tagif, context->local);
     }
 
   if ((force_encap || in_list(req_options, OPTION_VENDOR_CLASS_OPT)) &&
diff --git a/src/tftp.c b/src/tftp.c
index 789c444..9cecdd9 100644
--- a/src/tftp.c
+++ b/src/tftp.c
@@ -222,17 +222,22 @@
     if (strcmp(ir->interface, name) == 0)
       special = 1;
 
-#ifdef HAVE_SOCKADDR_SA_LEN
-  addr.sa.sa_len = sa_len(&addr);
-#endif
-
   if (listen->family == AF_INET)
-    addr.in.sin_port = htons(port);
+    {
+      addr.in.sin_port = htons(port);
+#ifdef HAVE_SOCKADDR_SA_LEN
+      addr.in.sin_len = sizeof(addr.in);
+#endif
+    }
 #ifdef HAVE_IPV6
   else
     {
       addr.in6.sin6_port = htons(port);
       addr.in6.sin6_flowinfo = 0;
+      addr.in6.sin6_scope_id = 0;
+#ifdef HAVE_SOCKADDR_SA_LEN
+      addr.in6.sin6_len = sizeof(addr.in6);
+#endif
     }
 #endif
 
@@ -260,7 +265,7 @@
   /* if we have a nailed-down range, iterate until we find a free one. */
   while (1)
     {
-      if (bind(transfer->sockfd, &addr.sa, sizeof(addr)) == -1 ||
+      if (bind(transfer->sockfd, &addr.sa, sa_len(&addr)) == -1 ||
 #if defined(IP_MTU_DISCOVER) && defined(IP_PMTUDISC_DONT)
 	  setsockopt(transfer->sockfd, SOL_IP, IP_MTU_DISCOVER, &mtuflag, sizeof(mtuflag)) == -1 ||
 #endif
@@ -685,15 +690,13 @@
 	  for (i = 0, newcarrylf = 0; i < size; i++)
 	    if (mess->data[i] == '\n' && ( i != 0 || !transfer->carrylf))
 	      {
-		if (size == transfer->blocksize)
-		  {
-		    transfer->expansion++;
-		    if (i == size - 1)
-		      newcarrylf = 1; /* don't expand LF again if it moves to the next block */
-		  }
-		else
+		transfer->expansion++;
+
+		if (size != transfer->blocksize)
 		  size++; /* room in this block */
-	      
+		else  if (i == size - 1)
+		  newcarrylf = 1; /* don't expand LF again if it moves to the next block */
+		  
 		/* make space and insert CR */
 		memmove(&mess->data[i+1], &mess->data[i], size - (i + 1));
 		mess->data[i] = '\r';
diff --git a/src/util.c b/src/util.c
index e64f1a6..e11360b 100644
--- a/src/util.c
+++ b/src/util.c
@@ -333,7 +333,15 @@
     }
   else if (addr->sa.sa_family == AF_INET6)
     {
+      char name[IF_NAMESIZE];
       inet_ntop(AF_INET6, &addr->in6.sin6_addr, buf, ADDRSTRLEN);
+      if (addr->in6.sin6_scope_id != 0 &&
+	  if_indextoname(addr->in6.sin6_scope_id, name) &&
+	  strlen(buf) + strlen(name) + 2 <= ADDRSTRLEN)
+	{
+	  strcat(buf, "%");
+	  strcat(buf, name);
+	}
       port = ntohs(addr->in6.sin6_port);
     }
 #else