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