aboutsummaryrefslogtreecommitdiff
path: root/common/tree.c
diff options
context:
space:
mode:
Diffstat (limited to 'common/tree.c')
-rw-r--r--common/tree.c4461
1 files changed, 4461 insertions, 0 deletions
diff --git a/common/tree.c b/common/tree.c
new file mode 100644
index 0000000..d09107b
--- /dev/null
+++ b/common/tree.c
@@ -0,0 +1,4461 @@
+/* tree.c
+
+ Routines for manipulating parse trees... */
+
+/*
+ * Copyright (c) 2011 by Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (c) 2004-2007,2009 by Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (c) 1995-2003 by Internet Software Consortium
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+ * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Internet Systems Consortium, Inc.
+ * 950 Charter Street
+ * Redwood City, CA 94063
+ * <info@isc.org>
+ * https://www.isc.org/
+ *
+ * This software has been written for Internet Systems Consortium
+ * by Ted Lemon in cooperation with Vixie Enterprises and Nominum, Inc.
+ * To learn more about Internet Systems Consortium, see
+ * ``https://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''.
+ */
+
+#include "dhcpd.h"
+#include <omapip/omapip_p.h>
+#include <ctype.h>
+#include <sys/wait.h>
+
+#ifdef HAVE_REGEX_H
+# include <regex.h>
+#endif
+
+struct binding_scope *global_scope;
+
+static int do_host_lookup (struct data_string *, struct dns_host_entry *);
+
+#define DS_SPRINTF_SIZE 128
+
+/*
+ * If we are using a data_string structure to hold a NUL-terminated
+ * ASCII string, this function can be used to append a printf-formatted
+ * string to the end of it. The data_string structure will be resized to
+ * be big enough to hold the new string.
+ *
+ * If the append works, then 1 is returned.
+ *
+ * If it is not possible to allocate a buffer big enough to hold the
+ * new value, then the old data_string is unchanged, and 0 is returned.
+ */
+int
+data_string_sprintfa(struct data_string *ds, const char *fmt, ...) {
+ va_list args;
+ int cur_strlen;
+ int max;
+ int vsnprintf_ret;
+ int new_len;
+ struct buffer *tmp_buffer;
+
+ /*
+ * If the data_string is empty, then initialize it.
+ */
+ if (ds->data == NULL) {
+ /* INSIST(ds.buffer == NULL); */
+ if (!buffer_allocate(&ds->buffer, DS_SPRINTF_SIZE, MDL)) {
+ return 0;
+ }
+ ds->data = ds->buffer->data;
+ ds->len = DS_SPRINTF_SIZE;
+ *((char *)ds->data) = '\0';
+ }
+
+ /*
+ * Get the length of the string, and figure out how much space
+ * is left.
+ */
+ cur_strlen = strlen((char *)ds->data);
+ max = ds->len - cur_strlen;
+
+ /*
+ * Use vsnprintf(), which won't write past our space, but will
+ * tell us how much space it wants.
+ */
+ va_start(args, fmt);
+ vsnprintf_ret = vsnprintf((char *)ds->data+cur_strlen, max, fmt, args);
+ va_end(args);
+ /* INSIST(vsnprintf_ret >= 0); */
+
+ /*
+ * If our buffer is not big enough, we need a new buffer.
+ */
+ if (vsnprintf_ret >= max) {
+ /*
+ * Figure out a size big enough.
+ */
+ new_len = ds->len * 2;
+ while (new_len <= cur_strlen + vsnprintf_ret) {
+ new_len *= 2;
+ }
+
+ /*
+ * Create a new buffer and fill it.
+ */
+ tmp_buffer = NULL;
+ if (!buffer_allocate(&tmp_buffer, new_len, MDL)) {
+ /*
+ * If we can't create a big enough buffer,
+ * we should remove any truncated output that we had.
+ */
+ *((char *)ds->data+cur_strlen) = '\0';
+ va_end(args);
+ return 0;
+ }
+ memcpy(tmp_buffer->data, ds->data, cur_strlen);
+
+ /* Rerun the vsprintf. */
+ va_start(args, fmt);
+ vsprintf((char *)tmp_buffer->data + cur_strlen, fmt, args);
+ va_end(args);
+
+ /*
+ * Replace our old buffer with the new buffer.
+ */
+ buffer_dereference(&ds->buffer, MDL);
+ buffer_reference(&ds->buffer, tmp_buffer, MDL);
+ buffer_dereference(&tmp_buffer, MDL);
+ ds->data = ds->buffer->data;
+ ds->len = new_len;
+ }
+ return 1;
+}
+
+pair cons (car, cdr)
+ caddr_t car;
+ pair cdr;
+{
+ pair foo = (pair)dmalloc (sizeof *foo, MDL);
+ if (!foo)
+ log_fatal ("no memory for cons.");
+ foo -> car = car;
+ foo -> cdr = cdr;
+ return foo;
+}
+
+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 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;
+ }
+ }
+
+ 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;
+ }
+
+ (*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);
+ option_reference(&((*oc)->option), option, MDL);
+ return 1;
+}
+
+int make_host_lookup (expr, name)
+ struct expression **expr;
+ const char *name;
+{
+ 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;
+}
+
+int enter_dns_host (dh, name)
+ struct dns_host_entry **dh;
+ const char *name;
+{
+ /* XXX This should really keep a hash table of hostnames
+ XXX and just add a new reference to a hostname that
+ XXX already exists, if possible, rather than creating
+ XXX a new structure. */
+ if (!dns_host_entry_allocate (dh, name, MDL)) {
+ log_error ("Can't allocate space for new host.");
+ return 0;
+ }
+ return 1;
+}
+
+int make_const_data (struct expression **expr, const unsigned char *data,
+ unsigned len, int terminated, int allocate,
+ const char *file, int line)
+{
+ 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;
+}
+
+int make_const_int (expr, val)
+ struct expression **expr;
+ unsigned long val;
+{
+ 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) {
+ 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 (!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;
+}
+
+int make_encapsulation (expr, name)
+ struct expression **expr;
+ struct data_string *name;
+{
+ /* Allocate a new node to store the encapsulation. */
+ if (!expression_allocate (expr, MDL)) {
+ log_error ("No memory for encapsulation expression node.");
+ return 0;
+ }
+
+ (*expr) -> op = expr_encapsulate;
+ data_string_copy (&(*expr) -> data.encapsulate, name, MDL);
+ return 1;
+}
+
+int make_substring (new, expr, offset, length)
+ struct expression **new;
+ struct expression *expr;
+ struct expression *offset;
+ struct expression *length;
+{
+ /* 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;
+}
+
+int make_limit (new, expr, limit)
+ struct expression **new;
+ struct expression *expr;
+ int limit;
+{
+ /* 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);
+
+ /* 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;
+
+ /* 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;
+
+ return 1;
+}
+
+int option_cache (struct option_cache **oc, struct data_string *dp,
+ struct expression *expr, struct option *option,
+ const char *file, int line)
+{
+ 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);
+ option_reference(&(*oc)->option, option, MDL);
+ return 1;
+}
+
+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 int do_host_lookup (result, dns)
+ struct data_string *result;
+ struct dns_host_entry *dns;
+{
+ struct hostent *h;
+ unsigned i, count;
+ unsigned new_len;
+
+#ifdef DEBUG_EVAL
+ 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
+ log_debug ("easy 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;
+ }
+#ifdef DEBUG_EVAL
+ log_debug ("Looking up %s", dns -> hostname);
+#endif
+
+ /* Otherwise, look it up... */
+ h = gethostbyname (dns -> hostname);
+ if (!h) {
+#ifndef NO_H_ERRNO
+ switch (h_errno) {
+ case HOST_NOT_FOUND:
+#endif
+ log_error ("%s: host unknown.", dns -> hostname);
+#ifndef NO_H_ERRNO
+ break;
+ case TRY_AGAIN:
+ log_error ("%s: temporary name server failure",
+ dns -> hostname);
+ break;
+ case NO_RECOVERY:
+ log_error ("%s: name server failed", dns -> hostname);
+ break;
+ case NO_DATA:
+ log_error ("%s: no A record associated with address",
+ dns -> hostname);
+ }
+#endif /* !NO_H_ERRNO */
+
+ /* Okay to try again after a minute. */
+ dns -> timeout = cur_time + 60;
+ data_string_forget (&dns -> data, MDL);
+ return 0;
+ }
+
+#ifdef DEBUG_EVAL
+ log_debug ("Lookup succeeded; first address is %s",
+ inet_ntoa (h -> h_addr_list [0]));
+#endif
+
+ /* Count the number of addresses we got... */
+ 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 = 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 < count; i++) {
+ memcpy (&dns -> data.buffer -> data [h -> h_length * i],
+ h -> h_addr_list [i], (unsigned)(h -> h_length));
+ }
+#ifdef DEBUG_EVAL
+ log_debug ("dns -> data: %x h -> h_addr_list [0]: %x",
+ *(int *)(dns -> buffer), h -> h_addr_list [0]);
+#endif
+
+ /* 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
+ 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));
+#if defined (NSUPDATE_OLD)
+ } else if (is_dns_expression (expr)) {
+ 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_OLD)
+ 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;
+ }
+ free_binding_value(bv, file, line);
+ return 1;
+}
+
+#if defined (NSUPDATE_OLD)
+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;
+{
+ unsigned long ttl = 0;
+ char *tname;
+ struct data_string name, data;
+ int r0, r1, r2;
+
+ 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;
+ tname = NULL;
+ }
+ 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;
+ /*%Audit% 16 bytes max. %2004.06.17,Safe%*/
+ sprintf ((char *)(*result) -> r_data_ephem,
+ "%u.%u.%u.%u",
+ data.data [0] & 0xff,
+ data.data [1] & 0xff,
+ data.data [2] & 0xff,
+ data.data [3] & 0xff);
+ (*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 separate 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_regex_match:
+ case expr_iregex_match:
+ 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_lcase:
+ case expr_ucase:
+ 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:
+ case expr_gethostname:
+ 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_OLD) */
+
+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;
+ int bleft, bright;
+ int sleft, sright;
+ struct binding *binding;
+ struct binding_value *bv, *obv;
+#ifdef HAVE_REGEX_H
+ int regflags = REG_EXTENDED | REG_NOSUB;
+ regex_t re;
+#endif
+
+ 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;
+#if defined (NSUPDATE_OLD)
+ 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;
+#endif /* NSUPDATE_OLD */
+ 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_iregex_match:
+#ifdef HAVE_REGEX_H
+ regflags |= REG_ICASE;
+#endif
+ /* FALL THROUGH */
+ case expr_regex_match:
+#ifdef HAVE_REGEX_H
+ memset(&left, 0, sizeof left);
+ bleft = evaluate_data_expression(&left, packet, lease,
+ client_state,
+ in_options, cfg_options,
+ scope,
+ expr->data.equal[0], MDL);
+ memset(&right, 0, sizeof right);
+ bright = evaluate_data_expression(&right, packet, lease,
+ client_state,
+ in_options, cfg_options,
+ scope,
+ expr->data.equal[1], MDL);
+
+ *result = 0;
+ memset(&re, 0, sizeof(re));
+ if (bleft && bright &&
+ (regcomp(&re, (char *)right.data, regflags) == 0) &&
+ (regexec(&re, (char *)left.data, (size_t)0, NULL, 0) == 0))
+ *result = 1;
+
+#if defined (DEBUG_EXPRESSIONS)
+ log_debug("bool: %s ~= %s yields %s",
+ bleft ? print_hex_1(left.len, left.data, 20)
+ : "NULL",
+ bright ? print_hex_2 (right.len, right.data, 20)
+ : "NULL",
+ *result ? "true" : "false");
+#endif
+
+ if (bleft)
+ data_string_forget(&left, MDL);
+ if (bright)
+ data_string_forget(&right, MDL);
+
+ regfree(&re);
+
+ /*
+ * If we have bleft and bright then we have a good
+ * syntax, otherwise not.
+ *
+ * XXX: we don't warn on invalid regular expression
+ * syntax, should we?
+ */
+ return bleft && bright;
+#else
+ /* It shouldn't be possible to configure a regex operator
+ * when there's no support.
+ */
+ log_fatal("Impossible condition at %s:%d.", MDL);
+ break;
+#endif
+
+ 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_lcase:
+ case expr_ucase:
+ 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:
+ case expr_gethostname:
+ 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;
+ unsigned 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;
+
+ /* Convert string to lowercase. */
+ case expr_lcase:
+ memset(&data, 0, sizeof data);
+ s0 = evaluate_data_expression(&data, packet, lease,
+ client_state,
+ in_options, cfg_options, scope,
+ expr->data.lcase, MDL);
+ s1 = 0;
+ if (s0) {
+ result->len = data.len;
+ if (buffer_allocate(&result->buffer,
+ result->len + data.terminated,
+ MDL)) {
+ result->data = &result->buffer->data[0];
+ memcpy(result->buffer->data, data.data,
+ data.len + data.terminated);
+ result->terminated = data.terminated;
+ s = (unsigned char *)result->data;
+ for (i = 0; i < result->len; i++, s++)
+ *s = tolower(*s);
+ s1 = 1;
+ } else {
+ log_error("data: lcase: no buffer memory.");
+ }
+ }
+
+#if defined (DEBUG_EXPRESSIONS)
+ log_debug("data: lcase (%s) = %s",
+ s0 ? print_hex_1(data.len, data.data, 30) : "NULL",
+ s1 ? print_hex_2(result->len, result->data, 30)
+ : "NULL");
+#endif
+ if (s0)
+ data_string_forget(&data, MDL);
+ return s1;
+
+ /* Convert string to uppercase. */
+ case expr_ucase:
+ memset(&data, 0, sizeof data);
+ s0 = evaluate_data_expression(&data, packet, lease,
+ client_state,
+ in_options, cfg_options, scope,
+ expr->data.lcase, MDL);
+ s1 = 0;
+ if (s0) {
+ result->len = data.len;
+ if (buffer_allocate(&result->buffer,
+ result->len + data.terminated,
+ file, line)) {
+ result->data = &result->buffer->data[0];
+ memcpy(result->buffer->data, data.data,
+ data.len + data.terminated);
+ result->terminated = data.terminated;
+ s = (unsigned char *)result->data;
+ for (i = 0; i < result->len; i++, s++)
+ *s = toupper(*s);
+ s1 = 1;
+ } else {
+ log_error("data: lcase: no buffer memory.");
+ }
+ }
+
+#if defined (DEBUG_EXPRESSIONS)
+ log_debug("data: ucase (%s) = %s",
+ s0 ? print_hex_1(data.len, data.data, 30) : "NULL",
+ s1 ? print_hex_2(result->len, result->data, 30)
+ : "NULL");
+#endif
+ if (s0)
+ data_string_forget(&data, MDL);
+ return 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 separator string. */
+ memset (&data, 0, sizeof data);
+ s2 = evaluate_data_expression (&data, packet, lease,
+ client_state,
+ in_options, cfg_options, scope,
+ expr -> data.b2a.separator,
+ 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);
+ status = 0;
+ 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) {
+ 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_debug("data: \"leased-address\" configuration "
+ "directive: there is no lease associated "
+ "with this client.");
+ 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;
+
+ /* Provide the system's local hostname as a return value. */
+ case expr_gethostname:
+ /*
+ * Allocate a buffer to return.
+ *
+ * The largest valid hostname is maybe 64 octets at a single
+ * label, or 255 octets if you think a hostname is allowed
+ * to contain labels (plus termination).
+ */
+ memset(result, 0, sizeof(*result));
+ if (!buffer_allocate(&result->buffer, 255, file, line)) {
+ log_error("data: gethostname(): no memory for buffer");
+ return 0;
+ }
+ result->data = result->buffer->data;
+
+ /*
+ * On successful completion, gethostname() resturns 0. It may
+ * not null-terminate the string if there was insufficient
+ * space.
+ */
+ if (!gethostname((char *)result->buffer->data, 255)) {
+ if (result->buffer->data[255] == '\0')
+ result->len =
+ strlen((char *)result->buffer->data);
+ else
+ result->len = 255;
+ return 1;
+ }
+
+ data_string_forget(result, MDL);
+ return 0;
+
+ case expr_check:
+ case expr_equal:
+ case expr_not_equal:
+ case expr_regex_match:
+ case expr_iregex_match:
+ 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_OLD)
+ ns_updrec *nut;
+ ns_updque uq;
+ struct expression *cur, *next;
+#endif
+
+ 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_regex_match:
+ case expr_iregex_match:
+ 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_lcase:
+ case expr_ucase:
+ 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:
+ case expr_gethostname:
+ 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_OLD)
+ 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_OLD */
+
+ 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
+ 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;
+
+ default:
+ log_fatal("Impossible case at %s:%d. Undefined operator "
+ "%d.", MDL, expr->op);
+ break;
+ }
+
+ log_error ("evaluate_numeric_expression: bogus opcode %d", expr -> op);
+ return 0;
+}
+
+/* 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;
+{
+ if (oc->data.data != NULL) {
+ 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. */
+
+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;
+
+ /* 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;
+
+ /* The boolean option cache is actually a trinary value. Zero is
+ * off, one is on, and 2 is 'ignore'.
+ */
+ if (ds.len) {
+ result = ds.data [0];
+ if (result == 2) {
+ result = 0;
+ if (ignorep != NULL)
+ *ignorep = 1;
+ } else if (ignorep != NULL)
+ *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_regex_match:
+ case expr_iregex_match:
+ 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_lcase:
+ if (expr->data.lcase)
+ expression_dereference(&expr->data.lcase, MDL);
+ break;
+
+ case expr_ucase:
+ if (expr->data.ucase)
+ expression_dereference(&expr->data.ucase, MDL);
+ 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.separator)
+ expression_dereference (&expr -> data.b2a.separator,
+ 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:
+ case expr_gethostname:
+ 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_regex_match ||
+ expr->op == expr_iregex_match ||
+ 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_lcase ||
+ expr->op == expr_ucase ||
+ 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 ||
+ expr->op == expr_gethostname);
+}
+
+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 (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_lcase:
+ case expr_ucase:
+ 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:
+ case expr_gethostname:
+ return 100;
+
+ case expr_equal:
+ case expr_not_equal:
+ case expr_regex_match:
+ case expr_iregex_match:
+ return 4;
+
+ case expr_or:
+ case expr_and:
+ return 3;
+
+ case expr_add:
+ case expr_subtract:
+ return 2;
+
+ case expr_multiply:
+ case expr_divide:
+ case expr_remainder:
+ return 1;
+ }
+ return 100;
+}
+
+int op_precedence (op1, op2)
+ enum expr_op op1, op2;
+{
+ 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_lcase:
+ case expr_ucase:
+ 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:
+ case expr_gethostname:
+ return context_any;
+
+ case expr_equal:
+ case expr_not_equal:
+ case expr_regex_match:
+ case expr_iregex_match:
+ 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_regex_match:
+ s = "~=";
+ goto binary;
+
+ case expr_iregex_match:
+ s = "~~";
+ goto binary;
+
+ 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, "", "", ")");
+
+ case expr_lcase:
+ col = token_print_indent(file, col, indent, "", "", "lcase");
+ col = token_print_indent(file, col, indent, " ", "", "(");
+ scol = col;
+ col = write_expression(file, expr->data.lcase, col, scol, 1);
+ col = token_print_indent(file, col, indent, "", "", ")");
+ break;
+
+ case expr_ucase:
+ col = token_print_indent(file, col, indent, "", "", "ucase");
+ col = token_print_indent(file, col, indent, " ", "", "(");
+ scol = col;
+ col = write_expression(file, expr->data.ucase, col, scol, 1);
+ 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.separator,
+ 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;
+
+ case expr_gethostname:
+ col = token_print_indent(file, col, indent, "", "",
+ "gethostname()");
+ break;
+
+ case expr_funcall:
+ col = token_print_indent(file, indent, indent, "", "",
+ expr->data.funcall.name);
+ col = token_print_indent(file, col, indent, " ", "", "(");
+
+ firstp = 1;
+ e = expr->data.funcall.arglist;
+ while (e != NULL) {
+ if (!firstp)
+ col = token_print_indent(file, col, indent,
+ "", " ", ",");
+
+ col = write_expression(file, e->data.arg.val, col,
+ indent, firstp);
+ firstp = 0;
+ e = e->data.arg.next;
+ }
+
+ 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;
+{
+ 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_lcase:
+ return data_subexpression_length(rv, expr->data.lcase);
+
+ case expr_ucase:
+ return data_subexpression_length(rv, expr->data.ucase);
+
+ 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_regex_match:
+ case expr_iregex_match:
+ 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:
+ case expr_gethostname:
+ 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: */