Annotation of embedaddon/dhcp/common/options.c, revision 1.1
1.1 ! misho 1: /* options.c
! 2:
! 3: DHCP options parsing and reassembly. */
! 4:
! 5: /*
! 6: * Copyright (c) 2004-2011 by Internet Systems Consortium, Inc. ("ISC")
! 7: * Copyright (c) 1995-2003 by Internet Software Consortium
! 8: *
! 9: * Permission to use, copy, modify, and distribute this software for any
! 10: * purpose with or without fee is hereby granted, provided that the above
! 11: * copyright notice and this permission notice appear in all copies.
! 12: *
! 13: * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
! 14: * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
! 15: * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
! 16: * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
! 17: * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
! 18: * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
! 19: * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
! 20: *
! 21: * Internet Systems Consortium, Inc.
! 22: * 950 Charter Street
! 23: * Redwood City, CA 94063
! 24: * <info@isc.org>
! 25: * https://www.isc.org/
! 26: *
! 27: * This software has been written for Internet Systems Consortium
! 28: * by Ted Lemon in cooperation with Vixie Enterprises and Nominum, Inc.
! 29: * To learn more about Internet Systems Consortium, see
! 30: * ``https://www.isc.org/''. To learn more about Vixie Enterprises,
! 31: * see ``http://www.vix.com''. To learn more about Nominum, Inc., see
! 32: * ``http://www.nominum.com''.
! 33: */
! 34:
! 35: #define DHCP_OPTION_DATA
! 36: #include "dhcpd.h"
! 37: #include <omapip/omapip_p.h>
! 38: #include <limits.h>
! 39:
! 40: struct option *vendor_cfg_option;
! 41:
! 42: static int pretty_text(char **, char *, const unsigned char **,
! 43: const unsigned char *, int);
! 44: static int pretty_domain(char **, char *, const unsigned char **,
! 45: const unsigned char *);
! 46: static int prepare_option_buffer(struct universe *universe, struct buffer *bp,
! 47: unsigned char *buffer, unsigned length,
! 48: unsigned code, int terminatep,
! 49: struct option_cache **opp);
! 50:
! 51: /* Parse all available options out of the specified packet. */
! 52:
! 53: int parse_options (packet)
! 54: struct packet *packet;
! 55: {
! 56: struct option_cache *op = (struct option_cache *)0;
! 57:
! 58: /* Allocate a new option state. */
! 59: if (!option_state_allocate (&packet -> options, MDL)) {
! 60: packet -> options_valid = 0;
! 61: return 0;
! 62: }
! 63:
! 64: /* If we don't see the magic cookie, there's nothing to parse. */
! 65: if (memcmp (packet -> raw -> options, DHCP_OPTIONS_COOKIE, 4)) {
! 66: packet -> options_valid = 0;
! 67: return 1;
! 68: }
! 69:
! 70: /* Go through the options field, up to the end of the packet
! 71: or the End field. */
! 72: if (!parse_option_buffer (packet -> options,
! 73: &packet -> raw -> options [4],
! 74: (packet -> packet_length -
! 75: DHCP_FIXED_NON_UDP - 4),
! 76: &dhcp_universe)) {
! 77:
! 78: /* STSN servers have a bug where they send a mangled
! 79: domain-name option, and whatever is beyond that in
! 80: the packet is junk. Microsoft clients accept this,
! 81: which is probably why whoever implemented the STSN
! 82: server isn't aware of the problem yet. To work around
! 83: this, we will accept corrupt packets from the server if
! 84: they contain a valid DHCP_MESSAGE_TYPE option, but
! 85: will not accept any corrupt client packets (the ISC DHCP
! 86: server is sufficiently widely used that it is probably
! 87: beneficial for it to be picky) and will not accept
! 88: packets whose type can't be determined. */
! 89:
! 90: if ((op = lookup_option (&dhcp_universe, packet -> options,
! 91: DHO_DHCP_MESSAGE_TYPE))) {
! 92: if (!op -> data.data ||
! 93: (op -> data.data [0] != DHCPOFFER &&
! 94: op -> data.data [0] != DHCPACK &&
! 95: op -> data.data [0] != DHCPNAK))
! 96: return 0;
! 97: } else
! 98: return 0;
! 99: }
! 100:
! 101: /* If we parsed a DHCP Option Overload option, parse more
! 102: options out of the buffer(s) containing them. */
! 103: if ((op = lookup_option (&dhcp_universe, packet -> options,
! 104: DHO_DHCP_OPTION_OVERLOAD))) {
! 105: if (op -> data.data [0] & 1) {
! 106: if (!parse_option_buffer
! 107: (packet -> options,
! 108: (unsigned char *)packet -> raw -> file,
! 109: sizeof packet -> raw -> file,
! 110: &dhcp_universe))
! 111: return 0;
! 112: }
! 113: if (op -> data.data [0] & 2) {
! 114: if (!parse_option_buffer
! 115: (packet -> options,
! 116: (unsigned char *)packet -> raw -> sname,
! 117: sizeof packet -> raw -> sname,
! 118: &dhcp_universe))
! 119: return 0;
! 120: }
! 121: }
! 122: packet -> options_valid = 1;
! 123: return 1;
! 124: }
! 125:
! 126: /* Parse options out of the specified buffer, storing addresses of option
! 127: * values in packet->options.
! 128: */
! 129: int parse_option_buffer (options, buffer, length, universe)
! 130: struct option_state *options;
! 131: const unsigned char *buffer;
! 132: unsigned length;
! 133: struct universe *universe;
! 134: {
! 135: unsigned len, offset;
! 136: unsigned code;
! 137: struct option_cache *op = NULL, *nop = NULL;
! 138: struct buffer *bp = (struct buffer *)0;
! 139: struct option *option = NULL;
! 140: char *reason = "general failure";
! 141:
! 142: if (!buffer_allocate (&bp, length, MDL)) {
! 143: log_error ("no memory for option buffer.");
! 144: return 0;
! 145: }
! 146: memcpy (bp -> data, buffer, length);
! 147:
! 148: for (offset = 0;
! 149: (offset + universe->tag_size) <= length &&
! 150: (code = universe->get_tag(buffer + offset)) != universe->end; ) {
! 151: offset += universe->tag_size;
! 152:
! 153: /* Pad options don't have a length - just skip them. */
! 154: if (code == DHO_PAD)
! 155: continue;
! 156:
! 157: /* Don't look for length if the buffer isn't that big. */
! 158: if ((offset + universe->length_size) > length) {
! 159: reason = "code tag at end of buffer - missing "
! 160: "length field";
! 161: goto bogus;
! 162: }
! 163:
! 164: /* All other fields (except PAD and END handled above)
! 165: * have a length field, unless it's a DHCPv6 zero-length
! 166: * options space (eg any of the enterprise-id'd options).
! 167: *
! 168: * Zero-length-size option spaces basically consume the
! 169: * entire options buffer, so have at it.
! 170: */
! 171: if (universe->get_length != NULL)
! 172: len = universe->get_length(buffer + offset);
! 173: else if (universe->length_size == 0)
! 174: len = length - universe->tag_size;
! 175: else {
! 176: log_fatal("Improperly configured option space(%s): "
! 177: "may not have a nonzero length size "
! 178: "AND a NULL get_length function.",
! 179: universe->name);
! 180:
! 181: /* Silence compiler warnings. */
! 182: return 0;
! 183: }
! 184:
! 185: offset += universe->length_size;
! 186:
! 187: option_code_hash_lookup(&option, universe->code_hash, &code,
! 188: 0, MDL);
! 189:
! 190: /* If the length is outrageous, the options are bad. */
! 191: if (offset + len > length) {
! 192: reason = "option length exceeds option buffer length";
! 193: bogus:
! 194: log_error("parse_option_buffer: malformed option "
! 195: "%s.%s (code %u): %s.", universe->name,
! 196: option ? option->name : "<unknown>",
! 197: code, reason);
! 198: buffer_dereference (&bp, MDL);
! 199: return 0;
! 200: }
! 201:
! 202: /* If the option contains an encapsulation, parse it. If
! 203: the parse fails, or the option isn't an encapsulation (by
! 204: far the most common case), or the option isn't entirely
! 205: an encapsulation, keep the raw data as well. */
! 206: if (!(option &&
! 207: (option->format[0] == 'e' ||
! 208: option->format[0] == 'E') &&
! 209: (parse_encapsulated_suboptions(options, option,
! 210: bp->data + offset, len,
! 211: universe, NULL)))) {
! 212: op = lookup_option(universe, options, code);
! 213:
! 214: if (op != NULL && universe->concat_duplicates) {
! 215: struct data_string new;
! 216: memset(&new, 0, sizeof new);
! 217: if (!buffer_allocate(&new.buffer,
! 218: op->data.len + len,
! 219: MDL)) {
! 220: log_error("parse_option_buffer: "
! 221: "No memory.");
! 222: buffer_dereference(&bp, MDL);
! 223: return 0;
! 224: }
! 225: /* Copy old option to new data object. */
! 226: memcpy(new.buffer->data, op->data.data,
! 227: op->data.len);
! 228: /* Concat new option behind old. */
! 229: memcpy(new.buffer->data + op->data.len,
! 230: bp->data + offset, len);
! 231: new.len = op->data.len + len;
! 232: new.data = new.buffer->data;
! 233: /* Save new concat'd object. */
! 234: data_string_forget(&op->data, MDL);
! 235: data_string_copy(&op->data, &new, MDL);
! 236: data_string_forget(&new, MDL);
! 237: } else if (op != NULL) {
! 238: /* We must append this statement onto the
! 239: * end of the list.
! 240: */
! 241: while (op->next != NULL)
! 242: op = op->next;
! 243:
! 244: if (!option_cache_allocate(&nop, MDL)) {
! 245: log_error("parse_option_buffer: "
! 246: "No memory.");
! 247: buffer_dereference(&bp, MDL);
! 248: return 0;
! 249: }
! 250:
! 251: option_reference(&nop->option, op->option, MDL);
! 252:
! 253: nop->data.buffer = NULL;
! 254: buffer_reference(&nop->data.buffer, bp, MDL);
! 255: nop->data.data = bp->data + offset;
! 256: nop->data.len = len;
! 257:
! 258: option_cache_reference(&op->next, nop, MDL);
! 259: option_cache_dereference(&nop, MDL);
! 260: } else {
! 261: save_option_buffer(universe, options, bp,
! 262: bp->data + offset, len,
! 263: code, 1);
! 264: }
! 265: }
! 266: option_dereference(&option, MDL);
! 267: offset += len;
! 268: }
! 269: buffer_dereference (&bp, MDL);
! 270: return 1;
! 271: }
! 272:
! 273: /* If an option in an option buffer turns out to be an encapsulation,
! 274: figure out what to do. If we don't know how to de-encapsulate it,
! 275: or it's not well-formed, return zero; otherwise, return 1, indicating
! 276: that we succeeded in de-encapsulating it. */
! 277:
! 278: struct universe *find_option_universe (struct option *eopt, const char *uname)
! 279: {
! 280: int i;
! 281: char *s, *t;
! 282: struct universe *universe = (struct universe *)0;
! 283:
! 284: /* Look for the E option in the option format. */
! 285: s = strchr (eopt -> format, 'E');
! 286: if (!s) {
! 287: log_error ("internal encapsulation format error 1.");
! 288: return 0;
! 289: }
! 290: /* Look for the universe name in the option format. */
! 291: t = strchr (++s, '.');
! 292: /* If there was no trailing '.', or there's something after the
! 293: trailing '.', the option is bogus and we can't use it. */
! 294: if (!t || t [1]) {
! 295: log_error ("internal encapsulation format error 2.");
! 296: return 0;
! 297: }
! 298: if (t == s && uname) {
! 299: for (i = 0; i < universe_count; i++) {
! 300: if (!strcmp (universes [i] -> name, uname)) {
! 301: universe = universes [i];
! 302: break;
! 303: }
! 304: }
! 305: } else if (t != s) {
! 306: for (i = 0; i < universe_count; i++) {
! 307: if (strlen (universes [i] -> name) == t - s &&
! 308: !memcmp (universes [i] -> name,
! 309: s, (unsigned)(t - s))) {
! 310: universe = universes [i];
! 311: break;
! 312: }
! 313: }
! 314: }
! 315: return universe;
! 316: }
! 317:
! 318: /* If an option in an option buffer turns out to be an encapsulation,
! 319: figure out what to do. If we don't know how to de-encapsulate it,
! 320: or it's not well-formed, return zero; otherwise, return 1, indicating
! 321: that we succeeded in de-encapsulating it. */
! 322:
! 323: int parse_encapsulated_suboptions (struct option_state *options,
! 324: struct option *eopt,
! 325: const unsigned char *buffer,
! 326: unsigned len, struct universe *eu,
! 327: const char *uname)
! 328: {
! 329: int i;
! 330: struct universe *universe = find_option_universe (eopt, uname);
! 331:
! 332: /* If we didn't find the universe, we can't do anything with it
! 333: right now (e.g., we can't decode vendor options until we've
! 334: decoded the packet and executed the scopes that it matches). */
! 335: if (!universe)
! 336: return 0;
! 337:
! 338: /* If we don't have a decoding function for it, we can't decode
! 339: it. */
! 340: if (!universe -> decode)
! 341: return 0;
! 342:
! 343: i = (*universe -> decode) (options, buffer, len, universe);
! 344:
! 345: /* If there is stuff before the suboptions, we have to keep it. */
! 346: if (eopt -> format [0] != 'E')
! 347: return 0;
! 348: /* Otherwise, return the status of the decode function. */
! 349: return i;
! 350: }
! 351:
! 352: int fqdn_universe_decode (struct option_state *options,
! 353: const unsigned char *buffer,
! 354: unsigned length, struct universe *u)
! 355: {
! 356: struct buffer *bp = (struct buffer *)0;
! 357:
! 358: /* FQDN options have to be at least four bytes long. */
! 359: if (length < 3)
! 360: return 0;
! 361:
! 362: /* Save the contents of the option in a buffer. */
! 363: if (!buffer_allocate (&bp, length + 4, MDL)) {
! 364: log_error ("no memory for option buffer.");
! 365: return 0;
! 366: }
! 367: memcpy (&bp -> data [3], buffer + 1, length - 1);
! 368:
! 369: if (buffer [0] & 4) /* encoded */
! 370: bp -> data [0] = 1;
! 371: else
! 372: bp -> data [0] = 0;
! 373: if (!save_option_buffer(&fqdn_universe, options, bp,
! 374: bp->data, 1, FQDN_ENCODED, 0)) {
! 375: bad:
! 376: buffer_dereference (&bp, MDL);
! 377: return 0;
! 378: }
! 379:
! 380: if (buffer [0] & 1) /* server-update */
! 381: bp -> data [2] = 1;
! 382: else
! 383: bp -> data [2] = 0;
! 384: if (buffer [0] & 2) /* no-client-update */
! 385: bp -> data [1] = 1;
! 386: else
! 387: bp -> data [1] = 0;
! 388:
! 389: /* XXX Ideally we should store the name in DNS format, so if the
! 390: XXX label isn't in DNS format, we convert it to DNS format,
! 391: XXX rather than converting labels specified in DNS format to
! 392: XXX the plain ASCII representation. But that's hard, so
! 393: XXX not now. */
! 394:
! 395: /* Not encoded using DNS format? */
! 396: if (!bp -> data [0]) {
! 397: unsigned i;
! 398:
! 399: /* Some broken clients NUL-terminate this option. */
! 400: if (buffer [length - 1] == 0) {
! 401: --length;
! 402: bp -> data [1] = 1;
! 403: }
! 404:
! 405: /* Determine the length of the hostname component of the
! 406: name. If the name contains no '.' character, it
! 407: represents a non-qualified label. */
! 408: for (i = 3; i < length && buffer [i] != '.'; i++);
! 409: i -= 3;
! 410:
! 411: /* Note: If the client sends a FQDN, the first '.' will
! 412: be used as a NUL terminator for the hostname. */
! 413: if (i && (!save_option_buffer(&fqdn_universe, options, bp,
! 414: &bp->data[5], i,
! 415: FQDN_HOSTNAME, 0)))
! 416: goto bad;
! 417: /* Note: If the client sends a single label, the
! 418: FQDN_DOMAINNAME option won't be set. */
! 419: if (length > 4 + i &&
! 420: (!save_option_buffer(&fqdn_universe, options, bp,
! 421: &bp -> data[6 + i], length - 4 - i,
! 422: FQDN_DOMAINNAME, 1)))
! 423: goto bad;
! 424: /* Also save the whole name. */
! 425: if (length > 3) {
! 426: if (!save_option_buffer(&fqdn_universe, options, bp,
! 427: &bp -> data [5], length - 3,
! 428: FQDN_FQDN, 1))
! 429: goto bad;
! 430: }
! 431: } else {
! 432: unsigned len;
! 433: unsigned total_len = 0;
! 434: unsigned first_len = 0;
! 435: int terminated = 0;
! 436: unsigned char *s;
! 437:
! 438: s = &bp -> data[5];
! 439:
! 440: while (s < &bp -> data[0] + length + 2) {
! 441: len = *s;
! 442: if (len > 63) {
! 443: log_info ("fancy bits in fqdn option");
! 444: return 0;
! 445: }
! 446: if (len == 0) {
! 447: terminated = 1;
! 448: break;
! 449: }
! 450: if (s + len > &bp -> data [0] + length + 3) {
! 451: log_info ("fqdn tag longer than buffer");
! 452: return 0;
! 453: }
! 454:
! 455: if (first_len == 0) {
! 456: first_len = len;
! 457: }
! 458:
! 459: *s = '.';
! 460: s += len + 1;
! 461: total_len += len + 1;
! 462: }
! 463:
! 464: /* We wind up with a length that's one too many because
! 465: we shouldn't increment for the last label, but there's
! 466: no way to tell we're at the last label until we exit
! 467: the loop. :'*/
! 468: if (total_len > 0)
! 469: total_len--;
! 470:
! 471: if (!terminated) {
! 472: first_len = total_len;
! 473: }
! 474:
! 475: if (first_len > 0 &&
! 476: !save_option_buffer(&fqdn_universe, options, bp,
! 477: &bp -> data[6], first_len,
! 478: FQDN_HOSTNAME, 0))
! 479: goto bad;
! 480: if (total_len > 0 && first_len != total_len) {
! 481: if (!save_option_buffer(&fqdn_universe, options, bp,
! 482: &bp->data[6 + first_len],
! 483: total_len - first_len,
! 484: FQDN_DOMAINNAME, 1))
! 485: goto bad;
! 486: }
! 487: if (total_len > 0)
! 488: if (!save_option_buffer (&fqdn_universe, options, bp,
! 489: &bp -> data [6], total_len,
! 490: FQDN_FQDN, 1))
! 491: goto bad;
! 492: }
! 493:
! 494: if (!save_option_buffer (&fqdn_universe, options, bp,
! 495: &bp -> data [1], 1,
! 496: FQDN_NO_CLIENT_UPDATE, 0))
! 497: goto bad;
! 498: if (!save_option_buffer (&fqdn_universe, options, bp,
! 499: &bp -> data [2], 1,
! 500: FQDN_SERVER_UPDATE, 0))
! 501: goto bad;
! 502:
! 503: if (!save_option_buffer (&fqdn_universe, options, bp,
! 504: &bp -> data [3], 1,
! 505: FQDN_RCODE1, 0))
! 506: goto bad;
! 507: if (!save_option_buffer (&fqdn_universe, options, bp,
! 508: &bp -> data [4], 1,
! 509: FQDN_RCODE2, 0))
! 510: goto bad;
! 511:
! 512: buffer_dereference (&bp, MDL);
! 513: return 1;
! 514: }
! 515:
! 516: /*
! 517: * Load all options into a buffer, and then split them out into the three
! 518: * separate fields in the dhcp packet (options, file, and sname) where
! 519: * options can be stored.
! 520: */
! 521: int
! 522: cons_options(struct packet *inpacket, struct dhcp_packet *outpacket,
! 523: struct lease *lease, struct client_state *client_state,
! 524: int mms, struct option_state *in_options,
! 525: struct option_state *cfg_options,
! 526: struct binding_scope **scope,
! 527: int overload_avail, int terminate, int bootpp,
! 528: struct data_string *prl, const char *vuname)
! 529: {
! 530: #define PRIORITY_COUNT 300
! 531: unsigned priority_list[PRIORITY_COUNT];
! 532: int priority_len;
! 533: unsigned char buffer[4096], agentopts[1024];
! 534: unsigned index = 0;
! 535: unsigned mb_size = 0, mb_max = 0;
! 536: unsigned option_size = 0, agent_size = 0;
! 537: unsigned length;
! 538: int i;
! 539: struct option_cache *op;
! 540: struct data_string ds;
! 541: pair pp, *hash;
! 542: int overload_used = 0;
! 543: int of1 = 0, of2 = 0;
! 544:
! 545: memset(&ds, 0, sizeof ds);
! 546:
! 547: /*
! 548: * If there's a Maximum Message Size option in the incoming packet
! 549: * and no alternate maximum message size has been specified, or
! 550: * if the one specified in the packet is shorter than the
! 551: * alternative, take the one in the packet.
! 552: */
! 553:
! 554: if (inpacket &&
! 555: (op = lookup_option(&dhcp_universe, inpacket->options,
! 556: DHO_DHCP_MAX_MESSAGE_SIZE))) {
! 557: evaluate_option_cache(&ds, inpacket,
! 558: lease, client_state, in_options,
! 559: cfg_options, scope, op, MDL);
! 560: if (ds.len >= sizeof (u_int16_t)) {
! 561: i = getUShort(ds.data);
! 562: if(!mms || (i < mms))
! 563: mms = i;
! 564: }
! 565: data_string_forget(&ds, MDL);
! 566: }
! 567:
! 568: /*
! 569: * If the client has provided a maximum DHCP message size,
! 570: * use that, up to the MTU limit. Otherwise, if it's BOOTP,
! 571: * only 64 bytes; otherwise use up to the minimum IP MTU size
! 572: * (576 bytes).
! 573: *
! 574: * XXX if a BOOTP client specifies a max message size, we will
! 575: * honor it.
! 576: */
! 577: if (mms) {
! 578: if (mms < DHCP_MTU_MIN)
! 579: /* Enforce minimum packet size, per RFC 2132 */
! 580: mb_size = DHCP_MIN_OPTION_LEN;
! 581: else if (mms > DHCP_MTU_MAX)
! 582: /*
! 583: * TODO: Packets longer than 1500 bytes really
! 584: * should be allowed, but it requires upstream
! 585: * changes to the way the packet is allocated. For
! 586: * now, we forbid them. They won't be needed very
! 587: * often anyway.
! 588: */
! 589: mb_size = DHCP_MAX_OPTION_LEN;
! 590: else
! 591: mb_size = mms - DHCP_FIXED_LEN;
! 592: } else if (bootpp) {
! 593: mb_size = 64;
! 594: if (inpacket != NULL &&
! 595: (inpacket->packet_length >= 64 + DHCP_FIXED_NON_UDP))
! 596: mb_size = inpacket->packet_length - DHCP_FIXED_NON_UDP;
! 597: } else
! 598: mb_size = DHCP_MIN_OPTION_LEN;
! 599:
! 600: /*
! 601: * If answering a client message, see whether any relay agent
! 602: * options were included with the message. If so, save them
! 603: * to copy back in later, and make space in the main buffer
! 604: * to accommodate them
! 605: */
! 606: if (client_state == NULL) {
! 607: priority_list[0] = DHO_DHCP_AGENT_OPTIONS;
! 608: priority_len = 1;
! 609: agent_size = store_options(NULL, agentopts, 0,
! 610: sizeof(agentopts),
! 611: inpacket, lease, client_state,
! 612: in_options, cfg_options, scope,
! 613: priority_list, priority_len,
! 614: 0, 0, 0, NULL);
! 615:
! 616: mb_size += agent_size;
! 617: if (mb_size > DHCP_MAX_OPTION_LEN)
! 618: mb_size = DHCP_MAX_OPTION_LEN;
! 619: }
! 620:
! 621: /*
! 622: * Set offsets for buffer data to be copied into filename
! 623: * and servername fields
! 624: */
! 625: mb_max = mb_size;
! 626:
! 627: if (overload_avail & 1) {
! 628: of1 = mb_max;
! 629: mb_max += DHCP_FILE_LEN;
! 630: }
! 631:
! 632: if (overload_avail & 2) {
! 633: of2 = mb_max;
! 634: mb_max += DHCP_SNAME_LEN;
! 635: }
! 636:
! 637: /*
! 638: * Preload the option priority list with protocol-mandatory options.
! 639: * This effectively gives these options the highest priority.
! 640: * This provides the order for any available options, the option
! 641: * must be in the option cache in order to actually be included.
! 642: */
! 643: priority_len = 0;
! 644: priority_list[priority_len++] = DHO_DHCP_MESSAGE_TYPE;
! 645: priority_list[priority_len++] = DHO_DHCP_SERVER_IDENTIFIER;
! 646: priority_list[priority_len++] = DHO_DHCP_LEASE_TIME;
! 647: priority_list[priority_len++] = DHO_DHCP_MESSAGE;
! 648: priority_list[priority_len++] = DHO_DHCP_REQUESTED_ADDRESS;
! 649: priority_list[priority_len++] = DHO_ASSOCIATED_IP;
! 650:
! 651: if (prl != NULL && prl->len > 0) {
! 652: if ((op = lookup_option(&dhcp_universe, cfg_options,
! 653: DHO_SUBNET_SELECTION))) {
! 654: if (priority_len < PRIORITY_COUNT)
! 655: priority_list[priority_len++] =
! 656: DHO_SUBNET_SELECTION;
! 657: }
! 658:
! 659: data_string_truncate(prl, (PRIORITY_COUNT - priority_len));
! 660:
! 661: for (i = 0; i < prl->len; i++) {
! 662: /*
! 663: * Prevent client from changing order of delivery
! 664: * of relay agent information option.
! 665: */
! 666: if (prl->data[i] != DHO_DHCP_AGENT_OPTIONS)
! 667: priority_list[priority_len++] = prl->data[i];
! 668: }
! 669:
! 670: /*
! 671: * If the client doesn't request the FQDN option explicitly,
! 672: * to indicate priority, consider it lowest priority. Fit
! 673: * in the packet if there is space. Note that the option
! 674: * may only be included if the client supplied one.
! 675: */
! 676: if ((priority_len < PRIORITY_COUNT) &&
! 677: (lookup_option(&fqdn_universe, inpacket->options,
! 678: FQDN_ENCODED) != NULL))
! 679: priority_list[priority_len++] = DHO_FQDN;
! 680:
! 681: /*
! 682: * Some DHCP Servers will give the subnet-mask option if
! 683: * it is not on the parameter request list - so some client
! 684: * implementations have come to rely on this - so we will
! 685: * also make sure we supply this, at lowest priority.
! 686: *
! 687: * This is only done in response to DHCPDISCOVER or
! 688: * DHCPREQUEST messages, to avoid providing the option on
! 689: * DHCPINFORM or DHCPLEASEQUERY responses (if the client
! 690: * didn't request it).
! 691: */
! 692: if ((priority_len < PRIORITY_COUNT) &&
! 693: ((inpacket->packet_type == DHCPDISCOVER) ||
! 694: (inpacket->packet_type == DHCPREQUEST)))
! 695: priority_list[priority_len++] = DHO_SUBNET_MASK;
! 696: } else {
! 697: /*
! 698: * First, hardcode some more options that ought to be
! 699: * sent first...these are high priority to have in the
! 700: * packet.
! 701: */
! 702: priority_list[priority_len++] = DHO_SUBNET_MASK;
! 703: priority_list[priority_len++] = DHO_ROUTERS;
! 704: priority_list[priority_len++] = DHO_DOMAIN_NAME_SERVERS;
! 705: priority_list[priority_len++] = DHO_HOST_NAME;
! 706: priority_list[priority_len++] = DHO_FQDN;
! 707:
! 708: /*
! 709: * Append a list of the standard DHCP options from the
! 710: * standard DHCP option space. Actually, if a site
! 711: * option space hasn't been specified, we wind up
! 712: * treating the dhcp option space as the site option
! 713: * space, and the first for loop is skipped, because
! 714: * it's slightly more general to do it this way,
! 715: * taking the 1Q99 DHCP futures work into account.
! 716: */
! 717: if (cfg_options->site_code_min) {
! 718: for (i = 0; i < OPTION_HASH_SIZE; i++) {
! 719: hash = cfg_options->universes[dhcp_universe.index];
! 720: if (hash) {
! 721: for (pp = hash[i]; pp; pp = pp->cdr) {
! 722: op = (struct option_cache *)(pp->car);
! 723: if (op->option->code <
! 724: cfg_options->site_code_min &&
! 725: priority_len < PRIORITY_COUNT &&
! 726: op->option->code != DHO_DHCP_AGENT_OPTIONS)
! 727: priority_list[priority_len++] =
! 728: op->option->code;
! 729: }
! 730: }
! 731: }
! 732: }
! 733:
! 734: /*
! 735: * Now cycle through the site option space, or if there
! 736: * is no site option space, we'll be cycling through the
! 737: * dhcp option space.
! 738: */
! 739: for (i = 0; i < OPTION_HASH_SIZE; i++) {
! 740: hash = cfg_options->universes[cfg_options->site_universe];
! 741: if (hash != NULL)
! 742: for (pp = hash[i]; pp; pp = pp->cdr) {
! 743: op = (struct option_cache *)(pp->car);
! 744: if (op->option->code >=
! 745: cfg_options->site_code_min &&
! 746: priority_len < PRIORITY_COUNT &&
! 747: op->option->code != DHO_DHCP_AGENT_OPTIONS)
! 748: priority_list[priority_len++] =
! 749: op->option->code;
! 750: }
! 751: }
! 752:
! 753: /*
! 754: * Put any spaces that are encapsulated on the list,
! 755: * sort out whether they contain values later.
! 756: */
! 757: for (i = 0; i < cfg_options->universe_count; i++) {
! 758: if (universes[i]->enc_opt &&
! 759: priority_len < PRIORITY_COUNT &&
! 760: universes[i]->enc_opt->universe == &dhcp_universe) {
! 761: if (universes[i]->enc_opt->code !=
! 762: DHO_DHCP_AGENT_OPTIONS)
! 763: priority_list[priority_len++] =
! 764: universes[i]->enc_opt->code;
! 765: }
! 766: }
! 767:
! 768: /*
! 769: * The vendor option space can't stand on its own, so always
! 770: * add it to the list.
! 771: */
! 772: if (priority_len < PRIORITY_COUNT)
! 773: priority_list[priority_len++] =
! 774: DHO_VENDOR_ENCAPSULATED_OPTIONS;
! 775: }
! 776:
! 777: /* Put the cookie up front... */
! 778: memcpy(buffer, DHCP_OPTIONS_COOKIE, 4);
! 779: index += 4;
! 780:
! 781: /* Copy the options into the big buffer... */
! 782: option_size = store_options(&overload_used, buffer, index, mb_max,
! 783: inpacket, lease, client_state,
! 784: in_options, cfg_options, scope,
! 785: priority_list, priority_len,
! 786: of1, of2, terminate, vuname);
! 787:
! 788: /* If store_options() failed */
! 789: if (option_size == 0)
! 790: return 0;
! 791:
! 792: /* How much was stored in the main buffer? */
! 793: index += option_size;
! 794:
! 795: /*
! 796: * If we're going to have to overload, store the overload
! 797: * option first.
! 798: */
! 799: if (overload_used) {
! 800: if (mb_size - agent_size - index < 3)
! 801: return 0;
! 802:
! 803: buffer[index++] = DHO_DHCP_OPTION_OVERLOAD;
! 804: buffer[index++] = 1;
! 805: buffer[index++] = overload_used;
! 806:
! 807: if (overload_used & 1)
! 808: memcpy(outpacket->file, &buffer[of1], DHCP_FILE_LEN);
! 809:
! 810: if (overload_used & 2)
! 811: memcpy(outpacket->sname, &buffer[of2], DHCP_SNAME_LEN);
! 812: }
! 813:
! 814: /* Now copy in preserved agent options, if any */
! 815: if (agent_size) {
! 816: if (mb_size - index >= agent_size) {
! 817: memcpy(&buffer[index], agentopts, agent_size);
! 818: index += agent_size;
! 819: } else
! 820: log_error("Unable to store relay agent information "
! 821: "in reply packet.");
! 822: }
! 823:
! 824: /* Tack a DHO_END option onto the packet if we need to. */
! 825: if (index < mb_size)
! 826: buffer[index++] = DHO_END;
! 827:
! 828: /* Copy main buffer into the options buffer of the packet */
! 829: memcpy(outpacket->options, buffer, index);
! 830:
! 831: /* Figure out the length. */
! 832: length = DHCP_FIXED_NON_UDP + index;
! 833: return length;
! 834: }
! 835:
! 836: /*
! 837: * XXX: We currently special case collecting VSIO options.
! 838: * We should be able to handle this in a more generic fashion, by
! 839: * including any encapsulated options that are present and desired.
! 840: * This will look something like the VSIO handling VSIO code.
! 841: * We may also consider handling the ORO-like options within
! 842: * encapsulated spaces.
! 843: */
! 844:
! 845: struct vsio_state {
! 846: char *buf;
! 847: int buflen;
! 848: int bufpos;
! 849: };
! 850:
! 851: static void
! 852: vsio_options(struct option_cache *oc,
! 853: struct packet *packet,
! 854: struct lease *dummy_lease,
! 855: struct client_state *dummy_client_state,
! 856: struct option_state *dummy_opt_state,
! 857: struct option_state *opt_state,
! 858: struct binding_scope **dummy_binding_scope,
! 859: struct universe *universe,
! 860: void *void_vsio_state) {
! 861: struct vsio_state *vs = (struct vsio_state *)void_vsio_state;
! 862: struct data_string ds;
! 863: int total_len;
! 864:
! 865: memset(&ds, 0, sizeof(ds));
! 866: if (evaluate_option_cache(&ds, packet, NULL,
! 867: NULL, opt_state, NULL,
! 868: &global_scope, oc, MDL)) {
! 869: total_len = ds.len + universe->tag_size + universe->length_size;
! 870: if (total_len <= (vs->buflen - vs->bufpos)) {
! 871: if (universe->tag_size == 1) {
! 872: vs->buf[vs->bufpos++] = oc->option->code;
! 873: } else if (universe->tag_size == 2) {
! 874: putUShort((unsigned char *)vs->buf+vs->bufpos,
! 875: oc->option->code);
! 876: vs->bufpos += 2;
! 877: } else if (universe->tag_size == 4) {
! 878: putULong((unsigned char *)vs->buf+vs->bufpos,
! 879: oc->option->code);
! 880: vs->bufpos += 4;
! 881: }
! 882: if (universe->length_size == 1) {
! 883: vs->buf[vs->bufpos++] = ds.len;
! 884: } else if (universe->length_size == 2) {
! 885: putUShort((unsigned char *)vs->buf+vs->bufpos,
! 886: ds.len);
! 887: vs->bufpos += 2;
! 888: } else if (universe->length_size == 4) {
! 889: putULong((unsigned char *)vs->buf+vs->bufpos,
! 890: ds.len);
! 891: vs->bufpos += 4;
! 892: }
! 893: memcpy(vs->buf + vs->bufpos, ds.data, ds.len);
! 894: vs->bufpos += ds.len;
! 895: } else {
! 896: log_debug("No space for option %d in VSIO space %s.",
! 897: oc->option->code, universe->name);
! 898: }
! 899: data_string_forget(&ds, MDL);
! 900: } else {
! 901: log_error("Error evaluating option %d in VSIO space %s.",
! 902: oc->option->code, universe->name);
! 903: }
! 904: }
! 905:
! 906: /*
! 907: * Stores the options from the DHCPv6 universe into the buffer given.
! 908: *
! 909: * Required options are given as a 0-terminated list of option codes.
! 910: * Once those are added, the ORO is consulted.
! 911: */
! 912:
! 913: int
! 914: store_options6(char *buf, int buflen,
! 915: struct option_state *opt_state,
! 916: struct packet *packet,
! 917: const int *required_opts,
! 918: struct data_string *oro) {
! 919: int i, j;
! 920: struct option_cache *oc;
! 921: struct option *o;
! 922: struct data_string ds;
! 923: int bufpos;
! 924: int oro_size;
! 925: u_int16_t code;
! 926: int in_required_opts;
! 927: int vsio_option_code;
! 928: int vsio_wanted;
! 929: struct vsio_state vs;
! 930: unsigned char *tmp;
! 931:
! 932: bufpos = 0;
! 933: vsio_wanted = 0;
! 934:
! 935: /*
! 936: * Find the option code for the VSIO universe.
! 937: */
! 938: vsio_option_code = 0;
! 939: o = vsio_universe.enc_opt;
! 940: while (o != NULL) {
! 941: if (o->universe == &dhcpv6_universe) {
! 942: vsio_option_code = o->code;
! 943: break;
! 944: }
! 945: o = o->universe->enc_opt;
! 946: }
! 947: if (vsio_option_code == 0) {
! 948: log_fatal("No VSIO option code found.");
! 949: }
! 950:
! 951: if (required_opts != NULL) {
! 952: for (i=0; required_opts[i] != 0; i++) {
! 953: if (required_opts[i] == vsio_option_code) {
! 954: vsio_wanted = 1;
! 955: }
! 956:
! 957: oc = lookup_option(&dhcpv6_universe,
! 958: opt_state, required_opts[i]);
! 959: if (oc == NULL) {
! 960: continue;
! 961: }
! 962: memset(&ds, 0, sizeof(ds));
! 963: for (; oc != NULL ; oc = oc->next) {
! 964: if (evaluate_option_cache(&ds, packet, NULL,
! 965: NULL, opt_state,
! 966: NULL, &global_scope,
! 967: oc, MDL)) {
! 968: if ((ds.len + 4) <=
! 969: (buflen - bufpos)) {
! 970: tmp = (unsigned char *)buf;
! 971: tmp += bufpos;
! 972: /* option tag */
! 973: putUShort(tmp,
! 974: required_opts[i]);
! 975: /* option length */
! 976: putUShort(tmp+2, ds.len);
! 977: /* option data */
! 978: memcpy(tmp+4, ds.data, ds.len);
! 979: /* update position */
! 980: bufpos += (4 + ds.len);
! 981: } else {
! 982: log_debug("No space for "
! 983: "option %d",
! 984: required_opts[i]);
! 985: }
! 986: data_string_forget(&ds, MDL);
! 987: } else {
! 988: log_error("Error evaluating option %d",
! 989: required_opts[i]);
! 990: }
! 991: }
! 992: }
! 993: }
! 994:
! 995: if (oro == NULL) {
! 996: oro_size = 0;
! 997: } else {
! 998: oro_size = oro->len / 2;
! 999: }
! 1000: for (i=0; i<oro_size; i++) {
! 1001: memcpy(&code, oro->data+(i*2), 2);
! 1002: code = ntohs(code);
! 1003:
! 1004: /*
! 1005: * See if we've already included this option because
! 1006: * it is required.
! 1007: */
! 1008: in_required_opts = 0;
! 1009: if (required_opts != NULL) {
! 1010: for (j=0; required_opts[j] != 0; j++) {
! 1011: if (required_opts[j] == code) {
! 1012: in_required_opts = 1;
! 1013: break;
! 1014: }
! 1015: }
! 1016: }
! 1017: if (in_required_opts) {
! 1018: continue;
! 1019: }
! 1020:
! 1021: /*
! 1022: * See if this is the VSIO option.
! 1023: */
! 1024: if (code == vsio_option_code) {
! 1025: vsio_wanted = 1;
! 1026: }
! 1027:
! 1028: /*
! 1029: * Not already added, find this option.
! 1030: */
! 1031: oc = lookup_option(&dhcpv6_universe, opt_state, code);
! 1032: memset(&ds, 0, sizeof(ds));
! 1033: for (; oc != NULL ; oc = oc->next) {
! 1034: if (evaluate_option_cache(&ds, packet, NULL, NULL,
! 1035: opt_state, NULL,
! 1036: &global_scope, oc, MDL)) {
! 1037: if ((ds.len + 4) <= (buflen - bufpos)) {
! 1038: tmp = (unsigned char *)buf + bufpos;
! 1039: /* option tag */
! 1040: putUShort(tmp, code);
! 1041: /* option length */
! 1042: putUShort(tmp+2, ds.len);
! 1043: /* option data */
! 1044: memcpy(tmp+4, ds.data, ds.len);
! 1045: /* update position */
! 1046: bufpos += (4 + ds.len);
! 1047: } else {
! 1048: log_debug("No space for option %d",
! 1049: code);
! 1050: }
! 1051: data_string_forget(&ds, MDL);
! 1052: } else {
! 1053: log_error("Error evaluating option %d", code);
! 1054: }
! 1055: }
! 1056: }
! 1057:
! 1058: if (vsio_wanted) {
! 1059: for (i=0; i < opt_state->universe_count; i++) {
! 1060: if (opt_state->universes[i] != NULL) {
! 1061: o = universes[i]->enc_opt;
! 1062: if ((o != NULL) &&
! 1063: (o->universe == &vsio_universe)) {
! 1064: /*
! 1065: * Add the data from this VSIO option.
! 1066: */
! 1067: vs.buf = buf;
! 1068: vs.buflen = buflen;
! 1069: vs.bufpos = bufpos+8;
! 1070: option_space_foreach(packet, NULL,
! 1071: NULL,
! 1072: NULL, opt_state,
! 1073: NULL,
! 1074: universes[i],
! 1075: (void *)&vs,
! 1076: vsio_options);
! 1077:
! 1078: /*
! 1079: * If there was actually data here,
! 1080: * add the "header".
! 1081: */
! 1082: if (vs.bufpos > bufpos+8) {
! 1083: tmp = (unsigned char *)buf +
! 1084: bufpos;
! 1085: putUShort(tmp,
! 1086: vsio_option_code);
! 1087: putUShort(tmp+2,
! 1088: vs.bufpos-bufpos-4);
! 1089: putULong(tmp+4, o->code);
! 1090:
! 1091: bufpos = vs.bufpos;
! 1092: }
! 1093: }
! 1094: }
! 1095: }
! 1096: }
! 1097:
! 1098: return bufpos;
! 1099: }
! 1100:
! 1101: /*
! 1102: * Store all the requested options into the requested buffer.
! 1103: * XXX: ought to be static
! 1104: */
! 1105: int
! 1106: store_options(int *ocount,
! 1107: unsigned char *buffer, unsigned index, unsigned buflen,
! 1108: struct packet *packet, struct lease *lease,
! 1109: struct client_state *client_state,
! 1110: struct option_state *in_options,
! 1111: struct option_state *cfg_options,
! 1112: struct binding_scope **scope,
! 1113: unsigned *priority_list, int priority_len,
! 1114: unsigned first_cutoff, int second_cutoff, int terminate,
! 1115: const char *vuname)
! 1116: {
! 1117: int bufix = 0, six = 0, tix = 0;
! 1118: int i;
! 1119: int ix;
! 1120: int tto;
! 1121: int bufend, sbufend;
! 1122: struct data_string od;
! 1123: struct option_cache *oc;
! 1124: struct option *option = NULL;
! 1125: unsigned code;
! 1126:
! 1127: /*
! 1128: * These arguments are relative to the start of the buffer, so
! 1129: * reduce them by the current buffer index, and advance the
! 1130: * buffer pointer to where we're going to start writing.
! 1131: */
! 1132: buffer = &buffer[index];
! 1133: buflen -= index;
! 1134: if (first_cutoff)
! 1135: first_cutoff -= index;
! 1136: if (second_cutoff)
! 1137: second_cutoff -= index;
! 1138:
! 1139: /* Calculate the start and end of each section of the buffer */
! 1140: bufend = sbufend = buflen;
! 1141: if (first_cutoff) {
! 1142: if (first_cutoff >= buflen)
! 1143: log_fatal("%s:%d:store_options: Invalid first cutoff.", MDL);
! 1144: bufend = first_cutoff;
! 1145:
! 1146: if (second_cutoff) {
! 1147: if (second_cutoff >= buflen)
! 1148: log_fatal("%s:%d:store_options: Invalid second cutoff.",
! 1149: MDL);
! 1150: sbufend = second_cutoff;
! 1151: }
! 1152: } else if (second_cutoff) {
! 1153: if (second_cutoff >= buflen)
! 1154: log_fatal("%s:%d:store_options: Invalid second cutoff.", MDL);
! 1155: bufend = second_cutoff;
! 1156: }
! 1157:
! 1158: memset (&od, 0, sizeof od);
! 1159:
! 1160: /* Eliminate duplicate options from the parameter request list.
! 1161: * Enforce RFC-mandated ordering of options that are present.
! 1162: */
! 1163: for (i = 0; i < priority_len - 1; i++) {
! 1164: /* Eliminate duplicates. */
! 1165: tto = 0;
! 1166: for (ix = i + 1; ix < priority_len + tto; ix++) {
! 1167: if (tto)
! 1168: priority_list [ix - tto] =
! 1169: priority_list [ix];
! 1170: if (priority_list [i] == priority_list [ix]) {
! 1171: tto++;
! 1172: priority_len--;
! 1173: }
! 1174: }
! 1175:
! 1176: /* Enforce ordering of SUBNET_MASK options, according to
! 1177: * RFC2132 Section 3.3:
! 1178: *
! 1179: * If both the subnet mask and the router option are
! 1180: * specified in a DHCP reply, the subnet mask option MUST
! 1181: * be first.
! 1182: *
! 1183: * This guidance does not specify what to do if the client
! 1184: * PRL explicitly requests the options out of order, it is
! 1185: * a general statement.
! 1186: */
! 1187: if (priority_list[i] == DHO_SUBNET_MASK) {
! 1188: for (ix = i - 1 ; ix >= 0 ; ix--) {
! 1189: if (priority_list[ix] == DHO_ROUTERS) {
! 1190: /* swap */
! 1191: priority_list[ix] = DHO_SUBNET_MASK;
! 1192: priority_list[i] = DHO_ROUTERS;
! 1193: break;
! 1194: }
! 1195: }
! 1196: }
! 1197: }
! 1198:
! 1199: /* Copy out the options in the order that they appear in the
! 1200: priority list... */
! 1201: for (i = 0; i < priority_len; i++) {
! 1202: /* Number of bytes left to store (some may already
! 1203: have been stored by a previous pass). */
! 1204: unsigned length;
! 1205: int optstart, soptstart, toptstart;
! 1206: struct universe *u;
! 1207: int have_encapsulation = 0;
! 1208: struct data_string encapsulation;
! 1209: int splitup;
! 1210:
! 1211: memset (&encapsulation, 0, sizeof encapsulation);
! 1212: have_encapsulation = 0;
! 1213:
! 1214: if (option != NULL)
! 1215: option_dereference(&option, MDL);
! 1216:
! 1217: /* Code for next option to try to store. */
! 1218: code = priority_list [i];
! 1219:
! 1220: /* Look up the option in the site option space if the code
! 1221: is above the cutoff, otherwise in the DHCP option space. */
! 1222: if (code >= cfg_options -> site_code_min)
! 1223: u = universes [cfg_options -> site_universe];
! 1224: else
! 1225: u = &dhcp_universe;
! 1226:
! 1227: oc = lookup_option (u, cfg_options, code);
! 1228:
! 1229: if (oc && oc->option)
! 1230: option_reference(&option, oc->option, MDL);
! 1231: else
! 1232: option_code_hash_lookup(&option, u->code_hash, &code, 0, MDL);
! 1233:
! 1234: /* If it's a straight encapsulation, and the user supplied a
! 1235: * value for the entire option, use that. Otherwise, search
! 1236: * the encapsulated space.
! 1237: *
! 1238: * If it's a limited encapsulation with preceding data, and the
! 1239: * user supplied values for the preceding bytes, search the
! 1240: * encapsulated space.
! 1241: */
! 1242: if ((option != NULL) &&
! 1243: (((oc == NULL) && (option->format[0] == 'E')) ||
! 1244: ((oc != NULL) && (option->format[0] == 'e')))) {
! 1245: static char *s, *t;
! 1246: struct option_cache *tmp;
! 1247: struct data_string name;
! 1248:
! 1249: s = strchr (option->format, 'E');
! 1250: if (s)
! 1251: t = strchr (++s, '.');
! 1252: if (s && t) {
! 1253: memset (&name, 0, sizeof name);
! 1254:
! 1255: /* A zero-length universe name means the vendor
! 1256: option space, if one is defined. */
! 1257: if (t == s) {
! 1258: if (vendor_cfg_option) {
! 1259: tmp = lookup_option (vendor_cfg_option -> universe,
! 1260: cfg_options,
! 1261: vendor_cfg_option -> code);
! 1262: if (tmp)
! 1263: evaluate_option_cache (&name, packet, lease,
! 1264: client_state,
! 1265: in_options,
! 1266: cfg_options,
! 1267: scope, tmp, MDL);
! 1268: } else if (vuname) {
! 1269: name.data = (unsigned char *)s;
! 1270: name.len = strlen (s);
! 1271: }
! 1272: } else {
! 1273: name.data = (unsigned char *)s;
! 1274: name.len = t - s;
! 1275: }
! 1276:
! 1277: /* If we found a universe, and there are options configured
! 1278: for that universe, try to encapsulate it. */
! 1279: if (name.len) {
! 1280: have_encapsulation =
! 1281: (option_space_encapsulate
! 1282: (&encapsulation, packet, lease, client_state,
! 1283: in_options, cfg_options, scope, &name));
! 1284: data_string_forget (&name, MDL);
! 1285: }
! 1286: }
! 1287: }
! 1288:
! 1289: /* In order to avoid memory leaks, we have to get to here
! 1290: with any option cache that we allocated in tmp not being
! 1291: referenced by tmp, and whatever option cache is referenced
! 1292: by oc being an actual reference. lookup_option doesn't
! 1293: generate a reference (this needs to be fixed), so the
! 1294: preceding goop ensures that if we *didn't* generate a new
! 1295: option cache, oc still winds up holding an actual reference. */
! 1296:
! 1297: /* If no data is available for this option, skip it. */
! 1298: if (!oc && !have_encapsulation) {
! 1299: continue;
! 1300: }
! 1301:
! 1302: /* Find the value of the option... */
! 1303: od.len = 0;
! 1304: if (oc) {
! 1305: evaluate_option_cache (&od, packet,
! 1306: lease, client_state, in_options,
! 1307: cfg_options, scope, oc, MDL);
! 1308:
! 1309: /* If we have encapsulation for this option, and an oc
! 1310: * lookup succeeded, but the evaluation failed, it is
! 1311: * either because this is a complex atom (atoms before
! 1312: * E on format list) and the top half of the option is
! 1313: * not configured, or this is a simple encapsulated
! 1314: * space and the evaluator is giving us a NULL. Prefer
! 1315: * the evaluator's opinion over the subspace.
! 1316: */
! 1317: if (!od.len) {
! 1318: data_string_forget (&encapsulation, MDL);
! 1319: data_string_forget (&od, MDL);
! 1320: continue;
! 1321: }
! 1322: }
! 1323:
! 1324: /* We should now have a constant length for the option. */
! 1325: length = od.len;
! 1326: if (have_encapsulation) {
! 1327: length += encapsulation.len;
! 1328:
! 1329: /* od.len can be nonzero if we got here without an
! 1330: * oc (cache lookup failed), but did have an encapsulated
! 1331: * simple encapsulation space.
! 1332: */
! 1333: if (!od.len) {
! 1334: data_string_copy (&od, &encapsulation, MDL);
! 1335: data_string_forget (&encapsulation, MDL);
! 1336: } else {
! 1337: struct buffer *bp = (struct buffer *)0;
! 1338: if (!buffer_allocate (&bp, length, MDL)) {
! 1339: option_cache_dereference (&oc, MDL);
! 1340: data_string_forget (&od, MDL);
! 1341: data_string_forget (&encapsulation, MDL);
! 1342: continue;
! 1343: }
! 1344: memcpy (&bp -> data [0], od.data, od.len);
! 1345: memcpy (&bp -> data [od.len], encapsulation.data,
! 1346: encapsulation.len);
! 1347: data_string_forget (&od, MDL);
! 1348: data_string_forget (&encapsulation, MDL);
! 1349: od.data = &bp -> data [0];
! 1350: buffer_reference (&od.buffer, bp, MDL);
! 1351: buffer_dereference (&bp, MDL);
! 1352: od.len = length;
! 1353: od.terminated = 0;
! 1354: }
! 1355: }
! 1356:
! 1357: /* Do we add a NUL? */
! 1358: if (terminate && option && format_has_text(option->format)) {
! 1359: length++;
! 1360: tto = 1;
! 1361: } else {
! 1362: tto = 0;
! 1363: }
! 1364:
! 1365: /* Try to store the option. */
! 1366:
! 1367: /* If the option's length is more than 255, we must store it
! 1368: in multiple hunks. Store 255-byte hunks first. However,
! 1369: in any case, if the option data will cross a buffer
! 1370: boundary, split it across that boundary. */
! 1371:
! 1372: if (length > 255)
! 1373: splitup = 1;
! 1374: else
! 1375: splitup = 0;
! 1376:
! 1377: ix = 0;
! 1378: optstart = bufix;
! 1379: soptstart = six;
! 1380: toptstart = tix;
! 1381: while (length) {
! 1382: unsigned incr = length;
! 1383: int *pix;
! 1384: unsigned char *base;
! 1385:
! 1386: /* Try to fit it in the options buffer. */
! 1387: if (!splitup &&
! 1388: ((!six && !tix && (i == priority_len - 1) &&
! 1389: (bufix + 2 + length < bufend)) ||
! 1390: (bufix + 5 + length < bufend))) {
! 1391: base = buffer;
! 1392: pix = &bufix;
! 1393: /* Try to fit it in the second buffer. */
! 1394: } else if (!splitup && first_cutoff &&
! 1395: (first_cutoff + six + 3 + length < sbufend)) {
! 1396: base = &buffer[first_cutoff];
! 1397: pix = &six;
! 1398: /* Try to fit it in the third buffer. */
! 1399: } else if (!splitup && second_cutoff &&
! 1400: (second_cutoff + tix + 3 + length < buflen)) {
! 1401: base = &buffer[second_cutoff];
! 1402: pix = &tix;
! 1403: /* Split the option up into the remaining space. */
! 1404: } else {
! 1405: splitup = 1;
! 1406:
! 1407: /* Use any remaining options space. */
! 1408: if (bufix + 6 < bufend) {
! 1409: incr = bufend - bufix - 5;
! 1410: base = buffer;
! 1411: pix = &bufix;
! 1412: /* Use any remaining first_cutoff space. */
! 1413: } else if (first_cutoff &&
! 1414: (first_cutoff + six + 4 < sbufend)) {
! 1415: incr = sbufend - (first_cutoff + six) - 3;
! 1416: base = &buffer[first_cutoff];
! 1417: pix = &six;
! 1418: /* Use any remaining second_cutoff space. */
! 1419: } else if (second_cutoff &&
! 1420: (second_cutoff + tix + 4 < buflen)) {
! 1421: incr = buflen - (second_cutoff + tix) - 3;
! 1422: base = &buffer[second_cutoff];
! 1423: pix = &tix;
! 1424: /* Give up, roll back this option. */
! 1425: } else {
! 1426: bufix = optstart;
! 1427: six = soptstart;
! 1428: tix = toptstart;
! 1429: break;
! 1430: }
! 1431: }
! 1432:
! 1433: if (incr > length)
! 1434: incr = length;
! 1435: if (incr > 255)
! 1436: incr = 255;
! 1437:
! 1438: /* Everything looks good - copy it in! */
! 1439: base [*pix] = code;
! 1440: base [*pix + 1] = (unsigned char)incr;
! 1441: if (tto && incr == length) {
! 1442: if (incr > 1)
! 1443: memcpy (base + *pix + 2,
! 1444: od.data + ix, (unsigned)(incr - 1));
! 1445: base [*pix + 2 + incr - 1] = 0;
! 1446: } else {
! 1447: memcpy (base + *pix + 2,
! 1448: od.data + ix, (unsigned)incr);
! 1449: }
! 1450: length -= incr;
! 1451: ix += incr;
! 1452: *pix += 2 + incr;
! 1453: }
! 1454: data_string_forget (&od, MDL);
! 1455: }
! 1456:
! 1457: if (option != NULL)
! 1458: option_dereference(&option, MDL);
! 1459:
! 1460: /* If we can overload, and we have, then PAD and END those spaces. */
! 1461: if (first_cutoff && six) {
! 1462: if ((first_cutoff + six + 1) < sbufend)
! 1463: memset (&buffer[first_cutoff + six + 1], DHO_PAD,
! 1464: sbufend - (first_cutoff + six + 1));
! 1465: else if (first_cutoff + six >= sbufend)
! 1466: log_fatal("Second buffer overflow in overloaded options.");
! 1467:
! 1468: buffer[first_cutoff + six] = DHO_END;
! 1469: if (ocount != NULL)
! 1470: *ocount |= 1; /* So that caller knows there's data there. */
! 1471: }
! 1472:
! 1473: if (second_cutoff && tix) {
! 1474: if (second_cutoff + tix + 1 < buflen) {
! 1475: memset (&buffer[second_cutoff + tix + 1], DHO_PAD,
! 1476: buflen - (second_cutoff + tix + 1));
! 1477: } else if (second_cutoff + tix >= buflen)
! 1478: log_fatal("Third buffer overflow in overloaded options.");
! 1479:
! 1480: buffer[second_cutoff + tix] = DHO_END;
! 1481: if (ocount != NULL)
! 1482: *ocount |= 2; /* So that caller knows there's data there. */
! 1483: }
! 1484:
! 1485: if ((six || tix) && (bufix + 3 > bufend))
! 1486: log_fatal("Not enough space for option overload option.");
! 1487:
! 1488: return bufix;
! 1489: }
! 1490:
! 1491: /* Return true if the format string has a variable length text option
! 1492: * ("t"), return false otherwise.
! 1493: */
! 1494:
! 1495: int
! 1496: format_has_text(format)
! 1497: const char *format;
! 1498: {
! 1499: const char *p;
! 1500:
! 1501: p = format;
! 1502: while (*p != '\0') {
! 1503: switch (*p++) {
! 1504: case 'd':
! 1505: case 't':
! 1506: return 1;
! 1507:
! 1508: /* These symbols are arbitrary, not fixed or
! 1509: * determinable length...text options with them is
! 1510: * invalid (whatever the case, they are never NULL
! 1511: * terminated).
! 1512: */
! 1513: case 'A':
! 1514: case 'a':
! 1515: case 'X':
! 1516: case 'x':
! 1517: case 'D':
! 1518: return 0;
! 1519:
! 1520: case 'c':
! 1521: /* 'c' only follows 'D' atoms, and indicates that
! 1522: * compression may be used. If there was a 'D'
! 1523: * atom already, we would have returned. So this
! 1524: * is an error, but continue looking for 't' anyway.
! 1525: */
! 1526: log_error("format_has_text(%s): 'c' atoms are illegal "
! 1527: "except after 'D' atoms.", format);
! 1528: break;
! 1529:
! 1530: /* 'E' is variable length, but not arbitrary...you
! 1531: * can find its length if you can find an END option.
! 1532: * N is (n)-byte in length but trails a name of a
! 1533: * space defining the enumeration values. So treat
! 1534: * both the same - valid, fixed-length fields.
! 1535: */
! 1536: case 'E':
! 1537: case 'N':
! 1538: /* Consume the space name. */
! 1539: while ((*p != '\0') && (*p++ != '.'))
! 1540: ;
! 1541: break;
! 1542:
! 1543: default:
! 1544: break;
! 1545: }
! 1546: }
! 1547:
! 1548: return 0;
! 1549: }
! 1550:
! 1551: /* Determine the minimum length of a DHCP option prior to any variable
! 1552: * or inconsistent length formats, according to its configured format
! 1553: * variable (and possibly from supplied option cache contents for variable
! 1554: * length format symbols).
! 1555: */
! 1556:
! 1557: int
! 1558: format_min_length(format, oc)
! 1559: const char *format;
! 1560: struct option_cache *oc;
! 1561: {
! 1562: const char *p, *name;
! 1563: int min_len = 0;
! 1564: int last_size = 0;
! 1565: struct enumeration *espace;
! 1566:
! 1567: p = format;
! 1568: while (*p != '\0') {
! 1569: switch (*p++) {
! 1570: case '6': /* IPv6 Address */
! 1571: min_len += 16;
! 1572: last_size = 16;
! 1573: break;
! 1574:
! 1575: case 'I': /* IPv4 Address */
! 1576: case 'l': /* int32_t */
! 1577: case 'L': /* uint32_t */
! 1578: case 'T': /* Lease Time, uint32_t equivalent */
! 1579: min_len += 4;
! 1580: last_size = 4;
! 1581: break;
! 1582:
! 1583: case 's': /* int16_t */
! 1584: case 'S': /* uint16_t */
! 1585: min_len += 2;
! 1586: last_size = 2;
! 1587: break;
! 1588:
! 1589: case 'N': /* Enumeration value. */
! 1590: /* Consume space name. */
! 1591: name = p;
! 1592: p = strchr(p, '.');
! 1593: if (p == NULL)
! 1594: log_fatal("Corrupt format: %s", format);
! 1595:
! 1596: espace = find_enumeration(name, p - name);
! 1597: if (espace == NULL) {
! 1598: log_error("Unknown enumeration: %s", format);
! 1599: /* Max is safest value to return. */
! 1600: return INT_MAX;
! 1601: }
! 1602:
! 1603: min_len += espace->width;
! 1604: last_size = espace->width;
! 1605: p++;
! 1606:
! 1607: break;
! 1608:
! 1609: case 'b': /* int8_t */
! 1610: case 'B': /* uint8_t */
! 1611: case 'F': /* Flag that is always true. */
! 1612: case 'f': /* Flag */
! 1613: min_len++;
! 1614: last_size = 1;
! 1615: break;
! 1616:
! 1617: case 'o': /* Last argument is optional. */
! 1618: min_len -= last_size;
! 1619:
! 1620: /* XXX: It MAY be possible to sense the end of an
! 1621: * encapsulated space, but right now this is too
! 1622: * hard to support. Return a safe value.
! 1623: */
! 1624: case 'e': /* Encapsulation hint (there is an 'E' later). */
! 1625: case 'E': /* Encapsulated options. */
! 1626: return min_len;
! 1627:
! 1628: case 'd': /* "Domain name" */
! 1629: case 'D': /* "rfc1035 formatted names" */
! 1630: case 't': /* "ASCII Text" */
! 1631: case 'X': /* "ASCII or Hex Conditional */
! 1632: case 'x': /* "Hex" */
! 1633: case 'A': /* Array of all that precedes. */
! 1634: case 'a': /* Array of preceding symbol. */
! 1635: case 'Z': /* nothing. */
! 1636: return min_len;
! 1637:
! 1638: case 'c': /* Compress flag for D atom. */
! 1639: log_error("format_min_length(%s): 'c' atom is illegal "
! 1640: "except after 'D' atom.", format);
! 1641: return INT_MAX;
! 1642:
! 1643: default:
! 1644: /* No safe value is known. */
! 1645: log_error("format_min_length(%s): No safe value "
! 1646: "for unknown format symbols.", format);
! 1647: return INT_MAX;
! 1648: }
! 1649: }
! 1650:
! 1651: return min_len;
! 1652: }
! 1653:
! 1654:
! 1655: /* Format the specified option so that a human can easily read it. */
! 1656:
! 1657: const char *pretty_print_option (option, data, len, emit_commas, emit_quotes)
! 1658: struct option *option;
! 1659: const unsigned char *data;
! 1660: unsigned len;
! 1661: int emit_commas;
! 1662: int emit_quotes;
! 1663: {
! 1664: static char optbuf [32768]; /* XXX */
! 1665: static char *endbuf = &optbuf[sizeof(optbuf)];
! 1666: int hunksize = 0;
! 1667: int opthunk = 0;
! 1668: int hunkinc = 0;
! 1669: int numhunk = -1;
! 1670: int numelem = 0;
! 1671: int count;
! 1672: int i, j, k, l;
! 1673: char fmtbuf[32] = "";
! 1674: struct iaddr iaddr;
! 1675: struct enumeration *enumbuf[32]; /* MUST be same as fmtbuf */
! 1676: char *op = optbuf;
! 1677: const unsigned char *dp = data;
! 1678: char comma;
! 1679: unsigned long tval;
! 1680:
! 1681: if (emit_commas)
! 1682: comma = ',';
! 1683: else
! 1684: comma = ' ';
! 1685:
! 1686: memset (enumbuf, 0, sizeof enumbuf);
! 1687:
! 1688: /* Figure out the size of the data. */
! 1689: for (l = i = 0; option -> format [i]; i++, l++) {
! 1690: if (l >= sizeof(fmtbuf) - 1)
! 1691: log_fatal("Bounds failure on internal buffer at "
! 1692: "%s:%d", MDL);
! 1693:
! 1694: if (!numhunk) {
! 1695: log_error ("%s: Extra codes in format string: %s",
! 1696: option -> name,
! 1697: &(option -> format [i]));
! 1698: break;
! 1699: }
! 1700: numelem++;
! 1701: fmtbuf [l] = option -> format [i];
! 1702: switch (option -> format [i]) {
! 1703: case 'a':
! 1704: case 'A':
! 1705: --numelem;
! 1706: fmtbuf [l] = 0;
! 1707: numhunk = 0;
! 1708: break;
! 1709: case 'E':
! 1710: /* Skip the universe name. */
! 1711: while (option -> format [i] &&
! 1712: option -> format [i] != '.')
! 1713: i++;
! 1714: /* Fall Through! */
! 1715: case 'X':
! 1716: for (k = 0; k < len; k++) {
! 1717: if (!isascii (data [k]) ||
! 1718: !isprint (data [k]))
! 1719: break;
! 1720: }
! 1721: /* If we found no bogus characters, or the bogus
! 1722: character we found is a trailing NUL, it's
! 1723: okay to print this option as text. */
! 1724: if (k == len || (k + 1 == len && data [k] == 0)) {
! 1725: fmtbuf [l] = 't';
! 1726: numhunk = -2;
! 1727: } else {
! 1728: fmtbuf [l] = 'x';
! 1729: hunksize++;
! 1730: comma = ':';
! 1731: numhunk = 0;
! 1732: }
! 1733: fmtbuf [l + 1] = 0;
! 1734: break;
! 1735: case 'c':
! 1736: /* The 'c' atom is a 'D' modifier only. */
! 1737: log_error("'c' atom not following D atom in format "
! 1738: "string: %s", option->format);
! 1739: break;
! 1740: case 'D':
! 1741: /*
! 1742: * Skip the 'c' atom, if present. It does not affect
! 1743: * how we convert wire->text format (if compression is
! 1744: * present either way, we still process it).
! 1745: */
! 1746: if (option->format[i+1] == 'c')
! 1747: i++;
! 1748: fmtbuf[l + 1] = 0;
! 1749: numhunk = -2;
! 1750: break;
! 1751: case 'd':
! 1752: fmtbuf[l] = 't';
! 1753: /* Fall Through ! */
! 1754: case 't':
! 1755: fmtbuf[l + 1] = 0;
! 1756: numhunk = -2;
! 1757: break;
! 1758: case 'N':
! 1759: k = i;
! 1760: while (option -> format [i] &&
! 1761: option -> format [i] != '.')
! 1762: i++;
! 1763: enumbuf [l] =
! 1764: find_enumeration (&option -> format [k] + 1,
! 1765: i - k - 1);
! 1766: if (enumbuf[l] == NULL) {
! 1767: hunksize += 1;
! 1768: hunkinc = 1;
! 1769: } else {
! 1770: hunksize += enumbuf[l]->width;
! 1771: hunkinc = enumbuf[l]->width;
! 1772: }
! 1773: break;
! 1774: case '6':
! 1775: hunksize += 16;
! 1776: hunkinc = 16;
! 1777: break;
! 1778: case 'I':
! 1779: case 'l':
! 1780: case 'L':
! 1781: case 'T':
! 1782: hunksize += 4;
! 1783: hunkinc = 4;
! 1784: break;
! 1785: case 's':
! 1786: case 'S':
! 1787: hunksize += 2;
! 1788: hunkinc = 2;
! 1789: break;
! 1790: case 'b':
! 1791: case 'B':
! 1792: case 'f':
! 1793: case 'F':
! 1794: hunksize++;
! 1795: hunkinc = 1;
! 1796: break;
! 1797: case 'e':
! 1798: case 'Z':
! 1799: break;
! 1800: case 'o':
! 1801: opthunk += hunkinc;
! 1802: break;
! 1803: default:
! 1804: log_error ("%s: garbage in format string: %s",
! 1805: option -> name,
! 1806: &(option -> format [i]));
! 1807: break;
! 1808: }
! 1809: }
! 1810:
! 1811: /* Check for too few bytes... */
! 1812: if (hunksize - opthunk > len) {
! 1813: log_error ("%s: expecting at least %d bytes; got %d",
! 1814: option -> name,
! 1815: hunksize, len);
! 1816: return "<error>";
! 1817: }
! 1818: /* Check for too many bytes... */
! 1819: if (numhunk == -1 && hunksize < len)
! 1820: log_error ("%s: %d extra bytes",
! 1821: option -> name,
! 1822: len - hunksize);
! 1823:
! 1824: /* If this is an array, compute its size. */
! 1825: if (!numhunk)
! 1826: numhunk = len / hunksize;
! 1827: /* See if we got an exact number of hunks. */
! 1828: if (numhunk > 0 && numhunk * hunksize < len)
! 1829: log_error ("%s: %d extra bytes at end of array\n",
! 1830: option -> name,
! 1831: len - numhunk * hunksize);
! 1832:
! 1833: /* A one-hunk array prints the same as a single hunk. */
! 1834: if (numhunk < 0)
! 1835: numhunk = 1;
! 1836:
! 1837: /* Cycle through the array (or hunk) printing the data. */
! 1838: for (i = 0; i < numhunk; i++) {
! 1839: for (j = 0; j < numelem; j++) {
! 1840: switch (fmtbuf [j]) {
! 1841: case 't':
! 1842: /* endbuf-1 leaves room for NULL. */
! 1843: k = pretty_text(&op, endbuf - 1, &dp,
! 1844: data + len, emit_quotes);
! 1845: if (k == -1) {
! 1846: log_error("Error printing text.");
! 1847: break;
! 1848: }
! 1849: *op = 0;
! 1850: break;
! 1851: case 'D': /* RFC1035 format name list */
! 1852: for( ; dp < (data + len) ; dp += k) {
! 1853: unsigned char nbuff[NS_MAXCDNAME];
! 1854: const unsigned char *nbp, *nend;
! 1855:
! 1856: nend = &nbuff[sizeof(nbuff)];
! 1857:
! 1858: /* If this is for ISC DHCP consumption
! 1859: * (emit_quotes), lay it out as a list
! 1860: * of STRING tokens. Otherwise, it is
! 1861: * a space-separated list of DNS-
! 1862: * escaped names as /etc/resolv.conf
! 1863: * might digest.
! 1864: */
! 1865: if (dp != data) {
! 1866: if (op + 2 > endbuf)
! 1867: break;
! 1868:
! 1869: if (emit_quotes)
! 1870: *op++ = ',';
! 1871: *op++ = ' ';
! 1872: }
! 1873:
! 1874: /* XXX: if fmtbuf[j+1] != 'c', we
! 1875: * should warn if the data was
! 1876: * compressed anyway.
! 1877: */
! 1878: k = MRns_name_unpack(data,
! 1879: data + len,
! 1880: dp, nbuff,
! 1881: sizeof(nbuff));
! 1882:
! 1883: if (k == -1) {
! 1884: log_error("Invalid domain "
! 1885: "list.");
! 1886: break;
! 1887: }
! 1888:
! 1889: /* If emit_quotes, then use ISC DHCP
! 1890: * escapes. Otherwise, rely only on
! 1891: * ns_name_ntop().
! 1892: */
! 1893: if (emit_quotes) {
! 1894: nbp = nbuff;
! 1895: pretty_domain(&op, endbuf-1,
! 1896: &nbp, nend);
! 1897: } else {
! 1898: /* ns_name_ntop() includes
! 1899: * a trailing NUL in its
! 1900: * count.
! 1901: */
! 1902: count = MRns_name_ntop(
! 1903: nbuff, op,
! 1904: (endbuf-op)-1);
! 1905:
! 1906: if (count <= 0) {
! 1907: log_error("Invalid "
! 1908: "domain name.");
! 1909: break;
! 1910: }
! 1911:
! 1912: /* Consume all but the trailing
! 1913: * NUL.
! 1914: */
! 1915: op += count - 1;
! 1916:
! 1917: /* Replace the trailing NUL
! 1918: * with the implicit root
! 1919: * (in the unlikely event the
! 1920: * domain name /is/ the root).
! 1921: */
! 1922: *op++ = '.';
! 1923: }
! 1924: }
! 1925: *op = '\0';
! 1926: break;
! 1927: /* pretty-printing an array of enums is
! 1928: going to get ugly. */
! 1929: case 'N':
! 1930: if (!enumbuf [j]) {
! 1931: tval = *dp++;
! 1932: goto enum_as_num;
! 1933: }
! 1934:
! 1935: switch (enumbuf[j]->width) {
! 1936: case 1:
! 1937: tval = getUChar(dp);
! 1938: break;
! 1939:
! 1940: case 2:
! 1941: tval = getUShort(dp);
! 1942: break;
! 1943:
! 1944: case 4:
! 1945: tval = getULong(dp);
! 1946: break;
! 1947:
! 1948: default:
! 1949: log_fatal("Impossible case at %s:%d.",
! 1950: MDL);
! 1951: return "<double impossible condition>";
! 1952: }
! 1953:
! 1954: for (i = 0; ;i++) {
! 1955: if (!enumbuf [j] -> values [i].name)
! 1956: goto enum_as_num;
! 1957: if (enumbuf [j] -> values [i].value ==
! 1958: tval)
! 1959: break;
! 1960: }
! 1961: strcpy (op, enumbuf [j] -> values [i].name);
! 1962: dp += enumbuf[j]->width;
! 1963: break;
! 1964:
! 1965: enum_as_num:
! 1966: sprintf(op, "%lu", tval);
! 1967: break;
! 1968:
! 1969: case 'I':
! 1970: iaddr.len = 4;
! 1971: memcpy(iaddr.iabuf, dp, 4);
! 1972: strcpy(op, piaddr(iaddr));
! 1973: dp += 4;
! 1974: break;
! 1975: case '6':
! 1976: iaddr.len = 16;
! 1977: memcpy(iaddr.iabuf, dp, 16);
! 1978: strcpy(op, piaddr(iaddr));
! 1979: dp += 16;
! 1980: break;
! 1981: case 'l':
! 1982: sprintf (op, "%ld", (long)getLong (dp));
! 1983: dp += 4;
! 1984: break;
! 1985: case 'T':
! 1986: tval = getULong (dp);
! 1987: if (tval == -1)
! 1988: sprintf (op, "%s", "infinite");
! 1989: else
! 1990: sprintf(op, "%lu", tval);
! 1991: break;
! 1992: case 'L':
! 1993: sprintf(op, "%lu",
! 1994: (unsigned long)getULong(dp));
! 1995: dp += 4;
! 1996: break;
! 1997: case 's':
! 1998: sprintf (op, "%d", (int)getShort (dp));
! 1999: dp += 2;
! 2000: break;
! 2001: case 'S':
! 2002: sprintf(op, "%u", (unsigned)getUShort(dp));
! 2003: dp += 2;
! 2004: break;
! 2005: case 'b':
! 2006: sprintf (op, "%d", *(const char *)dp++);
! 2007: break;
! 2008: case 'B':
! 2009: sprintf (op, "%d", *dp++);
! 2010: break;
! 2011: case 'X':
! 2012: case 'x':
! 2013: sprintf (op, "%x", *dp++);
! 2014: break;
! 2015: case 'f':
! 2016: strcpy (op, *dp++ ? "true" : "false");
! 2017: break;
! 2018: case 'F':
! 2019: strcpy (op, "true");
! 2020: break;
! 2021: case 'e':
! 2022: case 'Z':
! 2023: *op = '\0';
! 2024: break;
! 2025: default:
! 2026: log_error ("Unexpected format code %c",
! 2027: fmtbuf [j]);
! 2028: }
! 2029: op += strlen (op);
! 2030: if (dp == data + len)
! 2031: break;
! 2032: if (j + 1 < numelem && comma != ':')
! 2033: *op++ = ' ';
! 2034: }
! 2035: if (i + 1 < numhunk) {
! 2036: *op++ = comma;
! 2037: }
! 2038: if (dp == data + len)
! 2039: break;
! 2040: }
! 2041: return optbuf;
! 2042: }
! 2043:
! 2044: int get_option (result, universe, packet, lease, client_state,
! 2045: in_options, cfg_options, options, scope, code, file, line)
! 2046: struct data_string *result;
! 2047: struct universe *universe;
! 2048: struct packet *packet;
! 2049: struct lease *lease;
! 2050: struct client_state *client_state;
! 2051: struct option_state *in_options;
! 2052: struct option_state *cfg_options;
! 2053: struct option_state *options;
! 2054: struct binding_scope **scope;
! 2055: unsigned code;
! 2056: const char *file;
! 2057: int line;
! 2058: {
! 2059: struct option_cache *oc;
! 2060:
! 2061: if (!universe -> lookup_func)
! 2062: return 0;
! 2063: oc = ((*universe -> lookup_func) (universe, options, code));
! 2064: if (!oc)
! 2065: return 0;
! 2066: if (!evaluate_option_cache (result, packet, lease, client_state,
! 2067: in_options, cfg_options, scope, oc,
! 2068: file, line))
! 2069: return 0;
! 2070: return 1;
! 2071: }
! 2072:
! 2073: void set_option (universe, options, option, op)
! 2074: struct universe *universe;
! 2075: struct option_state *options;
! 2076: struct option_cache *option;
! 2077: enum statement_op op;
! 2078: {
! 2079: struct option_cache *oc, *noc;
! 2080:
! 2081: switch (op) {
! 2082: case if_statement:
! 2083: case add_statement:
! 2084: case eval_statement:
! 2085: case break_statement:
! 2086: default:
! 2087: log_error ("bogus statement type in set_option.");
! 2088: break;
! 2089:
! 2090: case default_option_statement:
! 2091: oc = lookup_option (universe, options,
! 2092: option -> option -> code);
! 2093: if (oc)
! 2094: break;
! 2095: save_option (universe, options, option);
! 2096: break;
! 2097:
! 2098: case supersede_option_statement:
! 2099: case send_option_statement:
! 2100: /* Install the option, replacing any existing version. */
! 2101: save_option (universe, options, option);
! 2102: break;
! 2103:
! 2104: case append_option_statement:
! 2105: case prepend_option_statement:
! 2106: oc = lookup_option (universe, options,
! 2107: option -> option -> code);
! 2108: if (!oc) {
! 2109: save_option (universe, options, option);
! 2110: break;
! 2111: }
! 2112: /* If it's not an expression, make it into one. */
! 2113: if (!oc -> expression && oc -> data.len) {
! 2114: if (!expression_allocate (&oc -> expression, MDL)) {
! 2115: log_error ("Can't allocate const expression.");
! 2116: break;
! 2117: }
! 2118: oc -> expression -> op = expr_const_data;
! 2119: data_string_copy
! 2120: (&oc -> expression -> data.const_data,
! 2121: &oc -> data, MDL);
! 2122: data_string_forget (&oc -> data, MDL);
! 2123: }
! 2124: noc = (struct option_cache *)0;
! 2125: if (!option_cache_allocate (&noc, MDL))
! 2126: break;
! 2127: if (op == append_option_statement) {
! 2128: if (!make_concat (&noc -> expression,
! 2129: oc -> expression,
! 2130: option -> expression)) {
! 2131: option_cache_dereference (&noc, MDL);
! 2132: break;
! 2133: }
! 2134: } else {
! 2135: if (!make_concat (&noc -> expression,
! 2136: option -> expression,
! 2137: oc -> expression)) {
! 2138: option_cache_dereference (&noc, MDL);
! 2139: break;
! 2140: }
! 2141: }
! 2142: option_reference(&(noc->option), oc->option, MDL);
! 2143: save_option (universe, options, noc);
! 2144: option_cache_dereference (&noc, MDL);
! 2145: break;
! 2146: }
! 2147: }
! 2148:
! 2149: struct option_cache *lookup_option (universe, options, code)
! 2150: struct universe *universe;
! 2151: struct option_state *options;
! 2152: unsigned code;
! 2153: {
! 2154: if (!options)
! 2155: return (struct option_cache *)0;
! 2156: if (universe -> lookup_func)
! 2157: return (*universe -> lookup_func) (universe, options, code);
! 2158: else
! 2159: log_error ("can't look up options in %s space.",
! 2160: universe -> name);
! 2161: return (struct option_cache *)0;
! 2162: }
! 2163:
! 2164: struct option_cache *lookup_hashed_option (universe, options, code)
! 2165: struct universe *universe;
! 2166: struct option_state *options;
! 2167: unsigned code;
! 2168: {
! 2169: int hashix;
! 2170: pair bptr;
! 2171: pair *hash;
! 2172:
! 2173: /* Make sure there's a hash table. */
! 2174: if (universe -> index >= options -> universe_count ||
! 2175: !(options -> universes [universe -> index]))
! 2176: return (struct option_cache *)0;
! 2177:
! 2178: hash = options -> universes [universe -> index];
! 2179:
! 2180: hashix = compute_option_hash (code);
! 2181: for (bptr = hash [hashix]; bptr; bptr = bptr -> cdr) {
! 2182: if (((struct option_cache *)(bptr -> car)) -> option -> code ==
! 2183: code)
! 2184: return (struct option_cache *)(bptr -> car);
! 2185: }
! 2186: return (struct option_cache *)0;
! 2187: }
! 2188:
! 2189: /* Save a specified buffer into an option cache. */
! 2190: int
! 2191: save_option_buffer(struct universe *universe, struct option_state *options,
! 2192: struct buffer *bp, unsigned char *buffer, unsigned length,
! 2193: unsigned code, int terminatep)
! 2194: {
! 2195: struct option_cache *op = NULL;
! 2196: int status = 1;
! 2197:
! 2198: status = prepare_option_buffer(universe, bp, buffer, length, code,
! 2199: terminatep, &op);
! 2200:
! 2201: if (status == 0)
! 2202: goto cleanup;
! 2203:
! 2204: save_option(universe, options, op);
! 2205:
! 2206: cleanup:
! 2207: if (op != NULL)
! 2208: option_cache_dereference(&op, MDL);
! 2209:
! 2210: return status;
! 2211: }
! 2212:
! 2213: /* Append a specified buffer onto the tail of an option cache. */
! 2214: int
! 2215: append_option_buffer(struct universe *universe, struct option_state *options,
! 2216: struct buffer *bp, unsigned char *buffer, unsigned length,
! 2217: unsigned code, int terminatep)
! 2218: {
! 2219: struct option_cache *op = NULL;
! 2220: int status = 1;
! 2221:
! 2222: status = prepare_option_buffer(universe, bp, buffer, length, code,
! 2223: terminatep, &op);
! 2224:
! 2225: if (status == 0)
! 2226: goto cleanup;
! 2227:
! 2228: also_save_option(universe, options, op);
! 2229:
! 2230: cleanup:
! 2231: if (op != NULL)
! 2232: option_cache_dereference(&op, MDL);
! 2233:
! 2234: return status;
! 2235: }
! 2236:
! 2237: /* Create/copy a buffer into a new option cache. */
! 2238: static int
! 2239: prepare_option_buffer(struct universe *universe, struct buffer *bp,
! 2240: unsigned char *buffer, unsigned length, unsigned code,
! 2241: int terminatep, struct option_cache **opp)
! 2242: {
! 2243: struct buffer *lbp = NULL;
! 2244: struct option *option = NULL;
! 2245: struct option_cache *op;
! 2246: int status = 1;
! 2247:
! 2248: /* Code sizes of 8, 16, and 32 bits are allowed. */
! 2249: switch(universe->tag_size) {
! 2250: case 1:
! 2251: if (code > 0xff)
! 2252: return 0;
! 2253: break;
! 2254: case 2:
! 2255: if (code > 0xffff)
! 2256: return 0;
! 2257: break;
! 2258: case 4:
! 2259: if (code > 0xffffffff)
! 2260: return 0;
! 2261: break;
! 2262:
! 2263: default:
! 2264: log_fatal("Inconsistent universe tag size at %s:%d.", MDL);
! 2265: }
! 2266:
! 2267: option_code_hash_lookup(&option, universe->code_hash, &code, 0, MDL);
! 2268:
! 2269: /* If we created an option structure for each option a client
! 2270: * supplied, it's possible we may create > 2^32 option structures.
! 2271: * That's not feasible. So by failing to enter these option
! 2272: * structures into the code and name hash tables, references will
! 2273: * never be more than 1 - when the option cache is destroyed, this
! 2274: * will be cleaned up.
! 2275: */
! 2276: if (!option) {
! 2277: char nbuf[sizeof("unknown-4294967295")];
! 2278:
! 2279: sprintf(nbuf, "unknown-%u", code);
! 2280:
! 2281: option = new_option(nbuf, MDL);
! 2282:
! 2283: if (!option)
! 2284: return 0;
! 2285:
! 2286: option->format = default_option_format;
! 2287: option->universe = universe;
! 2288: option->code = code;
! 2289:
! 2290: /* new_option() doesn't set references, pretend. */
! 2291: option->refcnt = 1;
! 2292: }
! 2293:
! 2294: if (!option_cache_allocate (opp, MDL)) {
! 2295: log_error("No memory for option code %s.%s.",
! 2296: universe->name, option->name);
! 2297: status = 0;
! 2298: goto cleanup;
! 2299: }
! 2300:
! 2301: /* Pointer rather than double pointer makes for less parens. */
! 2302: op = *opp;
! 2303:
! 2304: option_reference(&op->option, option, MDL);
! 2305:
! 2306: /* If we weren't passed a buffer in which the data are saved and
! 2307: refcounted, allocate one now. */
! 2308: if (!bp) {
! 2309: if (!buffer_allocate (&lbp, length + terminatep, MDL)) {
! 2310: log_error ("no memory for option buffer.");
! 2311:
! 2312: status = 0;
! 2313: goto cleanup;
! 2314: }
! 2315: memcpy (lbp -> data, buffer, length + terminatep);
! 2316: bp = lbp;
! 2317: buffer = &bp -> data [0]; /* Refer to saved buffer. */
! 2318: }
! 2319:
! 2320: /* Reference buffer copy to option cache. */
! 2321: op -> data.buffer = (struct buffer *)0;
! 2322: buffer_reference (&op -> data.buffer, bp, MDL);
! 2323:
! 2324: /* Point option cache into buffer. */
! 2325: op -> data.data = buffer;
! 2326: op -> data.len = length;
! 2327:
! 2328: if (terminatep) {
! 2329: /* NUL terminate (we can get away with this because we (or
! 2330: the caller!) allocated one more than the buffer size, and
! 2331: because the byte following the end of an option is always
! 2332: the code of the next option, which the caller is getting
! 2333: out of the *original* buffer. */
! 2334: buffer [length] = 0;
! 2335: op -> data.terminated = 1;
! 2336: } else
! 2337: op -> data.terminated = 0;
! 2338:
! 2339: /* If this option is ultimately a text option, null determinate to
! 2340: * comply with RFC2132 section 2. Mark a flag so this can be sensed
! 2341: * later to echo NULLs back to clients that supplied them (they
! 2342: * probably expect them).
! 2343: */
! 2344: if (format_has_text(option->format)) {
! 2345: int min_len = format_min_length(option->format, op);
! 2346:
! 2347: while ((op->data.len > min_len) &&
! 2348: (op->data.data[op->data.len-1] == '\0')) {
! 2349: op->data.len--;
! 2350: op->flags |= OPTION_HAD_NULLS;
! 2351: }
! 2352: }
! 2353:
! 2354: /* And let go of our references. */
! 2355: cleanup:
! 2356: option_dereference(&option, MDL);
! 2357:
! 2358: return 1;
! 2359: }
! 2360:
! 2361: static void
! 2362: count_options(struct option_cache *dummy_oc,
! 2363: struct packet *dummy_packet,
! 2364: struct lease *dummy_lease,
! 2365: struct client_state *dummy_client_state,
! 2366: struct option_state *dummy_opt_state,
! 2367: struct option_state *opt_state,
! 2368: struct binding_scope **dummy_binding_scope,
! 2369: struct universe *dummy_universe,
! 2370: void *void_accumulator) {
! 2371: int *accumulator = (int *)void_accumulator;
! 2372:
! 2373: *accumulator += 1;
! 2374: }
! 2375:
! 2376: static void
! 2377: collect_oro(struct option_cache *oc,
! 2378: struct packet *dummy_packet,
! 2379: struct lease *dummy_lease,
! 2380: struct client_state *dummy_client_state,
! 2381: struct option_state *dummy_opt_state,
! 2382: struct option_state *opt_state,
! 2383: struct binding_scope **dummy_binding_scope,
! 2384: struct universe *dummy_universe,
! 2385: void *void_oro) {
! 2386: struct data_string *oro = (struct data_string *)void_oro;
! 2387:
! 2388: putUShort(oro->buffer->data + oro->len, oc->option->code);
! 2389: oro->len += 2;
! 2390: }
! 2391:
! 2392: /* build_server_oro() is presently unusued, but may be used at a future date
! 2393: * with support for Reconfigure messages (as a hint to the client about new
! 2394: * option value contents).
! 2395: */
! 2396: void
! 2397: build_server_oro(struct data_string *server_oro,
! 2398: struct option_state *options,
! 2399: const char *file, int line) {
! 2400: int num_opts;
! 2401: int i;
! 2402: struct option *o;
! 2403:
! 2404: /*
! 2405: * Count the number of options, so we can allocate enough memory.
! 2406: * We want to mention sub-options too, so check all universes.
! 2407: */
! 2408: num_opts = 0;
! 2409: option_space_foreach(NULL, NULL, NULL, NULL, options,
! 2410: NULL, &dhcpv6_universe, (void *)&num_opts,
! 2411: count_options);
! 2412: for (i=0; i < options->universe_count; i++) {
! 2413: if (options->universes[i] != NULL) {
! 2414: o = universes[i]->enc_opt;
! 2415: while (o != NULL) {
! 2416: if (o->universe == &dhcpv6_universe) {
! 2417: num_opts++;
! 2418: break;
! 2419: }
! 2420: o = o->universe->enc_opt;
! 2421: }
! 2422: }
! 2423: }
! 2424:
! 2425: /*
! 2426: * Allocate space.
! 2427: */
! 2428: memset(server_oro, 0, sizeof(*server_oro));
! 2429: if (!buffer_allocate(&server_oro->buffer, num_opts * 2, MDL)) {
! 2430: log_fatal("no memory to build server ORO");
! 2431: }
! 2432: server_oro->data = server_oro->buffer->data;
! 2433:
! 2434: /*
! 2435: * Copy the data in.
! 2436: * We want to mention sub-options too, so check all universes.
! 2437: */
! 2438: server_oro->len = 0; /* gets set in collect_oro */
! 2439: option_space_foreach(NULL, NULL, NULL, NULL, options,
! 2440: NULL, &dhcpv6_universe, (void *)server_oro,
! 2441: collect_oro);
! 2442: for (i=0; i < options->universe_count; i++) {
! 2443: if (options->universes[i] != NULL) {
! 2444: o = universes[i]->enc_opt;
! 2445: while (o != NULL) {
! 2446: if (o->universe == &dhcpv6_universe) {
! 2447: unsigned char *tmp;
! 2448: tmp = server_oro->buffer->data;
! 2449: putUShort(tmp + server_oro->len,
! 2450: o->code);
! 2451: server_oro->len += 2;
! 2452: break;
! 2453: }
! 2454: o = o->universe->enc_opt;
! 2455: }
! 2456: }
! 2457: }
! 2458: }
! 2459:
! 2460: /* Wrapper function to put an option cache into an option state. */
! 2461: void
! 2462: save_option(struct universe *universe, struct option_state *options,
! 2463: struct option_cache *oc)
! 2464: {
! 2465: if (universe->save_func)
! 2466: (*universe->save_func)(universe, options, oc, ISC_FALSE);
! 2467: else
! 2468: log_error("can't store options in %s space.", universe->name);
! 2469: }
! 2470:
! 2471: /* Wrapper function to append an option cache into an option state's list. */
! 2472: void
! 2473: also_save_option(struct universe *universe, struct option_state *options,
! 2474: struct option_cache *oc)
! 2475: {
! 2476: if (universe->save_func)
! 2477: (*universe->save_func)(universe, options, oc, ISC_TRUE);
! 2478: else
! 2479: log_error("can't store options in %s space.", universe->name);
! 2480: }
! 2481:
! 2482: void
! 2483: save_hashed_option(struct universe *universe, struct option_state *options,
! 2484: struct option_cache *oc, isc_boolean_t appendp)
! 2485: {
! 2486: int hashix;
! 2487: pair bptr;
! 2488: pair *hash = options -> universes [universe -> index];
! 2489: struct option_cache **ocloc;
! 2490:
! 2491: if (oc -> refcnt == 0)
! 2492: abort ();
! 2493:
! 2494: /* Compute the hash. */
! 2495: hashix = compute_option_hash (oc -> option -> code);
! 2496:
! 2497: /* If there's no hash table, make one. */
! 2498: if (!hash) {
! 2499: hash = (pair *)dmalloc (OPTION_HASH_SIZE * sizeof *hash, MDL);
! 2500: if (!hash) {
! 2501: log_error ("no memory to store %s.%s",
! 2502: universe -> name, oc -> option -> name);
! 2503: return;
! 2504: }
! 2505: memset (hash, 0, OPTION_HASH_SIZE * sizeof *hash);
! 2506: options -> universes [universe -> index] = (void *)hash;
! 2507: } else {
! 2508: /* Try to find an existing option matching the new one. */
! 2509: for (bptr = hash [hashix]; bptr; bptr = bptr -> cdr) {
! 2510: if (((struct option_cache *)
! 2511: (bptr -> car)) -> option -> code ==
! 2512: oc -> option -> code)
! 2513: break;
! 2514: }
! 2515:
! 2516: /* Deal with collisions on the hash list. */
! 2517: if (bptr) {
! 2518: ocloc = (struct option_cache **)&bptr->car;
! 2519:
! 2520: /*
! 2521: * If appendp is set, append it onto the tail of the
! 2522: * ->next list. If it is not set, rotate it into
! 2523: * position at the head of the list.
! 2524: */
! 2525: if (appendp) {
! 2526: do {
! 2527: ocloc = &(*ocloc)->next;
! 2528: } while (*ocloc != NULL);
! 2529: } else {
! 2530: option_cache_dereference(ocloc, MDL);
! 2531: }
! 2532:
! 2533: option_cache_reference(ocloc, oc, MDL);
! 2534: return;
! 2535: }
! 2536: }
! 2537:
! 2538: /* Otherwise, just put the new one at the head of the list. */
! 2539: bptr = new_pair (MDL);
! 2540: if (!bptr) {
! 2541: log_error ("No memory for option_cache reference.");
! 2542: return;
! 2543: }
! 2544: bptr -> cdr = hash [hashix];
! 2545: bptr -> car = 0;
! 2546: option_cache_reference ((struct option_cache **)&bptr -> car, oc, MDL);
! 2547: hash [hashix] = bptr;
! 2548: }
! 2549:
! 2550: void delete_option (universe, options, code)
! 2551: struct universe *universe;
! 2552: struct option_state *options;
! 2553: int code;
! 2554: {
! 2555: if (universe -> delete_func)
! 2556: (*universe -> delete_func) (universe, options, code);
! 2557: else
! 2558: log_error ("can't delete options from %s space.",
! 2559: universe -> name);
! 2560: }
! 2561:
! 2562: void delete_hashed_option (universe, options, code)
! 2563: struct universe *universe;
! 2564: struct option_state *options;
! 2565: int code;
! 2566: {
! 2567: int hashix;
! 2568: pair bptr, prev = (pair)0;
! 2569: pair *hash = options -> universes [universe -> index];
! 2570:
! 2571: /* There may not be any options in this space. */
! 2572: if (!hash)
! 2573: return;
! 2574:
! 2575: /* Try to find an existing option matching the new one. */
! 2576: hashix = compute_option_hash (code);
! 2577: for (bptr = hash [hashix]; bptr; bptr = bptr -> cdr) {
! 2578: if (((struct option_cache *)(bptr -> car)) -> option -> code
! 2579: == code)
! 2580: break;
! 2581: prev = bptr;
! 2582: }
! 2583: /* If we found one, wipe it out... */
! 2584: if (bptr) {
! 2585: if (prev)
! 2586: prev -> cdr = bptr -> cdr;
! 2587: else
! 2588: hash [hashix] = bptr -> cdr;
! 2589: option_cache_dereference
! 2590: ((struct option_cache **)(&bptr -> car), MDL);
! 2591: free_pair (bptr, MDL);
! 2592: }
! 2593: }
! 2594:
! 2595: extern struct option_cache *free_option_caches; /* XXX */
! 2596:
! 2597: int option_cache_dereference (ptr, file, line)
! 2598: struct option_cache **ptr;
! 2599: const char *file;
! 2600: int line;
! 2601: {
! 2602: if (!ptr || !*ptr) {
! 2603: log_error ("Null pointer in option_cache_dereference: %s(%d)",
! 2604: file, line);
! 2605: #if defined (POINTER_DEBUG)
! 2606: abort ();
! 2607: #else
! 2608: return 0;
! 2609: #endif
! 2610: }
! 2611:
! 2612: (*ptr) -> refcnt--;
! 2613: rc_register (file, line, ptr, *ptr, (*ptr) -> refcnt, 1, RC_MISC);
! 2614: if (!(*ptr) -> refcnt) {
! 2615: if ((*ptr) -> data.buffer)
! 2616: data_string_forget (&(*ptr) -> data, file, line);
! 2617: if ((*ptr)->option)
! 2618: option_dereference(&(*ptr)->option, MDL);
! 2619: if ((*ptr) -> expression)
! 2620: expression_dereference (&(*ptr) -> expression,
! 2621: file, line);
! 2622: if ((*ptr) -> next)
! 2623: option_cache_dereference (&((*ptr) -> next),
! 2624: file, line);
! 2625: /* Put it back on the free list... */
! 2626: (*ptr) -> expression = (struct expression *)free_option_caches;
! 2627: free_option_caches = *ptr;
! 2628: dmalloc_reuse (free_option_caches, (char *)0, 0, 0);
! 2629: }
! 2630: if ((*ptr) -> refcnt < 0) {
! 2631: log_error ("%s(%d): negative refcnt!", file, line);
! 2632: #if defined (DEBUG_RC_HISTORY)
! 2633: dump_rc_history (*ptr);
! 2634: #endif
! 2635: #if defined (POINTER_DEBUG)
! 2636: abort ();
! 2637: #else
! 2638: *ptr = (struct option_cache *)0;
! 2639: return 0;
! 2640: #endif
! 2641: }
! 2642: *ptr = (struct option_cache *)0;
! 2643: return 1;
! 2644:
! 2645: }
! 2646:
! 2647: int hashed_option_state_dereference (universe, state, file, line)
! 2648: struct universe *universe;
! 2649: struct option_state *state;
! 2650: const char *file;
! 2651: int line;
! 2652: {
! 2653: pair *heads;
! 2654: pair cp, next;
! 2655: int i;
! 2656:
! 2657: /* Get the pointer to the array of hash table bucket heads. */
! 2658: heads = (pair *)(state -> universes [universe -> index]);
! 2659: if (!heads)
! 2660: return 0;
! 2661:
! 2662: /* For each non-null head, loop through all the buckets dereferencing
! 2663: the attached option cache structures and freeing the buckets. */
! 2664: for (i = 0; i < OPTION_HASH_SIZE; i++) {
! 2665: for (cp = heads [i]; cp; cp = next) {
! 2666: next = cp -> cdr;
! 2667: option_cache_dereference
! 2668: ((struct option_cache **)&cp -> car,
! 2669: file, line);
! 2670: free_pair (cp, file, line);
! 2671: }
! 2672: }
! 2673:
! 2674: dfree (heads, file, line);
! 2675: state -> universes [universe -> index] = (void *)0;
! 2676: return 1;
! 2677: }
! 2678:
! 2679: /* The 'data_string' primitive doesn't have an appension mechanism.
! 2680: * This function must then append a new option onto an existing buffer
! 2681: * by first duplicating the original buffer and appending the desired
! 2682: * values, followed by coping the new value into place.
! 2683: */
! 2684: int
! 2685: append_option(struct data_string *dst, struct universe *universe,
! 2686: struct option *option, struct data_string *src)
! 2687: {
! 2688: struct data_string tmp;
! 2689:
! 2690: if (src->len == 0 && option->format[0] != 'Z')
! 2691: return 0;
! 2692:
! 2693: memset(&tmp, 0, sizeof(tmp));
! 2694:
! 2695: /* Allocate a buffer to hold existing data, the current option's
! 2696: * tag and length, and the option's content.
! 2697: */
! 2698: if (!buffer_allocate(&tmp.buffer,
! 2699: (dst->len + universe->length_size +
! 2700: universe->tag_size + src->len), MDL)) {
! 2701: /* XXX: This kills all options presently stored in the
! 2702: * destination buffer. This is the way the original code
! 2703: * worked, and assumes an 'all or nothing' approach to
! 2704: * eg encapsulated option spaces. It may or may not be
! 2705: * desirable.
! 2706: */
! 2707: data_string_forget(dst, MDL);
! 2708: return 0;
! 2709: }
! 2710: tmp.data = tmp.buffer->data;
! 2711:
! 2712: /* Copy the existing data off the destination. */
! 2713: if (dst->len != 0)
! 2714: memcpy(tmp.buffer->data, dst->data, dst->len);
! 2715: tmp.len = dst->len;
! 2716:
! 2717: /* Place the new option tag and length. */
! 2718: (*universe->store_tag)(tmp.buffer->data + tmp.len, option->code);
! 2719: tmp.len += universe->tag_size;
! 2720: (*universe->store_length)(tmp.buffer->data + tmp.len, src->len);
! 2721: tmp.len += universe->length_size;
! 2722:
! 2723: /* Copy the option contents onto the end. */
! 2724: memcpy(tmp.buffer->data + tmp.len, src->data, src->len);
! 2725: tmp.len += src->len;
! 2726:
! 2727: /* Play the shell game. */
! 2728: data_string_forget(dst, MDL);
! 2729: data_string_copy(dst, &tmp, MDL);
! 2730: data_string_forget(&tmp, MDL);
! 2731: return 1;
! 2732: }
! 2733:
! 2734: int
! 2735: store_option(struct data_string *result, struct universe *universe,
! 2736: struct packet *packet, struct lease *lease,
! 2737: struct client_state *client_state,
! 2738: struct option_state *in_options, struct option_state *cfg_options,
! 2739: struct binding_scope **scope, struct option_cache *oc)
! 2740: {
! 2741: struct data_string tmp;
! 2742: struct universe *subu=NULL;
! 2743: int status;
! 2744: char *start, *end;
! 2745:
! 2746: memset(&tmp, 0, sizeof(tmp));
! 2747:
! 2748: if (evaluate_option_cache(&tmp, packet, lease, client_state,
! 2749: in_options, cfg_options, scope, oc, MDL)) {
! 2750: /* If the option is an extended 'e'ncapsulation (not a
! 2751: * direct 'E'ncapsulation), append the encapsulated space
! 2752: * onto the currently prepared value.
! 2753: */
! 2754: do {
! 2755: if (oc->option->format &&
! 2756: oc->option->format[0] == 'e') {
! 2757: /* Skip forward to the universe name. */
! 2758: start = strchr(oc->option->format, 'E');
! 2759: if (start == NULL)
! 2760: break;
! 2761:
! 2762: /* Locate the name-terminating '.'. */
! 2763: end = strchr(++start, '.');
! 2764:
! 2765: /* A zero-length name is not allowed in
! 2766: * these kinds of encapsulations.
! 2767: */
! 2768: if (end == NULL || start == end)
! 2769: break;
! 2770:
! 2771: universe_hash_lookup(&subu, universe_hash,
! 2772: start, end - start, MDL);
! 2773:
! 2774: if (subu == NULL) {
! 2775: log_error("store_option: option %d "
! 2776: "refers to unknown "
! 2777: "option space '%.*s'.",
! 2778: oc->option->code,
! 2779: (int)(end - start), start);
! 2780: break;
! 2781: }
! 2782:
! 2783: /* Append encapsulations, if any. We
! 2784: * already have the prepended values, so
! 2785: * we send those even if there are no
! 2786: * encapsulated options (and ->encapsulate()
! 2787: * returns zero).
! 2788: */
! 2789: subu->encapsulate(&tmp, packet, lease,
! 2790: client_state, in_options,
! 2791: cfg_options, scope, subu);
! 2792: subu = NULL;
! 2793: }
! 2794: } while (ISC_FALSE);
! 2795:
! 2796: status = append_option(result, universe, oc->option, &tmp);
! 2797: data_string_forget(&tmp, MDL);
! 2798:
! 2799: return status;
! 2800: }
! 2801:
! 2802: return 0;
! 2803: }
! 2804:
! 2805: int option_space_encapsulate (result, packet, lease, client_state,
! 2806: in_options, cfg_options, scope, name)
! 2807: struct data_string *result;
! 2808: struct packet *packet;
! 2809: struct lease *lease;
! 2810: struct client_state *client_state;
! 2811: struct option_state *in_options;
! 2812: struct option_state *cfg_options;
! 2813: struct binding_scope **scope;
! 2814: struct data_string *name;
! 2815: {
! 2816: struct universe *u = NULL;
! 2817: int status = 0;
! 2818:
! 2819: universe_hash_lookup(&u, universe_hash,
! 2820: (const char *)name->data, name->len, MDL);
! 2821: if (u == NULL) {
! 2822: log_error("option_space_encapsulate: option space '%.*s' does "
! 2823: "not exist, but is configured.",
! 2824: (int)name->len, name->data);
! 2825: return status;
! 2826: }
! 2827:
! 2828: if (u->encapsulate != NULL) {
! 2829: if (u->encapsulate(result, packet, lease, client_state,
! 2830: in_options, cfg_options, scope, u))
! 2831: status = 1;
! 2832: } else
! 2833: log_error("encapsulation requested for '%s' with no support.",
! 2834: name->data);
! 2835:
! 2836: return status;
! 2837: }
! 2838:
! 2839: /* Attempt to store any 'E'ncapsulated options that have not yet been
! 2840: * placed on the option buffer by the above (configuring a value in
! 2841: * the space over-rides any values in the child universe).
! 2842: *
! 2843: * Note that there are far fewer universes than there will ever be
! 2844: * options in any universe. So it is faster to traverse the
! 2845: * configured universes, checking if each is encapsulated in the
! 2846: * current universe, and if so attempting to do so.
! 2847: *
! 2848: * For each configured universe for this configuration option space,
! 2849: * which is encapsulated within the current universe, can not be found
! 2850: * by the lookup function (the universe-specific encapsulation
! 2851: * functions would already have stored such a value), and encapsulates
! 2852: * at least one option, append it.
! 2853: */
! 2854: static int
! 2855: search_subencapsulation(struct data_string *result, struct packet *packet,
! 2856: struct lease *lease, struct client_state *client_state,
! 2857: struct option_state *in_options,
! 2858: struct option_state *cfg_options,
! 2859: struct binding_scope **scope,
! 2860: struct universe *universe)
! 2861: {
! 2862: struct data_string sub;
! 2863: struct universe *subu;
! 2864: int i, status = 0;
! 2865:
! 2866: memset(&sub, 0, sizeof(sub));
! 2867: for (i = 0 ; i < cfg_options->universe_count ; i++) {
! 2868: subu = universes[i];
! 2869:
! 2870: if (subu == NULL)
! 2871: log_fatal("Impossible condition at %s:%d.", MDL);
! 2872:
! 2873: if (subu->enc_opt != NULL &&
! 2874: subu->enc_opt->universe == universe &&
! 2875: subu->enc_opt->format != NULL &&
! 2876: subu->enc_opt->format[0] == 'E' &&
! 2877: lookup_option(universe, cfg_options,
! 2878: subu->enc_opt->code) == NULL &&
! 2879: subu->encapsulate(&sub, packet, lease, client_state,
! 2880: in_options, cfg_options,
! 2881: scope, subu)) {
! 2882: if (append_option(result, universe,
! 2883: subu->enc_opt, &sub))
! 2884: status = 1;
! 2885:
! 2886: data_string_forget(&sub, MDL);
! 2887: }
! 2888: }
! 2889:
! 2890: return status;
! 2891: }
! 2892:
! 2893: int hashed_option_space_encapsulate (result, packet, lease, client_state,
! 2894: in_options, cfg_options, scope, universe)
! 2895: struct data_string *result;
! 2896: struct packet *packet;
! 2897: struct lease *lease;
! 2898: struct client_state *client_state;
! 2899: struct option_state *in_options;
! 2900: struct option_state *cfg_options;
! 2901: struct binding_scope **scope;
! 2902: struct universe *universe;
! 2903: {
! 2904: pair p, *hash;
! 2905: int status;
! 2906: int i;
! 2907:
! 2908: if (universe -> index >= cfg_options -> universe_count)
! 2909: return 0;
! 2910:
! 2911: hash = cfg_options -> universes [universe -> index];
! 2912: if (!hash)
! 2913: return 0;
! 2914:
! 2915: /* For each hash bucket, and each configured option cache within
! 2916: * that bucket, append the option onto the buffer in encapsulated
! 2917: * format appropriate to the universe.
! 2918: */
! 2919: status = 0;
! 2920: for (i = 0; i < OPTION_HASH_SIZE; i++) {
! 2921: for (p = hash [i]; p; p = p -> cdr) {
! 2922: if (store_option(result, universe, packet, lease,
! 2923: client_state, in_options, cfg_options,
! 2924: scope, (struct option_cache *)p->car))
! 2925: status = 1;
! 2926: }
! 2927: }
! 2928:
! 2929: if (search_subencapsulation(result, packet, lease, client_state,
! 2930: in_options, cfg_options, scope, universe))
! 2931: status = 1;
! 2932:
! 2933: return status;
! 2934: }
! 2935:
! 2936: int nwip_option_space_encapsulate (result, packet, lease, client_state,
! 2937: in_options, cfg_options, scope, universe)
! 2938: struct data_string *result;
! 2939: struct packet *packet;
! 2940: struct lease *lease;
! 2941: struct client_state *client_state;
! 2942: struct option_state *in_options;
! 2943: struct option_state *cfg_options;
! 2944: struct binding_scope **scope;
! 2945: struct universe *universe;
! 2946: {
! 2947: pair ocp;
! 2948: int status;
! 2949: static struct option_cache *no_nwip;
! 2950: struct data_string ds;
! 2951: struct option_chain_head *head;
! 2952:
! 2953: if (universe -> index >= cfg_options -> universe_count)
! 2954: return 0;
! 2955: head = ((struct option_chain_head *)
! 2956: cfg_options -> universes [nwip_universe.index]);
! 2957: if (!head)
! 2958: return 0;
! 2959:
! 2960: status = 0;
! 2961: for (ocp = head -> first; ocp; ocp = ocp -> cdr) {
! 2962: if (store_option (result, universe, packet,
! 2963: lease, client_state, in_options,
! 2964: cfg_options, scope,
! 2965: (struct option_cache *)ocp -> car))
! 2966: status = 1;
! 2967: }
! 2968:
! 2969: /* If there's no data, the nwip suboption is supposed to contain
! 2970: a suboption saying there's no data. */
! 2971: if (!status) {
! 2972: if (!no_nwip) {
! 2973: unsigned one = 1;
! 2974: static unsigned char nni [] = { 1, 0 };
! 2975:
! 2976: memset (&ds, 0, sizeof ds);
! 2977: ds.data = nni;
! 2978: ds.len = 2;
! 2979: if (option_cache_allocate (&no_nwip, MDL))
! 2980: data_string_copy (&no_nwip -> data, &ds, MDL);
! 2981: if (!option_code_hash_lookup(&no_nwip->option,
! 2982: nwip_universe.code_hash,
! 2983: &one, 0, MDL))
! 2984: log_fatal("Nwip option hash does not contain "
! 2985: "1 (%s:%d).", MDL);
! 2986: }
! 2987: if (no_nwip) {
! 2988: if (store_option (result, universe, packet, lease,
! 2989: client_state, in_options,
! 2990: cfg_options, scope, no_nwip))
! 2991: status = 1;
! 2992: }
! 2993: } else {
! 2994: memset (&ds, 0, sizeof ds);
! 2995:
! 2996: /* If we have nwip options, the first one has to be the
! 2997: nwip-exists-in-option-area option. */
! 2998: if (!buffer_allocate (&ds.buffer, result -> len + 2, MDL)) {
! 2999: data_string_forget (result, MDL);
! 3000: return 0;
! 3001: }
! 3002: ds.data = &ds.buffer -> data [0];
! 3003: ds.buffer -> data [0] = 2;
! 3004: ds.buffer -> data [1] = 0;
! 3005: memcpy (&ds.buffer -> data [2], result -> data, result -> len);
! 3006: data_string_forget (result, MDL);
! 3007: data_string_copy (result, &ds, MDL);
! 3008: data_string_forget (&ds, MDL);
! 3009: }
! 3010:
! 3011: return status;
! 3012: }
! 3013:
! 3014: /* We don't want to use ns_name_pton()...it doesn't tell us how many bytes
! 3015: * it has consumed, and it plays havoc with our escapes.
! 3016: *
! 3017: * So this function does DNS encoding, and returns either the number of
! 3018: * octects consumed (on success), or -1 on failure.
! 3019: */
! 3020: static int
! 3021: fqdn_encode(unsigned char *dst, int dstlen, const unsigned char *src,
! 3022: int srclen)
! 3023: {
! 3024: unsigned char *out;
! 3025: int i, j, len, outlen=0;
! 3026:
! 3027: out = dst;
! 3028: for (i = 0, j = 0 ; i < srclen ; i = j) {
! 3029: while ((j < srclen) && (src[j] != '.') && (src[j] != '\0'))
! 3030: j++;
! 3031:
! 3032: len = j - i;
! 3033: if ((outlen + 1 + len) > dstlen)
! 3034: return -1;
! 3035:
! 3036: *out++ = len;
! 3037: outlen++;
! 3038:
! 3039: /* We only do one FQDN, ending in one root label. */
! 3040: if (len == 0)
! 3041: return outlen;
! 3042:
! 3043: memcpy(out, src + i, len);
! 3044: out += len;
! 3045: outlen += len;
! 3046:
! 3047: /* Advance past the root label. */
! 3048: j++;
! 3049: }
! 3050:
! 3051: if ((outlen + 1) > dstlen)
! 3052: return -1;
! 3053:
! 3054: /* Place the root label. */
! 3055: *out++ = 0;
! 3056: outlen++;
! 3057:
! 3058: return outlen;
! 3059: }
! 3060:
! 3061: int fqdn_option_space_encapsulate (result, packet, lease, client_state,
! 3062: in_options, cfg_options, scope, universe)
! 3063: struct data_string *result;
! 3064: struct packet *packet;
! 3065: struct lease *lease;
! 3066: struct client_state *client_state;
! 3067: struct option_state *in_options;
! 3068: struct option_state *cfg_options;
! 3069: struct binding_scope **scope;
! 3070: struct universe *universe;
! 3071: {
! 3072: pair ocp;
! 3073: struct data_string results [FQDN_SUBOPTION_COUNT + 1];
! 3074: int status = 1;
! 3075: int i;
! 3076: unsigned len;
! 3077: struct buffer *bp = (struct buffer *)0;
! 3078: struct option_chain_head *head;
! 3079:
! 3080: /* If there's no FQDN universe, don't encapsulate. */
! 3081: if (fqdn_universe.index >= cfg_options -> universe_count)
! 3082: return 0;
! 3083: head = ((struct option_chain_head *)
! 3084: cfg_options -> universes [fqdn_universe.index]);
! 3085: if (!head)
! 3086: return 0;
! 3087:
! 3088: /* Figure out the values of all the suboptions. */
! 3089: memset (results, 0, sizeof results);
! 3090: for (ocp = head -> first; ocp; ocp = ocp -> cdr) {
! 3091: struct option_cache *oc = (struct option_cache *)(ocp -> car);
! 3092: if (oc -> option -> code > FQDN_SUBOPTION_COUNT)
! 3093: continue;
! 3094: evaluate_option_cache (&results [oc -> option -> code],
! 3095: packet, lease, client_state, in_options,
! 3096: cfg_options, scope, oc, MDL);
! 3097: }
! 3098: /* We add a byte for the flags field.
! 3099: * We add two bytes for the two RCODE fields.
! 3100: * We add a byte because we will prepend a label count.
! 3101: * We add a byte because the input len doesn't count null termination,
! 3102: * and we will add a root label.
! 3103: */
! 3104: len = 5 + results [FQDN_FQDN].len;
! 3105: /* Save the contents of the option in a buffer. */
! 3106: if (!buffer_allocate (&bp, len, MDL)) {
! 3107: log_error ("no memory for option buffer.");
! 3108: status = 0;
! 3109: goto exit;
! 3110: }
! 3111: buffer_reference (&result -> buffer, bp, MDL);
! 3112: result -> len = 3;
! 3113: result -> data = &bp -> data [0];
! 3114:
! 3115: memset (&bp -> data [0], 0, len);
! 3116: /* XXX: The server should set bit 4 (yes, 4, not 3) to 1 if it is
! 3117: * not going to perform any ddns updates. The client should set the
! 3118: * bit if it doesn't want the server to perform any updates.
! 3119: * The problem is at this layer of abstraction we have no idea if
! 3120: * the caller is a client or server.
! 3121: *
! 3122: * See RFC4702, Section 3.1, 'The "N" bit'.
! 3123: *
! 3124: * if (?)
! 3125: * bp->data[0] |= 8;
! 3126: */
! 3127: if (results [FQDN_NO_CLIENT_UPDATE].len &&
! 3128: results [FQDN_NO_CLIENT_UPDATE].data [0])
! 3129: bp -> data [0] |= 2;
! 3130: if (results [FQDN_SERVER_UPDATE].len &&
! 3131: results [FQDN_SERVER_UPDATE].data [0])
! 3132: bp -> data [0] |= 1;
! 3133: if (results [FQDN_RCODE1].len)
! 3134: bp -> data [1] = results [FQDN_RCODE1].data [0];
! 3135: if (results [FQDN_RCODE2].len)
! 3136: bp -> data [2] = results [FQDN_RCODE2].data [0];
! 3137:
! 3138: if (results [FQDN_ENCODED].len &&
! 3139: results [FQDN_ENCODED].data [0]) {
! 3140: bp->data[0] |= 4;
! 3141: if (results [FQDN_FQDN].len) {
! 3142: i = fqdn_encode(&bp->data[3], len - 3,
! 3143: results[FQDN_FQDN].data,
! 3144: results[FQDN_FQDN].len);
! 3145:
! 3146: if (i < 0) {
! 3147: status = 0;
! 3148: goto exit;
! 3149: }
! 3150:
! 3151: result->len += i;
! 3152: result->terminated = 0;
! 3153: }
! 3154: } else {
! 3155: if (results [FQDN_FQDN].len) {
! 3156: memcpy (&bp -> data [3], results [FQDN_FQDN].data,
! 3157: results [FQDN_FQDN].len);
! 3158: result -> len += results [FQDN_FQDN].len;
! 3159: result -> terminated = 0;
! 3160: }
! 3161: }
! 3162: exit:
! 3163: for (i = 1; i <= FQDN_SUBOPTION_COUNT; i++) {
! 3164: if (results [i].len)
! 3165: data_string_forget (&results [i], MDL);
! 3166: }
! 3167: buffer_dereference (&bp, MDL);
! 3168: if (!status)
! 3169: data_string_forget(result, MDL);
! 3170: return status;
! 3171: }
! 3172:
! 3173: /*
! 3174: * Trap invalid attempts to inspect FQND6 contents.
! 3175: */
! 3176: struct option_cache *
! 3177: lookup_fqdn6_option(struct universe *universe, struct option_state *options,
! 3178: unsigned code)
! 3179: {
! 3180: log_fatal("Impossible condition at %s:%d.", MDL);
! 3181: return NULL;
! 3182: }
! 3183:
! 3184: /*
! 3185: * Trap invalid attempts to save options directly to FQDN6 rather than FQDN.
! 3186: */
! 3187: void
! 3188: save_fqdn6_option(struct universe *universe, struct option_state *options,
! 3189: struct option_cache *oc, isc_boolean_t appendp)
! 3190: {
! 3191: log_fatal("Impossible condition at %s:%d.", MDL);
! 3192: }
! 3193:
! 3194: /*
! 3195: * Trap invalid attempts to delete an option out of the FQDN6 universe.
! 3196: */
! 3197: void
! 3198: delete_fqdn6_option(struct universe *universe, struct option_state *options,
! 3199: int code)
! 3200: {
! 3201: log_fatal("Impossible condition at %s:%d.", MDL);
! 3202: }
! 3203:
! 3204: /* Shill to the DHCPv4 fqdn option cache any attempts to traverse the
! 3205: * V6's option cache entry.
! 3206: *
! 3207: * This function is called speculatively by dhclient to setup
! 3208: * environment variables. But it would have already called the
! 3209: * foreach on the normal fqdn universe, so this is superfluous.
! 3210: */
! 3211: void
! 3212: fqdn6_option_space_foreach(struct packet *packet, struct lease *lease,
! 3213: struct client_state *client_state,
! 3214: struct option_state *in_options,
! 3215: struct option_state *cfg_options,
! 3216: struct binding_scope **scope,
! 3217: struct universe *u, void *stuff,
! 3218: void (*func)(struct option_cache *,
! 3219: struct packet *,
! 3220: struct lease *,
! 3221: struct client_state *,
! 3222: struct option_state *,
! 3223: struct option_state *,
! 3224: struct binding_scope **,
! 3225: struct universe *, void *))
! 3226: {
! 3227: /* Pretend it is empty. */
! 3228: return;
! 3229: }
! 3230:
! 3231: /* Turn the FQDN option space into a DHCPv6 FQDN option buffer.
! 3232: */
! 3233: int
! 3234: fqdn6_option_space_encapsulate(struct data_string *result,
! 3235: struct packet *packet, struct lease *lease,
! 3236: struct client_state *client_state,
! 3237: struct option_state *in_options,
! 3238: struct option_state *cfg_options,
! 3239: struct binding_scope **scope,
! 3240: struct universe *universe)
! 3241: {
! 3242: pair ocp;
! 3243: struct option_chain_head *head;
! 3244: struct option_cache *oc;
! 3245: unsigned char *data;
! 3246: int i, len, rval = 0, count;
! 3247: struct data_string results[FQDN_SUBOPTION_COUNT + 1];
! 3248:
! 3249: if (fqdn_universe.index >= cfg_options->universe_count)
! 3250: return 0;
! 3251: head = ((struct option_chain_head *)
! 3252: cfg_options->universes[fqdn_universe.index]);
! 3253: if (head == NULL)
! 3254: return 0;
! 3255:
! 3256: memset(results, 0, sizeof(results));
! 3257: for (ocp = head->first ; ocp != NULL ; ocp = ocp->cdr) {
! 3258: oc = (struct option_cache *)(ocp->car);
! 3259: if (oc->option->code > FQDN_SUBOPTION_COUNT)
! 3260: log_fatal("Impossible condition at %s:%d.", MDL);
! 3261:
! 3262: evaluate_option_cache(&results[oc->option->code], packet,
! 3263: lease, client_state, in_options,
! 3264: cfg_options, scope, oc, MDL);
! 3265: }
! 3266:
! 3267: /* We add a byte for the flags field at the start of the option.
! 3268: * We add a byte because we will prepend a label count.
! 3269: * We add a byte because the input length doesn't include a trailing
! 3270: * NULL, and we will add a root label.
! 3271: */
! 3272: len = results[FQDN_FQDN].len + 3;
! 3273: if (!buffer_allocate(&result->buffer, len, MDL)) {
! 3274: log_error("No memory for virtual option buffer.");
! 3275: goto exit;
! 3276: }
! 3277: data = result->buffer->data;
! 3278: result->data = data;
! 3279:
! 3280: /* The first byte is the flags field. */
! 3281: result->len = 1;
! 3282: data[0] = 0;
! 3283: /* XXX: The server should set bit 3 (yes, 3, not 4) to 1 if we
! 3284: * are not going to perform any DNS updates. The problem is
! 3285: * that at this layer of abstraction, we do not know if the caller
! 3286: * is the client or the server.
! 3287: *
! 3288: * See RFC4704 Section 4.1, 'The "N" bit'.
! 3289: *
! 3290: * if (?)
! 3291: * data[0] |= 4;
! 3292: */
! 3293: if (results[FQDN_NO_CLIENT_UPDATE].len &&
! 3294: results[FQDN_NO_CLIENT_UPDATE].data[0])
! 3295: data[0] |= 2;
! 3296: if (results[FQDN_SERVER_UPDATE].len &&
! 3297: results[FQDN_SERVER_UPDATE].data[0])
! 3298: data[0] |= 1;
! 3299:
! 3300: /* If there is no name, we're done. */
! 3301: if (results[FQDN_FQDN].len == 0) {
! 3302: rval = 1;
! 3303: goto exit;
! 3304: }
! 3305:
! 3306: /* Convert textual representation to DNS format. */
! 3307: count = fqdn_encode(data + 1, len - 1,
! 3308: results[FQDN_FQDN].data, results[FQDN_FQDN].len);
! 3309:
! 3310: if (count < 0) {
! 3311: rval = 0;
! 3312: data_string_forget(result, MDL);
! 3313: goto exit;
! 3314: }
! 3315:
! 3316: result->len += count;
! 3317: result->terminated = 0;
! 3318:
! 3319: /* Success! */
! 3320: rval = 1;
! 3321:
! 3322: exit:
! 3323: for (i = 1 ; i <= FQDN_SUBOPTION_COUNT ; i++) {
! 3324: if (result[i].len)
! 3325: data_string_forget(&results[i], MDL);
! 3326: }
! 3327:
! 3328: return rval;
! 3329: }
! 3330:
! 3331: /* Read the DHCPv6 FQDN option's contents into the FQDN virtual space.
! 3332: */
! 3333: int
! 3334: fqdn6_universe_decode(struct option_state *options,
! 3335: const unsigned char *buffer, unsigned length,
! 3336: struct universe *u)
! 3337: {
! 3338: struct buffer *bp = NULL;
! 3339: unsigned char *first_dot;
! 3340: int len, hlen, dlen;
! 3341:
! 3342: /* The FQDN option has to be at least 1 byte long. */
! 3343: if (length < 1)
! 3344: return 0;
! 3345:
! 3346: /* Save the contents of the option in a buffer. There are 3
! 3347: * one-byte values we record from the packet, so we go ahead
! 3348: * and allocate a bigger buffer to accommodate them. But the
! 3349: * 'length' we got (because it is a DNS encoded string) is
! 3350: * one longer than we need...so we only add two extra octets.
! 3351: */
! 3352: if (!buffer_allocate(&bp, length + 2, MDL)) {
! 3353: log_error("No memory for dhcp6.fqdn option buffer.");
! 3354: return 0;
! 3355: }
! 3356:
! 3357: /* The v6 FQDN is always 'encoded' per DNS. */
! 3358: bp->data[0] = 1;
! 3359: if (!save_option_buffer(&fqdn_universe, options, bp,
! 3360: bp->data, 1, FQDN_ENCODED, 0))
! 3361: goto error;
! 3362:
! 3363: /* XXX: We need to process 'The "N" bit'. */
! 3364:
! 3365: if (buffer[0] & 1) /* server-update. */
! 3366: bp->data[2] = 1;
! 3367: else
! 3368: bp->data[2] = 0;
! 3369:
! 3370: if (!save_option_buffer(&fqdn_universe, options, bp, bp->data + 2, 1,
! 3371: FQDN_SERVER_UPDATE, 0))
! 3372: goto error;
! 3373:
! 3374: if (buffer[0] & 2) /* no-client-update. */
! 3375: bp->data[1] = 1;
! 3376: else
! 3377: bp->data[1] = 0;
! 3378:
! 3379: if (!save_option_buffer(&fqdn_universe, options, bp, bp->data + 1, 1,
! 3380: FQDN_NO_CLIENT_UPDATE, 0))
! 3381: goto error;
! 3382:
! 3383: /* Convert the domain name to textual representation for config. */
! 3384: len = MRns_name_ntop(buffer + 1, (char *)bp->data + 3, length - 1);
! 3385: if (len == -1) {
! 3386: log_error("Unable to convert dhcp6.fqdn domain name to "
! 3387: "printable form.");
! 3388: goto error;
! 3389: }
! 3390:
! 3391: /* Save the domain name. */
! 3392: if (len > 0) {
! 3393: unsigned char *fqdn_start = bp->data + 3;
! 3394:
! 3395: if (!save_option_buffer(&fqdn_universe, options, bp,
! 3396: fqdn_start, len, FQDN_FQDN, 1))
! 3397: goto error;
! 3398:
! 3399: first_dot = (unsigned char *)strchr((char *)fqdn_start, '.');
! 3400:
! 3401: if (first_dot != NULL) {
! 3402: hlen = first_dot - fqdn_start;
! 3403: dlen = len - hlen;
! 3404: } else {
! 3405: hlen = len;
! 3406: dlen = 0;
! 3407: }
! 3408:
! 3409: if (!save_option_buffer(&fqdn_universe, options, bp,
! 3410: fqdn_start, len, FQDN_FQDN, 1) ||
! 3411: ((hlen > 0) &&
! 3412: !save_option_buffer(&fqdn_universe, options, bp,
! 3413: fqdn_start, hlen,
! 3414: FQDN_HOSTNAME, 0)) ||
! 3415: ((dlen > 0) &&
! 3416: !save_option_buffer(&fqdn_universe, options, bp,
! 3417: first_dot, dlen, FQDN_DOMAINNAME, 0)))
! 3418: goto error;
! 3419: }
! 3420:
! 3421: buffer_dereference(&bp, MDL);
! 3422: return 1;
! 3423:
! 3424: error:
! 3425: buffer_dereference(&bp, MDL);
! 3426: return 0;
! 3427: }
! 3428:
! 3429: void option_space_foreach (struct packet *packet, struct lease *lease,
! 3430: struct client_state *client_state,
! 3431: struct option_state *in_options,
! 3432: struct option_state *cfg_options,
! 3433: struct binding_scope **scope,
! 3434: struct universe *u, void *stuff,
! 3435: void (*func) (struct option_cache *,
! 3436: struct packet *,
! 3437: struct lease *, struct client_state *,
! 3438: struct option_state *,
! 3439: struct option_state *,
! 3440: struct binding_scope **,
! 3441: struct universe *, void *))
! 3442: {
! 3443: if (u -> foreach)
! 3444: (*u -> foreach) (packet, lease, client_state, in_options,
! 3445: cfg_options, scope, u, stuff, func);
! 3446: }
! 3447:
! 3448: void suboption_foreach (struct packet *packet, struct lease *lease,
! 3449: struct client_state *client_state,
! 3450: struct option_state *in_options,
! 3451: struct option_state *cfg_options,
! 3452: struct binding_scope **scope,
! 3453: struct universe *u, void *stuff,
! 3454: void (*func) (struct option_cache *,
! 3455: struct packet *,
! 3456: struct lease *, struct client_state *,
! 3457: struct option_state *,
! 3458: struct option_state *,
! 3459: struct binding_scope **,
! 3460: struct universe *, void *),
! 3461: struct option_cache *oc,
! 3462: const char *vsname)
! 3463: {
! 3464: struct universe *universe = find_option_universe (oc -> option,
! 3465: vsname);
! 3466: if (universe -> foreach)
! 3467: (*universe -> foreach) (packet, lease, client_state,
! 3468: in_options, cfg_options,
! 3469: scope, universe, stuff, func);
! 3470: }
! 3471:
! 3472: void hashed_option_space_foreach (struct packet *packet, struct lease *lease,
! 3473: struct client_state *client_state,
! 3474: struct option_state *in_options,
! 3475: struct option_state *cfg_options,
! 3476: struct binding_scope **scope,
! 3477: struct universe *u, void *stuff,
! 3478: void (*func) (struct option_cache *,
! 3479: struct packet *,
! 3480: struct lease *,
! 3481: struct client_state *,
! 3482: struct option_state *,
! 3483: struct option_state *,
! 3484: struct binding_scope **,
! 3485: struct universe *, void *))
! 3486: {
! 3487: pair *hash;
! 3488: int i;
! 3489: struct option_cache *oc;
! 3490:
! 3491: if (cfg_options -> universe_count <= u -> index)
! 3492: return;
! 3493:
! 3494: hash = cfg_options -> universes [u -> index];
! 3495: if (!hash)
! 3496: return;
! 3497: for (i = 0; i < OPTION_HASH_SIZE; i++) {
! 3498: pair p;
! 3499: /* XXX save _all_ options! XXX */
! 3500: for (p = hash [i]; p; p = p -> cdr) {
! 3501: oc = (struct option_cache *)p -> car;
! 3502: (*func) (oc, packet, lease, client_state,
! 3503: in_options, cfg_options, scope, u, stuff);
! 3504: }
! 3505: }
! 3506: }
! 3507:
! 3508: void
! 3509: save_linked_option(struct universe *universe, struct option_state *options,
! 3510: struct option_cache *oc, isc_boolean_t appendp)
! 3511: {
! 3512: pair *tail;
! 3513: struct option_chain_head *head;
! 3514: struct option_cache **ocloc;
! 3515:
! 3516: if (universe -> index >= options -> universe_count)
! 3517: return;
! 3518: head = ((struct option_chain_head *)
! 3519: options -> universes [universe -> index]);
! 3520: if (!head) {
! 3521: if (!option_chain_head_allocate (((struct option_chain_head **)
! 3522: &options -> universes
! 3523: [universe -> index]), MDL))
! 3524: return;
! 3525: head = ((struct option_chain_head *)
! 3526: options -> universes [universe -> index]);
! 3527: }
! 3528:
! 3529: /* Find the tail of the list. */
! 3530: for (tail = &head -> first; *tail; tail = &((*tail) -> cdr)) {
! 3531: ocloc = (struct option_cache **)&(*tail)->car;
! 3532:
! 3533: if (oc->option->code == (*ocloc)->option->code) {
! 3534: if (appendp) {
! 3535: do {
! 3536: ocloc = &(*ocloc)->next;
! 3537: } while (*ocloc != NULL);
! 3538: } else {
! 3539: option_cache_dereference(ocloc, MDL);
! 3540: }
! 3541: option_cache_reference(ocloc, oc, MDL);
! 3542: return;
! 3543: }
! 3544: }
! 3545:
! 3546: *tail = cons (0, 0);
! 3547: if (*tail) {
! 3548: option_cache_reference ((struct option_cache **)
! 3549: (&(*tail) -> car), oc, MDL);
! 3550: }
! 3551: }
! 3552:
! 3553: int linked_option_space_encapsulate (result, packet, lease, client_state,
! 3554: in_options, cfg_options, scope, universe)
! 3555: struct data_string *result;
! 3556: struct packet *packet;
! 3557: struct lease *lease;
! 3558: struct client_state *client_state;
! 3559: struct option_state *in_options;
! 3560: struct option_state *cfg_options;
! 3561: struct binding_scope **scope;
! 3562: struct universe *universe;
! 3563: {
! 3564: int status = 0;
! 3565: pair oc;
! 3566: struct option_chain_head *head;
! 3567:
! 3568: if (universe -> index >= cfg_options -> universe_count)
! 3569: return status;
! 3570: head = ((struct option_chain_head *)
! 3571: cfg_options -> universes [universe -> index]);
! 3572: if (!head)
! 3573: return status;
! 3574:
! 3575: for (oc = head -> first; oc; oc = oc -> cdr) {
! 3576: if (store_option (result, universe, packet,
! 3577: lease, client_state, in_options, cfg_options,
! 3578: scope, (struct option_cache *)(oc -> car)))
! 3579: status = 1;
! 3580: }
! 3581:
! 3582: if (search_subencapsulation(result, packet, lease, client_state,
! 3583: in_options, cfg_options, scope, universe))
! 3584: status = 1;
! 3585:
! 3586: return status;
! 3587: }
! 3588:
! 3589: void delete_linked_option (universe, options, code)
! 3590: struct universe *universe;
! 3591: struct option_state *options;
! 3592: int code;
! 3593: {
! 3594: pair *tail, tmp = (pair)0;
! 3595: struct option_chain_head *head;
! 3596:
! 3597: if (universe -> index >= options -> universe_count)
! 3598: return;
! 3599: head = ((struct option_chain_head *)
! 3600: options -> universes [universe -> index]);
! 3601: if (!head)
! 3602: return;
! 3603:
! 3604: for (tail = &head -> first; *tail; tail = &((*tail) -> cdr)) {
! 3605: if (code ==
! 3606: ((struct option_cache *)(*tail) -> car) -> option -> code)
! 3607: {
! 3608: tmp = (*tail) -> cdr;
! 3609: option_cache_dereference ((struct option_cache **)
! 3610: (&(*tail) -> car), MDL);
! 3611: dfree (*tail, MDL);
! 3612: (*tail) = tmp;
! 3613: break;
! 3614: }
! 3615: }
! 3616: }
! 3617:
! 3618: struct option_cache *lookup_linked_option (universe, options, code)
! 3619: struct universe *universe;
! 3620: struct option_state *options;
! 3621: unsigned code;
! 3622: {
! 3623: pair oc;
! 3624: struct option_chain_head *head;
! 3625:
! 3626: if (universe -> index >= options -> universe_count)
! 3627: return 0;
! 3628: head = ((struct option_chain_head *)
! 3629: options -> universes [universe -> index]);
! 3630: if (!head)
! 3631: return 0;
! 3632:
! 3633: for (oc = head -> first; oc; oc = oc -> cdr) {
! 3634: if (code ==
! 3635: ((struct option_cache *)(oc -> car)) -> option -> code) {
! 3636: return (struct option_cache *)(oc -> car);
! 3637: }
! 3638: }
! 3639:
! 3640: return (struct option_cache *)0;
! 3641: }
! 3642:
! 3643: int linked_option_state_dereference (universe, state, file, line)
! 3644: struct universe *universe;
! 3645: struct option_state *state;
! 3646: const char *file;
! 3647: int line;
! 3648: {
! 3649: return (option_chain_head_dereference
! 3650: ((struct option_chain_head **)
! 3651: (&state -> universes [universe -> index]), MDL));
! 3652: }
! 3653:
! 3654: void linked_option_space_foreach (struct packet *packet, struct lease *lease,
! 3655: struct client_state *client_state,
! 3656: struct option_state *in_options,
! 3657: struct option_state *cfg_options,
! 3658: struct binding_scope **scope,
! 3659: struct universe *u, void *stuff,
! 3660: void (*func) (struct option_cache *,
! 3661: struct packet *,
! 3662: struct lease *,
! 3663: struct client_state *,
! 3664: struct option_state *,
! 3665: struct option_state *,
! 3666: struct binding_scope **,
! 3667: struct universe *, void *))
! 3668: {
! 3669: pair car;
! 3670: struct option_chain_head *head;
! 3671:
! 3672: if (u -> index >= cfg_options -> universe_count)
! 3673: return;
! 3674: head = ((struct option_chain_head *)
! 3675: cfg_options -> universes [u -> index]);
! 3676: if (!head)
! 3677: return;
! 3678: for (car = head -> first; car; car = car -> cdr) {
! 3679: (*func) ((struct option_cache *)(car -> car),
! 3680: packet, lease, client_state,
! 3681: in_options, cfg_options, scope, u, stuff);
! 3682: }
! 3683: }
! 3684:
! 3685: void do_packet (interface, packet, len, from_port, from, hfrom)
! 3686: struct interface_info *interface;
! 3687: struct dhcp_packet *packet;
! 3688: unsigned len;
! 3689: unsigned int from_port;
! 3690: struct iaddr from;
! 3691: struct hardware *hfrom;
! 3692: {
! 3693: struct option_cache *op;
! 3694: struct packet *decoded_packet;
! 3695: #if defined (DEBUG_MEMORY_LEAKAGE)
! 3696: unsigned long previous_outstanding = dmalloc_outstanding;
! 3697: #endif
! 3698:
! 3699: #if defined (TRACING)
! 3700: trace_inpacket_stash (interface, packet, len, from_port, from, hfrom);
! 3701: #endif
! 3702:
! 3703: decoded_packet = (struct packet *)0;
! 3704: if (!packet_allocate (&decoded_packet, MDL)) {
! 3705: log_error ("do_packet: no memory for incoming packet!");
! 3706: return;
! 3707: }
! 3708: decoded_packet -> raw = packet;
! 3709: decoded_packet -> packet_length = len;
! 3710: decoded_packet -> client_port = from_port;
! 3711: decoded_packet -> client_addr = from;
! 3712: interface_reference (&decoded_packet -> interface, interface, MDL);
! 3713: decoded_packet -> haddr = hfrom;
! 3714:
! 3715: if (packet -> hlen > sizeof packet -> chaddr) {
! 3716: packet_dereference (&decoded_packet, MDL);
! 3717: log_info ("Discarding packet with bogus hlen.");
! 3718: return;
! 3719: }
! 3720:
! 3721: /* If there's an option buffer, try to parse it. */
! 3722: if (decoded_packet -> packet_length >= DHCP_FIXED_NON_UDP + 4) {
! 3723: if (!parse_options (decoded_packet)) {
! 3724: if (decoded_packet -> options)
! 3725: option_state_dereference
! 3726: (&decoded_packet -> options, MDL);
! 3727: packet_dereference (&decoded_packet, MDL);
! 3728: return;
! 3729: }
! 3730:
! 3731: if (decoded_packet -> options_valid &&
! 3732: (op = lookup_option (&dhcp_universe,
! 3733: decoded_packet -> options,
! 3734: DHO_DHCP_MESSAGE_TYPE))) {
! 3735: struct data_string dp;
! 3736: memset (&dp, 0, sizeof dp);
! 3737: evaluate_option_cache (&dp, decoded_packet,
! 3738: (struct lease *)0,
! 3739: (struct client_state *)0,
! 3740: decoded_packet -> options,
! 3741: (struct option_state *)0,
! 3742: (struct binding_scope **)0,
! 3743: op, MDL);
! 3744: if (dp.len > 0)
! 3745: decoded_packet -> packet_type = dp.data [0];
! 3746: else
! 3747: decoded_packet -> packet_type = 0;
! 3748: data_string_forget (&dp, MDL);
! 3749: }
! 3750: }
! 3751:
! 3752: if (decoded_packet -> packet_type)
! 3753: dhcp (decoded_packet);
! 3754: else
! 3755: bootp (decoded_packet);
! 3756:
! 3757: /* If the caller kept the packet, they'll have upped the refcnt. */
! 3758: packet_dereference (&decoded_packet, MDL);
! 3759:
! 3760: #if defined (DEBUG_MEMORY_LEAKAGE)
! 3761: log_info ("generation %ld: %ld new, %ld outstanding, %ld long-term",
! 3762: dmalloc_generation,
! 3763: dmalloc_outstanding - previous_outstanding,
! 3764: dmalloc_outstanding, dmalloc_longterm);
! 3765: #endif
! 3766: #if defined (DEBUG_MEMORY_LEAKAGE)
! 3767: dmalloc_dump_outstanding ();
! 3768: #endif
! 3769: #if defined (DEBUG_RC_HISTORY_EXHAUSTIVELY)
! 3770: dump_rc_history (0);
! 3771: #endif
! 3772: }
! 3773:
! 3774: int
! 3775: packet6_len_okay(const char *packet, int len) {
! 3776: if (len < 1) {
! 3777: return 0;
! 3778: }
! 3779: if ((packet[0] == DHCPV6_RELAY_FORW) ||
! 3780: (packet[0] == DHCPV6_RELAY_REPL)) {
! 3781: if (len >= offsetof(struct dhcpv6_relay_packet, options)) {
! 3782: return 1;
! 3783: } else {
! 3784: return 0;
! 3785: }
! 3786: } else {
! 3787: if (len >= offsetof(struct dhcpv6_packet, options)) {
! 3788: return 1;
! 3789: } else {
! 3790: return 0;
! 3791: }
! 3792: }
! 3793: }
! 3794:
! 3795: #ifdef DHCPv6
! 3796: void
! 3797: do_packet6(struct interface_info *interface, const char *packet,
! 3798: int len, int from_port, const struct iaddr *from,
! 3799: isc_boolean_t was_unicast) {
! 3800: unsigned char msg_type;
! 3801: const struct dhcpv6_packet *msg;
! 3802: const struct dhcpv6_relay_packet *relay;
! 3803: struct packet *decoded_packet;
! 3804:
! 3805: if (!packet6_len_okay(packet, len)) {
! 3806: log_info("do_packet6: "
! 3807: "short packet from %s port %d, len %d, dropped",
! 3808: piaddr(*from), from_port, len);
! 3809: return;
! 3810: }
! 3811:
! 3812: decoded_packet = NULL;
! 3813: if (!packet_allocate(&decoded_packet, MDL)) {
! 3814: log_error("do_packet6: no memory for incoming packet.");
! 3815: return;
! 3816: }
! 3817:
! 3818: if (!option_state_allocate(&decoded_packet->options, MDL)) {
! 3819: log_error("do_packet6: no memory for options.");
! 3820: packet_dereference(&decoded_packet, MDL);
! 3821: return;
! 3822: }
! 3823:
! 3824: /* IPv4 information, already set to 0 */
! 3825: /* decoded_packet->packet_type = 0; */
! 3826: /* memset(&decoded_packet->haddr, 0, sizeof(decoded_packet->haddr)); */
! 3827: /* decoded_packet->circuit_id = NULL; */
! 3828: /* decoded_packet->circuit_id_len = 0; */
! 3829: /* decoded_packet->remote_id = NULL; */
! 3830: /* decoded_packet->remote_id_len = 0; */
! 3831: decoded_packet->raw = (struct dhcp_packet *) packet;
! 3832: decoded_packet->packet_length = (unsigned) len;
! 3833: decoded_packet->client_port = from_port;
! 3834: decoded_packet->client_addr = *from;
! 3835: interface_reference(&decoded_packet->interface, interface, MDL);
! 3836:
! 3837: decoded_packet->unicast = was_unicast;
! 3838:
! 3839: msg_type = packet[0];
! 3840: if ((msg_type == DHCPV6_RELAY_FORW) ||
! 3841: (msg_type == DHCPV6_RELAY_REPL)) {
! 3842: relay = (const struct dhcpv6_relay_packet *)packet;
! 3843: decoded_packet->dhcpv6_msg_type = relay->msg_type;
! 3844:
! 3845: /* relay-specific data */
! 3846: decoded_packet->dhcpv6_hop_count = relay->hop_count;
! 3847: memcpy(&decoded_packet->dhcpv6_link_address,
! 3848: relay->link_address, sizeof(relay->link_address));
! 3849: memcpy(&decoded_packet->dhcpv6_peer_address,
! 3850: relay->peer_address, sizeof(relay->peer_address));
! 3851:
! 3852: if (!parse_option_buffer(decoded_packet->options,
! 3853: relay->options, len-sizeof(*relay),
! 3854: &dhcpv6_universe)) {
! 3855: /* no logging here, as parse_option_buffer() logs all
! 3856: cases where it fails */
! 3857: packet_dereference(&decoded_packet, MDL);
! 3858: return;
! 3859: }
! 3860: } else {
! 3861: msg = (const struct dhcpv6_packet *)packet;
! 3862: decoded_packet->dhcpv6_msg_type = msg->msg_type;
! 3863:
! 3864: /* message-specific data */
! 3865: memcpy(decoded_packet->dhcpv6_transaction_id,
! 3866: msg->transaction_id,
! 3867: sizeof(decoded_packet->dhcpv6_transaction_id));
! 3868:
! 3869: if (!parse_option_buffer(decoded_packet->options,
! 3870: msg->options, len-sizeof(*msg),
! 3871: &dhcpv6_universe)) {
! 3872: /* no logging here, as parse_option_buffer() logs all
! 3873: cases where it fails */
! 3874: packet_dereference(&decoded_packet, MDL);
! 3875: return;
! 3876: }
! 3877: }
! 3878:
! 3879: dhcpv6(decoded_packet);
! 3880:
! 3881: packet_dereference(&decoded_packet, MDL);
! 3882: }
! 3883: #endif /* DHCPv6 */
! 3884:
! 3885: int
! 3886: pretty_escape(char **dst, char *dend, const unsigned char **src,
! 3887: const unsigned char *send)
! 3888: {
! 3889: int count = 0;
! 3890:
! 3891: /* If there aren't as many bytes left as there are in the source
! 3892: * buffer, don't even bother entering the loop.
! 3893: */
! 3894: if (dst == NULL || dend == NULL || src == NULL || send == NULL ||
! 3895: *dst == NULL || *src == NULL || (*dst >= dend) || (*src > send) ||
! 3896: ((send - *src) > (dend - *dst)))
! 3897: return -1;
! 3898:
! 3899: for ( ; *src < send ; (*src)++) {
! 3900: if (!isascii (**src) || !isprint (**src)) {
! 3901: /* Skip trailing NUL. */
! 3902: if ((*src + 1) != send || **src != '\0') {
! 3903: if (*dst + 4 > dend)
! 3904: return -1;
! 3905:
! 3906: sprintf(*dst, "\\%03o",
! 3907: **src);
! 3908: (*dst) += 4;
! 3909: count += 4;
! 3910: }
! 3911: } else if (**src == '"' || **src == '\'' || **src == '$' ||
! 3912: **src == '`' || **src == '\\' || **src == '|' ||
! 3913: **src == '&') {
! 3914: if (*dst + 2 > dend)
! 3915: return -1;
! 3916:
! 3917: **dst = '\\';
! 3918: (*dst)++;
! 3919: **dst = **src;
! 3920: (*dst)++;
! 3921: count += 2;
! 3922: } else {
! 3923: if (*dst + 1 > dend)
! 3924: return -1;
! 3925:
! 3926: **dst = **src;
! 3927: (*dst)++;
! 3928: count++;
! 3929: }
! 3930: }
! 3931:
! 3932: return count;
! 3933: }
! 3934:
! 3935: static int
! 3936: pretty_text(char **dst, char *dend, const unsigned char **src,
! 3937: const unsigned char *send, int emit_quotes)
! 3938: {
! 3939: int count;
! 3940:
! 3941: if (dst == NULL || dend == NULL || src == NULL || send == NULL ||
! 3942: *dst == NULL || *src == NULL ||
! 3943: ((*dst + (emit_quotes ? 2 : 0)) > dend) || (*src > send))
! 3944: return -1;
! 3945:
! 3946: if (emit_quotes) {
! 3947: **dst = '"';
! 3948: (*dst)++;
! 3949: }
! 3950:
! 3951: /* dend-1 leaves 1 byte for the closing quote. */
! 3952: count = pretty_escape(dst, dend - (emit_quotes ? 1 : 0), src, send);
! 3953:
! 3954: if (count == -1)
! 3955: return -1;
! 3956:
! 3957: if (emit_quotes && (*dst < dend)) {
! 3958: **dst = '"';
! 3959: (*dst)++;
! 3960:
! 3961: /* Includes quote prior to pretty_escape(); */
! 3962: count += 2;
! 3963: }
! 3964:
! 3965: return count;
! 3966: }
! 3967:
! 3968: static int
! 3969: pretty_domain(char **dst, char *dend, const unsigned char **src,
! 3970: const unsigned char *send)
! 3971: {
! 3972: const unsigned char *tend;
! 3973: int count = 2;
! 3974: int tsiz, status;
! 3975:
! 3976: if (dst == NULL || dend == NULL || src == NULL || send == NULL ||
! 3977: *dst == NULL || *src == NULL ||
! 3978: ((*dst + 2) > dend) || (*src >= send))
! 3979: return -1;
! 3980:
! 3981: **dst = '"';
! 3982: (*dst)++;
! 3983:
! 3984: do {
! 3985: /* Continue loop until end of src buffer. */
! 3986: if (*src >= send)
! 3987: break;
! 3988:
! 3989: /* Consume tag size. */
! 3990: tsiz = **src;
! 3991: (*src)++;
! 3992:
! 3993: /* At root, finis. */
! 3994: if (tsiz == 0)
! 3995: break;
! 3996:
! 3997: tend = (*src) + tsiz;
! 3998:
! 3999: /* If the tag exceeds the source buffer, it's illegal.
! 4000: * This should also trap compression pointers (which should
! 4001: * not be in these buffers).
! 4002: */
! 4003: if (tend > send)
! 4004: return -1;
! 4005:
! 4006: /* dend-2 leaves room for a trailing dot and quote. */
! 4007: status = pretty_escape(dst, dend-2, src, tend);
! 4008:
! 4009: if ((status == -1) || ((*dst + 2) > dend))
! 4010: return -1;
! 4011:
! 4012: **dst = '.';
! 4013: (*dst)++;
! 4014: count += status + 1;
! 4015: }
! 4016: while(1);
! 4017:
! 4018: **dst = '"';
! 4019: (*dst)++;
! 4020:
! 4021: return count;
! 4022: }
! 4023:
! 4024: /*
! 4025: * Add the option identified with the option number and data to the
! 4026: * options state.
! 4027: */
! 4028: int
! 4029: add_option(struct option_state *options,
! 4030: unsigned int option_num,
! 4031: void *data,
! 4032: unsigned int data_len)
! 4033: {
! 4034: struct option_cache *oc;
! 4035: struct option *option;
! 4036:
! 4037: /* INSIST(options != NULL); */
! 4038: /* INSIST(data != NULL); */
! 4039:
! 4040: option = NULL;
! 4041: if (!option_code_hash_lookup(&option, dhcp_universe.code_hash,
! 4042: &option_num, 0, MDL)) {
! 4043: log_error("Attempting to add unknown option %d.", option_num);
! 4044: return 0;
! 4045: }
! 4046:
! 4047: oc = NULL;
! 4048: if (!option_cache_allocate(&oc, MDL)) {
! 4049: log_error("No memory for option cache adding %s (option %d).",
! 4050: option->name, option_num);
! 4051: return 0;
! 4052: }
! 4053:
! 4054: if (!make_const_data(&oc->expression,
! 4055: data,
! 4056: data_len,
! 4057: 0,
! 4058: 0,
! 4059: MDL)) {
! 4060: log_error("No memory for constant data adding %s (option %d).",
! 4061: option->name, option_num);
! 4062: option_cache_dereference(&oc, MDL);
! 4063: return 0;
! 4064: }
! 4065:
! 4066: option_reference(&(oc->option), option, MDL);
! 4067: save_option(&dhcp_universe, options, oc);
! 4068: option_cache_dereference(&oc, MDL);
! 4069:
! 4070: return 1;
! 4071: }
! 4072:
! 4073:
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>