• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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