dhcp-common.c revision a3595821594453ea89ef8e6790927694b0a1adf1
1/* 2 * dhcpcd - DHCP client daemon 3 * Copyright (c) 2006-2015 Roy Marples <roy@marples.name> 4 * All rights reserved 5 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25 * SUCH DAMAGE. 26 */ 27 28#include <sys/utsname.h> 29 30#include <ctype.h> 31#include <errno.h> 32#include <fcntl.h> 33#include <inttypes.h> 34#include <stdlib.h> 35#include <string.h> 36#include <unistd.h> 37 38#include "config.h" 39 40#include "common.h" 41#include "dhcp-common.h" 42#include "dhcp.h" 43#include "if.h" 44#include "ipv6.h" 45 46void 47dhcp_print_option_encoding(const struct dhcp_opt *opt, int cols) 48{ 49 50 while (cols < 40) { 51 putchar(' '); 52 cols++; 53 } 54 putchar('\t'); 55 if (opt->type & EMBED) 56 printf(" embed"); 57 if (opt->type & ENCAP) 58 printf(" encap"); 59 if (opt->type & INDEX) 60 printf(" index"); 61 if (opt->type & ARRAY) 62 printf(" array"); 63 if (opt->type & UINT8) 64 printf(" byte"); 65 else if (opt->type & UINT16) 66 printf(" uint16"); 67 else if (opt->type & SINT16) 68 printf(" sint16"); 69 else if (opt->type & UINT32) 70 printf(" uint32"); 71 else if (opt->type & SINT32) 72 printf(" sint32"); 73 else if (opt->type & ADDRIPV4) 74 printf(" ipaddress"); 75 else if (opt->type & ADDRIPV6) 76 printf(" ip6address"); 77 else if (opt->type & FLAG) 78 printf(" flag"); 79 else if (opt->type & RFC3397) 80 printf(" domain"); 81 else if (opt->type & DOMAIN) 82 printf(" dname"); 83 else if (opt->type & ASCII) 84 printf(" ascii"); 85 else if (opt->type & RAW) 86 printf(" raw"); 87 else if (opt->type & BINHEX) 88 printf(" binhex"); 89 else if (opt->type & STRING) 90 printf(" string"); 91 if (opt->type & RFC3361) 92 printf(" rfc3361"); 93 if (opt->type & RFC3442) 94 printf(" rfc3442"); 95 if (opt->type & RFC5969) 96 printf(" rfc5969"); 97 if (opt->type & REQUEST) 98 printf(" request"); 99 if (opt->type & NOREQ) 100 printf(" norequest"); 101 putchar('\n'); 102} 103 104struct dhcp_opt * 105vivso_find(uint32_t iana_en, const void *arg) 106{ 107 const struct interface *ifp; 108 size_t i; 109 struct dhcp_opt *opt; 110 111 ifp = arg; 112 for (i = 0, opt = ifp->options->vivso_override; 113 i < ifp->options->vivso_override_len; 114 i++, opt++) 115 if (opt->option == iana_en) 116 return opt; 117 for (i = 0, opt = ifp->ctx->vivso; 118 i < ifp->ctx->vivso_len; 119 i++, opt++) 120 if (opt->option == iana_en) 121 return opt; 122 return NULL; 123} 124 125ssize_t 126dhcp_vendor(char *str, size_t len) 127{ 128 struct utsname utn; 129 char *p; 130 int l; 131 132 if (uname(&utn) != 0) 133 return (ssize_t)snprintf(str, len, "%s-%s", 134 PACKAGE, VERSION); 135 p = str; 136 l = snprintf(p, len, 137 "%s-%s:%s-%s:%s", PACKAGE, VERSION, 138 utn.sysname, utn.release, utn.machine); 139 if (l == -1 || (size_t)(l + 1) > len) 140 return -1; 141 p += l; 142 len -= (size_t)l; 143 l = if_machinearch(p, len); 144 if (l == -1 || (size_t)(l + 1) > len) 145 return -1; 146 p += l; 147 return p - str; 148} 149 150int 151make_option_mask(const struct dhcp_opt *dopts, size_t dopts_len, 152 const struct dhcp_opt *odopts, size_t odopts_len, 153 uint8_t *mask, const char *opts, int add) 154{ 155 char *token, *o, *p; 156 const struct dhcp_opt *opt; 157 int match, e; 158 unsigned int n; 159 size_t i; 160 161 if (opts == NULL) 162 return -1; 163 o = p = strdup(opts); 164 while ((token = strsep(&p, ", "))) { 165 if (*token == '\0') 166 continue; 167 match = 0; 168 for (i = 0, opt = odopts; i < odopts_len; i++, opt++) { 169 if (strcmp(opt->var, token) == 0) 170 match = 1; 171 else { 172 n = (unsigned int)strtou(token, NULL, 0, 173 0, UINT_MAX, &e); 174 if (e == 0 && opt->option == n) 175 match = 1; 176 } 177 if (match) 178 break; 179 } 180 if (match == 0) { 181 for (i = 0, opt = dopts; i < dopts_len; i++, opt++) { 182 if (strcmp(opt->var, token) == 0) 183 match = 1; 184 else { 185 n = (unsigned int)strtou(token, NULL, 0, 186 0, UINT_MAX, &e); 187 if (e == 0 && opt->option == n) 188 match = 1; 189 } 190 if (match) 191 break; 192 } 193 } 194 if (!match || !opt->option) { 195 free(o); 196 errno = ENOENT; 197 return -1; 198 } 199 if (add == 2 && !(opt->type & ADDRIPV4)) { 200 free(o); 201 errno = EINVAL; 202 return -1; 203 } 204 if (add == 1 || add == 2) 205 add_option_mask(mask, opt->option); 206 else 207 del_option_mask(mask, opt->option); 208 } 209 free(o); 210 return 0; 211} 212 213size_t 214encode_rfc1035(const char *src, uint8_t *dst) 215{ 216 uint8_t *p; 217 uint8_t *lp; 218 size_t len; 219 uint8_t has_dot; 220 221 if (src == NULL || *src == '\0') 222 return 0; 223 224 if (dst) { 225 p = dst; 226 lp = p++; 227 } 228 /* Silence bogus GCC warnings */ 229 else 230 p = lp = NULL; 231 232 len = 1; 233 has_dot = 0; 234 for (; *src; src++) { 235 if (*src == '\0') 236 break; 237 if (*src == '.') { 238 /* Skip the trailing . */ 239 if (src[1] == '\0') 240 break; 241 has_dot = 1; 242 if (dst) { 243 *lp = (uint8_t)(p - lp - 1); 244 if (*lp == '\0') 245 return len; 246 lp = p++; 247 } 248 } else if (dst) 249 *p++ = (uint8_t)*src; 250 len++; 251 } 252 253 if (dst) { 254 *lp = (uint8_t)(p - lp - 1); 255 if (has_dot) 256 *p++ = '\0'; 257 } 258 259 if (has_dot) 260 len++; 261 262 return len; 263} 264 265/* Decode an RFC3397 DNS search order option into a space 266 * separated string. Returns length of string (including 267 * terminating zero) or zero on error. out may be NULL 268 * to just determine output length. */ 269ssize_t 270decode_rfc3397(char *out, size_t len, const uint8_t *p, size_t pl) 271{ 272 const char *start; 273 size_t start_len, l, count; 274 const uint8_t *r, *q = p, *e; 275 int hops; 276 uint8_t ltype; 277 278 count = 0; 279 start = out; 280 start_len = len; 281 q = p; 282 e = p + pl; 283 while (q < e) { 284 r = NULL; 285 hops = 0; 286 /* Check we are inside our length again in-case 287 * the name isn't fully qualified (ie, not terminated) */ 288 while (q < e && (l = (size_t)*q++)) { 289 ltype = l & 0xc0; 290 if (ltype == 0x80 || ltype == 0x40) 291 return -1; 292 else if (ltype == 0xc0) { /* pointer */ 293 if (q == e) { 294 errno = ERANGE; 295 return -1; 296 } 297 l = (l & 0x3f) << 8; 298 l |= *q++; 299 /* save source of first jump. */ 300 if (!r) 301 r = q; 302 hops++; 303 if (hops > 255) { 304 errno = ERANGE; 305 return -1; 306 } 307 q = p + l; 308 if (q >= e) { 309 errno = ERANGE; 310 return -1; 311 } 312 } else { 313 /* straightforward name segment, add with '.' */ 314 if (q + l > e) { 315 errno = ERANGE; 316 return -1; 317 } 318 count += l + 1; 319 if (out) { 320 if (l + 1 > len) { 321 errno = ENOBUFS; 322 return -1; 323 } 324 memcpy(out, q, l); 325 out += l; 326 *out++ = '.'; 327 len -= l; 328 len--; 329 } 330 q += l; 331 } 332 } 333 /* change last dot to space */ 334 if (out && out != start) 335 *(out - 1) = ' '; 336 if (r) 337 q = r; 338 } 339 340 /* change last space to zero terminator */ 341 if (out) { 342 if (out != start) 343 *(out - 1) = '\0'; 344 else if (start_len > 0) 345 *out = '\0'; 346 } 347 348 if (count) 349 /* Don't count the trailing NUL */ 350 count--; 351 return (ssize_t)count; 352} 353 354/* Check for a valid domain name as per RFC1123 with the exception of 355 * allowing - and _ (but not at start or end) as they seem to be widely used. */ 356static int 357valid_domainname(char *lbl, int type) 358{ 359 char *slbl, *lst; 360 unsigned char c; 361 int start, len, errset; 362 363 if (lbl == NULL || *lbl == '\0') { 364 errno = EINVAL; 365 return 0; 366 } 367 368 slbl = lbl; 369 lst = NULL; 370 start = 1; 371 len = errset = 0; 372 for (;;) { 373 c = (unsigned char)*lbl++; 374 if (c == '\0') 375 return 1; 376 if (c == ' ') { 377 if (lbl - 1 == slbl) /* No space at start */ 378 break; 379 if (!(type & ARRAY)) 380 break; 381 /* Skip to the next label */ 382 if (!start) { 383 start = 1; 384 lst = lbl - 1; 385 } 386 if (len) 387 len = 0; 388 continue; 389 } 390 if (c == '.') { 391 if (*lbl == '.') 392 break; 393 len = 0; 394 continue; 395 } 396 if (((c == '-' || c == '_') && 397 !start && *lbl != ' ' && *lbl != '\0') || 398 isalnum(c)) 399 { 400 if (++len > 63) { 401 errno = ERANGE; 402 errset = 1; 403 break; 404 } 405 } else 406 break; 407 if (start) 408 start = 0; 409 } 410 411 if (!errset) 412 errno = EINVAL; 413 if (lst) { 414 /* At least one valid domain, return it */ 415 *lst = '\0'; 416 return 1; 417 } 418 return 0; 419} 420 421/* 422 * Prints a chunk of data to a string. 423 * PS_SHELL goes as it is these days, it's upto the target to validate it. 424 * PS_SAFE has all non ascii and non printables changes to escaped octal. 425 */ 426static const char hexchrs[] = "0123456789abcdef"; 427ssize_t 428print_string(char *dst, size_t len, int type, const uint8_t *data, size_t dl) 429{ 430 char *odst; 431 uint8_t c; 432 const uint8_t *e; 433 size_t bytes; 434 435 odst = dst; 436 bytes = 0; 437 e = data + dl; 438 439 while (data < e) { 440 c = *data++; 441 if (type & BINHEX) { 442 if (dst) { 443 if (len == 0 || len == 1) { 444 errno = ENOSPC; 445 return -1; 446 } 447 *dst++ = hexchrs[(c & 0xF0) >> 4]; 448 *dst++ = hexchrs[(c & 0x0F)]; 449 len -= 2; 450 } 451 bytes += 2; 452 continue; 453 } 454 if (type & ASCII && (!isascii(c))) { 455 errno = EINVAL; 456 break; 457 } 458 if (!(type & (ASCII | RAW | ESCSTRING | ESCFILE)) /* plain */ && 459 (!isascii(c) && !isprint(c))) 460 { 461 errno = EINVAL; 462 break; 463 } 464 if ((type & (ESCSTRING | ESCFILE) && 465 (c == '\\' || !isascii(c) || !isprint(c))) || 466 (type & ESCFILE && (c == '/' || c == ' '))) 467 { 468 errno = EINVAL; 469 if (c == '\\') { 470 if (dst) { 471 if (len == 0 || len == 1) { 472 errno = ENOSPC; 473 return -1; 474 } 475 *dst++ = '\\'; *dst++ = '\\'; 476 len -= 2; 477 } 478 bytes += 2; 479 continue; 480 } 481 if (dst) { 482 if (len < 5) { 483 errno = ENOSPC; 484 return -1; 485 } 486 *dst++ = '\\'; 487 *dst++ = (char)(((c >> 6) & 03) + '0'); 488 *dst++ = (char)(((c >> 3) & 07) + '0'); 489 *dst++ = (char)(( c & 07) + '0'); 490 len -= 4; 491 } 492 bytes += 4; 493 } else { 494 if (dst) { 495 if (len == 0) { 496 errno = ENOSPC; 497 return -1; 498 } 499 *dst++ = (char)c; 500 len--; 501 } 502 bytes++; 503 } 504 } 505 506 /* NULL */ 507 if (dst) { 508 if (len == 0) { 509 errno = ENOSPC; 510 return -1; 511 } 512 *dst = '\0'; 513 514 /* Now we've printed it, validate the domain */ 515 if (type & DOMAIN && !valid_domainname(odst, type)) { 516 *odst = '\0'; 517 return 1; 518 } 519 520 } 521 522 return (ssize_t)bytes; 523} 524 525#define ADDRSZ 4 526#define ADDR6SZ 16 527static size_t 528dhcp_optlen(const struct dhcp_opt *opt, size_t dl) 529{ 530 size_t sz; 531 532 if (dl == 0) 533 return 0; 534 535 if (opt->type == 0 || 536 opt->type & (STRING | BINHEX | RFC3442 | RFC5969)) 537 { 538 if (opt->len) { 539 if ((size_t)opt->len > dl) 540 return 0; 541 return (size_t)opt->len; 542 } 543 return dl; 544 } 545 546 if ((opt->type & (ADDRIPV4 | ARRAY)) == (ADDRIPV4 | ARRAY)) { 547 if (dl < ADDRSZ) 548 return 0; 549 return dl - (dl % ADDRSZ); 550 } 551 552 if ((opt->type & (ADDRIPV6 | ARRAY)) == (ADDRIPV6 | ARRAY)) { 553 if (dl < ADDR6SZ) 554 return 0; 555 return dl - (dl % ADDR6SZ); 556 } 557 558 if (opt->type & (UINT32 | ADDRIPV4)) 559 sz = sizeof(uint32_t); 560 else if (opt->type & UINT16) 561 sz = sizeof(uint16_t); 562 else if (opt->type & UINT8) 563 sz = sizeof(uint8_t); 564 else if (opt->type & ADDRIPV6) 565 sz = ADDR6SZ; 566 else 567 /* If we don't know the size, assume it's valid */ 568 return dl; 569 return (dl < sz ? 0 : sz); 570} 571 572#ifdef INET6 573#define PO_IFNAME 574#else 575#define PO_IFNAME __unused 576#endif 577 578ssize_t 579print_option(char *s, size_t len, int type, const uint8_t *data, size_t dl, 580 PO_IFNAME const char *ifname) 581{ 582 const uint8_t *e, *t; 583 uint16_t u16; 584 int16_t s16; 585 uint32_t u32; 586 int32_t s32; 587 struct in_addr addr; 588 ssize_t bytes = 0, sl; 589 size_t l; 590 char *tmp; 591 592 if (type & RFC3397) { 593 sl = decode_rfc3397(NULL, 0, data, dl); 594 if (sl == 0 || sl == -1) 595 return sl; 596 l = (size_t)sl + 1; 597 tmp = malloc(l); 598 if (tmp == NULL) 599 return -1; 600 decode_rfc3397(tmp, l, data, dl); 601 sl = print_string(s, len, type, (uint8_t *)tmp, l - 1); 602 free(tmp); 603 return sl; 604 } 605 606#ifdef INET 607 if (type & RFC3361) { 608 if ((tmp = decode_rfc3361(data, dl)) == NULL) 609 return -1; 610 l = strlen(tmp); 611 sl = print_string(s, len, type, (uint8_t *)tmp, l); 612 free(tmp); 613 return sl; 614 } 615 616 if (type & RFC3442) 617 return decode_rfc3442(s, len, data, dl); 618 619 if (type & RFC5969) 620 return decode_rfc5969(s, len, data, dl); 621#endif 622 623 if (type & STRING) 624 return print_string(s, len, type, data, dl); 625 626 if (type & FLAG) { 627 if (s) { 628 *s++ = '1'; 629 *s = '\0'; 630 } 631 return 1; 632 } 633 634 if (!s) { 635 if (type & UINT8) 636 l = 3; 637 else if (type & UINT16) { 638 l = 5; 639 dl /= 2; 640 } else if (type & SINT16) { 641 l = 6; 642 dl /= 2; 643 } else if (type & UINT32) { 644 l = 10; 645 dl /= 4; 646 } else if (type & SINT32) { 647 l = 11; 648 dl /= 4; 649 } else if (type & ADDRIPV4) { 650 l = 16; 651 dl /= 4; 652 } 653#ifdef INET6 654 else if (type & ADDRIPV6) { 655 e = data + dl; 656 l = 0; 657 while (data < e) { 658 if (l) 659 l++; /* space */ 660 sl = ipv6_printaddr(NULL, 0, data, ifname); 661 if (sl != -1) 662 l += (size_t)sl; 663 data += 16; 664 } 665 return (ssize_t)l; 666 } 667#endif 668 else { 669 errno = EINVAL; 670 return -1; 671 } 672 return (ssize_t)(l * dl); 673 } 674 675 t = data; 676 e = data + dl; 677 while (data < e) { 678 if (data != t) { 679 *s++ = ' '; 680 bytes++; 681 len--; 682 } 683 if (type & UINT8) { 684 sl = snprintf(s, len, "%u", *data); 685 data++; 686 } else if (type & UINT16) { 687 memcpy(&u16, data, sizeof(u16)); 688 u16 = ntohs(u16); 689 sl = snprintf(s, len, "%u", u16); 690 data += sizeof(u16); 691 } else if (type & SINT16) { 692 memcpy(&u16, data, sizeof(u16)); 693 s16 = (int16_t)ntohs(u16); 694 sl = snprintf(s, len, "%d", s16); 695 data += sizeof(u16); 696 } else if (type & UINT32) { 697 memcpy(&u32, data, sizeof(u32)); 698 u32 = ntohl(u32); 699 sl = snprintf(s, len, "%u", u32); 700 data += sizeof(u32); 701 } else if (type & SINT32) { 702 memcpy(&u32, data, sizeof(u32)); 703 s32 = (int32_t)ntohl(u32); 704 sl = snprintf(s, len, "%d", s32); 705 data += sizeof(u32); 706 } else if (type & ADDRIPV4) { 707 memcpy(&addr.s_addr, data, sizeof(addr.s_addr)); 708 sl = snprintf(s, len, "%s", inet_ntoa(addr)); 709 data += sizeof(addr.s_addr); 710 } 711#ifdef INET6 712 else if (type & ADDRIPV6) { 713 ssize_t r; 714 715 r = ipv6_printaddr(s, len, data, ifname); 716 if (r != -1) 717 sl = r; 718 else 719 sl = 0; 720 data += 16; 721 } 722#endif 723 else 724 sl = 0; 725 len -= (size_t)sl; 726 bytes += sl; 727 s += sl; 728 } 729 730 return bytes; 731} 732 733int 734dhcp_set_leasefile(char *leasefile, size_t len, int family, 735 const struct interface *ifp, const char *extra) 736{ 737 char ssid[len]; 738 739 if (ifp->name[0] == '\0') { 740 strlcpy(leasefile, ifp->ctx->pidfile, len); 741 return 0; 742 } 743 744 if (strlen(ifp->lease_identifier) > 0) { 745 /* Only supports lease identifier for IPv4 for now. */ 746 if (family == AF_INET) { 747 return snprintf(leasefile, len, LEASEFILE, 748 ifp->lease_identifier, "", ""); 749 } 750 } 751 752 switch (family) { 753 case AF_INET: 754 case AF_INET6: 755 break; 756 default: 757 errno = EINVAL; 758 return -1; 759 } 760 761 if (ifp->wireless) { 762 ssid[0] = '-'; 763 print_string(ssid + 1, sizeof(ssid) - 1, 764 ESCFILE, 765 (const uint8_t *)ifp->ssid, ifp->ssid_len); 766 } else 767 ssid[0] = '\0'; 768 return snprintf(leasefile, len, 769 family == AF_INET ? LEASEFILE : LEASEFILE6, 770 ifp->name, ssid, extra); 771} 772 773static size_t 774dhcp_envoption1(struct dhcpcd_ctx *ctx, char **env, const char *prefix, 775 const struct dhcp_opt *opt, int vname, const uint8_t *od, size_t ol, 776 const char *ifname) 777{ 778 ssize_t len; 779 size_t e; 780 char *v, *val; 781 782 if (opt->len && opt->len < ol) 783 ol = opt->len; 784 len = print_option(NULL, 0, opt->type, od, ol, ifname); 785 if (len < 0) 786 return 0; 787 if (vname) 788 e = strlen(opt->var) + 1; 789 else 790 e = 0; 791 if (prefix) 792 e += strlen(prefix); 793 e += (size_t)len + 2; 794 if (env == NULL) 795 return e; 796 v = val = *env = malloc(e); 797 if (v == NULL) { 798 logger(ctx, LOG_ERR, "%s: %m", __func__); 799 return 0; 800 } 801 if (vname) 802 v += snprintf(val, e, "%s_%s=", prefix, opt->var); 803 else 804 v += snprintf(val, e, "%s=", prefix); 805 if (len != 0) 806 print_option(v, (size_t)len + 1, opt->type, od, ol, ifname); 807 return e; 808} 809 810size_t 811dhcp_envoption(struct dhcpcd_ctx *ctx, char **env, const char *prefix, 812 const char *ifname, struct dhcp_opt *opt, 813 const uint8_t *(*dgetopt)(struct dhcpcd_ctx *, 814 size_t *, unsigned int *, size_t *, 815 const uint8_t *, size_t, struct dhcp_opt **), 816 const uint8_t *od, size_t ol) 817{ 818 size_t e, i, n, eos, eol; 819 unsigned int eoc; 820 const uint8_t *eod; 821 int ov; 822 struct dhcp_opt *eopt, *oopt; 823 char *pfx; 824 825 /* If no embedded or encapsulated options, it's easy */ 826 if (opt->embopts_len == 0 && opt->encopts_len == 0) { 827 if (dhcp_envoption1(ctx, env == NULL ? NULL : &env[0], 828 prefix, opt, 1, od, ol, ifname)) 829 return 1; 830 return 0; 831 } 832 833 /* Create a new prefix based on the option */ 834 if (env) { 835 if (opt->type & INDEX) { 836 if (opt->index > 999) { 837 errno = ENOBUFS; 838 logger(ctx, LOG_ERR, "%s: %m", __func__); 839 return 0; 840 } 841 } 842 e = strlen(prefix) + strlen(opt->var) + 2 + 843 (opt->type & INDEX ? 3 : 0); 844 pfx = malloc(e); 845 if (pfx == NULL) { 846 logger(ctx, LOG_ERR, "%s: %m", __func__); 847 return 0; 848 } 849 if (opt->type & INDEX) 850 snprintf(pfx, e, "%s_%s%d", prefix, 851 opt->var, ++opt->index); 852 else 853 snprintf(pfx, e, "%s_%s", prefix, opt->var); 854 } else 855 pfx = NULL; 856 857 /* Embedded options are always processed first as that 858 * is a fixed layout */ 859 n = 0; 860 for (i = 0, eopt = opt->embopts; i < opt->embopts_len; i++, eopt++) { 861 e = dhcp_optlen(eopt, ol); 862 if (e == 0) 863 /* Report error? */ 864 return 0; 865 /* Use the option prefix if the embedded option 866 * name is different. 867 * This avoids new_fqdn_fqdn which would be silly. */ 868 ov = strcmp(opt->var, eopt->var); 869 if (dhcp_envoption1(ctx, env == NULL ? NULL : &env[n], 870 pfx, eopt, ov, od, e, ifname)) 871 n++; 872 od += e; 873 ol -= e; 874 } 875 876 /* Enumerate our encapsulated options */ 877 if (opt->encopts_len && ol > 0) { 878 /* Zero any option indexes 879 * We assume that referenced encapsulated options are NEVER 880 * recursive as the index order could break. */ 881 for (i = 0, eopt = opt->encopts; 882 i < opt->encopts_len; 883 i++, eopt++) 884 { 885 eoc = opt->option; 886 if (eopt->type & OPTION) { 887 dgetopt(ctx, NULL, &eoc, NULL, NULL, 0, &oopt); 888 if (oopt) 889 oopt->index = 0; 890 } 891 } 892 893 while ((eod = dgetopt(ctx, &eos, &eoc, &eol, od, ol, &oopt))) { 894 for (i = 0, eopt = opt->encopts; 895 i < opt->encopts_len; 896 i++, eopt++) 897 { 898 if (eopt->option == eoc) { 899 if (eopt->type & OPTION) { 900 if (oopt == NULL) 901 /* Report error? */ 902 continue; 903 } 904 n += dhcp_envoption(ctx, 905 env == NULL ? NULL : &env[n], pfx, 906 ifname, 907 eopt->type & OPTION ? oopt : eopt, 908 dgetopt, eod, eol); 909 break; 910 } 911 } 912 od += eos + eol; 913 ol -= eos + eol; 914 } 915 } 916 917 if (env) 918 free(pfx); 919 920 /* Return number of options found */ 921 return n; 922} 923 924void 925dhcp_zero_index(struct dhcp_opt *opt) 926{ 927 size_t i; 928 struct dhcp_opt *o; 929 930 opt->index = 0; 931 for (i = 0, o = opt->embopts; i < opt->embopts_len; i++, o++) 932 dhcp_zero_index(o); 933 for (i = 0, o = opt->encopts; i < opt->encopts_len; i++, o++) 934 dhcp_zero_index(o); 935} 936