1Index: src/dnsmasq.c
2===================================================================
3--- src/dnsmasq.c	(revision 696)
4+++ src/dnsmasq.c	(revision 821)
5@@ -59,7 +59,6 @@
6 static int set_dns_listeners(time_t now, fd_set *set, int *maxfdp);
7 static void check_dns_listeners(fd_set *set, time_t now);
8 static void sig_handler(int sig);
9-static void async_event(int pipe, time_t now);
10 static void fatal_event(struct event_desc *ev);
11 static void poll_resolv(void);
12 
13@@ -275,7 +274,7 @@
14   piperead = pipefd[0];
15   pipewrite = pipefd[1];
16   /* prime the pipe to load stuff first time. */
17-  send_event(pipewrite, EVENT_RELOAD, 0); 
18+  send_event(pipewrite, EVENT_RELOAD, 0, 0); 
19 
20   err_pipe[1] = -1;
21   
22@@ -340,7 +339,7 @@
23 	    }
24 	  else if (getuid() == 0)
25 	    {
26-	      send_event(err_pipe[1], EVENT_PIDFILE, errno);
27+	      send_event(err_pipe[1], EVENT_PIDFILE, errno, 0);
28 	      _exit(0);
29 	    }
30 	}
31@@ -372,7 +371,7 @@
32 	  (setgroups(0, &dummy) == -1 ||
33 	   setgid(gp->gr_gid) == -1))
34 	{
35-	  send_event(err_pipe[1], EVENT_GROUP_ERR, errno);
36+	  send_event(err_pipe[1], EVENT_GROUP_ERR, errno, 0);
37 	  _exit(0);
38 	}
39   
40@@ -415,14 +414,14 @@
41 
42 	  if (bad_capabilities != 0)
43 	    {
44-	      send_event(err_pipe[1], EVENT_CAP_ERR, bad_capabilities);
45+	      send_event(err_pipe[1], EVENT_CAP_ERR, bad_capabilities, 0);
46 	      _exit(0);
47 	    }
48 	  
49 	  /* finally drop root */
50 	  if (setuid(ent_pw->pw_uid) == -1)
51 	    {
52-	      send_event(err_pipe[1], EVENT_USER_ERR, errno);
53+	      send_event(err_pipe[1], EVENT_USER_ERR, errno, 0);
54 	      _exit(0);
55 	    }     
56 
57@@ -434,7 +433,7 @@
58 	  /* lose the setuid and setgid capbilities */
59 	  if (capset(hdr, data) == -1)
60 	    {
61-	      send_event(err_pipe[1], EVENT_CAP_ERR, errno);
62+	      send_event(err_pipe[1], EVENT_CAP_ERR, errno, 0);
63 	      _exit(0);
64 	    }
65 #endif
66@@ -647,7 +646,7 @@
67 	}
68       
69       if (FD_ISSET(piperead, &rset))
70-	async_event(piperead, now);
71+	async_event(piperead, now, NULL, 0);
72       
73 #ifdef HAVE_LINUX_NETWORK
74       if (FD_ISSET(daemon->netlinkfd, &rset))
75@@ -674,7 +673,7 @@
76 #endif      
77 
78       if (daemon->dhcp && FD_ISSET(daemon->dhcpfd, &rset))
79-	dhcp_packet(now);
80+	dhcp_packet(piperead, now);
81 
82 #ifndef NO_FORK
83       if (daemon->helperfd != -1 && FD_ISSET(daemon->helperfd, &wset))
84@@ -719,17 +718,18 @@
85       else
86 	return;
87 
88-      send_event(pipewrite, event, 0); 
89+      send_event(pipewrite, event, 0, 0); 
90       errno = errsave;
91     }
92 }
93 
94-void send_event(int fd, int event, int data)
95+void send_event(int fd, int event, int data, int priv)
96 {
97   struct event_desc ev;
98   
99   ev.event = event;
100   ev.data = data;
101+  ev.priv = priv;
102   
103   /* error pipe, debug mode. */
104   if (fd == -1)
105@@ -771,14 +771,17 @@
106       die(_("cannot open %s: %s"), daemon->log_file ? daemon->log_file : "log", EC_FILE);
107     }
108 }	
109-      
110-static void async_event(int pipe, time_t now)
111+
112+/* returns the private data of the event
113+ */
114+int async_event(int pipe, time_t now, struct event_desc* event, unsigned int secs)
115 {
116   pid_t p;
117   struct event_desc ev;
118   int i;
119 
120-  if (read_write(pipe, (unsigned char *)&ev, sizeof(ev), 1))
121+  if (read_timeout(pipe, (unsigned char *)&ev, sizeof(ev), now, secs) > 0) 
122+    {
123     switch (ev.event)
124       {
125       case EVENT_RELOAD:
126@@ -872,6 +875,14 @@
127 	flush_log();
128 	exit(EC_GOOD);
129       }
130+    }
131+  else
132+    return -1; /* timeout */
133+
134+  if (event)
135+    memcpy( event, &ev, sizeof(ev));
136+    
137+  return 0;
138 }
139 
140 static void poll_resolv()
141Index: src/config.h
142===================================================================
143--- src/config.h	(revision 696)
144+++ src/config.h	(revision 821)
145@@ -51,6 +51,8 @@
146 #define TFTP_MAX_CONNECTIONS 50 /* max simultaneous connections */
147 #define LOG_MAX 5 /* log-queue length */
148 #define RANDFILE "/dev/urandom"
149+#define SCRIPT_TIMEOUT 6
150+#define LEASE_CHECK_TIMEOUT 10
151 
152 /* DBUS interface specifics */
153 #define DNSMASQ_SERVICE "uk.org.thekelleys.dnsmasq"
154Index: src/dnsmasq.h
155===================================================================
156--- src/dnsmasq.h	(revision 696)
157+++ src/dnsmasq.h	(revision 821)
158@@ -116,6 +116,7 @@
159 /* Async event queue */
160 struct event_desc {
161   int event, data;
162+  unsigned int priv;
163 };
164 
165 #define EVENT_RELOAD    1
166@@ -390,6 +391,7 @@
167 #define ACTION_OLD_HOSTNAME  2
168 #define ACTION_OLD           3
169 #define ACTION_ADD           4
170+#define ACTION_ACCESS        5
171 
172 #define DHCP_CHADDR_MAX 16
173 
174@@ -709,6 +711,7 @@
175 char *print_mac(char *buff, unsigned char *mac, int len);
176 void bump_maxfd(int fd, int *max);
177 int read_write(int fd, unsigned char *packet, int size, int rw);
178+int read_timeout(int fd, unsigned char *packet, int size, time_t now, int secs);
179 
180 /* log.c */
181 void die(char *message, char *arg1, int exit_code);
182@@ -748,7 +751,7 @@
183 
184 /* dhcp.c */
185 void dhcp_init(void);
186-void dhcp_packet(time_t now);
187+void dhcp_packet(int piperead, time_t now);
188 
189 struct dhcp_context *address_available(struct dhcp_context *context, 
190 				       struct in_addr addr,
191@@ -792,14 +795,16 @@
192 void rerun_scripts(void);
193 
194 /* rfc2131.c */
195-size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
196+size_t dhcp_reply(int pipefd, struct dhcp_context *context, char *iface_name, int int_index,
197 		  size_t sz, time_t now, int unicast_dest, int *is_inform);
198 
199 /* dnsmasq.c */
200 int make_icmp_sock(void);
201 int icmp_ping(struct in_addr addr);
202-void send_event(int fd, int event, int data);
203+void send_event(int fd, int event, int data, int priv);
204 void clear_cache_and_reload(time_t now);
205+int wait_for_child(int pipe);
206+int async_event(int pipe, time_t now, struct event_desc*, unsigned int timeout);
207 
208 /* isc.c */
209 #ifdef HAVE_ISC_READER
210@@ -832,9 +837,9 @@
211 /* helper.c */
212 #ifndef NO_FORK
213 int create_helper(int event_fd, int err_fd, uid_t uid, gid_t gid, long max_fd);
214-void helper_write(void);
215+int helper_write(void);
216 void queue_script(int action, struct dhcp_lease *lease, 
217-		  char *hostname, time_t now);
218+		  char *hostname, time_t now, unsigned int uid);
219 int helper_buf_empty(void);
220 #endif
221 
222Index: src/util.c
223===================================================================
224--- src/util.c	(revision 696)
225+++ src/util.c	(revision 821)
226@@ -444,3 +444,38 @@
227   return 1;
228 }
229 
230+int read_timeout(int fd, unsigned char *packet, int size, time_t now, int secs)
231+{
232+  ssize_t n, done;
233+  time_t expire;
234+  
235+  expire = now + secs;
236+  
237+  for (done = 0; done < size; done += n)
238+    {
239+    retry:
240+      if (secs > 0) alarm(secs);
241+      n = read(fd, &packet[done], (size_t)(size - done));
242+
243+      if (n == 0)
244+        return 0;
245+      else if (n == -1)
246+        {
247+          if (errno == EINTR) {
248+            my_syslog(LOG_INFO, _("read timed out (errno %d)"), errno);
249+            return 0;
250+          }
251+
252+          if (retry_send() || errno == ENOMEM || errno == ENOBUFS || errno == EAGAIN)
253+            {
254+              if (secs == 0 || (secs > 0 && dnsmasq_time() < expire))
255+                goto retry;
256+            }
257+
258+          my_syslog(LOG_INFO, _("error in read (timeout %d, errno %d)"), secs, errno);
259+          return 0;
260+        }
261+    }
262+  return 1;
263+}
264+
265Index: src/dhcp.c
266===================================================================
267--- src/dhcp.c	(revision 696)
268+++ src/dhcp.c	(revision 821)
269@@ -103,7 +103,7 @@
270   daemon->dhcp_packet.iov_base = safe_malloc(daemon->dhcp_packet.iov_len);
271 }
272   
273-void dhcp_packet(time_t now)
274+void dhcp_packet(int piperead, time_t now)
275 {
276   struct dhcp_packet *mess;
277   struct dhcp_context *context;
278@@ -239,7 +239,8 @@
279   if (!iface_enumerate(&parm, complete_context, NULL))
280     return;
281   lease_prune(NULL, now); /* lose any expired leases */
282-  iov.iov_len = dhcp_reply(parm.current, ifr.ifr_name, iface_index, (size_t)sz, 
283+
284+  iov.iov_len = dhcp_reply(piperead, parm.current, ifr.ifr_name, iface_index, (size_t)sz, 
285 			   now, unicast_dest, &is_inform);
286   lease_update_file(now);
287   lease_update_dns();
288Index: src/helper.c
289===================================================================
290--- src/helper.c	(revision 696)
291+++ src/helper.c	(revision 821)
292@@ -45,6 +45,7 @@
293 #endif
294   unsigned char hwaddr[DHCP_CHADDR_MAX];
295   char interface[IF_NAMESIZE];
296+  unsigned int uid;
297 };
298 
299 static struct script_data *buf = NULL;
300@@ -60,7 +61,7 @@
301      then fork our process. */
302   if (pipe(pipefd) == -1 || !fix_fd(pipefd[1]) || (pid = fork()) == -1)
303     {
304-      send_event(err_fd, EVENT_PIPE_ERR, errno);
305+      send_event(err_fd, EVENT_PIPE_ERR, errno, 0);
306       _exit(0);
307     }
308 
309@@ -87,13 +88,13 @@
310 	{
311 	  if (daemon->options & OPT_NO_FORK)
312 	    /* send error to daemon process if no-fork */
313-	    send_event(event_fd, EVENT_HUSER_ERR, errno);
314+	    send_event(event_fd, EVENT_HUSER_ERR, errno, 0);
315 	  else
316 	    {
317 	      /* kill daemon */
318-	      send_event(event_fd, EVENT_DIE, 0);
319+	      send_event(event_fd, EVENT_DIE, 0, 0);
320 	      /* return error */
321-	      send_event(err_fd, EVENT_HUSER_ERR, errno);;
322+	      send_event(err_fd, EVENT_HUSER_ERR, errno, 0);
323 	    }
324 	  _exit(0);
325 	}
326@@ -122,6 +123,8 @@
327 	action_str = "del";
328       else if (data.action == ACTION_ADD)
329 	action_str = "add";
330+      else if (data.action == ACTION_ACCESS)
331+	action_str = "access";
332       else if (data.action == ACTION_OLD || data.action == ACTION_OLD_HOSTNAME)
333 	action_str = "old";
334       else
335@@ -178,9 +181,11 @@
336 		{
337 		  /* On error send event back to main process for logging */
338 		  if (WIFSIGNALED(status))
339-		    send_event(event_fd, EVENT_KILLED, WTERMSIG(status));
340-		  else if (WIFEXITED(status) && WEXITSTATUS(status) != 0)
341-		    send_event(event_fd, EVENT_EXITED, WEXITSTATUS(status));
342+		    send_event(event_fd, EVENT_KILLED, WTERMSIG(status), data.uid);
343+		  else if (WIFEXITED(status))
344+		    send_event(event_fd, EVENT_EXITED, WEXITSTATUS(status), data.uid);
345+                  else
346+		    send_event(event_fd, EVENT_EXITED, -1, data.uid);
347 		  break;
348 		}
349 	      
350@@ -263,7 +268,7 @@
351 	  err = errno;
352 	}
353       /* failed, send event so the main process logs the problem */
354-      send_event(event_fd, EVENT_EXEC_ERR, err);
355+      send_event(event_fd, EVENT_EXEC_ERR, err, data.uid);
356       _exit(0); 
357     }
358 }
359@@ -295,7 +300,7 @@
360 }
361  
362 /* pack up lease data into a buffer */    
363-void queue_script(int action, struct dhcp_lease *lease, char *hostname, time_t now)
364+void queue_script(int action, struct dhcp_lease *lease, char *hostname, time_t now, unsigned int uid)
365 {
366   unsigned char *p;
367   size_t size;
368@@ -332,6 +337,7 @@
369       buf_size = size;
370     }
371 
372+  buf->uid = uid;
373   buf->action = action;
374   buf->hwaddr_len = lease->hwaddr_len;
375   buf->hwaddr_type = lease->hwaddr_type;
376@@ -393,12 +399,15 @@
377   return bytes_in_buf == 0;
378 }
379 
380-void helper_write(void)
381+/* returns -1 if write failed for a reason, 1 if no data exist
382+ * and 0 if everything was ok.
383+ */
384+int helper_write(void)
385 {
386   ssize_t rc;
387 
388   if (bytes_in_buf == 0)
389-    return;
390+    return 1;
391   
392   if ((rc = write(daemon->helperfd, buf, bytes_in_buf)) != -1)
393     {
394@@ -409,9 +418,11 @@
395   else
396     {
397       if (errno == EAGAIN || errno == EINTR)
398-	return;
399+	return -1;
400       bytes_in_buf = 0;
401     }
402+    
403+  return 0;
404 }
405 
406 #endif
407Index: src/rfc2131.c
408===================================================================
409--- src/rfc2131.c	(revision 696)
410+++ src/rfc2131.c	(revision 821)
411@@ -100,8 +100,49 @@
412 				      int clid_len, unsigned char *clid, int *len_out);
413 static void match_vendor_opts(unsigned char *opt, struct dhcp_opt *dopt); 
414 
415+static int check_access_script( int piperead, struct dhcp_lease *lease, struct dhcp_packet *mess, time_t now)
416+{
417+#ifndef NO_FORK
418+unsigned int uid;
419+struct event_desc ev;
420+int ret;
421+struct dhcp_lease _lease;
422+
423+  if (daemon->lease_change_command == NULL) return 0; /* ok */
424+
425+  if (!lease) { /* if host has not been seen before lease is NULL */
426+      memset(&_lease, 0, sizeof(_lease));
427+      lease = &_lease;
428+      lease_set_hwaddr(lease, mess->chaddr, NULL, mess->hlen, mess->htype, 0);
429+  }
430+
431+  uid = rand16();
432+  queue_script(ACTION_ACCESS, lease, NULL, now, uid);
433+
434+  /* send all data to helper process */
435+  do 
436+    {
437+      helper_write();
438+    } while (helper_buf_empty() == 0);
439+
440+  /* wait for our event */
441+  ret = 0;
442+  do 
443+    {
444+      ret = async_event( piperead, now, &ev, SCRIPT_TIMEOUT);
445+    }
446+  while(ev.priv != uid && ret >= 0);
447+
448+  if (ret < 0 || ev.data != 0) /* timeout or error */
449+    {
450+      return -1;
451+    }
452+
453+#endif
454+  return 0; /* ok */
455+}
456 	  
457-size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
458+size_t dhcp_reply(int piperead, struct dhcp_context *context, char *iface_name, int int_index,
459 		  size_t sz, time_t now, int unicast_dest, int *is_inform)
460 {
461   unsigned char *opt, *clid = NULL;
462@@ -252,7 +293,7 @@
463 	mac->netid.next = netid;
464 	netid = &mac->netid;
465       }
466-  
467+
468   /* Determine network for this packet. Our caller will have already linked all the 
469      contexts which match the addresses of the receiving interface but if the 
470      machine has an address already, or came via a relay, or we have a subnet selector, 
471@@ -329,7 +370,7 @@
472 	    my_syslog(LOG_INFO, _("Available DHCP range: %s -- %s"), daemon->namebuff, inet_ntoa(context_tmp->end));
473 	}
474     }
475-
476+    
477   mess->op = BOOTREPLY;
478   
479   config = find_config(daemon->dhcp_conf, context, clid, clid_len, 
480@@ -418,7 +459,7 @@
481 	      else
482 		mess->yiaddr = lease->addr;
483 	    }
484-	  
485+
486 	  if (!message && 
487 	      !lease && 
488 	      (!(lease = lease_allocate(mess->yiaddr))))
489@@ -641,7 +682,14 @@
490       memcpy(req_options, option_ptr(opt, 0), option_len(opt));
491       req_options[option_len(opt)] = OPTION_END;
492     }
493-  
494+
495+  if (mess_type == DHCPREQUEST || mess_type == DHCPDISCOVER)
496+    if (check_access_script(piperead, lease, mess, now) < 0)
497+      {
498+        my_syslog(LOG_INFO, _("Ignoring client due to access script"));
499+        return 0;
500+      }
501+
502   switch (mess_type)
503     {
504     case DHCPDECLINE:
505Index: src/log.c
506===================================================================
507--- src/log.c	(revision 696)
508+++ src/log.c	(revision 821)
509@@ -73,7 +73,7 @@
510 
511   if (!log_reopen(daemon->log_file))
512     {
513-      send_event(errfd, EVENT_LOG_ERR, errno);
514+      send_event(errfd, EVENT_LOG_ERR, errno, 0);
515       _exit(0);
516     }
517 
518Index: src/lease.c
519===================================================================
520--- src/lease.c	(revision 696)
521+++ src/lease.c	(revision 821)
522@@ -511,7 +511,7 @@
523       if (lease->old_hostname)
524 	{
525 #ifndef NO_FORK
526-	  queue_script(ACTION_OLD_HOSTNAME, lease, lease->old_hostname, now);
527+	  queue_script(ACTION_OLD_HOSTNAME, lease, lease->old_hostname, now, 0);
528 #endif
529 	  free(lease->old_hostname);
530 	  lease->old_hostname = NULL;
531@@ -520,7 +520,7 @@
532       else 
533 	{
534 #ifndef NO_FORK
535-	  queue_script(ACTION_DEL, lease, lease->hostname, now);
536+	  queue_script(ACTION_DEL, lease, lease->hostname, now, 0);
537 #endif
538 	  old_leases = lease->next;
539 	  
540@@ -540,7 +540,7 @@
541     if (lease->old_hostname)
542       {	
543 #ifndef NO_FORK
544-	queue_script(ACTION_OLD_HOSTNAME, lease, lease->old_hostname, now);
545+	queue_script(ACTION_OLD_HOSTNAME, lease, lease->old_hostname, now, 0);
546 #endif
547 	free(lease->old_hostname);
548 	lease->old_hostname = NULL;
549@@ -552,7 +552,7 @@
550 	(lease->aux_changed && (daemon->options & OPT_LEASE_RO)))
551       {
552 #ifndef NO_FORK
553-	queue_script(lease->new ? ACTION_ADD : ACTION_OLD, lease, lease->hostname, now);
554+	queue_script(lease->new ? ACTION_ADD : ACTION_OLD, lease, lease->hostname, now, 0);
555 #endif
556 	lease->new = lease->changed = lease->aux_changed = 0;
557 	
558Index: man/dnsmasq.8
559===================================================================
560--- man/dnsmasq.8	(revision 696)
561+++ man/dnsmasq.8	(revision 821)
562@@ -724,12 +724,15 @@
563 .B \-6 --dhcp-script=<path>
564 Whenever a new DHCP lease is created, or an old one destroyed, the
565 binary specified by this option is run. The arguments to the process
566-are "add", "old" or "del", the MAC
567+are "add", "old", "access" or "del", the MAC
568 address of the host (or "<null>"), the IP address, and the hostname,
569 if known. "add" means a lease has been created, "del" means it has
570 been destroyed, "old" is a notification of an existing lease when
571 dnsmasq starts or a change to MAC address or hostname of an existing
572 lease (also, lease length or expiry and client-id, if leasefile-ro is set).
573+The "access" keyword means that a request was just received and depending
574+on the script exit status request for address will be granted, if exit status
575+is zero or not if it is non-zero.
576 The process is run as root (assuming that dnsmasq was originally run as
577 root) even if dnsmasq is configured to change UID to an unprivileged user.
578 The environment is inherited from the invoker of dnsmasq, and if the
579