summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTed Lemon <source@isc.org>2000-10-10 22:39:47 +0000
committerTed Lemon <source@isc.org>2000-10-10 22:39:47 +0000
commit77956158d4c7f40e3804d1fb055221b5ed012394 (patch)
tree7e66b1d306307e68048dc65b03c8448f8a41c817
parente31656de632f7bd2fe3dbbf82439c1753e9309bc (diff)
downloadisc-dhcp-77956158d4c7f40e3804d1fb055221b5ed012394.tar.gz
- Make option spaces stored as linked lists first-class objects.
- Use these spaces for the agent option space, fqdn option space and nwip option space. - Make encapsulator and decoder for fqdn option space. - Support the agent option space the way all option spaces are supported to the extent possibol, rather than special-casing it. - Add an enumerator function for defined options in an option space. - Support encapsulated options directly with a format string, rather than as another special case.
-rw-r--r--common/options.c1011
1 files changed, 782 insertions, 229 deletions
diff --git a/common/options.c b/common/options.c
index 637a3373..54b96431 100644
--- a/common/options.c
+++ b/common/options.c
@@ -43,13 +43,15 @@
#ifndef lint
static char copyright[] =
-"$Id: options.c,v 1.65 2000/09/29 19:58:24 mellon Exp $ Copyright (c) 1995-2000 The Internet Software Consortium. All rights reserved.\n";
+"$Id: options.c,v 1.66 2000/10/10 22:39:47 mellon Exp $ Copyright (c) 1995-2000 The Internet Software 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));
@@ -76,9 +78,11 @@ int parse_options (packet)
/* Go through the options field, up to the end of the packet
or the End field. */
- if (!parse_option_buffer (packet, &packet -> raw -> options [4],
+ if (!parse_option_buffer (packet -> options,
+ &packet -> raw -> options [4],
(packet -> packet_length -
- DHCP_FIXED_NON_UDP - 4)))
+ DHCP_FIXED_NON_UDP - 4),
+ &dhcp_universe))
return 0;
/* If we parsed a DHCP Option Overload option, parse more
@@ -88,18 +92,22 @@ int parse_options (packet)
DHO_DHCP_OPTION_OVERLOAD))) {
if (op -> data.data [0] & 1) {
if (!parse_option_buffer
- (packet, (unsigned char *)packet -> raw -> file,
- sizeof packet -> raw -> file))
+ (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,
+ (packet -> options,
(unsigned char *)packet -> raw -> sname,
- sizeof packet -> raw -> sname))
+ sizeof packet -> raw -> sname,
+ &dhcp_universe))
return 0;
}
}
+ packet -> options_valid = 1;
return 1;
}
@@ -107,14 +115,15 @@ int parse_options (packet)
values in packet -> options and setting packet -> options_valid if no
errors are encountered. */
-int parse_option_buffer (packet, buffer, length)
- struct packet *packet;
+int parse_option_buffer (options, buffer, length, universe)
+ struct option_state *options;
unsigned char *buffer;
unsigned length;
+ struct universe *universe;
{
unsigned char *t;
unsigned char *end = buffer + length;
- int len, offset;
+ unsigned len, offset;
int code;
struct option_cache *op = (struct option_cache *)0;
struct buffer *bp = (struct buffer *)0;
@@ -145,51 +154,186 @@ int parse_option_buffer (packet, buffer, length)
return 0;
}
- /* If this is a Relay Agent Information option, we must
- handle it specially. */
- if (code == DHO_DHCP_AGENT_OPTIONS) {
- if (!parse_agent_information_option
- (packet, len, buffer + offset + 2)) {
- log_error ("bad agent information option.");
- buffer_dereference (&bp, MDL);
+ /* 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, (struct universe *)0)))) {
+ 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. */
+
+int parse_encapsulated_suboptions (struct option_state *options,
+ struct option *eopt,
+ unsigned char *buffer,
+ unsigned len, struct universe *eu,
+ struct universe *vu)
+{
+ struct universe *universe;
+ int i;
+ char *s, *t;
+
+ /* 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) {
+ /* It's the vendor universe. */
+ universe = vu;
+ } else {
+ 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;
+ }
+ }
+ }
+
+ /* 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 (s != eopt -> format)
+ return 0;
+ /* Otherwise, return the status of the decode function. */
+ return i;
+}
+
+int fqdn_universe_decode (struct option_state *options,
+ 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 < 4)
+ 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] & 2) /* no-client-update */
+ bp -> data [1] = 1;
+ else
+ bp -> data [1] = 0;
+ if (!save_option_buffer (&fqdn_universe, options, bp,
+ &bp -> data [1], 1,
+ &fqdn_options [FQDN_NO_CLIENT_UPDATE], 0))
+ goto bad;
+
+ if (buffer [0] & 1) /* server-update */
+ bp -> data [2] = 1;
+ else
+ bp -> data [2] = 0;
+ 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;
+
+ /* 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]) {
+ if (!save_option_buffer (&fqdn_universe, options, bp,
+ &bp -> data [5], length - 3,
+ &fqdn_options [FQDN_NAME], 1))
+ goto bad;
+ } else {
+ int len;
+ unsigned char *s, *t, *u;
+
+ u = t = s = &bp -> data [5];
+ do {
+ len = *s++;
+ if (len > 63) {
+ log_info ("fancy bits in fqdn option");
return 0;
+ }
+ if (len == 0) {
+ *t++ = '.';
+ break;
}
- } else {
- if (!option_cache_allocate (&op, MDL)) {
- log_error ("No memory for option %s.",
- dhcp_options [code].name);
- buffer_dereference (&bp, MDL);
+ if (s + len > &bp -> data [0] + length + 3) {
+ log_info ("fqdn tag longer than buffer");
return 0;
}
-
- /* 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 = &bp -> data [offset + 2];
- op -> data.len = len;
-
- /* NUL terminate (we can get away with this
- because we 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 we're getting out of
- the *original* buffer. */
- bp -> data [offset + 2 + len] = 0;
- op -> data.terminated = 1;
-
- op -> option = &dhcp_options [code];
- /* Now store the option. */
- save_option (&dhcp_universe, packet -> options, op);
-
- /* And let go of our reference. */
- option_cache_dereference (&op, MDL);
- }
- offset += len + 2;
+ while (len--)
+ *t++ = *s++;
+ *t++ = '.';
+ } while (s + len < &bp -> data [0] + length + 3);
+ if (!save_option_buffer (&fqdn_universe, options, bp,
+ &bp -> data [5], (unsigned)(t - u),
+ &fqdn_options [FQDN_NAME], 1))
+ goto bad;
}
- packet -> options_valid = 1;
- buffer_dereference (&bp, MDL);
return 1;
}
@@ -198,7 +342,7 @@ int parse_option_buffer (packet, buffer, length)
of vendor options using the same routine. */
int cons_options (inpacket, outpacket, lease, mms, in_options, cfg_options,
- scope, overload, terminate, bootpp, prl)
+ scope, overload, terminate, bootpp, prl, vuname)
struct packet *inpacket;
struct dhcp_packet *outpacket;
struct lease *lease;
@@ -210,6 +354,7 @@ int cons_options (inpacket, outpacket, lease, mms, in_options, cfg_options,
int terminate;
int bootpp;
struct data_string *prl;
+ const char *vuname;
{
#define PRIORITY_COUNT 300
unsigned priority_list [PRIORITY_COUNT];
@@ -278,8 +423,13 @@ int cons_options (inpacket, outpacket, lease, mms, in_options, cfg_options,
if (prl && prl -> len > 0) {
data_string_truncate (prl, (PRIORITY_COUNT - priority_len));
- for (i = 0; i < prl -> len; i++)
- priority_list [priority_len++] = prl -> data [i];
+ 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... */
@@ -302,7 +452,9 @@ int cons_options (inpacket, outpacket, lease, mms, in_options, cfg_options,
op = (struct option_cache *)(pp -> car);
if (op -> option -> code <
cfg_options -> site_code_min &&
- priority_len < PRIORITY_COUNT)
+ priority_len < PRIORITY_COUNT &&
+ (op -> option -> code !=
+ DHO_DHCP_AGENT_OPTIONS))
priority_list [priority_len++] =
op -> option -> code;
}
@@ -319,11 +471,36 @@ int cons_options (inpacket, outpacket, lease, mms, in_options, cfg_options,
op = (struct option_cache *)(pp -> car);
if (op -> option -> code >=
cfg_options -> site_code_min &&
- priority_len < PRIORITY_COUNT)
+ 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... */
@@ -338,7 +515,7 @@ int cons_options (inpacket, outpacket, lease, mms, in_options, cfg_options,
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);
@@ -403,17 +580,25 @@ int cons_options (inpacket, outpacket, lease, mms, in_options, cfg_options,
}
}
- length = cons_agent_information_options (cfg_options,
- outpacket, agentix, length);
-
+ /* 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,
+ in_options, cfg_options, scope,
+ priority_list, priority_len,
+ 1500 - DHCP_FIXED_LEN - agentix,
+ 1500 - DHCP_FIXED_LEN - agentix, 0, (char *)0);
return length;
}
/* Store all the requested options into the requested buffer. */
int store_options (buffer, buflen, packet, lease,
- in_options, cfg_options, scope, priority_list,
- priority_len, first_cutoff, second_cutoff, terminate)
+ in_options, cfg_options, scope, priority_list, priority_len,
+ first_cutoff, second_cutoff, terminate, vuname)
unsigned char *buffer;
unsigned buflen;
struct packet *packet;
@@ -425,6 +610,7 @@ int store_options (buffer, buflen, packet, lease,
int priority_len;
unsigned first_cutoff, second_cutoff;
int terminate;
+ const char *vuname;
{
int bufix = 0;
int i;
@@ -432,6 +618,8 @@ int store_options (buffer, buflen, packet, lease,
int tto;
struct data_string od;
struct option_cache *oc;
+ unsigned code;
+ int optstart;
memset (&od, 0, sizeof od);
@@ -455,93 +643,187 @@ int store_options (buffer, buflen, packet, lease,
/* 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. */
- unsigned code = priority_list [i];
- int optstart;
-
- /* Number of bytes left to store (some may already
- have been stored by a previous pass). */
- int length;
-
- /* 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)
- oc = lookup_option
- (universes [cfg_options -> site_universe],
- cfg_options, code);
- else
- oc = lookup_option (&dhcp_universe, cfg_options, code);
-
- /* If no data is available for this option, skip it. */
- if (!oc) {
- continue;
+ /* 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;
+
+ /* 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] -> 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,
+ in_options,
+ cfg_options,
+ scope, tmp, MDL);
+ } else if (vuname) {
+ name.data = s;
+ name.len = strlen (s);
+ }
+ } else {
+ name.data = 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) {
+ memset (&encapsulation, 0, sizeof encapsulation);
+ have_encapsulation =
+ (option_space_encapsulate
+ (&encapsulation, packet, lease,
+ in_options, cfg_options, scope, &name));
+ data_string_forget (&name, MDL);
+ }
}
-
- /* Find the value of the option... */
+ }
+
+ /* 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, in_options,
cfg_options, scope, oc, MDL);
if (!od.len) {
- continue;
- }
-
- /* We should now have a constant length for the option. */
- length = od.len;
-
- /* 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;
-
- /* 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;
+ option_cache_dereference (&oc, MDL);
+ data_string_forget (&encapsulation, MDL);
+ continue;
}
- data_string_forget (&od, MDL);
+ }
+
+ /* 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);
+ if (oc)
+ option_cache_dereference (&oc, MDL);
}
+
return bufix;
}
@@ -751,8 +1033,8 @@ const char *pretty_print_option (code, data, len, emit_commas, emit_quotes)
return optbuf;
}
-int hashed_option_get (result, universe, packet, lease,
- in_options, cfg_options, options, scope, code)
+int get_option (result, universe, packet, lease,
+ in_options, cfg_options, options, scope, code)
struct data_string *result;
struct universe *universe;
struct packet *packet;
@@ -776,55 +1058,7 @@ int hashed_option_get (result, universe, packet, lease,
return 1;
}
-int agent_option_get (result, universe, packet, lease,
- in_options, cfg_options, options, scope, code)
- struct data_string *result;
- struct universe *universe;
- struct packet *packet;
- struct lease *lease;
- struct option_state *in_options;
- struct option_state *cfg_options;
- struct option_state *options;
- struct binding_scope **scope;
- unsigned code;
-{
- struct agent_options *ao;
- struct option_tag *t;
-
- /* Make sure there's agent option state. */
- if (universe -> index >= options -> universe_count ||
- !(options -> universes [universe -> index]))
- return 0;
- ao = (struct agent_options *)options -> universes [universe -> index];
-
- /* Find the last set of agent options and consider it definitive. */
- for (; ao -> next; ao = ao -> next)
- ;
- if (ao) {
- for (t = ao -> first; t; t = t -> next) {
- if (t -> data [0] == code) {
- result -> len = t -> data [1];
- if (!(buffer_allocate (&result -> buffer,
- result -> len + 1,
- MDL))) {
- result -> len = 0;
- buffer_dereference
- (&result -> buffer, MDL);
- return 0;
- }
- result -> data = &result -> buffer -> data [0];
- memcpy (result -> buffer -> data,
- &t -> data [2], result -> len);
- result -> buffer -> data [result -> len] = 0;
- result -> terminated = 1;
- return 1;
- }
- }
- }
- return 0;
-}
-
-void hashed_option_set (universe, options, option, op)
+void set_option (universe, options, option, op)
struct universe *universe;
struct option_state *options;
struct option_cache *option;
@@ -937,10 +1171,67 @@ struct option_cache *lookup_hashed_option (universe, options, code)
return (struct option_cache *)0;
}
-void save_option (universe, options, oc)
- struct universe *universe;
- struct option_state *options;
- struct option_cache *oc;
+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);
@@ -1074,6 +1365,9 @@ int option_cache_dereference (ptr, 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;
@@ -1117,7 +1411,8 @@ int hashed_option_state_dereference (universe, state, file, line)
for (cp = heads [i]; cp; cp = next) {
next = cp -> cdr;
option_cache_dereference
- ((struct option_cache **)&cp -> car, file, line);
+ ((struct option_cache **)&cp -> car,
+ file, line);
free_pair (cp, file, line);
}
}
@@ -1127,34 +1422,6 @@ int hashed_option_state_dereference (universe, state, file, line)
return 1;
}
-int agent_option_state_dereference (universe, state, file, line)
- struct universe *universe;
- struct option_state *state;
- const char *file;
- int line;
-{
- struct agent_options *a, *na;
- struct option_tag *ot, *not;
-
- if (universe -> index >= state -> universe_count ||
- !state -> universes [universe -> index])
- return 0;
-
- /* We can also release the agent options, if any... */
- for (a = (struct agent_options *)(state -> universes
- [universe -> index]); a; a = na) {
- na = a -> next;
- for (ot = a -> first; ot; ot = not) {
- not = ot -> next;
- dfree (ot, file, line);
- }
- }
-
- dfree (state -> universes [universe -> index], file, line);
- state -> universes [universe -> index] = (void *)0;
- return 1;
-}
-
int store_option (result, universe, packet, lease,
in_options, cfg_options, scope, oc)
struct data_string *result;
@@ -1218,10 +1485,8 @@ int option_space_encapsulate (result, packet, lease,
u = (struct universe *)0;
universe_hash_lookup (&u, universe_hash,
(const char *)name -> data, name -> len, MDL);
- if (!u) {
- log_error ("unknown option space %s.", name -> data);
+ if (!u)
return 0;
- }
if (u -> encapsulate)
return (*u -> encapsulate) (result, packet, lease,
@@ -1332,6 +1597,294 @@ int nwip_option_space_encapsulate (result, packet, lease,
return status;
}
+int fqdn_option_space_encapsulate (result, packet, lease,
+ in_options, cfg_options, scope, universe)
+ struct data_string *result;
+ struct packet *packet;
+ struct lease *lease;
+ struct option_state *in_options;
+ struct option_state *cfg_options;
+ struct binding_scope **scope;
+ struct universe *universe;
+{
+ struct option_cache *oc;
+ struct data_string results [FQDN_SUBOPTION_COUNT + 1];
+ unsigned i;
+ unsigned len;
+ struct buffer *bp = (struct buffer *)0;
+
+ /* If there's no FQDN universe, don't encapsulate. */
+ if (!cfg_options -> universes [fqdn_universe.index])
+ return 0;
+
+ /* Figure out the values of all the suboptions. */
+ memset (results, 0, sizeof results);
+ for (oc = ((struct option_cache *)
+ cfg_options -> universes [universe -> index]);
+ oc; oc = oc -> next) {
+ if (oc -> option -> code > FQDN_SUBOPTION_COUNT)
+ continue;
+ evaluate_option_cache (&results [oc -> option -> code],
+ packet, lease, in_options,
+ cfg_options, scope, oc, MDL);
+ }
+ len = 4 + results [FQDN_NAME].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;
+ }
+ result -> buffer = bp;
+ 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_NAME].len) {
+ i = 0;
+ while (i < results [FQDN_NAME].len) {
+ int j;
+ for (j = i; ('.' !=
+ results [FQDN_NAME].data [j]) &&
+ j < results [FQDN_NAME].len; j++)
+ ;
+ *out++ = j - i;
+ memcpy (out, &results [FQDN_NAME].data [i],
+ (unsigned)(j - i));
+ out += j - i;
+ i = j;
+ if (results [FQDN_NAME].data [j] == '.')
+ i++;
+ }
+ if ((results [FQDN_NAME].data
+ [results [FQDN_NAME].len - 1] == '.'))
+ *out++ = 0;
+ result -> len = out - result -> data;
+ result -> terminated = 0;
+ }
+ } else {
+ if (results [FQDN_NAME].len) {
+ memcpy (&bp -> data [3], results [FQDN_NAME].data,
+ results [FQDN_NAME].len);
+ result -> len += results [FQDN_NAME].len;
+ result -> terminated = 0;
+ }
+ }
+ for (i = 1; i <= FQDN_SUBOPTION_COUNT; i++) {
+ if (results [i].len)
+ data_string_forget (&results [i], MDL);
+ }
+ return 1;
+}
+
+void option_space_foreach (struct packet *packet, struct lease *lease,
+ 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 option_state *,
+ struct option_state *,
+ struct binding_scope **,
+ struct universe *, void *))
+{
+ if (u -> foreach)
+ (*u -> foreach) (packet, lease, in_options, cfg_options,
+ scope, u, stuff, func);
+}
+
+void hashed_option_space_foreach (struct packet *packet, struct lease *lease,
+ 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 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,
+ 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;
+{
+ struct option_cache **tail;
+
+ /* Find the tail of the list. */
+ for (tail = ((struct option_cache **)
+ &options -> universes [universe -> index]);
+ *tail; tail = &((*tail) -> next)) {
+ if ((*tail) -> option == oc -> option) {
+ if ((*tail) -> next) {
+ option_cache_reference (&oc -> next,
+ (*tail) -> next, MDL);
+ }
+ option_cache_dereference (tail, MDL);
+ break;
+ }
+ }
+
+ option_cache_reference (tail, oc, MDL);
+}
+
+int linked_option_space_encapsulate (result, packet, lease,
+ in_options, cfg_options, scope, universe)
+ struct data_string *result;
+ struct packet *packet;
+ struct lease *lease;
+ struct option_state *in_options;
+ struct option_state *cfg_options;
+ struct binding_scope **scope;
+ struct universe *universe;
+{
+ int status;
+ struct option_cache *oc;
+
+ if (universe -> index >= cfg_options -> universe_count)
+ return 0;
+
+ status = 0;
+ for (oc = ((struct option_cache *)
+ cfg_options -> universes [universe -> index]);
+ oc; oc = oc -> next) {
+ if (store_option (result, universe, packet, lease,
+ in_options, cfg_options, scope, oc))
+ status = 1;
+ }
+
+ return status;
+}
+
+void delete_linked_option (universe, options, code)
+ struct universe *universe;
+ struct option_state *options;
+ int code;
+{
+ struct option_cache **tail, *tmp = (struct option_cache *)0;
+
+ /* Find the tail of the list. */
+ for (tail = ((struct option_cache **)
+ &options -> universes [universe -> index]);
+ *tail; tail = &((*tail) -> next)) {
+ if ((*tail) -> option -> code == code) {
+ if ((*tail) -> next) {
+ option_cache_reference (&tmp,
+ (*tail) -> next, MDL);
+ option_cache_dereference (tail, MDL);
+ }
+ if (tmp) {
+ option_cache_reference (tail, tmp, MDL);
+ option_cache_dereference (&tmp, MDL);
+ }
+ break;
+ }
+ }
+}
+
+struct option_cache *lookup_linked_option (universe, options, code)
+ struct universe *universe;
+ struct option_state *options;
+ unsigned code;
+{
+ struct option_cache *oc;
+
+ if (universe -> index >= options -> universe_count)
+ return 0;
+
+ for (oc = ((struct option_cache *)
+ options -> universes [universe -> index]);
+ oc; oc = oc -> next) {
+ if (oc -> option -> code == code) {
+ return oc;
+ }
+ }
+
+ 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;
+{
+ struct option_cache **tail;
+
+ /* Find the tail of the list. */
+ tail = ((struct option_cache **)
+ &state -> universes [universe -> index]);
+ if (*tail)
+ return option_cache_dereference (tail, file, line);
+ return 1;
+}
+
+void linked_option_space_foreach (struct packet *packet, struct lease *lease,
+ 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 option_state *,
+ struct option_state *,
+ struct binding_scope **,
+ struct universe *, void *))
+{
+ struct option_cache *oc;
+
+ if (u -> index >= cfg_options -> universe_count)
+ return;
+
+ for (oc = ((struct option_cache *)
+ cfg_options -> universes [u -> index]);
+ oc; oc = oc -> next) {
+ (*func) (oc, packet, lease,
+ 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;