1 /*
2 * dhcpcd - DHCP client daemon
3 * Copyright (c) 2006-2015 Roy Marples <roy@marples.name>
4 * All rights reserved
5
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25 * SUCH DAMAGE.
26 */
27
28 #include <sys/utsname.h>
29
30 #include <ctype.h>
31 #include <errno.h>
32 #include <fcntl.h>
33 #include <inttypes.h>
34 #include <stdlib.h>
35 #include <string.h>
36 #include <unistd.h>
37
38 #include "config.h"
39
40 #include "common.h"
41 #include "dhcp-common.h"
42 #include "dhcp.h"
43 #include "if.h"
44 #include "ipv6.h"
45
46 void
dhcp_print_option_encoding(const struct dhcp_opt * opt,int cols)47 dhcp_print_option_encoding(const struct dhcp_opt *opt, int cols)
48 {
49
50 while (cols < 40) {
51 putchar(' ');
52 cols++;
53 }
54 putchar('\t');
55 if (opt->type & EMBED)
56 printf(" embed");
57 if (opt->type & ENCAP)
58 printf(" encap");
59 if (opt->type & INDEX)
60 printf(" index");
61 if (opt->type & ARRAY)
62 printf(" array");
63 if (opt->type & UINT8)
64 printf(" byte");
65 else if (opt->type & UINT16)
66 printf(" uint16");
67 else if (opt->type & SINT16)
68 printf(" sint16");
69 else if (opt->type & UINT32)
70 printf(" uint32");
71 else if (opt->type & SINT32)
72 printf(" sint32");
73 else if (opt->type & ADDRIPV4)
74 printf(" ipaddress");
75 else if (opt->type & ADDRIPV6)
76 printf(" ip6address");
77 else if (opt->type & FLAG)
78 printf(" flag");
79 else if (opt->type & RFC3397)
80 printf(" domain");
81 else if (opt->type & DOMAIN)
82 printf(" dname");
83 else if (opt->type & ASCII)
84 printf(" ascii");
85 else if (opt->type & RAW)
86 printf(" raw");
87 else if (opt->type & BINHEX)
88 printf(" binhex");
89 else if (opt->type & STRING)
90 printf(" string");
91 if (opt->type & RFC3361)
92 printf(" rfc3361");
93 if (opt->type & RFC3442)
94 printf(" rfc3442");
95 if (opt->type & RFC5969)
96 printf(" rfc5969");
97 if (opt->type & REQUEST)
98 printf(" request");
99 if (opt->type & NOREQ)
100 printf(" norequest");
101 putchar('\n');
102 }
103
104 struct dhcp_opt *
vivso_find(uint32_t iana_en,const void * arg)105 vivso_find(uint32_t iana_en, const void *arg)
106 {
107 const struct interface *ifp;
108 size_t i;
109 struct dhcp_opt *opt;
110
111 ifp = arg;
112 for (i = 0, opt = ifp->options->vivso_override;
113 i < ifp->options->vivso_override_len;
114 i++, opt++)
115 if (opt->option == iana_en)
116 return opt;
117 for (i = 0, opt = ifp->ctx->vivso;
118 i < ifp->ctx->vivso_len;
119 i++, opt++)
120 if (opt->option == iana_en)
121 return opt;
122 return NULL;
123 }
124
125 ssize_t
dhcp_vendor(char * str,size_t len)126 dhcp_vendor(char *str, size_t len)
127 {
128 struct utsname utn;
129 char *p;
130 int l;
131
132 if (uname(&utn) != 0)
133 return (ssize_t)snprintf(str, len, "%s-%s",
134 PACKAGE, VERSION);
135 p = str;
136 l = snprintf(p, len,
137 "%s-%s:%s-%s:%s", PACKAGE, VERSION,
138 utn.sysname, utn.release, utn.machine);
139 if (l == -1 || (size_t)(l + 1) > len)
140 return -1;
141 p += l;
142 len -= (size_t)l;
143 l = if_machinearch(p, len);
144 if (l == -1 || (size_t)(l + 1) > len)
145 return -1;
146 p += l;
147 return p - str;
148 }
149
150 int
make_option_mask(const struct dhcp_opt * dopts,size_t dopts_len,const struct dhcp_opt * odopts,size_t odopts_len,uint8_t * mask,const char * opts,int add)151 make_option_mask(const struct dhcp_opt *dopts, size_t dopts_len,
152 const struct dhcp_opt *odopts, size_t odopts_len,
153 uint8_t *mask, const char *opts, int add)
154 {
155 char *token, *o, *p;
156 const struct dhcp_opt *opt;
157 int match, e;
158 unsigned int n;
159 size_t i;
160
161 if (opts == NULL)
162 return -1;
163 o = p = strdup(opts);
164 while ((token = strsep(&p, ", "))) {
165 if (*token == '\0')
166 continue;
167 match = 0;
168 for (i = 0, opt = odopts; i < odopts_len; i++, opt++) {
169 if (strcmp(opt->var, token) == 0)
170 match = 1;
171 else {
172 n = (unsigned int)strtou(token, NULL, 0,
173 0, UINT_MAX, &e);
174 if (e == 0 && opt->option == n)
175 match = 1;
176 }
177 if (match)
178 break;
179 }
180 if (match == 0) {
181 for (i = 0, opt = dopts; i < dopts_len; i++, opt++) {
182 if (strcmp(opt->var, token) == 0)
183 match = 1;
184 else {
185 n = (unsigned int)strtou(token, NULL, 0,
186 0, UINT_MAX, &e);
187 if (e == 0 && opt->option == n)
188 match = 1;
189 }
190 if (match)
191 break;
192 }
193 }
194 if (!match || !opt->option) {
195 free(o);
196 errno = ENOENT;
197 return -1;
198 }
199 if (add == 2 && !(opt->type & ADDRIPV4)) {
200 free(o);
201 errno = EINVAL;
202 return -1;
203 }
204 if (add == 1 || add == 2)
205 add_option_mask(mask, opt->option);
206 else
207 del_option_mask(mask, opt->option);
208 }
209 free(o);
210 return 0;
211 }
212
213 size_t
encode_rfc1035(const char * src,uint8_t * dst)214 encode_rfc1035(const char *src, uint8_t *dst)
215 {
216 uint8_t *p;
217 uint8_t *lp;
218 size_t len;
219 uint8_t has_dot;
220
221 if (src == NULL || *src == '\0')
222 return 0;
223
224 if (dst) {
225 p = dst;
226 lp = p++;
227 }
228 /* Silence bogus GCC warnings */
229 else
230 p = lp = NULL;
231
232 len = 1;
233 has_dot = 0;
234 for (; *src; src++) {
235 if (*src == '\0')
236 break;
237 if (*src == '.') {
238 /* Skip the trailing . */
239 if (src[1] == '\0')
240 break;
241 has_dot = 1;
242 if (dst) {
243 *lp = (uint8_t)(p - lp - 1);
244 if (*lp == '\0')
245 return len;
246 lp = p++;
247 }
248 } else if (dst)
249 *p++ = (uint8_t)*src;
250 len++;
251 }
252
253 if (dst) {
254 *lp = (uint8_t)(p - lp - 1);
255 if (has_dot)
256 *p++ = '\0';
257 }
258
259 if (has_dot)
260 len++;
261
262 return len;
263 }
264
265 /* Decode an RFC3397 DNS search order option into a space
266 * separated string. Returns length of string (including
267 * terminating zero) or zero on error. out may be NULL
268 * to just determine output length. */
269 ssize_t
decode_rfc3397(char * out,size_t len,const uint8_t * p,size_t pl)270 decode_rfc3397(char *out, size_t len, const uint8_t *p, size_t pl)
271 {
272 const char *start;
273 size_t start_len, l, count;
274 const uint8_t *r, *q = p, *e;
275 int hops;
276 uint8_t ltype;
277
278 count = 0;
279 start = out;
280 start_len = len;
281 q = p;
282 e = p + pl;
283 while (q < e) {
284 r = NULL;
285 hops = 0;
286 /* Check we are inside our length again in-case
287 * the name isn't fully qualified (ie, not terminated) */
288 while (q < e && (l = (size_t)*q++)) {
289 ltype = l & 0xc0;
290 if (ltype == 0x80 || ltype == 0x40)
291 return -1;
292 else if (ltype == 0xc0) { /* pointer */
293 if (q == e) {
294 errno = ERANGE;
295 return -1;
296 }
297 l = (l & 0x3f) << 8;
298 l |= *q++;
299 /* save source of first jump. */
300 if (!r)
301 r = q;
302 hops++;
303 if (hops > 255) {
304 errno = ERANGE;
305 return -1;
306 }
307 q = p + l;
308 if (q >= e) {
309 errno = ERANGE;
310 return -1;
311 }
312 } else {
313 /* straightforward name segment, add with '.' */
314 if (q + l > e) {
315 errno = ERANGE;
316 return -1;
317 }
318 count += l + 1;
319 if (out) {
320 if (l + 1 > len) {
321 errno = ENOBUFS;
322 return -1;
323 }
324 memcpy(out, q, l);
325 out += l;
326 *out++ = '.';
327 len -= l;
328 len--;
329 }
330 q += l;
331 }
332 }
333 /* change last dot to space */
334 if (out && out != start)
335 *(out - 1) = ' ';
336 if (r)
337 q = r;
338 }
339
340 /* change last space to zero terminator */
341 if (out) {
342 if (out != start)
343 *(out - 1) = '\0';
344 else if (start_len > 0)
345 *out = '\0';
346 }
347
348 if (count)
349 /* Don't count the trailing NUL */
350 count--;
351 return (ssize_t)count;
352 }
353
354 /* Check for a valid domain name as per RFC1123 with the exception of
355 * allowing - and _ (but not at start or end) as they seem to be widely used. */
356 static int
valid_domainname(char * lbl,int type)357 valid_domainname(char *lbl, int type)
358 {
359 char *slbl, *lst;
360 unsigned char c;
361 int start, len, errset;
362
363 if (lbl == NULL || *lbl == '\0') {
364 errno = EINVAL;
365 return 0;
366 }
367
368 slbl = lbl;
369 lst = NULL;
370 start = 1;
371 len = errset = 0;
372 for (;;) {
373 c = (unsigned char)*lbl++;
374 if (c == '\0')
375 return 1;
376 if (c == ' ') {
377 if (lbl - 1 == slbl) /* No space at start */
378 break;
379 if (!(type & ARRAY))
380 break;
381 /* Skip to the next label */
382 if (!start) {
383 start = 1;
384 lst = lbl - 1;
385 }
386 if (len)
387 len = 0;
388 continue;
389 }
390 if (c == '.') {
391 if (*lbl == '.')
392 break;
393 len = 0;
394 continue;
395 }
396 if (((c == '-' || c == '_') &&
397 !start && *lbl != ' ' && *lbl != '\0') ||
398 isalnum(c))
399 {
400 if (++len > 63) {
401 errno = ERANGE;
402 errset = 1;
403 break;
404 }
405 } else
406 break;
407 if (start)
408 start = 0;
409 }
410
411 if (!errset)
412 errno = EINVAL;
413 if (lst) {
414 /* At least one valid domain, return it */
415 *lst = '\0';
416 return 1;
417 }
418 return 0;
419 }
420
421 /*
422 * Prints a chunk of data to a string.
423 * PS_SHELL goes as it is these days, it's upto the target to validate it.
424 * PS_SAFE has all non ascii and non printables changes to escaped octal.
425 */
426 static const char hexchrs[] = "0123456789abcdef";
427 ssize_t
print_string(char * dst,size_t len,int type,const uint8_t * data,size_t dl)428 print_string(char *dst, size_t len, int type, const uint8_t *data, size_t dl)
429 {
430 char *odst;
431 uint8_t c;
432 const uint8_t *e;
433 size_t bytes;
434
435 odst = dst;
436 bytes = 0;
437 e = data + dl;
438
439 while (data < e) {
440 c = *data++;
441 if (type & BINHEX) {
442 if (dst) {
443 if (len == 0 || len == 1) {
444 errno = ENOSPC;
445 return -1;
446 }
447 *dst++ = hexchrs[(c & 0xF0) >> 4];
448 *dst++ = hexchrs[(c & 0x0F)];
449 len -= 2;
450 }
451 bytes += 2;
452 continue;
453 }
454 if (type & ASCII && (!isascii(c))) {
455 errno = EINVAL;
456 break;
457 }
458 if (!(type & (ASCII | RAW | ESCSTRING | ESCFILE)) /* plain */ &&
459 (!isascii(c) && !isprint(c)))
460 {
461 errno = EINVAL;
462 break;
463 }
464 if ((type & (ESCSTRING | ESCFILE) &&
465 (c == '\\' || !isascii(c) || !isprint(c))) ||
466 (type & ESCFILE && (c == '/' || c == ' ')))
467 {
468 errno = EINVAL;
469 if (c == '\\') {
470 if (dst) {
471 if (len == 0 || len == 1) {
472 errno = ENOSPC;
473 return -1;
474 }
475 *dst++ = '\\'; *dst++ = '\\';
476 len -= 2;
477 }
478 bytes += 2;
479 continue;
480 }
481 if (dst) {
482 if (len < 5) {
483 errno = ENOSPC;
484 return -1;
485 }
486 *dst++ = '\\';
487 *dst++ = (char)(((c >> 6) & 03) + '0');
488 *dst++ = (char)(((c >> 3) & 07) + '0');
489 *dst++ = (char)(( c & 07) + '0');
490 len -= 4;
491 }
492 bytes += 4;
493 } else {
494 if (dst) {
495 if (len == 0) {
496 errno = ENOSPC;
497 return -1;
498 }
499 *dst++ = (char)c;
500 len--;
501 }
502 bytes++;
503 }
504 }
505
506 /* NULL */
507 if (dst) {
508 if (len == 0) {
509 errno = ENOSPC;
510 return -1;
511 }
512 *dst = '\0';
513
514 /* Now we've printed it, validate the domain */
515 if (type & DOMAIN && !valid_domainname(odst, type)) {
516 *odst = '\0';
517 return 1;
518 }
519
520 }
521
522 return (ssize_t)bytes;
523 }
524
525 #define ADDR6SZ 16
526 static size_t
dhcp_optlen(const struct dhcp_opt * opt,size_t dl)527 dhcp_optlen(const struct dhcp_opt *opt, size_t dl)
528 {
529 size_t sz;
530
531 if (opt->type & ADDRIPV6)
532 sz = ADDR6SZ;
533 else if (opt->type & (UINT32 | ADDRIPV4))
534 sz = sizeof(uint32_t);
535 else if (opt->type & UINT16)
536 sz = sizeof(uint16_t);
537 else if (opt->type & (UINT8 | BITFLAG))
538 sz = sizeof(uint8_t);
539 else if (opt->type & FLAG)
540 return 0;
541 else {
542 /* All other types are variable length */
543 if (opt->len) {
544 if ((size_t)opt->len > dl) {
545 errno = ENODATA;
546 return -1;
547 }
548 return (ssize_t)opt->len;
549 }
550 return (ssize_t)dl;
551 }
552 if (dl < sz) {
553 errno = ENODATA;
554 return -1;
555 }
556
557 /* Trim any extra data.
558 * Maybe we need a settng to reject DHCP options with extra data? */
559 if (opt->type & ARRAY)
560 return (ssize_t)(dl - (dl % sz));
561 return (ssize_t)sz;
562 }
563
564 #ifdef INET6
565 #define PO_IFNAME
566 #else
567 #define PO_IFNAME __unused
568 #endif
569
570 ssize_t
print_option(char * s,size_t len,int type,const uint8_t * data,size_t dl,PO_IFNAME const char * ifname)571 print_option(char *s, size_t len, int type, const uint8_t *data, size_t dl,
572 PO_IFNAME const char *ifname)
573 {
574 const uint8_t *e, *t;
575 uint16_t u16;
576 int16_t s16;
577 uint32_t u32;
578 int32_t s32;
579 struct in_addr addr;
580 ssize_t bytes = 0, sl;
581 size_t l;
582 char *tmp;
583
584 if (type & RFC3397) {
585 sl = decode_rfc3397(NULL, 0, data, dl);
586 if (sl == 0 || sl == -1)
587 return sl;
588 l = (size_t)sl + 1;
589 tmp = malloc(l);
590 if (tmp == NULL)
591 return -1;
592 decode_rfc3397(tmp, l, data, dl);
593 sl = print_string(s, len, type, (uint8_t *)tmp, l - 1);
594 free(tmp);
595 return sl;
596 }
597
598 #ifdef INET
599 if (type & RFC3361) {
600 if ((tmp = decode_rfc3361(data, dl)) == NULL)
601 return -1;
602 l = strlen(tmp);
603 sl = print_string(s, len, type, (uint8_t *)tmp, l);
604 free(tmp);
605 return sl;
606 }
607
608 if (type & RFC3442)
609 return decode_rfc3442(s, len, data, dl);
610
611 if (type & RFC5969)
612 return decode_rfc5969(s, len, data, dl);
613 #endif
614
615 if (type & STRING)
616 return print_string(s, len, type, data, dl);
617
618 if (type & FLAG) {
619 if (s) {
620 *s++ = '1';
621 *s = '\0';
622 }
623 return 1;
624 }
625
626 if (!s) {
627 if (type & UINT8)
628 l = 3;
629 else if (type & UINT16) {
630 l = 5;
631 dl /= 2;
632 } else if (type & SINT16) {
633 l = 6;
634 dl /= 2;
635 } else if (type & UINT32) {
636 l = 10;
637 dl /= 4;
638 } else if (type & SINT32) {
639 l = 11;
640 dl /= 4;
641 } else if (type & ADDRIPV4) {
642 l = 16;
643 dl /= 4;
644 }
645 #ifdef INET6
646 else if (type & ADDRIPV6) {
647 e = data + dl;
648 l = 0;
649 while (data < e) {
650 if (l)
651 l++; /* space */
652 sl = ipv6_printaddr(NULL, 0, data, ifname);
653 if (sl != -1)
654 l += (size_t)sl;
655 data += 16;
656 }
657 return (ssize_t)l;
658 }
659 #endif
660 else {
661 errno = EINVAL;
662 return -1;
663 }
664 return (ssize_t)(l * dl);
665 }
666
667 t = data;
668 e = data + dl;
669 while (data < e) {
670 if (data != t) {
671 *s++ = ' ';
672 bytes++;
673 len--;
674 }
675 if (type & UINT8) {
676 sl = snprintf(s, len, "%u", *data);
677 data++;
678 } else if (type & UINT16) {
679 memcpy(&u16, data, sizeof(u16));
680 u16 = ntohs(u16);
681 sl = snprintf(s, len, "%u", u16);
682 data += sizeof(u16);
683 } else if (type & SINT16) {
684 memcpy(&u16, data, sizeof(u16));
685 s16 = (int16_t)ntohs(u16);
686 sl = snprintf(s, len, "%d", s16);
687 data += sizeof(u16);
688 } else if (type & UINT32) {
689 memcpy(&u32, data, sizeof(u32));
690 u32 = ntohl(u32);
691 sl = snprintf(s, len, "%u", u32);
692 data += sizeof(u32);
693 } else if (type & SINT32) {
694 memcpy(&u32, data, sizeof(u32));
695 s32 = (int32_t)ntohl(u32);
696 sl = snprintf(s, len, "%d", s32);
697 data += sizeof(u32);
698 } else if (type & ADDRIPV4) {
699 memcpy(&addr.s_addr, data, sizeof(addr.s_addr));
700 sl = snprintf(s, len, "%s", inet_ntoa(addr));
701 data += sizeof(addr.s_addr);
702 }
703 #ifdef INET6
704 else if (type & ADDRIPV6) {
705 ssize_t r;
706
707 r = ipv6_printaddr(s, len, data, ifname);
708 if (r != -1)
709 sl = r;
710 else
711 sl = 0;
712 data += 16;
713 }
714 #endif
715 else
716 sl = 0;
717 if (len <= sl) {
718 bytes += len;
719 break;
720 }
721 len -= (size_t)sl;
722 bytes += sl;
723 s += sl;
724 }
725
726 return bytes;
727 }
728
729 /* Lease file name is formatted according to the expectation of the ChromiumOS's
730 * connection manager (shill). */
731 int
dhcp_set_leasefile(char * leasefile,size_t len,int family,const struct interface * ifp,const char * extra)732 dhcp_set_leasefile(char *leasefile, size_t len, int family,
733 const struct interface *ifp, const char *extra)
734 {
735 char ssid[len];
736
737 if (ifp->name[0] == '\0') {
738 strlcpy(leasefile, ifp->ctx->pidfile, len);
739 return 0;
740 }
741
742 if (strlen(ifp->lease_identifier) > 0) {
743 return snprintf(leasefile, len,
744 family == AF_INET ? LEASEFILE : LEASEFILE6,
745 ifp->lease_identifier, "", extra);
746 }
747 return snprintf(leasefile, len,
748 family == AF_INET ? LEASEFILE : LEASEFILE6,
749 ifp->name, "", extra);
750 }
751
752 static size_t
dhcp_envoption1(struct dhcpcd_ctx * ctx,char ** env,const char * prefix,const struct dhcp_opt * opt,int vname,const uint8_t * od,size_t ol,const char * ifname)753 dhcp_envoption1(struct dhcpcd_ctx *ctx, char **env, const char *prefix,
754 const struct dhcp_opt *opt, int vname, const uint8_t *od, size_t ol,
755 const char *ifname)
756 {
757 ssize_t len;
758 size_t e;
759 char *v, *val;
760
761 /* Ensure a valid length */
762 ol = (size_t)dhcp_optlen(opt, ol);
763 if ((ssize_t)ol == -1)
764 return 0;
765
766 len = print_option(NULL, 0, opt->type, od, ol, ifname);
767 if (len < 0)
768 return 0;
769 if (vname)
770 e = strlen(opt->var) + 1;
771 else
772 e = 0;
773 if (prefix)
774 e += strlen(prefix);
775 e += (size_t)len + 2;
776 if (env == NULL)
777 return e;
778 v = val = *env = malloc(e);
779 if (v == NULL) {
780 logger(ctx, LOG_ERR, "%s: %m", __func__);
781 return 0;
782 }
783 if (vname)
784 v += snprintf(val, e, "%s_%s=", prefix, opt->var);
785 else
786 v += snprintf(val, e, "%s=", prefix);
787 if (len != 0)
788 print_option(v, (size_t)len + 1, opt->type, od, ol, ifname);
789 return e;
790 }
791
792 size_t
dhcp_envoption(struct dhcpcd_ctx * ctx,char ** env,const char * prefix,const char * ifname,struct dhcp_opt * opt,const uint8_t * (* dgetopt)(struct dhcpcd_ctx *,size_t *,unsigned int *,size_t *,const uint8_t *,size_t,struct dhcp_opt **),const uint8_t * od,size_t ol)793 dhcp_envoption(struct dhcpcd_ctx *ctx, char **env, const char *prefix,
794 const char *ifname, struct dhcp_opt *opt,
795 const uint8_t *(*dgetopt)(struct dhcpcd_ctx *,
796 size_t *, unsigned int *, size_t *,
797 const uint8_t *, size_t, struct dhcp_opt **),
798 const uint8_t *od, size_t ol)
799 {
800 size_t e, i, n, eos, eol;
801 unsigned int eoc;
802 const uint8_t *eod;
803 int ov;
804 struct dhcp_opt *eopt, *oopt;
805 char *pfx;
806
807 /* If no embedded or encapsulated options, it's easy */
808 if (opt->embopts_len == 0 && opt->encopts_len == 0) {
809 if (dhcp_envoption1(ctx, env == NULL ? NULL : &env[0],
810 prefix, opt, 1, od, ol, ifname))
811 return 1;
812 return 0;
813 }
814
815 /* Create a new prefix based on the option */
816 if (env) {
817 if (opt->type & INDEX) {
818 if (opt->index > 999) {
819 errno = ENOBUFS;
820 logger(ctx, LOG_ERR, "%s: %m", __func__);
821 return 0;
822 }
823 }
824 e = strlen(prefix) + strlen(opt->var) + 2 +
825 (opt->type & INDEX ? 3 : 0);
826 pfx = malloc(e);
827 if (pfx == NULL) {
828 logger(ctx, LOG_ERR, "%s: %m", __func__);
829 return 0;
830 }
831 if (opt->type & INDEX)
832 snprintf(pfx, e, "%s_%s%d", prefix,
833 opt->var, ++opt->index);
834 else
835 snprintf(pfx, e, "%s_%s", prefix, opt->var);
836 } else
837 pfx = NULL;
838
839 /* Embedded options are always processed first as that
840 * is a fixed layout */
841 n = 0;
842 for (i = 0, eopt = opt->embopts; i < opt->embopts_len; i++, eopt++) {
843 e = dhcp_optlen(eopt, ol);
844 if (e == 0) {
845 /* An option was expected, but there is not enough
846 * data for it.
847 * This may not be an error as some options like
848 * DHCP FQDN in RFC4702 have a string as the last
849 * option which is optional.
850 * FIXME: Add an flag to the options to indicate
851 * wether this is allowable or not. */
852 if (ol != 0 || i + 1 < opt->embopts_len)
853 logger(ctx, LOG_WARNING,
854 "%s: %s: malformed option %d",
855 ifname, __func__, opt->option);
856 goto out;
857 }
858 /* Use the option prefix if the embedded option
859 * name is different.
860 * This avoids new_fqdn_fqdn which would be silly. */
861 ov = strcmp(opt->var, eopt->var);
862 if (dhcp_envoption1(ctx, env == NULL ? NULL : &env[n],
863 pfx, eopt, ov, od, e, ifname))
864 n++;
865 od += e;
866 ol -= e;
867 }
868
869 /* Enumerate our encapsulated options */
870 if (opt->encopts_len && ol > 0) {
871 /* Zero any option indexes
872 * We assume that referenced encapsulated options are NEVER
873 * recursive as the index order could break. */
874 for (i = 0, eopt = opt->encopts;
875 i < opt->encopts_len;
876 i++, eopt++)
877 {
878 eoc = opt->option;
879 if (eopt->type & OPTION) {
880 dgetopt(ctx, NULL, &eoc, NULL, NULL, 0, &oopt);
881 if (oopt)
882 oopt->index = 0;
883 }
884 }
885
886 while ((eod = dgetopt(ctx, &eos, &eoc, &eol, od, ol, &oopt))) {
887 for (i = 0, eopt = opt->encopts;
888 i < opt->encopts_len;
889 i++, eopt++)
890 {
891 if (eopt->option == eoc) {
892 if (eopt->type & OPTION) {
893 if (oopt == NULL)
894 /* Report error? */
895 continue;
896 }
897 n += dhcp_envoption(ctx,
898 env == NULL ? NULL : &env[n], pfx,
899 ifname,
900 eopt->type & OPTION ? oopt : eopt,
901 dgetopt, eod, eol);
902 break;
903 }
904 }
905 od += eos + eol;
906 ol -= eos + eol;
907 }
908 }
909
910 out:
911 if (env)
912 free(pfx);
913
914 /* Return number of options found */
915 return n;
916 }
917
918 void
dhcp_zero_index(struct dhcp_opt * opt)919 dhcp_zero_index(struct dhcp_opt *opt)
920 {
921 size_t i;
922 struct dhcp_opt *o;
923
924 opt->index = 0;
925 for (i = 0, o = opt->embopts; i < opt->embopts_len; i++, o++)
926 dhcp_zero_index(o);
927 for (i = 0, o = opt->encopts; i < opt->encopts_len; i++, o++)
928 dhcp_zero_index(o);
929 }
930