File:  [ELWIX - Embedded LightWeight unIX -] / embedaddon / dnsmasq / contrib / lease-access / lease.access.patch
Revision 1.1.1.1 (vendor branch): download - view: text, annotated - select for diffs - revision graph
Mon Jul 29 19:37:40 2013 UTC (10 years, 11 months ago) by misho
Branches: elwix, dnsmasq, MAIN
CVS tags: v2_76p1, v2_71, v2_66p0, v2_66, HEAD
dnsmasq

    1: Index: 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()
  141: Index: 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"
  154: Index: 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:  
  222: Index: 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: +
  265: Index: 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();
  288: Index: 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
  407: Index: 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:
  505: Index: 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:  
  518: Index: 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:  	
  558: Index: 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

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>