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