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.c2106
1 files changed, 1830 insertions, 276 deletions
diff --git a/contrib/isc-dhcp/common/options.c b/contrib/isc-dhcp/common/options.c
index b840716..b7a5a04 100644
--- a/contrib/isc-dhcp/common/options.c
+++ b/contrib/isc-dhcp/common/options.c
@@ -3,7 +3,7 @@
DHCP options parsing and reassembly. */
/*
- * Copyright (c) 1995, 1996, 1997, 1998 The Internet Software Consortium.
+ * Copyright (c) 1995-2001 Internet Software Consortium.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -34,176 +34,470 @@
* SUCH DAMAGE.
*
* This software has been written for the Internet Software Consortium
- * by Ted Lemon <mellon@fugue.com> in cooperation with Vixie
- * Enterprises. To learn more about the Internet Software Consortium,
- * see ``http://www.vix.com/isc''. To learn more about Vixie
- * Enterprises, see ``http://www.vix.com''.
+ * by Ted Lemon in cooperation with Vixie Enterprises and Nominum, Inc.
+ * To learn more about the Internet Software 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.26.2.11 2000/06/24 07:24:02 mellon Exp $ Copyright (c) 1995, 1996, 1997, 1998 The Internet Software Consortium. All rights reserved.\n";
+"$Id: options.c,v 1.85.2.6 2001/10/18 20:11:38 mellon Exp $ Copyright (c) 1995-2001 The Internet Software Consortium. All rights reserved.\n";
#endif /* not lint */
#define DHCP_OPTION_DATA
#include "dhcpd.h"
-#include <ctype.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. */
-void parse_options (packet)
+int parse_options (packet)
struct packet *packet;
{
- /* Initially, zero all option pointers. */
- memset (packet -> options, 0, sizeof (packet -> options));
+ 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;
+ return 1;
}
/* Go through the options field, up to the end of the packet
or the End field. */
- parse_option_buffer (packet, &packet -> raw -> options [4],
- packet -> packet_length - DHCP_FIXED_NON_UDP - 4);
+ 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
- && packet -> options [DHO_DHCP_OPTION_OVERLOAD].data) {
- if (packet -> options [DHO_DHCP_OPTION_OVERLOAD].data [0] & 1)
- parse_option_buffer (packet,
- (unsigned char *)
- packet -> raw -> file,
- sizeof packet -> raw -> file);
- if (packet -> options [DHO_DHCP_OPTION_OVERLOAD].data [0] & 2)
- parse_option_buffer (packet,
- (unsigned char *)
- packet -> raw -> sname,
- sizeof packet -> raw -> sname);
+ 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. */
-void parse_option_buffer (packet, buffer, length)
- struct packet *packet;
- unsigned char *buffer;
- int length;
+int parse_option_buffer (options, buffer, length, universe)
+ struct option_state *options;
+ const unsigned char *buffer;
+ unsigned length;
+ struct universe *universe;
{
- unsigned char *s, *t;
- unsigned char *end = buffer + length;
- int len;
+ 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;
- for (s = buffer; *s != DHO_END && s < end; ) {
- code = s [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) {
- ++s;
+ ++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 = s [1];
+ len = buffer [offset + 1];
/* If the length is outrageous, the options are bad. */
- if (s + len + 2 > end) {
- warn ("Option %s length %d overflows input buffer.",
- dhcp_options [code].name,
- len);
- packet -> options_valid = 0;
- return;
+ 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 we haven't seen this option before, just make
- space for it and copy it there. */
- if (!packet -> options [code].data) {
- if (!(t = ((unsigned char *)
- dmalloc (len + 1, "parse_option_buffer"))))
- error ("Can't allocate storage for option %s.",
- dhcp_options [code].name);
- /* Copy and NUL-terminate the option (in case it's an
- ASCII string. */
- memcpy (t, &s [2], len);
- t [len] = 0;
- packet -> options [code].len = len;
- packet -> options [code].data = t;
- } else {
- /* If it's a repeat, concatenate it to whatever
- we last saw. This is really only required
- for clients, but what the heck... */
- t = ((unsigned char *)
- dmalloc (len + packet -> options [code].len + 1,
- "parse_option_buffer"));
- if (!t)
- error ("Can't expand storage for option %s.",
- dhcp_options [code].name);
- memcpy (t, packet -> options [code].data,
- packet -> options [code].len);
- memcpy (t + packet -> options [code].len,
- &s [2], len);
- packet -> options [code].len += len;
- t [packet -> options [code].len] = 0;
- dfree (packet -> options [code].data,
- "parse_option_buffer");
- packet -> options [code].data = t;
- }
- s += len + 2;
+
+ /* 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] -> format [0] == 'e' ||
+ universe -> options [code] -> format [0] == 'E') &&
+ (parse_encapsulated_suboptions
+ (options, universe -> options [code],
+ buffer + offset + 2, len,
+ universe, (const char *)0)))) {
+ save_option_buffer (universe, options, bp,
+ &bp -> data [offset + 2], len,
+ universe -> options [code], 1);
+ }
+ offset += len + 2;
}
- packet -> options_valid = 1;
+ 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;
+ }
+
+ 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, mms,
- options, overload, terminate, bootpp, prl, prl_len)
+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 tree_cache **options;
+ 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;
- u_int8_t *prl;
- int prl_len;
+ struct data_string *prl;
+ const char *vuname;
{
- unsigned char priority_list [300];
+#define PRIORITY_COUNT 300
+ unsigned priority_list [PRIORITY_COUNT];
int priority_len;
unsigned char buffer [4096]; /* Really big buffer... */
- int main_buffer_size;
- int mainbufix, bufix;
- int option_size;
- int length;
+ 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;
- /* 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 &&
- inpacket &&
- inpacket -> options [DHO_DHCP_MAX_MESSAGE_SIZE].data &&
- (inpacket -> options [DHO_DHCP_MAX_MESSAGE_SIZE].len >=
- sizeof (u_int16_t)))
- mms = getUShort (inpacket -> options
- [DHO_DHCP_MAX_MESSAGE_SIZE].data);
+ 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 (!mms && 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))
+ mms = getUShort (ds.data);
+ 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)
+
+ if (mms) {
main_buffer_size = mms - DHCP_FIXED_LEN;
- else if (bootpp)
- main_buffer_size = 64;
- else
+
+ /* 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;
@@ -213,35 +507,96 @@ int cons_options (inpacket, outpacket, mms,
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;
- /* If the client has provided a list of options that it wishes
- returned, use it to prioritize. Otherwise, prioritize
- based on the default priority list. */
-
- if (inpacket &&
- inpacket -> options [DHO_DHCP_PARAMETER_REQUEST_LIST].data) {
- int prlen = (inpacket ->
- options [DHO_DHCP_PARAMETER_REQUEST_LIST].len);
- if (prlen + priority_len > sizeof priority_list)
- prlen = (sizeof priority_list) - priority_len;
-
- memcpy (&priority_list [priority_len],
- (inpacket -> options
- [DHO_DHCP_PARAMETER_REQUEST_LIST].data), prlen);
- priority_len += prlen;
- prl = priority_list;
- } else if (prl) {
- if (prl_len + priority_len > sizeof priority_list)
- prl_len = (sizeof priority_list) - priority_len;
-
- memcpy (&priority_list [priority_len], prl, prl_len);
- priority_len += prl_len;
- prl = priority_list;
+ 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 {
- memcpy (&priority_list [priority_len],
- dhcp_option_default_priority_list,
- sizeof_dhcp_option_default_priority_list);
- priority_len += sizeof_dhcp_option_default_priority_list;
+ /* 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];
+ 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]);
+ 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... */
@@ -249,11 +604,13 @@ int cons_options (inpacket, outpacket, mms,
(main_buffer_size - 7 +
((overload & 1) ? DHCP_FILE_LEN : 0) +
((overload & 2) ? DHCP_SNAME_LEN : 0)),
- options, priority_list, priority_len,
+ 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);
+ terminate, vuname);
/* Put the cookie up front... */
memcpy (outpacket -> options, DHCP_OPTIONS_COOKIE, 4);
@@ -267,13 +624,12 @@ int cons_options (inpacket, outpacket, mms,
memcpy (&outpacket -> options [mainbufix],
buffer, option_size);
mainbufix += option_size;
+ agentix = mainbufix;
if (mainbufix < main_buffer_size)
- outpacket -> options [mainbufix++]
- = DHO_END;
+ need_endopt = 1;
length = DHCP_FIXED_NON_UDP + mainbufix;
} else {
- outpacket -> options [mainbufix++] =
- DHO_DHCP_OPTION_OVERLOAD;
+ 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;
@@ -282,8 +638,10 @@ int cons_options (inpacket, outpacket, mms,
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;
- length = DHCP_FIXED_NON_UDP + mainbufix;
if (overload & 1) {
if (option_size - bufix <= DHCP_FILE_LEN) {
memcpy (outpacket -> file,
@@ -314,220 +672,400 @@ int cons_options (inpacket, outpacket, mms,
= 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, options, priority_list, priority_len,
- first_cutoff, second_cutoff, terminate)
+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;
- int buflen;
- struct tree_cache **options;
- unsigned char *priority_list;
+ 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;
- int first_cutoff, second_cutoff;
+ unsigned first_cutoff, second_cutoff;
int terminate;
+ const char *vuname;
{
int bufix = 0;
- int option_stored [256];
int i;
int ix;
int tto;
+ struct data_string od;
+ struct option_cache *oc;
+ unsigned code;
+ int optstart;
+
+ memset (&od, 0, sizeof od);
- /* Zero out the stored-lengths array. */
- memset (option_stored, 0, sizeof option_stored);
+ /* 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++) {
- /* Code for next option to try to store. */
- int code = priority_list [i];
- int optstart;
+ /* 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;
- /* Number of bytes left to store (some may already
- have been stored by a previous pass). */
- int length;
+ memset (&encapsulation, 0, sizeof encapsulation);
- /* If no data is available for this option, skip it. */
- if (!options [code]) {
- continue;
- }
+ /* 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;
- /* The client could ask for things that are mandatory,
- in which case we should avoid storing them twice... */
- if (option_stored [code])
- continue;
- option_stored [code] = 1;
+ oc = lookup_option (u, cfg_options, code);
- /* Find the value of the option... */
- if (!tree_evaluate (options [code])) {
- continue;
- }
+ /* 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] -> format [0] == 'E' && !oc) ||
+ u -> options [code] -> format [0] == 'e') {
+ int uix;
+ static char *s, *t;
+ struct option_cache *tmp;
+ struct data_string name;
- /* We should now have a constant length for the option. */
- length = options [code] -> len;
+ s = strchr (u -> options [code] -> format, 'E');
+ if (s)
+ t = strchr (++s, '.');
+ if (s && t) {
+ memset (&name, 0, sizeof name);
- /* Do we add a NUL? */
- if (terminate && dhcp_options [code].format [0] == 't') {
- length++;
- tto = 1;
- } else {
- tto = 0;
+ /* 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);
+ }
}
+ }
- /* 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. */
+ /* 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. */
- ix = 0;
+ /* 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;
+ }
+ }
- optstart = bufix;
- while (length) {
- unsigned char incr = length > 255 ? 255 : length;
+ /* 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;
+ }
+ }
- /* 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;
+ /* Do we add a NUL? */
+ if (terminate && dhcp_options [code].format [0] == 't') {
+ length++;
+ tto = 1;
+ } else {
+ tto = 0;
+ }
- /* If this option is going to overflow the buffer,
- skip it. */
- if (bufix + 2 + incr > buflen) {
- bufix = optstart;
- break;
- }
+ /* 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. */
- /* Everything looks good - copy it in! */
- buffer [bufix] = code;
- buffer [bufix + 1] = incr;
- if (tto && incr == length) {
- memcpy (buffer + bufix + 2,
- options [code] -> value + ix,
- incr - 1);
- buffer [bufix + 2 + incr - 1] = 0;
- } else {
- memcpy (buffer + bufix + 2,
- options [code] -> value + ix, incr);
- }
- length -= incr;
- ix += incr;
- bufix += 2 + incr;
- }
+ 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. */
-char *pretty_print_option (code, data, len, emit_commas, emit_quotes)
- unsigned int code;
- unsigned char *data;
- int len;
+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];
- int i, j, k;
+ struct enumeration *enumbuf [32];
+ int i, j, k, l;
char *op = optbuf;
- unsigned char *dp = data;
+ const unsigned char *dp = data;
struct in_addr foo;
char comma;
-
- /* Code should be between 0 and 255. */
- if (code > 255)
- error ("pretty_print_option: bad code %d\n", code);
+ unsigned long tval;
if (emit_commas)
comma = ',';
else
comma = ' ';
+ memset (enumbuf, 0, sizeof enumbuf);
+
/* Figure out the size of the data. */
- for (i = 0; dhcp_options [code].format [i]; i++) {
+ for (l = i = 0; option -> format [i]; i++, l++) {
if (!numhunk) {
- warn ("%s: Excess information in format string: %s\n",
- dhcp_options [code].name,
- &(dhcp_options [code].format [i]));
+ log_error ("%s: Extra codes in format string: %s",
+ option -> name,
+ &(option -> format [i]));
break;
}
numelem++;
- fmtbuf [i] = dhcp_options [code].format [i];
- switch (dhcp_options [code].format [i]) {
+ fmtbuf [l] = option -> format [i];
+ switch (option -> format [i]) {
+ case 'a':
+ --numelem;
+ fmtbuf [l] = 0;
+ numhunk = 0;
+ break;
case 'A':
--numelem;
- fmtbuf [i] = 0;
+ 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 (k == len) {
- fmtbuf [i] = 't';
+ /* 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 [i] = 'x';
+ fmtbuf [l] = 'x';
hunksize++;
comma = ':';
numhunk = 0;
}
- fmtbuf [i + 1] = 0;
+ fmtbuf [l + 1] = 0;
break;
+ case 'd':
case 't':
- fmtbuf [i] = 't';
- fmtbuf [i + 1] = 0;
+ 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:
- warn ("%s: garbage in format string: %s\n",
- dhcp_options [code].name,
- &(dhcp_options [code].format [i]));
+ log_error ("%s: garbage in format string: %s",
+ option -> name,
+ &(option -> format [i]));
break;
}
}
/* Check for too few bytes... */
- if (hunksize > len) {
- warn ("%s: expecting at least %d bytes; got %d",
- dhcp_options [code].name,
+ 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)
- warn ("%s: %d extra bytes",
- dhcp_options [code].name,
+ log_error ("%s: %d extra bytes",
+ option -> name,
len - hunksize);
/* If this is an array, compute its size. */
@@ -535,8 +1073,8 @@ char *pretty_print_option (code, data, len, emit_commas, emit_quotes)
numhunk = len / hunksize;
/* See if we got an exact number of hunks. */
if (numhunk > 0 && numhunk * hunksize < len)
- warn ("%s: %d extra bytes at end of array\n",
- dhcp_options [code].name,
+ 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. */
@@ -574,6 +1112,21 @@ char *pretty_print_option (code, data, len, emit_commas, 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));
@@ -583,23 +1136,31 @@ char *pretty_print_option (code, data, len, emit_commas, emit_quotes)
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", getShort (dp));
+ sprintf (op, "%d", (int)getShort (dp));
dp += 2;
break;
case 'S':
- sprintf (op, "%d", getUShort (dp));
+ sprintf (op, "%d", (unsigned)getUShort (dp));
dp += 2;
break;
case 'b':
- sprintf (op, "%d", *(char *)dp++);
+ sprintf (op, "%d", *(const char *)dp++);
break;
case 'B':
+ enum_as_num:
sprintf (op, "%d", *dp++);
break;
case 'x':
@@ -609,58 +1170,1051 @@ char *pretty_print_option (code, data, len, emit_commas, emit_quotes)
strcpy (op, *dp++ ? "true" : "false");
break;
default:
- warn ("Unexpected format code %c", fmtbuf [j]);
+ 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;
- int len;
+ unsigned len;
unsigned int from_port;
struct iaddr from;
struct hardware *hfrom;
{
- struct packet tp;
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) {
- note ("Discarding packet with invalid hlen.");
+ packet_dereference (&decoded_packet, MDL);
+ log_info ("Discarding packet with bogus hlen.");
return;
}
- memset (&tp, 0, sizeof tp);
- tp.raw = packet;
- tp.packet_length = len;
- tp.client_port = from_port;
- tp.client_addr = from;
- tp.interface = interface;
- tp.haddr = hfrom;
-
- parse_options (&tp);
- if (tp.options_valid &&
- tp.options [DHO_DHCP_MESSAGE_TYPE].data)
- tp.packet_type =
- tp.options [DHO_DHCP_MESSAGE_TYPE].data [0];
- if (tp.packet_type)
- dhcp (&tp);
- else
- bootp (&tp);
+ /* 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;
+ }
- /* Free the data associated with the options. */
- for (i = 0; i < 256; i++) {
- if (tp.options [i].len && tp.options [i].data)
- dfree (tp.options [i].data, "do_packet");
+ 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