2
0
mirror of https://gitlab.isc.org/isc-projects/dhcp synced 2025-08-22 09:57:20 +00:00
isc-dhcp/common/tree.c
Shawn Routhier 0f750c4fb1 [master]
[rt23833]
Clean up a number of items identified by the Coverity
static analysis tool.  Runs courtesy of Red Hat.
2012-10-16 15:05:24 -07:00

4474 lines
117 KiB
C

/* tree.c
Routines for manipulating parse trees... */
/*
* Copyright (c) 2011-2012 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 &&
(left.data != NULL) && (right.data != NULL) &&
(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;
int rc = 0;
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);
rc = 1;
}
#if defined (DEBUG_EXPRESSIONS)
if (rc == 1) {
log_debug ("num: extract_int16 (%s) = %ld",
print_hex_1(data.len, data.data, 60)
*result);
} else {
log_debug ("num: extract_int16 (NULL) = NULL");
}
#endif
if (status) data_string_forget (&data, MDL);
return (rc);
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);
rc = 1;
}
#if defined (DEBUG_EXPRESSIONS)
if (rc == 1) {
log_debug ("num: extract_int32 (%s) = %ld",
print_hex_1 (data.len, data.data, 60),
*result);
} else {
log_debug ("num: extract_int32 (NULL) = NULL");
}
#endif
if (status) data_string_forget (&data, MDL);
return (rc);
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.
*
* returns 0 if there wasn't an expression or it couldn't be evaluated
* returns non-zero if there was an expression or string that was evaluated
* When it returns zero the arguements, in particualr resutl, should not
* be modified
*/
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, "", "", ")");
break;
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;
struct string_list *sp, *next;
if ((ptr == NULL) || (*ptr == NULL)) {
log_error ("%s(%d): null pointer", file, line);
#if defined (POINTER_DEBUG)
abort ();
#else
return 0;
#endif
}
bp = *ptr;
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 {
if (buffer_allocate (&value->buffer,
binding->value->value.data.len,
MDL) == 0) {
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: */