"Fossies" - the Fresh Open Source Software Archive

Member "dnsmasq-2.85/src/dhcp-common.c" (7 Apr 2021, 27296 Bytes) of package /linux/misc/dns/dnsmasq-2.85.tar.xz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file.

    1 /* dnsmasq is Copyright (c) 2000-2021 Simon Kelley
    2 
    3    This program is free software; you can redistribute it and/or modify
    4    it under the terms of the GNU General Public License as published by
    5    the Free Software Foundation; version 2 dated June, 1991, or
    6    (at your option) version 3 dated 29 June, 2007.
    7  
    8    This program is distributed in the hope that it will be useful,
    9    but WITHOUT ANY WARRANTY; without even the implied warranty of
   10    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   11    GNU General Public License for more details.
   12      
   13    You should have received a copy of the GNU General Public License
   14    along with this program.  If not, see <http://www.gnu.org/licenses/>.
   15 */
   16 
   17 #include "dnsmasq.h"
   18 
   19 #ifdef HAVE_DHCP
   20 
   21 void dhcp_common_init(void)
   22 {
   23   /* These each hold a DHCP option max size 255
   24      and get a terminating zero added */
   25   daemon->dhcp_buff = safe_malloc(DHCP_BUFF_SZ);
   26   daemon->dhcp_buff2 = safe_malloc(DHCP_BUFF_SZ); 
   27   daemon->dhcp_buff3 = safe_malloc(DHCP_BUFF_SZ);
   28   
   29   /* dhcp_packet is used by v4 and v6, outpacket only by v6 
   30      sizeof(struct dhcp_packet) is as good an initial size as any,
   31      even for v6 */
   32   expand_buf(&daemon->dhcp_packet, sizeof(struct dhcp_packet));
   33 #ifdef HAVE_DHCP6
   34   if (daemon->dhcp6)
   35     expand_buf(&daemon->outpacket, sizeof(struct dhcp_packet));
   36 #endif
   37 }
   38 
   39 ssize_t recv_dhcp_packet(int fd, struct msghdr *msg)
   40 {  
   41   ssize_t sz, new_sz;
   42  
   43   while (1)
   44     {
   45       msg->msg_flags = 0;
   46       while ((sz = recvmsg(fd, msg, MSG_PEEK | MSG_TRUNC)) == -1 && errno == EINTR);
   47       
   48       if (sz == -1)
   49     return -1;
   50       
   51       if (!(msg->msg_flags & MSG_TRUNC))
   52     break;
   53 
   54       /* Very new Linux kernels return the actual size needed, 
   55      older ones always return truncated size */
   56       if ((size_t)sz == msg->msg_iov->iov_len)
   57     {
   58       if (!expand_buf(msg->msg_iov, sz + 100))
   59         return -1;
   60     }
   61       else
   62     {
   63       expand_buf(msg->msg_iov, sz);
   64       break;
   65     }
   66     }
   67   
   68   while ((new_sz = recvmsg(fd, msg, 0)) == -1 && errno == EINTR);
   69 
   70   /* Some kernels seem to ignore MSG_PEEK, and dequeue the packet anyway. 
   71      If that happens we get EAGAIN here because the socket is non-blocking.
   72      Use the result of the original testing recvmsg as long as the buffer
   73      was big enough. There's a small race here that may lose the odd packet,
   74      but it's UDP anyway. */
   75   
   76   if (new_sz == -1 && (errno == EWOULDBLOCK || errno == EAGAIN))
   77     new_sz = sz;
   78   
   79   return (msg->msg_flags & MSG_TRUNC) ? -1 : new_sz;
   80 }
   81 
   82 struct dhcp_netid *run_tag_if(struct dhcp_netid *tags)
   83 {
   84   struct tag_if *exprs;
   85   struct dhcp_netid_list *list;
   86 
   87   for (exprs = daemon->tag_if; exprs; exprs = exprs->next)
   88     if (match_netid(exprs->tag, tags, 1))
   89       for (list = exprs->set; list; list = list->next)
   90     {
   91       list->list->next = tags;
   92       tags = list->list;
   93     }
   94 
   95   return tags;
   96 }
   97 
   98 
   99 struct dhcp_netid *option_filter(struct dhcp_netid *tags, struct dhcp_netid *context_tags, struct dhcp_opt *opts)
  100 {
  101   struct dhcp_netid *tagif = run_tag_if(tags);
  102   struct dhcp_opt *opt;
  103   struct dhcp_opt *tmp;  
  104 
  105   /* flag options which are valid with the current tag set (sans context tags) */
  106   for (opt = opts; opt; opt = opt->next)
  107     {
  108       opt->flags &= ~DHOPT_TAGOK;
  109       if (!(opt->flags & (DHOPT_ENCAPSULATE | DHOPT_VENDOR | DHOPT_RFC3925)) &&
  110       match_netid(opt->netid, tagif, 0))
  111     opt->flags |= DHOPT_TAGOK;
  112     }
  113 
  114   /* now flag options which are valid, including the context tags,
  115      otherwise valid options are inhibited if we found a higher priority one above */
  116   if (context_tags)
  117     {
  118       struct dhcp_netid *last_tag;
  119 
  120       for (last_tag = context_tags; last_tag->next; last_tag = last_tag->next);
  121       last_tag->next = tags;
  122       tagif = run_tag_if(context_tags);
  123       
  124       /* reset stuff with tag:!<tag> which now matches. */
  125       for (opt = opts; opt; opt = opt->next)
  126     if (!(opt->flags & (DHOPT_ENCAPSULATE | DHOPT_VENDOR | DHOPT_RFC3925)) &&
  127         (opt->flags & DHOPT_TAGOK) &&
  128         !match_netid(opt->netid, tagif, 0))
  129       opt->flags &= ~DHOPT_TAGOK;
  130 
  131       for (opt = opts; opt; opt = opt->next)
  132     if (!(opt->flags & (DHOPT_ENCAPSULATE | DHOPT_VENDOR | DHOPT_RFC3925 | DHOPT_TAGOK)) &&
  133         match_netid(opt->netid, tagif, 0))
  134       {
  135         struct dhcp_opt *tmp;  
  136         for (tmp = opts; tmp; tmp = tmp->next) 
  137           if (tmp->opt == opt->opt && opt->netid && (tmp->flags & DHOPT_TAGOK))
  138         break;
  139         if (!tmp)
  140           opt->flags |= DHOPT_TAGOK;
  141       }      
  142     }
  143   
  144   /* now flag untagged options which are not overridden by tagged ones */
  145   for (opt = opts; opt; opt = opt->next)
  146     if (!(opt->flags & (DHOPT_ENCAPSULATE | DHOPT_VENDOR | DHOPT_RFC3925 | DHOPT_TAGOK)) && !opt->netid)
  147       {
  148     for (tmp = opts; tmp; tmp = tmp->next) 
  149       if (tmp->opt == opt->opt && (tmp->flags & DHOPT_TAGOK))
  150         break;
  151     if (!tmp)
  152       opt->flags |= DHOPT_TAGOK;
  153     else if (!tmp->netid)
  154       my_syslog(MS_DHCP | LOG_WARNING, _("Ignoring duplicate dhcp-option %d"), tmp->opt); 
  155       }
  156 
  157   /* Finally, eliminate duplicate options later in the chain, and therefore earlier in the config file. */
  158   for (opt = opts; opt; opt = opt->next)
  159     if (opt->flags & DHOPT_TAGOK)
  160       for (tmp = opt->next; tmp; tmp = tmp->next) 
  161     if (tmp->opt == opt->opt)
  162       tmp->flags &= ~DHOPT_TAGOK;
  163   
  164   return tagif;
  165 }
  166     
  167 /* Is every member of check matched by a member of pool? 
  168    If tagnotneeded, untagged is OK */
  169 int match_netid(struct dhcp_netid *check, struct dhcp_netid *pool, int tagnotneeded)
  170 {
  171   struct dhcp_netid *tmp1;
  172   
  173   if (!check && !tagnotneeded)
  174     return 0;
  175 
  176   for (; check; check = check->next)
  177     {
  178       /* '#' for not is for backwards compat. */
  179       if (check->net[0] != '!' && check->net[0] != '#')
  180     {
  181       for (tmp1 = pool; tmp1; tmp1 = tmp1->next)
  182         if (strcmp(check->net, tmp1->net) == 0)
  183           break;
  184       if (!tmp1)
  185         return 0;
  186     }
  187       else
  188     for (tmp1 = pool; tmp1; tmp1 = tmp1->next)
  189       if (strcmp((check->net)+1, tmp1->net) == 0)
  190         return 0;
  191     }
  192   return 1;
  193 }
  194 
  195 /* return domain or NULL if none. */
  196 char *strip_hostname(char *hostname)
  197 {
  198   char *dot = strchr(hostname, '.');
  199  
  200   if (!dot)
  201     return NULL;
  202   
  203   *dot = 0; /* truncate */
  204   if (strlen(dot+1) != 0)
  205     return dot+1;
  206   
  207   return NULL;
  208 }
  209 
  210 void log_tags(struct dhcp_netid *netid, u32 xid)
  211 {
  212   if (netid && option_bool(OPT_LOG_OPTS))
  213     {
  214       char *s = daemon->namebuff;
  215       for (*s = 0; netid; netid = netid->next)
  216     {
  217       /* kill dupes. */
  218       struct dhcp_netid *n;
  219       
  220       for (n = netid->next; n; n = n->next)
  221         if (strcmp(netid->net, n->net) == 0)
  222           break;
  223       
  224       if (!n)
  225         {
  226           strncat (s, netid->net, (MAXDNAME-1) - strlen(s));
  227           if (netid->next)
  228         strncat (s, ", ", (MAXDNAME-1) - strlen(s));
  229         }
  230     }
  231       my_syslog(MS_DHCP | LOG_INFO, _("%u tags: %s"), xid, s);
  232     } 
  233 }   
  234   
  235 int match_bytes(struct dhcp_opt *o, unsigned char *p, int len)
  236 {
  237   int i;
  238   
  239   if (o->len > len)
  240     return 0;
  241   
  242   if (o->len == 0)
  243     return 1;
  244      
  245   if (o->flags & DHOPT_HEX)
  246     { 
  247       if (memcmp_masked(o->val, p, o->len, o->u.wildcard_mask))
  248     return 1;
  249     }
  250   else 
  251     for (i = 0; i <= (len - o->len); ) 
  252       {
  253     if (memcmp(o->val, p + i, o->len) == 0)
  254       return 1;
  255         
  256     if (o->flags & DHOPT_STRING)
  257       i++;
  258     else
  259       i += o->len;
  260       }
  261   
  262   return 0;
  263 }
  264 
  265 int config_has_mac(struct dhcp_config *config, unsigned char *hwaddr, int len, int type)
  266 {
  267   struct hwaddr_config *conf_addr;
  268   
  269   for (conf_addr = config->hwaddr; conf_addr; conf_addr = conf_addr->next)
  270     if (conf_addr->wildcard_mask == 0 &&
  271     conf_addr->hwaddr_len == len &&
  272     (conf_addr->hwaddr_type == type || conf_addr->hwaddr_type == 0) &&
  273     memcmp(conf_addr->hwaddr, hwaddr, len) == 0)
  274       return 1;
  275   
  276   return 0;
  277 }
  278 
  279 static int is_config_in_context(struct dhcp_context *context, struct dhcp_config *config)
  280 {
  281   if (!context) /* called via find_config() from lease_update_from_configs() */
  282     return 1; 
  283 
  284   if (!(config->flags & (CONFIG_ADDR | CONFIG_ADDR6)))
  285     return 1;
  286   
  287 #ifdef HAVE_DHCP6
  288   if (context->flags & CONTEXT_V6)
  289     {
  290        struct addrlist *addr_list;
  291 
  292        if (config->flags & CONFIG_ADDR6)
  293      for (; context; context = context->current)
  294        for (addr_list = config->addr6; addr_list; addr_list = addr_list->next)
  295          {
  296            if ((addr_list->flags & ADDRLIST_WILDCARD) && context->prefix == 64)
  297          return 1;
  298            
  299            if (is_same_net6(&addr_list->addr.addr6, &context->start6, context->prefix))
  300          return 1;
  301          }
  302     }
  303   else
  304 #endif
  305     {
  306       for (; context; context = context->current)
  307     if ((config->flags & CONFIG_ADDR) && is_same_net(config->addr, context->start, context->netmask))
  308       return 1;
  309     }
  310 
  311   return 0;
  312 }
  313 
  314 static struct dhcp_config *find_config_match(struct dhcp_config *configs,
  315                          struct dhcp_context *context,
  316                          unsigned char *clid, int clid_len,
  317                          unsigned char *hwaddr, int hw_len, 
  318                          int hw_type, char *hostname,
  319                          struct dhcp_netid *tags, int tag_not_needed)
  320 {
  321   int count, new;
  322   struct dhcp_config *config, *candidate; 
  323   struct hwaddr_config *conf_addr;
  324 
  325   if (clid)
  326     for (config = configs; config; config = config->next)
  327       if (config->flags & CONFIG_CLID)
  328     {
  329       if (config->clid_len == clid_len && 
  330           memcmp(config->clid, clid, clid_len) == 0 &&
  331           is_config_in_context(context, config) &&
  332           match_netid(config->filter, tags, tag_not_needed))
  333         
  334         return config;
  335       
  336       /* dhcpcd prefixes ASCII client IDs by zero which is wrong, but we try and
  337          cope with that here. This is IPv4 only. context==NULL implies IPv4, 
  338          see lease_update_from_configs() */
  339       if ((!context || !(context->flags & CONTEXT_V6)) && *clid == 0 && config->clid_len == clid_len-1  &&
  340           memcmp(config->clid, clid+1, clid_len-1) == 0 &&
  341           is_config_in_context(context, config) &&
  342           match_netid(config->filter, tags, tag_not_needed))
  343         return config;
  344     }
  345   
  346 
  347   if (hwaddr)
  348     for (config = configs; config; config = config->next)
  349       if (config_has_mac(config, hwaddr, hw_len, hw_type) &&
  350       is_config_in_context(context, config) &&
  351       match_netid(config->filter, tags, tag_not_needed))
  352     return config;
  353   
  354   if (hostname && context)
  355     for (config = configs; config; config = config->next)
  356       if ((config->flags & CONFIG_NAME) && 
  357       hostname_isequal(config->hostname, hostname) &&
  358       is_config_in_context(context, config) &&
  359       match_netid(config->filter, tags, tag_not_needed))
  360     return config;
  361 
  362   
  363   if (!hwaddr)
  364     return NULL;
  365 
  366   /* use match with fewest wildcard octets */
  367   for (candidate = NULL, count = 0, config = configs; config; config = config->next)
  368     if (is_config_in_context(context, config) &&
  369     match_netid(config->filter, tags, tag_not_needed))
  370       for (conf_addr = config->hwaddr; conf_addr; conf_addr = conf_addr->next)
  371     if (conf_addr->wildcard_mask != 0 &&
  372         conf_addr->hwaddr_len == hw_len &&  
  373         (conf_addr->hwaddr_type == hw_type || conf_addr->hwaddr_type == 0) &&
  374         (new = memcmp_masked(conf_addr->hwaddr, hwaddr, hw_len, conf_addr->wildcard_mask)) > count)
  375       {
  376           count = new;
  377           candidate = config;
  378       }
  379   
  380   return candidate;
  381 }
  382 
  383 /* Find tagged configs first. */
  384 struct dhcp_config *find_config(struct dhcp_config *configs,
  385                 struct dhcp_context *context,
  386                 unsigned char *clid, int clid_len,
  387                 unsigned char *hwaddr, int hw_len, 
  388                 int hw_type, char *hostname, struct dhcp_netid *tags)
  389 {
  390   struct dhcp_config *ret = find_config_match(configs, context, clid, clid_len, hwaddr, hw_len, hw_type, hostname, tags, 0);
  391 
  392   if (!ret)
  393     ret = find_config_match(configs, context, clid, clid_len, hwaddr, hw_len, hw_type, hostname, tags, 1);
  394 
  395   return ret;
  396 }
  397 
  398 void dhcp_update_configs(struct dhcp_config *configs)
  399 {
  400   /* Some people like to keep all static IP addresses in /etc/hosts.
  401      This goes through /etc/hosts and sets static addresses for any DHCP config
  402      records which don't have an address and whose name matches. 
  403      We take care to maintain the invariant that any IP address can appear
  404      in at most one dhcp-host. Since /etc/hosts can be re-read by SIGHUP, 
  405      restore the status-quo ante first. */
  406   
  407   struct dhcp_config *config, *conf_tmp;
  408   struct crec *crec;
  409   int prot = AF_INET;
  410 
  411   for (config = configs; config; config = config->next)
  412   {
  413     if (config->flags & CONFIG_ADDR_HOSTS)
  414       config->flags &= ~(CONFIG_ADDR | CONFIG_ADDR_HOSTS);
  415 #ifdef HAVE_DHCP6
  416     if (config->flags & CONFIG_ADDR6_HOSTS)
  417       config->flags &= ~(CONFIG_ADDR6 | CONFIG_ADDR6_HOSTS);
  418 #endif
  419   }
  420 
  421 #ifdef HAVE_DHCP6 
  422  again:  
  423 #endif
  424 
  425   if (daemon->port != 0)
  426     for (config = configs; config; config = config->next)
  427       {
  428     int conflags = CONFIG_ADDR;
  429     int cacheflags = F_IPV4;
  430 
  431 #ifdef HAVE_DHCP6
  432     if (prot == AF_INET6)
  433       {
  434         conflags = CONFIG_ADDR6;
  435         cacheflags = F_IPV6;
  436       }
  437 #endif
  438     if (!(config->flags & conflags) &&
  439         (config->flags & CONFIG_NAME) && 
  440         (crec = cache_find_by_name(NULL, config->hostname, 0, cacheflags)) &&
  441         (crec->flags & F_HOSTS))
  442       {
  443         if (cache_find_by_name(crec, config->hostname, 0, cacheflags))
  444           {
  445         /* use primary (first) address */
  446         while (crec && !(crec->flags & F_REVERSE))
  447           crec = cache_find_by_name(crec, config->hostname, 0, cacheflags);
  448         if (!crec)
  449           continue; /* should be never */
  450         inet_ntop(prot, &crec->addr, daemon->addrbuff, ADDRSTRLEN);
  451         my_syslog(MS_DHCP | LOG_WARNING, _("%s has more than one address in hostsfile, using %s for DHCP"), 
  452               config->hostname, daemon->addrbuff);
  453           }
  454         
  455         if (prot == AF_INET && 
  456         (!(conf_tmp = config_find_by_address(configs, crec->addr.addr4)) || conf_tmp == config))
  457           {
  458         config->addr = crec->addr.addr4;
  459         config->flags |= CONFIG_ADDR | CONFIG_ADDR_HOSTS;
  460         continue;
  461           }
  462 
  463 #ifdef HAVE_DHCP6
  464         if (prot == AF_INET6 && 
  465         (!(conf_tmp = config_find_by_address6(configs, NULL, 0, &crec->addr.addr6)) || conf_tmp == config))
  466           {
  467         /* host must have exactly one address if comming from /etc/hosts. */
  468         if (!config->addr6 && (config->addr6 = whine_malloc(sizeof(struct addrlist))))
  469           {
  470             config->addr6->next = NULL;
  471             config->addr6->flags = 0;
  472           }
  473 
  474         if (config->addr6 && !config->addr6->next && !(config->addr6->flags & (ADDRLIST_WILDCARD|ADDRLIST_PREFIX)))
  475           {
  476             memcpy(&config->addr6->addr.addr6, &crec->addr.addr6, IN6ADDRSZ);
  477             config->flags |= CONFIG_ADDR6 | CONFIG_ADDR6_HOSTS;
  478           }
  479         
  480         continue;
  481           }
  482 #endif
  483 
  484         inet_ntop(prot, &crec->addr, daemon->addrbuff, ADDRSTRLEN);
  485         my_syslog(MS_DHCP | LOG_WARNING, _("duplicate IP address %s (%s) in dhcp-config directive"), 
  486               daemon->addrbuff, config->hostname);
  487         
  488         
  489       }
  490       }
  491 
  492 #ifdef HAVE_DHCP6
  493   if (prot == AF_INET)
  494     {
  495       prot = AF_INET6;
  496       goto again;
  497     }
  498 #endif
  499 
  500 }
  501 
  502 #ifdef HAVE_LINUX_NETWORK 
  503 char *whichdevice(void)
  504 {
  505   /* If we are doing DHCP on exactly one interface, and running linux, do SO_BINDTODEVICE
  506      to that device. This is for the use case of  (eg) OpenStack, which runs a new
  507      dnsmasq instance for each VLAN interface it creates. Without the BINDTODEVICE, 
  508      individual processes don't always see the packets they should.
  509      SO_BINDTODEVICE is only available Linux. 
  510 
  511      Note that if wildcards are used in --interface, or --interface is not used at all,
  512      or a configured interface doesn't yet exist, then more interfaces may arrive later, 
  513      so we can't safely assert there is only one interface and proceed.
  514 */
  515   
  516   struct irec *iface, *found;
  517   struct iname *if_tmp;
  518   
  519   if (!daemon->if_names)
  520     return NULL;
  521   
  522   for (if_tmp = daemon->if_names; if_tmp; if_tmp = if_tmp->next)
  523     if (if_tmp->name && (!if_tmp->used || strchr(if_tmp->name, '*')))
  524       return NULL;
  525 
  526   for (found = NULL, iface = daemon->interfaces; iface; iface = iface->next)
  527     if (iface->dhcp_ok)
  528       {
  529     if (!found)
  530       found = iface;
  531     else if (strcmp(found->name, iface->name) != 0) 
  532       return NULL; /* more than one. */
  533       }
  534 
  535   if (found)
  536     return found->name;
  537 
  538   return NULL;
  539 }
  540  
  541 void  bindtodevice(char *device, int fd)
  542 {
  543   size_t len = strlen(device)+1;
  544   if (len > IFNAMSIZ)
  545     len = IFNAMSIZ;
  546   /* only allowed by root. */
  547   if (setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, device, len) == -1 &&
  548       errno != EPERM)
  549     die(_("failed to set SO_BINDTODEVICE on DHCP socket: %s"), NULL, EC_BADNET);
  550 }
  551 #endif
  552 
  553 static const struct opttab_t {
  554   char *name;
  555   u16 val, size;
  556 } opttab[] = {
  557   { "netmask", 1, OT_ADDR_LIST },
  558   { "time-offset", 2, 4 },
  559   { "router", 3, OT_ADDR_LIST  },
  560   { "dns-server", 6, OT_ADDR_LIST },
  561   { "log-server", 7, OT_ADDR_LIST },
  562   { "lpr-server", 9, OT_ADDR_LIST },
  563   { "hostname", 12, OT_INTERNAL | OT_NAME },
  564   { "boot-file-size", 13, 2 | OT_DEC },
  565   { "domain-name", 15, OT_NAME },
  566   { "swap-server", 16, OT_ADDR_LIST },
  567   { "root-path", 17, OT_NAME },
  568   { "extension-path", 18, OT_NAME },
  569   { "ip-forward-enable", 19, 1 },
  570   { "non-local-source-routing", 20, 1 },
  571   { "policy-filter", 21, OT_ADDR_LIST },
  572   { "max-datagram-reassembly", 22, 2 | OT_DEC },
  573   { "default-ttl", 23, 1 | OT_DEC },
  574   { "mtu", 26, 2 | OT_DEC },
  575   { "all-subnets-local", 27, 1 },
  576   { "broadcast", 28, OT_INTERNAL | OT_ADDR_LIST },
  577   { "router-discovery", 31, 1 },
  578   { "router-solicitation", 32, OT_ADDR_LIST },
  579   { "static-route", 33, OT_ADDR_LIST },
  580   { "trailer-encapsulation", 34, 1 },
  581   { "arp-timeout", 35, 4 | OT_DEC },
  582   { "ethernet-encap", 36, 1 },
  583   { "tcp-ttl", 37, 1 },
  584   { "tcp-keepalive", 38, 4 | OT_DEC },
  585   { "nis-domain", 40, OT_NAME },
  586   { "nis-server", 41, OT_ADDR_LIST },
  587   { "ntp-server", 42, OT_ADDR_LIST },
  588   { "vendor-encap", 43, OT_INTERNAL },
  589   { "netbios-ns", 44, OT_ADDR_LIST },
  590   { "netbios-dd", 45, OT_ADDR_LIST },
  591   { "netbios-nodetype", 46, 1 },
  592   { "netbios-scope", 47, 0 },
  593   { "x-windows-fs", 48, OT_ADDR_LIST },
  594   { "x-windows-dm", 49, OT_ADDR_LIST },
  595   { "requested-address", 50, OT_INTERNAL | OT_ADDR_LIST },
  596   { "lease-time", 51, OT_INTERNAL | OT_TIME },
  597   { "option-overload", 52, OT_INTERNAL },
  598   { "message-type", 53, OT_INTERNAL | OT_DEC },
  599   { "server-identifier", 54, OT_INTERNAL | OT_ADDR_LIST },
  600   { "parameter-request", 55, OT_INTERNAL },
  601   { "message", 56, OT_INTERNAL },
  602   { "max-message-size", 57, OT_INTERNAL },
  603   { "T1", 58, OT_TIME},
  604   { "T2", 59, OT_TIME},
  605   { "vendor-class", 60, 0 },
  606   { "client-id", 61, OT_INTERNAL },
  607   { "nis+-domain", 64, OT_NAME },
  608   { "nis+-server", 65, OT_ADDR_LIST },
  609   { "tftp-server", 66, OT_NAME },
  610   { "bootfile-name", 67, OT_NAME },
  611   { "mobile-ip-home", 68, OT_ADDR_LIST }, 
  612   { "smtp-server", 69, OT_ADDR_LIST }, 
  613   { "pop3-server", 70, OT_ADDR_LIST }, 
  614   { "nntp-server", 71, OT_ADDR_LIST }, 
  615   { "irc-server", 74, OT_ADDR_LIST }, 
  616   { "user-class", 77, 0 },
  617   { "rapid-commit", 80, 0 },
  618   { "FQDN", 81, OT_INTERNAL },
  619   { "agent-id", 82, OT_INTERNAL },
  620   { "client-arch", 93, 2 | OT_DEC },
  621   { "client-interface-id", 94, 0 },
  622   { "client-machine-id", 97, 0 },
  623   { "subnet-select", 118, OT_INTERNAL },
  624   { "domain-search", 119, OT_RFC1035_NAME },
  625   { "sip-server", 120, 0 },
  626   { "classless-static-route", 121, 0 },
  627   { "vendor-id-encap", 125, 0 },
  628   { "tftp-server-address", 150, OT_ADDR_LIST },
  629   { "server-ip-address", 255, OT_ADDR_LIST }, /* special, internal only, sets siaddr */
  630   { NULL, 0, 0 }
  631 };
  632 
  633 #ifdef HAVE_DHCP6
  634 static const struct opttab_t opttab6[] = {
  635   { "client-id", 1, OT_INTERNAL },
  636   { "server-id", 2, OT_INTERNAL },
  637   { "ia-na", 3, OT_INTERNAL },
  638   { "ia-ta", 4, OT_INTERNAL },
  639   { "iaaddr", 5, OT_INTERNAL },
  640   { "oro", 6, OT_INTERNAL },
  641   { "preference", 7, OT_INTERNAL | OT_DEC },
  642   { "unicast", 12, OT_INTERNAL },
  643   { "status", 13, OT_INTERNAL },
  644   { "rapid-commit", 14, OT_INTERNAL },
  645   { "user-class", 15, OT_INTERNAL | OT_CSTRING },
  646   { "vendor-class", 16, OT_INTERNAL | OT_CSTRING },
  647   { "vendor-opts", 17, OT_INTERNAL },
  648   { "sip-server-domain", 21,  OT_RFC1035_NAME },
  649   { "sip-server", 22, OT_ADDR_LIST },
  650   { "dns-server", 23, OT_ADDR_LIST },
  651   { "domain-search", 24, OT_RFC1035_NAME },
  652   { "nis-server", 27, OT_ADDR_LIST },
  653   { "nis+-server", 28, OT_ADDR_LIST },
  654   { "nis-domain", 29,  OT_RFC1035_NAME },
  655   { "nis+-domain", 30, OT_RFC1035_NAME },
  656   { "sntp-server", 31,  OT_ADDR_LIST },
  657   { "information-refresh-time", 32, OT_TIME },
  658   { "FQDN", 39, OT_INTERNAL | OT_RFC1035_NAME },
  659   { "ntp-server", 56, 0 /* OT_ADDR_LIST | OT_RFC1035_NAME */ },
  660   { "bootfile-url", 59, OT_NAME },
  661   { "bootfile-param", 60, OT_CSTRING },
  662   { NULL, 0, 0 }
  663 };
  664 #endif
  665 
  666 
  667 
  668 void display_opts(void)
  669 {
  670   int i;
  671   
  672   printf(_("Known DHCP options:\n"));
  673   
  674   for (i = 0; opttab[i].name; i++)
  675     if (!(opttab[i].size & OT_INTERNAL))
  676       printf("%3d %s\n", opttab[i].val, opttab[i].name);
  677 }
  678 
  679 #ifdef HAVE_DHCP6
  680 void display_opts6(void)
  681 {
  682   int i;
  683   printf(_("Known DHCPv6 options:\n"));
  684   
  685   for (i = 0; opttab6[i].name; i++)
  686     if (!(opttab6[i].size & OT_INTERNAL))
  687       printf("%3d %s\n", opttab6[i].val, opttab6[i].name);
  688 }
  689 #endif
  690 
  691 int lookup_dhcp_opt(int prot, char *name)
  692 {
  693   const struct opttab_t *t;
  694   int i;
  695 
  696   (void)prot;
  697 
  698 #ifdef HAVE_DHCP6
  699   if (prot == AF_INET6)
  700     t = opttab6;
  701   else
  702 #endif
  703     t = opttab;
  704 
  705   for (i = 0; t[i].name; i++)
  706     if (strcasecmp(t[i].name, name) == 0)
  707       return t[i].val;
  708   
  709   return -1;
  710 }
  711 
  712 int lookup_dhcp_len(int prot, int val)
  713 {
  714   const struct opttab_t *t;
  715   int i;
  716 
  717   (void)prot;
  718 
  719 #ifdef HAVE_DHCP6
  720   if (prot == AF_INET6)
  721     t = opttab6;
  722   else
  723 #endif
  724     t = opttab;
  725 
  726   for (i = 0; t[i].name; i++)
  727     if (val == t[i].val)
  728       return t[i].size & ~OT_DEC;
  729 
  730    return 0;
  731 }
  732 
  733 char *option_string(int prot, unsigned int opt, unsigned char *val, int opt_len, char *buf, int buf_len)
  734 {
  735   int o, i, j, nodecode = 0;
  736   const struct opttab_t *ot = opttab;
  737 
  738 #ifdef HAVE_DHCP6
  739   if (prot == AF_INET6)
  740     ot = opttab6;
  741 #endif
  742 
  743   for (o = 0; ot[o].name; o++)
  744     if (ot[o].val == opt)
  745       {
  746     if (buf)
  747       {
  748         memset(buf, 0, buf_len);
  749         
  750         if (ot[o].size & OT_ADDR_LIST) 
  751           {
  752         union all_addr addr;
  753         int addr_len = INADDRSZ;
  754 
  755 #ifdef HAVE_DHCP6
  756         if (prot == AF_INET6)
  757           addr_len = IN6ADDRSZ;
  758 #endif
  759         for (buf[0]= 0, i = 0; i <= opt_len - addr_len; i += addr_len) 
  760           {
  761             if (i != 0)
  762               strncat(buf, ", ", buf_len - strlen(buf));
  763             /* align */
  764             memcpy(&addr, &val[i], addr_len); 
  765             inet_ntop(prot, &val[i], daemon->addrbuff, ADDRSTRLEN);
  766             strncat(buf, daemon->addrbuff, buf_len - strlen(buf));
  767           }
  768           }
  769         else if (ot[o].size & OT_NAME)
  770         for (i = 0, j = 0; i < opt_len && j < buf_len ; i++)
  771           {
  772             char c = val[i];
  773             if (isprint((int)c))
  774               buf[j++] = c;
  775           }
  776 #ifdef HAVE_DHCP6
  777         /* We don't handle compressed rfc1035 names, so no good in IPv4 land */
  778         else if ((ot[o].size & OT_RFC1035_NAME) && prot == AF_INET6)
  779           {
  780         i = 0, j = 0;
  781         while (i < opt_len && val[i] != 0)
  782           {
  783             int k, l = i + val[i] + 1;
  784             for (k = i + 1; k < opt_len && k < l && j < buf_len ; k++)
  785              {
  786                char c = val[k];
  787                if (isprint((int)c))
  788              buf[j++] = c;
  789              }
  790             i = l;
  791             if (val[i] != 0 && j < buf_len)
  792               buf[j++] = '.';
  793           }
  794           }
  795         else if ((ot[o].size & OT_CSTRING))
  796           {
  797         int k, len;
  798         unsigned char *p;
  799 
  800         i = 0, j = 0;
  801         while (1)
  802           {
  803             p = &val[i];
  804             GETSHORT(len, p);
  805             for (k = 0; k < len && j < buf_len; k++)
  806               {
  807                char c = *p++;
  808                if (isprint((int)c))
  809              buf[j++] = c;
  810              }
  811             i += len +2;
  812             if (i >= opt_len)
  813               break;
  814 
  815             if (j < buf_len)
  816               buf[j++] = ',';
  817           }
  818           }       
  819 #endif
  820         else if ((ot[o].size & (OT_DEC | OT_TIME)) && opt_len != 0)
  821           {
  822         unsigned int dec = 0;
  823         
  824         for (i = 0; i < opt_len; i++)
  825           dec = (dec << 8) | val[i]; 
  826 
  827         if (ot[o].size & OT_TIME)
  828           prettyprint_time(buf, dec);
  829         else
  830           sprintf(buf, "%u", dec);
  831           }
  832         else
  833           nodecode = 1;
  834       }
  835     break;
  836       }
  837 
  838   if (opt_len != 0 && buf && (!ot[o].name || nodecode))
  839     {
  840       int trunc  = 0;
  841       if (opt_len > 14)
  842     {
  843       trunc = 1;
  844       opt_len = 14;
  845     }
  846       print_mac(buf, val, opt_len);
  847       if (trunc)
  848     strncat(buf, "...", buf_len - strlen(buf));
  849     
  850 
  851     }
  852 
  853   return ot[o].name ? ot[o].name : "";
  854 
  855 }
  856 
  857 void log_context(int family, struct dhcp_context *context)
  858 {
  859   /* Cannot use dhcp_buff* for RA contexts */
  860 
  861   void *start = &context->start;
  862   void *end = &context->end;
  863   char *template = "", *p = daemon->namebuff;
  864   
  865   *p = 0;
  866     
  867 #ifdef HAVE_DHCP6
  868   if (family == AF_INET6)
  869     {
  870       struct in6_addr subnet = context->start6;
  871       if (!(context->flags & CONTEXT_TEMPLATE))
  872     setaddr6part(&subnet, 0);
  873       inet_ntop(AF_INET6, &subnet, daemon->addrbuff, ADDRSTRLEN); 
  874       start = &context->start6;
  875       end = &context->end6;
  876     }
  877 #endif
  878 
  879   if (family != AF_INET && (context->flags & CONTEXT_DEPRECATE))
  880     strcpy(daemon->namebuff, _(", prefix deprecated"));
  881   else
  882     {
  883       p += sprintf(p, _(", lease time "));
  884       prettyprint_time(p, context->lease_time);
  885       p += strlen(p);
  886     }   
  887 
  888 #ifdef HAVE_DHCP6
  889   if (context->flags & CONTEXT_CONSTRUCTED)
  890     {
  891       char ifrn_name[IFNAMSIZ];
  892       
  893       template = p;
  894       p += sprintf(p, ", ");
  895       
  896       if (indextoname(daemon->icmp6fd, context->if_index, ifrn_name))
  897     sprintf(p, "%s for %s", (context->flags & CONTEXT_OLD) ? "old prefix" : "constructed", ifrn_name);
  898     }
  899   else if (context->flags & CONTEXT_TEMPLATE && !(context->flags & CONTEXT_RA_STATELESS))
  900     {
  901       template = p;
  902       p += sprintf(p, ", ");
  903       
  904       sprintf(p, "template for %s", context->template_interface);  
  905     }
  906 #endif
  907      
  908   if (!(context->flags & CONTEXT_OLD) &&
  909       ((context->flags & CONTEXT_DHCP) || family == AF_INET)) 
  910     {
  911 #ifdef HAVE_DHCP6
  912       if (context->flags & CONTEXT_RA_STATELESS)
  913     {
  914       if (context->flags & CONTEXT_TEMPLATE)
  915         strncpy(daemon->dhcp_buff, context->template_interface, DHCP_BUFF_SZ);
  916       else
  917         strcpy(daemon->dhcp_buff, daemon->addrbuff);
  918     }
  919       else 
  920 #endif
  921     inet_ntop(family, start, daemon->dhcp_buff, DHCP_BUFF_SZ);
  922       inet_ntop(family, end, daemon->dhcp_buff3, DHCP_BUFF_SZ);
  923       my_syslog(MS_DHCP | LOG_INFO, 
  924         (context->flags & CONTEXT_RA_STATELESS) ? 
  925         _("%s stateless on %s%.0s%.0s%s") :
  926         (context->flags & CONTEXT_STATIC) ? 
  927         _("%s, static leases only on %.0s%s%s%.0s") :
  928         (context->flags & CONTEXT_PROXY) ?
  929         _("%s, proxy on subnet %.0s%s%.0s%.0s") :
  930         _("%s, IP range %s -- %s%s%.0s"),
  931         (family != AF_INET) ? "DHCPv6" : "DHCP",
  932         daemon->dhcp_buff, daemon->dhcp_buff3, daemon->namebuff, template);
  933     }
  934   
  935 #ifdef HAVE_DHCP6
  936   if (context->flags & CONTEXT_TEMPLATE)
  937     {
  938       strcpy(daemon->addrbuff, context->template_interface);
  939       template = "";
  940     }
  941 
  942   if ((context->flags & CONTEXT_RA_NAME) && !(context->flags & CONTEXT_OLD))
  943     my_syslog(MS_DHCP | LOG_INFO, _("DHCPv4-derived IPv6 names on %s%s"), daemon->addrbuff, template);
  944   
  945   if ((context->flags & CONTEXT_RA) || (option_bool(OPT_RA) && (context->flags & CONTEXT_DHCP) && family == AF_INET6)) 
  946     my_syslog(MS_DHCP | LOG_INFO, _("router advertisement on %s%s"), daemon->addrbuff, template);
  947 #endif
  948 
  949 }
  950 
  951 void log_relay(int family, struct dhcp_relay *relay)
  952 {
  953   inet_ntop(family, &relay->local, daemon->addrbuff, ADDRSTRLEN);
  954   inet_ntop(family, &relay->server, daemon->namebuff, ADDRSTRLEN); 
  955 
  956   if (relay->interface)
  957     my_syslog(MS_DHCP | LOG_INFO, _("DHCP relay from %s to %s via %s"), daemon->addrbuff, daemon->namebuff, relay->interface);
  958   else
  959     my_syslog(MS_DHCP | LOG_INFO, _("DHCP relay from %s to %s"), daemon->addrbuff, daemon->namebuff);
  960 }
  961    
  962 #endif