]> git.ipfire.org Git - thirdparty/dhcpcd.git/blame - src/dhcp-common.c
Fix neighbor discovery option parsing (#16)
[thirdparty/dhcpcd.git] / src / dhcp-common.c
CommitLineData
114167a2 1/* SPDX-License-Identifier: BSD-2-Clause */
8cc47ba2 2/*
294eff4d 3 * dhcpcd - DHCP client daemon
94354c03 4 * Copyright (c) 2006-2019 Roy Marples <roy@marples.name>
294eff4d
RM
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
6c6c2328 29#include <sys/stat.h>
d074b249
RM
30#include <sys/utsname.h>
31
294eff4d
RM
32#include <ctype.h>
33#include <errno.h>
34#include <fcntl.h>
f94b4eab 35#include <inttypes.h>
294eff4d
RM
36#include <stdlib.h>
37#include <string.h>
294eff4d
RM
38#include <unistd.h>
39
40#include "config.h"
4f219984 41
294eff4d
RM
42#include "common.h"
43#include "dhcp-common.h"
44#include "dhcp.h"
1a7ff100
RM
45#include "if.h"
46#include "ipv6.h"
94d1ded9 47#include "logerr.h"
c8521994 48#include "script.h"
7374150c 49
d056e4b5
RM
50const char *
51dhcp_get_hostname(char *buf, size_t buf_len, const struct if_options *ifo)
52{
d056e4b5
RM
53
54 if (ifo->hostname[0] == '\0') {
55 if (gethostname(buf, buf_len) != 0)
56 return NULL;
57 buf[buf_len - 1] = '\0';
58 } else
b1128230 59 strlcpy(buf, ifo->hostname, buf_len);
d056e4b5
RM
60
61 /* Deny sending of these local hostnames */
02e24339
RM
62 if (buf[0] == '\0' || buf[0] == '.' ||
63 strcmp(buf, "(none)") == 0 ||
d056e4b5 64 strcmp(buf, "localhost") == 0 ||
02e24339 65 strncmp(buf, "localhost.", strlen("localhost.")) == 0)
d056e4b5
RM
66 return NULL;
67
68 /* Shorten the hostname if required */
69 if (ifo->options & DHCPCD_HOSTNAME_SHORT) {
70 char *hp;
71
72 hp = strchr(buf, '.');
73 if (hp != NULL)
74 *hp = '\0';
75 }
76
77 return buf;
78}
79
8f008ca7
RM
80void
81dhcp_print_option_encoding(const struct dhcp_opt *opt, int cols)
82{
83
84 while (cols < 40) {
85 putchar(' ');
86 cols++;
87 }
88 putchar('\t');
41eefe94 89 if (opt->type & OT_EMBED)
8f008ca7 90 printf(" embed");
41eefe94 91 if (opt->type & OT_ENCAP)
8f008ca7 92 printf(" encap");
41eefe94 93 if (opt->type & OT_INDEX)
8f008ca7 94 printf(" index");
41eefe94 95 if (opt->type & OT_ARRAY)
8f008ca7 96 printf(" array");
41eefe94 97 if (opt->type & OT_UINT8)
c960d1e6 98 printf(" uint8");
41eefe94 99 else if (opt->type & OT_INT8)
c960d1e6 100 printf(" int8");
41eefe94 101 else if (opt->type & OT_UINT16)
8f008ca7 102 printf(" uint16");
41eefe94 103 else if (opt->type & OT_INT16)
c960d1e6 104 printf(" int16");
41eefe94 105 else if (opt->type & OT_UINT32)
8f008ca7 106 printf(" uint32");
41eefe94 107 else if (opt->type & OT_INT32)
c960d1e6 108 printf(" int32");
41eefe94 109 else if (opt->type & OT_ADDRIPV4)
8f008ca7 110 printf(" ipaddress");
41eefe94 111 else if (opt->type & OT_ADDRIPV6)
8f008ca7 112 printf(" ip6address");
41eefe94 113 else if (opt->type & OT_FLAG)
8f008ca7 114 printf(" flag");
41eefe94 115 else if (opt->type & OT_BITFLAG)
cc71162d 116 printf(" bitflags");
41eefe94 117 else if (opt->type & OT_RFC1035)
8f008ca7 118 printf(" domain");
41eefe94 119 else if (opt->type & OT_DOMAIN)
8f008ca7 120 printf(" dname");
41eefe94 121 else if (opt->type & OT_ASCII)
8f008ca7 122 printf(" ascii");
41eefe94 123 else if (opt->type & OT_RAW)
8f008ca7 124 printf(" raw");
41eefe94 125 else if (opt->type & OT_BINHEX)
8f008ca7 126 printf(" binhex");
41eefe94 127 else if (opt->type & OT_STRING)
8f008ca7 128 printf(" string");
41eefe94 129 if (opt->type & OT_RFC3361)
8f008ca7 130 printf(" rfc3361");
41eefe94 131 if (opt->type & OT_RFC3442)
8f008ca7 132 printf(" rfc3442");
41eefe94 133 if (opt->type & OT_REQUEST)
8f008ca7 134 printf(" request");
41eefe94 135 if (opt->type & OT_NOREQ)
8f008ca7
RM
136 printf(" norequest");
137 putchar('\n');
138}
139
7a911e57 140struct dhcp_opt *
34457fe6 141vivso_find(uint32_t iana_en, const void *arg)
7a911e57 142{
04a19189 143 const struct interface *ifp;
7a911e57
RM
144 size_t i;
145 struct dhcp_opt *opt;
146
04a19189
RM
147 ifp = arg;
148 for (i = 0, opt = ifp->options->vivso_override;
149 i < ifp->options->vivso_override_len;
150 i++, opt++)
151 if (opt->option == iana_en)
152 return opt;
153 for (i = 0, opt = ifp->ctx->vivso;
154 i < ifp->ctx->vivso_len;
155 i++, opt++)
7a911e57
RM
156 if (opt->option == iana_en)
157 return opt;
158 return NULL;
159}
8e7d8c37 160
fec195b5 161ssize_t
34457fe6 162dhcp_vendor(char *str, size_t len)
d074b249
RM
163{
164 struct utsname utn;
165 char *p;
34457fe6 166 int l;
d074b249 167
7ac84761 168 if (uname(&utn) == -1)
fec195b5 169 return (ssize_t)snprintf(str, len, "%s-%s",
34457fe6 170 PACKAGE, VERSION);
d074b249 171 p = str;
34457fe6 172 l = snprintf(p, len,
d074b249
RM
173 "%s-%s:%s-%s:%s", PACKAGE, VERSION,
174 utn.sysname, utn.release, utn.machine);
fec195b5
RM
175 if (l == -1 || (size_t)(l + 1) > len)
176 return -1;
34457fe6
RM
177 p += l;
178 len -= (size_t)l;
1a7ff100 179 l = if_machinearch(p, len);
fec195b5
RM
180 if (l == -1 || (size_t)(l + 1) > len)
181 return -1;
a8c8e686 182 p += l;
fec195b5 183 return p - str;
d074b249
RM
184}
185
7a911e57
RM
186int
187make_option_mask(const struct dhcp_opt *dopts, size_t dopts_len,
49b749f0 188 const struct dhcp_opt *odopts, size_t odopts_len,
294eff4d
RM
189 uint8_t *mask, const char *opts, int add)
190{
f94b4eab 191 char *token, *o, *p;
294eff4d 192 const struct dhcp_opt *opt;
f94b4eab 193 int match, e;
7a911e57 194 unsigned int n;
ae4e592f 195 size_t i;
294eff4d 196
78369646
RM
197 if (opts == NULL)
198 return -1;
49b749f0 199 o = p = strdup(opts);
294eff4d
RM
200 while ((token = strsep(&p, ", "))) {
201 if (*token == '\0')
202 continue;
18aa94f8
RM
203 if (strncmp(token, "dhcp6_", 6) == 0)
204 token += 6;
825221f9
AC
205 if (strncmp(token, "nd_", 3) == 0)
206 token += 3;
49b749f0
RM
207 match = 0;
208 for (i = 0, opt = odopts; i < odopts_len; i++, opt++) {
04d965fe 209 if (opt->var == NULL || opt->option == 0)
2b0e688c 210 continue; /* buggy dhcpcd-definitions.conf */
bc22d277 211 if (strcmp(opt->var, token) == 0)
294eff4d
RM
212 match = 1;
213 else {
f94b4eab
RM
214 n = (unsigned int)strtou(token, NULL, 0,
215 0, UINT_MAX, &e);
216 if (e == 0 && opt->option == n)
217 match = 1;
294eff4d 218 }
49b749f0 219 if (match)
294eff4d 220 break;
49b749f0
RM
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 {
f94b4eab
RM
227 n = (unsigned int)strtou(token, NULL, 0,
228 0, UINT_MAX, &e);
229 if (e == 0 && opt->option == n)
230 match = 1;
49b749f0
RM
231 }
232 if (match)
233 break;
294eff4d
RM
234 }
235 }
49b749f0 236 if (!match || !opt->option) {
294eff4d
RM
237 free(o);
238 errno = ENOENT;
239 return -1;
240 }
41eefe94 241 if (add == 2 && !(opt->type & OT_ADDRIPV4)) {
49b749f0
RM
242 free(o);
243 errno = EINVAL;
244 return -1;
245 }
246 if (add == 1 || add == 2)
60ccc7df 247 add_option_mask(mask, opt->option);
49b749f0 248 else
60ccc7df 249 del_option_mask(mask, opt->option);
294eff4d
RM
250 }
251 free(o);
252 return 0;
253}
254
cc3c3560
RM
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;
b340cfc9 265
cc3c3560
RM
266 if (dst) {
267 p = dst;
268 lp = p++;
269 }
b340cfc9
RM
270 /* Silence bogus GCC warnings */
271 else
272 p = lp = NULL;
273
cc3c3560
RM
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) {
34457fe6 285 *lp = (uint8_t)(p - lp - 1);
cc3c3560
RM
286 if (*lp == '\0')
287 return len;
288 lp = p++;
289 }
290 } else if (dst)
291 *p++ = (uint8_t)*src;
292 len++;
293 }
b340cfc9 294
cc3c3560 295 if (dst) {
34457fe6 296 *lp = (uint8_t)(p - lp - 1);
cc3c3560
RM
297 if (has_dot)
298 *p++ = '\0';
299 }
b340cfc9 300
cc3c3560
RM
301 if (has_dot)
302 len++;
b340cfc9 303
cc3c3560
RM
304 return len;
305}
306
167b9b9b 307/* Decode an RFC1035 DNS search order option into a space
294eff4d
RM
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
167b9b9b 312decode_rfc1035(char *out, size_t len, const uint8_t *p, size_t pl)
294eff4d
RM
313{
314 const char *start;
77fbcbc8 315 size_t start_len, l, d_len, o_len;
34457fe6 316 const uint8_t *r, *q = p, *e;
f3ba5179 317 int hops;
294eff4d
RM
318 uint8_t ltype;
319
77fbcbc8 320 o_len = 0;
294eff4d
RM
321 start = out;
322 start_len = len;
34457fe6
RM
323 q = p;
324 e = p + pl;
325 while (q < e) {
294eff4d 326 r = NULL;
77fbcbc8 327 d_len = 0;
294eff4d 328 hops = 0;
1b77c520
RM
329 /* Check we are inside our length again in-case
330 * the name isn't fully qualified (ie, not terminated) */
34457fe6 331 while (q < e && (l = (size_t)*q++)) {
294eff4d 332 ltype = l & 0xc0;
3afee65d
RM
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;
34457fe6 338 return -1;
3afee65d 339 }
294eff4d 340 else if (ltype == 0xc0) { /* pointer */
8f38710f
RM
341 if (q == e) {
342 errno = ERANGE;
343 return -1;
344 }
294eff4d
RM
345 l = (l & 0x3f) << 8;
346 l |= *q++;
347 /* save source of first jump. */
348 if (!r)
349 r = q;
350 hops++;
34457fe6
RM
351 if (hops > 255) {
352 errno = ERANGE;
353 return -1;
354 }
294eff4d 355 q = p + l;
34457fe6
RM
356 if (q >= e) {
357 errno = ERANGE;
358 return -1;
359 }
294eff4d
RM
360 } else {
361 /* straightforward name segment, add with '.' */
8f38710f
RM
362 if (q + l > e) {
363 errno = ERANGE;
364 return -1;
365 }
77fbcbc8
RM
366 if (l > NS_MAXLABEL) {
367 errno = EINVAL;
368 return -1;
369 }
370 d_len += l + 1;
294eff4d 371 if (out) {
34457fe6 372 if (l + 1 > len) {
294eff4d
RM
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 }
77fbcbc8
RM
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
294eff4d
RM
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
77fbcbc8
RM
408 /* Remove the trailing NUL */
409 if (o_len != 0)
410 o_len--;
411
412 return (ssize_t)o_len;
294eff4d
RM
413}
414
cb2d5677 415/* Check for a valid name as per RFC952 and RFC1123 section 2.1 */
8f008ca7
RM
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;
41eefe94 439 if (!(type & OT_ARRAY))
8f008ca7
RM
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 {
e3e434d4 460 if (++len > NS_MAXLABEL) {
8f008ca7 461 errno = ERANGE;
34a9f2d1 462 errset = 1;
8f008ca7
RM
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}
4f219984
RM
480
481/*
482 * Prints a chunk of data to a string.
8f008ca7
RM
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.
4f219984 485 */
8f008ca7 486static const char hexchrs[] = "0123456789abcdef";
294eff4d 487ssize_t
8f008ca7 488print_string(char *dst, size_t len, int type, const uint8_t *data, size_t dl)
294eff4d 489{
8f008ca7 490 char *odst;
294eff4d 491 uint8_t c;
8f008ca7 492 const uint8_t *e;
4f219984 493 size_t bytes;
294eff4d 494
8f008ca7 495 odst = dst;
4f219984 496 bytes = 0;
294eff4d 497 e = data + dl;
34a9f2d1 498
294eff4d
RM
499 while (data < e) {
500 c = *data++;
41eefe94 501 if (type & OT_BINHEX) {
8f008ca7
RM
502 if (dst) {
503 if (len == 0 || len == 1) {
61318a5b 504 errno = ENOBUFS;
8f008ca7
RM
505 return -1;
506 }
507 *dst++ = hexchrs[(c & 0xF0) >> 4];
508 *dst++ = hexchrs[(c & 0x0F)];
509 len -= 2;
510 }
511 bytes += 2;
512 continue;
294eff4d 513 }
41eefe94 514 if (type & OT_ASCII && (!isascii(c))) {
8f008ca7
RM
515 errno = EINVAL;
516 break;
294eff4d 517 }
41eefe94 518 if (!(type & (OT_ASCII | OT_RAW | OT_ESCSTRING | OT_ESCFILE)) &&
8f008ca7
RM
519 (!isascii(c) && !isprint(c)))
520 {
521 errno = EINVAL;
522 break;
523 }
41eefe94 524 if ((type & (OT_ESCSTRING | OT_ESCFILE) &&
937bc321 525 (c == '\\' || !isascii(c) || !isprint(c))) ||
41eefe94 526 (type & OT_ESCFILE && (c == '/' || c == ' ')))
8f008ca7
RM
527 {
528 errno = EINVAL;
529 if (c == '\\') {
8f008ca7 530 if (dst) {
2cb87923 531 if (len == 0 || len == 1) {
61318a5b 532 errno = ENOBUFS;
2cb87923
RM
533 return -1;
534 }
8f008ca7
RM
535 *dst++ = '\\'; *dst++ = '\\';
536 len -= 2;
537 }
538 bytes += 2;
539 continue;
540 }
541 if (dst) {
542 if (len < 5) {
61318a5b 543 errno = ENOBUFS;
8f008ca7
RM
544 return -1;
545 }
546 *dst++ = '\\';
a8cbd6a0
RM
547 *dst++ = (char)(((c >> 6) & 03) + '0');
548 *dst++ = (char)(((c >> 3) & 07) + '0');
549 *dst++ = (char)(( c & 07) + '0');
8f008ca7
RM
550 len -= 4;
551 }
552 bytes += 4;
553 } else {
554 if (dst) {
555 if (len == 0) {
61318a5b 556 errno = ENOBUFS;
8f008ca7
RM
557 return -1;
558 }
559 *dst++ = (char)c;
560 len--;
561 }
562 bytes++;
294eff4d 563 }
294eff4d
RM
564 }
565
566 /* NULL */
8f008ca7
RM
567 if (dst) {
568 if (len == 0) {
61318a5b 569 errno = ENOBUFS;
8f008ca7
RM
570 return -1;
571 }
572 *dst = '\0';
573
574 /* Now we've printed it, validate the domain */
41eefe94 575 if (type & OT_DOMAIN && !valid_domainname(odst, type)) {
8f008ca7
RM
576 *odst = '\0';
577 return 1;
578 }
579
580 }
8f008ca7 581
4f219984 582 return (ssize_t)bytes;
294eff4d
RM
583}
584
03476881 585#define ADDR6SZ 16
4dd68670 586static ssize_t
b21cd906 587dhcp_optlen(const struct dhcp_opt *opt, size_t dl)
8e7d8c37
RM
588{
589 size_t sz;
590
41eefe94 591 if (opt->type & OT_ADDRIPV6)
1475a702 592 sz = ADDR6SZ;
41eefe94 593 else if (opt->type & (OT_INT32 | OT_UINT32 | OT_ADDRIPV4))
1475a702 594 sz = sizeof(uint32_t);
41eefe94 595 else if (opt->type & (OT_INT16 | OT_UINT16))
1475a702 596 sz = sizeof(uint16_t);
41eefe94 597 else if (opt->type & (OT_INT8 | OT_UINT8 | OT_BITFLAG))
1475a702 598 sz = sizeof(uint8_t);
41eefe94 599 else if (opt->type & OT_FLAG)
1475a702
RM
600 return 0;
601 else {
602 /* All other types are variable length */
b21cd906 603 if (opt->len) {
1475a702 604 if ((size_t)opt->len > dl) {
a0800a8a 605 errno = EOVERFLOW;
4dd68670 606 return -1;
1475a702 607 }
4dd68670 608 return (ssize_t)opt->len;
b21cd906 609 }
4dd68670 610 return (ssize_t)dl;
b21cd906 611 }
1475a702 612 if (dl < sz) {
a0800a8a 613 errno = EOVERFLOW;
1475a702 614 return -1;
8e7d8c37
RM
615 }
616
1475a702
RM
617 /* Trim any extra data.
618 * Maybe we need a settng to reject DHCP options with extra data? */
41eefe94 619 if (opt->type & OT_ARRAY)
1475a702
RM
620 return (ssize_t)(dl - (dl % sz));
621 return (ssize_t)sz;
8e7d8c37
RM
622}
623
cc71162d 624static ssize_t
c8521994
RM
625print_option(FILE *fp, const char *prefix, const struct dhcp_opt *opt,
626 int vname,
cc71162d 627 const uint8_t *data, size_t dl, const char *ifname)
294eff4d 628{
2dae918b 629 fpos_t fp_pos;
294eff4d
RM
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;
2dae918b 636 ssize_t sl;
34457fe6 637 size_t l;
c8521994
RM
638
639 /* Ensure a valid length */
640 dl = (size_t)dhcp_optlen(opt, dl);
641 if ((ssize_t)dl == -1)
642 return 0;
643
2dae918b 644 if (fgetpos(fp, &fp_pos) == -1)
c8521994 645 return -1;
2dae918b
RM
646 if (fprintf(fp, "%s", prefix) == -1)
647 goto err;
859110a8
RM
648
649 /* We printed something, so always goto err from now-on
650 * to terminate the string. */
c8521994
RM
651 if (vname) {
652 if (fprintf(fp, "_%s", opt->var) == -1)
859110a8 653 goto err;
c8521994
RM
654 }
655 if (fputc('=', fp) == EOF)
859110a8 656 goto err;
c8521994 657 if (dl == 0)
859110a8 658 goto done;
294eff4d 659
41eefe94 660 if (opt->type & OT_RFC1035) {
c8521994
RM
661 char domain[NS_MAXDNAME];
662
663 sl = decode_rfc1035(domain, sizeof(domain), data, dl);
87def87d 664 if (sl == -1)
859110a8 665 goto err;
87def87d 666 if (sl == 0)
859110a8 667 goto done;
c8521994 668 if (valid_domainname(domain, opt->type) == -1)
859110a8 669 goto err;
c8521994 670 return efprintf(fp, "%s", domain);
294eff4d
RM
671 }
672
aae24feb 673#ifdef INET
c8521994
RM
674 if (opt->type & OT_RFC3361)
675 return print_rfc3361(fp, data, dl);
294eff4d 676
41eefe94 677 if (opt->type & OT_RFC3442)
c8521994 678 return print_rfc3442(fp, data, dl);
aae24feb 679#endif
294eff4d 680
c8521994
RM
681 if (opt->type & OT_STRING) {
682 char buf[1024];
294eff4d 683
c8521994 684 if (print_string(buf, sizeof(buf), opt->type, data, dl) == -1)
859110a8 685 goto err;
c8521994 686 return efprintf(fp, "%s", buf);
6bb6f717
RM
687 }
688
c8521994
RM
689 if (opt->type & OT_FLAG)
690 return efprintf(fp, "1");
691
41eefe94 692 if (opt->type & OT_BITFLAG) {
cc71162d
RM
693 /* bitflags are a string, MSB first, such as ABCDEFGH
694 * where A is 10000000, B is 01000000, etc. */
cc71162d
RM
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 {
c8521994 704 if (fputc(opt->bitflags[l], fp) == EOF)
859110a8 705 goto err;
cc71162d
RM
706 }
707 }
859110a8 708 goto done;
294eff4d
RM
709 }
710
711 t = data;
712 e = data + dl;
713 while (data < e) {
8f008ca7 714 if (data != t) {
c8521994 715 if (fputc(' ', fp) == EOF)
859110a8 716 goto err;
294eff4d 717 }
41eefe94 718 if (opt->type & OT_UINT8) {
c8521994 719 if (fprintf(fp, "%u", *data) == -1)
859110a8 720 goto err;
294eff4d 721 data++;
41eefe94 722 } else if (opt->type & OT_INT8) {
c8521994 723 if (fprintf(fp, "%d", *data) == -1)
859110a8 724 goto err;
c960d1e6 725 data++;
41eefe94 726 } else if (opt->type & OT_UINT16) {
294eff4d
RM
727 memcpy(&u16, data, sizeof(u16));
728 u16 = ntohs(u16);
c8521994 729 if (fprintf(fp, "%u", u16) == -1)
859110a8 730 goto err;
294eff4d 731 data += sizeof(u16);
41eefe94 732 } else if (opt->type & OT_INT16) {
34457fe6
RM
733 memcpy(&u16, data, sizeof(u16));
734 s16 = (int16_t)ntohs(u16);
c8521994 735 if (fprintf(fp, "%d", s16) == -1)
859110a8 736 goto err;
34457fe6 737 data += sizeof(u16);
41eefe94 738 } else if (opt->type & OT_UINT32) {
294eff4d
RM
739 memcpy(&u32, data, sizeof(u32));
740 u32 = ntohl(u32);
c8521994 741 if (fprintf(fp, "%u", u32) == -1)
859110a8 742 goto err;
294eff4d 743 data += sizeof(u32);
41eefe94 744 } else if (opt->type & OT_INT32) {
34457fe6
RM
745 memcpy(&u32, data, sizeof(u32));
746 s32 = (int32_t)ntohl(u32);
c8521994 747 if (fprintf(fp, "%d", s32) == -1)
859110a8 748 goto err;
34457fe6 749 data += sizeof(u32);
41eefe94 750 } else if (opt->type & OT_ADDRIPV4) {
294eff4d 751 memcpy(&addr.s_addr, data, sizeof(addr.s_addr));
c8521994 752 if (fprintf(fp, "%s", inet_ntoa(addr)) == -1)
859110a8 753 goto err;
294eff4d 754 data += sizeof(addr.s_addr);
41eefe94 755 } else if (opt->type & OT_ADDRIPV6) {
c8521994
RM
756 char buf[INET6_ADDRSTRLEN];
757
758 if (inet_ntop(AF_INET6, data, buf, sizeof(buf)) == NULL)
859110a8 759 goto err;
c8521994 760 if (fprintf(fp, "%s", buf) == -1)
859110a8 761 goto err;
c8521994
RM
762 if (data[0] == 0xfe && (data[1] & 0xc0) == 0x80) {
763 if (fprintf(fp,"%%%s", ifname) == -1)
859110a8 764 goto err;
c8521994 765 }
294eff4d 766 data += 16;
93f3066b
RM
767 } else {
768 errno = EINVAL;
859110a8 769 goto err;
aae24feb 770 }
294eff4d
RM
771 }
772
859110a8 773done:
c8521994
RM
774 if (fputc('\0', fp) == EOF)
775 return -1;
2dae918b
RM
776 return 1;
777
778err:
779 (void)fsetpos(fp, &fp_pos);
780 return -1;
294eff4d 781}
8e7d8c37 782
cb1e1319
RM
783int
784dhcp_set_leasefile(char *leasefile, size_t len, int family,
1f7bfb39 785 const struct interface *ifp)
cb1e1319 786{
28322203 787 char ssid[1 + (IF_SSIDLEN * 4) + 1]; /* - prefix and NUL terminated. */
cb1e1319 788
ff4df8e0
RM
789 if (ifp->name[0] == '\0') {
790 strlcpy(leasefile, ifp->ctx->pidfile, len);
791 return 0;
792 }
793
cb1e1319
RM
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,
41eefe94 806 OT_ESCFILE,
cb1e1319
RM
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,
1f7bfb39 812 ifp->name, ssid);
cb1e1319
RM
813}
814
c8521994
RM
815void
816dhcp_envoption(struct dhcpcd_ctx *ctx, FILE *fp, const char *prefix,
03476881 817 const char *ifname, struct dhcp_opt *opt,
4eb7b489 818 const uint8_t *(*dgetopt)(struct dhcpcd_ctx *,
76bb4d03
RM
819 size_t *, unsigned int *, size_t *,
820 const uint8_t *, size_t, struct dhcp_opt **),
34457fe6 821 const uint8_t *od, size_t ol)
8e7d8c37 822{
c8521994 823 size_t i, eos, eol;
4dd68670 824 ssize_t eo;
76bb4d03 825 unsigned int eoc;
03476881 826 const uint8_t *eod;
7a911e57 827 int ov;
03476881
RM
828 struct dhcp_opt *eopt, *oopt;
829 char *pfx;
8e7d8c37
RM
830
831 /* If no embedded or encapsulated options, it's easy */
832 if (opt->embopts_len == 0 && opt->encopts_len == 0) {
c8521994
RM
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;
8e7d8c37
RM
838 }
839
63bdd2c2 840 /* Create a new prefix based on the option */
c8521994
RM
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 }
63bdd2c2 853
8e7d8c37
RM
854 /* Embedded options are always processed first as that
855 * is a fixed layout */
03476881 856 for (i = 0, eopt = opt->embopts; i < opt->embopts_len; i++, eopt++) {
4dd68670
RM
857 eo = dhcp_optlen(eopt, ol);
858 if (eo == -1) {
c8521994
RM
859 logerrx("%s: %s %d.%d/%zu: "
860 "malformed embedded option",
861 ifname, __func__, opt->option,
862 eopt->option, i);
4dd68670
RM
863 goto out;
864 }
865 if (eo == 0) {
866 /* An option was expected, but there is no data
6b1f3b3e
RM
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
ecdbb919 870 * option which is optional. */
c8521994
RM
871 if (ol != 0 || !(eopt->type & OT_OPTIONAL))
872 logerrx("%s: %s %d.%d/%zu: "
873 "missing embedded option",
4dd68670 874 ifname, __func__, opt->option,
ecdbb919 875 eopt->option, i);
4dd68670 876 goto out;
6b1f3b3e 877 }
2f8d2564
RM
878 /* Use the option prefix if the embedded option
879 * name is different.
880 * This avoids new_fqdn_fqdn which would be silly. */
41eefe94 881 if (!(eopt->type & OT_RESERVED)) {
cc71162d 882 ov = strcmp(opt->var, eopt->var);
c8521994
RM
883 if (print_option(fp, pfx, eopt, ov, od, (size_t)eo,
884 ifname) == -1)
94d1ded9 885 logerr("%s: %s %d.%d/%zu",
e0a0ba22
RM
886 ifname, __func__,
887 opt->option, eopt->option, i);
cc71162d 888 }
4dd68670
RM
889 od += (size_t)eo;
890 ol -= (size_t)eo;
8e7d8c37
RM
891 }
892
03476881
RM
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;
41eefe94 903 if (eopt->type & OT_OPTION) {
4eb7b489 904 dgetopt(ctx, NULL, &eoc, NULL, NULL, 0, &oopt);
03476881
RM
905 if (oopt)
906 oopt->index = 0;
63bdd2c2 907 }
03476881
RM
908 }
909
4eb7b489 910 while ((eod = dgetopt(ctx, &eos, &eoc, &eol, od, ol, &oopt))) {
03476881
RM
911 for (i = 0, eopt = opt->encopts;
912 i < opt->encopts_len;
913 i++, eopt++)
914 {
c8521994
RM
915 if (eopt->option != eoc)
916 continue;
917 if (eopt->type & OT_OPTION) {
918 if (oopt == NULL)
919 /* Report error? */
920 continue;
03476881 921 }
c8521994
RM
922 dhcp_envoption(ctx, fp, pfx, ifname,
923 eopt->type & OT_OPTION ? oopt:eopt,
924 dgetopt, eod, eol);
03476881
RM
925 }
926 od += eos + eol;
927 ol -= eos + eol;
8e7d8c37
RM
928 }
929 }
930
6b1f3b3e 931out:
c8521994 932 free(pfx);
8e7d8c37 933}
03476881
RM
934
935void
936dhcp_zero_index(struct dhcp_opt *opt)
937{
938 size_t i;
939 struct dhcp_opt *o;
940
941 opt->index = 0;
22e299e6 942 for (i = 0, o = opt->embopts; i < opt->embopts_len; i++, o++)
03476881 943 dhcp_zero_index(o);
22e299e6 944 for (i = 0, o = opt->encopts; i < opt->encopts_len; i++, o++)
03476881
RM
945 dhcp_zero_index(o);
946}
416a319e
RM
947
948size_t
d4595ab1 949dhcp_read_lease_fd(int fd, void **lease)
416a319e 950{
6c6c2328
RM
951 struct stat st;
952 size_t sz;
953 void *buf;
954 ssize_t len;
c8521994 955
6c6c2328
RM
956 if (fstat(fd, &st) != 0)
957 goto out;
958 if (!S_ISREG(st.st_mode)) {
959 errno = EINVAL;
416a319e 960 goto out;
6c6c2328
RM
961 }
962 if (st.st_size > UINT32_MAX) {
963 errno = E2BIG;
964 goto out;
965 }
416a319e 966
6c6c2328 967 sz = (size_t)st.st_size;
268e1de9
RM
968 if (sz == 0)
969 goto out;
6c6c2328
RM
970 if ((buf = malloc(sz)) == NULL)
971 goto out;
972 if ((len = read(fd, buf, sz)) == -1) {
973 free(buf);
974 goto out;
416a319e 975 }
6c6c2328
RM
976 *lease = buf;
977 return (size_t)len;
416a319e 978
416a319e
RM
979out:
980 *lease = NULL;
981 return 0;
982}