Handle async notification of address changes using the event system.
diff --git a/CHANGELOG b/CHANGELOG
index aba3a67..4e56a6a 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -4,6 +4,10 @@
 	    Add support for "ipsets" in *BSD, using pf. Thanks to 
 	    Sven Falempim for the patch.
 
+	    Fix race condition which could lock up dnsmasq when an 
+	    interface goes down and up rapidly. Thanks to Conrad 
+	    Kostecki for helping to chase this down.
+	    
 
 version 2.71
             Subtle change to error handling to help DNSSEC validation 
diff --git a/src/bpf.c b/src/bpf.c
index f9c6063..fea84b1 100644
--- a/src/bpf.c
+++ b/src/bpf.c
@@ -376,7 +376,7 @@
     die(_("cannot create PF_ROUTE socket: %s"), NULL, EC_BADNET);
 }
 
-void route_sock(time_t now)
+void route_sock(void)
 {
   struct if_msghdr *msg;
   int rc = recv(daemon->routefd, daemon->packet, daemon->packet_buff_sz, 0);
@@ -401,7 +401,7 @@
    else if (msg->ifm_type == RTM_NEWADDR)
      {
        del_family = 0;
-       newaddress(now);
+       send_newaddr();
      }
    else if (msg->ifm_type == RTM_DELADDR)
      {
@@ -439,7 +439,7 @@
 	       of += sizeof(long) - (diff & (sizeof(long) - 1));
 	   }
        
-       newaddress(now);
+       send_newaddr();
      }
 }
 
diff --git a/src/dhcp6.c b/src/dhcp6.c
index 0e470cf..bc48fdd 100644
--- a/src/dhcp6.c
+++ b/src/dhcp6.c
@@ -708,20 +708,12 @@
 
 void dhcp_construct_contexts(time_t now)
 { 
-  static int active = 0;
   struct dhcp_context *context, *tmp, **up;
   struct cparam param;
   param.newone = 0;
   param.newname = 0;
   param.now = now;
 
-  /* Various calls that we make may end up calling iface_enumerate(), which can then 
-     call us again, We're NOT re-entrant, so ignore a second invokation. */
-  if (active)
-    return;
-
-  active = 1;
-
   for (context = daemon->dhcp6; context; context = context->next)
     if (context->flags & CONTEXT_CONSTRUCTED)
       context->flags |= CONTEXT_GC;
@@ -779,8 +771,6 @@
 	/* Not doing DHCP, so no lease system, manage alarms for ra only */
 	send_alarm(periodic_ra(now), now);
     }
-
-  active = 0;
 }
 
 #endif
diff --git a/src/dnsmasq.c b/src/dnsmasq.c
index 1c96a0e..5560aa9 100644
--- a/src/dnsmasq.c
+++ b/src/dnsmasq.c
@@ -917,10 +917,10 @@
 
 #if defined(HAVE_LINUX_NETWORK)
       if (FD_ISSET(daemon->netlinkfd, &rset))
-	netlink_multicast(now);
+	netlink_multicast();
 #elif defined(HAVE_BSD_NETWORK)
       if (FD_ISSET(daemon->routefd, &rset))
-	route_sock(now);
+	route_sock();
 #endif
 
       /* Check for changes to resolv files once per second max. */
@@ -1037,6 +1037,11 @@
     }
 }
 
+void send_newaddr(void)
+{
+  send_event(pipewrite, EVENT_NEWADDR, 0, NULL);
+}
+
 void send_event(int fd, int event, int data, char *msg)
 {
   struct event_desc ev;
@@ -1230,6 +1235,10 @@
 	if (daemon->log_file != NULL)
 	  log_reopen(daemon->log_file);
 	break;
+
+      case EVENT_NEWADDR:
+	newaddress(now);
+	break;
 	
       case EVENT_TERM:
 	/* Knock all our children on the head. */
diff --git a/src/dnsmasq.h b/src/dnsmasq.h
index e11fad4..de98956 100644
--- a/src/dnsmasq.h
+++ b/src/dnsmasq.h
@@ -165,6 +165,7 @@
 #define EVENT_LUA_ERR   19
 #define EVENT_TFTP_ERR  20
 #define EVENT_INIT      21
+#define EVENT_NEWADDR   22
 
 /* Exit codes. */
 #define EC_GOOD        0
@@ -1288,6 +1289,7 @@
 int make_icmp_sock(void);
 int icmp_ping(struct in_addr addr);
 #endif
+void send_newaddr(void);
 void send_alarm(time_t event, time_t now);
 void send_event(int fd, int event, int data, char *msg);
 void clear_cache_and_reload(time_t now);
@@ -1296,7 +1298,7 @@
 /* netlink.c */
 #ifdef HAVE_LINUX_NETWORK
 void netlink_init(void);
-void netlink_multicast(time_t now);
+void netlink_multicast(void);
 #endif
 
 /* bpf.c */
@@ -1305,7 +1307,7 @@
 void send_via_bpf(struct dhcp_packet *mess, size_t len,
 		  struct in_addr iface_addr, struct ifreq *ifr);
 void route_init(void);
-void route_sock(time_t now);
+void route_sock(void);
 #endif
 
 /* bpf.c or netlink.c */
diff --git a/src/netlink.c b/src/netlink.c
index 3c1e465..022c363 100644
--- a/src/netlink.c
+++ b/src/netlink.c
@@ -38,7 +38,7 @@
 static struct iovec iov;
 static u32 netlink_pid;
 
-static int nl_async(struct nlmsghdr *h);
+static void nl_async(struct nlmsghdr *h);
 
 void netlink_init(void)
 {
@@ -142,7 +142,7 @@
   struct nlmsghdr *h;
   ssize_t len;
   static unsigned int seq = 0;
-  int callback_ok = 1, newaddr = 0;
+  int callback_ok = 1;
 
   struct {
     struct nlmsghdr nlh;
@@ -191,21 +191,10 @@
 	if (h->nlmsg_seq != seq || h->nlmsg_pid != netlink_pid || h->nlmsg_type == NLMSG_ERROR)
 	  {
 	    /* May be multicast arriving async */
-	    if (nl_async(h))
-	      {
-		newaddr = 1; 
-		enumerate_interfaces(1); /* reset */
-	      }
+	    nl_async(h);
 	  }
 	else if (h->nlmsg_type == NLMSG_DONE)
-	  {
-	    /* handle async new interface address arrivals, these have to be done
-	       after we complete as we're not re-entrant */
-	    if (newaddr) 
-	      newaddress(dnsmasq_time());
-		
-	    return callback_ok;
-	  }
+	  return callback_ok;
 	else if (h->nlmsg_type == RTM_NEWADDR && family != AF_UNSPEC && family != AF_LOCAL)
 	  {
 	    struct ifaddrmsg *ifa = NLMSG_DATA(h);  
@@ -330,11 +319,11 @@
     }
 }
 
-void netlink_multicast(time_t now)
+void netlink_multicast(void)
 {
   ssize_t len;
   struct nlmsghdr *h;
-  int flags, newaddr = 0;
+  int flags;
   
   /* don't risk blocking reading netlink messages here. */
   if ((flags = fcntl(daemon->netlinkfd, F_GETFL)) == -1 ||
@@ -343,24 +332,19 @@
   
   if ((len = netlink_recv()) != -1)
     for (h = (struct nlmsghdr *)iov.iov_base; NLMSG_OK(h, (size_t)len); h = NLMSG_NEXT(h, len))
-      if (nl_async(h))
-	newaddr = 1;
+      nl_async(h);
   
   /* restore non-blocking status */
   fcntl(daemon->netlinkfd, F_SETFL, flags);
-  
-  if (newaddr) 
-    newaddress(now);
 }
 
-static int nl_async(struct nlmsghdr *h)
+static void nl_async(struct nlmsghdr *h)
 {
   if (h->nlmsg_type == NLMSG_ERROR)
     {
       struct nlmsgerr *err = NLMSG_DATA(h);
       if (err->error != 0)
 	my_syslog(LOG_ERR, _("netlink returns error: %s"), strerror(-(err->error)));
-      return 0;
     }
   else if (h->nlmsg_pid == 0 && h->nlmsg_type == RTM_NEWROUTE) 
     {
@@ -385,18 +369,15 @@
 	      else if (daemon->rfd_save && daemon->rfd_save->refcount != 0)
 		fd = daemon->rfd_save->fd;
 	      else
-		return 0;
+		return;
 	      
 	      while(sendto(fd, daemon->packet, daemon->packet_len, 0,
 			   &daemon->srv_save->addr.sa, sa_len(&daemon->srv_save->addr)) == -1 && retry_send()); 
 	    }
 	}
-      return 0;
     }
   else if (h->nlmsg_type == RTM_NEWADDR || h->nlmsg_type == RTM_DELADDR) 
-    return 1; /* clever bind mode - rescan */
-  
-  return 0;
+    send_newaddr();
 }
 #endif
 
diff --git a/src/network.c b/src/network.c
index 3db7c43..ff9cabb 100644
--- a/src/network.c
+++ b/src/network.c
@@ -551,7 +551,7 @@
 int enumerate_interfaces(int reset)
 {
   static struct addrlist *spare = NULL;
-  static int done = 0, active = 0;
+  static int done = 0;
   struct iface_param param;
   int errsave, ret = 1;
   struct addrlist *addr, *tmp;
@@ -570,14 +570,11 @@
       return 1;
     }
 
-  if (done || active)
+  if (done)
     return 1;
 
   done = 1;
 
-  /* protect against recusive calls from iface_enumerate(); */
-  active = 1;
-
   if ((param.fd = socket(PF_INET, SOCK_DGRAM, 0)) == -1)
     return 0;
  
@@ -677,10 +674,8 @@
     }
   
   errno = errsave;
-  
   spare = param.spare;
-  active = 0;
-  
+    
   return ret;
 }