Update to dhcpcd-9.4.1 with the following changes:
[dragonfly.git] / contrib / dhcpcd / src / dhcp-common.c
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
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         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
139 struct dhcp_opt *
140 vivso_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
160 ssize_t
161 dhcp_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
186 int
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)
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
255 size_t
256 encode_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. */
311 ssize_t
312 decode_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 */
416 static int
417 valid_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  */
486 static const char hexchrs[] = "0123456789abcdef";
487 ssize_t
488 print_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
586 static ssize_t
587 dhcp_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
624 static ssize_t
625 print_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
773 done:
774         if (fputc('\0', fp) == EOF)
775                 return -1;
776         return 1;
777
778 err:
779         (void)fsetpos(fp, &fp_pos);
780         return -1;
781 }
782
783 int
784 dhcp_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
815 void
816 dhcp_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
931 out:
932         free(pfx);
933 }
934
935 void
936 dhcp_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
948 ssize_t
949 dhcp_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
963 ssize_t
964 dhcp_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
979 int
980 dhcp_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
994 int
995 dhcp_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
1009 size_t
1010 dhcp_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 }