import of dnsmasq-2.53.tar.gz
diff --git a/src/dhcp.c b/src/dhcp.c
index dcc57b1..8c300cf 100644
--- a/src/dhcp.c
+++ b/src/dhcp.c
@@ -127,7 +127,10 @@
   int iface_index = 0, unicast_dest = 0, is_inform = 0;
   struct in_addr iface_addr, *addrp = NULL;
   struct iface_param parm;
-
+#ifdef HAVE_LINUX_NETWORK
+  struct arpreq arp_req;
+#endif
+  
   union {
     struct cmsghdr align; /* this ensures alignment */
 #if defined(HAVE_LINUX_NETWORK)
@@ -189,8 +192,13 @@
     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)
+	  union {
+	    unsigned char *c;
+	    struct in_pktinfo *p;
+	  } p;
+	  p.c = CMSG_DATA(cmptr);
+	  iface_index = p.p->ipi_ifindex;
+	  if (p.p->ipi_addr.s_addr != INADDR_BROADCAST)
 	    unicast_dest = 1;
 	}
 
@@ -198,20 +206,37 @@
   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;
-
+        {
+	  union {
+            unsigned char *c;
+            struct sockaddr_dl *s;
+          } p;
+	  p.c = CMSG_DATA(cmptr);
+	  iface_index = p.s->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));
-	  
+	{
+	  union {
+	    unsigned char *c;
+	    unsigned int *i;
+	  } p;
+	  p.c = CMSG_DATA(cmptr);
+	  iface_index = *(p.i);
+	}
 #endif
 	
   if (!indextoname(daemon->dhcpfd, iface_index, ifr.ifr_name))
     return;
 
+#ifdef HAVE_LINUX_NETWORK
+  /* ARP fiddling uses original interface even if we pretend to use a different one. */
+  strncpy(arp_req.arp_dev, ifr.ifr_name, 16);
+#endif 
+
 #ifdef MSG_BCAST
   /* OpenBSD tells us when a packet was broadcast */
   if (!(msg.msg_flags & MSG_BCAST))
@@ -231,18 +256,14 @@
   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;
-    }
+
+  /* weird libvirt-inspired access control */
+  for (context = daemon->dhcp; context; context = context->next)
+    if (!context->interface || strcmp(context->interface, ifr.ifr_name) == 0)
+      break;
+
+  if (!context)
+    return;
   
   /* unlinked contexts are marked by context->current == context */
   for (context = daemon->dhcp; context; context = context->next)
@@ -253,6 +274,27 @@
   parm.current = NULL;
   parm.ind = iface_index;
 
+    /* interface may have been changed by alias in iface_check, make sure it gets priority in case
+       there is more than one address on the interface in the same subnet */
+  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;
+      if (ioctl(daemon->dhcpfd, SIOCGIFNETMASK, &ifr) != -1)
+	{
+	  struct in_addr netmask =  ((struct sockaddr_in *) &ifr.ifr_addr)->sin_addr;
+	  if (ioctl(daemon->dhcpfd, SIOCGIFBRDADDR, &ifr) != -1)
+	    {
+	      struct in_addr broadcast =  ((struct sockaddr_in *) &ifr.ifr_addr)->sin_addr;
+	      complete_context(iface_addr, iface_index, netmask, broadcast, &parm);
+	    }
+	}
+    } 
+
   if (!iface_enumerate(&parm, complete_context, NULL))
     return;
   lease_prune(NULL, now); /* lose any expired leases */
@@ -324,15 +366,14 @@
     {
       /* 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);
+      memcpy(&arp_req.arp_pa, &dest, sizeof(struct sockaddr_in));
+      arp_req.arp_ha.sa_family = mess->htype;
+      memcpy(arp_req.arp_ha.sa_data, mess->chaddr, mess->hlen);
+      /* interface name already copied in */
+      arp_req.arp_flags = ATF_COM;
+      ioctl(daemon->dhcpfd, SIOCSARP, &arp_req);
     }
 #elif defined(HAVE_SOLARIS_NETWORK)
   else if ((ntohs(mess->flags) & 0x8000) || mess->hlen != ETHER_ADDR_LEN || mess->htype != ARPHRD_ETHER)
@@ -491,13 +532,15 @@
   if (!(tmp = address_available(context, taddr, netids)))
     {
       for (tmp = context; tmp; tmp = tmp->current)
-	if (is_same_net(taddr, tmp->start, tmp->netmask) && 
+	if (match_netid(tmp->filter, netids, 1) &&
+	    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))
+	  if (match_netid(tmp->filter, netids, 1) &&
+	      is_same_net(taddr, tmp->start, tmp->netmask))
 	    break;
     }
   
@@ -530,7 +573,8 @@
 
   for (; check; check = check->next)
     {
-      if (check->net[0] != '#')
+      /* '#' for not is for backwards compat. */
+      if (check->net[0] != '!' && check->net[0] != '#')
 	{
 	  for (tmp1 = pool; tmp1; tmp1 = tmp1->next)
 	    if (strcmp(check->net, tmp1->net) == 0)
@@ -546,6 +590,22 @@
   return 1;
 }
 
+struct dhcp_netid *run_tag_if(struct dhcp_netid *tags)
+{
+  struct tag_if *exprs;
+  struct dhcp_netid_list *list;
+
+  for (exprs = daemon->tag_if; exprs; exprs = exprs->next)
+    if (match_netid(exprs->tag, tags, 1))
+      for (list = exprs->set; list; list = list->next)
+	{
+	  list->list->next = tags;
+	  tags = list->list;
+	}
+
+  return tags;
+}
+
 int address_allocate(struct dhcp_context *context,
 		     struct in_addr *addrp, unsigned char *hwaddr, int hw_len, 
 		     struct dhcp_netid *netids, time_t now)   
@@ -559,9 +619,10 @@
   int i, pass;
   unsigned int j; 
 
-  /* hash hwaddr */
+  /* hash hwaddr: use the SDBM hashing algorithm.  Seems to give good
+     dispersal even with similarly-valued "strings". */ 
   for (j = 0, i = 0; i < hw_len; i++)
-    j += hwaddr[i] + (hwaddr[i] << 8) + (hwaddr[i] << 16);
+    j += hwaddr[i] + (j << 6) + (j << 16) - j;
   
   for (pass = 0; pass <= 1; pass++)
     for (c = context; c; c = c->current)
@@ -977,29 +1038,40 @@
 /* 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. */
+   so check here that the domain name is legal as a hostname. 
+   NOTE: we're only allowed to overwrite daemon->dhcp_buff if we succeed. */
 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;
+      char *dot, *hostname = cache_get_name(lookup);
+      dot = strchr(hostname, '.');
+      
+      if (dot && strlen(dot+1) != 0)
+	{
+	  char *d2 = get_domain(addr);
+	  if (!d2 || !hostname_isequal(dot+1, d2))
+	    return NULL; /* wrong domain */
+	}
+
+      if (!legal_hostname(hostname))
+	return NULL;
+      
+      strncpy(daemon->dhcp_buff, hostname, 256);
+      daemon->dhcp_buff[255] = 0;
+      strip_hostname(daemon->dhcp_buff);
+
+      return daemon->dhcp_buff;
     }
   
-  return hostname;
+  return NULL;
 }
 
 /* return domain or NULL if none. */