]> git.ipfire.org Git - people/ms/dnsmasq.git/blame - src/dhcp-common.c
Move DHCP option stuff to dhcp-common.c
[people/ms/dnsmasq.git] / src / dhcp-common.c
CommitLineData
4cb1b320
SK
1/* dnsmasq is Copyright (c) 2000-2012 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 */
c5ad4e79 56 if ((size_t)sz == msg->msg_iov->iov_len)
4cb1b320 57 {
c5ad4e79 58 if (!expand_buf(msg->msg_iov, sz + 100))
4cb1b320
SK
59 return -1;
60 }
61 else
62 {
c5ad4e79 63 expand_buf(msg->msg_iov, sz);
4cb1b320
SK
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
95 /* flag options which are valid with the current tag set (sans context tags) */
96 for (opt = opts; opt; opt = opt->next)
97 {
98 opt->flags &= ~DHOPT_TAGOK;
99 if (!(opt->flags & (DHOPT_ENCAPSULATE | DHOPT_VENDOR | DHOPT_RFC3925)) &&
100 match_netid(opt->netid, tagif, 0))
101 opt->flags |= DHOPT_TAGOK;
102 }
103
104 /* now flag options which are valid, including the context tags,
6caacacf 105 otherwise valid options are inhibited if we found a higher priority one above */
4cb1b320
SK
106 if (context_tags)
107 {
108 struct dhcp_netid *last_tag;
109
110 for (last_tag = context_tags; last_tag->next; last_tag = last_tag->next);
111 last_tag->next = tags;
112 tagif = run_tag_if(context_tags);
113
114 for (opt = opts; opt; opt = opt->next)
115 if (!(opt->flags & (DHOPT_ENCAPSULATE | DHOPT_VENDOR | DHOPT_RFC3925 | DHOPT_TAGOK)) &&
116 match_netid(opt->netid, tagif, 0))
117 {
118 struct dhcp_opt *tmp;
119 for (tmp = opts; tmp; tmp = tmp->next)
120 if (tmp->opt == opt->opt && opt->netid && (tmp->flags & DHOPT_TAGOK))
121 break;
122 if (!tmp)
123 opt->flags |= DHOPT_TAGOK;
124 }
125 }
126
127 /* now flag untagged options which are not overridden by tagged ones */
128 for (opt = opts; opt; opt = opt->next)
129 if (!(opt->flags & (DHOPT_ENCAPSULATE | DHOPT_VENDOR | DHOPT_RFC3925 | DHOPT_TAGOK)) && !opt->netid)
130 {
131 struct dhcp_opt *tmp;
132 for (tmp = opts; tmp; tmp = tmp->next)
133 if (tmp->opt == opt->opt && (tmp->flags & DHOPT_TAGOK))
134 break;
135 if (!tmp)
136 opt->flags |= DHOPT_TAGOK;
137 else if (!tmp->netid)
138 my_syslog(MS_DHCP | LOG_WARNING, _("Ignoring duplicate dhcp-option %d"), tmp->opt);
139 }
140
141 return tagif;
142}
143
144/* Is every member of check matched by a member of pool?
145 If tagnotneeded, untagged is OK */
146int match_netid(struct dhcp_netid *check, struct dhcp_netid *pool, int tagnotneeded)
147{
148 struct dhcp_netid *tmp1;
149
150 if (!check && !tagnotneeded)
151 return 0;
152
153 for (; check; check = check->next)
154 {
155 /* '#' for not is for backwards compat. */
156 if (check->net[0] != '!' && check->net[0] != '#')
157 {
158 for (tmp1 = pool; tmp1; tmp1 = tmp1->next)
159 if (strcmp(check->net, tmp1->net) == 0)
160 break;
161 if (!tmp1)
162 return 0;
163 }
164 else
165 for (tmp1 = pool; tmp1; tmp1 = tmp1->next)
166 if (strcmp((check->net)+1, tmp1->net) == 0)
167 return 0;
168 }
169 return 1;
170}
171
172/* return domain or NULL if none. */
173char *strip_hostname(char *hostname)
174{
175 char *dot = strchr(hostname, '.');
176
177 if (!dot)
178 return NULL;
179
180 *dot = 0; /* truncate */
181 if (strlen(dot+1) != 0)
182 return dot+1;
183
184 return NULL;
185}
186
187void log_tags(struct dhcp_netid *netid, u32 xid)
188{
189 if (netid && option_bool(OPT_LOG_OPTS))
190 {
191 char *s = daemon->namebuff;
192 for (*s = 0; netid; netid = netid->next)
193 {
194 /* kill dupes. */
195 struct dhcp_netid *n;
196
197 for (n = netid->next; n; n = n->next)
198 if (strcmp(netid->net, n->net) == 0)
199 break;
200
201 if (!n)
202 {
203 strncat (s, netid->net, (MAXDNAME-1) - strlen(s));
204 if (netid->next)
205 strncat (s, ", ", (MAXDNAME-1) - strlen(s));
206 }
207 }
208 my_syslog(MS_DHCP | LOG_INFO, _("%u tags: %s"), xid, s);
209 }
210}
211
3634c54e
SK
212int match_bytes(struct dhcp_opt *o, unsigned char *p, int len)
213{
214 int i;
215
216 if (o->len > len)
217 return 0;
218
219 if (o->len == 0)
220 return 1;
221
222 if (o->flags & DHOPT_HEX)
223 {
224 if (memcmp_masked(o->val, p, o->len, o->u.wildcard_mask))
225 return 1;
226 }
227 else
228 for (i = 0; i <= (len - o->len); )
229 {
230 if (memcmp(o->val, p + i, o->len) == 0)
231 return 1;
232
233 if (o->flags & DHOPT_STRING)
234 i++;
235 else
236 i += o->len;
237 }
238
239 return 0;
240}
ceae00dd
SK
241
242void check_dhcp_hosts(int fatal)
243{
244 /* If the same IP appears in more than one host config, then DISCOVER
245 for one of the hosts will get the address, but REQUEST will be NAKed,
246 since the address is reserved by the other one -> protocol loop.
247 Also check that FQDNs match the domain we are using. */
248
249 struct dhcp_config *configs, *cp;
250
251 for (configs = daemon->dhcp_conf; configs; configs = configs->next)
252 {
253 char *domain;
254
255 if ((configs->flags & DHOPT_BANK) || fatal)
256 {
257 for (cp = configs->next; cp; cp = cp->next)
258 if ((configs->flags & cp->flags & CONFIG_ADDR) && configs->addr.s_addr == cp->addr.s_addr)
259 {
260 if (fatal)
261 die(_("duplicate IP address %s in dhcp-config directive."),
262 inet_ntoa(cp->addr), EC_BADCONF);
263 else
264 my_syslog(MS_DHCP | LOG_ERR, _("duplicate IP address %s in %s."),
265 inet_ntoa(cp->addr), daemon->dhcp_hosts_file);
266 configs->flags &= ~CONFIG_ADDR;
267 }
268
269 /* split off domain part */
270 if ((configs->flags & CONFIG_NAME) && (domain = strip_hostname(configs->hostname)))
271 configs->domain = domain;
272 }
273 }
274}
275
276void dhcp_update_configs(struct dhcp_config *configs)
277{
278 /* Some people like to keep all static IP addresses in /etc/hosts.
279 This goes through /etc/hosts and sets static addresses for any DHCP config
280 records which don't have an address and whose name matches.
281 We take care to maintain the invariant that any IP address can appear
282 in at most one dhcp-host. Since /etc/hosts can be re-read by SIGHUP,
283 restore the status-quo ante first. */
284
285 struct dhcp_config *config;
286 struct crec *crec;
287 int prot = AF_INET;
288
289 for (config = configs; config; config = config->next)
290 if (config->flags & CONFIG_ADDR_HOSTS)
291 config->flags &= ~(CONFIG_ADDR | CONFIG_ADDR6 | CONFIG_ADDR_HOSTS);
292
293#ifdef HAVE_DHCP6
294 again:
295#endif
296
297 if (daemon->port != 0)
298 for (config = configs; config; config = config->next)
299 {
300 int conflags = CONFIG_ADDR;
301 int cacheflags = F_IPV4;
302
303#ifdef HAVE_DHCP6
304 if (prot == AF_INET6)
305 {
306 conflags = CONFIG_ADDR6;
307 cacheflags = F_IPV6;
308 }
309#endif
310 if (!(config->flags & conflags) &&
311 (config->flags & CONFIG_NAME) &&
312 (crec = cache_find_by_name(NULL, config->hostname, 0, cacheflags)) &&
313 (crec->flags & F_HOSTS))
314 {
315 if (cache_find_by_name(crec, config->hostname, 0, cacheflags))
316 {
317 /* use primary (first) address */
318 while (crec && !(crec->flags & F_REVERSE))
319 crec = cache_find_by_name(crec, config->hostname, 0, cacheflags);
320 if (!crec)
321 continue; /* should be never */
322 inet_ntop(prot, &crec->addr.addr, daemon->addrbuff, ADDRSTRLEN);
323 my_syslog(MS_DHCP | LOG_WARNING, _("%s has more than one address in hostsfile, using %s for DHCP"),
324 config->hostname, daemon->addrbuff);
325 }
326
327 if (prot == AF_INET && !config_find_by_address(configs, crec->addr.addr.addr.addr4))
328 {
329 config->addr = crec->addr.addr.addr.addr4;
330 config->flags |= CONFIG_ADDR | CONFIG_ADDR_HOSTS;
331 continue;
332 }
333
334#ifdef HAVE_DHCP6
e44ddcac 335 if (prot == AF_INET6 && !config_find_by_address6(configs, &crec->addr.addr.addr.addr6, 128, 0))
ceae00dd 336 {
e44ddcac 337 memcpy(&config->addr6, &crec->addr.addr.addr.addr6, IN6ADDRSZ);
ceae00dd
SK
338 config->flags |= CONFIG_ADDR6 | CONFIG_ADDR_HOSTS;
339 continue;
340 }
341#endif
342
343 inet_ntop(prot, &crec->addr.addr, daemon->addrbuff, ADDRSTRLEN);
344 my_syslog(MS_DHCP | LOG_WARNING, _("duplicate IP address %s (%s) in dhcp-config directive"),
345 daemon->addrbuff, config->hostname);
346
347
348 }
349 }
350
351#ifdef HAVE_DHCP6
352 if (prot == AF_INET)
353 {
354 prot = AF_INET6;
355 goto again;
356 }
357#endif
358
359}
4cb1b320 360
843c96b4
SK
361#ifdef HAVE_DHCP6
362static int join_multicast_worker(struct in6_addr *local, int prefix,
363 int scope, int if_index, int dad, void *vparam)
364{
365 char ifrn_name[IFNAMSIZ];
366 struct ipv6_mreq mreq;
367 int fd, i, max = *((int *)vparam);
368 struct dhcp_context *context;
369 struct iname *tmp;
370
371 (void)prefix;
372 (void)scope;
373 (void)dad;
374
375 /* record which interfaces we join on, so that we do it at most one per
376 interface, even when they have multiple addresses. Use outpacket
377 as an array of int, since it's always allocated here and easy
378 to expand for theoretical vast numbers of interfaces. */
379 for (i = 0; i < max; i++)
380 if (if_index == ((int *)daemon->outpacket.iov_base)[i])
381 return 1;
382
383 if ((fd = socket(PF_INET6, SOCK_DGRAM, 0)) == -1)
384 return 0;
385
386 if (!indextoname(fd, if_index, ifrn_name))
387 {
388 close(fd);
389 return 0;
390 }
391
392 close(fd);
393
394 /* Are we doing DHCP on this interface? */
395 if (!iface_check(AF_INET6, (struct all_addr *)local, ifrn_name))
396 return 1;
397
398 for (tmp = daemon->dhcp_except; tmp; tmp = tmp->next)
399 if (tmp->name && (strcmp(tmp->name, ifrn_name) == 0))
400 return 1;
401
402 /* weird libvirt-inspired access control */
403 for (context = daemon->dhcp6; context; context = context->next)
404 if (!context->interface || strcmp(context->interface, ifrn_name) == 0)
405 break;
406
407 if (!context)
408 return 1;
409
410 mreq.ipv6mr_interface = if_index;
411
412 inet_pton(AF_INET6, ALL_RELAY_AGENTS_AND_SERVERS, &mreq.ipv6mr_multiaddr);
413
414 if (daemon->dhcp6 &&
415 setsockopt(daemon->dhcp6fd, IPPROTO_IPV6, IPV6_JOIN_GROUP, &mreq, sizeof(mreq)) == -1)
416 return 0;
417
418 inet_pton(AF_INET6, ALL_SERVERS, &mreq.ipv6mr_multiaddr);
419
420 if (daemon->dhcp6 &&
421 setsockopt(daemon->dhcp6fd, IPPROTO_IPV6, IPV6_JOIN_GROUP, &mreq, sizeof(mreq)) == -1)
422 return 0;
423
424 inet_pton(AF_INET6, ALL_ROUTERS, &mreq.ipv6mr_multiaddr);
425
426 if (daemon->ra_contexts &&
427 setsockopt(daemon->icmp6fd, IPPROTO_IPV6, IPV6_JOIN_GROUP, &mreq, sizeof(mreq)) == -1)
428 return 0;
429
430 expand_buf(&daemon->outpacket, (max+1) * sizeof(int));
431 ((int *)daemon->outpacket.iov_base)[max++] = if_index;
432
433 *((int *)vparam) = max;
434
435 return 1;
436}
437
438void join_multicast(void)
439{
440 int count = 0;
441
442 if (!iface_enumerate(AF_INET6, &count, join_multicast_worker))
443 die(_("failed to join DHCPv6 multicast group: %s"), NULL, EC_BADNET);
444}
445#endif
446
40ef23b5
SK
447
448static const struct opttab_t {
449 char *name;
450 u16 val, size;
451} opttab[] = {
452 { "netmask", 1, OT_ADDR_LIST },
453 { "time-offset", 2, 4 },
454 { "router", 3, OT_ADDR_LIST },
455 { "dns-server", 6, OT_ADDR_LIST },
456 { "log-server", 7, OT_ADDR_LIST },
457 { "lpr-server", 9, OT_ADDR_LIST },
458 { "hostname", 12, OT_INTERNAL | OT_NAME },
459 { "boot-file-size", 13, 2 | OT_DEC },
460 { "domain-name", 15, OT_NAME },
461 { "swap-server", 16, OT_ADDR_LIST },
462 { "root-path", 17, OT_NAME },
463 { "extension-path", 18, OT_NAME },
464 { "ip-forward-enable", 19, 1 },
465 { "non-local-source-routing", 20, 1 },
466 { "policy-filter", 21, OT_ADDR_LIST },
467 { "max-datagram-reassembly", 22, 2 | OT_DEC },
468 { "default-ttl", 23, 1 | OT_DEC },
469 { "mtu", 26, 2 | OT_DEC },
470 { "all-subnets-local", 27, 1 },
471 { "broadcast", 28, OT_INTERNAL | OT_ADDR_LIST },
472 { "router-discovery", 31, 1 },
473 { "router-solicitation", 32, OT_ADDR_LIST },
474 { "static-route", 33, OT_ADDR_LIST },
475 { "trailer-encapsulation", 34, 1 },
476 { "arp-timeout", 35, 4 | OT_DEC },
477 { "ethernet-encap", 36, 1 },
478 { "tcp-ttl", 37, 1 },
479 { "tcp-keepalive", 38, 4 | OT_DEC },
480 { "nis-domain", 40, OT_NAME },
481 { "nis-server", 41, OT_ADDR_LIST },
482 { "ntp-server", 42, OT_ADDR_LIST },
483 { "vendor-encap", 43, OT_INTERNAL },
484 { "netbios-ns", 44, OT_ADDR_LIST },
485 { "netbios-dd", 45, OT_ADDR_LIST },
486 { "netbios-nodetype", 46, 1 },
487 { "netbios-scope", 47, 0 },
488 { "x-windows-fs", 48, OT_ADDR_LIST },
489 { "x-windows-dm", 49, OT_ADDR_LIST },
490 { "requested-address", 50, OT_INTERNAL | OT_ADDR_LIST },
491 { "lease-time", 51, OT_INTERNAL | OT_DEC },
492 { "option-overload", 52, OT_INTERNAL },
493 { "message-type", 53, OT_INTERNAL | OT_DEC },
494 { "server-identifier", 54, OT_INTERNAL | OT_ADDR_LIST },
495 { "parameter-request", 55, OT_INTERNAL },
496 { "message", 56, OT_INTERNAL },
497 { "max-message-size", 57, OT_INTERNAL },
498 { "T1", 58, OT_INTERNAL | OT_DEC},
499 { "T2", 59, OT_INTERNAL | OT_DEC},
500 { "vendor-class", 60, 0 },
501 { "client-id", 61, OT_INTERNAL },
502 { "nis+-domain", 64, OT_NAME },
503 { "nis+-server", 65, OT_ADDR_LIST },
504 { "tftp-server", 66, OT_NAME },
505 { "bootfile-name", 67, OT_NAME },
506 { "mobile-ip-home", 68, OT_ADDR_LIST },
507 { "smtp-server", 69, OT_ADDR_LIST },
508 { "pop3-server", 70, OT_ADDR_LIST },
509 { "nntp-server", 71, OT_ADDR_LIST },
510 { "irc-server", 74, OT_ADDR_LIST },
511 { "user-class", 77, 0 },
512 { "FQDN", 81, OT_INTERNAL },
513 { "agent-id", 82, OT_INTERNAL },
514 { "client-arch", 93, 2 | OT_DEC },
515 { "client-interface-id", 94, 0 },
516 { "client-machine-id", 97, 0 },
517 { "subnet-select", 118, OT_INTERNAL },
518 { "domain-search", 119, OT_RFC1035_NAME },
519 { "sip-server", 120, 0 },
520 { "classless-static-route", 121, 0 },
521 { "vendor-id-encap", 125, 0 },
522 { "server-ip-address", 255, OT_ADDR_LIST }, /* special, internal only, sets siaddr */
523 { NULL, 0, 0 }
524};
525
526#ifdef HAVE_DHCP6
527static const struct opttab_t opttab6[] = {
528 { "client-id", 1, OT_INTERNAL },
529 { "server-id", 2, OT_INTERNAL },
530 { "ia-na", 3, OT_INTERNAL },
531 { "ia-ta", 4, OT_INTERNAL },
532 { "iaaddr", 5, OT_INTERNAL },
533 { "oro", 6, OT_INTERNAL },
534 { "preference", 7, OT_INTERNAL | OT_DEC },
535 { "unicast", 12, OT_INTERNAL },
536 { "status", 13, OT_INTERNAL },
537 { "rapid-commit", 14, OT_INTERNAL },
538 { "user-class", 15, OT_INTERNAL | OT_CSTRING },
539 { "vendor-class", 16, OT_INTERNAL | OT_CSTRING },
540 { "vendor-opts", 17, OT_INTERNAL },
541 { "sip-server-domain", 21, OT_RFC1035_NAME },
542 { "sip-server", 22, OT_ADDR_LIST },
543 { "dns-server", 23, OT_ADDR_LIST },
544 { "domain-search", 24, OT_RFC1035_NAME },
545 { "nis-server", 27, OT_ADDR_LIST },
546 { "nis+-server", 28, OT_ADDR_LIST },
547 { "nis-domain", 29, OT_RFC1035_NAME },
548 { "nis+-domain", 30, OT_RFC1035_NAME },
549 { "sntp-server", 31, OT_ADDR_LIST },
550 { "FQDN", 39, OT_INTERNAL | OT_RFC1035_NAME },
551 { "ntp-server", 56, OT_ADDR_LIST },
552 { "bootfile-url", 59, OT_NAME },
553 { "bootfile-param", 60, OT_CSTRING },
554 { NULL, 0, 0 }
555};
556#endif
557
558
559
560void display_opts(void)
561{
562 int i;
563
564 printf(_("Known DHCP options:\n"));
565
566 for (i = 0; opttab[i].name; i++)
567 if (!(opttab[i].size & OT_INTERNAL))
568 printf("%3d %s\n", opttab[i].val, opttab[i].name);
569}
570
571#ifdef HAVE_DHCP6
572void display_opts6(void)
573{
574 int i;
575 printf(_("Known DHCPv6 options:\n"));
576
577 for (i = 0; opttab6[i].name; i++)
578 if (!(opttab6[i].size & OT_INTERNAL))
579 printf("%3d %s\n", opttab6[i].val, opttab6[i].name);
580}
581#endif
582
583u16 lookup_dhcp_opt(int prot, char *name)
584{
585 const struct opttab_t *t;
586 int i;
587
588#ifdef HAVE_DHCP6
589 if (prot == AF_INET6)
590 t = opttab6;
591 else
592#endif
593 t = opttab;
594
595 for (i = 0; t[i].name; i++)
596 if (!(t[i].size & OT_INTERNAL) &&
597 strcasecmp(t[i].name, name) == 0)
598 return t[i].val;
599
600 return 0;
601}
602
603u16 lookup_dhcp_len(int prot, u16 val)
604{
605 const struct opttab_t *t;
606 int i;
607
608#ifdef HAVE_DHCP6
609 if (prot == AF_INET6)
610 t = opttab6;
611 else
612#endif
613 t = opttab;
614
615 for (i = 0; t[i].name; i++)
616 if (val == t[i].val)
617 {
618 if (t[i].size & OT_INTERNAL)
619 return 0;
620
621 return t[i].size & ~OT_DEC;
622 }
623
624 return 0;
625}
626
627char *option_string(int prot, unsigned int opt, unsigned char *val, int opt_len, char *buf, int buf_len)
628{
629 int o, i, j, nodecode = 0;
630 const struct opttab_t *ot = opttab;
631
632#ifdef HAVE_DHCP6
633 if (prot == AF_INET6)
634 ot = opttab6;
635#endif
636
637 for (o = 0; ot[o].name; o++)
638 if (ot[o].val == opt)
639 {
640 if (buf)
641 {
642 memset(buf, 0, buf_len);
643
644 if (ot[o].size & OT_ADDR_LIST)
645 {
646 struct all_addr addr;
647 int addr_len = INADDRSZ;
648
649#ifdef HAVE_DHCP6
650 if (prot == AF_INET6)
651 addr_len = IN6ADDRSZ;
652#endif
653 for (buf[0]= 0, i = 0; i <= opt_len - addr_len; i += addr_len)
654 {
655 if (i != 0)
656 strncat(buf, ", ", buf_len - strlen(buf));
657 /* align */
658 memcpy(&addr, &val[i], addr_len);
659 inet_ntop(prot, &val[i], daemon->addrbuff, ADDRSTRLEN);
660 strncat(buf, daemon->addrbuff, buf_len - strlen(buf));
661 }
662 }
663 else if (ot[o].size & OT_NAME)
664 for (i = 0, j = 0; i < opt_len && j < buf_len ; i++)
665 {
666 char c = val[i];
667 if (isprint((int)c))
668 buf[j++] = c;
669 }
670#ifdef HAVE_DHCP6
671 /* We don't handle compressed rfc1035 names, so no good in IPv4 land */
672 else if ((ot[o].size & OT_RFC1035_NAME) && prot == AF_INET6)
673 {
674 i = 0, j = 0;
675 while (i < opt_len && val[i] != 0)
676 {
677 int k, l = i + val[i] + 1;
678 for (k = i + 1; k < opt_len && k < l && j < buf_len ; k++)
679 {
680 char c = val[k];
681 if (isprint((int)c))
682 buf[j++] = c;
683 }
684 i = l;
685 if (val[i] != 0 && j < buf_len)
686 buf[j++] = '.';
687 }
688 }
689 else if ((ot[o].size & OT_CSTRING))
690 {
691 int k, len;
692 unsigned char *p;
693
694 i = 0, j = 0;
695 while (1)
696 {
697 p = &val[i];
698 GETSHORT(len, p);
699 for (k = 0; k < len && j < buf_len; k++)
700 {
701 char c = *p++;
702 if (isprint((int)c))
703 buf[j++] = c;
704 }
705 i += len +2;
706 if (i >= opt_len)
707 break;
708
709 if (j < buf_len)
710 buf[j++] = ',';
711 }
712 }
713#endif
714 else if ((ot[o].size & OT_DEC) && opt_len != 0)
715 {
716 unsigned int dec = 0;
717
718 for (i = 0; i < opt_len; i++)
719 dec = (dec << 8) | val[i];
720
721 sprintf(buf, "%u", dec);
722 }
723 else
724 nodecode = 1;
725 }
726 break;
727 }
728
729 if (opt_len != 0 && buf && (!ot[o].name || nodecode))
730 {
731 int trunc = 0;
732 if (opt_len > 14)
733 {
734 trunc = 1;
735 opt_len = 14;
736 }
737 print_mac(buf, val, opt_len);
738 if (trunc)
739 strncat(buf, "...", buf_len - strlen(buf));
740
741
742 }
743
744 return ot[o].name ? ot[o].name : "";
745
746}
747
4cb1b320 748#endif