blob: da982aa680cec6d627aa51d7081c4d410eba520c [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
Simon Kelleyceae00d2012-02-09 21:28:14 +0000249void dhcp_update_configs(struct dhcp_config *configs)
250{
251 /* Some people like to keep all static IP addresses in /etc/hosts.
252 This goes through /etc/hosts and sets static addresses for any DHCP config
253 records which don't have an address and whose name matches.
254 We take care to maintain the invariant that any IP address can appear
255 in at most one dhcp-host. Since /etc/hosts can be re-read by SIGHUP,
256 restore the status-quo ante first. */
257
258 struct dhcp_config *config;
259 struct crec *crec;
260 int prot = AF_INET;
261
262 for (config = configs; config; config = config->next)
263 if (config->flags & CONFIG_ADDR_HOSTS)
264 config->flags &= ~(CONFIG_ADDR | CONFIG_ADDR6 | CONFIG_ADDR_HOSTS);
265
266#ifdef HAVE_DHCP6
267 again:
268#endif
269
270 if (daemon->port != 0)
271 for (config = configs; config; config = config->next)
272 {
273 int conflags = CONFIG_ADDR;
274 int cacheflags = F_IPV4;
275
276#ifdef HAVE_DHCP6
277 if (prot == AF_INET6)
278 {
279 conflags = CONFIG_ADDR6;
280 cacheflags = F_IPV6;
281 }
282#endif
283 if (!(config->flags & conflags) &&
284 (config->flags & CONFIG_NAME) &&
285 (crec = cache_find_by_name(NULL, config->hostname, 0, cacheflags)) &&
286 (crec->flags & F_HOSTS))
287 {
288 if (cache_find_by_name(crec, config->hostname, 0, cacheflags))
289 {
290 /* use primary (first) address */
291 while (crec && !(crec->flags & F_REVERSE))
292 crec = cache_find_by_name(crec, config->hostname, 0, cacheflags);
293 if (!crec)
294 continue; /* should be never */
295 inet_ntop(prot, &crec->addr.addr, daemon->addrbuff, ADDRSTRLEN);
296 my_syslog(MS_DHCP | LOG_WARNING, _("%s has more than one address in hostsfile, using %s for DHCP"),
297 config->hostname, daemon->addrbuff);
298 }
299
300 if (prot == AF_INET && !config_find_by_address(configs, crec->addr.addr.addr.addr4))
301 {
302 config->addr = crec->addr.addr.addr.addr4;
303 config->flags |= CONFIG_ADDR | CONFIG_ADDR_HOSTS;
304 continue;
305 }
306
307#ifdef HAVE_DHCP6
Simon Kelleye44ddca2012-02-18 17:08:50 +0000308 if (prot == AF_INET6 && !config_find_by_address6(configs, &crec->addr.addr.addr.addr6, 128, 0))
Simon Kelleyceae00d2012-02-09 21:28:14 +0000309 {
Simon Kelleye44ddca2012-02-18 17:08:50 +0000310 memcpy(&config->addr6, &crec->addr.addr.addr.addr6, IN6ADDRSZ);
Simon Kelleyceae00d2012-02-09 21:28:14 +0000311 config->flags |= CONFIG_ADDR6 | CONFIG_ADDR_HOSTS;
312 continue;
313 }
314#endif
315
316 inet_ntop(prot, &crec->addr.addr, daemon->addrbuff, ADDRSTRLEN);
317 my_syslog(MS_DHCP | LOG_WARNING, _("duplicate IP address %s (%s) in dhcp-config directive"),
318 daemon->addrbuff, config->hostname);
319
320
321 }
322 }
323
324#ifdef HAVE_DHCP6
325 if (prot == AF_INET)
326 {
327 prot = AF_INET6;
328 goto again;
329 }
330#endif
331
332}
Simon Kelley4cb1b322012-02-06 14:30:41 +0000333
Simon Kelley843c96b2012-02-27 17:42:38 +0000334#ifdef HAVE_DHCP6
335static int join_multicast_worker(struct in6_addr *local, int prefix,
336 int scope, int if_index, int dad, void *vparam)
337{
338 char ifrn_name[IFNAMSIZ];
339 struct ipv6_mreq mreq;
340 int fd, i, max = *((int *)vparam);
Simon Kelley843c96b2012-02-27 17:42:38 +0000341 struct iname *tmp;
342
343 (void)prefix;
344 (void)scope;
345 (void)dad;
346
347 /* record which interfaces we join on, so that we do it at most one per
348 interface, even when they have multiple addresses. Use outpacket
349 as an array of int, since it's always allocated here and easy
350 to expand for theoretical vast numbers of interfaces. */
351 for (i = 0; i < max; i++)
352 if (if_index == ((int *)daemon->outpacket.iov_base)[i])
353 return 1;
354
355 if ((fd = socket(PF_INET6, SOCK_DGRAM, 0)) == -1)
356 return 0;
357
358 if (!indextoname(fd, if_index, ifrn_name))
359 {
360 close(fd);
361 return 0;
362 }
363
364 close(fd);
365
366 /* Are we doing DHCP on this interface? */
367 if (!iface_check(AF_INET6, (struct all_addr *)local, ifrn_name))
368 return 1;
369
370 for (tmp = daemon->dhcp_except; tmp; tmp = tmp->next)
371 if (tmp->name && (strcmp(tmp->name, ifrn_name) == 0))
372 return 1;
373
Simon Kelley843c96b2012-02-27 17:42:38 +0000374 mreq.ipv6mr_interface = if_index;
375
376 inet_pton(AF_INET6, ALL_RELAY_AGENTS_AND_SERVERS, &mreq.ipv6mr_multiaddr);
377
378 if (daemon->dhcp6 &&
379 setsockopt(daemon->dhcp6fd, IPPROTO_IPV6, IPV6_JOIN_GROUP, &mreq, sizeof(mreq)) == -1)
380 return 0;
381
382 inet_pton(AF_INET6, ALL_SERVERS, &mreq.ipv6mr_multiaddr);
383
384 if (daemon->dhcp6 &&
385 setsockopt(daemon->dhcp6fd, IPPROTO_IPV6, IPV6_JOIN_GROUP, &mreq, sizeof(mreq)) == -1)
386 return 0;
387
388 inet_pton(AF_INET6, ALL_ROUTERS, &mreq.ipv6mr_multiaddr);
389
390 if (daemon->ra_contexts &&
391 setsockopt(daemon->icmp6fd, IPPROTO_IPV6, IPV6_JOIN_GROUP, &mreq, sizeof(mreq)) == -1)
392 return 0;
393
394 expand_buf(&daemon->outpacket, (max+1) * sizeof(int));
395 ((int *)daemon->outpacket.iov_base)[max++] = if_index;
396
397 *((int *)vparam) = max;
398
399 return 1;
400}
401
402void join_multicast(void)
403{
404 int count = 0;
405
406 if (!iface_enumerate(AF_INET6, &count, join_multicast_worker))
407 die(_("failed to join DHCPv6 multicast group: %s"), NULL, EC_BADNET);
408}
409#endif
410
Simon Kelley9380ba72012-04-16 14:41:56 +0100411#ifdef HAVE_LINUX_NETWORK
412void bindtodevice(int fd)
413{
414 /* If we are doing DHCP on exactly one interface, and running linux, do SO_BINDTODEVICE
415 to that device. This is for the use case of (eg) OpenStack, which runs a new
416 dnsmasq instance for each VLAN interface it creates. Without the BINDTODEVICE,
417 individual processes don't always see the packets they should.
418 SO_BINDTODEVICE is only available Linux. */
419
420 struct irec *iface, *found;
421
422 for (found = NULL, iface = daemon->interfaces; iface; iface = iface->next)
423 if (iface->dhcp_ok)
424 {
425 if (!found)
426 found = iface;
427 else if (strcmp(found->name, iface->name) != 0)
428 {
429 /* more than one. */
430 found = NULL;
431 break;
432 }
433 }
434
435 if (found)
436 {
437 struct ifreq ifr;
438 strcpy(ifr.ifr_name, found->name);
439 /* only allowed by root. */
440 if (setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, (void *)&ifr, sizeof(ifr)) == -1 &&
441 errno != EPERM)
442 die(_("failed to set SO_BINDTODEVICE on DHCP socket: %s"), NULL, EC_BADNET);
443 }
444}
445#endif
Simon Kelley40ef23b2012-03-13 21:59:28 +0000446
447static const struct opttab_t {
448 char *name;
449 u16 val, size;
450} opttab[] = {
451 { "netmask", 1, OT_ADDR_LIST },
452 { "time-offset", 2, 4 },
453 { "router", 3, OT_ADDR_LIST },
454 { "dns-server", 6, OT_ADDR_LIST },
455 { "log-server", 7, OT_ADDR_LIST },
456 { "lpr-server", 9, OT_ADDR_LIST },
457 { "hostname", 12, OT_INTERNAL | OT_NAME },
458 { "boot-file-size", 13, 2 | OT_DEC },
459 { "domain-name", 15, OT_NAME },
460 { "swap-server", 16, OT_ADDR_LIST },
461 { "root-path", 17, OT_NAME },
462 { "extension-path", 18, OT_NAME },
463 { "ip-forward-enable", 19, 1 },
464 { "non-local-source-routing", 20, 1 },
465 { "policy-filter", 21, OT_ADDR_LIST },
466 { "max-datagram-reassembly", 22, 2 | OT_DEC },
467 { "default-ttl", 23, 1 | OT_DEC },
468 { "mtu", 26, 2 | OT_DEC },
469 { "all-subnets-local", 27, 1 },
470 { "broadcast", 28, OT_INTERNAL | OT_ADDR_LIST },
471 { "router-discovery", 31, 1 },
472 { "router-solicitation", 32, OT_ADDR_LIST },
473 { "static-route", 33, OT_ADDR_LIST },
474 { "trailer-encapsulation", 34, 1 },
475 { "arp-timeout", 35, 4 | OT_DEC },
476 { "ethernet-encap", 36, 1 },
477 { "tcp-ttl", 37, 1 },
478 { "tcp-keepalive", 38, 4 | OT_DEC },
479 { "nis-domain", 40, OT_NAME },
480 { "nis-server", 41, OT_ADDR_LIST },
481 { "ntp-server", 42, OT_ADDR_LIST },
482 { "vendor-encap", 43, OT_INTERNAL },
483 { "netbios-ns", 44, OT_ADDR_LIST },
484 { "netbios-dd", 45, OT_ADDR_LIST },
485 { "netbios-nodetype", 46, 1 },
486 { "netbios-scope", 47, 0 },
487 { "x-windows-fs", 48, OT_ADDR_LIST },
488 { "x-windows-dm", 49, OT_ADDR_LIST },
489 { "requested-address", 50, OT_INTERNAL | OT_ADDR_LIST },
Simon Kelley23245c02012-07-18 16:21:11 +0100490 { "lease-time", 51, OT_INTERNAL | OT_TIME },
Simon Kelley40ef23b2012-03-13 21:59:28 +0000491 { "option-overload", 52, OT_INTERNAL },
492 { "message-type", 53, OT_INTERNAL | OT_DEC },
493 { "server-identifier", 54, OT_INTERNAL | OT_ADDR_LIST },
494 { "parameter-request", 55, OT_INTERNAL },
495 { "message", 56, OT_INTERNAL },
496 { "max-message-size", 57, OT_INTERNAL },
Simon Kelley23245c02012-07-18 16:21:11 +0100497 { "T1", 58, OT_INTERNAL | OT_TIME},
498 { "T2", 59, OT_INTERNAL | OT_TIME},
Simon Kelley40ef23b2012-03-13 21:59:28 +0000499 { "vendor-class", 60, 0 },
500 { "client-id", 61, OT_INTERNAL },
501 { "nis+-domain", 64, OT_NAME },
502 { "nis+-server", 65, OT_ADDR_LIST },
503 { "tftp-server", 66, OT_NAME },
504 { "bootfile-name", 67, OT_NAME },
505 { "mobile-ip-home", 68, OT_ADDR_LIST },
506 { "smtp-server", 69, OT_ADDR_LIST },
507 { "pop3-server", 70, OT_ADDR_LIST },
508 { "nntp-server", 71, OT_ADDR_LIST },
509 { "irc-server", 74, OT_ADDR_LIST },
510 { "user-class", 77, 0 },
511 { "FQDN", 81, OT_INTERNAL },
512 { "agent-id", 82, OT_INTERNAL },
513 { "client-arch", 93, 2 | OT_DEC },
514 { "client-interface-id", 94, 0 },
515 { "client-machine-id", 97, 0 },
516 { "subnet-select", 118, OT_INTERNAL },
517 { "domain-search", 119, OT_RFC1035_NAME },
518 { "sip-server", 120, 0 },
519 { "classless-static-route", 121, 0 },
520 { "vendor-id-encap", 125, 0 },
521 { "server-ip-address", 255, OT_ADDR_LIST }, /* special, internal only, sets siaddr */
522 { NULL, 0, 0 }
523};
524
525#ifdef HAVE_DHCP6
526static const struct opttab_t opttab6[] = {
527 { "client-id", 1, OT_INTERNAL },
528 { "server-id", 2, OT_INTERNAL },
529 { "ia-na", 3, OT_INTERNAL },
530 { "ia-ta", 4, OT_INTERNAL },
531 { "iaaddr", 5, OT_INTERNAL },
532 { "oro", 6, OT_INTERNAL },
533 { "preference", 7, OT_INTERNAL | OT_DEC },
534 { "unicast", 12, OT_INTERNAL },
535 { "status", 13, OT_INTERNAL },
536 { "rapid-commit", 14, OT_INTERNAL },
537 { "user-class", 15, OT_INTERNAL | OT_CSTRING },
538 { "vendor-class", 16, OT_INTERNAL | OT_CSTRING },
539 { "vendor-opts", 17, OT_INTERNAL },
540 { "sip-server-domain", 21, OT_RFC1035_NAME },
541 { "sip-server", 22, OT_ADDR_LIST },
542 { "dns-server", 23, OT_ADDR_LIST },
543 { "domain-search", 24, OT_RFC1035_NAME },
544 { "nis-server", 27, OT_ADDR_LIST },
545 { "nis+-server", 28, OT_ADDR_LIST },
546 { "nis-domain", 29, OT_RFC1035_NAME },
547 { "nis+-domain", 30, OT_RFC1035_NAME },
548 { "sntp-server", 31, OT_ADDR_LIST },
Simon Kelley23245c02012-07-18 16:21:11 +0100549 { "information-refresh-time", 32, OT_TIME },
Simon Kelley40ef23b2012-03-13 21:59:28 +0000550 { "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
Simon Kelley23245c02012-07-18 16:21:11 +0100714 else if ((ot[o].size & (OT_DEC | OT_TIME)) && opt_len != 0)
Simon Kelley40ef23b2012-03-13 21:59:28 +0000715 {
716 unsigned int dec = 0;
717
718 for (i = 0; i < opt_len; i++)
719 dec = (dec << 8) | val[i];
720
Simon Kelley23245c02012-07-18 16:21:11 +0100721 if (ot[o].size & OT_TIME)
722 prettyprint_time(buf, dec);
723 else
724 sprintf(buf, "%u", dec);
Simon Kelley40ef23b2012-03-13 21:59:28 +0000725 }
726 else
727 nodecode = 1;
728 }
729 break;
730 }
731
732 if (opt_len != 0 && buf && (!ot[o].name || nodecode))
733 {
734 int trunc = 0;
735 if (opt_len > 14)
736 {
737 trunc = 1;
738 opt_len = 14;
739 }
740 print_mac(buf, val, opt_len);
741 if (trunc)
742 strncat(buf, "...", buf_len - strlen(buf));
743
744
745 }
746
747 return ot[o].name ? ot[o].name : "";
748
749}
750
Simon Kelley4cb1b322012-02-06 14:30:41 +0000751#endif