Diff for /embedaddon/dnsmasq/src/rfc2131.c between versions 1.1.1.1 and 1.1.1.4

version 1.1.1.1, 2013/07/29 19:37:40 version 1.1.1.4, 2021/03/17 00:56:46
Line 1 Line 1
/* dnsmasq is Copyright (c) 2000-2013 Simon Kelley/* dnsmasq is Copyright (c) 2000-2021 Simon Kelley
   
    This program is free software; you can redistribute it and/or modify     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     it under the terms of the GNU General Public License as published by
Line 30  static struct in_addr server_id(struct dhcp_context *c Line 30  static struct in_addr server_id(struct dhcp_context *c
 static unsigned int calc_time(struct dhcp_context *context, struct dhcp_config *config, unsigned char *opt);  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(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,   static void option_put_string(struct dhcp_packet *mess, unsigned char *end, 
                              int opt, char *string, int null_term);                              int opt, const char *string, int null_term);
 static struct in_addr option_addr(unsigned char *opt);  static struct in_addr option_addr(unsigned char *opt);
static unsigned int option_uint(unsigned char *opt, int i, int size);static unsigned int option_uint(unsigned char *opt, int offset, int size);
 static void log_packet(char *type, void *addr, unsigned char *ext_mac,   static void log_packet(char *type, void *addr, unsigned char *ext_mac, 
                       int mac_len, char *interface, char *string, u32 xid);                       int mac_len, char *interface, char *string, char *err, u32 xid);
 static unsigned char *option_find(struct dhcp_packet *mess, size_t size, int opt_type, int minsize);  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 unsigned char *option_find1(unsigned char *p, unsigned char *end, int opt, int minsize);
 static size_t dhcp_packet_size(struct dhcp_packet *mess, unsigned char *agent_id, unsigned char *real_end);  static size_t dhcp_packet_size(struct dhcp_packet *mess, unsigned char *agent_id, unsigned char *real_end);
 static void clear_packet(struct dhcp_packet *mess, unsigned char *end);  static void clear_packet(struct dhcp_packet *mess, unsigned char *end);
   static int in_list(unsigned char *list, int opt);
 static void do_options(struct dhcp_context *context,  static void do_options(struct dhcp_context *context,
                        struct dhcp_packet *mess,                         struct dhcp_packet *mess,
                       unsigned char *real_end,                        unsigned char *end,
                        unsigned char *req_options,                         unsigned char *req_options,
                        char *hostname,                          char *hostname, 
                       char *config_domain,                       char *domain,
                        struct dhcp_netid *netid,                         struct dhcp_netid *netid,
                        struct in_addr subnet_addr,                          struct in_addr subnet_addr, 
                        unsigned char fqdn_flags,                         unsigned char fqdn_flags,
                       int null_term, int pxearch,                       int null_term, int pxe_arch,
                        unsigned char *uuid,                         unsigned char *uuid,
                        int vendor_class_len,                         int vendor_class_len,
                       time_t now);                       time_t now,
                        unsigned int lease_time,
                        unsigned short fuzz,
                        const char *pxevendor);
   
   
 static void match_vendor_opts(unsigned char *opt, struct dhcp_opt *dopt);   static void match_vendor_opts(unsigned char *opt, struct dhcp_opt *dopt); 
static int do_encap_opts(struct dhcp_opt *opts, int encap, int flag, struct dhcp_packet *mess, unsigned char *end, int null_term);static int do_encap_opts(struct dhcp_opt *opt, 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 void pxe_misc(struct dhcp_packet *mess, unsigned char *end, unsigned char *uuid, const char *pxevendor);
 static int prune_vendor_opts(struct dhcp_netid *netid);  static int prune_vendor_opts(struct dhcp_netid *netid);
 static struct dhcp_opt *pxe_opts(int pxe_arch, struct dhcp_netid *netid, struct in_addr local, time_t now);  static struct dhcp_opt *pxe_opts(int pxe_arch, struct dhcp_netid *netid, struct in_addr local, time_t now);
 struct dhcp_boot *find_boot(struct dhcp_netid *netid);  struct dhcp_boot *find_boot(struct dhcp_netid *netid);
   static int pxe_uefi_workaround(int pxe_arch, struct dhcp_netid *netid, struct dhcp_packet *mess, struct in_addr local, time_t now, int pxe);
   static void apply_delay(u32 xid, time_t recvtime, struct dhcp_netid *netid);
   static int is_pxe_client(struct dhcp_packet *mess, size_t sz, const char **pxe_vendor);
   
     
 size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,  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, int pxe, struct in_addr fallback)                  size_t sz, time_t now, int unicast_dest, int loopback,
                   int *is_inform, int pxe, struct in_addr fallback, time_t recvtime)
 {  {
   unsigned char *opt, *clid = NULL;    unsigned char *opt, *clid = NULL;
   struct dhcp_lease *ltmp, *lease = NULL;    struct dhcp_lease *ltmp, *lease = NULL;
   struct dhcp_vendor *vendor;    struct dhcp_vendor *vendor;
   struct dhcp_mac *mac;    struct dhcp_mac *mac;
   struct dhcp_netid_list *id_list;    struct dhcp_netid_list *id_list;
  int clid_len = 0, ignore = 0, do_classes = 0, selecting = 0, pxearch = -1;  int clid_len = 0, ignore = 0, do_classes = 0, rapid_commit = 0, selecting = 0, pxearch = -1;
   const char *pxevendor = NULL;
   struct dhcp_packet *mess = (struct dhcp_packet *)daemon->dhcp_packet.iov_base;    struct dhcp_packet *mess = (struct dhcp_packet *)daemon->dhcp_packet.iov_base;
   unsigned char *end = (unsigned char *)(mess + 1);     unsigned char *end = (unsigned char *)(mess + 1); 
   unsigned char *real_end = (unsigned char *)(mess + 1);     unsigned char *real_end = (unsigned char *)(mess + 1); 
Line 91  size_t dhcp_reply(struct dhcp_context *context, char * Line 99  size_t dhcp_reply(struct dhcp_context *context, char *
   struct dhcp_netid known_id, iface_id, cpewan_id;    struct dhcp_netid known_id, iface_id, cpewan_id;
   struct dhcp_opt *o;    struct dhcp_opt *o;
   unsigned char pxe_uuid[17];    unsigned char pxe_uuid[17];
  unsigned char *oui = NULL, *serial = NULL, *class = NULL;  unsigned char *oui = NULL, *serial = NULL;
 #ifdef HAVE_SCRIPT
   unsigned char *class = NULL;
 #endif
   
   subnet_addr.s_addr = override.s_addr = 0;    subnet_addr.s_addr = override.s_addr = 0;
   
Line 149  size_t dhcp_reply(struct dhcp_context *context, char * Line 160  size_t dhcp_reply(struct dhcp_context *context, char *
           for (offset = 0; offset < (len - 5); offset += elen + 5)            for (offset = 0; offset < (len - 5); offset += elen + 5)
             {              {
               elen = option_uint(opt, offset + 4 , 1);                elen = option_uint(opt, offset + 4 , 1);
              if (option_uint(opt, offset, 4) == BRDBAND_FORUM_IANA)              if (option_uint(opt, offset, 4) == BRDBAND_FORUM_IANA && offset + elen + 5 <= len)
                 {                  {
                   unsigned char *x = option_ptr(opt, offset + 5);                    unsigned char *x = option_ptr(opt, offset + 5);
                   unsigned char *y = option_ptr(opt, offset + elen + 5);                    unsigned char *y = option_ptr(opt, offset + elen + 5);
                   oui = option_find1(x, y, 1, 1);                    oui = option_find1(x, y, 1, 1);
                   serial = option_find1(x, y, 2, 1);                    serial = option_find1(x, y, 2, 1);
                  class = option_find1(x, y, 3, 1);#ifdef HAVE_SCRIPT
                                    class = option_find1(x, y, 3, 1);               
 #endif
                   /* If TR069-id is present set the tag "cpewan-id" to facilitate echoing                     /* If TR069-id is present set the tag "cpewan-id" to facilitate echoing 
                      the gateway id back. Note that the device class is optional */                       the gateway id back. Note that the device class is optional */
                   if (oui && serial)                    if (oui && serial)
Line 179  size_t dhcp_reply(struct dhcp_context *context, char * Line 191  size_t dhcp_reply(struct dhcp_context *context, char *
              be enough free space at the end of the packet to copy the option. */               be enough free space at the end of the packet to copy the option. */
           unsigned char *sopt;            unsigned char *sopt;
           unsigned int total = option_len(opt) + 2;            unsigned int total = option_len(opt) + 2;
          unsigned char *last_opt = option_find(mess, sz, OPTION_END, 0);          unsigned char *last_opt = option_find1(&mess->options[0] + sizeof(u32), ((unsigned char *)mess) + sz,
                                                  OPTION_END, 0);
           if (last_opt && last_opt < end - total)            if (last_opt && last_opt < end - total)
             {              {
               end -= total;                end -= total;
Line 224  size_t dhcp_reply(struct dhcp_context *context, char * Line 237  size_t dhcp_reply(struct dhcp_context *context, char *
         subnet_addr = option_addr(opt);          subnet_addr = option_addr(opt);
               
       /* If there is no client identifier option, use the hardware address */        /* If there is no client identifier option, use the hardware address */
      if ((opt = option_find(mess, sz, OPTION_CLIENT_ID, 1)))      if (!option_bool(OPT_IGNORE_CLID) && (opt = option_find(mess, sz, OPTION_CLIENT_ID, 1)))
         {          {
           clid_len = option_len(opt);            clid_len = option_len(opt);
           clid = option_ptr(opt, 0);            clid = option_ptr(opt, 0);
Line 264  size_t dhcp_reply(struct dhcp_context *context, char * Line 277  size_t dhcp_reply(struct dhcp_context *context, char *
   if (mess->giaddr.s_addr || subnet_addr.s_addr || mess->ciaddr.s_addr)    if (mess->giaddr.s_addr || subnet_addr.s_addr || mess->ciaddr.s_addr)
     {      {
       struct dhcp_context *context_tmp, *context_new = NULL;        struct dhcp_context *context_tmp, *context_new = NULL;
         struct shared_network *share = NULL;
       struct in_addr addr;        struct in_addr addr;
      int force = 0;      int force = 0, via_relay = 0;
               
       if (subnet_addr.s_addr)        if (subnet_addr.s_addr)
         {          {
Line 276  size_t dhcp_reply(struct dhcp_context *context, char * Line 290  size_t dhcp_reply(struct dhcp_context *context, char *
         {          {
           addr = mess->giaddr;            addr = mess->giaddr;
           force = 1;            force = 1;
             via_relay = 1;
         }          }
       else        else
         {          {
Line 292  size_t dhcp_reply(struct dhcp_context *context, char * Line 307  size_t dhcp_reply(struct dhcp_context *context, char *
         }           } 
                                   
       if (!context_new)        if (!context_new)
        for (context_tmp = daemon->dhcp; context_tmp; context_tmp = context_tmp->next)        {
          {          for (context_tmp = daemon->dhcp; context_tmp; context_tmp = context_tmp->next)
            struct in_addr netmask = context_tmp->netmask;            {
               struct in_addr netmask = context_tmp->netmask;
               
               /* guess the netmask for relayed networks */
               if (!(context_tmp->flags & CONTEXT_NETMASK) && context_tmp->netmask.s_addr == 0)
                 {
                   if (IN_CLASSA(ntohl(context_tmp->start.s_addr)) && IN_CLASSA(ntohl(context_tmp->end.s_addr)))
                     netmask.s_addr = htonl(0xff000000);
                   else if (IN_CLASSB(ntohl(context_tmp->start.s_addr)) && IN_CLASSB(ntohl(context_tmp->end.s_addr)))
                     netmask.s_addr = htonl(0xffff0000);
                   else if (IN_CLASSC(ntohl(context_tmp->start.s_addr)) && IN_CLASSC(ntohl(context_tmp->end.s_addr)))
                     netmask.s_addr = htonl(0xffffff00); 
                 }
   
            /* guess the netmask for relayed networks */              /* check to see is a context is OK because of a shared address on
            if (!(context_tmp->flags & CONTEXT_NETMASK) && context_tmp->netmask.s_addr == 0)                 the relayed subnet. */
              {              if (via_relay)
                if (IN_CLASSA(ntohl(context_tmp->start.s_addr)) && IN_CLASSA(ntohl(context_tmp->end.s_addr)))                for (share = daemon->shared_networks; share; share = share->next)
                  netmask.s_addr = htonl(0xff000000);                  {
                else if (IN_CLASSB(ntohl(context_tmp->start.s_addr)) && IN_CLASSB(ntohl(context_tmp->end.s_addr)))#ifdef HAVE_DHCP6
                  netmask.s_addr = htonl(0xffff0000);                    if (share->shared_addr.s_addr == 0)
                else if (IN_CLASSC(ntohl(context_tmp->start.s_addr)) && IN_CLASSC(ntohl(context_tmp->end.s_addr)))                      continue;
                  netmask.s_addr = htonl(0xffffff00)#endif
              }                    if (share->if_index != 0 ||
                                    share->match_addr.s_addr != mess->giaddr.s_addr)
            /* This section fills in context mainly when a client which is on a remote (relayed)                      continue;
               network renews a lease without using the relay, after dnsmasq has restarted. */                    
            if (netmask.s_addr != 0  &&                     if (netmask.s_addr != 0  && 
                is_same_net(addr, context_tmp->start, netmask) &&                        is_same_net(share->shared_addr, context_tmp->start, netmask) &&
                is_same_net(addr, context_tmp->end, netmask))                        is_same_net(share->shared_addr, context_tmp->end, netmask))
              {                      break;
                context_tmp->netmask = netmask;                  }
                if (context_tmp->local.s_addr == 0)              
                  context_tmp->local = fallback;              /* This section fills in context mainly when a client which is on a remote (relayed)
                if (context_tmp->router.s_addr == 0)                 network renews a lease without using the relay, after dnsmasq has restarted. */
                  context_tmp->router = mess->giaddr;              if (share ||
                             (netmask.s_addr != 0  && 
                /* fill in missing broadcast addresses for relayed ranges */                   is_same_net(addr, context_tmp->start, netmask) &&
                if (!(context_tmp->flags & CONTEXT_BRDCAST) && context_tmp->broadcast.s_addr == 0 )                   is_same_net(addr, context_tmp->end, netmask)))
                  context_tmp->broadcast.s_addr = context_tmp->start.s_addr | ~context_tmp->netmask.s_addr;                {
                                  context_tmp->netmask = netmask;
                context_tmp->current = context_new;                  if (context_tmp->local.s_addr == 0)
                context_new = context_tmp;                    context_tmp->local = fallback;
              }                  if (context_tmp->router.s_addr == 0 && !share)
          }                    context_tmp->router = mess->giaddr;
                        
                   /* fill in missing broadcast addresses for relayed ranges */
                   if (!(context_tmp->flags & CONTEXT_BRDCAST) && context_tmp->broadcast.s_addr == 0 )
                     context_tmp->broadcast.s_addr = context_tmp->start.s_addr | ~context_tmp->netmask.s_addr;
                   
                   context_tmp->current = context_new;
                   context_new = context_tmp;
                 }
               
             }
         }
           
       if (context_new || force)        if (context_new || force)
         context = context_new;           context = context_new; 
     }      }
Line 354  size_t dhcp_reply(struct dhcp_context *context, char * Line 392  size_t dhcp_reply(struct dhcp_context *context, char *
                       ntohl(mess->xid), daemon->namebuff, inet_ntoa(context_tmp->end));                        ntohl(mess->xid), daemon->namebuff, inet_ntoa(context_tmp->end));
         }          }
     }      }
     
     /* 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, existence of the option is enough. This code handles 
        rfc3925 V-I classes too. */
     for (o = daemon->dhcp_match; o; o = o->next)
       {
         unsigned int len, elen, match = 0;
         size_t offset, o2;
   
         if (o->flags & DHOPT_RFC3925)
           {
             if (!(opt = option_find(mess, sz, OPTION_VENDOR_IDENT, 5)))
               continue;
             
             for (offset = 0; offset < (option_len(opt) - 5u); offset += len + 5)
               {
                 len = option_uint(opt, offset + 4 , 1);
                 /* Need to take care that bad data can't run us off the end of the packet */
                 if ((offset + len + 5 <= (unsigned)(option_len(opt))) &&
                     (option_uint(opt, offset, 4) == (unsigned int)o->u.encap))
                   for (o2 = offset + 5; o2 < offset + len + 5; o2 += elen + 1)
                     { 
                       elen = option_uint(opt, o2, 1);
                       if ((o2 + elen + 1 <= (unsigned)option_len(opt)) &&
                           (match = match_bytes(o, option_ptr(opt, o2 + 1), elen)))
                         break;
                     }
                 if (match) 
                   break;
               }     
           }
         else
           {
             if (!(opt = option_find(mess, sz, o->opt, 1)))
               continue;
             
             match = match_bytes(o, option_ptr(opt, 0), option_len(opt));
           } 
   
         if (match)
           {
             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,
        save client-supplied vendor class */
     if ((opt = option_find(mess, sz, OPTION_VENDOR_ID, 1)))
       {
         memcpy(daemon->dhcp_buff3, option_ptr(opt, 0), option_len(opt));
         vendor_class_len = option_len(opt);
       }
     match_vendor_opts(opt, daemon->dhcp_opts);
     
     if (option_bool(OPT_LOG_OPTS))
       {
         if (sanitise(opt, 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);
       }
   
   mess->op = BOOTREPLY;    mess->op = BOOTREPLY;
       
   config = find_config(daemon->dhcp_conf, context, clid, clid_len,     config = find_config(daemon->dhcp_conf, context, clid, clid_len, 
                       mess->chaddr, mess->hlen, mess->htype, NULL);                       mess->chaddr, mess->hlen, mess->htype, NULL, run_tag_if(netid));
   
   /* set "known" tag for known hosts */    /* set "known" tag for known hosts */
   if (config)    if (config)
Line 367  size_t dhcp_reply(struct dhcp_context *context, char * Line 516  size_t dhcp_reply(struct dhcp_context *context, char *
       known_id.next = netid;        known_id.next = netid;
       netid = &known_id;        netid = &known_id;
     }      }
     else if (find_config(daemon->dhcp_conf, NULL, clid, clid_len, 
                          mess->chaddr, mess->hlen, mess->htype, NULL, run_tag_if(netid)))
       {
         known_id.net = "known-othernet";
         known_id.next = netid;
         netid = &known_id;
       }
       
   if (mess_type == 0 && !pxe)    if (mess_type == 0 && !pxe)
     {      {
Line 448  size_t dhcp_reply(struct dhcp_context *context, char * Line 604  size_t dhcp_reply(struct dhcp_context *context, char *
                        lease_prune(lease, now);                         lease_prune(lease, now);
                        lease = NULL;                         lease = NULL;
                      }                       }
                   if (!address_allocate(context, &mess->yiaddr, mess->chaddr, mess->hlen, tagif_netid, now))                   if (!address_allocate(context, &mess->yiaddr, mess->chaddr, mess->hlen, tagif_netid, now, loopback))
                      message = _("no address available");                       message = _("no address available");
                 }                  }
               else                else
Line 493  size_t dhcp_reply(struct dhcp_context *context, char * Line 649  size_t dhcp_reply(struct dhcp_context *context, char *
               lease_set_interface(lease, int_index, now);                lease_set_interface(lease, int_index, now);
                               
               clear_packet(mess, end);                clear_packet(mess, end);
               match_vendor_opts(NULL, daemon->dhcp_opts); /* clear flags */  
               do_options(context, mess, end, NULL, hostname, get_domain(mess->yiaddr),                 do_options(context, mess, end, NULL, hostname, get_domain(mess->yiaddr), 
                         netid, subnet_addr, 0, 0, -1, NULL, 0, now);                         netid, subnet_addr, 0, 0, -1, NULL, vendor_class_len, now, 0xffffffff, 0, NULL);
             }              }
         }          }
               
      log_packet("BOOTP", logaddr, mess->chaddr, mess->hlen, iface_name, message, mess->xid);      daemon->metrics[METRIC_BOOTP]++;
       log_packet("BOOTP", logaddr, mess->chaddr, mess->hlen, iface_name, NULL, message, mess->xid);
               
       return message ? 0 : dhcp_packet_size(mess, agent_id, real_end);        return message ? 0 : dhcp_packet_size(mess, agent_id, real_end);
     }      }
               
  if ((opt = option_find(mess, sz, OPTION_CLIENT_FQDN, 4)))  if ((opt = option_find(mess, sz, OPTION_CLIENT_FQDN, 3)))
     {      {
       /* http://tools.ietf.org/wg/dhc/draft-ietf-dhc-fqdn-option/draft-ietf-dhc-fqdn-option-10.txt */        /* http://tools.ietf.org/wg/dhc/draft-ietf-dhc-fqdn-option/draft-ietf-dhc-fqdn-option-10.txt */
       int len = option_len(opt);        int len = option_len(opt);
Line 534  size_t dhcp_reply(struct dhcp_context *context, char * Line 690  size_t dhcp_reply(struct dhcp_context *context, char *
         }          }
               
       if (fqdn_flags & 0x04)        if (fqdn_flags & 0x04)
        while (*op != 0 && ((op + (*op) + 1) - pp) < len)        while (*op != 0 && ((op + (*op)) - pp) < len)
           {            {
             memcpy(pq, op+1, *op);              memcpy(pq, op+1, *op);
             pq += *op;              pq += *op;
Line 572  size_t dhcp_reply(struct dhcp_context *context, char * Line 728  size_t dhcp_reply(struct dhcp_context *context, char *
         client_hostname = daemon->dhcp_buff;          client_hostname = daemon->dhcp_buff;
     }      }
   
  if (client_hostname && option_bool(OPT_LOG_OPTS))  if (client_hostname)
    my_syslog(MS_DHCP | LOG_INFO, _("%u client provides name: %s"), ntohl(mess->xid), client_hostname);    {
       struct dhcp_match_name *m;
       size_t nl = strlen(client_hostname);
       
       if (option_bool(OPT_LOG_OPTS))
         my_syslog(MS_DHCP | LOG_INFO, _("%u client provides name: %s"), ntohl(mess->xid), client_hostname);
       for (m = daemon->dhcp_name_match; m; m = m->next)
         {
           size_t ml = strlen(m->name);
           char save = 0;
           
           if (nl < ml)
             continue;
           if (nl > ml)
             {
               save = client_hostname[ml];
               client_hostname[ml] = 0;
             }
           
           if (hostname_isequal(client_hostname, m->name) &&
               (save == 0 || m->wildcard))
             {
               m->netid->next = netid;
               netid = m->netid;
             }
           
           if (save != 0)
             client_hostname[ml] = save;
         }
     }
       
   if (have_config(config, CONFIG_NAME))    if (have_config(config, CONFIG_NAME))
     {      {
Line 591  size_t dhcp_reply(struct dhcp_context *context, char * Line 776  size_t dhcp_reply(struct dhcp_context *context, char *
       if (strlen(client_hostname) != 0)        if (strlen(client_hostname) != 0)
         {          {
           hostname = client_hostname;            hostname = client_hostname;
             
           if (!config)            if (!config)
             {              {
               /* Search again now we have a hostname.                 /* Search again now we have a hostname. 
Line 598  size_t dhcp_reply(struct dhcp_context *context, char * Line 784  size_t dhcp_reply(struct dhcp_context *context, char *
                  to avoid impersonation by name. */                   to avoid impersonation by name. */
               struct dhcp_config *new = find_config(daemon->dhcp_conf, context, NULL, 0,                struct dhcp_config *new = find_config(daemon->dhcp_conf, context, NULL, 0,
                                                     mess->chaddr, mess->hlen,                                                       mess->chaddr, mess->hlen, 
                                                    mess->htype, hostname);                                                    mess->htype, hostname, run_tag_if(netid));
               if (new && !have_config(new, CONFIG_CLID) && !new->hwaddr)                if (new && !have_config(new, CONFIG_CLID) && !new->hwaddr)
                 {                  {
                   config = new;                    config = new;
Line 622  size_t dhcp_reply(struct dhcp_context *context, char * Line 808  size_t dhcp_reply(struct dhcp_context *context, char *
         }          }
     }      }
       
   /* 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. This code handles   
      rfc3925 V-I classes too. */  
   for (o = daemon->dhcp_match; o; o = o->next)  
     {  
       unsigned int len, elen, match = 0;  
       size_t offset, o2;  
   
       if (o->flags & DHOPT_RFC3925)  
         {  
           if (!(opt = option_find(mess, sz, OPTION_VENDOR_IDENT, 5)))  
             continue;  
             
           for (offset = 0; offset < (option_len(opt) - 5u); offset += len + 5)  
             {  
               len = option_uint(opt, offset + 4 , 1);  
               /* Need to take care that bad data can't run us off the end of the packet */  
               if ((offset + len + 5 <= (option_len(opt))) &&  
                   (option_uint(opt, offset, 4) == (unsigned int)o->u.encap))  
                 for (o2 = offset + 5; o2 < offset + len + 5; o2 += elen + 1)  
                   {   
                     elen = option_uint(opt, o2, 1);  
                     if ((o2 + elen + 1 <= option_len(opt)) &&  
                         (match = match_bytes(o, option_ptr(opt, o2 + 1), elen)))  
                       break;  
                   }  
               if (match)   
                 break;  
             }       
         }  
       else  
         {  
           if (!(opt = option_find(mess, sz, o->opt, 1)))  
             continue;  
             
           match = match_bytes(o, option_ptr(opt, 0), option_len(opt));  
         }   
   
       if (match)  
         {  
           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,  
      save client-supplied vendor class */  
   if ((opt = option_find(mess, sz, OPTION_VENDOR_ID, 1)))  
     {  
       memcpy(daemon->dhcp_buff3, option_ptr(opt, 0), option_len(opt));  
       vendor_class_len = option_len(opt);  
     }  
   match_vendor_opts(opt, daemon->dhcp_opts);  
     
   if (option_bool(OPT_LOG_OPTS))  
     {  
       if (sanitise(opt, 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);  
     }  
   
   tagif_netid = run_tag_if(netid);    tagif_netid = run_tag_if(netid);
  
   /* if all the netids in the ignore list are present, ignore this client */    /* 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)    for (id_list = daemon->dhcp_ignore; id_list; id_list = id_list->next)
     if (match_netid(id_list->list, tagif_netid, 0))      if (match_netid(id_list->list, tagif_netid, 0))
Line 763  size_t dhcp_reply(struct dhcp_context *context, char * Line 838  size_t dhcp_reply(struct dhcp_context *context, char *
     clid = NULL;      clid = NULL;
                       
   /* Check if client is PXE client. */    /* Check if client is PXE client. */
  if (daemon->enable_pxe &&   if (daemon->enable_pxe &&
      (opt = option_find(mess, sz, OPTION_VENDOR_ID, 9)) &&       is_pxe_client(mess, sz, &pxevendor))
      strncmp(option_ptr(opt, 0), "PXEClient", 9) == 0) 
     {      {
       if ((opt = option_find(mess, sz, OPTION_PXE_UUID, 17)))        if ((opt = option_find(mess, sz, OPTION_PXE_UUID, 17)))
         {          {
Line 799  size_t dhcp_reply(struct dhcp_context *context, char * Line 873  size_t dhcp_reply(struct dhcp_context *context, char *
             if (service->type == type)              if (service->type == type)
               break;                break;
                       
          if (!service || !service->basename)          for (; context; context = context->current)
            return 0;            if (match_netid(context->filter, tagif_netid, 1) &&
                 is_same_net(mess->ciaddr, context->start, context->netmask))
               break;
                       
             if (!service || !service->basename || !context)
               return 0;
                     
           clear_packet(mess, end);            clear_packet(mess, end);
                       
           mess->yiaddr = mess->ciaddr;            mess->yiaddr = mess->ciaddr;
Line 813  size_t dhcp_reply(struct dhcp_context *context, char * Line 892  size_t dhcp_reply(struct dhcp_context *context, char *
           else            else
             mess->siaddr = context->local;               mess->siaddr = context->local; 
                       
          snprintf((char *)mess->file, sizeof(mess->file), "%s.%d", service->basename, layer);          if (strchr(service->basename, '.'))
             snprintf((char *)mess->file, sizeof(mess->file),
                 "%s", service->basename);
           else
             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_MESSAGE_TYPE, 1, DHCPACK);
           option_put(mess, end, OPTION_SERVER_IDENTIFIER, INADDRSZ, htonl(context->local.s_addr));            option_put(mess, end, OPTION_SERVER_IDENTIFIER, INADDRSZ, htonl(context->local.s_addr));
          pxe_misc(mess, end, uuid);          pxe_misc(mess, end, uuid, pxevendor);
                       
           prune_vendor_opts(tagif_netid);            prune_vendor_opts(tagif_netid);
           opt71.val = save71;            opt71.val = save71;
Line 827  size_t dhcp_reply(struct dhcp_context *context, char * Line 912  size_t dhcp_reply(struct dhcp_context *context, char *
           opt71.next = daemon->dhcp_opts;            opt71.next = daemon->dhcp_opts;
           do_encap_opts(&opt71, OPTION_VENDOR_CLASS_OPT, DHOPT_VENDOR_MATCH, mess, end, 0);            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);          log_packet("PXE", &mess->yiaddr, emac, emac_len, iface_name, (char *)mess->file, NULL, mess->xid);
           log_tags(tagif_netid, ntohl(mess->xid));            log_tags(tagif_netid, ntohl(mess->xid));
           return dhcp_packet_size(mess, agent_id, real_end);                  return dhcp_packet_size(mess, agent_id, real_end);      
         }          }
Line 840  size_t dhcp_reply(struct dhcp_context *context, char * Line 925  size_t dhcp_reply(struct dhcp_context *context, char *
           if ((mess_type == DHCPDISCOVER || (pxe && mess_type == DHCPREQUEST)))            if ((mess_type == DHCPDISCOVER || (pxe && mess_type == DHCPREQUEST)))
             {              {
               struct dhcp_context *tmp;                struct dhcp_context *tmp;
                 int workaround = 0;
                               
               for (tmp = context; tmp; tmp = tmp->current)                for (tmp = context; tmp; tmp = tmp->current)
                 if ((tmp->flags & CONTEXT_PROXY) &&                  if ((tmp->flags & CONTEXT_PROXY) &&
Line 848  size_t dhcp_reply(struct dhcp_context *context, char * Line 934  size_t dhcp_reply(struct dhcp_context *context, char *
                               
               if (tmp)                if (tmp)
                 {                  {
                  struct dhcp_boot *boot = find_boot(tagif_netid);                  struct dhcp_boot *boot;
                                  int redirect4011 = 0;
 
                   if (tmp->netid.net)
                     {
                       tmp->netid.next = netid;
                       tagif_netid = run_tag_if(&tmp->netid);
                     }
                   
                   boot = find_boot(tagif_netid);
                   
                   mess->yiaddr.s_addr = 0;                    mess->yiaddr.s_addr = 0;
                   if  (mess_type == DHCPDISCOVER || mess->ciaddr.s_addr == 0)                    if  (mess_type == DHCPDISCOVER || mess->ciaddr.s_addr == 0)
                     {                      {
Line 859  size_t dhcp_reply(struct dhcp_context *context, char * Line 954  size_t dhcp_reply(struct dhcp_context *context, char *
                                       
                   clear_packet(mess, end);                    clear_packet(mess, end);
                                       
                  /* Provide the bootfile here, for gPXE, and in case we have no menu items                  /* Redirect EFI clients to port 4011 */
                     and set discovery_control = 8 */                  if (pxearch >= 6)
                  if (boot) 
                     {                      {
                         redirect4011 = 1;
                         mess->siaddr = tmp->local;
                       }
                     
                     /* Returns true if only one matching service is available. On port 4011, 
                        it also inserts the boot file and server name. */
                     workaround = pxe_uefi_workaround(pxearch, tagif_netid, mess, tmp->local, now, pxe);
                     
                     if (!workaround && boot)
                       {
                         /* Provide the bootfile here, for iPXE, and in case we have no menu items
                            and set discovery_control = 8 */
                       if (boot->next_server.s_addr)                         if (boot->next_server.s_addr) 
                         mess->siaddr = boot->next_server;                          mess->siaddr = boot->next_server;
                       else if (boot->tftp_sname)                         else if (boot->tftp_sname) 
                         mess->siaddr = a_record_from_hosts(boot->tftp_sname, now);                          mess->siaddr = a_record_from_hosts(boot->tftp_sname, now);
                                               
                       if (boot->file)                        if (boot->file)
                        strncpy((char *)mess->file, boot->file, sizeof(mess->file)-1);                        safe_strncpy((char *)mess->file, boot->file, sizeof(mess->file));
                     }                      }
                                       
                   option_put(mess, end, OPTION_MESSAGE_TYPE, 1,                     option_put(mess, end, OPTION_MESSAGE_TYPE, 1, 
                              mess_type == DHCPDISCOVER ? DHCPOFFER : DHCPACK);                               mess_type == DHCPDISCOVER ? DHCPOFFER : DHCPACK);
                  option_put(mess, end, OPTION_SERVER_IDENTIFIER, INADDRSZ, htonl(context->local.s_addr));                  option_put(mess, end, OPTION_SERVER_IDENTIFIER, INADDRSZ, htonl(tmp->local.s_addr));
                  pxe_misc(mess, end, uuid);                  pxe_misc(mess, end, uuid, pxevendor);
                   prune_vendor_opts(tagif_netid);                    prune_vendor_opts(tagif_netid);
                  do_encap_opts(pxe_opts(pxearch, tagif_netid, context->local, now), OPTION_VENDOR_CLASS_OPT, DHOPT_VENDOR_MATCH, mess, end, 0);                  if ((pxe && !workaround) || !redirect4011)
                                      do_encap_opts(pxe_opts(pxearch, tagif_netid, tmp->local, now), OPTION_VENDOR_CLASS_OPT, DHOPT_VENDOR_MATCH, mess, end, 0);
                  log_packet("PXE", NULL, emac, emac_len, iface_name, ignore ? "proxy-ignored" : "proxy", mess->xid);            
                   daemon->metrics[METRIC_PXE]++;
                   log_packet("PXE", NULL, emac, emac_len, iface_name, ignore ? "proxy-ignored" : "proxy", NULL, mess->xid);
                   log_tags(tagif_netid, ntohl(mess->xid));                    log_tags(tagif_netid, ntohl(mess->xid));
                     if (!ignore)
                       apply_delay(mess->xid, recvtime, tagif_netid);
                   return ignore ? 0 : dhcp_packet_size(mess, agent_id, real_end);                             return ignore ? 0 : dhcp_packet_size(mess, agent_id, real_end);         
                 }                  }
             }              }
Line 911  size_t dhcp_reply(struct dhcp_context *context, char * Line 1021  size_t dhcp_reply(struct dhcp_context *context, char *
       if (!(opt = option_find(mess, sz, OPTION_REQUESTED_IP, INADDRSZ)))        if (!(opt = option_find(mess, sz, OPTION_REQUESTED_IP, INADDRSZ)))
         return 0;          return 0;
               
      log_packet("DHCPDECLINE", option_ptr(opt, 0), emac, emac_len, iface_name, daemon->dhcp_buff, mess->xid);      daemon->metrics[METRIC_DHCPDECLINE]++;
       log_packet("DHCPDECLINE", option_ptr(opt, 0), emac, emac_len, iface_name, NULL, daemon->dhcp_buff, mess->xid);
               
       if (lease && lease->addr.s_addr == option_addr(opt).s_addr)        if (lease && lease->addr.s_addr == option_addr(opt).s_addr)
         lease_prune(lease, now);          lease_prune(lease, now);
Line 943  size_t dhcp_reply(struct dhcp_context *context, char * Line 1054  size_t dhcp_reply(struct dhcp_context *context, char *
       else        else
         message = _("unknown lease");          message = _("unknown lease");
   
      log_packet("DHCPRELEASE", &mess->ciaddr, emac, emac_len, iface_name, message, mess->xid);      daemon->metrics[METRIC_DHCPRELEASE]++;
       log_packet("DHCPRELEASE", &mess->ciaddr, emac, emac_len, iface_name, NULL, message, mess->xid);
                   
       return 0;        return 0;
               
     case DHCPDISCOVER:      case DHCPDISCOVER:
       if (ignore || have_config(config, CONFIG_DISABLE))        if (ignore || have_config(config, CONFIG_DISABLE))
         {          {
             if (option_bool(OPT_QUIET_DHCP))
               return 0;
           message = _("ignored");            message = _("ignored");
           opt = NULL;            opt = NULL;
         }          }
Line 999  size_t dhcp_reply(struct dhcp_context *context, char * Line 1113  size_t dhcp_reply(struct dhcp_context *context, char *
                    !config_find_by_address(daemon->dhcp_conf, lease->addr))                     !config_find_by_address(daemon->dhcp_conf, lease->addr))
             mess->yiaddr = lease->addr;              mess->yiaddr = lease->addr;
           else if (opt && address_available(context, addr, tagif_netid) && !lease_find_by_addr(addr) &&             else if (opt && address_available(context, addr, tagif_netid) && !lease_find_by_addr(addr) && 
                   !config_find_by_address(daemon->dhcp_conf, addr))                   !config_find_by_address(daemon->dhcp_conf, addr) && do_icmp_ping(now, addr, 0, loopback))
             mess->yiaddr = addr;              mess->yiaddr = addr;
           else if (emac_len == 0)            else if (emac_len == 0)
             message = _("no unique-id");              message = _("no unique-id");
          else if (!address_allocate(context, &mess->yiaddr, emac, emac_len, tagif_netid, now))          else if (!address_allocate(context, &mess->yiaddr, emac, emac_len, tagif_netid, now, loopback))
             message = _("no address available");                    message = _("no address available");      
         }          }
               
      log_packet("DHCPDISCOVER", opt ? option_ptr(opt, 0) : NULL, emac, emac_len, iface_name, message, mess->xid);       daemon->metrics[METRIC_DHCPDISCOVER]++;
       log_packet("DHCPDISCOVER", opt ? option_ptr(opt, 0) : NULL, emac, emac_len, iface_name, NULL, message, mess->xid); 
   
       if (message || !(context = narrow_context(context, mess->yiaddr, tagif_netid)))        if (message || !(context = narrow_context(context, mess->yiaddr, tagif_netid)))
         return 0;          return 0;
Line 1019  size_t dhcp_reply(struct dhcp_context *context, char * Line 1134  size_t dhcp_reply(struct dhcp_context *context, char *
         }          }
   
       log_tags(tagif_netid, ntohl(mess->xid));        log_tags(tagif_netid, ntohl(mess->xid));
         apply_delay(mess->xid, recvtime, tagif_netid);
   
         if (option_bool(OPT_RAPID_COMMIT) && option_find(mess, sz, OPTION_RAPID_COMMIT, 0))
           {
             rapid_commit = 1;
             goto rapid_commit;
           }
               
      log_packet("DHCPOFFER" , &mess->yiaddr, emac, emac_len, iface_name, NULL, mess->xid);      daemon->metrics[METRIC_DHCPOFFER]++;
       log_packet("DHCPOFFER" , &mess->yiaddr, emac, emac_len, iface_name, NULL, NULL, mess->xid);
               
       time = calc_time(context, config, option_find(mess, sz, OPTION_LEASE_TIME, 4));        time = calc_time(context, config, option_find(mess, sz, OPTION_LEASE_TIME, 4));
       clear_packet(mess, end);        clear_packet(mess, end);
Line 1028  size_t dhcp_reply(struct dhcp_context *context, char * Line 1151  size_t dhcp_reply(struct dhcp_context *context, char *
       option_put(mess, end, OPTION_SERVER_IDENTIFIER, INADDRSZ, ntohl(server_id(context, override, fallback).s_addr));        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);        option_put(mess, end, OPTION_LEASE_TIME, 4, time);
       /* T1 and T2 are required in DHCPOFFER by HP's wacky Jetdirect client. */        /* 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),         do_options(context, mess, end, req_options, offer_hostname, get_domain(mess->yiaddr), 
                 netid, subnet_addr, fqdn_flags, borken_opt, pxearch, uuid, vendor_class_len, now);                 netid, subnet_addr, fqdn_flags, borken_opt, pxearch, uuid, vendor_class_len, now, time, fuzz, pxevendor);
               
       return dhcp_packet_size(mess, agent_id, real_end);        return dhcp_packet_size(mess, agent_id, real_end);
              
 
     case DHCPREQUEST:      case DHCPREQUEST:
       if (ignore || have_config(config, CONFIG_DISABLE))        if (ignore || have_config(config, CONFIG_DISABLE))
         return 0;          return 0;
Line 1072  size_t dhcp_reply(struct dhcp_context *context, char * Line 1191  size_t dhcp_reply(struct dhcp_context *context, char *
                          Have to set override to make sure we echo back the correct server-id */                           Have to set override to make sure we echo back the correct server-id */
                       struct irec *intr;                        struct irec *intr;
                                               
                      enumerate_interfaces();                      enumerate_interfaces(0);
   
                       for (intr = daemon->interfaces; intr; intr = intr->next)                        for (intr = daemon->interfaces; intr; intr = intr->next)
                         if (intr->addr.sa.sa_family == AF_INET &&                          if (intr->addr.sa.sa_family == AF_INET &&
Line 1135  size_t dhcp_reply(struct dhcp_context *context, char * Line 1254  size_t dhcp_reply(struct dhcp_context *context, char *
           fuzz = rand16();            fuzz = rand16();
           mess->yiaddr = mess->ciaddr;            mess->yiaddr = mess->ciaddr;
         }          }
   
         daemon->metrics[METRIC_DHCPREQUEST]++;
         log_packet("DHCPREQUEST", &mess->yiaddr, emac, emac_len, iface_name, NULL, NULL, mess->xid);
               
      log_packet("DHCPREQUEST", &mess->yiaddr, emac, emac_len, iface_name, NULL, mess->xid);    rapid_commit:
  
       if (!message)        if (!message)
         {          {
           struct dhcp_config *addr_config;            struct dhcp_config *addr_config;
Line 1208  size_t dhcp_reply(struct dhcp_context *context, char * Line 1329  size_t dhcp_reply(struct dhcp_context *context, char *
   
       if (message)        if (message)
         {          {
          log_packet("DHCPNAK", &mess->yiaddr, emac, emac_len, iface_name, message, mess->xid);          daemon->metrics[rapid_commit ? METRIC_NOANSWER : METRIC_DHCPNAK]++;
           log_packet(rapid_commit ? "NOANSWER" : "DHCPNAK", &mess->yiaddr, emac, emac_len, iface_name, NULL, message, mess->xid);
 
           /* rapid commit case: lease allocate failed but don't send DHCPNAK */
           if (rapid_commit)
             return 0;
                       
           mess->yiaddr.s_addr = 0;            mess->yiaddr.s_addr = 0;
           clear_packet(mess, end);            clear_packet(mess, end);
Line 1270  size_t dhcp_reply(struct dhcp_context *context, char * Line 1396  size_t dhcp_reply(struct dhcp_context *context, char *
                       add_extradata_opt(lease, NULL);                        add_extradata_opt(lease, NULL);
                     }                      }
   
                     /* DNSMASQ_REQUESTED_OPTIONS */
                     if ((opt = option_find(mess, sz, OPTION_REQUESTED_OPTIONS, 1)))
                       {
                         int len = option_len(opt);
                         unsigned char *rop = option_ptr(opt, 0);
                         char *q = daemon->namebuff;
                         int i;
                         for (i = 0; i < len; i++)
                           {
                             q += snprintf(q, MAXDNAME - (q - daemon->namebuff), "%d%s", rop[i], i + 1 == len ? "" : ",");
                           }
                         lease_add_extradata(lease, (unsigned char *)daemon->namebuff, (q - daemon->namebuff), 0); 
                       }
                     else
                       {
                         add_extradata_opt(lease, NULL);
                       }
   
                   /* space-concat tag set */                    /* space-concat tag set */
                   if (!tagif_netid)                    if (!tagif_netid)
                     add_extradata_opt(lease, NULL);                      add_extradata_opt(lease, NULL);
Line 1292  size_t dhcp_reply(struct dhcp_context *context, char * Line 1436  size_t dhcp_reply(struct dhcp_context *context, char *
                       /* If the user-class option started as counted strings, the first byte will be zero. */                        /* If the user-class option started as counted strings, the first byte will be zero. */
                       if (len != 0 && ucp[0] == 0)                        if (len != 0 && ucp[0] == 0)
                         ucp++, len--;                          ucp++, len--;
                      lease_add_extradata(lease, ucp, len, 0);                      lease_add_extradata(lease, ucp, len, -1);
                     }                      }
                 }                  }
 #endif  #endif
Line 1347  size_t dhcp_reply(struct dhcp_context *context, char * Line 1491  size_t dhcp_reply(struct dhcp_context *context, char *
           else            else
             override = lease->override;              override = lease->override;
   
          log_packet("DHCPACK", &mess->yiaddr, emac, emac_len, iface_name, hostname, mess->xid);            daemon->metrics[METRIC_DHCPACK]++;
                    log_packet("DHCPACK", &mess->yiaddr, emac, emac_len, iface_name, hostname, NULL, mess->xid);  
 
           clear_packet(mess, end);            clear_packet(mess, end);
           option_put(mess, end, OPTION_MESSAGE_TYPE, 1, DHCPACK);            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_SERVER_IDENTIFIER, INADDRSZ, ntohl(server_id(context, override, fallback).s_addr));
           option_put(mess, end, OPTION_LEASE_TIME, 4, time);            option_put(mess, end, OPTION_LEASE_TIME, 4, time);
          if (time != 0xffffffff)          if (rapid_commit)
            {             option_put(mess, end, OPTION_RAPID_COMMIT, 0, 0);
              while (fuzz > (time/16))           do_options(context, mess, end, req_options, hostname, get_domain(mess->yiaddr), 
                fuzz = fuzz/2;                      netid, subnet_addr, fqdn_flags, borken_opt, pxearch, uuid, vendor_class_len, now, time, fuzz, pxevendor);
              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),  
                     netid, subnet_addr, fqdn_flags, borken_opt, pxearch, uuid, vendor_class_len, now); 
         }          }
   
       return dhcp_packet_size(mess, agent_id, real_end);         return dhcp_packet_size(mess, agent_id, real_end); 
Line 1370  size_t dhcp_reply(struct dhcp_context *context, char * Line 1510  size_t dhcp_reply(struct dhcp_context *context, char *
       if (ignore || have_config(config, CONFIG_DISABLE))        if (ignore || have_config(config, CONFIG_DISABLE))
         message = _("ignored");          message = _("ignored");
               
      log_packet("DHCPINFORM", &mess->ciaddr, emac, emac_len, iface_name, message, mess->xid);      daemon->metrics[METRIC_DHCPINFORM]++;
       log_packet("DHCPINFORM", &mess->ciaddr, emac, emac_len, iface_name, message, NULL, mess->xid);
             
       if (message || mess->ciaddr.s_addr == 0)        if (message || mess->ciaddr.s_addr == 0)
         return 0;          return 0;
Line 1385  size_t dhcp_reply(struct dhcp_context *context, char * Line 1526  size_t dhcp_reply(struct dhcp_context *context, char *
           lease->hostname)            lease->hostname)
         hostname = lease->hostname;          hostname = lease->hostname;
               
      if (!hostname && (hostname = host_from_dns(mess->ciaddr)))      if (!hostname)
        domain = get_domain(mess->ciaddr);        hostname = host_from_dns(mess->ciaddr);
               
       if (context && context->netid.net)        if (context && context->netid.net)
         {          {
Line 1396  size_t dhcp_reply(struct dhcp_context *context, char * Line 1537  size_t dhcp_reply(struct dhcp_context *context, char *
   
       log_tags(tagif_netid, ntohl(mess->xid));        log_tags(tagif_netid, ntohl(mess->xid));
               
      log_packet("DHCPACK", &mess->ciaddr, emac, emac_len, iface_name, hostname, mess->xid);      daemon->metrics[METRIC_DHCPACK]++;
       log_packet("DHCPACK", &mess->ciaddr, emac, emac_len, iface_name, hostname, NULL, mess->xid);
               
       if (lease)        if (lease)
         {          {
Line 1410  size_t dhcp_reply(struct dhcp_context *context, char * Line 1552  size_t dhcp_reply(struct dhcp_context *context, char *
       clear_packet(mess, end);        clear_packet(mess, end);
       option_put(mess, end, OPTION_MESSAGE_TYPE, 1, DHCPACK);        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_SERVER_IDENTIFIER, INADDRSZ, ntohl(server_id(context, override, fallback).s_addr));
           
       /* RFC 2131 says that DHCPINFORM shouldn't include lease-time parameters, but 
          we supply a utility which makes DHCPINFORM requests to get this information.
          Only include lease time if OPTION_LEASE_TIME is in the parameter request list,
          which won't be true for ordinary clients, but will be true for the 
          dhcp_lease_time utility. */
       if (lease && in_list(req_options, OPTION_LEASE_TIME))
         {
           if (lease->expires == 0)
             time = 0xffffffff;
           else
             time = (unsigned int)difftime(lease->expires, now);
           option_put(mess, end, OPTION_LEASE_TIME, 4, time);
         }
 
       do_options(context, mess, end, req_options, hostname, get_domain(mess->ciaddr),        do_options(context, mess, end, req_options, hostname, get_domain(mess->ciaddr),
                 netid, subnet_addr, fqdn_flags, borken_opt, pxearch, uuid, vendor_class_len, now);                 netid, subnet_addr, fqdn_flags, borken_opt, pxearch, uuid, vendor_class_len, now, 0xffffffff, 0, pxevendor);
               
       *is_inform = 1; /* handle reply differently */        *is_inform = 1; /* handle reply differently */
       return dhcp_packet_size(mess, agent_id, real_end);         return dhcp_packet_size(mess, agent_id, real_end); 
Line 1516  static void add_extradata_opt(struct dhcp_lease *lease Line 1672  static void add_extradata_opt(struct dhcp_lease *lease
 #endif  #endif
   
 static void log_packet(char *type, void *addr, unsigned char *ext_mac,   static void log_packet(char *type, void *addr, unsigned char *ext_mac, 
                       int mac_len, char *interface, char *string, u32 xid)                       int mac_len, char *interface, char *string, char *err, u32 xid)
 {  {
   struct in_addr a;    struct in_addr a;
     
     if (!err && !option_bool(OPT_LOG_OPTS) && option_bool(OPT_QUIET_DHCP))
       return;
     
   /* addr may be misaligned */    /* addr may be misaligned */
   if (addr)    if (addr)
     memcpy(&a, addr, sizeof(a));      memcpy(&a, addr, sizeof(a));
Line 1527  static void log_packet(char *type, void *addr, unsigne Line 1686  static void log_packet(char *type, void *addr, unsigne
   print_mac(daemon->namebuff, ext_mac, mac_len);    print_mac(daemon->namebuff, ext_mac, mac_len);
       
   if(option_bool(OPT_LOG_OPTS))    if(option_bool(OPT_LOG_OPTS))
     my_syslog(MS_DHCP | LOG_INFO, "%u %s(%s) %s%s%s %s",     my_syslog(MS_DHCP | LOG_INFO, "%u %s(%s) %s%s%s %s%s",
                ntohl(xid),                  ntohl(xid), 
                type,                 type,
                interface,                  interface, 
                addr ? inet_ntoa(a) : "",                 addr ? inet_ntoa(a) : "",
                addr ? " " : "",                 addr ? " " : "",
                daemon->namebuff,                 daemon->namebuff,
               string ? string : "");               string ? string : "",
                err ? err : "");
   else    else
    my_syslog(MS_DHCP | LOG_INFO, "%s(%s) %s%s%s %s",    my_syslog(MS_DHCP | LOG_INFO, "%s(%s) %s%s%s %s%s",
               type,                type,
               interface,                 interface, 
               addr ? inet_ntoa(a) : "",                addr ? inet_ntoa(a) : "",
               addr ? " " : "",                addr ? " " : "",
               daemon->namebuff,                daemon->namebuff,
              string ? string : "");              string ? string : "",
               err ? err : "");
 
 #ifdef HAVE_UBUS
         if (!strcmp(type, "DHCPACK"))
                 ubus_event_bcast("dhcp.ack", daemon->namebuff, addr ? inet_ntoa(a) : NULL, string ? string : NULL, interface);
         else if (!strcmp(type, "DHCPRELEASE"))
                 ubus_event_bcast("dhcp.release", daemon->namebuff, addr ? inet_ntoa(a) : NULL, string ? string : NULL, interface);
 #endif
 }  }
   
 static void log_options(unsigned char *start, u32 xid)  static void log_options(unsigned char *start, u32 xid)
Line 1561  static unsigned char *option_find1(unsigned char *p, u Line 1729  static unsigned char *option_find1(unsigned char *p, u
 {  {
   while (1)     while (1) 
     {      {
      if (p > end)      if (p >= end)
         return NULL;          return NULL;
       else if (*p == OPTION_END)        else if (*p == OPTION_END)
         return opt == OPTION_END ? p : NULL;          return opt == OPTION_END ? p : NULL;
Line 1782  static void option_put(struct dhcp_packet *mess, unsig Line 1950  static void option_put(struct dhcp_packet *mess, unsig
 }  }
   
 static void option_put_string(struct dhcp_packet *mess, unsigned char *end, int opt,   static void option_put_string(struct dhcp_packet *mess, unsigned char *end, int opt, 
                              char *string, int null_term)                              const char *string, int null_term)
 {  {
   unsigned char *p;    unsigned char *p;
   size_t len = strlen(string);    size_t len = strlen(string);
Line 1819  static int do_opt(struct dhcp_opt *opt, unsigned char  Line 1987  static int do_opt(struct dhcp_opt *opt, unsigned char 
             }              }
         }          }
       else        else
        memcpy(p, opt->val, len);        /* empty string may be extended to "\0" by null_term */
         memcpy(p, opt->val ? opt->val : (unsigned char *)"", len);
     }        }  
   return len;    return len;
 }  }
Line 1859  static void match_vendor_opts(unsigned char *opt, stru Line 2028  static void match_vendor_opts(unsigned char *opt, stru
       dopt->flags &= ~DHOPT_VENDOR_MATCH;        dopt->flags &= ~DHOPT_VENDOR_MATCH;
       if (opt && (dopt->flags & DHOPT_VENDOR))        if (opt && (dopt->flags & DHOPT_VENDOR))
         {          {
          int i, len = 0;          const struct dhcp_pxe_vendor *pv;
          if (dopt->u.vendor_class)          struct dhcp_pxe_vendor dummy_vendor = {
            len = strlen((char *)dopt->u.vendor_class);            .data = (char *)dopt->u.vendor_class,
          for (i = 0; i <= (option_len(opt) - len); i++)            .next = NULL,
            if (len == 0 || memcmp(dopt->u.vendor_class, option_ptr(opt, i), len) == 0)          };
              {          if (dopt->flags & DHOPT_VENDOR_PXE)
                dopt->flags |= DHOPT_VENDOR_MATCH;            pv = daemon->dhcp_pxe_vendors;
                break;          else
              }            pv = &dummy_vendor;
           for (; pv; pv = pv->next)
             {
               int i, len = 0, matched = 0;
               if (pv->data)
                 len = strlen(pv->data);
               for (i = 0; i <= (option_len(opt) - len); i++)
                 if (len == 0 || memcmp(pv->data, option_ptr(opt, i), len) == 0)
                   {
                     matched = 1;
                     break;
                   }
               if (matched)
                 {
                   dopt->flags |= DHOPT_VENDOR_MATCH;
                   break;
                 }
             }
         }          }
     }      }
 }  }
Line 1920  static int do_encap_opts(struct dhcp_opt *opt, int enc Line 2106  static int do_encap_opts(struct dhcp_opt *opt, int enc
   return ret;    return ret;
 }  }
   
static void pxe_misc(struct dhcp_packet *mess, unsigned char *end, unsigned char *uuid)static void pxe_misc(struct dhcp_packet *mess, unsigned char *end, unsigned char *uuid, const char *pxevendor)
 {  {
   unsigned char *p;    unsigned char *p;
   
  option_put_string(mess, end, OPTION_VENDOR_ID, "PXEClient", 0);  if (!pxevendor)
     pxevendor="PXEClient";
   option_put_string(mess, end, OPTION_VENDOR_ID, pxevendor, 0);
   if (uuid && (p = free_space(mess, end, OPTION_PXE_UUID, 17)))    if (uuid && (p = free_space(mess, end, OPTION_PXE_UUID, 17)))
     memcpy(p, uuid, 17);      memcpy(p, uuid, 17);
 }  }
Line 1946  static int prune_vendor_opts(struct dhcp_netid *netid) Line 2134  static int prune_vendor_opts(struct dhcp_netid *netid)
   return force;    return force;
 }  }
   
   
   /* Many UEFI PXE implementations have badly broken menu code.
      If there's exactly one relevant menu item, we abandon the menu system,
      and jamb the data direct into the DHCP file, siaddr and sname fields.
      Note that in this case, we have to assume that layer zero would be requested
      by the client PXE stack. */
   static int pxe_uefi_workaround(int pxe_arch, struct dhcp_netid *netid, struct dhcp_packet *mess, struct in_addr local, time_t now, int pxe)
   {
     struct pxe_service *service, *found;
   
     /* Only workaround UEFI archs. */
     if (pxe_arch < 6)
       return 0;
     
     for (found = NULL, service = daemon->pxe_services; service; service = service->next)
       if (pxe_arch == service->CSA && service->basename && match_netid(service->netid, netid, 1))
         {
           if (found)
             return 0; /* More than one relevant menu item */
             
           found = service;
         }
   
     if (!found)
       return 0; /* No relevant menu items. */
     
     if (!pxe)
        return 1;
     
     if (found->sname)
       {
         mess->siaddr = a_record_from_hosts(found->sname, now);
         snprintf((char *)mess->sname, sizeof(mess->sname), "%s", found->sname);
       }
     else 
       {
         if (found->server.s_addr != 0)
           mess->siaddr = found->server; 
         else
           mess->siaddr = local;
     
         inet_ntop(AF_INET, &mess->siaddr, (char *)mess->sname, INET_ADDRSTRLEN);
       }
     
     snprintf((char *)mess->file, sizeof(mess->file), 
              strchr(found->basename, '.') ? "%s" : "%s.0", found->basename);
     
     return 1;
   }
   
 static struct dhcp_opt *pxe_opts(int pxe_arch, struct dhcp_netid *netid, struct in_addr local, time_t now)  static struct dhcp_opt *pxe_opts(int pxe_arch, struct dhcp_netid *netid, struct in_addr local, time_t now)
 {  {
 #define NUM_OPTS 4    #define NUM_OPTS 4  
Line 2091  struct dhcp_boot *find_boot(struct dhcp_netid *netid) Line 2329  struct dhcp_boot *find_boot(struct dhcp_netid *netid)
   return boot;    return boot;
 }  }
   
   static int is_pxe_client(struct dhcp_packet *mess, size_t sz, const char **pxe_vendor)
   {
     const unsigned char *opt = NULL;
     ssize_t conf_len = 0;
     const struct dhcp_pxe_vendor *conf = daemon->dhcp_pxe_vendors;
     opt = option_find(mess, sz, OPTION_VENDOR_ID, 0);
     if (!opt) 
       return 0;
     for (; conf; conf = conf->next)
       {
         conf_len = strlen(conf->data);
         if (option_len(opt) < conf_len)
           continue;
         if (strncmp(option_ptr(opt, 0), conf->data, conf_len) == 0)
           {
             if (pxe_vendor)
               *pxe_vendor = conf->data;
             return 1;
           }
       }
     return 0;
   }
   
 static void do_options(struct dhcp_context *context,  static void do_options(struct dhcp_context *context,
                        struct dhcp_packet *mess,                         struct dhcp_packet *mess,
                        unsigned char *end,                          unsigned char *end, 
Line 2103  static void do_options(struct dhcp_context *context, Line 2364  static void do_options(struct dhcp_context *context,
                        int null_term, int pxe_arch,                         int null_term, int pxe_arch,
                        unsigned char *uuid,                         unsigned char *uuid,
                        int vendor_class_len,                         int vendor_class_len,
                       time_t now)                       time_t now,
                        unsigned int lease_time,
                        unsigned short fuzz,
                        const char *pxevendor)
 {  {
   struct dhcp_opt *opt, *config_opts = daemon->dhcp_opts;    struct dhcp_opt *opt, *config_opts = daemon->dhcp_opts;
   struct dhcp_boot *boot;    struct dhcp_boot *boot;
Line 2153  static void do_options(struct dhcp_context *context, Line 2417  static void do_options(struct dhcp_context *context,
   /* See if we can send the boot stuff as options.    /* See if we can send the boot stuff as options.
      To do this we need a requested option list, BOOTP       To do this we need a requested option list, BOOTP
      and very old DHCP clients won't have this, we also        and very old DHCP clients won't have this, we also 
     provide an manual option to disable it.     provide a manual option to disable it.
      Some PXE ROMs have bugs (surprise!) and need zero-terminated        Some PXE ROMs have bugs (surprise!) and need zero-terminated 
      names, so we always send those.  */       names, so we always send those.  */
   if ((boot = find_boot(tagif)))    if ((boot = find_boot(tagif)))
Line 2165  static void do_options(struct dhcp_context *context, Line 2429  static void do_options(struct dhcp_context *context,
               in_list(req_options, OPTION_SNAME))                in_list(req_options, OPTION_SNAME))
             option_put_string(mess, end, OPTION_SNAME, boot->sname, 1);              option_put_string(mess, end, OPTION_SNAME, boot->sname, 1);
           else            else
            strncpy((char *)mess->sname, boot->sname, sizeof(mess->sname)-1);            safe_strncpy((char *)mess->sname, boot->sname, sizeof(mess->sname));
         }          }
               
       if (boot->file)        if (boot->file)
Line 2175  static void do_options(struct dhcp_context *context, Line 2439  static void do_options(struct dhcp_context *context,
               in_list(req_options, OPTION_FILENAME))                in_list(req_options, OPTION_FILENAME))
             option_put_string(mess, end, OPTION_FILENAME, boot->file, 1);              option_put_string(mess, end, OPTION_FILENAME, boot->file, 1);
           else            else
            strncpy((char *)mess->file, boot->file, sizeof(mess->file)-1);            safe_strncpy((char *)mess->file, boot->file, sizeof(mess->file));
         }          }
               
       if (boot->next_server.s_addr)         if (boot->next_server.s_addr) 
Line 2192  static void do_options(struct dhcp_context *context, Line 2456  static void do_options(struct dhcp_context *context,
       if ((!req_options || !in_list(req_options, OPTION_FILENAME)) &&        if ((!req_options || !in_list(req_options, OPTION_FILENAME)) &&
           (opt = option_find2(OPTION_FILENAME)) && !(opt->flags & DHOPT_FORCE))            (opt = option_find2(OPTION_FILENAME)) && !(opt->flags & DHOPT_FORCE))
         {          {
          strncpy((char *)mess->file, (char *)opt->val, sizeof(mess->file)-1);          safe_strncpy((char *)mess->file, (char *)opt->val, sizeof(mess->file));
           done_file = 1;            done_file = 1;
         }          }
               
       if ((!req_options || !in_list(req_options, OPTION_SNAME)) &&        if ((!req_options || !in_list(req_options, OPTION_SNAME)) &&
           (opt = option_find2(OPTION_SNAME)) && !(opt->flags & DHOPT_FORCE))            (opt = option_find2(OPTION_SNAME)) && !(opt->flags & DHOPT_FORCE))
         {          {
          strncpy((char *)mess->sname, (char *)opt->val, sizeof(mess->sname)-1);          safe_strncpy((char *)mess->sname, (char *)opt->val, sizeof(mess->sname));
           done_server = 1;            done_server = 1;
         }          }
               
Line 2227  static void do_options(struct dhcp_context *context, Line 2491  static void do_options(struct dhcp_context *context,
   /* rfc3011 says this doesn't need to be in the requested options list. */    /* rfc3011 says this doesn't need to be in the requested options list. */
   if (subnet_addr.s_addr)    if (subnet_addr.s_addr)
     option_put(mess, end, OPTION_SUBNET_SELECT, INADDRSZ, ntohl(subnet_addr.s_addr));      option_put(mess, end, OPTION_SUBNET_SELECT, INADDRSZ, ntohl(subnet_addr.s_addr));
     
   if (lease_time != 0xffffffff)
     { 
       unsigned int t1val = lease_time/2; 
       unsigned int t2val = (lease_time*7)/8;
       unsigned int hval;
       
       /* If set by user, sanity check, so not longer than lease. */
       if ((opt = option_find2(OPTION_T1)))
         {
           hval = ntohl(*((unsigned int *)opt->val));
           if (hval < lease_time && hval > 2)
             t1val = hval;
         }
 
        if ((opt = option_find2(OPTION_T2)))
         {
           hval = ntohl(*((unsigned int *)opt->val));
           if (hval < lease_time && hval > 2)
             t2val = hval;
         }
           
        /* ensure T1 is still < T2 */
        if (t2val <= t1val)
          t1val = t2val - 1; 
 
        while (fuzz > (t1val/8))
          fuzz = fuzz/2;
          
        t1val -= fuzz;
        t2val -= fuzz;
        
        option_put(mess, end, OPTION_T1, 4, t1val);
        option_put(mess, end, OPTION_T2, 4, t2val);
     }
 
   /* replies to DHCPINFORM may not have a valid context */    /* replies to DHCPINFORM may not have a valid context */
   if (context)    if (context)
     {      {
Line 2275  static void do_options(struct dhcp_context *context, Line 2574  static void do_options(struct dhcp_context *context,
   
           if (domain)            if (domain)
             len += strlen(domain) + 1;              len += strlen(domain) + 1;
                    else if (fqdn_flags & 0x04)
             len--;
 
           if ((p = free_space(mess, end, OPTION_CLIENT_FQDN, len)))            if ((p = free_space(mess, end, OPTION_CLIENT_FQDN, len)))
             {              {
               *(p++) = fqdn_flags & 0x0f; /* MBZ bits to zero */                 *(p++) = fqdn_flags & 0x0f; /* MBZ bits to zero */ 
Line 2284  static void do_options(struct dhcp_context *context, Line 2585  static void do_options(struct dhcp_context *context,
   
               if (fqdn_flags & 0x04)                if (fqdn_flags & 0x04)
                 {                  {
                  p = do_rfc1035_name(p, hostname);                  p = do_rfc1035_name(p, hostname, NULL);
                   if (domain)                    if (domain)
                    p = do_rfc1035_name(p, domain);                    {
                  *p++ = 0;                      p = do_rfc1035_name(p, domain, NULL);
                       *p++ = 0;
                     }
                 }                  }
               else                else
                 {                  {
Line 2318  static void do_options(struct dhcp_context *context, Line 2621  static void do_options(struct dhcp_context *context,
       if (!(opt->flags & DHOPT_FORCE) && !in_list(req_options, optno))        if (!(opt->flags & DHOPT_FORCE) && !in_list(req_options, optno))
         continue;          continue;
               
      /* prohibit some used-internally options */      /* prohibit some used-internally options. T1 and T2 already handled. */
       if (optno == OPTION_CLIENT_FQDN ||        if (optno == OPTION_CLIENT_FQDN ||
           optno == OPTION_MAXMESSAGE ||            optno == OPTION_MAXMESSAGE ||
           optno == OPTION_OVERLOAD ||            optno == OPTION_OVERLOAD ||
           optno == OPTION_PAD ||            optno == OPTION_PAD ||
          optno == OPTION_END)          optno == OPTION_END ||
           optno == OPTION_T1 ||
           optno == OPTION_T2)
         continue;          continue;
   
       if (optno == OPTION_SNAME && done_server)        if (optno == OPTION_SNAME && done_server)
Line 2436  static void do_options(struct dhcp_context *context, Line 2741  static void do_options(struct dhcp_context *context,
       
   if (context && pxe_arch != -1)    if (context && pxe_arch != -1)
     {      {
      pxe_misc(mess, end, uuid);      pxe_misc(mess, end, uuid, pxevendor);
      config_opts = pxe_opts(pxe_arch, tagif, context->local, now);      if (!pxe_uefi_workaround(pxe_arch, tagif, mess, context->local, now, 0))
         config_opts = pxe_opts(pxe_arch, tagif, context->local, now);
     }      }
   
   if ((force_encap || in_list(req_options, OPTION_VENDOR_CLASS_OPT)) &&    if ((force_encap || in_list(req_options, OPTION_VENDOR_CLASS_OPT)) &&
Line 2453  static void do_options(struct dhcp_context *context, Line 2759  static void do_options(struct dhcp_context *context,
     {      {
       mess->file[0] = f0;        mess->file[0] = f0;
       mess->sname[0] = s0;        mess->sname[0] = s0;
       }
   }
   
   static void apply_delay(u32 xid, time_t recvtime, struct dhcp_netid *netid)
   {
     struct delay_config *delay_conf;
     
     /* Decide which delay_config option we're using */
     for (delay_conf = daemon->delay_conf; delay_conf; delay_conf = delay_conf->next)
       if (match_netid(delay_conf->netid, netid, 0))
         break;
     
     if (!delay_conf)
       /* No match, look for one without a netid */
       for (delay_conf = daemon->delay_conf; delay_conf; delay_conf = delay_conf->next)
         if (match_netid(delay_conf->netid, netid, 1))
           break;
   
     if (delay_conf)
       {
         if (!option_bool(OPT_QUIET_DHCP))
           my_syslog(MS_DHCP | LOG_INFO, _("%u reply delay: %d"), ntohl(xid), delay_conf->delay);
         delay_dhcp(recvtime, delay_conf->delay, -1, 0, 0);
     }      }
 }  }
   

Removed from v.1.1.1.1  
changed lines
  Added in v.1.1.1.4


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