summaryrefslogtreecommitdiffstats
path: root/contrib/isc-dhcp/common/options.c
diff options
context:
space:
mode:
Diffstat (limited to 'contrib/isc-dhcp/common/options.c')
-rw-r--r--contrib/isc-dhcp/common/options.c2248
1 files changed, 0 insertions, 2248 deletions
diff --git a/contrib/isc-dhcp/common/options.c b/contrib/isc-dhcp/common/options.c
deleted file mode 100644
index 9b67b23..0000000
--- a/contrib/isc-dhcp/common/options.c
+++ /dev/null
@@ -1,2248 +0,0 @@
-/* options.c
-
- DHCP options parsing and reassembly. */
-
-/*
- * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC")
- * Copyright (c) 1995-2003 by Internet Software Consortium
- *
- * Permission to use, copy, modify, and distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
- * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- *
- * Internet Systems Consortium, Inc.
- * 950 Charter Street
- * Redwood City, CA 94063
- * <info@isc.org>
- * http://www.isc.org/
- *
- * This software has been written for Internet Systems Consortium
- * by Ted Lemon in cooperation with Vixie Enterprises and Nominum, Inc.
- * To learn more about Internet Systems Consortium, see
- * ``http://www.isc.org/''. To learn more about Vixie Enterprises,
- * see ``http://www.vix.com''. To learn more about Nominum, Inc., see
- * ``http://www.nominum.com''.
- */
-
-#ifndef lint
-static char copyright[] =
-"$Id: options.c,v 1.85.2.13 2004/06/10 17:59:19 dhankins Exp $ Copyright (c) 2004 Internet Systems Consortium. All rights reserved.\n";
-#endif /* not lint */
-
-#define DHCP_OPTION_DATA
-#include "dhcpd.h"
-#include <omapip/omapip_p.h>
-
-struct option *vendor_cfg_option;
-
-static void do_option_set PROTO ((pair *,
- struct option_cache *,
- enum statement_op));
-
-/* Parse all available options out of the specified packet. */
-
-int parse_options (packet)
- struct packet *packet;
-{
- int i;
- struct option_cache *op = (struct option_cache *)0;
-
- /* Allocate a new option state. */
- if (!option_state_allocate (&packet -> options, MDL)) {
- packet -> options_valid = 0;
- return 0;
- }
-
- /* If we don't see the magic cookie, there's nothing to parse. */
- if (memcmp (packet -> raw -> options, DHCP_OPTIONS_COOKIE, 4)) {
- packet -> options_valid = 0;
- return 1;
- }
-
- /* Go through the options field, up to the end of the packet
- or the End field. */
- if (!parse_option_buffer (packet -> options,
- &packet -> raw -> options [4],
- (packet -> packet_length -
- DHCP_FIXED_NON_UDP - 4),
- &dhcp_universe))
- return 0;
-
- /* If we parsed a DHCP Option Overload option, parse more
- options out of the buffer(s) containing them. */
- if (packet -> options_valid &&
- (op = lookup_option (&dhcp_universe, packet -> options,
- DHO_DHCP_OPTION_OVERLOAD))) {
- if (op -> data.data [0] & 1) {
- if (!parse_option_buffer
- (packet -> options,
- (unsigned char *)packet -> raw -> file,
- sizeof packet -> raw -> file,
- &dhcp_universe))
- return 0;
- }
- if (op -> data.data [0] & 2) {
- if (!parse_option_buffer
- (packet -> options,
- (unsigned char *)packet -> raw -> sname,
- sizeof packet -> raw -> sname,
- &dhcp_universe))
- return 0;
- }
- }
- packet -> options_valid = 1;
- return 1;
-}
-
-/* Parse options out of the specified buffer, storing addresses of option
- values in packet -> options and setting packet -> options_valid if no
- errors are encountered. */
-
-int parse_option_buffer (options, buffer, length, universe)
- struct option_state *options;
- const unsigned char *buffer;
- unsigned length;
- struct universe *universe;
-{
- unsigned char *t;
- const unsigned char *end = buffer + length;
- unsigned len, offset;
- int code;
- struct option_cache *op = (struct option_cache *)0;
- struct buffer *bp = (struct buffer *)0;
-
- if (!buffer_allocate (&bp, length, MDL)) {
- log_error ("no memory for option buffer.");
- return 0;
- }
- memcpy (bp -> data, buffer, length);
-
- for (offset = 0; buffer [offset] != DHO_END && offset < length; ) {
- code = buffer [offset];
- /* Pad options don't have a length - just skip them. */
- if (code == DHO_PAD) {
- ++offset;
- continue;
- }
-
- /* Don't look for length if the buffer isn't that big. */
- if (offset + 2 > length) {
- len = 65536;
- goto bogus;
- }
-
- /* All other fields (except end, see above) have a
- one-byte length. */
- len = buffer [offset + 1];
-
- /* If the length is outrageous, the options are bad. */
- if (offset + len + 2 > length) {
- bogus:
- log_error ("parse_option_buffer: option %s (%d) %s.",
- dhcp_options [code].name, len,
- "larger than buffer");
- buffer_dereference (&bp, MDL);
- return 0;
- }
-
- /* If the option contains an encapsulation, parse it. If
- the parse fails, or the option isn't an encapsulation (by
- far the most common case), or the option isn't entirely
- an encapsulation, keep the raw data as well. */
- if (universe -> options [code] &&
- !((universe -> options [code] -> format [0] == 'e' ||
- universe -> options [code] -> format [0] == 'E') &&
- (parse_encapsulated_suboptions
- (options, universe -> options [code],
- buffer + offset + 2, len,
- universe, (const char *)0)))) {
- op = lookup_option (universe, options, code);
- if (op) {
- struct data_string new;
- memset (&new, 0, sizeof new);
- if (!buffer_allocate (&new.buffer, op -> data.len + len,
- MDL)) {
- log_error ("parse_option_buffer: No memory.");
- return 0;
- }
- memcpy (new.buffer -> data, op -> data.data,
- op -> data.len);
- memcpy (&new.buffer -> data [op -> data.len],
- &bp -> data [offset + 2], len);
- new.len = op -> data.len + len;
- new.data = new.buffer -> data;
- data_string_forget (&op -> data, MDL);
- data_string_copy (&op -> data, &new, MDL);
- data_string_forget (&new, MDL);
- } else {
- save_option_buffer (universe, options, bp,
- &bp -> data [offset + 2], len,
- universe -> options [code], 1);
- }
- }
- offset += len + 2;
- }
- buffer_dereference (&bp, MDL);
- return 1;
-}
-
-/* If an option in an option buffer turns out to be an encapsulation,
- figure out what to do. If we don't know how to de-encapsulate it,
- or it's not well-formed, return zero; otherwise, return 1, indicating
- that we succeeded in de-encapsulating it. */
-
-struct universe *find_option_universe (struct option *eopt, const char *uname)
-{
- int i;
- char *s, *t;
- struct universe *universe = (struct universe *)0;
-
- /* Look for the E option in the option format. */
- s = strchr (eopt -> format, 'E');
- if (!s) {
- log_error ("internal encapsulation format error 1.");
- return 0;
- }
- /* Look for the universe name in the option format. */
- t = strchr (++s, '.');
- /* If there was no trailing '.', or there's something after the
- trailing '.', the option is bogus and we can't use it. */
- if (!t || t [1]) {
- log_error ("internal encapsulation format error 2.");
- return 0;
- }
- if (t == s && uname) {
- for (i = 0; i < universe_count; i++) {
- if (!strcmp (universes [i] -> name, uname)) {
- universe = universes [i];
- break;
- }
- }
- } else if (t != s) {
- for (i = 0; i < universe_count; i++) {
- if (strlen (universes [i] -> name) == t - s &&
- !memcmp (universes [i] -> name,
- s, (unsigned)(t - s))) {
- universe = universes [i];
- break;
- }
- }
- }
- return universe;
-}
-
-/* If an option in an option buffer turns out to be an encapsulation,
- figure out what to do. If we don't know how to de-encapsulate it,
- or it's not well-formed, return zero; otherwise, return 1, indicating
- that we succeeded in de-encapsulating it. */
-
-int parse_encapsulated_suboptions (struct option_state *options,
- struct option *eopt,
- const unsigned char *buffer,
- unsigned len, struct universe *eu,
- const char *uname)
-{
- int i;
- struct universe *universe = find_option_universe (eopt, uname);
-
- /* If we didn't find the universe, we can't do anything with it
- right now (e.g., we can't decode vendor options until we've
- decoded the packet and executed the scopes that it matches). */
- if (!universe)
- return 0;
-
- /* If we don't have a decoding function for it, we can't decode
- it. */
- if (!universe -> decode)
- return 0;
-
- i = (*universe -> decode) (options, buffer, len, universe);
-
- /* If there is stuff before the suboptions, we have to keep it. */
- if (eopt -> format [0] != 'E')
- return 0;
- /* Otherwise, return the status of the decode function. */
- return i;
-}
-
-int fqdn_universe_decode (struct option_state *options,
- const unsigned char *buffer,
- unsigned length, struct universe *u)
-{
- char *name;
- struct buffer *bp = (struct buffer *)0;
-
- /* FQDN options have to be at least four bytes long. */
- if (length < 3)
- return 0;
-
- /* Save the contents of the option in a buffer. */
- if (!buffer_allocate (&bp, length + 4, MDL)) {
- log_error ("no memory for option buffer.");
- return 0;
- }
- memcpy (&bp -> data [3], buffer + 1, length - 1);
-
- if (buffer [0] & 4) /* encoded */
- bp -> data [0] = 1;
- else
- bp -> data [0] = 0;
- if (!save_option_buffer (&fqdn_universe, options, bp,
- &bp -> data [0], 1,
- &fqdn_options [FQDN_ENCODED], 0)) {
- bad:
- buffer_dereference (&bp, MDL);
- return 0;
- }
-
- if (buffer [0] & 1) /* server-update */
- bp -> data [2] = 1;
- else
- bp -> data [2] = 0;
- if (buffer [0] & 2) /* no-client-update */
- bp -> data [1] = 1;
- else
- bp -> data [1] = 0;
-
- /* XXX Ideally we should store the name in DNS format, so if the
- XXX label isn't in DNS format, we convert it to DNS format,
- XXX rather than converting labels specified in DNS format to
- XXX the plain ASCII representation. But that's hard, so
- XXX not now. */
-
- /* Not encoded using DNS format? */
- if (!bp -> data [0]) {
- unsigned i;
-
- /* Some broken clients NUL-terminate this option. */
- if (buffer [length - 1] == 0) {
- --length;
- bp -> data [1] = 1;
- }
-
- /* Determine the length of the hostname component of the
- name. If the name contains no '.' character, it
- represents a non-qualified label. */
- for (i = 3; i < length && buffer [i] != '.'; i++);
- i -= 3;
-
- /* Note: If the client sends a FQDN, the first '.' will
- be used as a NUL terminator for the hostname. */
- if (i)
- if (!save_option_buffer (&fqdn_universe, options, bp,
- &bp -> data[5], i,
- &fqdn_options [FQDN_HOSTNAME],
- 0))
- goto bad;
- /* Note: If the client sends a single label, the
- FQDN_DOMAINNAME option won't be set. */
- if (length > 4 + i &&
- !save_option_buffer (&fqdn_universe, options, bp,
- &bp -> data[6 + i], length - 4 - i,
- &fqdn_options [FQDN_DOMAINNAME], 1))
- goto bad;
- /* Also save the whole name. */
- if (length > 3)
- if (!save_option_buffer (&fqdn_universe, options, bp,
- &bp -> data [5], length - 3,
- &fqdn_options [FQDN_FQDN], 1))
- goto bad;
- } else {
- unsigned len;
- unsigned total_len = 0;
- unsigned first_len = 0;
- int terminated = 0;
- unsigned char *s;
-
- s = &bp -> data[5];
-
- while (s < &bp -> data[0] + length + 2) {
- len = *s;
- if (len > 63) {
- log_info ("fancy bits in fqdn option");
- return 0;
- }
- if (len == 0) {
- terminated = 1;
- break;
- }
- if (s + len > &bp -> data [0] + length + 3) {
- log_info ("fqdn tag longer than buffer");
- return 0;
- }
-
- if (first_len == 0) {
- first_len = len;
- }
-
- *s = '.';
- s += len + 1;
- total_len += len + 1;
- }
-
- /* We wind up with a length that's one too many because
- we shouldn't increment for the last label, but there's
- no way to tell we're at the last label until we exit
- the loop. :'*/
- if (total_len > 0)
- total_len--;
-
- if (!terminated) {
- first_len = total_len;
- }
-
- if (first_len > 0 &&
- !save_option_buffer (&fqdn_universe, options, bp,
- &bp -> data[6], first_len,
- &fqdn_options [FQDN_HOSTNAME], 0))
- goto bad;
- if (total_len > 0 && first_len != total_len) {
- if (!save_option_buffer
- (&fqdn_universe, options, bp,
- &bp -> data[6 + first_len], total_len - first_len,
- &fqdn_options [FQDN_DOMAINNAME], 1))
- goto bad;
- }
- if (total_len > 0)
- if (!save_option_buffer (&fqdn_universe, options, bp,
- &bp -> data [6], total_len,
- &fqdn_options [FQDN_FQDN], 1))
- goto bad;
- }
-
- if (!save_option_buffer (&fqdn_universe, options, bp,
- &bp -> data [1], 1,
- &fqdn_options [FQDN_NO_CLIENT_UPDATE], 0))
- goto bad;
- if (!save_option_buffer (&fqdn_universe, options, bp,
- &bp -> data [2], 1,
- &fqdn_options [FQDN_SERVER_UPDATE], 0))
- goto bad;
-
- if (!save_option_buffer (&fqdn_universe, options, bp,
- &bp -> data [3], 1,
- &fqdn_options [FQDN_RCODE1], 0))
- goto bad;
- if (!save_option_buffer (&fqdn_universe, options, bp,
- &bp -> data [4], 1,
- &fqdn_options [FQDN_RCODE2], 0))
- goto bad;
-
- buffer_dereference (&bp, MDL);
- return 1;
-}
-
-/* cons options into a big buffer, and then split them out into the
- three seperate buffers if needed. This allows us to cons up a set
- of vendor options using the same routine. */
-
-int cons_options (inpacket, outpacket, lease, client_state,
- mms, in_options, cfg_options,
- scope, overload, terminate, bootpp, prl, vuname)
- struct packet *inpacket;
- struct dhcp_packet *outpacket;
- struct lease *lease;
- struct client_state *client_state;
- int mms;
- struct option_state *in_options;
- struct option_state *cfg_options;
- struct binding_scope **scope;
- int overload; /* Overload flags that may be set. */
- int terminate;
- int bootpp;
- struct data_string *prl;
- const char *vuname;
-{
-#define PRIORITY_COUNT 300
- unsigned priority_list [PRIORITY_COUNT];
- int priority_len;
- unsigned char buffer [4096]; /* Really big buffer... */
- unsigned main_buffer_size;
- unsigned mainbufix, bufix, agentix;
- unsigned option_size;
- unsigned length;
- int i;
- struct option_cache *op;
- struct data_string ds;
- pair pp, *hash;
- int need_endopt = 0;
- int have_sso = 0;
-
- memset (&ds, 0, sizeof ds);
-
- /* If there's a Maximum Message Size option in the incoming packet
- and no alternate maximum message size has been specified, take the
- one in the packet. */
-
- if (inpacket &&
- (op = lookup_option (&dhcp_universe, inpacket -> options,
- DHO_DHCP_MAX_MESSAGE_SIZE))) {
- evaluate_option_cache (&ds, inpacket,
- lease, client_state, in_options,
- cfg_options, scope, op, MDL);
- if (ds.len >= sizeof (u_int16_t)) {
- i = getUShort (ds.data);
-
- if(!mms || (i < mms))
- mms = i;
- }
- data_string_forget (&ds, MDL);
- }
-
- /* If the client has provided a maximum DHCP message size,
- use that; otherwise, if it's BOOTP, only 64 bytes; otherwise
- use up to the minimum IP MTU size (576 bytes). */
- /* XXX if a BOOTP client specifies a max message size, we will
- honor it. */
-
- if (mms) {
- main_buffer_size = mms - DHCP_FIXED_LEN;
-
- /* Enforce a minimum packet size... */
- if (main_buffer_size < (576 - DHCP_FIXED_LEN))
- main_buffer_size = 576 - DHCP_FIXED_LEN;
- } else if (bootpp) {
- if (inpacket) {
- main_buffer_size =
- inpacket -> packet_length - DHCP_FIXED_LEN;
- if (main_buffer_size < 64)
- main_buffer_size = 64;
- } else
- main_buffer_size = 64;
- } else
- main_buffer_size = 576 - DHCP_FIXED_LEN;
-
- /* Set a hard limit at the size of the output buffer. */
- if (main_buffer_size > sizeof buffer)
- main_buffer_size = sizeof buffer;
-
- /* Preload the option priority list with mandatory options. */
- priority_len = 0;
- priority_list [priority_len++] = DHO_DHCP_MESSAGE_TYPE;
- priority_list [priority_len++] = DHO_DHCP_SERVER_IDENTIFIER;
- priority_list [priority_len++] = DHO_DHCP_LEASE_TIME;
- priority_list [priority_len++] = DHO_DHCP_MESSAGE;
- priority_list [priority_len++] = DHO_DHCP_REQUESTED_ADDRESS;
- priority_list [priority_len++] = DHO_FQDN;
-
- if (prl && prl -> len > 0) {
- if ((op = lookup_option (&dhcp_universe, cfg_options,
- DHO_SUBNET_SELECTION))) {
- if (priority_len < PRIORITY_COUNT)
- priority_list [priority_len++] =
- DHO_SUBNET_SELECTION;
- }
-
- data_string_truncate (prl, (PRIORITY_COUNT - priority_len));
-
- for (i = 0; i < prl -> len; i++) {
- /* Prevent client from changing order of delivery
- of relay agent information option. */
- if (prl -> data [i] != DHO_DHCP_AGENT_OPTIONS)
- priority_list [priority_len++] =
- prl -> data [i];
- }
- } else {
- /* First, hardcode some more options that ought to be
- sent first... */
- priority_list [priority_len++] = DHO_SUBNET_MASK;
- priority_list [priority_len++] = DHO_ROUTERS;
- priority_list [priority_len++] = DHO_DOMAIN_NAME_SERVERS;
- priority_list [priority_len++] = DHO_HOST_NAME;
-
- /* Append a list of the standard DHCP options from the
- standard DHCP option space. Actually, if a site
- option space hasn't been specified, we wind up
- treating the dhcp option space as the site option
- space, and the first for loop is skipped, because
- it's slightly more general to do it this way,
- taking the 1Q99 DHCP futures work into account. */
- if (cfg_options -> site_code_min) {
- for (i = 0; i < OPTION_HASH_SIZE; i++) {
- hash = cfg_options -> universes [dhcp_universe.index];
- if (hash) {
- for (pp = hash [i]; pp; pp = pp -> cdr) {
- op = (struct option_cache *)(pp -> car);
- if (op -> option -> code <
- cfg_options -> site_code_min &&
- priority_len < PRIORITY_COUNT &&
- (op -> option -> code !=
- DHO_DHCP_AGENT_OPTIONS))
- priority_list [priority_len++] =
- op -> option -> code;
- }
- }
- }
- }
-
- /* Now cycle through the site option space, or if there
- is no site option space, we'll be cycling through the
- dhcp option space. */
- for (i = 0; i < OPTION_HASH_SIZE; i++) {
- hash = (cfg_options -> universes
- [cfg_options -> site_universe]);
- if (hash)
- for (pp = hash [i]; pp; pp = pp -> cdr) {
- op = (struct option_cache *)(pp -> car);
- if (op -> option -> code >=
- cfg_options -> site_code_min &&
- priority_len < PRIORITY_COUNT &&
- (op -> option -> code !=
- DHO_DHCP_AGENT_OPTIONS))
- priority_list [priority_len++] =
- op -> option -> code;
- }
- }
-
- /* Now go through all the universes for which options
- were set and see if there are encapsulations for
- them; if there are, put the encapsulation options
- on the priority list as well. */
- for (i = 0; i < cfg_options -> universe_count; i++) {
- if (cfg_options -> universes [i] &&
- universes [i] -> enc_opt &&
- priority_len < PRIORITY_COUNT &&
- universes [i] -> enc_opt -> universe == &dhcp_universe)
- {
- if (universes [i] -> enc_opt -> code !=
- DHO_DHCP_AGENT_OPTIONS)
- priority_list [priority_len++] =
- universes [i] -> enc_opt -> code;
- }
- }
-
- /* The vendor option space can't stand on its own, so always
- add it to the list. */
- if (priority_len < PRIORITY_COUNT)
- priority_list [priority_len++] =
- DHO_VENDOR_ENCAPSULATED_OPTIONS;
- }
-
- /* Copy the options into the big buffer... */
- option_size = store_options (buffer,
- (main_buffer_size - 7 +
- ((overload & 1) ? DHCP_FILE_LEN : 0) +
- ((overload & 2) ? DHCP_SNAME_LEN : 0)),
- inpacket, lease, client_state,
- in_options, cfg_options, scope,
- priority_list, priority_len,
- main_buffer_size,
- (main_buffer_size +
- ((overload & 1) ? DHCP_FILE_LEN : 0)),
- terminate, vuname);
-
- /* Put the cookie up front... */
- memcpy (outpacket -> options, DHCP_OPTIONS_COOKIE, 4);
- mainbufix = 4;
-
- /* If we're going to have to overload, store the overload
- option at the beginning. If we can, though, just store the
- whole thing in the packet's option buffer and leave it at
- that. */
- if (option_size <= main_buffer_size - mainbufix) {
- memcpy (&outpacket -> options [mainbufix],
- buffer, option_size);
- mainbufix += option_size;
- agentix = mainbufix;
- if (mainbufix < main_buffer_size)
- need_endopt = 1;
- length = DHCP_FIXED_NON_UDP + mainbufix;
- } else {
- outpacket -> options [mainbufix++] = DHO_DHCP_OPTION_OVERLOAD;
- outpacket -> options [mainbufix++] = 1;
- if (option_size > main_buffer_size - mainbufix + DHCP_FILE_LEN)
- outpacket -> options [mainbufix++] = 3;
- else
- outpacket -> options [mainbufix++] = 1;
-
- memcpy (&outpacket -> options [mainbufix],
- buffer, main_buffer_size - mainbufix);
- length = DHCP_FIXED_NON_UDP + main_buffer_size;
- agentix = main_buffer_size;
-
- bufix = main_buffer_size - mainbufix;
- if (overload & 1) {
- if (option_size - bufix <= DHCP_FILE_LEN) {
- memcpy (outpacket -> file,
- &buffer [bufix], option_size - bufix);
- mainbufix = option_size - bufix;
- if (mainbufix < DHCP_FILE_LEN)
- outpacket -> file [mainbufix++]
- = DHO_END;
- while (mainbufix < DHCP_FILE_LEN)
- outpacket -> file [mainbufix++]
- = DHO_PAD;
- } else {
- memcpy (outpacket -> file,
- &buffer [bufix], DHCP_FILE_LEN);
- bufix += DHCP_FILE_LEN;
- }
- }
- if ((overload & 2) && option_size < bufix) {
- memcpy (outpacket -> sname,
- &buffer [bufix], option_size - bufix);
-
- mainbufix = option_size - bufix;
- if (mainbufix < DHCP_SNAME_LEN)
- outpacket -> file [mainbufix++]
- = DHO_END;
- while (mainbufix < DHCP_SNAME_LEN)
- outpacket -> file [mainbufix++]
- = DHO_PAD;
- }
- }
-
- /* Now hack in the agent options if there are any. */
- priority_list [0] = DHO_DHCP_AGENT_OPTIONS;
- priority_len = 1;
- agentix +=
- store_options (&outpacket -> options [agentix],
- 1500 - DHCP_FIXED_LEN - agentix,
- inpacket, lease, client_state,
- in_options, cfg_options, scope,
- priority_list, priority_len,
- 1500 - DHCP_FIXED_LEN - agentix,
- 1500 - DHCP_FIXED_LEN - agentix, 0, (char *)0);
-
- /* Tack a DHO_END option onto the packet if we need to. */
- if (agentix < 1500 - DHCP_FIXED_LEN && need_endopt)
- outpacket -> options [agentix++] = DHO_END;
-
- /* Figure out the length. */
- length = DHCP_FIXED_NON_UDP + agentix;
- return length;
-}
-
-/* Store all the requested options into the requested buffer. */
-
-int store_options (buffer, buflen, packet, lease, client_state,
- in_options, cfg_options, scope, priority_list, priority_len,
- first_cutoff, second_cutoff, terminate, vuname)
- unsigned char *buffer;
- unsigned buflen;
- struct packet *packet;
- struct lease *lease;
- struct client_state *client_state;
- struct option_state *in_options;
- struct option_state *cfg_options;
- struct binding_scope **scope;
- unsigned *priority_list;
- int priority_len;
- unsigned first_cutoff, second_cutoff;
- int terminate;
- const char *vuname;
-{
- int bufix = 0;
- int i;
- int ix;
- int tto;
- struct data_string od;
- struct option_cache *oc;
- unsigned code;
- int optstart;
-
- memset (&od, 0, sizeof od);
-
- /* Eliminate duplicate options in the parameter request list.
- There's got to be some clever knuthian way to do this:
- Eliminate all but the first occurance of a value in an array
- of values without otherwise disturbing the order of the array. */
- for (i = 0; i < priority_len - 1; i++) {
- tto = 0;
- for (ix = i + 1; ix < priority_len + tto; ix++) {
- if (tto)
- priority_list [ix - tto] =
- priority_list [ix];
- if (priority_list [i] == priority_list [ix]) {
- tto++;
- priority_len--;
- }
- }
- }
-
- /* Copy out the options in the order that they appear in the
- priority list... */
- for (i = 0; i < priority_len; i++) {
- /* Number of bytes left to store (some may already
- have been stored by a previous pass). */
- unsigned length;
- int optstart;
- struct universe *u;
- int have_encapsulation = 0;
- struct data_string encapsulation;
-
- memset (&encapsulation, 0, sizeof encapsulation);
-
- /* Code for next option to try to store. */
- code = priority_list [i];
-
- /* Look up the option in the site option space if the code
- is above the cutoff, otherwise in the DHCP option space. */
- if (code >= cfg_options -> site_code_min)
- u = universes [cfg_options -> site_universe];
- else
- u = &dhcp_universe;
-
- oc = lookup_option (u, cfg_options, code);
-
- /* It's an encapsulation, try to find the universe
- to be encapsulated first, except that if it's a straight
- encapsulation and the user has provided a value for the
- encapsulation option, use the user-provided value. */
- if (u -> options [code] &&
- ((u -> options [code] -> format [0] == 'E' && !oc) ||
- u -> options [code] -> format [0] == 'e')) {
- int uix;
- static char *s, *t;
- struct option_cache *tmp;
- struct data_string name;
-
- s = strchr (u -> options [code] -> format, 'E');
- if (s)
- t = strchr (++s, '.');
- if (s && t) {
- memset (&name, 0, sizeof name);
-
- /* A zero-length universe name means the vendor
- option space, if one is defined. */
- if (t == s) {
- if (vendor_cfg_option) {
- tmp = lookup_option (vendor_cfg_option -> universe,
- cfg_options,
- vendor_cfg_option -> code);
- if (tmp)
- evaluate_option_cache (&name, packet, lease,
- client_state,
- in_options,
- cfg_options,
- scope, tmp, MDL);
- } else if (vuname) {
- name.data = (unsigned char *)s;
- name.len = strlen (s);
- }
- } else {
- name.data = (unsigned char *)s;
- name.len = t - s;
- }
-
- /* If we found a universe, and there are options configured
- for that universe, try to encapsulate it. */
- if (name.len) {
- have_encapsulation =
- (option_space_encapsulate
- (&encapsulation, packet, lease, client_state,
- in_options, cfg_options, scope, &name));
- data_string_forget (&name, MDL);
- }
- }
- }
-
- /* In order to avoid memory leaks, we have to get to here
- with any option cache that we allocated in tmp not being
- referenced by tmp, and whatever option cache is referenced
- by oc being an actual reference. lookup_option doesn't
- generate a reference (this needs to be fixed), so the
- preceding goop ensures that if we *didn't* generate a new
- option cache, oc still winds up holding an actual reference. */
-
- /* If no data is available for this option, skip it. */
- if (!oc && !have_encapsulation) {
- continue;
- }
-
- /* Find the value of the option... */
- if (oc) {
- evaluate_option_cache (&od, packet,
- lease, client_state, in_options,
- cfg_options, scope, oc, MDL);
- if (!od.len) {
- data_string_forget (&encapsulation, MDL);
- data_string_forget (&od, MDL);
- have_encapsulation = 0;
- continue;
- }
- }
-
- /* We should now have a constant length for the option. */
- length = od.len;
- if (have_encapsulation) {
- length += encapsulation.len;
- if (!od.len) {
- data_string_copy (&od, &encapsulation, MDL);
- data_string_forget (&encapsulation, MDL);
- } else {
- struct buffer *bp = (struct buffer *)0;
- if (!buffer_allocate (&bp, length, MDL)) {
- option_cache_dereference (&oc, MDL);
- data_string_forget (&od, MDL);
- data_string_forget (&encapsulation, MDL);
- continue;
- }
- memcpy (&bp -> data [0], od.data, od.len);
- memcpy (&bp -> data [od.len], encapsulation.data,
- encapsulation.len);
- data_string_forget (&od, MDL);
- data_string_forget (&encapsulation, MDL);
- od.data = &bp -> data [0];
- buffer_reference (&od.buffer, bp, MDL);
- buffer_dereference (&bp, MDL);
- od.len = length;
- od.terminated = 0;
- }
- }
-
- /* Do we add a NUL? */
- if (terminate && dhcp_options [code].format [0] == 't') {
- length++;
- tto = 1;
- } else {
- tto = 0;
- }
-
- /* Try to store the option. */
-
- /* If the option's length is more than 255, we must store it
- in multiple hunks. Store 255-byte hunks first. However,
- in any case, if the option data will cross a buffer
- boundary, split it across that boundary. */
-
- ix = 0;
- optstart = bufix;
- while (length) {
- unsigned char incr = length > 255 ? 255 : length;
- int consumed = 0;
-
- /* If this hunk of the buffer will cross a
- boundary, only go up to the boundary in this
- pass. */
- if (bufix < first_cutoff &&
- bufix + incr > first_cutoff)
- incr = first_cutoff - bufix;
- else if (bufix < second_cutoff &&
- bufix + incr > second_cutoff)
- incr = second_cutoff - bufix;
-
- /* If this option is going to overflow the buffer,
- skip it. */
- if (bufix + 2 + incr > buflen) {
- bufix = optstart;
- break;
- }
-
- /* Everything looks good - copy it in! */
- buffer [bufix] = code;
- buffer [bufix + 1] = incr;
- if (tto && incr == length) {
- memcpy (buffer + bufix + 2,
- od.data + ix, (unsigned)(incr - 1));
- buffer [bufix + 2 + incr - 1] = 0;
- } else {
- memcpy (buffer + bufix + 2,
- od.data + ix, (unsigned)incr);
- }
- length -= incr;
- ix += incr;
- bufix += 2 + incr;
- }
- data_string_forget (&od, MDL);
- }
-
- return bufix;
-}
-
-/* Format the specified option so that a human can easily read it. */
-
-const char *pretty_print_option (option, data, len, emit_commas, emit_quotes)
- struct option *option;
- const unsigned char *data;
- unsigned len;
- int emit_commas;
- int emit_quotes;
-{
- static char optbuf [32768]; /* XXX */
- int hunksize = 0;
- int opthunk = 0;
- int hunkinc = 0;
- int numhunk = -1;
- int numelem = 0;
- char fmtbuf [32];
- struct enumeration *enumbuf [32];
- int i, j, k, l;
- char *op = optbuf;
- const unsigned char *dp = data;
- struct in_addr foo;
- char comma;
- unsigned long tval;
-
- if (emit_commas)
- comma = ',';
- else
- comma = ' ';
-
- memset (enumbuf, 0, sizeof enumbuf);
-
- /* Figure out the size of the data. */
- for (l = i = 0; option -> format [i]; i++, l++) {
- if (!numhunk) {
- log_error ("%s: Extra codes in format string: %s",
- option -> name,
- &(option -> format [i]));
- break;
- }
- numelem++;
- fmtbuf [l] = option -> format [i];
- switch (option -> format [i]) {
- case 'a':
- --numelem;
- fmtbuf [l] = 0;
- numhunk = 0;
- break;
- case 'A':
- --numelem;
- fmtbuf [l] = 0;
- numhunk = 0;
- break;
- case 'E':
- /* Skip the universe name. */
- while (option -> format [i] &&
- option -> format [i] != '.')
- i++;
- case 'X':
- for (k = 0; k < len; k++) {
- if (!isascii (data [k]) ||
- !isprint (data [k]))
- break;
- }
- /* If we found no bogus characters, or the bogus
- character we found is a trailing NUL, it's
- okay to print this option as text. */
- if (k == len || (k + 1 == len && data [k] == 0)) {
- fmtbuf [l] = 't';
- numhunk = -2;
- } else {
- fmtbuf [l] = 'x';
- hunksize++;
- comma = ':';
- numhunk = 0;
- }
- fmtbuf [l + 1] = 0;
- break;
- case 'd':
- case 't':
- fmtbuf [l] = 't';
- fmtbuf [l + 1] = 0;
- numhunk = -2;
- break;
- case 'N':
- k = i;
- while (option -> format [i] &&
- option -> format [i] != '.')
- i++;
- enumbuf [l] =
- find_enumeration (&option -> format [k] + 1,
- i - k - 1);
- hunksize += 1;
- hunkinc = 1;
- break;
- case 'I':
- case 'l':
- case 'L':
- case 'T':
- hunksize += 4;
- hunkinc = 4;
- break;
- case 's':
- case 'S':
- hunksize += 2;
- hunkinc = 2;
- break;
- case 'b':
- case 'B':
- case 'f':
- hunksize++;
- hunkinc = 1;
- break;
- case 'e':
- break;
- case 'o':
- opthunk += hunkinc;
- break;
- default:
- log_error ("%s: garbage in format string: %s",
- option -> name,
- &(option -> format [i]));
- break;
- }
- }
-
- /* Check for too few bytes... */
- if (hunksize - opthunk > len) {
- log_error ("%s: expecting at least %d bytes; got %d",
- option -> name,
- hunksize, len);
- return "<error>";
- }
- /* Check for too many bytes... */
- if (numhunk == -1 && hunksize < len)
- log_error ("%s: %d extra bytes",
- option -> name,
- len - hunksize);
-
- /* If this is an array, compute its size. */
- if (!numhunk)
- numhunk = len / hunksize;
- /* See if we got an exact number of hunks. */
- if (numhunk > 0 && numhunk * hunksize < len)
- log_error ("%s: %d extra bytes at end of array\n",
- option -> name,
- len - numhunk * hunksize);
-
- /* A one-hunk array prints the same as a single hunk. */
- if (numhunk < 0)
- numhunk = 1;
-
- /* Cycle through the array (or hunk) printing the data. */
- for (i = 0; i < numhunk; i++) {
- for (j = 0; j < numelem; j++) {
- switch (fmtbuf [j]) {
- case 't':
- if (emit_quotes)
- *op++ = '"';
- for (; dp < data + len; dp++) {
- if (!isascii (*dp) ||
- !isprint (*dp)) {
- /* Skip trailing NUL. */
- if (dp + 1 != data + len ||
- *dp != 0) {
- sprintf (op, "\\%03o",
- *dp);
- op += 4;
- }
- } else if (*dp == '"' ||
- *dp == '\'' ||
- *dp == '$' ||
- *dp == '`' ||
- *dp == '\\') {
- *op++ = '\\';
- *op++ = *dp;
- } else
- *op++ = *dp;
- }
- if (emit_quotes)
- *op++ = '"';
- *op = 0;
- break;
- /* pretty-printing an array of enums is
- going to get ugly. */
- case 'N':
- if (!enumbuf [j])
- goto enum_as_num;
- for (i = 0; ;i++) {
- if (!enumbuf [j] -> values [i].name)
- goto enum_as_num;
- if (enumbuf [j] -> values [i].value ==
- *dp)
- break;
- }
- strcpy (op, enumbuf [j] -> values [i].name);
- op += strlen (op);
- break;
- case 'I':
- foo.s_addr = htonl (getULong (dp));
- strcpy (op, inet_ntoa (foo));
- dp += 4;
- break;
- case 'l':
- sprintf (op, "%ld", (long)getLong (dp));
- dp += 4;
- break;
- case 'T':
- tval = getULong (dp);
- if (tval == -1)
- sprintf (op, "%s", "infinite");
- else
- sprintf (op, "%ld", tval);
- break;
- case 'L':
- sprintf (op, "%ld",
- (unsigned long)getULong (dp));
- dp += 4;
- break;
- case 's':
- sprintf (op, "%d", (int)getShort (dp));
- dp += 2;
- break;
- case 'S':
- sprintf (op, "%d", (unsigned)getUShort (dp));
- dp += 2;
- break;
- case 'b':
- sprintf (op, "%d", *(const char *)dp++);
- break;
- case 'B':
- enum_as_num:
- sprintf (op, "%d", *dp++);
- break;
- case 'x':
- sprintf (op, "%x", *dp++);
- break;
- case 'f':
- strcpy (op, *dp++ ? "true" : "false");
- break;
- default:
- log_error ("Unexpected format code %c",
- fmtbuf [j]);
- }
- op += strlen (op);
- if (dp == data + len)
- break;
- if (j + 1 < numelem && comma != ':')
- *op++ = ' ';
- }
- if (i + 1 < numhunk) {
- *op++ = comma;
- }
- if (dp == data + len)
- break;
- }
- return optbuf;
-}
-
-int get_option (result, universe, packet, lease, client_state,
- in_options, cfg_options, options, scope, code, file, line)
- struct data_string *result;
- struct universe *universe;
- struct packet *packet;
- struct lease *lease;
- struct client_state *client_state;
- struct option_state *in_options;
- struct option_state *cfg_options;
- struct option_state *options;
- struct binding_scope **scope;
- unsigned code;
- const char *file;
- int line;
-{
- struct option_cache *oc;
-
- if (!universe -> lookup_func)
- return 0;
- oc = ((*universe -> lookup_func) (universe, options, code));
- if (!oc)
- return 0;
- if (!evaluate_option_cache (result, packet, lease, client_state,
- in_options, cfg_options, scope, oc,
- file, line))
- return 0;
- return 1;
-}
-
-void set_option (universe, options, option, op)
- struct universe *universe;
- struct option_state *options;
- struct option_cache *option;
- enum statement_op op;
-{
- struct option_cache *oc, *noc;
-
- switch (op) {
- case if_statement:
- case add_statement:
- case eval_statement:
- case break_statement:
- default:
- log_error ("bogus statement type in do_option_set.");
- break;
-
- case default_option_statement:
- oc = lookup_option (universe, options,
- option -> option -> code);
- if (oc)
- break;
- save_option (universe, options, option);
- break;
-
- case supersede_option_statement:
- case send_option_statement:
- /* Install the option, replacing any existing version. */
- save_option (universe, options, option);
- break;
-
- case append_option_statement:
- case prepend_option_statement:
- oc = lookup_option (universe, options,
- option -> option -> code);
- if (!oc) {
- save_option (universe, options, option);
- break;
- }
- /* If it's not an expression, make it into one. */
- if (!oc -> expression && oc -> data.len) {
- if (!expression_allocate (&oc -> expression, MDL)) {
- log_error ("Can't allocate const expression.");
- break;
- }
- oc -> expression -> op = expr_const_data;
- data_string_copy
- (&oc -> expression -> data.const_data,
- &oc -> data, MDL);
- data_string_forget (&oc -> data, MDL);
- }
- noc = (struct option_cache *)0;
- if (!option_cache_allocate (&noc, MDL))
- break;
- if (op == append_option_statement) {
- if (!make_concat (&noc -> expression,
- oc -> expression,
- option -> expression)) {
- option_cache_dereference (&noc, MDL);
- break;
- }
- } else {
- if (!make_concat (&noc -> expression,
- option -> expression,
- oc -> expression)) {
- option_cache_dereference (&noc, MDL);
- break;
- }
- }
- noc -> option = oc -> option;
- save_option (universe, options, noc);
- option_cache_dereference (&noc, MDL);
- break;
- }
-}
-
-struct option_cache *lookup_option (universe, options, code)
- struct universe *universe;
- struct option_state *options;
- unsigned code;
-{
- if (!options)
- return (struct option_cache *)0;
- if (universe -> lookup_func)
- return (*universe -> lookup_func) (universe, options, code);
- else
- log_error ("can't look up options in %s space.",
- universe -> name);
- return (struct option_cache *)0;
-}
-
-struct option_cache *lookup_hashed_option (universe, options, code)
- struct universe *universe;
- struct option_state *options;
- unsigned code;
-{
- int hashix;
- pair bptr;
- pair *hash;
-
- /* Make sure there's a hash table. */
- if (universe -> index >= options -> universe_count ||
- !(options -> universes [universe -> index]))
- return (struct option_cache *)0;
-
- hash = options -> universes [universe -> index];
-
- hashix = compute_option_hash (code);
- for (bptr = hash [hashix]; bptr; bptr = bptr -> cdr) {
- if (((struct option_cache *)(bptr -> car)) -> option -> code ==
- code)
- return (struct option_cache *)(bptr -> car);
- }
- return (struct option_cache *)0;
-}
-
-int save_option_buffer (struct universe *universe,
- struct option_state *options,
- struct buffer *bp,
- unsigned char *buffer, unsigned length,
- struct option *option, int tp)
-{
- struct buffer *lbp = (struct buffer *)0;
- struct option_cache *op = (struct option_cache *)0;
-
- if (!option_cache_allocate (&op, MDL)) {
- log_error ("No memory for option %s.%s.",
- universe -> name,
- option -> name);
- return 0;
- }
-
- /* If we weren't passed a buffer in which the data are saved and
- refcounted, allocate one now. */
- if (!bp) {
- if (!buffer_allocate (&lbp, length, MDL)) {
- log_error ("no memory for option buffer.");
-
- option_cache_dereference (&op, MDL);
- return 0;
- }
- memcpy (lbp -> data, buffer, length + tp);
- bp = lbp;
- buffer = &bp -> data [0]; /* Refer to saved buffer. */
- }
-
- /* Reference buffer copy to option cache. */
- op -> data.buffer = (struct buffer *)0;
- buffer_reference (&op -> data.buffer, bp, MDL);
-
- /* Point option cache into buffer. */
- op -> data.data = buffer;
- op -> data.len = length;
-
- if (tp) {
- /* NUL terminate (we can get away with this because we (or
- the caller!) allocated one more than the buffer size, and
- because the byte following the end of an option is always
- the code of the next option, which the caller is getting
- out of the *original* buffer. */
- buffer [length] = 0;
- op -> data.terminated = 1;
- } else
- op -> data.terminated = 0;
-
- op -> option = option;
-
- /* Now store the option. */
- save_option (universe, options, op);
-
- /* And let go of our reference. */
- option_cache_dereference (&op, MDL);
-
- return 1;
-}
-
-void save_option (struct universe *universe,
- struct option_state *options, struct option_cache *oc)
-{
- if (universe -> save_func)
- (*universe -> save_func) (universe, options, oc);
- else
- log_error ("can't store options in %s space.",
- universe -> name);
-}
-
-void save_hashed_option (universe, options, oc)
- struct universe *universe;
- struct option_state *options;
- struct option_cache *oc;
-{
- int hashix;
- pair bptr;
- pair *hash = options -> universes [universe -> index];
-
- if (oc -> refcnt == 0)
- abort ();
-
- /* Compute the hash. */
- hashix = compute_option_hash (oc -> option -> code);
-
- /* If there's no hash table, make one. */
- if (!hash) {
- hash = (pair *)dmalloc (OPTION_HASH_SIZE * sizeof *hash, MDL);
- if (!hash) {
- log_error ("no memory to store %s.%s",
- universe -> name, oc -> option -> name);
- return;
- }
- memset (hash, 0, OPTION_HASH_SIZE * sizeof *hash);
- options -> universes [universe -> index] = (VOIDPTR)hash;
- } else {
- /* Try to find an existing option matching the new one. */
- for (bptr = hash [hashix]; bptr; bptr = bptr -> cdr) {
- if (((struct option_cache *)
- (bptr -> car)) -> option -> code ==
- oc -> option -> code)
- break;
- }
-
- /* If we find one, dereference it and put the new one
- in its place. */
- if (bptr) {
- option_cache_dereference
- ((struct option_cache **)&bptr -> car, MDL);
- option_cache_reference
- ((struct option_cache **)&bptr -> car,
- oc, MDL);
- return;
- }
- }
-
- /* Otherwise, just put the new one at the head of the list. */
- bptr = new_pair (MDL);
- if (!bptr) {
- log_error ("No memory for option_cache reference.");
- return;
- }
- bptr -> cdr = hash [hashix];
- bptr -> car = 0;
- option_cache_reference ((struct option_cache **)&bptr -> car, oc, MDL);
- hash [hashix] = bptr;
-}
-
-void delete_option (universe, options, code)
- struct universe *universe;
- struct option_state *options;
- int code;
-{
- if (universe -> delete_func)
- (*universe -> delete_func) (universe, options, code);
- else
- log_error ("can't delete options from %s space.",
- universe -> name);
-}
-
-void delete_hashed_option (universe, options, code)
- struct universe *universe;
- struct option_state *options;
- int code;
-{
- int hashix;
- pair bptr, prev = (pair)0;
- pair *hash = options -> universes [universe -> index];
-
- /* There may not be any options in this space. */
- if (!hash)
- return;
-
- /* Try to find an existing option matching the new one. */
- hashix = compute_option_hash (code);
- for (bptr = hash [hashix]; bptr; bptr = bptr -> cdr) {
- if (((struct option_cache *)(bptr -> car)) -> option -> code
- == code)
- break;
- prev = bptr;
- }
- /* If we found one, wipe it out... */
- if (bptr) {
- if (prev)
- prev -> cdr = bptr -> cdr;
- else
- hash [hashix] = bptr -> cdr;
- option_cache_dereference
- ((struct option_cache **)(&bptr -> car), MDL);
- free_pair (bptr, MDL);
- }
-}
-
-extern struct option_cache *free_option_caches; /* XXX */
-
-int option_cache_dereference (ptr, file, line)
- struct option_cache **ptr;
- const char *file;
- int line;
-{
- if (!ptr || !*ptr) {
- log_error ("Null pointer in option_cache_dereference: %s(%d)",
- file, line);
-#if defined (POINTER_DEBUG)
- abort ();
-#else
- return 0;
-#endif
- }
-
- (*ptr) -> refcnt--;
- rc_register (file, line, ptr, *ptr, (*ptr) -> refcnt, 1, RC_MISC);
- if (!(*ptr) -> refcnt) {
- if ((*ptr) -> data.buffer)
- data_string_forget (&(*ptr) -> data, file, line);
- if ((*ptr) -> expression)
- expression_dereference (&(*ptr) -> expression,
- file, line);
- if ((*ptr) -> next)
- option_cache_dereference (&((*ptr) -> next),
- file, line);
- /* Put it back on the free list... */
- (*ptr) -> expression = (struct expression *)free_option_caches;
- free_option_caches = *ptr;
- dmalloc_reuse (free_option_caches, (char *)0, 0, 0);
- }
- if ((*ptr) -> refcnt < 0) {
- log_error ("%s(%d): negative refcnt!", file, line);
-#if defined (DEBUG_RC_HISTORY)
- dump_rc_history (*ptr);
-#endif
-#if defined (POINTER_DEBUG)
- abort ();
-#else
- *ptr = (struct option_cache *)0;
- return 0;
-#endif
- }
- *ptr = (struct option_cache *)0;
- return 1;
-
-}
-
-int hashed_option_state_dereference (universe, state, file, line)
- struct universe *universe;
- struct option_state *state;
- const char *file;
- int line;
-{
- pair *heads;
- pair cp, next;
- int i;
-
- /* Get the pointer to the array of hash table bucket heads. */
- heads = (pair *)(state -> universes [universe -> index]);
- if (!heads)
- return 0;
-
- /* For each non-null head, loop through all the buckets dereferencing
- the attached option cache structures and freeing the buckets. */
- for (i = 0; i < OPTION_HASH_SIZE; i++) {
- for (cp = heads [i]; cp; cp = next) {
- next = cp -> cdr;
- option_cache_dereference
- ((struct option_cache **)&cp -> car,
- file, line);
- free_pair (cp, file, line);
- }
- }
-
- dfree (heads, file, line);
- state -> universes [universe -> index] = (void *)0;
- return 1;
-}
-
-int store_option (result, universe, packet, lease, client_state,
- in_options, cfg_options, scope, oc)
- struct data_string *result;
- struct universe *universe;
- struct packet *packet;
- struct lease *lease;
- struct client_state *client_state;
- struct option_state *in_options;
- struct option_state *cfg_options;
- struct binding_scope **scope;
- struct option_cache *oc;
-{
- struct data_string d1, d2;
-
- memset (&d1, 0, sizeof d1);
- memset (&d2, 0, sizeof d2);
-
- if (evaluate_option_cache (&d2, packet, lease, client_state,
- in_options, cfg_options, scope, oc, MDL)) {
- if (!buffer_allocate (&d1.buffer,
- (result -> len +
- universe -> length_size +
- universe -> tag_size + d2.len), MDL)) {
- data_string_forget (result, MDL);
- data_string_forget (&d2, MDL);
- return 0;
- }
- d1.data = &d1.buffer -> data [0];
- if (result -> len)
- memcpy (d1.buffer -> data,
- result -> data, result -> len);
- d1.len = result -> len;
- (*universe -> store_tag) (&d1.buffer -> data [d1.len],
- oc -> option -> code);
- d1.len += universe -> tag_size;
- (*universe -> store_length) (&d1.buffer -> data [d1.len],
- d2.len);
- d1.len += universe -> length_size;
- memcpy (&d1.buffer -> data [d1.len], d2.data, d2.len);
- d1.len += d2.len;
- data_string_forget (&d2, MDL);
- data_string_forget (result, MDL);
- data_string_copy (result, &d1, MDL);
- data_string_forget (&d1, MDL);
- return 1;
- }
- return 0;
-}
-
-int option_space_encapsulate (result, packet, lease, client_state,
- in_options, cfg_options, scope, name)
- struct data_string *result;
- struct packet *packet;
- struct lease *lease;
- struct client_state *client_state;
- struct option_state *in_options;
- struct option_state *cfg_options;
- struct binding_scope **scope;
- struct data_string *name;
-{
- struct universe *u;
-
- u = (struct universe *)0;
- universe_hash_lookup (&u, universe_hash,
- (const char *)name -> data, name -> len, MDL);
- if (!u)
- return 0;
-
- if (u -> encapsulate)
- return (*u -> encapsulate) (result, packet, lease,
- client_state,
- in_options, cfg_options, scope, u);
- log_error ("encapsulation requested for %s with no support.",
- name -> data);
- return 0;
-}
-
-int hashed_option_space_encapsulate (result, packet, lease, client_state,
- in_options, cfg_options, scope, universe)
- struct data_string *result;
- struct packet *packet;
- struct lease *lease;
- struct client_state *client_state;
- struct option_state *in_options;
- struct option_state *cfg_options;
- struct binding_scope **scope;
- struct universe *universe;
-{
- pair p, *hash;
- int status;
- int i;
-
- if (universe -> index >= cfg_options -> universe_count)
- return 0;
-
- hash = cfg_options -> universes [universe -> index];
- if (!hash)
- return 0;
-
- status = 0;
- for (i = 0; i < OPTION_HASH_SIZE; i++) {
- for (p = hash [i]; p; p = p -> cdr) {
- if (store_option (result, universe, packet,
- lease, client_state, in_options,
- cfg_options, scope,
- (struct option_cache *)p -> car))
- status = 1;
- }
- }
-
- return status;
-}
-
-int nwip_option_space_encapsulate (result, packet, lease, client_state,
- in_options, cfg_options, scope, universe)
- struct data_string *result;
- struct packet *packet;
- struct lease *lease;
- struct client_state *client_state;
- struct option_state *in_options;
- struct option_state *cfg_options;
- struct binding_scope **scope;
- struct universe *universe;
-{
- pair ocp;
- int status;
- int i;
- static struct option_cache *no_nwip;
- struct data_string ds;
- struct option_chain_head *head;
-
- if (universe -> index >= cfg_options -> universe_count)
- return 0;
- head = ((struct option_chain_head *)
- cfg_options -> universes [fqdn_universe.index]);
- if (!head)
- return 0;
-
- status = 0;
- for (ocp = head -> first; ocp; ocp = ocp -> cdr) {
- struct option_cache *oc = (struct option_cache *)(ocp -> car);
- if (store_option (result, universe, packet,
- lease, client_state, in_options,
- cfg_options, scope,
- (struct option_cache *)ocp -> car))
- status = 1;
- }
-
- /* If there's no data, the nwip suboption is supposed to contain
- a suboption saying there's no data. */
- if (!status) {
- if (!no_nwip) {
- static unsigned char nni [] = { 1, 0 };
- memset (&ds, 0, sizeof ds);
- ds.data = nni;
- ds.len = 2;
- if (option_cache_allocate (&no_nwip, MDL))
- data_string_copy (&no_nwip -> data, &ds, MDL);
- no_nwip -> option = nwip_universe.options [1];
- }
- if (no_nwip) {
- if (store_option (result, universe, packet, lease,
- client_state, in_options,
- cfg_options, scope, no_nwip))
- status = 1;
- }
- } else {
- memset (&ds, 0, sizeof ds);
-
- /* If we have nwip options, the first one has to be the
- nwip-exists-in-option-area option. */
- if (!buffer_allocate (&ds.buffer, result -> len + 2, MDL)) {
- data_string_forget (result, MDL);
- return 0;
- }
- ds.data = &ds.buffer -> data [0];
- ds.buffer -> data [0] = 2;
- ds.buffer -> data [1] = 0;
- memcpy (&ds.buffer -> data [2], result -> data, result -> len);
- data_string_forget (result, MDL);
- data_string_copy (result, &ds, MDL);
- data_string_forget (&ds, MDL);
- }
-
- return status;
-}
-
-int fqdn_option_space_encapsulate (result, packet, lease, client_state,
- in_options, cfg_options, scope, universe)
- struct data_string *result;
- struct packet *packet;
- struct lease *lease;
- struct client_state *client_state;
- struct option_state *in_options;
- struct option_state *cfg_options;
- struct binding_scope **scope;
- struct universe *universe;
-{
- pair ocp;
- struct data_string results [FQDN_SUBOPTION_COUNT + 1];
- unsigned i;
- unsigned len;
- struct buffer *bp = (struct buffer *)0;
- struct option_chain_head *head;
-
- /* If there's no FQDN universe, don't encapsulate. */
- if (fqdn_universe.index >= cfg_options -> universe_count)
- return 0;
- head = ((struct option_chain_head *)
- cfg_options -> universes [fqdn_universe.index]);
- if (!head)
- return 0;
-
- /* Figure out the values of all the suboptions. */
- memset (results, 0, sizeof results);
- for (ocp = head -> first; ocp; ocp = ocp -> cdr) {
- struct option_cache *oc = (struct option_cache *)(ocp -> car);
- if (oc -> option -> code > FQDN_SUBOPTION_COUNT)
- continue;
- evaluate_option_cache (&results [oc -> option -> code],
- packet, lease, client_state, in_options,
- cfg_options, scope, oc, MDL);
- }
- len = 4 + results [FQDN_FQDN].len;
- /* Save the contents of the option in a buffer. */
- if (!buffer_allocate (&bp, len, MDL)) {
- log_error ("no memory for option buffer.");
- return 0;
- }
- buffer_reference (&result -> buffer, bp, MDL);
- result -> len = 3;
- result -> data = &bp -> data [0];
-
- memset (&bp -> data [0], 0, len);
- if (results [FQDN_NO_CLIENT_UPDATE].len &&
- results [FQDN_NO_CLIENT_UPDATE].data [0])
- bp -> data [0] |= 2;
- if (results [FQDN_SERVER_UPDATE].len &&
- results [FQDN_SERVER_UPDATE].data [0])
- bp -> data [0] |= 1;
- if (results [FQDN_RCODE1].len)
- bp -> data [1] = results [FQDN_RCODE1].data [0];
- if (results [FQDN_RCODE2].len)
- bp -> data [2] = results [FQDN_RCODE2].data [0];
-
- if (results [FQDN_ENCODED].len &&
- results [FQDN_ENCODED].data [0]) {
- unsigned char *out;
- int i;
- bp -> data [0] |= 4;
- out = &bp -> data [3];
- if (results [FQDN_FQDN].len) {
- i = 0;
- while (i < results [FQDN_FQDN].len) {
- int j;
- for (j = i; ('.' !=
- results [FQDN_FQDN].data [j]) &&
- j < results [FQDN_FQDN].len; j++)
- ;
- *out++ = j - i;
- memcpy (out, &results [FQDN_FQDN].data [i],
- (unsigned)(j - i));
- out += j - i;
- i = j;
- if (results [FQDN_FQDN].data [j] == '.')
- i++;
- }
- if ((results [FQDN_FQDN].data
- [results [FQDN_FQDN].len - 1] == '.'))
- *out++ = 0;
- result -> len = out - result -> data;
- result -> terminated = 0;
- }
- } else {
- if (results [FQDN_FQDN].len) {
- memcpy (&bp -> data [3], results [FQDN_FQDN].data,
- results [FQDN_FQDN].len);
- result -> len += results [FQDN_FQDN].len;
- result -> terminated = 0;
- }
- }
- for (i = 1; i <= FQDN_SUBOPTION_COUNT; i++) {
- if (results [i].len)
- data_string_forget (&results [i], MDL);
- }
- buffer_dereference (&bp, MDL);
- return 1;
-}
-
-void option_space_foreach (struct packet *packet, struct lease *lease,
- struct client_state *client_state,
- struct option_state *in_options,
- struct option_state *cfg_options,
- struct binding_scope **scope,
- struct universe *u, void *stuff,
- void (*func) (struct option_cache *,
- struct packet *,
- struct lease *, struct client_state *,
- struct option_state *,
- struct option_state *,
- struct binding_scope **,
- struct universe *, void *))
-{
- if (u -> foreach)
- (*u -> foreach) (packet, lease, client_state, in_options,
- cfg_options, scope, u, stuff, func);
-}
-
-void suboption_foreach (struct packet *packet, struct lease *lease,
- struct client_state *client_state,
- struct option_state *in_options,
- struct option_state *cfg_options,
- struct binding_scope **scope,
- struct universe *u, void *stuff,
- void (*func) (struct option_cache *,
- struct packet *,
- struct lease *, struct client_state *,
- struct option_state *,
- struct option_state *,
- struct binding_scope **,
- struct universe *, void *),
- struct option_cache *oc,
- const char *vsname)
-{
- struct universe *universe = find_option_universe (oc -> option,
- vsname);
- int i;
-
- if (universe -> foreach)
- (*universe -> foreach) (packet, lease, client_state,
- in_options, cfg_options,
- scope, universe, stuff, func);
-}
-
-void hashed_option_space_foreach (struct packet *packet, struct lease *lease,
- struct client_state *client_state,
- struct option_state *in_options,
- struct option_state *cfg_options,
- struct binding_scope **scope,
- struct universe *u, void *stuff,
- void (*func) (struct option_cache *,
- struct packet *,
- struct lease *,
- struct client_state *,
- struct option_state *,
- struct option_state *,
- struct binding_scope **,
- struct universe *, void *))
-{
- pair *hash;
- int i;
- struct option_cache *oc;
-
- if (cfg_options -> universe_count <= u -> index)
- return;
-
- hash = cfg_options -> universes [u -> index];
- if (!hash)
- return;
- for (i = 0; i < OPTION_HASH_SIZE; i++) {
- pair p;
- /* XXX save _all_ options! XXX */
- for (p = hash [i]; p; p = p -> cdr) {
- oc = (struct option_cache *)p -> car;
- (*func) (oc, packet, lease, client_state,
- in_options, cfg_options, scope, u, stuff);
- }
- }
-}
-
-void save_linked_option (universe, options, oc)
- struct universe *universe;
- struct option_state *options;
- struct option_cache *oc;
-{
- pair *tail;
- pair np = (pair )0;
- struct option_chain_head *head;
-
- if (universe -> index >= options -> universe_count)
- return;
- head = ((struct option_chain_head *)
- options -> universes [universe -> index]);
- if (!head) {
- if (!option_chain_head_allocate (((struct option_chain_head **)
- &options -> universes
- [universe -> index]), MDL))
- return;
- head = ((struct option_chain_head *)
- options -> universes [universe -> index]);
- }
-
- /* Find the tail of the list. */
- for (tail = &head -> first; *tail; tail = &((*tail) -> cdr)) {
- if (oc -> option ==
- ((struct option_cache *)((*tail) -> car)) -> option) {
- option_cache_dereference ((struct option_cache **)
- (&(*tail) -> car), MDL);
- option_cache_reference ((struct option_cache **)
- (&(*tail) -> car), oc, MDL);
- return;
- }
- }
-
- *tail = cons (0, 0);
- if (*tail) {
- option_cache_reference ((struct option_cache **)
- (&(*tail) -> car), oc, MDL);
- }
-}
-
-int linked_option_space_encapsulate (result, packet, lease, client_state,
- in_options, cfg_options, scope, universe)
- struct data_string *result;
- struct packet *packet;
- struct lease *lease;
- struct client_state *client_state;
- struct option_state *in_options;
- struct option_state *cfg_options;
- struct binding_scope **scope;
- struct universe *universe;
-{
- int status;
- pair oc;
- struct option_chain_head *head;
-
- if (universe -> index >= cfg_options -> universe_count)
- return 0;
- head = ((struct option_chain_head *)
- cfg_options -> universes [universe -> index]);
- if (!head)
- return 0;
-
- status = 0;
- for (oc = head -> first; oc; oc = oc -> cdr) {
- if (store_option (result, universe, packet,
- lease, client_state, in_options, cfg_options,
- scope, (struct option_cache *)(oc -> car)))
- status = 1;
- }
-
- return status;
-}
-
-void delete_linked_option (universe, options, code)
- struct universe *universe;
- struct option_state *options;
- int code;
-{
- pair *tail, tmp = (pair)0;
- struct option_chain_head *head;
-
- if (universe -> index >= options -> universe_count)
- return;
- head = ((struct option_chain_head *)
- options -> universes [universe -> index]);
- if (!head)
- return;
-
- for (tail = &head -> first; *tail; tail = &((*tail) -> cdr)) {
- if (code ==
- ((struct option_cache *)(*tail) -> car) -> option -> code)
- {
- tmp = (*tail) -> cdr;
- option_cache_dereference ((struct option_cache **)
- (&(*tail) -> car), MDL);
- dfree (*tail, MDL);
- (*tail) = tmp;
- break;
- }
- }
-}
-
-struct option_cache *lookup_linked_option (universe, options, code)
- struct universe *universe;
- struct option_state *options;
- unsigned code;
-{
- pair oc;
- struct option_chain_head *head;
-
- if (universe -> index >= options -> universe_count)
- return 0;
- head = ((struct option_chain_head *)
- options -> universes [universe -> index]);
- if (!head)
- return 0;
-
- for (oc = head -> first; oc; oc = oc -> cdr) {
- if (code ==
- ((struct option_cache *)(oc -> car)) -> option -> code) {
- return (struct option_cache *)(oc -> car);
- }
- }
-
- return (struct option_cache *)0;
-}
-
-int linked_option_state_dereference (universe, state, file, line)
- struct universe *universe;
- struct option_state *state;
- const char *file;
- int line;
-{
- return (option_chain_head_dereference
- ((struct option_chain_head **)
- (&state -> universes [universe -> index]), MDL));
-}
-
-void linked_option_space_foreach (struct packet *packet, struct lease *lease,
- struct client_state *client_state,
- struct option_state *in_options,
- struct option_state *cfg_options,
- struct binding_scope **scope,
- struct universe *u, void *stuff,
- void (*func) (struct option_cache *,
- struct packet *,
- struct lease *,
- struct client_state *,
- struct option_state *,
- struct option_state *,
- struct binding_scope **,
- struct universe *, void *))
-{
- pair car;
- struct option_chain_head *head;
-
- if (u -> index >= cfg_options -> universe_count)
- return;
- head = ((struct option_chain_head *)
- cfg_options -> universes [u -> index]);
- if (!head)
- return;
- for (car = head -> first; car; car = car -> cdr) {
- (*func) ((struct option_cache *)(car -> car),
- packet, lease, client_state,
- in_options, cfg_options, scope, u, stuff);
- }
-}
-
-void do_packet (interface, packet, len, from_port, from, hfrom)
- struct interface_info *interface;
- struct dhcp_packet *packet;
- unsigned len;
- unsigned int from_port;
- struct iaddr from;
- struct hardware *hfrom;
-{
- int i;
- struct option_cache *op;
- struct packet *decoded_packet;
-#if defined (DEBUG_MEMORY_LEAKAGE)
- unsigned long previous_outstanding = dmalloc_outstanding;
-#endif
-
-#if defined (TRACING)
- trace_inpacket_stash (interface, packet, len, from_port, from, hfrom);
-#endif
-
- decoded_packet = (struct packet *)0;
- if (!packet_allocate (&decoded_packet, MDL)) {
- log_error ("do_packet: no memory for incoming packet!");
- return;
- }
- decoded_packet -> raw = packet;
- decoded_packet -> packet_length = len;
- decoded_packet -> client_port = from_port;
- decoded_packet -> client_addr = from;
- interface_reference (&decoded_packet -> interface, interface, MDL);
- decoded_packet -> haddr = hfrom;
-
- if (packet -> hlen > sizeof packet -> chaddr) {
- packet_dereference (&decoded_packet, MDL);
- log_info ("Discarding packet with bogus hlen.");
- return;
- }
-
- /* If there's an option buffer, try to parse it. */
- if (decoded_packet -> packet_length >= DHCP_FIXED_NON_UDP + 4) {
- if (!parse_options (decoded_packet)) {
- if (decoded_packet -> options)
- option_state_dereference
- (&decoded_packet -> options, MDL);
- packet_dereference (&decoded_packet, MDL);
- return;
- }
-
- if (decoded_packet -> options_valid &&
- (op = lookup_option (&dhcp_universe,
- decoded_packet -> options,
- DHO_DHCP_MESSAGE_TYPE))) {
- struct data_string dp;
- memset (&dp, 0, sizeof dp);
- evaluate_option_cache (&dp, decoded_packet,
- (struct lease *)0,
- (struct client_state *)0,
- decoded_packet -> options,
- (struct option_state *)0,
- (struct binding_scope **)0,
- op, MDL);
- if (dp.len > 0)
- decoded_packet -> packet_type = dp.data [0];
- else
- decoded_packet -> packet_type = 0;
- data_string_forget (&dp, MDL);
- }
- }
-
- if (decoded_packet -> packet_type)
- dhcp (decoded_packet);
- else
- bootp (decoded_packet);
-
- /* If the caller kept the packet, they'll have upped the refcnt. */
- packet_dereference (&decoded_packet, MDL);
-
-#if defined (DEBUG_MEMORY_LEAKAGE)
- log_info ("generation %ld: %ld new, %ld outstanding, %ld long-term",
- dmalloc_generation,
- dmalloc_outstanding - previous_outstanding,
- dmalloc_outstanding, dmalloc_longterm);
-#endif
-#if defined (DEBUG_MEMORY_LEAKAGE)
- dmalloc_dump_outstanding ();
-#endif
-#if defined (DEBUG_RC_HISTORY_EXHAUSTIVELY)
- dump_rc_history (0);
-#endif
-}
-
OpenPOWER on IntegriCloud