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