dnsmasq: Direct import of version 2.51

Signed-off-by: San Mehat <san@google.com>
diff --git a/src/bpf.c b/src/bpf.c
new file mode 100755
index 0000000..abde78c
--- /dev/null
+++ b/src/bpf.c
@@ -0,0 +1,254 @@
+/* dnsmasq is Copyright (c) 2000-2009 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"
+
+#if defined(HAVE_BSD_NETWORK) || defined(HAVE_SOLARIS_NETWORK)
+
+static struct iovec ifconf = {
+  .iov_base = NULL,
+  .iov_len = 0
+};
+
+static struct iovec ifreq = {
+  .iov_base = NULL,
+  .iov_len = 0
+};
+
+int iface_enumerate(void *parm, int (*ipv4_callback)(), int (*ipv6_callback)())
+{
+  char *ptr;
+  struct ifreq *ifr;
+  struct ifconf ifc;
+  int fd, errsav, ret = 0;
+  int lastlen = 0;
+  size_t len = 0;
+  
+  if ((fd = socket(PF_INET, SOCK_DGRAM, 0)) == -1)
+    return 0;
+  
+  while(1)
+    {
+      len += 10*sizeof(struct ifreq);
+      
+      if (!expand_buf(&ifconf, len))
+	goto err;
+      
+      ifc.ifc_len = len;
+      ifc.ifc_buf = ifconf.iov_base;
+      
+      if (ioctl(fd, SIOCGIFCONF, &ifc) == -1)
+	{
+	  if (errno != EINVAL || lastlen != 0)
+	    goto err;
+	}
+      else
+	{
+	  if (ifc.ifc_len == lastlen)
+	    break; /* got a big enough buffer now */
+	  lastlen = ifc.ifc_len;
+	}
+    }
+  
+  for (ptr = ifc.ifc_buf; ptr < (char *)(ifc.ifc_buf + ifc.ifc_len); ptr += len)
+    {
+      /* subsequent entries may not be aligned, so copy into
+	 an aligned buffer to avoid nasty complaints about 
+	 unaligned accesses. */
+
+      len = sizeof(struct ifreq);
+      
+#ifdef HAVE_SOCKADDR_SA_LEN
+      ifr = (struct ifreq *)ptr;
+      if (ifr->ifr_addr.sa_len > sizeof(ifr->ifr_ifru))
+	len = ifr->ifr_addr.sa_len + offsetof(struct ifreq, ifr_ifru);
+#endif
+      
+      if (!expand_buf(&ifreq, len))
+	goto err;
+
+      ifr = (struct ifreq *)ifreq.iov_base;
+      memcpy(ifr, ptr, len);
+           
+      if (ifr->ifr_addr.sa_family == AF_INET && ipv4_callback)
+	{
+	  struct in_addr addr, netmask, broadcast;
+	  broadcast.s_addr = 0;
+	  addr = ((struct sockaddr_in *) &ifr->ifr_addr)->sin_addr;
+	  if (ioctl(fd, SIOCGIFNETMASK, ifr) == -1)
+	    continue;
+	  netmask = ((struct sockaddr_in *) &ifr->ifr_addr)->sin_addr;
+	  if (ioctl(fd, SIOCGIFBRDADDR, ifr) != -1)
+	    broadcast = ((struct sockaddr_in *) &ifr->ifr_addr)->sin_addr; 
+	  if (!((*ipv4_callback)(addr, 
+				 (int)if_nametoindex(ifr->ifr_name),
+				 netmask, broadcast, 
+				 parm)))
+	    goto err;
+	}
+#ifdef HAVE_IPV6
+      else if (ifr->ifr_addr.sa_family == AF_INET6 && ipv6_callback)
+	{
+	  struct in6_addr *addr = &((struct sockaddr_in6 *)&ifr->ifr_addr)->sin6_addr;
+	  /* voodoo to clear interface field in address */
+	  if (!(daemon->options & OPT_NOWILD) && IN6_IS_ADDR_LINKLOCAL(addr))
+	    {
+	      addr->s6_addr[2] = 0;
+	      addr->s6_addr[3] = 0;
+	    }
+	  if (!((*ipv6_callback)(addr,
+				 (int)((struct sockaddr_in6 *)&ifr->ifr_addr)->sin6_scope_id,
+				 (int)if_nametoindex(ifr->ifr_name),
+				 parm)))
+	    goto err;
+	}
+#endif
+    }
+  
+  ret = 1;
+
+ err:
+  errsav = errno;
+  close(fd);  
+  errno = errsav;
+
+  return ret;
+}
+#endif
+
+
+#if defined(HAVE_BSD_NETWORK) && defined(HAVE_DHCP)
+#include <net/bpf.h>
+
+void init_bpf(void)
+{
+  int i = 0;
+
+  while (1) 
+    {
+      /* useful size which happens to be sufficient */
+      if (expand_buf(&ifreq, sizeof(struct ifreq)))
+	{
+	  sprintf(ifreq.iov_base, "/dev/bpf%d", i++);
+	  if ((daemon->dhcp_raw_fd = open(ifreq.iov_base, O_RDWR, 0)) != -1)
+	    return;
+	}
+      if (errno != EBUSY)
+	die(_("cannot create DHCP BPF socket: %s"), NULL, EC_BADNET);
+    }	     
+}
+
+void send_via_bpf(struct dhcp_packet *mess, size_t len,
+		  struct in_addr iface_addr, struct ifreq *ifr)
+{
+   /* Hairy stuff, packet either has to go to the
+      net broadcast or the destination can't reply to ARP yet,
+      but we do know the physical address. 
+      Build the packet by steam, and send directly, bypassing
+      the kernel IP stack */
+  
+  struct ether_header ether; 
+  struct ip ip;
+  struct udphdr {
+    u16 uh_sport;               /* source port */
+    u16 uh_dport;               /* destination port */
+    u16 uh_ulen;                /* udp length */
+    u16 uh_sum;                 /* udp checksum */
+  } udp;
+  
+  u32 i, sum;
+  struct iovec iov[4];
+
+  /* Only know how to do ethernet on *BSD */
+  if (mess->htype != ARPHRD_ETHER || mess->hlen != ETHER_ADDR_LEN)
+    {
+      my_syslog(MS_DHCP | LOG_WARNING, _("DHCP request for unsupported hardware type (%d) received on %s"), 
+		mess->htype, ifr->ifr_name);
+      return;
+    }
+   
+  ifr->ifr_addr.sa_family = AF_LINK;
+  if (ioctl(daemon->dhcpfd, SIOCGIFADDR, ifr) < 0)
+    return;
+  
+  memcpy(ether.ether_shost, LLADDR((struct sockaddr_dl *)&ifr->ifr_addr), ETHER_ADDR_LEN);
+  ether.ether_type = htons(ETHERTYPE_IP);
+  
+  if (ntohs(mess->flags) & 0x8000)
+    {
+      memset(ether.ether_dhost, 255,  ETHER_ADDR_LEN);
+      ip.ip_dst.s_addr = INADDR_BROADCAST;
+    }
+  else
+    {
+      memcpy(ether.ether_dhost, mess->chaddr, ETHER_ADDR_LEN); 
+      ip.ip_dst.s_addr = mess->yiaddr.s_addr;
+    }
+  
+  ip.ip_p = IPPROTO_UDP;
+  ip.ip_src.s_addr = iface_addr.s_addr;
+  ip.ip_len = htons(sizeof(struct ip) + 
+		    sizeof(struct udphdr) +
+		    len) ;
+  ip.ip_hl = sizeof(struct ip) / 4;
+  ip.ip_v = IPVERSION;
+  ip.ip_tos = 0;
+  ip.ip_id = htons(0);
+  ip.ip_off = htons(0x4000); /* don't fragment */
+  ip.ip_ttl = IPDEFTTL;
+  ip.ip_sum = 0;
+  for (sum = 0, i = 0; i < sizeof(struct ip) / 2; i++)
+    sum += ((u16 *)&ip)[i];
+  while (sum>>16)
+    sum = (sum & 0xffff) + (sum >> 16);  
+  ip.ip_sum = (sum == 0xffff) ? sum : ~sum;
+  
+  udp.uh_sport = htons(daemon->dhcp_server_port);
+  udp.uh_dport = htons(daemon->dhcp_client_port);
+  if (len & 1)
+    ((char *)mess)[len] = 0; /* for checksum, in case length is odd. */
+  udp.uh_sum = 0;
+  udp.uh_ulen = sum = htons(sizeof(struct udphdr) + len);
+  sum += htons(IPPROTO_UDP);
+  sum += ip.ip_src.s_addr & 0xffff;
+  sum += (ip.ip_src.s_addr >> 16) & 0xffff;
+  sum += ip.ip_dst.s_addr & 0xffff;
+  sum += (ip.ip_dst.s_addr >> 16) & 0xffff;
+  for (i = 0; i < sizeof(struct udphdr)/2; i++)
+    sum += ((u16 *)&udp)[i];
+  for (i = 0; i < (len + 1) / 2; i++)
+    sum += ((u16 *)mess)[i];
+  while (sum>>16)
+    sum = (sum & 0xffff) + (sum >> 16);
+  udp.uh_sum = (sum == 0xffff) ? sum : ~sum;
+  
+  ioctl(daemon->dhcp_raw_fd, BIOCSETIF, ifr);
+  
+  iov[0].iov_base = &ether;
+  iov[0].iov_len = sizeof(ether);
+  iov[1].iov_base = &ip;
+  iov[1].iov_len = sizeof(ip);
+  iov[2].iov_base = &udp;
+  iov[2].iov_len = sizeof(udp);
+  iov[3].iov_base = mess;
+  iov[3].iov_len = len;
+
+  while (writev(daemon->dhcp_raw_fd, iov, 4) == -1 && retry_send());
+}
+
+#endif
+
+
diff --git a/src/cache.c b/src/cache.c
new file mode 100755
index 0000000..d641ae7
--- /dev/null
+++ b/src/cache.c
@@ -0,0 +1,1317 @@
+/* dnsmasq is Copyright (c) 2000-2009 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"
+
+static struct crec *cache_head = NULL, *cache_tail = NULL, **hash_table = NULL;
+#ifdef HAVE_DHCP
+static struct crec *dhcp_spare = NULL;
+#endif
+static struct crec *new_chain = NULL;
+static int cache_inserted = 0, cache_live_freed = 0, insert_error;
+static union bigname *big_free = NULL;
+static int bignames_left, hash_size;
+static int uid = 0;
+static char *addrbuff = NULL;
+
+/* type->string mapping: this is also used by the name-hash function as a mixing table. */
+static const struct {
+  unsigned int type;
+  const char * const name;
+} typestr[] = {
+  { 1,   "A" },
+  { 2,   "NS" },
+  { 5,   "CNAME" },
+  { 6,   "SOA" },
+  { 10,  "NULL" },
+  { 11,  "WKS" },
+  { 12,  "PTR" },
+  { 13,  "HINFO" },	
+  { 15,  "MX" },
+  { 16,  "TXT" },
+  { 22,  "NSAP" },
+  { 23,  "NSAP_PTR" },
+  { 24,  "SIG" },
+  { 25,  "KEY" },
+  { 28,  "AAAA" },
+  { 33,  "SRV" },
+  { 35,  "NAPTR" },
+  { 36,  "KX" },
+  { 37,  "CERT" },
+  { 38,  "A6" },
+  { 39,  "DNAME" },
+  { 41,  "OPT" },
+  { 48,  "DNSKEY" },
+  { 249, "TKEY" },
+  { 250, "TSIG" },
+  { 251, "IXFR" },
+  { 252, "AXFR" },
+  { 253, "MAILB" },
+  { 254, "MAILA" },
+  { 255, "ANY" }
+};
+
+static void cache_free(struct crec *crecp);
+static void cache_unlink(struct crec *crecp);
+static void cache_link(struct crec *crecp);
+static void rehash(int size);
+static void cache_hash(struct crec *crecp);
+
+void cache_init(void)
+{
+  struct crec *crecp;
+  int i;
+
+  if (daemon->options & OPT_LOG)
+    addrbuff = safe_malloc(ADDRSTRLEN);
+  
+  bignames_left = daemon->cachesize/10;
+  
+  if (daemon->cachesize > 0)
+    {
+      crecp = safe_malloc(daemon->cachesize*sizeof(struct crec));
+      
+      for (i=0; i < daemon->cachesize; i++, crecp++)
+	{
+	  cache_link(crecp);
+	  crecp->flags = 0;
+	  crecp->uid = uid++;
+	}
+    }
+  
+  /* create initial hash table*/
+  rehash(daemon->cachesize);
+}
+
+/* In most cases, we create the hash table once here by calling this with (hash_table == NULL)
+   but if the hosts file(s) are big (some people have 50000 ad-block entries), the table
+   will be much too small, so the hosts reading code calls rehash every 1000 addresses, to
+   expand the table. */
+static void rehash(int size)
+{
+  struct crec **new, **old, *p, *tmp;
+  int i, new_size, old_size;
+
+  /* hash_size is a power of two. */
+  for (new_size = 64; new_size < size/10; new_size = new_size << 1);
+  
+  /* must succeed in getting first instance, failure later is non-fatal */
+  if (!hash_table)
+    new = safe_malloc(new_size * sizeof(struct crec *));
+  else if (new_size <= hash_size || !(new = whine_malloc(new_size * sizeof(struct crec *))))
+    return;
+
+  for(i = 0; i < new_size; i++)
+    new[i] = NULL;
+
+  old = hash_table;
+  old_size = hash_size;
+  hash_table = new;
+  hash_size = new_size;
+  
+  if (old)
+    {
+      for (i = 0; i < old_size; i++)
+	for (p = old[i]; p ; p = tmp)
+	  {
+	    tmp = p->hash_next;
+	    cache_hash(p);
+	  }
+      free(old);
+    }
+}
+  
+static struct crec **hash_bucket(char *name)
+{
+  unsigned int c, val = 017465; /* Barker code - minimum self-correlation in cyclic shift */
+  const unsigned char *mix_tab = (const unsigned char*)typestr; 
+
+  while((c = (unsigned char) *name++))
+    {
+      /* don't use tolower and friends here - they may be messed up by LOCALE */
+      if (c >= 'A' && c <= 'Z')
+	c += 'a' - 'A';
+      val = ((val << 7) | (val >> (32 - 7))) + (mix_tab[(val + c) & 0x3F] ^ c);
+    } 
+  
+  /* hash_size is a power of two */
+  return hash_table + ((val ^ (val >> 16)) & (hash_size - 1));
+}
+
+static void cache_hash(struct crec *crecp)
+{
+  /* maintain an invariant that all entries with F_REVERSE set
+     are at the start of the hash-chain  and all non-reverse
+     immortal entries are at the end of the hash-chain.
+     This allows reverse searches and garbage collection to be optimised */
+
+  struct crec **up = hash_bucket(cache_get_name(crecp));
+
+  if (!(crecp->flags & F_REVERSE))
+    {
+      while (*up && ((*up)->flags & F_REVERSE))
+	up = &((*up)->hash_next); 
+      
+      if (crecp->flags & F_IMMORTAL)
+	while (*up && !((*up)->flags & F_IMMORTAL))
+	  up = &((*up)->hash_next);
+    }
+  crecp->hash_next = *up;
+  *up = crecp;
+}
+ 
+static void cache_free(struct crec *crecp)
+{
+  crecp->flags &= ~F_FORWARD;
+  crecp->flags &= ~F_REVERSE;
+  crecp->uid = uid++; /* invalidate CNAMES pointing to this. */
+  
+  if (cache_tail)
+    cache_tail->next = crecp;
+  else
+    cache_head = crecp;
+  crecp->prev = cache_tail;
+  crecp->next = NULL;
+  cache_tail = crecp;
+  
+  /* retrieve big name for further use. */
+  if (crecp->flags & F_BIGNAME)
+    {
+      crecp->name.bname->next = big_free;
+      big_free = crecp->name.bname;
+      crecp->flags &= ~F_BIGNAME;
+    }
+}    
+
+/* insert a new cache entry at the head of the list (youngest entry) */
+static void cache_link(struct crec *crecp)
+{
+  if (cache_head) /* check needed for init code */
+    cache_head->prev = crecp;
+  crecp->next = cache_head;
+  crecp->prev = NULL;
+  cache_head = crecp;
+  if (!cache_tail)
+    cache_tail = crecp;
+}
+
+/* remove an arbitrary cache entry for promotion */ 
+static void cache_unlink (struct crec *crecp)
+{
+  if (crecp->prev)
+    crecp->prev->next = crecp->next;
+  else
+    cache_head = crecp->next;
+
+  if (crecp->next)
+    crecp->next->prev = crecp->prev;
+  else
+    cache_tail = crecp->prev;
+}
+
+char *cache_get_name(struct crec *crecp)
+{
+  if (crecp->flags & F_BIGNAME)
+    return crecp->name.bname->name;
+  else if (crecp->flags & (F_DHCP | F_CONFIG)) 
+    return crecp->name.namep;
+  
+  return crecp->name.sname;
+}
+
+static int is_outdated_cname_pointer(struct crec *crecp)
+{
+  if (!(crecp->flags & F_CNAME))
+    return 0;
+  
+  if (crecp->addr.cname.cache && crecp->addr.cname.uid == crecp->addr.cname.cache->uid)
+    return 0;
+  
+  return 1;
+}
+
+static int is_expired(time_t now, struct crec *crecp)
+{
+  if (crecp->flags & F_IMMORTAL)
+    return 0;
+
+  if (difftime(now, crecp->ttd) < 0)
+    return 0;
+  
+  return 1;
+}
+
+static int cache_scan_free(char *name, struct all_addr *addr, time_t now, unsigned short flags)
+{
+  /* Scan and remove old entries.
+     If (flags & F_FORWARD) then remove any forward entries for name and any expired
+     entries but only in the same hash bucket as name.
+     If (flags & F_REVERSE) then remove any reverse entries for addr and any expired
+     entries in the whole cache.
+     If (flags == 0) remove any expired entries in the whole cache. 
+
+     In the flags & F_FORWARD case, the return code is valid, and returns zero if the
+     name exists in the cache as a HOSTS or DHCP entry (these are never deleted)
+
+     We take advantage of the fact that hash chains have stuff in the order <reverse>,<other>,<immortal>
+     so that when we hit an entry which isn't reverse and is immortal, we're done. */
+ 
+  struct crec *crecp, **up;
+  
+  if (flags & F_FORWARD)
+    {
+      for (up = hash_bucket(name), crecp = *up; crecp; crecp = crecp->hash_next)
+	if (is_expired(now, crecp) || is_outdated_cname_pointer(crecp))
+	  { 
+	    *up = crecp->hash_next;
+	    if (!(crecp->flags & (F_HOSTS | F_DHCP)))
+	      {
+		cache_unlink(crecp);
+		cache_free(crecp);
+	      }
+	  } 
+	else if ((crecp->flags & F_FORWARD) && 
+		 ((flags & crecp->flags & (F_IPV4 | F_IPV6)) || ((crecp->flags | flags) & F_CNAME)) &&
+		 hostname_isequal(cache_get_name(crecp), name))
+	  {
+	    if (crecp->flags & (F_HOSTS | F_DHCP))
+	      return 0;
+	    *up = crecp->hash_next;
+	    cache_unlink(crecp);
+	    cache_free(crecp);
+	  }
+	else
+	  up = &crecp->hash_next;
+    }
+  else
+    {
+      int i;
+#ifdef HAVE_IPV6
+      int addrlen = (flags & F_IPV6) ? IN6ADDRSZ : INADDRSZ;
+#else
+      int addrlen = INADDRSZ;
+#endif 
+      for (i = 0; i < hash_size; i++)
+	for (crecp = hash_table[i], up = &hash_table[i]; 
+	     crecp && ((crecp->flags & F_REVERSE) || !(crecp->flags & F_IMMORTAL));
+	     crecp = crecp->hash_next)
+	  if (is_expired(now, crecp))
+	    {
+	      *up = crecp->hash_next;
+	      if (!(crecp->flags & (F_HOSTS | F_DHCP)))
+		{ 
+		  cache_unlink(crecp);
+		  cache_free(crecp);
+		}
+	    }
+	  else if (!(crecp->flags & (F_HOSTS | F_DHCP)) &&
+		   (flags & crecp->flags & F_REVERSE) && 
+		   (flags & crecp->flags & (F_IPV4 | F_IPV6)) &&
+		   memcmp(&crecp->addr.addr, addr, addrlen) == 0)
+	    {
+	      *up = crecp->hash_next;
+	      cache_unlink(crecp);
+	      cache_free(crecp);
+	    }
+	  else
+	    up = &crecp->hash_next;
+    }
+  
+  return 1;
+}
+
+/* Note: The normal calling sequence is
+   cache_start_insert
+   cache_insert * n
+   cache_end_insert
+
+   but an abort can cause the cache_end_insert to be missed 
+   in which can the next cache_start_insert cleans things up. */
+
+void cache_start_insert(void)
+{
+  /* Free any entries which didn't get committed during the last
+     insert due to error.
+  */
+  while (new_chain)
+    {
+      struct crec *tmp = new_chain->next;
+      cache_free(new_chain);
+      new_chain = tmp;
+    }
+  new_chain = NULL;
+  insert_error = 0;
+}
+ 
+struct crec *cache_insert(char *name, struct all_addr *addr, 
+			  time_t now,  unsigned long ttl, unsigned short flags)
+{
+  struct crec *new;
+  union bigname *big_name = NULL;
+  int freed_all = flags & F_REVERSE;
+  int free_avail = 0;
+
+  log_query(flags | F_UPSTREAM, name, addr, NULL);
+
+  /* CONFIG bit means something else when stored in cache entries */
+  flags &= ~F_CONFIG;
+
+  /* if previous insertion failed give up now. */
+  if (insert_error)
+    return NULL;
+
+  /* First remove any expired entries and entries for the name/address we
+     are currently inserting. Fail is we attempt to delete a name from
+     /etc/hosts or DHCP. */
+  if (!cache_scan_free(name, addr, now, flags))
+    {
+      insert_error = 1;
+      return NULL;
+    }
+  
+  /* Now get a cache entry from the end of the LRU list */
+  while (1) {
+    if (!(new = cache_tail)) /* no entries left - cache is too small, bail */
+      {
+	insert_error = 1;
+	return NULL;
+      }
+    
+    /* End of LRU list is still in use: if we didn't scan all the hash
+       chains for expired entries do that now. If we already tried that
+       then it's time to start spilling things. */
+    
+    if (new->flags & (F_FORWARD | F_REVERSE))
+      { 
+	/* If free_avail set, we believe that an entry has been freed.
+	   Bugs have been known to make this not true, resulting in
+	   a tight loop here. If that happens, abandon the
+	   insert. Once in this state, all inserts will probably fail. */
+	if (free_avail)
+	  {
+	    insert_error = 1;
+	    return NULL;
+	  }
+		
+	if (freed_all)
+	  {
+	    free_avail = 1; /* Must be free space now. */
+	    cache_scan_free(cache_get_name(new), &new->addr.addr, now, new->flags);
+	    cache_live_freed++;
+	  }
+	else
+	  {
+	    cache_scan_free(NULL, NULL, now, 0);
+	    freed_all = 1;
+	  }
+	continue;
+      }
+ 
+    /* Check if we need to and can allocate extra memory for a long name.
+       If that fails, give up now. */
+    if (name && (strlen(name) > SMALLDNAME-1))
+      {
+	if (big_free)
+	  { 
+	    big_name = big_free;
+	    big_free = big_free->next;
+	  }
+	else if (!bignames_left ||
+		 !(big_name = (union bigname *)whine_malloc(sizeof(union bigname))))
+	  {
+	    insert_error = 1;
+	    return NULL;
+	  }
+	else
+	  bignames_left--;
+	
+      }
+
+    /* Got the rest: finally grab entry. */
+    cache_unlink(new);
+    break;
+  }
+  
+  new->flags = flags;
+  if (big_name)
+    {
+      new->name.bname = big_name;
+      new->flags |= F_BIGNAME;
+    }
+
+  if (name)
+    strcpy(cache_get_name(new), name);
+  else
+    *cache_get_name(new) = 0;
+
+  if (addr)
+    new->addr.addr = *addr;
+  else
+    new->addr.cname.cache = NULL;
+  
+  new->ttd = now + (time_t)ttl;
+  new->next = new_chain;
+  new_chain = new;
+
+  return new;
+}
+
+/* after end of insertion, commit the new entries */
+void cache_end_insert(void)
+{
+  if (insert_error)
+    return;
+  
+  while (new_chain)
+    { 
+      struct crec *tmp = new_chain->next;
+      /* drop CNAMEs which didn't find a target. */
+      if (is_outdated_cname_pointer(new_chain))
+	cache_free(new_chain);
+      else
+	{
+	  cache_hash(new_chain);
+	  cache_link(new_chain);
+	  cache_inserted++;
+	}
+      new_chain = tmp;
+    }
+  new_chain = NULL;
+}
+
+struct crec *cache_find_by_name(struct crec *crecp, char *name, time_t now, unsigned short prot)
+{
+  struct crec *ans;
+
+  if (crecp) /* iterating */
+    ans = crecp->next;
+  else
+    {
+      /* first search, look for relevant entries and push to top of list
+	 also free anything which has expired */
+      struct crec *next, **up, **insert = NULL, **chainp = &ans;
+      int ins_flags = 0;
+      
+      for (up = hash_bucket(name), crecp = *up; crecp; crecp = next)
+	{
+	  next = crecp->hash_next;
+	  
+	  if (!is_expired(now, crecp) && !is_outdated_cname_pointer(crecp))
+	    {
+	      if ((crecp->flags & F_FORWARD) && 
+		  (crecp->flags & prot) &&
+		  hostname_isequal(cache_get_name(crecp), name))
+		{
+		  if (crecp->flags & (F_HOSTS | F_DHCP))
+		    {
+		      *chainp = crecp;
+		      chainp = &crecp->next;
+		    }
+		  else
+		    {
+		      cache_unlink(crecp);
+		      cache_link(crecp);
+		    }
+	      	      
+		  /* Move all but the first entry up the hash chain
+		     this implements round-robin. 
+		     Make sure that re-ordering doesn't break the hash-chain
+		     order invariants. 
+		  */
+		  if (insert && (crecp->flags & (F_REVERSE | F_IMMORTAL)) == ins_flags)
+		    {
+		      *up = crecp->hash_next;
+		      crecp->hash_next = *insert;
+		      *insert = crecp;
+		      insert = &crecp->hash_next;
+		    }
+		  else
+		    {
+		      if (!insert)
+			{
+			  insert = up;
+			  ins_flags = crecp->flags & (F_REVERSE | F_IMMORTAL);
+			}
+		      up = &crecp->hash_next; 
+		    }
+		}
+	      else
+		/* case : not expired, incorrect entry. */
+		up = &crecp->hash_next; 
+	    }
+	  else
+	    {
+	      /* expired entry, free it */
+	      *up = crecp->hash_next;
+	      if (!(crecp->flags & (F_HOSTS | F_DHCP)))
+		{ 
+		  cache_unlink(crecp);
+		  cache_free(crecp);
+		}
+	    }
+	}
+	  
+      *chainp = cache_head;
+    }
+
+  if (ans && 
+      (ans->flags & F_FORWARD) &&
+      (ans->flags & prot) &&
+      hostname_isequal(cache_get_name(ans), name))
+    return ans;
+  
+  return NULL;
+}
+
+struct crec *cache_find_by_addr(struct crec *crecp, struct all_addr *addr, 
+				time_t now, unsigned short prot)
+{
+  struct crec *ans;
+#ifdef HAVE_IPV6
+  int addrlen = (prot == F_IPV6) ? IN6ADDRSZ : INADDRSZ;
+#else
+  int addrlen = INADDRSZ;
+#endif
+  
+  if (crecp) /* iterating */
+    ans = crecp->next;
+  else
+    {  
+      /* first search, look for relevant entries and push to top of list
+	 also free anything which has expired. All the reverse entries are at the
+	 start of the hash chain, so we can give up when we find the first 
+	 non-REVERSE one.  */
+       int i;
+       struct crec **up, **chainp = &ans;
+       
+       for (i=0; i<hash_size; i++)
+	 for (crecp = hash_table[i], up = &hash_table[i]; 
+	      crecp && (crecp->flags & F_REVERSE);
+	      crecp = crecp->hash_next)
+	   if (!is_expired(now, crecp))
+	     {      
+	       if ((crecp->flags & prot) &&
+		   memcmp(&crecp->addr.addr, addr, addrlen) == 0)
+		 {	    
+		   if (crecp->flags & (F_HOSTS | F_DHCP))
+		     {
+		       *chainp = crecp;
+		       chainp = &crecp->next;
+		     }
+		   else
+		     {
+		       cache_unlink(crecp);
+		       cache_link(crecp);
+		     }
+		 }
+	       up = &crecp->hash_next;
+	     }
+	   else
+	     {
+	       *up = crecp->hash_next;
+	       if (!(crecp->flags & (F_HOSTS | F_DHCP)))
+		 {
+		   cache_unlink(crecp);
+		   cache_free(crecp);
+		 }
+	     }
+       
+       *chainp = cache_head;
+    }
+  
+  if (ans && 
+      (ans->flags & F_REVERSE) &&
+      (ans->flags & prot) &&
+      memcmp(&ans->addr.addr, addr, addrlen) == 0)
+    return ans;
+  
+  return NULL;
+}
+
+static void add_hosts_entry(struct crec *cache, struct all_addr *addr, int addrlen, 
+			    unsigned short flags, int index, int addr_dup)
+{
+  struct crec *lookup = cache_find_by_name(NULL, cache->name.sname, 0, flags & (F_IPV4 | F_IPV6));
+  int i, nameexists = 0;
+  struct cname *a;
+
+  /* Remove duplicates in hosts files. */
+  if (lookup && (lookup->flags & F_HOSTS))
+    {
+      nameexists = 1;
+      if (memcmp(&lookup->addr.addr, addr, addrlen) == 0)
+	{
+	  free(cache);
+	  return;
+	}
+    }
+  
+  /* Ensure there is only one address -> name mapping (first one trumps) 
+     We do this by steam here, first we see if the address is the same as
+     the last one we saw, which eliminates most in the case of an ad-block 
+     file with thousands of entries for the same address.
+     Then we search and bail at the first matching address that came from
+     a HOSTS file. Since the first host entry gets reverse, we know 
+     then that it must exist without searching exhaustively for it. */
+  
+  if (addr_dup)
+    flags &= ~F_REVERSE;
+  else
+    for (i=0; i<hash_size; i++)
+      {
+	for (lookup = hash_table[i]; lookup; lookup = lookup->hash_next)
+	  if ((lookup->flags & F_HOSTS) && 
+	      (lookup->flags & flags & (F_IPV4 | F_IPV6)) &&
+	      memcmp(&lookup->addr.addr, addr, addrlen) == 0)
+	    {
+	      flags &= ~F_REVERSE;
+	      break;
+	    }
+	if (lookup)
+	  break;
+      }
+  
+  cache->flags = flags;
+  cache->uid = index;
+  memcpy(&cache->addr.addr, addr, addrlen);
+  cache_hash(cache);
+  
+  /* don't need to do alias stuff for second and subsequent addresses. */
+  if (!nameexists)
+    for (a = daemon->cnames; a; a = a->next)
+      if (hostname_isequal(cache->name.sname, a->target) &&
+	  (lookup = whine_malloc(sizeof(struct crec))))
+	{
+	  lookup->flags = F_FORWARD | F_IMMORTAL | F_CONFIG | F_HOSTS | F_CNAME;
+	  lookup->name.namep = a->alias;
+	  lookup->addr.cname.cache = cache;
+	  lookup->addr.cname.uid = index;
+	  cache_hash(lookup);
+	}
+}
+
+static int eatspace(FILE *f)
+{
+  int c, nl = 0;
+
+  while (1)
+    {
+      if ((c = getc(f)) == '#')
+	while (c != '\n' && c != EOF)
+	  c = getc(f);
+      
+      if (c == EOF)
+	return 1;
+
+      if (!isspace(c))
+	{
+	  ungetc(c, f);
+	  return nl;
+	}
+
+      if (c == '\n')
+	nl = 1;
+    }
+}
+	 
+static int gettok(FILE *f, char *token)
+{
+  int c, count = 0;
+ 
+  while (1)
+    {
+      if ((c = getc(f)) == EOF)
+	return (count == 0) ? EOF : 1;
+
+      if (isspace(c) || c == '#')
+	{
+	  ungetc(c, f);
+	  return eatspace(f);
+	}
+      
+      if (count < (MAXDNAME - 1))
+	{
+	  token[count++] = c;
+	  token[count] = 0;
+	}
+    }
+}
+
+static int read_hostsfile(char *filename, int index, int cache_size)
+{  
+  FILE *f = fopen(filename, "r");
+  char *token = daemon->namebuff, *domain_suffix = NULL;
+  int addr_count = 0, name_count = cache_size, lineno = 0;
+  unsigned short flags = 0, saved_flags = 0;
+  struct all_addr addr, saved_addr;
+  int atnl, addrlen = 0, addr_dup;
+
+  if (!f)
+    {
+      my_syslog(LOG_ERR, _("failed to load names from %s: %s"), filename, strerror(errno));
+      return 0;
+    }
+  
+  eatspace(f);
+  
+  while ((atnl = gettok(f, token)) != EOF)
+    {
+      addr_dup = 0;
+      lineno++;
+      
+#ifdef HAVE_IPV6      
+      if (inet_pton(AF_INET, token, &addr) > 0)
+	{
+	  flags = F_HOSTS | F_IMMORTAL | F_FORWARD | F_REVERSE | F_IPV4;
+	  addrlen = INADDRSZ;
+	  domain_suffix = get_domain(addr.addr.addr4);
+	}
+      else if (inet_pton(AF_INET6, token, &addr) > 0)
+	{
+	  flags = F_HOSTS | F_IMMORTAL | F_FORWARD | F_REVERSE | F_IPV6;
+	  addrlen = IN6ADDRSZ;
+	  domain_suffix = daemon->domain_suffix;
+	}
+#else 
+      if ((addr.addr.addr4.s_addr = inet_addr(token)) != (in_addr_t) -1)
+	{
+	  flags = F_HOSTS | F_IMMORTAL | F_FORWARD | F_REVERSE | F_IPV4;
+	  addrlen = INADDRSZ;
+	  domain_suffix = get_domain(addr.addr.addr4);
+	}
+#endif
+      else
+	{
+	  my_syslog(LOG_ERR, _("bad address at %s line %d"), filename, lineno); 
+	  while (atnl == 0)
+	    atnl = gettok(f, token);
+	  continue;
+	}
+      
+      if (saved_flags == flags && memcmp(&addr, &saved_addr, addrlen) == 0)
+	addr_dup = 1;
+      else
+	{
+	  saved_flags = flags;
+	  saved_addr = addr;
+	}
+      
+      addr_count++;
+      
+      /* rehash every 1000 names. */
+      if ((name_count - cache_size) > 1000)
+	{
+	  rehash(name_count);
+	  cache_size = name_count;
+	} 
+      
+      while (atnl == 0)
+	{
+	  struct crec *cache;
+	  int fqdn, nomem;
+	  char *canon;
+	  
+	  if ((atnl = gettok(f, token)) == EOF)
+	    break;
+
+	  fqdn = !!strchr(token, '.');
+
+	  if ((canon = canonicalise(token, &nomem)))
+	    {
+	      /* If set, add a version of the name with a default domain appended */
+	      if ((daemon->options & OPT_EXPAND) && domain_suffix && !fqdn && 
+		  (cache = whine_malloc(sizeof(struct crec) + 
+					strlen(canon)+2+strlen(domain_suffix)-SMALLDNAME)))
+		{
+		  strcpy(cache->name.sname, canon);
+		  strcat(cache->name.sname, ".");
+		  strcat(cache->name.sname, domain_suffix);
+		  add_hosts_entry(cache, &addr, addrlen, flags, index, addr_dup);
+		  addr_dup = 1;
+		  name_count++;
+		}
+	      if ((cache = whine_malloc(sizeof(struct crec) + strlen(canon)+1-SMALLDNAME)))
+		{
+		  strcpy(cache->name.sname, canon);
+		  add_hosts_entry(cache, &addr, addrlen, flags, index, addr_dup);
+		  name_count++;
+		}
+	      free(canon);
+	      
+	    }
+	  else if (!nomem)
+	    my_syslog(LOG_ERR, _("bad name at %s line %d"), filename, lineno); 
+	}
+    } 
+
+  fclose(f);
+  rehash(name_count);
+  
+  my_syslog(LOG_INFO, _("read %s - %d addresses"), filename, addr_count);
+  
+  return name_count;
+}
+	    
+void cache_reload(void)
+{
+  struct crec *cache, **up, *tmp;
+  int i, total_size = daemon->cachesize;
+  struct hostsfile *ah;
+
+  cache_inserted = cache_live_freed = 0;
+  
+  for (i=0; i<hash_size; i++)
+    for (cache = hash_table[i], up = &hash_table[i]; cache; cache = tmp)
+      {
+	tmp = cache->hash_next;
+	if (cache->flags & F_HOSTS)
+	  {
+	    *up = cache->hash_next;
+	    free(cache);
+	  }
+	else if (!(cache->flags & F_DHCP))
+	  {
+	    *up = cache->hash_next;
+	    if (cache->flags & F_BIGNAME)
+	      {
+		cache->name.bname->next = big_free;
+		big_free = cache->name.bname;
+	      }
+	    cache->flags = 0;
+	  }
+	else
+	  up = &cache->hash_next;
+      }
+  
+  if ((daemon->options & OPT_NO_HOSTS) && !daemon->addn_hosts)
+    {
+      if (daemon->cachesize > 0)
+	my_syslog(LOG_INFO, _("cleared cache"));
+      return;
+    }
+
+  if (!(daemon->options & OPT_NO_HOSTS))
+    total_size = read_hostsfile(HOSTSFILE, 0, total_size);
+  
+  for (i = 0, ah = daemon->addn_hosts; ah; ah = ah->next)
+    {
+      if (i <= ah->index)
+	i = ah->index + 1;
+
+      if (ah->flags & AH_DIR)
+	ah->flags |= AH_INACTIVE;
+      else
+	ah->flags &= ~AH_INACTIVE;
+    }
+
+  for (ah = daemon->addn_hosts; ah; ah = ah->next)
+    if (!(ah->flags & AH_INACTIVE))
+      {
+	struct stat buf;
+	if (stat(ah->fname, &buf) != -1 && S_ISDIR(buf.st_mode))
+	  {
+	    DIR *dir_stream;
+	    struct dirent *ent;
+	    
+	    /* don't read this as a file */
+	    ah->flags |= AH_INACTIVE;
+	    
+	    if (!(dir_stream = opendir(ah->fname)))
+	      my_syslog(LOG_ERR, _("cannot access directory %s: %s"), 
+			ah->fname, strerror(errno));
+	    else
+	      {
+		while ((ent = readdir(dir_stream)))
+		  {
+		    size_t lendir = strlen(ah->fname);
+		    size_t lenfile = strlen(ent->d_name);
+		    struct hostsfile *ah1;
+		    char *path;
+		    
+		    /* ignore emacs backups and dotfiles */
+		    if (lenfile == 0 || 
+			ent->d_name[lenfile - 1] == '~' ||
+			(ent->d_name[0] == '#' && ent->d_name[lenfile - 1] == '#') ||
+			ent->d_name[0] == '.')
+		      continue;
+		    
+		    /* see if we have an existing record.
+		       dir is ah->fname 
+		       file is ent->d_name
+		       path to match is ah1->fname */
+
+		    for (ah1 = daemon->addn_hosts; ah1; ah1 = ah1->next)
+		      {
+			if (lendir < strlen(ah1->fname) &&
+			    strstr(ah1->fname, ah->fname) == ah1->fname &&
+			    ah1->fname[lendir] == '/' &&
+			    strcmp(ah1->fname + lendir + 1, ent->d_name) == 0)
+			  {
+			    ah1->flags &= ~AH_INACTIVE;
+			    break;
+			  }
+		      }
+		    
+		    /* make new record */
+		    if (!ah1)
+		      {
+			if (!(ah1 = whine_malloc(sizeof(struct hostsfile))))
+			  continue;
+			
+			if (!(path = whine_malloc(lendir + lenfile + 2)))
+			  {
+			    free(ah1);
+			    continue;
+			  }
+		      	
+			strcpy(path, ah->fname);
+			strcat(path, "/");
+			strcat(path, ent->d_name);
+			ah1->fname = path;
+			ah1->index = i++;
+			ah1->flags = AH_DIR;
+			ah1->next = daemon->addn_hosts;
+			daemon->addn_hosts = ah1;
+		      }
+		    
+		    /* inactivate record if not regular file */
+		    if ((ah1->flags & AH_DIR) && stat(ah1->fname, &buf) != -1 && !S_ISREG(buf.st_mode))
+		      ah1->flags |= AH_INACTIVE; 
+
+		  }
+		closedir(dir_stream);
+	      }
+	  }
+      }
+	    
+  for (ah = daemon->addn_hosts; ah; ah = ah->next)
+    if (!(ah->flags & AH_INACTIVE))
+      total_size = read_hostsfile(ah->fname, ah->index, total_size);
+} 
+
+char *get_domain(struct in_addr addr)
+{
+  struct cond_domain *c;
+
+  for (c = daemon->cond_domain; c; c = c->next)
+    if (ntohl(addr.s_addr) >= ntohl(c->start.s_addr) &&
+        ntohl(addr.s_addr) <= ntohl(c->end.s_addr))
+      return c->domain;
+
+  return daemon->domain_suffix;
+}
+
+#ifdef HAVE_DHCP
+void cache_unhash_dhcp(void)
+{
+  struct crec *cache, **up;
+  int i;
+
+  for (i=0; i<hash_size; i++)
+    for (cache = hash_table[i], up = &hash_table[i]; cache; cache = cache->hash_next)
+      if (cache->flags & F_DHCP)
+	{
+	  *up = cache->hash_next;
+	  cache->next = dhcp_spare;
+	  dhcp_spare = cache;
+	}
+      else
+	up = &cache->hash_next;
+}
+
+void cache_add_dhcp_entry(char *host_name, 
+			  struct in_addr *host_address, time_t ttd) 
+{
+  struct crec *crec = NULL, *aliasc;
+  unsigned short flags =  F_DHCP | F_FORWARD | F_IPV4 | F_REVERSE;
+  int in_hosts = 0;
+  struct cname *a;
+  
+  while ((crec = cache_find_by_name(crec, host_name, 0, F_IPV4 | F_CNAME)))
+    {
+      /* check all addresses associated with name */
+      if (crec->flags & F_HOSTS)
+	{
+	  if (crec->addr.addr.addr.addr4.s_addr != host_address->s_addr)
+	    {
+	      strcpy(daemon->namebuff, inet_ntoa(crec->addr.addr.addr.addr4));
+	      my_syslog(LOG_WARNING, 
+			_("not giving name %s to the DHCP lease of %s because "
+			  "the name exists in %s with address %s"), 
+			host_name, inet_ntoa(*host_address),
+			record_source(crec->uid), daemon->namebuff);
+	      return;
+	    }
+	  else
+	    /* if in hosts, don't need DHCP record */
+	    in_hosts = 1;
+	}
+      else if (!(crec->flags & F_DHCP))
+	{
+	  cache_scan_free(host_name, NULL, 0, crec->flags & (F_IPV4 | F_CNAME | F_FORWARD));
+	  /* scan_free deletes all addresses associated with name */
+	  break;
+	}
+    }
+  
+   if (in_hosts)
+    return;
+
+   if ((crec = cache_find_by_addr(NULL, (struct all_addr *)host_address, 0, F_IPV4)))
+     {
+       if (crec->flags & F_NEG)
+	 cache_scan_free(NULL, (struct all_addr *)host_address, 0, F_IPV4 | F_REVERSE);
+       else
+	 /* avoid multiple reverse mappings */
+	 flags &= ~F_REVERSE;
+     }
+   
+   if ((crec = dhcp_spare))
+    dhcp_spare = dhcp_spare->next;
+  else /* need new one */
+    crec = whine_malloc(sizeof(struct crec));
+  
+  if (crec) /* malloc may fail */
+    {
+      crec->flags = flags;
+      if (ttd == 0)
+	crec->flags |= F_IMMORTAL;
+      else
+	crec->ttd = ttd;
+      crec->addr.addr.addr.addr4 = *host_address;
+      crec->name.namep = host_name;
+      crec->uid = uid++;
+      cache_hash(crec);
+
+      for (a = daemon->cnames; a; a = a->next)
+	if (hostname_isequal(host_name, a->target))
+	  {
+	    if ((aliasc = dhcp_spare))
+	      dhcp_spare = dhcp_spare->next;
+	    else /* need new one */
+	      aliasc = whine_malloc(sizeof(struct crec));
+	    
+	    if (aliasc)
+	      {
+		aliasc->flags = F_FORWARD | F_CONFIG | F_DHCP | F_CNAME;
+		if (ttd == 0)
+		  aliasc->flags |= F_IMMORTAL;
+		else
+		  aliasc->ttd = ttd;
+		aliasc->name.namep = a->alias;
+		aliasc->addr.cname.cache = crec;
+		aliasc->addr.cname.uid = crec->uid;
+		cache_hash(aliasc);
+	      }
+	  }
+    }
+}
+#endif
+
+
+void dump_cache(time_t now)
+{
+  struct server *serv, *serv1;
+
+  my_syslog(LOG_INFO, _("time %lu"), (unsigned long)now);
+  my_syslog(LOG_INFO, _("cache size %d, %d/%d cache insertions re-used unexpired cache entries."), 
+	    daemon->cachesize, cache_live_freed, cache_inserted);
+  my_syslog(LOG_INFO, _("queries forwarded %u, queries answered locally %u"), 
+	    daemon->queries_forwarded, daemon->local_answer);
+
+  if (!addrbuff && !(addrbuff = whine_malloc(ADDRSTRLEN)))
+    return;
+
+  /* sum counts from different records for same server */
+  for (serv = daemon->servers; serv; serv = serv->next)
+    serv->flags &= ~SERV_COUNTED;
+  
+  for (serv = daemon->servers; serv; serv = serv->next)
+    if (!(serv->flags & (SERV_NO_ADDR | SERV_LITERAL_ADDRESS | SERV_COUNTED)))
+      {
+	int port;
+	unsigned int queries = 0, failed_queries = 0;
+	for (serv1 = serv; serv1; serv1 = serv1->next)
+	  if (!(serv1->flags & (SERV_NO_ADDR | SERV_LITERAL_ADDRESS | SERV_COUNTED)) && sockaddr_isequal(&serv->addr, &serv1->addr))
+	    {
+	      serv1->flags |= SERV_COUNTED;
+	      queries += serv1->queries;
+	      failed_queries += serv1->failed_queries;
+	    }
+	port = prettyprint_addr(&serv->addr, addrbuff);
+	my_syslog(LOG_INFO, _("server %s#%d: queries sent %u, retried or failed %u"), addrbuff, port, queries, failed_queries);
+      }
+  
+  if ((daemon->options & (OPT_DEBUG | OPT_LOG)))
+    {
+      struct crec *cache ;
+      int i;
+      my_syslog(LOG_DEBUG, "Host                                     Address                        Flags     Expires");
+    
+      for (i=0; i<hash_size; i++)
+	for (cache = hash_table[i]; cache; cache = cache->hash_next)
+	  {
+	    char *a, *p = daemon->namebuff;
+	    p += sprintf(p, "%-40.40s ", cache_get_name(cache));
+	    if ((cache->flags & F_NEG) && (cache->flags & F_FORWARD))
+	      a = ""; 
+	    else if (cache->flags & F_CNAME) 
+	      {
+		a = "";
+		if (!is_outdated_cname_pointer(cache))
+		  a = cache_get_name(cache->addr.cname.cache);
+	      }
+#ifdef HAVE_IPV6
+	    else 
+	      { 
+		a = addrbuff;
+		if (cache->flags & F_IPV4)
+		  inet_ntop(AF_INET, &cache->addr.addr, addrbuff, ADDRSTRLEN);
+		else if (cache->flags & F_IPV6)
+		  inet_ntop(AF_INET6, &cache->addr.addr, addrbuff, ADDRSTRLEN);
+	      }
+#else
+            else 
+	      a = inet_ntoa(cache->addr.addr.addr.addr4);
+#endif
+	    p += sprintf(p, "%-30.30s %s%s%s%s%s%s%s%s%s%s  ", a, 
+			 cache->flags & F_IPV4 ? "4" : "",
+			 cache->flags & F_IPV6 ? "6" : "",
+			 cache->flags & F_CNAME ? "C" : "",
+			 cache->flags & F_FORWARD ? "F" : " ",
+			 cache->flags & F_REVERSE ? "R" : " ",
+			 cache->flags & F_IMMORTAL ? "I" : " ",
+			 cache->flags & F_DHCP ? "D" : " ",
+			 cache->flags & F_NEG ? "N" : " ",
+			 cache->flags & F_NXDOMAIN ? "X" : " ",
+			 cache->flags & F_HOSTS ? "H" : " ");
+#ifdef HAVE_BROKEN_RTC
+	    p += sprintf(p, "%lu", cache->flags & F_IMMORTAL ? 0: (unsigned long)(cache->ttd - now));
+#else
+	    p += sprintf(p, "%s", cache->flags & F_IMMORTAL ? "\n" : ctime(&(cache->ttd)));
+	    /* ctime includes trailing \n - eat it */
+	    *(p-1) = 0;
+#endif
+	    my_syslog(LOG_DEBUG, daemon->namebuff);
+	  }
+    }
+}
+
+char *record_source(int index)
+{
+  struct hostsfile *ah;
+
+  if (index == 0)
+    return HOSTSFILE;
+
+  for (ah = daemon->addn_hosts; ah; ah = ah->next)
+    if (ah->index == index)
+      return ah->fname;
+  
+  return "<unknown>";
+}
+
+void querystr(char *str, unsigned short type)
+{
+  unsigned int i;
+  
+  sprintf(str, "query[type=%d]", type); 
+  for (i = 0; i < (sizeof(typestr)/sizeof(typestr[0])); i++)
+    if (typestr[i].type == type)
+      sprintf(str,"query[%s]", typestr[i].name);
+}
+
+void log_query(unsigned short flags, char *name, struct all_addr *addr, char *arg)
+{
+  char *source, *dest = addrbuff;
+  char *verb = "is";
+  
+  if (!(daemon->options & OPT_LOG))
+    return;
+
+  if (addr)
+    {
+#ifdef HAVE_IPV6
+      inet_ntop(flags & F_IPV4 ? AF_INET : AF_INET6,
+		addr, addrbuff, ADDRSTRLEN);
+#else
+      strncpy(addrbuff, inet_ntoa(addr->addr.addr4), ADDRSTRLEN);  
+#endif
+    }
+
+  if (flags & F_REVERSE)
+    {
+      dest = name;
+      name = addrbuff;
+    }
+  
+  if (flags & F_NEG)
+    {
+      if (flags & F_NXDOMAIN)
+	{
+	  if (flags & F_IPV4)
+	    dest = "NXDOMAIN-IPv4";
+	  else if (flags & F_IPV6)
+	    dest = "NXDOMAIN-IPv6";
+	  else
+	    dest = "NXDOMAIN";
+	}
+      else
+	{      
+	  if (flags & F_IPV4)
+	    dest = "NODATA-IPv4";
+	  else if (flags & F_IPV6)
+	    dest = "NODATA-IPv6";
+	  else
+	    dest = "NODATA";
+	}
+    }
+  else if (flags & F_CNAME)
+    {
+      /* nasty abuse of NXDOMAIN and CNAME flags */
+      if (flags & F_NXDOMAIN)
+	dest = arg;
+      else
+	dest = "<CNAME>";
+    }
+    
+  if (flags & F_CONFIG)
+    source = "config";
+  else if (flags & F_DHCP)
+    source = "DHCP";
+  else if (flags & F_HOSTS)
+    source = arg;
+  else if (flags & F_UPSTREAM)
+    source = "reply";
+  else if (flags & F_SERVER)
+    {
+      source = "forwarded";
+      verb = "to";
+    }
+  else if (flags & F_QUERY)
+    {
+      source = arg;
+      verb = "from";
+    }
+  else
+    source = "cached";
+  
+  if (strlen(name) == 0)
+    name = ".";
+
+  my_syslog(LOG_DEBUG, "%s %s %s %s", source, name, verb, dest);
+}
+
diff --git a/src/config.h b/src/config.h
new file mode 100755
index 0000000..d5d3b15
--- /dev/null
+++ b/src/config.h
@@ -0,0 +1,293 @@
+/* dnsmasq is Copyright (c) 2000-2009 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/>.
+*/
+
+#define VERSION "2.51"
+
+#define FTABSIZ 150 /* max number of outstanding requests (default) */
+#define MAX_PROCS 20 /* max no children for TCP requests */
+#define CHILD_LIFETIME 150 /* secs 'till terminated (RFC1035 suggests > 120s) */
+#define EDNS_PKTSZ 1280 /* default max EDNS.0 UDP packet from RFC2671 */
+#define TIMEOUT 10 /* drop UDP queries after TIMEOUT seconds */
+#define FORWARD_TEST 50 /* try all servers every 50 queries */
+#define FORWARD_TIME 10 /* or 10 seconds */
+#define RANDOM_SOCKS 64 /* max simultaneous random ports */
+#define LEASE_RETRY 60 /* on error, retry writing leasefile after LEASE_RETRY seconds */
+#define CACHESIZ 150 /* default cache size */
+#define MAXLEASES 150 /* maximum number of DHCP leases */
+#define PING_WAIT 3 /* wait for ping address-in-use test */
+#define PING_CACHE_TIME 30 /* Ping test assumed to be valid this long. */
+#define DECLINE_BACKOFF 600 /* disable DECLINEd static addresses for this long */
+#define DHCP_PACKET_MAX 16384 /* hard limit on DHCP packet size */
+#define SMALLDNAME 40 /* most domain names are smaller than this */
+#define HOSTSFILE "/etc/hosts"
+#define ETHERSFILE "/etc/ethers"
+#ifdef __uClinux__
+#  define RESOLVFILE "/etc/config/resolv.conf"
+#else
+#  define RESOLVFILE "/etc/resolv.conf"
+#endif
+#define RUNFILE "/var/run/dnsmasq.pid"
+
+#ifndef LEASEFILE
+#   if defined(__FreeBSD__) || defined (__OpenBSD__) || defined(__DragonFly__) || defined(__NetBSD__)
+#      define LEASEFILE "/var/db/dnsmasq.leases"
+#   elif defined(__sun__) || defined (__sun)
+#      define LEASEFILE "/var/cache/dnsmasq.leases"
+#   else
+#      define LEASEFILE "/var/lib/misc/dnsmasq.leases"
+#   endif
+#endif
+
+#ifndef CONFFILE
+#   if defined(__FreeBSD__)
+#      define CONFFILE "/usr/local/etc/dnsmasq.conf"
+#   else
+#      define CONFFILE "/etc/dnsmasq.conf"
+#   endif
+#endif
+
+#define DEFLEASE 3600 /* default lease time, 1 hour */
+#define CHUSER "nobody"
+#define CHGRP "dip"
+#define DHCP_SERVER_PORT 67
+#define DHCP_CLIENT_PORT 68
+#define DHCP_SERVER_ALTPORT 1067
+#define DHCP_CLIENT_ALTPORT 1068
+#define TFTP_PORT 69
+#define TFTP_MAX_CONNECTIONS 50 /* max simultaneous connections */
+#define LOG_MAX 5 /* log-queue length */
+#define RANDFILE "/dev/urandom"
+#define DAD_WAIT 20 /* retry binding IPv6 sockets for this long */
+
+/* DBUS interface specifics */
+#define DNSMASQ_SERVICE "uk.org.thekelleys.dnsmasq"
+#define DNSMASQ_PATH "/uk/org/thekelleys/dnsmasq"
+
+/* A small collection of RR-types which are missing on some platforms */
+
+#ifndef T_SIG
+#  define T_SIG 24
+#endif
+
+#ifndef T_SRV
+#  define T_SRV 33
+#endif
+
+#ifndef T_OPT
+#  define T_OPT 41
+#endif
+
+#ifndef T_TKEY
+#  define T_TKEY 249
+#endif
+
+#ifndef T_TSIG
+#  define T_TSIG 250
+#endif
+
+
+/* Follows system specific switches. If you run on a 
+   new system, you may want to edit these. 
+   May replace this with Autoconf one day. 
+
+HAVE_LINUX_NETWORK
+HAVE_BSD_NETWORK
+HAVE_SOLARIS_NETWORK
+   define exactly one of these to alter interaction with kernel networking.
+
+HAVE_BROKEN_RTC
+   define this on embedded systems which don't have an RTC
+   which keeps time over reboots. Causes dnsmasq to use uptime
+   for timing, and keep lease lengths rather than expiry times
+   in its leases file. This also make dnsmasq "flash disk friendly".
+   Normally, dnsmasq tries very hard to keep the on-disk leases file
+   up-to-date: rewriting it after every renewal.  When HAVE_BROKEN_RTC 
+   is in effect, the lease file is only written when a new lease is 
+   created, or an old one destroyed. (Because those are the only times 
+   it changes.) This vastly reduces the number of file writes, and makes
+   it viable to keep the lease file on a flash filesystem.
+   NOTE: when enabling or disabling this, be sure to delete any old
+   leases file, otherwise dnsmasq may get very confused.
+
+HAVE_TFTP
+   define this to get dnsmasq's built-in TFTP server.
+
+HAVE_DHCP
+   define this to get dnsmasq's DHCP server.
+
+HAVE_SCRIPT
+   define this to get the ability to call scripts on lease-change
+
+HAVE_GETOPT_LONG
+   define this if you have GNU libc or GNU getopt. 
+
+HAVE_ARC4RANDOM
+   define this if you have arc4random() to get better security from DNS spoofs
+   by using really random ids (OpenBSD) 
+
+HAVE_SOCKADDR_SA_LEN
+   define this if struct sockaddr has sa_len field (*BSD) 
+
+HAVE_DBUS
+   Define this if you want to link against libdbus, and have dnsmasq
+   define some methods to allow (re)configuration of the upstream DNS 
+   servers via DBus.
+
+NOTES:
+   For Linux you should define 
+      HAVE_LINUX_NETWORK
+      HAVE_GETOPT_LONG
+  you should NOT define 
+      HAVE_ARC4RANDOM
+      HAVE_SOCKADDR_SA_LEN
+
+   For *BSD systems you should define 
+     HAVE_BSD_NETWORK
+     HAVE_SOCKADDR_SA_LEN
+   and you MAY define  
+     HAVE_ARC4RANDOM - OpenBSD and FreeBSD and NetBSD version 2.0 or later
+     HAVE_GETOPT_LONG - NetBSD, later FreeBSD 
+                       (FreeBSD and OpenBSD only if you link GNU getopt) 
+
+*/
+
+/* platform independent options- uncomment to enable */
+#define HAVE_DHCP
+#define HAVE_TFTP
+#define HAVE_SCRIPT
+/* #define HAVE_BROKEN_RTC */
+/* #define HAVE_DBUS */
+
+/* Allow TFTP to be disabled with COPTS=-DNO_TFTP */
+#ifdef NO_TFTP
+#undef HAVE_TFTP
+#endif
+
+/* Allow DHCP to be disabled with COPTS=-DNO_DHCP */
+#ifdef NO_DHCP
+#undef HAVE_DHCP
+#endif
+
+/* Allow scripts to be disabled with COPTS=-DNO_SCRIPT */
+#ifdef NO_SCRIPT
+#undef HAVE_SCRIPT
+#endif
+
+
+
+/* platform dependent options. */
+
+/* Must preceed __linux__ since uClinux defines __linux__ too. */
+#if defined(__uClinux__)
+#define HAVE_LINUX_NETWORK
+#define HAVE_GETOPT_LONG
+#undef HAVE_ARC4RANDOM
+#undef HAVE_SOCKADDR_SA_LEN
+/* Never use fork() on uClinux. Note that this is subtly different from the
+   --keep-in-foreground option, since it also  suppresses forking new 
+   processes for TCP connections and disables the call-a-script on leasechange
+   system. It's intended for use on MMU-less kernels. */
+#define NO_FORK
+
+#elif defined(__UCLIBC__)
+#define HAVE_LINUX_NETWORK
+#if defined(__UCLIBC_HAS_GNU_GETOPT__) || \
+   ((__UCLIBC_MAJOR__==0) && (__UCLIBC_MINOR__==9) && (__UCLIBC_SUBLEVEL__<21))
+#    define HAVE_GETOPT_LONG
+#endif
+#undef HAVE_ARC4RANDOM
+#undef HAVE_SOCKADDR_SA_LEN
+#if !defined(__ARCH_HAS_MMU__) && !defined(__UCLIBC_HAS_MMU__)
+#  define NO_FORK
+#endif
+#if defined(__UCLIBC_HAS_IPV6__)
+#  ifndef IPV6_V6ONLY
+#    define IPV6_V6ONLY 26
+#  endif
+#endif
+
+/* This is for glibc 2.x */
+#elif defined(__linux__)
+#define HAVE_LINUX_NETWORK
+#define HAVE_GETOPT_LONG
+#undef HAVE_ARC4RANDOM
+#undef HAVE_SOCKADDR_SA_LEN
+
+#elif defined(__FreeBSD__) || \
+      defined(__OpenBSD__) || \
+      defined(__DragonFly__) || \
+      defined (__FreeBSD_kernel__)
+#define HAVE_BSD_NETWORK
+/* Later verions of FreeBSD have getopt_long() */
+#if defined(optional_argument) && defined(required_argument)
+#   define HAVE_GETOPT_LONG
+#endif
+#if !defined (__FreeBSD_kernel__)
+#   define HAVE_ARC4RANDOM
+#endif
+#define HAVE_SOCKADDR_SA_LEN
+
+#elif defined(__APPLE__)
+#define HAVE_BSD_NETWORK
+#undef HAVE_GETOPT_LONG
+#define HAVE_ARC4RANDOM
+#define HAVE_SOCKADDR_SA_LEN
+/* Define before sys/socket.h is included so we get socklen_t */
+#define _BSD_SOCKLEN_T_
+ 
+#elif defined(__NetBSD__)
+#define HAVE_BSD_NETWORK
+#define HAVE_GETOPT_LONG
+#undef HAVE_ARC4RANDOM
+#define HAVE_SOCKADDR_SA_LEN
+
+#elif defined(__sun) || defined(__sun__)
+#define HAVE_SOLARIS_NETWORK
+#define HAVE_GETOPT_LONG
+#undef HAVE_ARC4RANDOM
+#undef HAVE_SOCKADDR_SA_LEN
+#define _XPG4_2
+#define __EXTENSIONS__
+#define ETHER_ADDR_LEN 6 
+ 
+#endif
+
+/* Decide if we're going to support IPv6 */
+/* IPv6 can be forced off with "make COPTS=-DNO_IPV6" */
+/* We assume that systems which don't have IPv6
+   headers don't have ntop and pton either */
+
+#if defined(INET6_ADDRSTRLEN) && defined(IPV6_V6ONLY) && !defined(NO_IPV6)
+#  define HAVE_IPV6
+#  define ADDRSTRLEN INET6_ADDRSTRLEN
+#  if defined(SOL_IPV6)
+#    define IPV6_LEVEL SOL_IPV6
+#  else
+#    define IPV6_LEVEL IPPROTO_IPV6
+#  endif
+#elif defined(INET_ADDRSTRLEN)
+#  undef HAVE_IPV6
+#  define ADDRSTRLEN INET_ADDRSTRLEN
+#else
+#  undef HAVE_IPV6
+#  define ADDRSTRLEN 16 /* 4*3 + 3 dots + NULL */
+#endif
+
+/* Can't do scripts without fork */
+#ifdef NOFORK
+#  undef HAVE_SCRIPT
+#endif
+
diff --git a/src/dbus.c b/src/dbus.c
new file mode 100755
index 0000000..841f924
--- /dev/null
+++ b/src/dbus.c
@@ -0,0 +1,436 @@
+/* dnsmasq is Copyright (c) 2000-2009 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_DBUS
+
+#include <dbus/dbus.h>
+
+const char* introspection_xml =
+"<!DOCTYPE node PUBLIC \"-//freedesktop//DTD D-BUS Object Introspection 1.0//EN\"\n"
+"\"http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd\">\n"
+"<node name=\"" DNSMASQ_PATH "\">\n"
+"  <interface name=\"org.freedesktop.DBus.Introspectable\">\n"
+"    <method name=\"Introspect\">\n"
+"      <arg name=\"data\" direction=\"out\" type=\"s\"/>\n"
+"    </method>\n"
+"  </interface>\n"
+"  <interface name=\"" DNSMASQ_SERVICE "\">\n"
+"    <method name=\"ClearCache\">\n"
+"    </method>\n"
+"    <method name=\"GetVersion\">\n"
+"      <arg name=\"version\" direction=\"out\" type=\"s\"/>\n"
+"    </method>\n"
+"    <method name=\"SetServers\">\n"
+"      <arg name=\"servers\" direction=\"in\" type=\"av\"/>\n"
+"    </method>\n"
+"    <signal name=\"DhcpLeaseAdded\">\n"
+"      <arg name=\"ipaddr\" type=\"s\"/>\n"
+"      <arg name=\"hwaddr\" type=\"s\"/>\n"
+"      <arg name=\"hostname\" type=\"s\"/>\n"
+"    </signal>\n"
+"    <signal name=\"DhcpLeaseDeleted\">\n"
+"      <arg name=\"ipaddr\" type=\"s\"/>\n"
+"      <arg name=\"hwaddr\" type=\"s\"/>\n"
+"      <arg name=\"hostname\" type=\"s\"/>\n"
+"    </signal>\n"
+"    <signal name=\"DhcpLeaseUpdated\">\n"
+"      <arg name=\"ipaddr\" type=\"s\"/>\n"
+"      <arg name=\"hwaddr\" type=\"s\"/>\n"
+"      <arg name=\"hostname\" type=\"s\"/>\n"
+"    </signal>\n"
+"  </interface>\n"
+"</node>\n";
+
+struct watch {
+  DBusWatch *watch;      
+  struct watch *next;
+};
+
+
+static dbus_bool_t add_watch(DBusWatch *watch, void *data)
+{
+  struct watch *w;
+
+  for (w = daemon->watches; w; w = w->next)
+    if (w->watch == watch)
+      return TRUE;
+
+  if (!(w = whine_malloc(sizeof(struct watch))))
+    return FALSE;
+
+  w->watch = watch;
+  w->next = daemon->watches;
+  daemon->watches = w;
+
+  w = data; /* no warning */
+  return TRUE;
+}
+
+static void remove_watch(DBusWatch *watch, void *data)
+{
+  struct watch **up, *w;  
+  
+  for (up = &(daemon->watches), w = daemon->watches; w; w = w->next)
+    if (w->watch == watch)
+      {
+        *up = w->next;
+        free(w);
+      }
+    else
+      up = &(w->next);
+
+  w = data; /* no warning */
+}
+
+static void dbus_read_servers(DBusMessage *message)
+{
+  struct server *serv, *tmp, **up;
+  DBusMessageIter iter;
+  union  mysockaddr addr, source_addr;
+  char *domain;
+  
+  dbus_message_iter_init(message, &iter);
+  
+  /* mark everything from DBUS */
+  for (serv = daemon->servers; serv; serv = serv->next)
+    if (serv->flags & SERV_FROM_DBUS)
+      serv->flags |= SERV_MARK;
+  
+  while (1)
+    {
+      int skip = 0;
+
+      if (dbus_message_iter_get_arg_type(&iter) == DBUS_TYPE_UINT32)
+	{
+	  u32 a;
+	  
+	  dbus_message_iter_get_basic(&iter, &a);
+	  dbus_message_iter_next (&iter);
+	  
+#ifdef HAVE_SOCKADDR_SA_LEN
+	  source_addr.in.sin_len = addr.in.sin_len = sizeof(struct sockaddr_in);
+#endif
+	  addr.in.sin_addr.s_addr = ntohl(a);
+	  source_addr.in.sin_family = addr.in.sin_family = AF_INET;
+	  addr.in.sin_port = htons(NAMESERVER_PORT);
+	  source_addr.in.sin_addr.s_addr = INADDR_ANY;
+	  source_addr.in.sin_port = htons(daemon->query_port);
+	}
+      else if (dbus_message_iter_get_arg_type(&iter) == DBUS_TYPE_BYTE)
+	{
+	  unsigned char p[sizeof(struct in6_addr)];
+	  unsigned int i;
+
+	  skip = 1;
+
+	  for(i = 0; i < sizeof(struct in6_addr); i++)
+	    {
+	      dbus_message_iter_get_basic(&iter, &p[i]);
+	      dbus_message_iter_next (&iter);
+	      if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_BYTE)
+		break;
+	    }
+
+#ifndef HAVE_IPV6
+	  my_syslog(LOG_WARNING, _("attempt to set an IPv6 server address via DBus - no IPv6 support"));
+#else
+	  if (i == sizeof(struct in6_addr)-1)
+	    {
+	      memcpy(&addr.in6.sin6_addr, p, sizeof(struct in6_addr));
+#ifdef HAVE_SOCKADDR_SA_LEN
+              source_addr.in6.sin6_len = addr.in6.sin6_len = sizeof(struct sockaddr_in6);
+#endif
+              source_addr.in6.sin6_family = addr.in6.sin6_family = AF_INET6;
+              addr.in6.sin6_port = htons(NAMESERVER_PORT);
+              source_addr.in6.sin6_flowinfo = addr.in6.sin6_flowinfo = 0;
+	      source_addr.in6.sin6_scope_id = addr.in6.sin6_scope_id = 0;
+              source_addr.in6.sin6_addr = in6addr_any;
+              source_addr.in6.sin6_port = htons(daemon->query_port);
+	      skip = 0;
+	    }
+#endif
+	}
+      else
+	/* At the end */
+	break;
+      
+      do {
+	if (dbus_message_iter_get_arg_type(&iter) == DBUS_TYPE_STRING)
+	  {
+	    dbus_message_iter_get_basic(&iter, &domain);
+	    dbus_message_iter_next (&iter);
+	  }
+	else
+	  domain = NULL;
+	
+	if (!skip)
+	  {
+	    /* See if this is already there, and unmark */
+	    for (serv = daemon->servers; serv; serv = serv->next)
+	      if ((serv->flags & SERV_FROM_DBUS) &&
+		  (serv->flags & SERV_MARK))
+		{
+		  if (!(serv->flags & SERV_HAS_DOMAIN) && !domain)
+		    {
+		      serv->flags &= ~SERV_MARK;
+		      break;
+		    }
+		  if ((serv->flags & SERV_HAS_DOMAIN) && 
+		      domain &&
+		      hostname_isequal(domain, serv->domain))
+		    {
+		      serv->flags &= ~SERV_MARK;
+		      break;
+		    }
+		}
+	    
+	    if (!serv && (serv = whine_malloc(sizeof (struct server))))
+	      {
+		/* Not found, create a new one. */
+		memset(serv, 0, sizeof(struct server));
+		
+		if (domain)
+		  serv->domain = whine_malloc(strlen(domain)+1);
+		
+		if (domain && !serv->domain)
+		  {
+		    free(serv);
+		    serv = NULL;
+		  }
+		else
+		  {
+		    serv->next = daemon->servers;
+		    daemon->servers = serv;
+		    serv->flags = SERV_FROM_DBUS;
+		    if (domain)
+		      {
+			strcpy(serv->domain, domain);
+			serv->flags |= SERV_HAS_DOMAIN;
+		      }
+		  }
+	      }
+
+	    if (serv)
+	      {
+		if (source_addr.in.sin_family == AF_INET &&
+		    addr.in.sin_addr.s_addr == 0 &&
+		    serv->domain)
+		  serv->flags |= SERV_NO_ADDR;
+		else
+		  {
+		    serv->flags &= ~SERV_NO_ADDR;
+		    serv->addr = addr;
+		    serv->source_addr = source_addr;
+		  }
+	      }
+	  }
+	} while (dbus_message_iter_get_arg_type(&iter) == DBUS_TYPE_STRING);
+    }
+  
+  /* unlink and free anything still marked. */
+  for (serv = daemon->servers, up = &daemon->servers; serv; serv = tmp) 
+    {
+      tmp = serv->next;
+      if (serv->flags & SERV_MARK)
+	{
+	  server_gone(serv);
+	  *up = serv->next;
+	  free(serv);
+	}
+      else 
+	up = &serv->next;
+    }
+
+}
+
+DBusHandlerResult message_handler(DBusConnection *connection, 
+				  DBusMessage *message, 
+				  void *user_data)
+{
+  char *method = (char *)dbus_message_get_member(message);
+   
+  if (dbus_message_is_method_call(message, DBUS_INTERFACE_INTROSPECTABLE, "Introspect"))
+    {
+      DBusMessage *reply = dbus_message_new_method_return(message);
+
+      dbus_message_append_args(reply, DBUS_TYPE_STRING, &introspection_xml, DBUS_TYPE_INVALID);
+      dbus_connection_send (connection, reply, NULL);
+      dbus_message_unref (reply);
+    }
+  else if (strcmp(method, "GetVersion") == 0)
+    {
+      char *v = VERSION;
+      DBusMessage *reply = dbus_message_new_method_return(message);
+      
+      dbus_message_append_args(reply, DBUS_TYPE_STRING, &v, DBUS_TYPE_INVALID);
+      dbus_connection_send (connection, reply, NULL);
+      dbus_message_unref (reply);
+    }
+  else if (strcmp(method, "SetServers") == 0)
+    {
+      my_syslog(LOG_INFO, _("setting upstream servers from DBus"));
+      dbus_read_servers(message);
+      check_servers();
+    }
+  else if (strcmp(method, "ClearCache") == 0)
+    clear_cache_and_reload(dnsmasq_time());
+  else
+    return (DBUS_HANDLER_RESULT_NOT_YET_HANDLED);
+  
+  method = user_data; /* no warning */
+
+  return (DBUS_HANDLER_RESULT_HANDLED);
+ 
+}
+ 
+
+/* returns NULL or error message, may fail silently if dbus daemon not yet up. */
+char *dbus_init(void)
+{
+  DBusConnection *connection = NULL;
+  DBusObjectPathVTable dnsmasq_vtable = {NULL, &message_handler, NULL, NULL, NULL, NULL };
+  DBusError dbus_error;
+  DBusMessage *message;
+
+  dbus_error_init (&dbus_error);
+  if (!(connection = dbus_bus_get (DBUS_BUS_SYSTEM, &dbus_error)))
+    return NULL;
+    
+  dbus_connection_set_exit_on_disconnect(connection, FALSE);
+  dbus_connection_set_watch_functions(connection, add_watch, remove_watch, 
+				      NULL, NULL, NULL);
+  dbus_error_init (&dbus_error);
+  dbus_bus_request_name (connection, DNSMASQ_SERVICE, 0, &dbus_error);
+  if (dbus_error_is_set (&dbus_error))
+    return (char *)dbus_error.message;
+  
+  if (!dbus_connection_register_object_path(connection,  DNSMASQ_PATH, 
+					    &dnsmasq_vtable, NULL))
+    return _("could not register a DBus message handler");
+  
+  daemon->dbus = connection; 
+  
+  if ((message = dbus_message_new_signal(DNSMASQ_PATH, DNSMASQ_SERVICE, "Up")))
+    {
+      dbus_connection_send(connection, message, NULL);
+      dbus_message_unref(message);
+    }
+
+  return NULL;
+}
+ 
+
+void set_dbus_listeners(int *maxfdp,
+			fd_set *rset, fd_set *wset, fd_set *eset)
+{
+  struct watch *w;
+  
+  for (w = daemon->watches; w; w = w->next)
+    if (dbus_watch_get_enabled(w->watch))
+      {
+	unsigned int flags = dbus_watch_get_flags(w->watch);
+	int fd = dbus_watch_get_unix_fd(w->watch);
+	
+	bump_maxfd(fd, maxfdp);
+	
+	if (flags & DBUS_WATCH_READABLE)
+	  FD_SET(fd, rset);
+	
+	if (flags & DBUS_WATCH_WRITABLE)
+	  FD_SET(fd, wset);
+	
+	FD_SET(fd, eset);
+      }
+}
+
+void check_dbus_listeners(fd_set *rset, fd_set *wset, fd_set *eset)
+{
+  DBusConnection *connection = (DBusConnection *)daemon->dbus;
+  struct watch *w;
+
+  for (w = daemon->watches; w; w = w->next)
+    if (dbus_watch_get_enabled(w->watch))
+      {
+	unsigned int flags = 0;
+	int fd = dbus_watch_get_unix_fd(w->watch);
+	
+	if (FD_ISSET(fd, rset))
+	  flags |= DBUS_WATCH_READABLE;
+	
+	if (FD_ISSET(fd, wset))
+	  flags |= DBUS_WATCH_WRITABLE;
+	
+	if (FD_ISSET(fd, eset))
+	  flags |= DBUS_WATCH_ERROR;
+
+	if (flags != 0)
+	  dbus_watch_handle(w->watch, flags);
+      }
+
+  if (connection)
+    {
+      dbus_connection_ref (connection);
+      while (dbus_connection_dispatch (connection) == DBUS_DISPATCH_DATA_REMAINS);
+      dbus_connection_unref (connection);
+    }
+}
+
+void emit_dbus_signal(int action, struct dhcp_lease *lease, char *hostname)
+{
+  DBusConnection *connection = (DBusConnection *)daemon->dbus;
+  DBusMessage* message = NULL;
+  DBusMessageIter args;
+  char *action_str, *addr, *mac = daemon->namebuff;
+  unsigned char *p;
+  int i;
+
+  if (!connection)
+    return;
+  
+  if (!hostname)
+    hostname = "";
+  
+  p = extended_hwaddr(lease->hwaddr_type, lease->hwaddr_len,
+		      lease->hwaddr, lease->clid_len, lease->clid, &i);
+  print_mac(mac, p, i);
+  
+  if (action == ACTION_DEL)
+    action_str = "DhcpLeaseDeleted";
+  else if (action == ACTION_ADD)
+    action_str = "DhcpLeaseAdded";
+  else if (action == ACTION_OLD)
+    action_str = "DhcpLeaseUpdated";
+  else
+    return;
+
+  addr = inet_ntoa(lease->addr);
+
+  if (!(message = dbus_message_new_signal(DNSMASQ_PATH, DNSMASQ_SERVICE, action_str)))
+    return;
+  
+  dbus_message_iter_init_append(message, &args);
+
+  if (dbus_message_iter_append_basic(&args, DBUS_TYPE_STRING, &addr) &&
+      dbus_message_iter_append_basic(&args, DBUS_TYPE_STRING, &mac) &&
+      dbus_message_iter_append_basic(&args, DBUS_TYPE_STRING, &hostname))
+    dbus_connection_send(connection, message, NULL);
+  
+  dbus_message_unref(message);
+}
+
+#endif
diff --git a/src/dhcp.c b/src/dhcp.c
new file mode 100755
index 0000000..335fc04
--- /dev/null
+++ b/src/dhcp.c
@@ -0,0 +1,1002 @@
+/* dnsmasq is Copyright (c) 2000-2009 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_DHCP
+
+struct iface_param {
+  struct in_addr relay, primary;
+  struct dhcp_context *current;
+  int ind;
+};
+
+static int complete_context(struct in_addr local, int if_index, 
+			    struct in_addr netmask, struct in_addr broadcast, void *vparam);
+
+void dhcp_init(void)
+{
+  int fd = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
+  struct sockaddr_in saddr;
+  int oneopt = 1;
+#if defined(IP_MTU_DISCOVER) && defined(IP_PMTUDISC_DONT)
+  int mtu = IP_PMTUDISC_DONT;
+#endif
+
+  if (fd == -1)
+    die (_("cannot create DHCP socket: %s"), NULL, EC_BADNET);
+  
+  if (!fix_fd(fd) ||
+#if defined(IP_MTU_DISCOVER) && defined(IP_PMTUDISC_DONT)
+      setsockopt(fd, SOL_IP, IP_MTU_DISCOVER, &mtu, sizeof(mtu)) == -1 ||
+#endif
+#if defined(HAVE_LINUX_NETWORK)
+      setsockopt(fd, SOL_IP, IP_PKTINFO, &oneopt, sizeof(oneopt)) == -1 ||
+#else
+      setsockopt(fd, IPPROTO_IP, IP_RECVIF, &oneopt, sizeof(oneopt)) == -1 ||
+#endif
+      setsockopt(fd, SOL_SOCKET, SO_BROADCAST, &oneopt, sizeof(oneopt)) == -1)  
+    die(_("failed to set options on DHCP socket: %s"), NULL, EC_BADNET);
+  
+  /* When bind-interfaces is set, there might be more than one dnmsasq
+     instance binding port 67. That's OK if they serve different networks.
+     Need to set REUSEADDR to make this posible, or REUSEPORT on *BSD. */
+  if (daemon->options & OPT_NOWILD)
+    {
+#ifdef SO_REUSEPORT
+      int rc = setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &oneopt, sizeof(oneopt));
+#else
+      int rc = setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &oneopt, sizeof(oneopt));
+#endif
+      if (rc == -1)
+	die(_("failed to set SO_REUSE{ADDR|PORT} on DHCP socket: %s"), NULL, EC_BADNET);
+    }
+  
+  memset(&saddr, 0, sizeof(saddr));
+  saddr.sin_family = AF_INET;
+  saddr.sin_port = htons(daemon->dhcp_server_port);
+  saddr.sin_addr.s_addr = INADDR_ANY;
+#ifdef HAVE_SOCKADDR_SA_LEN
+  saddr.sin_len = sizeof(struct sockaddr_in);
+#endif
+
+  if (bind(fd, (struct sockaddr *)&saddr, sizeof(struct sockaddr_in)))
+    die(_("failed to bind DHCP server socket: %s"), NULL, EC_BADNET);
+
+  daemon->dhcpfd = fd;
+
+#if defined(HAVE_BSD_NETWORK)
+  /* When we're not using capabilities, we need to do this here before
+     we drop root. Also, set buffer size small, to avoid wasting
+     kernel buffers */
+  
+  if (daemon->options & OPT_NO_PING)
+    daemon->dhcp_icmp_fd = -1;
+  else if ((daemon->dhcp_icmp_fd = make_icmp_sock()) == -1 ||
+	   setsockopt(daemon->dhcp_icmp_fd, SOL_SOCKET, SO_RCVBUF, &oneopt, sizeof(oneopt)) == -1 )
+    die(_("cannot create ICMP raw socket: %s."), NULL, EC_BADNET);
+  
+  /* Make BPF raw send socket */
+  init_bpf();
+#endif
+  
+  check_dhcp_hosts(1);
+    
+  daemon->dhcp_packet.iov_len = sizeof(struct dhcp_packet); 
+  daemon->dhcp_packet.iov_base = safe_malloc(daemon->dhcp_packet.iov_len);
+}
+  
+void dhcp_packet(time_t now)
+{
+  struct dhcp_packet *mess;
+  struct dhcp_context *context;
+  struct iname *tmp;
+  struct ifreq ifr;
+  struct msghdr msg;
+  struct sockaddr_in dest;
+  struct cmsghdr *cmptr;
+  struct iovec iov;
+  ssize_t sz; 
+  int iface_index = 0, unicast_dest = 0, is_inform = 0;
+  struct in_addr iface_addr, *addrp = NULL;
+  struct iface_param parm;
+
+  union {
+    struct cmsghdr align; /* this ensures alignment */
+#if defined(HAVE_LINUX_NETWORK)
+    char control[CMSG_SPACE(sizeof(struct in_pktinfo))];
+#elif defined(HAVE_SOLARIS_NETWORK)
+    char control[CMSG_SPACE(sizeof(unsigned int))];
+#elif defined(HAVE_BSD_NETWORK) 
+    char control[CMSG_SPACE(sizeof(struct sockaddr_dl))];
+#endif
+  } control_u;
+  
+  msg.msg_control = NULL;
+  msg.msg_controllen = 0;
+  msg.msg_name = NULL;
+  msg.msg_namelen = 0;
+  msg.msg_iov = &daemon->dhcp_packet;
+  msg.msg_iovlen = 1;
+  
+  while (1)
+    {
+      msg.msg_flags = 0;
+      while ((sz = recvmsg(daemon->dhcpfd, &msg, MSG_PEEK | MSG_TRUNC)) == -1 && errno == EINTR);
+      
+      if (sz == -1)
+	return;
+      
+      if (!(msg.msg_flags & MSG_TRUNC))
+	break;
+
+      /* Very new Linux kernels return the actual size needed, 
+	 older ones always return truncated size */
+      if ((size_t)sz == daemon->dhcp_packet.iov_len)
+	{
+	  if (!expand_buf(&daemon->dhcp_packet, sz + 100))
+	    return;
+	}
+      else
+	{
+	  expand_buf(&daemon->dhcp_packet, sz);
+	  break;
+	}
+    }
+  
+  /* expand_buf may have moved buffer */
+  mess = (struct dhcp_packet *)daemon->dhcp_packet.iov_base;
+  msg.msg_controllen = sizeof(control_u);
+  msg.msg_control = control_u.control;
+  msg.msg_flags = 0;
+  msg.msg_name = &dest;
+  msg.msg_namelen = sizeof(dest);
+
+  while ((sz = recvmsg(daemon->dhcpfd, &msg, 0)) == -1 && errno == EINTR);
+ 
+  if ((msg.msg_flags & MSG_TRUNC) || sz < (ssize_t)(sizeof(*mess) - sizeof(mess->options)))
+    return;
+  
+#if defined (HAVE_LINUX_NETWORK)
+  if (msg.msg_controllen >= sizeof(struct cmsghdr))
+    for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr))
+      if (cmptr->cmsg_level == SOL_IP && cmptr->cmsg_type == IP_PKTINFO)
+	{
+	  iface_index = ((struct in_pktinfo *)CMSG_DATA(cmptr))->ipi_ifindex;
+	  if (((struct in_pktinfo *)CMSG_DATA(cmptr))->ipi_addr.s_addr != INADDR_BROADCAST)
+	    unicast_dest = 1;
+	}
+
+#elif defined(HAVE_BSD_NETWORK) 
+  if (msg.msg_controllen >= sizeof(struct cmsghdr))
+    for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr))
+      if (cmptr->cmsg_level == IPPROTO_IP && cmptr->cmsg_type == IP_RECVIF)
+        iface_index = ((struct sockaddr_dl *)CMSG_DATA(cmptr))->sdl_index;
+
+  
+#elif defined(HAVE_SOLARIS_NETWORK) 
+  if (msg.msg_controllen >= sizeof(struct cmsghdr))
+    for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr))
+      if (cmptr->cmsg_level == IPPROTO_IP && cmptr->cmsg_type == IP_RECVIF)
+	iface_index = *((unsigned int *)CMSG_DATA(cmptr));
+	  
+#endif
+	
+  if (!indextoname(daemon->dhcpfd, iface_index, ifr.ifr_name))
+    return;
+
+#ifdef MSG_BCAST
+  /* OpenBSD tells us when a packet was broadcast */
+  if (!(msg.msg_flags & MSG_BCAST))
+    unicast_dest = 1;
+#endif
+
+  ifr.ifr_addr.sa_family = AF_INET;
+  if (ioctl(daemon->dhcpfd, SIOCGIFADDR, &ifr) != -1 )
+    {
+      addrp = &iface_addr;
+      iface_addr = ((struct sockaddr_in *) &ifr.ifr_addr)->sin_addr;
+    }
+
+  if (!iface_check(AF_INET, (struct all_addr *)addrp, ifr.ifr_name, &iface_index))
+    return;
+  
+  for (tmp = daemon->dhcp_except; tmp; tmp = tmp->next)
+    if (tmp->name && (strcmp(tmp->name, ifr.ifr_name) == 0))
+      return;
+  
+  /* interface may have been changed by alias in iface_check */
+  if (!addrp)
+    {
+      if (ioctl(daemon->dhcpfd, SIOCGIFADDR, &ifr) == -1)
+	{
+	  my_syslog(MS_DHCP | LOG_WARNING, _("DHCP packet received on %s which has no address"), ifr.ifr_name);
+	  return;
+	}
+      else
+	iface_addr = ((struct sockaddr_in *) &ifr.ifr_addr)->sin_addr;
+    }
+  
+  /* unlinked contexts are marked by context->current == context */
+  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;
+
+  if (!iface_enumerate(&parm, complete_context, NULL))
+    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);
+  lease_update_file(now);
+  lease_update_dns();
+    
+  if (iov.iov_len == 0)
+    return;
+  
+  msg.msg_name = &dest;
+  msg.msg_namelen = sizeof(dest);
+  msg.msg_control = NULL;
+  msg.msg_controllen = 0;
+  msg.msg_iov = &iov;
+  iov.iov_base = daemon->dhcp_packet.iov_base;
+  
+  /* packet buffer may have moved */
+  mess = (struct dhcp_packet *)daemon->dhcp_packet.iov_base;
+  
+#ifdef HAVE_SOCKADDR_SA_LEN
+  dest.sin_len = sizeof(struct sockaddr_in);
+#endif
+     
+  if (mess->giaddr.s_addr)
+    {
+      /* Send to BOOTP relay  */
+      dest.sin_port = htons(daemon->dhcp_server_port);
+      dest.sin_addr = mess->giaddr; 
+    }
+  else if (mess->ciaddr.s_addr)
+    {
+      /* If the client's idea of its own address tallys with
+	 the source address in the request packet, we believe the
+	 source port too, and send back to that.  If we're replying 
+	 to a DHCPINFORM, trust the source address always. */
+      if ((!is_inform && dest.sin_addr.s_addr != mess->ciaddr.s_addr) ||
+	  dest.sin_port == 0 || dest.sin_addr.s_addr == 0)
+	{
+	  dest.sin_port = htons(daemon->dhcp_client_port); 
+	  dest.sin_addr = mess->ciaddr;
+	}
+    } 
+#if defined(HAVE_LINUX_NETWORK)
+  else if ((ntohs(mess->flags) & 0x8000) || mess->hlen == 0 ||
+	   mess->hlen > sizeof(ifr.ifr_addr.sa_data) || mess->htype == 0)
+    {
+      /* broadcast to 255.255.255.255 (or mac address invalid) */
+      struct in_pktinfo *pkt;
+      msg.msg_control = control_u.control;
+      msg.msg_controllen = sizeof(control_u);
+      cmptr = CMSG_FIRSTHDR(&msg);
+      pkt = (struct in_pktinfo *)CMSG_DATA(cmptr);
+      pkt->ipi_ifindex = iface_index;
+      pkt->ipi_spec_dst.s_addr = 0;
+      msg.msg_controllen = cmptr->cmsg_len = CMSG_LEN(sizeof(struct in_pktinfo));
+      cmptr->cmsg_level = SOL_IP;
+      cmptr->cmsg_type = IP_PKTINFO;  
+      dest.sin_addr.s_addr = INADDR_BROADCAST;
+      dest.sin_port = htons(daemon->dhcp_client_port);
+    }
+  else
+    {
+      /* unicast to unconfigured client. Inject mac address direct into ARP cache. 
+	 struct sockaddr limits size to 14 bytes. */
+      struct arpreq req;
+      dest.sin_addr = mess->yiaddr;
+      dest.sin_port = htons(daemon->dhcp_client_port);
+      *((struct sockaddr_in *)&req.arp_pa) = dest;
+      req.arp_ha.sa_family = mess->htype;
+      memcpy(req.arp_ha.sa_data, mess->chaddr, mess->hlen);
+      strncpy(req.arp_dev, ifr.ifr_name, 16);
+      req.arp_flags = ATF_COM;
+      ioctl(daemon->dhcpfd, SIOCSARP, &req);
+    }
+#elif defined(HAVE_SOLARIS_NETWORK)
+  else if ((ntohs(mess->flags) & 0x8000) || mess->hlen != ETHER_ADDR_LEN || mess->htype != ARPHRD_ETHER)
+    {
+      /* broadcast to 255.255.255.255 (or mac address invalid) */
+      dest.sin_addr.s_addr = INADDR_BROADCAST;
+      dest.sin_port = htons(daemon->dhcp_client_port);
+      /* note that we don't specify the interface here: that's done by the
+	 IP_BOUND_IF sockopt lower down. */
+    }
+  else
+    {
+      /* unicast to unconfigured client. Inject mac address direct into ARP cache. 
+	 Note that this only works for ethernet on solaris, because we use SIOCSARP
+	 and not SIOCSXARP, which would be perfect, except that it returns ENXIO 
+	 mysteriously. Bah. Fall back to broadcast for other net types. */
+      struct arpreq req;
+      dest.sin_addr = mess->yiaddr;
+      dest.sin_port = htons(daemon->dhcp_client_port);
+      *((struct sockaddr_in *)&req.arp_pa) = dest;
+      req.arp_ha.sa_family = AF_UNSPEC;
+      memcpy(req.arp_ha.sa_data, mess->chaddr, mess->hlen);
+      req.arp_flags = ATF_COM;
+      ioctl(daemon->dhcpfd, SIOCSARP, &req);
+    }
+#elif defined(HAVE_BSD_NETWORK)
+  else 
+    {
+      send_via_bpf(mess, iov.iov_len, iface_addr, &ifr);
+      return;
+    }
+#endif
+   
+#ifdef HAVE_SOLARIS_NETWORK
+  setsockopt(daemon->dhcpfd, IPPROTO_IP, IP_BOUND_IF, &iface_index, sizeof(iface_index));
+#endif
+  
+  while(sendmsg(daemon->dhcpfd, &msg, 0) == -1 && retry_send());
+}
+ 
+/* This is a complex routine: it gets called with each (address,netmask,broadcast) triple 
+   of each interface (and any relay address) and does the  following things:
+
+   1) Discards stuff for interfaces other than the one on which a DHCP packet just arrived.
+   2) Fills in any netmask and broadcast addresses which have not been explicitly configured.
+   3) Fills in local (this host) and router (this host or relay) addresses.
+   4) Links contexts which are valid for hosts directly connected to the arrival interface on ->current.
+
+   Note that the current chain may be superceded later for configured hosts or those coming via gateways. */
+
+static int complete_context(struct in_addr local, int if_index, 
+			    struct in_addr netmask, struct in_addr broadcast, void *vparam)
+{
+  struct dhcp_context *context;
+  struct iface_param *param = vparam;
+  
+  for (context = daemon->dhcp; context; context = context->next)
+    {
+      if (!(context->flags & CONTEXT_NETMASK) &&
+	  (is_same_net(local, context->start, netmask) ||
+	   is_same_net(local, context->end, netmask)))
+      { 
+	if (context->netmask.s_addr != netmask.s_addr &&
+	    !(is_same_net(local, context->start, netmask) &&
+	      is_same_net(local, context->end, netmask)))
+	  {
+	    strcpy(daemon->dhcp_buff, inet_ntoa(context->start));
+	    strcpy(daemon->dhcp_buff2, inet_ntoa(context->end));
+	    my_syslog(MS_DHCP | LOG_WARNING, _("DHCP range %s -- %s is not consistent with netmask %s"),
+		      daemon->dhcp_buff, daemon->dhcp_buff2, inet_ntoa(netmask));
+	  }	
+ 	context->netmask = netmask;
+      }
+      
+      if (context->netmask.s_addr)
+	{
+	  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)
+		{
+		  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 = param->relay;
+	      context->local = param->primary;
+	      /* fill in missing broadcast addresses for relayed ranges */
+	      if (!(context->flags & CONTEXT_BRDCAST))
+		context->broadcast.s_addr  = context->start.s_addr | ~context->netmask.s_addr;
+	    }
+
+	}
+    }
+
+  return 1;
+}
+	  
+struct dhcp_context *address_available(struct dhcp_context *context, 
+				       struct in_addr taddr,
+				       struct dhcp_netid *netids)
+{
+  /* Check is an address is OK for this network, check all
+     possible ranges. Make sure that the address isn't in use
+     by the server itself. */
+  
+  unsigned int start, end, addr = ntohl(taddr.s_addr);
+  struct dhcp_context *tmp;
+
+  for (tmp = context; tmp; tmp = tmp->current)
+    if (taddr.s_addr == context->router.s_addr)
+      return NULL;
+  
+  for (tmp = context; tmp; tmp = tmp->current)
+    {
+      start = ntohl(tmp->start.s_addr);
+      end = ntohl(tmp->end.s_addr);
+
+      if (!(tmp->flags & CONTEXT_STATIC) &&
+	  addr >= start &&
+	  addr <= end &&
+	  match_netid(tmp->filter, netids, 1))
+	return tmp;
+    }
+
+  return NULL;
+}
+
+struct dhcp_context *narrow_context(struct dhcp_context *context, 
+				    struct in_addr taddr,
+				    struct dhcp_netid *netids)
+{
+  /* We start of with a set of possible contexts, all on the current physical interface.
+     These are chained on ->current.
+     Here we have an address, and return the actual context correponding to that
+     address. Note that none may fit, if the address came a dhcp-host and is outside
+     any dhcp-range. In that case we return a static range if possible, or failing that,
+     any context on the correct subnet. (If there's more than one, this is a dodgy 
+     configuration: maybe there should be a warning.) */
+  
+  struct dhcp_context *tmp;
+
+  if (!(tmp = address_available(context, taddr, netids)))
+    {
+      for (tmp = context; tmp; tmp = tmp->current)
+	if (is_same_net(taddr, tmp->start, tmp->netmask) && 
+	    (tmp->flags & CONTEXT_STATIC))
+	  break;
+      
+      if (!tmp)
+	for (tmp = context; tmp; tmp = tmp->current)
+	  if (is_same_net(taddr, tmp->start, tmp->netmask))
+	    break;
+    }
+  
+  /* Only one context allowed now */
+  if (tmp)
+    tmp->current = NULL;
+  
+  return tmp;
+}
+
+struct dhcp_config *config_find_by_address(struct dhcp_config *configs, struct in_addr addr)
+{
+  struct dhcp_config *config;
+  
+  for (config = configs; config; config = config->next)
+    if ((config->flags & CONFIG_ADDR) && config->addr.s_addr == addr.s_addr)
+      return config;
+
+  return NULL;
+}
+
+/* Is every member of check matched by a member of pool? 
+   If tagnotneeded, untagged is OK */
+int match_netid(struct dhcp_netid *check, struct dhcp_netid *pool, int tagnotneeded)
+{
+  struct dhcp_netid *tmp1;
+  
+  if (!check && !tagnotneeded)
+    return 0;
+
+  for (; check; check = check->next)
+    {
+      if (check->net[0] != '#')
+	{
+	  for (tmp1 = pool; tmp1; tmp1 = tmp1->next)
+	    if (strcmp(check->net, tmp1->net) == 0)
+	      break;
+	  if (!tmp1)
+	    return 0;
+	}
+      else
+	for (tmp1 = pool; tmp1; tmp1 = tmp1->next)
+	  if (strcmp((check->net)+1, tmp1->net) == 0)
+	    return 0;
+    }
+  return 1;
+}
+
+int address_allocate(struct dhcp_context *context,
+		     struct in_addr *addrp, unsigned char *hwaddr, int hw_len, 
+		     struct dhcp_netid *netids, time_t now)   
+{
+  /* Find a free address: exclude anything in use and anything allocated to
+     a particular hwaddr/clientid/hostname in our configuration.
+     Try to return from contexts which match netids first. */
+
+  struct in_addr start, addr;
+  struct dhcp_context *c, *d;
+  int i, pass;
+  unsigned int j; 
+
+  /* hash hwaddr */
+  for (j = 0, i = 0; i < hw_len; i++)
+    j += hwaddr[i] + (hwaddr[i] << 8) + (hwaddr[i] << 16);
+  
+  for (pass = 0; pass <= 1; pass++)
+    for (c = context; c; c = c->current)
+      if (c->flags & CONTEXT_STATIC)
+	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))));
+	  
+	  do {
+	    /* eliminate addresses in use by the server. */
+	    for (d = context; d; d = d->current)
+	      if (addr.s_addr == d->router.s_addr)
+		break;
+
+	    /* Addresses which end in .255 and .0 are broken in Windows even when using 
+	       supernetting. ie dhcp-range=192.168.0.1,192.168.1.254,255,255,254.0
+	       then 192.168.0.255 is a valid IP address, but not for Windows as it's
+	       in the class C range. See  KB281579. We therefore don't allocate these 
+	       addresses to avoid hard-to-diagnose problems. Thanks Bill. */	    
+	    if (!d &&
+		!lease_find_by_addr(addr) && 
+		!config_find_by_address(daemon->dhcp_conf, addr) &&
+		(!IN_CLASSC(ntohl(addr.s_addr)) || 
+		 ((ntohl(addr.s_addr) & 0xff) != 0xff && ((ntohl(addr.s_addr) & 0xff) != 0x0))))
+	      {
+		struct ping_result *r, *victim = NULL;
+		int count, max = (int)(0.6 * (((float)PING_CACHE_TIME)/
+					      ((float)PING_WAIT)));
+		
+		*addrp = addr;
+
+		if (daemon->options & 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
+		   on us repeatedly. As a final check, if we did more
+		   than 60% of the possible ping checks in the last 
+		   PING_CACHE_TIME, we are in high-load mode, so don't do any more. */
+		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
+		  {
+		    /* at this point victim may hold an expired record */
+		    if (!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;
+		      }
+		    return 1;
+		  }
+	      }
+
+	    addr.s_addr = htonl(ntohl(addr.s_addr) + 1);
+	    
+	    if (addr.s_addr == htonl(ntohl(c->end.s_addr) + 1))
+	      addr = c->start;
+	    
+	  } while (addr.s_addr != start.s_addr);
+	}
+  return 0;
+}
+
+static int is_addr_in_context(struct dhcp_context *context, struct dhcp_config *config)
+{
+  if (!context) /* called via find_config() from lease_update_from_configs() */
+    return 1; 
+  if (!(config->flags & CONFIG_ADDR))
+    return 1;
+  for (; context; context = context->current)
+    if (is_same_net(config->addr, context->start, context->netmask))
+      return 1;
+  
+  return 0;
+}
+
+int config_has_mac(struct dhcp_config *config, unsigned char *hwaddr, int len, int type)
+{
+  struct hwaddr_config *conf_addr;
+  
+  for (conf_addr = config->hwaddr; conf_addr; conf_addr = conf_addr->next)
+    if (conf_addr->wildcard_mask == 0 &&
+	conf_addr->hwaddr_len == len &&
+	(conf_addr->hwaddr_type == type || conf_addr->hwaddr_type == 0) &&
+	memcmp(conf_addr->hwaddr, hwaddr, len) == 0)
+      return 1;
+  
+  return 0;
+}
+
+struct dhcp_config *find_config(struct dhcp_config *configs,
+				struct dhcp_context *context,
+				unsigned char *clid, int clid_len,
+				unsigned char *hwaddr, int hw_len, 
+				int hw_type, char *hostname)
+{
+  int count, new;
+  struct dhcp_config *config, *candidate; 
+  struct hwaddr_config *conf_addr;
+
+  if (clid)
+    for (config = configs; config; config = config->next)
+      if (config->flags & CONFIG_CLID)
+	{
+	  if (config->clid_len == clid_len && 
+	      memcmp(config->clid, clid, clid_len) == 0 &&
+	      is_addr_in_context(context, config))
+	    return config;
+	  
+	  /* dhcpcd prefixes ASCII client IDs by zero which is wrong, but we try and
+	     cope with that here */
+	  if (*clid == 0 && config->clid_len == clid_len-1  &&
+	      memcmp(config->clid, clid+1, clid_len-1) == 0 &&
+	      is_addr_in_context(context, config))
+	    return config;
+	}
+  
+
+  for (config = configs; config; config = config->next)
+    if (config_has_mac(config, hwaddr, hw_len, hw_type) &&
+	is_addr_in_context(context, config))
+      return config;
+  
+  if (hostname && context)
+    for (config = configs; config; config = config->next)
+      if ((config->flags & CONFIG_NAME) && 
+	  hostname_isequal(config->hostname, hostname) &&
+	  is_addr_in_context(context, config))
+	return config;
+
+  /* use match with fewest wildcast octets */
+  for (candidate = NULL, count = 0, config = configs; config; config = config->next)
+    if (is_addr_in_context(context, config))
+      for (conf_addr = config->hwaddr; conf_addr; conf_addr = conf_addr->next)
+	if (conf_addr->wildcard_mask != 0 &&
+	    conf_addr->hwaddr_len == hw_len &&	
+	    (conf_addr->hwaddr_type == hw_type || conf_addr->hwaddr_type == 0) &&
+	    (new = memcmp_masked(conf_addr->hwaddr, hwaddr, hw_len, conf_addr->wildcard_mask)) > count)
+	  {
+	    count = new;
+	    candidate = config;
+	  }
+
+  return candidate;
+}
+
+void dhcp_read_ethers(void)
+{
+  FILE *f = fopen(ETHERSFILE, "r");
+  unsigned int flags;
+  char *buff = daemon->namebuff;
+  char *ip, *cp;
+  struct in_addr addr;
+  unsigned char hwaddr[ETHER_ADDR_LEN];
+  struct dhcp_config **up, *tmp;
+  struct dhcp_config *config;
+  int count = 0, lineno = 0;
+
+  addr.s_addr = 0; /* eliminate warning */
+  
+  if (!f)
+    {
+      my_syslog(MS_DHCP | LOG_ERR, _("failed to read %s: %s"), ETHERSFILE, strerror(errno));
+      return;
+    }
+
+  /* This can be called again on SIGHUP, so remove entries created last time round. */
+  for (up = &daemon->dhcp_conf, config = daemon->dhcp_conf; config; config = tmp)
+    {
+      tmp = config->next;
+      if (config->flags & CONFIG_FROM_ETHERS)
+	{
+	  *up = tmp;
+	  /* cannot have a clid */
+	  if (config->flags & CONFIG_NAME)
+	    free(config->hostname);
+	  free(config->hwaddr);
+	  free(config);
+	}
+      else
+	up = &config->next;
+    }
+
+  while (fgets(buff, MAXDNAME, f))
+    {
+      char *host = NULL;
+      
+      lineno++;
+      
+      while (strlen(buff) > 0 && isspace((int)buff[strlen(buff)-1]))
+	buff[strlen(buff)-1] = 0;
+      
+      if ((*buff == '#') || (*buff == '+') || (*buff == 0))
+	continue;
+      
+      for (ip = buff; *ip && !isspace((int)*ip); ip++);
+      for(; *ip && isspace((int)*ip); ip++)
+	*ip = 0;
+      if (!*ip || parse_hex(buff, hwaddr, ETHER_ADDR_LEN, NULL, NULL) != ETHER_ADDR_LEN)
+	{
+	  my_syslog(MS_DHCP | LOG_ERR, _("bad line at %s line %d"), ETHERSFILE, lineno); 
+	  continue;
+	}
+      
+      /* check for name or dotted-quad */
+      for (cp = ip; *cp; cp++)
+	if (!(*cp == '.' || (*cp >='0' && *cp <= '9')))
+	  break;
+      
+      if (!*cp)
+	{
+	  if ((addr.s_addr = inet_addr(ip)) == (in_addr_t)-1)
+	    {
+	      my_syslog(MS_DHCP | LOG_ERR, _("bad address at %s line %d"), ETHERSFILE, lineno); 
+	      continue;
+	    }
+
+	  flags = CONFIG_ADDR;
+	  
+	  for (config = daemon->dhcp_conf; config; config = config->next)
+	    if ((config->flags & CONFIG_ADDR) && config->addr.s_addr == addr.s_addr)
+	      break;
+	}
+      else 
+	{
+	  int nomem;
+	  if (!(host = canonicalise(ip, &nomem)) || !legal_hostname(host))
+	    {
+	      if (!nomem)
+		my_syslog(MS_DHCP | LOG_ERR, _("bad name at %s line %d"), ETHERSFILE, lineno); 
+	      free(host);
+	      continue;
+	    }
+	      
+	  flags = CONFIG_NAME;
+
+	  for (config = daemon->dhcp_conf; config; config = config->next)
+	    if ((config->flags & CONFIG_NAME) && hostname_isequal(config->hostname, host))
+	      break;
+	}
+
+      if (config && (config->flags & CONFIG_FROM_ETHERS))
+	{
+	  my_syslog(MS_DHCP | LOG_ERR, _("ignoring %s line %d, duplicate name or IP address"), ETHERSFILE, lineno); 
+	  continue;
+	}
+	
+      if (!config)
+	{ 
+	  for (config = daemon->dhcp_conf; config; config = config->next)
+	    {
+	      struct hwaddr_config *conf_addr = config->hwaddr;
+	      if (conf_addr && 
+		  conf_addr->next == NULL && 
+		  conf_addr->wildcard_mask == 0 &&
+		  conf_addr->hwaddr_len == ETHER_ADDR_LEN &&
+		  (conf_addr->hwaddr_type == ARPHRD_ETHER || conf_addr->hwaddr_type == 0) &&
+		  memcmp(conf_addr->hwaddr, hwaddr, ETHER_ADDR_LEN) == 0)
+		break;
+	    }
+	  
+	  if (!config)
+	    {
+	      if (!(config = whine_malloc(sizeof(struct dhcp_config))))
+		continue;
+	      config->flags = CONFIG_FROM_ETHERS;
+	      config->hwaddr = NULL;
+	      config->domain = NULL;
+	      config->next = daemon->dhcp_conf;
+	      daemon->dhcp_conf = config;
+	    }
+	  
+	  config->flags |= flags;
+	  
+	  if (flags & CONFIG_NAME)
+	    {
+	      config->hostname = host;
+	      host = NULL;
+	    }
+	  
+	  if (flags & CONFIG_ADDR)
+	    config->addr = addr;
+	}
+      
+      config->flags |= CONFIG_NOCLID;
+      if (!config->hwaddr)
+	config->hwaddr = whine_malloc(sizeof(struct hwaddr_config));
+      if (config->hwaddr)
+	{
+	  memcpy(config->hwaddr->hwaddr, hwaddr, ETHER_ADDR_LEN);
+	  config->hwaddr->hwaddr_len = ETHER_ADDR_LEN;
+	  config->hwaddr->hwaddr_type = ARPHRD_ETHER;
+	  config->hwaddr->wildcard_mask = 0;
+	  config->hwaddr->next = NULL;
+	}
+      count++;
+      
+      free(host);
+
+    }
+  
+  fclose(f);
+
+  my_syslog(MS_DHCP | LOG_INFO, _("read %s - %d addresses"), ETHERSFILE, count);
+}
+
+void check_dhcp_hosts(int fatal)
+{
+  /* If the same IP appears in more than one host config, then DISCOVER
+     for one of the hosts will get the address, but REQUEST will be NAKed,
+     since the address is reserved by the other one -> protocol loop. 
+     Also check that FQDNs match the domain we are using. */
+  
+  struct dhcp_config *configs, *cp;
+ 
+  for (configs = daemon->dhcp_conf; configs; configs = configs->next)
+    {
+      char *domain;
+
+      if ((configs->flags & DHOPT_BANK) || fatal)
+       {
+	 for (cp = configs->next; cp; cp = cp->next)
+	   if ((configs->flags & cp->flags & CONFIG_ADDR) && configs->addr.s_addr == cp->addr.s_addr)
+	     {
+	       if (fatal)
+		 die(_("duplicate IP address %s in dhcp-config directive."), 
+		     inet_ntoa(cp->addr), EC_BADCONF);
+	       else
+		 my_syslog(MS_DHCP | LOG_ERR, _("duplicate IP address %s in %s."), 
+			   inet_ntoa(cp->addr), daemon->dhcp_hosts_file);
+	       configs->flags &= ~CONFIG_ADDR;
+	     }
+	 
+	 /* split off domain part */
+	 if ((configs->flags & CONFIG_NAME) && (domain = strip_hostname(configs->hostname)))
+	   configs->domain = domain;
+       }
+    }
+}
+
+void dhcp_update_configs(struct dhcp_config *configs)
+{
+  /* Some people like to keep all static IP addresses in /etc/hosts.
+     This goes through /etc/hosts and sets static addresses for any DHCP config
+     records which don't have an address and whose name matches. 
+     We take care to maintain the invariant that any IP address can appear
+     in at most one dhcp-host. Since /etc/hosts can be re-read by SIGHUP, 
+     restore the status-quo ante first. */
+  
+  struct dhcp_config *config;
+  struct crec *crec;
+
+  for (config = configs; config; config = config->next)
+    if (config->flags & CONFIG_ADDR_HOSTS)
+      config->flags &= ~(CONFIG_ADDR | CONFIG_ADDR_HOSTS);
+  
+  
+  if (daemon->port != 0)
+    for (config = configs; config; config = config->next)
+      if (!(config->flags & CONFIG_ADDR) &&
+	  (config->flags & CONFIG_NAME) && 
+	  (crec = cache_find_by_name(NULL, config->hostname, 0, F_IPV4)) &&
+	  (crec->flags & F_HOSTS))
+	{
+	  if (cache_find_by_name(crec, config->hostname, 0, F_IPV4))
+	    {
+	      /* use primary (first) address */
+	      while (crec && !(crec->flags & F_REVERSE))
+		crec = cache_find_by_name(crec, config->hostname, 0, F_IPV4);
+	      if (!crec)
+		continue; /* should be never */
+	      my_syslog(MS_DHCP | LOG_WARNING, _("%s has more than one address in hostsfile, using %s for DHCP"), 
+			config->hostname, inet_ntoa(crec->addr.addr.addr.addr4));
+	    }
+
+	  if (config_find_by_address(configs, crec->addr.addr.addr.addr4))
+	    my_syslog(MS_DHCP | LOG_WARNING, _("duplicate IP address %s (%s) in dhcp-config directive"), 
+		      inet_ntoa(crec->addr.addr.addr.addr4), config->hostname);
+	  else 
+	    {
+	      config->addr = crec->addr.addr.addr.addr4;
+	      config->flags |= CONFIG_ADDR | CONFIG_ADDR_HOSTS;
+	    }
+	}
+}
+
+/* If we've not found a hostname any other way, try and see if there's one in /etc/hosts
+   for this address. If it has a domain part, that must match the set domain and
+   it gets stripped. The set of legal domain names is bigger than the set of legal hostnames
+   so check here that the domain name is legal as a hostname. */
+char *host_from_dns(struct in_addr addr)
+{
+  struct crec *lookup;
+  char *hostname = NULL;
+  char *d1, *d2;
+
+  if (daemon->port == 0)
+    return NULL; /* DNS disabled. */
+  
+  lookup = cache_find_by_addr(NULL, (struct all_addr *)&addr, 0, F_IPV4);
+  if (lookup && (lookup->flags & F_HOSTS))
+    {
+      hostname = daemon->dhcp_buff;
+      strncpy(hostname, cache_get_name(lookup), 256);
+      hostname[255] = 0;
+      d1 = strip_hostname(hostname);
+      d2 = get_domain(addr);
+      if (!legal_hostname(hostname) || (d1 && (!d2 || !hostname_isequal(d1, d2))))
+	hostname = NULL;
+    }
+  
+  return hostname;
+}
+
+/* return domain or NULL if none. */
+char *strip_hostname(char *hostname)
+{
+  char *dot = strchr(hostname, '.');
+ 
+  if (!dot)
+    return NULL;
+  
+  *dot = 0; /* truncate */
+  if (strlen(dot+1) != 0)
+    return dot+1;
+  
+  return NULL;
+}
+
+#endif
+
diff --git a/src/dnsmasq.c b/src/dnsmasq.c
new file mode 100755
index 0000000..48e3ecf
--- /dev/null
+++ b/src/dnsmasq.c
@@ -0,0 +1,1287 @@
+/* dnsmasq is Copyright (c) 2000-2009 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"
+
+struct daemon *daemon;
+
+static char *compile_opts = 
+#ifndef HAVE_IPV6
+"no-"
+#endif
+"IPv6 "
+#ifndef HAVE_GETOPT_LONG
+"no-"
+#endif
+"GNU-getopt "
+#ifdef HAVE_BROKEN_RTC
+"no-RTC "
+#endif
+#ifdef NO_FORK
+"no-MMU "
+#endif
+#ifndef HAVE_DBUS
+"no-"
+#endif
+"DBus "
+#ifndef LOCALEDIR
+"no-"
+#endif
+"I18N "
+#ifndef HAVE_DHCP
+"no-"
+#endif
+"DHCP "
+#if defined(HAVE_DHCP) && !defined(HAVE_SCRIPT)
+"no-scripts "
+#endif
+#ifndef HAVE_TFTP
+"no-"
+#endif
+"TFTP";
+
+
+
+static volatile pid_t pid = 0;
+static volatile int pipewrite;
+
+static int set_dns_listeners(time_t now, fd_set *set, int *maxfdp);
+static void check_dns_listeners(fd_set *set, time_t now);
+static void sig_handler(int sig);
+static void async_event(int pipe, time_t now);
+static void fatal_event(struct event_desc *ev);
+static void poll_resolv(void);
+
+int main (int argc, char **argv)
+{
+  int bind_fallback = 0;
+  time_t now;
+  struct sigaction sigact;
+  struct iname *if_tmp;
+  int piperead, pipefd[2], err_pipe[2];
+  struct passwd *ent_pw = NULL;
+#if defined(HAVE_DHCP) && defined(HAVE_SCRIPT)
+  uid_t script_uid = 0;
+  gid_t script_gid = 0;
+#endif
+  struct group *gp = NULL;
+  long i, max_fd = sysconf(_SC_OPEN_MAX);
+  char *baduser = NULL;
+  int log_err;
+#if defined(HAVE_LINUX_NETWORK)
+  cap_user_header_t hdr = NULL;
+  cap_user_data_t data = NULL;
+#endif 
+
+#ifdef LOCALEDIR
+  setlocale(LC_ALL, "");
+  bindtextdomain("dnsmasq", LOCALEDIR); 
+  textdomain("dnsmasq");
+#endif
+
+  sigact.sa_handler = sig_handler;
+  sigact.sa_flags = 0;
+  sigemptyset(&sigact.sa_mask);
+  sigaction(SIGUSR1, &sigact, NULL);
+  sigaction(SIGUSR2, &sigact, NULL);
+  sigaction(SIGHUP, &sigact, NULL);
+  sigaction(SIGTERM, &sigact, NULL);
+  sigaction(SIGALRM, &sigact, NULL);
+  sigaction(SIGCHLD, &sigact, NULL);
+
+  /* ignore SIGPIPE */
+  sigact.sa_handler = SIG_IGN;
+  sigaction(SIGPIPE, &sigact, NULL);
+
+  umask(022); /* known umask, create leases and pid files as 0644 */
+
+  read_opts(argc, argv, compile_opts);
+    
+  if (daemon->edns_pktsz < PACKETSZ)
+    daemon->edns_pktsz = PACKETSZ;
+  daemon->packet_buff_sz = daemon->edns_pktsz > DNSMASQ_PACKETSZ ? 
+    daemon->edns_pktsz : DNSMASQ_PACKETSZ;
+  daemon->packet = safe_malloc(daemon->packet_buff_sz);
+
+#ifdef HAVE_DHCP
+  if (!daemon->lease_file)
+    {
+      if (daemon->dhcp)
+	daemon->lease_file = LEASEFILE;
+    }
+#endif
+  
+  /* Close any file descriptors we inherited apart from std{in|out|err} */
+  for (i = 0; i < max_fd; i++)
+    if (i != STDOUT_FILENO && i != STDERR_FILENO && i != STDIN_FILENO)
+      close(i);
+
+#ifdef HAVE_LINUX_NETWORK
+  netlink_init();
+#elif !(defined(IP_RECVDSTADDR) && \
+	defined(IP_RECVIF) && \
+	defined(IP_SENDSRCADDR))
+  if (!(daemon->options & OPT_NOWILD))
+    {
+      bind_fallback = 1;
+      daemon->options |= OPT_NOWILD;
+    }
+#endif
+
+#ifndef HAVE_TFTP
+  if (daemon->options & OPT_TFTP)
+    die(_("TFTP server not available: set HAVE_TFTP 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);
+#endif
+  
+  rand_init();
+  
+  now = dnsmasq_time();
+  
+#ifdef HAVE_DHCP
+  if (daemon->dhcp)
+    {
+      /* Note that order matters here, we must call lease_init before
+	 creating any file descriptors which shouldn't be leaked
+	 to the lease-script init process. */
+      lease_init(now);
+      dhcp_init();
+    }
+#endif
+
+  if (!enumerate_interfaces())
+    die(_("failed to find list of interfaces: %s"), NULL, EC_MISC);
+    
+  if (daemon->options & OPT_NOWILD) 
+    {
+      daemon->listeners = create_bound_listeners();
+
+      for (if_tmp = daemon->if_names; if_tmp; if_tmp = if_tmp->next)
+	if (if_tmp->name && !if_tmp->used)
+	  die(_("unknown interface %s"), if_tmp->name, EC_BADNET);
+  
+      for (if_tmp = daemon->if_addrs; if_tmp; if_tmp = if_tmp->next)
+	if (!if_tmp->used)
+	  {
+	    prettyprint_addr(&if_tmp->addr, daemon->namebuff);
+	    die(_("no interface with address %s"), daemon->namebuff, EC_BADNET);
+	  }
+    }
+  else if ((daemon->port != 0 || (daemon->options & OPT_TFTP)) &&
+	   !(daemon->listeners = create_wildcard_listeners()))
+    die(_("failed to create listening socket: %s"), NULL, EC_BADNET);
+  
+  if (daemon->port != 0)
+    cache_init();
+    
+  if (daemon->options & OPT_DBUS)
+#ifdef HAVE_DBUS
+    {
+      char *err;
+      daemon->dbus = NULL;
+      daemon->watches = NULL;
+      if ((err = dbus_init()))
+	die(_("DBus error: %s"), err, EC_MISC);
+    }
+#else
+  die(_("DBus not available: set HAVE_DBUS in src/config.h"), NULL, EC_BADCONF);
+#endif
+  
+  if (daemon->port != 0)
+    pre_allocate_sfds();
+
+#if defined(HAVE_DHCP) && defined(HAVE_SCRIPT)
+  /* Note getpwnam returns static storage */
+  if (daemon->dhcp && daemon->lease_change_command && daemon->scriptuser)
+    {
+      if ((ent_pw = getpwnam(daemon->scriptuser)))
+	{
+	  script_uid = ent_pw->pw_uid;
+	  script_gid = ent_pw->pw_gid;
+	 }
+      else
+	baduser = daemon->scriptuser;
+    }
+#endif
+  
+  if (daemon->username && !(ent_pw = getpwnam(daemon->username)))
+    baduser = daemon->username;
+  else if (daemon->groupname && !(gp = getgrnam(daemon->groupname)))
+    baduser = daemon->groupname;
+
+  if (baduser)
+    die(_("unknown user or group: %s"), baduser, EC_BADCONF);
+   
+  /* implement group defaults, "dip" if available, or group associated with uid */
+  if (!daemon->group_set && !gp)
+    {
+      if (!(gp = getgrnam(CHGRP)) && ent_pw)
+	gp = getgrgid(ent_pw->pw_gid);
+      
+      /* for error message */
+      if (gp)
+	daemon->groupname = gp->gr_name; 
+    }
+
+#if defined(HAVE_LINUX_NETWORK)
+  /* determine capability API version here, while we can still
+     call safe_malloc */
+  if (ent_pw && ent_pw->pw_uid != 0)
+    {
+      int capsize = 1; /* for header version 1 */
+      hdr = safe_malloc(sizeof(*hdr));
+
+      /* find version supported by kernel */
+      memset(hdr, 0, sizeof(*hdr));
+      capget(hdr, NULL);
+      
+      if (hdr->version != LINUX_CAPABILITY_VERSION_1)
+	{
+	  /* if unknown version, use largest supported version (3) */
+	  if (hdr->version != LINUX_CAPABILITY_VERSION_2)
+	    hdr->version = LINUX_CAPABILITY_VERSION_3;
+	  capsize = 2;
+	}
+      
+      data = safe_malloc(sizeof(*data) * capsize);
+      memset(data, 0, sizeof(*data) * capsize);
+    }
+#endif
+
+  /* Use a pipe to carry signals and other events back to the event loop 
+     in a race-free manner and another to carry errors to daemon-invoking process */
+  safe_pipe(pipefd, 1);
+  
+  piperead = pipefd[0];
+  pipewrite = pipefd[1];
+  /* prime the pipe to load stuff first time. */
+  send_event(pipewrite, EVENT_RELOAD, 0); 
+
+  err_pipe[1] = -1;
+  
+  if (!(daemon->options & OPT_DEBUG))   
+    {
+      int nullfd;
+
+      /* The following code "daemonizes" the process. 
+	 See Stevens section 12.4 */
+      
+      if (chdir("/") != 0)
+	die(_("cannot chdir to filesystem root: %s"), NULL, EC_MISC); 
+
+#ifndef NO_FORK      
+      if (!(daemon->options & OPT_NO_FORK))
+	{
+	  pid_t pid;
+	  
+	  /* pipe to carry errors back to original process.
+	     When startup is complete we close this and the process terminates. */
+	  safe_pipe(err_pipe, 0);
+	  
+	  if ((pid = fork()) == -1)
+	    /* fd == -1 since we've not forked, never returns. */
+	    send_event(-1, EVENT_FORK_ERR, errno);
+	   
+	  if (pid != 0)
+	    {
+	      struct event_desc ev;
+	      
+	      /* close our copy of write-end */
+	      close(err_pipe[1]);
+	      
+	      /* check for errors after the fork */
+	      if (read_write(err_pipe[0], (unsigned char *)&ev, sizeof(ev), 1))
+		fatal_event(&ev);
+	      
+	      _exit(EC_GOOD);
+	    } 
+	  
+	  close(err_pipe[0]);
+
+	  /* NO calls to die() from here on. */
+	  
+	  setsid();
+	 
+	  if ((pid = fork()) == -1)
+	    send_event(err_pipe[1], EVENT_FORK_ERR, errno);
+	 
+	  if (pid != 0)
+	    _exit(0);
+	}
+#endif
+            
+      /* write pidfile _after_ forking ! */
+      if (daemon->runfile)
+	{
+	  FILE *pidfile;
+	  
+	  /* only complain if started as root */
+	  if ((pidfile = fopen(daemon->runfile, "w")))
+	    {
+	      fprintf(pidfile, "%d\n", (int) getpid());
+	      fclose(pidfile);
+	    }
+	  else if (getuid() == 0)
+	    {
+	      send_event(err_pipe[1], EVENT_PIDFILE, errno);
+	      _exit(0);
+	    }
+	}
+         
+      /* open  stdout etc to /dev/null */
+      nullfd = open("/dev/null", O_RDWR);
+      dup2(nullfd, STDOUT_FILENO);
+      dup2(nullfd, STDERR_FILENO);
+      dup2(nullfd, STDIN_FILENO);
+      close(nullfd);
+    }
+  
+   log_err = log_start(ent_pw, err_pipe[1]); 
+   
+   /* if we are to run scripts, we need to fork a helper before dropping root. */
+  daemon->helperfd = -1;
+#if defined(HAVE_DHCP) && defined(HAVE_SCRIPT) 
+  if (daemon->dhcp && daemon->lease_change_command)
+    daemon->helperfd = create_helper(pipewrite, err_pipe[1], script_uid, script_gid, max_fd);
+#endif
+
+  if (!(daemon->options & OPT_DEBUG) && getuid() == 0)   
+    {
+      int bad_capabilities = 0;
+      gid_t dummy;
+      
+      /* remove all supplimentary groups */
+      if (gp && 
+	  (setgroups(0, &dummy) == -1 ||
+	   setgid(gp->gr_gid) == -1))
+	{
+	  send_event(err_pipe[1], EVENT_GROUP_ERR, errno);
+	  _exit(0);
+	}
+  
+      if (ent_pw && ent_pw->pw_uid != 0)
+	{     
+#if defined(HAVE_LINUX_NETWORK)
+	  /* On linux, we keep CAP_NETADMIN (for ARP-injection) and
+	     CAP_NET_RAW (for icmp) if we're doing dhcp */
+	  data->effective = data->permitted = data->inheritable =
+	    (1 << CAP_NET_ADMIN) | (1 << CAP_NET_RAW) | (1 << CAP_SETUID);
+	  
+	  /* Tell kernel to not clear capabilities when dropping root */
+	  if (capset(hdr, data) == -1 || prctl(PR_SET_KEEPCAPS, 1) == -1)
+	    bad_capabilities = errno;
+			  
+#elif defined(HAVE_SOLARIS_NETWORK)
+	  /* http://developers.sun.com/solaris/articles/program_privileges.html */
+	  priv_set_t *priv_set;
+	  
+	  if (!(priv_set = priv_str_to_set("basic", ",", NULL)) ||
+	      priv_addset(priv_set, PRIV_NET_ICMPACCESS) == -1 ||
+	      priv_addset(priv_set, PRIV_SYS_NET_CONFIG) == -1)
+	    bad_capabilities = errno;
+
+	  if (priv_set && bad_capabilities == 0)
+	    {
+	      priv_inverse(priv_set);
+	  
+	      if (setppriv(PRIV_OFF, PRIV_LIMIT, priv_set) == -1)
+		bad_capabilities = errno;
+	    }
+
+	  if (priv_set)
+	    priv_freeset(priv_set);
+
+#endif    
+
+	  if (bad_capabilities != 0)
+	    {
+	      send_event(err_pipe[1], EVENT_CAP_ERR, bad_capabilities);
+	      _exit(0);
+	    }
+	  
+	  /* finally drop root */
+	  if (setuid(ent_pw->pw_uid) == -1)
+	    {
+	      send_event(err_pipe[1], EVENT_USER_ERR, errno);
+	      _exit(0);
+	    }     
+
+#ifdef HAVE_LINUX_NETWORK
+	  data->effective = data->permitted = 
+	    (1 << CAP_NET_ADMIN) | (1 << CAP_NET_RAW);
+	  data->inheritable = 0;
+	  
+	  /* lose the setuid and setgid capbilities */
+	  if (capset(hdr, data) == -1)
+	    {
+	      send_event(err_pipe[1], EVENT_CAP_ERR, errno);
+	      _exit(0);
+	    }
+#endif
+	  
+	}
+    }
+  
+#ifdef HAVE_LINUX_NETWORK
+  if (daemon->options & OPT_DEBUG) 
+    prctl(PR_SET_DUMPABLE, 1);
+#endif
+
+  if (daemon->port == 0)
+    my_syslog(LOG_INFO, _("started, version %s DNS disabled"), VERSION);
+  else if (daemon->cachesize != 0)
+    my_syslog(LOG_INFO, _("started, version %s cachesize %d"), VERSION, daemon->cachesize);
+  else
+    my_syslog(LOG_INFO, _("started, version %s cache disabled"), VERSION);
+  
+  my_syslog(LOG_INFO, _("compile time options: %s"), compile_opts);
+  
+#ifdef HAVE_DBUS
+  if (daemon->options & OPT_DBUS)
+    {
+      if (daemon->dbus)
+	my_syslog(LOG_INFO, _("DBus support enabled: connected to system bus"));
+      else
+	my_syslog(LOG_INFO, _("DBus support enabled: bus connection pending"));
+    }
+#endif
+
+  if (log_err != 0)
+    my_syslog(LOG_WARNING, _("warning: failed to change owner of %s: %s"), 
+	      daemon->log_file, strerror(log_err));
+
+  if (bind_fallback)
+    my_syslog(LOG_WARNING, _("setting --bind-interfaces option because of OS limitations"));
+  
+  if (!(daemon->options & OPT_NOWILD)) 
+    for (if_tmp = daemon->if_names; if_tmp; if_tmp = if_tmp->next)
+      if (if_tmp->name && !if_tmp->used)
+	my_syslog(LOG_WARNING, _("warning: interface %s does not currently exist"), if_tmp->name);
+   
+  if (daemon->port != 0 && (daemon->options & OPT_NO_RESOLV))
+    {
+      if (daemon->resolv_files && !daemon->resolv_files->is_default)
+	my_syslog(LOG_WARNING, _("warning: ignoring resolv-file flag because no-resolv is set"));
+      daemon->resolv_files = NULL;
+      if (!daemon->servers)
+	my_syslog(LOG_WARNING, _("warning: no upstream servers configured"));
+    } 
+
+  if (daemon->max_logs != 0)
+    my_syslog(LOG_INFO, _("asynchronous logging enabled, queue limit is %d messages"), daemon->max_logs);
+
+#ifdef HAVE_DHCP
+  if (daemon->dhcp)
+    {
+      struct dhcp_context *dhcp_tmp;
+      
+      for (dhcp_tmp = daemon->dhcp; dhcp_tmp; dhcp_tmp = dhcp_tmp->next)
+	{
+	  prettyprint_time(daemon->dhcp_buff2, dhcp_tmp->lease_time);
+	  strcpy(daemon->dhcp_buff, inet_ntoa(dhcp_tmp->start));
+	  my_syslog(MS_DHCP | LOG_INFO, 
+		    (dhcp_tmp->flags & CONTEXT_STATIC) ? 
+		    _("DHCP, static leases only on %.0s%s, lease time %s") :
+		    (dhcp_tmp->flags & CONTEXT_PROXY) ?
+		    _("DHCP, proxy on subnet %.0s%s%.0s") :
+		    _("DHCP, IP range %s -- %s, lease time %s"),
+		    daemon->dhcp_buff, inet_ntoa(dhcp_tmp->end), daemon->dhcp_buff2);
+	}
+    }
+#endif
+
+#ifdef HAVE_TFTP
+  if (daemon->options & OPT_TFTP)
+    {
+#ifdef FD_SETSIZE
+      if (FD_SETSIZE < (unsigned)max_fd)
+	max_fd = FD_SETSIZE;
+#endif
+
+      my_syslog(MS_TFTP | LOG_INFO, "TFTP %s%s %s", 
+		daemon->tftp_prefix ? _("root is ") : _("enabled"),
+		daemon->tftp_prefix ? daemon->tftp_prefix: "",
+		daemon->options & OPT_TFTP_SECURE ? _("secure mode") : "");
+      
+      /* This is a guess, it assumes that for small limits, 
+	 disjoint files might be served, but for large limits, 
+	 a single file will be sent to may clients (the file only needs
+	 one fd). */
+
+      max_fd -= 30; /* use other than TFTP */
+      
+      if (max_fd < 0)
+	max_fd = 5;
+      else if (max_fd < 100)
+	max_fd = max_fd/2;
+      else
+	max_fd = max_fd - 20;
+      
+      /* if we have to use a limited range of ports, 
+	 that will limit the number of transfers */
+      if (daemon->start_tftp_port != 0 &&
+	  daemon->end_tftp_port - daemon->start_tftp_port + 1 < max_fd)
+	max_fd = daemon->end_tftp_port - daemon->start_tftp_port + 1;
+
+      if (daemon->tftp_max > max_fd)
+	{
+	  daemon->tftp_max = max_fd;
+	  my_syslog(MS_TFTP | LOG_WARNING, 
+		    _("restricting maximum simultaneous TFTP transfers to %d"), 
+		    daemon->tftp_max);
+	}
+    }
+#endif
+
+  /* finished start-up - release original process */
+  if (err_pipe[1] != -1)
+    close(err_pipe[1]);
+  
+  if (daemon->port != 0)
+    check_servers();
+  
+  pid = getpid();
+  
+  while (1)
+    {
+      int maxfd = -1;
+      struct timeval t, *tp = NULL;
+      fd_set rset, wset, eset;
+      
+      FD_ZERO(&rset);
+      FD_ZERO(&wset);
+      FD_ZERO(&eset);
+      
+      /* if we are out of resources, find how long we have to wait
+	 for some to come free, we'll loop around then and restart
+	 listening for queries */
+      if ((t.tv_sec = set_dns_listeners(now, &rset, &maxfd)) != 0)
+	{
+	  t.tv_usec = 0;
+	  tp = &t;
+	}
+
+      /* Whilst polling for the dbus, or doing a tftp transfer, wake every quarter second */
+      if (daemon->tftp_trans ||
+	  ((daemon->options & OPT_DBUS) && !daemon->dbus))
+	{
+	  t.tv_sec = 0;
+	  t.tv_usec = 250000;
+	  tp = &t;
+	}
+
+#ifdef HAVE_DBUS
+      set_dbus_listeners(&maxfd, &rset, &wset, &eset);
+#endif	
+  
+#ifdef HAVE_DHCP
+      if (daemon->dhcp)
+	{
+	  FD_SET(daemon->dhcpfd, &rset);
+	  bump_maxfd(daemon->dhcpfd, &maxfd);
+	}
+#endif
+
+#ifdef HAVE_LINUX_NETWORK
+      FD_SET(daemon->netlinkfd, &rset);
+      bump_maxfd(daemon->netlinkfd, &maxfd);
+#endif
+      
+      FD_SET(piperead, &rset);
+      bump_maxfd(piperead, &maxfd);
+
+#ifdef HAVE_DHCP
+#  ifdef HAVE_SCRIPT
+      while (helper_buf_empty() && do_script_run(now));
+
+      if (!helper_buf_empty())
+	{
+	  FD_SET(daemon->helperfd, &wset);
+	  bump_maxfd(daemon->helperfd, &maxfd);
+	}
+#  else
+      /* need this for other side-effects */
+      while (do_script_run(now));
+#  endif
+#endif
+   
+      /* must do this just before select(), when we know no
+	 more calls to my_syslog() can occur */
+      set_log_writer(&wset, &maxfd);
+      
+      if (select(maxfd+1, &rset, &wset, &eset, tp) < 0)
+	{
+	  /* otherwise undefined after error */
+	  FD_ZERO(&rset); FD_ZERO(&wset); FD_ZERO(&eset);
+	}
+
+      now = dnsmasq_time();
+
+      check_log_writer(&wset);
+
+      /* Check for changes to resolv files once per second max. */
+      /* Don't go silent for long periods if the clock goes backwards. */
+      if (daemon->last_resolv == 0 || 
+	  difftime(now, daemon->last_resolv) > 1.0 || 
+	  difftime(now, daemon->last_resolv) < -1.0)
+	{
+	  daemon->last_resolv = now;
+
+	  if (daemon->port != 0 && !(daemon->options & OPT_NO_POLL))
+	    poll_resolv();
+	}
+      
+      if (FD_ISSET(piperead, &rset))
+	async_event(piperead, now);
+      
+#ifdef HAVE_LINUX_NETWORK
+      if (FD_ISSET(daemon->netlinkfd, &rset))
+	netlink_multicast();
+#endif
+      
+#ifdef HAVE_DBUS
+      /* if we didn't create a DBus connection, retry now. */ 
+     if ((daemon->options & OPT_DBUS) && !daemon->dbus)
+	{
+	  char *err;
+	  if ((err = dbus_init()))
+	    my_syslog(LOG_WARNING, _("DBus error: %s"), err);
+	  if (daemon->dbus)
+	    my_syslog(LOG_INFO, _("connected to system DBus"));
+	}
+      check_dbus_listeners(&rset, &wset, &eset);
+#endif
+      
+      check_dns_listeners(&rset, now);
+
+#ifdef HAVE_TFTP
+      check_tftp_listeners(&rset, now);
+#endif      
+
+#ifdef HAVE_DHCP
+      if (daemon->dhcp && FD_ISSET(daemon->dhcpfd, &rset))
+	dhcp_packet(now);
+
+#  ifdef HAVE_SCRIPT
+      if (daemon->helperfd != -1 && FD_ISSET(daemon->helperfd, &wset))
+	helper_write();
+#  endif
+#endif
+
+    }
+}
+
+static void sig_handler(int sig)
+{
+  if (pid == 0)
+    {
+      /* ignore anything other than TERM during startup
+	 and in helper proc. (helper ignore TERM too) */
+      if (sig == SIGTERM)
+	exit(EC_MISC);
+    }
+  else if (pid != getpid())
+    {
+      /* alarm is used to kill TCP children after a fixed time. */
+      if (sig == SIGALRM)
+	_exit(0);
+    }
+  else
+    {
+      /* master process */
+      int event, errsave = errno;
+      
+      if (sig == SIGHUP)
+	event = EVENT_RELOAD;
+      else if (sig == SIGCHLD)
+	event = EVENT_CHILD;
+      else if (sig == SIGALRM)
+	event = EVENT_ALARM;
+      else if (sig == SIGTERM)
+	event = EVENT_TERM;
+      else if (sig == SIGUSR1)
+	event = EVENT_DUMP;
+      else if (sig == SIGUSR2)
+	event = EVENT_REOPEN;
+      else
+	return;
+
+      send_event(pipewrite, event, 0); 
+      errno = errsave;
+    }
+}
+
+void send_event(int fd, int event, int data)
+{
+  struct event_desc ev;
+  
+  ev.event = event;
+  ev.data = data;
+  
+  /* error pipe, debug mode. */
+  if (fd == -1)
+    fatal_event(&ev);
+  else
+    /* pipe is non-blocking and struct event_desc is smaller than
+       PIPE_BUF, so this either fails or writes everything */
+    while (write(fd, &ev, sizeof(ev)) == -1 && errno == EINTR);
+}
+
+static void fatal_event(struct event_desc *ev)
+{
+  errno = ev->data;
+  
+  switch (ev->event)
+    {
+    case EVENT_DIE:
+      exit(0);
+
+    case EVENT_FORK_ERR:
+      die(_("cannot fork into background: %s"), NULL, EC_MISC);
+  
+    case EVENT_PIPE_ERR:
+      die(_("failed to create helper: %s"), NULL, EC_MISC);
+  
+    case EVENT_CAP_ERR:
+      die(_("setting capabilities failed: %s"), NULL, EC_MISC);
+
+    case EVENT_USER_ERR:
+    case EVENT_HUSER_ERR:
+      die(_("failed to change user-id to %s: %s"), 
+	  ev->event == EVENT_USER_ERR ? daemon->username : daemon->scriptuser,
+	  EC_MISC);
+
+    case EVENT_GROUP_ERR:
+      die(_("failed to change group-id to %s: %s"), daemon->groupname, EC_MISC);
+      
+    case EVENT_PIDFILE:
+      die(_("failed to open pidfile %s: %s"), daemon->runfile, EC_FILE);
+
+    case EVENT_LOG_ERR:
+      die(_("cannot open %s: %s"), daemon->log_file ? daemon->log_file : "log", EC_FILE);
+    }
+}	
+      
+static void async_event(int pipe, time_t now)
+{
+  pid_t p;
+  struct event_desc ev;
+  int i;
+
+  if (read_write(pipe, (unsigned char *)&ev, sizeof(ev), 1))
+    switch (ev.event)
+      {
+      case EVENT_RELOAD:
+	clear_cache_and_reload(now);
+	if (daemon->port != 0 && daemon->resolv_files && (daemon->options & OPT_NO_POLL))
+	  {
+	    reload_servers(daemon->resolv_files->name);
+	    check_servers();
+	  }
+#ifdef HAVE_DHCP
+	rerun_scripts();
+#endif
+	break;
+	
+      case EVENT_DUMP:
+	if (daemon->port != 0)
+	  dump_cache(now);
+	break;
+	
+      case EVENT_ALARM:
+#ifdef HAVE_DHCP
+	if (daemon->dhcp)
+	  {
+	    lease_prune(NULL, now);
+	    lease_update_file(now);
+	  }
+#endif
+	break;
+		
+      case EVENT_CHILD:
+	/* See Stevens 5.10 */
+	while ((p = waitpid(-1, NULL, WNOHANG)) != 0)
+	  if (p == -1)
+	    {
+	      if (errno != EINTR)
+		break;
+	    }      
+	  else 
+	    for (i = 0 ; i < MAX_PROCS; i++)
+	      if (daemon->tcp_pids[i] == p)
+		daemon->tcp_pids[i] = 0;
+	break;
+	
+      case EVENT_KILLED:
+	my_syslog(LOG_WARNING, _("child process killed by signal %d"), ev.data);
+	break;
+
+      case EVENT_EXITED:
+	my_syslog(LOG_WARNING, _("child process exited with status %d"), ev.data);
+	break;
+
+      case EVENT_EXEC_ERR:
+	my_syslog(LOG_ERR, _("failed to execute %s: %s"), 
+		  daemon->lease_change_command, strerror(ev.data));
+	break;
+
+	/* necessary for fatal errors in helper */
+      case EVENT_HUSER_ERR:
+      case EVENT_DIE:
+	fatal_event(&ev);
+	break;
+
+      case EVENT_REOPEN:
+	/* Note: this may leave TCP-handling processes with the old file still open.
+	   Since any such process will die in CHILD_LIFETIME or probably much sooner,
+	   we leave them logging to the old file. */
+	if (daemon->log_file != NULL)
+	  log_reopen(daemon->log_file);
+	break;
+	
+      case EVENT_TERM:
+	/* Knock all our children on the head. */
+	for (i = 0; i < MAX_PROCS; i++)
+	  if (daemon->tcp_pids[i] != 0)
+	    kill(daemon->tcp_pids[i], SIGALRM);
+	
+#if defined(HAVE_DHCP) && defined(HAVE_SCRIPT)
+	/* handle pending lease transitions */
+	if (daemon->helperfd != -1)
+	  {
+	    /* block in writes until all done */
+	    if ((i = fcntl(daemon->helperfd, F_GETFL)) != -1)
+	      fcntl(daemon->helperfd, F_SETFL, i & ~O_NONBLOCK); 
+	    do {
+	      helper_write();
+	    } while (!helper_buf_empty() || do_script_run(now));
+	    close(daemon->helperfd);
+	  }
+#endif
+	
+	if (daemon->lease_stream)
+	  fclose(daemon->lease_stream);
+
+	if (daemon->runfile)
+	  unlink(daemon->runfile);
+	
+	my_syslog(LOG_INFO, _("exiting on receipt of SIGTERM"));
+	flush_log();
+	exit(EC_GOOD);
+      }
+}
+
+static void poll_resolv()
+{
+  struct resolvc *res, *latest;
+  struct stat statbuf;
+  time_t last_change = 0;
+  /* There may be more than one possible file. 
+     Go through and find the one which changed _last_.
+     Warn of any which can't be read. */
+  for (latest = NULL, res = daemon->resolv_files; res; res = res->next)
+    if (stat(res->name, &statbuf) == -1)
+      {
+	if (!res->logged)
+	  my_syslog(LOG_WARNING, _("failed to access %s: %s"), res->name, strerror(errno));
+	res->logged = 1;
+      }
+    else
+      {
+	res->logged = 0;
+	if (statbuf.st_mtime != res->mtime)
+	  {
+	    res->mtime = statbuf.st_mtime;
+	    if (difftime(statbuf.st_mtime, last_change) > 0.0)
+	      {
+		last_change = statbuf.st_mtime;
+		latest = res;
+	      }
+	  }
+      }
+  
+  if (latest)
+    {
+      static int warned = 0;
+      if (reload_servers(latest->name))
+	{
+	  my_syslog(LOG_INFO, _("reading %s"), latest->name);
+	  warned = 0;
+	  check_servers();
+	  if (daemon->options & OPT_RELOAD)
+	    cache_reload();
+	}
+      else 
+	{
+	  latest->mtime = 0;
+	  if (!warned)
+	    {
+	      my_syslog(LOG_WARNING, _("no servers found in %s, will retry"), latest->name);
+	      warned = 1;
+	    }
+	}
+    }
+}       
+
+void clear_cache_and_reload(time_t now)
+{
+  if (daemon->port != 0)
+    cache_reload();
+  
+#ifdef HAVE_DHCP
+  if (daemon->dhcp)
+    {
+      if (daemon->options & OPT_ETHERS)
+	dhcp_read_ethers();
+      reread_dhcp();
+      dhcp_update_configs(daemon->dhcp_conf);
+      check_dhcp_hosts(0);
+      lease_update_from_configs(); 
+      lease_update_file(now); 
+      lease_update_dns();
+    }
+#endif
+}
+
+static int set_dns_listeners(time_t now, fd_set *set, int *maxfdp)
+{
+  struct serverfd *serverfdp;
+  struct listener *listener;
+  int wait = 0, i;
+  
+#ifdef HAVE_TFTP
+  int  tftp = 0;
+  struct tftp_transfer *transfer;
+  for (transfer = daemon->tftp_trans; transfer; transfer = transfer->next)
+    {
+      tftp++;
+      FD_SET(transfer->sockfd, set);
+      bump_maxfd(transfer->sockfd, maxfdp);
+    }
+#endif
+  
+  /* will we be able to get memory? */
+  if (daemon->port != 0)
+    get_new_frec(now, &wait);
+  
+  for (serverfdp = daemon->sfds; serverfdp; serverfdp = serverfdp->next)
+    {
+      FD_SET(serverfdp->fd, set);
+      bump_maxfd(serverfdp->fd, maxfdp);
+    }
+
+  if (daemon->port != 0 && !daemon->osport)
+    for (i = 0; i < RANDOM_SOCKS; i++)
+      if (daemon->randomsocks[i].refcount != 0)
+	{
+	  FD_SET(daemon->randomsocks[i].fd, set);
+	  bump_maxfd(daemon->randomsocks[i].fd, maxfdp);
+	}
+  
+  for (listener = daemon->listeners; listener; listener = listener->next)
+    {
+      /* only listen for queries if we have resources */
+      if (listener->fd != -1 && wait == 0)
+	{
+	  FD_SET(listener->fd, set);
+	  bump_maxfd(listener->fd, maxfdp);
+	}
+
+      /* death of a child goes through the select loop, so
+	 we don't need to explicitly arrange to wake up here */
+      if  (listener->tcpfd != -1)
+	for (i = 0; i < MAX_PROCS; i++)
+	  if (daemon->tcp_pids[i] == 0)
+	    {
+	      FD_SET(listener->tcpfd, set);
+	      bump_maxfd(listener->tcpfd, maxfdp);
+	      break;
+	    }
+
+#ifdef HAVE_TFTP
+      if (tftp <= daemon->tftp_max && listener->tftpfd != -1)
+	{
+	  FD_SET(listener->tftpfd, set);
+	  bump_maxfd(listener->tftpfd, maxfdp);
+	}
+#endif
+
+    }
+  
+  return wait;
+}
+
+static void check_dns_listeners(fd_set *set, time_t now)
+{
+  struct serverfd *serverfdp;
+  struct listener *listener;
+  int i;
+
+  for (serverfdp = daemon->sfds; serverfdp; serverfdp = serverfdp->next)
+    if (FD_ISSET(serverfdp->fd, set))
+      reply_query(serverfdp->fd, serverfdp->source_addr.sa.sa_family, now);
+  
+  if (daemon->port != 0 && !daemon->osport)
+    for (i = 0; i < RANDOM_SOCKS; i++)
+      if (daemon->randomsocks[i].refcount != 0 && 
+	  FD_ISSET(daemon->randomsocks[i].fd, set))
+	reply_query(daemon->randomsocks[i].fd, daemon->randomsocks[i].family, now);
+  
+  for (listener = daemon->listeners; listener; listener = listener->next)
+    {
+      if (listener->fd != -1 && FD_ISSET(listener->fd, set))
+	receive_query(listener, now); 
+      
+#ifdef HAVE_TFTP     
+      if (listener->tftpfd != -1 && FD_ISSET(listener->tftpfd, set))
+	tftp_request(listener, now);
+#endif
+
+      if (listener->tcpfd != -1 && FD_ISSET(listener->tcpfd, set))
+	{
+	  int confd;
+	  struct irec *iface = NULL;
+	  pid_t p;
+	  
+	  while((confd = accept(listener->tcpfd, NULL, NULL)) == -1 && errno == EINTR);
+	  
+	  if (confd == -1)
+	    continue;
+	  
+	  if (daemon->options & OPT_NOWILD)
+	    iface = listener->iface;
+	  else
+	    {
+	      union mysockaddr tcp_addr;
+	      socklen_t tcp_len = sizeof(union mysockaddr);
+	      /* Check for allowed interfaces when binding the wildcard address:
+		 we do this by looking for an interface with the same address as 
+		 the local address of the TCP connection, then looking to see if that's
+		 an allowed interface. As a side effect, we get the netmask of the
+		 interface too, for localisation. */
+	      
+	      /* interface may be new since startup */
+	      if (enumerate_interfaces() &&
+		  getsockname(confd, (struct sockaddr *)&tcp_addr, &tcp_len) != -1)
+		for (iface = daemon->interfaces; iface; iface = iface->next)
+		  if (sockaddr_isequal(&iface->addr, &tcp_addr))
+		    break;
+	    }
+	  
+	  if (!iface)
+	    {
+	      shutdown(confd, SHUT_RDWR);
+	      close(confd);
+	    }
+#ifndef NO_FORK
+	  else if (!(daemon->options & OPT_DEBUG) && (p = fork()) != 0)
+	    {
+	      if (p != -1)
+		{
+		  int i;
+		  for (i = 0; i < MAX_PROCS; i++)
+		    if (daemon->tcp_pids[i] == 0)
+		      {
+			daemon->tcp_pids[i] = p;
+			break;
+		      }
+		}
+	      close(confd);
+	    }
+#endif
+	  else
+	    {
+	      unsigned char *buff;
+	      struct server *s; 
+	      int flags;
+	      struct in_addr dst_addr_4;
+	      
+	      dst_addr_4.s_addr = 0;
+	      
+	       /* Arrange for SIGALARM after CHILD_LIFETIME seconds to
+		  terminate the process. */
+	      if (!(daemon->options & OPT_DEBUG))
+		alarm(CHILD_LIFETIME);
+	      
+	      /* start with no upstream connections. */
+	      for (s = daemon->servers; s; s = s->next)
+		 s->tcpfd = -1; 
+	      
+	      /* The connected socket inherits non-blocking
+		 attribute from the listening socket. 
+		 Reset that here. */
+	      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);
+	       
+	      shutdown(confd, SHUT_RDWR);
+	      close(confd);
+	      
+	      if (buff)
+		free(buff);
+	      
+	      for (s = daemon->servers; s; s = s->next)
+		if (s->tcpfd != -1)
+		  {
+		    shutdown(s->tcpfd, SHUT_RDWR);
+		    close(s->tcpfd);
+		  }
+#ifndef NO_FORK		   
+	      if (!(daemon->options & OPT_DEBUG))
+		{
+		  flush_log();
+		  _exit(0);
+		}
+#endif
+	    }
+	}
+    }
+}
+
+#ifdef HAVE_DHCP
+int make_icmp_sock(void)
+{
+  int fd;
+  int zeroopt = 0;
+
+  if ((fd = socket (AF_INET, SOCK_RAW, IPPROTO_ICMP)) != -1)
+    {
+      if (!fix_fd(fd) ||
+	  setsockopt(fd, SOL_SOCKET, SO_DONTROUTE, &zeroopt, sizeof(zeroopt)) == -1)
+	{
+	  close(fd);
+	  fd = -1;
+	}
+    }
+
+  return fd;
+}
+
+int icmp_ping(struct in_addr addr)
+{
+  /* Try and get an ICMP echo from a machine. */
+
+  /* Note that whilst in the three second wait, we check for 
+     (and service) events on the DNS and TFTP  sockets, (so doing that
+     better not use any resources our caller has in use...)
+     but we remain deaf to signals or further DHCP packets. */
+
+  int fd;
+  struct sockaddr_in saddr;
+  struct { 
+    struct ip ip;
+    struct icmp icmp;
+  } packet;
+  unsigned short id = rand16();
+  unsigned int i, j;
+  int gotreply = 0;
+  time_t start, now;
+
+#if defined(HAVE_LINUX_NETWORK) || defined (HAVE_SOLARIS_NETWORK)
+  if ((fd = make_icmp_sock()) == -1)
+    return 0;
+#else
+  int opt = 2000;
+  fd = daemon->dhcp_icmp_fd;
+  setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &opt, sizeof(opt));
+#endif
+
+  saddr.sin_family = AF_INET;
+  saddr.sin_port = 0;
+  saddr.sin_addr = addr;
+#ifdef HAVE_SOCKADDR_SA_LEN
+  saddr.sin_len = sizeof(struct sockaddr_in);
+#endif
+  
+  memset(&packet.icmp, 0, sizeof(packet.icmp));
+  packet.icmp.icmp_type = ICMP_ECHO;
+  packet.icmp.icmp_id = id;
+  for (j = 0, i = 0; i < sizeof(struct icmp) / 2; i++)
+    j += ((u16 *)&packet.icmp)[i];
+  while (j>>16)
+    j = (j & 0xffff) + (j >> 16);  
+  packet.icmp.icmp_cksum = (j == 0xffff) ? j : ~j;
+  
+  while (sendto(fd, (char *)&packet.icmp, sizeof(struct icmp), 0, 
+		(struct sockaddr *)&saddr, sizeof(saddr)) == -1 &&
+	 retry_send());
+  
+  for (now = start = dnsmasq_time(); 
+       difftime(now, start) < (float)PING_WAIT;)
+    {
+      struct timeval tv;
+      fd_set rset, wset;
+      struct sockaddr_in faddr;
+      int maxfd = fd; 
+      socklen_t len = sizeof(faddr);
+      
+      tv.tv_usec = 250000;
+      tv.tv_sec = 0; 
+      
+      FD_ZERO(&rset);
+      FD_ZERO(&wset);
+      FD_SET(fd, &rset);
+      set_dns_listeners(now, &rset, &maxfd);
+      set_log_writer(&wset, &maxfd);
+
+      if (select(maxfd+1, &rset, &wset, NULL, &tv) < 0)
+	{
+	  FD_ZERO(&rset);
+	  FD_ZERO(&wset);
+	}
+
+      now = dnsmasq_time();
+
+      check_log_writer(&wset);
+      check_dns_listeners(&rset, now);
+
+#ifdef HAVE_TFTP
+      check_tftp_listeners(&rset, now);
+#endif
+
+      if (FD_ISSET(fd, &rset) &&
+	  recvfrom(fd, &packet, sizeof(packet), 0,
+		   (struct sockaddr *)&faddr, &len) == sizeof(packet) &&
+	  saddr.sin_addr.s_addr == faddr.sin_addr.s_addr &&
+	  packet.icmp.icmp_type == ICMP_ECHOREPLY &&
+	  packet.icmp.icmp_seq == 0 &&
+	  packet.icmp.icmp_id == id)
+	{
+	  gotreply = 1;
+	  break;
+	}
+    }
+  
+#if defined(HAVE_LINUX_NETWORK) || defined(HAVE_SOLARIS_NETWORK)
+  close(fd);
+#else
+  opt = 1;
+  setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &opt, sizeof(opt));
+#endif
+
+  return gotreply;
+}
+#endif
+
+ 
diff --git a/src/dnsmasq.h b/src/dnsmasq.h
new file mode 100755
index 0000000..e84ea85
--- /dev/null
+++ b/src/dnsmasq.h
@@ -0,0 +1,885 @@
+/* dnsmasq is Copyright (c) 2000-2009 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/>.
+*/
+
+#define COPYRIGHT "Copyright (C) 2000-2009 Simon Kelley" 
+
+#ifndef NO_LARGEFILE
+/* Ensure we can use files >2GB (log files may grow this big) */
+#  define _LARGEFILE_SOURCE 1
+#  define _FILE_OFFSET_BITS 64
+#endif
+
+/* Get linux C library versions. */
+#ifdef __linux__
+#  define _GNU_SOURCE
+#  include <features.h> 
+#endif
+
+/* get these before config.h  for IPv6 stuff... */
+#include <sys/types.h> 
+#include <sys/socket.h>
+#include <netinet/in.h>
+
+#ifdef __APPLE__
+#  include <nameser.h>
+#  include <arpa/nameser_compat.h>
+#else
+#  include <arpa/nameser.h>
+#endif
+
+/* and this. */
+#include <getopt.h>
+
+#include "config.h"
+
+#define gettext_noop(S) (S)
+#ifndef LOCALEDIR
+#  define _(S) (S)
+#else
+#  include <libintl.h>
+#  include <locale.h>   
+#  define _(S) gettext(S)
+#endif
+
+#include <arpa/inet.h>
+#include <sys/stat.h>
+#include <sys/ioctl.h>
+#if defined(HAVE_SOLARIS_NETWORK)
+#include <sys/sockio.h>
+#endif
+#include <sys/select.h>
+#include <sys/wait.h>
+#include <sys/time.h>
+#include <sys/un.h>
+#include <limits.h>
+#include <net/if.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <ctype.h>
+#include <signal.h>
+#include <stddef.h>
+#include <time.h>
+#include <errno.h>
+#include <pwd.h>
+#include <grp.h>
+#include <stdarg.h>
+#if defined(__OpenBSD__) || defined(__NetBSD__) || defined(__sun__) || defined (__sun)
+#  include <netinet/if_ether.h>
+#else
+#  include <net/ethernet.h>
+#endif
+#include <net/if_arp.h>
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+#include <netinet/ip_icmp.h>
+#include <sys/uio.h>
+#include <syslog.h>
+#include <dirent.h>
+#ifndef HAVE_LINUX_NETWORK
+#  include <net/if_dl.h>
+#endif
+
+#if defined(HAVE_LINUX_NETWORK)
+#include <linux/capability.h>
+/* There doesn't seem to be a universally-available 
+   userpace header for these. */
+extern int capset(cap_user_header_t header, cap_user_data_t data);
+extern int capget(cap_user_header_t header, cap_user_data_t data);
+#define LINUX_CAPABILITY_VERSION_1  0x19980330
+#define LINUX_CAPABILITY_VERSION_2  0x20071026
+#define LINUX_CAPABILITY_VERSION_3  0x20080522
+
+#include <sys/prctl.h>
+#elif defined(HAVE_SOLARIS_NETWORK)
+#include <priv.h>
+#endif
+
+/* daemon is function in the C library.... */
+#define daemon dnsmasq_daemon
+
+/* Async event queue */
+struct event_desc {
+  int event, data;
+};
+
+#define EVENT_RELOAD    1
+#define EVENT_DUMP      2
+#define EVENT_ALARM     3
+#define EVENT_TERM      4
+#define EVENT_CHILD     5
+#define EVENT_REOPEN    6
+#define EVENT_EXITED    7
+#define EVENT_KILLED    8
+#define EVENT_EXEC_ERR  9
+#define EVENT_PIPE_ERR  10
+#define EVENT_USER_ERR  11
+#define EVENT_CAP_ERR   12
+#define EVENT_PIDFILE   13
+#define EVENT_HUSER_ERR 14
+#define EVENT_GROUP_ERR 15
+#define EVENT_DIE       16
+#define EVENT_LOG_ERR   17
+#define EVENT_FORK_ERR  18
+
+/* Exit codes. */
+#define EC_GOOD        0
+#define EC_BADCONF     1
+#define EC_BADNET      2
+#define EC_FILE        3
+#define EC_NOMEM       4
+#define EC_MISC        5
+#define EC_INIT_OFFSET 10
+
+/* Min buffer size: we check after adding each record, so there must be 
+   memory for the largest packet, and the largest record so the
+   min for DNS is PACKETSZ+MAXDNAME+RRFIXEDSZ which is < 1000.
+   This might be increased is EDNS packet size if greater than the minimum.
+*/
+#define DNSMASQ_PACKETSZ PACKETSZ+MAXDNAME+RRFIXEDSZ
+
+#define OPT_BOGUSPRIV      (1u<<0)
+#define OPT_FILTER         (1u<<1)
+#define OPT_LOG            (1u<<2)
+#define OPT_SELFMX         (1u<<3)
+#define OPT_NO_HOSTS       (1u<<4)
+#define OPT_NO_POLL        (1u<<5)
+#define OPT_DEBUG          (1u<<6)
+#define OPT_ORDER          (1u<<7)
+#define OPT_NO_RESOLV      (1u<<8)
+#define OPT_EXPAND         (1u<<9)
+#define OPT_LOCALMX        (1u<<10)
+#define OPT_NO_NEG         (1u<<11)
+#define OPT_NODOTS_LOCAL   (1u<<12)
+#define OPT_NOWILD         (1u<<13)
+#define OPT_ETHERS         (1u<<14)
+#define OPT_RESOLV_DOMAIN  (1u<<15)
+#define OPT_NO_FORK        (1u<<16)
+#define OPT_AUTHORITATIVE  (1u<<17)
+#define OPT_LOCALISE       (1u<<18)
+#define OPT_DBUS           (1u<<19)
+#define OPT_DHCP_FQDN      (1u<<20)
+#define OPT_NO_PING        (1u<<21)
+#define OPT_LEASE_RO       (1u<<22)
+#define OPT_ALL_SERVERS    (1u<<23)
+#define OPT_RELOAD         (1u<<24)
+#define OPT_TFTP           (1u<<25)
+#define OPT_TFTP_SECURE    (1u<<26)
+#define OPT_TFTP_NOBLOCK   (1u<<27)
+#define OPT_LOG_OPTS       (1u<<28)
+#define OPT_TFTP_APREF     (1u<<29)
+#define OPT_NO_OVERRIDE    (1u<<30)
+#define OPT_NO_REBIND      (1u<<31)
+
+/* 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. */
+#define MS_TFTP LOG_USER
+#define MS_DHCP LOG_DAEMON 
+
+struct all_addr {
+  union {
+    struct in_addr addr4;
+#ifdef HAVE_IPV6
+    struct in6_addr addr6;
+#endif
+  } addr;
+};
+
+struct bogus_addr {
+  struct in_addr addr;
+  struct bogus_addr *next;
+};
+
+/* dns doctor param */
+struct doctor {
+  struct in_addr in, end, out, mask;
+  struct doctor *next;
+};
+
+struct mx_srv_record {
+  char *name, *target;
+  int issrv, srvport, priority, weight;
+  unsigned int offset;
+  struct mx_srv_record *next;
+};
+
+struct naptr {
+  char *name, *replace, *regexp, *services, *flags;
+  unsigned int order, pref;
+  struct naptr *next;
+};
+
+struct txt_record {
+  char *name, *txt;
+  unsigned short class, len;
+  struct txt_record *next;
+};
+
+struct ptr_record {
+  char *name, *ptr;
+  struct ptr_record *next;
+};
+
+struct cname {
+  char *alias, *target;
+  struct cname *next;
+};
+
+struct interface_name {
+  char *name; /* domain name */
+  char *intr; /* interface name */
+  struct interface_name *next;
+};
+
+union bigname {
+  char name[MAXDNAME];
+  union bigname *next; /* freelist */
+};
+
+struct crec { 
+  struct crec *next, *prev, *hash_next;
+  time_t ttd; /* time to die */
+  int uid; 
+  union {
+    struct all_addr addr;
+    struct {
+      struct crec *cache;
+      int uid;
+    } cname;
+  } addr;
+  unsigned short flags;
+  union {
+    char sname[SMALLDNAME];
+    union bigname *bname;
+    char *namep;
+  } name;
+};
+
+#define F_IMMORTAL  1
+#define F_CONFIG    2
+#define F_REVERSE   4
+#define F_FORWARD   8
+#define F_DHCP      16 
+#define F_NEG       32       
+#define F_HOSTS     64
+#define F_IPV4      128
+#define F_IPV6      256
+#define F_BIGNAME   512
+#define F_UPSTREAM  1024
+#define F_SERVER    2048
+#define F_NXDOMAIN  4096
+#define F_QUERY     8192
+#define F_CNAME     16384
+#define F_NOERR     32768
+
+/* struct sockaddr is not large enough to hold any address,
+   and specifically not big enough to hold an IPv6 address.
+   Blech. Roll our own. */
+union mysockaddr {
+  struct sockaddr sa;
+  struct sockaddr_in in;
+#if defined(HAVE_IPV6)
+  struct sockaddr_in6 in6;
+#endif
+};
+
+#define SERV_FROM_RESOLV       1  /* 1 for servers from resolv, 0 for command line. */
+#define SERV_NO_ADDR           2  /* no server, this domain is local only */
+#define SERV_LITERAL_ADDRESS   4  /* addr is the answer, not the server */ 
+#define SERV_HAS_DOMAIN        8  /* server for one domain only */
+#define SERV_HAS_SOURCE       16  /* source address defined */
+#define SERV_FOR_NODOTS       32  /* server for names with no domain part only */
+#define SERV_WARNED_RECURSIVE 64  /* avoid warning spam */
+#define SERV_FROM_DBUS       128  /* 1 if source is DBus */
+#define SERV_MARK            256  /* for mark-and-delete */
+#define SERV_TYPE    (SERV_HAS_DOMAIN | SERV_FOR_NODOTS)
+#define SERV_COUNTED         512  /* workspace for log code */
+
+struct serverfd {
+  int fd;
+  union mysockaddr source_addr;
+  char interface[IF_NAMESIZE+1];
+  struct serverfd *next;
+};
+
+struct randfd {
+  int fd;
+  unsigned short refcount, family;
+};
+  
+struct server {
+  union mysockaddr addr, source_addr;
+  char interface[IF_NAMESIZE+1];
+  struct serverfd *sfd; 
+  char *domain; /* set if this server only handles a domain. */ 
+  int flags, tcpfd;
+  unsigned int queries, failed_queries;
+  struct server *next; 
+};
+
+struct irec {
+  union mysockaddr addr;
+  struct in_addr netmask; /* only valid for IPv4 */
+  int dhcp_ok, mtu;
+  struct irec *next;
+};
+
+struct listener {
+  int fd, tcpfd, tftpfd, family;
+  struct irec *iface; /* only valid for non-wildcard */
+  struct listener *next;
+};
+
+/* interface and address parms from command line. */
+struct iname {
+  char *name;
+  union mysockaddr addr;
+  int isloop, used;
+  struct iname *next;
+};
+
+/* resolv-file parms from command-line */
+struct resolvc {
+  struct resolvc *next;
+  int is_default, logged;
+  time_t mtime;
+  char *name;
+};
+
+/* adn-hosts parms from command-line */
+#define AH_DIR      1
+#define AH_INACTIVE 2
+struct hostsfile {
+  struct hostsfile *next;
+  int flags;
+  char *fname;
+  int index; /* matches to cache entries for logging */
+};
+
+struct frec {
+  union mysockaddr source;
+  struct all_addr dest;
+  struct server *sentto; /* NULL means free */
+  struct randfd *rfd4;
+#ifdef HAVE_IPV6
+  struct randfd *rfd6;
+#endif
+  unsigned int iface;
+  unsigned short orig_id, new_id;
+  int fd, forwardall;
+  unsigned int crc;
+  time_t time;
+  struct frec *next;
+};
+
+/* actions in the daemon->helper RPC */
+#define ACTION_DEL           1
+#define ACTION_OLD_HOSTNAME  2
+#define ACTION_OLD           3
+#define ACTION_ADD           4
+
+#define DHCP_CHADDR_MAX 16
+
+struct dhcp_lease {
+  int clid_len;          /* length of client identifier */
+  unsigned char *clid;   /* clientid */
+  char *hostname, *fqdn; /* name from client-hostname option or config */
+  char *old_hostname;    /* hostname before it moved to another lease */
+  char auth_name;        /* hostname came from config, not from client */
+  char new;              /* newly created */
+  char changed;          /* modified */
+  char aux_changed;      /* CLID or expiry changed */
+  time_t expires;        /* lease expiry */
+#ifdef HAVE_BROKEN_RTC
+  unsigned int length;
+#endif
+  int hwaddr_len, hwaddr_type;
+  unsigned char hwaddr[DHCP_CHADDR_MAX]; 
+  struct in_addr addr, override, giaddr;
+  unsigned char *vendorclass, *userclass, *supplied_hostname;
+  unsigned int vendorclass_len, userclass_len, supplied_hostname_len;
+  int last_interface;
+  struct dhcp_lease *next;
+};
+
+struct dhcp_netid {
+  char *net;
+  struct dhcp_netid *next;
+};
+
+struct dhcp_netid_list {
+  struct dhcp_netid *list;
+  struct dhcp_netid_list *next;
+};
+
+struct hwaddr_config {
+  int hwaddr_len, hwaddr_type;
+  unsigned char hwaddr[DHCP_CHADDR_MAX];
+  unsigned int wildcard_mask;
+  struct hwaddr_config *next;
+};
+
+struct dhcp_config {
+  unsigned int flags;
+  int clid_len;          /* length of client identifier */
+  unsigned char *clid;   /* clientid */
+  char *hostname, *domain;
+  struct dhcp_netid netid;
+  struct in_addr addr;
+  time_t decline_time;
+  unsigned int lease_time;
+  struct hwaddr_config *hwaddr;
+  struct dhcp_config *next;
+};
+
+#define CONFIG_DISABLE           1
+#define CONFIG_CLID              2
+#define CONFIG_TIME              8
+#define CONFIG_NAME             16
+#define CONFIG_ADDR             32
+#define CONFIG_NETID            64
+#define CONFIG_NOCLID          128
+#define CONFIG_FROM_ETHERS     256    /* entry created by /etc/ethers */
+#define CONFIG_ADDR_HOSTS      512    /* address added by from /etc/hosts */
+#define CONFIG_DECLINED       1024    /* address declined by client */
+#define CONFIG_BANK           2048    /* from dhcp hosts file */
+
+struct dhcp_opt {
+  int opt, len, flags;
+  union {
+    int encap;
+    unsigned int wildcard_mask;
+    unsigned char *vendor_class;
+  } u;
+  unsigned char *val;
+  struct dhcp_netid *netid;
+  struct dhcp_opt *next;
+};
+
+#define DHOPT_ADDR               1
+#define DHOPT_STRING             2
+#define DHOPT_ENCAPSULATE        4
+#define DHOPT_ENCAP_MATCH        8
+#define DHOPT_FORCE             16
+#define DHOPT_BANK              32
+#define DHOPT_ENCAP_DONE        64
+#define DHOPT_MATCH            128
+#define DHOPT_VENDOR           256
+#define DHOPT_HEX              512
+#define DHOPT_VENDOR_MATCH    1024
+
+struct dhcp_boot {
+  char *file, *sname;
+  struct in_addr next_server;
+  struct dhcp_netid *netid;
+  struct dhcp_boot *next;
+};
+
+struct pxe_service {
+  unsigned short CSA, type; 
+  char *menu, *basename;
+  struct in_addr server;
+  struct dhcp_netid *netid;
+  struct pxe_service *next;
+};
+
+#define MATCH_VENDOR     1
+#define MATCH_USER       2
+#define MATCH_CIRCUIT    3
+#define MATCH_REMOTE     4
+#define MATCH_SUBSCRIBER 5
+
+/* vendorclass, userclass, remote-id or cicuit-id */
+struct dhcp_vendor {
+  int len, match_type, option;
+  char *data;
+  struct dhcp_netid netid;
+  struct dhcp_vendor *next;
+};
+
+struct dhcp_mac {
+  unsigned int mask;
+  int hwaddr_len, hwaddr_type;
+  unsigned char hwaddr[DHCP_CHADDR_MAX];
+  struct dhcp_netid netid;
+  struct dhcp_mac *next;
+};
+
+struct dhcp_bridge {
+  char iface[IF_NAMESIZE];
+  struct dhcp_bridge *alias, *next;
+};
+
+struct cond_domain {
+  char *domain;
+  struct in_addr start, end;
+  struct cond_domain *next;
+};
+
+struct dhcp_context {
+  unsigned int lease_time, addr_epoch;
+  struct in_addr netmask, broadcast;
+  struct in_addr local, router;
+  struct in_addr start, end; /* range of available addresses */
+  int flags;
+  struct dhcp_netid netid, *filter;
+  struct dhcp_context *next, *current;
+};
+
+#define CONTEXT_STATIC    1
+#define CONTEXT_NETMASK   2
+#define CONTEXT_BRDCAST   4
+#define CONTEXT_PROXY     8
+
+
+typedef unsigned char u8;
+typedef unsigned short u16;
+typedef unsigned int u32;
+
+
+struct dhcp_packet {
+  u8 op, htype, hlen, hops;
+  u32 xid;
+  u16 secs, flags;
+  struct in_addr ciaddr, yiaddr, siaddr, giaddr;
+  u8 chaddr[DHCP_CHADDR_MAX], sname[64], file[128];
+  u8 options[312];
+};
+
+struct ping_result {
+  struct in_addr addr;
+  time_t time;
+  struct ping_result *next;
+};
+
+struct tftp_file {
+  int refcount, fd;
+  off_t size;
+  dev_t dev;
+  ino_t inode;
+  char filename[];
+};
+
+struct tftp_transfer {
+  int sockfd;
+  time_t timeout;
+  int backoff;
+  unsigned int block, blocksize, expansion;
+  off_t offset;
+  struct sockaddr_in peer;
+  char opt_blocksize, opt_transize, netascii, carrylf;
+  struct tftp_file *file;
+  struct tftp_transfer *next;
+};
+
+extern struct daemon {
+  /* datastuctures representing the command-line and 
+     config file arguments. All set (including defaults)
+     in option.c */
+
+  unsigned int options;
+  struct resolvc default_resolv, *resolv_files;
+  time_t last_resolv;
+  struct mx_srv_record *mxnames;
+  struct naptr *naptr;
+  struct txt_record *txt;
+  struct ptr_record *ptr;
+  struct cname *cnames;
+  struct interface_name *int_names;
+  char *mxtarget;
+  char *lease_file; 
+  char *username, *groupname, *scriptuser;
+  int group_set, osport;
+  char *domain_suffix;
+  struct cond_domain *cond_domain;
+  char *runfile; 
+  char *lease_change_command;
+  struct iname *if_names, *if_addrs, *if_except, *dhcp_except;
+  struct bogus_addr *bogus_addr;
+  struct server *servers;
+  int log_fac; /* log facility */
+  char *log_file; /* optional log file */
+  int max_logs;  /* queue limit */
+  int cachesize, ftabsize;
+  int port, query_port, min_port;
+  unsigned long local_ttl, neg_ttl;
+  struct hostsfile *addn_hosts;
+  struct dhcp_context *dhcp;
+  struct dhcp_config *dhcp_conf;
+  struct dhcp_opt *dhcp_opts, *dhcp_match;
+  struct dhcp_vendor *dhcp_vendors;
+  struct dhcp_mac *dhcp_macs;
+  struct dhcp_boot *boot_config;
+  struct pxe_service *pxe_services;
+  int enable_pxe;
+  struct dhcp_netid_list *dhcp_ignore, *dhcp_ignore_names, *force_broadcast, *bootp_dynamic;
+  char *dhcp_hosts_file, *dhcp_opts_file;
+  int dhcp_max, tftp_max;
+  int dhcp_server_port, dhcp_client_port;
+  int start_tftp_port, end_tftp_port; 
+  unsigned int min_leasetime;
+  struct doctor *doctors;
+  unsigned short edns_pktsz;
+  char *tftp_prefix; 
+
+  /* globally used stuff for DNS */
+  char *packet; /* packet buffer */
+  int packet_buff_sz; /* size of above */
+  char *namebuff; /* MAXDNAME size buffer */
+  unsigned int local_answer, queries_forwarded;
+  struct frec *frec_list;
+  struct serverfd *sfds;
+  struct irec *interfaces;
+  struct listener *listeners;
+  struct server *last_server;
+  time_t forwardtime;
+  int forwardcount;
+  struct server *srv_save; /* Used for resend on DoD */
+  size_t packet_len;       /*      "        "        */
+  struct randfd *rfd_save; /*      "        "        */
+  pid_t tcp_pids[MAX_PROCS];
+  struct randfd randomsocks[RANDOM_SOCKS];
+
+  /* DHCP state */
+  int dhcpfd, helperfd; 
+#if defined(HAVE_LINUX_NETWORK)
+  int netlinkfd;
+#elif defined(HAVE_BSD_NETWORK)
+  int dhcp_raw_fd, dhcp_icmp_fd;
+#endif
+  struct iovec dhcp_packet;
+  char *dhcp_buff, *dhcp_buff2;
+  struct ping_result *ping_results;
+  FILE *lease_stream;
+  struct dhcp_bridge *bridges;
+
+  /* DBus stuff */
+  /* void * here to avoid depending on dbus headers outside dbus.c */
+  void *dbus;
+#ifdef HAVE_DBUS
+  struct watch *watches;
+#endif
+
+  /* TFTP stuff */
+  struct tftp_transfer *tftp_trans;
+
+} *daemon;
+
+/* cache.c */
+void cache_init(void);
+void log_query(unsigned short flags, char *name, struct all_addr *addr, char *arg); 
+char *record_source(int index);
+void querystr(char *str, unsigned short type);
+struct crec *cache_find_by_addr(struct crec *crecp,
+				struct all_addr *addr, time_t now, 
+				unsigned short prot);
+struct crec *cache_find_by_name(struct crec *crecp, 
+				char *name, time_t now, unsigned short  prot);
+void cache_end_insert(void);
+void cache_start_insert(void);
+struct crec *cache_insert(char *name, struct all_addr *addr,
+			  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);
+void cache_unhash_dhcp(void);
+void dump_cache(time_t now);
+char *cache_get_name(struct crec *crecp);
+char *get_domain(struct in_addr addr);
+
+/* rfc1035.c */
+unsigned short extract_request(HEADER *header, size_t qlen, 
+			       char *name, unsigned short *typep);
+size_t setup_reply(HEADER *header, size_t  qlen,
+		   struct all_addr *addrp, unsigned short flags,
+		   unsigned long local_ttl);
+int extract_addresses(HEADER *header, size_t qlen, char *namebuff, time_t now);
+size_t answer_request(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(HEADER *header, size_t qlen, char *name, 
+			     struct bogus_addr *addr, time_t now);
+unsigned char *find_pseudoheader(HEADER *header, size_t plen,
+				 size_t *len, unsigned char **p, int *is_sign);
+int check_for_local_domain(char *name, time_t now);
+unsigned int questions_crc(HEADER *header, size_t plen, char *buff);
+size_t resize_packet(HEADER *header, size_t plen, 
+		  unsigned char *pheader, size_t hlen);
+
+/* util.c */
+void rand_init(void);
+unsigned short rand16(void);
+int legal_hostname(char *c);
+char *canonicalise(char *s, int *nomem);
+unsigned char *do_rfc1035_name(unsigned char *p, char *sval);
+void *safe_malloc(size_t size);
+void safe_pipe(int *fd, int read_noblock);
+void *whine_malloc(size_t size);
+int sa_len(union mysockaddr *addr);
+int sockaddr_isequal(union mysockaddr *s1, union mysockaddr *s2);
+int hostname_isequal(char *a, char *b);
+time_t dnsmasq_time(void);
+int is_same_net(struct in_addr a, struct in_addr b, struct in_addr mask);
+int retry_send(void);
+void prettyprint_time(char *buf, unsigned int t);
+int prettyprint_addr(union mysockaddr *addr, char *buf);
+int parse_hex(char *in, unsigned char *out, int maxlen, 
+	      unsigned int *wildcard_mask, int *mac_type);
+int memcmp_masked(unsigned char *a, unsigned char *b, int len, 
+		  unsigned int mask);
+int expand_buf(struct iovec *iov, size_t size);
+char *print_mac(char *buff, unsigned char *mac, int len);
+void bump_maxfd(int fd, int *max);
+int read_write(int fd, unsigned char *packet, int size, int rw);
+
+/* log.c */
+void die(char *message, char *arg1, int exit_code);
+int log_start(struct passwd *ent_pw, int errfd);
+int log_reopen(char *log_file);
+void my_syslog(int priority, const char *format, ...);
+void set_log_writer(fd_set *set, int *maxfdp);
+void check_log_writer(fd_set *set);
+void flush_log(void);
+
+/* option.c */
+void read_opts (int argc, char **argv, char *compile_opts);
+char *option_string(unsigned char opt, int *is_ip, int *is_name);
+void reread_dhcp(void);
+
+/* forward.c */
+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);
+void server_gone(struct server *server);
+struct frec *get_new_frec(time_t now, int *wait);
+
+/* network.c */
+int indextoname(int fd, int index, char *name);
+int local_bind(int fd, union mysockaddr *addr, char *intname, int is_tcp);
+int random_sock(int family);
+void pre_allocate_sfds(void);
+int reload_servers(char *fname);
+void check_servers(void);
+int enumerate_interfaces();
+struct listener *create_wildcard_listeners(void);
+struct listener *create_bound_listeners(void);
+int iface_check(int family, struct all_addr *addr, char *name, int *indexp);
+int fix_fd(int fd);
+struct in_addr get_ifaddr(char *intr);
+
+/* dhcp.c */
+#ifdef HAVE_DHCP
+void dhcp_init(void);
+void dhcp_packet(time_t now);
+struct dhcp_context *address_available(struct dhcp_context *context, 
+				       struct in_addr addr,
+				       struct dhcp_netid *netids);
+struct dhcp_context *narrow_context(struct dhcp_context *context, 
+				    struct in_addr taddr,
+				    struct dhcp_netid *netids);
+int match_netid(struct dhcp_netid *check, struct dhcp_netid *pool, int negonly);int address_allocate(struct dhcp_context *context,
+		     struct in_addr *addrp, unsigned char *hwaddr, int hw_len,
+		     struct dhcp_netid *netids, time_t now);
+int config_has_mac(struct dhcp_config *config, unsigned char *hwaddr, int len, int type);
+struct dhcp_config *find_config(struct dhcp_config *configs,
+				struct dhcp_context *context,
+				unsigned char *clid, int clid_len,
+				unsigned char *hwaddr, int hw_len, 
+				int hw_type, char *hostname);
+void dhcp_update_configs(struct dhcp_config *configs);
+void dhcp_read_ethers(void);
+void check_dhcp_hosts(int fatal);
+struct dhcp_config *config_find_by_address(struct dhcp_config *configs, struct in_addr addr);
+char *strip_hostname(char *hostname);
+char *host_from_dns(struct in_addr addr);
+char *get_domain(struct in_addr addr);
+#endif
+
+/* lease.c */
+#ifdef HAVE_DHCP
+void lease_update_file(time_t now);
+void lease_update_dns();
+void lease_init(time_t now);
+struct dhcp_lease *lease_allocate(struct in_addr addr);
+void lease_set_hwaddr(struct dhcp_lease *lease, unsigned char *hwaddr,
+		      unsigned char *clid, int hw_len, int hw_type, int clid_len);
+void lease_set_hostname(struct dhcp_lease *lease, char *name, int auth);
+void lease_set_expires(struct dhcp_lease *lease, unsigned int len, time_t now);
+void lease_set_interface(struct dhcp_lease *lease, int interface);
+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);
+void lease_prune(struct dhcp_lease *target, time_t now);
+void lease_update_from_configs(void);
+int do_script_run(time_t now);
+void rerun_scripts(void);
+#endif
+
+/* 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);
+unsigned char *extended_hwaddr(int hwtype, int hwlen, unsigned char *hwaddr, 
+			       int clid_len, unsigned char *clid, int *len_out);
+#endif
+
+/* dnsmasq.c */
+#ifdef HAVE_DHCP
+int make_icmp_sock(void);
+int icmp_ping(struct in_addr addr);
+#endif
+void send_event(int fd, int event, int data);
+void clear_cache_and_reload(time_t now);
+
+/* netlink.c */
+#ifdef HAVE_LINUX_NETWORK
+void netlink_init(void);
+void netlink_multicast(void);
+#endif
+
+/* bpf.c */
+#ifdef HAVE_BSD_NETWORK
+void init_bpf(void);
+void send_via_bpf(struct dhcp_packet *mess, size_t len,
+		  struct in_addr iface_addr, struct ifreq *ifr);
+#endif
+
+/* bpf.c or netlink.c */
+int iface_enumerate(void *parm, int (*ipv4_callback)(), int (*ipv6_callback)());
+
+/* dbus.c */
+#ifdef HAVE_DBUS
+char *dbus_init(void);
+void check_dbus_listeners(fd_set *rset, fd_set *wset, fd_set *eset);
+void set_dbus_listeners(int *maxfdp, fd_set *rset, fd_set *wset, fd_set *eset);
+void emit_dbus_signal(int action, struct dhcp_lease *lease, char *hostname);
+#endif
+
+/* helper.c */
+#if defined(HAVE_DHCP) && !defined(NO_FORK)
+int create_helper(int event_fd, int err_fd, uid_t uid, gid_t gid, long max_fd);
+void helper_write(void);
+void queue_script(int action, struct dhcp_lease *lease, 
+		  char *hostname, time_t now);
+int helper_buf_empty(void);
+#endif
+
+/* tftp.c */
+#ifdef HAVE_TFTP
+void tftp_request(struct listener *listen, time_t now);
+void check_tftp_listeners(fd_set *rset, time_t now);
+#endif
diff --git a/src/forward.c b/src/forward.c
new file mode 100755
index 0000000..40cda1c
--- /dev/null
+++ b/src/forward.c
@@ -0,0 +1,1094 @@
+/* dnsmasq is Copyright (c) 2000-2009 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"
+
+static struct frec *lookup_frec(unsigned short id, unsigned int crc);
+static struct frec *lookup_frec_by_sender(unsigned short id,
+					  union mysockaddr *addr,
+					  unsigned int crc);
+static unsigned short get_id(int force, unsigned short force_id, unsigned int crc);
+static void free_frec(struct frec *f);
+static struct randfd *allocate_rfd(int family);
+
+/* Send a UDP packet with its source address set as "source" 
+   unless nowild is true, when we just send it with the kernel default */
+static void send_from(int fd, int nowild, char *packet, size_t len, 
+		      union mysockaddr *to, struct all_addr *source,
+		      unsigned int iface)
+{
+  struct msghdr msg;
+  struct iovec iov[1]; 
+  union {
+    struct cmsghdr align; /* this ensures alignment */
+#if defined(HAVE_LINUX_NETWORK)
+    char control[CMSG_SPACE(sizeof(struct in_pktinfo))];
+#elif defined(IP_SENDSRCADDR)
+    char control[CMSG_SPACE(sizeof(struct in_addr))];
+#endif
+#ifdef HAVE_IPV6
+    char control6[CMSG_SPACE(sizeof(struct in6_pktinfo))];
+#endif
+  } control_u;
+  
+  iov[0].iov_base = packet;
+  iov[0].iov_len = len;
+
+  msg.msg_control = NULL;
+  msg.msg_controllen = 0;
+  msg.msg_flags = 0;
+  msg.msg_name = to;
+  msg.msg_namelen = sa_len(to);
+  msg.msg_iov = iov;
+  msg.msg_iovlen = 1;
+  
+  if (!nowild)
+    {
+      struct cmsghdr *cmptr;
+      msg.msg_control = &control_u;
+      msg.msg_controllen = sizeof(control_u);
+      cmptr = CMSG_FIRSTHDR(&msg);
+
+      if (to->sa.sa_family == AF_INET)
+	{
+#if defined(HAVE_LINUX_NETWORK)
+	  struct in_pktinfo *pkt = (struct in_pktinfo *)CMSG_DATA(cmptr);
+	  pkt->ipi_ifindex = 0;
+	  pkt->ipi_spec_dst = source->addr.addr4;
+	  msg.msg_controllen = cmptr->cmsg_len = CMSG_LEN(sizeof(struct in_pktinfo));
+	  cmptr->cmsg_level = SOL_IP;
+	  cmptr->cmsg_type = IP_PKTINFO;
+#elif defined(IP_SENDSRCADDR)
+	  struct in_addr *a = (struct in_addr *)CMSG_DATA(cmptr);
+	  *a = source->addr.addr4;
+	  msg.msg_controllen = cmptr->cmsg_len = CMSG_LEN(sizeof(struct in_addr));
+	  cmptr->cmsg_level = IPPROTO_IP;
+	  cmptr->cmsg_type = IP_SENDSRCADDR;
+#endif
+	}
+      else
+#ifdef HAVE_IPV6
+	{
+	  struct in6_pktinfo *pkt = (struct in6_pktinfo *)CMSG_DATA(cmptr);
+	  pkt->ipi6_ifindex = iface; /* Need iface for IPv6 to handle link-local addrs */
+	  pkt->ipi6_addr = source->addr.addr6;
+	  msg.msg_controllen = cmptr->cmsg_len = CMSG_LEN(sizeof(struct in6_pktinfo));
+	  cmptr->cmsg_type = IPV6_PKTINFO;
+	  cmptr->cmsg_level = IPV6_LEVEL;
+	}
+#else
+      iface = 0; /* eliminate warning */
+#endif
+    }
+  
+ retry:
+  if (sendmsg(fd, &msg, 0) == -1)
+    {
+      /* certain Linux kernels seem to object to setting the source address in the IPv6 stack
+	 by returning EINVAL from sendmsg. In that case, try again without setting the
+	 source address, since it will nearly alway be correct anyway.  IPv6 stinks. */
+      if (errno == EINVAL && msg.msg_controllen)
+	{
+	  msg.msg_controllen = 0;
+	  goto retry;
+	}
+      if (retry_send())
+	goto retry;
+    }
+}
+          
+static unsigned short search_servers(time_t now, struct all_addr **addrpp, 
+				     unsigned short qtype, char *qdomain, int *type, char **domain)
+			      
+{
+  /* If the query ends in the domain in one of our servers, set
+     domain to point to that name. We find the largest match to allow both
+     domain.org and sub.domain.org to exist. */
+  
+  unsigned int namelen = strlen(qdomain);
+  unsigned int matchlen = 0;
+  struct server *serv;
+  unsigned short flags = 0;
+  
+  for (serv = daemon->servers; serv; serv=serv->next)
+    /* domain matches take priority over NODOTS matches */
+    if ((serv->flags & SERV_FOR_NODOTS) && *type != SERV_HAS_DOMAIN && !strchr(qdomain, '.') && namelen != 0)
+      {
+	unsigned short sflag = serv->addr.sa.sa_family == AF_INET ? F_IPV4 : F_IPV6; 
+	*type = SERV_FOR_NODOTS;
+	if (serv->flags & SERV_NO_ADDR)
+	  flags = F_NXDOMAIN;
+	else if (serv->flags & SERV_LITERAL_ADDRESS) 
+	  { 
+	    if (sflag & qtype)
+	      {
+		flags = sflag;
+		if (serv->addr.sa.sa_family == AF_INET) 
+		  *addrpp = (struct all_addr *)&serv->addr.in.sin_addr;
+#ifdef HAVE_IPV6
+		else
+		  *addrpp = (struct all_addr *)&serv->addr.in6.sin6_addr;
+#endif 
+	      }
+	    else if (!flags || (flags & F_NXDOMAIN))
+	      flags = F_NOERR;
+	  } 
+      }
+    else if (serv->flags & SERV_HAS_DOMAIN)
+      {
+	unsigned int domainlen = strlen(serv->domain);
+	char *matchstart = qdomain + namelen - domainlen;
+	if (namelen >= domainlen &&
+	    hostname_isequal(matchstart, serv->domain) &&
+	    domainlen >= matchlen &&
+	    (domainlen == 0 || namelen == domainlen || *(serv->domain) == '.' || *(matchstart-1) == '.' ))
+	  {
+	    unsigned short sflag = serv->addr.sa.sa_family == AF_INET ? F_IPV4 : F_IPV6;
+	    *type = SERV_HAS_DOMAIN;
+	    *domain = serv->domain;
+	    matchlen = domainlen;
+	    if (serv->flags & SERV_NO_ADDR)
+	      flags = F_NXDOMAIN;
+	    else if (serv->flags & SERV_LITERAL_ADDRESS)
+	      {
+		if (sflag & qtype)
+		  {
+		    flags = sflag;
+		    if (serv->addr.sa.sa_family == AF_INET) 
+		      *addrpp = (struct all_addr *)&serv->addr.in.sin_addr;
+#ifdef HAVE_IPV6
+		    else
+		      *addrpp = (struct all_addr *)&serv->addr.in6.sin6_addr;
+#endif
+		  }
+		else if (!flags || (flags & F_NXDOMAIN))
+		  flags = F_NOERR;
+	      }
+	  } 
+      }
+
+  if (flags == 0 && !(qtype & F_BIGNAME) && 
+      (daemon->options & OPT_NODOTS_LOCAL) && !strchr(qdomain, '.') && namelen != 0)
+    /* don't forward simple names, make exception for NS queries and empty name. */
+    flags = F_NXDOMAIN;
+    
+  if (flags == F_NXDOMAIN && check_for_local_domain(qdomain, now))
+    flags = F_NOERR;
+
+  if (flags)
+    {
+      int logflags = 0;
+      
+      if (flags == F_NXDOMAIN || flags == F_NOERR)
+	logflags = F_NEG | qtype;
+  
+      log_query(logflags | flags | F_CONFIG | F_FORWARD, qdomain, *addrpp, NULL);
+    }
+
+  return  flags;
+}
+
+static int forward_query(int udpfd, union mysockaddr *udpaddr,
+			 struct all_addr *dst_addr, unsigned int dst_iface,
+			 HEADER *header, size_t plen, time_t now, struct frec *forward)
+{
+  char *domain = NULL;
+  int type = 0;
+  struct all_addr *addrp = NULL;
+  unsigned int crc = questions_crc(header, plen, daemon->namebuff);
+  unsigned short flags = 0;
+  unsigned short gotname = extract_request(header, plen, daemon->namebuff, NULL);
+  struct server *start = NULL;
+    
+  /* may be no servers available. */
+  if (!daemon->servers)
+    forward = NULL;
+  else if (forward || (forward = lookup_frec_by_sender(ntohs(header->id), udpaddr, crc)))
+    {
+      /* retry on existing query, send to all available servers  */
+      domain = forward->sentto->domain;
+      forward->sentto->failed_queries++;
+      if (!(daemon->options & OPT_ORDER))
+	{
+	  forward->forwardall = 1;
+	  daemon->last_server = NULL;
+	}
+      type = forward->sentto->flags & SERV_TYPE;
+      if (!(start = forward->sentto->next))
+	start = daemon->servers; /* at end of list, recycle */
+      header->id = htons(forward->new_id);
+    }
+  else 
+    {
+      if (gotname)
+	flags = search_servers(now, &addrp, gotname, daemon->namebuff, &type, &domain);
+      
+      if (!flags && !(forward = get_new_frec(now, NULL)))
+	/* table full - server failure. */
+	flags = F_NEG;
+      
+      if (forward)
+	{
+	  /* force unchanging id for signed packets */
+	  int is_sign;
+	  find_pseudoheader(header, plen, NULL, NULL, &is_sign);
+	  
+	  forward->source = *udpaddr;
+	  forward->dest = *dst_addr;
+	  forward->iface = dst_iface;
+	  forward->orig_id = ntohs(header->id);
+	  forward->new_id = get_id(is_sign, forward->orig_id, crc);
+	  forward->fd = udpfd;
+	  forward->crc = crc;
+	  forward->forwardall = 0;
+	  header->id = htons(forward->new_id);
+
+	  /* In strict_order mode, or when using domain specific servers
+	     always try servers in the order specified in resolv.conf,
+	     otherwise, use the one last known to work. */
+	  
+	  if (type != 0  || (daemon->options & OPT_ORDER))
+	    start = daemon->servers;
+	  else if (!(start = daemon->last_server) ||
+		   daemon->forwardcount++ > FORWARD_TEST ||
+		   difftime(now, daemon->forwardtime) > FORWARD_TIME)
+	    {
+	      start = daemon->servers;
+	      forward->forwardall = 1;
+	      daemon->forwardcount = 0;
+	      daemon->forwardtime = now;
+	    }
+	}
+    }
+
+  /* check for send errors here (no route to host) 
+     if we fail to send to all nameservers, send back an error
+     packet straight away (helps modem users when offline)  */
+  
+  if (!flags && forward)
+    {
+      struct server *firstsentto = start;
+      int forwarded = 0;
+
+      while (1)
+	{ 
+	  /* only send to servers dealing with our domain.
+	     domain may be NULL, in which case server->domain 
+	     must be NULL also. */
+	  
+	  if (type == (start->flags & SERV_TYPE) &&
+	      (type != SERV_HAS_DOMAIN || hostname_isequal(domain, start->domain)) &&
+	      !(start->flags & SERV_LITERAL_ADDRESS))
+	    {
+	      int fd;
+
+	      /* find server socket to use, may need to get random one. */
+	      if (start->sfd)
+		fd = start->sfd->fd;
+	      else 
+		{
+#ifdef HAVE_IPV6
+		  if (start->addr.sa.sa_family == AF_INET6)
+		    {
+		      if (!forward->rfd6 &&
+			  !(forward->rfd6 = allocate_rfd(AF_INET6)))
+			break;
+		      daemon->rfd_save = forward->rfd6;
+		      fd = forward->rfd6->fd;
+		    }
+		  else
+#endif
+		    {
+		      if (!forward->rfd4 &&
+			  !(forward->rfd4 = allocate_rfd(AF_INET)))
+			break;
+		      daemon->rfd_save = forward->rfd4;
+		      fd = forward->rfd4->fd;
+		    }
+		}
+	      
+	      if (sendto(fd, (char *)header, plen, 0,
+			 &start->addr.sa,
+			 sa_len(&start->addr)) == -1)
+		{
+		  if (retry_send())
+		    continue;
+		}
+	      else
+		{
+		  /* Keep info in case we want to re-send this packet */
+		  daemon->srv_save = start;
+		  daemon->packet_len = plen;
+		  
+		  if (!gotname)
+		    strcpy(daemon->namebuff, "query");
+		  if (start->addr.sa.sa_family == AF_INET)
+		    log_query(F_SERVER | F_IPV4 | F_FORWARD, daemon->namebuff, 
+			      (struct all_addr *)&start->addr.in.sin_addr, NULL); 
+#ifdef HAVE_IPV6
+		  else
+		    log_query(F_SERVER | F_IPV6 | F_FORWARD, daemon->namebuff, 
+			      (struct all_addr *)&start->addr.in6.sin6_addr, NULL);
+#endif 
+		  start->queries++;
+		  forwarded = 1;
+		  forward->sentto = start;
+		  if (!forward->forwardall) 
+		    break;
+		  forward->forwardall++;
+		}
+	    } 
+	  
+	  if (!(start = start->next))
+ 	    start = daemon->servers;
+	  
+	  if (start == firstsentto)
+	    break;
+	}
+      
+      if (forwarded)
+	return 1;
+      
+      /* could not send on, prepare to return */ 
+      header->id = htons(forward->orig_id);
+      free_frec(forward); /* cancel */
+    }	  
+  
+  /* could not send on, return empty answer or address if known for whole domain */
+  if (udpfd != -1)
+    {
+      plen = setup_reply(header, plen, addrp, flags, daemon->local_ttl);
+      send_from(udpfd, daemon->options & OPT_NOWILD, (char *)header, plen, udpaddr, dst_addr, dst_iface);
+    }
+
+  return 0;
+}
+
+static size_t process_reply(HEADER *header, time_t now, 
+			    struct server *server, size_t n)
+{
+  unsigned char *pheader, *sizep;
+  int munged = 0, is_sign;
+  size_t plen; 
+
+  /* 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. */
+
+  if ((pheader = find_pseudoheader(header, n, &plen, &sizep, &is_sign)) && !is_sign)
+    {
+      unsigned short udpsz;
+      unsigned char *psave = sizep;
+      
+      GETSHORT(udpsz, sizep);
+      if (udpsz > daemon->edns_pktsz)
+	PUTSHORT(daemon->edns_pktsz, psave);
+    }
+
+  if (header->opcode != QUERY || (header->rcode != NOERROR && header->rcode != NXDOMAIN))
+    return n;
+  
+  /* Complain loudly if the upstream server is non-recursive. */
+  if (!header->ra && header->rcode == NOERROR && ntohs(header->ancount) == 0 &&
+      server && !(server->flags & SERV_WARNED_RECURSIVE))
+    {
+      prettyprint_addr(&server->addr, daemon->namebuff);
+      my_syslog(LOG_WARNING, _("nameserver %s refused to do a recursive query"), daemon->namebuff);
+      if (!(daemon->options & OPT_LOG))
+	server->flags |= SERV_WARNED_RECURSIVE;
+    }  
+    
+  if (daemon->bogus_addr && header->rcode != NXDOMAIN &&
+      check_for_bogus_wildcard(header, n, daemon->namebuff, daemon->bogus_addr, now))
+    {
+      munged = 1;
+      header->rcode = NXDOMAIN;
+      header->aa = 0;
+    }
+  else 
+    {
+      if (header->rcode == NXDOMAIN && 
+	  extract_request(header, n, daemon->namebuff, NULL) &&
+	  check_for_local_domain(daemon->namebuff, now))
+	{
+	  /* if we forwarded a query for a locally known name (because it was for 
+	     an unknown type) and the answer is NXDOMAIN, convert that to NODATA,
+	     since we know that the domain exists, even if upstream doesn't */
+	  munged = 1;
+	  header->aa = 1;
+	  header->rcode = NOERROR;
+	}
+      
+      if (extract_addresses(header, n, daemon->namebuff, now))
+	{
+	  my_syslog(LOG_WARNING, _("possible DNS-rebind attack detected"));
+	  munged = 1;
+	}
+    }
+  
+  /* do this after extract_addresses. Ensure NODATA reply and remove
+     nameserver info. */
+  
+  if (munged)
+    {
+      header->ancount = htons(0);
+      header->nscount = htons(0);
+      header->arcount = htons(0);
+    }
+  
+  /* the bogus-nxdomain stuff, doctor and NXDOMAIN->NODATA munging can all elide
+     sections of the packet. Find the new length here and put back pseudoheader
+     if it was removed. */
+  return resize_packet(header, n, pheader, plen);
+}
+
+/* sets new last_server */
+void reply_query(int fd, int family, time_t now)
+{
+  /* packet from peer server, extract data for cache, and send to
+     original requester */
+  HEADER *header;
+  union mysockaddr serveraddr;
+  struct frec *forward;
+  socklen_t addrlen = sizeof(serveraddr);
+  ssize_t n = recvfrom(fd, daemon->packet, daemon->edns_pktsz, 0, &serveraddr.sa, &addrlen);
+  size_t nn;
+  struct server *server;
+  
+  /* packet buffer overwritten */
+  daemon->srv_save = NULL;
+  
+  /* Determine the address of the server replying  so that we can mark that as good */
+  serveraddr.sa.sa_family = family;
+#ifdef HAVE_IPV6
+  if (serveraddr.sa.sa_family == AF_INET6)
+    serveraddr.in6.sin6_flowinfo = 0;
+#endif
+  
+  /* spoof check: answer must come from known server, */
+  for (server = daemon->servers; server; server = server->next)
+    if (!(server->flags & (SERV_LITERAL_ADDRESS | SERV_NO_ADDR)) &&
+	sockaddr_isequal(&server->addr, &serveraddr))
+      break;
+   
+  header = (HEADER *)daemon->packet;
+  
+  if (!server ||
+      n < (int)sizeof(HEADER) || !header->qr ||
+      !(forward = lookup_frec(ntohs(header->id), questions_crc(header, n, daemon->namebuff))))
+    return;
+   
+  server = forward->sentto;
+  
+  if ((header->rcode == SERVFAIL || header->rcode == REFUSED) &&
+      !(daemon->options & OPT_ORDER) &&
+      forward->forwardall == 0)
+    /* for broken servers, attempt to send to another one. */
+    {
+      unsigned char *pheader;
+      size_t plen;
+      int is_sign;
+      
+      /* recreate query from reply */
+      pheader = find_pseudoheader(header, (size_t)n, &plen, NULL, &is_sign);
+      if (!is_sign)
+	{
+	  header->ancount = htons(0);
+	  header->nscount = htons(0);
+	  header->arcount = htons(0);
+	  if ((nn = resize_packet(header, (size_t)n, pheader, plen)))
+	    {
+	      header->qr = 0;
+	      header->tc = 0;
+	      forward_query(-1, NULL, NULL, 0, header, nn, now, forward);
+	      return;
+	    }
+	}
+    }   
+  
+  if ((forward->sentto->flags & SERV_TYPE) == 0)
+    {
+      if (header->rcode == SERVFAIL || header->rcode == REFUSED)
+	server = NULL;
+      else
+	{
+	  struct server *last_server;
+	  
+	  /* find good server by address if possible, otherwise assume the last one we sent to */ 
+	  for (last_server = daemon->servers; last_server; last_server = last_server->next)
+	    if (!(last_server->flags & (SERV_LITERAL_ADDRESS | SERV_HAS_DOMAIN | SERV_FOR_NODOTS | SERV_NO_ADDR)) &&
+		sockaddr_isequal(&last_server->addr, &serveraddr))
+	      {
+		server = last_server;
+		break;
+	      }
+	} 
+      if (!(daemon->options & OPT_ALL_SERVERS))
+	daemon->last_server = server;
+    }
+  
+  /* If the answer is an error, keep the forward record in place in case
+     we get a good reply from another server. Kill it when we've
+     had replies from all to avoid filling the forwarding table when
+     everything is broken */
+  if (forward->forwardall == 0 || --forward->forwardall == 1 || 
+      (header->rcode != REFUSED && header->rcode != SERVFAIL))
+    {
+      if ((nn = process_reply(header, now, server, (size_t)n)))
+	{
+	  header->id = htons(forward->orig_id);
+	  header->ra = 1; /* recursion if available */
+	  send_from(forward->fd, daemon->options & OPT_NOWILD, daemon->packet, nn, 
+		    &forward->source, &forward->dest, forward->iface);
+	}
+      free_frec(forward); /* cancel */
+    }
+}
+
+
+void receive_query(struct listener *listen, time_t now)
+{
+  HEADER *header = (HEADER *)daemon->packet;
+  union mysockaddr source_addr;
+  unsigned short type;
+  struct all_addr dst_addr;
+  struct in_addr netmask, dst_addr_4;
+  size_t m;
+  ssize_t n;
+  int if_index = 0;
+  struct iovec iov[1];
+  struct msghdr msg;
+  struct cmsghdr *cmptr;
+  union {
+    struct cmsghdr align; /* this ensures alignment */
+#ifdef HAVE_IPV6
+    char control6[CMSG_SPACE(sizeof(struct in6_pktinfo))];
+#endif
+#if defined(HAVE_LINUX_NETWORK)
+    char control[CMSG_SPACE(sizeof(struct in_pktinfo))];
+#elif defined(IP_RECVDSTADDR) && defined(HAVE_SOLARIS_NETWORK)
+    char control[CMSG_SPACE(sizeof(struct in_addr)) +
+		 CMSG_SPACE(sizeof(unsigned int))];
+#elif defined(IP_RECVDSTADDR)
+    char control[CMSG_SPACE(sizeof(struct in_addr)) +
+		 CMSG_SPACE(sizeof(struct sockaddr_dl))];
+#endif
+  } control_u;
+  
+  /* packet buffer overwritten */
+  daemon->srv_save = NULL;
+  
+  if (listen->family == AF_INET && (daemon->options & OPT_NOWILD))
+    {
+      dst_addr_4 = listen->iface->addr.in.sin_addr;
+      netmask = listen->iface->netmask;
+    }
+  else
+    {
+      dst_addr_4.s_addr = 0;
+      netmask.s_addr = 0;
+    }
+
+  iov[0].iov_base = daemon->packet;
+  iov[0].iov_len = daemon->edns_pktsz;
+    
+  msg.msg_control = control_u.control;
+  msg.msg_controllen = sizeof(control_u);
+  msg.msg_flags = 0;
+  msg.msg_name = &source_addr;
+  msg.msg_namelen = sizeof(source_addr);
+  msg.msg_iov = iov;
+  msg.msg_iovlen = 1;
+  
+  if ((n = recvmsg(listen->fd, &msg, 0)) == -1)
+    return;
+  
+  if (n < (int)sizeof(HEADER) || 
+      (msg.msg_flags & MSG_TRUNC) ||
+      header->qr)
+    return;
+  
+  source_addr.sa.sa_family = listen->family;
+#ifdef HAVE_IPV6
+  if (listen->family == AF_INET6)
+    source_addr.in6.sin6_flowinfo = 0;
+#endif
+  
+  if (!(daemon->options & OPT_NOWILD))
+    {
+      struct ifreq ifr;
+
+      if (msg.msg_controllen < sizeof(struct cmsghdr))
+	return;
+
+#if defined(HAVE_LINUX_NETWORK)
+      if (listen->family == AF_INET)
+	for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr))
+	  if (cmptr->cmsg_level == SOL_IP && cmptr->cmsg_type == IP_PKTINFO)
+	    {
+	      dst_addr_4 = dst_addr.addr.addr4 = ((struct in_pktinfo *)CMSG_DATA(cmptr))->ipi_spec_dst;
+	      if_index = ((struct in_pktinfo *)CMSG_DATA(cmptr))->ipi_ifindex;
+	    }
+#elif defined(IP_RECVDSTADDR) && defined(IP_RECVIF)
+      if (listen->family == AF_INET)
+	{
+	  for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr))
+	    if (cmptr->cmsg_level == IPPROTO_IP && cmptr->cmsg_type == IP_RECVDSTADDR)
+	      dst_addr_4 = dst_addr.addr.addr4 = *((struct in_addr *)CMSG_DATA(cmptr));
+	    else if (cmptr->cmsg_level == IPPROTO_IP && cmptr->cmsg_type == IP_RECVIF)
+#ifdef HAVE_SOLARIS_NETWORK
+	      if_index = *((unsigned int *)CMSG_DATA(cmptr));
+#else
+	      if_index = ((struct sockaddr_dl *)CMSG_DATA(cmptr))->sdl_index;
+#endif
+	}
+#endif
+      
+#ifdef HAVE_IPV6
+      if (listen->family == AF_INET6)
+	{
+	  for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr))
+	    if (cmptr->cmsg_level == IPV6_LEVEL && cmptr->cmsg_type == IPV6_PKTINFO)
+	      {
+		dst_addr.addr.addr6 = ((struct in6_pktinfo *)CMSG_DATA(cmptr))->ipi6_addr;
+		if_index =((struct in6_pktinfo *)CMSG_DATA(cmptr))->ipi6_ifindex;
+	      }
+	}
+#endif
+      
+      /* enforce available interface configuration */
+      
+      if (!indextoname(listen->fd, if_index, ifr.ifr_name) ||
+	  !iface_check(listen->family, &dst_addr, ifr.ifr_name, &if_index))
+	return;
+      
+      if (listen->family == AF_INET &&
+	  (daemon->options & OPT_LOCALISE) &&
+	  ioctl(listen->fd, SIOCGIFNETMASK, &ifr) == -1)
+	return;
+      
+      netmask = ((struct sockaddr_in *) &ifr.ifr_addr)->sin_addr;
+    }
+  
+  if (extract_request(header, (size_t)n, daemon->namebuff, &type))
+    {
+      char types[20];
+
+      querystr(types, type);
+
+      if (listen->family == AF_INET) 
+	log_query(F_QUERY | F_IPV4 | F_FORWARD, daemon->namebuff, 
+		  (struct all_addr *)&source_addr.in.sin_addr, types);
+#ifdef HAVE_IPV6
+      else
+	log_query(F_QUERY | F_IPV6 | F_FORWARD, daemon->namebuff, 
+		  (struct all_addr *)&source_addr.in6.sin6_addr, types);
+#endif
+    }
+
+  m = answer_request (header, ((char *) header) + PACKETSZ, (size_t)n, 
+		      dst_addr_4, netmask, now);
+  if (m >= 1)
+    {
+      send_from(listen->fd, daemon->options & OPT_NOWILD, (char *)header, 
+		m, &source_addr, &dst_addr, if_index);
+      daemon->local_answer++;
+    }
+  else if (forward_query(listen->fd, &source_addr, &dst_addr, if_index,
+			 header, (size_t)n, now, NULL))
+    daemon->queries_forwarded++;
+  else
+    daemon->local_answer++;
+}
+
+/* The daemon forks before calling this: it should deal with one connection,
+   blocking as neccessary, and then return. Note, need to be a bit careful
+   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)
+{
+  int size = 0;
+  size_t m;
+  unsigned short qtype, gotname;
+  unsigned char c1, c2;
+  /* Max TCP packet + slop */
+  unsigned char *packet = whine_malloc(65536 + MAXDNAME + RRFIXEDSZ);
+  HEADER *header;
+  struct server *last_server;
+  
+  while (1)
+    {
+      if (!packet ||
+	  !read_write(confd, &c1, 1, 1) || !read_write(confd, &c2, 1, 1) ||
+	  !(size = c1 << 8 | c2) ||
+	  !read_write(confd, packet, size, 1))
+       	return packet; 
+  
+      if (size < (int)sizeof(HEADER))
+	continue;
+      
+      header = (HEADER *)packet;
+      
+      if ((gotname = extract_request(header, (unsigned int)size, daemon->namebuff, &qtype)))
+	{
+	  union mysockaddr peer_addr;
+	  socklen_t peer_len = sizeof(union mysockaddr);
+	  
+	  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);
+#ifdef HAVE_IPV6
+	      else
+		log_query(F_QUERY | F_IPV6 | F_FORWARD, daemon->namebuff, 
+			  (struct all_addr *)&peer_addr.in6.sin6_addr, types);
+#endif
+	    }
+	}
+      
+      /* m > 0 if answered from cache */
+      m = answer_request(header, ((char *) header) + 65536, (unsigned int)size, 
+			 local_addr, netmask, now);
+
+      /* Do this by steam now we're not in the select() loop */
+      check_log_writer(NULL); 
+      
+      if (m == 0)
+	{
+	  unsigned short flags = 0;
+	  struct all_addr *addrp = NULL;
+	  int type = 0;
+	  char *domain = NULL;
+	  
+	  if (gotname)
+	    flags = search_servers(now, &addrp, gotname, daemon->namebuff, &type, &domain);
+	  
+	  if (type != 0  || (daemon->options & OPT_ORDER) || !daemon->last_server)
+	    last_server = daemon->servers;
+	  else
+	    last_server = daemon->last_server;
+      
+	  if (!flags && last_server)
+	    {
+	      struct server *firstsendto = NULL;
+	      unsigned int crc = questions_crc(header, (unsigned int)size, daemon->namebuff);
+
+	      /* Loop round available servers until we succeed in connecting to one.
+	         Note that this code subtley ensures that consecutive queries on this connection
+	         which can go to the same server, do so. */
+	      while (1) 
+		{
+		  if (!firstsendto)
+		    firstsendto = last_server;
+		  else
+		    {
+		      if (!(last_server = last_server->next))
+			last_server = daemon->servers;
+		      
+		      if (last_server == firstsendto)
+			break;
+		    }
+	      
+		  /* server for wrong domain */
+		  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))
+		    {
+		      close(last_server->tcpfd);
+		      last_server->tcpfd = -1;
+		    }
+		  
+		  if (last_server->tcpfd == -1)	
+		    continue;
+
+		  c1 = size >> 8;
+		  c2 = size;
+		  
+		  if (!read_write(last_server->tcpfd, &c1, 1, 0) ||
+		      !read_write(last_server->tcpfd, &c2, 1, 0) ||
+		      !read_write(last_server->tcpfd, packet, size, 0) ||
+		      !read_write(last_server->tcpfd, &c1, 1, 1) ||
+		      !read_write(last_server->tcpfd, &c2, 1, 1))
+		    {
+		      close(last_server->tcpfd);
+		      last_server->tcpfd = -1;
+		      continue;
+		    } 
+		  
+		  m = (c1 << 8) | c2;
+		  if (!read_write(last_server->tcpfd, packet, m, 1))
+		    return packet;
+		  
+		  if (!gotname)
+		    strcpy(daemon->namebuff, "query");
+		  if (last_server->addr.sa.sa_family == AF_INET)
+		    log_query(F_SERVER | F_IPV4 | F_FORWARD, daemon->namebuff, 
+			      (struct all_addr *)&last_server->addr.in.sin_addr, NULL); 
+#ifdef HAVE_IPV6
+		  else
+		    log_query(F_SERVER | F_IPV6 | F_FORWARD, daemon->namebuff, 
+			      (struct all_addr *)&last_server->addr.in6.sin6_addr, NULL);
+#endif 
+		  
+		  /* There's no point in updating the cache, since this process will exit and
+		     lose the information after a few queries. We make this call for the alias and 
+		     bogus-nxdomain side-effects. */
+		  /* If the crc of the question section doesn't match the crc we sent, then
+		     someone might be attempting to insert bogus values into the cache by 
+		     sending replies containing questions and bogus answers. */
+		  if (crc == questions_crc(header, (unsigned int)m, daemon->namebuff))
+		    m = process_reply(header, now, last_server, (unsigned int)m);
+		  
+		  break;
+		}
+	    }
+	  
+	  /* In case of local answer or no connections made. */
+	  if (m == 0)
+	    m = setup_reply(header, (unsigned int)size, addrp, flags, daemon->local_ttl);
+	}
+
+      check_log_writer(NULL);
+      
+      c1 = m>>8;
+      c2 = m;
+      if (!read_write(confd, &c1, 1, 0) ||
+	  !read_write(confd, &c2, 1, 0) || 
+	  !read_write(confd, packet, m, 0))
+	return packet;
+    }
+}
+
+static struct frec *allocate_frec(time_t now)
+{
+  struct frec *f;
+  
+  if ((f = (struct frec *)whine_malloc(sizeof(struct frec))))
+    {
+      f->next = daemon->frec_list;
+      f->time = now;
+      f->sentto = NULL;
+      f->rfd4 = NULL;
+#ifdef HAVE_IPV6
+      f->rfd6 = NULL;
+#endif
+      daemon->frec_list = f;
+    }
+
+  return f;
+}
+
+static struct randfd *allocate_rfd(int family)
+{
+  static int finger = 0;
+  int i;
+
+  /* limit the number of sockets we have open to avoid starvation of 
+     (eg) TFTP. Once we have a reasonable number, randomness should be OK */
+
+  for (i = 0; i < RANDOM_SOCKS; i++)
+    if (daemon->randomsocks[i].refcount == 0)
+      {
+	if ((daemon->randomsocks[i].fd = random_sock(family)) == -1)
+	  break;
+      
+	daemon->randomsocks[i].refcount = 1;
+	daemon->randomsocks[i].family = family;
+	return &daemon->randomsocks[i];
+      }
+
+  /* No free ones or cannot get new socket, grab an existing one */
+  for (i = 0; i < RANDOM_SOCKS; i++)
+    {
+      int j = (i+finger) % RANDOM_SOCKS;
+      if (daemon->randomsocks[j].refcount != 0 &&
+	  daemon->randomsocks[j].family == family && 
+	  daemon->randomsocks[j].refcount != 0xffff)
+	{
+	  finger = j;
+	  daemon->randomsocks[j].refcount++;
+	  return &daemon->randomsocks[j];
+	}
+    }
+
+  return NULL; /* doom */
+}
+
+static void free_frec(struct frec *f)
+{
+  if (f->rfd4 && --(f->rfd4->refcount) == 0)
+    close(f->rfd4->fd);
+    
+  f->rfd4 = NULL;
+  f->sentto = NULL;
+  
+#ifdef HAVE_IPV6
+  if (f->rfd6 && --(f->rfd6->refcount) == 0)
+    close(f->rfd6->fd);
+    
+  f->rfd6 = NULL;
+#endif
+}
+
+/* if wait==NULL return a free or older than TIMEOUT record.
+   else return *wait zero if one available, or *wait is delay to
+   when the oldest in-use record will expire. Impose an absolute
+   limit of 4*TIMEOUT before we wipe things (for random sockets) */
+struct frec *get_new_frec(time_t now, int *wait)
+{
+  struct frec *f, *oldest, *target;
+  int count;
+  
+  if (wait)
+    *wait = 0;
+
+  for (f = daemon->frec_list, oldest = NULL, target =  NULL, count = 0; f; f = f->next, count++)
+    if (!f->sentto)
+      target = f;
+    else 
+      {
+	if (difftime(now, f->time) >= 4*TIMEOUT)
+	  {
+	    free_frec(f);
+	    target = f;
+	  }
+	
+	if (!oldest || difftime(f->time, oldest->time) <= 0)
+	  oldest = f;
+      }
+
+  if (target)
+    {
+      target->time = now;
+      return target;
+    }
+  
+  /* can't find empty one, use oldest if there is one
+     and it's older than timeout */
+  if (oldest && ((int)difftime(now, oldest->time)) >= TIMEOUT)
+    { 
+      /* keep stuff for twice timeout if we can by allocating a new
+	 record instead */
+      if (difftime(now, oldest->time) < 2*TIMEOUT && 
+	  count <= daemon->ftabsize &&
+	  (f = allocate_frec(now)))
+	return f;
+
+      if (!wait)
+	{
+	  free_frec(oldest);
+	  oldest->time = now;
+	}
+      return oldest;
+    }
+  
+  /* none available, calculate time 'till oldest record expires */
+  if (count > daemon->ftabsize)
+    {
+      if (oldest && wait)
+	*wait = oldest->time + (time_t)TIMEOUT - now;
+      return NULL;
+    }
+  
+  if (!(f = allocate_frec(now)) && wait)
+    /* wait one second on malloc failure */
+    *wait = 1;
+
+  return f; /* OK if malloc fails and this is NULL */
+}
+ 
+/* crc is all-ones if not known. */
+static struct frec *lookup_frec(unsigned short id, unsigned int crc)
+{
+  struct frec *f;
+
+  for(f = daemon->frec_list; f; f = f->next)
+    if (f->sentto && f->new_id == id && 
+	(f->crc == crc || crc == 0xffffffff))
+      return f;
+      
+  return NULL;
+}
+
+static struct frec *lookup_frec_by_sender(unsigned short id,
+					  union mysockaddr *addr,
+					  unsigned int crc)
+{
+  struct frec *f;
+  
+  for(f = daemon->frec_list; f; f = f->next)
+    if (f->sentto &&
+	f->orig_id == id && 
+	f->crc == crc &&
+	sockaddr_isequal(&f->source, addr))
+      return f;
+   
+  return NULL;
+}
+
+/* A server record is going away, remove references to it */
+void server_gone(struct server *server)
+{
+  struct frec *f;
+  
+  for (f = daemon->frec_list; f; f = f->next)
+    if (f->sentto && f->sentto == server)
+      free_frec(f);
+  
+  if (daemon->last_server == server)
+    daemon->last_server = NULL;
+
+  if (daemon->srv_save == server)
+    daemon->srv_save = NULL;
+}
+
+/* return unique random ids.
+   For signed packets we can't change the ID without breaking the
+   signing, so we keep the same one. In this case force is set, and this
+   routine degenerates into killing any conflicting forward record. */
+static unsigned short get_id(int force, unsigned short force_id, unsigned int crc)
+{
+  unsigned short ret = 0;
+  
+  if (force)
+    {
+      struct frec *f = lookup_frec(force_id, crc);
+      if (f)
+	free_frec(f); /* free */
+      ret = force_id;
+    }
+  else do 
+    ret = rand16();
+  while (lookup_frec(ret, crc));
+  
+  return ret;
+}
+
+
+
+
+
diff --git a/src/helper.c b/src/helper.c
new file mode 100755
index 0000000..b992e78
--- /dev/null
+++ b/src/helper.c
@@ -0,0 +1,428 @@
+/* dnsmasq is Copyright (c) 2000-2009 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"
+
+/* This file has code to fork a helper process which recieves data via a pipe 
+   shared with the main process and which is responsible for calling a script when
+   DHCP leases change.
+
+   The helper process is forked before the main process drops root, so it retains root 
+   privs to pass on to the script. For this reason it tries to be paranoid about 
+   data received from the main process, in case that has been compromised. We don't
+   want the helper to give an attacker root. In particular, the script to be run is
+   not settable via the pipe, once the fork has taken place it is not alterable by the 
+   main process.
+*/
+
+#if defined(HAVE_DHCP) && defined(HAVE_SCRIPT)
+
+static void my_setenv(const char *name, const char *value, int *error);
+
+struct script_data
+{
+  unsigned char action, hwaddr_len, hwaddr_type;
+  unsigned char clid_len, hostname_len, uclass_len, vclass_len, shost_len;
+  struct in_addr addr, giaddr;
+  unsigned int remaining_time;
+#ifdef HAVE_BROKEN_RTC
+  unsigned int length;
+#else
+  time_t expires;
+#endif
+  unsigned char hwaddr[DHCP_CHADDR_MAX];
+  char interface[IF_NAMESIZE];
+};
+
+static struct script_data *buf = NULL;
+static size_t bytes_in_buf = 0, buf_size = 0;
+
+int create_helper(int event_fd, int err_fd, uid_t uid, gid_t gid, long max_fd)
+{
+  pid_t pid;
+  int i, pipefd[2];
+  struct sigaction sigact;
+
+  /* create the pipe through which the main program sends us commands,
+     then fork our process. */
+  if (pipe(pipefd) == -1 || !fix_fd(pipefd[1]) || (pid = fork()) == -1)
+    {
+      send_event(err_fd, EVENT_PIPE_ERR, errno);
+      _exit(0);
+    }
+
+  if (pid != 0)
+    {
+      close(pipefd[0]); /* close reader side */
+      return pipefd[1];
+    }
+
+  /* ignore SIGTERM, so that we can clean up when the main process gets hit
+     and SIGALRM so that we can use sleep() */
+  sigact.sa_handler = SIG_IGN;
+  sigact.sa_flags = 0;
+  sigemptyset(&sigact.sa_mask);
+  sigaction(SIGTERM, &sigact, NULL);
+  sigaction(SIGALRM, &sigact, NULL);
+
+  if (!(daemon->options & OPT_DEBUG) && uid != 0)
+    {
+      gid_t dummy;
+      if (setgroups(0, &dummy) == -1 || 
+	  setgid(gid) == -1 || 
+	  setuid(uid) == -1)
+	{
+	  if (daemon->options & OPT_NO_FORK)
+	    /* send error to daemon process if no-fork */
+	    send_event(event_fd, EVENT_HUSER_ERR, errno);
+	  else
+	    {
+	      /* kill daemon */
+	      send_event(event_fd, EVENT_DIE, 0);
+	      /* return error */
+	      send_event(err_fd, EVENT_HUSER_ERR, errno);
+	    }
+	  _exit(0);
+	}
+    }
+
+  /* close all the sockets etc, we don't need them here. This closes err_fd, so that
+     main process can return. */
+  for (max_fd--; max_fd >= 0; max_fd--)
+    if (max_fd != STDOUT_FILENO && max_fd != STDERR_FILENO && 
+	max_fd != STDIN_FILENO && max_fd != pipefd[0] && max_fd != event_fd)
+      close(max_fd);
+  
+  /* loop here */
+  while(1)
+    {
+      struct script_data data;
+      char *p, *action_str, *hostname = NULL;
+      unsigned char *buf = (unsigned char *)daemon->namebuff;
+      int err = 0;
+
+      /* we read zero bytes when pipe closed: this is our signal to exit */ 
+      if (!read_write(pipefd[0], (unsigned char *)&data, sizeof(data), 1))
+	_exit(0);
+      
+      if (data.action == ACTION_DEL)
+	action_str = "del";
+      else if (data.action == ACTION_ADD)
+	action_str = "add";
+      else if (data.action == ACTION_OLD || data.action == ACTION_OLD_HOSTNAME)
+	action_str = "old";
+      else
+	continue;
+	
+      /* stringify MAC into dhcp_buff */
+      p = daemon->dhcp_buff;
+      if (data.hwaddr_type != ARPHRD_ETHER || data.hwaddr_len == 0) 
+        p += sprintf(p, "%.2x-", data.hwaddr_type);
+      for (i = 0; (i < data.hwaddr_len) && (i < DHCP_CHADDR_MAX); i++)
+        {
+          p += sprintf(p, "%.2x", data.hwaddr[i]);
+          if (i != data.hwaddr_len - 1)
+            p += sprintf(p, ":");
+        }
+      
+      /* and CLID into packet */
+      if (!read_write(pipefd[0], buf, data.clid_len, 1))
+	continue;
+      for (p = daemon->packet, i = 0; i < data.clid_len; i++)
+	{
+	  p += sprintf(p, "%.2x", buf[i]);
+	  if (i != data.clid_len - 1) 
+	    p += sprintf(p, ":");
+	}
+      
+      /* and expiry or length into dhcp_buff2 */
+#ifdef HAVE_BROKEN_RTC
+      sprintf(daemon->dhcp_buff2, "%u ", data.length);
+#else
+      sprintf(daemon->dhcp_buff2, "%lu ", (unsigned long)data.expires);
+#endif
+      
+      if (!read_write(pipefd[0], buf, 
+		      data.hostname_len + data.uclass_len + data.vclass_len + data.shost_len, 1))
+	continue;
+      
+      /* possible fork errors are all temporary resource problems */
+      while ((pid = fork()) == -1 && (errno == EAGAIN || errno == ENOMEM))
+	sleep(2);
+      
+      if (pid == -1)
+	continue;
+	  
+      /* wait for child to complete */
+      if (pid != 0)
+	{
+	  /* reap our children's children, if necessary */
+	  while (1)
+	    {
+	      int status;
+	      pid_t rc = wait(&status);
+	      
+	      if (rc == pid)
+		{
+		  /* On error send event back to main process for logging */
+		  if (WIFSIGNALED(status))
+		    send_event(event_fd, EVENT_KILLED, WTERMSIG(status));
+		  else if (WIFEXITED(status) && WEXITSTATUS(status) != 0)
+		    send_event(event_fd, EVENT_EXITED, WEXITSTATUS(status));
+		  break;
+		}
+	      
+	      if (rc == -1 && errno != EINTR)
+		break;
+	    }
+	  
+	  continue;
+	}
+      
+      if (data.clid_len != 0)
+	my_setenv("DNSMASQ_CLIENT_ID", daemon->packet, &err);
+
+      if (strlen(data.interface) != 0)
+	my_setenv("DNSMASQ_INTERFACE", data.interface, &err);
+            
+#ifdef HAVE_BROKEN_RTC
+      my_setenv("DNSMASQ_LEASE_LENGTH", daemon->dhcp_buff2, &err);
+#else
+      my_setenv("DNSMASQ_LEASE_EXPIRES", daemon->dhcp_buff2, &err); 
+#endif
+      
+      if (data.vclass_len != 0)
+	{
+	  buf[data.vclass_len - 1] = 0; /* don't trust zero-term */
+	  /* cannot have = chars in env - truncate if found . */
+	  if ((p = strchr((char *)buf, '=')))
+	    *p = 0;
+	  my_setenv("DNSMASQ_VENDOR_CLASS", (char *)buf, &err);
+	  buf += data.vclass_len;
+	}
+      
+      if (data.uclass_len != 0)
+	{
+	  unsigned char *end = buf + data.uclass_len;
+	  buf[data.uclass_len - 1] = 0; /* don't trust zero-term */
+	  
+	  for (i = 0; buf < end;)
+	    {
+	      size_t len = strlen((char *)buf) + 1;
+	      if ((p = strchr((char *)buf, '=')))
+		*p = 0;
+	      if (strlen((char *)buf) != 0)
+		{
+		  sprintf(daemon->dhcp_buff2, "DNSMASQ_USER_CLASS%i", i++);
+		  my_setenv(daemon->dhcp_buff2, (char *)buf, &err);
+		}
+	      buf += len;
+	    }
+	}
+      
+      if (data.shost_len != 0)
+	{
+	  buf[data.shost_len - 1] = 0; /* don't trust zero-term */
+	  /* cannot have = chars in env - truncate if found . */
+	  if ((p = strchr((char *)buf, '=')))
+	    *p = 0;
+	  my_setenv("DNSMASQ_SUPPLIED_HOSTNAME", (char *)buf, &err);
+	  buf += data.shost_len;
+	}
+
+      if (data.giaddr.s_addr != 0)
+	my_setenv("DNSMASQ_RELAY_ADDRESS", inet_ntoa(data.giaddr), &err); 
+
+      sprintf(daemon->dhcp_buff2, "%u ", data.remaining_time);
+      my_setenv("DNSMASQ_TIME_REMAINING", daemon->dhcp_buff2, &err);
+      
+      if (data.hostname_len != 0)
+	{
+	  char *dot;
+	  hostname = (char *)buf;
+	  hostname[data.hostname_len - 1] = 0;
+	  if (!legal_hostname(hostname))
+	    hostname = NULL;
+	  else if ((dot = strchr(hostname, '.')))
+	    {
+	      my_setenv("DNSMASQ_DOMAIN", dot+1, &err);
+	      *dot = 0;
+	    }
+	}
+      
+      if (data.action == ACTION_OLD_HOSTNAME && hostname)
+	{
+	  my_setenv("DNSMASQ_OLD_HOSTNAME", hostname, &err);
+	  hostname = NULL;
+	}
+
+      /* we need to have the event_fd around if exec fails */
+      if ((i = fcntl(event_fd, F_GETFD)) != -1)
+	fcntl(event_fd, F_SETFD, i | FD_CLOEXEC);
+      close(pipefd[0]);
+
+      p =  strrchr(daemon->lease_change_command, '/');
+      if (err == 0)
+	{
+	  execl(daemon->lease_change_command, 
+		p ? p+1 : daemon->lease_change_command,
+		action_str, daemon->dhcp_buff, inet_ntoa(data.addr), hostname, (char*)NULL);
+	  err = errno;
+	}
+      /* failed, send event so the main process logs the problem */
+      send_event(event_fd, EVENT_EXEC_ERR, err);
+      _exit(0); 
+    }
+}
+
+static void my_setenv(const char *name, const char *value, int *error)
+{
+  if (*error == 0 && setenv(name, value, 1) != 0)
+    *error = errno;
+}
+ 
+/* pack up lease data into a buffer */    
+void queue_script(int action, struct dhcp_lease *lease, char *hostname, time_t now)
+{
+  unsigned char *p;
+  size_t size;
+  unsigned int hostname_len = 0, clid_len = 0, vclass_len = 0;
+  unsigned int uclass_len = 0, shost_len = 0;
+  
+  /* no script */
+  if (daemon->helperfd == -1)
+    return;
+
+  if (lease->vendorclass)
+    vclass_len = lease->vendorclass_len;
+  if (lease->userclass)
+    uclass_len = lease->userclass_len;
+  if (lease->supplied_hostname)
+    shost_len = lease->supplied_hostname_len;
+  if (lease->clid)
+    clid_len = lease->clid_len;
+  if (hostname)
+    hostname_len = strlen(hostname) + 1;
+
+  size = sizeof(struct script_data) +  clid_len + vclass_len + uclass_len + shost_len + hostname_len;
+
+  if (size > buf_size)
+    {
+      struct script_data *new;
+      
+      /* start with reasonable size, will almost never need extending. */
+      if (size < sizeof(struct script_data) + 200)
+	size = sizeof(struct script_data) + 200;
+
+      if (!(new = whine_malloc(size)))
+	return;
+      if (buf)
+	free(buf);
+      buf = new;
+      buf_size = size;
+    }
+
+  buf->action = action;
+  buf->hwaddr_len = lease->hwaddr_len;
+  buf->hwaddr_type = lease->hwaddr_type;
+  buf->clid_len = clid_len;
+  buf->vclass_len = vclass_len;
+  buf->uclass_len = uclass_len;
+  buf->shost_len = shost_len;
+  buf->hostname_len = hostname_len;
+  buf->addr = lease->addr;
+  buf->giaddr = lease->giaddr;
+  memcpy(buf->hwaddr, lease->hwaddr, lease->hwaddr_len);
+  buf->interface[0] = 0;
+#ifdef HAVE_LINUX_NETWORK
+  if (lease->last_interface != 0)
+    {
+      struct ifreq ifr;
+      ifr.ifr_ifindex = lease->last_interface;
+      if (ioctl(daemon->dhcpfd, SIOCGIFNAME, &ifr) != -1)
+	strncpy(buf->interface, ifr.ifr_name, IF_NAMESIZE);
+    }
+#else
+  if (lease->last_interface != 0)
+    if_indextoname(lease->last_interface, buf->interface);
+#endif
+  
+#ifdef HAVE_BROKEN_RTC 
+  buf->length = lease->length;
+#else
+  buf->expires = lease->expires;
+#endif
+  buf->remaining_time = (unsigned int)difftime(lease->expires, now);
+
+  p = (unsigned char *)(buf+1);
+  if (clid_len != 0)
+    {
+      memcpy(p, lease->clid, clid_len);
+      p += clid_len;
+    }
+  if (vclass_len != 0)
+    {
+      memcpy(p, lease->vendorclass, vclass_len);
+      p += vclass_len;
+    }
+  if (uclass_len != 0)
+    {
+      memcpy(p, lease->userclass, uclass_len);
+      p += uclass_len;
+    }
+  if (shost_len != 0)
+    {
+      memcpy(p, lease->supplied_hostname, shost_len);
+      p += shost_len;
+    } 
+  if (hostname_len != 0)
+    {
+      memcpy(p, hostname, hostname_len);
+      p += hostname_len;
+    }
+
+  bytes_in_buf = p - (unsigned char *)buf;
+}
+
+int helper_buf_empty(void)
+{
+  return bytes_in_buf == 0;
+}
+
+void helper_write(void)
+{
+  ssize_t rc;
+
+  if (bytes_in_buf == 0)
+    return;
+  
+  if ((rc = write(daemon->helperfd, buf, bytes_in_buf)) != -1)
+    {
+      if (bytes_in_buf != (size_t)rc)
+	memmove(buf, buf + rc, bytes_in_buf - rc); 
+      bytes_in_buf -= rc;
+    }
+  else
+    {
+      if (errno == EAGAIN || errno == EINTR)
+	return;
+      bytes_in_buf = 0;
+    }
+}
+
+#endif
+
+
diff --git a/src/lease.c b/src/lease.c
new file mode 100755
index 0000000..c021d15
--- /dev/null
+++ b/src/lease.c
@@ -0,0 +1,622 @@
+/* dnsmasq is Copyright (c) 2000-2009 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_DHCP
+
+static struct dhcp_lease *leases = NULL, *old_leases = NULL;
+static int dns_dirty, file_dirty, leases_left;
+
+void lease_init(time_t now)
+{
+  unsigned long ei;
+  struct in_addr addr;
+  struct dhcp_lease *lease;
+  int clid_len, hw_len, hw_type;
+  FILE *leasestream;
+  
+  /* These two each hold a DHCP option max size 255
+     and get a terminating zero added */
+  daemon->dhcp_buff = safe_malloc(256);
+  daemon->dhcp_buff2 = safe_malloc(256); 
+  
+  leases_left = daemon->dhcp_max;
+
+  if (daemon->options & OPT_LEASE_RO)
+    {
+      /* run "<lease_change_script> init" once to get the
+	 initial state of the database. If leasefile-ro is
+	 set without a script, we just do without any 
+	 lease database. */
+#ifdef HAVE_SCRIPT
+      if (daemon->lease_change_command)
+	{
+	  strcpy(daemon->dhcp_buff, daemon->lease_change_command);
+	  strcat(daemon->dhcp_buff, " init");
+	  leasestream = popen(daemon->dhcp_buff, "r");
+	}
+      else
+#endif
+	{
+          file_dirty = dns_dirty = 0;
+          return;
+        }
+
+    }
+  else
+    {
+      /* NOTE: need a+ mode to create file if it doesn't exist */
+      leasestream = daemon->lease_stream = fopen(daemon->lease_file, "a+");
+      
+      if (!leasestream)
+	die(_("cannot open or create lease file %s: %s"), daemon->lease_file, EC_FILE);
+      
+      /* a+ mode leaves pointer at end. */
+      rewind(leasestream);
+    }
+  
+  /* client-id max length is 255 which is 255*2 digits + 254 colons 
+     borrow DNS packet buffer which is always larger than 1000 bytes */
+  if (leasestream)
+    while (fscanf(leasestream, "%lu %255s %16s %255s %764s",
+		  &ei, daemon->dhcp_buff2, daemon->namebuff, 
+		  daemon->dhcp_buff, daemon->packet) == 5)
+      {
+	hw_len = parse_hex(daemon->dhcp_buff2, (unsigned char *)daemon->dhcp_buff2, DHCP_CHADDR_MAX, NULL, &hw_type);
+	/* For backwards compatibility, no explict MAC address type means ether. */
+	if (hw_type == 0 && hw_len != 0)
+	  hw_type = ARPHRD_ETHER;
+	
+	addr.s_addr = inet_addr(daemon->namebuff);
+	
+	/* decode hex in place */
+	clid_len = 0;
+	if (strcmp(daemon->packet, "*") != 0)
+	  clid_len = parse_hex(daemon->packet, (unsigned char *)daemon->packet, 255, NULL, NULL);
+	
+	if (!(lease = lease_allocate(addr)))
+	  die (_("too many stored leases"), NULL, EC_MISC);
+       	
+#ifdef HAVE_BROKEN_RTC
+	if (ei != 0)
+	  lease->expires = (time_t)ei + now;
+	else
+	  lease->expires = (time_t)0;
+	lease->length = ei;
+#else
+	/* strictly time_t is opaque, but this hack should work on all sane systems,
+	   even when sizeof(time_t) == 8 */
+	lease->expires = (time_t)ei;
+#endif
+	
+	lease_set_hwaddr(lease, (unsigned char *)daemon->dhcp_buff2, (unsigned char *)daemon->packet, hw_len, hw_type, clid_len);
+	
+	if (strcmp(daemon->dhcp_buff, "*") !=  0)
+	  lease_set_hostname(lease, daemon->dhcp_buff, 0);
+
+	/* set these correctly: the "old" events are generated later from
+	   the startup synthesised SIGHUP. */
+	lease->new = lease->changed = 0;
+      }
+  
+#ifdef HAVE_SCRIPT
+  if (!daemon->lease_stream)
+    {
+      int rc = 0;
+
+      /* shell returns 127 for "command not found", 126 for bad permissions. */
+      if (!leasestream || (rc = pclose(leasestream)) == -1 || WEXITSTATUS(rc) == 127 || WEXITSTATUS(rc) == 126)
+	{
+	  if (WEXITSTATUS(rc) == 127)
+	    errno = ENOENT;
+	  else if (WEXITSTATUS(rc) == 126)
+	    errno = EACCES;
+	  die(_("cannot run lease-init script %s: %s"), daemon->lease_change_command, EC_FILE);
+	}
+      
+      if (WEXITSTATUS(rc) != 0)
+	{
+	  sprintf(daemon->dhcp_buff, "%d", WEXITSTATUS(rc));
+	  die(_("lease-init script returned exit code %s"), daemon->dhcp_buff, WEXITSTATUS(rc) + EC_INIT_OFFSET);
+	}
+    }
+#endif
+
+  /* Some leases may have expired */
+  file_dirty = 0;
+  lease_prune(NULL, now);
+  dns_dirty = 1;
+}
+
+void lease_update_from_configs(void)
+{
+  /* changes to the config may change current leases. */
+  
+  struct dhcp_lease *lease;
+  struct dhcp_config *config;
+  char *name;
+
+  for (lease = leases; lease; lease = lease->next)
+    if ((config = find_config(daemon->dhcp_conf, NULL, lease->clid, lease->clid_len, 
+			      lease->hwaddr, lease->hwaddr_len, lease->hwaddr_type, NULL)) && 
+	(config->flags & CONFIG_NAME) &&
+	(!(config->flags & CONFIG_ADDR) || config->addr.s_addr == lease->addr.s_addr))
+      lease_set_hostname(lease, config->hostname, 1);
+    else if ((name = host_from_dns(lease->addr)))
+      lease_set_hostname(lease, name, 1); /* updates auth flag only */
+}
+
+static void ourprintf(int *errp, char *format, ...)
+{
+  va_list ap;
+  
+  va_start(ap, format);
+  if (!(*errp) && vfprintf(daemon->lease_stream, format, ap) < 0)
+    *errp = errno;
+  va_end(ap);
+}
+
+void lease_update_file(time_t now)
+{
+  struct dhcp_lease *lease;
+  time_t next_event;
+  int i, err = 0;
+
+  if (file_dirty != 0 && daemon->lease_stream)
+    {
+      errno = 0;
+      rewind(daemon->lease_stream);
+      if (errno != 0 || ftruncate(fileno(daemon->lease_stream), 0) != 0)
+	err = errno;
+      
+      for (lease = leases; lease; lease = lease->next)
+	{
+#ifdef HAVE_BROKEN_RTC
+	  ourprintf(&err, "%u ", lease->length);
+#else
+	  ourprintf(&err, "%lu ", (unsigned long)lease->expires);
+#endif
+	  if (lease->hwaddr_type != ARPHRD_ETHER || lease->hwaddr_len == 0) 
+	    ourprintf(&err, "%.2x-", lease->hwaddr_type);
+	  for (i = 0; i < lease->hwaddr_len; i++)
+	    {
+	      ourprintf(&err, "%.2x", lease->hwaddr[i]);
+	      if (i != lease->hwaddr_len - 1)
+		ourprintf(&err, ":");
+	    }
+
+	  ourprintf(&err, " %s ", inet_ntoa(lease->addr));
+	  ourprintf(&err, "%s ", lease->hostname ? lease->hostname : "*");
+	  	  
+	  if (lease->clid && lease->clid_len != 0)
+	    {
+	      for (i = 0; i < lease->clid_len - 1; i++)
+		ourprintf(&err, "%.2x:", lease->clid[i]);
+	      ourprintf(&err, "%.2x\n", lease->clid[i]);
+	    }
+	  else
+	    ourprintf(&err, "*\n");	  
+	}
+      
+      if (fflush(daemon->lease_stream) != 0 ||
+	  fsync(fileno(daemon->lease_stream)) < 0)
+	err = errno;
+      
+      if (!err)
+	file_dirty = 0;
+    }
+  
+  /* Set alarm for when the first lease expires + slop. */
+  for (next_event = 0, lease = leases; lease; lease = lease->next)
+    if (lease->expires != 0 &&
+	(next_event == 0 || difftime(next_event, lease->expires + 10) > 0.0))
+      next_event = lease->expires + 10;
+   
+  if (err)
+    {
+      if (next_event == 0 || difftime(next_event, LEASE_RETRY + now) > 0.0)
+	next_event = LEASE_RETRY + now;
+      
+      my_syslog(MS_DHCP | LOG_ERR, _("failed to write %s: %s (retry in %us)"), 
+		daemon->lease_file, strerror(err),
+		(unsigned int)difftime(next_event, now));
+    }
+
+  if (next_event != 0)
+    alarm((unsigned)difftime(next_event, now)); 
+}
+
+void lease_update_dns(void)
+{
+  struct dhcp_lease *lease;
+  
+  if (daemon->port != 0 && dns_dirty)
+    {
+      cache_unhash_dhcp();
+      
+      for (lease = leases; lease; lease = lease->next)
+	{
+	  if (lease->fqdn)
+	    cache_add_dhcp_entry(lease->fqdn, &lease->addr, lease->expires);
+	     
+	  if (!(daemon->options & OPT_DHCP_FQDN) && lease->hostname)
+	    cache_add_dhcp_entry(lease->hostname, &lease->addr, lease->expires);
+	}
+      
+      dns_dirty = 0;
+    }
+}
+
+void lease_prune(struct dhcp_lease *target, time_t now)
+{
+  struct dhcp_lease *lease, *tmp, **up;
+
+  for (lease = leases, up = &leases; lease; lease = tmp)
+    {
+      tmp = lease->next;
+      if ((lease->expires != 0 && difftime(now, lease->expires) > 0) || lease == target)
+	{
+	  file_dirty = 1;
+	  if (lease->hostname)
+	    dns_dirty = 1;
+	  
+	  *up = lease->next; /* unlink */
+	  
+	  /* Put on old_leases list 'till we
+	     can run the script */
+	  lease->next = old_leases;
+	  old_leases = lease;
+	  
+	  leases_left++;
+	}
+      else
+	up = &lease->next;
+    }
+} 
+	
+  
+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;
+
+  if (clid)
+    for (lease = leases; lease; lease = lease->next)
+      if (lease->clid && clid_len == lease->clid_len &&
+	  memcmp(clid, lease->clid, clid_len) == 0)
+	return lease;
+  
+  for (lease = leases; lease; lease = lease->next)	
+    if ((!lease->clid || !clid) && 
+	hw_len != 0 && 
+	lease->hwaddr_len == hw_len &&
+	lease->hwaddr_type == hw_type &&
+	memcmp(hwaddr, lease->hwaddr, hw_len) == 0)
+      return lease;
+  
+  return NULL;
+}
+
+struct dhcp_lease *lease_find_by_addr(struct in_addr addr)
+{
+  struct dhcp_lease *lease;
+
+  for (lease = leases; lease; lease = lease->next)
+    if (lease->addr.s_addr == addr.s_addr)
+      return lease;
+  
+  return NULL;
+}
+
+
+struct dhcp_lease *lease_allocate(struct in_addr addr)
+{
+  struct dhcp_lease *lease;
+  if (!leases_left || !(lease = whine_malloc(sizeof(struct dhcp_lease))))
+    return NULL;
+
+  memset(lease, 0, sizeof(struct dhcp_lease));
+  lease->new = 1;
+  lease->addr = addr;
+  lease->hwaddr_len = 256; /* illegal value */
+  lease->expires = 1;
+#ifdef HAVE_BROKEN_RTC
+  lease->length = 0xffffffff; /* illegal value */
+#endif
+  lease->next = leases;
+  leases = lease;
+  
+  file_dirty = 1;
+  leases_left--;
+
+  return lease;
+}
+
+void lease_set_expires(struct dhcp_lease *lease, unsigned int len, time_t now)
+{
+  time_t exp = now + (time_t)len;
+  
+  if (len == 0xffffffff)
+    {
+      exp = 0;
+      len = 0;
+    }
+  
+  if (exp != lease->expires)
+    {
+      dns_dirty = 1;
+      lease->expires = exp;
+#ifndef HAVE_BROKEN_RTC
+      lease->aux_changed = file_dirty = 1;
+#endif
+    }
+  
+#ifdef HAVE_BROKEN_RTC
+  if (len != lease->length)
+    {
+      lease->length = len;
+      lease->aux_changed = file_dirty = 1; 
+    }
+#endif
+} 
+
+void lease_set_hwaddr(struct dhcp_lease *lease, unsigned char *hwaddr,
+		      unsigned char *clid, int hw_len, int hw_type, int clid_len)
+{
+  if (hw_len != lease->hwaddr_len ||
+      hw_type != lease->hwaddr_type || 
+      (hw_len != 0 && memcmp(lease->hwaddr, hwaddr, hw_len) != 0))
+    {
+      memcpy(lease->hwaddr, hwaddr, hw_len);
+      lease->hwaddr_len = hw_len;
+      lease->hwaddr_type = hw_type;
+      lease->changed = file_dirty = 1; /* run script on change */
+    }
+
+  /* only update clid when one is available, stops packets
+     without a clid removing the record. Lease init uses
+     clid_len == 0 for no clid. */
+  if (clid_len != 0 && clid)
+    {
+      if (!lease->clid)
+	lease->clid_len = 0;
+
+      if (lease->clid_len != clid_len)
+	{
+	  lease->aux_changed = file_dirty = 1;
+	  free(lease->clid);
+	  if (!(lease->clid = whine_malloc(clid_len)))
+	    return;
+	}
+      else if (memcmp(lease->clid, clid, clid_len) != 0)
+	lease->aux_changed = file_dirty = 1;
+	  
+      lease->clid_len = clid_len;
+      memcpy(lease->clid, clid, clid_len);
+    }
+
+}
+
+static void kill_name(struct dhcp_lease *lease)
+{
+  /* run script to say we lost our old name */
+  
+  /* this shouldn't happen unless updates are very quick and the
+     script very slow, we just avoid a memory leak if it does. */
+  free(lease->old_hostname);
+  
+  /* If we know the fqdn, pass that. The helper will derive the
+     unqualified name from it, free the unqulaified name here. */
+
+  if (lease->fqdn)
+    {
+      lease->old_hostname = lease->fqdn;
+      free(lease->hostname);
+    }
+  else
+    lease->old_hostname = lease->hostname;
+
+  lease->hostname = lease->fqdn = NULL;
+}
+
+void lease_set_hostname(struct dhcp_lease *lease, char *name, int auth)
+{
+  struct dhcp_lease *lease_tmp;
+  char *new_name = NULL, *new_fqdn = NULL;
+  
+  if (lease->hostname && name && hostname_isequal(lease->hostname, name))
+    {
+      lease->auth_name = auth;
+      return;
+    }
+  
+  if (!name && !lease->hostname)
+    return;
+
+  /* If a machine turns up on a new net without dropping the old lease,
+     or two machines claim the same name, then we end up with two interfaces with
+     the same name. Check for that here and remove the name from the old lease.
+     Don't allow a name from the client to override a name from dnsmasq config. */
+  
+  if (name)
+    {
+      if ((new_name = whine_malloc(strlen(name) + 1)))
+	{
+	  char *suffix = get_domain(lease->addr);
+	  strcpy(new_name, name);
+	  if (suffix && (new_fqdn = whine_malloc(strlen(new_name) + strlen(suffix) + 2)))
+	    {
+	      strcpy(new_fqdn, name);
+	      strcat(new_fqdn, ".");
+	      strcat(new_fqdn, suffix);
+	    }
+	}
+	  
+      /* Depending on mode, we check either unqualified name or FQDN. */
+      for (lease_tmp = leases; lease_tmp; lease_tmp = lease_tmp->next)
+	{
+	  if (daemon->options & OPT_DHCP_FQDN)
+	    {
+	      if (!new_fqdn || !lease_tmp->fqdn || !hostname_isequal(lease_tmp->fqdn, new_fqdn) )
+		continue;
+	    }
+	  else
+	    {
+	      if (!new_name || !lease_tmp->hostname || !hostname_isequal(lease_tmp->hostname, new_name) )
+		continue; 
+	    }
+	  
+	  if (lease_tmp->auth_name && !auth)
+	    {
+	      free(new_name);
+	      free(new_fqdn);
+	      return;
+	    }
+	
+	  kill_name(lease_tmp);
+	  break;
+	}
+    }
+
+  if (lease->hostname)
+    kill_name(lease);
+
+  lease->hostname = new_name;
+  lease->fqdn = new_fqdn;
+  lease->auth_name = auth;
+  
+  file_dirty = 1;
+  dns_dirty = 1; 
+  lease->changed = 1; /* run script on change */
+}
+
+void lease_set_interface(struct dhcp_lease *lease, int interface)
+{
+  if (lease->last_interface == interface)
+    return;
+
+  lease->last_interface = interface;
+  lease->changed = 1;
+}
+
+void rerun_scripts(void)
+{
+  struct dhcp_lease *lease;
+  
+  for (lease = leases; lease; lease = lease->next)
+    lease->changed = 1;
+}
+
+/* deleted leases get transferred to the old_leases list.
+   remove them here, after calling the lease change
+   script. Also run the lease change script on new/modified leases.
+
+   Return zero if nothing to do. */
+int do_script_run(time_t now)
+{
+  struct dhcp_lease *lease;
+
+#ifdef HAVE_DBUS
+  /* If we're going to be sending DBus signals, but the connection is not yet up,
+     delay everything until it is. */
+  if ((daemon->options & OPT_DBUS) && !daemon->dbus)
+    return 0;
+#endif
+
+  if (old_leases)
+    {
+      lease = old_leases;
+                  
+      /* If the lease still has an old_hostname, do the "old" action on that first */
+      if (lease->old_hostname)
+	{
+#ifdef HAVE_SCRIPT
+	  queue_script(ACTION_OLD_HOSTNAME, lease, lease->old_hostname, now);
+#endif
+	  free(lease->old_hostname);
+	  lease->old_hostname = NULL;
+	  return 1;
+	}
+      else 
+	{
+	  kill_name(lease);
+#ifdef HAVE_SCRIPT
+	  queue_script(ACTION_DEL, lease, lease->old_hostname, now);
+#endif
+#ifdef HAVE_DBUS
+	  emit_dbus_signal(ACTION_DEL, lease, lease->old_hostname);
+#endif
+	  old_leases = lease->next;
+	  
+	  free(lease->old_hostname); 
+	  free(lease->clid);
+	  free(lease->vendorclass);
+	  free(lease->userclass);
+	  free(lease->supplied_hostname);
+	  free(lease);
+	    
+	  return 1; 
+	}
+    }
+  
+  /* make sure we announce the loss of a hostname before its new location. */
+  for (lease = leases; lease; lease = lease->next)
+    if (lease->old_hostname)
+      {	
+#ifdef HAVE_SCRIPT
+	queue_script(ACTION_OLD_HOSTNAME, lease, lease->old_hostname, now);
+#endif
+	free(lease->old_hostname);
+	lease->old_hostname = NULL;
+	return 1;
+      }
+  
+  for (lease = leases; lease; lease = lease->next)
+    if (lease->new || lease->changed || 
+	(lease->aux_changed && (daemon->options & OPT_LEASE_RO)))
+      {
+#ifdef HAVE_SCRIPT
+	queue_script(lease->new ? ACTION_ADD : ACTION_OLD, lease, 
+		     lease->fqdn ? lease->fqdn : lease->hostname, now);
+#endif
+#ifdef HAVE_DBUS
+	emit_dbus_signal(lease->new ? ACTION_ADD : ACTION_OLD, lease,
+			 lease->fqdn ? lease->fqdn : lease->hostname);
+#endif
+	lease->new = lease->changed = lease->aux_changed = 0;
+	
+	/* these are used for the "add" call, then junked, since they're not in the database */
+	free(lease->vendorclass);
+	lease->vendorclass = NULL;
+	
+	free(lease->userclass);
+	lease->userclass = NULL;
+	
+	free(lease->supplied_hostname);
+	lease->supplied_hostname = NULL;
+			
+	return 1;
+      }
+
+  return 0; /* nothing to do */
+}
+
+#endif
+	  
+
+      
+
diff --git a/src/log.c b/src/log.c
new file mode 100755
index 0000000..7ec491d
--- /dev/null
+++ b/src/log.c
@@ -0,0 +1,419 @@
+/* dnsmasq is Copyright (c) 2000-2009 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"
+
+/* Implement logging to /dev/log asynchronously. If syslogd is 
+   making DNS lookups through dnsmasq, and dnsmasq blocks awaiting
+   syslogd, then the two daemons can deadlock. We get around this
+   by not blocking when talking to syslog, instead we queue up to 
+   MAX_LOGS messages. If more are queued, they will be dropped,
+   and the drop event itself logged. */
+
+/* The "wire" protocol for logging is defined in RFC 3164 */
+
+/* From RFC 3164 */
+#define MAX_MESSAGE 1024
+
+/* defaults in case we die() before we log_start() */
+static int log_fac = LOG_DAEMON;
+static int log_stderr = 0; 
+static int log_fd = -1;
+static int log_to_file = 0;
+static int entries_alloced = 0;
+static int entries_lost = 0;
+static int connection_good = 1;
+static int max_logs = 0;
+static int connection_type = SOCK_DGRAM;
+
+struct log_entry {
+  int offset, length;
+  pid_t pid; /* to avoid duplicates over a fork */
+  struct log_entry *next;
+  char payload[MAX_MESSAGE];
+};
+
+static struct log_entry *entries = NULL;
+static struct log_entry *free_entries = NULL;
+
+
+int log_start(struct passwd *ent_pw, int errfd)
+{
+  int ret = 0;
+
+  log_stderr = !!(daemon->options & OPT_DEBUG);
+
+  if (daemon->log_fac != -1)
+    log_fac = daemon->log_fac;
+#ifdef LOG_LOCAL0
+  else if (daemon->options & OPT_DEBUG)
+    log_fac = LOG_LOCAL0;
+#endif
+
+  if (daemon->log_file)
+    { 
+      log_to_file = 1;
+      daemon->max_logs = 0;
+    }
+  
+  max_logs = daemon->max_logs;
+
+  if (!log_reopen(daemon->log_file))
+    {
+      send_event(errfd, EVENT_LOG_ERR, errno);
+      _exit(0);
+    }
+
+  /* if queuing is inhibited, make sure we allocate
+     the one required buffer now. */
+  if (max_logs == 0)
+    {  
+      free_entries = safe_malloc(sizeof(struct log_entry));
+      free_entries->next = NULL;
+      entries_alloced = 1;
+    }
+
+  /* If we're running as root and going to change uid later,
+     change the ownership here so that the file is always owned by
+     the dnsmasq user. Then logrotate can just copy the owner.
+     Failure of the chown call is OK, (for instance when started as non-root) */
+  if (log_to_file && ent_pw && ent_pw->pw_uid != 0 && 
+      fchown(log_fd, ent_pw->pw_uid, -1) != 0)
+    ret = errno;
+
+  return ret;
+}
+
+int log_reopen(char *log_file)
+{
+  if (log_fd != -1)
+    close(log_fd);
+
+  /* NOTE: umask is set to 022 by the time this gets called */
+     
+  if (log_file)
+    {
+      log_fd = open(log_file, O_WRONLY|O_CREAT|O_APPEND, S_IRUSR|S_IWUSR|S_IRGRP);
+      return log_fd != -1;
+    }
+  else
+#ifdef HAVE_SOLARIS_NETWORK
+    /* Solaris logging is "different", /dev/log is not unix-domain socket.
+       Just leave log_fd == -1 and use the vsyslog call for everything.... */
+#   define _PATH_LOG ""  /* dummy */
+    log_fd = -1;
+#else
+    {
+       int flags;
+       log_fd = socket(AF_UNIX, connection_type, 0);
+      
+      if (log_fd == -1)
+	return 0;
+      
+      /* if max_logs is zero, leave the socket blocking */
+      if (max_logs != 0 && (flags = fcntl(log_fd, F_GETFL)) != -1)
+	fcntl(log_fd, F_SETFL, flags | O_NONBLOCK);
+    }
+#endif
+
+  return 1;
+}
+
+static void free_entry(void)
+{
+  struct log_entry *tmp = entries;
+  entries = tmp->next;
+  tmp->next = free_entries;
+  free_entries = tmp;
+}      
+
+static void log_write(void)
+{
+  ssize_t rc;
+   
+  while (entries)
+    {
+      /* Avoid duplicates over a fork() */
+      if (entries->pid != getpid())
+	{
+	  free_entry();
+	  continue;
+	}
+
+      connection_good = 1;
+
+      if ((rc = write(log_fd, entries->payload + entries->offset, entries->length)) != -1)
+	{
+	  entries->length -= rc;
+	  entries->offset += rc;
+	  if (entries->length == 0)
+	    {
+	      free_entry();
+	      if (entries_lost != 0)
+		{
+		  int e = entries_lost;
+		  entries_lost = 0; /* avoid wild recursion */
+		  my_syslog(LOG_WARNING, _("overflow: %d log entries lost"), e);
+		}	  
+	    }
+	  continue;
+	}
+      
+      if (errno == EINTR)
+	continue;
+
+      if (errno == EAGAIN)
+	return; /* syslogd busy, go again when select() or poll() says so */
+      
+      if (errno == ENOBUFS)
+	{
+	  connection_good = 0;
+	  return;
+	}
+
+      /* errors handling after this assumes sockets */ 
+      if (!log_to_file)
+	{
+	  /* Once a stream socket hits EPIPE, we have to close and re-open
+	     (we ignore SIGPIPE) */
+	  if (errno == EPIPE)
+	    {
+	      if (log_reopen(NULL))
+		continue;
+	    }
+	  else if (errno == ECONNREFUSED || 
+		   errno == ENOTCONN || 
+		   errno == EDESTADDRREQ || 
+		   errno == ECONNRESET)
+	    {
+	      /* socket went (syslogd down?), try and reconnect. If we fail,
+		 stop trying until the next call to my_syslog() 
+		 ECONNREFUSED -> connection went down
+		 ENOTCONN -> nobody listening
+		 (ECONNRESET, EDESTADDRREQ are *BSD equivalents) */
+	      
+	      struct sockaddr_un logaddr;
+	      
+#ifdef HAVE_SOCKADDR_SA_LEN
+	      logaddr.sun_len = sizeof(logaddr) - sizeof(logaddr.sun_path) + strlen(_PATH_LOG) + 1; 
+#endif
+	      logaddr.sun_family = AF_UNIX;
+	      strncpy(logaddr.sun_path, _PATH_LOG, sizeof(logaddr.sun_path));
+	      
+	      /* Got connection back? try again. */
+	      if (connect(log_fd, (struct sockaddr *)&logaddr, sizeof(logaddr)) != -1)
+		continue;
+	      
+	      /* errors from connect which mean we should keep trying */
+	      if (errno == ENOENT || 
+		  errno == EALREADY || 
+		  errno == ECONNREFUSED ||
+		  errno == EISCONN || 
+		  errno == EINTR ||
+		  errno == EAGAIN)
+		{
+		  /* try again on next syslog() call */
+		  connection_good = 0;
+		  return;
+		}
+	      
+	      /* try the other sort of socket... */
+	      if (errno == EPROTOTYPE)
+		{
+		  connection_type = connection_type == SOCK_DGRAM ? SOCK_STREAM : SOCK_DGRAM;
+		  if (log_reopen(NULL))
+		    continue;
+		}
+	    }
+	}
+
+      /* give up - fall back to syslog() - this handles out-of-space
+	 when logging to a file, for instance. */
+      log_fd = -1;
+      my_syslog(LOG_CRIT, _("log failed: %s"), strerror(errno));
+      return;
+    }
+}
+
+/* priority is one of LOG_DEBUG, LOG_INFO, LOG_NOTICE, etc. See sys/syslog.h.
+   OR'd to priority can be MS_TFTP, MS_DHCP, ... to be able to do log separation between
+   DNS, DHCP and TFTP services.
+*/
+void my_syslog(int priority, const char *format, ...)
+{
+  va_list ap;
+  struct log_entry *entry;
+  time_t time_now;
+  char *p;
+  size_t len;
+  pid_t pid = getpid();
+  char *func = "";
+
+  if ((LOG_FACMASK & priority) == MS_TFTP)
+    func = "-tftp";
+  else if ((LOG_FACMASK & priority) == MS_DHCP)
+    func = "-dhcp";
+      
+  priority = LOG_PRI(priority);
+  
+  if (log_stderr) 
+    {
+      fprintf(stderr, "dnsmasq%s: ", func);
+      va_start(ap, format);
+      vfprintf(stderr, format, ap);
+      va_end(ap);
+      fputc('\n', stderr);
+    }
+
+  if (log_fd == -1)
+    {
+      /* fall-back to syslog if we die during startup or fail during running. */
+      static int isopen = 0;
+      if (!isopen)
+	{
+	  openlog("dnsmasq", LOG_PID, log_fac);
+	  isopen = 1;
+	}
+      va_start(ap, format);  
+      vsyslog(priority, format, ap);
+      va_end(ap);
+      return;
+    }
+  
+  if ((entry = free_entries))
+    free_entries = entry->next;
+  else if (entries_alloced < max_logs && (entry = malloc(sizeof(struct log_entry))))
+    entries_alloced++;
+  
+  if (!entry)
+    entries_lost++;
+  else
+    {
+      /* add to end of list, consumed from the start */
+      entry->next = NULL;
+      if (!entries)
+	entries = entry;
+      else
+	{
+	  struct log_entry *tmp;
+	  for (tmp = entries; tmp->next; tmp = tmp->next);
+	  tmp->next = entry;
+	}
+      
+      time(&time_now);
+      p = entry->payload;
+      if (!log_to_file)
+	p += sprintf(p, "<%d>", priority | log_fac);
+
+      p += sprintf(p, "%.15s dnsmasq%s[%d]: ", ctime(&time_now) + 4, func, (int)pid);
+        
+      len = p - entry->payload;
+      va_start(ap, format);  
+      len += vsnprintf(p, MAX_MESSAGE - len, format, ap) + 1; /* include zero-terminator */
+      va_end(ap);
+      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,
+     to save collecting too many log messages during a select loop. */
+  log_write();
+  
+  /* Since we're doing things asynchronously, a cache-dump, for instance,
+     can now generate log lines very fast. With a small buffer (desirable),
+     that means it can overflow the log-buffer very quickly,
+     so that the cache dump becomes mainly a count of how many lines 
+     overflowed. To avoid this, we delay here, the delay is controlled 
+     by queue-occupancy, and grows exponentially. The delay is limited to (2^8)ms.
+     The scaling stuff ensures that when the queue is bigger than 8, the delay
+     only occurs for the last 8 entries. Once the queue is full, we stop delaying
+     to preserve performance.
+  */
+
+  if (entries && max_logs != 0)
+    {
+      int d;
+      
+      for (d = 0,entry = entries; entry; entry = entry->next, d++);
+      
+      if (d == max_logs)
+	d = 0;
+      else if (max_logs > 8)
+	d -= max_logs - 8;
+
+      if (d > 0)
+	{
+	  struct timespec waiter;
+	  waiter.tv_sec = 0;
+	  waiter.tv_nsec = 1000000 << (d - 1); /* 1 ms */
+	  nanosleep(&waiter, NULL);
+      
+	  /* Have another go now */
+	  log_write();
+	}
+    } 
+}
+
+void set_log_writer(fd_set *set, int *maxfdp)
+{
+  if (entries && log_fd != -1 && connection_good)
+    {
+      FD_SET(log_fd, set);
+      bump_maxfd(log_fd, maxfdp);
+    }
+}
+
+void check_log_writer(fd_set *set)
+{
+  if (log_fd != -1 && (!set || FD_ISSET(log_fd, set)))
+    log_write();
+}
+
+void flush_log(void)
+{
+  /* block until queue empty */
+  if (log_fd != -1)
+    {
+      int flags;
+      if ((flags = fcntl(log_fd, F_GETFL)) != -1)
+	fcntl(log_fd, F_SETFL, flags & ~O_NONBLOCK);
+      log_write();
+      close(log_fd);
+    }
+}
+
+void die(char *message, char *arg1, int exit_code)
+{
+  char *errmess = strerror(errno);
+  
+  if (!arg1)
+    arg1 = errmess;
+
+  log_stderr = 1; /* print as well as log when we die.... */
+  fputc('\n', stderr); /* prettyfy  startup-script message */
+  my_syslog(LOG_CRIT, message, arg1, errmess);
+  
+  log_stderr = 0;
+  my_syslog(LOG_CRIT, _("FAILED to start up"));
+  flush_log();
+  
+  exit(exit_code);
+}
diff --git a/src/netlink.c b/src/netlink.c
new file mode 100755
index 0000000..77c4385
--- /dev/null
+++ b/src/netlink.c
@@ -0,0 +1,303 @@
+/* dnsmasq is Copyright (c) 2000-2009 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_LINUX_NETWORK
+
+#include <linux/types.h>
+#include <linux/netlink.h>
+#include <linux/rtnetlink.h>
+
+/* linux 2.6.19 buggers up the headers, patch it up here. */ 
+#ifndef IFA_RTA
+#  define IFA_RTA(r)  \
+       ((struct rtattr*)(((char*)(r)) + NLMSG_ALIGN(sizeof(struct ifaddrmsg))))
+
+#  include <linux/if_addr.h>
+#endif
+
+static struct iovec iov;
+static u32 netlink_pid;
+
+static void nl_err(struct nlmsghdr *h);
+static void nl_routechange(struct nlmsghdr *h);
+
+void netlink_init(void)
+{
+  struct sockaddr_nl addr;
+  socklen_t slen = sizeof(addr);
+
+  addr.nl_family = AF_NETLINK;
+  addr.nl_pad = 0;
+  addr.nl_pid = 0; /* autobind */
+#ifdef HAVE_IPV6
+  addr.nl_groups = RTMGRP_IPV4_ROUTE | RTMGRP_IPV6_ROUTE;
+#else
+  addr.nl_groups = RTMGRP_IPV4_ROUTE;
+#endif
+  
+  /* May not be able to have permission to set multicast groups don't die in that case */
+  if ((daemon->netlinkfd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE)) != -1)
+    {
+      if (bind(daemon->netlinkfd, (struct sockaddr *)&addr, sizeof(addr)) == -1)
+	{
+	  addr.nl_groups = 0;
+	  if (errno != EPERM || bind(daemon->netlinkfd, (struct sockaddr *)&addr, sizeof(addr)) == -1)
+	    daemon->netlinkfd = -1;
+	}
+    }
+  
+  if (daemon->netlinkfd == -1 || 
+      getsockname(daemon->netlinkfd, (struct sockaddr *)&addr, &slen) == 1)
+    die(_("cannot create netlink socket: %s"), NULL, EC_MISC);
+   
+  /* save pid assigned by bind() and retrieved by getsockname() */ 
+  netlink_pid = addr.nl_pid;
+  
+  iov.iov_len = 100;
+  iov.iov_base = safe_malloc(iov.iov_len);
+}
+
+static ssize_t netlink_recv(void)
+{
+  struct msghdr msg;
+  struct sockaddr_nl nladdr;
+  ssize_t rc;
+
+  while (1)
+    {
+      msg.msg_control = NULL;
+      msg.msg_controllen = 0;
+      msg.msg_name = &nladdr;
+      msg.msg_namelen = sizeof(nladdr);
+      msg.msg_iov = &iov;
+      msg.msg_iovlen = 1;
+      msg.msg_flags = 0;
+      
+      while ((rc = recvmsg(daemon->netlinkfd, &msg, MSG_PEEK | MSG_TRUNC)) == -1 && errno == EINTR);
+      
+      /* make buffer big enough */
+      if (rc != -1 && (msg.msg_flags & MSG_TRUNC))
+	{
+	  /* Very new Linux kernels return the actual size needed, older ones always return truncated size */
+	  if ((size_t)rc == iov.iov_len)
+	    {
+	      if (expand_buf(&iov, rc + 100))
+		continue;
+	    }
+	  else
+	    expand_buf(&iov, rc);
+	}
+
+      /* read it for real */
+      msg.msg_flags = 0;
+      while ((rc = recvmsg(daemon->netlinkfd, &msg, 0)) == -1 && errno == EINTR);
+      
+      /* Make sure this is from the kernel */
+      if (rc == -1 || nladdr.nl_pid == 0)
+	break;
+    }
+      
+  /* discard stuff which is truncated at this point (expand_buf() may fail) */
+  if (msg.msg_flags & MSG_TRUNC)
+    {
+      rc = -1;
+      errno = ENOMEM;
+    }
+  
+  return rc;
+}
+  
+int iface_enumerate(void *parm, int (*ipv4_callback)(), int (*ipv6_callback)())
+{
+  struct sockaddr_nl addr;
+  struct nlmsghdr *h;
+  ssize_t len;
+  static unsigned int seq = 0;
+  int family = AF_INET;
+
+  struct {
+    struct nlmsghdr nlh;
+    struct rtgenmsg g; 
+  } req;
+
+  addr.nl_family = AF_NETLINK;
+  addr.nl_pad = 0;
+  addr.nl_groups = 0;
+  addr.nl_pid = 0; /* address to kernel */
+
+ again:
+  req.nlh.nlmsg_len = sizeof(req);
+  req.nlh.nlmsg_type = RTM_GETADDR;
+  req.nlh.nlmsg_flags = NLM_F_ROOT | NLM_F_MATCH | NLM_F_REQUEST | NLM_F_ACK; 
+  req.nlh.nlmsg_pid = 0;
+  req.nlh.nlmsg_seq = ++seq;
+  req.g.rtgen_family = family; 
+
+  /* Don't block in recvfrom if send fails */
+  while((len = sendto(daemon->netlinkfd, (void *)&req, sizeof(req), 0, 
+		      (struct sockaddr *)&addr, sizeof(addr))) == -1 && retry_send());
+  
+  if (len == -1)
+    return 0;
+    
+  while (1)
+    {
+      if ((len = netlink_recv()) == -1)
+	{
+	  if (errno == ENOBUFS)
+	    {
+	      sleep(1);
+	      goto again;
+	    }
+	  return 0;
+	}
+
+      for (h = (struct nlmsghdr *)iov.iov_base; NLMSG_OK(h, (size_t)len); h = NLMSG_NEXT(h, len))
+	if (h->nlmsg_seq != seq || h->nlmsg_pid != netlink_pid)
+	  nl_routechange(h); /* May be multicast arriving async */
+	else if (h->nlmsg_type == NLMSG_ERROR)
+	  nl_err(h);
+	else if (h->nlmsg_type == NLMSG_DONE)
+	  {
+#ifdef HAVE_IPV6
+	    if (family == AF_INET && ipv6_callback)
+	      {
+		family = AF_INET6;
+		goto again;
+	      }
+#endif
+	    return 1;
+	  }
+	else if (h->nlmsg_type == RTM_NEWADDR)
+	  {
+	    struct ifaddrmsg *ifa = NLMSG_DATA(h);  
+	    struct rtattr *rta = IFA_RTA(ifa);
+	    unsigned int len1 = h->nlmsg_len - NLMSG_LENGTH(sizeof(*ifa));
+	    
+	    if (ifa->ifa_family == AF_INET)
+	      {
+		struct in_addr netmask, addr, broadcast;
+		
+		netmask.s_addr = htonl(0xffffffff << (32 - ifa->ifa_prefixlen));
+		addr.s_addr = 0;
+		broadcast.s_addr = 0;
+		
+		while (RTA_OK(rta, len1))
+		  {
+		    if (rta->rta_type == IFA_LOCAL)
+		      addr = *((struct in_addr *)(rta+1));
+		    else if (rta->rta_type == IFA_BROADCAST)
+		      broadcast = *((struct in_addr *)(rta+1));
+		    
+		    rta = RTA_NEXT(rta, len1);
+		  }
+		
+		if (addr.s_addr && ipv4_callback)
+		  if (!((*ipv4_callback)(addr, ifa->ifa_index, netmask, broadcast, parm)))
+		    return 0;
+	      }
+#ifdef HAVE_IPV6
+	    else if (ifa->ifa_family == AF_INET6)
+	      {
+		struct in6_addr *addrp = NULL;
+		while (RTA_OK(rta, len1))
+		  {
+		    if (rta->rta_type == IFA_ADDRESS)
+		      addrp = ((struct in6_addr *)(rta+1)); 
+		    
+		    rta = RTA_NEXT(rta, len1);
+		  }
+		
+		if (addrp && ipv6_callback)
+		  if (!((*ipv6_callback)(addrp, ifa->ifa_index, ifa->ifa_index, parm)))
+		    return 0;
+	      }
+#endif
+	  }
+    }
+}
+
+
+void netlink_multicast(void)
+{
+  ssize_t len;
+  struct nlmsghdr *h;
+  int flags;
+  
+  /* don't risk blocking reading netlink messages here. */
+  if ((flags = fcntl(daemon->netlinkfd, F_GETFL)) == -1 ||
+      fcntl(daemon->netlinkfd, F_SETFL, flags | O_NONBLOCK) == -1) 
+    return;
+  
+  if ((len = netlink_recv()) != -1)
+    {
+      for (h = (struct nlmsghdr *)iov.iov_base; NLMSG_OK(h, (size_t)len); h = NLMSG_NEXT(h, len))
+	if (h->nlmsg_type == NLMSG_ERROR)
+	  nl_err(h);
+	else
+	  nl_routechange(h);
+    }
+
+  /* restore non-blocking status */
+  fcntl(daemon->netlinkfd, F_SETFL, flags); 
+}
+
+static void nl_err(struct nlmsghdr *h)
+{
+  struct nlmsgerr *err = NLMSG_DATA(h);
+  
+  if (err->error != 0)
+    my_syslog(LOG_ERR, _("netlink returns error: %s"), strerror(-(err->error)));
+}
+
+/* We arrange to receive netlink multicast messages whenever the network route is added.
+   If this happens and we still have a DNS packet in the buffer, we re-send it.
+   This helps on DoD links, where frequently the packet which triggers dialling is
+   a DNS query, which then gets lost. By re-sending, we can avoid the lookup
+   failing. Note that we only accept these messages from the kernel (pid == 0) */ 
+static void nl_routechange(struct nlmsghdr *h)
+{
+  if (h->nlmsg_pid == 0 && h->nlmsg_type == RTM_NEWROUTE)
+    {
+      struct rtmsg *rtm = NLMSG_DATA(h);
+      int fd;
+
+      if (rtm->rtm_type != RTN_UNICAST || rtm->rtm_scope != RT_SCOPE_LINK)
+	return;
+
+      /* Force re-reading resolv file right now, for luck. */
+      daemon->last_resolv = 0;
+
+      if (daemon->srv_save)
+	{
+	  if (daemon->srv_save->sfd)
+	    fd = daemon->srv_save->sfd->fd;
+	  else if (daemon->rfd_save && daemon->rfd_save->refcount != 0)
+	    fd = daemon->rfd_save->fd;
+	  else
+	    return;
+	  
+	  while(sendto(fd, daemon->packet, daemon->packet_len, 0,
+		       &daemon->srv_save->addr.sa, sa_len(&daemon->srv_save->addr)) == -1 && retry_send()); 
+	}
+    }
+}
+
+#endif
+
+      
diff --git a/src/network.c b/src/network.c
new file mode 100755
index 0000000..68113fb
--- /dev/null
+++ b/src/network.c
@@ -0,0 +1,874 @@
+/* dnsmasq is Copyright (c) 2000-2009 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_LINUX_NETWORK
+
+int indextoname(int fd, int index, char *name)
+{
+  struct ifreq ifr;
+  
+  if (index == 0)
+    return 0;
+
+  ifr.ifr_ifindex = index;
+  if (ioctl(fd, SIOCGIFNAME, &ifr) == -1)
+    return 0;
+
+  strncpy(name, ifr.ifr_name, IF_NAMESIZE);
+
+  return 1;
+}
+
+#else
+
+int indextoname(int fd, int index, char *name)
+{ 
+  if (index == 0 || !if_indextoname(index, name))
+    return 0;
+
+  return 1;
+}
+
+#endif
+
+int iface_check(int family, struct all_addr *addr, char *name, int *indexp)
+{
+  struct iname *tmp;
+  int ret = 1;
+
+  /* Note: have to check all and not bail out early, so that we set the
+     "used" flags. */
+
+  if (indexp)
+    {
+      /* One form of bridging on BSD has the property that packets
+	 can be recieved on bridge interfaces which do not have an IP address.
+	 We allow these to be treated as aliases of another interface which does have
+	 an IP address with --dhcp-bridge=interface,alias,alias */
+      struct dhcp_bridge *bridge, *alias;
+      for (bridge = daemon->bridges; bridge; bridge = bridge->next)
+	{
+	  for (alias = bridge->alias; alias; alias = alias->next)
+	    if (strncmp(name, alias->iface, IF_NAMESIZE) == 0)
+	      {
+		int newindex;
+		
+		if (!(newindex = if_nametoindex(bridge->iface)))
+		  {
+		    my_syslog(LOG_WARNING, _("unknown interface %s in bridge-interface"), name);
+		    return 0;
+		  }
+		else 
+		  {
+		    *indexp = newindex;
+		    strncpy(name,  bridge->iface, IF_NAMESIZE);
+		    break;
+		  }
+	      }
+	  if (alias)
+	    break;
+	}
+    }
+  
+  if (daemon->if_names || (addr && daemon->if_addrs))
+    {
+      ret = 0;
+
+      for (tmp = daemon->if_names; tmp; tmp = tmp->next)
+	if (tmp->name && (strcmp(tmp->name, name) == 0))
+	  ret = tmp->used = 1;
+	        
+      for (tmp = daemon->if_addrs; tmp; tmp = tmp->next)
+	if (addr && tmp->addr.sa.sa_family == family)
+	  {
+	    if (family == AF_INET &&
+		tmp->addr.in.sin_addr.s_addr == addr->addr.addr4.s_addr)
+	      ret = tmp->used = 1;
+#ifdef HAVE_IPV6
+	    else if (family == AF_INET6 &&
+		     IN6_ARE_ADDR_EQUAL(&tmp->addr.in6.sin6_addr, 
+					&addr->addr.addr6))
+	      ret = tmp->used = 1;
+#endif
+	  }          
+    }
+  
+  for (tmp = daemon->if_except; tmp; tmp = tmp->next)
+    if (tmp->name && (strcmp(tmp->name, name) == 0))
+      ret = 0;
+  
+  return ret; 
+}
+      
+static int iface_allowed(struct irec **irecp, int if_index, 
+			 union mysockaddr *addr, struct in_addr netmask) 
+{
+  struct irec *iface;
+  int fd, mtu = 0, loopback;
+  struct ifreq ifr;
+  int dhcp_ok = 1;
+  struct iname *tmp;
+  
+  /* check whether the interface IP has been added already 
+     we call this routine multiple times. */
+  for (iface = *irecp; iface; iface = iface->next) 
+    if (sockaddr_isequal(&iface->addr, addr))
+      return 1;
+  
+  if ((fd = socket(PF_INET, SOCK_DGRAM, 0)) == -1 ||
+      !indextoname(fd, if_index, ifr.ifr_name) ||
+      ioctl(fd, SIOCGIFFLAGS, &ifr) == -1)
+    {
+      if (fd != -1)
+	{
+	  int errsave = errno;
+	  close(fd);
+	  errno = errsave;
+	}
+      return 0;
+    }
+   
+  loopback = ifr.ifr_flags & IFF_LOOPBACK;
+
+  if (ioctl(fd, SIOCGIFMTU, &ifr) != -1)
+    mtu = ifr.ifr_mtu;
+  
+  close(fd);
+  
+  /* If we are restricting the set of interfaces to use, make
+     sure that loopback interfaces are in that set. */
+  if (daemon->if_names && loopback)
+    {
+      struct iname *lo;
+      for (lo = daemon->if_names; lo; lo = lo->next)
+	if (lo->name && strcmp(lo->name, ifr.ifr_name) == 0)
+	  {
+	    lo->isloop = 1;
+	    break;
+	  }
+      
+      if (!lo && 
+	  (lo = whine_malloc(sizeof(struct iname))) &&
+	  (lo->name = whine_malloc(strlen(ifr.ifr_name)+1)))
+	{
+	  strcpy(lo->name, ifr.ifr_name);
+	  lo->isloop = lo->used = 1;
+	  lo->next = daemon->if_names;
+	  daemon->if_names = lo;
+	}
+    }
+  
+  if (addr->sa.sa_family == AF_INET &&
+      !iface_check(AF_INET, (struct all_addr *)&addr->in.sin_addr, ifr.ifr_name, NULL))
+    return 1;
+  
+  for (tmp = daemon->dhcp_except; tmp; tmp = tmp->next)
+    if (tmp->name && (strcmp(tmp->name, ifr.ifr_name) == 0))
+      dhcp_ok = 0;
+  
+#ifdef HAVE_IPV6
+  if (addr->sa.sa_family == AF_INET6 &&
+      !iface_check(AF_INET6, (struct all_addr *)&addr->in6.sin6_addr, ifr.ifr_name, NULL))
+    return 1;
+#endif
+
+  /* add to list */
+  if ((iface = whine_malloc(sizeof(struct irec))))
+    {
+      iface->addr = *addr;
+      iface->netmask = netmask;
+      iface->dhcp_ok = dhcp_ok;
+      iface->mtu = mtu;
+      iface->next = *irecp;
+      *irecp = iface;
+      return 1;
+    }
+  
+  errno = ENOMEM; 
+  return 0;
+}
+
+#ifdef HAVE_IPV6
+static int iface_allowed_v6(struct in6_addr *local, 
+			    int scope, int if_index, void *vparam)
+{
+  union mysockaddr addr;
+  struct in_addr netmask; /* dummy */
+  
+  netmask.s_addr = 0;
+  
+  memset(&addr, 0, sizeof(addr));
+#ifdef HAVE_SOCKADDR_SA_LEN
+  addr.in6.sin6_len = sizeof(addr.in6);
+#endif
+  addr.in6.sin6_family = AF_INET6;
+  addr.in6.sin6_addr = *local;
+  addr.in6.sin6_port = htons(daemon->port);
+  addr.in6.sin6_scope_id = scope;
+  
+  return iface_allowed((struct irec **)vparam, if_index, &addr, netmask);
+}
+#endif
+
+static int iface_allowed_v4(struct in_addr local, int if_index, 
+			    struct in_addr netmask, struct in_addr broadcast, void *vparam)
+{
+  union mysockaddr addr;
+
+  memset(&addr, 0, sizeof(addr));
+#ifdef HAVE_SOCKADDR_SA_LEN
+  addr.in.sin_len = sizeof(addr.in);
+#endif
+  addr.in.sin_family = AF_INET;
+  addr.in.sin_addr = broadcast; /* warning */
+  addr.in.sin_addr = local;
+  addr.in.sin_port = htons(daemon->port);
+
+  return iface_allowed((struct irec **)vparam, if_index, &addr, netmask);
+}
+   
+int enumerate_interfaces(void)
+{
+#ifdef HAVE_IPV6
+  return iface_enumerate(&daemon->interfaces, iface_allowed_v4, iface_allowed_v6);
+#else
+  return iface_enumerate(&daemon->interfaces, iface_allowed_v4, NULL);
+#endif
+}
+
+/* set NONBLOCK bit on fd: See Stevens 16.6 */
+int fix_fd(int fd)
+{
+  int flags;
+
+  if ((flags = fcntl(fd, F_GETFL)) == -1 ||
+      fcntl(fd, F_SETFL, flags | O_NONBLOCK) == -1)
+    return 0;
+  
+  return 1;
+}
+
+#if defined(HAVE_IPV6)
+static int create_ipv6_listener(struct listener **link, int port)
+{
+  union mysockaddr addr;
+  int tcpfd, fd;
+  struct listener *l;
+  int opt = 1;
+
+  memset(&addr, 0, sizeof(addr));
+  addr.in6.sin6_family = AF_INET6;
+  addr.in6.sin6_addr = in6addr_any;
+  addr.in6.sin6_port = htons(port);
+#ifdef HAVE_SOCKADDR_SA_LEN
+  addr.in6.sin6_len = sizeof(addr.in6);
+#endif
+
+  /* No error of the kernel doesn't support IPv6 */
+  if ((fd = socket(AF_INET6, SOCK_DGRAM, 0)) == -1)
+    return (errno == EPROTONOSUPPORT ||
+	    errno == EAFNOSUPPORT ||
+	    errno == EINVAL);
+  
+  if ((tcpfd = socket(AF_INET6, SOCK_STREAM, 0)) == -1)
+    return 0;
+      
+  if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) == -1 ||
+      setsockopt(tcpfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) == -1 ||
+      setsockopt(fd, IPV6_LEVEL, IPV6_V6ONLY, &opt, sizeof(opt)) == -1 ||
+      setsockopt(tcpfd, IPV6_LEVEL, IPV6_V6ONLY, &opt, sizeof(opt)) == -1 ||
+      !fix_fd(fd) ||
+      !fix_fd(tcpfd) ||
+#ifdef IPV6_RECVPKTINFO
+      setsockopt(fd, IPV6_LEVEL, IPV6_RECVPKTINFO, &opt, sizeof(opt)) == -1 ||
+#else
+      setsockopt(fd, IPV6_LEVEL, IPV6_PKTINFO, &opt, sizeof(opt)) == -1 ||
+#endif
+      bind(tcpfd, (struct sockaddr *)&addr, sa_len(&addr)) == -1 ||
+      listen(tcpfd, 5) == -1 ||
+      bind(fd, (struct sockaddr *)&addr, sa_len(&addr)) == -1) 
+    return 0;
+      
+  l = safe_malloc(sizeof(struct listener));
+  l->fd = fd;
+  l->tcpfd = tcpfd;
+  l->tftpfd = -1;
+  l->family = AF_INET6;
+  l->next = NULL;
+  *link = l;
+  
+  return 1;
+}
+#endif
+
+struct listener *create_wildcard_listeners(void)
+{
+  union mysockaddr addr;
+  int opt = 1;
+  struct listener *l, *l6 = NULL;
+  int tcpfd = -1, fd = -1, tftpfd = -1;
+
+  memset(&addr, 0, sizeof(addr));
+  addr.in.sin_family = AF_INET;
+  addr.in.sin_addr.s_addr = INADDR_ANY;
+  addr.in.sin_port = htons(daemon->port);
+#ifdef HAVE_SOCKADDR_SA_LEN
+  addr.in.sin_len = sizeof(struct sockaddr_in);
+#endif
+
+  if (daemon->port != 0)
+    {
+      
+      if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) == -1 ||
+	  (tcpfd = socket(AF_INET, SOCK_STREAM, 0)) == -1)
+	return NULL;
+      
+      if (setsockopt(tcpfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) == -1 ||
+	  bind(tcpfd, (struct sockaddr *)&addr, sa_len(&addr)) == -1 ||
+	  listen(tcpfd, 5) == -1 ||
+	  !fix_fd(tcpfd) ||
+#ifdef HAVE_IPV6
+	  !create_ipv6_listener(&l6, daemon->port) ||
+#endif
+	  setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) == -1 ||
+	  !fix_fd(fd) ||
+#if defined(HAVE_LINUX_NETWORK) 
+	  setsockopt(fd, SOL_IP, IP_PKTINFO, &opt, sizeof(opt)) == -1 ||
+#elif defined(IP_RECVDSTADDR) && defined(IP_RECVIF)
+	  setsockopt(fd, IPPROTO_IP, IP_RECVDSTADDR, &opt, sizeof(opt)) == -1 ||
+	  setsockopt(fd, IPPROTO_IP, IP_RECVIF, &opt, sizeof(opt)) == -1 ||
+#endif 
+	  bind(fd, (struct sockaddr *)&addr, sa_len(&addr)) == -1)
+	return NULL;
+    }
+  
+#ifdef HAVE_TFTP
+  if (daemon->options & OPT_TFTP)
+    {
+      addr.in.sin_port = htons(TFTP_PORT);
+      if ((tftpfd = socket(AF_INET, SOCK_DGRAM, 0)) == -1)
+	return NULL;
+      
+      if (!fix_fd(tftpfd) ||
+#if defined(HAVE_LINUX_NETWORK) 
+	  setsockopt(tftpfd, SOL_IP, IP_PKTINFO, &opt, sizeof(opt)) == -1 ||
+#elif defined(IP_RECVDSTADDR) && defined(IP_RECVIF)
+	  setsockopt(tftpfd, IPPROTO_IP, IP_RECVDSTADDR, &opt, sizeof(opt)) == -1 ||
+	  setsockopt(tftpfd, IPPROTO_IP, IP_RECVIF, &opt, sizeof(opt)) == -1 ||
+#endif 
+	  bind(tftpfd, (struct sockaddr *)&addr, sa_len(&addr)) == -1)
+	return NULL;
+    }
+#endif
+  
+  l = safe_malloc(sizeof(struct listener));
+  l->family = AF_INET;
+  l->fd = fd;
+  l->tcpfd = tcpfd;
+  l->tftpfd = tftpfd;
+  l->next = l6;
+
+  return l;
+}
+
+struct listener *create_bound_listeners(void)
+{
+  struct listener *listeners = NULL;
+  struct irec *iface;
+  int rc, opt = 1;
+#ifdef HAVE_IPV6
+  static int dad_count = 0;
+#endif
+
+  for (iface = daemon->interfaces; iface; iface = iface->next)
+    {
+      struct listener *new = safe_malloc(sizeof(struct listener));
+      new->family = iface->addr.sa.sa_family;
+      new->iface = iface;
+      new->next = listeners;
+      new->tftpfd = -1;
+      new->tcpfd = -1;
+      new->fd = -1;
+      listeners = new;
+
+      if (daemon->port != 0)
+	{
+	  if ((new->tcpfd = socket(iface->addr.sa.sa_family, SOCK_STREAM, 0)) == -1 ||
+	      (new->fd = socket(iface->addr.sa.sa_family, SOCK_DGRAM, 0)) == -1 ||
+	      setsockopt(new->fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) == -1 ||
+	      setsockopt(new->tcpfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) == -1 ||
+	      !fix_fd(new->tcpfd) ||
+	      !fix_fd(new->fd))
+	    die(_("failed to create listening socket: %s"), NULL, EC_BADNET);
+	  
+#ifdef HAVE_IPV6
+	  if (iface->addr.sa.sa_family == AF_INET6)
+	    {
+	      if (setsockopt(new->fd, IPV6_LEVEL, IPV6_V6ONLY, &opt, sizeof(opt)) == -1 ||
+		  setsockopt(new->tcpfd, IPV6_LEVEL, IPV6_V6ONLY, &opt, sizeof(opt)) == -1)
+		die(_("failed to set IPV6 options on listening socket: %s"), NULL, EC_BADNET);
+	    }
+#endif
+
+	  while(1)
+	    {
+	      if ((rc = bind(new->fd, &iface->addr.sa, sa_len(&iface->addr))) != -1)
+		break;
+	      
+#ifdef HAVE_IPV6
+	      /* An interface may have an IPv6 address which is still undergoing DAD. 
+		 If so, the bind will fail until the DAD completes, so we try over 20 seconds
+		 before failing. */
+	      if (iface->addr.sa.sa_family == AF_INET6 && (errno == ENODEV || errno == EADDRNOTAVAIL) && 
+		  dad_count++ < DAD_WAIT)
+		{
+		  sleep(1);
+		  continue;
+		}
+#endif
+	      break;
+	    }
+	  
+	  if (rc == -1 || bind(new->tcpfd, &iface->addr.sa, sa_len(&iface->addr)) == -1)
+	    {
+	      prettyprint_addr(&iface->addr, daemon->namebuff);
+	      die(_("failed to bind listening socket for %s: %s"), 
+		  daemon->namebuff, EC_BADNET);
+	    }
+	    
+	  if (listen(new->tcpfd, 5) == -1)
+	    die(_("failed to listen on socket: %s"), NULL, EC_BADNET);
+	}
+
+#ifdef HAVE_TFTP
+      if ((daemon->options & OPT_TFTP) && iface->addr.sa.sa_family == AF_INET && iface->dhcp_ok)
+	{
+	  short save = iface->addr.in.sin_port;
+	  iface->addr.in.sin_port = htons(TFTP_PORT);
+	  if ((new->tftpfd = socket(AF_INET, SOCK_DGRAM, 0)) == -1 ||
+	      setsockopt(new->tftpfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) == -1 ||
+	      !fix_fd(new->tftpfd) ||
+	      bind(new->tftpfd, &iface->addr.sa, sa_len(&iface->addr)) == -1)
+	    die(_("failed to create TFTP socket: %s"), NULL, EC_BADNET);
+	  iface->addr.in.sin_port = save;
+	}
+#endif
+
+    }
+
+  return listeners;
+}
+
+
+/* return a UDP socket bound to a random port, have to cope with straying into
+   occupied port nos and reserved ones. */
+int random_sock(int family)
+{
+  int fd;
+
+  if ((fd = socket(family, SOCK_DGRAM, 0)) != -1)
+    {
+      union mysockaddr addr;
+      unsigned int ports_avail = 65536u - (unsigned short)daemon->min_port;
+      int tries = ports_avail < 30 ? 3 * ports_avail : 100;
+
+      memset(&addr, 0, sizeof(addr));
+      addr.sa.sa_family = family;
+
+      /* don't loop forever if all ports in use. */
+
+      if (fix_fd(fd))
+	while(tries--)
+	  {
+	    unsigned short port = rand16();
+	    
+	    if (daemon->min_port != 0)
+	      port = htons(daemon->min_port + (port % ((unsigned short)ports_avail)));
+	    
+	    if (family == AF_INET) 
+	      {
+		addr.in.sin_addr.s_addr = INADDR_ANY;
+		addr.in.sin_port = port;
+#ifdef HAVE_SOCKADDR_SA_LEN
+		addr.in.sin_len = sizeof(struct sockaddr_in);
+#endif
+	      }
+#ifdef HAVE_IPV6
+	    else
+	      {
+		addr.in6.sin6_addr = in6addr_any; 
+		addr.in6.sin6_port = port;
+#ifdef HAVE_SOCKADDR_SA_LEN
+		addr.in6.sin6_len = sizeof(struct sockaddr_in6);
+#endif
+	      }
+#endif
+	    
+	    if (bind(fd, (struct sockaddr *)&addr, sa_len(&addr)) == 0)
+	      return fd;
+	    
+	    if (errno != EADDRINUSE && errno != EACCES)
+	      break;
+	  }
+
+      close(fd);
+    }
+
+  return -1; 
+}
+  
+
+int local_bind(int fd, union mysockaddr *addr, char *intname, int is_tcp)
+{
+  union mysockaddr addr_copy = *addr;
+
+  /* cannot set source _port_ for TCP connections. */
+  if (is_tcp)
+    {
+      if (addr_copy.sa.sa_family == AF_INET)
+	addr_copy.in.sin_port = 0;
+#ifdef HAVE_IPV6
+      else
+	addr_copy.in6.sin6_port = 0;
+#endif
+    }
+  
+  if (bind(fd, (struct sockaddr *)&addr_copy, sa_len(&addr_copy)) == -1)
+    return 0;
+    
+#if defined(SO_BINDTODEVICE)
+  if (intname[0] != 0 &&
+      setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, intname, strlen(intname)) == -1)
+    return 0;
+#endif
+
+  return 1;
+}
+
+static struct serverfd *allocate_sfd(union mysockaddr *addr, char *intname)
+{
+  struct serverfd *sfd;
+  int errsave;
+
+  /* when using random ports, servers which would otherwise use
+     the INADDR_ANY/port0 socket have sfd set to NULL */
+  if (!daemon->osport && intname[0] == 0)
+    {
+      errno = 0;
+      
+      if (addr->sa.sa_family == AF_INET &&
+	  addr->in.sin_addr.s_addr == INADDR_ANY &&
+	  addr->in.sin_port == htons(0)) 
+	return NULL;
+
+#ifdef HAVE_IPV6
+      if (addr->sa.sa_family == AF_INET6 &&
+	  memcmp(&addr->in6.sin6_addr, &in6addr_any, sizeof(in6addr_any)) == 0 &&
+	  addr->in6.sin6_port == htons(0)) 
+	return NULL;
+#endif
+    }
+      
+  /* may have a suitable one already */
+  for (sfd = daemon->sfds; sfd; sfd = sfd->next )
+    if (sockaddr_isequal(&sfd->source_addr, addr) &&
+	strcmp(intname, sfd->interface) == 0)
+      return sfd;
+  
+  /* need to make a new one. */
+  errno = ENOMEM; /* in case malloc fails. */
+  if (!(sfd = whine_malloc(sizeof(struct serverfd))))
+    return NULL;
+  
+  if ((sfd->fd = socket(addr->sa.sa_family, SOCK_DGRAM, 0)) == -1)
+    {
+      free(sfd);
+      return NULL;
+    }
+  
+  if (!local_bind(sfd->fd, addr, intname, 0) || !fix_fd(sfd->fd))
+    { 
+      errsave = errno; /* save error from bind. */
+      close(sfd->fd);
+      free(sfd);
+      errno = errsave;
+      return NULL;
+    }
+    
+  strcpy(sfd->interface, intname); 
+  sfd->source_addr = *addr;
+  sfd->next = daemon->sfds;
+  daemon->sfds = sfd;
+  return sfd; 
+}
+
+/* create upstream sockets during startup, before root is dropped which may be needed
+   this allows query_port to be a low port and interface binding */
+void pre_allocate_sfds(void)
+{
+  struct server *srv;
+  
+  if (daemon->query_port != 0)
+    {
+      union  mysockaddr addr;
+      memset(&addr, 0, sizeof(addr));
+      addr.in.sin_family = AF_INET;
+      addr.in.sin_addr.s_addr = INADDR_ANY;
+      addr.in.sin_port = htons(daemon->query_port);
+#ifdef HAVE_SOCKADDR_SA_LEN
+      addr.in.sin_len = sizeof(struct sockaddr_in);
+#endif
+      allocate_sfd(&addr, "");
+#ifdef HAVE_IPV6
+      memset(&addr, 0, sizeof(addr));
+      addr.in6.sin6_family = AF_INET6;
+      addr.in6.sin6_addr = in6addr_any;
+      addr.in6.sin6_port = htons(daemon->query_port);
+#ifdef HAVE_SOCKADDR_SA_LEN
+      addr.in6.sin6_len = sizeof(struct sockaddr_in6);
+#endif
+      allocate_sfd(&addr, "");
+#endif
+    }
+  
+  for (srv = daemon->servers; srv; srv = srv->next)
+    if (!(srv->flags & (SERV_LITERAL_ADDRESS | SERV_NO_ADDR)) &&
+	!allocate_sfd(&srv->source_addr, srv->interface) &&
+	errno != 0 &&
+	(daemon->options & OPT_NOWILD))
+      {
+	prettyprint_addr(&srv->addr, daemon->namebuff);
+	if (srv->interface[0] != 0)
+	  {
+	    strcat(daemon->namebuff, " ");
+	    strcat(daemon->namebuff, srv->interface);
+	  }
+	die(_("failed to bind server socket for %s: %s"),
+	    daemon->namebuff, EC_BADNET);
+      }  
+}
+
+
+void check_servers(void)
+{
+  struct irec *iface;
+  struct server *new, *tmp, *ret = NULL;
+  int port = 0;
+
+  for (new = daemon->servers; new; new = tmp)
+    {
+      tmp = new->next;
+      
+      if (!(new->flags & (SERV_LITERAL_ADDRESS | SERV_NO_ADDR)))
+	{
+	  port = prettyprint_addr(&new->addr, daemon->namebuff);
+
+	  /* 0.0.0.0 is nothing, the stack treats it like 127.0.0.1 */
+	  if (new->addr.sa.sa_family == AF_INET &&
+	      new->addr.in.sin_addr.s_addr == 0)
+	    {
+	      free(new);
+	      continue;
+	    }
+
+	  for (iface = daemon->interfaces; iface; iface = iface->next)
+	    if (sockaddr_isequal(&new->addr, &iface->addr))
+	      break;
+	  if (iface)
+	    {
+	      my_syslog(LOG_WARNING, _("ignoring nameserver %s - local interface"), daemon->namebuff);
+	      free(new);
+	      continue;
+	    }
+	  
+	  /* Do we need a socket set? */
+	  if (!new->sfd && 
+	      !(new->sfd = allocate_sfd(&new->source_addr, new->interface)) &&
+	      errno != 0)
+	    {
+	      my_syslog(LOG_WARNING, 
+			_("ignoring nameserver %s - cannot make/bind socket: %s"),
+			daemon->namebuff, strerror(errno));
+	      free(new);
+	      continue;
+	    }
+	}
+      
+      /* reverse order - gets it right. */
+      new->next = ret;
+      ret = new;
+      
+      if (new->flags & (SERV_HAS_DOMAIN | SERV_FOR_NODOTS))
+	{
+	  char *s1, *s2;
+	  if (!(new->flags & SERV_HAS_DOMAIN))
+	    s1 = _("unqualified"), s2 = _("names");
+	  else if (strlen(new->domain) == 0)
+	    s1 = _("default"), s2 = "";
+	  else
+	    s1 = _("domain"), s2 = new->domain;
+	  
+	  if (new->flags & SERV_NO_ADDR)
+	    my_syslog(LOG_INFO, _("using local addresses only for %s %s"), s1, s2);
+	  else if (!(new->flags & SERV_LITERAL_ADDRESS))
+	    my_syslog(LOG_INFO, _("using nameserver %s#%d for %s %s"), daemon->namebuff, port, s1, s2);
+	}
+      else if (new->interface[0] != 0)
+	my_syslog(LOG_INFO, _("using nameserver %s#%d(via %s)"), daemon->namebuff, port, new->interface); 
+      else
+	my_syslog(LOG_INFO, _("using nameserver %s#%d"), daemon->namebuff, port); 
+    }
+  
+  daemon->servers = ret;
+}
+
+/* Return zero if no servers found, in that case we keep polling.
+   This is a protection against an update-time/write race on resolv.conf */
+int reload_servers(char *fname)
+{
+  FILE *f;
+  char *line;
+  struct server *old_servers = NULL;
+  struct server *new_servers = NULL;
+  struct server *serv;
+  int gotone = 0;
+
+  /* buff happens to be MAXDNAME long... */
+  if (!(f = fopen(fname, "r")))
+    {
+      my_syslog(LOG_ERR, _("failed to read %s: %s"), fname, strerror(errno));
+      return 0;
+    }
+  
+  /* move old servers to free list - we can reuse the memory 
+     and not risk malloc if there are the same or fewer new servers. 
+     Servers which were specced on the command line go to the new list. */
+  for (serv = daemon->servers; serv;)
+    {
+      struct server *tmp = serv->next;
+      if (serv->flags & SERV_FROM_RESOLV)
+	{
+	  serv->next = old_servers;
+	  old_servers = serv; 
+	  /* forward table rules reference servers, so have to blow them away */
+	  server_gone(serv);
+	}
+      else
+	{
+	  serv->next = new_servers;
+	  new_servers = serv;
+	}
+      serv = tmp;
+    }
+  
+  while ((line = fgets(daemon->namebuff, MAXDNAME, f)))
+    {
+      union mysockaddr addr, source_addr;
+      char *token = strtok(line, " \t\n\r");
+      
+      if (!token)
+	continue;
+      if (strcmp(token, "nameserver") != 0 && strcmp(token, "server") != 0)
+	continue;
+      if (!(token = strtok(NULL, " \t\n\r")))
+	continue;
+      
+      memset(&addr, 0, sizeof(addr));
+      memset(&source_addr, 0, sizeof(source_addr));
+      
+      if ((addr.in.sin_addr.s_addr = inet_addr(token)) != (in_addr_t) -1)
+	{
+#ifdef HAVE_SOCKADDR_SA_LEN
+	  source_addr.in.sin_len = addr.in.sin_len = sizeof(source_addr.in);
+#endif
+	  source_addr.in.sin_family = addr.in.sin_family = AF_INET;
+	  addr.in.sin_port = htons(NAMESERVER_PORT);
+	  source_addr.in.sin_addr.s_addr = INADDR_ANY;
+	  source_addr.in.sin_port = htons(daemon->query_port);
+	}
+#ifdef HAVE_IPV6
+      else 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);
+#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);
+	}
+#endif /* IPV6 */
+      else
+	continue;
+      
+      if (old_servers)
+	{
+	  serv = old_servers;
+	  old_servers = old_servers->next;
+	}
+      else if (!(serv = whine_malloc(sizeof (struct server))))
+	continue;
+      
+      /* this list is reverse ordered: 
+	 it gets reversed again in check_servers */
+      serv->next = new_servers;
+      new_servers = serv;
+      serv->addr = addr;
+      serv->source_addr = source_addr;
+      serv->domain = NULL;
+      serv->interface[0] = 0;
+      serv->sfd = NULL;
+      serv->flags = SERV_FROM_RESOLV;
+      serv->queries = serv->failed_queries = 0;
+      gotone = 1;
+    }
+  
+  /* Free any memory not used. */
+  while (old_servers)
+    {
+      struct server *tmp = old_servers->next;
+      free(old_servers);
+      old_servers = tmp;
+    }
+
+  daemon->servers = new_servers;
+  fclose(f);
+
+  return gotone;
+}
+
+
+/* Use an IPv4 listener socket for ioctling */
+struct in_addr get_ifaddr(char *intr)
+{
+  struct listener *l;
+  struct ifreq ifr;
+
+  for (l = daemon->listeners; l && l->family != AF_INET; l = l->next);
+  
+  strncpy(ifr.ifr_name, intr, IF_NAMESIZE);
+  ifr.ifr_addr.sa_family = AF_INET;
+  
+  if (!l || ioctl(l->fd, SIOCGIFADDR, &ifr) == -1)
+    ((struct sockaddr_in *) &ifr.ifr_addr)->sin_addr.s_addr = -1;
+  
+  return ((struct sockaddr_in *) &ifr.ifr_addr)->sin_addr;
+}
+
+
+
diff --git a/src/option.c b/src/option.c
new file mode 100755
index 0000000..abea37e
--- /dev/null
+++ b/src/option.c
@@ -0,0 +1,2990 @@
+/* dnsmasq is Copyright (c) 2000-2009 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/>.
+*/
+
+/* define this to get facilitynames */
+#define SYSLOG_NAMES
+#include "dnsmasq.h"
+#include <setjmp.h>
+
+static volatile int mem_recover = 0;
+static jmp_buf mem_jmp;
+static void one_file(char *file, int nest, int hard_opt);
+
+/* Solaris headers don't have facility names. */
+#ifdef HAVE_SOLARIS_NETWORK
+static const struct {
+  char *c_name;
+  unsigned int c_val;
+}  facilitynames[] = {
+  { "kern",   LOG_KERN },
+  { "user",   LOG_USER },
+  { "mail",   LOG_MAIL },
+  { "daemon", LOG_DAEMON },
+  { "auth",   LOG_AUTH },
+  { "syslog", LOG_SYSLOG },
+  { "lpr",    LOG_LPR },
+  { "news",   LOG_NEWS },
+  { "uucp",   LOG_UUCP },
+  { "audit",  LOG_AUDIT },
+  { "cron",   LOG_CRON },
+  { "local0", LOG_LOCAL0 },
+  { "local1", LOG_LOCAL1 },
+  { "local2", LOG_LOCAL2 },
+  { "local3", LOG_LOCAL3 },
+  { "local4", LOG_LOCAL4 },
+  { "local5", LOG_LOCAL5 },
+  { "local6", LOG_LOCAL6 },
+  { "local7", LOG_LOCAL7 },
+  { NULL, 0 }
+};
+#endif
+
+#ifndef HAVE_GETOPT_LONG
+struct myoption {
+  const char *name;
+  int has_arg;
+  int *flag;
+  int val;
+};
+#endif
+
+#define OPTSTRING "951yZDNLERKzowefnbvhdkqr:m:p:c:l:s:i:t:u:g:a:x:S:C:A:T:H:Q:I:B:F:G:O:M:X:V:U:j:P:J:W:Y:2:4:6:7:8:0:3:"
+
+/* options which don't have a one-char version */
+#define LOPT_RELOAD    256
+#define LOPT_NO_NAMES  257
+#define LOPT_TFTP      258
+#define LOPT_SECURE    259
+#define LOPT_PREFIX    260
+#define LOPT_PTR       261
+#define LOPT_BRIDGE    262
+#define LOPT_TFTP_MAX  263
+#define LOPT_FORCE     264
+#define LOPT_NOBLOCK   265
+#define LOPT_LOG_OPTS  266
+#define LOPT_MAX_LOGS  267
+#define LOPT_CIRCUIT   268
+#define LOPT_REMOTE    269
+#define LOPT_SUBSCR    270
+#define LOPT_INTNAME   271
+#define LOPT_BANK      272
+#define LOPT_DHCP_HOST 273
+#define LOPT_APREF     274
+#define LOPT_OVERRIDE  275
+#define LOPT_TFTPPORTS 276
+#define LOPT_REBIND    277
+#define LOPT_NOLAST    278
+#define LOPT_OPTS      279
+#define LOPT_DHCP_OPTS 280
+#define LOPT_MATCH     281
+#define LOPT_BROADCAST 282
+#define LOPT_NEGTTL    283
+#define LOPT_ALTPORT   284
+#define LOPT_SCRIPTUSR 285
+#define LOPT_LOCAL     286
+#define LOPT_NAPTR     287
+#define LOPT_MINPORT   288
+#define LOPT_DHCP_FQDN 289
+#define LOPT_CNAME     290
+#define LOPT_PXE_PROMT 291
+#define LOPT_PXE_SERV  292
+#define LOPT_TEST      293
+
+#ifdef HAVE_GETOPT_LONG
+static const struct option opts[] =  
+#else
+static const struct myoption opts[] = 
+#endif
+  { 
+    { "version", 0, 0, 'v' },
+    { "no-hosts", 0, 0, 'h' },
+    { "no-poll", 0, 0, 'n' },
+    { "help", 0, 0, 'w' },
+    { "no-daemon", 0, 0, 'd' },
+    { "log-queries", 0, 0, 'q' },
+    { "user", 2, 0, 'u' },
+    { "group", 2, 0, 'g' },
+    { "resolv-file", 2, 0, 'r' },
+    { "mx-host", 1, 0, 'm' },
+    { "mx-target", 1, 0, 't' },
+    { "cache-size", 2, 0, 'c' },
+    { "port", 1, 0, 'p' },
+    { "dhcp-leasefile", 2, 0, 'l' },
+    { "dhcp-lease", 1, 0, 'l' },
+    { "dhcp-host", 1, 0, 'G' },
+    { "dhcp-range", 1, 0, 'F' },
+    { "dhcp-option", 1, 0, 'O' },
+    { "dhcp-boot", 1, 0, 'M' },
+    { "domain", 1, 0, 's' },
+    { "domain-suffix", 1, 0, 's' },
+    { "interface", 1, 0, 'i' },
+    { "listen-address", 1, 0, 'a' },
+    { "bogus-priv", 0, 0, 'b' },
+    { "bogus-nxdomain", 1, 0, 'B' },
+    { "selfmx", 0, 0, 'e' },
+    { "filterwin2k", 0, 0, 'f' },
+    { "pid-file", 2, 0, 'x' },
+    { "strict-order", 0, 0, 'o' },
+    { "server", 1, 0, 'S' },
+    { "local", 1, 0, LOPT_LOCAL },
+    { "address", 1, 0, 'A' },
+    { "conf-file", 2, 0, 'C' },
+    { "no-resolv", 0, 0, 'R' },
+    { "expand-hosts", 0, 0, 'E' },
+    { "localmx", 0, 0, 'L' },
+    { "local-ttl", 1, 0, 'T' },
+    { "no-negcache", 0, 0, 'N' },
+    { "addn-hosts", 1, 0, 'H' },
+    { "query-port", 1, 0, 'Q' },
+    { "except-interface", 1, 0, 'I' },
+    { "no-dhcp-interface", 1, 0, '2' },
+    { "domain-needed", 0, 0, 'D' },
+    { "dhcp-lease-max", 1, 0, 'X' },
+    { "bind-interfaces", 0, 0, 'z' },
+    { "read-ethers", 0, 0, 'Z' },
+    { "alias", 1, 0, 'V' },
+    { "dhcp-vendorclass", 1, 0, 'U' },
+    { "dhcp-userclass", 1, 0, 'j' },
+    { "dhcp-ignore", 1, 0, 'J' },
+    { "edns-packet-max", 1, 0, 'P' },
+    { "keep-in-foreground", 0, 0, 'k' },
+    { "dhcp-authoritative", 0, 0, 'K' },
+    { "srv-host", 1, 0, 'W' },
+    { "localise-queries", 0, 0, 'y' },
+    { "txt-record", 1, 0, 'Y' },
+    { "enable-dbus", 0, 0, '1' },
+    { "bootp-dynamic", 2, 0, '3' },
+    { "dhcp-mac", 1, 0, '4' },
+    { "no-ping", 0, 0, '5' },
+    { "dhcp-script", 1, 0, '6' },
+    { "conf-dir", 1, 0, '7' },
+    { "log-facility", 1, 0 ,'8' },
+    { "leasefile-ro", 0, 0, '9' },
+    { "dns-forward-max", 1, 0, '0' },
+    { "clear-on-reload", 0, 0, LOPT_RELOAD },
+    { "dhcp-ignore-names", 2, 0, LOPT_NO_NAMES },
+    { "enable-tftp", 0, 0, LOPT_TFTP },
+    { "tftp-secure", 0, 0, LOPT_SECURE },
+    { "tftp-unique-root", 0, 0, LOPT_APREF },
+    { "tftp-root", 1, 0, LOPT_PREFIX },
+    { "tftp-max", 1, 0, LOPT_TFTP_MAX },
+    { "ptr-record", 1, 0, LOPT_PTR },
+    { "naptr-record", 1, 0, LOPT_NAPTR },
+    { "bridge-interface", 1, 0 , LOPT_BRIDGE },
+    { "dhcp-option-force", 1, 0, LOPT_FORCE },
+    { "tftp-no-blocksize", 0, 0, LOPT_NOBLOCK },
+    { "log-dhcp", 0, 0, LOPT_LOG_OPTS },
+    { "log-async", 2, 0, LOPT_MAX_LOGS },
+    { "dhcp-circuitid", 1, 0, LOPT_CIRCUIT },
+    { "dhcp-remoteid", 1, 0, LOPT_REMOTE },
+    { "dhcp-subscrid", 1, 0, LOPT_SUBSCR },
+    { "interface-name", 1, 0, LOPT_INTNAME },
+    { "dhcp-hostsfile", 1, 0, LOPT_DHCP_HOST },
+    { "dhcp-optsfile", 1, 0, LOPT_DHCP_OPTS },
+    { "dhcp-no-override", 0, 0, LOPT_OVERRIDE },
+    { "tftp-port-range", 1, 0, LOPT_TFTPPORTS },
+    { "stop-dns-rebind", 0, 0, LOPT_REBIND },
+    { "all-servers", 0, 0, LOPT_NOLAST }, 
+    { "dhcp-match", 1, 0, LOPT_MATCH }, 
+    { "dhcp-broadcast", 1, 0, LOPT_BROADCAST },
+    { "neg-ttl", 1, 0, LOPT_NEGTTL },
+    { "dhcp-alternate-port", 2, 0, LOPT_ALTPORT },
+    { "dhcp-scriptuser", 1, 0, LOPT_SCRIPTUSR },
+    { "min-port", 1, 0, LOPT_MINPORT },
+    { "dhcp-fqdn", 0, 0, LOPT_DHCP_FQDN },
+    { "cname", 1, 0, LOPT_CNAME },
+    { "pxe-prompt", 1, 0, LOPT_PXE_PROMT },
+    { "pxe-service", 1, 0, LOPT_PXE_SERV },
+    { "test", 0, 0, LOPT_TEST },
+    { NULL, 0, 0, 0 }
+  };
+
+/* These must have more the one '1' bit */
+#define ARG_DUP       3
+#define ARG_ONE       5
+#define ARG_USED_CL   7
+#define ARG_USED_FILE 9
+
+static struct {
+  int opt;
+  unsigned int rept;
+  char * const flagdesc;
+  char * const desc;
+  char * const arg;
+} usage[] = {
+  { 'a', ARG_DUP, "ipaddr",  gettext_noop("Specify local address(es) to listen on."), NULL },
+  { 'A', ARG_DUP, "/domain/ipaddr", gettext_noop("Return ipaddr for all hosts in specified domains."), NULL },
+  { 'b', OPT_BOGUSPRIV, NULL, gettext_noop("Fake reverse lookups for RFC1918 private address ranges."), NULL },
+  { 'B', ARG_DUP, "ipaddr", gettext_noop("Treat ipaddr as NXDOMAIN (defeats Verisign wildcard)."), NULL }, 
+  { 'c', ARG_ONE, "cachesize", gettext_noop("Specify the size of the cache in entries (defaults to %s)."), "$" },
+  { 'C', ARG_DUP, "path", gettext_noop("Specify configuration file (defaults to %s)."), CONFFILE },
+  { 'd', OPT_DEBUG, NULL, gettext_noop("Do NOT fork into the background: run in debug mode."), NULL },
+  { 'D', OPT_NODOTS_LOCAL, NULL, gettext_noop("Do NOT forward queries with no domain part."), NULL }, 
+  { 'e', OPT_SELFMX, NULL, gettext_noop("Return self-pointing MX records for local hosts."), NULL },
+  { 'E', OPT_EXPAND, NULL, gettext_noop("Expand simple names in /etc/hosts with domain-suffix."), NULL },
+  { 'f', OPT_FILTER, NULL, gettext_noop("Don't forward spurious DNS requests from Windows hosts."), NULL },
+  { 'F', ARG_DUP, "ipaddr,ipaddr,time", gettext_noop("Enable DHCP in the range given with lease duration."), NULL },
+  { 'g', ARG_ONE, "groupname", gettext_noop("Change to this group after startup (defaults to %s)."), CHGRP },
+  { 'G', ARG_DUP, "<hostspec>", gettext_noop("Set address or hostname for a specified machine."), NULL },
+  { LOPT_DHCP_HOST, ARG_ONE, "<filename>", gettext_noop("Read DHCP host specs from file"), NULL },
+  { LOPT_DHCP_OPTS, ARG_ONE, "<filename>", gettext_noop("Read DHCP option specs from file"), NULL },
+  { 'h', OPT_NO_HOSTS, NULL, gettext_noop("Do NOT load %s file."), HOSTSFILE },
+  { 'H', ARG_DUP, "path", gettext_noop("Specify a hosts file to be read in addition to %s."), HOSTSFILE },
+  { 'i', ARG_DUP, "interface", gettext_noop("Specify interface(s) to listen on."), NULL },
+  { 'I', ARG_DUP, "int", gettext_noop("Specify interface(s) NOT to listen on.") , NULL },
+  { 'j', ARG_DUP, "<tag>,<class>", gettext_noop("Map DHCP user class to tag."), NULL },
+  { LOPT_CIRCUIT, ARG_DUP, "<tag>,<circuit>", gettext_noop("Map RFC3046 circuit-id to tag."), NULL },
+  { LOPT_REMOTE, ARG_DUP, "<tag>,<remote>", gettext_noop("Map RFC3046 remote-id to tag."), NULL },
+  { LOPT_SUBSCR, ARG_DUP, "<tag>,<remote>", gettext_noop("Map RFC3993 subscriber-id to tag."), NULL },
+  { 'J', ARG_DUP, "=<id>[,<id>]", gettext_noop("Don't do DHCP for hosts with tag set."), NULL },
+  { LOPT_BROADCAST, ARG_DUP, "=<id>[,<id>]", gettext_noop("Force broadcast replies for hosts with tag set."), NULL }, 
+  { 'k', OPT_NO_FORK, NULL, gettext_noop("Do NOT fork into the background, do NOT run in debug mode."), NULL },
+  { 'K', OPT_AUTHORITATIVE, NULL, gettext_noop("Assume we are the only DHCP server on the local network."), NULL },
+  { 'l', ARG_ONE, "path", gettext_noop("Specify where to store DHCP leases (defaults to %s)."), LEASEFILE },
+  { 'L', OPT_LOCALMX, NULL, gettext_noop("Return MX records for local hosts."), NULL },
+  { 'm', ARG_DUP, "host_name,target,pref", gettext_noop("Specify an MX record."), NULL },
+  { 'M', ARG_DUP, "<bootp opts>", gettext_noop("Specify BOOTP options to DHCP server."), NULL },
+  { 'n', OPT_NO_POLL, NULL, gettext_noop("Do NOT poll %s file, reload only on SIGHUP."), RESOLVFILE }, 
+  { 'N', OPT_NO_NEG, NULL, gettext_noop("Do NOT cache failed search results."), NULL },
+  { 'o', OPT_ORDER, NULL, gettext_noop("Use nameservers strictly in the order given in %s."), RESOLVFILE },
+  { 'O', ARG_DUP, "<optspec>", gettext_noop("Specify options to be sent to DHCP clients."), NULL },
+  { LOPT_FORCE, ARG_DUP, "<optspec>", gettext_noop("DHCP option sent even if the client does not request it."), NULL},
+  { 'p', ARG_ONE, "number", gettext_noop("Specify port to listen for DNS requests on (defaults to 53)."), NULL },
+  { 'P', ARG_ONE, "<size>", gettext_noop("Maximum supported UDP packet size for EDNS.0 (defaults to %s)."), "*" },
+  { 'q', OPT_LOG, NULL, gettext_noop("Log DNS queries."), NULL },
+  { 'Q', ARG_ONE, "number", gettext_noop("Force the originating port for upstream DNS queries."), NULL },
+  { 'R', OPT_NO_RESOLV, NULL, gettext_noop("Do NOT read resolv.conf."), NULL },
+  { 'r', ARG_DUP, "path", gettext_noop("Specify path to resolv.conf (defaults to %s)."), RESOLVFILE }, 
+  { 'S', ARG_DUP, "/domain/ipaddr", gettext_noop("Specify address(es) of upstream servers with optional domains."), NULL },
+  { LOPT_LOCAL, ARG_DUP, "/domain/", gettext_noop("Never forward queries to specified domains."), NULL },
+  { 's', ARG_DUP, "<domain>[,<range>]", gettext_noop("Specify the domain to be assigned in DHCP leases."), NULL },
+  { 't', ARG_ONE, "host_name", gettext_noop("Specify default target in an MX record."), NULL },
+  { 'T', ARG_ONE, "time", gettext_noop("Specify time-to-live in seconds for replies from /etc/hosts."), NULL },
+  { LOPT_NEGTTL, ARG_ONE, "time", gettext_noop("Specify time-to-live in seconds for negative caching."), NULL },
+  { 'u', ARG_ONE, "username", gettext_noop("Change to this user after startup. (defaults to %s)."), CHUSER }, 
+  { 'U', ARG_DUP, "<id>,<class>", gettext_noop("Map DHCP vendor class to tag."), NULL },
+  { 'v', 0, NULL, gettext_noop("Display dnsmasq version and copyright information."), NULL },
+  { 'V', ARG_DUP, "addr,addr,mask", gettext_noop("Translate IPv4 addresses from upstream servers."), NULL },
+  { 'W', ARG_DUP, "name,target,...", gettext_noop("Specify a SRV record."), NULL },
+  { 'w', 0, NULL, gettext_noop("Display this message. Use --help dhcp for known DHCP options."), NULL },
+  { 'x', ARG_ONE, "path", gettext_noop("Specify path of PID file (defaults to %s)."), RUNFILE },
+  { 'X', ARG_ONE, "number", gettext_noop("Specify maximum number of DHCP leases (defaults to %s)."), "&" },
+  { 'y', OPT_LOCALISE, NULL, gettext_noop("Answer DNS queries based on the interface a query was sent to."), NULL },
+  { 'Y', ARG_DUP, "name,txt....", gettext_noop("Specify TXT DNS record."), NULL },
+  { LOPT_PTR, ARG_DUP, "name,target", gettext_noop("Specify PTR DNS record."), NULL },
+  { LOPT_INTNAME, ARG_DUP, "name,interface", gettext_noop("Give DNS name to IPv4 address of interface."), NULL },
+  { 'z', OPT_NOWILD, NULL, gettext_noop("Bind only to interfaces in use."), NULL },
+  { 'Z', OPT_ETHERS, NULL, gettext_noop("Read DHCP static host information from %s."), ETHERSFILE },
+  { '1', OPT_DBUS, NULL, gettext_noop("Enable the DBus interface for setting upstream servers, etc."), NULL },
+  { '2', ARG_DUP, "interface", gettext_noop("Do not provide DHCP on this interface, only provide DNS."), NULL },
+  { '3', ARG_DUP, "[=<id>[,<id>]]", gettext_noop("Enable dynamic address allocation for bootp."), NULL },
+  { '4', ARG_DUP, "<id>,<mac address>", gettext_noop("Map MAC address (with wildcards) to option set."), NULL },
+  { LOPT_BRIDGE, ARG_DUP, "iface,alias,..", gettext_noop("Treat DHCP requests on aliases as arriving from interface."), NULL },
+  { '5', OPT_NO_PING, NULL, gettext_noop("Disable ICMP echo address checking in the DHCP server."), NULL },
+  { '6', ARG_ONE, "path", gettext_noop("Script to run on DHCP lease creation and destruction."), NULL },
+  { '7', ARG_DUP, "path", gettext_noop("Read configuration from all the files in this directory."), NULL },
+  { '8', ARG_ONE, "<facilty>|<file>", gettext_noop("Log to this syslog facility or file. (defaults to DAEMON)"), NULL },
+  { '9', OPT_LEASE_RO, NULL, gettext_noop("Do not use leasefile."), NULL },
+  { '0', ARG_ONE, "<queries>", gettext_noop("Maximum number of concurrent DNS queries. (defaults to %s)"), "!" }, 
+  { LOPT_RELOAD, OPT_RELOAD, NULL, gettext_noop("Clear DNS cache when reloading %s."), RESOLVFILE },
+  { LOPT_NO_NAMES, ARG_DUP, "[=<id>[,<id>]]", gettext_noop("Ignore hostnames provided by DHCP clients."), NULL },
+  { LOPT_OVERRIDE, OPT_NO_OVERRIDE, NULL, gettext_noop("Do NOT reuse filename and server fields for extra DHCP options."), NULL },
+  { LOPT_TFTP, OPT_TFTP, NULL, gettext_noop("Enable integrated read-only TFTP server."), NULL },
+  { LOPT_PREFIX, ARG_ONE, "<directory>", gettext_noop("Export files by TFTP only from the specified subtree."), NULL },
+  { LOPT_APREF, OPT_TFTP_APREF, NULL, gettext_noop("Add client IP address to tftp-root."), NULL },
+  { LOPT_SECURE, OPT_TFTP_SECURE, NULL, gettext_noop("Allow access only to files owned by the user running dnsmasq."), NULL },
+  { LOPT_TFTP_MAX, ARG_ONE, "<connections>", gettext_noop("Maximum number of conncurrent TFTP transfers (defaults to %s)."), "#" },
+  { LOPT_NOBLOCK, OPT_TFTP_NOBLOCK, NULL, gettext_noop("Disable the TFTP blocksize extension."), NULL },
+  { LOPT_TFTPPORTS, ARG_ONE, "<start>,<end>", gettext_noop("Ephemeral port range for use by TFTP transfers."), NULL },
+  { LOPT_LOG_OPTS, OPT_LOG_OPTS, NULL, gettext_noop("Extra logging for DHCP."), NULL },
+  { LOPT_MAX_LOGS, ARG_ONE, "[=<log lines>]", gettext_noop("Enable async. logging; optionally set queue length."), NULL },
+  { LOPT_REBIND, OPT_NO_REBIND, NULL, gettext_noop("Stop DNS rebinding. Filter private IP ranges when resolving."), NULL },
+  { LOPT_NOLAST, OPT_ALL_SERVERS, NULL, gettext_noop("Always perform DNS queries to all servers."), NULL },
+  { LOPT_MATCH, ARG_DUP, "<netid>,<optspec>", gettext_noop("Set tag if client includes matching option in request."), NULL },
+  { LOPT_ALTPORT, ARG_ONE, "[=<ports>]", gettext_noop("Use alternative ports for DHCP."), NULL },
+  { LOPT_SCRIPTUSR, ARG_ONE, "<username>", gettext_noop("Run lease-change script as this user."), NULL },
+  { LOPT_NAPTR, ARG_DUP, "<name>,<naptr>", gettext_noop("Specify NAPTR DNS record."), NULL },
+  { LOPT_MINPORT, ARG_ONE, "<port>", gettext_noop("Specify lowest port available for DNS query transmission."), NULL },
+  { LOPT_DHCP_FQDN, OPT_DHCP_FQDN, NULL, gettext_noop("Use only fully qualified domain names for DHCP clients."), NULL },
+  { LOPT_CNAME, ARG_DUP, "<alias>,<target>", gettext_noop("Specify alias name for LOCAL DNS name."), NULL },
+  { 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 },
+  { 0, 0, NULL, NULL, NULL }
+}; 
+
+#ifdef HAVE_DHCP
+/* makes options which take a list of addresses */
+#define OT_ADDR_LIST 0x80
+/* DHCP-internal options, for logging. not valid in config file */
+#define OT_INTERNAL 0x40
+#define OT_NAME 0x20
+
+static const struct {
+  char *name;
+  unsigned char val, size;
+} opttab[] = {
+  { "netmask", 1, OT_ADDR_LIST },
+  { "time-offset", 2, 4 },
+  { "router", 3, OT_ADDR_LIST  },
+  { "dns-server", 6, OT_ADDR_LIST },
+  { "log-server", 7, OT_ADDR_LIST },
+  { "lpr-server", 9, OT_ADDR_LIST },
+  { "hostname", 12, OT_INTERNAL | OT_NAME },
+  { "boot-file-size", 13, 2 },
+  { "domain-name", 15, OT_NAME },
+  { "swap-server", 16, OT_ADDR_LIST },
+  { "root-path", 17, 0 },
+  { "extension-path", 18, 0 },
+  { "ip-forward-enable", 19, 1 },
+  { "non-local-source-routing", 20, 1 },
+  { "policy-filter", 21, OT_ADDR_LIST },
+  { "max-datagram-reassembly", 22, 2 },
+  { "default-ttl", 23, 1 },
+  { "mtu", 26, 2 },
+  { "all-subnets-local", 27, 1 },
+  { "broadcast", 28, OT_INTERNAL | OT_ADDR_LIST },
+  { "router-discovery", 31, 1 },
+  { "router-solicitation", 32, OT_ADDR_LIST },
+  { "static-route", 33, OT_ADDR_LIST },
+  { "trailer-encapsulation", 34, 1 },
+  { "arp-timeout", 35, 4 },
+  { "ethernet-encap", 36, 1 },
+  { "tcp-ttl", 37, 1 },
+  { "tcp-keepalive", 38, 4 },
+  { "nis-domain", 40, 0 },
+  { "nis-server", 41, OT_ADDR_LIST },
+  { "ntp-server", 42, OT_ADDR_LIST },
+  { "vendor-encap", 43, OT_INTERNAL },
+  { "netbios-ns", 44, OT_ADDR_LIST },
+  { "netbios-dd", 45, OT_ADDR_LIST },
+  { "netbios-nodetype", 46, 1 },
+  { "netbios-scope", 47, 0 },
+  { "x-windows-fs", 48, OT_ADDR_LIST },
+  { "x-windows-dm", 49, OT_ADDR_LIST },
+  { "requested-address", 50, OT_INTERNAL | OT_ADDR_LIST },
+  { "lease-time", 51, OT_INTERNAL },
+  { "option-overload", 52, OT_INTERNAL },
+  { "message-type", 53, OT_INTERNAL, },
+  { "server-identifier", 54, OT_INTERNAL | OT_ADDR_LIST },
+  { "parameter-request", 55, OT_INTERNAL },
+  { "message", 56, OT_INTERNAL },
+  { "max-message-size", 57, OT_INTERNAL },
+  { "T1", 58, OT_INTERNAL },
+  { "T2", 59, OT_INTERNAL },
+  { "vendor-class", 60, 0 },
+  { "client-id", 61,OT_INTERNAL },
+  { "nis+-domain", 64, 0 },
+  { "nis+-server", 65, OT_ADDR_LIST },
+  { "tftp-server", 66, 0 },
+  { "bootfile-name", 67, 0 },
+  { "mobile-ip-home", 68, OT_ADDR_LIST }, 
+  { "smtp-server", 69, OT_ADDR_LIST }, 
+  { "pop3-server", 70, OT_ADDR_LIST }, 
+  { "nntp-server", 71, OT_ADDR_LIST }, 
+  { "irc-server", 74, OT_ADDR_LIST }, 
+  { "user-class", 77, 0 },
+  { "FQDN", 81, OT_INTERNAL },
+  { "agent-id", 82, OT_INTERNAL },
+  { "client-arch", 93, 2 },
+  { "client-interface-id", 94, 0 },
+  { "client-machine-id", 97, 0 },
+  { "subnet-select", 118, OT_INTERNAL },
+  { "domain-search", 119, 0 },
+  { "sip-server", 120, 0 },
+  { "classless-static-route", 121, 0 },
+  { "server-ip-address", 255, OT_ADDR_LIST }, /* special, internal only, sets siaddr */
+  { NULL, 0, 0 }
+};
+
+char *option_string(unsigned char opt, int *is_ip, int *is_name)
+{
+  int i;
+
+  for (i = 0; opttab[i].name; i++)
+    if (opttab[i].val == opt)
+      {
+	if (is_ip)
+	  *is_ip = !!(opttab[i].size & OT_ADDR_LIST);
+	if (is_name)
+	  *is_name = !!(opttab[i].size & OT_NAME);
+	return opttab[i].name;
+      }
+
+  return NULL;
+}
+
+#endif
+
+/* We hide metacharaters in quoted strings by mapping them into the ASCII control
+   character space. Note that the \0, \t \b \r \033 and \n characters are carefully placed in the
+   following sequence so that they map to themselves: it is therefore possible to call
+   unhide_metas repeatedly on string without breaking things.
+   The transformation gets undone by opt_canonicalise, atoi_check and opt_string_alloc, and a 
+   couple of other places. 
+   Note that space is included here so that
+   --dhcp-option=3, string
+   has five characters, whilst
+   --dhcp-option=3," string"
+   has six.
+*/
+
+static const char meta[] = "\000123456 \b\t\n78\r90abcdefABCDE\033F:,.";
+
+static char hide_meta(char c)
+{
+  unsigned int i;
+
+  for (i = 0; i < (sizeof(meta) - 1); i++)
+    if (c == meta[i])
+      return (char)i;
+  
+  return c;
+}
+
+static char unhide_meta(char cr)
+{ 
+  unsigned int c = cr;
+  
+  if (c < (sizeof(meta) - 1))
+    cr = meta[c];
+  
+  return cr;
+}
+
+static void unhide_metas(char *cp)
+{
+  if (cp)
+    for(; *cp; cp++)
+      *cp = unhide_meta(*cp);
+}
+
+static void *opt_malloc(size_t size)
+{
+  void *ret;
+
+  if (mem_recover)
+    {
+      ret = whine_malloc(size);
+      if (!ret)
+	longjmp(mem_jmp, 1);
+    }
+  else
+    ret = safe_malloc(size);
+  
+  return ret;
+}
+
+static char *opt_string_alloc(char *cp)
+{
+  char *ret = NULL;
+  
+  if (cp && strlen(cp) != 0)
+    {
+      ret = opt_malloc(strlen(cp)+1);
+      strcpy(ret, cp); 
+      
+      /* restore hidden metachars */
+      unhide_metas(ret);
+    }
+    
+  return ret;
+}
+
+
+/* find next comma, split string with zero and eliminate spaces.
+   return start of string following comma */
+
+static char *split_chr(char *s, char c)
+{
+  char *comma, *p;
+
+  if (!s || !(comma = strchr(s, c)))
+    return NULL;
+  
+  p = comma;
+  *comma = ' ';
+  
+  for (; isspace((int)*comma); comma++);
+ 
+  for (; (p >= s) && isspace((int)*p); p--)
+    *p = 0;
+    
+  return comma;
+}
+
+static char *split(char *s)
+{
+  return split_chr(s, ',');
+}
+
+static char *canonicalise_opt(char *s)
+{
+  char *ret;
+  int nomem;
+
+  if (!s)
+    return 0;
+
+  unhide_metas(s);
+  if (!(ret = canonicalise(s, &nomem)) && nomem)
+    {
+      if (mem_recover)
+	longjmp(mem_jmp, 1);
+      else
+	die(_("could not get memory"), NULL, EC_NOMEM);
+    }
+
+  return ret;
+}
+
+static int atoi_check(char *a, int *res)
+{
+  char *p;
+
+  if (!a)
+    return 0;
+
+  unhide_metas(a);
+  
+  for (p = a; *p; p++)
+     if (*p < '0' || *p > '9')
+       return 0;
+
+  *res = atoi(a);
+  return 1;
+}
+
+static int atoi_check16(char *a, int *res)
+{
+  if (!(atoi_check(a, res)) ||
+      *res < 0 ||
+      *res > 0xffff)
+    return 0;
+
+  return 1;
+}
+	
+static void add_txt(char *name, char *txt)
+{
+  size_t len = strlen(txt);
+  struct txt_record *r = opt_malloc(sizeof(struct txt_record));
+  
+  r->name = opt_string_alloc(name);
+  r->next = daemon->txt;
+  daemon->txt = r;
+  r->class = C_CHAOS;
+  r->txt = opt_malloc(len+1);
+  r->len = len+1;
+  *(r->txt) = len;
+  memcpy((r->txt)+1, txt, len);
+}
+
+static void do_usage(void)
+{
+  char buff[100];
+  int i, j;
+
+  struct {
+    char handle;
+    int val;
+  } tab[] = {
+    { '$', CACHESIZ },
+    { '*', EDNS_PKTSZ },
+    { '&', MAXLEASES },
+    { '!', FTABSIZ },
+    { '#', TFTP_MAX_CONNECTIONS },
+    { '\0', 0 }
+  };
+
+  printf(_("Usage: dnsmasq [options]\n\n"));
+#ifndef HAVE_GETOPT_LONG
+  printf(_("Use short options only on the command line.\n"));
+#endif
+  printf(_("Valid options are:\n"));
+  
+  for (i = 0; usage[i].opt != 0; i++)
+    {
+      char *desc = usage[i].flagdesc; 
+      char *eq = "=";
+      
+      if (!desc || *desc == '[')
+	eq = "";
+      
+      if (!desc)
+	desc = "";
+
+      for ( j = 0; opts[j].name; j++)
+	if (opts[j].val == usage[i].opt)
+	  break;
+      if (usage[i].opt < 256)
+	sprintf(buff, "-%c, ", usage[i].opt);
+      else
+	sprintf(buff, "    ");
+      
+      sprintf(buff+4, "--%s%s%s", opts[j].name, eq, desc);
+      printf("%-36.36s", buff);
+	     
+      if (usage[i].arg)
+	{
+	  strcpy(buff, usage[i].arg);
+	  for (j = 0; tab[j].handle; j++)
+	    if (tab[j].handle == *(usage[i].arg))
+	      sprintf(buff, "%d", tab[j].val);
+	}
+      printf(_(usage[i].desc), buff);
+      printf("\n");
+    }
+}
+
+#ifdef HAVE_DHCP
+static void display_opts(void)
+{
+  int i;
+  
+  printf(_("Known DHCP options:\n"));
+  
+  for (i = 0; opttab[i].name; i++)
+    if (!(opttab[i].size & OT_INTERNAL))
+      printf("%3d %s\n", opttab[i].val, opttab[i].name);
+}
+
+/* This is too insanely large to keep in-line in the switch */
+static char *parse_dhcp_opt(char *arg, int flags)
+{
+  struct dhcp_opt *new = opt_malloc(sizeof(struct dhcp_opt));
+  char lenchar = 0, *cp;
+  int i, addrs, digs, is_addr, is_hex, is_dec, is_string, dots;
+  char *comma = NULL, *problem = NULL;
+  struct dhcp_netid *np = NULL;
+  unsigned char opt_len = 0;
+
+  new->len = 0;
+  new->flags = flags;
+  new->netid = NULL;
+  new->val = NULL;
+  new->opt = 0;
+  
+  while (arg)
+    {
+      comma = split(arg);      
+
+      for (cp = arg; *cp; cp++)
+	if (*cp < '0' || *cp > '9')
+	  break;
+      
+      if (!*cp)
+	{
+	  new->opt = atoi(arg);
+	  opt_len = 0;
+	  break;
+	}
+      
+      if (strstr(arg, "option:") == arg)
+	{
+	  for (i = 0; opttab[i].name; i++)
+	    if (!(opttab[i].size & OT_INTERNAL) &&
+		strcasecmp(opttab[i].name, arg+7) == 0)
+	      {
+		new->opt = opttab[i].val;
+		opt_len = opttab[i].size;
+		break;
+	      }
+	  /* option:<optname> must follow tag and vendor string. */
+	  break;
+	}
+      else if (strstr(arg, "vendor:") == arg)
+	{
+	  new->u.vendor_class = (unsigned char *)opt_string_alloc(arg+7);
+	  new->flags |= DHOPT_VENDOR;
+	}
+      else if (strstr(arg, "encap:") == arg)
+	{
+	  new->u.encap = atoi(arg+6);
+	  new->flags |= DHOPT_ENCAPSULATE;
+	}
+      else
+	{
+	  new->netid = opt_malloc(sizeof (struct dhcp_netid));
+	  /* allow optional "net:" for consistency */
+	  if (strstr(arg, "net:") == arg)
+	    new->netid->net = opt_string_alloc(arg+4);
+	  else
+	    new->netid->net = opt_string_alloc(arg);
+	  new->netid->next = np;
+	  np = new->netid;
+	}
+      
+      arg = comma; 
+    }
+  
+  if (new->opt == 0)
+    problem = _("bad dhcp-option");
+  else if (comma)
+    {
+      /* characterise the value */
+      char c;
+      is_addr = is_hex = is_dec = is_string = 1;
+      addrs = digs = 1;
+      dots = 0;
+      for (cp = comma; (c = *cp); cp++)
+	if (c == ',')
+	  {
+	    addrs++;
+	    is_dec = is_hex = 0;
+	  }
+	else if (c == ':')
+	  {
+	    digs++;
+	    is_dec = is_addr = 0;
+	  }
+	else if (c == '/') 
+	  {
+	    is_dec = is_hex = 0;
+	    if (cp == comma) /* leading / means a pathname */
+	      is_addr = 0;
+	  } 
+	else if (c == '.')	
+	  {
+	    is_dec = is_hex = 0;
+	    dots++;
+	  }
+	else if (c == '-')
+	  is_hex = is_addr = 0;
+	else if (c == ' ')
+	  is_dec = is_hex = 0;
+	else if (!(c >='0' && c <= '9'))
+	  {
+	    is_addr = 0;
+	    if (cp[1] == 0 && is_dec &&
+		(c == 'b' || c == 's' || c == 'i'))
+	      {
+		lenchar = c;
+		*cp = 0;
+	      }
+	    else
+	      is_dec = 0;
+	    if (!((c >='A' && c <= 'F') ||
+		  (c >='a' && c <= 'f') || 
+		  (c == '*' && (flags & DHOPT_MATCH))))
+	      is_hex = 0;
+	  }
+     
+      /* We know that some options take addresses */
+
+      if (opt_len & OT_ADDR_LIST)
+	{
+	  is_string = is_dec = is_hex = 0;
+	  if (!is_addr || dots == 0)
+	    problem = _("bad IP address");
+	}
+	  
+      if (is_hex && digs > 1)
+	{
+	  new->len = digs;
+	  new->val = opt_malloc(new->len);
+	  parse_hex(comma, new->val, digs, (flags & DHOPT_MATCH) ? &new->u.wildcard_mask : NULL, NULL);
+	  new->flags |= DHOPT_HEX;
+	}
+      else if (is_dec)
+	{
+	  int i, val = atoi(comma);
+	  /* assume numeric arg is 1 byte except for
+	     options where it is known otherwise.
+	     For vendor class option, we have to hack. */
+	  if (opt_len != 0)
+	    new->len = opt_len;
+	  else if (val & 0xffff0000)
+	    new->len = 4;
+	  else if (val & 0xff00)
+	    new->len = 2;
+	  else
+	    new->len = 1;
+
+	  if (lenchar == 'b')
+	    new->len = 1;
+	  else if (lenchar == 's')
+	    new->len = 2;
+	  else if (lenchar == 'i')
+	    new->len = 4;
+	  
+	  new->val = opt_malloc(new->len);
+	  for (i=0; i<new->len; i++)
+	    new->val[i] = val>>((new->len - i - 1)*8);
+	}
+      else if (is_addr)	
+	{
+	  struct in_addr in;
+	  unsigned char *op;
+	  char *slash;
+	  /* max length of address/subnet descriptor is five bytes,
+	     add one for the option 120 enc byte too */
+	  new->val = op = opt_malloc((5 * addrs) + 1);
+	  new->flags |= DHOPT_ADDR;
+
+	  if (!(new->flags & DHOPT_ENCAPSULATE) && new->opt == 120)
+	    {
+	      *(op++) = 1; /* RFC 3361 "enc byte" */
+	      new->flags &= ~DHOPT_ADDR;
+	    }
+	  while (addrs--) 
+	    {
+	      cp = comma;
+	      comma = split(cp);
+	      slash = split_chr(cp, '/');
+	      in.s_addr = inet_addr(cp);
+	      if (!slash)
+		{
+		  memcpy(op, &in, INADDRSZ);
+		  op += INADDRSZ;
+		}
+	      else
+		{
+		  unsigned char *p = (unsigned char *)&in;
+		  int netsize = atoi(slash);
+		  *op++ = netsize;
+		  if (netsize > 0)
+		    *op++ = *p++;
+		  if (netsize > 8)
+		    *op++ = *p++;
+		  if (netsize > 16)
+		    *op++ = *p++;
+		  if (netsize > 24)
+		    *op++ = *p++;
+		  new->flags &= ~DHOPT_ADDR; /* cannot re-write descriptor format */
+		} 
+	    }
+	  new->len = op - new->val;
+	}
+      else if (is_string)
+	{
+	  /* text arg */
+	  if ((new->opt == 119 || new->opt == 120) && !(new->flags & DHOPT_ENCAPSULATE))
+	    {
+	      /* dns search, RFC 3397, or SIP, RFC 3361 */
+	      unsigned char *q, *r, *tail;
+	      unsigned char *p, *m = NULL, *newp;
+	      size_t newlen, len = 0;
+	      int header_size = (new->opt == 119) ? 0 : 1;
+	      
+	      arg = comma;
+	      comma = split(arg);
+	      
+	      while (arg && *arg)
+		{
+		  char *dom;
+		  if (!(dom = arg = canonicalise_opt(arg)))
+		    {
+		      problem = _("bad domain in dhcp-option");
+		      break;
+		    }
+		  
+		  newp = opt_malloc(len + strlen(arg) + 2 + header_size);
+		  if (m)
+		    memcpy(newp, m, header_size + len);
+		  m = newp;
+		  p = m + header_size;
+		  q = p + len;
+		  
+		  /* add string on the end in RFC1035 format */
+		  while (*arg) 
+		    {
+		      unsigned char *cp = q++;
+		      int j;
+		      for (j = 0; *arg && (*arg != '.'); arg++, j++)
+			*q++ = *arg;
+		      *cp = j;
+		      if (*arg)
+			arg++;
+		    }
+		  *q++ = 0;
+		  free(dom);
+
+		  /* Now tail-compress using earlier names. */
+		  newlen = q - p;
+		  for (tail = p + len; *tail; tail += (*tail) + 1)
+		    for (r = p; r - p < (int)len; r += (*r) + 1)
+		      if (strcmp((char *)r, (char *)tail) == 0)
+			{
+			  PUTSHORT((r - p) | 0xc000, tail); 
+			  newlen = tail - p;
+			  goto end;
+			}
+		end:
+		  len = newlen;
+		  
+		  arg = comma;
+		  comma = split(arg);
+		}
+      
+	      /* RFC 3361, enc byte is zero for names */
+	      if (new->opt == 120)
+		m[0] = 0;
+	      new->len = (int) len + header_size;
+	      new->val = m;
+	    }
+	  else
+	    {
+	      new->len = strlen(comma);
+	      /* keep terminating zero on string */
+	      new->val = (unsigned char *)opt_string_alloc(comma);
+	      new->flags |= DHOPT_STRING;
+	    }
+	}
+    }
+
+  if ((new->len > 255) || (new->len > 253 && (new->flags & (DHOPT_VENDOR | DHOPT_ENCAPSULATE))))
+    problem = _("dhcp-option too long");
+  
+  if (!problem)
+    {
+      if (flags == DHOPT_MATCH)
+	{
+	  if ((new->flags & (DHOPT_ENCAPSULATE | DHOPT_VENDOR)) ||
+	      !new->netid ||
+	      new->netid->next)
+	    problem = _("illegal dhcp-match");
+	  else
+	    {
+	      new->next = daemon->dhcp_match;
+	      daemon->dhcp_match = new;
+	    }
+	}
+      else     
+	{
+	  new->next = daemon->dhcp_opts;
+	  daemon->dhcp_opts = new;
+	}
+    }
+
+  return problem;
+}
+
+#endif
+
+static char *one_opt(int option, char *arg, char *gen_prob, int nest)
+{      
+  int i;
+  char *comma, *problem = NULL;;
+
+  if (option == '?')
+    return gen_prob;
+  
+  for (i=0; usage[i].opt != 0; i++)
+    if (usage[i].opt == option)
+      {
+	 int rept = usage[i].rept;
+	 
+	 if (nest == 0)
+	   {
+	     /* command line */
+	     if (rept == ARG_USED_CL)
+	       return _("illegal repeated flag");
+	     if (rept == ARG_ONE)
+	       usage[i].rept = ARG_USED_CL;
+	   }
+	 else
+	   {
+	     /* allow file to override command line */
+	     if (rept == ARG_USED_FILE)
+	       return _("illegal repeated keyword");
+	     if (rept == ARG_USED_CL || rept == ARG_ONE)
+	       usage[i].rept = ARG_USED_FILE;
+	   }
+
+	 if (rept != ARG_DUP && rept != ARG_ONE && rept != ARG_USED_CL) 
+	   {
+	     daemon->options |= rept;
+	     return NULL;
+	   }
+       
+	 break;
+      }
+  
+  switch (option)
+    { 
+    case 'C': /* --conf-file */
+      {
+	char *file = opt_string_alloc(arg);
+	if (file)
+	  {
+	    one_file(file, nest, 0);
+	    free(file);
+	  }
+	break;
+      }
+
+    case '7': /* --conf-dir */	      
+      {
+	DIR *dir_stream;
+	struct dirent *ent;
+	char *directory, *path;
+	struct list {
+	  char *suffix;
+	  struct list *next;
+	} *ignore_suffix = NULL, *li;
+	
+	comma = split(arg);
+	if (!(directory = opt_string_alloc(arg)))
+	  break;
+	
+	for (arg = comma; arg; arg = comma) 
+	  {
+	    comma = split(arg);
+	    li = opt_malloc(sizeof(struct list));
+	    li->next = ignore_suffix;
+	    ignore_suffix = li;
+	    /* Have to copy: buffer is overwritten */
+	    li->suffix = opt_string_alloc(arg);
+	  };
+	
+	if (!(dir_stream = opendir(directory)))
+	  die(_("cannot access directory %s: %s"), directory, EC_FILE);
+	
+	while ((ent = readdir(dir_stream)))
+	  {
+	    size_t len = strlen(ent->d_name);
+	    struct stat buf;
+	    
+	    /* ignore emacs backups and dotfiles */
+	    if (len == 0 ||
+		ent->d_name[len - 1] == '~' ||
+		(ent->d_name[0] == '#' && ent->d_name[len - 1] == '#') ||
+		ent->d_name[0] == '.')
+	      continue;
+
+	    for (li = ignore_suffix; li; li = li->next)
+	      {
+		/* check for proscribed suffices */
+		size_t ls = strlen(li->suffix);
+		if (len > ls &&
+		    strcmp(li->suffix, &ent->d_name[len - ls]) == 0)
+		  break;
+	      }
+	    if (li)
+	      continue;
+	    
+	    path = opt_malloc(strlen(directory) + len + 2);
+	    strcpy(path, directory);
+	    strcat(path, "/");
+	    strcat(path, ent->d_name);
+
+	    if (stat(path, &buf) == -1)
+	      die(_("cannot access %s: %s"), path, EC_FILE);
+	    /* only reg files allowed. */
+	    if (!S_ISREG(buf.st_mode))
+	      continue;
+	    
+	    /* dir is one level, so files must be readable */
+	    one_file(path, nest + 1, 0);
+	    free(path);
+	  }
+     
+	closedir(dir_stream);
+	free(directory);
+	for(; ignore_suffix; ignore_suffix = li)
+	  {
+	    li = ignore_suffix->next;
+	    free(ignore_suffix->suffix);
+	    free(ignore_suffix);
+	  }
+	      
+	break;
+      }
+
+    case '8': /* --log-facility */
+      /* may be a filename */
+      if (strchr(arg, '/'))
+	daemon->log_file = opt_string_alloc(arg);
+      else
+	{	  
+	  for (i = 0; facilitynames[i].c_name; i++)
+	    if (hostname_isequal((char *)facilitynames[i].c_name, arg))
+	      break;
+	  
+	  if (facilitynames[i].c_name)
+	    daemon->log_fac = facilitynames[i].c_val;
+	  else
+	    problem = "bad log facility";
+	}
+      break;
+      
+    case 'x': /* --pid-file */
+      daemon->runfile = opt_string_alloc(arg);
+      break;
+
+    case LOPT_DHCP_HOST: /* --dhcp-hostfile */
+      if (daemon->dhcp_hosts_file)
+	problem = _("only one dhcp-hostsfile allowed");
+      else
+	daemon->dhcp_hosts_file = opt_string_alloc(arg);
+      break;
+     
+    case LOPT_DHCP_OPTS: /* --dhcp-optsfile */
+      if (daemon->dhcp_opts_file)
+	problem = _("only one dhcp-optsfile allowed");
+      else
+	daemon->dhcp_opts_file = opt_string_alloc(arg);
+      break; 
+      
+    case 'r': /* --resolv-file */
+      {
+	char *name = opt_string_alloc(arg);
+	struct resolvc *new, *list = daemon->resolv_files;
+	
+	if (list && list->is_default)
+	  {
+	    /* replace default resolv file - possibly with nothing */
+	    if (name)
+	      {
+		list->is_default = 0;
+		list->name = name;
+	      }
+	    else
+	      list = NULL;
+	  }
+	else if (name)
+	  {
+	    new = opt_malloc(sizeof(struct resolvc));
+	    new->next = list;
+	    new->name = name;
+	    new->is_default = 0;
+	    new->mtime = 0;
+	    new->logged = 0;
+	    list = new;
+	  }
+	daemon->resolv_files = list;
+	break;
+      }
+      
+    case 'm':  /* --mx-host */
+      {
+	int pref = 1;
+	struct mx_srv_record *new;
+	char *name, *target = NULL;
+
+	if ((comma = split(arg)))
+	  {
+	    char *prefstr;
+	    if ((prefstr = split(comma)) && !atoi_check16(prefstr, &pref))
+	      problem = _("bad MX preference");
+	  }
+	
+	if (!(name = canonicalise_opt(arg)) || 
+	    (comma && !(target = canonicalise_opt(comma))))
+	  problem = _("bad MX name");
+	
+	new = opt_malloc(sizeof(struct mx_srv_record));
+	new->next = daemon->mxnames;
+	daemon->mxnames = new;
+	new->issrv = 0;
+	new->name = name;
+	new->target = target; /* may be NULL */
+	new->weight = pref;
+	break;
+      }
+      
+    case 't': /*  --mx-target */
+      if (!(daemon->mxtarget = canonicalise_opt(arg)))
+	problem = _("bad MX target");
+      break;
+
+#ifdef HAVE_DHCP      
+    case 'l':  /* --dhcp-leasefile */
+      daemon->lease_file = opt_string_alloc(arg);
+      break;
+      
+    case '6': /* --dhcp-script */
+#  if defined(NO_FORK)
+      problem = _("cannot run scripts under uClinux");
+#  elif !defined(HAVE_SCRIPT)
+      problem = _("recompile with HAVE_SCRIPT defined to enable lease-change scripts");
+#  else
+      daemon->lease_change_command = opt_string_alloc(arg);
+#  endif
+      break;
+#endif
+
+    case 'H': /* --addn-hosts */
+      {
+	struct hostsfile *new = opt_malloc(sizeof(struct hostsfile));
+	static int hosts_index = 1;
+	new->fname = opt_string_alloc(arg);
+	new->index = hosts_index++;
+	new->flags = 0;
+	new->next = daemon->addn_hosts;
+	daemon->addn_hosts = new;
+	break;
+      }
+      
+    case 's': /* --domain */
+      if (strcmp (arg, "#") == 0)
+	daemon->options |= OPT_RESOLV_DOMAIN;
+      else
+	{
+	  char *d;
+	  comma = split(arg);
+	  if (!(d = canonicalise_opt(arg)))
+	    option = '?';
+	  else
+	    {
+	      if (comma)
+		{
+		  struct cond_domain *new = safe_malloc(sizeof(struct cond_domain));
+		  unhide_metas(comma);
+		  if ((arg = split_chr(comma, '/')))
+		    {
+		      int mask;
+		      if ((new->start.s_addr = inet_addr(comma)) == (in_addr_t)-1 ||
+			  !atoi_check(arg, &mask))
+			option = '?';
+		      else
+			{
+			  mask = (1 << (32 - mask)) - 1;
+			  new->start.s_addr = ntohl(htonl(new->start.s_addr) & ~mask);
+			  new->end.s_addr = new->start.s_addr | htonl(mask);
+			}
+		    }
+		  else if ((arg = split(comma)))
+		    {
+		      if ((new->start.s_addr = inet_addr(comma)) == (in_addr_t)-1 ||
+			  (new->end.s_addr = inet_addr(arg)) == (in_addr_t)-1)
+			option = '?';
+		    }
+		  else if ((new->start.s_addr = new->end.s_addr = inet_addr(comma)) == (in_addr_t)-1)
+		    option = '?';
+
+		  new->domain = d;
+		  new->next = daemon->cond_domain;
+		  daemon->cond_domain = new;
+		}
+	      else
+		daemon->domain_suffix = d;
+	    }
+	}
+      break;
+      
+    case 'u':  /* --user */
+      daemon->username = opt_string_alloc(arg);
+      break;
+      
+    case 'g':  /* --group */
+      daemon->groupname = opt_string_alloc(arg);
+      daemon->group_set = 1;
+      break;
+
+#ifdef HAVE_DHCP
+    case LOPT_SCRIPTUSR: /* --scriptuser */
+      daemon->scriptuser = opt_string_alloc(arg);
+      break;
+#endif
+      
+    case 'i':  /* --interface */
+      do {
+	struct iname *new = opt_malloc(sizeof(struct iname));
+	comma = split(arg);
+	new->next = daemon->if_names;
+	daemon->if_names = new;
+	/* new->name may be NULL if someone does
+	   "interface=" to disable all interfaces except loop. */
+	new->name = opt_string_alloc(arg);
+	new->isloop = new->used = 0;
+	arg = comma;
+      } while (arg);
+      break;
+      
+    case 'I':  /* --except-interface */
+    case '2':  /* --no-dhcp-interface */
+      do {
+	struct iname *new = opt_malloc(sizeof(struct iname));
+	comma = split(arg);
+	new->name = opt_string_alloc(arg);
+	if (option == 'I')
+	  {
+	    new->next = daemon->if_except;
+	    daemon->if_except = new;
+	  }
+	else
+	  {
+	    new->next = daemon->dhcp_except;
+	    daemon->dhcp_except = new;
+	  }
+	arg = comma;
+      } while (arg);
+      break;
+      
+    case 'B':  /* --bogus-nxdomain */
+      {
+	struct in_addr addr;
+	unhide_metas(arg);
+	if (arg && (addr.s_addr = inet_addr(arg)) != (in_addr_t)-1)
+	  {
+	    struct bogus_addr *baddr = opt_malloc(sizeof(struct bogus_addr));
+	    baddr->next = daemon->bogus_addr;
+	    daemon->bogus_addr = baddr;
+	    baddr->addr = addr;
+	  }
+	else
+	  option = '?'; /* error */
+	break;	
+      }
+      
+    case 'a':  /* --listen-address */
+      do {
+	struct iname *new = opt_malloc(sizeof(struct iname));
+	comma = split(arg);
+	unhide_metas(arg);
+	new->next = daemon->if_addrs;
+	if (arg && (new->addr.in.sin_addr.s_addr = inet_addr(arg)) != (in_addr_t)-1)
+	  {
+	    new->addr.sa.sa_family = AF_INET;
+#ifdef HAVE_SOCKADDR_SA_LEN
+	    new->addr.in.sin_len = sizeof(new->addr.in);
+#endif
+	  }
+#ifdef HAVE_IPV6
+	else if (arg && inet_pton(AF_INET6, arg, &new->addr.in6.sin6_addr) > 0)
+	  {
+	    new->addr.sa.sa_family = AF_INET6;
+	    new->addr.in6.sin6_flowinfo = 0;
+	    new->addr.in6.sin6_scope_id = 0;
+#ifdef HAVE_SOCKADDR_SA_LEN
+	    new->addr.in6.sin6_len = sizeof(new->addr.in6);
+#endif
+	  }
+#endif
+	else
+	  {
+	    option = '?'; /* error */
+	    break;
+	  }
+	
+	daemon->if_addrs = new;
+	arg = comma;
+      } while (arg);
+      break;
+      
+    case 'S':        /*  --server */
+    case LOPT_LOCAL: /*  --local */
+    case 'A':        /*  --address */
+      {
+	struct server *serv, *newlist = NULL;
+	
+	unhide_metas(arg);
+	
+	if (arg && *arg == '/')
+	  {
+	    char *end;
+	    arg++;
+	    while ((end = split_chr(arg, '/')))
+	      {
+		char *domain = NULL;
+		/* # matches everything and becomes a zero length domain string */
+		if (strcmp(arg, "#") == 0)
+		  domain = "";
+		else if (strlen (arg) != 0 && !(domain = canonicalise_opt(arg)))
+		  option = '?';
+		serv = opt_malloc(sizeof(struct server));
+		memset(serv, 0, sizeof(struct server));
+		serv->next = newlist;
+		newlist = serv;
+		serv->domain = domain;
+		serv->flags = domain ? SERV_HAS_DOMAIN : SERV_FOR_NODOTS;
+		arg = end;
+	      }
+	    if (!newlist)
+	      {
+		option = '?';
+		break;
+	      }
+	    
+	  }
+	else
+	  {
+	    newlist = opt_malloc(sizeof(struct server));
+	    memset(newlist, 0, sizeof(struct server));
+	  }
+	
+	if (option == 'A')
+	  {
+	    newlist->flags |= SERV_LITERAL_ADDRESS;
+	    if (!(newlist->flags & SERV_TYPE))
+	      option = '?';
+	  }
+	
+	if (!arg || !*arg)
+	  {
+	    newlist->flags |= SERV_NO_ADDR; /* no server */
+	    if (newlist->flags & SERV_LITERAL_ADDRESS)
+	      option = '?';
+	  }
+	else
+	  {
+	    int source_port = 0, serv_port = NAMESERVER_PORT;
+	    char *portno, *source;
+	    
+	    if ((source = split_chr(arg, '@')) && /* is there a source. */
+		(portno = split_chr(source, '#')) &&
+		!atoi_check16(portno, &source_port))
+	      problem = _("bad port");
+	       	    
+	    if ((portno = split_chr(arg, '#')) && /* is there a port no. */
+		!atoi_check16(portno, &serv_port))
+	      problem = _("bad port");
+	    
+	    if ((newlist->addr.in.sin_addr.s_addr = inet_addr(arg)) != (in_addr_t) -1)
+	      {
+		newlist->addr.in.sin_port = htons(serv_port);	
+		newlist->source_addr.in.sin_port = htons(source_port); 
+		newlist->addr.sa.sa_family = newlist->source_addr.sa.sa_family = AF_INET;
+#ifdef HAVE_SOCKADDR_SA_LEN
+		newlist->source_addr.in.sin_len = newlist->addr.in.sin_len = sizeof(struct sockaddr_in);
+#endif
+		if (source)
+		  {
+		    newlist->flags |= SERV_HAS_SOURCE;
+		    if ((newlist->source_addr.in.sin_addr.s_addr = inet_addr(source)) == (in_addr_t) -1)
+		      {
+#if defined(SO_BINDTODEVICE)
+			newlist->source_addr.in.sin_addr.s_addr = INADDR_ANY;
+			strncpy(newlist->interface, source, IF_NAMESIZE);
+#else
+			problem = _("interface binding not supported");
+#endif
+		      }
+		  }
+		else
+		  newlist->source_addr.in.sin_addr.s_addr = INADDR_ANY;
+	      }
+#ifdef HAVE_IPV6
+	    else if (inet_pton(AF_INET6, arg, &newlist->addr.in6.sin6_addr) > 0)
+	      {
+		newlist->addr.in6.sin6_port = htons(serv_port);
+		newlist->source_addr.in6.sin6_port = htons(source_port);
+		newlist->addr.sa.sa_family = newlist->source_addr.sa.sa_family = AF_INET6;
+#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)
+		      {
+#if defined(SO_BINDTODEVICE)
+			newlist->source_addr.in6.sin6_addr = in6addr_any; 
+			strncpy(newlist->interface, source, IF_NAMESIZE);
+#else
+			problem = _("interface binding not supported");
+#endif
+		      }
+		  }
+		else
+		  newlist->source_addr.in6.sin6_addr = in6addr_any; 
+	      }
+#endif
+	    else
+	      option = '?'; /* error */
+	    
+	  }
+	
+	serv = newlist;
+	while (serv->next)
+	  {
+	    serv->next->flags = serv->flags;
+	    serv->next->addr = serv->addr;
+	    serv->next->source_addr = serv->source_addr;
+	    serv = serv->next;
+	  }
+	serv->next = daemon->servers;
+	daemon->servers = newlist;
+	break;
+      }
+      
+    case 'c':  /* --cache-size */
+      {
+	int size;
+	
+	if (!atoi_check(arg, &size))
+	  option = '?';
+	else
+	  {
+	    /* zero is OK, and means no caching. */
+	    
+	    if (size < 0)
+	      size = 0;
+	    else if (size > 10000)
+	      size = 10000;
+	    
+	    daemon->cachesize = size;
+	  }
+	break;
+      }
+      
+    case 'p':  /* --port */
+      if (!atoi_check16(arg, &daemon->port))
+	option = '?';
+      break;
+    
+    case LOPT_MINPORT:  /* --min-port */
+      if (!atoi_check16(arg, &daemon->min_port))
+	option = '?';
+      break;
+
+    case '0':  /* --dns-forward-max */
+      if (!atoi_check(arg, &daemon->ftabsize))
+	option = '?';
+      break;  
+    
+    case LOPT_MAX_LOGS:  /* --log-async */
+      daemon->max_logs = LOG_MAX; /* default */
+      if (arg && !atoi_check(arg, &daemon->max_logs))
+	option = '?';
+      else if (daemon->max_logs > 100)
+	daemon->max_logs = 100;
+      break;  
+
+    case 'P': /* --edns-packet-max */
+      {
+	int i;
+	if (!atoi_check(arg, &i))
+	  option = '?';
+	daemon->edns_pktsz = (unsigned short)i;	
+	break;
+      }
+      
+    case 'Q':  /* --query-port */
+      if (!atoi_check16(arg, &daemon->query_port))
+	option = '?';
+      /* if explicitly set to zero, use single OS ephemeral port
+	 and disable random ports */
+      if (daemon->query_port == 0)
+	daemon->osport = 1;
+      break;
+      
+    case 'T':         /* --local-ttl */
+    case LOPT_NEGTTL: /* --neg-ttl */
+      {
+	int ttl;
+	if (!atoi_check(arg, &ttl))
+	  option = '?';
+	else if (option == LOPT_NEGTTL)
+	  daemon->neg_ttl = (unsigned long)ttl;
+	else
+	  daemon->local_ttl = (unsigned long)ttl;
+	break;
+      }
+      
+#ifdef HAVE_DHCP
+    case 'X': /* --dhcp-lease-max */
+      if (!atoi_check(arg, &daemon->dhcp_max))
+	option = '?';
+      break;
+#endif
+      
+#ifdef HAVE_TFTP
+    case LOPT_TFTP_MAX:  /*  --tftp-max */
+      if (!atoi_check(arg, &daemon->tftp_max))
+	option = '?';
+      break;  
+
+    case LOPT_PREFIX: /* --tftp-prefix */
+      daemon->tftp_prefix = opt_string_alloc(arg);
+      break;
+
+    case LOPT_TFTPPORTS: /* --tftp-port-range */
+      if (!(comma = split(arg)) || 
+	  !atoi_check16(arg, &daemon->start_tftp_port) ||
+	  !atoi_check16(comma, &daemon->end_tftp_port))
+	problem = _("bad port range");
+      
+      if (daemon->start_tftp_port > daemon->end_tftp_port)
+	{
+	  int tmp = daemon->start_tftp_port;
+	  daemon->start_tftp_port = daemon->end_tftp_port;
+	  daemon->end_tftp_port = tmp;
+	} 
+      
+      break;
+#endif
+	      
+    case LOPT_BRIDGE:   /* --bridge-interface */
+      {
+	struct dhcp_bridge *new = opt_malloc(sizeof(struct dhcp_bridge));
+	if (!(comma = split(arg)))
+	  {
+	    problem = _("bad bridge-interface");
+	    break;
+	  }
+	
+	strncpy(new->iface, arg, IF_NAMESIZE);
+	new->alias = NULL;
+	new->next = daemon->bridges;
+	daemon->bridges = new;
+
+	do {
+	  arg = comma;
+	  comma = split(arg);
+	  if (strlen(arg) != 0)
+	    {
+	      struct dhcp_bridge *b = opt_malloc(sizeof(struct dhcp_bridge)); 
+	      b->next = new->alias;
+	      new->alias = b;
+	      strncpy(b->iface, arg, IF_NAMESIZE);
+	    }
+	} while (comma);
+	
+	break;
+      }
+
+#ifdef HAVE_DHCP
+    case 'F':  /* --dhcp-range */
+      {
+	int k, leasepos = 2;
+	char *cp, *a[5] = { NULL, NULL, NULL, NULL, NULL };
+	struct dhcp_context *new = opt_malloc(sizeof(struct dhcp_context));
+	
+	new->next = daemon->dhcp;
+	new->lease_time = DEFLEASE;
+	new->addr_epoch = 0;
+	new->netmask.s_addr = 0;
+	new->broadcast.s_addr = 0;
+	new->router.s_addr = 0;
+	new->netid.net = NULL;
+	new->filter = NULL;
+	new->flags = 0;
+	
+	gen_prob = _("bad dhcp-range");
+	
+	if (!arg)
+	  {
+	    option = '?';
+	    break;
+	  }
+	
+	while(1)
+	  {
+	    for (cp = arg; *cp; cp++)
+	      if (!(*cp == ' ' || *cp == '.' ||  (*cp >='0' && *cp <= '9')))
+		break;
+	    
+	    if (*cp != ',' && (comma = split(arg)))
+	      {
+		if (strstr(arg, "net:") == arg)
+		  {
+		    struct dhcp_netid *tt = opt_malloc(sizeof (struct dhcp_netid));
+		    tt->net = opt_string_alloc(arg+4);
+		    tt->next = new->filter;
+		    new->filter = tt;
+		  }
+		else
+		  {
+		    if (new->netid.net)
+		      problem = _("only one netid tag allowed");
+		    else
+		      new->netid.net = opt_string_alloc(arg);
+		  }
+		arg = comma;
+	      }
+	    else
+	      {
+		a[0] = arg;
+		break;
+	      }
+	  }
+	
+	for (k = 1; k < 5; k++)
+	  if (!(a[k] = split(a[k-1])))
+	    break;
+	
+	if ((k < 2) || ((new->start.s_addr = inet_addr(a[0])) == (in_addr_t)-1))
+	  option = '?';
+	else if (strcmp(a[1], "static") == 0)
+	  {
+	    new->end = new->start;
+	    new->flags |= CONTEXT_STATIC;
+	  }
+	else if (strcmp(a[1], "proxy") == 0)
+	  {
+	    new->end = new->start;
+	    new->flags |= CONTEXT_PROXY;
+	  }
+	else if ((new->end.s_addr = inet_addr(a[1])) == (in_addr_t)-1)
+	  option = '?';
+	
+	if (ntohl(new->start.s_addr) > ntohl(new->end.s_addr))
+	  {
+	    struct in_addr tmp = new->start;
+	    new->start = new->end;
+	    new->end = tmp;
+	  }
+	
+	if (option != '?' && k >= 3 && strchr(a[2], '.') &&  
+	    ((new->netmask.s_addr = inet_addr(a[2])) != (in_addr_t)-1))
+	  {
+	    new->flags |= CONTEXT_NETMASK;
+	    leasepos = 3;
+	    if (!is_same_net(new->start, new->end, new->netmask))
+	      problem = _("inconsistent DHCP range");
+	  }
+	daemon->dhcp = new;
+	
+	if (k >= 4 && strchr(a[3], '.') &&  
+	    ((new->broadcast.s_addr = inet_addr(a[3])) != (in_addr_t)-1))
+	  {
+	    new->flags |= CONTEXT_BRDCAST;
+	    leasepos = 4;
+	  }
+	
+	if (k >= leasepos+1)
+	  {
+	    if (strcmp(a[leasepos], "infinite") == 0)
+	      new->lease_time = 0xffffffff;
+	    else
+	      {
+		int fac = 1;
+		if (strlen(a[leasepos]) > 0)
+		  {
+		    switch (a[leasepos][strlen(a[leasepos]) - 1])
+		      {
+		      case 'd':
+		      case 'D':
+			fac *= 24;
+			/* fall though */
+		      case 'h':
+		      case 'H':
+			fac *= 60;
+			/* fall through */
+		      case 'm':
+		      case 'M':
+			fac *= 60;
+			/* fall through */
+		      case 's':
+		      case 'S':
+			a[leasepos][strlen(a[leasepos]) - 1] = 0;
+		      }
+		    
+		    new->lease_time = atoi(a[leasepos]) * fac;
+		    /* Leases of a minute or less confuse
+		       some clients, notably Apple's */
+		    if (new->lease_time < 120)
+		      new->lease_time = 120;
+		  }
+	      }
+	  }
+	break;
+      }
+
+    case LOPT_BANK:
+    case 'G':  /* --dhcp-host */
+      {
+	int j, k = 0;
+	char *a[6] = { NULL, NULL, NULL, NULL, NULL, NULL };
+	struct dhcp_config *new;
+	struct in_addr in;
+	
+	new = opt_malloc(sizeof(struct dhcp_config));
+	
+	new->next = daemon->dhcp_conf;
+	new->flags = (option == LOPT_BANK) ? CONFIG_BANK : 0;
+	new->hwaddr = NULL;
+	
+	if ((a[0] = arg))
+	  for (k = 1; k < 6; k++)
+	    if (!(a[k] = split(a[k-1])))
+	      break;
+	
+	for (j = 0; j < k; j++)
+	  if (strchr(a[j], ':')) /* ethernet address, netid or binary CLID */
+	    {
+	      char *arg = a[j];
+	      
+	      if ((arg[0] == 'i' || arg[0] == 'I') &&
+		  (arg[1] == 'd' || arg[1] == 'D') &&
+		  arg[2] == ':')
+		{
+		  if (arg[3] == '*')
+		    new->flags |= CONFIG_NOCLID;
+		  else
+		    {
+		      int len;
+		      arg += 3; /* dump id: */
+		      if (strchr(arg, ':'))
+			len = parse_hex(arg, (unsigned char *)arg, -1, NULL, NULL);
+		      else
+			{
+			  unhide_metas(arg);
+			  len = (int) strlen(arg);
+			}
+
+		      if ((new->clid = opt_malloc(len)))
+			{
+			  new->flags |= CONFIG_CLID;
+			  new->clid_len = len;
+			  memcpy(new->clid, arg, len);
+			}
+		    }
+		}
+	      else if (strstr(arg, "net:") == arg)
+		{
+		  int len = strlen(arg + 4) + 1;
+		  if ((new->netid.net = opt_malloc(len)))
+		    {
+		      new->flags |= CONFIG_NETID;
+		      strcpy(new->netid.net, arg+4);
+		      unhide_metas(new->netid.net);
+		    }
+		}
+	      else 
+		{
+		  struct hwaddr_config *newhw = opt_malloc(sizeof(struct hwaddr_config));
+		  newhw->next = new->hwaddr;
+		  new->hwaddr = newhw;
+		  newhw->hwaddr_len = parse_hex(a[j], newhw->hwaddr, DHCP_CHADDR_MAX, 
+						&newhw->wildcard_mask, &newhw->hwaddr_type);
+		}
+	    }
+	  else if (strchr(a[j], '.') && (in.s_addr = inet_addr(a[j])) != (in_addr_t)-1)
+	    {
+	      new->addr = in;
+	      new->flags |= CONFIG_ADDR;
+	    }
+	  else
+	    {
+	      char *cp, *lastp = NULL, last = 0;
+	      int fac = 1;
+	      
+	      if (strlen(a[j]) > 1)
+		{
+		  lastp = a[j] + strlen(a[j]) - 1;
+		  last = *lastp;
+		  switch (last)
+		    {
+		    case 'd':
+		    case 'D':
+		      fac *= 24;
+		      /* fall through */
+		    case 'h':
+		    case 'H':
+		      fac *= 60;
+		      /* fall through */
+		    case 'm':
+		    case 'M':
+		      fac *= 60;
+		      /* fall through */
+		    case 's':
+		    case 'S':
+		      *lastp = 0;
+		    }
+		}
+	      
+	      for (cp = a[j]; *cp; cp++)
+		if (!isdigit((int)*cp) && *cp != ' ')
+		  break;
+	      
+	      if (*cp)
+		{
+		  if (lastp)
+		    *lastp = last;
+		  if (strcmp(a[j], "infinite") == 0)
+		    {
+		      new->lease_time = 0xffffffff;
+		      new->flags |= CONFIG_TIME;
+		    }
+		  else if (strcmp(a[j], "ignore") == 0)
+		    new->flags |= CONFIG_DISABLE;
+		  else
+		    {
+		      if (!(new->hostname = canonicalise_opt(a[j])) ||
+			  !legal_hostname(new->hostname))
+			problem = _("bad DHCP host name");
+		      else
+			new->flags |= CONFIG_NAME;
+		      new->domain = NULL;			
+		    }
+		}
+	      else
+		{
+		  new->lease_time = atoi(a[j]) * fac; 
+		  /* Leases of a minute or less confuse
+		     some clients, notably Apple's */
+		  if (new->lease_time < 120)
+		    new->lease_time = 120;
+		  new->flags |= CONFIG_TIME;
+		}
+	    }
+	
+	daemon->dhcp_conf = new;
+	break;
+      }
+      
+    case 'O':           /* --dhcp-option */
+    case LOPT_FORCE:    /* --dhcp-option-force */
+    case LOPT_OPTS:
+    case LOPT_MATCH:    /* --dhcp-match */
+      problem = parse_dhcp_opt(arg, 
+			       option == LOPT_FORCE ? DHOPT_FORCE : 
+			       (option == LOPT_MATCH ? DHOPT_MATCH :
+			       (option == LOPT_OPTS ? DHOPT_BANK : 0)));
+      break;
+      
+    case 'M': /* --dhcp-boot */
+      {
+	struct dhcp_netid *id = NULL;
+	while (arg && strstr(arg, "net:") == arg)
+	  {
+	    struct dhcp_netid *newid = opt_malloc(sizeof(struct dhcp_netid));
+	    newid->next = id;
+	    id = newid;
+	    comma = split(arg);
+	    newid->net = opt_string_alloc(arg+4);
+	    arg = comma;
+	  };
+	
+	if (!arg)
+	  option = '?';
+	else 
+	  {
+	    char *dhcp_file, *dhcp_sname = NULL;
+	    struct in_addr dhcp_next_server;
+	    comma = split(arg);
+	    dhcp_file = opt_string_alloc(arg);
+	    dhcp_next_server.s_addr = 0;
+	    if (comma)
+	      {
+		arg = comma;
+		comma = split(arg);
+		dhcp_sname = opt_string_alloc(arg);
+		if (comma)
+		  {
+		    unhide_metas(comma);
+		    if ((dhcp_next_server.s_addr = inet_addr(comma)) == (in_addr_t)-1)
+		      option = '?';
+		  }
+	      }
+	    if (option != '?')
+	      {
+		struct dhcp_boot *new = opt_malloc(sizeof(struct dhcp_boot));
+		new->file = dhcp_file;
+		new->sname = dhcp_sname;
+		new->next_server = dhcp_next_server;
+		new->netid = id;
+		new->next = daemon->boot_config;
+		daemon->boot_config = new;
+	      }
+	  }
+	
+	break;
+      }
+
+    case LOPT_PXE_PROMT:  /* --pxe-prompt */
+       {
+	 struct dhcp_opt *new = opt_malloc(sizeof(struct dhcp_opt));
+	 int timeout;
+
+	 new->netid = NULL;
+	 new->opt = 10; /* PXE_MENU_PROMPT */
+
+	 while (arg && strstr(arg, "net:") == arg)
+	   {
+	     struct dhcp_netid *nn = opt_malloc(sizeof (struct dhcp_netid));
+	     comma = split(arg);
+	     nn->next = new->netid;
+	     new->netid = nn;
+	     nn->net = opt_string_alloc(arg+4);
+	     arg = comma;
+	   }
+	 
+	 if (!arg)
+	   option = '?';
+	 else
+	   {
+	     comma = split(arg);
+	     unhide_metas(arg);
+	     new->len = strlen(arg) + 1;
+	     new->val = opt_malloc(new->len);
+	     memcpy(new->val + 1, arg, new->len - 1);
+	     
+	     new->u.vendor_class = (unsigned char *)"PXEClient";
+	     new->flags = DHOPT_VENDOR;
+	     
+	     if (comma && atoi_check(comma, &timeout))
+	       *(new->val) = timeout;
+	     else
+	       *(new->val) = 255;
+
+	     new->next = daemon->dhcp_opts;
+	     daemon->dhcp_opts = new;
+	     daemon->enable_pxe = 1;
+	   }
+	 
+	 break;
+       }
+       
+    case LOPT_PXE_SERV:  /* --pxe-service */
+       {
+	 struct pxe_service *new = opt_malloc(sizeof(struct pxe_service));
+	 char *CSA[] = { "x86PC", "PC98", "IA64_EFI", "Alpha", "Arc_x86", "Intel_Lean_Client",
+			 "IA32_EFI", "BC_EFI", "Xscale_EFI", "x86-64_EFI", NULL };  
+	 static int boottype = 32768;
+	 
+	 new->netid = NULL;
+	 new->server.s_addr = 0;
+
+	 while (arg && strstr(arg, "net:") == arg)
+	   {
+	     struct dhcp_netid *nn = opt_malloc(sizeof (struct dhcp_netid));
+	     comma = split(arg);
+	     nn->next = new->netid;
+	     new->netid = nn;
+	     nn->net = opt_string_alloc(arg+4);
+	     arg = comma;
+	   }
+       
+	 if (arg && (comma = split(arg)))
+	   {
+	     for (i = 0; CSA[i]; i++)
+	       if (strcasecmp(CSA[i], arg) == 0)
+		 break;
+	     
+	     if (CSA[i] || atoi_check(arg, &i))
+	       {
+		 arg = comma;
+		 comma = split(arg);
+		 
+		 new->CSA = i;
+		 new->menu = opt_string_alloc(arg);
+		 
+		 if (comma)
+		   {
+		     arg = comma;
+		     comma = split(arg);
+		     if (atoi_check(arg, &i))
+		       {
+			 new->type = i;
+			 new->basename = NULL;
+		       }
+		     else
+		       {
+			 new->type = boottype++;
+			 new->basename = opt_string_alloc(arg);
+		       }
+		     
+		     if (comma && (new->server.s_addr = inet_addr(comma)) == (in_addr_t)-1)
+		       option = '?';
+		     
+		     /* Order matters */
+		     new->next = NULL;
+		     if (!daemon->pxe_services)
+		       daemon->pxe_services = new; 
+		     else
+		       {
+			 struct pxe_service *s;
+			 for (s = daemon->pxe_services; s->next; s = s->next);
+			 s->next = new;
+		       }
+		     
+		     daemon->enable_pxe = 1;
+		     break;
+		   }
+	       }
+	   }
+	 
+	 option = '?';
+	 break;
+       }
+	 
+    case '4':  /* --dhcp-mac */
+      {
+	if (!(comma = split(arg)))
+	  option = '?';
+	else
+	  {
+	    struct dhcp_mac *new = opt_malloc(sizeof(struct dhcp_mac));
+	    if (strstr(arg, "net:") == arg)
+	      new->netid.net = opt_string_alloc(arg+4);
+	    else
+	      new->netid.net = opt_string_alloc(arg);
+	    unhide_metas(comma);
+	    new->hwaddr_len = parse_hex(comma, new->hwaddr, DHCP_CHADDR_MAX, &new->mask, &new->hwaddr_type);
+	    new->next = daemon->dhcp_macs;
+	    daemon->dhcp_macs = new;
+	  }
+      }
+      break;
+      
+    case 'U':           /* --dhcp-vendorclass */
+    case 'j':           /* --dhcp-userclass */
+    case LOPT_CIRCUIT:  /* --dhcp-circuitid */
+    case LOPT_REMOTE:   /* --dhcp-remoteid */
+    case LOPT_SUBSCR:   /* --dhcp-subscrid */
+      {
+	if (!(comma = split(arg)))
+	  option = '?';
+	else
+	  {
+	    char *p;
+	    int dig = 0;
+	    struct dhcp_vendor *new = opt_malloc(sizeof(struct dhcp_vendor));
+	    if (strstr(arg, "net:") == arg)
+	      new->netid.net = opt_string_alloc(arg+4);
+	    else
+	      new->netid.net = opt_string_alloc(arg);
+	    /* check for hex string - must digits may include : must not have nothing else, 
+	       only allowed for agent-options. */
+	    for (p = comma; *p; p++)
+	      if (isxdigit((int)*p))
+		dig = 1;
+	      else if (*p != ':')
+		break;
+	    unhide_metas(comma);
+	    if (option == 'U' || option == 'j' || *p || !dig)
+	      {
+		new->len = strlen(comma);  
+		new->data = opt_malloc(new->len);
+		memcpy(new->data, comma, new->len);
+	      }
+	    else
+	      {
+		new->len = parse_hex(comma, (unsigned char *)comma, strlen(comma), NULL, NULL);
+		new->data = opt_malloc(new->len);
+		memcpy(new->data, comma, new->len);
+	      }
+
+	    switch (option)
+	      {
+	      case 'j':
+		new->match_type = MATCH_USER;
+		break;
+	      case 'U':
+		new->match_type = MATCH_VENDOR;
+		break; 
+	      case LOPT_CIRCUIT:
+		new->match_type = MATCH_CIRCUIT;
+		break;
+	      case LOPT_REMOTE:
+		new->match_type = MATCH_REMOTE;
+		break;
+	      case LOPT_SUBSCR:
+		new->match_type = MATCH_SUBSCRIBER;
+		break;
+	      }
+	    new->next = daemon->dhcp_vendors;
+	    daemon->dhcp_vendors = new;
+	  }
+	break;
+      }
+      
+    case LOPT_ALTPORT:   /* --dhcp-alternate-port */
+      if (!arg)
+	{
+	  daemon->dhcp_server_port = DHCP_SERVER_ALTPORT;
+	  daemon->dhcp_client_port = DHCP_CLIENT_ALTPORT;
+	}
+      else
+	{
+	  comma = split(arg);
+	  if (!atoi_check16(arg, &daemon->dhcp_server_port) || 
+	      (comma && !atoi_check16(comma, &daemon->dhcp_client_port)))
+	    problem = _("invalid port number");
+	  if (!comma)
+	    daemon->dhcp_client_port = daemon->dhcp_server_port+1; 
+	}
+      break;
+
+    case 'J':            /* --dhcp-ignore */
+    case LOPT_NO_NAMES:  /* --dhcp-ignore-names */
+    case LOPT_BROADCAST: /* --dhcp-broadcast */
+    case '3':            /* --bootp-dynamic */ 
+      {
+	struct dhcp_netid_list *new = opt_malloc(sizeof(struct dhcp_netid_list));
+	struct dhcp_netid *list = NULL;
+	if (option == 'J')
+	  {
+	    new->next = daemon->dhcp_ignore;
+	    daemon->dhcp_ignore = new;
+	  }
+	else if (option == LOPT_BROADCAST)
+	  {
+	    new->next = daemon->force_broadcast;
+	    daemon->force_broadcast = new;
+	  }
+	else if (option == '3')
+	  {
+	    new->next = daemon->bootp_dynamic;
+	    daemon->bootp_dynamic = new;
+	  }
+	else
+	  {
+	    new->next = daemon->dhcp_ignore_names;
+	    daemon->dhcp_ignore_names = new;
+	  }
+	
+	while (arg) {
+	  struct dhcp_netid *member = opt_malloc(sizeof(struct dhcp_netid));
+	  comma = split(arg);
+	  member->next = list;
+	  list = member;
+	  if (strstr(arg, "net:") == arg)
+	    member->net = opt_string_alloc(arg+4);
+	  else
+	    member->net = opt_string_alloc(arg);
+	  arg = comma;
+	}
+	
+	new->list = list;
+	break;
+      }
+#endif
+      
+    case 'V':  /* --alias */
+      {
+	char *dash, *a[3] = { NULL, NULL, NULL };
+	int k = 0;
+	struct doctor *new = opt_malloc(sizeof(struct doctor));
+	new->next = daemon->doctors;
+	daemon->doctors = new;
+	new->mask.s_addr = 0xffffffff;
+	new->end.s_addr = 0;
+
+	if ((a[0] = arg))
+	  for (k = 1; k < 3; k++)
+	    {
+	      if (!(a[k] = split(a[k-1])))
+		break;
+	      unhide_metas(a[k]);
+	    }
+	
+	dash = split_chr(a[0], '-');
+
+	if ((k < 2) || 
+	    ((new->in.s_addr = inet_addr(a[0])) == (in_addr_t)-1) ||
+	    ((new->out.s_addr = inet_addr(a[1])) == (in_addr_t)-1))
+	  option = '?';
+	
+	if (k == 3)
+	  new->mask.s_addr = inet_addr(a[2]);
+	
+	if (dash && 
+	    ((new->end.s_addr = inet_addr(dash)) == (in_addr_t)-1 ||
+	     !is_same_net(new->in, new->end, new->mask) ||
+	     ntohl(new->in.s_addr) > ntohl(new->end.s_addr)))
+	  problem = _("invalid alias range");
+	
+	break;
+      }
+      
+    case LOPT_INTNAME:  /* --interface-name */
+      {
+	struct interface_name *new, **up;
+	char *domain = NULL;
+
+	comma = split(arg);
+	
+	if (!comma || !(domain = canonicalise_opt(arg)))
+	  problem = _("bad interface name");
+	
+	new = opt_malloc(sizeof(struct interface_name));
+	new->next = NULL;
+	/* Add to the end of the list, so that first name
+	   of an interface is used for PTR lookups. */
+	for (up = &daemon->int_names; *up; up = &((*up)->next));
+	*up = new;
+	new->name = domain;
+	new->intr = opt_string_alloc(comma);
+	break;
+      }
+      
+    case LOPT_CNAME: /* --cname */
+      {
+	struct cname *new;
+	
+	if (!(comma = split(arg)))
+	  option = '?';
+	else
+	  {
+	    char *alias = canonicalise_opt(arg);
+	    char *target = canonicalise_opt(comma);
+	    
+	    if (!alias || !target)
+	      problem = _("bad CNAME");
+	    else
+	      {
+		for (new = daemon->cnames; new; new = new->next)
+		  if (hostname_isequal(new->alias, arg))
+		    problem = _("duplicate CNAME");
+		new = opt_malloc(sizeof(struct cname));
+		new->next = daemon->cnames;
+		daemon->cnames = new;
+		new->alias = alias;
+		new->target = target;
+	      }
+	  }
+	break;
+      }
+
+    case LOPT_PTR:  /* --ptr-record */
+      {
+	struct ptr_record *new;
+	char *dom, *target = NULL;
+
+	comma = split(arg);
+	
+	if (!(dom = canonicalise_opt(arg)) ||
+	    (comma && !(target = canonicalise_opt(comma))))
+	  problem = _("bad PTR record");
+	else
+	  {
+	    new = opt_malloc(sizeof(struct ptr_record));
+	    new->next = daemon->ptr;
+	    daemon->ptr = new;
+	    new->name = dom;
+	    new->ptr = target;
+	  }
+	break;
+      }
+
+    case LOPT_NAPTR: /* --naptr-record */
+      {
+	char *a[7] = { NULL, NULL, NULL, NULL, NULL, NULL, NULL };
+	int k = 0;
+	struct naptr *new;
+	int order, pref;
+	char *name, *replace = NULL;
+
+	if ((a[0] = arg))
+	  for (k = 1; k < 7; k++)
+	    if (!(a[k] = split(a[k-1])))
+	      break;
+	
+	
+	if (k < 6 || 
+	    !(name = canonicalise_opt(a[0])) ||
+	    !atoi_check16(a[1], &order) || 
+	    !atoi_check16(a[2], &pref) ||
+	    (k == 7 && !(replace = canonicalise_opt(a[6]))))
+	  problem = _("bad NAPTR record");
+	else
+	  {
+	    new = opt_malloc(sizeof(struct naptr));
+	    new->next = daemon->naptr;
+	    daemon->naptr = new;
+	    new->name = name;
+	    new->flags = opt_string_alloc(a[3]);
+	    new->services = opt_string_alloc(a[4]);
+	    new->regexp = opt_string_alloc(a[5]);
+	    new->replace = replace;
+	    new->order = order;
+	    new->pref = pref;
+	  }
+	break;
+      }
+       
+    case 'Y':  /* --txt-record */
+      {
+	struct txt_record *new;
+	unsigned char *p, *q;
+	
+	if ((comma = split(arg)))
+	  comma--;
+	
+	gen_prob = _("TXT record string too long");
+	
+	if ((q = (unsigned char *)comma))
+	  while (1)
+	    {
+	      size_t len;
+	      if ((p = (unsigned char *)strchr((char*)q+1, ',')))
+		{
+		  if ((len = p - q - 1) > 255)
+		    option = '?';
+		  *q = len;
+		  for (q = q+1; q < p; q++)
+		    *q = unhide_meta(*q);
+		}
+	      else
+		{
+		  if ((len = strlen((char *)q+1)) > 255)
+		    option = '?';
+		  *q = len;
+		  for (q = q+1; *q; q++)
+		    *q = unhide_meta(*q);
+		  break;
+		}
+	    }
+	
+	new = opt_malloc(sizeof(struct txt_record));
+	new->next = daemon->txt;
+	daemon->txt = new;
+	new->class = C_IN;
+	if (comma)
+	  {
+	    new->len = q - ((unsigned char *)comma);
+	    new->txt = opt_malloc(new->len);
+	    memcpy(new->txt, comma, new->len);
+	  }
+	else
+	  {
+	    static char empty[] = "";
+	    new->len = 1;
+	    new->txt = empty;
+	  }
+	
+	/* ensure arg is terminated */
+	if (comma)
+	  *comma = 0;
+
+	if (!(new->name = canonicalise_opt(arg)))
+	  {
+	    problem = _("bad TXT record");
+	    break;
+	  }
+
+	break;
+      }
+      
+    case 'W':  /* --srv-host */
+      {
+	int port = 1, priority = 0, weight = 0;
+	char *name, *target = NULL;
+	struct mx_srv_record *new;
+	
+	comma = split(arg);
+	
+	if (!(name = canonicalise_opt(arg)))
+	  problem = _("bad SRV record");
+	  
+	if (comma)
+	  {
+	    arg = comma;
+	    comma = split(arg);
+	    if (!(target = canonicalise_opt(arg))
+)	      problem = _("bad SRV target");
+		
+	    if (comma)
+	      {
+		arg = comma;
+		comma = split(arg);
+		if (!atoi_check16(arg, &port))
+		  problem = _("invalid port number");
+		
+		if (comma)
+		  {
+		    arg = comma;
+		    comma = split(arg);
+		    if (!atoi_check16(arg, &priority))
+		      problem = _("invalid priority");
+			
+		    if (comma)
+		      {
+			arg = comma;
+			comma = split(arg);
+			if (!atoi_check16(arg, &weight))
+			  problem = _("invalid weight");
+		      }
+		  }
+	      }
+	  }
+	
+	new = opt_malloc(sizeof(struct mx_srv_record));
+	new->next = daemon->mxnames;
+	daemon->mxnames = new;
+	new->issrv = 1;
+	new->name = name;
+	new->target = target;
+	new->srvport = port;
+	new->priority = priority;
+	new->weight = weight;
+	break;
+      }
+      
+    default:
+      return _("unsupported option (check that dnsmasq was compiled with DHCP/TFTP/DBus support)");
+
+    }
+
+  if (problem)
+    return problem;
+  
+  if (option == '?')
+    return gen_prob;
+
+  return NULL;
+}
+
+static void one_file(char *file, int nest, int hard_opt)	
+{
+  volatile int lineno = 0;
+  int i, option; 
+  FILE *f;
+  char *p, *arg, *start, *buff = daemon->namebuff;
+  static struct fileread {
+    dev_t dev;
+    ino_t ino;
+    struct fileread *next;
+  } *filesread = NULL;
+  struct stat statbuf;
+  
+  /* ignore repeated files. */
+  if (hard_opt == 0 && stat(file, &statbuf) == 0)
+    {
+      struct fileread *r;
+
+      for (r = filesread; r; r = r->next)
+	if (r->dev == statbuf.st_dev && r->ino == statbuf.st_ino)
+	  return;
+
+      r = safe_malloc(sizeof(struct fileread));
+      r->next = filesread;
+      filesread = r;
+      r->dev = statbuf.st_dev;
+      r->ino = statbuf.st_ino;
+    }
+
+  if (nest > 20)
+    die(_("files nested too deep in %s"), file, EC_BADCONF);
+
+  if (!(f = fopen(file, "r")))
+    {   
+      if (errno == ENOENT && nest == 0)
+	return; /* No conffile, all done. */
+      else
+	{
+	  char *str = _("cannot read %s: %s");
+	  if (hard_opt != 0)
+	    {
+	      my_syslog(LOG_ERR, str, file, strerror(errno));
+	      return;
+	    }
+	  else
+	    die(str, file, EC_FILE);
+	}
+    } 
+  
+  while (fgets(buff, MAXDNAME, f))
+    {
+      int white;
+      unsigned int lastquote;
+      char *errmess;
+
+      /* Memory allocation failure longjmps here if mem_recover == 1 */ 
+      if (hard_opt)
+	{
+	  if (setjmp(mem_jmp))
+	    continue;
+	  mem_recover = 1;
+	}
+      
+      lineno++;
+      errmess = NULL;
+      
+      /* Implement quotes, inside quotes we allow \\ \" \n and \t 
+	 metacharacters get hidden also strip comments */
+      
+      for (white = 1, lastquote = 0, p = buff; *p; p++)
+	{
+	  if (*p == '"')
+	    {
+	      memmove(p, p+1, strlen(p+1)+1);
+	      for(; *p && *p != '"'; p++)
+		{
+		  if (*p == '\\' && strchr("\"tnebr\\", p[1]))
+		    {
+		      if (p[1] == 't')
+			p[1] = '\t';
+		      else if (p[1] == 'n')
+			p[1] = '\n';
+		      else if (p[1] == 'b')
+			p[1] = '\b';
+		      else if (p[1] == 'r')
+			p[1] = '\r';
+		      else if (p[1] == 'e') /* escape */
+			p[1] = '\033';
+		      memmove(p, p+1, strlen(p+1)+1);
+		    }
+		  *p = hide_meta(*p);
+		}
+	      if (*p == '"') 
+		{
+		  memmove(p, p+1, strlen(p+1)+1);
+		  lastquote = p - buff;
+		}
+	      else
+		{
+		  errmess = _("missing \"");
+		  goto oops; 
+		}
+	    }
+
+	  if (white && *p == '#')
+	    { 
+	      *p = 0;
+	      break;
+	    }
+	  white = isspace((int)unhide_meta(*p)); 
+	}
+
+      /* fgets gets end of line char too. */
+      while (strlen(buff) > lastquote && isspace((int)unhide_meta(buff[strlen(buff)-1])))
+	buff[strlen(buff)-1] = 0;
+
+      if (*buff == 0)
+	continue; 
+
+      if (hard_opt != 0)
+	arg = buff;
+      else if ((p=strchr(buff, '=')))
+	{
+	  /* allow spaces around "=" */
+	  arg = p+1;
+	  for (; p >= buff && (isspace((int)*p) || *p == '='); p--)
+	    *p = 0;
+	}
+      else
+	arg = NULL;
+
+      if (hard_opt != 0)
+	option = hard_opt;
+      else
+	{
+	  /* skip leading space */
+	  for (start = buff; *start && isspace((int)*start); start++);
+	  
+	  for (option = 0, i = 0; opts[i].name; i++) 
+	    if (strcmp(opts[i].name, start) == 0)
+	      {
+		option = opts[i].val;
+		break;
+	      }
+	  
+	  if (!option)
+	    errmess = _("bad option");
+	  else if (opts[i].has_arg == 0 && arg)
+	    errmess = _("extraneous parameter");
+	  else if (opts[i].has_arg == 1 && !arg)
+	    errmess = _("missing parameter");
+	}
+	  
+      if (!errmess)
+	{
+	  if (arg)
+	    for (; isspace((int)*arg); arg++);
+	  
+	  errmess = one_opt(option, arg, _("error"), nest + 1);
+	}
+      
+      if (errmess)
+	{
+	oops:
+	  sprintf(buff, _("%s at line %d of %%s"), errmess, lineno);
+	  if (hard_opt != 0)
+	    my_syslog(LOG_ERR, buff, file);
+	  else
+	    die(buff, file, EC_BADCONF);
+	}
+    }
+
+  mem_recover = 1;
+  fclose(f);
+}
+
+#ifdef HAVE_DHCP
+void reread_dhcp(void)
+{
+  if (daemon->dhcp_hosts_file)
+    {
+      struct dhcp_config *configs, *cp, **up;
+      
+      /* remove existing... */
+      for (up = &daemon->dhcp_conf, configs = daemon->dhcp_conf; configs; configs = cp)
+	{
+	  cp = configs->next;
+	  
+	  if (configs->flags & CONFIG_BANK)
+	    {
+	      struct hwaddr_config *mac, *tmp;
+	      
+	      for (mac = configs->hwaddr; mac; mac = tmp)
+		{
+		  tmp = mac->next;
+		  free(mac);
+		}
+	      if (configs->flags & CONFIG_CLID)
+		free(configs->clid);
+	      if (configs->flags & CONFIG_NETID)
+		free(configs->netid.net);
+	      if (configs->flags & CONFIG_NAME)
+		free(configs->hostname);
+	      
+     
+	      *up = configs->next;
+	      free(configs);
+	    }
+	  else
+	    up = &configs->next;
+	}
+      
+      one_file(daemon->dhcp_hosts_file, 1, LOPT_BANK);  
+      my_syslog(MS_DHCP | LOG_INFO, _("read %s"), daemon->dhcp_hosts_file);
+    }
+
+  if (daemon->dhcp_opts_file)
+    {
+      struct dhcp_opt *opts, *cp, **up;
+      struct dhcp_netid *id, *next;
+
+      for (up = &daemon->dhcp_opts, opts = daemon->dhcp_opts; opts; opts = cp)
+	{
+	  cp = opts->next;
+	  
+	  if (opts->flags & DHOPT_BANK)
+	    {
+	      if ((opts->flags & DHOPT_VENDOR))
+		free(opts->u.vendor_class);
+	      free(opts->val);
+	      for (id = opts->netid; id; id = next)
+		{
+		  next = id->next;
+		  free(id->net);
+		  free(id);
+		}
+	      *up = opts->next;
+	      free(opts);
+	    }
+	  else
+	    up = &opts->next;
+	}
+      
+      one_file(daemon->dhcp_opts_file, 1, LOPT_OPTS);  
+      my_syslog(MS_DHCP | LOG_INFO, _("read %s"), daemon->dhcp_opts_file);
+    }
+}
+#endif
+    
+void read_opts(int argc, char **argv, char *compile_opts)
+{
+  char *buff = opt_malloc(MAXDNAME);
+  int option, nest = 0, testmode = 0;
+  char *errmess, *arg, *conffile = CONFFILE;
+      
+  opterr = 0;
+
+  daemon = opt_malloc(sizeof(struct daemon));
+  memset(daemon, 0, sizeof(struct daemon));
+  daemon->namebuff = buff;
+
+  /* Set defaults - everything else is zero or NULL */
+  daemon->cachesize = CACHESIZ;
+  daemon->ftabsize = FTABSIZ;
+  daemon->port = NAMESERVER_PORT;
+  daemon->dhcp_client_port = DHCP_CLIENT_PORT;
+  daemon->dhcp_server_port = DHCP_SERVER_PORT;
+  daemon->default_resolv.is_default = 1;
+  daemon->default_resolv.name = RESOLVFILE;
+  daemon->resolv_files = &daemon->default_resolv;
+  daemon->username = CHUSER;
+  daemon->runfile =  RUNFILE;
+  daemon->dhcp_max = MAXLEASES;
+  daemon->tftp_max = TFTP_MAX_CONNECTIONS;
+  daemon->edns_pktsz = EDNS_PKTSZ;
+  daemon->log_fac = -1;
+  add_txt("version.bind", "dnsmasq-" VERSION );
+  add_txt("authors.bind", "Simon Kelley");
+  add_txt("copyright.bind", COPYRIGHT);
+
+  while (1) 
+    {
+#ifdef HAVE_GETOPT_LONG
+      option = getopt_long(argc, argv, OPTSTRING, opts, NULL);
+#else
+      option = getopt(argc, argv, OPTSTRING);
+#endif
+      
+      if (option == -1)
+	break;
+      
+      /* Copy optarg so that argv doesn't get changed */
+      if (optarg)
+	{
+	  strncpy(buff, optarg, MAXDNAME);
+	  buff[MAXDNAME-1] = 0;
+	  arg = buff;
+	}
+      else
+	arg = NULL;
+      
+      /* command-line only stuff */
+      if (option == LOPT_TEST)
+	testmode = 1;
+      else if (option == 'w')
+	{
+	  if (argc != 3 ||  strcmp(argv[2], "dhcp") != 0)
+	    do_usage();
+#ifdef HAVE_DHCP
+	  else
+	    display_opts();
+#endif
+	  exit(0);
+	}
+      else if (option == 'v')
+	{
+	  printf(_("Dnsmasq version %s  %s\n"), VERSION, COPYRIGHT);
+	  printf(_("Compile time options %s\n\n"), compile_opts); 
+	  printf(_("This software comes with ABSOLUTELY NO WARRANTY.\n"));
+	  printf(_("Dnsmasq is free software, and you are welcome to redistribute it\n"));
+	  printf(_("under the terms of the GNU General Public License, version 2 or 3.\n"));
+          exit(0);
+        }
+      else if (option == 'C')
+	{
+	  conffile = opt_string_alloc(arg);
+	  nest++;
+	}
+      else
+	{
+#ifdef HAVE_GETOPT_LONG
+	  errmess = one_opt(option, arg, _("try --help"), 0);
+#else 
+	  errmess = one_opt(option, arg, _("try -w"), 0); 
+#endif  
+	  if (errmess)
+	    die(_("bad command line options: %s"), errmess, EC_BADCONF);
+	}
+    }
+
+  if (conffile)
+    one_file(conffile, nest, 0);
+
+  /* port might not be known when the address is parsed - fill in here */
+  if (daemon->servers)
+    {
+      struct server *tmp;
+      for (tmp = daemon->servers; tmp; tmp = tmp->next)
+	if (!(tmp->flags & SERV_HAS_SOURCE))
+	  {
+	    if (tmp->source_addr.sa.sa_family == AF_INET)
+	      tmp->source_addr.in.sin_port = htons(daemon->query_port);
+#ifdef HAVE_IPV6
+	    else if (tmp->source_addr.sa.sa_family == AF_INET6)
+	      tmp->source_addr.in6.sin6_port = htons(daemon->query_port);
+#endif 
+	  } 
+    }
+  
+  if (daemon->if_addrs)
+    {  
+      struct iname *tmp;
+      for(tmp = daemon->if_addrs; tmp; tmp = tmp->next)
+	if (tmp->addr.sa.sa_family == AF_INET)
+	  tmp->addr.in.sin_port = htons(daemon->port);
+#ifdef HAVE_IPV6
+	else if (tmp->addr.sa.sa_family == AF_INET6)
+	  tmp->addr.in6.sin6_port = htons(daemon->port);
+#endif /* IPv6 */
+    }
+		      
+  /* only one of these need be specified: the other defaults to the host-name */
+  if ((daemon->options & OPT_LOCALMX) || daemon->mxnames || daemon->mxtarget)
+    {
+      struct mx_srv_record *mx;
+      
+      if (gethostname(buff, MAXDNAME) == -1)
+	die(_("cannot get host-name: %s"), NULL, EC_MISC);
+      
+      for (mx = daemon->mxnames; mx; mx = mx->next)
+	if (!mx->issrv && hostname_isequal(mx->name, buff))
+	  break;
+      
+      if ((daemon->mxtarget || (daemon->options & OPT_LOCALMX)) && !mx)
+	{
+	  mx = opt_malloc(sizeof(struct mx_srv_record));
+	  mx->next = daemon->mxnames;
+	  mx->issrv = 0;
+	  mx->target = NULL;
+	  mx->name = opt_string_alloc(buff);
+	  daemon->mxnames = mx;
+	}
+      
+      if (!daemon->mxtarget)
+	daemon->mxtarget = opt_string_alloc(buff);
+
+      for (mx = daemon->mxnames; mx; mx = mx->next)
+	if (!mx->issrv && !mx->target)
+	  mx->target = daemon->mxtarget;
+    }
+
+  if (!(daemon->options & OPT_NO_RESOLV) &&
+      daemon->resolv_files && 
+      daemon->resolv_files->next && 
+      (daemon->options & OPT_NO_POLL))
+    die(_("only one resolv.conf file allowed in no-poll mode."), NULL, EC_BADCONF);
+  
+  if (daemon->options & OPT_RESOLV_DOMAIN)
+    {
+      char *line;
+      FILE *f;
+
+      if ((daemon->options & OPT_NO_RESOLV) ||
+	  !daemon->resolv_files || 
+	  (daemon->resolv_files)->next)
+	die(_("must have exactly one resolv.conf to read domain from."), NULL, EC_BADCONF);
+      
+      if (!(f = fopen((daemon->resolv_files)->name, "r")))
+	die(_("failed to read %s: %s"), (daemon->resolv_files)->name, EC_FILE);
+      
+      while ((line = fgets(buff, MAXDNAME, f)))
+	{
+	  char *token = strtok(line, " \t\n\r");
+	  
+	  if (!token || strcmp(token, "search") != 0)
+	    continue;
+	  
+	  if ((token = strtok(NULL, " \t\n\r")) &&  
+	      (daemon->domain_suffix = canonicalise_opt(token)))
+	    break;
+	}
+
+      fclose(f);
+
+      if (!daemon->domain_suffix)
+	die(_("no search directive found in %s"), (daemon->resolv_files)->name, EC_MISC);
+    }
+
+  if (daemon->domain_suffix)
+    {
+       /* add domain for any srv record without one. */
+      struct mx_srv_record *srv;
+      
+      for (srv = daemon->mxnames; srv; srv = srv->next)
+	if (srv->issrv &&
+	    strchr(srv->name, '.') && 
+	    strchr(srv->name, '.') == strrchr(srv->name, '.'))
+	  {
+	    strcpy(buff, srv->name);
+	    strcat(buff, ".");
+	    strcat(buff, daemon->domain_suffix);
+	    free(srv->name);
+	    srv->name = opt_string_alloc(buff);
+	  }
+    }
+  else if (daemon->options & OPT_DHCP_FQDN)
+    die(_("there must be a default domain when --dhcp-fqdn is set"), NULL, EC_BADCONF);
+
+  if (testmode)
+    {
+      fprintf(stderr, "dnsmasq: %s.\n", _("syntax check OK"));
+      exit(0);
+    }
+}  
diff --git a/src/rfc1035.c b/src/rfc1035.c
new file mode 100755
index 0000000..ca5ceba
--- /dev/null
+++ b/src/rfc1035.c
@@ -0,0 +1,1604 @@
+/* dnsmasq is Copyright (c) 2000-2009 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"
+
+static int add_resource_record(HEADER *header, char *limit, int *truncp, 
+			       unsigned int nameoffset, unsigned char **pp, 
+			       unsigned long ttl, unsigned int *offset, unsigned short type, 
+			       unsigned short class, char *format, ...);
+
+#define CHECK_LEN(header, pp, plen, len) \
+    ((size_t)((pp) - (unsigned char *)(header) + (len)) <= (plen))
+
+#define ADD_RDLEN(header, pp, plen, len) \
+    (!CHECK_LEN(header, pp, plen, len) ? 0 : (long)((pp) += (len)), 1)
+
+static int extract_name(HEADER *header, size_t plen, unsigned char **pp, 
+			char *name, int isExtract, int extrabytes)
+{
+  unsigned char *cp = (unsigned char *)name, *p = *pp, *p1 = NULL;
+  unsigned int j, l, hops = 0;
+  int retvalue = 1;
+  
+  if (isExtract)
+    *cp = 0;
+
+  while (1)
+    { 
+      unsigned int label_type;
+
+      if (!CHECK_LEN(header, p, plen, 1))
+	return 0;
+      
+      if ((l = *p++) == 0) 
+	/* end marker */
+	{
+	  /* check that there are the correct no of bytes after the name */
+	  if (!CHECK_LEN(header, p, plen, extrabytes))
+	    return 0;
+	  
+	  if (isExtract)
+	    {
+	      if (cp != (unsigned char *)name)
+		cp--;
+	      *cp = 0; /* terminate: lose final period */
+	    }
+	  else if (*cp != 0)
+	    retvalue = 2;
+	  
+	  if (p1) /* we jumped via compression */
+	    *pp = p1;
+	  else
+	    *pp = p;
+	  
+	  return retvalue;
+	}
+
+      label_type = l & 0xc0;
+      
+      if (label_type == 0xc0) /* pointer */
+	{ 
+	  if (!CHECK_LEN(header, p, plen, 1))
+	    return 0;
+	      
+	  /* get offset */
+	  l = (l&0x3f) << 8;
+	  l |= *p++;
+	  
+	  if (!p1) /* first jump, save location to go back to */
+	    p1 = p;
+	      
+	  hops++; /* break malicious infinite loops */
+	  if (hops > 255)
+	    return 0;
+	  
+	  p = l + (unsigned char *)header;
+	}
+      else if (label_type == 0x80)
+	return 0; /* reserved */
+      else if (label_type == 0x40)
+	{ /* ELT */
+	  unsigned int count, digs;
+	  
+	  if ((l & 0x3f) != 1)
+	    return 0; /* we only understand bitstrings */
+
+	  if (!isExtract)
+	    return 0; /* Cannot compare bitsrings */
+	  
+	  count = *p++;
+	  if (count == 0)
+	    count = 256;
+	  digs = ((count-1)>>2)+1;
+	  
+	  /* output is \[x<hex>/siz]. which is digs+9 chars */
+	  if (cp - (unsigned char *)name + digs + 9 >= MAXDNAME)
+	    return 0;
+	  if (!CHECK_LEN(header, p, plen, (count-1)>>3))
+	    return 0;
+
+	  *cp++ = '\\';
+	  *cp++ = '[';
+	  *cp++ = 'x';
+	  for (j=0; j<digs; j++)
+	    {
+	      unsigned int dig;
+	      if (j%2 == 0)
+		dig = *p >> 4;
+	      else
+		dig = *p++ & 0x0f;
+	      
+	      *cp++ = dig < 10 ? dig + '0' : dig + 'A' - 10;
+	    } 
+	  cp += sprintf((char *)cp, "/%d]", count);
+	  /* do this here to overwrite the zero char from sprintf */
+	  *cp++ = '.';
+	}
+      else 
+	{ /* label_type = 0 -> label. */
+	  if (cp - (unsigned char *)name + l + 1 >= MAXDNAME)
+	    return 0;
+	  if (!CHECK_LEN(header, p, plen, l))
+	    return 0;
+	  
+	  for(j=0; j<l; j++, p++)
+	    if (isExtract)
+	      {
+		unsigned char c = *p;
+		if (isascii(c) && !iscntrl(c) && c != '.')
+		  *cp++ = *p;
+		else
+		  return 0;
+	      }
+	    else 
+	      {
+		unsigned char c1 = *cp, c2 = *p;
+		
+		if (c1 == 0)
+		  retvalue = 2;
+		else 
+		  {
+		    cp++;
+		    if (c1 >= 'A' && c1 <= 'Z')
+		      c1 += 'a' - 'A';
+		    if (c2 >= 'A' && c2 <= 'Z')
+		      c2 += 'a' - 'A';
+		    
+		    if (c1 != c2)
+		      retvalue =  2;
+		  }
+	      }
+	  
+	  if (isExtract)
+	    *cp++ = '.';
+	  else if (*cp != 0 && *cp++ != '.')
+	    retvalue = 2;
+	}
+    }
+}
+ 
+/* Max size of input string (for IPv6) is 75 chars.) */
+#define MAXARPANAME 75
+static int in_arpa_name_2_addr(char *namein, struct all_addr *addrp)
+{
+  int j;
+  char name[MAXARPANAME+1], *cp1;
+  unsigned char *addr = (unsigned char *)addrp;
+  char *lastchunk = NULL, *penchunk = NULL;
+  
+  if (strlen(namein) > MAXARPANAME)
+    return 0;
+
+  memset(addrp, 0, sizeof(struct all_addr));
+
+  /* turn name into a series of asciiz strings */
+  /* j counts no of labels */
+  for(j = 1,cp1 = name; *namein; cp1++, namein++)
+    if (*namein == '.')
+      {
+	penchunk = lastchunk;
+        lastchunk = cp1 + 1;
+	*cp1 = 0;
+	j++;
+      }
+    else
+      *cp1 = *namein;
+  
+  *cp1 = 0;
+
+  if (j<3)
+    return 0;
+
+  if (hostname_isequal(lastchunk, "arpa") && hostname_isequal(penchunk, "in-addr"))
+    {
+      /* IP v4 */
+      /* address arives as a name of the form
+	 www.xxx.yyy.zzz.in-addr.arpa
+	 some of the low order address octets might be missing
+	 and should be set to zero. */
+      for (cp1 = name; cp1 != penchunk; cp1 += strlen(cp1)+1)
+	{
+	  /* check for digits only (weeds out things like
+	     50.0/24.67.28.64.in-addr.arpa which are used 
+	     as CNAME targets according to RFC 2317 */
+	  char *cp;
+	  for (cp = cp1; *cp; cp++)
+	    if (!isdigit((int)*cp))
+	      return 0;
+	  
+	  addr[3] = addr[2];
+	  addr[2] = addr[1];
+	  addr[1] = addr[0];
+	  addr[0] = atoi(cp1);
+	}
+
+      return F_IPV4;
+    }
+#ifdef HAVE_IPV6
+  else if (hostname_isequal(penchunk, "ip6") && 
+	   (hostname_isequal(lastchunk, "int") || hostname_isequal(lastchunk, "arpa")))
+    {
+      /* IP v6:
+         Address arrives as 0.1.2.3.4.5.6.7.8.9.a.b.c.d.e.f.ip6.[int|arpa]
+    	 or \[xfedcba9876543210fedcba9876543210/128].ip6.[int|arpa]
+      
+	 Note that most of these the various reprentations are obsolete and 
+	 left-over from the many DNS-for-IPv6 wars. We support all the formats
+	 that we can since there is no reason not to.
+      */
+
+      if (*name == '\\' && *(name+1) == '[' && 
+	  (*(name+2) == 'x' || *(name+2) == 'X'))
+	{	  
+	  for (j = 0, cp1 = name+3; *cp1 && isxdigit((int) *cp1) && j < 32; cp1++, j++)
+	    {
+	      char xdig[2];
+	      xdig[0] = *cp1;
+	      xdig[1] = 0;
+	      if (j%2)
+		addr[j/2] |= strtol(xdig, NULL, 16);
+	      else
+		addr[j/2] = strtol(xdig, NULL, 16) << 4;
+	    }
+	  
+	  if (*cp1 == '/' && j == 32)
+	    return F_IPV6;
+	}
+      else
+	{
+	  for (cp1 = name; cp1 != penchunk; cp1 += strlen(cp1)+1)
+	    {
+	      if (*(cp1+1) || !isxdigit((int)*cp1))
+		return 0;
+	      
+	      for (j = sizeof(struct all_addr)-1; j>0; j--)
+		addr[j] = (addr[j] >> 4) | (addr[j-1] << 4);
+	      addr[0] = (addr[0] >> 4) | (strtol(cp1, NULL, 16) << 4);
+	    }
+	  
+	  return F_IPV6;
+	}
+    }
+#endif
+  
+  return 0;
+}
+
+static unsigned char *skip_name(unsigned char *ansp, HEADER *header, size_t plen, int extrabytes)
+{
+  while(1)
+    {
+      unsigned int label_type;
+      
+      if (!CHECK_LEN(header, ansp, plen, 1))
+	return NULL;
+      
+      label_type = (*ansp) & 0xc0;
+
+      if (label_type == 0xc0)
+	{
+	  /* pointer for compression. */
+	  ansp += 2;	
+	  break;
+	}
+      else if (label_type == 0x80)
+	return NULL; /* reserved */
+      else if (label_type == 0x40)
+	{
+	  /* Extended label type */
+	  unsigned int count;
+	  
+	  if (!CHECK_LEN(header, ansp, plen, 2))
+	    return NULL;
+	  
+	  if (((*ansp++) & 0x3f) != 1)
+	    return NULL; /* we only understand bitstrings */
+	  
+	  count = *(ansp++); /* Bits in bitstring */
+	  
+	  if (count == 0) /* count == 0 means 256 bits */
+	    ansp += 32;
+	  else
+	    ansp += ((count-1)>>3)+1;
+	}
+      else
+	{ /* label type == 0 Bottom six bits is length */
+	  unsigned int len = (*ansp++) & 0x3f;
+	  
+	  if (!ADD_RDLEN(header, ansp, plen, len))
+	    return NULL;
+
+	  if (len == 0)
+	    break; /* zero length label marks the end. */
+	}
+    }
+
+  if (!CHECK_LEN(header, ansp, plen, extrabytes))
+    return NULL;
+  
+  return ansp;
+}
+
+static unsigned char *skip_questions(HEADER *header, size_t plen)
+{
+  int q;
+  unsigned char *ansp = (unsigned char *)(header+1);
+
+  for (q = ntohs(header->qdcount); q != 0; q--)
+    {
+      if (!(ansp = skip_name(ansp, header, plen, 4)))
+	return NULL;
+      ansp += 4; /* class and type */
+    }
+  
+  return ansp;
+}
+
+static unsigned char *skip_section(unsigned char *ansp, int count, HEADER *header, size_t plen)
+{
+  int i, rdlen;
+  
+  for (i = 0; i < count; i++)
+    {
+      if (!(ansp = skip_name(ansp, header, plen, 10)))
+	return NULL; 
+      ansp += 8; /* type, class, TTL */
+      GETSHORT(rdlen, ansp);
+      if (!ADD_RDLEN(header, ansp, plen, rdlen))
+	return NULL;
+    }
+
+  return ansp;
+}
+
+/* CRC the question section. This is used to safely detect query 
+   retransmision and to detect answers to questions we didn't ask, which 
+   might be poisoning attacks. Note that we decode the name rather 
+   than CRC the raw bytes, since replies might be compressed differently. 
+   We ignore case in the names for the same reason. Return all-ones
+   if there is not question section. */
+unsigned int questions_crc(HEADER *header, size_t plen, char *name)
+{
+  int q;
+  unsigned int crc = 0xffffffff;
+  unsigned char *p1, *p = (unsigned char *)(header+1);
+
+  for (q = ntohs(header->qdcount); q != 0; q--) 
+    {
+      if (!extract_name(header, plen, &p, name, 1, 4))
+	return crc; /* bad packet */
+      
+      for (p1 = (unsigned char *)name; *p1; p1++)
+	{
+	  int i = 8;
+	  char c = *p1;
+
+	  if (c >= 'A' && c <= 'Z')
+	    c += 'a' - 'A';
+
+	  crc ^= c << 24;
+	  while (i--)
+	    crc = crc & 0x80000000 ? (crc << 1) ^ 0x04c11db7 : crc << 1;
+	}
+      
+      /* CRC the class and type as well */
+      for (p1 = p; p1 < p+4; p1++)
+	{
+	  int i = 8;
+	  crc ^= *p1 << 24;
+	  while (i--)
+	    crc = crc & 0x80000000 ? (crc << 1) ^ 0x04c11db7 : crc << 1;
+	}
+
+      p += 4;
+      if (!CHECK_LEN(header, p, plen, 0))
+	return crc; /* bad packet */
+    }
+
+  return crc;
+}
+
+
+size_t resize_packet(HEADER *header, size_t plen, unsigned char *pheader, size_t hlen)
+{
+  unsigned char *ansp = skip_questions(header, plen);
+    
+  /* if packet is malformed, just return as-is. */
+  if (!ansp)
+    return plen;
+  
+  if (!(ansp = skip_section(ansp, ntohs(header->ancount) + ntohs(header->nscount) + ntohs(header->arcount),
+			    header, plen)))
+    return plen;
+    
+  /* restore pseudoheader */
+  if (pheader && ntohs(header->arcount) == 0)
+    {
+      /* must use memmove, may overlap */
+      memmove(ansp, pheader, hlen);
+      header->arcount = htons(1);
+      ansp += hlen;
+    }
+
+  return ansp - (unsigned char *)header;
+}
+
+unsigned char *find_pseudoheader(HEADER *header, size_t plen, size_t  *len, unsigned char **p, int *is_sign)
+{
+  /* See if packet has an RFC2671 pseudoheader, and if so return a pointer to it. 
+     also return length of pseudoheader in *len and pointer to the UDP size in *p
+     Finally, check to see if a packet is signed. If it is we cannot change a single bit before
+     forwarding. We look for SIG and TSIG in the addition section, and TKEY queries (for GSS-TSIG) */
+  
+  int i, arcount = ntohs(header->arcount);
+  unsigned char *ansp = (unsigned char *)(header+1);
+  unsigned short rdlen, type, class;
+  unsigned char *ret = NULL;
+
+  if (is_sign)
+    {
+      *is_sign = 0;
+
+      if (header->opcode == QUERY)
+	{
+	  for (i = ntohs(header->qdcount); i != 0; i--)
+	    {
+	      if (!(ansp = skip_name(ansp, header, plen, 4)))
+		return NULL;
+	      
+	      GETSHORT(type, ansp); 
+	      GETSHORT(class, ansp);
+	      
+	      if (class == C_IN && type == T_TKEY)
+		*is_sign = 1;
+	    }
+	}
+    }
+  else
+    {
+      if (!(ansp = skip_questions(header, plen)))
+	return NULL;
+    }
+    
+  if (arcount == 0)
+    return NULL;
+  
+  if (!(ansp = skip_section(ansp, ntohs(header->ancount) + ntohs(header->nscount), header, plen)))
+    return NULL; 
+  
+  for (i = 0; i < arcount; i++)
+    {
+      unsigned char *save, *start = ansp;
+      if (!(ansp = skip_name(ansp, header, plen, 10)))
+	return NULL; 
+
+      GETSHORT(type, ansp);
+      save = ansp;
+      GETSHORT(class, ansp);
+      ansp += 4; /* TTL */
+      GETSHORT(rdlen, ansp);
+      if (!ADD_RDLEN(header, ansp, plen, rdlen))
+	return NULL;
+      if (type == T_OPT)
+	{
+	  if (len)
+	    *len = ansp - start;
+	  if (p)
+	    *p = save;
+	  ret = start;
+	}
+      else if (is_sign && 
+	       i == arcount - 1 && 
+	       class == C_ANY && 
+	       (type == T_SIG || type == T_TSIG))
+	*is_sign = 1;
+    }
+  
+  return ret;
+}
+      
+  
+/* is addr in the non-globally-routed IP space? */ 
+static int private_net(struct in_addr addr) 
+{
+  in_addr_t ip_addr = ntohl(addr.s_addr);
+
+  return
+    ((ip_addr & 0xFF000000) == 0x7F000000)  /* 127.0.0.0/8    (loopback) */ || 
+    ((ip_addr & 0xFFFF0000) == 0xC0A80000)  /* 192.168.0.0/16 (private)  */ ||
+    ((ip_addr & 0xFF000000) == 0x0A000000)  /* 10.0.0.0/8     (private)  */ ||
+    ((ip_addr & 0xFFF00000) == 0xAC100000)  /* 172.16.0.0/12  (private)  */ ||
+    ((ip_addr & 0xFFFF0000) == 0xA9FE0000)  /* 169.254.0.0/16 (zeroconf) */ ;
+}
+
+static unsigned char *do_doctor(unsigned char *p, int count, HEADER *header, size_t qlen)
+{
+  int i, qtype, qclass, rdlen;
+  unsigned long ttl;
+
+  for (i = count; i != 0; i--)
+    {
+      if (!(p = skip_name(p, header, qlen, 10)))
+	return 0; /* bad packet */
+      
+      GETSHORT(qtype, p); 
+      GETSHORT(qclass, p);
+      GETLONG(ttl, p);
+      GETSHORT(rdlen, p);
+      
+      if ((qclass == C_IN) && (qtype == T_A))
+	{
+	  struct doctor *doctor;
+	  struct in_addr addr;
+	  
+	  if (!CHECK_LEN(header, p, qlen, INADDRSZ))
+	    return 0;
+	   
+	   /* alignment */
+	  memcpy(&addr, p, INADDRSZ);
+	  
+	  for (doctor = daemon->doctors; doctor; doctor = doctor->next)
+	    {
+	      if (doctor->end.s_addr == 0)
+		{
+		  if (!is_same_net(doctor->in, addr, doctor->mask))
+		    continue;
+		}
+	      else if (ntohl(doctor->in.s_addr) > ntohl(addr.s_addr) || 
+		       ntohl(doctor->end.s_addr) < ntohl(addr.s_addr))
+		continue;
+	     
+	      addr.s_addr &= ~doctor->mask.s_addr;
+	      addr.s_addr |= (doctor->out.s_addr & doctor->mask.s_addr);
+	      /* Since we munged the data, the server it came from is no longer authoritative */
+	      header->aa = 0;
+	      memcpy(p, &addr, INADDRSZ);
+	      break;
+	    }
+	}
+      
+      if (!ADD_RDLEN(header, p, qlen, rdlen))
+	 return 0; /* bad packet */
+    }
+  
+  return p; 
+}
+
+static int find_soa(HEADER *header, size_t qlen)
+{
+  unsigned char *p;
+  int qtype, qclass, rdlen;
+  unsigned long ttl, minttl = ULONG_MAX;
+  int i, found_soa = 0;
+  
+  /* first move to NS section and find TTL from any SOA section */
+  if (!(p = skip_questions(header, qlen)) ||
+      !(p = do_doctor(p, ntohs(header->ancount), header, qlen)))
+    return 0;  /* bad packet */
+  
+  for (i = ntohs(header->nscount); i != 0; i--)
+    {
+      if (!(p = skip_name(p, header, qlen, 10)))
+	return 0; /* bad packet */
+      
+      GETSHORT(qtype, p); 
+      GETSHORT(qclass, p);
+      GETLONG(ttl, p);
+      GETSHORT(rdlen, p);
+      
+      if ((qclass == C_IN) && (qtype == T_SOA))
+	{
+	  found_soa = 1;
+	  if (ttl < minttl)
+	    minttl = ttl;
+
+	  /* MNAME */
+	  if (!(p = skip_name(p, header, qlen, 0)))
+	    return 0;
+	  /* RNAME */
+	  if (!(p = skip_name(p, header, qlen, 20)))
+	    return 0;
+	  p += 16; /* SERIAL REFRESH RETRY EXPIRE */
+	  
+	  GETLONG(ttl, p); /* minTTL */
+	  if (ttl < minttl)
+	    minttl = ttl;
+	}
+      else if (!ADD_RDLEN(header, p, qlen, rdlen))
+	return 0; /* bad packet */
+    }
+  
+  /* rewrite addresses in additioal section too */
+  if (!do_doctor(p, ntohs(header->arcount), header, qlen))
+    return 0;
+  
+  if (!found_soa)
+    minttl = daemon->neg_ttl;
+
+  return minttl;
+}
+
+/* Note that the following code can create CNAME chains that don't point to a real record,
+   either because of lack of memory, or lack of SOA records.  These are treated by the cache code as 
+   expired and cleaned out that way. 
+   Return 1 if we reject an address because it look like parct of dns-rebinding attack. */
+int extract_addresses(HEADER *header, size_t qlen, char *name, time_t now)
+{
+  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;
+
+  cache_start_insert();
+
+  /* find_soa is needed for dns_doctor side-effects, so don't call it lazily if there are any. */
+  if (daemon->doctors)
+    {
+      searched_soa = 1;
+      ttl = find_soa(header, qlen);
+    }
+  
+  /* go through the questions. */
+  p = (unsigned char *)(header+1);
+  
+  for (i = ntohs(header->qdcount); i != 0; i--)
+    {
+      int found = 0, cname_count = 5;
+      struct crec *cpp = NULL;
+      int flags = header->rcode == NXDOMAIN ? F_NXDOMAIN : 0;
+      unsigned long cttl = ULONG_MAX, attl;
+      
+      namep = p;
+      if (!extract_name(header, qlen, &p, name, 1, 4))
+	return 0; /* bad packet */
+           
+      GETSHORT(qtype, p); 
+      GETSHORT(qclass, p);
+      
+      if (qclass != C_IN)
+	continue;
+
+      /* PTRs: we chase CNAMEs here, since we have no way to 
+	 represent them in the cache. */
+      if (qtype == T_PTR)
+	{ 
+	  int name_encoding = in_arpa_name_2_addr(name, &addr);
+	  
+	  if (!name_encoding)
+	    continue;
+
+	  if (!(flags & F_NXDOMAIN))
+	    {
+	    cname_loop:
+	      if (!(p1 = skip_questions(header, qlen)))
+		return 0;
+	      
+	      for (j = ntohs(header->ancount); j != 0; j--) 
+		{
+		  unsigned char *tmp = namep;
+		  /* the loop body overwrites the original name, so get it back here. */
+		  if (!extract_name(header, qlen, &tmp, name, 1, 0) ||
+		      !(res = extract_name(header, qlen, &p1, name, 0, 10)))
+		    return 0; /* bad packet */
+		  
+		  GETSHORT(aqtype, p1); 
+		  GETSHORT(aqclass, p1);
+		  GETLONG(attl, p1);
+		  GETSHORT(ardlen, p1);
+		  endrr = p1+ardlen;
+		  
+		  /* TTL of record is minimum of CNAMES and PTR */
+		  if (attl < cttl)
+		    cttl = attl;
+
+		  if (aqclass == C_IN && res != 2 && (aqtype == T_CNAME || aqtype == T_PTR))
+		    {
+		      if (!extract_name(header, qlen, &p1, name, 1, 0))
+			return 0;
+		      
+		      if (aqtype == T_CNAME)
+			{
+			  if (!cname_count--)
+			    return 0; /* looped CNAMES */
+			  goto cname_loop;
+			}
+		      
+		      cache_insert(name, &addr, now, cttl, name_encoding | F_REVERSE);
+		      found = 1; 
+		    }
+		  
+		  p1 = endrr;
+		  if (!CHECK_LEN(header, p1, qlen, 0))
+		    return 0; /* bad packet */
+		}
+	    }
+	  
+	   if (!found && !(daemon->options & OPT_NO_NEG))
+	    {
+	      if (!searched_soa)
+		{
+		  searched_soa = 1;
+		  ttl = find_soa(header, qlen);
+		}
+	      if (ttl)
+		cache_insert(NULL, &addr, now, ttl, name_encoding | F_REVERSE | F_NEG | flags);	
+	    }
+	}
+      else
+	{
+	  /* everything other than PTR */
+	  struct crec *newc;
+	  int addrlen;
+
+	  if (qtype == T_A)
+	    {
+	      addrlen = INADDRSZ;
+	      flags |= F_IPV4;
+	    }
+#ifdef HAVE_IPV6
+	  else if (qtype == T_AAAA)
+	    {
+	      addrlen = IN6ADDRSZ;
+	      flags |= F_IPV6;
+	    }
+#endif
+	  else 
+	    continue;
+	    
+	  if (!(flags & F_NXDOMAIN))
+	    {
+	    cname_loop1:
+	      if (!(p1 = skip_questions(header, qlen)))
+		return 0;
+	      
+	      for (j = ntohs(header->ancount); j != 0; j--) 
+		{
+		  if (!(res = extract_name(header, qlen, &p1, name, 0, 10)))
+		    return 0; /* bad packet */
+		  
+		  GETSHORT(aqtype, p1); 
+		  GETSHORT(aqclass, p1);
+		  GETLONG(attl, p1);
+		  GETSHORT(ardlen, p1);
+		  endrr = p1+ardlen;
+		  
+		  if (aqclass == C_IN && res != 2 && (aqtype == T_CNAME || aqtype == qtype))
+		    {
+		      if (aqtype == T_CNAME)
+			{
+			  if (!cname_count--)
+			    return 0; /* looped CNAMES */
+			  newc = cache_insert(name, NULL, now, attl, F_CNAME | F_FORWARD);
+			  if (newc && cpp)
+			    {
+			      cpp->addr.cname.cache = newc;
+			      cpp->addr.cname.uid = newc->uid;
+			    }
+
+			  cpp = newc;
+			  if (attl < cttl)
+			    cttl = attl;
+			  
+			  if (!extract_name(header, qlen, &p1, name, 1, 0))
+			    return 0;
+			  goto cname_loop1;
+			}
+		      else
+			{
+			  found = 1;
+			  
+			  /* copy address into aligned storage */
+			  if (!CHECK_LEN(header, p1, qlen, addrlen))
+			    return 0; /* bad packet */
+			  memcpy(&addr, p1, addrlen);
+			  
+			  /* check for returned address in private space */
+			  if ((daemon->options & OPT_NO_REBIND) &&
+			      (flags & F_IPV4) &&
+			      private_net(addr.addr.addr4))
+			    return 1;
+			  
+			  newc = cache_insert(name, &addr, now, attl, flags | F_FORWARD);
+			  if (newc && cpp)
+			    {
+			      cpp->addr.cname.cache = newc;
+			      cpp->addr.cname.uid = newc->uid;
+			    }
+			  cpp = NULL;
+			}
+		    }
+		  
+		  p1 = endrr;
+		  if (!CHECK_LEN(header, p1, qlen, 0))
+		    return 0; /* bad packet */
+		}
+	    }
+	  
+	  if (!found && !(daemon->options & OPT_NO_NEG))
+	    {
+	      if (!searched_soa)
+		{
+		  searched_soa = 1;
+		  ttl = find_soa(header, qlen);
+		}
+	      /* If there's no SOA to get the TTL from, but there is a CNAME 
+		 pointing at this, inherit its TTL */
+	      if (ttl || cpp)
+		{
+		  newc = cache_insert(name, NULL, now, ttl ? ttl : cttl, F_FORWARD | F_NEG | flags);	
+		  if (newc && cpp)
+		    {
+		      cpp->addr.cname.cache = newc;
+		      cpp->addr.cname.uid = newc->uid;
+		    }
+		}
+	    }
+	}
+    }
+  
+  /* Don't put stuff from a truncated packet into the cache, but do everything else */
+  if (!header->tc)
+    cache_end_insert();
+
+  return 0;
+}
+
+/* If the packet holds exactly one query
+   return F_IPV4 or F_IPV6  and leave the name from the query in name. 
+   Abuse F_BIGNAME to indicate an NS query - yuck. */
+
+unsigned short extract_request(HEADER *header, size_t qlen, char *name, unsigned short *typep)
+{
+  unsigned char *p = (unsigned char *)(header+1);
+  int qtype, qclass;
+
+  if (typep)
+    *typep = 0;
+
+  if (ntohs(header->qdcount) != 1 || header->opcode != QUERY)
+    return 0; /* must be exactly one query. */
+  
+  if (!extract_name(header, qlen, &p, name, 1, 4))
+    return 0; /* bad packet */
+   
+  GETSHORT(qtype, p); 
+  GETSHORT(qclass, p);
+
+  if (typep)
+    *typep = qtype;
+
+  if (qclass == C_IN)
+    {
+      if (qtype == T_A)
+	return F_IPV4;
+      if (qtype == T_AAAA)
+	return F_IPV6;
+      if (qtype == T_ANY)
+	return  F_IPV4 | F_IPV6;
+      if (qtype == T_NS || qtype == T_SOA)
+	return F_QUERY | F_BIGNAME;
+    }
+  
+  return F_QUERY;
+}
+
+
+size_t setup_reply(HEADER *header, size_t qlen,
+		struct all_addr *addrp, unsigned short flags, unsigned long ttl)
+{
+  unsigned char *p = skip_questions(header, qlen);
+  
+  header->qr = 1; /* response */
+  header->aa = 0; /* authoritive */
+  header->ra = 1; /* recursion if available */
+  header->tc = 0; /* not truncated */
+  header->nscount = htons(0);
+  header->arcount = htons(0);
+  header->ancount = htons(0); /* no answers unless changed below */
+  if (flags == F_NEG)
+    header->rcode = SERVFAIL; /* couldn't get memory */
+  else if (flags == F_NOERR)
+    header->rcode = NOERROR; /* empty domain */
+  else if (flags == F_NXDOMAIN)
+    header->rcode = NXDOMAIN;
+  else if (p && flags == F_IPV4)
+    { /* we know the address */
+      header->rcode = NOERROR;
+      header->ancount = htons(1);
+      header->aa = 1;
+      add_resource_record(header, NULL, NULL, sizeof(HEADER), &p, ttl, NULL, T_A, C_IN, "4", addrp);
+    }
+#ifdef HAVE_IPV6
+  else if (p && flags == F_IPV6)
+    {
+      header->rcode = NOERROR;
+      header->ancount = htons(1);
+      header->aa = 1;
+      add_resource_record(header, NULL, NULL, sizeof(HEADER), &p, ttl, NULL, T_AAAA, C_IN, "6", addrp);
+    }
+#endif
+  else /* nowhere to forward to */
+    header->rcode = REFUSED;
+ 
+  return p - (unsigned char *)header;
+}
+
+/* check if name matches local names ie from /etc/hosts or DHCP or local mx names. */
+int check_for_local_domain(char *name, time_t now)
+{
+  struct crec *crecp;
+  struct mx_srv_record *mx;
+  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)) &&
+      (crecp->flags & (F_HOSTS | F_DHCP)))
+    return 1;
+  
+  for (mx = daemon->mxnames; mx; mx = mx->next)
+    if (hostname_isequal(name, mx->name))
+      return 1;
+
+  for (txt = daemon->txt; txt; txt = txt->next)
+    if (hostname_isequal(name, txt->name))
+      return 1;
+
+  for (intr = daemon->int_names; intr; intr = intr->next)
+    if (hostname_isequal(name, intr->name))
+      return 1;
+
+  for (ptr = daemon->ptr; ptr; ptr = ptr->next)
+    if (hostname_isequal(name, ptr->name))
+      return 1;
+ 
+  return 0;
+}
+
+/* Is the packet a reply with the answer address equal to addr?
+   If so mung is into an NXDOMAIN reply and also put that information
+   in the cache. */
+int check_for_bogus_wildcard(HEADER *header, size_t qlen, char *name, 
+			     struct bogus_addr *baddr, time_t now)
+{
+  unsigned char *p;
+  int i, qtype, qclass, rdlen;
+  unsigned long ttl;
+  struct bogus_addr *baddrp;
+
+  /* skip over questions */
+  if (!(p = skip_questions(header, qlen)))
+    return 0; /* bad packet */
+
+  for (i = ntohs(header->ancount); i != 0; i--)
+    {
+      if (!extract_name(header, qlen, &p, name, 1, 10))
+	return 0; /* bad packet */
+  
+      GETSHORT(qtype, p); 
+      GETSHORT(qclass, p);
+      GETLONG(ttl, p);
+      GETSHORT(rdlen, p);
+      
+      if (qclass == C_IN && qtype == T_A)
+	{
+	  if (!CHECK_LEN(header, p, qlen, INADDRSZ))
+	    return 0;
+	  
+	  for (baddrp = baddr; baddrp; baddrp = baddrp->next)
+	    if (memcmp(&baddrp->addr, p, INADDRSZ) == 0)
+	      {
+		/* Found a bogus address. Insert that info here, since there no SOA record
+		   to get the ttl from in the normal processing */
+		cache_start_insert();
+		cache_insert(name, NULL, now, ttl, F_IPV4 | F_FORWARD | F_NEG | F_NXDOMAIN | F_CONFIG);
+		cache_end_insert();
+		
+		return 1;
+	      }
+	}
+      
+      if (!ADD_RDLEN(header, p, qlen, rdlen))
+	return 0;
+    }
+  
+  return 0;
+}
+
+static int add_resource_record(HEADER *header, char *limit, int *truncp, unsigned int nameoffset, unsigned char **pp, 
+			       unsigned long ttl, unsigned int *offset, unsigned short type, unsigned short class, char *format, ...)
+{
+  va_list ap;
+  unsigned char *sav, *p = *pp;
+  int j;
+  unsigned short usval;
+  long lval;
+  char *sval;
+
+  if (truncp && *truncp)
+    return 0;
+
+  PUTSHORT(nameoffset | 0xc000, p);
+  PUTSHORT(type, p);
+  PUTSHORT(class, p);
+  PUTLONG(ttl, p);      /* TTL */
+
+  sav = p;              /* Save pointer to RDLength field */
+  PUTSHORT(0, p);       /* Placeholder RDLength */
+
+  va_start(ap, format);   /* make ap point to 1st unamed argument */
+  
+  for (; *format; format++)
+    switch (*format)
+      {
+#ifdef HAVE_IPV6
+      case '6':
+	sval = va_arg(ap, char *); 
+	memcpy(p, sval, IN6ADDRSZ);
+	p += IN6ADDRSZ;
+	break;
+#endif
+	
+      case '4':
+	sval = va_arg(ap, char *); 
+	memcpy(p, sval, INADDRSZ);
+	p += INADDRSZ;
+	break;
+	
+      case 's':
+	usval = va_arg(ap, int);
+	PUTSHORT(usval, p);
+	break;
+	
+      case 'l':
+	lval = va_arg(ap, long);
+	PUTLONG(lval, p);
+	break;
+	
+      case 'd':
+	/* get domain-name answer arg and store it in RDATA field */
+	if (offset)
+	  *offset = p - (unsigned char *)header;
+	p = do_rfc1035_name(p, va_arg(ap, char *));
+	*p++ = 0;
+	break;
+	
+      case 't':
+	usval = va_arg(ap, int);
+	sval = va_arg(ap, char *);
+	memcpy(p, sval, usval);
+	p += usval;
+	break;
+
+      case 'z':
+	sval = va_arg(ap, char *);
+	usval = sval ? strlen(sval) : 0;
+	if (usval > 255)
+	  usval = 255;
+	*p++ = (unsigned char)usval;
+	memcpy(p, sval, usval);
+	p += usval;
+	break;
+      }
+
+  va_end(ap);	/* clean up variable argument pointer */
+  
+  j = p - sav - 2;
+  PUTSHORT(j, sav);     /* Now, store real RDLength */
+  
+  /* check for overflow of buffer */
+  if (limit && ((unsigned char *)limit - p) < 0)
+    {
+      if (truncp)
+	*truncp = 1;
+      return 0;
+    }
+  
+  *pp = p;
+  return 1;
+}
+
+static unsigned long crec_ttl(struct crec *crecp, time_t now)
+{
+  /* Return 0 ttl for DHCP entries, which might change
+     before the lease expires. */
+
+  if  (crecp->flags & (F_IMMORTAL | F_DHCP))
+    return daemon->local_ttl;
+  
+  return crecp->ttd - now;
+}
+  
+
+/* return zero if we can't answer from cache, or packet size if we can */
+size_t answer_request(HEADER *header, char *limit, size_t qlen,  
+		      struct in_addr local_addr, struct in_addr local_netmask, time_t now) 
+{
+  char *name = daemon->namebuff;
+  unsigned char *p, *ansp, *pheader;
+  int qtype, qclass;
+  struct all_addr addr;
+  unsigned int nameoffset;
+  unsigned short flag;
+  int q, ans, anscount = 0, addncount = 0;
+  int dryrun = 0, sec_reqd = 0;
+  int is_sign;
+  struct crec *crecp;
+  int nxdomain = 0, auth = 1, trunc = 0;
+  struct mx_srv_record *rec;
+ 
+  /* If there is an RFC2671 pseudoheader then it will be overwritten by
+     partial replies, so we have to do a dry run to see if we can answer
+     the query. We check to see if the do bit is set, if so we always
+     forward rather than answering from the cache, which doesn't include
+     security information. */
+
+  if (find_pseudoheader(header, qlen, NULL, &pheader, &is_sign))
+    { 
+      unsigned short udpsz, ext_rcode, flags;
+      unsigned char *psave = pheader;
+
+      GETSHORT(udpsz, pheader);
+      GETSHORT(ext_rcode, pheader);
+      GETSHORT(flags, pheader);
+      
+      sec_reqd = flags & 0x8000; /* do bit */ 
+
+      /* If our client is advertising a larger UDP packet size
+	 than we allow, trim it so that we don't get an overlarge
+	 response from upstream */
+
+      if (!is_sign && (udpsz > daemon->edns_pktsz))
+	PUTSHORT(daemon->edns_pktsz, psave); 
+
+      dryrun = 1;
+    }
+
+  if (ntohs(header->qdcount) == 0 || header->opcode != QUERY )
+    return 0;
+  
+  for (rec = daemon->mxnames; rec; rec = rec->next)
+    rec->offset = 0;
+  
+ rerun:
+  /* determine end of question section (we put answers there) */
+  if (!(ansp = skip_questions(header, qlen)))
+    return 0; /* bad packet */
+   
+  /* now process each question, answers go in RRs after the question */
+  p = (unsigned char *)(header+1);
+
+  for (q = ntohs(header->qdcount); q != 0; q--)
+    {
+      /* save pointer to name for copying into answers */
+      nameoffset = p - (unsigned char *)header;
+
+      /* now extract name as .-concatenated string into name */
+      if (!extract_name(header, qlen, &p, name, 1, 4))
+	return 0; /* bad packet */
+            
+      GETSHORT(qtype, p); 
+      GETSHORT(qclass, p);
+
+      ans = 0; /* have we answered this question */
+      
+      if (qtype == T_TXT || qtype == T_ANY)
+	{
+	  struct txt_record *t;
+	  for(t = daemon->txt; t ; t = t->next)
+	    {
+	      if (t->class == qclass && hostname_isequal(name, t->name))
+		{
+		  ans = 1;
+		  if (!dryrun)
+		    {
+		      log_query(F_CNAME | F_FORWARD | F_CONFIG | F_NXDOMAIN, name, NULL, "<TXT>");
+		      if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, 
+					      daemon->local_ttl, NULL,
+					      T_TXT, t->class, "t", t->len, t->txt))
+			anscount++;
+
+		    }
+		}
+	    }
+	}
+
+      if (qclass == C_IN)
+	{
+	  if (qtype == T_PTR || qtype == T_ANY)
+	    {
+	      /* see if it's w.z.y.z.in-addr.arpa format */
+	      int is_arpa = in_arpa_name_2_addr(name, &addr);
+	      struct ptr_record *ptr;
+	      struct interface_name* intr = NULL;
+
+	      for (ptr = daemon->ptr; ptr; ptr = ptr->next)
+		if (hostname_isequal(name, ptr->name))
+		  break;
+
+	      if (is_arpa == F_IPV4)
+		for (intr = daemon->int_names; intr; intr = intr->next)
+		  {
+		    if (addr.addr.addr4.s_addr == get_ifaddr(intr->intr).s_addr)
+		      break;
+		    else
+		      while (intr->next && strcmp(intr->intr, intr->next->intr) == 0)
+			intr = intr->next;
+		  }
+	      
+	      if (intr)
+		{
+		  ans = 1;
+		  if (!dryrun)
+		    {
+		      log_query(F_IPV4 | F_REVERSE | F_CONFIG, intr->name, &addr, NULL);
+		      if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, 
+					      daemon->local_ttl, NULL,
+					      T_PTR, C_IN, "d", intr->name))
+			anscount++;
+		    }
+		}
+	      else if (ptr)
+		{
+		  ans = 1;
+		  if (!dryrun)
+		    {
+		      log_query(F_CNAME | F_FORWARD | F_CONFIG | F_NXDOMAIN, name, NULL, "<PTR>");
+		      for (ptr = daemon->ptr; ptr; ptr = ptr->next)
+			if (hostname_isequal(name, ptr->name) &&
+			    add_resource_record(header, limit, &trunc, nameoffset, &ansp, 
+						daemon->local_ttl, NULL,
+						T_PTR, C_IN, "d", ptr->ptr))
+			  anscount++;
+			 
+		    }
+		}
+	      else if ((crecp = cache_find_by_addr(NULL, &addr, now, is_arpa)))
+		do 
+		  { 
+		    /* don't answer wildcard queries with data not from /etc/hosts or dhcp leases */
+		    if (qtype == T_ANY && !(crecp->flags & (F_HOSTS | F_DHCP)))
+		      continue;
+		    
+		    if (crecp->flags & F_NEG)
+		      {
+			ans = 1;
+			auth = 0;
+			if (crecp->flags & F_NXDOMAIN)
+			  nxdomain = 1;
+			if (!dryrun)
+			  log_query(crecp->flags & ~F_FORWARD, name, &addr, NULL);
+		      }
+		    else if ((crecp->flags & (F_HOSTS | F_DHCP)) || !sec_reqd)
+		      {
+			ans = 1;
+			if (!(crecp->flags & (F_HOSTS | F_DHCP)))
+			  auth = 0;
+			if (!dryrun)
+			  {
+			    log_query(crecp->flags & ~F_FORWARD, cache_get_name(crecp), &addr, 
+				      record_source(crecp->uid));
+			    
+			    if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, 
+						    crec_ttl(crecp, now), NULL,
+						    T_PTR, C_IN, "d", cache_get_name(crecp)))
+			      anscount++;
+			  }
+		      }
+		  } while ((crecp = cache_find_by_addr(crecp, &addr, now, is_arpa)));
+	      else if (is_arpa == F_IPV4 && 
+		       (daemon->options & OPT_BOGUSPRIV) && 
+		       private_net(addr.addr.addr4))
+		{
+		  /* if not in cache, enabled and private IPV4 address, return NXDOMAIN */
+		  ans = 1;
+		  nxdomain = 1;
+		  if (!dryrun)
+		    log_query(F_CONFIG | F_REVERSE | F_IPV4 | F_NEG | F_NXDOMAIN, 
+			      name, &addr, NULL);
+		}
+	    }
+	    
+	  for (flag = F_IPV4; flag; flag = (flag == F_IPV4) ? F_IPV6 : 0)
+	    {
+	      unsigned short type = T_A;
+	      
+	      if (flag == F_IPV6)
+#ifdef HAVE_IPV6
+		type = T_AAAA;
+#else
+	        break;
+#endif
+	      
+	      if (qtype != type && qtype != T_ANY)
+		continue;
+	      
+	      /* Check for "A for A"  queries */
+	      if (qtype == T_A && (addr.addr.addr4.s_addr = inet_addr(name)) != (in_addr_t) -1)
+		{
+		  ans = 1;
+		  if (!dryrun)
+		    {
+		      log_query(F_FORWARD | F_CONFIG | F_IPV4, name, &addr, NULL);
+		      if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, 
+					      daemon->local_ttl, NULL, type, C_IN, "4", &addr))
+			anscount++;
+		    }
+		  continue;
+		}
+
+	      /* interface name stuff */
+	      if (qtype == T_A)
+		{
+		  struct interface_name *intr;
+
+		  for (intr = daemon->int_names; intr; intr = intr->next)
+		    if (hostname_isequal(name, intr->name))
+		      break;
+		  
+		  if (intr)
+		    {
+		      ans = 1;
+		      if (!dryrun)
+			{
+			  if ((addr.addr.addr4 = get_ifaddr(intr->intr)).s_addr == (in_addr_t) -1)
+			    log_query(F_FORWARD | F_CONFIG | F_IPV4 | F_NEG, name, NULL, NULL);
+			  else
+			    {
+			      log_query(F_FORWARD | F_CONFIG | F_IPV4, name, &addr, NULL);
+			      if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, 
+						      daemon->local_ttl, NULL, type, C_IN, "4", &addr))
+				anscount++;
+			    }
+			}
+		      continue;
+		    }
+		}
+
+	    cname_restart:
+	      if ((crecp = cache_find_by_name(NULL, name, now, flag | F_CNAME)))
+		{
+		  int localise = 0;
+		  
+		  /* See if a putative address is on the network from which we recieved
+		     the query, is so we'll filter other answers. */
+		  if (local_addr.s_addr != 0 && (daemon->options & OPT_LOCALISE) && flag == F_IPV4)
+		    {
+		      struct crec *save = crecp;
+		      do {
+			if ((crecp->flags & F_HOSTS) &&
+			    is_same_net(*((struct in_addr *)&crecp->addr), local_addr, local_netmask))
+			  {
+			    localise = 1;
+			    break;
+			  } 
+			} while ((crecp = cache_find_by_name(crecp, name, now, flag | F_CNAME)));
+		      crecp = save;
+		    }
+			  
+		  do
+		    { 
+		      /* don't answer wildcard queries with data not from /etc/hosts
+			 or DHCP leases */
+		      if (qtype == T_ANY && !(crecp->flags & (F_HOSTS | F_DHCP)))
+			break;
+		      
+		      if (crecp->flags & F_CNAME)
+			{
+			  if (!dryrun)
+			    {
+			      log_query(crecp->flags, name, NULL, record_source(crecp->uid));
+			      if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, 
+						      crec_ttl(crecp, now), &nameoffset,
+						      T_CNAME, C_IN, "d", cache_get_name(crecp->addr.cname.cache)))
+				anscount++;
+			    }
+			  
+			  strcpy(name, cache_get_name(crecp->addr.cname.cache));
+			  goto cname_restart;
+			}
+		      
+		      if (crecp->flags & F_NEG)
+			{
+			  ans = 1;
+			  auth = 0;
+			  if (crecp->flags & F_NXDOMAIN)
+			    nxdomain = 1;
+			  if (!dryrun)
+			    log_query(crecp->flags, name, NULL, NULL);
+			}
+		      else if ((crecp->flags & (F_HOSTS | F_DHCP)) || !sec_reqd)
+			{
+			  /* If we are returning local answers depending on network,
+			     filter here. */
+			  if (localise && 
+			      (crecp->flags & F_HOSTS) &&
+			      !is_same_net(*((struct in_addr *)&crecp->addr), local_addr, local_netmask))
+			    continue;
+       
+			  if (!(crecp->flags & (F_HOSTS | F_DHCP)))
+			    auth = 0;
+			  
+			  ans = 1;
+			  if (!dryrun)
+			    {
+			      log_query(crecp->flags & ~F_REVERSE, name, &crecp->addr.addr,
+					record_source(crecp->uid));
+			      
+			      if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, 
+						      crec_ttl(crecp, now), NULL, type, C_IN, 
+						      type == T_A ? "4" : "6", &crecp->addr))
+				anscount++;
+			    }
+			}
+		    } while ((crecp = cache_find_by_name(crecp, name, now, flag | F_CNAME)));
+		}
+	    }
+	  
+	  if (qtype == T_MX || qtype == T_ANY)
+	    {
+	      int found = 0;
+	      for (rec = daemon->mxnames; rec; rec = rec->next)
+		if (!rec->issrv && hostname_isequal(name, rec->name))
+		  {
+		  ans = found = 1;
+		  if (!dryrun)
+		    {
+		      unsigned int offset;
+		      log_query(F_CNAME | F_FORWARD | F_CONFIG | F_NXDOMAIN, name, NULL, "<MX>");
+		      if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, daemon->local_ttl,
+					      &offset, T_MX, C_IN, "sd", rec->weight, rec->target))
+			{
+			  anscount++;
+			  if (rec->target)
+			    rec->offset = offset;
+			}
+		    }
+		  }
+	      
+	      if (!found && (daemon->options & (OPT_SELFMX | OPT_LOCALMX)) && 
+		  cache_find_by_name(NULL, name, now, F_HOSTS | F_DHCP))
+		{ 
+		  ans = 1;
+		  if (!dryrun)
+		    {
+		      log_query(F_CNAME | F_FORWARD | F_CONFIG | F_NXDOMAIN, name, NULL, "<MX>");
+		      if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, daemon->local_ttl, NULL, 
+					      T_MX, C_IN, "sd", 1, 
+					      (daemon->options & OPT_SELFMX) ? name : daemon->mxtarget))
+			anscount++;
+		    }
+		}
+	    }
+	  	  
+	  if (qtype == T_SRV || qtype == T_ANY)
+	    {
+	      int found = 0;
+	      
+	      for (rec = daemon->mxnames; rec; rec = rec->next)
+		if (rec->issrv && hostname_isequal(name, rec->name))
+		  {
+		    found = ans = 1;
+		    if (!dryrun)
+		      {
+			unsigned int offset;
+			log_query(F_CNAME | F_FORWARD | F_CONFIG | F_NXDOMAIN, name, NULL, "<SRV>");
+			if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, daemon->local_ttl, 
+						&offset, T_SRV, C_IN, "sssd", 
+						rec->priority, rec->weight, rec->srvport, rec->target))
+			  {
+			    anscount++;
+			    if (rec->target)
+			      rec->offset = offset;
+			  }
+		      }
+		  }
+	      
+	      if (!found && (daemon->options & OPT_FILTER) &&  (qtype == T_SRV || (qtype == T_ANY && strchr(name, '_'))))
+		{
+		  ans = 1;
+		  if (!dryrun)
+		    log_query(F_CONFIG | F_NEG, name, NULL, NULL);
+		}
+	    }
+
+	  if (qtype == T_NAPTR || qtype == T_ANY)
+	    {
+	      struct naptr *na;
+	      for (na = daemon->naptr; na; na = na->next)
+		if (hostname_isequal(name, na->name))
+		  {
+		    ans = 1;
+		    if (!dryrun)
+		      {
+			log_query(F_CNAME | F_FORWARD | F_CONFIG | F_NXDOMAIN, name, NULL, "<NAPTR>");
+			if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, daemon->local_ttl, 
+						NULL, T_NAPTR, C_IN, "sszzzd", 
+						na->order, na->pref, na->flags, na->services, na->regexp, na->replace))
+			  anscount++;
+		      }
+		  }
+	    }
+	  
+	  if (qtype == T_MAILB)
+	    ans = 1, nxdomain = 1;
+
+	  if (qtype == T_SOA && (daemon->options & OPT_FILTER))
+	    {
+	      ans = 1; 
+	      if (!dryrun)
+		log_query(F_CONFIG | F_NEG, name, &addr, NULL);
+	    }
+	}
+
+      if (!ans)
+	return 0; /* failed to answer a question */
+    }
+  
+  if (dryrun)
+    {
+      dryrun = 0;
+      goto rerun;
+    }
+  
+  /* create an additional data section, for stuff in SRV and MX record replies. */
+  for (rec = daemon->mxnames; rec; rec = rec->next)
+    if (rec->offset != 0)
+      {
+	/* squash dupes */
+	struct mx_srv_record *tmp;
+	for (tmp = rec->next; tmp; tmp = tmp->next)
+	  if (tmp->offset != 0 && hostname_isequal(rec->target, tmp->target))
+	    tmp->offset = 0;
+	
+	crecp = NULL;
+	while ((crecp = cache_find_by_name(crecp, rec->target, now, F_IPV4 | F_IPV6)))
+	  {
+#ifdef HAVE_IPV6
+	    int type =  crecp->flags & F_IPV4 ? T_A : T_AAAA;
+#else
+	    int type = T_A;
+#endif
+	    if (crecp->flags & F_NEG)
+	      continue;
+
+	    if (add_resource_record(header, limit, NULL, rec->offset, &ansp, 
+				    crec_ttl(crecp, now), NULL, type, C_IN, 
+				    crecp->flags & F_IPV4 ? "4" : "6", &crecp->addr))
+	      addncount++;
+	  }
+      }
+  
+  /* done all questions, set up header and return length of result */
+  header->qr = 1; /* response */
+  header->aa = auth; /* authoritive - only hosts and DHCP derived names. */
+  header->ra = 1; /* recursion if available */
+  header->tc = trunc; /* truncation */
+  if (anscount == 0 && nxdomain)
+    header->rcode = NXDOMAIN;
+  else
+    header->rcode = NOERROR; /* no error */
+  header->ancount = htons(anscount);
+  header->nscount = htons(0);
+  header->arcount = htons(addncount);
+  return ansp - (unsigned char *)header;
+}
+
+
+
+
+
diff --git a/src/rfc2131.c b/src/rfc2131.c
new file mode 100755
index 0000000..1ec1bcf
--- /dev/null
+++ b/src/rfc2131.c
@@ -0,0 +1,2332 @@
+/* dnsmasq is Copyright (c) 2000-2009 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_DHCP
+
+#define BOOTREQUEST              1
+#define BOOTREPLY                2
+#define DHCP_COOKIE              0x63825363
+
+/* The Linux in-kernel DHCP client silently ignores any packet 
+   smaller than this. Sigh...........   */
+#define MIN_PACKETSZ             300
+
+#define OPTION_PAD               0
+#define OPTION_NETMASK           1
+#define OPTION_ROUTER            3
+#define OPTION_DNSSERVER         6
+#define OPTION_HOSTNAME          12
+#define OPTION_DOMAINNAME        15
+#define OPTION_BROADCAST         28
+#define OPTION_VENDOR_CLASS_OPT  43
+#define OPTION_REQUESTED_IP      50 
+#define OPTION_LEASE_TIME        51
+#define OPTION_OVERLOAD          52
+#define OPTION_MESSAGE_TYPE      53
+#define OPTION_SERVER_IDENTIFIER 54
+#define OPTION_REQUESTED_OPTIONS 55
+#define OPTION_MESSAGE           56
+#define OPTION_MAXMESSAGE        57
+#define OPTION_T1                58
+#define OPTION_T2                59
+#define OPTION_VENDOR_ID         60
+#define OPTION_CLIENT_ID         61
+#define OPTION_SNAME             66
+#define OPTION_FILENAME          67
+#define OPTION_USER_CLASS        77
+#define OPTION_CLIENT_FQDN       81
+#define OPTION_AGENT_ID          82
+#define OPTION_ARCH              93
+#define OPTION_PXE_UUID          97
+#define OPTION_SUBNET_SELECT     118
+#define OPTION_END               255
+
+#define SUBOPT_CIRCUIT_ID        1
+#define SUBOPT_REMOTE_ID         2
+#define SUBOPT_SUBNET_SELECT     5     /* RFC 3527 */
+#define SUBOPT_SUBSCR_ID         6     /* RFC 3393 */
+#define SUBOPT_SERVER_OR         11    /* RFC 5107 */
+
+#define SUBOPT_PXE_BOOT_ITEM     71    /* PXE standard */
+#define SUBOPT_PXE_DISCOVERY     6
+#define SUBOPT_PXE_SERVERS       8
+#define SUBOPT_PXE_MENU          9
+#define SUBOPT_PXE_MENU_PROMPT   10
+
+#define DHCPDISCOVER             1
+#define DHCPOFFER                2
+#define DHCPREQUEST              3
+#define DHCPDECLINE              4
+#define DHCPACK                  5
+#define DHCPNAK                  6
+#define DHCPRELEASE              7
+#define DHCPINFORM               8
+
+#define have_config(config, mask) ((config) && ((config)->flags & (mask))) 
+#define option_len(opt) ((int)(((unsigned char *)(opt))[1]))
+#define option_ptr(opt, i) ((void *)&(((unsigned char *)(opt))[2u+(unsigned int)(i)]))
+
+static int sanitise(unsigned char *opt, char *buf);
+static struct in_addr server_id(struct dhcp_context *context, struct in_addr override, struct in_addr fallback);
+static unsigned int calc_time(struct dhcp_context *context, struct dhcp_config *config, unsigned char *opt);
+static void option_put(struct dhcp_packet *mess, unsigned char *end, int opt, int len, unsigned int val);
+static void option_put_string(struct dhcp_packet *mess, unsigned char *end, 
+			      int opt, char *string, int null_term);
+static struct in_addr option_addr(unsigned char *opt);
+static struct in_addr option_addr_arr(unsigned char *opt, int offset);
+static unsigned int option_uint(unsigned char *opt, int i, int size);
+static void log_packet(char *type, void *addr, unsigned char *ext_mac, 
+		       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 clear_packet(struct dhcp_packet *mess, unsigned char *end);
+static void do_options(struct dhcp_context *context,
+		       struct dhcp_packet *mess,
+		       unsigned char *real_end, 
+		       unsigned char *req_options,
+		       char *hostname, 
+		       char *domain, char *config_domain,
+		       struct dhcp_netid *netid,
+		       struct in_addr subnet_addr,
+		       unsigned char fqdn_flags,
+		       int null_term, int pxearch,
+		       unsigned char *uuid);
+
+
+static void match_vendor_opts(unsigned char *opt, struct dhcp_opt *dopt); 
+static void do_encap_opts(struct dhcp_opt *opts, int encap, int flag, struct dhcp_packet *mess, unsigned char *end, int null_term);
+static void pxe_misc(struct dhcp_packet *mess, unsigned char *end, unsigned char *uuid);
+static int prune_vendor_opts(struct dhcp_netid *netid);
+static struct dhcp_opt *pxe_opts(int pxe_arch, struct dhcp_netid *netid);
+struct dhcp_boot *find_boot(struct dhcp_netid *netid);
+
+  
+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)
+{
+  unsigned char *opt, *clid = NULL;
+  struct dhcp_lease *ltmp, *lease = NULL;
+  struct dhcp_vendor *vendor;
+  struct dhcp_mac *mac;
+  struct dhcp_netid_list *id_list;
+  int clid_len = 0, ignore = 0, do_classes = 0, selecting = 0, pxearch = -1;
+  struct dhcp_packet *mess = (struct dhcp_packet *)daemon->dhcp_packet.iov_base;
+  unsigned char *end = (unsigned char *)(mess + 1); 
+  unsigned char *real_end = (unsigned char *)(mess + 1); 
+  char *hostname = NULL, *offer_hostname = NULL, *client_hostname = NULL, *domain = NULL;
+  int hostname_auth = 0, borken_opt = 0;
+  unsigned char *req_options = NULL;
+  char *message = NULL;
+  unsigned int time;
+  struct dhcp_config *config;
+  struct dhcp_netid *netid;
+  struct in_addr subnet_addr, fallback, override;
+  unsigned short fuzz = 0;
+  unsigned int mess_type = 0;
+  unsigned char fqdn_flags = 0;
+  unsigned char *agent_id = NULL, *uuid = NULL;
+  unsigned char *emac = NULL;
+  int emac_len = 0;
+  struct dhcp_netid known_id, iface_id;
+  struct dhcp_opt *o;
+  unsigned char pxe_uuid[17];
+
+  subnet_addr.s_addr = override.s_addr = 0;
+
+  /* set tag with name == interface */
+  iface_id.net = iface_name;
+  iface_id.next = NULL;
+  netid = &iface_id; 
+  
+  if (mess->op != BOOTREQUEST || mess->hlen > DHCP_CHADDR_MAX)
+    return 0;
+   
+  if (mess->htype == 0 && mess->hlen != 0)
+    return 0;
+
+  /* check for DHCP rather than BOOTP */
+  if ((opt = option_find(mess, sz, OPTION_MESSAGE_TYPE, 1)))
+    {
+      mess_type = option_uint(opt, 0, 1);
+
+      /* only insist on a cookie for DHCP. */
+      if (*((u32 *)&mess->options) != htonl(DHCP_COOKIE))
+	return 0;
+
+      /* two things to note here: expand_buf may move the packet,
+	 so reassign mess from daemon->packet. Also, the size
+	 sent includes the IP and UDP headers, hence the magic "-28" */
+      if ((opt = option_find(mess, sz, OPTION_MAXMESSAGE, 2)))
+	{
+	  size_t size = (size_t)option_uint(opt, 0, 2) - 28;
+	  
+	  if (size > DHCP_PACKET_MAX)
+	    size = DHCP_PACKET_MAX;
+	  else if (size < sizeof(struct dhcp_packet))
+	    size = sizeof(struct dhcp_packet);
+	  
+	  if (expand_buf(&daemon->dhcp_packet, size))
+	    {
+	      mess = (struct dhcp_packet *)daemon->dhcp_packet.iov_base;
+	      real_end = end = ((unsigned char *)mess) + size;
+	    }
+	}
+
+      /* Some buggy clients set ciaddr when they shouldn't, so clear that here since
+	 it can affect the context-determination code. */
+      if ((option_find(mess, sz, OPTION_REQUESTED_IP, INADDRSZ) || mess_type == DHCPDISCOVER))
+	mess->ciaddr.s_addr = 0;
+
+      if ((opt = option_find(mess, sz, OPTION_AGENT_ID, 1)))
+	{
+	  /* Any agent-id needs to be copied back out, verbatim, as the last option
+	     in the packet. Here, we shift it to the very end of the buffer, if it doesn't
+	     get overwritten, then it will be shuffled back at the end of processing.
+	     Note that the incoming options must not be overwritten here, so there has to 
+	     be enough free space at the end of the packet to copy the option. */
+	  unsigned char *sopt;
+	  unsigned int total = option_len(opt) + 2;
+	  unsigned char *last_opt = option_find(mess, sz, OPTION_END, 0);
+	  if (last_opt && last_opt < end - total)
+	    {
+	      end -= total;
+	      agent_id = end;
+	      memcpy(agent_id, opt, total);
+	    }
+
+	  /* look for RFC3527 Link selection sub-option */
+	  if ((sopt = option_find1(option_ptr(opt, 0), option_ptr(opt, option_len(opt)), SUBOPT_SUBNET_SELECT, INADDRSZ)))
+	    subnet_addr = option_addr(sopt);
+
+	  /* look for RFC5107 server-identifier-override */
+	  if ((sopt = option_find1(option_ptr(opt, 0), option_ptr(opt, option_len(opt)), SUBOPT_SERVER_OR, INADDRSZ)))
+	    override = option_addr(sopt);
+	  
+	  /* if a circuit-id or remote-is option is provided, exact-match to options. */ 
+	  for (vendor = daemon->dhcp_vendors; vendor; vendor = vendor->next)
+	    {
+	      int search;
+	      
+	      if (vendor->match_type == MATCH_CIRCUIT)
+		search = SUBOPT_CIRCUIT_ID;
+	      else if (vendor->match_type == MATCH_REMOTE)
+		search = SUBOPT_REMOTE_ID;
+	      else if (vendor->match_type == MATCH_SUBSCRIBER)
+		search = SUBOPT_SUBSCR_ID;
+	      else 
+		continue;
+
+	      if ((sopt = option_find1(option_ptr(opt, 0), option_ptr(opt, option_len(opt)), search, 1)) &&
+		  vendor->len == option_len(sopt) &&
+		  memcmp(option_ptr(sopt, 0), vendor->data, vendor->len) == 0)
+		{
+		  vendor->netid.next = netid;
+		  netid = &vendor->netid;
+		  break;
+		} 
+	    }
+	}
+
+      /* Check for RFC3011 subnet selector - only if RFC3527 one not present */
+      if (subnet_addr.s_addr == 0 && (opt = option_find(mess, sz, OPTION_SUBNET_SELECT, INADDRSZ)))
+	subnet_addr = option_addr(opt);
+      
+      /* If there is no client identifier option, use the hardware address */
+      if ((opt = option_find(mess, sz, OPTION_CLIENT_ID, 1)))
+	{
+	  clid_len = option_len(opt);
+	  clid = option_ptr(opt, 0);
+	}
+
+      /* do we have a lease in store? */
+      lease = lease_find_by_client(mess->chaddr, mess->hlen, mess->htype, clid, clid_len);
+
+      /* If this request is missing a clid, but we've seen one before, 
+	 use it again for option matching etc. */
+      if (lease && !clid && lease->clid)
+	{
+	  clid_len = lease->clid_len;
+	  clid = lease->clid;
+	}
+
+      /* find mac to use for logging and hashing */
+      emac = extended_hwaddr(mess->htype, mess->hlen, mess->chaddr, clid_len, clid, &emac_len);
+    }
+  
+  for (mac = daemon->dhcp_macs; mac; mac = mac->next)
+    if (mac->hwaddr_len == mess->hlen &&
+	(mac->hwaddr_type == mess->htype || mac->hwaddr_type == 0) &&
+	memcmp_masked(mac->hwaddr, mess->chaddr, mess->hlen, mac->mask))
+      {
+	mac->netid.next = netid;
+	netid = &mac->netid;
+      }
+  
+  /* Determine network for this packet. Our caller will have already linked all the 
+     contexts which match the addresses of the receiving interface but if the 
+     machine has an address already, or came via a relay, or we have a subnet selector, 
+     we search again. If we don't have have a giaddr or explicit subnet selector, 
+     use the ciaddr. This is necessary because a  machine which got a lease via a 
+     relay won't use the relay to renew. If matching a ciaddr fails but we have a context 
+     from the physical network, continue using that to allow correct DHCPNAK generation later. */
+  if (mess->giaddr.s_addr || subnet_addr.s_addr || mess->ciaddr.s_addr)
+    {
+      struct dhcp_context *context_tmp, *context_new = NULL;
+      struct in_addr addr;
+      int force = 0;
+      
+      if (subnet_addr.s_addr)
+	{
+	  addr = subnet_addr;
+	  force = 1;
+	}
+      else if (mess->giaddr.s_addr)
+	{
+	  addr = mess->giaddr;
+	  force = 1;
+	}
+      else
+	{
+	  /* If ciaddr is in the hardware derived set of contexts, leave that unchanged */
+	  addr = mess->ciaddr;
+	  for (context_tmp = context; context_tmp; context_tmp = context_tmp->current)
+	    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_new = context;
+		break;
+	      }
+	} 
+		
+      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;
+	    }
+      
+      if (context_new || force)
+	context = context_new;
+      
+    }
+  
+  if (!context)
+    {
+      my_syslog(MS_DHCP | LOG_WARNING, _("no address range available for DHCP request %s %s"), 
+		subnet_addr.s_addr ? _("with subnet selector") : _("via"),
+		subnet_addr.s_addr ? inet_ntoa(subnet_addr) : (mess->giaddr.s_addr ? inet_ntoa(mess->giaddr) : iface_name));
+      return 0;
+    }
+
+  /* keep _a_ local address available. */
+  fallback = context->local;
+  
+  if (daemon->options & OPT_LOG_OPTS)
+    {
+      struct dhcp_context *context_tmp;
+      for (context_tmp = context; context_tmp; context_tmp = context_tmp->current)
+	{
+	  strcpy(daemon->namebuff, inet_ntoa(context_tmp->start));
+	  if (context_tmp->flags & (CONTEXT_STATIC | CONTEXT_PROXY))
+	    my_syslog(MS_DHCP | LOG_INFO, _("%u Available DHCP subnet: %s/%s"),
+		      ntohl(mess->xid), daemon->namebuff, inet_ntoa(context_tmp->netmask));
+	  else
+	    my_syslog(MS_DHCP | LOG_INFO, _("%u Available DHCP range: %s -- %s"), 
+		      ntohl(mess->xid), daemon->namebuff, inet_ntoa(context_tmp->end));
+	}
+    }
+
+  mess->op = BOOTREPLY;
+  
+  config = find_config(daemon->dhcp_conf, context, clid, clid_len, 
+		       mess->chaddr, mess->hlen, mess->htype, NULL);
+
+  /* set "known" tag for known hosts */
+  if (config)
+    {
+      known_id.net = "known";
+      known_id.next = netid;
+      netid = &known_id;
+    }
+  
+  if (mess_type == 0)
+    {
+      /* BOOTP request */
+      struct dhcp_netid id, bootp_id;
+      struct in_addr *logaddr = NULL;
+
+      /* must have a MAC addr for bootp */
+      if (mess->htype == 0 || mess->hlen == 0 || (context->flags & CONTEXT_PROXY))
+	return 0;
+      
+      if (have_config(config, CONFIG_DISABLE))
+	message = _("disabled");
+
+      end = mess->options + 64; /* BOOTP vend area is only 64 bytes */
+            
+      if (have_config(config, CONFIG_NAME))
+	{
+	  hostname = config->hostname;
+	  domain = config->domain;
+	}
+
+      if (have_config(config, CONFIG_NETID))
+	{
+	  config->netid.next = netid;
+	  netid = &config->netid;
+	}
+
+      /* Match incoming filename field as a netid. */
+      if (mess->file[0])
+	{
+	  memcpy(daemon->dhcp_buff2, mess->file, sizeof(mess->file));
+	  daemon->dhcp_buff2[sizeof(mess->file) + 1] = 0; /* ensure zero term. */
+	  id.net = (char *)daemon->dhcp_buff2;
+	  id.next = netid;
+	  netid = &id;
+	}
+
+      /* Add "bootp" as a tag to allow different options, address ranges etc
+	 for BOOTP clients */
+      bootp_id.net = "bootp";
+      bootp_id.next = netid;
+      netid = &bootp_id;
+      
+      for (id_list = daemon->dhcp_ignore; id_list; id_list = id_list->next)
+	if (match_netid(id_list->list, netid, 0))
+	  message = _("ignored");
+      
+      if (!message)
+	{
+	  int nailed = 0;
+
+	  if (have_config(config, CONFIG_ADDR))
+	    {
+	      nailed = 1;
+	      logaddr = &config->addr;
+	      mess->yiaddr = config->addr;
+	      if ((lease = lease_find_by_addr(config->addr)) &&
+		  (lease->hwaddr_len != mess->hlen ||
+		   lease->hwaddr_type != mess->htype ||
+		   memcmp(lease->hwaddr, mess->chaddr, lease->hwaddr_len) != 0))
+		message = _("address in use");
+	    }
+	  else
+	    {
+	      if (!(lease = lease_find_by_client(mess->chaddr, mess->hlen, mess->htype, NULL, 0)) ||
+		  !address_available(context, lease->addr, netid))
+		{
+		   if (lease)
+		     {
+		       /* lease exists, wrong network. */
+		       lease_prune(lease, now);
+		       lease = NULL;
+		     }
+		   if (!address_allocate(context, &mess->yiaddr, mess->chaddr, mess->hlen, netid, now))
+		     message = _("no address available");
+		}
+	      else
+		mess->yiaddr = lease->addr;
+	    }
+	  
+	  if (!message && !(context = narrow_context(context, mess->yiaddr, netid)))
+	    message = _("wrong network");
+	  else if (context->netid.net)
+	    {
+	      context->netid.next = netid;
+	      netid = &context->netid;
+	    }	 
+	  
+	  if (!message && !nailed)
+	    {
+	      for (id_list = daemon->bootp_dynamic; id_list; id_list = id_list->next)
+		if ((!id_list->list) || match_netid(id_list->list, netid, 0))
+		  break;
+	      if (!id_list)
+		message = _("no address configured");
+	    }
+
+	  if (!message && 
+	      !lease && 
+	      (!(lease = lease_allocate(mess->yiaddr))))
+	    message = _("no leases left");
+	  
+	  if (!message)
+	    {
+	      logaddr = &mess->yiaddr;
+		
+	      lease_set_hwaddr(lease, mess->chaddr, NULL, mess->hlen, mess->htype, 0);
+	      if (hostname)
+		lease_set_hostname(lease, hostname, 1); 
+	      /* infinite lease unless nailed in dhcp-host line. */
+	      lease_set_expires(lease,  
+				have_config(config, CONFIG_TIME) ? config->lease_time : 0xffffffff, 
+				now); 
+	      lease_set_interface(lease, int_index);
+	      
+	      clear_packet(mess, end);
+	      do_options(context, mess, end, NULL, hostname, get_domain(mess->yiaddr), 
+			 domain, netid, subnet_addr, 0, 0, 0, NULL);
+	    }
+	}
+      
+      log_packet("BOOTP", logaddr, mess->chaddr, mess->hlen, iface_name, message, mess->xid);
+      
+      return message ? 0 : dhcp_packet_size(mess, netid, agent_id, real_end);
+    }
+      
+  if ((opt = option_find(mess, sz, OPTION_CLIENT_FQDN, 4)))
+    {
+      /* http://tools.ietf.org/wg/dhc/draft-ietf-dhc-fqdn-option/draft-ietf-dhc-fqdn-option-10.txt */
+      int len = option_len(opt);
+      char *pq = daemon->dhcp_buff;
+      unsigned char *pp, *op = option_ptr(opt, 0);
+      
+      fqdn_flags = *op;
+      len -= 3;
+      op += 3;
+      pp = op;
+      
+      /* Always force update, since the client has no way to do it itself. */
+      if (!(fqdn_flags & 0x01))
+	fqdn_flags |= 0x02;
+      
+      fqdn_flags &= ~0x08;
+      fqdn_flags |= 0x01;
+      
+      if (fqdn_flags & 0x04)
+	while (*op != 0 && ((op + (*op) + 1) - pp) < len)
+	  {
+	    memcpy(pq, op+1, *op);
+	    pq += *op;
+	    op += (*op)+1;
+	    *(pq++) = '.';
+	  }
+      else
+	{
+	  memcpy(pq, op, len);
+	  if (len > 0 && op[len-1] == 0)
+	    borken_opt = 1;
+	  pq += len + 1;
+	}
+      
+      if (pq != daemon->dhcp_buff)
+	pq--;
+      
+      *pq = 0;
+      
+      if (legal_hostname(daemon->dhcp_buff))
+	offer_hostname = client_hostname = daemon->dhcp_buff;
+    }
+  else if ((opt = option_find(mess, sz, OPTION_HOSTNAME, 1)))
+    {
+      int len = option_len(opt);
+      memcpy(daemon->dhcp_buff, option_ptr(opt, 0), len);
+      /* Microsoft clients are broken, and need zero-terminated strings
+	 in options. We detect this state here, and do the same in
+	 any options we send */
+      if (len > 0 && daemon->dhcp_buff[len-1] == 0)
+	borken_opt = 1;
+      else
+	daemon->dhcp_buff[len] = 0;
+      if (legal_hostname(daemon->dhcp_buff))
+	client_hostname = daemon->dhcp_buff;
+    }
+
+  if (client_hostname && daemon->options & OPT_LOG_OPTS)
+    my_syslog(MS_DHCP | LOG_INFO, _("%u client provides name: %s"), ntohl(mess->xid), client_hostname);
+  
+  if (have_config(config, CONFIG_NAME))
+    {
+      hostname = config->hostname;
+      domain = config->domain;
+      hostname_auth = 1;
+      /* be careful not to send an OFFER with a hostname not matching the DISCOVER. */
+      if (fqdn_flags != 0 || !client_hostname || hostname_isequal(hostname, client_hostname))
+        offer_hostname = hostname;
+    }
+  else if (client_hostname)
+    {
+      domain = strip_hostname(client_hostname);
+      
+      if (strlen(client_hostname) != 0)
+	{
+	  hostname = client_hostname;
+	  if (!config)
+	    {
+	      /* Search again now we have a hostname. 
+		 Only accept configs without CLID and HWADDR here, (they won't match)
+		 to avoid impersonation by name. */
+	      struct dhcp_config *new = find_config(daemon->dhcp_conf, context, NULL, 0,
+						    mess->chaddr, mess->hlen, 
+						    mess->htype, hostname);
+	      if (new && !have_config(new, CONFIG_CLID) && !new->hwaddr)
+		{
+		  config = new;
+		  /* set "known" tag for known hosts */
+		  known_id.net = "known";
+		  known_id.next = netid;
+		  netid = &known_id;
+		}
+	    }
+	}
+    }
+  
+  if (have_config(config, CONFIG_NETID))
+    {
+      config->netid.next = netid;
+      netid = &config->netid;
+    }
+  
+  /* dhcp-match. If we have hex-and-wildcards, look for a left-anchored match.
+     Otherwise assume the option is an array, and look for a matching element. 
+     If no data given, existance of the option is enough. */
+  for (o = daemon->dhcp_match; o; o = o->next)
+    {
+      int i, matched = 0;
+      
+      if (!(opt = option_find(mess, sz, o->opt, 1)) ||
+	  o->len > option_len(opt))
+	continue;
+      
+      if (o->len == 0)
+	matched = 1;
+      else if (o->flags & DHOPT_HEX)
+	{ 
+	  if (memcmp_masked(o->val, option_ptr(opt, 0), o->len, o->u.wildcard_mask))
+	    matched = 1;
+	}
+      else 
+	for (i = 0; i <= (option_len(opt) - o->len); ) 
+	  {
+	    if (memcmp(o->val, option_ptr(opt, i), o->len) == 0)
+	      {
+		matched = 1;
+		break;
+	      }
+
+	    if (o->flags & DHOPT_STRING)
+	      i++;
+	    else
+	      i += o->len;
+	  }
+      
+      if (matched)
+	{
+	  o->netid->next = netid;
+	  netid = o->netid;
+	}
+    }
+	
+  /* user-class options are, according to RFC3004, supposed to contain
+     a set of counted strings. Here we check that this is so (by seeing
+     if the counts are consistent with the overall option length) and if
+     so zero the counts so that we don't get spurious matches between 
+     the vendor string and the counts. If the lengths don't add up, we
+     assume that the option is a single string and non RFC3004 compliant 
+     and just do the substring match. dhclient provides these broken options.
+     The code, later, which sends user-class data to the lease-change script
+     relies on the transformation done here.
+  */
+
+  if ((opt = option_find(mess, sz, OPTION_USER_CLASS, 1)))
+    {
+      unsigned char *ucp = option_ptr(opt, 0);
+      int tmp, j;
+      for (j = 0; j < option_len(opt); j += ucp[j] + 1);
+      if (j == option_len(opt))
+	for (j = 0; j < option_len(opt); j = tmp)
+	  {
+	    tmp = j + ucp[j] + 1;
+	    ucp[j] = 0;
+	  }
+    }
+    
+  for (vendor = daemon->dhcp_vendors; vendor; vendor = vendor->next)
+    {
+      int mopt;
+      
+      if (vendor->match_type == MATCH_VENDOR)
+	mopt = OPTION_VENDOR_ID;
+      else if (vendor->match_type == MATCH_USER)
+	mopt = OPTION_USER_CLASS; 
+      else
+	continue;
+
+      if ((opt = option_find(mess, sz, mopt, 1)))
+	{
+	  int i;
+	  for (i = 0; i <= (option_len(opt) - vendor->len); i++)
+	    if (memcmp(vendor->data, option_ptr(opt, i), vendor->len) == 0)
+	      {
+		vendor->netid.next = netid;
+		netid = &vendor->netid;
+		break;
+	      }
+	}
+    }
+
+  /* mark vendor-encapsulated options which match the client-supplied vendor class */
+  match_vendor_opts(option_find(mess, sz, OPTION_VENDOR_ID, 1), daemon->dhcp_opts);
+    
+  if (daemon->options & OPT_LOG_OPTS)
+    {
+      if (sanitise(option_find(mess, sz, OPTION_VENDOR_ID, 1), daemon->namebuff))
+	my_syslog(MS_DHCP | LOG_INFO, _("%u Vendor class: %s"), ntohl(mess->xid), daemon->namebuff);
+      if (sanitise(option_find(mess, sz, OPTION_USER_CLASS, 1), daemon->namebuff))
+	my_syslog(MS_DHCP | LOG_INFO, _("%u User class: %s"), ntohl(mess->xid), daemon->namebuff);
+    }
+
+  /* if all the netids in the ignore list are present, ignore this client */
+  for (id_list = daemon->dhcp_ignore; id_list; id_list = id_list->next)
+    if (match_netid(id_list->list, netid, 0))
+      ignore = 1;
+   
+  /* Can have setting to ignore the client ID for a particular MAC address or hostname */
+  if (have_config(config, CONFIG_NOCLID))
+    clid = NULL;
+          
+  /* Check if client is PXE client. */
+  if (daemon->enable_pxe && 
+      (opt = option_find(mess, sz, OPTION_VENDOR_ID, 9)) && 
+      strncmp(option_ptr(opt, 0), "PXEClient", 9) == 0)
+    {
+      if ((opt = option_find(mess, sz, OPTION_PXE_UUID, 17)))
+	{
+	  memcpy(pxe_uuid, option_ptr(opt, 0), 17);
+	  uuid = pxe_uuid;
+	}
+
+      /* Check if this is really a PXE bootserver request, and handle specially if so. */
+      if ((mess_type == DHCPREQUEST || mess_type == DHCPINFORM) &&
+	  (opt = option_find(mess, sz, OPTION_VENDOR_CLASS_OPT, 1)) &&
+	  (opt = option_find1(option_ptr(opt, 0), option_ptr(opt, option_len(opt)), SUBOPT_PXE_BOOT_ITEM, 4)))
+	{
+	  struct pxe_service *service;
+	  int type = option_uint(opt, 0, 2);
+	  int layer = option_uint(opt, 2, 2);
+	  unsigned char save71[4];
+	  struct dhcp_opt opt71;
+
+	  if (ignore)
+	    return 0;
+
+	  if (layer & 0x8000)
+	    {
+	      my_syslog(MS_DHCP | LOG_ERR, _("PXE BIS not supported"));
+	      return 0;
+	    }
+
+	  memcpy(save71, option_ptr(opt, 0), 4);
+	  
+	  for (service = daemon->pxe_services; service; service = service->next)
+	    if (service->type == type)
+	      break;
+	  
+	  if (!service || !service->basename)
+	    return 0;
+	  
+	  clear_packet(mess, end);
+	  
+	  mess->yiaddr = mess->ciaddr;
+	  mess->ciaddr.s_addr = 0;
+	  if (service->server.s_addr != 0)
+	    mess->siaddr = service->server; 
+	  else
+	    mess->siaddr = context->local; 
+	  
+	  snprintf((char *)mess->file, sizeof(mess->file), "%s.%d", service->basename, layer);
+	  option_put(mess, end, OPTION_MESSAGE_TYPE, 1, DHCPACK);
+	  option_put(mess, end, OPTION_SERVER_IDENTIFIER, INADDRSZ, htonl(context->local.s_addr));
+	  pxe_misc(mess, end, uuid);
+	  
+	  prune_vendor_opts(netid);
+	  opt71.val = save71;
+	  opt71.opt = SUBOPT_PXE_BOOT_ITEM;
+	  opt71.len = 4;
+	  opt71.flags = DHOPT_VENDOR_MATCH;
+	  opt71.netid = NULL;
+	  opt71.next = daemon->dhcp_opts;
+	  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, netid, agent_id, real_end);	  
+	}
+      
+      if ((opt = option_find(mess, sz, OPTION_ARCH, 2)))
+	{
+	  pxearch = option_uint(opt, 0, 2);
+
+	  /* proxy DHCP here. The DHCPREQUEST stuff is for gPXE */
+	  if ((mess_type == DHCPDISCOVER || mess_type == DHCPREQUEST) && 
+	      (context->flags & CONTEXT_PROXY))
+	    {
+	      struct dhcp_boot *boot = find_boot(netid);
+
+	      mess->yiaddr.s_addr = 0;
+	      if  (mess_type == DHCPDISCOVER || mess->ciaddr.s_addr == 0)
+		{
+		  mess->ciaddr.s_addr = 0;
+		  mess->flags |= htons(0x8000); /* broadcast */
+		}
+
+	      clear_packet(mess, end);
+	      
+	      /* Provide the bootfile here, for gPXE, and in case we have no menu items
+		 and set discovery_control = 8 */
+	      if (boot)
+		{
+		  if (boot->next_server.s_addr)
+		    mess->siaddr = boot->next_server;
+		  
+		  if (boot->file)
+		    strncpy((char *)mess->file, boot->file, sizeof(mess->file)-1);
+		}
+
+	      option_put(mess, end, OPTION_MESSAGE_TYPE, 1, 
+			 mess_type == DHCPDISCOVER ? DHCPOFFER : DHCPACK);
+	      option_put(mess, end, OPTION_SERVER_IDENTIFIER, INADDRSZ, htonl(context->local.s_addr));
+	      pxe_misc(mess, end, uuid);
+	      prune_vendor_opts(netid);
+	      do_encap_opts(pxe_opts(pxearch, netid), OPTION_VENDOR_CLASS_OPT, DHOPT_VENDOR_MATCH, mess, end, 0);
+	      
+	      log_packet("PXE", NULL, emac, emac_len, iface_name, ignore ? "proxy" : "proxy-ignored", mess->xid);
+	      return ignore ? 0 : dhcp_packet_size(mess, netid, agent_id, real_end);	  
+	    }
+	}
+    }
+
+  /* if we're just a proxy server, go no further */
+  if (context->flags & CONTEXT_PROXY)
+    return 0;
+  
+  if ((opt = option_find(mess, sz, OPTION_REQUESTED_OPTIONS, 0)))
+    {
+      req_options = (unsigned char *)daemon->dhcp_buff2;
+      memcpy(req_options, option_ptr(opt, 0), option_len(opt));
+      req_options[option_len(opt)] = OPTION_END;
+    }
+  
+  switch (mess_type)
+    {
+    case DHCPDECLINE:
+      if (!(opt = option_find(mess, sz, OPTION_SERVER_IDENTIFIER, INADDRSZ)) ||
+	  option_addr(opt).s_addr != server_id(context, override, fallback).s_addr)
+	return 0;
+      
+      /* sanitise any message. Paranoid? Moi? */
+      sanitise(option_find(mess, sz, OPTION_MESSAGE, 1), daemon->dhcp_buff);
+      
+      if (!(opt = option_find(mess, sz, OPTION_REQUESTED_IP, INADDRSZ)))
+	return 0;
+      
+      log_packet("DHCPDECLINE", option_ptr(opt, 0), emac, emac_len, iface_name, daemon->dhcp_buff, mess->xid);
+      
+      if (lease && lease->addr.s_addr == option_addr(opt).s_addr)
+	lease_prune(lease, now);
+      
+      if (have_config(config, CONFIG_ADDR) && 
+	  config->addr.s_addr == option_addr(opt).s_addr)
+	{
+	  prettyprint_time(daemon->dhcp_buff, DECLINE_BACKOFF);
+	  my_syslog(MS_DHCP | LOG_WARNING, _("disabling DHCP static address %s for %s"), 
+		    inet_ntoa(config->addr), daemon->dhcp_buff);
+	  config->flags |= CONFIG_DECLINED;
+	  config->decline_time = now;
+	}
+      else
+	/* make sure this host gets a different address next time. */
+	for (; context; context = context->current)
+	  context->addr_epoch++;
+      
+      return 0;
+
+    case DHCPRELEASE:
+      if (!(context = narrow_context(context, mess->ciaddr, netid)) ||
+	  !(opt = option_find(mess, sz, OPTION_SERVER_IDENTIFIER, INADDRSZ)) ||
+	  option_addr(opt).s_addr != server_id(context, override, fallback).s_addr)
+	return 0;
+      
+      if (lease && lease->addr.s_addr == mess->ciaddr.s_addr)
+	lease_prune(lease, now);
+      else
+	message = _("unknown lease");
+
+      log_packet("DHCPRELEASE", &mess->ciaddr, emac, emac_len, iface_name, message, mess->xid);
+	
+      return 0;
+      
+    case DHCPDISCOVER:
+      if (ignore || have_config(config, CONFIG_DISABLE))
+	{
+	  message = _("ignored");
+	  opt = NULL;
+	}
+      else 
+	{
+	  struct in_addr addr, conf;
+	  
+	  addr.s_addr = conf.s_addr = 0;
+
+	  if ((opt = option_find(mess, sz, OPTION_REQUESTED_IP, INADDRSZ)))	 
+	    addr = option_addr(opt);
+	  
+	  if (have_config(config, CONFIG_ADDR))
+	    {
+	      char *addrs = inet_ntoa(config->addr);
+	      
+	      if ((ltmp = lease_find_by_addr(config->addr)) && 
+		  ltmp != lease &&
+		  !config_has_mac(config, ltmp->hwaddr, ltmp->hwaddr_len, ltmp->hwaddr_type))
+		{
+		  int len;
+		  unsigned char *mac = extended_hwaddr(ltmp->hwaddr_type, ltmp->hwaddr_len,
+						       ltmp->hwaddr, ltmp->clid_len, ltmp->clid, &len);
+		  my_syslog(MS_DHCP | LOG_WARNING, _("not using configured address %s because it is leased to %s"),
+			    addrs, print_mac(daemon->namebuff, mac, len));
+		}
+	      else
+		{
+		  struct dhcp_context *tmp;
+		  for (tmp = context; tmp; tmp = tmp->current)
+		    if (context->router.s_addr == config->addr.s_addr)
+		      break;
+		  if (tmp)
+		    my_syslog(MS_DHCP | LOG_WARNING, _("not using configured address %s because it is in use by the server or relay"), addrs);
+		  else if (have_config(config, CONFIG_DECLINED) &&
+			   difftime(now, config->decline_time) < (float)DECLINE_BACKOFF)
+		    my_syslog(MS_DHCP | LOG_WARNING, _("not using configured address %s because it was previously declined"), addrs);
+		  else
+		    conf = config->addr;
+		}
+	    }
+	  
+	  if (conf.s_addr)
+	    mess->yiaddr = conf;
+	  else if (lease && 
+		   address_available(context, lease->addr, netid) && 
+		   !config_find_by_address(daemon->dhcp_conf, lease->addr))
+	    mess->yiaddr = lease->addr;
+	  else if (opt && address_available(context, addr, netid) && !lease_find_by_addr(addr) && 
+		   !config_find_by_address(daemon->dhcp_conf, addr))
+	    mess->yiaddr = addr;
+	  else if (emac_len == 0)
+	    message = _("no unique-id");
+	  else if (!address_allocate(context, &mess->yiaddr, emac, emac_len, netid, now))
+	    message = _("no address available");      
+	}
+      
+      log_packet("DHCPDISCOVER", opt ? option_ptr(opt, 0) : NULL, emac, emac_len, iface_name, message, mess->xid); 
+
+      if (message || !(context = narrow_context(context, mess->yiaddr, 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;
+	}
+       
+      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);
+      option_put(mess, end, OPTION_SERVER_IDENTIFIER, INADDRSZ, ntohl(server_id(context, override, fallback).s_addr));
+      option_put(mess, end, OPTION_LEASE_TIME, 4, time);
+      /* T1 and T2 are required in DHCPOFFER by HP's wacky Jetdirect client. */
+      if (time != 0xffffffff)
+	{
+	  option_put(mess, end, OPTION_T1, 4, (time/2));
+	  option_put(mess, end, OPTION_T2, 4, (time*7)/8);
+	}
+      do_options(context, mess, end, req_options, offer_hostname, get_domain(mess->yiaddr), 
+		 domain, netid, subnet_addr, fqdn_flags, borken_opt, pxearch, uuid);
+      
+      return dhcp_packet_size(mess, netid, agent_id, real_end);
+      
+    case DHCPREQUEST:
+      if (ignore || have_config(config, CONFIG_DISABLE))
+	return 0;
+      if ((opt = option_find(mess, sz, OPTION_REQUESTED_IP, INADDRSZ)))
+	{
+	  /* SELECTING  or INIT_REBOOT */
+	  mess->yiaddr = option_addr(opt);
+	  
+	  /* send vendor and user class info for new or recreated lease */
+	  do_classes = 1;
+	  
+	  if ((opt = option_find(mess, sz, OPTION_SERVER_IDENTIFIER, INADDRSZ)))
+	    {
+	      /* SELECTING */
+	      selecting = 1;
+	      
+	      if (override.s_addr != 0)
+		{
+		  if (option_addr(opt).s_addr != override.s_addr)
+		    return 0;
+		}
+	      else 
+		{
+		  for (; context; context = context->current)
+		    if (context->local.s_addr == option_addr(opt).s_addr)
+		      break;
+		  
+		  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 (!(daemon->options & OPT_AUTHORITATIVE))
+			return 0;
+		      message = _("wrong server-ID");
+		    }
+		}
+
+	      /* If a lease exists for this host and another address, squash it. */
+	      if (lease && lease->addr.s_addr != mess->yiaddr.s_addr)
+		{
+		  lease_prune(lease, now);
+		  lease = NULL;
+		}
+	    }
+	  else
+	    {
+	      /* INIT-REBOOT */
+	      if (!lease && !(daemon->options & OPT_AUTHORITATIVE))
+		return 0;
+	      
+	      if (lease && lease->addr.s_addr != mess->yiaddr.s_addr)
+		{
+		  message = _("wrong address");
+		  /* avoid loops when client brain-dead */
+		  lease_prune(lease, now);
+		  lease = NULL;
+		}
+	    }
+	}
+      else
+	{
+	  /* RENEWING or REBINDING */ 
+	  /* Check existing lease for this address.
+	     We allow it to be missing if dhcp-authoritative mode
+	     as long as we can allocate the lease now - checked below.
+	     This makes for a smooth recovery from a lost lease DB */
+	  if ((lease && mess->ciaddr.s_addr != lease->addr.s_addr) ||
+	      (!lease && !(daemon->options & OPT_AUTHORITATIVE)))
+	    {
+	      message = _("lease not found");
+	      /* ensure we broadcast NAK */
+	      unicast_dest = 0;
+	    }
+	  /* desynchronise renewals */
+	  fuzz = rand16();
+	  mess->yiaddr = mess->ciaddr;
+	}
+      
+      log_packet("DHCPREQUEST", &mess->yiaddr, emac, emac_len, iface_name, NULL, mess->xid);
+ 
+      if (!message)
+	{
+	  struct dhcp_config *addr_config;
+	  struct dhcp_context *tmp = NULL;
+	  
+	  if (have_config(config, CONFIG_ADDR))
+	    for (tmp = context; tmp; tmp = tmp->current)
+	      if (context->router.s_addr == config->addr.s_addr)
+		break;
+	  
+	  if (!(context = narrow_context(context, mess->yiaddr, netid)))
+	    {
+	      /* If a machine moves networks whilst it has a lease, we catch that here. */
+	      message = _("wrong network");
+	      /* ensure we broadcast NAK */
+	      unicast_dest = 0;
+	    }
+	  
+	  /* Check for renewal of a lease which is outside the allowed range. */
+	  else if (!address_available(context, mess->yiaddr, netid) &&
+		   (!have_config(config, CONFIG_ADDR) || config->addr.s_addr != mess->yiaddr.s_addr))
+	    message = _("address not available");
+	  
+	  /* Check if a new static address has been configured. Be very sure that
+	     when the client does DISCOVER, it will get the static address, otherwise
+	     an endless protocol loop will ensue. */
+	  else if (!tmp && !selecting &&
+		   have_config(config, CONFIG_ADDR) && 
+		   (!have_config(config, CONFIG_DECLINED) ||
+		    difftime(now, config->decline_time) > (float)DECLINE_BACKOFF) &&
+		   config->addr.s_addr != mess->yiaddr.s_addr &&
+		   (!(ltmp = lease_find_by_addr(config->addr)) || ltmp == lease))
+	    message = _("static lease available");
+
+	  /* Check to see if the address is reserved as a static address for another host */
+	  else if ((addr_config = config_find_by_address(daemon->dhcp_conf, mess->yiaddr)) && addr_config != config)
+	    message = _("address reserved");
+
+	  else if (!lease && (ltmp = lease_find_by_addr(mess->yiaddr)))
+	    {
+	      /* If a host is configured with more than one MAC address, it's OK to 'nix 
+		 a lease from one of it's MACs to give the address to another. */
+	      if (config && config_has_mac(config, ltmp->hwaddr, ltmp->hwaddr_len, ltmp->hwaddr_type))
+		{
+		  my_syslog(MS_DHCP | LOG_INFO, _("abandoning lease to %s of %s"),
+			    print_mac(daemon->namebuff, ltmp->hwaddr, ltmp->hwaddr_len), 
+			    inet_ntoa(ltmp->addr));
+		  lease = ltmp;
+		}
+	      else
+		message = _("address in use");
+	    }
+
+	  if (!message)
+	    {
+	      if (emac_len == 0)
+		message = _("no unique-id");
+	      
+	      else if (!lease)
+		{	     
+		  if ((lease = lease_allocate(mess->yiaddr)))
+		    do_classes = 1;
+		  else
+		    message = _("no leases left");
+		}
+	    }
+	}
+
+      if (message)
+	{
+	  log_packet("DHCPNAK", &mess->yiaddr, emac, emac_len, iface_name, message, mess->xid);
+	  
+	  mess->yiaddr.s_addr = 0;
+	  clear_packet(mess, end);
+	  option_put(mess, end, OPTION_MESSAGE_TYPE, 1, DHCPNAK);
+	  option_put(mess, end, OPTION_SERVER_IDENTIFIER, INADDRSZ, ntohl(server_id(context, override, fallback).s_addr));
+	  option_put_string(mess, end, OPTION_MESSAGE, message, borken_opt);
+	  /* This fixes a problem with the DHCP spec, broadcasting a NAK to a host on 
+	     a distant subnet which unicast a REQ to us won't work. */
+	  if (!unicast_dest || mess->giaddr.s_addr != 0 || 
+	      mess->ciaddr.s_addr == 0 || is_same_net(context->local, mess->ciaddr, context->netmask))
+	    {
+	      mess->flags |= htons(0x8000); /* broadcast */
+	      mess->ciaddr.s_addr = 0;
+	    }
+	}
+      else
+	{
+	   if (do_classes)
+	     {
+	       if (mess->giaddr.s_addr)
+		 lease->giaddr = mess->giaddr;
+	       
+	       lease->changed = 1;
+	       /* copy user-class and vendor class into new lease, for the script */
+	       if ((opt = option_find(mess, sz, OPTION_USER_CLASS, 1)))
+		 {
+		   int len = option_len(opt);
+		   unsigned char *ucp = option_ptr(opt, 0);
+		   /* If the user-class option started as counted strings, the first byte will be zero. */
+		   if (len != 0 && ucp[0] == 0)
+		     ucp++, len--;
+		   free(lease->userclass);
+		   if ((lease->userclass = whine_malloc(len+1)))
+		     {
+		       memcpy(lease->userclass, ucp, len);
+		       lease->userclass[len] = 0;
+		       lease->userclass_len = len+1;
+		     }
+		 }
+	       if ((opt = option_find(mess, sz, OPTION_VENDOR_ID, 1)))
+		 {
+		   int len = option_len(opt);
+		   unsigned char *ucp = option_ptr(opt, 0);
+		   free(lease->vendorclass);
+		   if ((lease->vendorclass = whine_malloc(len+1)))
+		     {
+		       memcpy(lease->vendorclass, ucp, len);
+		       lease->vendorclass[len] = 0;
+		       lease->vendorclass_len = len+1;
+		     }
+		 }
+	       if ((opt = option_find(mess, sz, OPTION_HOSTNAME, 1)))
+		 {
+		   int len = option_len(opt);
+		   unsigned char *ucp = option_ptr(opt, 0);
+		   free(lease->supplied_hostname);
+		   if ((lease->supplied_hostname = whine_malloc(len+1)))
+		     {
+		       memcpy(lease->supplied_hostname, ucp, len);
+		       lease->supplied_hostname[len] = 0;
+		       lease->supplied_hostname_len = len+1;
+		     }
+		 }
+	     }
+	   
+	   if (!hostname_auth && (client_hostname = host_from_dns(mess->yiaddr)))
+	     {
+	      hostname = client_hostname;
+	      hostname_auth = 1;
+	    }
+      
+	  if (context->netid.net)
+	    {
+	      context->netid.next = netid;
+	      netid = &context->netid;
+	    }
+	
+	  time = calc_time(context, config, option_find(mess, sz, OPTION_LEASE_TIME, 4));
+	  lease_set_hwaddr(lease, mess->chaddr, clid, mess->hlen, mess->htype, clid_len);
+	   
+	  /* if all the netids in the ignore_name list are present, ignore client-supplied name */
+	  if (!hostname_auth)
+	    {
+	      for (id_list = daemon->dhcp_ignore_names; id_list; id_list = id_list->next)
+		if ((!id_list->list) || match_netid(id_list->list, netid, 0))
+		  break;
+	      if (id_list)
+		hostname = NULL;
+	    }
+	  if (hostname)
+	    lease_set_hostname(lease, hostname, hostname_auth);
+	  
+	  lease_set_expires(lease, time, now);
+	  lease_set_interface(lease, int_index);
+
+	  if (override.s_addr != 0)
+	    lease->override = override;
+	  else
+	    override = lease->override;
+
+	  log_packet("DHCPACK", &mess->yiaddr, emac, emac_len, iface_name, hostname, mess->xid);  
+	  
+	  clear_packet(mess, end);
+	  option_put(mess, end, OPTION_MESSAGE_TYPE, 1, DHCPACK);
+	  option_put(mess, end, OPTION_SERVER_IDENTIFIER, INADDRSZ, ntohl(server_id(context, override, fallback).s_addr));
+	  option_put(mess, end, OPTION_LEASE_TIME, 4, time);
+	  if (time != 0xffffffff)
+	    {
+	      while (fuzz > (time/16))
+		fuzz = fuzz/2; 
+	      option_put(mess, end, OPTION_T1, 4, (time/2) - fuzz);
+	      option_put(mess, end, OPTION_T2, 4, ((time/8)*7) - fuzz);
+	    }
+	  do_options(context, mess, end, req_options, hostname, get_domain(mess->yiaddr), 
+		     domain, netid, subnet_addr, fqdn_flags, borken_opt, pxearch, uuid);
+	}
+
+      return dhcp_packet_size(mess, netid, agent_id, real_end); 
+      
+    case DHCPINFORM:
+      if (ignore || have_config(config, CONFIG_DISABLE))
+	message = _("ignored");
+      
+      log_packet("DHCPINFORM", &mess->ciaddr, emac, emac_len, iface_name, message, mess->xid);
+     
+      if (message || mess->ciaddr.s_addr == 0)
+	return 0;
+
+      /* For DHCPINFORM only, cope without a valid context */
+      context = narrow_context(context, mess->ciaddr, netid);
+      
+      /* Find a least based on IP address if we didn't
+	 get one from MAC address/client-d */
+      if (!lease &&
+	  (lease = lease_find_by_addr(mess->ciaddr)) && 
+	  lease->hostname)
+	hostname = lease->hostname;
+      
+      if (!hostname)
+	hostname = host_from_dns(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;
+	}
+      
+      if (lease)
+	{
+	  if (override.s_addr != 0)
+	    lease->override = override;
+	  else
+	    override = lease->override;
+	}
+
+      clear_packet(mess, end);
+      option_put(mess, end, OPTION_MESSAGE_TYPE, 1, DHCPACK);
+      option_put(mess, end, OPTION_SERVER_IDENTIFIER, INADDRSZ, ntohl(server_id(context, override, fallback).s_addr));
+      
+      if (lease)
+	{
+	  if (lease->expires == 0)
+	    time = 0xffffffff;
+	  else
+	    time = (unsigned int)difftime(lease->expires, now);
+	  option_put(mess, end, OPTION_LEASE_TIME, 4, time);
+	  lease_set_interface(lease, int_index);
+	}
+
+      do_options(context, mess, end, req_options, hostname, get_domain(mess->ciaddr),
+		 domain, netid, subnet_addr, fqdn_flags, borken_opt, pxearch, uuid);
+      
+      *is_inform = 1; /* handle reply differently */
+      return dhcp_packet_size(mess, netid, agent_id, real_end); 
+    }
+  
+  return 0;
+}
+
+/* find a good value to use as MAC address for logging and address-allocation hashing.
+   This is normally just the chaddr field from the DHCP packet,
+   but eg Firewire will have hlen == 0 and use the client-id instead. 
+   This could be anything, but will normally be EUI64 for Firewire.
+   We assume that if the first byte of the client-id equals the htype byte
+   then the client-id is using the usual encoding and use the rest of the 
+   client-id: if not we can use the whole client-id. This should give
+   sane MAC address logs. */
+unsigned char *extended_hwaddr(int hwtype, int hwlen, unsigned char *hwaddr, 
+				      int clid_len, unsigned char *clid, int *len_out)
+{
+  if (hwlen == 0 && clid && clid_len > 3)
+    {
+      if (clid[0]  == hwtype)
+	{
+	  *len_out = clid_len - 1 ;
+	  return clid + 1;
+	}
+
+#if defined(ARPHRD_EUI64) && defined(ARPHRD_IEEE1394)
+      if (clid[0] ==  ARPHRD_EUI64 && hwtype == ARPHRD_IEEE1394)
+	{
+	  *len_out = clid_len - 1 ;
+	  return clid + 1;
+	}
+#endif
+      
+      *len_out = clid_len;
+      return clid;
+    }
+  
+  *len_out = hwlen;
+  return hwaddr;
+}
+
+static unsigned int calc_time(struct dhcp_context *context, struct dhcp_config *config, unsigned char *opt)
+{
+  unsigned int time = have_config(config, CONFIG_TIME) ? config->lease_time : context->lease_time;
+  
+  if (opt)
+    { 
+      unsigned int req_time = option_uint(opt, 0, 4);
+      if (req_time < 120 )
+	req_time = 120; /* sanity */
+      if (time == 0xffffffff || (req_time != 0xffffffff && req_time < time))
+	time = req_time;
+    }
+
+  return time;
+}
+
+static struct in_addr server_id(struct dhcp_context *context, struct in_addr override, struct in_addr fallback)
+{
+  if (override.s_addr != 0)
+    return override;
+  else if (context)
+    return context->local;
+  else
+    return fallback;
+}
+
+static int sanitise(unsigned char *opt, char *buf)
+{
+  char *p;
+  int i;
+  
+  *buf = 0;
+  
+  if (!opt)
+    return 0;
+
+  p = option_ptr(opt, 0);
+
+  for (i = option_len(opt); i > 0; i--)
+    {
+      char c = *p++;
+      if (isprint((int)c))
+	*buf++ = c;
+    }
+  *buf = 0; /* add terminator */
+  
+  return 1;
+}
+
+static void log_packet(char *type, void *addr, unsigned char *ext_mac, 
+		       int mac_len, char *interface, char *string, u32 xid)
+{
+  struct in_addr a;
+ 
+  /* addr may be misaligned */
+  if (addr)
+    memcpy(&a, addr, sizeof(a));
+  
+  print_mac(daemon->namebuff, ext_mac, mac_len);
+  
+  if(daemon->options & OPT_LOG_OPTS)
+     my_syslog(MS_DHCP | LOG_INFO, "%u %s(%s) %s%s%s %s",
+	       ntohl(xid), 
+	       type,
+	       interface, 
+	       addr ? inet_ntoa(a) : "",
+	       addr ? " " : "",
+	       daemon->namebuff,
+	       string ? string : "");
+  else
+    my_syslog(MS_DHCP | LOG_INFO, "%s(%s) %s%s%s %s",
+	      type,
+	      interface, 
+	      addr ? inet_ntoa(a) : "",
+	      addr ? " " : "",
+	      daemon->namebuff,
+	      string ? string : "");
+}
+
+static void log_options(unsigned char *start, u32 xid)
+{
+  while (*start != OPTION_END)
+    {
+      int is_ip, is_name, i;
+      char *text = option_string(start[0], &is_ip, &is_name);
+      unsigned char trunc = option_len(start);
+      
+      if (is_ip)
+	for (daemon->namebuff[0]= 0, i = 0; i <= trunc - INADDRSZ; i += INADDRSZ) 
+	  {
+	    if (i != 0)
+	      strncat(daemon->namebuff, ", ", 256 - strlen(daemon->namebuff));
+	    strncat(daemon->namebuff, inet_ntoa(option_addr_arr(start, i)), 256 - strlen(daemon->namebuff));
+	  }
+      else if (!is_name || !sanitise(start, daemon->namebuff))
+	{
+	  if (trunc > 13)
+	    trunc = 13;
+	  print_mac(daemon->namebuff, option_ptr(start, 0), trunc);
+	}
+      
+      my_syslog(MS_DHCP | LOG_INFO, "%u sent size:%3d option:%3d%s%s%s%s%s", 
+		ntohl(xid), option_len(start), start[0],
+		text ? ":" : "", text ? text : "",
+		trunc == 0 ? "" : "  ",
+		trunc == 0 ? "" : daemon->namebuff,
+		trunc == option_len(start) ? "" : "...");
+      start += start[1] + 2;
+    }
+}
+
+static unsigned char *option_find1(unsigned char *p, unsigned char *end, int opt, int minsize)
+{
+  while (1) 
+    {
+      if (p > end)
+	return NULL;
+      else if (*p == OPTION_END)
+	return opt == OPTION_END ? p : NULL;
+      else if (*p == OPTION_PAD)
+	p++;
+      else 
+	{ 
+	  int opt_len;
+	  if (p > end - 2)
+	    return NULL; /* malformed packet */
+	  opt_len = option_len(p);
+	  if (p > end - (2 + opt_len))
+	    return NULL; /* malformed packet */
+	  if (*p == opt && opt_len >= minsize)
+	    return p;
+	  p += opt_len + 2;
+	}
+    }
+}
+ 
+static unsigned char *option_find(struct dhcp_packet *mess, size_t size, int opt_type, int minsize)
+{
+  unsigned char *ret, *overload;
+  
+  /* skip over DHCP cookie; */
+  if ((ret = option_find1(&mess->options[0] + sizeof(u32), ((unsigned char *)mess) + size, opt_type, minsize)))
+    return ret;
+
+  /* look for overload option. */
+  if (!(overload = option_find1(&mess->options[0] + sizeof(u32), ((unsigned char *)mess) + size, OPTION_OVERLOAD, 1)))
+    return NULL;
+  
+  /* Can we look in filename area ? */
+  if ((overload[2] & 1) &&
+      (ret = option_find1(&mess->file[0], &mess->file[128], opt_type, minsize)))
+    return ret;
+
+  /* finally try sname area */
+  if ((overload[2] & 2) &&
+      (ret = option_find1(&mess->sname[0], &mess->sname[64], opt_type, minsize)))
+    return ret;
+
+  return NULL;
+}
+
+static struct in_addr option_addr_arr(unsigned char *opt, int offset)
+{
+  /* this worries about unaligned data in the option. */
+  /* struct in_addr is network byte order */
+  struct in_addr ret;
+
+  memcpy(&ret, option_ptr(opt, offset), INADDRSZ);
+
+  return ret;
+}
+
+static struct in_addr option_addr(unsigned char *opt)
+{
+  return option_addr_arr(opt, 0);
+}
+
+static unsigned int option_uint(unsigned char *opt, int offset, int size)
+{
+  /* this worries about unaligned data and byte order */
+  unsigned int ret = 0;
+  int i;
+  unsigned char *p = option_ptr(opt, offset);
+  
+  for (i = 0; i < size; i++)
+    ret = (ret << 8) | *p++;
+
+  return ret;
+}
+
+static unsigned char *dhcp_skip_opts(unsigned char *start)
+{
+  while (*start != 0)
+    start += start[1] + 2;
+  return start;
+}
+
+/* only for use when building packet: doesn't check for bad data. */ 
+static unsigned char *find_overload(struct dhcp_packet *mess)
+{
+  unsigned char *p = &mess->options[0] + sizeof(u32);
+  
+  while (*p != 0)
+    {
+      if (*p == OPTION_OVERLOAD)
+	return p;
+      p += p[1] + 2;
+    }
+  return NULL;
+}
+
+static size_t dhcp_packet_size(struct dhcp_packet *mess, struct dhcp_netid *netid,
+			       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;
+  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 && (daemon->options & OPT_LOG_OPTS))
+    {
+      char *s = daemon->namebuff;
+      for (*s = 0; netid; netid = netid->next)
+	{
+	  /* kill dupes. */
+	  for (n = netid->next; n; n = n->next)
+	    if (strcmp(netid->net, n->net) == 0)
+	      break;
+	  
+	  if (!n)
+	    {
+	      strncat (s, netid->net, (MAXDNAME-1) - strlen(s));
+	      if (netid->next)
+		strncat (s, ", ", (MAXDNAME-1) - strlen(s));
+	    }
+	}
+      my_syslog(MS_DHCP | LOG_INFO, _("%u tags: %s"), ntohl(mess->xid), s);
+    } 
+   
+  /* add END options to the regions. */
+  overload = find_overload(mess);
+  
+  if (overload && (option_uint(overload, 0, 1) & 1))
+    {
+      *dhcp_skip_opts(mess->file) = OPTION_END;
+      if (daemon->options & OPT_LOG_OPTS)
+	log_options(mess->file, mess->xid);
+    }
+  else if ((daemon->options & OPT_LOG_OPTS) && strlen((char *)mess->file) != 0)
+    my_syslog(MS_DHCP | LOG_INFO, _("%u bootfile name: %s"), ntohl(mess->xid), (char *)mess->file);
+  
+  if (overload && (option_uint(overload, 0, 1) & 2))
+    {
+      *dhcp_skip_opts(mess->sname) = OPTION_END;
+      if (daemon->options & OPT_LOG_OPTS)
+	log_options(mess->sname, mess->xid);
+    }
+  else if ((daemon->options & OPT_LOG_OPTS) && strlen((char *)mess->sname) != 0)
+    my_syslog(MS_DHCP | LOG_INFO, _("%u server name: %s"), ntohl(mess->xid), (char *)mess->sname);
+
+
+  *p++ = OPTION_END;
+  
+  if (daemon->options & OPT_LOG_OPTS)
+    {
+      if (mess->siaddr.s_addr != 0)
+	my_syslog(MS_DHCP | LOG_INFO, _("%u next server: %s"), ntohl(mess->xid), inet_ntoa(mess->siaddr));
+      
+      log_options(&mess->options[0] + sizeof(u32), mess->xid);
+    } 
+  
+  for (id_list = daemon->force_broadcast; id_list; id_list = id_list->next)
+    if (match_netid(id_list->list, netid, 0))
+      mess->flags |= htons(0x8000); /* force broadcast */
+  
+  ret = (size_t)(p - (unsigned char *)mess);
+  
+  if (ret < MIN_PACKETSZ)
+    ret = MIN_PACKETSZ;
+
+  return ret;
+}
+
+static unsigned char *free_space(struct dhcp_packet *mess, unsigned char *end, int opt, int len)
+{
+  unsigned char *p = dhcp_skip_opts(&mess->options[0] + sizeof(u32));
+  
+  if (p + len + 3 >= end)
+    /* not enough space in options area, try and use overload, if poss */
+    {
+      unsigned char *overload;
+      
+      if (!(overload = find_overload(mess)) &&
+	  (mess->file[0] == 0 || mess->sname[0] == 0))
+	{
+	  /* attempt to overload fname and sname areas, we've reserved space for the
+	     overflow option previuously. */
+	  overload = p;
+	  *(p++) = OPTION_OVERLOAD;
+	  *(p++) = 1;
+	}
+      
+      p = NULL;
+      
+      /* using filename field ? */
+      if (overload)
+	{
+	  if (mess->file[0] == 0)
+	    overload[2] |= 1;
+	  
+	  if (overload[2] & 1)
+	    {
+	      p = dhcp_skip_opts(mess->file);
+	      if (p + len + 3 >= mess->file + sizeof(mess->file))
+		p = NULL;
+	    }
+	  
+	  if (!p)
+	    {
+	      /* try to bring sname into play (it may be already) */
+	      if (mess->sname[0] == 0)
+		overload[2] |= 2;
+	      
+	      if (overload[2] & 2)
+		{
+		  p = dhcp_skip_opts(mess->sname);
+		  if (p + len + 3 >= mess->sname + sizeof(mess->file))
+		    p = NULL;
+		}
+	    }
+	}
+      
+      if (!p)
+	my_syslog(MS_DHCP | LOG_WARNING, _("cannot send DHCP/BOOTP option %d: no space left in packet"), opt);
+    }
+ 
+  if (p)
+    {
+      *(p++) = opt;
+      *(p++) = len;
+    }
+
+  return p;
+}
+	      
+static void option_put(struct dhcp_packet *mess, unsigned char *end, int opt, int len, unsigned int val)
+{
+  int i;
+  unsigned char *p = free_space(mess, end, opt, len);
+  
+  if (p) 
+    for (i = 0; i < len; i++)
+      *(p++) = val >> (8 * (len - (i + 1)));
+}
+
+static void option_put_string(struct dhcp_packet *mess, unsigned char *end, int opt, 
+			      char *string, int null_term)
+{
+  unsigned char *p;
+  size_t len = strlen(string);
+
+  if (null_term && len != 255)
+    len++;
+
+  if ((p = free_space(mess, end, opt, len)))
+    memcpy(p, string, len);
+}
+
+/* return length, note this only does the data part */
+static int do_opt(struct dhcp_opt *opt, unsigned char *p, struct dhcp_context *context, int null_term)
+{
+  int len = opt->len;
+  
+  if ((opt->flags & DHOPT_STRING) && null_term && len != 255)
+    len++;
+
+  if (p && len != 0)
+    {
+      if (context && (opt->flags & DHOPT_ADDR))
+	{
+	  int j;
+	  struct in_addr *a = (struct in_addr *)opt->val;
+	  for (j = 0; j < opt->len; j+=INADDRSZ, a++)
+	    {
+	      /* zero means "self" (but not in vendorclass options.) */
+	      if (a->s_addr == 0)
+		memcpy(p, &context->local, INADDRSZ);
+	      else
+		memcpy(p, a, INADDRSZ);
+	      p += INADDRSZ;
+	    }
+	}
+      else
+	memcpy(p, opt->val, len);
+    }  
+  return len;
+}
+
+static int in_list(unsigned char *list, int opt)
+{
+  int i;
+
+   /* If no requested options, send everything, not nothing. */
+  if (!list)
+    return 1;
+  
+  for (i = 0; list[i] != OPTION_END; i++)
+    if (opt == list[i])
+      return 1;
+
+  return 0;
+}
+
+static struct dhcp_opt *option_find2(struct dhcp_netid *netid, struct dhcp_opt *opts, int opt)
+{
+  struct dhcp_opt *tmp;  
+  for (tmp = opts; tmp; tmp = tmp->next)
+    if (tmp->opt == opt && !(tmp->flags & (DHOPT_ENCAPSULATE | DHOPT_VENDOR)))
+      if (match_netid(tmp->netid, netid, netid ? 0 : 1))
+	return tmp;
+	      
+  return netid ? option_find2(NULL, opts, opt) : NULL;
+}
+
+/* mark vendor-encapsulated options which match the client-supplied  or
+   config-supplied vendor class */
+static void match_vendor_opts(unsigned char *opt, struct dhcp_opt *dopt)
+{
+  for (; dopt; dopt = dopt->next)
+    {
+      dopt->flags &= ~DHOPT_VENDOR_MATCH;
+      if (opt && (dopt->flags & DHOPT_VENDOR))
+	{
+	  int i, len = 0;
+	  if (dopt->u.vendor_class)
+	    len = strlen((char *)dopt->u.vendor_class);
+	  for (i = 0; i <= (option_len(opt) - len); i++)
+	    if (len == 0 || memcmp(dopt->u.vendor_class, option_ptr(opt, i), len) == 0)
+	      {
+		dopt->flags |= DHOPT_VENDOR_MATCH;
+		break;
+	      }
+	}
+    }
+}
+
+static void do_encap_opts(struct dhcp_opt *opt, int encap, int flag,  
+			  struct dhcp_packet *mess, unsigned char *end, int null_term)
+{
+  int len, enc_len;
+  struct dhcp_opt *start;
+  unsigned char *p;
+    
+  /* find size in advance */
+  for (enc_len = 0, start = opt; opt; opt = opt->next)
+    if (opt->flags & flag)
+      {
+	int new = do_opt(opt, NULL, NULL, null_term) + 2;
+	if (enc_len + new <= 255)
+	  enc_len += new;
+	else
+	  {
+	    p = free_space(mess, end, encap, enc_len);
+	    for (; start && start != opt; start = start->next)
+	      if (p && (start->flags & flag))
+		{
+		  len = do_opt(start, p + 2, NULL, null_term);
+		  *(p++) = start->opt;
+		  *(p++) = len;
+		  p += len;
+		}
+	    enc_len = new;
+	    start = opt;
+	  }
+      }
+  
+  if (enc_len != 0 &&
+      (p = free_space(mess, end, encap, enc_len + 1)))
+    {
+      for (; start; start = start->next)
+	if (start->flags & flag)
+	  {
+	    len = do_opt(start, p + 2, NULL, null_term);
+	    *(p++) = start->opt;
+	    *(p++) = len;
+	    p += len;
+	  }
+      *p = OPTION_END;
+    }
+}
+
+static void pxe_misc(struct dhcp_packet *mess, unsigned char *end, unsigned char *uuid)
+{
+  unsigned char *p;
+
+  option_put_string(mess, end, OPTION_VENDOR_ID, "PXEClient", 0);
+  if (uuid && (p = free_space(mess, end, OPTION_PXE_UUID, 17)))
+    memcpy(p, uuid, 17);
+}
+
+static int prune_vendor_opts(struct dhcp_netid *netid)
+{
+  int force = 0;
+  struct dhcp_opt *opt;
+
+  /* prune vendor-encapsulated options based on netid, and look if we're forcing them to be sent */
+  for (opt = daemon->dhcp_opts; opt; opt = opt->next)
+    if (opt->flags & DHOPT_VENDOR_MATCH)
+      {
+	if (!match_netid(opt->netid, netid, 1))
+	  opt->flags &= ~DHOPT_VENDOR_MATCH;
+	else if (opt->flags & DHOPT_FORCE)
+	  force = 1;
+      }
+  return force;
+}
+
+static struct dhcp_opt *pxe_opts(int pxe_arch, struct dhcp_netid *netid)
+{
+#define NUM_OPTS 4  
+
+  unsigned  char *p, *q;
+  struct pxe_service *service;
+  static struct dhcp_opt *o, *ret;
+  int i, j = NUM_OPTS - 1;
+  
+  /* We pass back references to these, hence they are declared static */
+  static unsigned char discovery_control;
+  static unsigned char fake_prompt[] = { 0, 'P', 'X', 'E' }; 
+  static struct dhcp_opt *fake_opts = NULL;
+  
+  /* We are found by broadcast, so disable multicast. It gets switched on again
+     if we point to other servers and don't give a unicast address. Note that
+     we don't provide our own address for services we are the boot server for because unicast 
+     discovery is to port 4011 and we don't listen there. If you are using proxy DHCP 
+     and DHCP relays, the relay will need to forward to the proxy too. */
+  discovery_control = 2;
+  
+  ret = daemon->dhcp_opts;
+  
+  if (!fake_opts && !(fake_opts = whine_malloc(NUM_OPTS * sizeof(struct dhcp_opt))))
+    return ret;
+
+  for (i = 0; i < NUM_OPTS; i++)
+    {
+      fake_opts[i].flags = DHOPT_VENDOR_MATCH;
+      fake_opts[i].netid = NULL;
+      fake_opts[i].next = i == (NUM_OPTS - 1) ? ret : &fake_opts[i+1];
+    }
+  
+  /* create the data for the PXE_MENU and PXE_SERVERS options. */
+  p = (unsigned char *)daemon->dhcp_buff;
+  q = (unsigned char *)daemon->dhcp_buff2;
+
+  for (i = 0, service = daemon->pxe_services; service; service = service->next)
+    if (pxe_arch == service->CSA && match_netid(service->netid, netid, 1))
+      {
+	size_t len = strlen(service->menu);
+	/* opt 43 max size is 255. encapsulated option has type and length
+	   bytes, so its max size is 253. */
+	if (p - (unsigned char *)daemon->dhcp_buff + len + 3 < 253)
+	  {
+	    *(p++) = service->type >> 8;
+	    *(p++) = service->type;
+	    *(p++) = len;
+	    memcpy(p, service->menu, len);
+	    p += len;
+	    i++;
+	  }
+	else
+	  {
+	  toobig:
+	    my_syslog(MS_DHCP | LOG_ERR, _("PXE menu too large"));
+	    return daemon->dhcp_opts;
+	  }
+	
+	if (!service->basename)
+	  {
+	    if (service->server.s_addr != 0)
+	      {
+		if (q - (unsigned char *)daemon->dhcp_buff2 + 3 + INADDRSZ >= 253)
+		  goto toobig;
+		
+		/* Boot service with known address - give it */
+		*(q++) = service->type >> 8;
+		*(q++) = service->type;
+		*(q++) = 1;
+		/* dest misaligned */
+		memcpy(q, &service->server.s_addr, INADDRSZ);
+		q += INADDRSZ;
+	      }
+	    else if (service->type != 0)
+	      /* We're not supplying a server, so let the client multicast.
+		 type zero is "local boot" so no need for M/C on that. */
+	      discovery_control = 0;
+	  }	  
+      }
+
+  /* if no prompt, wait forever if there's a choice */
+  fake_prompt[0] = (i > 1) ? 255 : 0;
+  
+  if (i == 0)
+    discovery_control = 8; /* no menu - just use use mess->filename */
+  else
+    {
+      ret = &fake_opts[j--];
+      ret->len = p - (unsigned char *)daemon->dhcp_buff;
+      ret->val = (unsigned char *)daemon->dhcp_buff;
+      ret->opt = SUBOPT_PXE_MENU;
+
+      if (q - (unsigned char *)daemon->dhcp_buff2 != 0)
+	{
+	  ret = &fake_opts[j--]; 
+	  ret->len = q - (unsigned char *)daemon->dhcp_buff2;
+	  ret->val = (unsigned char *)daemon->dhcp_buff2;
+	  ret->opt = SUBOPT_PXE_SERVERS;
+	}
+    }
+
+  for (o = daemon->dhcp_opts; o; o = o->next)
+    if ((o->flags & DHOPT_VENDOR_MATCH) && o->opt == SUBOPT_PXE_MENU_PROMPT)
+      break;
+  
+  if (!o)
+    {
+      ret = &fake_opts[j--]; 
+      ret->len = sizeof(fake_prompt);
+      ret->val = fake_prompt;
+      ret->opt = SUBOPT_PXE_MENU_PROMPT;
+    }
+  
+  if (discovery_control != 0)
+    {
+      ret = &fake_opts[j--]; 
+      ret->len = 1;
+      ret->opt = SUBOPT_PXE_DISCOVERY;
+      ret->val= &discovery_control;
+    }
+
+  return ret;
+}
+  
+static void clear_packet(struct dhcp_packet *mess, unsigned char *end)
+{
+  memset(mess->sname, 0, sizeof(mess->sname));
+  memset(mess->file, 0, sizeof(mess->file));
+  memset(&mess->options[0] + sizeof(u32), 0, end - (&mess->options[0] + sizeof(u32)));
+  mess->siaddr.s_addr = 0;
+}
+
+struct dhcp_boot *find_boot(struct dhcp_netid *netid)
+{
+  struct dhcp_boot *boot;
+
+  /* decide which dhcp-boot option we're using */
+  for (boot = daemon->boot_config; boot; boot = boot->next)
+    if (match_netid(boot->netid, netid, 0))
+      break;
+  if (!boot)
+    /* No match, look for one without a netid */
+    for (boot = daemon->boot_config; boot; boot = boot->next)
+      if (match_netid(boot->netid, netid, 1))
+	break;
+
+  return boot;
+}
+
+static void do_options(struct dhcp_context *context,
+		       struct dhcp_packet *mess,
+		       unsigned char *end, 
+		       unsigned char *req_options,
+		       char *hostname, 
+		       char *domain, char *config_domain,
+		       struct dhcp_netid *netid,
+		       struct in_addr subnet_addr,
+		       unsigned char fqdn_flags,
+		       int null_term, int pxe_arch,
+		       unsigned char *uuid)
+{
+  struct dhcp_opt *opt, *config_opts = daemon->dhcp_opts;
+  struct dhcp_boot *boot;
+  unsigned char *p;
+  int i, len, force_encap = 0;
+  unsigned char f0 = 0, s0 = 0;
+  int done_file = 0, done_server = 0;
+
+  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);
+  
+  /* logging */
+  if ((daemon->options & OPT_LOG_OPTS) && req_options)
+    {
+      char *q = daemon->namebuff;
+      for (i = 0; req_options[i] != OPTION_END; i++)
+	{
+	  char *s = option_string(req_options[i], NULL, NULL);
+	  q += snprintf(q, MAXDNAME - (q - daemon->namebuff),
+			"%d%s%s%s", 
+			req_options[i],
+			s ? ":" : "",
+			s ? s : "", 
+			req_options[i+1] == OPTION_END ? "" : ", ");
+	  if (req_options[i+1] == OPTION_END || (q - daemon->namebuff) > 40)
+	    {
+	      q = daemon->namebuff;
+	      my_syslog(MS_DHCP | LOG_INFO, _("%u requested options: %s"), ntohl(mess->xid), daemon->namebuff);
+	    }
+	}
+    }
+      
+  if (context)
+    mess->siaddr = context->local;
+  
+  /* See if we can send the boot stuff as options.
+     To do this we need a requested option list, BOOTP
+     and very old DHCP clients won't have this, we also 
+     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->sname)
+	{	  
+	  if (!(daemon->options & OPT_NO_OVERRIDE) &&
+	      req_options && 
+	      in_list(req_options, OPTION_SNAME))
+	    option_put_string(mess, end, OPTION_SNAME, boot->sname, 1);
+	  else
+	    strncpy((char *)mess->sname, boot->sname, sizeof(mess->sname)-1);
+	}
+      
+      if (boot->file)
+	{
+	  if (!(daemon->options & OPT_NO_OVERRIDE) &&
+	      req_options && 
+	      in_list(req_options, OPTION_FILENAME))
+	    option_put_string(mess, end, OPTION_FILENAME, boot->file, 1);
+	  else
+	    strncpy((char *)mess->file, boot->file, sizeof(mess->file)-1);
+	}
+      
+      if (boot->next_server.s_addr)
+	mess->siaddr = boot->next_server;
+    }
+  else
+    /* Use the values of the relevant options if no dhcp-boot given and
+       they're not explicitly asked for as options. OPTION_END is used
+       as an internal way to specify siaddr without using dhcp-boot, for use in
+       dhcp-optsfile. */
+    {
+      if ((!req_options || !in_list(req_options, OPTION_FILENAME)) && mess->file[0] == 0 &&
+	  (opt = option_find2(netid, config_opts, 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))
+	{
+	  strncpy((char *)mess->sname, (char *)opt->val, sizeof(mess->sname)-1);
+	  done_server = 1;
+	}
+      
+      if ((opt = option_find2(netid, config_opts, OPTION_END)))
+	mess->siaddr.s_addr = ((struct in_addr *)opt->val)->s_addr;	
+    }
+        
+  /* We don't want to do option-overload for BOOTP, so make the file and sname
+     fields look like they are in use, even when they aren't. This gets restored
+     at the end of this function. */
+
+  if (!req_options || (daemon->options & OPT_NO_OVERRIDE))
+    {
+      f0 = mess->file[0];
+      mess->file[0] = 1;
+      s0 = mess->sname[0];
+      mess->sname[0] = 1;
+    }
+      
+  /* At this point, if mess->sname or mess->file are zeroed, they are available
+     for option overload, reserve space for the overload option. */
+  if (mess->file[0] == 0 || mess->sname[0] == 0)
+    end -= 3;
+
+  /* rfc3011 says this doesn't need to be in the requested options list. */
+  if (subnet_addr.s_addr)
+    option_put(mess, end, OPTION_SUBNET_SELECT, INADDRSZ, ntohl(subnet_addr.s_addr));
+  
+  /* replies to DHCPINFORM may not have a valid context */
+  if (context)
+    {
+      if (!option_find2(netid, config_opts, 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_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_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_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_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_put_string(mess, end, OPTION_HOSTNAME, hostname, null_term);
+      
+      if (fqdn_flags != 0)
+	{
+	  len = strlen(hostname) + 3;
+	  
+	  if (fqdn_flags & 0x04)
+	    len += 2;
+	  else if (null_term)
+	    len++;
+
+	  if (domain)
+	    len += strlen(domain) + 1;
+	  
+	  if ((p = free_space(mess, end, OPTION_CLIENT_FQDN, len)))
+	    {
+	      *(p++) = fqdn_flags;
+	      *(p++) = 255;
+	      *(p++) = 255;
+
+	      if (fqdn_flags & 0x04)
+		{
+		  p = do_rfc1035_name(p, hostname);
+		  if (domain)
+		    p = do_rfc1035_name(p, domain);
+		  *p++ = 0;
+		}
+	      else
+		{
+		  memcpy(p, hostname, strlen(hostname));
+		  p += strlen(hostname);
+		  if (domain)
+		    {
+		      *(p++) = '.';
+		      memcpy(p, domain, strlen(domain));
+		      p += strlen(domain);
+		    }
+		  if (null_term)
+		    *(p++) = 0;
+		}
+	    }
+	}
+    }      
+
+  for (opt = config_opts; opt; opt = opt->next)
+    {
+      int optno = opt->opt;
+
+      /* was it asked for, or are we sending it anyway? */
+      if (!(opt->flags & DHOPT_FORCE) && !in_list(req_options, optno))
+	continue;
+      
+      /* prohibit some used-internally options */
+      if (optno == OPTION_CLIENT_FQDN ||
+	  optno == OPTION_MAXMESSAGE ||
+	  optno == OPTION_OVERLOAD ||
+	  optno == OPTION_PAD ||
+	  optno == OPTION_END)
+	continue;
+
+      if (optno == OPTION_SNAME && done_server)
+	continue;
+
+      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" */
+      if (opt->len == 0 && 
+	  (optno == OPTION_NETMASK ||
+	   optno == OPTION_BROADCAST ||
+	   optno == OPTION_ROUTER ||
+	   optno == OPTION_DNSSERVER || 
+	   optno == OPTION_DOMAINNAME ||
+	   optno == OPTION_HOSTNAME))
+	continue;
+
+      /* vendor-class comes from elsewhere for PXE */
+      if (pxe_arch != -1 && optno == OPTION_VENDOR_ID)
+	continue;
+      
+      /* always force null-term for filename and servername - buggy PXE again. */
+      len = do_opt(opt, NULL, context, 
+		   (optno == OPTION_SNAME || optno == OPTION_FILENAME) ? 1 : null_term);
+
+      if ((p = free_space(mess, end, optno, len)))
+	{
+	  do_opt(opt, p, context, 
+		 (optno == OPTION_SNAME || optno == OPTION_FILENAME) ? 1 : null_term);
+	  
+	  /* If we send a vendor-id, revisit which vendor-ops we consider 
+	     it appropriate to send. */
+	  if (optno == OPTION_VENDOR_ID)
+	    match_vendor_opts(p - 2, config_opts);
+	}  
+    }
+
+  /* Now send options to be encapsulated in arbitrary options, 
+     eg dhcp-option=encap:172,17,.......
+     The may be more that one "outer" to do, so group
+     all the options which match each outer in turn. */
+  for (opt = config_opts; opt; opt = opt->next)
+    opt->flags &= ~DHOPT_ENCAP_DONE;
+  
+  for (opt = config_opts; opt; opt = opt->next)
+    if ((opt->flags & (DHOPT_ENCAPSULATE | DHOPT_ENCAP_DONE)) ==  DHOPT_ENCAPSULATE)
+      {
+	struct dhcp_opt *o;
+	int found = 0;
+	
+	for (o = config_opts; o; o = o->next)
+	  {
+	    o->flags &= ~DHOPT_ENCAP_MATCH;
+	    if ((o->flags & DHOPT_ENCAPSULATE) && opt->u.encap == o->u.encap)
+	      {
+		o->flags |= DHOPT_ENCAP_DONE;
+		if (match_netid(o->netid, netid, 1) &&
+		    (o->flags & DHOPT_FORCE || in_list(req_options, o->u.encap)))
+		  {
+		    o->flags |= DHOPT_ENCAP_MATCH;
+		    found = 1;
+		  }
+	      }
+	  }
+	
+	if (found)
+	  do_encap_opts(config_opts, opt->u.encap, DHOPT_ENCAP_MATCH, mess, end, null_term);
+      }
+
+  /* Must precede pxe_opts, since it overwrites req_options */
+  force_encap = prune_vendor_opts(netid);
+  if (in_list(req_options, OPTION_VENDOR_CLASS_OPT))
+    force_encap = 1;
+
+  if (pxe_arch != -1)
+    {
+      pxe_misc(mess, end, uuid);
+      config_opts = pxe_opts(pxe_arch, netid);
+    }
+
+  if (force_encap)
+    do_encap_opts(config_opts, OPTION_VENDOR_CLASS_OPT, DHOPT_VENDOR_MATCH, mess, end, null_term);
+  
+   /* restore BOOTP anti-overload hack */
+  if (!req_options || (daemon->options & OPT_NO_OVERRIDE))
+    {
+      mess->file[0] = f0;
+      mess->sname[0] = s0;
+    }
+}
+
+#endif
+  
+
+  
+  
+
+
+  
diff --git a/src/tftp.c b/src/tftp.c
new file mode 100755
index 0000000..c1ddb19
--- /dev/null
+++ b/src/tftp.c
@@ -0,0 +1,600 @@
+/* dnsmasq is Copyright (c) 2000-2009 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_TFTP
+
+static struct tftp_file *check_tftp_fileperm(ssize_t *len);
+static void free_transfer(struct tftp_transfer *transfer);
+static ssize_t tftp_err(int err, char *packet, char *mess, char *file);
+static ssize_t tftp_err_oops(char *packet, char *file);
+static ssize_t get_block(char *packet, struct tftp_transfer *transfer);
+static char *next(char **p, char *end);
+
+#define OP_RRQ  1
+#define OP_WRQ  2
+#define OP_DATA 3
+#define OP_ACK  4
+#define OP_ERR  5
+#define OP_OACK 6
+
+#define ERR_NOTDEF 0
+#define ERR_FNF    1
+#define ERR_PERM   2
+#define ERR_FULL   3
+#define ERR_ILL    4
+
+void tftp_request(struct listener *listen, time_t now)
+{
+  ssize_t len;
+  char *packet = daemon->packet;
+  char *filename, *mode, *p, *end, *opt;
+  struct sockaddr_in addr, peer;
+  struct msghdr msg;
+  struct iovec iov;
+  struct ifreq ifr;
+  int is_err = 1, if_index = 0, mtu = 0;
+  struct iname *tmp;
+  struct tftp_transfer *transfer;
+  int port = daemon->start_tftp_port; /* may be zero to use ephemeral port */
+#if defined(IP_MTU_DISCOVER) && defined(IP_PMTUDISC_DONT)
+  int mtuflag = IP_PMTUDISC_DONT;
+#endif
+  
+  union {
+    struct cmsghdr align; /* this ensures alignment */
+#if defined(HAVE_LINUX_NETWORK)
+    char control[CMSG_SPACE(sizeof(struct in_pktinfo))];
+#elif defined(HAVE_SOLARIS_NETWORK)
+    char control[CMSG_SPACE(sizeof(unsigned int))];
+#elif defined(IP_RECVDSTADDR) && defined(IP_RECVIF)
+    char control[CMSG_SPACE(sizeof(struct sockaddr_dl))];
+#endif
+  } control_u; 
+
+  msg.msg_controllen = sizeof(control_u);
+  msg.msg_control = control_u.control;
+  msg.msg_flags = 0;
+  msg.msg_name = &peer;
+  msg.msg_namelen = sizeof(peer);
+  msg.msg_iov = &iov;
+  msg.msg_iovlen = 1;
+
+  iov.iov_base = packet;
+  iov.iov_len = daemon->packet_buff_sz;
+
+  /* we overwrote the buffer... */
+  daemon->srv_save = NULL;
+
+  if ((len = recvmsg(listen->tftpfd, &msg, 0)) < 2)
+    return;
+  
+  if (daemon->options & OPT_NOWILD)
+    {
+      addr = listen->iface->addr.in;
+      mtu = listen->iface->mtu;
+    }
+  else
+    {
+      char name[IF_NAMESIZE];
+      struct cmsghdr *cmptr;
+      
+      addr.sin_addr.s_addr = 0;
+      
+#if defined(HAVE_LINUX_NETWORK)
+      for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr))
+	if (cmptr->cmsg_level == SOL_IP && cmptr->cmsg_type == IP_PKTINFO)
+	  {
+	    addr.sin_addr = ((struct in_pktinfo *)CMSG_DATA(cmptr))->ipi_spec_dst;
+	    if_index = ((struct in_pktinfo *)CMSG_DATA(cmptr))->ipi_ifindex;
+	  }
+      
+#elif defined(HAVE_SOLARIS_NETWORK)
+      for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr))
+	if (cmptr->cmsg_level == IPPROTO_IP && cmptr->cmsg_type == IP_RECVDSTADDR)
+	  addr.sin_addr = *((struct in_addr *)CMSG_DATA(cmptr));
+	else if (cmptr->cmsg_level == IPPROTO_IP && cmptr->cmsg_type == IP_RECVIF)
+	  if_index = *((unsigned int *)CMSG_DATA(cmptr));
+
+
+#elif defined(IP_RECVDSTADDR) && defined(IP_RECVIF)
+      for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr))
+	if (cmptr->cmsg_level == IPPROTO_IP && cmptr->cmsg_type == IP_RECVDSTADDR)
+	  addr.sin_addr = *((struct in_addr *)CMSG_DATA(cmptr));
+	else if (cmptr->cmsg_level == IPPROTO_IP && cmptr->cmsg_type == IP_RECVIF)
+	  if_index = ((struct sockaddr_dl *)CMSG_DATA(cmptr))->sdl_index;
+           
+#endif
+      
+      if (!indextoname(listen->tftpfd, if_index, name) ||
+	  addr.sin_addr.s_addr == 0 ||
+	  !iface_check(AF_INET, (struct all_addr *)&addr.sin_addr, name, &if_index))
+	return;
+      
+      /* allowed interfaces are the same as for DHCP */
+      for (tmp = daemon->dhcp_except; tmp; tmp = tmp->next)
+	if (tmp->name && (strcmp(tmp->name, name) == 0))
+	  return;
+     
+      strncpy(name, ifr.ifr_name, IF_NAMESIZE);
+      if (ioctl(listen->tftpfd, SIOCGIFMTU, &ifr) != -1)
+	mtu = ifr.ifr_mtu;      
+    }
+  
+  addr.sin_port = htons(port);
+  addr.sin_family = AF_INET;
+#ifdef HAVE_SOCKADDR_SA_LEN
+  addr.sin_len = sizeof(addr);
+#endif
+  
+  if (!(transfer = whine_malloc(sizeof(struct tftp_transfer))))
+    return;
+  
+  if ((transfer->sockfd = socket(AF_INET, SOCK_DGRAM, 0)) == -1)
+    {
+      free(transfer);
+      return;
+    }
+  
+  transfer->peer = peer;
+  transfer->timeout = now + 2;
+  transfer->backoff = 1;
+  transfer->block = 1;
+  transfer->blocksize = 512;
+  transfer->offset = 0;
+  transfer->file = NULL;
+  transfer->opt_blocksize = transfer->opt_transize = 0;
+  transfer->netascii = transfer->carrylf = 0;
+
+  /* if we have a nailed-down range, iterate until we find a free one. */
+  while (1)
+    {
+      if (bind(transfer->sockfd, (struct sockaddr *)&addr, sizeof(addr)) == -1 ||
+#if defined(IP_MTU_DISCOVER) && defined(IP_PMTUDISC_DONT)
+	  setsockopt(transfer->sockfd, SOL_IP, IP_MTU_DISCOVER, &mtuflag, sizeof(mtuflag)) == -1 ||
+#endif
+	  !fix_fd(transfer->sockfd))
+	{
+	  if (errno == EADDRINUSE && daemon->start_tftp_port != 0)
+	    {
+	      if (++port <= daemon->end_tftp_port)
+		{ 
+		  addr.sin_port = htons(port);
+		  continue;
+		}
+	      my_syslog(MS_TFTP | LOG_ERR, _("unable to get free port for TFTP"));
+	    }
+	  free_transfer(transfer);
+	  return;
+	}
+      break;
+    }
+  
+  p = packet + 2;
+  end = packet + len;
+
+  if (ntohs(*((unsigned short *)packet)) != OP_RRQ ||
+      !(filename = next(&p, end)) ||
+      !(mode = next(&p, end)) ||
+      (strcasecmp(mode, "octet") != 0 && strcasecmp(mode, "netascii") != 0))
+    len = tftp_err(ERR_ILL, packet, _("unsupported request from %s"), inet_ntoa(peer.sin_addr));
+  else
+    {
+      if (strcasecmp(mode, "netascii") == 0)
+	transfer->netascii = 1;
+      
+      while ((opt = next(&p, end)))
+	{
+	  if (strcasecmp(opt, "blksize") == 0)
+	    {
+	      if ((opt = next(&p, end)) &&
+		  !(daemon->options & OPT_TFTP_NOBLOCK))
+		{
+		  transfer->blocksize = atoi(opt);
+		  if (transfer->blocksize < 1)
+		    transfer->blocksize = 1;
+		  if (transfer->blocksize > (unsigned)daemon->packet_buff_sz - 4)
+		    transfer->blocksize = (unsigned)daemon->packet_buff_sz - 4;
+		  /* 32 bytes for IP, UDP and TFTP headers */
+		  if (mtu != 0 && transfer->blocksize > (unsigned)mtu - 32)
+		    transfer->blocksize = (unsigned)mtu - 32;
+		  transfer->opt_blocksize = 1;
+		  transfer->block = 0;
+		}
+	    }
+	  else if (strcasecmp(opt, "tsize") == 0 && next(&p, end) && !transfer->netascii)
+	    {
+	      transfer->opt_transize = 1;
+	      transfer->block = 0;
+	    }
+	}
+
+      /* cope with backslashes from windows boxen. */
+      while ((p = strchr(filename, '\\')))
+	*p = '/';
+
+      strcpy(daemon->namebuff, "/");
+      if (daemon->tftp_prefix)
+	{
+	  if (daemon->tftp_prefix[0] == '/')
+	    daemon->namebuff[0] = 0;
+	  strncat(daemon->namebuff, daemon->tftp_prefix, (MAXDNAME-1) - strlen(daemon->namebuff));
+	  if (daemon->tftp_prefix[strlen(daemon->tftp_prefix)-1] != '/')
+	    strncat(daemon->namebuff, "/", (MAXDNAME-1) - strlen(daemon->namebuff));
+
+	  if (daemon->options & OPT_TFTP_APREF)
+	    {
+	      size_t oldlen = strlen(daemon->namebuff);
+	      struct stat statbuf;
+	      
+	      strncat(daemon->namebuff, inet_ntoa(peer.sin_addr), (MAXDNAME-1) - strlen(daemon->namebuff));
+	      strncat(daemon->namebuff, "/", (MAXDNAME-1) - strlen(daemon->namebuff));
+	      
+	      /* remove unique-directory if it doesn't exist */
+	      if (stat(daemon->namebuff, &statbuf) == -1 || !S_ISDIR(statbuf.st_mode))
+		daemon->namebuff[oldlen] = 0;
+	    }
+		
+	  /* Absolute pathnames OK if they match prefix */
+	  if (filename[0] == '/')
+	    {
+	      if (strstr(filename, daemon->namebuff) == filename)
+		daemon->namebuff[0] = 0;
+	      else
+		filename++;
+	    }
+	}
+      else if (filename[0] == '/')
+	daemon->namebuff[0] = 0;
+      strncat(daemon->namebuff, filename, (MAXDNAME-1) - strlen(daemon->namebuff));
+
+      /* check permissions and open file */
+      if ((transfer->file = check_tftp_fileperm(&len)))
+	{
+	  if ((len = get_block(packet, transfer)) == -1)
+	    len = tftp_err_oops(packet, daemon->namebuff);
+	  else
+	    is_err = 0;
+	}
+    }
+  
+  while (sendto(transfer->sockfd, packet, len, 0, 
+		(struct sockaddr *)&peer, sizeof(peer)) == -1 && errno == EINTR);
+  
+  if (is_err)
+    free_transfer(transfer);
+  else
+    {
+      my_syslog(MS_TFTP | LOG_INFO, _("TFTP sent %s to %s"), daemon->namebuff, inet_ntoa(peer.sin_addr));
+      transfer->next = daemon->tftp_trans;
+      daemon->tftp_trans = transfer;
+    }
+}
+ 
+static struct tftp_file *check_tftp_fileperm(ssize_t *len)
+{
+  char *packet = daemon->packet, *namebuff = daemon->namebuff;
+  struct tftp_file *file;
+  struct tftp_transfer *t;
+  uid_t uid = geteuid();
+  struct stat statbuf;
+  int fd = -1;
+
+  /* trick to ban moving out of the subtree */
+  if (daemon->tftp_prefix && strstr(namebuff, "/../"))
+    goto perm;
+  
+  if ((fd = open(namebuff, O_RDONLY)) == -1)
+    {
+      if (errno == ENOENT)
+	{
+	  *len = tftp_err(ERR_FNF, packet, _("file %s not found"), namebuff);
+	  return NULL;
+	}
+      else if (errno == EACCES)
+	goto perm;
+      else
+	goto oops;
+    }
+  
+  /* stat the file descriptor to avoid stat->open races */
+  if (fstat(fd, &statbuf) == -1)
+    goto oops;
+  
+  /* running as root, must be world-readable */
+  if (uid == 0)
+    {
+      if (!(statbuf.st_mode & S_IROTH))
+	goto perm;
+    }
+  /* in secure mode, must be owned by user running dnsmasq */
+  else if ((daemon->options & OPT_TFTP_SECURE) && uid != statbuf.st_uid)
+    goto perm;
+      
+  /* If we're doing many tranfers from the same file, only 
+     open it once this saves lots of file descriptors 
+     when mass-booting a big cluster, for instance. 
+     Be conservative and only share when inode and name match
+     this keeps error messages sane. */
+  for (t = daemon->tftp_trans; t; t = t->next)
+    if (t->file->dev == statbuf.st_dev && 
+	t->file->inode == statbuf.st_ino &&
+	strcmp(t->file->filename, namebuff) == 0)
+      {
+	close(fd);
+	t->file->refcount++;
+	return t->file;
+      }
+  
+  if (!(file = whine_malloc(sizeof(struct tftp_file) + strlen(namebuff) + 1)))
+    {
+      errno = ENOMEM;
+      goto oops;
+    }
+
+  file->fd = fd;
+  file->size = statbuf.st_size;
+  file->dev = statbuf.st_dev;
+  file->inode = statbuf.st_ino;
+  file->refcount = 1;
+  strcpy(file->filename, namebuff);
+  return file;
+  
+ perm:
+  errno = EACCES;
+  *len =  tftp_err(ERR_PERM, packet, _("cannot access %s: %s"), namebuff);
+  if (fd != -1)
+    close(fd);
+  return NULL;
+
+ oops:
+  *len =  tftp_err_oops(packet, namebuff);
+  if (fd != -1)
+    close(fd);
+  return NULL;
+}
+
+void check_tftp_listeners(fd_set *rset, time_t now)
+{
+  struct tftp_transfer *transfer, *tmp, **up;
+  ssize_t len;
+  
+  struct ack {
+    unsigned short op, block;
+  } *mess = (struct ack *)daemon->packet;
+  
+  /* Check for activity on any existing transfers */
+  for (transfer = daemon->tftp_trans, up = &daemon->tftp_trans; transfer; transfer = tmp)
+    {
+      tmp = transfer->next;
+      
+      if (FD_ISSET(transfer->sockfd, rset))
+	{
+	  /* we overwrote the buffer... */
+	  daemon->srv_save = NULL;
+	  
+	  if ((len = recv(transfer->sockfd, daemon->packet, daemon->packet_buff_sz, 0)) >= (ssize_t)sizeof(struct ack))
+	    {
+	      if (ntohs(mess->op) == OP_ACK && ntohs(mess->block) == (unsigned short)transfer->block) 
+		{
+		  /* Got ack, ensure we take the (re)transmit path */
+		  transfer->timeout = now;
+		  transfer->backoff = 0;
+		  if (transfer->block++ != 0)
+		    transfer->offset += transfer->blocksize - transfer->expansion;
+		}
+	      else if (ntohs(mess->op) == OP_ERR)
+		{
+		  char *p = daemon->packet + sizeof(struct ack);
+		  char *end = daemon->packet + len;
+		  char *err = next(&p, end);
+		  /* Sanitise error message */
+		  if (!err)
+		    err = "";
+		  else
+		    {
+		      char *q, *r;
+		      for (q = r = err; *r; r++)
+			if (isprint((int)*r))
+			  *(q++) = *r;
+		      *q = 0;
+		    }
+		  my_syslog(MS_TFTP | LOG_ERR, _("TFTP error %d %s received from %s"),
+			    (int)ntohs(mess->block), err, 
+			    inet_ntoa(transfer->peer.sin_addr));	
+		  
+		  /* Got err, ensure we take abort */
+		  transfer->timeout = now;
+		  transfer->backoff = 100;
+		}
+	    }
+	}
+      
+      if (difftime(now, transfer->timeout) >= 0.0)
+	{
+	  int endcon = 0;
+
+	  /* timeout, retransmit */
+	  transfer->timeout += 1 + (1<<transfer->backoff);
+	  	  
+	  /* we overwrote the buffer... */
+	  daemon->srv_save = NULL;
+	 
+	  if ((len = get_block(daemon->packet, transfer)) == -1)
+	    {
+	      len = tftp_err_oops(daemon->packet, transfer->file->filename);
+	      endcon = 1;
+	    }
+	  else if (++transfer->backoff > 5)
+	    {
+	      /* don't complain about timeout when we're awaiting the last
+		 ACK, some clients never send it */
+	      if (len != 0)
+		my_syslog(MS_TFTP | LOG_ERR, _("TFTP failed sending %s to %s"), 
+			  transfer->file->filename, inet_ntoa(transfer->peer.sin_addr));
+	      len = 0;
+	    }
+	  
+	  if (len != 0)
+	    while(sendto(transfer->sockfd, daemon->packet, len, 0, 
+			 (struct sockaddr *)&transfer->peer, sizeof(transfer->peer)) == -1 && errno == EINTR);
+	  
+	  if (endcon || len == 0)
+	    {
+	      /* unlink */
+	      *up = tmp;
+	      free_transfer(transfer);
+	      continue;
+	    }
+	}
+
+      up = &transfer->next;
+    }
+}
+
+static void free_transfer(struct tftp_transfer *transfer)
+{
+  close(transfer->sockfd);
+  if (transfer->file && (--transfer->file->refcount) == 0)
+    {
+      close(transfer->file->fd);
+      free(transfer->file);
+    }
+  free(transfer);
+}
+
+static char *next(char **p, char *end)
+{
+  char *ret = *p;
+  size_t len;
+
+  if (*(end-1) != 0 || 
+      *p == end ||
+      (len = strlen(ret)) == 0)
+    return NULL;
+
+  *p += len + 1;
+  return ret;
+}
+
+static ssize_t tftp_err(int err, char *packet, char *message, char *file)
+{
+  struct errmess {
+    unsigned short op, err;
+    char message[];
+  } *mess = (struct errmess *)packet;
+  ssize_t ret = 4;
+  char *errstr = strerror(errno);
+ 
+  mess->op = htons(OP_ERR);
+  mess->err = htons(err);
+  ret += (snprintf(mess->message, 500,  message, file, errstr) + 1);
+  my_syslog(MS_TFTP | LOG_ERR, "TFTP %s", mess->message);
+  
+  return  ret;
+}
+
+static ssize_t tftp_err_oops(char *packet, char *file)
+{
+  return tftp_err(ERR_NOTDEF, packet, _("cannot read %s: %s"), file);
+}
+
+/* return -1 for error, zero for done. */
+static ssize_t get_block(char *packet, struct tftp_transfer *transfer)
+{
+  if (transfer->block == 0)
+    {
+      /* send OACK */
+      char *p;
+      struct oackmess {
+	unsigned short op;
+	char data[];
+      } *mess = (struct oackmess *)packet;
+      
+      p = mess->data;
+      mess->op = htons(OP_OACK);
+      if (transfer->opt_blocksize)
+	{
+	  p += (sprintf(p, "blksize") + 1);
+	  p += (sprintf(p, "%d", transfer->blocksize) + 1);
+	}
+      if (transfer->opt_transize)
+	{
+	  p += (sprintf(p,"tsize") + 1);
+	  p += (sprintf(p, "%u", (unsigned int)transfer->file->size) + 1);
+	}
+
+      return p - packet;
+    }
+  else
+    {
+      /* send data packet */
+      struct datamess {
+	unsigned short op, block;
+	unsigned char data[];
+      } *mess = (struct datamess *)packet;
+      
+      size_t size = transfer->file->size - transfer->offset; 
+      
+      if (transfer->offset > transfer->file->size)
+	return 0; /* finished */
+      
+      if (size > transfer->blocksize)
+	size = transfer->blocksize;
+      
+      mess->op = htons(OP_DATA);
+      mess->block = htons((unsigned short)(transfer->block));
+      
+      if (lseek(transfer->file->fd, transfer->offset, SEEK_SET) == (off_t)-1 ||
+	  !read_write(transfer->file->fd, mess->data, size, 1))
+	return -1;
+      
+      transfer->expansion = 0;
+      
+      /* Map '\n' to CR-LF in netascii mode */
+      if (transfer->netascii)
+	{
+	  size_t i;
+	  int newcarrylf;
+
+	  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
+		  size++; /* room in this block */
+	      
+		/* make space and insert CR */
+		memmove(&mess->data[i+1], &mess->data[i], size - (i + 1));
+		mess->data[i] = '\r';
+		
+		i++;
+	      }
+	  transfer->carrylf = newcarrylf;
+	  
+	}
+
+      return size + 4;
+    }
+}
+
+#endif
diff --git a/src/util.c b/src/util.c
new file mode 100755
index 0000000..ce77f05
--- /dev/null
+++ b/src/util.c
@@ -0,0 +1,514 @@
+/* dnsmasq is Copyright (c) 2000-2009 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/>.
+*/
+
+/* The SURF random number generator was taken from djbdns-1.05, by 
+   Daniel J Bernstein, which is public domain. */
+
+
+#include "dnsmasq.h"
+
+#ifdef HAVE_BROKEN_RTC
+#include <sys/times.h>
+#endif
+
+#ifdef LOCALEDIR
+#include <idna.h>
+#endif
+
+#ifdef HAVE_ARC4RANDOM
+void rand_init(void)
+{
+  return;
+}
+
+unsigned short rand16(void)
+{
+   return (unsigned short) (arc4random() >> 15);
+}
+
+#else
+
+/* SURF random number generator */
+
+typedef unsigned int uint32;
+
+static uint32 seed[32];
+static uint32 in[12];
+static uint32 out[8];
+
+void rand_init()
+{
+  int fd = open(RANDFILE, O_RDONLY);
+  
+  if (fd == -1 ||
+      !read_write(fd, (unsigned char *)&seed, sizeof(seed), 1) ||
+      !read_write(fd, (unsigned char *)&in, sizeof(in), 1))
+    die(_("failed to seed the random number generator: %s"), NULL, EC_MISC);
+  
+  close(fd);
+}
+
+#define ROTATE(x,b) (((x) << (b)) | ((x) >> (32 - (b))))
+#define MUSH(i,b) x = t[i] += (((x ^ seed[i]) + sum) ^ ROTATE(x,b));
+
+static void surf(void)
+{
+  uint32 t[12]; uint32 x; uint32 sum = 0;
+  int r; int i; int loop;
+
+  for (i = 0;i < 12;++i) t[i] = in[i] ^ seed[12 + i];
+  for (i = 0;i < 8;++i) out[i] = seed[24 + i];
+  x = t[11];
+  for (loop = 0;loop < 2;++loop) {
+    for (r = 0;r < 16;++r) {
+      sum += 0x9e3779b9;
+      MUSH(0,5) MUSH(1,7) MUSH(2,9) MUSH(3,13)
+      MUSH(4,5) MUSH(5,7) MUSH(6,9) MUSH(7,13)
+      MUSH(8,5) MUSH(9,7) MUSH(10,9) MUSH(11,13)
+    }
+    for (i = 0;i < 8;++i) out[i] ^= t[i + 4];
+  }
+}
+
+unsigned short rand16(void)
+{
+  static int outleft = 0;
+
+  if (!outleft) {
+    if (!++in[0]) if (!++in[1]) if (!++in[2]) ++in[3];
+    surf();
+    outleft = 8;
+  }
+
+  return (unsigned short) out[--outleft];
+}
+
+#endif
+
+static int check_name(char *in)
+{
+  /* remove trailing . 
+     also fail empty string and label > 63 chars */
+  size_t dotgap = 0, l = strlen(in);
+  char c;
+  int nowhite = 0;
+  
+  if (l == 0 || l > MAXDNAME) return 0;
+  
+  if (in[l-1] == '.')
+    {
+      if (l == 1) return 0;
+      in[l-1] = 0;
+    }
+  
+  for (; (c = *in); in++)
+    {
+      if (c == '.')
+	dotgap = 0;
+      else if (++dotgap > MAXLABEL)
+	return 0;
+      else if (isascii(c) && iscntrl(c)) 
+	/* iscntrl only gives expected results for ascii */
+	return 0;
+#ifndef LOCALEDIR
+      else if (!isascii(c))
+	return 0;
+#endif
+      else if (c != ' ')
+	nowhite = 1;
+    }
+
+  if (!nowhite)
+    return 0;
+
+  return 1;
+}
+
+/* Hostnames have a more limited valid charset than domain names
+   so check for legal char a-z A-Z 0-9 - _ 
+   Note that this may receive a FQDN, so only check the first label 
+   for the tighter criteria. */
+int legal_hostname(char *name)
+{
+  char c;
+
+  if (!check_name(name))
+    return 0;
+
+  for (; (c = *name); name++)
+    /* check for legal char a-z A-Z 0-9 - _ . */
+    {
+      if ((c >= 'A' && c <= 'Z') ||
+	  (c >= 'a' && c <= 'z') ||
+	  (c >= '0' && c <= '9') ||
+	  c == '-' || c == '_')
+	continue;
+      
+      /* end of hostname part */
+      if (c == '.')
+	return 1;
+      
+      return 0;
+    }
+  
+  return 1;
+}
+  
+char *canonicalise(char *in, int *nomem)
+{
+  char *ret = NULL;
+#ifdef LOCALEDIR
+  int rc;
+#endif
+
+  if (nomem)
+    *nomem = 0;
+  
+  if (!check_name(in))
+    return NULL;
+  
+#ifdef LOCALEDIR
+  if ((rc = idna_to_ascii_lz(in, &ret, 0)) != IDNA_SUCCESS)
+    {
+      if (ret)
+	free(ret);
+
+      if (nomem && (rc == IDNA_MALLOC_ERROR || rc == IDNA_DLOPEN_ERROR))
+	{
+	  my_syslog(LOG_ERR, _("failed to allocate memory"));
+	  *nomem = 1;
+	}
+    
+      return NULL;
+    }
+#else
+  if ((ret = whine_malloc(strlen(in)+1)))
+    strcpy(ret, in);
+  else if (nomem)
+    *nomem = 1;    
+#endif
+
+  return ret;
+}
+
+unsigned char *do_rfc1035_name(unsigned char *p, char *sval)
+{
+  int j;
+  
+  while (sval && *sval)
+    {
+      unsigned char *cp = p++;
+      for (j = 0; *sval && (*sval != '.'); sval++, j++)
+	*p++ = *sval;
+      *cp  = j;
+      if (*sval)
+	sval++;
+    }
+  return p;
+}
+
+/* for use during startup */
+void *safe_malloc(size_t size)
+{
+  void *ret = malloc(size);
+  
+  if (!ret)
+    die(_("could not get memory"), NULL, EC_NOMEM);
+     
+  return ret;
+}    
+
+void safe_pipe(int *fd, int read_noblock)
+{
+  if (pipe(fd) == -1 || 
+      !fix_fd(fd[1]) ||
+      (read_noblock && !fix_fd(fd[0])))
+    die(_("cannot create pipe: %s"), NULL, EC_MISC);
+}
+
+void *whine_malloc(size_t size)
+{
+  void *ret = malloc(size);
+
+  if (!ret)
+    my_syslog(LOG_ERR, _("failed to allocate %d bytes"), (int) size);
+
+  return ret;
+}
+
+int sockaddr_isequal(union mysockaddr *s1, union mysockaddr *s2)
+{
+  if (s1->sa.sa_family == s2->sa.sa_family)
+    { 
+      if (s1->sa.sa_family == AF_INET &&
+	  s1->in.sin_port == s2->in.sin_port &&
+	  s1->in.sin_addr.s_addr == s2->in.sin_addr.s_addr)
+	return 1;
+#ifdef HAVE_IPV6      
+      if (s1->sa.sa_family == AF_INET6 &&
+	  s1->in6.sin6_port == s2->in6.sin6_port &&
+	  IN6_ARE_ADDR_EQUAL(&s1->in6.sin6_addr, &s2->in6.sin6_addr))
+	return 1;
+#endif
+    }
+  return 0;
+}
+
+int sa_len(union mysockaddr *addr)
+{
+#ifdef HAVE_SOCKADDR_SA_LEN
+  return addr->sa.sa_len;
+#else
+#ifdef HAVE_IPV6
+  if (addr->sa.sa_family == AF_INET6)
+    return sizeof(addr->in6);
+  else
+#endif
+    return sizeof(addr->in); 
+#endif
+}
+
+/* don't use strcasecmp and friends here - they may be messed up by LOCALE */
+int hostname_isequal(char *a, char *b)
+{
+  unsigned int c1, c2;
+  
+  do {
+    c1 = (unsigned char) *a++;
+    c2 = (unsigned char) *b++;
+    
+    if (c1 >= 'A' && c1 <= 'Z')
+      c1 += 'a' - 'A';
+    if (c2 >= 'A' && c2 <= 'Z')
+      c2 += 'a' - 'A';
+    
+    if (c1 != c2)
+      return 0;
+  } while (c1);
+  
+  return 1;
+}
+    
+time_t dnsmasq_time(void)
+{
+#ifdef HAVE_BROKEN_RTC
+  struct tms dummy;
+  static long tps = 0;
+
+  if (tps == 0)
+    tps = sysconf(_SC_CLK_TCK);
+
+  return (time_t)(times(&dummy)/tps);
+#else
+  return time(NULL);
+#endif
+}
+
+int is_same_net(struct in_addr a, struct in_addr b, struct in_addr mask)
+{
+  return (a.s_addr & mask.s_addr) == (b.s_addr & mask.s_addr);
+} 
+
+/* returns port number from address */
+int prettyprint_addr(union mysockaddr *addr, char *buf)
+{
+  int port = 0;
+  
+#ifdef HAVE_IPV6
+  if (addr->sa.sa_family == AF_INET)
+    {
+      inet_ntop(AF_INET, &addr->in.sin_addr, buf, ADDRSTRLEN);
+      port = ntohs(addr->in.sin_port);
+    }
+  else if (addr->sa.sa_family == AF_INET6)
+    {
+      inet_ntop(AF_INET6, &addr->in6.sin6_addr, buf, ADDRSTRLEN);
+      port = ntohs(addr->in6.sin6_port);
+    }
+#else
+  strcpy(buf, inet_ntoa(addr->in.sin_addr));
+  port = ntohs(addr->in.sin_port); 
+#endif
+  
+  return port;
+}
+
+void prettyprint_time(char *buf, unsigned int t)
+{
+  if (t == 0xffffffff)
+    sprintf(buf, _("infinite"));
+  else
+    {
+      unsigned int x, p = 0;
+       if ((x = t/86400))
+	p += sprintf(&buf[p], "%dd", x);
+       if ((x = (t/3600)%24))
+	p += sprintf(&buf[p], "%dh", x);
+      if ((x = (t/60)%60))
+	p += sprintf(&buf[p], "%dm", x);
+      if ((x = t%60))
+	p += sprintf(&buf[p], "%ds", x);
+    }
+}
+
+
+/* in may equal out, when maxlen may be -1 (No max len). */
+int parse_hex(char *in, unsigned char *out, int maxlen, 
+	      unsigned int *wildcard_mask, int *mac_type)
+{
+  int mask = 0, i = 0;
+  char *r;
+    
+  if (mac_type)
+    *mac_type = 0;
+  
+  while (maxlen == -1 || i < maxlen)
+    {
+      for (r = in; *r != 0 && *r != ':' && *r != '-'; r++);
+      if (*r == 0)
+	maxlen = i;
+      
+      if (r != in )
+	{
+	  if (*r == '-' && i == 0 && mac_type)
+	   {
+	      *r = 0;
+	      *mac_type = strtol(in, NULL, 16);
+	      mac_type = NULL;
+	   }
+	  else
+	    {
+	      *r = 0;
+	      mask = mask << 1;
+	      if (strcmp(in, "*") == 0)
+		mask |= 1;
+	      else
+		out[i] = strtol(in, NULL, 16);
+	      i++;
+	    }
+	}
+      in = r+1;
+    }
+  
+  if (wildcard_mask)
+    *wildcard_mask = mask;
+
+  return i;
+}
+
+/* return 0 for no match, or (no matched octets) + 1 */
+int memcmp_masked(unsigned char *a, unsigned char *b, int len, unsigned int mask)
+{
+  int i, count;
+  for (count = 1, i = len - 1; i >= 0; i--, mask = mask >> 1)
+    if (!(mask & 1))
+      {
+	if (a[i] == b[i])
+	  count++;
+	else
+	  return 0;
+      }
+  return count;
+}
+
+/* _note_ may copy buffer */
+int expand_buf(struct iovec *iov, size_t size)
+{
+  void *new;
+
+  if (size <= (size_t)iov->iov_len)
+    return 1;
+
+  if (!(new = whine_malloc(size)))
+    {
+      errno = ENOMEM;
+      return 0;
+    }
+
+  if (iov->iov_base)
+    {
+      memcpy(new, iov->iov_base, iov->iov_len);
+      free(iov->iov_base);
+    }
+
+  iov->iov_base = new;
+  iov->iov_len = size;
+
+  return 1;
+}
+
+char *print_mac(char *buff, unsigned char *mac, int len)
+{
+  char *p = buff;
+  int i;
+   
+  if (len == 0)
+    sprintf(p, "<null>");
+  else
+    for (i = 0; i < len; i++)
+      p += sprintf(p, "%.2x%s", mac[i], (i == len - 1) ? "" : ":");
+  
+  return buff;
+}
+
+void bump_maxfd(int fd, int *max)
+{
+  if (fd > *max)
+    *max = fd;
+}
+
+int retry_send(void)
+{
+   struct timespec waiter;
+   if (errno == EAGAIN)
+     {
+       waiter.tv_sec = 0;
+       waiter.tv_nsec = 10000;
+       nanosleep(&waiter, NULL);
+       return 1;
+     }
+   
+   if (errno == EINTR)
+     return 1;
+
+   return 0;
+}
+
+int read_write(int fd, unsigned char *packet, int size, int rw)
+{
+  ssize_t n, done;
+  
+  for (done = 0; done < size; done += n)
+    {
+    retry:
+      if (rw)
+        n = read(fd, &packet[done], (size_t)(size - done));
+      else
+        n = write(fd, &packet[done], (size_t)(size - done));
+
+      if (n == 0)
+        return 0;
+      else if (n == -1)
+        {
+          if (retry_send() || errno == ENOMEM || errno == ENOBUFS)
+            goto retry;
+          else
+            return 0;
+        }
+    }
+  return 1;
+}
+