xref: /dragonfly/contrib/dhcpcd/src/dhcp-common.c (revision 2b3f93ea)
1 /* SPDX-License-Identifier: BSD-2-Clause */
2 /*
3  * dhcpcd - DHCP client daemon
4  * Copyright (c) 2006-2023 Roy Marples <roy@marples.name>
5  * All rights reserved
6 
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26  * SUCH DAMAGE.
27  */
28 
29 #include <sys/utsname.h>
30 
31 #include <ctype.h>
32 #include <errno.h>
33 #include <fcntl.h>
34 #include <inttypes.h>
35 #include <stdlib.h>
36 #include <string.h>
37 #include <unistd.h>
38 
39 #include "config.h"
40 
41 #include "common.h"
42 #include "dhcp-common.h"
43 #include "dhcp.h"
44 #include "if.h"
45 #include "ipv6.h"
46 #include "logerr.h"
47 #include "script.h"
48 
49 const char *
50 dhcp_get_hostname(char *buf, size_t buf_len, const struct if_options *ifo)
51 {
52 
53 	if (ifo->hostname[0] == '\0') {
54 		if (gethostname(buf, buf_len) != 0)
55 			return NULL;
56 		buf[buf_len - 1] = '\0';
57 	} else
58 		strlcpy(buf, ifo->hostname, buf_len);
59 
60 	/* Deny sending of these local hostnames */
61 	if (buf[0] == '\0' || buf[0] == '.' ||
62 	    strcmp(buf, "(none)") == 0 ||
63 	    strcmp(buf, "localhost") == 0 ||
64 	    strncmp(buf, "localhost.", strlen("localhost.")) == 0)
65 		return NULL;
66 
67 	/* Shorten the hostname if required */
68 	if (ifo->options & DHCPCD_HOSTNAME_SHORT) {
69 		char *hp;
70 
71 		hp = strchr(buf, '.');
72 		if (hp != NULL)
73 			*hp = '\0';
74 	}
75 
76 	return buf;
77 }
78 
79 void
80 dhcp_print_option_encoding(const struct dhcp_opt *opt, int cols)
81 {
82 
83 	while (cols < 40) {
84 		putchar(' ');
85 		cols++;
86 	}
87 	putchar('\t');
88 	if (opt->type & OT_EMBED)
89 		printf(" embed");
90 	if (opt->type & OT_ENCAP)
91 		printf(" encap");
92 	if (opt->type & OT_INDEX)
93 		printf(" index");
94 	if (opt->type & OT_ARRAY)
95 		printf(" array");
96 	if (opt->type & OT_UINT8)
97 		printf(" uint8");
98 	else if (opt->type & OT_INT8)
99 		printf(" int8");
100 	else if (opt->type & OT_UINT16)
101 		printf(" uint16");
102 	else if (opt->type & OT_INT16)
103 		printf(" int16");
104 	else if (opt->type & OT_UINT32)
105 		printf(" uint32");
106 	else if (opt->type & OT_INT32)
107 		printf(" int32");
108 	else if (opt->type & OT_ADDRIPV4)
109 		printf(" ipaddress");
110 	else if (opt->type & OT_ADDRIPV6)
111 		printf(" ip6address");
112 	else if (opt->type & OT_FLAG)
113 		printf(" flag");
114 	else if (opt->type & OT_BITFLAG)
115 		printf(" bitflags");
116 	else if (opt->type & OT_RFC1035)
117 		printf(" domain");
118 	else if (opt->type & OT_DOMAIN)
119 		printf(" dname");
120 	else if (opt->type & OT_ASCII)
121 		printf(" ascii");
122 	else if (opt->type & OT_RAW)
123 		printf(" raw");
124 	else if (opt->type & OT_BINHEX)
125 		printf(" binhex");
126 	else if (opt->type & OT_STRING)
127 		printf(" string");
128 	else if (opt->type & OT_URI)
129 		printf(" uri");
130 	if (opt->type & OT_RFC3361)
131 		printf(" rfc3361");
132 	if (opt->type & OT_RFC3442)
133 		printf(" rfc3442");
134 	if (opt->type & OT_REQUEST)
135 		printf(" request");
136 	if (opt->type & OT_NOREQ)
137 		printf(" norequest");
138 	putchar('\n');
139 }
140 
141 struct dhcp_opt *
142 vivso_find(uint32_t iana_en, const void *arg)
143 {
144 	const struct interface *ifp;
145 	size_t i;
146 	struct dhcp_opt *opt;
147 
148 	ifp = arg;
149 	for (i = 0, opt = ifp->options->vivso_override;
150 	    i < ifp->options->vivso_override_len;
151 	    i++, opt++)
152 		if (opt->option == iana_en)
153 			return opt;
154 	for (i = 0, opt = ifp->ctx->vivso;
155 	    i < ifp->ctx->vivso_len;
156 	    i++, opt++)
157 		if (opt->option == iana_en)
158 			return opt;
159 	return NULL;
160 }
161 
162 ssize_t
163 dhcp_vendor(char *str, size_t len)
164 {
165 	struct utsname utn;
166 	char *p;
167 	int l;
168 
169 	if (uname(&utn) == -1)
170 		return (ssize_t)snprintf(str, len, "%s-%s",
171 		    PACKAGE, VERSION);
172 	p = str;
173 	l = snprintf(p, len,
174 	    "%s-%s:%s-%s:%s", PACKAGE, VERSION,
175 	    utn.sysname, utn.release, utn.machine);
176 	if (l == -1 || (size_t)(l + 1) > len)
177 		return -1;
178 	p += l;
179 	len -= (size_t)l;
180 	l = if_machinearch(p + 1, len - 1);
181 	if (l == -1 || (size_t)(l + 1) > len)
182 		return -1;
183 	*p = ':';
184 	p += l;
185 	return p - str;
186 }
187 
188 int
189 make_option_mask(const struct dhcp_opt *dopts, size_t dopts_len,
190     const struct dhcp_opt *odopts, size_t odopts_len,
191     uint8_t *mask, const char *opts, int add)
192 {
193 	char *token, *o, *p;
194 	const struct dhcp_opt *opt;
195 	int match, e;
196 	unsigned int n;
197 	size_t i;
198 
199 	if (opts == NULL)
200 		return -1;
201 	o = p = strdup(opts);
202 	while ((token = strsep(&p, ", "))) {
203 		if (*token == '\0')
204 			continue;
205 		if (strncmp(token, "dhcp6_", 6) == 0)
206 			token += 6;
207 		if (strncmp(token, "nd_", 3) == 0)
208 			token += 3;
209 		match = 0;
210 		for (i = 0, opt = odopts; i < odopts_len; i++, opt++) {
211 			if (opt->var == NULL || opt->option == 0)
212 				continue; /* buggy dhcpcd-definitions.conf */
213 			if (strcmp(opt->var, token) == 0)
214 				match = 1;
215 			else {
216 				n = (unsigned int)strtou(token, NULL, 0,
217 				    0, UINT_MAX, &e);
218 				if (e == 0 && opt->option == n)
219 					match = 1;
220 			}
221 			if (match)
222 				break;
223 		}
224 		if (match == 0) {
225 			for (i = 0, opt = dopts; i < dopts_len; i++, opt++) {
226 				if (strcmp(opt->var, token) == 0)
227 				        match = 1;
228 				else {
229 					n = (unsigned int)strtou(token, NULL, 0,
230 					    0, UINT_MAX, &e);
231 					if (e == 0 && opt->option == n)
232 						match = 1;
233 				}
234 				if (match)
235 					break;
236 			}
237 		}
238 		if (!match || !opt->option) {
239 			free(o);
240 			errno = ENOENT;
241 			return -1;
242 		}
243 		if (add == 2 && !(opt->type & OT_ADDRIPV4)) {
244 			free(o);
245 			errno = EINVAL;
246 			return -1;
247 		}
248 		if (add == 1 || add == 2)
249 			add_option_mask(mask, opt->option);
250 		else
251 			del_option_mask(mask, opt->option);
252 	}
253 	free(o);
254 	return 0;
255 }
256 
257 size_t
258 encode_rfc1035(const char *src, uint8_t *dst)
259 {
260 	uint8_t *p;
261 	uint8_t *lp;
262 	size_t len;
263 	uint8_t has_dot;
264 
265 	if (src == NULL || *src == '\0')
266 		return 0;
267 
268 	if (dst) {
269 		p = dst;
270 		lp = p++;
271 	}
272 	/* Silence bogus GCC warnings */
273 	else
274 		p = lp = NULL;
275 
276 	len = 1;
277 	has_dot = 0;
278 	for (; *src; src++) {
279 		if (*src == '\0')
280 			break;
281 		if (*src == '.') {
282 			/* Skip the trailing . */
283 			if (src[1] == '\0')
284 				break;
285 			has_dot = 1;
286 			if (dst) {
287 				*lp = (uint8_t)(p - lp - 1);
288 				if (*lp == '\0')
289 					return len;
290 				lp = p++;
291 			}
292 		} else if (dst)
293 			*p++ = (uint8_t)*src;
294 		len++;
295 	}
296 
297 	if (dst) {
298 		*lp = (uint8_t)(p - lp - 1);
299 		if (has_dot)
300 			*p++ = '\0';
301 	}
302 
303 	if (has_dot)
304 		len++;
305 
306 	return len;
307 }
308 
309 /* Decode an RFC1035 DNS search order option into a space
310  * separated string. Returns length of string (including
311  * terminating zero) or zero on error. out may be NULL
312  * to just determine output length. */
313 ssize_t
314 decode_rfc1035(char *out, size_t len, const uint8_t *p, size_t pl)
315 {
316 	const char *start;
317 	size_t start_len, l, d_len, o_len;
318 	const uint8_t *r, *q = p, *e;
319 	int hops;
320 	uint8_t ltype;
321 
322 	o_len = 0;
323 	start = out;
324 	start_len = len;
325 	q = p;
326 	e = p + pl;
327 	while (q < e) {
328 		r = NULL;
329 		d_len = 0;
330 		hops = 0;
331 		/* Check we are inside our length again in-case
332 		 * the name isn't fully qualified (ie, not terminated) */
333 		while (q < e && (l = (size_t)*q++)) {
334 			ltype = l & 0xc0;
335 			if (ltype == 0x80 || ltype == 0x40) {
336 				/* Currently reserved for future use as noted
337 				 * in RFC1035 4.1.4 as the 10 and 01
338 				 * combinations. */
339 				errno = ENOTSUP;
340 				return -1;
341 			}
342 			else if (ltype == 0xc0) { /* pointer */
343 				if (q == e) {
344 					errno = ERANGE;
345 					return -1;
346 				}
347 				l = (l & 0x3f) << 8;
348 				l |= *q++;
349 				/* save source of first jump. */
350 				if (!r)
351 					r = q;
352 				hops++;
353 				if (hops > 255) {
354 					errno = ERANGE;
355 					return -1;
356 				}
357 				q = p + l;
358 				if (q >= e) {
359 					errno = ERANGE;
360 					return -1;
361 				}
362 			} else {
363 				/* straightforward name segment, add with '.' */
364 				if (q + l > e) {
365 					errno = ERANGE;
366 					return -1;
367 				}
368 				if (l > NS_MAXLABEL) {
369 					errno = EINVAL;
370 					return -1;
371 				}
372 				d_len += l + 1;
373 				if (out) {
374 					if (l + 1 > len) {
375 						errno = ENOBUFS;
376 						return -1;
377 					}
378 					memcpy(out, q, l);
379 					out += l;
380 					*out++ = '.';
381 					len -= l;
382 					len--;
383 				}
384 				q += l;
385 			}
386 		}
387 
388 		/* Don't count the trailing NUL */
389 		if (d_len > NS_MAXDNAME + 1) {
390 			errno = E2BIG;
391 			return -1;
392 		}
393 		o_len += d_len;
394 
395 		/* change last dot to space */
396 		if (out && out != start)
397 			*(out - 1) = ' ';
398 		if (r)
399 			q = r;
400 	}
401 
402 	/* change last space to zero terminator */
403 	if (out) {
404 		if (out != start)
405 			*(out - 1) = '\0';
406 		else if (start_len > 0)
407 			*out = '\0';
408 	}
409 
410 	/* Remove the trailing NUL */
411 	if (o_len != 0)
412 		o_len--;
413 
414 	return (ssize_t)o_len;
415 }
416 
417 /* Check for a valid name as per RFC952 and RFC1123 section 2.1 */
418 static ssize_t
419 valid_domainname(char *lbl, int type)
420 {
421 	char *slbl = lbl, *lst = NULL;
422 	unsigned char c;
423 	int len = 0;
424 	bool start = true, errset = false;
425 
426 	if (lbl == NULL || *lbl == '\0') {
427 		errno = EINVAL;
428 		return 0;
429 	}
430 
431 	for (;;) {
432 		c = (unsigned char)*lbl++;
433 		if (c == '\0')
434 			return lbl - slbl - 1;
435 		if (c == ' ') {
436 			if (lbl - 1 == slbl) /* No space at start */
437 				break;
438 			if (!(type & OT_ARRAY))
439 				break;
440 			/* Skip to the next label */
441 			if (!start) {
442 				start = true;
443 				lst = lbl - 1;
444 			}
445 			if (len)
446 				len = 0;
447 			continue;
448 		}
449 		if (c == '.') {
450 			if (*lbl == '.')
451 				break;
452 			len = 0;
453 			continue;
454 		}
455 		if (((c == '-' || c == '_') &&
456 		    !start && *lbl != ' ' && *lbl != '\0') ||
457 		    isalnum(c))
458 		{
459 			if (++len > NS_MAXLABEL) {
460 				errno = ERANGE;
461 				errset = true;
462 				break;
463 			}
464 		} else
465 			break;
466 		if (start)
467 			start = false;
468 	}
469 
470 	if (!errset)
471 		errno = EINVAL;
472 	if (lst) {
473 		/* At least one valid domain, return it */
474 		*lst = '\0';
475 		return lst - slbl;
476 	}
477 	return 0;
478 }
479 
480 /*
481  * Prints a chunk of data to a string.
482  * PS_SHELL goes as it is these days, it's upto the target to validate it.
483  * PS_SAFE has all non ascii and non printables changes to escaped octal.
484  */
485 static const char hexchrs[] = "0123456789abcdef";
486 ssize_t
487 print_string(char *dst, size_t len, int type, const uint8_t *data, size_t dl)
488 {
489 	char *odst;
490 	uint8_t c;
491 	const uint8_t *e;
492 	size_t bytes;
493 
494 	odst = dst;
495 	bytes = 0;
496 	e = data + dl;
497 
498 	while (data < e) {
499 		c = *data++;
500 		if (type & OT_BINHEX) {
501 			if (dst) {
502 				if (len  == 0 || len == 1) {
503 					errno = ENOBUFS;
504 					return -1;
505 				}
506 				*dst++ = hexchrs[(c & 0xF0) >> 4];
507 				*dst++ = hexchrs[(c & 0x0F)];
508 				len -= 2;
509 			}
510 			bytes += 2;
511 			continue;
512 		}
513 		if (type & OT_ASCII && (!isascii(c))) {
514 			errno = EINVAL;
515 			break;
516 		}
517 		if (!(type & (OT_ASCII | OT_RAW | OT_ESCSTRING | OT_ESCFILE)) &&
518 		    (!isascii(c) && !isprint(c)))
519 		{
520 			errno = EINVAL;
521 			break;
522 		}
523 		if (type & OT_URI && isspace(c)) {
524 			errno = EINVAL;
525 			break;
526 		}
527 		if ((type & (OT_ESCSTRING | OT_ESCFILE) &&
528 		    (c == '\\' || !isascii(c) || !isprint(c))) ||
529 		    (type & OT_ESCFILE && (c == '/' || c == ' ')))
530 		{
531 			errno = EINVAL;
532 			if (c == '\\') {
533 				if (dst) {
534 					if (len  == 0 || len == 1) {
535 						errno = ENOBUFS;
536 						return -1;
537 					}
538 					*dst++ = '\\'; *dst++ = '\\';
539 					len -= 2;
540 				}
541 				bytes += 2;
542 				continue;
543 			}
544 			if (dst) {
545 				if (len < 5) {
546 					errno = ENOBUFS;
547 					return -1;
548 				}
549 				*dst++ = '\\';
550 		                *dst++ = (char)(((c >> 6) & 03) + '0');
551 		                *dst++ = (char)(((c >> 3) & 07) + '0');
552 		                *dst++ = (char)(( c       & 07) + '0');
553 				len -= 4;
554 			}
555 			bytes += 4;
556 		} else {
557 			if (dst) {
558 				if (len == 0) {
559 					errno = ENOBUFS;
560 					return -1;
561 				}
562 				*dst++ = (char)c;
563 				len--;
564 			}
565 			bytes++;
566 		}
567 	}
568 
569 	/* NULL */
570 	if (dst) {
571 		if (len == 0) {
572 			errno = ENOBUFS;
573 			return -1;
574 		}
575 		*dst = '\0';
576 
577 		/* Now we've printed it, validate the domain */
578 		if (type & OT_DOMAIN && !valid_domainname(odst, type)) {
579 			*odst = '\0';
580 			return 1;
581 		}
582 
583 	}
584 
585 	return (ssize_t)bytes;
586 }
587 
588 #define ADDR6SZ		16
589 static ssize_t
590 dhcp_optlen(const struct dhcp_opt *opt, size_t dl)
591 {
592 	size_t sz;
593 
594 	if (opt->type & OT_ADDRIPV6)
595 		sz = ADDR6SZ;
596 	else if (opt->type & (OT_INT32 | OT_UINT32 | OT_ADDRIPV4))
597 		sz = sizeof(uint32_t);
598 	else if (opt->type & (OT_INT16 | OT_UINT16))
599 		sz = sizeof(uint16_t);
600 	else if (opt->type & (OT_INT8 | OT_UINT8 | OT_BITFLAG))
601 		sz = sizeof(uint8_t);
602 	else if (opt->type & OT_FLAG)
603 		return 0;
604 	else {
605 		/* All other types are variable length */
606 		if (opt->len) {
607 			if ((size_t)opt->len > dl) {
608 				errno = EOVERFLOW;
609 				return -1;
610 			}
611 			return (ssize_t)opt->len;
612 		}
613 		return (ssize_t)dl;
614 	}
615 	if (dl < sz) {
616 		errno = EOVERFLOW;
617 		return -1;
618 	}
619 
620 	/* Trim any extra data.
621 	 * Maybe we need a setting to reject DHCP options with extra data? */
622 	if (opt->type & OT_ARRAY)
623 		return (ssize_t)(dl - (dl % sz));
624 	return (ssize_t)sz;
625 }
626 
627 static ssize_t
628 print_option(FILE *fp, const char *prefix, const struct dhcp_opt *opt,
629     int vname,
630     const uint8_t *data, size_t dl, const char *ifname)
631 {
632 	fpos_t fp_pos;
633 	const uint8_t *e, *t;
634 	uint16_t u16;
635 	int16_t s16;
636 	uint32_t u32;
637 	int32_t s32;
638 	struct in_addr addr;
639 	ssize_t sl;
640 	size_t l;
641 
642 	/* Ensure a valid length */
643 	dl = (size_t)dhcp_optlen(opt, dl);
644 	if ((ssize_t)dl == -1)
645 		return 0;
646 
647 	if (fgetpos(fp, &fp_pos) == -1)
648 		return -1;
649 	if (fprintf(fp, "%s", prefix) == -1)
650 		goto err;
651 
652 	/* We printed something, so always goto err from now-on
653 	 * to terminate the string. */
654 	if (vname) {
655 		if (fprintf(fp, "_%s", opt->var) == -1)
656 			goto err;
657 	}
658 	if (fputc('=', fp) == EOF)
659 		goto err;
660 	if (dl == 0)
661 		goto done;
662 
663 	if (opt->type & OT_RFC1035) {
664 		char domain[NS_MAXDNAME];
665 
666 		sl = decode_rfc1035(domain, sizeof(domain), data, dl);
667 		if (sl == -1)
668 			goto err;
669 		if (sl == 0)
670 			goto done;
671 		if (!valid_domainname(domain, opt->type))
672 			goto err;
673 		return efprintf(fp, "%s", domain);
674 	}
675 
676 #ifdef INET
677 	if (opt->type & OT_RFC3361)
678 		return print_rfc3361(fp, data, dl);
679 
680 	if (opt->type & OT_RFC3442)
681 		return print_rfc3442(fp, data, dl);
682 #endif
683 
684 	/* Produces a space separated list of URIs.
685 	 * This is valid as a URI cannot contain a space. */
686 	if ((opt->type & (OT_ARRAY | OT_URI)) == (OT_ARRAY | OT_URI)) {
687 #ifdef SMALL
688 		errno = ENOTSUP;
689 		return -1;
690 #else
691 		char buf[UINT16_MAX + 1];
692 		uint16_t sz;
693 		bool first = true;
694 
695 		while (dl) {
696 			if (dl < 2) {
697 				errno = EINVAL;
698 				goto err;
699 			}
700 
701 			memcpy(&u16, data, sizeof(u16));
702 			sz = ntohs(u16);
703 			data += sizeof(u16);
704 			dl -= sizeof(u16);
705 
706 			if (sz == 0)
707 				continue;
708 			if (sz > dl) {
709 				errno = EINVAL;
710 				goto err;
711 			}
712 
713 			if (print_string(buf, sizeof(buf),
714 			    opt->type, data, sz) == -1)
715 				goto err;
716 
717 			if (first)
718 				first = false;
719 			else if (fputc(' ', fp) == EOF)
720 				goto err;
721 
722 			if (fprintf(fp, "%s", buf) == -1)
723 				goto err;
724 
725 			data += sz;
726 			dl -= sz;
727 		}
728 
729 		if (fputc('\0', fp) == EOF)
730 			goto err;
731 		return 0;
732 #endif
733 	}
734 
735 	if (opt->type & (OT_STRING | OT_URI)) {
736 		char buf[1024];
737 
738 		if (print_string(buf, sizeof(buf), opt->type, data, dl) == -1)
739 			goto err;
740 		return efprintf(fp, "%s", buf);
741 	}
742 
743 	if (opt->type & OT_FLAG)
744 		return efprintf(fp, "1");
745 
746 	if (opt->type & OT_BITFLAG) {
747 		/* bitflags are a string, MSB first, such as ABCDEFGH
748 		 * where A is 10000000, B is 01000000, etc. */
749 		for (l = 0, sl = sizeof(opt->bitflags) - 1;
750 		    l < sizeof(opt->bitflags);
751 		    l++, sl--)
752 		{
753 			/* Don't print NULL or 0 flags */
754 			if (opt->bitflags[l] != '\0' &&
755 			    opt->bitflags[l] != '0' &&
756 			    *data & (1 << sl))
757 			{
758 				if (fputc(opt->bitflags[l], fp) == EOF)
759 					goto err;
760 			}
761 		}
762 		goto done;
763 	}
764 
765 	t = data;
766 	e = data + dl;
767 	while (data < e) {
768 		if (data != t) {
769 			if (fputc(' ', fp) == EOF)
770 				goto err;
771 		}
772 		if (opt->type & OT_UINT8) {
773 			if (fprintf(fp, "%u", *data) == -1)
774 				goto err;
775 			data++;
776 		} else if (opt->type & OT_INT8) {
777 			if (fprintf(fp, "%d", *data) == -1)
778 				goto err;
779 			data++;
780 		} else if (opt->type & OT_UINT16) {
781 			memcpy(&u16, data, sizeof(u16));
782 			u16 = ntohs(u16);
783 			if (fprintf(fp, "%u", u16) == -1)
784 				goto err;
785 			data += sizeof(u16);
786 		} else if (opt->type & OT_INT16) {
787 			memcpy(&u16, data, sizeof(u16));
788 			s16 = (int16_t)ntohs(u16);
789 			if (fprintf(fp, "%d", s16) == -1)
790 				goto err;
791 			data += sizeof(u16);
792 		} else if (opt->type & OT_UINT32) {
793 			memcpy(&u32, data, sizeof(u32));
794 			u32 = ntohl(u32);
795 			if (fprintf(fp, "%u", u32) == -1)
796 				goto err;
797 			data += sizeof(u32);
798 		} else if (opt->type & OT_INT32) {
799 			memcpy(&u32, data, sizeof(u32));
800 			s32 = (int32_t)ntohl(u32);
801 			if (fprintf(fp, "%d", s32) == -1)
802 				goto err;
803 			data += sizeof(u32);
804 		} else if (opt->type & OT_ADDRIPV4) {
805 			memcpy(&addr.s_addr, data, sizeof(addr.s_addr));
806 			if (fprintf(fp, "%s", inet_ntoa(addr)) == -1)
807 				goto err;
808 			data += sizeof(addr.s_addr);
809 		} else if (opt->type & OT_ADDRIPV6) {
810 			char buf[INET6_ADDRSTRLEN];
811 
812 			if (inet_ntop(AF_INET6, data, buf, sizeof(buf)) == NULL)
813 				goto err;
814 			if (fprintf(fp, "%s", buf) == -1)
815 				goto err;
816 			if (data[0] == 0xfe && (data[1] & 0xc0) == 0x80) {
817 				if (fprintf(fp,"%%%s", ifname) == -1)
818 					goto err;
819 			}
820 			data += 16;
821 		} else {
822 			errno = EINVAL;
823 			goto err;
824 		}
825 	}
826 
827 done:
828 	if (fputc('\0', fp) == EOF)
829 		return -1;
830 	return 1;
831 
832 err:
833 	(void)fsetpos(fp, &fp_pos);
834 	return -1;
835 }
836 
837 int
838 dhcp_set_leasefile(char *leasefile, size_t len, int family,
839     const struct interface *ifp)
840 {
841 	char ssid[1 + (IF_SSIDLEN * 4) + 1]; /* - prefix and NUL terminated. */
842 
843 	if (ifp->name[0] == '\0') {
844 		strlcpy(leasefile, ifp->ctx->pidfile, len);
845 		return 0;
846 	}
847 
848 	switch (family) {
849 	case AF_INET:
850 	case AF_INET6:
851 		break;
852 	default:
853 		errno = EINVAL;
854 		return -1;
855 	}
856 
857 	if (ifp->wireless) {
858 		ssid[0] = '-';
859 		print_string(ssid + 1, sizeof(ssid) - 1,
860 		    OT_ESCFILE,
861 		    (const uint8_t *)ifp->ssid, ifp->ssid_len);
862 	} else
863 		ssid[0] = '\0';
864 	return snprintf(leasefile, len,
865 	    family == AF_INET ? LEASEFILE : LEASEFILE6,
866 	    ifp->name, ssid);
867 }
868 
869 void
870 dhcp_envoption(struct dhcpcd_ctx *ctx, FILE *fp, const char *prefix,
871     const char *ifname, struct dhcp_opt *opt,
872     const uint8_t *(*dgetopt)(struct dhcpcd_ctx *,
873     size_t *, unsigned int *, size_t *,
874     const uint8_t *, size_t, struct dhcp_opt **),
875     const uint8_t *od, size_t ol)
876 {
877 	size_t i, eos, eol;
878 	ssize_t eo;
879 	unsigned int eoc;
880 	const uint8_t *eod;
881 	int ov;
882 	struct dhcp_opt *eopt, *oopt;
883 	char *pfx;
884 
885 	/* If no embedded or encapsulated options, it's easy */
886 	if (opt->embopts_len == 0 && opt->encopts_len == 0) {
887 		if (opt->type & OT_RESERVED)
888 			return;
889 		if (print_option(fp, prefix, opt, 1, od, ol, ifname) == -1)
890 			logerr("%s: %s %d", ifname, __func__, opt->option);
891 		return;
892 	}
893 
894 	/* Create a new prefix based on the option */
895 	if (opt->type & OT_INDEX) {
896 		if (asprintf(&pfx, "%s_%s%d",
897 		    prefix, opt->var, ++opt->index) == -1)
898 			pfx = NULL;
899 	} else {
900 		if (asprintf(&pfx, "%s_%s", prefix, opt->var) == -1)
901 			pfx = NULL;
902 	}
903 	if (pfx == NULL) {
904 		logerr(__func__);
905 		return;
906 	}
907 
908 	/* Embedded options are always processed first as that
909 	 * is a fixed layout */
910 	for (i = 0, eopt = opt->embopts; i < opt->embopts_len; i++, eopt++) {
911 		eo = dhcp_optlen(eopt, ol);
912 		if (eo == -1) {
913 			logerrx("%s: %s %d.%d/%zu: "
914 			    "malformed embedded option",
915 			    ifname, __func__, opt->option,
916 			    eopt->option, i);
917 			goto out;
918 		}
919 		if (eo == 0) {
920 			/* An option was expected, but there is no data
921 			 * data for it.
922 			 * This may not be an error as some options like
923 			 * DHCP FQDN in RFC4702 have a string as the last
924 			 * option which is optional. */
925 			if (ol != 0 || !(eopt->type & OT_OPTIONAL))
926 				logerrx("%s: %s %d.%d/%zu: "
927 				    "missing embedded option",
928 				    ifname, __func__, opt->option,
929 				    eopt->option, i);
930 			goto out;
931 		}
932 		/* Use the option prefix if the embedded option
933 		 * name is different.
934 		 * This avoids new_fqdn_fqdn which would be silly. */
935 		if (!(eopt->type & OT_RESERVED)) {
936 			ov = strcmp(opt->var, eopt->var);
937 			if (print_option(fp, pfx, eopt, ov, od, (size_t)eo,
938 			    ifname) == -1)
939 				logerr("%s: %s %d.%d/%zu",
940 				    ifname, __func__,
941 				    opt->option, eopt->option, i);
942 		}
943 		od += (size_t)eo;
944 		ol -= (size_t)eo;
945 	}
946 
947 	/* Enumerate our encapsulated options */
948 	if (opt->encopts_len && ol > 0) {
949 		/* Zero any option indexes
950 		 * We assume that referenced encapsulated options are NEVER
951 		 * recursive as the index order could break. */
952 		for (i = 0, eopt = opt->encopts;
953 		    i < opt->encopts_len;
954 		    i++, eopt++)
955 		{
956 			eoc = opt->option;
957 			if (eopt->type & OT_OPTION) {
958 				dgetopt(ctx, NULL, &eoc, NULL, NULL, 0, &oopt);
959 				if (oopt)
960 					oopt->index = 0;
961 			}
962 		}
963 
964 		while ((eod = dgetopt(ctx, &eos, &eoc, &eol, od, ol, &oopt))) {
965 			for (i = 0, eopt = opt->encopts;
966 			    i < opt->encopts_len;
967 			    i++, eopt++)
968 			{
969 				if (eopt->option != eoc)
970 					continue;
971 				if (eopt->type & OT_OPTION) {
972 					if (oopt == NULL)
973 						/* Report error? */
974 						continue;
975 				}
976 				dhcp_envoption(ctx, fp, pfx, ifname,
977 				    eopt->type & OT_OPTION ? oopt:eopt,
978 				    dgetopt, eod, eol);
979 			}
980 			od += eos + eol;
981 			ol -= eos + eol;
982 		}
983 	}
984 
985 out:
986 	free(pfx);
987 }
988 
989 void
990 dhcp_zero_index(struct dhcp_opt *opt)
991 {
992 	size_t i;
993 	struct dhcp_opt *o;
994 
995 	opt->index = 0;
996 	for (i = 0, o = opt->embopts; i < opt->embopts_len; i++, o++)
997 		dhcp_zero_index(o);
998 	for (i = 0, o = opt->encopts; i < opt->encopts_len; i++, o++)
999 		dhcp_zero_index(o);
1000 }
1001 
1002 ssize_t
1003 dhcp_readfile(struct dhcpcd_ctx *ctx, const char *file, void *data, size_t len)
1004 {
1005 
1006 #ifdef PRIVSEP
1007 	if (ctx->options & DHCPCD_PRIVSEP &&
1008 	    !(ctx->options & DHCPCD_PRIVSEPROOT))
1009 		return ps_root_readfile(ctx, file, data, len);
1010 #else
1011 	UNUSED(ctx);
1012 #endif
1013 
1014 	return readfile(file, data, len);
1015 }
1016 
1017 ssize_t
1018 dhcp_writefile(struct dhcpcd_ctx *ctx, const char *file, mode_t mode,
1019     const void *data, size_t len)
1020 {
1021 
1022 #ifdef PRIVSEP
1023 	if (ctx->options & DHCPCD_PRIVSEP &&
1024 	    !(ctx->options & DHCPCD_PRIVSEPROOT))
1025 		return ps_root_writefile(ctx, file, mode, data, len);
1026 #else
1027 	UNUSED(ctx);
1028 #endif
1029 
1030 	return writefile(file, mode, data, len);
1031 }
1032 
1033 int
1034 dhcp_filemtime(struct dhcpcd_ctx *ctx, const char *file, time_t *time)
1035 {
1036 
1037 #ifdef PRIVSEP
1038 	if (ctx->options & DHCPCD_PRIVSEP &&
1039 	    !(ctx->options & DHCPCD_PRIVSEPROOT))
1040 		return (int)ps_root_filemtime(ctx, file, time);
1041 #else
1042 	UNUSED(ctx);
1043 #endif
1044 
1045 	return filemtime(file, time);
1046 }
1047 
1048 int
1049 dhcp_unlink(struct dhcpcd_ctx *ctx, const char *file)
1050 {
1051 
1052 #ifdef PRIVSEP
1053 	if (ctx->options & DHCPCD_PRIVSEP &&
1054 	    !(ctx->options & DHCPCD_PRIVSEPROOT))
1055 		return (int)ps_root_unlink(ctx, file);
1056 #else
1057 	UNUSED(ctx);
1058 #endif
1059 
1060 	return unlink(file);
1061 }
1062 
1063 size_t
1064 dhcp_read_hwaddr_aton(struct dhcpcd_ctx *ctx, uint8_t **data, const char *file)
1065 {
1066 	char buf[BUFSIZ];
1067 	ssize_t bytes;
1068 	size_t len;
1069 
1070 	bytes = dhcp_readfile(ctx, file, buf, sizeof(buf));
1071 	if (bytes == -1 || bytes == sizeof(buf))
1072 		return 0;
1073 
1074 	bytes[buf] = '\0';
1075 	len = hwaddr_aton(NULL, buf);
1076 	if (len == 0)
1077 		return 0;
1078 	*data = malloc(len);
1079 	if (*data == NULL)
1080 		return 0;
1081 	hwaddr_aton(*data, buf);
1082 	return len;
1083 }
1084