diff options
Diffstat (limited to 'contrib/isc-dhcp/common/tree.c')
-rw-r--r-- | contrib/isc-dhcp/common/tree.c | 4195 |
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: */ |