#ifndef lint
static char copyright[] =
-"$Id: tree.c,v 1.12 1998/06/25 03:10:32 mellon Exp $ Copyright (c) 1995, 1996, 1997, 1998 The Internet Software Consortium. All rights reserved.\n";
+"$Id: tree.c,v 1.13 1998/11/05 18:45:23 mellon Exp $ Copyright (c) 1995, 1996, 1997, 1998 The Internet Software Consortium. All rights reserved.\n";
#endif /* not lint */
#include "dhcpd.h"
-static struct data_string do_host_lookup PROTO ((struct dns_host_entry *));
+static int do_host_lookup PROTO ((struct data_string *,
+ struct dns_host_entry *));
pair cons (car, cdr)
caddr_t car;
return foo;
}
-struct expression *make_host_lookup (name)
+int make_const_option_cache (oc, buffer, data, len, option, name)
+ struct option_cache **oc;
+ struct buffer **buffer;
+ u_int8_t *data;
+ int len;
+ struct option *option;
char *name;
{
- struct expression *nt;
- nt = new_expression ("make_host_lookup");
- if (!nt)
- error ("No memory for host lookup tree node.");
- nt -> op = expr_host_lookup;
- nt -> data.host_lookup = enter_dns_host (name);
- return nt;
+ struct buffer *bp;
+
+ if (buffer) {
+ bp = *buffer;
+ *buffer = 0;
+ } else {
+ bp = (struct buffer *)0;
+ if (!buffer_allocate (&bp, len, name))
+ warn ("%s: can't allocate buffer.", name);
+ return 0;
+ }
+
+ if (!option_cache_allocate (oc, name)) {
+ warn ("%s: can't allocate option cache.");
+ buffer_dereference (&bp, name);
+ return 0;
+ }
+
+ (*oc) -> data.len = len;
+ (*oc) -> data.data = &bp -> data [0];
+ (*oc) -> data.terminated = 0;
+ if (data)
+ memcpy ((*oc) -> data.data, data, len);
+ (*oc) -> option = option;
+ return 1;
}
-struct dns_host_entry *enter_dns_host (name)
+int make_host_lookup (expr, name)
+ struct expression **expr;
char *name;
{
- struct dns_host_entry *dh;
-
- if (!(dh = (struct dns_host_entry *)dmalloc
- (sizeof (struct dns_host_entry), "enter_dns_host")))
- error ("Can't allocate space for new host.");
- memset (dh, 0, sizeof *dh);
+ if (!expression_allocate (expr, "make_host_lookup")) {
+ warn ("No memory for host lookup tree node.");
+ return 0;
+ }
+ (*expr) -> op = expr_host_lookup;
+ if (!enter_dns_host (&((*expr) -> data.host_lookup), name)) {
+ expression_dereference (expr, "make_host_lookup");
+ return 0;
+ }
+ return 1;
+}
- dh -> hostname = dmalloc (strlen (name) + 1, "enter_dns_host");
- strcpy (dh -> hostname, name);
- return dh;
+int enter_dns_host (dh, name)
+ struct dns_host_entry **dh;
+ char *name;
+{
+ /* XXX This should really keep a hash table of hostnames
+ XXX and just add a new reference to a hostname that
+ XXX already exists, if possible, rather than creating
+ XXX a new structure. */
+ if (!dns_host_entry_allocate (dh, name)) {
+ warn ("Can't allocate space for new host.");
+ return 0;
+ }
+ return 1;
}
-struct expression *make_const_data (data, len, terminated, allocate)
+int make_const_data (expr, data, len, terminated, allocate)
+ struct expression **expr;
unsigned char *data;
int len;
int terminated;
int allocate;
{
struct expression *nt;
- if (!(nt = new_expression ("tree_const")))
- error ("No memory for constant data tree node.");
- memset (nt, 0, sizeof *nt);
+
+ if (!expression_allocate (expr, "make_host_lookup")) {
+ warn ("No memory for make_const_data tree node.");
+ return 0;
+ }
+ nt = *expr;
+
if (len) {
if (allocate) {
- if (!(nt -> data.const_data.data =
- (unsigned char *)dmalloc (len + terminated,
- "tree_const")))
- error ("No memory for const_data node.");
+ if (!buffer_allocate (&nt -> data.const_data.buffer,
+ len + terminated,
+ "make_const_data")) {
+ warn ("Can't allocate const_data buffer.");
+ expression_dereference (expr,
+ "make_const_data");
+ return 0;
+ }
+ nt -> data.const_data.data =
+ &nt -> data.const_data.buffer -> data [0];
memcpy (nt -> data.const_data.data,
data, len + terminated);
- nt -> data.const_data.buffer =
- nt -> data.const_data.data;
- } else
+ } else
nt -> data.const_data.data = data;
nt -> data.const_data.terminated = terminated;
} else
nt -> op = expr_const_data;
nt -> data.const_data.len = len;
- return nt;
+ return 1;
}
-struct expression *make_concat (left, right)
+int make_concat (expr, left, right)
+ struct expression **expr;
struct expression *left, *right;
{
- struct expression *nt;
-
/* If we're concatenating a null tree to a non-null tree, just
return the non-null tree; if both trees are null, return
a null tree. */
- if (!left)
- return right;
- if (!right)
- return left;
-
- /* If both expressions are constant, combine them. */
- if (left -> op == expr_const_data &&
- right -> op == expr_const_data) {
- unsigned char *buf =
- dmalloc (left -> data.const_data.len
- + right -> data.const_data.len
- + right -> data.const_data.terminated,
- "tree_concat");
- if (!buf)
- error ("No memory to concatenate constants.");
- memcpy (buf, left -> data.const_data.data,
- left -> data.const_data.len);
- memcpy (buf + left -> data.const_data.len,
- right -> data.const_data.data,
- right -> data.const_data.len);
- if (left -> data.const_data.buffer)
- dfree (left -> data.const_data.buffer, "make_concat");
- if (right -> data.const_data.buffer)
- dfree (right -> data.const_data.buffer, "make_concat");
- left -> data.const_data.data = buf;
- left -> data.const_data.buffer = buf;
- left -> data.const_data.len += right -> data.const_data.len;
- free_expression (right, "make_concat");
- return left;
+ if (!left) {
+ if (!right)
+ return 0;
+ expression_reference (expr, right, "make_concat");
+ return 1;
+ }
+ if (!right) {
+ expression_reference (expr, left, "make_concat");
+ return 1;
}
/* Otherwise, allocate a new node to concatenate the two. */
- if (!(nt = new_expression ("make_concat")))
- error ("No memory for concatenation expression node.");
- nt -> op = expr_concat;
- nt -> data.concat [0] = left;
- nt -> data.concat [1] = right;
- return nt;
+ if (!expression_allocate (expr, "make_concat")) {
+ warn ("No memory for concatenation expression node.");
+ return 0;
+ }
+
+ (*expr) -> op = expr_concat;
+ expression_reference (&(*expr) -> data.concat [0],
+ left, "make_concat");
+ expression_reference (&(*expr) -> data.concat [1],
+ right, "make_concat");
+ return 1;
}
-struct expression *make_substring (expr, offset, length)
+int make_substring (new, expr, offset, length)
+ struct expression **new;
struct expression *expr;
struct expression *offset;
struct expression *length;
{
- struct expression *rv;
-
- /* If the expression we're limiting is constant, limit it now. */
- if (expr -> op == expr_const_data &&
- offset -> op == expr_const_int &&
- length -> op == expr_const_int) {
- int off = offset -> data.const_int;
- int len = length -> data.const_int;
- if (expr -> data.const_data.len > off) {
- expr -> data.const_data.data += off;
- expr -> data.const_data.len -= off;
- if (expr -> data.const_data.len > len) {
- expr -> data.const_data.len = len;
- expr -> data.const_data.terminated = 0;
- }
- } else {
- expr -> data.const_data.len = 0;
- expr -> data.const_data.terminated = 0;
- }
-
- free_expression (offset, "make_substring");
- free_expression (length, "make_substring");
- return expr;
+ /* Allocate an expression node to compute the substring. */
+ if (!expression_allocate (new, "make_substring")) {
+ warn ("no memory for substring expression.");
+ return 0;
}
-
- /* Otherwise, put in a node which enforces the limit on evaluation. */
- rv = new_expression ("make_substring");
- if (!rv)
- error ("no memory for substring expression.");
- memset (rv, 0, sizeof *rv);
- rv -> op = expr_substring;
- rv -> data.substring.expr = expr;
- rv -> data.substring.offset = offset;
- rv -> data.substring.len = length;
- return rv;
+ (*new) -> op = expr_substring;
+ expression_reference (&(*new) -> data.substring.expr,
+ expr, "make_concat");
+ expression_reference (&(*new) -> data.substring.offset,
+ offset, "make_concat");
+ expression_reference (&(*new) -> data.substring.len,
+ length, "make_concat");
+ return 1;
}
-struct expression *make_limit (expr, limit)
+int make_limit (new, expr, limit)
+ struct expression **new;
struct expression *expr;
int limit;
{
struct expression *rv;
- /* If the expression we're limiting is constant, limit it now. */
- if (expr -> op == expr_const_data) {
- if (expr -> data.const_data.len > limit) {
- expr -> data.const_data.len = limit;
- expr -> data.const_data.terminated = 0;
- }
- return expr;
- }
-
- /* Otherwise, put in a node which enforces the limit on evaluation. */
- rv = new_expression ("make_limit 1");
- if (!rv)
- error ("no memory for limit expression");
- memset (rv, 0, sizeof *rv);
- rv -> op = expr_substring;
- rv -> data.substring.expr = expr;
+ /* Allocate a node to enforce a limit on evaluation. */
+ if (!expression_allocate (new, "make_limit"))
+ warn ("no memory for limit expression");
+ (*new) -> op = expr_substring;
+ expression_reference (&(*new) -> data.substring.expr,
+ expr, "make_limit");
/* Offset is a constant 0. */
- rv -> data.substring.offset = new_expression ("make_limit 2");
- if (!rv -> data.substring.offset)
- error ("no memory for limit offset expression");
- memset (rv -> data.substring.offset, 0, sizeof *rv);
- rv -> data.substring.offset -> op = expr_const_int;
- rv -> data.substring.offset -> data.const_int = 0;
+ if (!expression_allocate (&(*new) -> data.substring.offset,
+ "make_limit")) {
+ warn ("no memory for limit offset expression");
+ expression_dereference (new, "make_limit");
+ return 0;
+ }
+ (*new) -> data.substring.offset -> op = expr_const_int;
+ (*new) -> data.substring.offset -> data.const_int = 0;
/* Length is a constant: the specified limit. */
- rv -> data.substring.len = new_expression ("make_limit 2");
- if (!rv -> data.substring.len)
- error ("no memory for limit length expression");
- memset (rv -> data.substring.len, 0, sizeof *rv);
- rv -> data.substring.offset -> op = expr_const_int;
- rv -> data.substring.offset -> data.const_int = limit;
-
- return rv;
+ if (!expression_allocate (&(*new) -> data.substring.len,
+ "make_limit")) {
+ warn ("no memory for limit length expression");
+ expression_dereference (new, "make_limit");
+ return 0;
+ }
+ (*new) -> data.substring.len -> op = expr_const_int;
+ (*new) -> data.substring.len -> data.const_int = limit;
+
+ return 1;
}
-struct option_cache *option_cache (expr, option)
+int option_cache (oc, dp, expr, option)
+ struct option_cache **oc;
+ struct data_string *dp;
struct expression *expr;
struct option *option;
{
- struct option_cache *oc = new_option_cache ("option_cache");
- if (!oc) {
- warn ("no memory for option cache.");
- return (struct option_cache *)0;
- }
- memset (oc, 0, sizeof *oc);
- oc -> expression = expr;
- oc -> option = option;
- return oc;
+ if (!option_cache_allocate (oc, "option_cache"))
+ return 0;
+ if (dp)
+ data_string_copy (&(*oc) -> data, dp, "option_cache");
+ if (expr)
+ expression_reference (&(*oc) -> expression,
+ expr, "option_cache");
+ (*oc) -> option = option;
+ return 1;
}
-static struct data_string do_host_lookup (dns)
+int do_host_lookup (result, dns)
+ struct data_string *result;
struct dns_host_entry *dns;
{
struct hostent *h;
- int i;
+ int i, count;
int new_len;
- struct data_string result;
memset (&result, 0, sizeof result);
/* If the record hasn't timed out, just copy the data and return. */
if (cur_time <= dns -> timeout) {
#ifdef DEBUG_EVAL
- debug ("easy copy: %x %d %s",
- dns -> data, dns -> data.len,
- dns -> data.data
- ? inet_ntoa (*(struct in_addr *)(dns -> data.data))
- : 0);
+ debug ("easy copy: %d %s",
+ dns -> data.len,
+ (dns -> data.len > 4
+ ? inet_ntoa (*(struct in_addr *)(dns -> data.data))
+ : 0));
#endif
- result.data = dns -> buffer;
- result.len = dns -> data_len;
- return result;
+ data_string_copy (result, &dns -> data, "do_host_lookup");
+ return 1;
}
#ifdef DEBUG_EVAL
debug ("Looking up %s", dns -> hostname);
/* Okay to try again after a minute. */
dns -> timeout = cur_time + 60;
- return result;
+ data_string_forget (&dns -> data, "do_host_lookup");
+ return 0;
}
#ifdef DEBUG_EVAL
#endif
/* Count the number of addresses we got... */
- for (i = 0; h -> h_addr_list [i]; i++)
+ for (count = 0; h -> h_addr_list [count]; count++)
;
+ /* Dereference the old data, if any. */
+ data_string_forget (&dns -> data, "do_host_lookup");
+
/* Do we need to allocate more memory? */
- new_len = i * h -> h_length;
- if (dns -> buf_len < i) {
- unsigned char *buf =
- (unsigned char *)dmalloc (new_len, "do_host_lookup");
- /* If we didn't get more memory, use what we have. */
- if (!buf) {
- new_len = dns -> buf_len;
- if (!dns -> buf_len) {
- dns -> timeout = cur_time + 60;
- return result;
- }
- } else {
- if (dns -> buffer)
- dfree (dns -> buffer, "do_host_lookup");
- dns -> buffer = buf;
- dns -> buf_len = new_len;
- }
+ new_len = count * h -> h_length;
+ if (!buffer_allocate (&dns -> data.buffer, new_len, "do_host_lookup"))
+ {
+ warn ("No memory for %s.", dns -> hostname);
+ return 0;
}
+ dns -> data.data = &dns -> data.buffer -> data [0];
+ dns -> data.len = new_len;
+ dns -> data.terminated = 0;
+
/* Addresses are conveniently stored one to the buffer, so we
have to copy them out one at a time... :'( */
- for (i = 0; i < new_len / h -> h_length; i++) {
- memcpy (dns -> buffer + h -> h_length * i,
+ for (i = 0; i < count; i++) {
+ memcpy (&dns -> data.data [h -> h_length * i],
h -> h_addr_list [i], h -> h_length);
}
#ifdef DEBUG_EVAL
debug ("dns -> data: %x h -> h_addr_list [0]: %x",
*(int *)(dns -> buffer), h -> h_addr_list [0]);
#endif
- dns -> data_len = new_len;
- /* Set the timeout for an hour from now.
+ /* XXX Set the timeout for an hour from now.
XXX This should really use the time on the DNS reply. */
dns -> timeout = cur_time + 3600;
#ifdef DEBUG_EVAL
- debug ("hard copy: %x %d %x",
- dns -> data, dns -> data_len, *(int *)(dns -> data));
+ debug ("hard copy: %d %s", dns -> data.len,
+ (dns -> data.len > 4
+ ? inet_ntoa (*(struct in_addr *)(dns -> data.data)) : 0));
#endif
- result.data = dns -> buffer;
- result.len = dns -> data_len;
- return result;
+ data_string_copy (result, &dns -> data, "do_host_lookup");
+ return 1;
}
-int evaluate_boolean_expression (packet, expr)
+int evaluate_boolean_expression (result, packet, options, expr)
+ int *result;
struct packet *packet;
+ struct option_state *options;
struct expression *expr;
{
struct data_string left, right;
- int result;
+ int bleft, bright;
+ int sleft, sright;
switch (expr -> op) {
case expr_check:
- return check_collection (packet, expr -> data.check);
+#if defined (DEBUG_EXPRESSIONS)
+ *result = check_collection (packet, expr -> data.check);
+ note ("bool: check (%s) returns %s",
+ expr -> data.check -> name, *result ? "true" : "false");
+#endif
+ return 1;
case expr_equal:
- left = evaluate_data_expression (packet,
- expr -> data.equal [0]);
- right = evaluate_data_expression (packet,
- expr -> data.equal [1]);
- if (left.len == right.len && !memcmp (left.data,
- right.data, left.len))
- result = 1;
- else
- result = 0;
- if (left.buffer)
- dfree ("evaluate_boolean_expression", left.buffer);
- if (right.buffer)
- dfree ("evaluate_boolean_expression", right.buffer);
- return result;
+ memset (&left, 0, sizeof left);
+ sleft = evaluate_data_expression (&left, packet, options,
+ expr -> data.equal [0]);
+ memset (&right, 0, sizeof right);
+ sright = evaluate_data_expression (&right, packet, options,
+ expr -> data.equal [1]);
+ if (sleft && sright) {
+ if (left.len == right.len &&
+ !memcmp (left.data, right.data, left.len))
+ *result = 1;
+ else
+ *result = 0;
+ }
+
+#if defined (DEBUG_EXPRESSIONS)
+ note ("bool: equal (%s, %s) = %s",
+ sleft ? print_hex_1 (left.len, left.data, 30) : "NULL",
+ sright ? print_hex_2 (right.len,
+ right.data, 30) : "NULL",
+ ((sleft && sright)
+ ? (*result ? "true" : "false")
+ : "NULL"));
+#endif
+ if (sleft)
+ data_string_forget (&left,
+ "evaluate_boolean_expression");
+ if (sright)
+ data_string_forget (&right,
+ "evaluate_boolean_expression");
+ return sleft && sright;
case expr_and:
- return (evaluate_boolean_expression (packet,
- expr -> data.and [0]) &&
- evaluate_boolean_expression (packet,
- expr -> data.and [1]));
+ sleft = evaluate_boolean_expression (&bleft, packet, options,
+ expr -> data.and [0]);
+ sright = evaluate_boolean_expression (&bright, packet, options,
+ expr -> data.and [1]);
+
+#if defined (DEBUG_EXPRESSIONS)
+ note ("bool: and (%s, %s) = %s",
+ sleft ? (bleft ? "true" : "false") : "NULL",
+ sright ? (bright ? "true" : "false") : "NULL",
+ ((sleft && sright)
+ ? (bleft && bright ? "true" : "false") : "NULL"));
+#endif
+ if (sleft && sright) {
+ *result = bleft && bright;
+ return 1;
+ }
+ return 0;
case expr_or:
- return (evaluate_boolean_expression (packet,
- expr -> data.or [0]) ||
- evaluate_boolean_expression (packet,
- expr -> data.or [1]));
+ sleft = evaluate_boolean_expression (&bleft, packet, options,
+ expr -> data.or [0]);
+ sright = evaluate_boolean_expression (&bright, packet, options,
+ expr -> data.or [1]);
+#if defined (DEBUG_EXPRESSIONS)
+ note ("bool: or (%s, %s) = %s",
+ sleft ? (bleft ? "true" : "false") : "NULL",
+ sright ? (bright ? "true" : "false") : "NULL",
+ ((sleft && sright)
+ ? (bleft || bright ? "true" : "false") : "NULL"));
+#endif
+ if (sleft && sright) {
+ *result = bleft || bright;
+ return 1;
+ }
+ return 0;
case expr_not:
- return (!evaluate_boolean_expression (packet,
- expr -> data.not));
+ sleft = evaluate_boolean_expression (&bleft, packet, options,
+ expr -> data.not);
+#if defined (DEBUG_EXPRESSIONS)
+ note ("bool: not (%s) = %s",
+ sleft ? (bleft ? "true" : "false") : "NULL",
+ ((sleft && sright)
+ ? (!bleft ? "true" : "false") : "NULL"));
+
+#endif
+ if (sleft) {
+ *result = !bleft;
+ return 1;
+ }
+ return 0;
+
+ case expr_exists:
+ memset (&left, 0, sizeof left);
+ if (!((*expr -> data.option -> universe -> lookup_func)
+ (&left, options, expr -> data.exists -> code)))
+ *result = 0;
+ else {
+ *result = 1;
+ data_string_forget (&left,
+ "evaluate_boolean_expression");
+ }
+#if defined (DEBUG_EXPRESSIONS)
+ note ("data: exists %s.%s = %s",
+ expr -> data.option -> universe -> name,
+ expr -> data.option -> name, *result ? "true" : "false");
+#endif
+ return 1;
case expr_substring:
case expr_suffix:
return 0;
}
-struct data_string evaluate_data_expression (packet, expr)
+int evaluate_data_expression (result, packet, options, expr)
+ struct data_string *result;
struct packet *packet;
+ struct option_state *options;
struct expression *expr;
{
- struct data_string result, data, other;
- int offset, len;
+ struct data_string data, other;
+ unsigned long offset, len;
+ int s0, s1, s2, s3;
switch (expr -> op) {
/* Extract N bytes starting at byte M of a data string. */
case expr_substring:
- data = evaluate_data_expression (packet,
- expr -> data.substring.expr);
+ memset (&data, 0, sizeof data);
+ s0 = evaluate_data_expression (&data, packet, options,
+ expr -> data.substring.expr);
/* Evaluate the offset and length. */
- offset = evaluate_numeric_expression
- (packet, expr -> data.substring.offset);
- len = evaluate_numeric_expression
- (packet, expr -> data.substring.len);
+ s1 = evaluate_numeric_expression
+ (&offset,
+ packet, options, expr -> data.substring.offset);
+ s2 = evaluate_numeric_expression (&len, packet, options,
+ expr -> data.substring.len);
/* If the offset is after end of the string, return
an empty string. */
- if (data.len <= offset) {
- if (data.buffer)
- dfree ("expr_substring", data.buffer);
- memset (&result, 0, sizeof result);
- return result;
- }
+ if (s0 && s1 && s2 && data.len > offset) {
+ /* Otherwise, do the adjustments and return
+ what's left. */
+ data_string_copy (result, &data,
+ "evaluate_data_expression");
+ result -> len -= offset;
+ if (result -> len > len) {
+ result -> len = len;
+ result -> terminated = 0;
+ }
+ result -> data += offset;
+ s3 = 1;
+ } else
+ s3 = 0;
+
+#if defined (DEBUG_EXPRESSIONS)
+ note ("data: substring (%s, %s, %s) = %s",
+ s0 ? print_hex_1 (data.len, data.data, 30) : "NULL",
+ s1 ? print_dec_1 (offset) : "NULL",
+ s2 ? print_dec_2 (len) : "NULL",
+ (s3 ? print_hex_2 (result -> len, result -> data, 30)
+ : "NULL"));
+#endif
+ if (s3)
+ return 1;
+ data_string_forget (&data, "evaluate_data_expression");
+ return 0;
- /* Otherwise, do the adjustments and return what's left. */
- data.len -= offset;
- if (data.len > len) {
- data.len = len;
- data.terminated = 0;
- }
- data.data += offset;
- return data;
/* Extract the last N bytes of a data string. */
case expr_suffix:
- data = evaluate_data_expression (packet,
- expr -> data.suffix.expr);
-
+ memset (&data, 0, sizeof data);
+ s0 = evaluate_data_expression (&data, packet, options,
+ expr -> data.suffix.expr);
/* Evaluate the length. */
- len = evaluate_numeric_expression
- (packet, expr -> data.substring.len);
+ s1 = evaluate_numeric_expression (&len, packet, options,
+ expr -> data.substring.len);
+ if (s0 && s1) {
+ data_string_copy (result, &data,
+ "evaluate_data_expression");
+
+ /* If we are returning the last N bytes of a
+ string whose length is <= N, just return
+ the string - otherwise, compute a new
+ starting address and decrease the
+ length. */
+ if (data.len > len) {
+ result -> data += data.len - len;
+ result -> len = len;
+ }
+ }
- /* If we are returning the last N bytes of a string whose
- length is <= N, just return the string. */
- if (data.len <= len)
- return data;
- data.data += data.len - len;
- data.len = len;
- return data;
+#if defined (DEBUG_EXPRESSIONS)
+ note ("data: suffix (%s, %d) = %s",
+ s0 ? print_hex_1 (data.len, data.data, 30) : "NULL",
+ s1 ? print_dec_1 (len) : "NULL",
+ ((s0 && s1)
+ ? print_hex_2 (result -> len, result -> data, 30)
+ : NULL));
+#endif
+ return 1;
/* Extract an option. */
case expr_option:
- return ((*expr -> data.option -> universe -> lookup_func)
- (packet, expr -> data.option -> code));
+ s0 = ((*expr -> data.option -> universe -> lookup_func)
+ (result, options, expr -> data.option -> code));
+#if defined (DEBUG_EXPRESSIONS)
+ note ("data: option %s.%s = %s",
+ expr -> data.option -> universe -> name,
+ expr -> data.option -> name,
+ s0 ? print_hex_1 (result -> len, result -> data, 60)
+ : "NULL");
+#endif
+ return s0;
/* Combine the hardware type and address. */
case expr_hardware:
- result.len = packet -> raw -> hlen + 1;
- result.buffer = dmalloc (result.len,
- "expr_hardware");
- if (!result.buffer) {
- warn ("no memory for expr_hardware");
- result.len = 0;
- } else {
- result.buffer [0] = packet -> raw -> htype;
- memcpy (&result.buffer [1], packet -> raw -> chaddr,
+ if (!packet || !packet -> raw) {
+ warn ("data: hardware: raw packet not available");
+ return 0;
+ }
+ result -> len = packet -> raw -> hlen + 1;
+ if (buffer_allocate (&result -> buffer, result -> len,
+ "evaluate_data_expression")) {
+ result -> data = &result -> buffer -> data [0];
+ result -> data [0] = packet -> raw -> htype;
+ memcpy (&result -> data [1], packet -> raw -> chaddr,
packet -> raw -> hlen);
+ result -> terminated = 0;
+ } else {
+ warn ("data: hardware: no memory for buffer.");
+ return 0;
}
- result.data = result.buffer;
- result.terminated = 0;
- return result;
+#if defined (DEBUG_EXPRESSIONS)
+ note ("data: hardware = %s",
+ print_hex_1 (result -> len, result -> data, 60));
+#endif
+ return 1;
/* Extract part of the raw packet. */
case expr_packet:
- len = evaluate_numeric_expression (packet,
- expr -> data.packet.len);
- offset = evaluate_numeric_expression (packet,
- expr -> data.packet.len);
- if (offset > packet -> packet_length) {
- warn ("expr_packet on %s: length %d + offset %d > %d",
- print_hw_addr (packet -> raw -> htype,
- packet -> raw -> hlen,
- packet -> raw -> chaddr),
- len, offset, packet -> packet_length);
- memset (&result, 0, sizeof result);
- return result;
+ if (!packet || !packet -> raw) {
+ warn ("data: packet: raw packet not available");
+ return 0;
}
- if (offset + len > packet -> packet_length)
- result.len = packet -> packet_length - offset;
- else
- result.len = len;
- result.data = ((unsigned char *)(packet -> raw)) + offset;
- result.buffer = (unsigned char *)0;
- result.terminated = 0;
- return result;
+
+ s0 = evaluate_numeric_expression (&len, packet, options,
+ expr -> data.packet.len);
+ s1 = evaluate_numeric_expression (&offset, packet, options,
+ expr -> data.packet.len);
+ if (s0 && s1 && offset < packet -> packet_length) {
+ if (offset + len > packet -> packet_length)
+ result -> len =
+ packet -> packet_length - offset;
+ else
+ result -> len = len;
+ if (buffer_allocate (&result -> buffer, result -> len,
+ "evaluate_data_expression")) {
+ result -> data = &result -> buffer -> data [0];
+ memcpy (result -> data,
+ (((unsigned char *)(packet -> raw))
+ + offset), result -> len);
+ result -> terminated = 0;
+ } else {
+ warn ("data: packet: no memory for buffer.");
+ return 0;
+ }
+ s2 = 1;
+ } else
+ s2 = 0;
+#if defined (DEBUG_EXPRESSIONS)
+ note ("data: packet (%d, %d) = %s",
+ offset, len,
+ s2 ? print_hex_1 (result -> len,
+ result -> data, 60) : NULL);
+#endif
+ return s2;
/* Some constant data... */
case expr_const_data:
- return expr -> data.const_data;
+#if defined (DEBUG_EXPRESSIONS)
+ note ("data: const = %s",
+ print_hex_1 (expr -> data.const_data.len,
+ expr -> data.const_data.data, 60));
+#endif
+ data_string_copy (result,
+ &expr -> data.const_data,
+ "evaluate_data_expression");
+ return 1;
/* Hostname lookup... */
case expr_host_lookup:
- return do_host_lookup (expr -> data.host_lookup);
- break;
+ s0 = do_host_lookup (result, expr -> data.host_lookup);
+#if defined (DEBUG_EXPRESSIONS)
+ note ("data: DNS lookup (%s) = %s",
+ expr -> data.host_lookup -> hostname,
+ (s0
+ ? print_dotted_quads (result -> len, result -> data)
+ : "NULL"));
+#endif
+ return s0;
/* Concatenation... */
case expr_concat:
- data = evaluate_data_expression (packet,
- expr -> data.concat [0]);
- other = evaluate_data_expression (packet,
- expr -> data.concat [1]);
-
- memset (&result, 0, sizeof result);
- result.buffer = dmalloc (data.len + other.len +
- other.terminated, "expr_concat");
- if (!result.buffer) {
- warn ("out of memory doing concatenation.");
- return result;
- }
-
- result.len = (data.len + other.len);
- result.data = result.buffer;
- memcpy (result.data, data.data, data.len);
- memcpy (&result.data [data.len], other.data,
- other.len + other.terminated);
- if (data.buffer)
- dfree (data.buffer, "expr_concat");
- if (other.buffer)
- dfree (other.buffer, "expr_concat");
- return result;
- break;
+ memset (&data, 0, sizeof data);
+ s0 = evaluate_data_expression (&data, packet, options,
+ expr -> data.concat [0]);
+ memset (&data, 0, sizeof data);
+ s1 = evaluate_data_expression (&other, packet, options,
+ expr -> data.concat [1]);
+
+ if (s0 && s1) {
+ result -> len = data.len + other.len;
+ if (!buffer_allocate (&result -> buffer,
+ (result -> len +
+ other.terminated),
+ "expr_concat")) {
+ warn ("data: concat: no memory");
+ result -> len = 0;
+ data_string_forget (&data, "expr_concat");
+ data_string_forget (&other, "expr_concat");
+ return 0;
+ }
+ result -> data = &result -> buffer -> data [0];
+ memcpy (result -> data, data.data, data.len);
+ memcpy (&result -> data [data.len],
+ other.data, other.len + other.terminated);
+ } else if (s0)
+ data_string_copy (result, &data, "expr_concat");
+ else if (s1)
+ data_string_copy (result, &other, "expr_concat");
+#if defined (DEBUG_EXPRESSIONS)
+ note ("data: concat (%s, %s) = %s",
+ s0 ? print_hex_1 (data.len, data.data, 20) : "NULL",
+ s1 ? print_hex_2 (other.len, other.data, 20) : "NULL",
+ ((s0 || s1)
+ ? print_hex_3 (result -> len, result -> data, 30)
+ : "NULL"));
+#endif
+ return s0 || s1;
case expr_check:
case expr_equal:
case expr_and:
case expr_or:
case expr_not:
+ case expr_match:
warn ("Boolean opcode in evaluate_data_expression: %d",
expr -> op);
- goto null_return;
+ return 0;
case expr_extract_int8:
case expr_extract_int16:
case expr_const_int:
warn ("Numeric opcode in evaluate_data_expression: %d",
expr -> op);
- goto null_return;
+ return 0;
}
warn ("Bogus opcode in evaluate_data_expression: %d", expr -> op);
- null_return:
- memset (&result, 0, sizeof result);
- return result;
+ return 0;
}
-unsigned long evaluate_numeric_expression (packet, expr)
+int evaluate_numeric_expression (result, packet, options, expr)
+ unsigned long *result;
struct packet *packet;
+ struct option_state *options;
struct expression *expr;
{
struct data_string data;
- unsigned long result;
+ int status;
switch (expr -> op) {
case expr_check:
case expr_and:
case expr_or:
case expr_not:
+ case expr_match:
warn ("Boolean opcode in evaluate_numeric_expression: %d",
expr -> op);
return 0;
return 0;
case expr_extract_int8:
- data = evaluate_data_expression (packet,
- expr ->
- data.extract_int.expr);
- if (data.len < 1)
- return 0;
- result = data.data [0];
- if (data.buffer)
- dfree (data.buffer, "expr_extract_int8");
- return result;
+ memset (&data, 0, sizeof data);
+ status = evaluate_data_expression
+ (&data, packet,
+ options, expr -> data.extract_int);
+ if (status)
+ *result = data.data [0];
+#if defined (DEBUG_EXPRESSIONS)
+ note ("num: extract_int8 (%s) = %s",
+ status ? print_hex_1 (data.len, data.data, 60) : "NULL",
+ status ? print_dec_1 (*result) : "NULL" );
+#endif
+ if (status)
+ data_string_forget (&data, "expr_extract_int8");
+ return status;
case expr_extract_int16:
- data = evaluate_data_expression (packet,
- expr ->
- data.extract_int.expr);
- if (data.len < 2)
- return 0;
- result = getUShort (data.data);
- if (data.buffer)
- dfree (data.buffer, "expr_extract_int16");
- return result;
+ memset (&data, 0, sizeof data);
+ status = (evaluate_data_expression
+ (&data, packet, options,
+ expr -> data.extract_int));
+ if (status && data.len >= 2)
+ *result = getUShort (data.data);
+#if defined (DEBUG_EXPRESSIONS)
+ note ("num: extract_int16 (%s) = %ld",
+ ((status && data.len >= 2) ?
+ print_hex_1 (data.len, data.data, 60) : "NULL"),
+ *result);
+#endif
+ if (status)
+ data_string_forget (&data, "expr_extract_int16");
+ return (status && data.len >= 2);
case expr_extract_int32:
- data = evaluate_data_expression (packet,
- expr ->
- data.extract_int.expr);
- if (data.len < 4)
- return 0;
- result = getULong (data.data);
- if (data.buffer)
- dfree (data.buffer, "expr_extract_int32");
- return result;
+ memset (&data, 0, sizeof data);
+ status = (evaluate_data_expression
+ (&data, packet, options,
+ expr -> data.extract_int));
+ if (status && data.len >= 4)
+ *result = getULong (data.data);
+#if defined (DEBUG_EXPRESSIONS)
+ note ("num: extract_int32 (%s) = %ld",
+ ((status && data.len >= 4) ?
+ print_hex_1 (data.len, data.data, 60) : "NULL"),
+ *result);
+#endif
+ if (status)
+ data_string_forget (&data, "expr_extract_int32");
+ return (status && data.len >= 4);
case expr_const_int:
- return expr -> data.const_int;
+ *result = expr -> data.const_int;
+ return 1;
}
warn ("Bogus opcode in evaluate_numeric_expression: %d", expr -> op);
return 0;
}
-void free_oc_ephemeral_state (oc)
+/* Return data hanging off of an option cache structure, or if there
+ isn't any, evaluate the expression hanging off of it and return the
+ result of that evaluation. There should never be both an expression
+ and a valid data_string. */
+
+int evaluate_option_cache (result, packet, options, oc)
+ struct data_string *result;
+ struct packet *packet;
+ struct option_state *options;
+ struct option_cache *oc;
+{
+ if (oc -> data.len) {
+ data_string_copy (result,
+ &oc -> data, "evaluate_option_cache");
+ return 1;
+ }
+ return evaluate_data_expression (result,
+ packet, options, oc -> expression);
+}
+
+/* Evaluate an option cache and extract a boolean from the result,
+ returning the boolean. Return false if there is no data. */
+
+int evaluate_boolean_option_cache (packet, options, oc)
+ struct packet *packet;
+ struct option_state *options;
struct option_cache *oc;
{
- if (free_ephemeral_outer_tree (expr))
- free_option_cache (oc, "free_oc_ephemeral_state");
+ struct data_string ds;
+ int result;
+
+ /* So that we can be called with option_lookup as an argument. */
+ if (!oc)
+ return 0;
+
+ memset (&ds, 0, sizeof ds);
+ if (!evaluate_option_cache (&ds, packet, options, oc))
+ return 0;
+
+ if (ds.len && ds.data [0])
+ result = 1;
+ else
+ result = 0;
+ data_string_forget (&ds, "evaluate_boolean_option_cache");
+ return result;
}
+
-/* Recursively free any ephemeral subexpressions of the passed expression,
- and then free that expression. */
+/* Evaluate a boolean expression and return the result of the evaluation,
+ or FALSE if it failed. */
-int free_ephemeral_outer_tree (expr)
+int evaluate_boolean_expression_result (packet, options, expr)
+ struct packet *packet;
+ struct option_state *options;
struct expression *expr;
{
- /* If this expression isn't ephemeral, notify the caller. */
- if (!(expr -> flags & EXPR_EPHEMERAL))
+ struct data_string ds;
+ int result;
+
+ /* So that we can be called with option_lookup as an argument. */
+ if (!expr)
+ return 0;
+
+ memset (&ds, 0, sizeof ds);
+ if (!evaluate_data_expression (&ds, packet, options, expr))
return 0;
- /* Free any ephemeral subexpressions... */
+ if (ds.len && ds.data [0])
+ result = 1;
+ else
+ result = 0;
+ data_string_forget (&ds, "evaluate_boolean_expression_result");
+ return result;
+}
+
+
+/* Dereference an expression node, and if the reference count goes to zero,
+ dereference any data it refers to, and then free it. */
+void expression_dereference (eptr, name)
+ struct expression **eptr;
+ char *name;
+{
+ struct expression *expr = *eptr;
+
+ /* Zero the pointer. */
+ *eptr = (struct expression *)0;
+
+ /* Decrement the reference count. If it's nonzero, we're
+ done. */
+ if (--(expr -> refcnt) > 0)
+ return;
+ if (expr -> refcnt < 0) {
+ warn ("expression_dereference: negative refcnt!");
+ abort ();
+ }
+
+ /* Dereference subexpressions. */
switch (expr -> op) {
/* All the binary operators can be handled the same way. */
case expr_equal:
case expr_concat:
case expr_and:
case expr_or:
- free_ephemeral_outer_tree (expr -> data.equal [0]);
- free_ephemeral_outer_tree (expr -> data.equal [1]);
+ if (expr -> data.equal [0])
+ expression_dereference (&expr -> data.equal [0], name);
+ if (expr -> data.equal [1])
+ expression_dereference (&expr -> data.equal [1], name);
break;
case expr_substring:
- free_ephemeral_outer_tree (expr -> data.substring.expr);
- free_ephemeral_outer_tree (expr -> data.substring.offset);
- free_ephemeral_outer_tree (expr -> data.substring.len);
+ if (expr -> data.substring.expr)
+ expression_dereference (&expr -> data.substring.expr,
+ name);
+ if (expr -> data.substring.offset)
+ expression_dereference (&expr -> data.substring.offset,
+ name);
+ if (expr -> data.substring.len)
+ expression_dereference (&expr -> data.substring.len,
+ name);
break;
case expr_suffix:
- free_ephemeral_outer_tree (expr -> data.suffix.expr);
- free_ephemeral_outer_tree (expr -> data.suffix.len);
+ if (expr -> data.suffix.expr)
+ expression_dereference (&expr -> data.suffix.expr,
+ name);
+ if (expr -> data.suffix.len)
+ expression_dereference (&expr -> data.suffix.len,
+ name);
break;
case expr_not:
- free_ephemeral_outer_tree (expr -> data.not);
+ if (expr -> data.not)
+ expression_dereference (&expr -> data.not, name);
break;
case expr_packet:
- free_ephemeral_outer_tree (expr -> data.packet.offset);
- free_ephemeral_outer_tree (expr -> data.packet.len);
+ if (expr -> data.packet.offset)
+ expression_dereference (&expr -> data.packet.offset,
+ name);
+ if (expr -> data.packet.len)
+ expression_dereference (&expr -> data.packet.len,
+ name);
break;
case expr_extract_int8:
case expr_extract_int16:
case expr_extract_int32:
- free_ephemeral_outer_tree (expr -> data.extract_int.expr);
- free_ephemeral_outer_tree (expr -> data.extract_int.width);
+ if (expr -> data.extract_int)
+ expression_dereference (&expr -> data.extract_int,
+ name);
+ break;
+
+ case expr_const_data:
+ data_string_forget (&expr -> data.const_data, name);
+ break;
+
+ case expr_host_lookup:
+ if (expr -> data.host_lookup)
+ dns_host_entry_dereference (&expr -> data.host_lookup,
+ name);
break;
/* No subexpressions. */
case expr_const_int:
case expr_check:
- case expr_host_lookup:
case expr_option:
- case expr_const_data:
case expr_hardware:
+ case expr_exists:
break;
default:
break;
}
- free_expression (expr, "free_expr_outer_tree");
- return 1;
+ free_expression (expr, "expression_dereference");
}
+
/* Free all of the state in an option state buffer. The buffer itself is
not freed, since these buffers are always contained in other structures. */
-void free_option_state (state)
+void option_state_dereference (state)
struct option_state *state;
{
int i;
- struct agent_option *ao;
+ struct agent_options *a, *na;
+ struct option_tag *ot, *not;
+ pair cp, next;
+
+ /* Having done the cons_options(), we can release the tree_cache
+ entries. */
+ for (i = 0; i < OPTION_HASH_SIZE; i++) {
+ for (cp = state -> dhcp_hash [i]; cp; cp = next) {
+ next = cp -> cdr;
+ option_cache_dereference
+ ((struct option_cache **)&cp -> car,
+ "option_state_dereference");
+ free_pair (cp, "option_state_dereference");
+ }
+ for (cp = state -> server_hash [i]; cp; cp = next) {
+ next = cp -> cdr;
+ option_cache_dereference
+ ((struct option_cache **)&cp -> car,
+ "option_state_dereference");
+ }
+ }
+
+ /* We can also release the agent options, if any... */
+ for (a = state -> agent_options; a; a = na) {
+ na = a -> next;
+ for (ot = a -> first; ot; ot = not) {
+ not = ot -> next;
+ free (ot);
+ }
+ }
+}
+
+/* Make a copy of the data in data_string, upping the buffer reference
+ count if there's a buffer. */
+
+void data_string_copy (dest, src, name)
+ struct data_string *dest;
+ struct data_string *src;
+ char *name;
+{
+ if (src -> buffer)
+ buffer_reference (&dest -> buffer, src -> buffer, name);
+ dest -> data = src -> data;
+ dest -> terminated = src -> terminated;
+ dest -> len = src -> len;
+}
+
+/* Release the reference count to a data string's buffer (if any) and
+ zero out the other information, yielding the null data string. */
+
+void data_string_forget (data, name)
+ struct data_string *data;
+ char *name;
+{
+ if (data -> buffer)
+ buffer_dereference (&data -> buffer, name);
+ memset (data, 0, sizeof *data);
+}
+
+/* Make a copy of the data in data_string, upping the buffer reference
+ count if there's a buffer. */
+
+void data_string_truncate (dp, len)
+ struct data_string *dp;
+ int len;
+{
+ if (len < dp -> len)
+ dp -> terminated = 0;
+ dp -> len = len;
+}
+
+int is_boolean_expression (expr)
+ struct expression *expr;
+{
+ return (expr -> op == expr_check ||
+ expr -> op == expr_equal ||
+ expr -> op == expr_and ||
+ expr -> op == expr_or ||
+ expr -> op == expr_not);
+}
+
+int is_data_expression (expr)
+ struct expression *expr;
+{
+ return (expr -> op == expr_substring ||
+ expr -> op == expr_suffix ||
+ expr -> op == expr_option ||
+ expr -> op == expr_hardware ||
+ expr -> op == expr_const_data ||
+ expr -> op == expr_packet ||
+ expr -> op == expr_concat ||
+ expr -> op == expr_host_lookup);
+}
+
+int is_numeric_expression (expr)
+ struct expression *expr;
+{
+ return (expr -> op == expr_extract_int8 ||
+ expr -> op == expr_extract_int16 ||
+ expr -> op == expr_extract_int32 ||
+ expr -> op == expr_const_int);
+}
+
+static int op_val PROTO ((enum expr_op));
+
+static int op_val (op)
+ enum expr_op op;
+{
+ switch (op) {
+ case expr_none:
+ case expr_match:
+ case expr_check:
+ case expr_substring:
+ case expr_suffix:
+ case expr_concat:
+ case expr_host_lookup:
+ case expr_not:
+ case expr_option:
+ case expr_hardware:
+ case expr_packet:
+ case expr_const_data:
+ case expr_extract_int8:
+ case expr_extract_int16:
+ case expr_extract_int32:
+ case expr_const_int:
+ case expr_exists:
+ return 100;
+
+ case expr_equal:
+ return 3;
+
+ case expr_and:
+ return 1;
+
+ case expr_or:
+ return 2;
+ }
+ return 100;
+}
+
+int op_precedence (op1, op2)
+ enum expr_op op1, op2;
+{
+ int ov1, ov2;
+
+ return op_val (op1) - op_val (op2);
+}
+
+enum expression_context op_context (op)
+ enum expr_op op;
+{
+ switch (op) {
+ case expr_none:
+ case expr_match:
+ case expr_check:
+ case expr_substring:
+ case expr_suffix:
+ case expr_concat:
+ case expr_host_lookup:
+ case expr_not:
+ case expr_option:
+ case expr_hardware:
+ case expr_packet:
+ case expr_const_data:
+ case expr_extract_int8:
+ case expr_extract_int16:
+ case expr_extract_int32:
+ case expr_const_int:
+ case expr_exists:
+ return context_any;
+
+ case expr_equal:
+ return context_data;
+
+ case expr_and:
+ return context_boolean;
+
+ case expr_or:
+ return context_boolean;
+ }
+ return context_any;
+}