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