dhcp-common.c revision a3595821594453ea89ef8e6790927694b0a1adf1
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
46void
47dhcp_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
104struct dhcp_opt *
105vivso_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
125ssize_t
126dhcp_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
150int
151make_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
213size_t
214encode_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. */
269ssize_t
270decode_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. */
356static int
357valid_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 */
426static const char hexchrs[] = "0123456789abcdef";
427ssize_t
428print_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 ADDRSZ		4
526#define ADDR6SZ		16
527static size_t
528dhcp_optlen(const struct dhcp_opt *opt, size_t dl)
529{
530	size_t sz;
531
532	if (dl == 0)
533		return 0;
534
535	if (opt->type == 0 ||
536	    opt->type & (STRING | BINHEX | RFC3442 | RFC5969))
537	{
538		if (opt->len) {
539			if ((size_t)opt->len > dl)
540				return 0;
541			return (size_t)opt->len;
542		}
543		return dl;
544	}
545
546	if ((opt->type & (ADDRIPV4 | ARRAY)) == (ADDRIPV4 | ARRAY)) {
547		if (dl < ADDRSZ)
548			return 0;
549		return dl - (dl % ADDRSZ);
550	}
551
552	if ((opt->type & (ADDRIPV6 | ARRAY)) == (ADDRIPV6 | ARRAY)) {
553		if (dl < ADDR6SZ)
554			return 0;
555		return dl - (dl % ADDR6SZ);
556	}
557
558	if (opt->type & (UINT32 | ADDRIPV4))
559		sz = sizeof(uint32_t);
560	else if (opt->type & UINT16)
561		sz = sizeof(uint16_t);
562	else if (opt->type & UINT8)
563		sz = sizeof(uint8_t);
564	else if (opt->type & ADDRIPV6)
565		sz = ADDR6SZ;
566	else
567		/* If we don't know the size, assume it's valid */
568		return dl;
569	return (dl < sz ? 0 : sz);
570}
571
572#ifdef INET6
573#define PO_IFNAME
574#else
575#define PO_IFNAME __unused
576#endif
577
578ssize_t
579print_option(char *s, size_t len, int type, const uint8_t *data, size_t dl,
580    PO_IFNAME const char *ifname)
581{
582	const uint8_t *e, *t;
583	uint16_t u16;
584	int16_t s16;
585	uint32_t u32;
586	int32_t s32;
587	struct in_addr addr;
588	ssize_t bytes = 0, sl;
589	size_t l;
590	char *tmp;
591
592	if (type & RFC3397) {
593		sl = decode_rfc3397(NULL, 0, data, dl);
594		if (sl == 0 || sl == -1)
595			return sl;
596		l = (size_t)sl + 1;
597		tmp = malloc(l);
598		if (tmp == NULL)
599			return -1;
600		decode_rfc3397(tmp, l, data, dl);
601		sl = print_string(s, len, type, (uint8_t *)tmp, l - 1);
602		free(tmp);
603		return sl;
604	}
605
606#ifdef INET
607	if (type & RFC3361) {
608		if ((tmp = decode_rfc3361(data, dl)) == NULL)
609			return -1;
610		l = strlen(tmp);
611		sl = print_string(s, len, type, (uint8_t *)tmp, l);
612		free(tmp);
613		return sl;
614	}
615
616	if (type & RFC3442)
617		return decode_rfc3442(s, len, data, dl);
618
619	if (type & RFC5969)
620		return decode_rfc5969(s, len, data, dl);
621#endif
622
623	if (type & STRING)
624		return print_string(s, len, type, data, dl);
625
626	if (type & FLAG) {
627		if (s) {
628			*s++ = '1';
629			*s = '\0';
630		}
631		return 1;
632	}
633
634	if (!s) {
635		if (type & UINT8)
636			l = 3;
637		else if (type & UINT16) {
638			l = 5;
639			dl /= 2;
640		} else if (type & SINT16) {
641			l = 6;
642			dl /= 2;
643		} else if (type & UINT32) {
644			l = 10;
645			dl /= 4;
646		} else if (type & SINT32) {
647			l = 11;
648			dl /= 4;
649		} else if (type & ADDRIPV4) {
650			l = 16;
651			dl /= 4;
652		}
653#ifdef INET6
654		else if (type & ADDRIPV6) {
655			e = data + dl;
656			l = 0;
657			while (data < e) {
658				if (l)
659					l++; /* space */
660				sl = ipv6_printaddr(NULL, 0, data, ifname);
661				if (sl != -1)
662					l += (size_t)sl;
663				data += 16;
664			}
665			return (ssize_t)l;
666		}
667#endif
668		else {
669			errno = EINVAL;
670			return -1;
671		}
672		return (ssize_t)(l * dl);
673	}
674
675	t = data;
676	e = data + dl;
677	while (data < e) {
678		if (data != t) {
679			*s++ = ' ';
680			bytes++;
681			len--;
682		}
683		if (type & UINT8) {
684			sl = snprintf(s, len, "%u", *data);
685			data++;
686		} else if (type & UINT16) {
687			memcpy(&u16, data, sizeof(u16));
688			u16 = ntohs(u16);
689			sl = snprintf(s, len, "%u", u16);
690			data += sizeof(u16);
691		} else if (type & SINT16) {
692			memcpy(&u16, data, sizeof(u16));
693			s16 = (int16_t)ntohs(u16);
694			sl = snprintf(s, len, "%d", s16);
695			data += sizeof(u16);
696		} else if (type & UINT32) {
697			memcpy(&u32, data, sizeof(u32));
698			u32 = ntohl(u32);
699			sl = snprintf(s, len, "%u", u32);
700			data += sizeof(u32);
701		} else if (type & SINT32) {
702			memcpy(&u32, data, sizeof(u32));
703			s32 = (int32_t)ntohl(u32);
704			sl = snprintf(s, len, "%d", s32);
705			data += sizeof(u32);
706		} else if (type & ADDRIPV4) {
707			memcpy(&addr.s_addr, data, sizeof(addr.s_addr));
708			sl = snprintf(s, len, "%s", inet_ntoa(addr));
709			data += sizeof(addr.s_addr);
710		}
711#ifdef INET6
712		else if (type & ADDRIPV6) {
713			ssize_t r;
714
715			r = ipv6_printaddr(s, len, data, ifname);
716			if (r != -1)
717				sl = r;
718			else
719				sl = 0;
720			data += 16;
721		}
722#endif
723		else
724			sl = 0;
725		len -= (size_t)sl;
726		bytes += sl;
727		s += sl;
728	}
729
730	return bytes;
731}
732
733int
734dhcp_set_leasefile(char *leasefile, size_t len, int family,
735    const struct interface *ifp, const char *extra)
736{
737	char ssid[len];
738
739	if (ifp->name[0] == '\0') {
740		strlcpy(leasefile, ifp->ctx->pidfile, len);
741		return 0;
742	}
743
744	if (strlen(ifp->lease_identifier) > 0) {
745		/* Only supports lease identifier for IPv4 for now. */
746		if (family == AF_INET) {
747			return snprintf(leasefile, len, LEASEFILE,
748					ifp->lease_identifier, "", "");
749		}
750	}
751
752	switch (family) {
753	case AF_INET:
754	case AF_INET6:
755		break;
756	default:
757		errno = EINVAL;
758		return -1;
759	}
760
761	if (ifp->wireless) {
762		ssid[0] = '-';
763		print_string(ssid + 1, sizeof(ssid) - 1,
764		    ESCFILE,
765		    (const uint8_t *)ifp->ssid, ifp->ssid_len);
766	} else
767		ssid[0] = '\0';
768	return snprintf(leasefile, len,
769	    family == AF_INET ? LEASEFILE : LEASEFILE6,
770	    ifp->name, ssid, extra);
771}
772
773static size_t
774dhcp_envoption1(struct dhcpcd_ctx *ctx, char **env, const char *prefix,
775    const struct dhcp_opt *opt, int vname, const uint8_t *od, size_t ol,
776    const char *ifname)
777{
778	ssize_t len;
779	size_t e;
780	char *v, *val;
781
782	if (opt->len && opt->len < ol)
783		ol = opt->len;
784	len = print_option(NULL, 0, opt->type, od, ol, ifname);
785	if (len < 0)
786		return 0;
787	if (vname)
788		e = strlen(opt->var) + 1;
789	else
790		e = 0;
791	if (prefix)
792		e += strlen(prefix);
793	e += (size_t)len + 2;
794	if (env == NULL)
795		return e;
796	v = val = *env = malloc(e);
797	if (v == NULL) {
798		logger(ctx, LOG_ERR, "%s: %m", __func__);
799		return 0;
800	}
801	if (vname)
802		v += snprintf(val, e, "%s_%s=", prefix, opt->var);
803	else
804		v += snprintf(val, e, "%s=", prefix);
805	if (len != 0)
806		print_option(v, (size_t)len + 1, opt->type, od, ol, ifname);
807	return e;
808}
809
810size_t
811dhcp_envoption(struct dhcpcd_ctx *ctx, char **env, const char *prefix,
812    const char *ifname, struct dhcp_opt *opt,
813    const uint8_t *(*dgetopt)(struct dhcpcd_ctx *,
814    size_t *, unsigned int *, size_t *,
815    const uint8_t *, size_t, struct dhcp_opt **),
816    const uint8_t *od, size_t ol)
817{
818	size_t e, i, n, eos, eol;
819	unsigned int eoc;
820	const uint8_t *eod;
821	int ov;
822	struct dhcp_opt *eopt, *oopt;
823	char *pfx;
824
825	/* If no embedded or encapsulated options, it's easy */
826	if (opt->embopts_len == 0 && opt->encopts_len == 0) {
827		if (dhcp_envoption1(ctx, env == NULL ? NULL : &env[0],
828		    prefix, opt, 1, od, ol, ifname))
829			return 1;
830		return 0;
831	}
832
833	/* Create a new prefix based on the option */
834	if (env) {
835		if (opt->type & INDEX) {
836			if (opt->index > 999) {
837				errno = ENOBUFS;
838				logger(ctx, LOG_ERR, "%s: %m", __func__);
839				return 0;
840			}
841		}
842		e = strlen(prefix) + strlen(opt->var) + 2 +
843		    (opt->type & INDEX ? 3 : 0);
844		pfx = malloc(e);
845		if (pfx == NULL) {
846			logger(ctx, LOG_ERR, "%s: %m", __func__);
847			return 0;
848		}
849		if (opt->type & INDEX)
850			snprintf(pfx, e, "%s_%s%d", prefix,
851			    opt->var, ++opt->index);
852		else
853			snprintf(pfx, e, "%s_%s", prefix, opt->var);
854	} else
855		pfx = NULL;
856
857	/* Embedded options are always processed first as that
858	 * is a fixed layout */
859	n = 0;
860	for (i = 0, eopt = opt->embopts; i < opt->embopts_len; i++, eopt++) {
861		e = dhcp_optlen(eopt, ol);
862		if (e == 0)
863			/* Report error? */
864			return 0;
865		/* Use the option prefix if the embedded option
866		 * name is different.
867		 * This avoids new_fqdn_fqdn which would be silly. */
868		ov = strcmp(opt->var, eopt->var);
869		if (dhcp_envoption1(ctx, env == NULL ? NULL : &env[n],
870		    pfx, eopt, ov, od, e, ifname))
871			n++;
872		od += e;
873		ol -= e;
874	}
875
876	/* Enumerate our encapsulated options */
877	if (opt->encopts_len && ol > 0) {
878		/* Zero any option indexes
879		 * We assume that referenced encapsulated options are NEVER
880		 * recursive as the index order could break. */
881		for (i = 0, eopt = opt->encopts;
882		    i < opt->encopts_len;
883		    i++, eopt++)
884		{
885			eoc = opt->option;
886			if (eopt->type & OPTION) {
887				dgetopt(ctx, NULL, &eoc, NULL, NULL, 0, &oopt);
888				if (oopt)
889					oopt->index = 0;
890			}
891		}
892
893		while ((eod = dgetopt(ctx, &eos, &eoc, &eol, od, ol, &oopt))) {
894			for (i = 0, eopt = opt->encopts;
895			    i < opt->encopts_len;
896			    i++, eopt++)
897			{
898				if (eopt->option == eoc) {
899					if (eopt->type & OPTION) {
900						if (oopt == NULL)
901							/* Report error? */
902							continue;
903					}
904					n += dhcp_envoption(ctx,
905					    env == NULL ? NULL : &env[n], pfx,
906					    ifname,
907					    eopt->type & OPTION ? oopt : eopt,
908					    dgetopt, eod, eol);
909					break;
910				}
911			}
912			od += eos + eol;
913			ol -= eos + eol;
914		}
915	}
916
917	if (env)
918		free(pfx);
919
920	/* Return number of options found */
921	return n;
922}
923
924void
925dhcp_zero_index(struct dhcp_opt *opt)
926{
927	size_t i;
928	struct dhcp_opt *o;
929
930	opt->index = 0;
931	for (i = 0, o = opt->embopts; i < opt->embopts_len; i++, o++)
932		dhcp_zero_index(o);
933	for (i = 0, o = opt->encopts; i < opt->encopts_len; i++, o++)
934		dhcp_zero_index(o);
935}
936