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