summaryrefslogtreecommitdiffstats
path: root/contrib/isc-dhcp/common/tree.c
diff options
context:
space:
mode:
Diffstat (limited to 'contrib/isc-dhcp/common/tree.c')
-rw-r--r--contrib/isc-dhcp/common/tree.c4195
1 files changed, 3942 insertions, 253 deletions
diff --git a/contrib/isc-dhcp/common/tree.c b/contrib/isc-dhcp/common/tree.c
index ac83d67..a9254cc 100644
--- a/contrib/isc-dhcp/common/tree.c
+++ b/contrib/isc-dhcp/common/tree.c
@@ -3,7 +3,7 @@
Routines for manipulating parse trees... */
/*
- * Copyright (c) 1995, 1996, 1997 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,281 +34,317 @@
* 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: tree.c,v 1.10 1997/05/09 08:14:57 mellon Exp $ Copyright (c) 1995, 1996, 1997 The Internet Software Consortium. All rights reserved.\n";
+"$Id: tree.c,v 1.101.2.6 2001/10/18 20:12:16 mellon Exp $ Copyright (c) 1995-2000 The Internet Software Consortium. All rights reserved.\n";
#endif /* not lint */
#include "dhcpd.h"
+#include <omapip/omapip_p.h>
-static TIME tree_evaluate_recurse PROTO ((int *, unsigned char **, int *,
- struct tree *));
-static TIME do_host_lookup PROTO ((int *, unsigned char **, int *,
- struct dns_host_entry *));
-static void do_data_copy PROTO ((int *, unsigned char **, int *,
- unsigned char *, int));
+struct binding_scope *global_scope;
+
+static int do_host_lookup PROTO ((struct data_string *,
+ struct dns_host_entry *));
+
+#ifdef NSUPDATE
+struct __res_state resolver_state;
+int resolver_inited = 0;
+#endif
pair cons (car, cdr)
caddr_t car;
pair cdr;
{
- pair foo = (pair)dmalloc (sizeof *foo, "cons");
+ pair foo = (pair)dmalloc (sizeof *foo, MDL);
if (!foo)
- error ("no memory for cons.");
+ log_fatal ("no memory for cons.");
foo -> car = car;
foo -> cdr = cdr;
return foo;
}
-struct tree_cache *tree_cache (tree)
- struct tree *tree;
+int make_const_option_cache (oc, buffer, data, len, option, file, line)
+ struct option_cache **oc;
+ struct buffer **buffer;
+ u_int8_t *data;
+ unsigned len;
+ struct option *option;
+ const char *file;
+ int line;
{
- struct tree_cache *tc;
+ struct buffer *bp;
+
+ if (buffer) {
+ bp = *buffer;
+ *buffer = 0;
+ } else {
+ bp = (struct buffer *)0;
+ if (!buffer_allocate (&bp, len, file, line)) {
+ log_error ("%s(%d): can't allocate buffer.",
+ file, line);
+ return 0;
+ }
+ }
- tc = new_tree_cache ("tree_cache");
- if (!tc)
+ if (!option_cache_allocate (oc, file, line)) {
+ log_error ("%s(%d): can't allocate option cache.", file, line);
+ buffer_dereference (&bp, file, line);
return 0;
- tc -> value = (unsigned char *)0;
- tc -> len = tc -> buf_size = 0;
- tc -> timeout = 0;
- tc -> tree = tree;
- return tc;
+ }
+
+ (*oc) -> data.len = len;
+ (*oc) -> data.buffer = bp;
+ (*oc) -> data.data = &bp -> data [0];
+ (*oc) -> data.terminated = 0;
+ if (data)
+ memcpy (&bp -> data [0], data, len);
+ (*oc) -> option = option;
+ return 1;
}
-struct tree *tree_host_lookup (name)
- char *name;
+int make_host_lookup (expr, name)
+ struct expression **expr;
+ const char *name;
{
- struct tree *nt;
- nt = new_tree ("tree_host_lookup");
- if (!nt)
- error ("No memory for host lookup tree node.");
- nt -> op = TREE_HOST_LOOKUP;
- nt -> data.host_lookup.host = enter_dns_host (name);
- return nt;
+ if (!expression_allocate (expr, MDL)) {
+ log_error ("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, MDL);
+ return 0;
+ }
+ return 1;
}
-struct dns_host_entry *enter_dns_host (name)
- char *name;
+int enter_dns_host (dh, name)
+ struct dns_host_entry **dh;
+ const char *name;
{
- struct dns_host_entry *dh;
-
- if (!(dh = (struct dns_host_entry *)dmalloc
- (sizeof (struct dns_host_entry), "enter_dns_host"))
- || !(dh -> hostname = dmalloc (strlen (name) + 1,
- "enter_dns_host")))
- error ("Can't allocate space for new host.");
- strcpy (dh -> hostname, name);
- dh -> data = (unsigned char *)0;
- dh -> data_len = 0;
- dh -> buf_len = 0;
- dh -> timeout = 0;
- return dh;
+ /* 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, MDL)) {
+ log_error ("Can't allocate space for new host.");
+ return 0;
+ }
+ return 1;
}
-struct tree *tree_const (data, len)
- unsigned char *data;
- int len;
+int make_const_data (struct expression **expr, const unsigned char *data,
+ unsigned len, int terminated, int allocate,
+ const char *file, int line)
{
- struct tree *nt;
- if (!(nt = new_tree ("tree_const"))
- || !(nt -> data.const_val.data =
- (unsigned char *)dmalloc (len, "tree_const")))
- error ("No memory for constant data tree node.");
- nt -> op = TREE_CONST;
- memcpy (nt -> data.const_val.data, data, len);
- nt -> data.const_val.len = len;
- return nt;
+ struct expression *nt;
+
+ if (!expression_allocate (expr, file, line)) {
+ log_error ("No memory for make_const_data tree node.");
+ return 0;
+ }
+ nt = *expr;
+
+ if (len) {
+ if (allocate) {
+ if (!buffer_allocate (&nt -> data.const_data.buffer,
+ len + terminated, file, line)) {
+ log_error ("Can't allocate const_data buffer");
+ expression_dereference (expr, file, line);
+ return 0;
+ }
+ nt -> data.const_data.data =
+ &nt -> data.const_data.buffer -> data [0];
+ memcpy (nt -> data.const_data.buffer -> data,
+ data, len + terminated);
+ } else
+ nt -> data.const_data.data = data;
+ nt -> data.const_data.terminated = terminated;
+ } else
+ nt -> data.const_data.data = 0;
+
+ nt -> op = expr_const_data;
+ nt -> data.const_data.len = len;
+ return 1;
}
-struct tree *tree_concat (left, right)
- struct tree *left, *right;
+int make_const_int (expr, val)
+ struct expression **expr;
+ unsigned long val;
{
- struct tree *nt;
+ if (!expression_allocate (expr, MDL)) {
+ log_error ("No memory for make_const_int tree node.");
+ return 0;
+ }
+
+ (*expr) -> op = expr_const_int;
+ (*expr) -> data.const_int = val;
+ return 1;
+}
+int make_concat (expr, left, right)
+ struct expression **expr;
+ struct expression *left, *right;
+{
/* 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 trees are constant, combine them. */
- if (left -> op == TREE_CONST && right -> op == TREE_CONST) {
- unsigned char *buf = dmalloc (left -> data.const_val.len
- + right -> data.const_val.len,
- "tree_concat");
- if (!buf)
- error ("No memory to concatenate constants.");
- memcpy (buf, left -> data.const_val.data,
- left -> data.const_val.len);
- memcpy (buf + left -> data.const_val.len,
- right -> data.const_val.data,
- right -> data.const_val.len);
- dfree (left -> data.const_val.data, "tree_concat");
- dfree (right -> data.const_val.data, "tree_concat");
- left -> data.const_val.data = buf;
- left -> data.const_val.len += right -> data.const_val.len;
- free_tree (right, "tree_concat");
- return left;
+ if (!left) {
+ if (!right)
+ return 0;
+ expression_reference (expr, right, MDL);
+ return 1;
+ }
+ if (!right) {
+ expression_reference (expr, left, MDL);
+ return 1;
}
/* Otherwise, allocate a new node to concatenate the two. */
- if (!(nt = new_tree ("tree_concat")))
- error ("No memory for data tree concatenation node.");
- nt -> op = TREE_CONCAT;
- nt -> data.concat.left = left;
- nt -> data.concat.right = right;
- return nt;
+ if (!expression_allocate (expr, MDL)) {
+ log_error ("No memory for concatenation expression node.");
+ return 0;
+ }
+
+ (*expr) -> op = expr_concat;
+ expression_reference (&(*expr) -> data.concat [0], left, MDL);
+ expression_reference (&(*expr) -> data.concat [1], right, MDL);
+ return 1;
}
-struct tree *tree_limit (tree, limit)
- struct tree *tree;
- int limit;
+int make_encapsulation (expr, name)
+ struct expression **expr;
+ struct data_string *name;
{
- struct tree *rv;
-
- /* If the tree we're limiting is constant, limit it now. */
- if (tree -> op == TREE_CONST) {
- if (tree -> data.const_val.len > limit)
- tree -> data.const_val.len = limit;
- return tree;
+ /* Allocate a new node to store the encapsulation. */
+ if (!expression_allocate (expr, MDL)) {
+ log_error ("No memory for encapsulation expression node.");
+ return 0;
}
-
- /* Otherwise, put in a node which enforces the limit on evaluation. */
- rv = new_tree ("tree_limit");
- if (!rv)
- return (struct tree *)0;
- rv -> op = TREE_LIMIT;
- rv -> data.limit.tree = tree;
- rv -> data.limit.limit = limit;
- return rv;
+
+ (*expr) -> op = expr_encapsulate;
+ data_string_copy (&(*expr) -> data.encapsulate, name, MDL);
+ return 1;
}
-int tree_evaluate (tree_cache)
- struct tree_cache *tree_cache;
+int make_substring (new, expr, offset, length)
+ struct expression **new;
+ struct expression *expr;
+ struct expression *offset;
+ struct expression *length;
{
- unsigned char *bp = tree_cache -> value;
- int bc = tree_cache -> buf_size;
- int bufix = 0;
+ /* Allocate an expression node to compute the substring. */
+ if (!expression_allocate (new, MDL)) {
+ log_error ("no memory for substring expression.");
+ return 0;
+ }
+ (*new) -> op = expr_substring;
+ expression_reference (&(*new) -> data.substring.expr, expr, MDL);
+ expression_reference (&(*new) -> data.substring.offset, offset, MDL);
+ expression_reference (&(*new) -> data.substring.len, length, MDL);
+ return 1;
+}
- /* If there's no tree associated with this cache, it evaluates
- to a constant and that was detected at startup. */
- if (!tree_cache -> tree)
- return 1;
+int make_limit (new, expr, limit)
+ struct expression **new;
+ struct expression *expr;
+ int limit;
+{
+ struct expression *rv;
- /* Try to evaluate the tree without allocating more memory... */
- tree_cache -> timeout = tree_evaluate_recurse (&bufix, &bp, &bc,
- tree_cache -> tree);
+ /* Allocate a node to enforce a limit on evaluation. */
+ if (!expression_allocate (new, MDL))
+ log_error ("no memory for limit expression");
+ (*new) -> op = expr_substring;
+ expression_reference (&(*new) -> data.substring.expr, expr, MDL);
- /* No additional allocation needed? */
- if (bufix <= bc) {
- tree_cache -> len = bufix;
- return 1;
+ /* Offset is a constant 0. */
+ if (!expression_allocate (&(*new) -> data.substring.offset, MDL)) {
+ log_error ("no memory for limit offset expression");
+ expression_dereference (new, MDL);
+ return 0;
}
+ (*new) -> data.substring.offset -> op = expr_const_int;
+ (*new) -> data.substring.offset -> data.const_int = 0;
- /* If we can't allocate more memory, return with what we
- have (maybe nothing). */
- if (!(bp = (unsigned char *)dmalloc (bufix, "tree_evaluate")))
+ /* Length is a constant: the specified limit. */
+ if (!expression_allocate (&(*new) -> data.substring.len, MDL)) {
+ log_error ("no memory for limit length expression");
+ expression_dereference (new, MDL);
return 0;
+ }
+ (*new) -> data.substring.len -> op = expr_const_int;
+ (*new) -> data.substring.len -> data.const_int = limit;
- /* Record the change in conditions... */
- bc = bufix;
- bufix = 0;
-
- /* Note that the size of the result shouldn't change on the
- second call to tree_evaluate_recurse, since we haven't
- changed the ``current'' time. */
- tree_evaluate_recurse (&bufix, &bp, &bc, tree_cache -> tree);
-
- /* Free the old buffer if needed, then store the new buffer
- location and size and return. */
- if (tree_cache -> value)
- dfree (tree_cache -> value, "tree_evaluate");
- tree_cache -> value = bp;
- tree_cache -> len = bufix;
- tree_cache -> buf_size = bc;
return 1;
}
-static TIME tree_evaluate_recurse (bufix, bufp, bufcount, tree)
- int *bufix;
- unsigned char **bufp;
- int *bufcount;
- struct tree *tree;
+int option_cache (struct option_cache **oc, struct data_string *dp,
+ struct expression *expr, struct option *option,
+ const char *file, int line)
{
- int limit;
- TIME t1, t2;
-
- switch (tree -> op) {
- case TREE_CONCAT:
- t1 = tree_evaluate_recurse (bufix, bufp, bufcount,
- tree -> data.concat.left);
- t2 = tree_evaluate_recurse (bufix, bufp, bufcount,
- tree -> data.concat.right);
- if (t1 > t2)
- return t2;
- return t1;
-
- case TREE_HOST_LOOKUP:
- return do_host_lookup (bufix, bufp, bufcount,
- tree -> data.host_lookup.host);
-
- case TREE_CONST:
- do_data_copy (bufix, bufp, bufcount,
- tree -> data.const_val.data,
- tree -> data.const_val.len);
- t1 = MAX_TIME;
- return t1;
-
- case TREE_LIMIT:
- limit = *bufix + tree -> data.limit.limit;
- t1 = tree_evaluate_recurse (bufix, bufp, bufcount,
- tree -> data.limit.tree);
- *bufix = limit;
- return t1;
+ if (!option_cache_allocate (oc, file, line))
+ return 0;
+ if (dp)
+ data_string_copy (&(*oc) -> data, dp, file, line);
+ if (expr)
+ expression_reference (&(*oc) -> expression, expr, file, line);
+ (*oc) -> option = option;
+ return 1;
+}
- default:
- warn ("Bad node id in tree: %d.");
- t1 = MAX_TIME;
- return t1;
+int make_let (result, name)
+ struct executable_statement **result;
+ const char *name;
+{
+ if (!(executable_statement_allocate (result, MDL)))
+ return 0;
+
+ (*result) -> op = let_statement;
+ (*result) -> data.let.name = dmalloc (strlen (name) + 1, MDL);
+ if (!(*result) -> data.let.name) {
+ executable_statement_dereference (result, MDL);
+ return 0;
}
+ strcpy ((*result) -> data.let.name, name);
+ return 1;
}
-
-static TIME do_host_lookup (bufix, bufp, bufcount, dns)
- int *bufix;
- unsigned char **bufp;
- int *bufcount;
+
+static int do_host_lookup (result, dns)
+ struct data_string *result;
struct dns_host_entry *dns;
{
struct hostent *h;
- int i;
- int new_len;
+ unsigned i, count;
+ unsigned new_len;
#ifdef DEBUG_EVAL
- debug ("time: now = %d dns = %d %d diff = %d",
+ log_debug ("time: now = %d dns = %d diff = %d",
cur_time, dns -> timeout, cur_time - dns -> timeout);
#endif
/* 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 %x",
- dns -> data, dns -> data_len,
- dns -> data ? *(int *)(dns -> data) : 0);
+ log_debug ("easy copy: %d %s",
+ dns -> data.len,
+ (dns -> data.len > 4
+ ? inet_ntoa (*(struct in_addr *)(dns -> data.data))
+ : 0));
#endif
- do_data_copy (bufix, bufp, bufcount,
- dns -> data, dns -> data_len);
- return dns -> timeout;
+ data_string_copy (result, &dns -> data, MDL);
+ return 1;
}
#ifdef DEBUG_EVAL
- debug ("Looking up %s", dns -> hostname);
+ log_debug ("Looking up %s", dns -> hostname);
#endif
/* Otherwise, look it up... */
@@ -318,95 +354,3748 @@ static TIME do_host_lookup (bufix, bufp, bufcount, dns)
switch (h_errno) {
case HOST_NOT_FOUND:
#endif
- warn ("%s: host unknown.", dns -> hostname);
+ log_error ("%s: host unknown.", dns -> hostname);
#ifndef NO_H_ERRNO
break;
case TRY_AGAIN:
- warn ("%s: temporary name server failure",
- dns -> hostname);
+ log_error ("%s: temporary name server failure",
+ dns -> hostname);
break;
case NO_RECOVERY:
- warn ("%s: name server failed", dns -> hostname);
+ log_error ("%s: name server failed", dns -> hostname);
break;
case NO_DATA:
- warn ("%s: no A record associated with address",
- dns -> hostname);
+ log_error ("%s: no A record associated with address",
+ dns -> hostname);
}
#endif /* !NO_H_ERRNO */
/* Okay to try again after a minute. */
- return cur_time + 60;
+ dns -> timeout = cur_time + 60;
+ data_string_forget (&dns -> data, MDL);
+ return 0;
}
#ifdef DEBUG_EVAL
- debug ("Lookup succeeded; first address is %x",
- h -> h_addr_list [0]);
+ log_debug ("Lookup succeeded; first address is %s",
+ inet_ntoa (h -> h_addr_list [0]));
#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, MDL);
+
/* 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 dns -> timeout;
- }
- } else {
- if (dns -> data)
- dfree (dns -> data, "do_host_lookup");
- dns -> data = buf;
- dns -> buf_len = new_len;
- }
+ new_len = count * h -> h_length;
+ if (!buffer_allocate (&dns -> data.buffer, new_len, MDL))
+ {
+ log_error ("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 -> data + h -> h_length * i,
- h -> h_addr_list [i], h -> h_length);
+ for (i = 0; i < count; i++) {
+ memcpy (&dns -> data.buffer -> data [h -> h_length * i],
+ h -> h_addr_list [i], (unsigned)(h -> h_length));
}
#ifdef DEBUG_EVAL
- debug ("dns -> data: %x h -> h_addr_list [0]: %x",
- *(int *)(dns -> data), h -> h_addr_list [0]);
+ log_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));
+ log_debug ("hard copy: %d %s", dns -> data.len,
+ (dns -> data.len > 4
+ ? inet_ntoa (*(struct in_addr *)(dns -> data.data)) : 0));
+#endif
+ data_string_copy (result, &dns -> data, MDL);
+ return 1;
+}
+
+int evaluate_expression (result, packet, lease, client_state,
+ in_options, cfg_options, scope, expr, file, line)
+ struct binding_value **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 expression *expr;
+ const char *file;
+ int line;
+{
+ struct binding_value *bv;
+ int status;
+ struct binding *binding;
+
+ bv = (struct binding_value *)0;
+
+ if (expr -> op == expr_variable_reference) {
+ if (!scope || !*scope)
+ return 0;
+
+ binding = find_binding (*scope, expr -> data.variable);
+
+ if (binding && binding -> value) {
+ if (result)
+ binding_value_reference (result,
+ binding -> value,
+ file, line);
+ return 1;
+ } else
+ return 0;
+ } else if (expr -> op == expr_funcall) {
+ struct string_list *s;
+ struct expression *arg;
+ struct binding_scope *ns;
+ struct binding *nb;
+
+ if (!scope || !*scope) {
+ log_error ("%s: no such function.",
+ expr -> data.funcall.name);
+ return 0;
+ }
+
+ binding = find_binding (*scope, expr -> data.funcall.name);
+
+ if (!binding || !binding -> value) {
+ log_error ("%s: no such function.",
+ expr -> data.funcall.name);
+ return 0;
+ }
+ if (binding -> value -> type != binding_function) {
+ log_error ("%s: not a function.",
+ expr -> data.funcall.name);
+ return 0;
+ }
+
+ /* Create a new binding scope in which to define
+ the arguments to the function. */
+ ns = (struct binding_scope *)0;
+ if (!binding_scope_allocate (&ns, MDL)) {
+ log_error ("%s: can't allocate argument scope.",
+ expr -> data.funcall.name);
+ return 0;
+ }
+
+ arg = expr -> data.funcall.arglist;
+ s = binding -> value -> value.fundef -> args;
+ while (arg && s) {
+ nb = dmalloc (sizeof *nb, MDL);
+ if (!nb) {
+ blb:
+ binding_scope_dereference (&ns, MDL);
+ return 0;
+ } else {
+ memset (nb, 0, sizeof *nb);
+ nb -> name = dmalloc (strlen (s -> string) + 1,
+ MDL);
+ if (nb -> name)
+ strcpy (nb -> name, s -> string);
+ else {
+ dfree (nb, MDL);
+ nb = (struct binding *)0;
+ goto blb;
+ }
+ }
+ evaluate_expression (&nb -> value, packet, lease,
+ client_state,
+ in_options, cfg_options, scope,
+ arg -> data.arg.val, file, line);
+ nb -> next = ns -> bindings;
+ ns -> bindings = nb;
+ arg = arg -> data.arg.next;
+ s = s -> next;
+ }
+ if (arg) {
+ log_error ("%s: too many arguments.",
+ expr -> data.funcall.name);
+ binding_scope_dereference (&ns, MDL);
+ return 0;
+ }
+ if (s) {
+ log_error ("%s: too few arguments.",
+ expr -> data.funcall.name);
+ binding_scope_dereference (&ns, MDL);
+ return 0;
+ }
+
+ if (scope && *scope)
+ binding_scope_reference (&ns -> outer, *scope, MDL);
+
+ status = (execute_statements
+ (&bv, packet,
+ lease, client_state, in_options, cfg_options, &ns,
+ binding -> value -> value.fundef -> statements));
+ binding_scope_dereference (&ns, MDL);
+
+ if (!bv)
+ return 1;
+ } else if (is_boolean_expression (expr)) {
+ if (!binding_value_allocate (&bv, MDL))
+ return 0;
+ bv -> type = binding_boolean;
+ status = (evaluate_boolean_expression
+ (&bv -> value.boolean, packet, lease, client_state,
+ in_options, cfg_options, scope, expr));
+ } else if (is_numeric_expression (expr)) {
+ if (!binding_value_allocate (&bv, MDL))
+ return 0;
+ bv -> type = binding_numeric;
+ status = (evaluate_numeric_expression
+ (&bv -> value.intval, packet, lease, client_state,
+ in_options, cfg_options, scope, expr));
+ } else if (is_data_expression (expr)) {
+ if (!binding_value_allocate (&bv, MDL))
+ return 0;
+ bv -> type = binding_data;
+ status = (evaluate_data_expression
+ (&bv -> value.data, packet, lease, client_state,
+ in_options, cfg_options, scope, expr, MDL));
+ } else if (is_dns_expression (expr)) {
+#if defined (NSUPDATE)
+ if (!binding_value_allocate (&bv, MDL))
+ return 0;
+ bv -> type = binding_dns;
+ status = (evaluate_dns_expression
+ (&bv -> value.dns, packet, lease, client_state,
+ in_options, cfg_options, scope, expr));
+#endif
+ } else {
+ log_error ("%s: invalid expression type: %d",
+ "evaluate_expression", expr -> op);
+ return 0;
+ }
+ if (result && status)
+ binding_value_reference (result, bv, file, line);
+ binding_value_dereference (&bv, MDL);
+
+ return status;
+}
+
+int binding_value_dereference (struct binding_value **v,
+ const char *file, int line)
+{
+ struct binding_value *bv = *v;
+
+ *v = (struct binding_value *)0;
+
+ /* Decrement the reference count. If it's nonzero, we're
+ done. */
+ --(bv -> refcnt);
+ rc_register (file, line, v, bv, bv -> refcnt, 1, RC_MISC);
+ if (bv -> refcnt > 0)
+ return 1;
+ if (bv -> refcnt < 0) {
+ log_error ("%s(%d): negative refcnt!", file, line);
+#if defined (DEBUG_RC_HISTORY)
+ dump_rc_history (bv);
+#endif
+#if defined (POINTER_DEBUG)
+ abort ();
+#else
+ return 0;
+#endif
+ }
+
+ switch (bv -> type) {
+ case binding_boolean:
+ case binding_numeric:
+ break;
+ case binding_data:
+ if (bv -> value.data.buffer)
+ data_string_forget (&bv -> value.data, file, line);
+ break;
+ case binding_dns:
+#if defined (NSUPDATE)
+ if (bv -> value.dns) {
+ if (bv -> value.dns -> r_data) {
+ dfree (bv -> value.dns -> r_data_ephem, MDL);
+ bv -> value.dns -> r_data = (unsigned char *)0;
+ bv -> value.dns -> r_data_ephem =
+ (unsigned char *)0;
+ }
+ minires_freeupdrec (bv -> value.dns);
+ }
+ break;
+#endif
+ default:
+ log_error ("%s(%d): invalid binding type: %d",
+ file, line, bv -> type);
+ return 0;
+ }
+ dfree (bv, file, line);
+ return 1;
+}
+
+#if defined (NSUPDATE)
+int evaluate_dns_expression (result, packet, lease, client_state, in_options,
+ cfg_options, scope, expr)
+ ns_updrec **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 expression *expr;
+{
+ ns_updrec *foo;
+ unsigned long ttl = 0;
+ char *tname;
+ struct data_string name, data;
+ int r0, r1, r2, r3;
+
+ if (!result || *result) {
+ log_error ("evaluate_dns_expression called with non-null %s",
+ "result pointer");
+#if defined (POINTER_DEBUG)
+ abort ();
+#else
+ return 0;
+#endif
+ }
+
+ switch (expr -> op) {
+#if defined (NSUPDATE)
+ case expr_ns_add:
+ r0 = evaluate_numeric_expression (&ttl, packet, lease,
+ client_state,
+ in_options, cfg_options,
+ scope,
+ expr -> data.ns_add.ttl);
+ goto nsfinish;
+
+ case expr_ns_exists:
+ ttl = 1;
+
+ case expr_ns_delete:
+ case expr_ns_not_exists:
+ r0 = 1;
+ nsfinish:
+ memset (&name, 0, sizeof name);
+ r1 = evaluate_data_expression (&name, packet, lease,
+ client_state,
+ in_options, cfg_options, scope,
+ expr -> data.ns_add.rrname,
+ MDL);
+ if (r1) {
+ /* The result of the evaluation may or may not
+ be NUL-terminated, but we need it
+ terminated for sure, so we have to allocate
+ a buffer and terminate it. */
+ tname = dmalloc (name.len + 1, MDL);
+ if (!tname) {
+ r2 = 0;
+ r1 = 0;
+ data_string_forget (&name, MDL);
+ } else {
+ memcpy (tname, name.data, name.len);
+ tname [name.len] = 0;
+ memset (&data, 0, sizeof data);
+ r2 = evaluate_data_expression
+ (&data, packet, lease, client_state,
+ in_options, cfg_options, scope,
+ expr -> data.ns_add.rrdata, MDL);
+ }
+ } else
+ r2 = 0;
+ if (r0 && r1 && (r2 || expr -> op != expr_ns_add)) {
+ *result = minires_mkupdrec (((expr -> op == expr_ns_add ||
+ expr -> op == expr_ns_delete)
+ ? S_UPDATE : S_PREREQ),
+ tname,
+ expr -> data.ns_add.rrclass,
+ expr -> data.ns_add.rrtype,
+ ttl);
+ if (!*result) {
+ ngood:
+ if (r2) {
+ data_string_forget (&data, MDL);
+ r2 = 0;
+ }
+ } else {
+ if (data.len) {
+ /* As a special case, if we get exactly
+ four bytes of data, it's an IP address
+ represented as a 32-bit quantity, which
+ is actually what we *should* be getting
+ here. Because res_mkupdrec is currently
+ broken and expects a dotted quad, convert
+ it. This should be fixed when the new
+ resolver is merged. */
+ if (data.len == 4) {
+ (*result) -> r_data_ephem =
+ dmalloc (16, MDL);
+ if (!(*result) -> r_data_ephem)
+ goto dpngood;
+ (*result) -> r_data =
+ (*result) -> r_data_ephem;
+ sprintf ((char *)(*result) -> r_data_ephem,
+ "%d.%d.%d.%d",
+ data.data [0], data.data [1],
+ data.data [2], data.data [3]);
+ (*result) -> r_size =
+ strlen ((const char *)
+ (*result) -> r_data);
+ } else {
+ (*result) -> r_size = data.len;
+ (*result) -> r_data_ephem =
+ dmalloc (data.len, MDL);
+ if (!(*result) -> r_data_ephem) {
+ dpngood: /* double plus ungood. */
+ minires_freeupdrec (*result);
+ *result = 0;
+ goto ngood;
+ }
+ (*result) -> r_data =
+ (*result) -> r_data_ephem;
+ memcpy ((*result) -> r_data_ephem,
+ data.data, data.len);
+ }
+ } else {
+ (*result) -> r_data = 0;
+ (*result) -> r_size = 0;
+ }
+ switch (expr -> op) {
+ case expr_ns_add:
+ (*result) -> r_opcode = ADD;
+ break;
+ case expr_ns_delete:
+ (*result) -> r_opcode = DELETE;
+ break;
+ case expr_ns_exists:
+ (*result) -> r_opcode = YXRRSET;
+ break;
+ case expr_ns_not_exists:
+ (*result) -> r_opcode = NXRRSET;
+ break;
+
+ /* Can't happen, but satisfy gcc. */
+ default:
+ break;
+ }
+ }
+ }
+ if (r1) {
+ data_string_forget (&name, MDL);
+ dfree (tname, MDL);
+ }
+ if (r2)
+ data_string_forget (&data, MDL);
+ /* One flaw in the thinking here: an IP address and an
+ ASCII string both look like data expressions, but
+ for A records, we want an ASCII string, not a
+ binary IP address. Do I need to turn binary IP
+ addresses into a seperate type? */
+ return (r0 && r1 &&
+ (r2 || expr -> op != expr_ns_add) && *result);
+
+#else
+ case expr_ns_add:
+ case expr_ns_delete:
+ case expr_ns_exists:
+ case expr_ns_not_exists:
+ return 0;
+#endif
+ case expr_funcall:
+ log_error ("%s: dns values for functions not supported.",
+ expr -> data.funcall.name);
+ break;
+
+ case expr_variable_reference:
+ log_error ("%s: dns values for variables not supported.",
+ expr -> data.variable);
+ break;
+
+ case expr_check:
+ case expr_equal:
+ case expr_not_equal:
+ case expr_and:
+ case expr_or:
+ case expr_not:
+ case expr_match:
+ case expr_static:
+ case expr_known:
+ case expr_exists:
+ case expr_variable_exists:
+ log_error ("Boolean opcode in evaluate_dns_expression: %d",
+ expr -> op);
+ return 0;
+
+ case expr_none:
+ case expr_substring:
+ case expr_suffix:
+ case expr_option:
+ case expr_hardware:
+ case expr_const_data:
+ case expr_packet:
+ case expr_concat:
+ case expr_encapsulate:
+ case expr_host_lookup:
+ case expr_encode_int8:
+ case expr_encode_int16:
+ case expr_encode_int32:
+ case expr_binary_to_ascii:
+ case expr_reverse:
+ case expr_filename:
+ case expr_sname:
+ case expr_pick_first_value:
+ case expr_host_decl_name:
+ case expr_config_option:
+ case expr_leased_address:
+ case expr_null:
+ log_error ("Data opcode in evaluate_dns_expression: %d",
+ expr -> op);
+ return 0;
+
+ case expr_extract_int8:
+ case expr_extract_int16:
+ case expr_extract_int32:
+ case expr_const_int:
+ case expr_lease_time:
+ case expr_dns_transaction:
+ case expr_add:
+ case expr_subtract:
+ case expr_multiply:
+ case expr_divide:
+ case expr_remainder:
+ case expr_binary_and:
+ case expr_binary_or:
+ case expr_binary_xor:
+ case expr_client_state:
+ log_error ("Numeric opcode in evaluate_dns_expression: %d",
+ expr -> op);
+ return 0;
+
+ case expr_function:
+ log_error ("Function opcode in evaluate_dns_expression: %d",
+ expr -> op);
+ return 0;
+
+ case expr_arg:
+ break;
+ }
+
+ log_error ("Bogus opcode in evaluate_dns_expression: %d",
+ expr -> op);
+ return 0;
+}
+#endif /* defined (NSUPDATE) */
+
+int evaluate_boolean_expression (result, packet, lease, client_state,
+ in_options, cfg_options, scope, expr)
+ int *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 expression *expr;
+{
+ struct data_string left, right;
+ struct data_string rrtype, rrname, rrdata;
+ unsigned long ttl;
+ int srrtype, srrname, srrdata, sttl;
+ int bleft, bright;
+ int sleft, sright;
+ struct binding *binding;
+ struct binding_value *bv, *obv;
+
+ switch (expr -> op) {
+ case expr_check:
+ *result = check_collection (packet, lease,
+ expr -> data.check);
+#if defined (DEBUG_EXPRESSIONS)
+ log_debug ("bool: check (%s) returns %s",
+ expr -> data.check -> name,
+ *result ? "true" : "false");
+#endif
+ return 1;
+
+ case expr_equal:
+ case expr_not_equal:
+ bv = obv = (struct binding_value *)0;
+ sleft = evaluate_expression (&bv, packet, lease, client_state,
+ in_options, cfg_options, scope,
+ expr -> data.equal [0], MDL);
+ sright = evaluate_expression (&obv, packet, lease,
+ client_state, in_options,
+ cfg_options, scope,
+ expr -> data.equal [1], MDL);
+ if (sleft && sright) {
+ if (bv -> type != obv -> type)
+ *result = expr -> op == expr_not_equal;
+ else {
+ switch (obv -> type) {
+ case binding_boolean:
+ if (bv -> value.boolean == obv -> value.boolean)
+ *result = expr -> op == expr_equal;
+ else
+ *result = expr -> op == expr_not_equal;
+ break;
+
+ case binding_data:
+ if ((bv -> value.data.len ==
+ obv -> value.data.len) &&
+ !memcmp (bv -> value.data.data,
+ obv -> value.data.data,
+ obv -> value.data.len))
+ *result = expr -> op == expr_equal;
+ else
+ *result = expr -> op == expr_not_equal;
+ break;
+
+ case binding_numeric:
+ if (bv -> value.intval == obv -> value.intval)
+ *result = expr -> op == expr_equal;
+ else
+ *result = expr -> op == expr_not_equal;
+ break;
+
+ case binding_dns:
+#if defined (NSUPDATE)
+ /* XXX This should be a comparison for equal
+ XXX values, not for identity. */
+ if (bv -> value.dns == obv -> value.dns)
+ *result = expr -> op == expr_equal;
+ else
+ *result = expr -> op == expr_not_equal;
+#else
+ *result = expr -> op == expr_not_equal;
+#endif
+ break;
+
+ case binding_function:
+ if (bv -> value.fundef == obv -> value.fundef)
+ *result = expr -> op == expr_equal;
+ else
+ *result = expr -> op == expr_not_equal;
+ break;
+ default:
+ *result = expr -> op == expr_not_equal;
+ break;
+ }
+ }
+ } else if (!sleft && !sright)
+ *result = expr -> op == expr_equal;
+ else
+ *result = expr -> op == expr_not_equal;
+
+#if defined (DEBUG_EXPRESSIONS)
+ log_debug ("bool: %sequal = %s",
+ expr -> op == expr_not_equal ? "not" : "",
+ (*result ? "true" : "false"));
+#endif
+ if (sleft)
+ binding_value_dereference (&bv, MDL);
+ if (sright)
+ binding_value_dereference (&obv, MDL);
+ return 1;
+
+ case expr_and:
+ sleft = evaluate_boolean_expression (&bleft, packet, lease,
+ client_state,
+ in_options, cfg_options,
+ scope,
+ expr -> data.and [0]);
+ if (sleft && bleft)
+ sright = evaluate_boolean_expression
+ (&bright, packet, lease, client_state,
+ in_options, cfg_options,
+ scope, expr -> data.and [1]);
+ else
+ sright = bright = 0;
+
+#if defined (DEBUG_EXPRESSIONS)
+ log_debug ("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:
+ bleft = bright = 0;
+ sleft = evaluate_boolean_expression (&bleft, packet, lease,
+ client_state,
+ in_options, cfg_options,
+ scope,
+ expr -> data.or [0]);
+ if (!sleft || !bleft)
+ sright = evaluate_boolean_expression
+ (&bright, packet, lease, client_state,
+ in_options, cfg_options,
+ scope, expr -> data.or [1]);
+ else
+ sright = 0;
+#if defined (DEBUG_EXPRESSIONS)
+ log_debug ("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:
+ sleft = evaluate_boolean_expression (&bleft, packet, lease,
+ client_state,
+ in_options, cfg_options,
+ scope,
+ expr -> data.not);
+#if defined (DEBUG_EXPRESSIONS)
+ log_debug ("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 (!in_options ||
+ !get_option (&left, expr -> data.exists -> universe,
+ packet, lease, client_state,
+ in_options, cfg_options, in_options,
+ scope, expr -> data.exists -> code, MDL))
+ *result = 0;
+ else {
+ *result = 1;
+ data_string_forget (&left, MDL);
+ }
+#if defined (DEBUG_EXPRESSIONS)
+ log_debug ("bool: exists %s.%s = %s",
+ expr -> data.option -> universe -> name,
+ expr -> data.option -> name,
+ *result ? "true" : "false");
+#endif
+ return 1;
+
+ case expr_known:
+ if (!packet) {
+#if defined (DEBUG_EXPRESSIONS)
+ log_debug ("bool: known = NULL");
+#endif
+ return 0;
+ }
+#if defined (DEBUG_EXPRESSIONS)
+ log_debug ("bool: known = %s",
+ packet -> known ? "true" : "false");
+#endif
+ *result = packet -> known;
+ return 1;
+
+ case expr_static:
+ if (!lease || !(lease -> flags & STATIC_LEASE)) {
+#if defined (DEBUG_EXPRESSIONS)
+ log_debug ("bool: static = false (%s %s %s %d)",
+ lease ? "y" : "n",
+ (lease && (lease -> flags & STATIC_LEASE)
+ ? "y" : "n"),
+ piaddr (lease -> ip_addr),
+ lease ? lease -> flags : 0);
+#endif
+ *result = 0;
+ return 1;
+ }
+#if defined (DEBUG_EXPRESSIONS)
+ log_debug ("bool: static = true");
+#endif
+ *result = 1;
+ return 1;
+
+ case expr_variable_exists:
+ if (scope && *scope) {
+ binding = find_binding (*scope, expr -> data.variable);
+
+ if (binding) {
+ if (binding -> value)
+ *result = 1;
+ else
+ *result = 0;
+ } else
+ *result = 0;
+ } else
+ *result = 0;
+#if defined (DEBUG_EXPRESSIONS)
+ log_debug ("boolean: %s? = %s", expr -> data.variable,
+ *result ? "true" : "false");
+#endif
+ return 1;
+
+ case expr_variable_reference:
+ if (scope && *scope) {
+ binding = find_binding (*scope, expr -> data.variable);
+
+ if (binding && binding -> value) {
+ if (binding -> value -> type ==
+ binding_boolean) {
+ *result = binding -> value -> value.boolean;
+ sleft = 1;
+ } else {
+ log_error ("binding type %d in %s.",
+ binding -> value -> type,
+ "evaluate_boolean_expression");
+ sleft = 0;
+ }
+ } else
+ sleft = 0;
+ } else
+ sleft = 0;
+#if defined (DEBUG_EXPRESSIONS)
+ log_debug ("boolean: %s = %s", expr -> data.variable,
+ sleft ? (*result ? "true" : "false") : "NULL");
+#endif
+ return sleft;
+
+ case expr_funcall:
+ bv = (struct binding_value *)0;
+ sleft = evaluate_expression (&bv, packet, lease, client_state,
+ in_options, cfg_options,
+ scope, expr, MDL);
+ if (sleft) {
+ if (bv -> type != binding_boolean)
+ log_error ("%s() returned type %d in %s.",
+ expr -> data.funcall.name,
+ bv -> type,
+ "evaluate_boolean_expression");
+ else
+ *result = bv -> value.boolean;
+ binding_value_dereference (&bv, MDL);
+ }
+#if defined (DEBUG_EXPRESSIONS)
+ log_debug ("boolean: %s() = %s", expr -> data.funcall.name,
+ sleft ? (*result ? "true" : "false") : "NULL");
+#endif
+ break;
+
+ case expr_none:
+ case expr_match:
+ case expr_substring:
+ case expr_suffix:
+ case expr_option:
+ case expr_hardware:
+ case expr_const_data:
+ case expr_packet:
+ case expr_concat:
+ case expr_encapsulate:
+ case expr_host_lookup:
+ case expr_encode_int8:
+ case expr_encode_int16:
+ case expr_encode_int32:
+ case expr_binary_to_ascii:
+ case expr_reverse:
+ case expr_pick_first_value:
+ case expr_host_decl_name:
+ case expr_config_option:
+ case expr_leased_address:
+ case expr_null:
+ case expr_filename:
+ case expr_sname:
+ log_error ("Data opcode in evaluate_boolean_expression: %d",
+ expr -> op);
+ return 0;
+
+ case expr_extract_int8:
+ case expr_extract_int16:
+ case expr_extract_int32:
+ case expr_const_int:
+ case expr_lease_time:
+ case expr_dns_transaction:
+ case expr_add:
+ case expr_subtract:
+ case expr_multiply:
+ case expr_divide:
+ case expr_remainder:
+ case expr_binary_and:
+ case expr_binary_or:
+ case expr_binary_xor:
+ case expr_client_state:
+ log_error ("Numeric opcode in evaluate_boolean_expression: %d",
+ expr -> op);
+ return 0;
+
+ case expr_ns_add:
+ case expr_ns_delete:
+ case expr_ns_exists:
+ case expr_ns_not_exists:
+ log_error ("dns opcode in evaluate_boolean_expression: %d",
+ expr -> op);
+ return 0;
+
+ case expr_function:
+ log_error ("function definition in evaluate_boolean_expr");
+ return 0;
+
+ case expr_arg:
+ break;
+ }
+
+ log_error ("Bogus opcode in evaluate_boolean_expression: %d",
+ expr -> op);
+ return 0;
+}
+
+int evaluate_data_expression (result, packet, lease, client_state,
+ in_options, cfg_options, scope, expr, file, line)
+ 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 expression *expr;
+ const char *file;
+ int line;
+{
+ struct data_string data, other;
+ unsigned long offset, len, i;
+ int s0, s1, s2, s3;
+ int status;
+ struct binding *binding;
+ char *s;
+ struct binding_value *bv;
+
+ switch (expr -> op) {
+ /* Extract N bytes starting at byte M of a data string. */
+ case expr_substring:
+ memset (&data, 0, sizeof data);
+ s0 = evaluate_data_expression (&data, packet, lease,
+ client_state,
+ in_options, cfg_options, scope,
+ expr -> data.substring.expr,
+ MDL);
+
+ /* Evaluate the offset and length. */
+ s1 = evaluate_numeric_expression
+ (&offset, packet, lease, client_state, in_options,
+ cfg_options, scope, expr -> data.substring.offset);
+ s2 = evaluate_numeric_expression (&len, packet, lease,
+ client_state,
+ in_options, cfg_options,
+ scope,
+ expr -> data.substring.len);
+
+ if (s0 && s1 && s2) {
+ /* If the offset is after end of the string,
+ return an empty string. Otherwise, do the
+ adjustments and return what's left. */
+ if (data.len > offset) {
+ data_string_copy (result, &data, file, line);
+ 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)
+ log_debug ("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 (s0)
+ data_string_forget (&data, MDL);
+ if (s3)
+ return 1;
+ return 0;
+
+
+ /* Extract the last N bytes of a data string. */
+ case expr_suffix:
+ memset (&data, 0, sizeof data);
+ s0 = evaluate_data_expression (&data, packet, lease,
+ client_state,
+ in_options, cfg_options, scope,
+ expr -> data.suffix.expr, MDL);
+ /* Evaluate the length. */
+ s1 = evaluate_numeric_expression (&len, packet, lease,
+ client_state,
+ in_options, cfg_options,
+ scope,
+ expr -> data.suffix.len);
+ if (s0 && s1) {
+ data_string_copy (result, &data, file, line);
+
+ /* 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;
+ }
+ data_string_forget (&data, MDL);
+ }
+
+#if defined (DEBUG_EXPRESSIONS)
+ log_debug ("data: suffix (%s, %s) = %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 s0 && s1;
+
+ /* Extract an option. */
+ case expr_option:
+ if (in_options)
+ s0 = get_option (result,
+ expr -> data.option -> universe,
+ packet, lease, client_state,
+ in_options, cfg_options, in_options,
+ scope, expr -> data.option -> code,
+ file, line);
+ else
+ s0 = 0;
+
+#if defined (DEBUG_EXPRESSIONS)
+ log_debug ("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;
+
+ case expr_config_option:
+ if (cfg_options)
+ s0 = get_option (result,
+ expr -> data.option -> universe,
+ packet, lease, client_state,
+ in_options, cfg_options, cfg_options,
+ scope, expr -> data.option -> code,
+ file, line);
+ else
+ s0 = 0;
+
+#if defined (DEBUG_EXPRESSIONS)
+ log_debug ("data: config-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:
+ /* On the client, hardware is our hardware. */
+ if (client_state) {
+ memset (result, 0, sizeof *result);
+ result -> data =
+ client_state -> interface -> hw_address.hbuf;
+ result -> len =
+ client_state -> interface -> hw_address.hlen;
+#if defined (DEBUG_EXPRESSIONS)
+ log_debug ("data: hardware = %s",
+ print_hex_1 (result -> len,
+ result -> data, 60));
+#endif
+ return 1;
+ }
+
+ /* The server cares about the client's hardware address,
+ so only in the case where we are examining a packet can
+ we return anything. */
+ if (!packet || !packet -> raw) {
+ log_error ("data: hardware: raw packet not available");
+ return 0;
+ }
+ if (packet -> raw -> hlen > sizeof packet -> raw -> chaddr) {
+ log_error ("data: hardware: invalid hlen (%d)\n",
+ packet -> raw -> hlen);
+ return 0;
+ }
+ result -> len = packet -> raw -> hlen + 1;
+ if (buffer_allocate (&result -> buffer, result -> len,
+ file, line)) {
+ result -> data = &result -> buffer -> data [0];
+ result -> buffer -> data [0] = packet -> raw -> htype;
+ memcpy (&result -> buffer -> data [1],
+ packet -> raw -> chaddr,
+ packet -> raw -> hlen);
+ result -> terminated = 0;
+ } else {
+ log_error ("data: hardware: no memory for buffer.");
+ return 0;
+ }
+#if defined (DEBUG_EXPRESSIONS)
+ log_debug ("data: hardware = %s",
+ print_hex_1 (result -> len, result -> data, 60));
+#endif
+ return 1;
+
+ /* Extract part of the raw packet. */
+ case expr_packet:
+ if (!packet || !packet -> raw) {
+ log_error ("data: packet: raw packet not available");
+ return 0;
+ }
+
+ s0 = evaluate_numeric_expression (&offset, packet, lease,
+ client_state,
+ in_options, cfg_options,
+ scope,
+ expr -> data.packet.offset);
+ s1 = evaluate_numeric_expression (&len,
+ packet, lease, client_state,
+ in_options, cfg_options,
+ scope,
+ 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, file, line)) {
+ result -> data = &result -> buffer -> data [0];
+ memcpy (result -> buffer -> data,
+ (((unsigned char *)(packet -> raw))
+ + offset), result -> len);
+ result -> terminated = 0;
+ } else {
+ log_error ("data: packet: no buffer memory.");
+ return 0;
+ }
+ s2 = 1;
+ } else
+ s2 = 0;
+#if defined (DEBUG_EXPRESSIONS)
+ log_debug ("data: packet (%ld, %ld) = %s",
+ offset, len,
+ s2 ? print_hex_1 (result -> len,
+ result -> data, 60) : NULL);
+#endif
+ return s2;
+
+ /* The encapsulation of all defined options in an
+ option space... */
+ case expr_encapsulate:
+ if (cfg_options)
+ s0 = option_space_encapsulate
+ (result, packet, lease, client_state,
+ in_options, cfg_options, scope,
+ &expr -> data.encapsulate);
+ else
+ s0 = 0;
+
+#if defined (DEBUG_EXPRESSIONS)
+ log_debug ("data: encapsulate (%s) = %s",
+ expr -> data.encapsulate.data,
+ s0 ? print_hex_1 (result -> len,
+ result -> data, 60) : "NULL");
+#endif
+ return s0;
+
+ /* Some constant data... */
+ case expr_const_data:
+#if defined (DEBUG_EXPRESSIONS)
+ log_debug ("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, file, line);
+ return 1;
+
+ /* Hostname lookup... */
+ case expr_host_lookup:
+ s0 = do_host_lookup (result, expr -> data.host_lookup);
+#if defined (DEBUG_EXPRESSIONS)
+ log_debug ("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:
+ memset (&data, 0, sizeof data);
+ s0 = evaluate_data_expression (&data, packet, lease,
+ client_state,
+ in_options, cfg_options, scope,
+ expr -> data.concat [0], MDL);
+ memset (&other, 0, sizeof other);
+ s1 = evaluate_data_expression (&other, packet, lease,
+ client_state,
+ in_options, cfg_options, scope,
+ expr -> data.concat [1], MDL);
+
+ if (s0 && s1) {
+ result -> len = data.len + other.len;
+ if (!buffer_allocate (&result -> buffer,
+ (result -> len + other.terminated),
+ file, line)) {
+ log_error ("data: concat: no memory");
+ result -> len = 0;
+ data_string_forget (&data, MDL);
+ data_string_forget (&other, MDL);
+ return 0;
+ }
+ result -> data = &result -> buffer -> data [0];
+ memcpy (result -> buffer -> data, data.data, data.len);
+ memcpy (&result -> buffer -> data [data.len],
+ other.data, other.len + other.terminated);
+ }
+
+ if (s0)
+ data_string_forget (&data, MDL);
+ if (s1)
+ data_string_forget (&other, MDL);
+#if defined (DEBUG_EXPRESSIONS)
+ log_debug ("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_encode_int8:
+ s0 = evaluate_numeric_expression (&len, packet, lease,
+ client_state,
+ in_options, cfg_options,
+ scope,
+ expr -> data.encode_int);
+ if (s0) {
+ result -> len = 1;
+ if (!buffer_allocate (&result -> buffer,
+ 1, file, line)) {
+ log_error ("data: encode_int8: no memory");
+ result -> len = 0;
+ s0 = 0;
+ } else {
+ result -> data = &result -> buffer -> data [0];
+ result -> buffer -> data [0] = len;
+ }
+ } else
+ result -> len = 0;
+
+#if defined (DEBUG_EXPRESSIONS)
+ if (!s0)
+ log_debug ("data: encode_int8 (NULL) = NULL");
+ else
+ log_debug ("data: encode_int8 (%ld) = %s", len,
+ print_hex_2 (result -> len,
+ result -> data, 20));
+#endif
+ return s0;
+
+
+ case expr_encode_int16:
+ s0 = evaluate_numeric_expression (&len, packet, lease,
+ client_state,
+ in_options, cfg_options,
+ scope,
+ expr -> data.encode_int);
+ if (s0) {
+ result -> len = 2;
+ if (!buffer_allocate (&result -> buffer, 2,
+ file, line)) {
+ log_error ("data: encode_int16: no memory");
+ result -> len = 0;
+ s0 = 0;
+ } else {
+ result -> data = &result -> buffer -> data [0];
+ putUShort (result -> buffer -> data, len);
+ }
+ } else
+ result -> len = 0;
+
+#if defined (DEBUG_EXPRESSIONS)
+ if (!s0)
+ log_debug ("data: encode_int16 (NULL) = NULL");
+ else
+ log_debug ("data: encode_int16 (%ld) = %s", len,
+ print_hex_2 (result -> len,
+ result -> data, 20));
+#endif
+ return s0;
+
+ case expr_encode_int32:
+ s0 = evaluate_numeric_expression (&len, packet, lease,
+ client_state,
+ in_options, cfg_options,
+ scope,
+ expr -> data.encode_int);
+ if (s0) {
+ result -> len = 4;
+ if (!buffer_allocate (&result -> buffer, 4,
+ file, line)) {
+ log_error ("data: encode_int32: no memory");
+ result -> len = 0;
+ s0 = 0;
+ } else {
+ result -> data = &result -> buffer -> data [0];
+ putULong (result -> buffer -> data, len);
+ }
+ } else
+ result -> len = 0;
+
+#if defined (DEBUG_EXPRESSIONS)
+ if (!s0)
+ log_debug ("data: encode_int32 (NULL) = NULL");
+ else
+ log_debug ("data: encode_int32 (%ld) = %s", len,
+ print_hex_2 (result -> len,
+ result -> data, 20));
+#endif
+ return s0;
+
+ case expr_binary_to_ascii:
+ /* Evaluate the base (offset) and width (len): */
+ s0 = evaluate_numeric_expression
+ (&offset, packet, lease, client_state, in_options,
+ cfg_options, scope, expr -> data.b2a.base);
+ s1 = evaluate_numeric_expression (&len, packet, lease,
+ client_state,
+ in_options, cfg_options,
+ scope,
+ expr -> data.b2a.width);
+
+ /* Evaluate the seperator string. */
+ memset (&data, 0, sizeof data);
+ s2 = evaluate_data_expression (&data, packet, lease,
+ client_state,
+ in_options, cfg_options, scope,
+ expr -> data.b2a.seperator,
+ MDL);
+
+ /* Evaluate the data to be converted. */
+ memset (&other, 0, sizeof other);
+ s3 = evaluate_data_expression (&other, packet, lease,
+ client_state,
+ in_options, cfg_options, scope,
+ expr -> data.b2a.buffer, MDL);
+
+ if (s0 && s1 && s2 && s3) {
+ unsigned buflen, i;
+
+ if (len != 8 && len != 16 && len != 32) {
+ log_info ("binary_to_ascii: %s %ld!",
+ "invalid width", len);
+ goto b2a_out;
+ }
+ len /= 8;
+
+ /* The buffer must be a multiple of the number's
+ width. */
+ if (other.len % len) {
+ log_info ("binary-to-ascii: %s %d %s %ld!",
+ "length of buffer", other.len,
+ "not a multiple of width", len);
+ status = 0;
+ goto b2a_out;
+ }
+
+ /* Count the width of the output. */
+ buflen = 0;
+ for (i = 0; i < other.len; i += len) {
+ if (len == 1) {
+ if (offset == 8) {
+ if (other.data [i] < 8)
+ buflen++;
+ else if (other.data [i] < 64)
+ buflen += 2;
+ else
+ buflen += 3;
+ } else if (offset == 10) {
+ if (other.data [i] < 10)
+ buflen++;
+ else if (other.data [i] < 100)
+ buflen += 2;
+ else
+ buflen += 3;
+ } else if (offset == 16) {
+ if (other.data [i] < 16)
+ buflen++;
+ else
+ buflen += 2;
+ } else
+ buflen += (converted_length
+ (&other.data [i],
+ offset, 1));
+ } else
+ buflen += (converted_length
+ (&other.data [i],
+ offset, len));
+ if (i + len != other.len)
+ buflen += data.len;
+ }
+
+ if (!buffer_allocate (&result -> buffer,
+ buflen + 1, file, line)) {
+ log_error ("data: binary-to-ascii: no memory");
+ status = 0;
+ goto b2a_out;
+ }
+ result -> data = &result -> buffer -> data [0];
+ result -> len = buflen;
+ result -> terminated = 1;
+
+ buflen = 0;
+ for (i = 0; i < other.len; i += len) {
+ buflen += (binary_to_ascii
+ (&result -> buffer -> data [buflen],
+ &other.data [i], offset, len));
+ if (i + len != other.len) {
+ memcpy (&result ->
+ buffer -> data [buflen],
+ data.data, data.len);
+ buflen += data.len;
+ }
+ }
+ /* NUL terminate. */
+ result -> buffer -> data [buflen] = 0;
+ status = 1;
+ } else
+ status = 0;
+
+ b2a_out:
+#if defined (DEBUG_EXPRESSIONS)
+ log_debug ("data: binary-to-ascii (%s, %s, %s, %s) = %s",
+ s0 ? print_dec_1 (offset) : "NULL",
+ s1 ? print_dec_2 (len) : "NULL",
+ s2 ? print_hex_1 (data.len, data.data, 30) : "NULL",
+ s3 ? print_hex_2 (other.len, other.data, 30) : "NULL",
+ (status ? print_hex_3 (result -> len, result -> data, 30)
+ : "NULL"));
+#endif
+ if (s2)
+ data_string_forget (&data, MDL);
+ if (s3)
+ data_string_forget (&other, MDL);
+ if (status)
+ return 1;
+ return 0;
+
+ case expr_reverse:
+ /* Evaluate the width (len): */
+ s0 = evaluate_numeric_expression
+ (&len, packet, lease, client_state, in_options,
+ cfg_options, scope, expr -> data.reverse.width);
+
+ /* Evaluate the data. */
+ memset (&data, 0, sizeof data);
+ s1 = evaluate_data_expression (&data, packet, lease,
+ client_state,
+ in_options, cfg_options, scope,
+ expr -> data.reverse.buffer,
+ MDL);
+
+ if (s0 && s1) {
+ char *upper;
+ int i;
+
+ /* The buffer must be a multiple of the number's
+ width. */
+ if (data.len % len) {
+ log_info ("reverse: %s %d %s %ld!",
+ "length of buffer", data.len,
+ "not a multiple of width", len);
+ status = 0;
+ goto reverse_out;
+ }
+
+ /* XXX reverse in place? I don't think we can. */
+ if (!buffer_allocate (&result -> buffer,
+ data.len, file, line)) {
+ log_error ("data: reverse: no memory");
+ status = 0;
+ goto reverse_out;
+ }
+ result -> data = &result -> buffer -> data [0];
+ result -> len = data.len;
+ result -> terminated = 0;
+
+ for (i = 0; i < data.len; i += len) {
+ memcpy (&result -> buffer -> data [i],
+ &data.data [data.len - i - len], len);
+ }
+ status = 1;
+ } else
+ status = 0;
+
+ reverse_out:
+#if defined (DEBUG_EXPRESSIONS)
+ log_debug ("data: reverse (%s, %s) = %s",
+ s0 ? print_dec_1 (len) : "NULL",
+ s1 ? print_hex_1 (data.len, data.data, 30) : "NULL",
+ (status ? print_hex_3 (result -> len, result -> data, 30)
+ : "NULL"));
+#endif
+ if (s0)
+ data_string_forget (&data, MDL);
+ if (status)
+ return 1;
+ return 0;
+
+ case expr_leased_address:
+ if (!lease) {
+ log_error ("data: leased_address: not available");
+ return 0;
+ }
+ result -> len = lease -> ip_addr.len;
+ if (buffer_allocate (&result -> buffer, result -> len,
+ file, line)) {
+ result -> data = &result -> buffer -> data [0];
+ memcpy (&result -> buffer -> data [0],
+ lease -> ip_addr.iabuf, lease -> ip_addr.len);
+ result -> terminated = 0;
+ } else {
+ log_error ("data: leased-address: no memory.");
+ return 0;
+ }
+#if defined (DEBUG_EXPRESSIONS)
+ log_debug ("data: leased-address = %s",
+ print_hex_1 (result -> len, result -> data, 60));
+#endif
+ return 1;
+
+ case expr_pick_first_value:
+ memset (&data, 0, sizeof data);
+ if ((evaluate_data_expression
+ (result, packet,
+ lease, client_state, in_options, cfg_options,
+ scope, expr -> data.pick_first_value.car, MDL))) {
+#if defined (DEBUG_EXPRESSIONS)
+ log_debug ("data: pick_first_value (%s, xxx)",
+ print_hex_1 (result -> len,
+ result -> data, 40));
+#endif
+ return 1;
+ }
+
+ if (expr -> data.pick_first_value.cdr &&
+ (evaluate_data_expression
+ (result, packet,
+ lease, client_state, in_options, cfg_options,
+ scope, expr -> data.pick_first_value.cdr, MDL))) {
+#if defined (DEBUG_EXPRESSIONS)
+ log_debug ("data: pick_first_value (NULL, %s)",
+ print_hex_1 (result -> len,
+ result -> data, 40));
+#endif
+ return 1;
+ }
+
+#if defined (DEBUG_EXPRESSIONS)
+ log_debug ("data: pick_first_value (NULL, NULL) = NULL");
+#endif
+ return 0;
+
+ case expr_host_decl_name:
+ if (!lease || !lease -> host) {
+ log_error ("data: host_decl_name: not available");
+ return 0;
+ }
+ result -> len = strlen (lease -> host -> name);
+ if (buffer_allocate (&result -> buffer,
+ result -> len + 1, file, line)) {
+ result -> data = &result -> buffer -> data [0];
+ strcpy ((char *)&result -> buffer -> data [0],
+ lease -> host -> name);
+ result -> terminated = 1;
+ } else {
+ log_error ("data: host-decl-name: no memory.");
+ return 0;
+ }
+#if defined (DEBUG_EXPRESSIONS)
+ log_debug ("data: host-decl-name = %s", lease -> host -> name);
+#endif
+ return 1;
+
+ case expr_null:
+#if defined (DEBUG_EXPRESSIONS)
+ log_debug ("data: null = NULL");
+#endif
+ return 0;
+
+ case expr_variable_reference:
+ if (scope && *scope) {
+ binding = find_binding (*scope, expr -> data.variable);
+
+ if (binding && binding -> value) {
+ if (binding -> value -> type == binding_data) {
+ data_string_copy (result,
+ &binding -> value -> value.data,
+ file, line);
+ s0 = 1;
+ } else if (binding -> value -> type != binding_data) {
+ log_error ("binding type %d in %s.",
+ binding -> value -> type,
+ "evaluate_data_expression");
+ s0 = 0;
+ } else
+ s0 = 0;
+ } else
+ s0 = 0;
+ } else
+ s0 = 0;
+#if defined (DEBUG_EXPRESSIONS)
+ log_debug ("data: %s = %s", expr -> data.variable,
+ s0 ? print_hex_1 (result -> len,
+ result -> data, 50) : "NULL");
+#endif
+ return s0;
+
+ case expr_funcall:
+ bv = (struct binding_value *)0;
+ s0 = evaluate_expression (&bv, packet, lease, client_state,
+ in_options, cfg_options,
+ scope, expr, MDL);
+ if (s0) {
+ if (bv -> type != binding_data)
+ log_error ("%s() returned type %d in %s.",
+ expr -> data.funcall.name,
+ bv -> type,
+ "evaluate_data_expression");
+ else
+ data_string_copy (result, &bv -> value.data,
+ file, line);
+ binding_value_dereference (&bv, MDL);
+ }
+#if defined (DEBUG_EXPRESSIONS)
+ log_debug ("data: %s = %s", expr -> data.funcall.name,
+ s0 ? print_hex_1 (result -> len,
+ result -> data, 50) : "NULL");
+#endif
+ break;
+
+ /* Extract the filename. */
+ case expr_filename:
+ if (packet && packet -> raw -> file [0]) {
+ char *fn =
+ memchr (packet -> raw -> file, 0,
+ sizeof packet -> raw -> file);
+ if (!fn)
+ fn = ((char *)packet -> raw -> file +
+ sizeof packet -> raw -> file);
+ result -> len = fn - &(packet -> raw -> file [0]);
+ if (buffer_allocate (&result -> buffer,
+ result -> len + 1, file, line)) {
+ result -> data = &result -> buffer -> data [0];
+ memcpy (&result -> buffer -> data [0],
+ packet -> raw -> file,
+ result -> len);
+ result -> buffer -> data [result -> len] = 0;
+ result -> terminated = 1;
+ s0 = 1;
+ } else {
+ log_error ("data: filename: no memory.");
+ s0 = 0;
+ }
+ } else
+ s0 = 0;
+
+#if defined (DEBUG_EXPRESSIONS)
+ log_info ("data: filename = \"%s\"",
+ s0 ? (const char *)(result -> data) : "NULL");
+#endif
+ return s0;
+
+ /* Extract the server name. */
+ case expr_sname:
+ if (packet && packet -> raw -> sname [0]) {
+ char *fn =
+ memchr (packet -> raw -> sname, 0,
+ sizeof packet -> raw -> sname);
+ if (!fn)
+ fn = ((char *)packet -> raw -> sname +
+ sizeof packet -> raw -> sname);
+ result -> len = fn - &packet -> raw -> sname [0];
+ if (buffer_allocate (&result -> buffer,
+ result -> len + 1, file, line)) {
+ result -> data = &result -> buffer -> data [0];
+ memcpy (&result -> buffer -> data [0],
+ packet -> raw -> sname,
+ result -> len);
+ result -> buffer -> data [result -> len] = 0;
+ result -> terminated = 1;
+ s0 = 1;
+ } else {
+ log_error ("data: sname: no memory.");
+ s0 = 0;
+ }
+ } else
+ s0 = 0;
+
+#if defined (DEBUG_EXPRESSIONS)
+ log_info ("data: sname = \"%s\"",
+ s0 ? (const char *)(result -> data) : "NULL");
+#endif
+ return s0;
+
+ case expr_check:
+ case expr_equal:
+ case expr_not_equal:
+ case expr_and:
+ case expr_or:
+ case expr_not:
+ case expr_match:
+ case expr_static:
+ case expr_known:
+ case expr_none:
+ case expr_exists:
+ case expr_variable_exists:
+ log_error ("Boolean opcode in evaluate_data_expression: %d",
+ expr -> op);
+ return 0;
+
+ case expr_extract_int8:
+ case expr_extract_int16:
+ case expr_extract_int32:
+ case expr_const_int:
+ case expr_lease_time:
+ case expr_dns_transaction:
+ case expr_add:
+ case expr_subtract:
+ case expr_multiply:
+ case expr_divide:
+ case expr_remainder:
+ case expr_binary_and:
+ case expr_binary_or:
+ case expr_binary_xor:
+ case expr_client_state:
+ log_error ("Numeric opcode in evaluate_data_expression: %d",
+ expr -> op);
+ return 0;
+
+ case expr_ns_add:
+ case expr_ns_delete:
+ case expr_ns_exists:
+ case expr_ns_not_exists:
+ log_error ("dns update opcode in evaluate_data_expression: %d",
+ expr -> op);
+ return 0;
+
+ case expr_function:
+ log_error ("function definition in evaluate_data_expression");
+ return 0;
+
+ case expr_arg:
+ break;
+ }
+
+ log_error ("Bogus opcode in evaluate_data_expression: %d", expr -> op);
+ return 0;
+}
+
+int evaluate_numeric_expression (result, packet, lease, client_state,
+ in_options, cfg_options, scope, expr)
+ unsigned long *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 expression *expr;
+{
+ struct data_string data;
+ int status, sleft, sright;
+#if defined (NSUPDATE)
+ ns_updrec *nut;
+ ns_updque uq;
+#endif
+ struct expression *cur, *next;
+ struct binding *binding;
+ struct binding_value *bv;
+ unsigned long ileft, iright;
+
+ switch (expr -> op) {
+ case expr_check:
+ case expr_equal:
+ case expr_not_equal:
+ case expr_and:
+ case expr_or:
+ case expr_not:
+ case expr_match:
+ case expr_static:
+ case expr_known:
+ case expr_none:
+ case expr_exists:
+ case expr_variable_exists:
+ log_error ("Boolean opcode in evaluate_numeric_expression: %d",
+ expr -> op);
+ return 0;
+
+ case expr_substring:
+ case expr_suffix:
+ case expr_option:
+ case expr_hardware:
+ case expr_const_data:
+ case expr_packet:
+ case expr_concat:
+ case expr_encapsulate:
+ case expr_host_lookup:
+ case expr_encode_int8:
+ case expr_encode_int16:
+ case expr_encode_int32:
+ case expr_binary_to_ascii:
+ case expr_reverse:
+ case expr_filename:
+ case expr_sname:
+ case expr_pick_first_value:
+ case expr_host_decl_name:
+ case expr_config_option:
+ case expr_leased_address:
+ case expr_null:
+ log_error ("Data opcode in evaluate_numeric_expression: %d",
+ expr -> op);
+ return 0;
+
+ case expr_extract_int8:
+ memset (&data, 0, sizeof data);
+ status = evaluate_data_expression
+ (&data, packet, lease, client_state, in_options,
+ cfg_options, scope, expr -> data.extract_int, MDL);
+ if (status)
+ *result = data.data [0];
+#if defined (DEBUG_EXPRESSIONS)
+ log_debug ("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, MDL);
+ return status;
+
+ case expr_extract_int16:
+ memset (&data, 0, sizeof data);
+ status = (evaluate_data_expression
+ (&data, packet, lease, client_state, in_options,
+ cfg_options, scope, expr -> data.extract_int, MDL));
+ if (status && data.len >= 2)
+ *result = getUShort (data.data);
+#if defined (DEBUG_EXPRESSIONS)
+ log_debug ("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, MDL);
+ return (status && data.len >= 2);
+
+ case expr_extract_int32:
+ memset (&data, 0, sizeof data);
+ status = (evaluate_data_expression
+ (&data, packet, lease, client_state, in_options,
+ cfg_options, scope, expr -> data.extract_int, MDL));
+ if (status && data.len >= 4)
+ *result = getULong (data.data);
+#if defined (DEBUG_EXPRESSIONS)
+ log_debug ("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, MDL);
+ return (status && data.len >= 4);
+
+ case expr_const_int:
+ *result = expr -> data.const_int;
+#if defined (DEBUG_EXPRESSIONS)
+ log_debug ("number: CONSTANT = %ld", *result);
+#endif
+ return 1;
+
+ case expr_lease_time:
+ if (!lease) {
+ log_error ("data: leased_lease: not available");
+ return 0;
+ }
+ if (lease -> ends < cur_time) {
+ log_error ("%s %lu when it is now %lu",
+ "data: lease_time: lease ends at",
+ (long)(lease -> ends), (long)cur_time);
+ return 0;
+ }
+ *result = lease -> ends - cur_time;
+#if defined (DEBUG_EXPRESSIONS)
+ log_debug ("number: lease-time = (%lu - %lu) = %ld",
+ lease -> ends,
+ cur_time, *result);
+#endif
+ return 1;
+
+ case expr_dns_transaction:
+#if !defined (NSUPDATE)
+ return 0;
+#else
+ if (!resolver_inited) {
+ minires_ninit (&resolver_state);
+ resolver_inited = 1;
+ resolver_state.retrans = 1;
+ resolver_state.retry = 1;
+ }
+ ISC_LIST_INIT (uq);
+ cur = expr;
+ do {
+ next = cur -> data.dns_transaction.cdr;
+ nut = 0;
+ status = (evaluate_dns_expression
+ (&nut, packet,
+ lease, client_state, in_options, cfg_options,
+ scope, cur -> data.dns_transaction.car));
+ if (!status)
+ goto dns_bad;
+ ISC_LIST_APPEND (uq, nut, r_link);
+ cur = next;
+ } while (next);
+
+ /* Do the update and record the error code, if there was
+ an error; otherwise set it to NOERROR. */
+ *result = minires_nupdate (&resolver_state,
+ ISC_LIST_HEAD (uq));
+ status = 1;
+
+ print_dns_status ((int)*result, &uq);
+
+ dns_bad:
+ while (!ISC_LIST_EMPTY (uq)) {
+ ns_updrec *tmp = ISC_LIST_HEAD (uq);
+ ISC_LIST_UNLINK (uq, tmp, r_link);
+ if (tmp -> r_data_ephem) {
+ dfree (tmp -> r_data_ephem, MDL);
+ tmp -> r_data = (unsigned char *)0;
+ tmp -> r_data_ephem = (unsigned char *)0;
+ }
+ minires_freeupdrec (tmp);
+ }
+ return status;
+#endif /* NSUPDATE */
+
+ case expr_variable_reference:
+ if (scope && *scope) {
+ binding = find_binding (*scope, expr -> data.variable);
+
+ if (binding && binding -> value) {
+ if (binding -> value -> type == binding_numeric) {
+ *result = binding -> value -> value.intval;
+ status = 1;
+ } else {
+ log_error ("binding type %d in %s.",
+ binding -> value -> type,
+ "evaluate_numeric_expression");
+ status = 0;
+ }
+ } else
+ status = 0;
+ } else
+ status = 0;
+#if defined (DEBUG_EXPRESSIONS)
+ if (status)
+ log_debug ("numeric: %s = %ld",
+ expr -> data.variable, *result);
+ else
+ log_debug ("numeric: %s = NULL",
+ expr -> data.variable);
+#endif
+ return status;
+
+ case expr_funcall:
+ bv = (struct binding_value *)0;
+ status = evaluate_expression (&bv, packet, lease,
+ client_state,
+ in_options, cfg_options,
+ scope, expr, MDL);
+ if (status) {
+ if (bv -> type != binding_numeric)
+ log_error ("%s() returned type %d in %s.",
+ expr -> data.funcall.name,
+ bv -> type,
+ "evaluate_numeric_expression");
+ else
+ *result = bv -> value.intval;
+ binding_value_dereference (&bv, MDL);
+ }
+#if defined (DEBUG_EXPRESSIONS)
+ log_debug ("data: %s = %ld", expr -> data.funcall.name,
+ status ? *result : 0);
+#endif
+ break;
+
+ case expr_add:
+ sleft = evaluate_numeric_expression (&ileft, packet, lease,
+ client_state,
+ in_options, cfg_options,
+ scope,
+ expr -> data.and [0]);
+ sright = evaluate_numeric_expression (&iright, packet, lease,
+ client_state,
+ in_options, cfg_options,
+ scope,
+ expr -> data.and [1]);
+
+#if defined (DEBUG_EXPRESSIONS)
+ if (sleft && sright)
+ log_debug ("num: %ld + %ld = %ld",
+ ileft, iright, ileft + iright);
+ else if (sleft)
+ log_debug ("num: %ld + NULL = NULL", ileft);
+ else
+ log_debug ("num: NULL + %ld = NULL", iright);
+#endif
+ if (sleft && sright) {
+ *result = ileft + iright;
+ return 1;
+ }
+ return 0;
+
+ case expr_subtract:
+ sleft = evaluate_numeric_expression (&ileft, packet, lease,
+ client_state,
+ in_options, cfg_options,
+ scope,
+ expr -> data.and [0]);
+ sright = evaluate_numeric_expression (&iright, packet, lease,
+ client_state,
+ in_options, cfg_options,
+ scope,
+ expr -> data.and [1]);
+
+#if defined (DEBUG_EXPRESSIONS)
+ if (sleft && sright)
+ log_debug ("num: %ld - %ld = %ld",
+ ileft, iright, ileft - iright);
+ else if (sleft)
+ log_debug ("num: %ld - NULL = NULL", ileft);
+ else
+ log_debug ("num: NULL - %ld = NULL", iright);
+#endif
+ if (sleft && sright) {
+ *result = ileft - iright;
+ return 1;
+ }
+ return 0;
+
+ case expr_multiply:
+ sleft = evaluate_numeric_expression (&ileft, packet, lease,
+ client_state,
+ in_options, cfg_options,
+ scope,
+ expr -> data.and [0]);
+ sright = evaluate_numeric_expression (&iright, packet, lease,
+ client_state,
+ in_options, cfg_options,
+ scope,
+ expr -> data.and [1]);
+
+#if defined (DEBUG_EXPRESSIONS)
+ if (sleft && sright)
+ log_debug ("num: %ld * %ld = %ld",
+ ileft, iright, ileft * iright);
+ else if (sleft)
+ log_debug ("num: %ld * NULL = NULL", ileft);
+ else
+ log_debug ("num: NULL * %ld = NULL", iright);
+#endif
+ if (sleft && sright) {
+ *result = ileft * iright;
+ return 1;
+ }
+ return 0;
+
+ case expr_divide:
+ sleft = evaluate_numeric_expression (&ileft, packet, lease,
+ client_state,
+ in_options, cfg_options,
+ scope,
+ expr -> data.and [0]);
+ sright = evaluate_numeric_expression (&iright, packet, lease,
+ client_state,
+ in_options, cfg_options,
+ scope,
+ expr -> data.and [1]);
+
+#if defined (DEBUG_EXPRESSIONS)
+ if (sleft && sright) {
+ if (iright != 0)
+ log_debug ("num: %ld / %ld = %ld",
+ ileft, iright, ileft / iright);
+ else
+ log_debug ("num: %ld / %ld = NULL",
+ ileft, iright);
+ } else if (sleft)
+ log_debug ("num: %ld / NULL = NULL", ileft);
+ else
+ log_debug ("num: NULL / %ld = NULL", iright);
#endif
- do_data_copy (bufix, bufp, bufcount, dns -> data, dns -> data_len);
- return dns -> timeout;
+ if (sleft && sright && iright) {
+ *result = ileft / iright;
+ return 1;
+ }
+ return 0;
+
+ case expr_remainder:
+ sleft = evaluate_numeric_expression (&ileft, packet, lease,
+ client_state,
+ in_options, cfg_options,
+ scope,
+ expr -> data.and [0]);
+ sright = evaluate_numeric_expression (&iright, packet, lease,
+ client_state,
+ in_options, cfg_options,
+ scope,
+ expr -> data.and [1]);
+
+#if defined (DEBUG_EXPRESSIONS)
+ if (sleft && sright) {
+ if (iright != 0)
+ log_debug ("num: %ld %% %ld = %ld",
+ ileft, iright, ileft % iright);
+ else
+ log_debug ("num: %ld %% %ld = NULL",
+ ileft, iright);
+ } else if (sleft)
+ log_debug ("num: %ld %% NULL = NULL", ileft);
+ else
+ log_debug ("num: NULL %% %ld = NULL", iright);
+#endif
+ if (sleft && sright && iright) {
+ *result = ileft % iright;
+ return 1;
+ }
+ return 0;
+
+ case expr_binary_and:
+ sleft = evaluate_numeric_expression (&ileft, packet, lease,
+ client_state,
+ in_options, cfg_options,
+ scope,
+ expr -> data.and [0]);
+ sright = evaluate_numeric_expression (&iright, packet, lease,
+ client_state,
+ in_options, cfg_options,
+ scope,
+ expr -> data.and [1]);
+
+#if defined (DEBUG_EXPRESSIONS)
+ if (sleft && sright)
+ log_debug ("num: %ld | %ld = %ld",
+ ileft, iright, ileft & iright);
+ else if (sleft)
+ log_debug ("num: %ld & NULL = NULL", ileft);
+ else
+ log_debug ("num: NULL & %ld = NULL", iright);
+#endif
+ if (sleft && sright) {
+ *result = ileft & iright;
+ return 1;
+ }
+ return 0;
+
+ case expr_binary_or:
+ sleft = evaluate_numeric_expression (&ileft, packet, lease,
+ client_state,
+ in_options, cfg_options,
+ scope,
+ expr -> data.and [0]);
+ sright = evaluate_numeric_expression (&iright, packet, lease,
+ client_state,
+ in_options, cfg_options,
+ scope,
+ expr -> data.and [1]);
+
+#if defined (DEBUG_EXPRESSIONS)
+ if (sleft && sright)
+ log_debug ("num: %ld | %ld = %ld",
+ ileft, iright, ileft | iright);
+ else if (sleft)
+ log_debug ("num: %ld | NULL = NULL", ileft);
+ else
+ log_debug ("num: NULL | %ld = NULL", iright);
+#endif
+ if (sleft && sright) {
+ *result = ileft | iright;
+ return 1;
+ }
+ return 0;
+
+ case expr_binary_xor:
+ sleft = evaluate_numeric_expression (&ileft, packet, lease,
+ client_state,
+ in_options, cfg_options,
+ scope,
+ expr -> data.and [0]);
+ sright = evaluate_numeric_expression (&iright, packet, lease,
+ client_state,
+ in_options, cfg_options,
+ scope,
+ expr -> data.and [1]);
+
+#if defined (DEBUG_EXPRESSIONS)
+ if (sleft && sright)
+ log_debug ("num: %ld ^ %ld = %ld",
+ ileft, iright, ileft ^ iright);
+ else if (sleft)
+ log_debug ("num: %ld ^ NULL = NULL", ileft);
+ else
+ log_debug ("num: NULL ^ %ld = NULL", iright);
+#endif
+ if (sleft && sright) {
+ *result = ileft ^ iright;
+ return 1;
+ }
+ return 0;
+
+ case expr_client_state:
+ if (client_state) {
+#if defined (DEBUG_EXPRESSIONS)
+ log_debug ("num: client-state = %d",
+ client_state -> state);
+#endif
+ *result = client_state -> state;
+ return 1;
+ } else {
+#if defined (DEBUG_EXPRESSIONS)
+ log_debug ("num: client-state = NULL");
+#endif
+ return 0;
+ }
+
+ case expr_ns_add:
+ case expr_ns_delete:
+ case expr_ns_exists:
+ case expr_ns_not_exists:
+ log_error ("dns opcode in evaluate_numeric_expression: %d",
+ expr -> op);
+ return 0;
+
+ case expr_function:
+ log_error ("function definition in evaluate_numeric_expr");
+ return 0;
+
+ case expr_arg:
+ break;
+ }
+
+ log_error ("evaluate_numeric_expression: bogus opcode %d", expr -> op);
+ return 0;
}
-static void do_data_copy (bufix, bufp, bufcount, data, len)
- int *bufix;
- unsigned char **bufp;
- int *bufcount;
- unsigned char *data;
- int len;
+/* 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, lease, client_state,
+ in_options, cfg_options, scope, oc, file, line)
+ 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 option_cache *oc;
+ const char *file;
+ int line;
{
- int space = *bufcount - *bufix;
+ if (oc -> data.len) {
+ data_string_copy (result, &oc -> data, file, line);
+ return 1;
+ }
+ if (!oc -> expression)
+ return 0;
+ return evaluate_data_expression (result, packet, lease, client_state,
+ in_options, cfg_options, scope,
+ oc -> expression, file, line);
+}
+
+/* Evaluate an option cache and extract a boolean from the result,
+ returning the boolean. Return false if there is no data. */
- /* If there's more space than we need, use only what we need. */
- if (space > len)
- space = len;
+int evaluate_boolean_option_cache (ignorep, packet,
+ lease, client_state, in_options,
+ cfg_options, scope, oc, file, line)
+ int *ignorep;
+ 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;
+ const char *file;
+ int line;
+{
+ struct data_string ds;
+ int result;
- /* Copy as much data as will fit, then increment the buffer index
- by the amount we actually had to copy, which could be more. */
- if (space > 0)
- memcpy (*bufp + *bufix, data, space);
- *bufix += len;
+ /* So that we can be called with option_lookup as an argument. */
+ if (!oc || !in_options)
+ return 0;
+
+ memset (&ds, 0, sizeof ds);
+ if (!evaluate_option_cache (&ds, packet,
+ lease, client_state, in_options,
+ cfg_options, scope, oc, file, line))
+ return 0;
+
+ if (ds.len) {
+ result = ds.data [0];
+ if (result == 2) {
+ result = 0;
+ *ignorep = 1;
+ } else
+ *ignorep = 0;
+ } else
+ result = 0;
+ data_string_forget (&ds, MDL);
+ return result;
}
+
+
+/* Evaluate a boolean expression and return the result of the evaluation,
+ or FALSE if it failed. */
+
+int evaluate_boolean_expression_result (ignorep, packet, lease, client_state,
+ in_options, cfg_options, scope, expr)
+ int *ignorep;
+ 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 expression *expr;
+{
+ int result;
+
+ /* So that we can be called with option_lookup as an argument. */
+ if (!expr)
+ return 0;
+
+ if (!evaluate_boolean_expression (&result, packet, lease, client_state,
+ in_options, cfg_options,
+ scope, expr))
+ return 0;
+
+ if (result == 2) {
+ *ignorep = 1;
+ result = 0;
+ } else
+ *ignorep = 0;
+ 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, file, line)
+ struct expression **eptr;
+ const char *file;
+ int line;
+{
+ struct expression *expr = *eptr;
+
+ /* Zero the pointer. */
+ *eptr = (struct expression *)0;
+
+ /* Decrement the reference count. If it's nonzero, we're
+ done. */
+ --(expr -> refcnt);
+ rc_register (file, line, eptr, expr, expr -> refcnt, 1, RC_MISC);
+ if (expr -> refcnt > 0)
+ return;
+ if (expr -> refcnt < 0) {
+ log_error ("%s(%d): negative refcnt!", file, line);
+#if defined (DEBUG_RC_HISTORY)
+ dump_rc_history (expr);
+#endif
+#if defined (POINTER_DEBUG)
+ abort ();
+#else
+ return;
+#endif
+ }
+
+ /* Dereference subexpressions. */
+ switch (expr -> op) {
+ /* All the binary operators can be handled the same way. */
+ case expr_equal:
+ case expr_not_equal:
+ case expr_concat:
+ case expr_and:
+ case expr_or:
+ case expr_add:
+ case expr_subtract:
+ case expr_multiply:
+ case expr_divide:
+ case expr_remainder:
+ case expr_binary_and:
+ case expr_binary_or:
+ case expr_binary_xor:
+ case expr_client_state:
+ if (expr -> data.equal [0])
+ expression_dereference (&expr -> data.equal [0],
+ file, line);
+ if (expr -> data.equal [1])
+ expression_dereference (&expr -> data.equal [1],
+ file, line);
+ break;
+
+ case expr_substring:
+ if (expr -> data.substring.expr)
+ expression_dereference (&expr -> data.substring.expr,
+ file, line);
+ if (expr -> data.substring.offset)
+ expression_dereference (&expr -> data.substring.offset,
+ file, line);
+ if (expr -> data.substring.len)
+ expression_dereference (&expr -> data.substring.len,
+ file, line);
+ break;
+
+ case expr_suffix:
+ if (expr -> data.suffix.expr)
+ expression_dereference (&expr -> data.suffix.expr,
+ file, line);
+ if (expr -> data.suffix.len)
+ expression_dereference (&expr -> data.suffix.len,
+ file, line);
+ break;
+
+ case expr_not:
+ if (expr -> data.not)
+ expression_dereference (&expr -> data.not, file, line);
+ break;
+
+ case expr_packet:
+ if (expr -> data.packet.offset)
+ expression_dereference (&expr -> data.packet.offset,
+ file, line);
+ if (expr -> data.packet.len)
+ expression_dereference (&expr -> data.packet.len,
+ file, line);
+ break;
+
+ case expr_extract_int8:
+ case expr_extract_int16:
+ case expr_extract_int32:
+ if (expr -> data.extract_int)
+ expression_dereference (&expr -> data.extract_int,
+ file, line);
+ break;
+
+ case expr_encode_int8:
+ case expr_encode_int16:
+ case expr_encode_int32:
+ if (expr -> data.encode_int)
+ expression_dereference (&expr -> data.encode_int,
+ file, line);
+ break;
+
+ case expr_encapsulate:
+ case expr_const_data:
+ data_string_forget (&expr -> data.const_data, file, line);
+ break;
+
+ case expr_host_lookup:
+ if (expr -> data.host_lookup)
+ dns_host_entry_dereference (&expr -> data.host_lookup,
+ file, line);
+ break;
+
+ case expr_binary_to_ascii:
+ if (expr -> data.b2a.base)
+ expression_dereference (&expr -> data.b2a.base,
+ file, line);
+ if (expr -> data.b2a.width)
+ expression_dereference (&expr -> data.b2a.width,
+ file, line);
+ if (expr -> data.b2a.seperator)
+ expression_dereference (&expr -> data.b2a.seperator,
+ file, line);
+ if (expr -> data.b2a.buffer)
+ expression_dereference (&expr -> data.b2a.buffer,
+ file, line);
+ break;
+
+ case expr_pick_first_value:
+ if (expr -> data.pick_first_value.car)
+ expression_dereference (&expr -> data.pick_first_value.car,
+ file, line);
+ if (expr -> data.pick_first_value.cdr)
+ expression_dereference (&expr -> data.pick_first_value.cdr,
+ file, line);
+ break;
+
+ case expr_reverse:
+ if (expr -> data.reverse.width)
+ expression_dereference (&expr -> data.reverse.width,
+ file, line);
+ if (expr -> data.reverse.buffer)
+ expression_dereference
+ (&expr -> data.reverse.buffer, file, line);
+ break;
+
+ case expr_dns_transaction:
+ if (expr -> data.dns_transaction.car)
+ expression_dereference (&expr -> data.dns_transaction.car,
+ file, line);
+ if (expr -> data.dns_transaction.cdr)
+ expression_dereference (&expr -> data.dns_transaction.cdr,
+ file, line);
+ break;
+
+ case expr_ns_add:
+ if (expr -> data.ns_add.rrname)
+ expression_dereference (&expr -> data.ns_add.rrname,
+ file, line);
+ if (expr -> data.ns_add.rrdata)
+ expression_dereference (&expr -> data.ns_add.rrdata,
+ file, line);
+ if (expr -> data.ns_add.ttl)
+ expression_dereference (&expr -> data.ns_add.ttl,
+ file, line);
+ break;
+
+ case expr_ns_delete:
+ case expr_ns_exists:
+ case expr_ns_not_exists:
+ if (expr -> data.ns_delete.rrname)
+ expression_dereference (&expr -> data.ns_delete.rrname,
+ file, line);
+ if (expr -> data.ns_delete.rrdata)
+ expression_dereference (&expr -> data.ns_delete.rrdata,
+ file, line);
+ break;
+
+ case expr_variable_reference:
+ case expr_variable_exists:
+ if (expr -> data.variable)
+ dfree (expr -> data.variable, file, line);
+ break;
+
+ case expr_funcall:
+ if (expr -> data.funcall.name)
+ dfree (expr -> data.funcall.name, file, line);
+ if (expr -> data.funcall.arglist)
+ expression_dereference (&expr -> data.funcall.arglist,
+ file, line);
+ break;
+
+ case expr_arg:
+ if (expr -> data.arg.val)
+ expression_dereference (&expr -> data.arg.val,
+ file, line);
+ if (expr -> data.arg.next)
+ expression_dereference (&expr -> data.arg.next,
+ file, line);
+ break;
+
+ case expr_function:
+ fundef_dereference (&expr -> data.func, file, line);
+ break;
+
+ /* No subexpressions. */
+ case expr_leased_address:
+ case expr_lease_time:
+ case expr_filename:
+ case expr_sname:
+ case expr_const_int:
+ case expr_check:
+ case expr_option:
+ case expr_hardware:
+ case expr_exists:
+ case expr_known:
+ case expr_null:
+ break;
+
+ default:
+ break;
+ }
+ free_expression (expr, MDL);
+}
+
+int is_dns_expression (expr)
+ struct expression *expr;
+{
+ return (expr -> op == expr_ns_add ||
+ expr -> op == expr_ns_delete ||
+ expr -> op == expr_ns_exists ||
+ expr -> op == expr_ns_not_exists);
+}
+
+int is_boolean_expression (expr)
+ struct expression *expr;
+{
+ return (expr -> op == expr_check ||
+ expr -> op == expr_exists ||
+ expr -> op == expr_variable_exists ||
+ expr -> op == expr_equal ||
+ expr -> op == expr_not_equal ||
+ expr -> op == expr_and ||
+ expr -> op == expr_or ||
+ expr -> op == expr_not ||
+ expr -> op == expr_known ||
+ expr -> op == expr_static);
+}
+
+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_encapsulate ||
+ expr -> op == expr_encode_int8 ||
+ expr -> op == expr_encode_int16 ||
+ expr -> op == expr_encode_int32 ||
+ expr -> op == expr_host_lookup ||
+ expr -> op == expr_binary_to_ascii ||
+ expr -> op == expr_filename ||
+ expr -> op == expr_sname ||
+ expr -> op == expr_reverse ||
+ expr -> op == expr_pick_first_value ||
+ expr -> op == expr_host_decl_name ||
+ expr -> op == expr_leased_address ||
+ expr -> op == expr_config_option ||
+ expr -> op == expr_null);
+}
+
+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 ||
+ expr -> op == expr_lease_time ||
+ expr -> op == expr_dns_transaction ||
+ expr -> op == expr_add ||
+ expr -> op == expr_subtract ||
+ expr -> op == expr_multiply ||
+ expr -> op == expr_divide ||
+ expr -> op == expr_remainder ||
+ expr -> op == expr_binary_and ||
+ expr -> op == expr_binary_or ||
+ expr -> op == expr_binary_xor ||
+ expr -> op == expr_client_state);
+}
+
+int is_compound_expression (expr)
+ struct expression *expr;
+{
+ return (expr -> op == expr_ns_add ||
+ expr -> op == expr_ns_delete ||
+ expr -> op == expr_ns_exists ||
+ expr -> op == expr_ns_not_exists ||
+ expr -> op == expr_substring ||
+ expr -> op == expr_suffix ||
+ expr -> op == expr_option ||
+ expr -> op == expr_concat ||
+ expr -> op == expr_encode_int8 ||
+ expr -> op == expr_encode_int16 ||
+ expr -> op == expr_encode_int32 ||
+ expr -> op == expr_binary_to_ascii ||
+ expr -> op == expr_reverse ||
+ expr -> op == expr_pick_first_value ||
+ expr -> op == expr_config_option ||
+ expr -> op == expr_extract_int8 ||
+ expr -> op == expr_extract_int16 ||
+ expr -> op == expr_extract_int32 ||
+ expr -> op == expr_dns_transaction);
+}
+
+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_static:
+ case expr_check:
+ case expr_substring:
+ case expr_suffix:
+ case expr_concat:
+ case expr_encapsulate:
+ 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_encode_int8:
+ case expr_encode_int16:
+ case expr_encode_int32:
+ case expr_const_int:
+ case expr_exists:
+ case expr_variable_exists:
+ case expr_known:
+ case expr_binary_to_ascii:
+ case expr_reverse:
+ case expr_filename:
+ case expr_sname:
+ case expr_pick_first_value:
+ case expr_host_decl_name:
+ case expr_config_option:
+ case expr_leased_address:
+ case expr_lease_time:
+ case expr_dns_transaction:
+ case expr_null:
+ case expr_variable_reference:
+ case expr_ns_add:
+ case expr_ns_delete:
+ case expr_ns_exists:
+ case expr_ns_not_exists:
+ case expr_arg:
+ case expr_funcall:
+ case expr_function:
+ /* XXXDPN: Need to assign sane precedences to these. */
+ case expr_binary_and:
+ case expr_binary_or:
+ case expr_binary_xor:
+ case expr_client_state:
+ return 100;
+
+ case expr_equal:
+ case expr_not_equal:
+ return 3;
+
+ case expr_and:
+ case expr_multiply:
+ case expr_divide:
+ case expr_remainder:
+ return 1;
+
+ case expr_or:
+ case expr_add:
+ case expr_subtract:
+ 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 expression_context (struct expression *expr)
+{
+ if (is_data_expression (expr))
+ return context_data;
+ if (is_numeric_expression (expr))
+ return context_numeric;
+ if (is_boolean_expression (expr))
+ return context_boolean;
+ if (is_dns_expression (expr))
+ return context_dns;
+ return context_any;
+}
+
+enum expression_context op_context (op)
+ enum expr_op op;
+{
+ switch (op) {
+/* XXX Why aren't these specific? */
+ case expr_none:
+ case expr_match:
+ case expr_static:
+ case expr_check:
+ case expr_substring:
+ case expr_suffix:
+ case expr_concat:
+ case expr_encapsulate:
+ 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_encode_int8:
+ case expr_encode_int16:
+ case expr_encode_int32:
+ case expr_const_int:
+ case expr_exists:
+ case expr_variable_exists:
+ case expr_known:
+ case expr_binary_to_ascii:
+ case expr_reverse:
+ case expr_filename:
+ case expr_sname:
+ case expr_pick_first_value:
+ case expr_host_decl_name:
+ case expr_config_option:
+ case expr_leased_address:
+ case expr_lease_time:
+ case expr_null:
+ case expr_variable_reference:
+ case expr_ns_add:
+ case expr_ns_delete:
+ case expr_ns_exists:
+ case expr_ns_not_exists:
+ case expr_dns_transaction:
+ case expr_arg:
+ case expr_funcall:
+ case expr_function:
+ return context_any;
+
+ case expr_equal:
+ case expr_not_equal:
+ return context_data;
+
+ case expr_and:
+ return context_boolean;
+
+ case expr_or:
+ return context_boolean;
+
+ case expr_add:
+ case expr_subtract:
+ case expr_multiply:
+ case expr_divide:
+ case expr_remainder:
+ case expr_binary_and:
+ case expr_binary_or:
+ case expr_binary_xor:
+ case expr_client_state:
+ return context_numeric;
+ }
+ return context_any;
+}
+
+int write_expression (file, expr, col, indent, firstp)
+ FILE *file;
+ struct expression *expr;
+ int col;
+ int indent;
+ int firstp;
+{
+ struct expression *e;
+ const char *s;
+ char obuf [65];
+ int scol;
+ int width;
+
+ /* If this promises to be a fat expression, start a new line. */
+ if (!firstp && is_compound_expression (expr)) {
+ indent_spaces (file, indent);
+ col = indent;
+ }
+
+ switch (expr -> op) {
+ case expr_none:
+ col = token_print_indent (file, col, indent, "", "", "null");
+ break;
+
+ case expr_check:
+ col = token_print_indent (file, col, indent, "", "", "check");
+ col = token_print_indent_concat (file, col, indent,
+ " ", "", "\"",
+ expr -> data.check -> name,
+ "\"", (char *)0);
+ break;
+
+ case expr_not_equal:
+ s = "!=";
+ goto binary;
+
+ case expr_equal:
+ s = "=";
+ binary:
+ col = write_expression (file, expr -> data.equal [0],
+ col, indent, 1);
+ col = token_print_indent (file, col, indent, " ", " ", s);
+ col = write_expression (file, expr -> data.equal [1],
+ col, indent + 2, 0);
+ break;
+
+ case expr_substring:
+ col = token_print_indent (file, col, indent, "", "",
+ "substring");
+ col = token_print_indent (file, col, indent, " ", "", "(");
+ scol = col;
+ col = write_expression (file, expr -> data.substring.expr,
+ col, scol, 1);
+ col = token_print_indent (file, col, indent, "", " ", ",");
+ col = write_expression (file, expr -> data.substring.offset,
+ col, indent, 0);
+ col = token_print_indent (file, col, scol, "", " ", ",");
+ col = write_expression (file, expr -> data.substring.len,
+ col, scol, 0);
+ col = token_print_indent (file, col, indent, "", "", ")");
+ break;
+
+ case expr_suffix:
+ col = token_print_indent (file, col, indent, "", "", "suffix");
+ col = token_print_indent (file, col, indent, " ", "", "(");
+ scol = col;
+ col = write_expression (file, expr -> data.suffix.expr,
+ col, scol, 1);
+ col = token_print_indent (file, col, scol, "", " ", ",");
+ col = write_expression (file, expr -> data.suffix.len,
+ col, scol, 0);
+ col = token_print_indent (file, col, indent, "", "", ")");
+ break;
+
+ case expr_concat:
+ e = expr;
+ col = token_print_indent (file, col, indent, "", "",
+ "concat");
+ col = token_print_indent (file, col, indent, " ", "", "(");
+ scol = col;
+ firstp = 1;
+ concat_again:
+ col = write_expression (file, e -> data.concat [0],
+ col, scol, firstp);
+ firstp = 0;
+ if (!e -> data.concat [1])
+ goto no_concat_cdr;
+ col = token_print_indent (file, col, scol, "", " ", ",");
+ if (e -> data.concat [1] -> op == expr_concat) {
+ e = e -> data.concat [1];
+ goto concat_again;
+ }
+ col = write_expression (file, e -> data.concat [1],
+ col, scol, 0);
+ no_concat_cdr:
+ col = token_print_indent (file, col, indent, "", "", ")");
+ break;
+
+ case expr_host_lookup:
+ col = token_print_indent (file, col, indent, "", "",
+ "gethostbyname");
+ col = token_print_indent (file, col, indent, " ", "", "(");
+ col = token_print_indent_concat
+ (file, col, indent, "", "",
+ "\"", expr -> data.host_lookup -> hostname, "\"",
+ (char *)0);
+ col = token_print_indent (file, col, indent, "", "", ")");
+ break;
+
+ case expr_add:
+ s = "+";
+ goto binary;
+
+ case expr_subtract:
+ s = "-";
+ goto binary;
+
+ case expr_multiply:
+ s = "*";
+ goto binary;
+
+ case expr_divide:
+ s = "/";
+ goto binary;
+
+ case expr_remainder:
+ s = "%";
+ goto binary;
+
+ case expr_binary_and:
+ s = "&";
+ goto binary;
+
+ case expr_binary_or:
+ s = "|";
+ goto binary;
+
+ case expr_binary_xor:
+ s = "^";
+ goto binary;
+
+ case expr_and:
+ s = "and";
+ goto binary;
+
+ case expr_or:
+ s = "or";
+ goto binary;
+
+ case expr_not:
+ col = token_print_indent (file, col, indent, "", " ", "not");
+ col = write_expression (file,
+ expr -> data.not, col, indent + 2, 1);
+ break;
+
+ case expr_option:
+ s = "option";
+
+ print_option_name:
+ col = token_print_indent (file, col, indent, "", "", s);
+
+ if (expr -> data.option -> universe != &dhcp_universe) {
+ col = token_print_indent (file, col, indent,
+ " ", "",
+ (expr -> data.option ->
+ universe -> name));
+ col = token_print_indent (file, col, indent, "", "",
+ ".");
+ col = token_print_indent (file, col, indent, "", "",
+ expr -> data.option -> name);
+ } else {
+ col = token_print_indent (file, col, indent, " ", "",
+ expr -> data.option -> name);
+ }
+ break;
+
+ case expr_hardware:
+ col = token_print_indent (file, col, indent, "", "",
+ "hardware");
+ break;
+
+ case expr_packet:
+ col = token_print_indent (file, col, indent, "", "",
+ "packet");
+ col = token_print_indent (file, col, indent, " ", "", "(");
+ scol = col;
+ col = write_expression (file, expr -> data.packet.offset,
+ col, indent, 1);
+ col = token_print_indent (file, col, scol, "", " ", ",");
+ col = write_expression (file, expr -> data.packet.len,
+ col, scol, 0);
+ col = token_print_indent (file, col, indent, "", "", ")");
+ break;
+
+ case expr_const_data:
+ col = token_indent_data_string (file, col, indent, "", "",
+ &expr -> data.const_data);
+ break;
+
+ case expr_extract_int8:
+ width = 8;
+ extract_int:
+ col = token_print_indent (file, col, indent, "", "",
+ "extract-int");
+ col = token_print_indent (file, col, indent, " ", "", "(");
+ scol = col;
+ col = write_expression (file, expr -> data.extract_int,
+ col, indent, 1);
+ col = token_print_indent (file, col, scol, "", " ", ",");
+ sprintf (obuf, "%d", width);
+ col = token_print_indent (file, col, scol, " ", "", obuf);
+ col = token_print_indent (file, col, indent, "", "", ")");
+ break;
+
+ case expr_extract_int16:
+ width = 16;
+ goto extract_int;
+
+ case expr_extract_int32:
+ width = 32;
+ goto extract_int;
+
+ case expr_encode_int8:
+ width = 8;
+ encode_int:
+ col = token_print_indent (file, col, indent, "", "",
+ "encode-int");
+ col = token_print_indent (file, col, indent, " ", "", "(");
+ scol = col;
+ col = write_expression (file, expr -> data.extract_int,
+ col, indent, 1);
+ col = token_print_indent (file, col, scol, "", " ", ",");
+ sprintf (obuf, "%d", width);
+ col = token_print_indent (file, col, scol, " ", "", obuf);
+ col = token_print_indent (file, col, indent, "", "",
+ ")");
+ break;
+
+ case expr_encode_int16:
+ width = 16;
+ goto encode_int;
+
+ case expr_encode_int32:
+ width = 32;
+ goto encode_int;
+
+ case expr_const_int:
+ sprintf (obuf, "%lu", expr -> data.const_int);
+ col = token_print_indent (file, col, indent, "", "", obuf);
+ break;
+
+ case expr_exists:
+ s = "exists";
+ goto print_option_name;
+
+ case expr_encapsulate:
+ col = token_print_indent (file, col, indent, "", "",
+ "encapsulate");
+ col = token_indent_data_string (file, col, indent, " ", "",
+ &expr -> data.encapsulate);
+ break;
+
+ case expr_known:
+ col = token_print_indent (file, col, indent, "", "", "known");
+ break;
+
+ case expr_reverse:
+ col = token_print_indent (file, col, indent, "", "",
+ "reverse");
+ col = token_print_indent (file, col, indent, " ", "", "(");
+ scol = col;
+ col = write_expression (file, expr -> data.reverse.width,
+ col, scol, 1);
+ col = token_print_indent (file, col, scol, "", " ", ",");
+ col = write_expression (file, expr -> data.reverse.buffer,
+ col, scol, 0);
+ col = token_print_indent (file, col, indent, "", "",
+ ")");
+ break;
+
+ case expr_leased_address:
+ col = token_print_indent (file, col, indent, "", "",
+ "leased-address");
+ break;
+
+ case expr_client_state:
+ col = token_print_indent (file, col, indent, "", "",
+ "client-state");
+ break;
+
+ case expr_binary_to_ascii:
+ col = token_print_indent (file, col, indent, "", "",
+ "binary-to-ascii");
+ col = token_print_indent (file, col, indent, " ", "",
+ "(");
+ scol = col;
+ col = write_expression (file, expr -> data.b2a.base,
+ col, scol, 1);
+ col = token_print_indent (file, col, scol, "", " ",
+ ",");
+ col = write_expression (file, expr -> data.b2a.width,
+ col, scol, 0);
+ col = token_print_indent (file, col, scol, "", " ",
+ ",");
+ col = write_expression (file, expr -> data.b2a.seperator,
+ col, scol, 0);
+ col = token_print_indent (file, col, scol, "", " ",
+ ",");
+ col = write_expression (file, expr -> data.b2a.buffer,
+ col, scol, 0);
+ col = token_print_indent (file, col, indent, "", "",
+ ")");
+ break;
+
+ case expr_config_option:
+ s = "config-option";
+ goto print_option_name;
+
+ case expr_host_decl_name:
+ col = token_print_indent (file, col, indent, "", "",
+ "host-decl-name");
+ break;
+
+ case expr_pick_first_value:
+ e = expr;
+ col = token_print_indent (file, col, indent, "", "",
+ "concat");
+ col = token_print_indent (file, col, indent, " ", "",
+ "(");
+ scol = col;
+ firstp = 1;
+ pick_again:
+ col = write_expression (file,
+ e -> data.pick_first_value.car,
+ col, scol, firstp);
+ firstp = 0;
+ /* We're being very lisp-like right now - instead of
+ representing this expression as (first middle . last) we're
+ representing it as (first middle last), which means that the
+ tail cdr is always nil. Apologies to non-wisp-lizards - may
+ this obscure way of describing the problem motivate you to
+ learn more about the one true computing language. */
+ if (!e -> data.pick_first_value.cdr)
+ goto no_pick_cdr;
+ col = token_print_indent (file, col, scol, "", " ",
+ ",");
+ if (e -> data.pick_first_value.cdr -> op ==
+ expr_pick_first_value) {
+ e = e -> data.pick_first_value.cdr;
+ goto pick_again;
+ }
+ col = write_expression (file,
+ e -> data.pick_first_value.cdr,
+ col, scol, 0);
+ no_pick_cdr:
+ col = token_print_indent (file, col, indent, "", "",
+ ")");
+ break;
+
+ case expr_lease_time:
+ col = token_print_indent (file, col, indent, "", "",
+ "lease-time");
+ break;
+
+ case expr_dns_transaction:
+ col = token_print_indent (file, col, indent, "", "",
+ "ns-update");
+ col = token_print_indent (file, col, indent, " ", "",
+ "(");
+ scol = 0;
+ for (e = expr;
+ e && e -> op == expr_dns_transaction;
+ e = e -> data.dns_transaction.cdr) {
+ if (!scol) {
+ scol = col;
+ firstp = 1;
+ } else
+ firstp = 0;
+ col = write_expression (file,
+ e -> data.dns_transaction.car,
+ col, scol, firstp);
+ if (e -> data.dns_transaction.cdr)
+ col = token_print_indent (file, col, scol,
+ "", " ", ",");
+ }
+ if (e)
+ col = write_expression (file, e, col, scol, 0);
+ col = token_print_indent (file, col, indent, "", "", ")");
+ break;
+
+ case expr_ns_add:
+ col = token_print_indent (file, col, indent, "", "",
+ "update");
+ col = token_print_indent (file, col, indent, " ", "",
+ "(");
+ scol = col;
+ sprintf (obuf, "%d", expr -> data.ns_add.rrclass);
+ col = token_print_indent (file, col, scol, "", "", obuf);
+ col = token_print_indent (file, col, scol, "", " ",
+ ",");
+ sprintf (obuf, "%d", expr -> data.ns_add.rrtype);
+ col = token_print_indent (file, col, scol, "", "", obuf);
+ col = token_print_indent (file, col, scol, "", " ",
+ ",");
+ col = write_expression (file, expr -> data.ns_add.rrname,
+ col, scol, 0);
+ col = token_print_indent (file, col, scol, "", " ",
+ ",");
+ col = write_expression (file, expr -> data.ns_add.rrdata,
+ col, scol, 0);
+ col = token_print_indent (file, col, scol, "", " ",
+ ",");
+ col = write_expression (file, expr -> data.ns_add.ttl,
+ col, scol, 0);
+ col = token_print_indent (file, col, indent, "", "",
+ ")");
+ break;
+
+ case expr_ns_delete:
+ col = token_print_indent (file, col, indent, "", "",
+ "delete");
+ col = token_print_indent (file, col, indent, " ", "",
+ "(");
+ finish_ns_small:
+ scol = col;
+ sprintf (obuf, "%d", expr -> data.ns_add.rrclass);
+ col = token_print_indent (file, col, scol, "", "", obuf);
+ col = token_print_indent (file, col, scol, "", " ",
+ ",");
+ sprintf (obuf, "%d", expr -> data.ns_add.rrtype);
+ col = token_print_indent (file, col, scol, "", "", obuf);
+ col = token_print_indent (file, col, scol, "", " ",
+ ",");
+ col = write_expression (file, expr -> data.ns_add.rrname,
+ col, scol, 0);
+ col = token_print_indent (file, col, scol, "", " ",
+ ",");
+ col = write_expression (file, expr -> data.ns_add.rrdata,
+ col, scol, 0);
+ col = token_print_indent (file, col, indent, "", "",
+ ")");
+ break;
+
+ case expr_ns_exists:
+ col = token_print_indent (file, col, indent, "", "",
+ "exists");
+ col = token_print_indent (file, col, indent, " ", "",
+ "(");
+ goto finish_ns_small;
+
+ case expr_ns_not_exists:
+ col = token_print_indent (file, col, indent, "", "",
+ "not exists");
+ col = token_print_indent (file, col, indent, " ", "",
+ "(");
+ goto finish_ns_small;
+
+ case expr_static:
+ col = token_print_indent (file, col, indent, "", "",
+ "static");
+ break;
+
+ case expr_null:
+ col = token_print_indent (file, col, indent, "", "", "null");
+ break;
+
+ case expr_variable_reference:
+ col = token_print_indent (file, indent, indent, "", "",
+ expr -> data.variable);
+ break;
+
+ case expr_variable_exists:
+ col = token_print_indent (file, indent, indent, "", "",
+ "defined");
+ col = token_print_indent (file, col, indent, " ", "", "(");
+ col = token_print_indent (file, col, indent, "", "",
+ expr -> data.variable);
+ col = token_print_indent (file, col, indent, "", "", ")");
+ break;
+
+ default:
+ log_fatal ("invalid expression type in print_expression: %d",
+ expr -> op);
+ }
+ return col;
+}
+
+struct binding *find_binding (struct binding_scope *scope, const char *name)
+{
+ struct binding *bp;
+ struct binding_scope *s;
+
+ for (s = scope; s; s = s -> outer) {
+ for (bp = s -> bindings; bp; bp = bp -> next) {
+ if (!strcasecmp (name, bp -> name)) {
+ return bp;
+ }
+ }
+ }
+ return (struct binding *)0;
+}
+
+int free_bindings (struct binding_scope *scope, const char *file, int line)
+{
+ struct binding *bp, *next;
+
+ for (bp = scope -> bindings; bp; bp = next) {
+ next = bp -> next;
+ if (bp -> name)
+ dfree (bp -> name, file, line);
+ if (bp -> value)
+ binding_value_dereference (&bp -> value, file, line);
+ dfree (bp, file, line);
+ }
+ scope -> bindings = (struct binding *)0;
+ return 1;
+}
+
+int binding_scope_dereference (ptr, file, line)
+ struct binding_scope **ptr;
+ const char *file;
+ int line;
+{
+ int i;
+ struct binding_scope *binding_scope;
+
+ if (!ptr || !*ptr) {
+ log_error ("%s(%d): null pointer", file, line);
+#if defined (POINTER_DEBUG)
+ abort ();
+#else
+ return 0;
+#endif
+ }
+
+ binding_scope = *ptr;
+ *ptr = (struct binding_scope *)0;
+ --binding_scope -> refcnt;
+ rc_register (file, line, ptr,
+ binding_scope, binding_scope -> refcnt, 1, RC_MISC);
+ if (binding_scope -> refcnt > 0)
+ return 1;
+
+ if (binding_scope -> refcnt < 0) {
+ log_error ("%s(%d): negative refcnt!", file, line);
+#if defined (DEBUG_RC_HISTORY)
+ dump_rc_history (binding_scope);
+#endif
+#if defined (POINTER_DEBUG)
+ abort ();
+#else
+ return 0;
+#endif
+ }
+
+ free_bindings (binding_scope, file, line);
+ if (binding_scope -> outer)
+ binding_scope_dereference (&binding_scope -> outer, MDL);
+ dfree (binding_scope, file, line);
+ return 1;
+}
+
+int fundef_dereference (ptr, file, line)
+ struct fundef **ptr;
+ const char *file;
+ int line;
+{
+ struct fundef *bp = *ptr;
+ struct string_list *sp, *next;
+
+ if (!ptr) {
+ log_error ("%s(%d): null pointer", file, line);
+#if defined (POINTER_DEBUG)
+ abort ();
+#else
+ return 0;
+#endif
+ }
+
+ if (!bp) {
+ log_error ("%s(%d): null pointer", file, line);
+#if defined (POINTER_DEBUG)
+ abort ();
+#else
+ return 0;
+#endif
+ }
+
+ bp -> refcnt--;
+ rc_register (file, line, ptr, bp, bp -> refcnt, 1, RC_MISC);
+ if (bp -> refcnt < 0) {
+ log_error ("%s(%d): negative refcnt!", file, line);
+#if defined (DEBUG_RC_HISTORY)
+ dump_rc_history (bp);
+#endif
+#if defined (POINTER_DEBUG)
+ abort ();
+#else
+ return 0;
+#endif
+ }
+ if (!bp -> refcnt) {
+ for (sp = bp -> args; sp; sp = next) {
+ next = sp -> next;
+ dfree (sp, file, line);
+ }
+ if (bp -> statements)
+ executable_statement_dereference (&bp -> statements,
+ file, line);
+ dfree (bp, file, line);
+ }
+ *ptr = (struct fundef *)0;
+ return 1;
+}
+
+#if defined (NOTYET) /* Post 3.0 final. */
+int data_subexpression_length (int *rv,
+ struct expression *expr)
+{
+ int crhs, clhs, llhs, lrhs;
+ switch (expr -> op) {
+ case expr_substring:
+ if (expr -> data.substring.len &&
+ expr -> data.substring.len -> op == expr_const_int) {
+ (*rv =
+ (int)expr -> data.substring.len -> data.const_int);
+ return 1;
+ }
+ return 0;
+
+ case expr_packet:
+ case expr_suffix:
+ if (expr -> data.suffix.len &&
+ expr -> data.suffix.len -> op == expr_const_int) {
+ (*rv =
+ (int)expr -> data.suffix.len -> data.const_int);
+ return 1;
+ }
+ return 0;
+
+ case expr_concat:
+ clhs = data_subexpression_length (&llhs,
+ expr -> data.concat [0]);
+ crhs = data_subexpression_length (&lrhs,
+ expr -> data.concat [1]);
+ if (crhs == 0 || clhs == 0)
+ return 0;
+ *rv = llhs + lrhs;
+ return 1;
+ break;
+
+ case expr_hardware:
+ return 0;
+
+ case expr_const_data:
+ *rv = expr -> data.const_data.len;
+ return 2;
+
+ case expr_reverse:
+ return data_subexpression_length (rv,
+ expr -> data.reverse.buffer);
+
+ case expr_leased_address:
+ case expr_lease_time:
+ *rv = 4;
+ return 2;
+
+ case expr_pick_first_value:
+ clhs = data_subexpression_length (&llhs,
+ expr -> data.concat [0]);
+ crhs = data_subexpression_length (&lrhs,
+ expr -> data.concat [1]);
+ if (crhs == 0 || clhs == 0)
+ return 0;
+ if (llhs > lrhs)
+ *rv = llhs;
+ else
+ *rv = lrhs;
+ return 1;
+
+ case expr_binary_to_ascii:
+ case expr_config_option:
+ case expr_host_decl_name:
+ case expr_encapsulate:
+ case expr_filename:
+ case expr_sname:
+ case expr_host_lookup:
+ case expr_option:
+ case expr_none:
+ case expr_match:
+ case expr_check:
+ case expr_equal:
+ case expr_and:
+ case expr_or:
+ case expr_not:
+ case expr_extract_int8:
+ case expr_extract_int16:
+ case expr_extract_int32:
+ case expr_encode_int8:
+ case expr_encode_int16:
+ case expr_encode_int32:
+ case expr_const_int:
+ case expr_exists:
+ case expr_known:
+ case expr_dns_transaction:
+ case expr_static:
+ case expr_ns_add:
+ case expr_ns_delete:
+ case expr_ns_exists:
+ case expr_ns_not_exists:
+ case expr_not_equal:
+ case expr_null:
+ case expr_variable_exists:
+ case expr_variable_reference:
+ case expr_arg:
+ case expr_funcall:
+ case expr_function:
+ case expr_add:
+ case expr_subtract:
+ case expr_multiply:
+ case expr_divide:
+ case expr_remainder:
+ case expr_binary_and:
+ case expr_binary_or:
+ case expr_binary_xor:
+ case expr_client_state:
+ return 0;
+ }
+ return 0;
+}
+
+int expr_valid_for_context (struct expression *expr,
+ enum expression_context context)
+{
+ /* We don't know at parse time what type of value a function may
+ return, so we can't flag an error on it. */
+ if (expr -> op == expr_funcall ||
+ expr -> op == expr_variable_reference)
+ return 1;
+
+ switch (context) {
+ case context_any:
+ return 1;
+
+ case context_boolean:
+ if (is_boolean_expression (expr))
+ return 1;
+ return 0;
+
+ case context_data:
+ if (is_data_expression (expr))
+ return 1;
+ return 0;
+
+ case context_numeric:
+ if (is_numeric_expression (expr))
+ return 1;
+ return 0;
+
+ case context_dns:
+ if (is_dns_expression (expr)) {
+ return 1;
+ }
+ return 0;
+
+ case context_data_or_numeric:
+ if (is_numeric_expression (expr) ||
+ is_data_expression (expr)) {
+ return 1;
+ }
+ return 0;
+
+ case context_function:
+ if (expr -> op == expr_function)
+ return 1;
+ return 0;
+ }
+ return 0;
+}
+#endif /* NOTYET */
+
+struct binding *create_binding (struct binding_scope **scope, const char *name)
+{
+ struct binding *binding;
+
+ if (!*scope) {
+ if (!binding_scope_allocate (scope, MDL))
+ return (struct binding *)0;
+ }
+
+ binding = find_binding (*scope, name);
+ if (!binding) {
+ binding = dmalloc (sizeof *binding, MDL);
+ if (!binding)
+ return (struct binding *)0;
+
+ memset (binding, 0, sizeof *binding);
+ binding -> name = dmalloc (strlen (name) + 1, MDL);
+ if (!binding -> name) {
+ dfree (binding, MDL);
+ return (struct binding *)0;
+ }
+ strcpy (binding -> name, name);
+
+ binding -> next = (*scope) -> bindings;
+ (*scope) -> bindings = binding;
+ }
+
+ return binding;
+}
+
+
+int bind_ds_value (struct binding_scope **scope,
+ const char *name,
+ struct data_string *value)
+{
+ struct binding *binding;
+
+ binding = create_binding (scope, name);
+ if (!binding)
+ return 0;
+
+ if (binding -> value)
+ binding_value_dereference (&binding -> value, MDL);
+
+ if (!binding_value_allocate (&binding -> value, MDL))
+ return 0;
+
+ data_string_copy (&binding -> value -> value.data, value, MDL);
+ binding -> value -> type = binding_data;
+
+ return 1;
+}
+
+
+int find_bound_string (struct data_string *value,
+ struct binding_scope *scope,
+ const char *name)
+{
+ struct binding *binding;
+
+ binding = find_binding (scope, name);
+ if (!binding ||
+ !binding -> value ||
+ binding -> value -> type != binding_data)
+ return 0;
+
+ if (binding -> value -> value.data.terminated) {
+ data_string_copy (value, &binding -> value -> value.data, MDL);
+ } else {
+ buffer_allocate (&value -> buffer,
+ binding -> value -> value.data.len,
+ MDL);
+ if (!value -> buffer)
+ return 0;
+
+ memcpy (value -> buffer -> data,
+ binding -> value -> value.data.data,
+ binding -> value -> value.data.len);
+ value -> data = value -> buffer -> data;
+ value -> len = binding -> value -> value.data.len;
+ }
+
+ return 1;
+}
+
+int unset (struct binding_scope *scope, const char *name)
+{
+ struct binding *binding;
+
+ binding = find_binding (scope, name);
+ if (binding) {
+ if (binding -> value)
+ binding_value_dereference
+ (&binding -> value, MDL);
+ return 1;
+ }
+ return 0;
+}
+
+/* vim: set tabstop=8: */
OpenPOWER on IntegriCloud