Import dhcpcd-10.0.2 with the following changes:
[dragonfly.git] / contrib / dhcpcd / src / dhcp-common.c
blob679af243286ae237745094eaf7ace3c93a4cf25a
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
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.
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.
29 #include <sys/utsname.h>
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>
39 #include "config.h"
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"
49 const char *
50 dhcp_get_hostname(char *buf, size_t buf_len, const struct if_options *ifo)
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);
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;
67 /* Shorten the hostname if required */
68 if (ifo->options & DHCPCD_HOSTNAME_SHORT) {
69 char *hp;
71 hp = strchr(buf, '.');
72 if (hp != NULL)
73 *hp = '\0';
76 return buf;
79 void
80 dhcp_print_option_encoding(const struct dhcp_opt *opt, int cols)
83 while (cols < 40) {
84 putchar(' ');
85 cols++;
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');
139 struct dhcp_opt *
140 vivso_find(uint32_t iana_en, const void *arg)
142 const struct interface *ifp;
143 size_t i;
144 struct dhcp_opt *opt;
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;
160 ssize_t
161 dhcp_vendor(char *str, size_t len)
163 struct utsname utn;
164 char *p;
165 int l;
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;
187 make_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)
191 char *token, *o, *p;
192 const struct dhcp_opt *opt;
193 int match, e;
194 unsigned int n;
195 size_t i;
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;
219 if (match)
220 break;
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;
232 if (match)
233 break;
236 if (!match || !opt->option) {
237 free(o);
238 errno = ENOENT;
239 return -1;
241 if (add == 2 && !(opt->type & OT_ADDRIPV4)) {
242 free(o);
243 errno = EINVAL;
244 return -1;
246 if (add == 1 || add == 2)
247 add_option_mask(mask, opt->option);
248 else
249 del_option_mask(mask, opt->option);
251 free(o);
252 return 0;
255 size_t
256 encode_rfc1035(const char *src, uint8_t *dst)
258 uint8_t *p;
259 uint8_t *lp;
260 size_t len;
261 uint8_t has_dot;
263 if (src == NULL || *src == '\0')
264 return 0;
266 if (dst) {
267 p = dst;
268 lp = p++;
270 /* Silence bogus GCC warnings */
271 else
272 p = lp = NULL;
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++;
290 } else if (dst)
291 *p++ = (uint8_t)*src;
292 len++;
295 if (dst) {
296 *lp = (uint8_t)(p - lp - 1);
297 if (has_dot)
298 *p++ = '\0';
301 if (has_dot)
302 len++;
304 return len;
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. */
311 ssize_t
312 decode_rfc1035(char *out, size_t len, const uint8_t *p, size_t pl)
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;
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;
340 else if (ltype == 0xc0) { /* pointer */
341 if (q == e) {
342 errno = ERANGE;
343 return -1;
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;
355 q = p + l;
356 if (q >= e) {
357 errno = ERANGE;
358 return -1;
360 } else {
361 /* straightforward name segment, add with '.' */
362 if (q + l > e) {
363 errno = ERANGE;
364 return -1;
366 if (l > NS_MAXLABEL) {
367 errno = EINVAL;
368 return -1;
370 d_len += l + 1;
371 if (out) {
372 if (l + 1 > len) {
373 errno = ENOBUFS;
374 return -1;
376 memcpy(out, q, l);
377 out += l;
378 *out++ = '.';
379 len -= l;
380 len--;
382 q += l;
386 /* Don't count the trailing NUL */
387 if (d_len > NS_MAXDNAME + 1) {
388 errno = E2BIG;
389 return -1;
391 o_len += d_len;
393 /* change last dot to space */
394 if (out && out != start)
395 *(out - 1) = ' ';
396 if (r)
397 q = r;
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';
408 /* Remove the trailing NUL */
409 if (o_len != 0)
410 o_len--;
412 return (ssize_t)o_len;
415 /* Check for a valid name as per RFC952 and RFC1123 section 2.1 */
416 static ssize_t
417 valid_domainname(char *lbl, int type)
419 char *slbl = lbl, *lst = NULL;
420 unsigned char c;
421 int len = 0;
422 bool start = true, errset = false;
424 if (lbl == NULL || *lbl == '\0') {
425 errno = EINVAL;
426 return 0;
429 for (;;) {
430 c = (unsigned char)*lbl++;
431 if (c == '\0')
432 return lbl - slbl - 1;
433 if (c == ' ') {
434 if (lbl - 1 == slbl) /* No space at start */
435 break;
436 if (!(type & OT_ARRAY))
437 break;
438 /* Skip to the next label */
439 if (!start) {
440 start = true;
441 lst = lbl - 1;
443 if (len)
444 len = 0;
445 continue;
447 if (c == '.') {
448 if (*lbl == '.')
449 break;
450 len = 0;
451 continue;
453 if (((c == '-' || c == '_') &&
454 !start && *lbl != ' ' && *lbl != '\0') ||
455 isalnum(c))
457 if (++len > NS_MAXLABEL) {
458 errno = ERANGE;
459 errset = true;
460 break;
462 } else
463 break;
464 if (start)
465 start = false;
468 if (!errset)
469 errno = EINVAL;
470 if (lst) {
471 /* At least one valid domain, return it */
472 *lst = '\0';
473 return lst - slbl;
475 return 0;
479 * Prints a chunk of data to a string.
480 * PS_SHELL goes as it is these days, it's upto the target to validate it.
481 * PS_SAFE has all non ascii and non printables changes to escaped octal.
483 static const char hexchrs[] = "0123456789abcdef";
484 ssize_t
485 print_string(char *dst, size_t len, int type, const uint8_t *data, size_t dl)
487 char *odst;
488 uint8_t c;
489 const uint8_t *e;
490 size_t bytes;
492 odst = dst;
493 bytes = 0;
494 e = data + dl;
496 while (data < e) {
497 c = *data++;
498 if (type & OT_BINHEX) {
499 if (dst) {
500 if (len == 0 || len == 1) {
501 errno = ENOBUFS;
502 return -1;
504 *dst++ = hexchrs[(c & 0xF0) >> 4];
505 *dst++ = hexchrs[(c & 0x0F)];
506 len -= 2;
508 bytes += 2;
509 continue;
511 if (type & OT_ASCII && (!isascii(c))) {
512 errno = EINVAL;
513 break;
515 if (!(type & (OT_ASCII | OT_RAW | OT_ESCSTRING | OT_ESCFILE)) &&
516 (!isascii(c) && !isprint(c)))
518 errno = EINVAL;
519 break;
521 if ((type & (OT_ESCSTRING | OT_ESCFILE) &&
522 (c == '\\' || !isascii(c) || !isprint(c))) ||
523 (type & OT_ESCFILE && (c == '/' || c == ' ')))
525 errno = EINVAL;
526 if (c == '\\') {
527 if (dst) {
528 if (len == 0 || len == 1) {
529 errno = ENOBUFS;
530 return -1;
532 *dst++ = '\\'; *dst++ = '\\';
533 len -= 2;
535 bytes += 2;
536 continue;
538 if (dst) {
539 if (len < 5) {
540 errno = ENOBUFS;
541 return -1;
543 *dst++ = '\\';
544 *dst++ = (char)(((c >> 6) & 03) + '0');
545 *dst++ = (char)(((c >> 3) & 07) + '0');
546 *dst++ = (char)(( c & 07) + '0');
547 len -= 4;
549 bytes += 4;
550 } else {
551 if (dst) {
552 if (len == 0) {
553 errno = ENOBUFS;
554 return -1;
556 *dst++ = (char)c;
557 len--;
559 bytes++;
563 /* NULL */
564 if (dst) {
565 if (len == 0) {
566 errno = ENOBUFS;
567 return -1;
569 *dst = '\0';
571 /* Now we've printed it, validate the domain */
572 if (type & OT_DOMAIN && !valid_domainname(odst, type)) {
573 *odst = '\0';
574 return 1;
579 return (ssize_t)bytes;
582 #define ADDR6SZ 16
583 static ssize_t
584 dhcp_optlen(const struct dhcp_opt *opt, size_t dl)
586 size_t sz;
588 if (opt->type & OT_ADDRIPV6)
589 sz = ADDR6SZ;
590 else if (opt->type & (OT_INT32 | OT_UINT32 | OT_ADDRIPV4))
591 sz = sizeof(uint32_t);
592 else if (opt->type & (OT_INT16 | OT_UINT16))
593 sz = sizeof(uint16_t);
594 else if (opt->type & (OT_INT8 | OT_UINT8 | OT_BITFLAG))
595 sz = sizeof(uint8_t);
596 else if (opt->type & OT_FLAG)
597 return 0;
598 else {
599 /* All other types are variable length */
600 if (opt->len) {
601 if ((size_t)opt->len > dl) {
602 errno = EOVERFLOW;
603 return -1;
605 return (ssize_t)opt->len;
607 return (ssize_t)dl;
609 if (dl < sz) {
610 errno = EOVERFLOW;
611 return -1;
614 /* Trim any extra data.
615 * Maybe we need a setting to reject DHCP options with extra data? */
616 if (opt->type & OT_ARRAY)
617 return (ssize_t)(dl - (dl % sz));
618 return (ssize_t)sz;
621 static ssize_t
622 print_option(FILE *fp, const char *prefix, const struct dhcp_opt *opt,
623 int vname,
624 const uint8_t *data, size_t dl, const char *ifname)
626 fpos_t fp_pos;
627 const uint8_t *e, *t;
628 uint16_t u16;
629 int16_t s16;
630 uint32_t u32;
631 int32_t s32;
632 struct in_addr addr;
633 ssize_t sl;
634 size_t l;
636 /* Ensure a valid length */
637 dl = (size_t)dhcp_optlen(opt, dl);
638 if ((ssize_t)dl == -1)
639 return 0;
641 if (fgetpos(fp, &fp_pos) == -1)
642 return -1;
643 if (fprintf(fp, "%s", prefix) == -1)
644 goto err;
646 /* We printed something, so always goto err from now-on
647 * to terminate the string. */
648 if (vname) {
649 if (fprintf(fp, "_%s", opt->var) == -1)
650 goto err;
652 if (fputc('=', fp) == EOF)
653 goto err;
654 if (dl == 0)
655 goto done;
657 if (opt->type & OT_RFC1035) {
658 char domain[NS_MAXDNAME];
660 sl = decode_rfc1035(domain, sizeof(domain), data, dl);
661 if (sl == -1)
662 goto err;
663 if (sl == 0)
664 goto done;
665 if (!valid_domainname(domain, opt->type))
666 goto err;
667 return efprintf(fp, "%s", domain);
670 #ifdef INET
671 if (opt->type & OT_RFC3361)
672 return print_rfc3361(fp, data, dl);
674 if (opt->type & OT_RFC3442)
675 return print_rfc3442(fp, data, dl);
676 #endif
678 if (opt->type & OT_STRING) {
679 char buf[1024];
681 if (print_string(buf, sizeof(buf), opt->type, data, dl) == -1)
682 goto err;
683 return efprintf(fp, "%s", buf);
686 if (opt->type & OT_FLAG)
687 return efprintf(fp, "1");
689 if (opt->type & OT_BITFLAG) {
690 /* bitflags are a string, MSB first, such as ABCDEFGH
691 * where A is 10000000, B is 01000000, etc. */
692 for (l = 0, sl = sizeof(opt->bitflags) - 1;
693 l < sizeof(opt->bitflags);
694 l++, sl--)
696 /* Don't print NULL or 0 flags */
697 if (opt->bitflags[l] != '\0' &&
698 opt->bitflags[l] != '0' &&
699 *data & (1 << sl))
701 if (fputc(opt->bitflags[l], fp) == EOF)
702 goto err;
705 goto done;
708 t = data;
709 e = data + dl;
710 while (data < e) {
711 if (data != t) {
712 if (fputc(' ', fp) == EOF)
713 goto err;
715 if (opt->type & OT_UINT8) {
716 if (fprintf(fp, "%u", *data) == -1)
717 goto err;
718 data++;
719 } else if (opt->type & OT_INT8) {
720 if (fprintf(fp, "%d", *data) == -1)
721 goto err;
722 data++;
723 } else if (opt->type & OT_UINT16) {
724 memcpy(&u16, data, sizeof(u16));
725 u16 = ntohs(u16);
726 if (fprintf(fp, "%u", u16) == -1)
727 goto err;
728 data += sizeof(u16);
729 } else if (opt->type & OT_INT16) {
730 memcpy(&u16, data, sizeof(u16));
731 s16 = (int16_t)ntohs(u16);
732 if (fprintf(fp, "%d", s16) == -1)
733 goto err;
734 data += sizeof(u16);
735 } else if (opt->type & OT_UINT32) {
736 memcpy(&u32, data, sizeof(u32));
737 u32 = ntohl(u32);
738 if (fprintf(fp, "%u", u32) == -1)
739 goto err;
740 data += sizeof(u32);
741 } else if (opt->type & OT_INT32) {
742 memcpy(&u32, data, sizeof(u32));
743 s32 = (int32_t)ntohl(u32);
744 if (fprintf(fp, "%d", s32) == -1)
745 goto err;
746 data += sizeof(u32);
747 } else if (opt->type & OT_ADDRIPV4) {
748 memcpy(&addr.s_addr, data, sizeof(addr.s_addr));
749 if (fprintf(fp, "%s", inet_ntoa(addr)) == -1)
750 goto err;
751 data += sizeof(addr.s_addr);
752 } else if (opt->type & OT_ADDRIPV6) {
753 char buf[INET6_ADDRSTRLEN];
755 if (inet_ntop(AF_INET6, data, buf, sizeof(buf)) == NULL)
756 goto err;
757 if (fprintf(fp, "%s", buf) == -1)
758 goto err;
759 if (data[0] == 0xfe && (data[1] & 0xc0) == 0x80) {
760 if (fprintf(fp,"%%%s", ifname) == -1)
761 goto err;
763 data += 16;
764 } else {
765 errno = EINVAL;
766 goto err;
770 done:
771 if (fputc('\0', fp) == EOF)
772 return -1;
773 return 1;
775 err:
776 (void)fsetpos(fp, &fp_pos);
777 return -1;
781 dhcp_set_leasefile(char *leasefile, size_t len, int family,
782 const struct interface *ifp)
784 char ssid[1 + (IF_SSIDLEN * 4) + 1]; /* - prefix and NUL terminated. */
786 if (ifp->name[0] == '\0') {
787 strlcpy(leasefile, ifp->ctx->pidfile, len);
788 return 0;
791 switch (family) {
792 case AF_INET:
793 case AF_INET6:
794 break;
795 default:
796 errno = EINVAL;
797 return -1;
800 if (ifp->wireless) {
801 ssid[0] = '-';
802 print_string(ssid + 1, sizeof(ssid) - 1,
803 OT_ESCFILE,
804 (const uint8_t *)ifp->ssid, ifp->ssid_len);
805 } else
806 ssid[0] = '\0';
807 return snprintf(leasefile, len,
808 family == AF_INET ? LEASEFILE : LEASEFILE6,
809 ifp->name, ssid);
812 void
813 dhcp_envoption(struct dhcpcd_ctx *ctx, FILE *fp, const char *prefix,
814 const char *ifname, struct dhcp_opt *opt,
815 const uint8_t *(*dgetopt)(struct dhcpcd_ctx *,
816 size_t *, unsigned int *, size_t *,
817 const uint8_t *, size_t, struct dhcp_opt **),
818 const uint8_t *od, size_t ol)
820 size_t i, eos, eol;
821 ssize_t eo;
822 unsigned int eoc;
823 const uint8_t *eod;
824 int ov;
825 struct dhcp_opt *eopt, *oopt;
826 char *pfx;
828 /* If no embedded or encapsulated options, it's easy */
829 if (opt->embopts_len == 0 && opt->encopts_len == 0) {
830 if (opt->type & OT_RESERVED)
831 return;
832 if (print_option(fp, prefix, opt, 1, od, ol, ifname) == -1)
833 logerr("%s: %s %d", ifname, __func__, opt->option);
834 return;
837 /* Create a new prefix based on the option */
838 if (opt->type & OT_INDEX) {
839 if (asprintf(&pfx, "%s_%s%d",
840 prefix, opt->var, ++opt->index) == -1)
841 pfx = NULL;
842 } else {
843 if (asprintf(&pfx, "%s_%s", prefix, opt->var) == -1)
844 pfx = NULL;
846 if (pfx == NULL) {
847 logerr(__func__);
848 return;
851 /* Embedded options are always processed first as that
852 * is a fixed layout */
853 for (i = 0, eopt = opt->embopts; i < opt->embopts_len; i++, eopt++) {
854 eo = dhcp_optlen(eopt, ol);
855 if (eo == -1) {
856 logerrx("%s: %s %d.%d/%zu: "
857 "malformed embedded option",
858 ifname, __func__, opt->option,
859 eopt->option, i);
860 goto out;
862 if (eo == 0) {
863 /* An option was expected, but there is no data
864 * data for it.
865 * This may not be an error as some options like
866 * DHCP FQDN in RFC4702 have a string as the last
867 * option which is optional. */
868 if (ol != 0 || !(eopt->type & OT_OPTIONAL))
869 logerrx("%s: %s %d.%d/%zu: "
870 "missing embedded option",
871 ifname, __func__, opt->option,
872 eopt->option, i);
873 goto out;
875 /* Use the option prefix if the embedded option
876 * name is different.
877 * This avoids new_fqdn_fqdn which would be silly. */
878 if (!(eopt->type & OT_RESERVED)) {
879 ov = strcmp(opt->var, eopt->var);
880 if (print_option(fp, pfx, eopt, ov, od, (size_t)eo,
881 ifname) == -1)
882 logerr("%s: %s %d.%d/%zu",
883 ifname, __func__,
884 opt->option, eopt->option, i);
886 od += (size_t)eo;
887 ol -= (size_t)eo;
890 /* Enumerate our encapsulated options */
891 if (opt->encopts_len && ol > 0) {
892 /* Zero any option indexes
893 * We assume that referenced encapsulated options are NEVER
894 * recursive as the index order could break. */
895 for (i = 0, eopt = opt->encopts;
896 i < opt->encopts_len;
897 i++, eopt++)
899 eoc = opt->option;
900 if (eopt->type & OT_OPTION) {
901 dgetopt(ctx, NULL, &eoc, NULL, NULL, 0, &oopt);
902 if (oopt)
903 oopt->index = 0;
907 while ((eod = dgetopt(ctx, &eos, &eoc, &eol, od, ol, &oopt))) {
908 for (i = 0, eopt = opt->encopts;
909 i < opt->encopts_len;
910 i++, eopt++)
912 if (eopt->option != eoc)
913 continue;
914 if (eopt->type & OT_OPTION) {
915 if (oopt == NULL)
916 /* Report error? */
917 continue;
919 dhcp_envoption(ctx, fp, pfx, ifname,
920 eopt->type & OT_OPTION ? oopt:eopt,
921 dgetopt, eod, eol);
923 od += eos + eol;
924 ol -= eos + eol;
928 out:
929 free(pfx);
932 void
933 dhcp_zero_index(struct dhcp_opt *opt)
935 size_t i;
936 struct dhcp_opt *o;
938 opt->index = 0;
939 for (i = 0, o = opt->embopts; i < opt->embopts_len; i++, o++)
940 dhcp_zero_index(o);
941 for (i = 0, o = opt->encopts; i < opt->encopts_len; i++, o++)
942 dhcp_zero_index(o);
945 ssize_t
946 dhcp_readfile(struct dhcpcd_ctx *ctx, const char *file, void *data, size_t len)
949 #ifdef PRIVSEP
950 if (ctx->options & DHCPCD_PRIVSEP &&
951 !(ctx->options & DHCPCD_PRIVSEPROOT))
952 return ps_root_readfile(ctx, file, data, len);
953 #else
954 UNUSED(ctx);
955 #endif
957 return readfile(file, data, len);
960 ssize_t
961 dhcp_writefile(struct dhcpcd_ctx *ctx, const char *file, mode_t mode,
962 const void *data, size_t len)
965 #ifdef PRIVSEP
966 if (ctx->options & DHCPCD_PRIVSEP &&
967 !(ctx->options & DHCPCD_PRIVSEPROOT))
968 return ps_root_writefile(ctx, file, mode, data, len);
969 #else
970 UNUSED(ctx);
971 #endif
973 return writefile(file, mode, data, len);
977 dhcp_filemtime(struct dhcpcd_ctx *ctx, const char *file, time_t *time)
980 #ifdef PRIVSEP
981 if (ctx->options & DHCPCD_PRIVSEP &&
982 !(ctx->options & DHCPCD_PRIVSEPROOT))
983 return (int)ps_root_filemtime(ctx, file, time);
984 #else
985 UNUSED(ctx);
986 #endif
988 return filemtime(file, time);
992 dhcp_unlink(struct dhcpcd_ctx *ctx, const char *file)
995 #ifdef PRIVSEP
996 if (ctx->options & DHCPCD_PRIVSEP &&
997 !(ctx->options & DHCPCD_PRIVSEPROOT))
998 return (int)ps_root_unlink(ctx, file);
999 #else
1000 UNUSED(ctx);
1001 #endif
1003 return unlink(file);
1006 size_t
1007 dhcp_read_hwaddr_aton(struct dhcpcd_ctx *ctx, uint8_t **data, const char *file)
1009 char buf[BUFSIZ];
1010 ssize_t bytes;
1011 size_t len;
1013 bytes = dhcp_readfile(ctx, file, buf, sizeof(buf));
1014 if (bytes == -1 || bytes == sizeof(buf))
1015 return 0;
1017 bytes[buf] = '\0';
1018 len = hwaddr_aton(NULL, buf);
1019 if (len == 0)
1020 return 0;
1021 *data = malloc(len);
1022 if (*data == NULL)
1023 return 0;
1024 hwaddr_aton(*data, buf);
1025 return len;