2
0
mirror of https://gitlab.isc.org/isc-projects/dhcp synced 2025-08-22 18:07:25 +00:00
isc-dhcp/common/tree.c

2377 lines
65 KiB
C
Raw Normal View History

1995-11-29 07:40:04 +00:00
/* tree.c
Routines for manipulating parse trees... */
/*
1999-03-16 05:50:46 +00:00
* Copyright (c) 1996-1999 Internet Software Consortium.
* Use is subject to license terms which appear in the file named
* ISC-LICENSE that should have accompanied this file when you
* received it. If a file named ISC-LICENSE did not accompany this
* file, or you are not sure the one you have is correct, you may
* obtain an applicable copy of the license at:
1995-11-29 07:40:04 +00:00
*
1999-03-16 05:50:46 +00:00
* http://www.isc.org/isc-license-1.0.html.
1995-11-29 07:40:04 +00:00
*
1999-03-16 05:50:46 +00:00
* This file is part of the ISC DHCP distribution. The documentation
* associated with this file is listed in the file DOCUMENTATION,
* included in the top-level directory of this release.
1995-11-29 07:40:04 +00:00
*
1999-03-16 05:50:46 +00:00
* Support and other services are available for ISC products - see
* http://www.isc.org for more information.
1995-11-29 07:40:04 +00:00
*/
#ifndef lint
static char copyright[] =
"$Id: tree.c,v 1.61 1999/10/14 18:00:46 mellon Exp $ Copyright (c) 1995, 1996, 1997, 1998 The Internet Software Consortium. All rights reserved.\n";
1995-11-29 07:40:04 +00:00
#endif /* not lint */
#include "dhcpd.h"
static int do_host_lookup PROTO ((struct data_string *,
struct dns_host_entry *));
1995-11-29 07:40:04 +00:00
pair cons (car, cdr)
caddr_t car;
pair cdr;
{
pair foo = (pair)dmalloc (sizeof *foo, "cons");
if (!foo)
log_fatal ("no memory for cons.");
1995-11-29 07:40:04 +00:00
foo -> car = car;
foo -> cdr = cdr;
return foo;
}
int make_const_option_cache (oc, buffer, data, len, option, name)
struct option_cache **oc;
struct buffer **buffer;
u_int8_t *data;
unsigned len;
struct option *option;
const char *name;
1995-11-29 07:40:04 +00:00
{
struct buffer *bp;
if (buffer) {
bp = *buffer;
*buffer = 0;
} else {
bp = (struct buffer *)0;
if (!buffer_allocate (&bp, len, name)) {
log_error ("%s: can't allocate buffer.", name);
return 0;
}
}
if (!option_cache_allocate (oc, name)) {
log_error ("%s: can't allocate option cache.", name);
buffer_dereference (&bp, name);
return 0;
}
(*oc) -> data.len = len;
(*oc) -> data.data = &bp -> data [0];
(*oc) -> data.terminated = 0;
if (data)
memcpy (&bp -> data [0], data, len);
(*oc) -> option = option;
return 1;
1995-11-29 07:40:04 +00:00
}
int make_host_lookup (expr, name)
struct expression **expr;
const char *name;
1995-11-29 07:40:04 +00:00
{
if (!expression_allocate (expr, "make_host_lookup")) {
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, "make_host_lookup");
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, "enter_dns_host")) {
log_error ("Can't allocate space for new host.");
return 0;
}
return 1;
1995-11-29 07:40:04 +00:00
}
int make_const_data (expr, data, len, terminated, allocate)
struct expression **expr;
const unsigned char *data;
unsigned len;
int terminated;
int allocate;
1995-11-29 07:40:04 +00:00
{
struct expression *nt;
if (!expression_allocate (expr, "make_host_lookup")) {
log_error ("No memory for make_const_data tree node.");
return 0;
}
nt = *expr;
1998-04-09 04:31:59 +00:00
if (len) {
if (allocate) {
if (!buffer_allocate (&nt -> data.const_data.buffer,
len + terminated,
"make_const_data")) {
log_error ("Can't allocate const_data buffer");
expression_dereference (expr,
"make_const_data");
return 0;
}
nt -> data.const_data.data =
&nt -> data.const_data.buffer -> data [0];
memcpy (nt -> data.const_data.buffer -> data,
data, len + terminated);
} else
nt -> data.const_data.data = data;
nt -> data.const_data.terminated = terminated;
1998-04-09 04:31:59 +00:00
} else
nt -> data.const_data.data = 0;
1998-04-09 04:31:59 +00:00
nt -> op = expr_const_data;
nt -> data.const_data.len = len;
return 1;
1995-11-29 07:40:04 +00:00
}
int make_concat (expr, left, right)
struct expression **expr;
struct expression *left, *right;
1995-11-29 07:40:04 +00:00
{
/* 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, "make_concat");
return 1;
}
if (!right) {
expression_reference (expr, left, "make_concat");
return 1;
1995-11-29 07:40:04 +00:00
}
/* Otherwise, allocate a new node to concatenate the two. */
if (!expression_allocate (expr, "make_concat")) {
log_error ("No memory for concatenation expression node.");
return 0;
}
(*expr) -> op = expr_concat;
expression_reference (&(*expr) -> data.concat [0],
left, "make_concat");
expression_reference (&(*expr) -> data.concat [1],
right, "make_concat");
return 1;
1995-11-29 07:40:04 +00:00
}
int make_encapsulation (expr, name)
struct expression **expr;
struct data_string *name;
{
/* Allocate a new node to store the encapsulation. */
if (!expression_allocate (expr, "make_encapsulation")) {
log_error ("No memory for encapsulation expression node.");
return 0;
}
(*expr) -> op = expr_encapsulate;
data_string_copy (&(*expr) -> data.encapsulate,
name, "make_concat");
return 1;
}
int make_substring (new, expr, offset, length)
struct expression **new;
struct expression *expr;
struct expression *offset;
struct expression *length;
1995-11-29 07:40:04 +00:00
{
/* Allocate an expression node to compute the substring. */
if (!expression_allocate (new, "make_substring")) {
log_error ("no memory for substring expression.");
return 0;
1995-11-29 07:40:04 +00:00
}
(*new) -> op = expr_substring;
expression_reference (&(*new) -> data.substring.expr,
expr, "make_concat");
expression_reference (&(*new) -> data.substring.offset,
offset, "make_concat");
expression_reference (&(*new) -> data.substring.len,
length, "make_concat");
return 1;
1995-11-29 07:40:04 +00:00
}
int make_limit (new, expr, limit)
struct expression **new;
struct expression *expr;
int limit;
1995-11-29 07:40:04 +00:00
{
struct expression *rv;
/* Allocate a node to enforce a limit on evaluation. */
if (!expression_allocate (new, "make_limit"))
log_error ("no memory for limit expression");
(*new) -> op = expr_substring;
expression_reference (&(*new) -> data.substring.expr,
expr, "make_limit");
/* Offset is a constant 0. */
if (!expression_allocate (&(*new) -> data.substring.offset,
"make_limit")) {
log_error ("no memory for limit offset expression");
expression_dereference (new, "make_limit");
return 0;
}
(*new) -> data.substring.offset -> op = expr_const_int;
(*new) -> data.substring.offset -> data.const_int = 0;
/* Length is a constant: the specified limit. */
if (!expression_allocate (&(*new) -> data.substring.len,
"make_limit")) {
log_error ("no memory for limit length expression");
expression_dereference (new, "make_limit");
return 0;
}
(*new) -> data.substring.len -> op = expr_const_int;
(*new) -> data.substring.len -> data.const_int = limit;
return 1;
1995-11-29 07:40:04 +00:00
}
int option_cache (oc, dp, expr, option)
struct option_cache **oc;
struct data_string *dp;
struct expression *expr;
struct option *option;
1995-11-29 07:40:04 +00:00
{
if (!option_cache_allocate (oc, "option_cache"))
return 0;
if (dp)
data_string_copy (&(*oc) -> data, dp, "option_cache");
if (expr)
expression_reference (&(*oc) -> expression,
expr, "option_cache");
(*oc) -> option = option;
return 1;
1995-11-29 07:40:04 +00:00
}
static int do_host_lookup (result, dns)
struct data_string *result;
1995-11-29 07:40:04 +00:00
struct dns_host_entry *dns;
{
struct hostent *h;
unsigned i, count;
unsigned new_len;
#ifdef DEBUG_EVAL
log_debug ("time: now = %d dns = %d %d diff = %d",
1995-11-29 07:40:04 +00:00
cur_time, dns -> timeout, cur_time - dns -> timeout);
#endif
1995-11-29 07:40:04 +00:00
/* 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, "do_host_lookup");
return 1;
1995-11-29 07:40:04 +00:00
}
#ifdef DEBUG_EVAL
log_debug ("Looking up %s", dns -> hostname);
#endif
1995-11-29 07:40:04 +00:00
/* Otherwise, look it up... */
h = gethostbyname (dns -> hostname);
if (!h) {
#ifndef NO_H_ERRNO
1995-11-29 07:40:04 +00:00
switch (h_errno) {
case HOST_NOT_FOUND:
#endif
log_error ("%s: host unknown.", dns -> hostname);
#ifndef NO_H_ERRNO
1995-11-29 07:40:04 +00:00
break;
case TRY_AGAIN:
log_error ("%s: temporary name server failure",
dns -> hostname);
1995-11-29 07:40:04 +00:00
break;
case NO_RECOVERY:
log_error ("%s: name server failed", dns -> hostname);
1995-11-29 07:40:04 +00:00
break;
case NO_DATA:
log_error ("%s: no A record associated with address",
dns -> hostname);
1995-11-29 07:40:04 +00:00
}
#endif /* !NO_H_ERRNO */
1995-11-29 07:40:04 +00:00
/* Okay to try again after a minute. */
dns -> timeout = cur_time + 60;
data_string_forget (&dns -> data, "do_host_lookup");
return 0;
1995-11-29 07:40:04 +00:00
}
#ifdef DEBUG_EVAL
log_debug ("Lookup succeeded; first address is %s",
inet_ntoa (h -> h_addr_list [0]));
#endif
1995-11-29 07:40:04 +00:00
/* Count the number of addresses we got... */
for (count = 0; h -> h_addr_list [count]; count++)
1995-11-29 07:40:04 +00:00
;
/* Dereference the old data, if any. */
data_string_forget (&dns -> data, "do_host_lookup");
1995-11-29 07:40:04 +00:00
/* Do we need to allocate more memory? */
new_len = count * h -> h_length;
if (!buffer_allocate (&dns -> data.buffer, new_len, "do_host_lookup"))
{
log_error ("No memory for %s.", dns -> hostname);
return 0;
1995-11-29 07:40:04 +00:00
}
dns -> data.data = &dns -> data.buffer -> data [0];
dns -> data.len = new_len;
dns -> data.terminated = 0;
1995-11-29 07:40:04 +00:00
/* 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));
1996-02-11 00:01:46 +00:00
}
#ifdef DEBUG_EVAL
log_debug ("dns -> data: %x h -> h_addr_list [0]: %x",
*(int *)(dns -> buffer), h -> h_addr_list [0]);
#endif
1995-11-29 07:40:04 +00:00
/* XXX Set the timeout for an hour from now.
1995-11-29 07:40:04 +00:00
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, "do_host_lookup");
return 1;
1995-11-29 07:40:04 +00:00
}
int evaluate_boolean_expression (result, packet, lease, in_options,
cfg_options, expr)
int *result;
struct packet *packet;
struct lease *lease;
struct option_state *in_options;
struct option_state *cfg_options;
struct expression *expr;
1995-11-29 07:40:04 +00:00
{
struct data_string left, right;
struct data_string rrtype, expr1, expr2;
unsigned long ttl;
int s0, s1, s2, s3;
int bleft, bright;
int sleft, sright;
switch (expr -> op) {
case expr_check:
*result = check_collection (packet, lease,
expr -> data.check);
#if defined (DEBUG_EXPRESSIONS)
1999-07-19 13:08:29 +00:00
log_debug ("bool: check (%s) returns %s",
expr -> data.check -> name, *result ? "true" : "false");
#endif
return 1;
case expr_equal:
memset (&left, 0, sizeof left);
sleft = evaluate_data_expression (&left, packet, lease,
in_options, cfg_options,
expr -> data.equal [0]);
memset (&right, 0, sizeof right);
sright = evaluate_data_expression (&right, packet, lease,
in_options, cfg_options,
expr -> data.equal [1]);
if (sleft && sright) {
if (left.len == right.len &&
!memcmp (left.data, right.data, left.len))
*result = 1;
else
*result = 0;
}
#if defined (DEBUG_EXPRESSIONS)
1999-07-19 13:08:29 +00:00
log_debug ("bool: equal (%s, %s) = %s",
sleft ? print_hex_1 (left.len, left.data, 30) : "NULL",
sright ? print_hex_2 (right.len,
right.data, 30) : "NULL",
((sleft && sright)
? (*result ? "true" : "false")
: "NULL"));
#endif
if (sleft)
data_string_forget (&left,
"evaluate_boolean_expression");
if (sright)
data_string_forget (&right,
"evaluate_boolean_expression");
return sleft && sright;
case expr_and:
sleft = evaluate_boolean_expression (&bleft, packet, lease,
in_options, cfg_options,
expr -> data.and [0]);
sright = evaluate_boolean_expression (&bright, packet, lease,
in_options, cfg_options,
expr -> data.and [1]);
#if defined (DEBUG_EXPRESSIONS)
1999-07-19 13:08:29 +00:00
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:
sleft = evaluate_boolean_expression (&bleft, packet, lease,
in_options, cfg_options,
expr -> data.or [0]);
sright = evaluate_boolean_expression (&bright, packet, lease,
in_options, cfg_options,
expr -> data.or [1]);
#if defined (DEBUG_EXPRESSIONS)
1999-07-19 13:08:29 +00:00
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,
in_options, cfg_options,
expr -> data.not);
#if defined (DEBUG_EXPRESSIONS)
1999-07-19 13:08:29 +00:00
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 ||
!((*expr -> data.option -> universe -> get_func)
(&left, expr -> data.exists -> universe,
packet, lease, in_options, cfg_options, in_options,
expr -> data.exists -> code)))
*result = 0;
else {
*result = 1;
data_string_forget (&left,
"evaluate_boolean_expression");
}
#if defined (DEBUG_EXPRESSIONS)
1999-07-19 13:08:29 +00:00
log_debug ("bool: exists %s.%s = %s",
expr -> data.option -> universe -> name,
expr -> data.option -> name, *result ? "true" : "false");
#endif
return 1;
1999-04-12 22:11:12 +00:00
case expr_known:
if (!packet) {
#if defined (DEBUG_EXPRESSIONS)
1999-07-19 13:08:29 +00:00
log_debug ("bool: known = NULL");
1999-04-12 22:11:12 +00:00
#endif
return 0;
}
#if defined (DEBUG_EXPRESSIONS)
1999-07-19 13:08:29 +00:00
log_debug ("bool: known = %s",
1999-04-12 22:11:12 +00:00
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");
#endif
*result = 0;
return 1;
}
#if defined (DEBUG_EXPRESSIONS)
log_debug ("bool: static = true");
#endif
*result = 1;
return 1;
case expr_dns_update:
#if !defined (NSUPDATE)
return 0;
#else
/* we only want to do this on a DHCPREQUEST */
if (!packet || packet -> packet_type != DHCPREQUEST)
return 0;
memset (&rrtype, 0, sizeof rrtype);
s0 = evaluate_data_expression (&rrtype, packet, lease,
in_options, cfg_options,
expr -> data.dns_update.type);
memset (&expr1, 0, sizeof expr1);
s1 = evaluate_data_expression (&expr1, packet, lease,
in_options, cfg_options,
expr -> data.dns_update.expr1);
memset (&expr2, 0, sizeof expr2);
s2 = evaluate_data_expression (&expr2, packet, lease,
in_options, cfg_options,
expr -> data.dns_update.expr2);
s3 = evaluate_numeric_expression (&ttl, packet, lease,
in_options, cfg_options,
expr -> data.dns_update.ttl);
*result = 0; /* assume failure */
if (s0 && s1 && s2 && s3) {
if (rrtype.len == 1 &&
strncmp((const char *)rrtype.data, "a", 1) == 0) {
1999-09-09 23:53:29 +00:00
#if defined (DEBUG_EXPRESSIONS)
log_debug("calling updateA(%s, %s, %ld, lease)",
expr1.data, expr2.data, ttl);
1999-09-09 23:53:29 +00:00
#endif
updateA(&expr1, &expr2, ttl, lease);
} else if (rrtype.len == 3 &&
strncmp((const char *)rrtype.data,
"ptr", 3) == 0) {
1999-09-09 23:53:29 +00:00
#if defined (DEBUG_EXPRESSIONS)
log_debug ("%s updatePTR(%s, %s, %ld, lease)",
"calling", expr1.data, expr2.data, ttl);
1999-09-09 23:53:29 +00:00
#endif
updatePTR(&expr1, &expr2, ttl, lease);
}
*result = 1;
} else {
log_error("dns-update: one or more subexpressions %s",
"evaluate to NULL.");
return 0;
}
#if defined (DEBUG_EXPRESSIONS)
1999-09-09 23:53:29 +00:00
log_debug ("dns-update (%s, %s, %s):",
print_hex_1(rrtype.len, rrtype.data, 60),
print_hex_2(expr1.len, expr1.data, 60),
print_hex_3(expr2.len, expr2.data, 60));
#endif
return 1;
#endif /* NSUPDATE */
case expr_dns_delete:
#if !defined (NSUPDATE)
return 0;
#else
memset (&rrtype, 0, sizeof rrtype);
s0 = evaluate_data_expression (&rrtype, packet, lease,
in_options, cfg_options,
expr -> data.dns_update.type);
memset (&expr1, 0, sizeof expr1);
s1 = evaluate_data_expression (&expr1, packet, lease,
in_options, cfg_options,
expr -> data.dns_update.expr1);
memset (&expr2, 0, sizeof expr2);
s2 = evaluate_data_expression (&expr2, packet, lease,
in_options, cfg_options,
expr -> data.dns_update.expr2);
*result = 0; /* assume failure */
if (s0 && s1 && s2) {
if (rrtype.len == 1 &&
strncmp((const char *)rrtype.data, "a", 1) == 0) {
#if defined (DEBUG_EXPRESSIONS)
log_debug("calling deleteA(%s, %s, lease)",
expr1.data , expr2.data);
#endif
deleteA(&expr1, &expr2, lease);
} else if (rrtype.len == 3 &&
strncmp((const char *)rrtype.data,
"ptr", 3) == 0) {
#if defined (DEBUG_EXPRESSIONS)
log_debug ("%s deletePTR(%s, %s, lease)",
"calling", expr1.data,
expr2.data);
#endif
deletePTR(&expr1, &expr2, lease);
}
*result = 1;
} else {
log_error("dns-update: one or more subexpressions %s",
"evaluate to NULL.");
return 0;
}
#if defined (DEBUG_EXPRESSIONS)
log_debug ("dns-delete (%s, %s, %s):",
print_hex_1(rrtype.len, rrtype.data, 60),
print_hex_2(expr1.len, expr1.data, 60),
print_hex_3(expr2.len, expr2.data, 60));
#endif
return 1;
#endif /* NSUPDATE */
case expr_none:
case expr_match:
case expr_substring:
case expr_suffix:
case expr_option:
case expr_hardware:
case expr_const_data:
case expr_packet:
case expr_concat:
case expr_encapsulate:
case expr_host_lookup:
1999-05-27 14:30:00 +00:00
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_updated_dns_rr:
log_error ("Data opcode in evaluate_boolean_expression: %d",
expr -> op);
return 0;
1995-11-29 07:40:04 +00:00
case expr_extract_int8:
case expr_extract_int16:
case expr_extract_int32:
case expr_const_int:
case expr_lease_time:
log_error ("Numeric opcode in evaluate_boolean_expression: %d",
expr -> op);
return 0;
}
1995-11-29 07:40:04 +00:00
1999-04-12 22:11:12 +00:00
log_error ("Bogus opcode in evaluate_boolean_expression: %d",
expr -> op);
return 0;
1995-11-29 07:40:04 +00:00
}
int evaluate_data_expression (result, packet, lease,
in_options, cfg_options, expr)
struct data_string *result;
struct packet *packet;
struct lease *lease;
struct option_state *in_options;
struct option_state *cfg_options;
struct expression *expr;
{
struct data_string data, other;
unsigned long offset, len, i;
int s0, s1, s2, s3;
int status;
char *s;
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,
in_options, cfg_options,
expr -> data.substring.expr);
/* Evaluate the offset and length. */
s1 = evaluate_numeric_expression
(&offset, packet, lease, in_options, cfg_options,
expr -> data.substring.offset);
s2 = evaluate_numeric_expression (&len, packet, lease,
in_options, cfg_options,
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,
"evaluate_data_expression");
result -> len -= offset;
if (result -> len > len) {
result -> len = len;
result -> terminated = 0;
}
result -> data += offset;
}
s3 = 1;
} else
s3 = 0;
#if defined (DEBUG_EXPRESSIONS)
1999-07-19 13:08:29 +00:00
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 (s3)
return 1;
data_string_forget (&data, "evaluate_data_expression");
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,
in_options, cfg_options,
expr -> data.suffix.expr);
/* Evaluate the length. */
s1 = evaluate_numeric_expression (&len, packet, lease,
in_options, cfg_options,
expr -> data.substring.len);
if (s0 && s1) {
data_string_copy (result, &data,
"evaluate_data_expression");
/* If we are returning the last N bytes of a
string whose length is <= N, just return
the string - otherwise, compute a new
starting address and decrease the
length. */
if (data.len > len) {
result -> data += data.len - len;
result -> len = len;
}
}
#if 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
1999-05-27 14:30:00 +00:00
return s0 && s1;
/* Extract an option. */
case expr_option:
if (in_options)
s0 = ((*expr -> data.option -> universe -> get_func)
(result, expr -> data.option -> universe,
packet, lease,
in_options, cfg_options, in_options,
expr -> data.option -> code));
else
s0 = 0;
1999-04-23 22:22:55 +00:00
#if defined (DEBUG_EXPRESSIONS)
1999-07-19 13:08:29 +00:00
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 = ((*expr -> data.option -> universe -> get_func)
(result, expr -> data.option -> universe,
packet, lease,
in_options, cfg_options, cfg_options,
expr -> data.option -> code));
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:
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,
"evaluate_data_expression")) {
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)
1999-07-19 13:08:29 +00:00
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 (&len, packet, lease,
in_options, cfg_options,
expr -> data.packet.len);
s1 = evaluate_numeric_expression (&offset,
packet, lease,
in_options, cfg_options,
expr -> data.packet.len);
if (s0 && s1 && offset < packet -> packet_length) {
if (offset + len > packet -> packet_length)
result -> len =
packet -> packet_length - offset;
else
result -> len = len;
if (buffer_allocate (&result -> buffer, result -> len,
"evaluate_data_expression")) {
result -> data = &result -> buffer -> data [0];
memcpy (result -> 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,
in_options, cfg_options,
&expr -> data.encapsulate);
else
s0 = 0;
#if defined (DEBUG_EXPRESSIONS)
1999-07-19 13:08:29 +00:00
log_debug ("data: encapsulate (%s) = %s",
1999-04-05 19:06:50 +00:00
expr -> data.encapsulate.data,
s0 ? print_hex_1 (result -> len,
result -> data, 60) : "null");
#endif
1999-04-05 19:06:50 +00:00
return s0;
/* Some constant data... */
case expr_const_data:
#if defined (DEBUG_EXPRESSIONS)
1999-07-19 13:08:29 +00:00
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,
"evaluate_data_expression");
return 1;
/* Hostname lookup... */
case expr_host_lookup:
s0 = do_host_lookup (result, expr -> data.host_lookup);
#if defined (DEBUG_EXPRESSIONS)
1999-07-19 13:08:29 +00:00
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,
in_options, cfg_options,
expr -> data.concat [0]);
memset (&other, 0, sizeof other);
s1 = evaluate_data_expression (&other, packet, lease,
in_options, cfg_options,
expr -> data.concat [1]);
if (s0 && s1) {
result -> len = data.len + other.len;
if (!buffer_allocate (&result -> buffer,
(result -> len +
other.terminated),
"expr_concat")) {
log_error ("data: concat: no memory");
result -> len = 0;
data_string_forget (&data, "expr_concat");
data_string_forget (&other, "expr_concat");
return 0;
}
result -> data = &result -> buffer -> data [0];
memcpy (result -> buffer -> data, data.data, data.len);
memcpy (&result -> buffer -> data [data.len],
other.data, other.len + other.terminated);
} else if (s0)
data_string_forget (&data, "expr_concat");
else if (s1)
data_string_forget (&other, "expr_concat");
#if defined (DEBUG_EXPRESSIONS)
1999-07-19 13:08:29 +00:00
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;
1999-05-27 14:30:00 +00:00
case expr_encode_int8:
s0 = evaluate_numeric_expression (&len, packet, lease,
in_options, cfg_options,
1999-05-27 14:30:00 +00:00
expr -> data.packet.len);
if (s0) {
result -> len = 1;
if (!buffer_allocate (&result -> buffer, 1,
"expr_encode_int8")) {
log_error ("data: encode_int8: no memory");
result -> len = 0;
s0 = 0;
} else {
result -> data = &result -> buffer -> data [0];
result -> buffer -> data [0] = len;
1999-05-27 14:30:00 +00:00
}
} else
result -> len = 0;
#if defined (DEBUG_EXPRESSIONS)
if (!s0)
1999-07-19 13:08:29 +00:00
log_debug ("data: encode_int8 (NULL) = NULL");
1999-05-27 14:30:00 +00:00
else
log_debug ("data: encode_int8 (%ld) = %s", len,
1999-05-27 14:30:00 +00:00
print_hex_2 (result -> len,
result -> data, 20));
#endif
return s0;
case expr_encode_int16:
s0 = evaluate_numeric_expression (&len, packet, lease,
in_options, cfg_options,
1999-05-27 14:30:00 +00:00
expr -> data.packet.len);
if (s0) {
result -> len = 2;
if (!buffer_allocate (&result -> buffer, 2,
"expr_encode_int16")) {
log_error ("data: encode_int16: no memory");
result -> len = 0;
s0 = 0;
} else {
result -> data = &result -> buffer -> data [0];
putUShort (result -> buffer -> data, len);
1999-05-27 14:30:00 +00:00
}
} else
result -> len = 0;
#if defined (DEBUG_EXPRESSIONS)
if (!s0)
1999-07-19 13:08:29 +00:00
log_debug ("data: encode_int16 (NULL) = NULL");
1999-05-27 14:30:00 +00:00
else
log_debug ("data: encode_int16 (%ld) = %s", len,
1999-05-27 14:30:00 +00:00
print_hex_2 (result -> len,
result -> data, 20));
#endif
return s0;
case expr_encode_int32:
s0 = evaluate_numeric_expression (&len, packet, lease,
in_options, cfg_options,
1999-05-27 14:30:00 +00:00
expr -> data.packet.len);
if (s0) {
result -> len = 4;
if (!buffer_allocate (&result -> buffer, 4,
"expr_encode_int32")) {
log_error ("data: encode_int32: no memory");
result -> len = 0;
s0 = 0;
} else {
result -> data = &result -> buffer -> data [0];
putULong (result -> buffer -> data, len);
1999-05-27 14:30:00 +00:00
}
} else
result -> len = 0;
#if defined (DEBUG_EXPRESSIONS)
if (!s0)
1999-07-19 13:08:29 +00:00
log_debug ("data: encode_int32 (NULL) = NULL");
1999-05-27 14:30:00 +00:00
else
log_debug ("data: encode_int32 (%ld) = %s", len,
1999-05-27 14:30:00 +00:00
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, in_options, cfg_options,
expr -> data.b2a.base);
s1 = evaluate_numeric_expression (&len, packet, lease,
in_options, cfg_options,
expr -> data.b2a.width);
/* Evaluate the seperator string. */
memset (&data, 0, sizeof data);
s2 = evaluate_data_expression (&data, packet, lease,
in_options, cfg_options,
expr -> data.b2a.seperator);
/* Evaluate the data to be converted. */
memset (&other, 0, sizeof other);
s3 = evaluate_data_expression (&other, packet, lease,
in_options, cfg_options,
expr -> data.b2a.buffer);
if (s0 && s1 && s2 && s3) {
unsigned buflen, i;
if (len != 8 && len != 16 && len != 32) {
log_info ("binary_to_ascii: %s %ld!",
"invalid width", len);
goto b2a_out;
}
len /= 8;
/* The buffer must be a multiple of the number's
width. */
if (other.len % len) {
log_info ("binary-to-ascii: %s %d %s %ld!",
"length of buffer", other.len,
"not a multiple of width", len);
status = 0;
goto b2a_out;
}
/* Count the width of the output. */
buflen = 0;
for (i = 0; i < other.len; i += len) {
if (len == 1) {
if (offset == 8) {
if (other.data [i] < 8)
buflen++;
else if (other.data [i] < 64)
buflen += 2;
else
buflen += 3;
} else if (offset == 10) {
if (other.data [i] < 10)
buflen++;
else if (other.data [i] < 100)
buflen += 2;
else
buflen += 3;
} else if (offset == 10) {
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,
"binary-to-ascii")) {
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)
1999-07-19 13:08:29 +00:00
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, "binary-to-ascii");
if (s3)
data_string_forget (&other, "binary-to-ascii");
if (status)
return 1;
return 0;
case expr_reverse:
/* Evaluate the width (len): */
s0 = evaluate_numeric_expression
(&len, packet, lease, in_options, cfg_options,
expr -> data.reverse.width);
/* Evaluate the data. */
memset (&data, 0, sizeof data);
s1 = evaluate_data_expression (&data, packet, lease,
in_options, cfg_options,
expr -> data.reverse.buffer);
if (s0 && s1) {
char *upper;
int i;
/* The buffer must be a multiple of the number's
width. */
if (other.len % len) {
log_info ("reverse: %s %d %s %ld!",
"length of buffer", other.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,
"reverse")) {
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)
1999-07-19 13:08:29 +00:00
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, "evaluate_data_expression");
if (status)
return 1;
return 0;
case expr_leased_address:
if (!lease) {
log_error ("data: leased_address: not available");
return 0;
}
result -> len = lease -> ip_addr.len;
if (buffer_allocate (&result -> buffer, result -> len,
"leased-address")) {
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)
1999-07-19 13:08:29 +00:00
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, in_options, cfg_options,
expr -> data.pick_first_value.car))) {
#if defined (DEBUG_EXPRESSIONS)
log_debug ("data: pick_first_value (%s, ???)",
print_hex_1 (result -> len,
result -> data, 40));
#endif
return 1;
}
if (expr -> data.pick_first_value.cdr &&
(evaluate_data_expression
(result, packet, lease, in_options, cfg_options,
expr -> data.pick_first_value.cdr))) {
#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,
"host-decl-name")) {
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_updated_dns_rr:
if (!lease) {
#if defined (DEBUG_EXPRESSIONS)
log_debug ("data: updated-dns-rr w/no lease = NULL");
#endif
return 0;
}
memset (&data, 0, sizeof data);
s0 = evaluate_data_expression
(&data, packet, lease, in_options, cfg_options,
expr -> data.updated_dns_rr);
if (!s0) {
#if defined (DEBUG_EXPRESSIONS)
log_debug ("data: updated-dns-rr (NULL) = NULL");
#endif
return 0;
}
if (data.len == 1 &&
!strncasecmp ((const char *)data.data, "a", 1)) {
s = lease -> ddns_fwd_name;
} else if (data.len == 3 &&
!strncasecmp ((const char *)data.data, "ptr", 3)) {
s = lease -> ddns_rev_name;
} else {
#if defined (DEBUG_EXPRESSIONS)
log_debug ("data: updated-dns-rr (%*s) = NULL",
result -> len > 50 ? 50 : result -> len,
result -> data);
#endif
data_string_forget (&data, "data: updated-dns-rr");
return 0;
}
if (!s) {
log_error ("data: updated-dns-rr: %*s value was NULL",
data.len, (const char *)data.data);
data_string_forget (&data, "data: updated-dns-rr");
return 0;
}
data_string_forget (&data, "data: updated-dns-rr");
result -> len = strlen (s);
if (buffer_allocate (&result -> buffer, result -> len + 1,
"updated-dns-rr")) {
result -> data = &result -> buffer -> data [0];
strcpy ((char *)&result -> buffer -> data [0], s);
result -> terminated = 1;
} else {
#if defined (DEBUG_EXPRESSIONS)
log_debug ("data: updated-dns-rr (%*s)",
result -> len > 50 ? 50 : result -> len,
result -> data);
#endif
log_error ("data: updated-dns-rr: no memory.");
return 0;
}
#if defined (DEBUG_EXPRESSIONS)
log_debug ("data: updated-dns-rr (%*s) = %s",
result -> len > 50 ? 50 : result -> len,
result -> data, s);
#endif
return 1;
case expr_check:
case expr_equal:
case expr_and:
case expr_or:
case expr_not:
case expr_match:
case expr_static:
1999-04-12 22:11:12 +00:00
case expr_known:
case expr_none:
case expr_exists:
case expr_dns_update:
case expr_dns_delete:
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:
log_error ("Numeric opcode in evaluate_data_expression: %d",
expr -> op);
return 0;
}
log_error ("Bogus opcode in evaluate_data_expression: %d", expr -> op);
return 0;
}
int evaluate_numeric_expression (result, packet, lease,
in_options, cfg_options, expr)
unsigned long *result;
struct packet *packet;
struct lease *lease;
struct option_state *in_options;
struct option_state *cfg_options;
struct expression *expr;
{
struct data_string data;
int status;
switch (expr -> op) {
case expr_check:
case expr_equal:
case expr_and:
case expr_or:
case expr_not:
case expr_match:
case expr_static:
1999-04-12 22:11:12 +00:00
case expr_known:
case expr_none:
case expr_exists:
case expr_dns_update:
case expr_dns_delete:
log_error ("Boolean opcode in evaluate_numeric_expression: %d",
expr -> op);
return 0;
case expr_substring:
case expr_suffix:
case expr_option:
case expr_hardware:
case expr_const_data:
case expr_packet:
case expr_concat:
case expr_encapsulate:
case expr_host_lookup:
1999-05-27 14:30:00 +00:00
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_updated_dns_rr:
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, in_options, cfg_options,
expr -> data.extract_int);
if (status)
*result = data.data [0];
#if defined (DEBUG_EXPRESSIONS)
1999-07-19 13:08:29 +00:00
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, "expr_extract_int8");
return status;
case expr_extract_int16:
memset (&data, 0, sizeof data);
status = (evaluate_data_expression
(&data, packet, lease, in_options, cfg_options,
expr -> data.extract_int));
if (status && data.len >= 2)
*result = getUShort (data.data);
#if defined (DEBUG_EXPRESSIONS)
1999-07-19 13:08:29 +00:00
log_debug ("num: extract_int16 (%s) = %ld",
((status && data.len >= 2) ?
print_hex_1 (data.len, data.data, 60) : "NULL"),
*result);
#endif
if (status)
data_string_forget (&data, "expr_extract_int16");
return (status && data.len >= 2);
case expr_extract_int32:
memset (&data, 0, sizeof data);
status = (evaluate_data_expression
(&data, packet, lease, in_options, cfg_options,
expr -> data.extract_int));
if (status && data.len >= 4)
*result = getULong (data.data);
#if defined (DEBUG_EXPRESSIONS)
1999-07-19 13:08:29 +00:00
log_debug ("num: extract_int32 (%s) = %ld",
((status && data.len >= 4) ?
print_hex_1 (data.len, data.data, 60) : "NULL"),
*result);
#endif
if (status)
data_string_forget (&data, "expr_extract_int32");
return (status && data.len >= 4);
case expr_const_int:
*result = expr -> data.const_int;
#if defined (DEBUG_EXPRESSIONS)
log_debug ("number: CONSTANT = %ld", *result);
#endif
return 1;
case expr_lease_time:
if (!lease) {
log_error ("data: leased_lease: not available");
return 0;
}
if (lease -> ends < cur_time) {
log_error ("%s %lu when it is now %lu",
"data: lease_time: lease ends at",
lease -> ends, 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;
}
log_error ("evaluate_numeric_expression: bogus opcode %d", expr -> op);
return 0;
}
/* Return data hanging off of an option cache structure, or if there
isn't any, evaluate the expression hanging off of it and return the
result of that evaluation. There should never be both an expression
and a valid data_string. */
int evaluate_option_cache (result, packet, lease, in_options, cfg_options, oc)
struct data_string *result;
struct packet *packet;
struct lease *lease;
struct option_state *in_options;
struct option_state *cfg_options;
struct option_cache *oc;
{
if (oc -> data.len) {
data_string_copy (result,
&oc -> data, "evaluate_option_cache");
return 1;
}
if (!oc -> expression)
return 0;
return evaluate_data_expression (result, packet, lease, in_options,
cfg_options,
oc -> expression);
}
/* Evaluate an option cache and extract a boolean from the result,
returning the boolean. Return false if there is no data. */
int evaluate_boolean_option_cache (packet, lease, in_options, cfg_options, oc)
struct packet *packet;
struct lease *lease;
struct option_state *in_options;
struct option_state *cfg_options;
struct option_cache *oc;
{
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,
in_options, cfg_options, oc))
return 0;
if (ds.len && ds.data [0])
result = 1;
else
result = 0;
data_string_forget (&ds, "evaluate_boolean_option_cache");
return result;
}
/* Evaluate a boolean expression and return the result of the evaluation,
or FALSE if it failed. */
int evaluate_boolean_expression_result (packet, lease,
in_options, cfg_options, expr)
struct packet *packet;
struct lease *lease;
struct option_state *in_options;
struct option_state *cfg_options;
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,
in_options, cfg_options, expr))
return 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, name)
struct expression **eptr;
const char *name;
{
struct expression *expr = *eptr;
/* Zero the pointer. */
*eptr = (struct expression *)0;
/* Decrement the reference count. If it's nonzero, we're
done. */
if (--(expr -> refcnt) > 0)
return;
if (expr -> refcnt < 0) {
log_error ("expression_dereference: negative refcnt!");
#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_concat:
case expr_and:
case expr_or:
if (expr -> data.equal [0])
expression_dereference (&expr -> data.equal [0], name);
if (expr -> data.equal [1])
expression_dereference (&expr -> data.equal [1], name);
break;
case expr_substring:
if (expr -> data.substring.expr)
expression_dereference (&expr -> data.substring.expr,
name);
if (expr -> data.substring.offset)
expression_dereference (&expr -> data.substring.offset,
name);
if (expr -> data.substring.len)
expression_dereference (&expr -> data.substring.len,
name);
break;
case expr_suffix:
if (expr -> data.suffix.expr)
expression_dereference (&expr -> data.suffix.expr,
name);
if (expr -> data.suffix.len)
expression_dereference (&expr -> data.suffix.len,
name);
break;
case expr_not:
if (expr -> data.not)
expression_dereference (&expr -> data.not, name);
break;
case expr_packet:
if (expr -> data.packet.offset)
expression_dereference (&expr -> data.packet.offset,
name);
if (expr -> data.packet.len)
expression_dereference (&expr -> data.packet.len,
name);
break;
case expr_extract_int8:
case expr_extract_int16:
case expr_extract_int32:
if (expr -> data.extract_int)
expression_dereference (&expr -> data.extract_int,
name);
break;
1999-05-27 14:30:00 +00:00
case expr_encode_int8:
case expr_encode_int16:
case expr_encode_int32:
if (expr -> data.encode_int)
expression_dereference (&expr -> data.encode_int,
name);
break;
case expr_encapsulate:
case expr_const_data:
data_string_forget (&expr -> data.const_data, name);
break;
case expr_host_lookup:
if (expr -> data.host_lookup)
dns_host_entry_dereference (&expr -> data.host_lookup,
name);
break;
case expr_binary_to_ascii:
if (expr -> data.b2a.base)
expression_dereference (&expr -> data.b2a.base, name);
if (expr -> data.b2a.width)
expression_dereference (&expr -> data.b2a.width, name);
if (expr -> data.b2a.seperator)
expression_dereference (&expr -> data.b2a.seperator,
name);
if (expr -> data.b2a.buffer)
expression_dereference (&expr -> data.b2a.buffer,
name);
break;
case expr_pick_first_value:
if (expr -> data.pick_first_value.car)
expression_dereference
(&expr -> data.pick_first_value.car, name);
if (expr -> data.pick_first_value.cdr)
expression_dereference
(&expr -> data.pick_first_value.cdr, name);
break;
case expr_reverse:
if (expr -> data.reverse.width)
expression_dereference (&expr -> data.reverse.width,
name);
if (expr -> data.reverse.buffer)
expression_dereference
(&expr -> data.reverse.buffer, name);
break;
case expr_dns_update:
if (expr -> data.dns_update.type)
expression_dereference (&expr -> data.dns_update.type,
name);
if (expr -> data.dns_update.expr1)
expression_dereference (&expr -> data.dns_update.expr1,
name);
if (expr -> data.dns_update.expr2)
expression_dereference (&expr -> data.dns_update.expr2,
name);
if (expr -> data.dns_update.ttl)
expression_dereference (&expr -> data.dns_update.ttl,
name);
/* No subexpressions. */
case expr_leased_address:
case expr_lease_time:
case expr_const_int:
case expr_check:
case expr_option:
case expr_hardware:
case expr_exists:
1999-04-12 22:11:12 +00:00
case expr_known:
case expr_updated_dns_rr:
break;
default:
break;
}
free_expression (expr, "expression_dereference");
}
/* Make a copy of the data in data_string, upping the buffer reference
count if there's a buffer. */
void data_string_copy (dest, src, name)
struct data_string *dest;
struct data_string *src;
const char *name;
{
if (src -> buffer)
buffer_reference (&dest -> buffer, src -> buffer, name);
dest -> data = src -> data;
dest -> terminated = src -> terminated;
dest -> len = src -> len;
}
/* Release the reference count to a data string's buffer (if any) and
zero out the other information, yielding the null data string. */
void data_string_forget (data, name)
struct data_string *data;
const char *name;
{
if (data -> buffer)
buffer_dereference (&data -> buffer, name);
memset (data, 0, sizeof *data);
}
/* Make a copy of the data in data_string, upping the buffer reference
count if there's a buffer. */
void data_string_truncate (dp, len)
struct data_string *dp;
int len;
{
if (len < dp -> len) {
dp -> terminated = 0;
dp -> len = len;
}
}
int is_boolean_expression (expr)
struct expression *expr;
{
return (expr -> op == expr_check ||
expr -> op == expr_exists ||
expr -> op == expr_equal ||
expr -> op == expr_and ||
expr -> op == expr_or ||
expr -> op == expr_dns_update ||
expr -> op == expr_dns_delete ||
expr -> op == expr_not ||
expr -> op == expr_known ||
expr -> op == expr_static);
}
int is_data_expression (expr)
struct expression *expr;
{
return (expr -> op == expr_substring ||
expr -> op == expr_suffix ||
expr -> op == expr_option ||
expr -> op == expr_hardware ||
expr -> op == expr_const_data ||
expr -> op == expr_packet ||
expr -> op == expr_concat ||
expr -> op == expr_encapsulate ||
1999-05-27 14:30:00 +00:00
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_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_updated_dns_rr);
}
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);
}
static int op_val PROTO ((enum expr_op));
static int op_val (op)
enum expr_op op;
{
switch (op) {
case expr_none:
case expr_match:
case expr_static:
case expr_check:
case expr_substring:
case expr_suffix:
case expr_concat:
case expr_encapsulate:
case expr_host_lookup:
case expr_not:
case expr_option:
case expr_hardware:
case expr_packet:
case expr_const_data:
case expr_extract_int8:
case expr_extract_int16:
case expr_extract_int32:
1999-05-27 14:30:00 +00:00
case expr_encode_int8:
case expr_encode_int16:
case expr_encode_int32:
case expr_const_int:
case expr_exists:
1999-04-12 22:11:12 +00:00
case expr_known:
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_lease_time:
case expr_dns_update:
case expr_dns_delete:
case expr_updated_dns_rr:
return 100;
case expr_equal:
return 3;
case expr_and:
return 1;
case expr_or:
return 2;
}
return 100;
}
int op_precedence (op1, op2)
enum expr_op op1, op2;
{
int ov1, ov2;
return op_val (op1) - op_val (op2);
}
enum expression_context op_context (op)
enum expr_op op;
{
switch (op) {
/* XXX Why aren't these specific? */
case expr_none:
case expr_match:
case expr_static:
case expr_check:
case expr_substring:
case expr_suffix:
case expr_concat:
case expr_encapsulate:
case expr_host_lookup:
case expr_not:
case expr_option:
case expr_hardware:
case expr_packet:
case expr_const_data:
case expr_extract_int8:
case expr_extract_int16:
case expr_extract_int32:
1999-05-27 14:30:00 +00:00
case expr_encode_int8:
case expr_encode_int16:
case expr_encode_int32:
case expr_const_int:
case expr_exists:
1999-04-12 22:11:12 +00:00
case expr_known:
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_lease_time:
case expr_dns_update:
case expr_dns_delete:
case expr_updated_dns_rr:
return context_any;
case expr_equal:
return context_data;
case expr_and:
return context_boolean;
case expr_or:
return context_boolean;
}
return context_any;
}
int write_expression (file, expr, col, indent)
FILE *file;
struct expression *expr;
int col;
int indent;
{
struct expression *e;
const char *s;
char obuf [65];
int scol;
int width;
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_equal:
s = "=";
binary:
col = write_expression (file,
expr -> data.equal [0], col, indent);
col = token_print_indent (file, col, indent, " ", " ", s);
col = write_expression (file,
expr -> data.equal [1], col, indent);
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);
col = token_print_indent (file, col, indent, "", " ", ",");
col = write_expression (file, expr -> data.substring.offset,
col, indent);
col = token_print_indent (file, col, scol, "", " ", ",");
col = write_expression (file, expr -> data.substring.len,
col, scol);
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);
col = token_print_indent (file, col, scol, "", " ", ",");
col = write_expression (file, expr -> data.suffix.len,
col, scol);
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;
concat_again:
col = write_expression (file, e -> data.concat [0],
col, scol);
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);
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_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);
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);
col = token_print_indent (file, col, scol, "", " ", ",");
col = write_expression (file, expr -> data.packet.len,
col, scol);
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);
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);
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);
col = token_print_indent (file, col, scol, "", " ", ",");
col = write_expression (file, expr -> data.reverse.buffer,
col, scol);
col = token_print_indent (file, col, indent, "", "",
")");
break;
case expr_leased_address:
col = token_print_indent (file, col, indent, "", "",
"leased-address");
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);
col = token_print_indent (file, col, scol, "", " ",
",");
col = write_expression (file, expr -> data.b2a.width,
col, scol);
col = token_print_indent (file, col, scol, "", " ",
",");
col = write_expression (file, expr -> data.b2a.seperator,
col, scol);
col = token_print_indent (file, col, scol, "", " ",
",");
col = write_expression (file, expr -> data.b2a.buffer,
col, scol);
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;
pick_again:
col = write_expression (file,
e -> data.pick_first_value.car,
col, scol);
/* 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);
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_update:
col = token_print_indent (file, col, indent, "", "",
"dns-update");
col = token_print_indent (file, col, indent, " ", "",
"(");
scol = col;
col = write_expression (file, expr -> data.dns_update.type,
col, scol);
col = token_print_indent (file, col, scol, "", " ",
",");
col = write_expression (file, expr -> data.dns_update.expr1,
col, scol);
col = token_print_indent (file, col, scol, "", " ",
",");
col = write_expression (file, expr -> data.dns_update.expr2,
col, scol);
col = token_print_indent (file, col, scol, "", " ",
",");
col = write_expression (file, expr -> data.dns_update.ttl,
col, scol);
col = token_print_indent (file, col, indent, "", "",
")");
break;
case expr_updated_dns_rr:
col = token_print_indent (file, col, indent, "", "",
"updated-dns-rr");
col = token_print_indent (file, col, indent, " ", "",
"(");
scol = col;
col = write_expression (file, expr -> data.updated_dns_rr,
col, scol);
col = token_print_indent (file, col, indent, "", "",
")");
break;
case expr_dns_delete:
col = token_print_indent (file, col, indent, "", "",
"dns-delete");
col = token_print_indent (file, col, indent, " ", "",
"(");
scol = col;
col = write_expression (file, expr -> data.dns_update.type,
col, scol);
col = token_print_indent (file, col, scol, "", " ",
",");
col = write_expression (file, expr -> data.dns_update.expr1,
col, scol);
col = token_print_indent (file, col, scol, "", " ",
",");
col = write_expression (file, expr -> data.dns_update.expr2,
col, scol);
col = token_print_indent (file, col, indent, "", "",
")");
break;
case expr_static:
col = token_print_indent (file, col, indent, "", "",
"static");
break;
default:
log_fatal ("invalid expression type in print_expression: %d",
expr -> op);
}
return col;
}
/* vim: set tabstop=8: */