From 33e712eab31c1a78b8b6937edc78d86681497768 Mon Sep 17 00:00:00 2001 From: Ted Lemon Date: Thu, 25 Jun 1998 03:10:32 +0000 Subject: [PATCH] Update all the names to reflect the unification of expression evaluation and dns lookup evaluation. Add expression evaluator. --- common/tree.c | 748 ++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 545 insertions(+), 203 deletions(-) diff --git a/common/tree.c b/common/tree.c index daf710fe..6f1f0383 100644 --- a/common/tree.c +++ b/common/tree.c @@ -42,17 +42,12 @@ #ifndef lint static char copyright[] = -"$Id: tree.c,v 1.11 1998/04/09 04:31:21 mellon Exp $ Copyright (c) 1995, 1996, 1997, 1998 The Internet Software Consortium. All rights reserved.\n"; +"$Id: tree.c,v 1.12 1998/06/25 03:10:32 mellon Exp $ Copyright (c) 1995, 1996, 1997, 1998 The Internet Software Consortium. All rights reserved.\n"; #endif /* not lint */ #include "dhcpd.h" -static TIME tree_evaluate_recurse PROTO ((int *, unsigned char **, int *, - struct tree *)); -static TIME do_host_lookup PROTO ((int *, unsigned char **, int *, - struct dns_host_entry *)); -static void do_data_copy PROTO ((int *, unsigned char **, int *, - unsigned char *, int)); +static struct data_string do_host_lookup PROTO ((struct dns_host_entry *)); pair cons (car, cdr) caddr_t car; @@ -66,30 +61,15 @@ pair cons (car, cdr) return foo; } -struct tree_cache *tree_cache (tree) - struct tree *tree; -{ - struct tree_cache *tc; - - tc = new_tree_cache ("tree_cache"); - if (!tc) - return 0; - tc -> value = (unsigned char *)0; - tc -> len = tc -> buf_size = 0; - tc -> timeout = 0; - tc -> tree = tree; - return tc; -} - -struct tree *tree_host_lookup (name) +struct expression *make_host_lookup (name) char *name; { - struct tree *nt; - nt = new_tree ("tree_host_lookup"); + struct expression *nt; + nt = new_expression ("make_host_lookup"); if (!nt) error ("No memory for host lookup tree node."); - nt -> op = TREE_HOST_LOOKUP; - nt -> data.host_lookup.host = enter_dns_host (name); + nt -> op = expr_host_lookup; + nt -> data.host_lookup = enter_dns_host (name); return nt; } @@ -99,42 +79,50 @@ struct dns_host_entry *enter_dns_host (name) struct dns_host_entry *dh; if (!(dh = (struct dns_host_entry *)dmalloc - (sizeof (struct dns_host_entry), "enter_dns_host")) - || !(dh -> hostname = dmalloc (strlen (name) + 1, - "enter_dns_host"))) + (sizeof (struct dns_host_entry), "enter_dns_host"))) error ("Can't allocate space for new host."); + memset (dh, 0, sizeof *dh); + + dh -> hostname = dmalloc (strlen (name) + 1, "enter_dns_host"); strcpy (dh -> hostname, name); - dh -> data = (unsigned char *)0; - dh -> data_len = 0; - dh -> buf_len = 0; - dh -> timeout = 0; return dh; } -struct tree *tree_const (data, len) +struct expression *make_const_data (data, len, terminated, allocate) unsigned char *data; int len; + int terminated; + int allocate; { - struct tree *nt; - if (!(nt = new_tree ("tree_const"))) + struct expression *nt; + if (!(nt = new_expression ("tree_const"))) error ("No memory for constant data tree node."); + memset (nt, 0, sizeof *nt); if (len) { - if (!(nt -> data.const_val.data = - (unsigned char *)dmalloc (len, "tree_const"))) - error ("No memory for constant data tree data."); - memcpy (nt -> data.const_val.data, data, len); + if (allocate) { + if (!(nt -> data.const_data.data = + (unsigned char *)dmalloc (len + terminated, + "tree_const"))) + error ("No memory for const_data node."); + memcpy (nt -> data.const_data.data, + data, len + terminated); + nt -> data.const_data.buffer = + nt -> data.const_data.data; + } else + nt -> data.const_data.data = data; + nt -> data.const_data.terminated = terminated; } else - nt -> data.const_val.data = 0; + nt -> data.const_data.data = 0; - nt -> op = TREE_CONST; - nt -> data.const_val.len = len; + nt -> op = expr_const_data; + nt -> data.const_data.len = len; return nt; } -struct tree *tree_concat (left, right) - struct tree *left, *right; +struct expression *make_concat (left, right) + struct expression *left, *right; { - struct tree *nt; + struct expression *nt; /* If we're concatenating a null tree to a non-null tree, just return the non-null tree; if both trees are null, return @@ -144,158 +132,149 @@ struct tree *tree_concat (left, right) if (!right) return left; - /* If both trees are constant, combine them. */ - if (left -> op == TREE_CONST && right -> op == TREE_CONST) { - unsigned char *buf = dmalloc (left -> data.const_val.len - + right -> data.const_val.len, - "tree_concat"); + /* If both expressions are constant, combine them. */ + if (left -> op == expr_const_data && + right -> op == expr_const_data) { + unsigned char *buf = + dmalloc (left -> data.const_data.len + + right -> data.const_data.len + + right -> data.const_data.terminated, + "tree_concat"); if (!buf) error ("No memory to concatenate constants."); - memcpy (buf, left -> data.const_val.data, - left -> data.const_val.len); - memcpy (buf + left -> data.const_val.len, - right -> data.const_val.data, - right -> data.const_val.len); - dfree (left -> data.const_val.data, "tree_concat"); - dfree (right -> data.const_val.data, "tree_concat"); - left -> data.const_val.data = buf; - left -> data.const_val.len += right -> data.const_val.len; - free_tree (right, "tree_concat"); + memcpy (buf, left -> data.const_data.data, + left -> data.const_data.len); + memcpy (buf + left -> data.const_data.len, + right -> data.const_data.data, + right -> data.const_data.len); + if (left -> data.const_data.buffer) + dfree (left -> data.const_data.buffer, "make_concat"); + if (right -> data.const_data.buffer) + dfree (right -> data.const_data.buffer, "make_concat"); + left -> data.const_data.data = buf; + left -> data.const_data.buffer = buf; + left -> data.const_data.len += right -> data.const_data.len; + free_expression (right, "make_concat"); return left; } /* Otherwise, allocate a new node to concatenate the two. */ - if (!(nt = new_tree ("tree_concat"))) - error ("No memory for data tree concatenation node."); - nt -> op = TREE_CONCAT; - nt -> data.concat.left = left; - nt -> data.concat.right = right; + if (!(nt = new_expression ("make_concat"))) + error ("No memory for concatenation expression node."); + nt -> op = expr_concat; + nt -> data.concat [0] = left; + nt -> data.concat [1] = right; return nt; } -struct tree *tree_limit (tree, limit) - struct tree *tree; - int limit; +struct expression *make_substring (expr, offset, length) + struct expression *expr; + struct expression *offset; + struct expression *length; { - struct tree *rv; + struct expression *rv; - /* If the tree we're limiting is constant, limit it now. */ - if (tree -> op == TREE_CONST) { - if (tree -> data.const_val.len > limit) - tree -> data.const_val.len = limit; - return tree; + /* If the expression we're limiting is constant, limit it now. */ + if (expr -> op == expr_const_data && + offset -> op == expr_const_int && + length -> op == expr_const_int) { + int off = offset -> data.const_int; + int len = length -> data.const_int; + if (expr -> data.const_data.len > off) { + expr -> data.const_data.data += off; + expr -> data.const_data.len -= off; + if (expr -> data.const_data.len > len) { + expr -> data.const_data.len = len; + expr -> data.const_data.terminated = 0; + } + } else { + expr -> data.const_data.len = 0; + expr -> data.const_data.terminated = 0; + } + + free_expression (offset, "make_substring"); + free_expression (length, "make_substring"); + return expr; } /* Otherwise, put in a node which enforces the limit on evaluation. */ - rv = new_tree ("tree_limit"); + rv = new_expression ("make_substring"); if (!rv) - return (struct tree *)0; - rv -> op = TREE_LIMIT; - rv -> data.limit.tree = tree; - rv -> data.limit.limit = limit; + error ("no memory for substring expression."); + memset (rv, 0, sizeof *rv); + rv -> op = expr_substring; + rv -> data.substring.expr = expr; + rv -> data.substring.offset = offset; + rv -> data.substring.len = length; return rv; } -int tree_evaluate (tree_cache) - struct tree_cache *tree_cache; -{ - unsigned char *bp = tree_cache -> value; - int bc = tree_cache -> buf_size; - int bufix = 0; - - /* If there's no tree associated with this cache, it evaluates - to a constant and that was detected at startup. */ - if (!tree_cache -> tree) - return 1; - - /* Try to evaluate the tree without allocating more memory... */ - tree_cache -> timeout = tree_evaluate_recurse (&bufix, &bp, &bc, - tree_cache -> tree); - - /* No additional allocation needed? */ - if (bufix <= bc) { - tree_cache -> len = bufix; - return 1; - } - - /* If we can't allocate more memory, return with what we - have (maybe nothing). */ - if (!(bp = (unsigned char *)dmalloc (bufix, "tree_evaluate"))) - return 0; - - /* Record the change in conditions... */ - bc = bufix; - bufix = 0; - - /* Note that the size of the result shouldn't change on the - second call to tree_evaluate_recurse, since we haven't - changed the ``current'' time. */ - tree_evaluate_recurse (&bufix, &bp, &bc, tree_cache -> tree); - - /* Free the old buffer if needed, then store the new buffer - location and size and return. */ - if (tree_cache -> value) - dfree (tree_cache -> value, "tree_evaluate"); - tree_cache -> value = bp; - tree_cache -> len = bufix; - tree_cache -> buf_size = bc; - return 1; -} - -static TIME tree_evaluate_recurse (bufix, bufp, bufcount, tree) - int *bufix; - unsigned char **bufp; - int *bufcount; - struct tree *tree; -{ +struct expression *make_limit (expr, limit) + struct expression *expr; int limit; - TIME t1, t2; +{ + struct expression *rv; - switch (tree -> op) { - case TREE_CONCAT: - t1 = tree_evaluate_recurse (bufix, bufp, bufcount, - tree -> data.concat.left); - t2 = tree_evaluate_recurse (bufix, bufp, bufcount, - tree -> data.concat.right); - if (t1 > t2) - return t2; - return t1; - - case TREE_HOST_LOOKUP: - return do_host_lookup (bufix, bufp, bufcount, - tree -> data.host_lookup.host); - - case TREE_CONST: - if (tree -> data.const_val.data) - do_data_copy (bufix, bufp, bufcount, - tree -> data.const_val.data, - tree -> data.const_val.len); - t1 = MAX_TIME; - return t1; - - case TREE_LIMIT: - limit = *bufix + tree -> data.limit.limit; - t1 = tree_evaluate_recurse (bufix, bufp, bufcount, - tree -> data.limit.tree); - *bufix = limit; - return t1; - - default: - warn ("Bad node id in tree: %d."); - t1 = MAX_TIME; - return t1; + /* If the expression we're limiting is constant, limit it now. */ + if (expr -> op == expr_const_data) { + if (expr -> data.const_data.len > limit) { + expr -> data.const_data.len = limit; + expr -> data.const_data.terminated = 0; + } + return expr; } + + /* Otherwise, put in a node which enforces the limit on evaluation. */ + rv = new_expression ("make_limit 1"); + if (!rv) + error ("no memory for limit expression"); + memset (rv, 0, sizeof *rv); + rv -> op = expr_substring; + rv -> data.substring.expr = expr; + + /* Offset is a constant 0. */ + rv -> data.substring.offset = new_expression ("make_limit 2"); + if (!rv -> data.substring.offset) + error ("no memory for limit offset expression"); + memset (rv -> data.substring.offset, 0, sizeof *rv); + rv -> data.substring.offset -> op = expr_const_int; + rv -> data.substring.offset -> data.const_int = 0; + + /* Length is a constant: the specified limit. */ + rv -> data.substring.len = new_expression ("make_limit 2"); + if (!rv -> data.substring.len) + error ("no memory for limit length expression"); + memset (rv -> data.substring.len, 0, sizeof *rv); + rv -> data.substring.offset -> op = expr_const_int; + rv -> data.substring.offset -> data.const_int = limit; + + return rv; } -static TIME do_host_lookup (bufix, bufp, bufcount, dns) - int *bufix; - unsigned char **bufp; - int *bufcount; +struct option_cache *option_cache (expr, option) + struct expression *expr; + struct option *option; +{ + struct option_cache *oc = new_option_cache ("option_cache"); + if (!oc) { + warn ("no memory for option cache."); + return (struct option_cache *)0; + } + memset (oc, 0, sizeof *oc); + oc -> expression = expr; + oc -> option = option; + return oc; +} + +static struct data_string do_host_lookup (dns) struct dns_host_entry *dns; { struct hostent *h; int i; int new_len; + struct data_string result; + + memset (&result, 0, sizeof result); #ifdef DEBUG_EVAL debug ("time: now = %d dns = %d %d diff = %d", @@ -305,13 +284,15 @@ static TIME do_host_lookup (bufix, bufp, bufcount, dns) /* If the record hasn't timed out, just copy the data and return. */ if (cur_time <= dns -> timeout) { #ifdef DEBUG_EVAL - debug ("easy copy: %x %d %x", - dns -> data, dns -> data_len, - dns -> data ? *(int *)(dns -> data) : 0); + debug ("easy copy: %x %d %s", + dns -> data, dns -> data.len, + dns -> data.data + ? inet_ntoa (*(struct in_addr *)(dns -> data.data)) + : 0); #endif - do_data_copy (bufix, bufp, bufcount, - dns -> data, dns -> data_len); - return dns -> timeout; + result.data = dns -> buffer; + result.len = dns -> data_len; + return result; } #ifdef DEBUG_EVAL debug ("Looking up %s", dns -> hostname); @@ -341,12 +322,13 @@ static TIME do_host_lookup (bufix, bufp, bufcount, dns) #endif /* !NO_H_ERRNO */ /* Okay to try again after a minute. */ - return cur_time + 60; + dns -> timeout = cur_time + 60; + return result; } #ifdef DEBUG_EVAL - debug ("Lookup succeeded; first address is %x", - h -> h_addr_list [0]); + debug ("Lookup succeeded; first address is %s", + inet_ntoa (h -> h_addr_list [0])); #endif /* Count the number of addresses we got... */ @@ -363,12 +345,12 @@ static TIME do_host_lookup (bufix, bufp, bufcount, dns) new_len = dns -> buf_len; if (!dns -> buf_len) { dns -> timeout = cur_time + 60; - return dns -> timeout; + return result; } } else { - if (dns -> data) - dfree (dns -> data, "do_host_lookup"); - dns -> data = buf; + if (dns -> buffer) + dfree (dns -> buffer, "do_host_lookup"); + dns -> buffer = buf; dns -> buf_len = new_len; } } @@ -376,12 +358,12 @@ static TIME do_host_lookup (bufix, bufp, bufcount, dns) /* Addresses are conveniently stored one to the buffer, so we have to copy them out one at a time... :'( */ for (i = 0; i < new_len / h -> h_length; i++) { - memcpy (dns -> data + h -> h_length * i, + memcpy (dns -> buffer + h -> h_length * i, h -> h_addr_list [i], h -> h_length); } #ifdef DEBUG_EVAL debug ("dns -> data: %x h -> h_addr_list [0]: %x", - *(int *)(dns -> data), h -> h_addr_list [0]); + *(int *)(dns -> buffer), h -> h_addr_list [0]); #endif dns -> data_len = new_len; @@ -393,26 +375,386 @@ static TIME do_host_lookup (bufix, bufp, bufcount, dns) debug ("hard copy: %x %d %x", dns -> data, dns -> data_len, *(int *)(dns -> data)); #endif - do_data_copy (bufix, bufp, bufcount, dns -> data, dns -> data_len); - return dns -> timeout; + result.data = dns -> buffer; + result.len = dns -> data_len; + return result; } -static void do_data_copy (bufix, bufp, bufcount, data, len) - int *bufix; - unsigned char **bufp; - int *bufcount; - unsigned char *data; - int len; +int evaluate_boolean_expression (packet, expr) + struct packet *packet; + struct expression *expr; { - int space = *bufcount - *bufix; + struct data_string left, right; + int result; - /* If there's more space than we need, use only what we need. */ - if (space > len) - space = len; + switch (expr -> op) { + case expr_check: + return check_collection (packet, expr -> data.check); - /* Copy as much data as will fit, then increment the buffer index - by the amount we actually had to copy, which could be more. */ - if (space > 0) - memcpy (*bufp + *bufix, data, space); - *bufix += len; + case expr_equal: + left = evaluate_data_expression (packet, + expr -> data.equal [0]); + right = evaluate_data_expression (packet, + expr -> data.equal [1]); + if (left.len == right.len && !memcmp (left.data, + right.data, left.len)) + result = 1; + else + result = 0; + if (left.buffer) + dfree ("evaluate_boolean_expression", left.buffer); + if (right.buffer) + dfree ("evaluate_boolean_expression", right.buffer); + return result; + + case expr_and: + return (evaluate_boolean_expression (packet, + expr -> data.and [0]) && + evaluate_boolean_expression (packet, + expr -> data.and [1])); + + case expr_or: + return (evaluate_boolean_expression (packet, + expr -> data.or [0]) || + evaluate_boolean_expression (packet, + expr -> data.or [1])); + + case expr_not: + return (!evaluate_boolean_expression (packet, + expr -> data.not)); + + case expr_substring: + case expr_suffix: + case expr_option: + case expr_hardware: + case expr_const_data: + case expr_packet: + case expr_concat: + case expr_host_lookup: + warn ("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: + warn ("Numeric opcode in evaluate_boolean_expression: %d", + expr -> op); + return 0; + } + + warn ("Bogus opcode in evaluate_boolean_expression: %d", expr -> op); + return 0; } + +struct data_string evaluate_data_expression (packet, expr) + struct packet *packet; + struct expression *expr; +{ + struct data_string result, data, other; + int offset, len; + + switch (expr -> op) { + /* Extract N bytes starting at byte M of a data string. */ + case expr_substring: + data = evaluate_data_expression (packet, + expr -> data.substring.expr); + + /* Evaluate the offset and length. */ + offset = evaluate_numeric_expression + (packet, expr -> data.substring.offset); + len = evaluate_numeric_expression + (packet, expr -> data.substring.len); + + /* If the offset is after end of the string, return + an empty string. */ + if (data.len <= offset) { + if (data.buffer) + dfree ("expr_substring", data.buffer); + memset (&result, 0, sizeof result); + return result; + } + + /* Otherwise, do the adjustments and return what's left. */ + data.len -= offset; + if (data.len > len) { + data.len = len; + data.terminated = 0; + } + data.data += offset; + return data; + + /* Extract the last N bytes of a data string. */ + case expr_suffix: + data = evaluate_data_expression (packet, + expr -> data.suffix.expr); + + /* Evaluate the length. */ + len = evaluate_numeric_expression + (packet, expr -> data.substring.len); + + /* If we are returning the last N bytes of a string whose + length is <= N, just return the string. */ + if (data.len <= len) + return data; + data.data += data.len - len; + data.len = len; + return data; + + /* Extract an option. */ + case expr_option: + return ((*expr -> data.option -> universe -> lookup_func) + (packet, expr -> data.option -> code)); + + /* Combine the hardware type and address. */ + case expr_hardware: + result.len = packet -> raw -> hlen + 1; + result.buffer = dmalloc (result.len, + "expr_hardware"); + if (!result.buffer) { + warn ("no memory for expr_hardware"); + result.len = 0; + } else { + result.buffer [0] = packet -> raw -> htype; + memcpy (&result.buffer [1], packet -> raw -> chaddr, + packet -> raw -> hlen); + } + result.data = result.buffer; + result.terminated = 0; + return result; + + /* Extract part of the raw packet. */ + case expr_packet: + len = evaluate_numeric_expression (packet, + expr -> data.packet.len); + offset = evaluate_numeric_expression (packet, + expr -> data.packet.len); + if (offset > packet -> packet_length) { + warn ("expr_packet on %s: length %d + offset %d > %d", + print_hw_addr (packet -> raw -> htype, + packet -> raw -> hlen, + packet -> raw -> chaddr), + len, offset, packet -> packet_length); + memset (&result, 0, sizeof result); + return result; + } + if (offset + len > packet -> packet_length) + result.len = packet -> packet_length - offset; + else + result.len = len; + result.data = ((unsigned char *)(packet -> raw)) + offset; + result.buffer = (unsigned char *)0; + result.terminated = 0; + return result; + + /* Some constant data... */ + case expr_const_data: + return expr -> data.const_data; + + /* Hostname lookup... */ + case expr_host_lookup: + return do_host_lookup (expr -> data.host_lookup); + break; + + /* Concatenation... */ + case expr_concat: + data = evaluate_data_expression (packet, + expr -> data.concat [0]); + other = evaluate_data_expression (packet, + expr -> data.concat [1]); + + memset (&result, 0, sizeof result); + result.buffer = dmalloc (data.len + other.len + + other.terminated, "expr_concat"); + if (!result.buffer) { + warn ("out of memory doing concatenation."); + return result; + } + + result.len = (data.len + other.len); + result.data = result.buffer; + memcpy (result.data, data.data, data.len); + memcpy (&result.data [data.len], other.data, + other.len + other.terminated); + if (data.buffer) + dfree (data.buffer, "expr_concat"); + if (other.buffer) + dfree (other.buffer, "expr_concat"); + return result; + break; + + case expr_check: + case expr_equal: + case expr_and: + case expr_or: + case expr_not: + warn ("Boolean opcode in evaluate_data_expression: %d", + expr -> op); + goto null_return; + + case expr_extract_int8: + case expr_extract_int16: + case expr_extract_int32: + case expr_const_int: + warn ("Numeric opcode in evaluate_data_expression: %d", + expr -> op); + goto null_return; + } + + warn ("Bogus opcode in evaluate_data_expression: %d", expr -> op); + null_return: + memset (&result, 0, sizeof result); + return result; +} + +unsigned long evaluate_numeric_expression (packet, expr) + struct packet *packet; + struct expression *expr; +{ + struct data_string data; + unsigned long result; + + switch (expr -> op) { + case expr_check: + case expr_equal: + case expr_and: + case expr_or: + case expr_not: + warn ("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_host_lookup: + warn ("Data opcode in evaluate_numeric_expression: %d", + expr -> op); + return 0; + + case expr_extract_int8: + data = evaluate_data_expression (packet, + expr -> + data.extract_int.expr); + if (data.len < 1) + return 0; + result = data.data [0]; + if (data.buffer) + dfree (data.buffer, "expr_extract_int8"); + return result; + + case expr_extract_int16: + data = evaluate_data_expression (packet, + expr -> + data.extract_int.expr); + if (data.len < 2) + return 0; + result = getUShort (data.data); + if (data.buffer) + dfree (data.buffer, "expr_extract_int16"); + return result; + + case expr_extract_int32: + data = evaluate_data_expression (packet, + expr -> + data.extract_int.expr); + if (data.len < 4) + return 0; + result = getULong (data.data); + if (data.buffer) + dfree (data.buffer, "expr_extract_int32"); + return result; + + case expr_const_int: + return expr -> data.const_int; + } + + warn ("Bogus opcode in evaluate_numeric_expression: %d", expr -> op); + return 0; +} + +void free_oc_ephemeral_state (oc) + struct option_cache *oc; +{ + if (free_ephemeral_outer_tree (expr)) + free_option_cache (oc, "free_oc_ephemeral_state"); +} + +/* Recursively free any ephemeral subexpressions of the passed expression, + and then free that expression. */ + +int free_ephemeral_outer_tree (expr) + struct expression *expr; +{ + /* If this expression isn't ephemeral, notify the caller. */ + if (!(expr -> flags & EXPR_EPHEMERAL)) + return 0; + + /* Free any ephemeral 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: + free_ephemeral_outer_tree (expr -> data.equal [0]); + free_ephemeral_outer_tree (expr -> data.equal [1]); + break; + + case expr_substring: + free_ephemeral_outer_tree (expr -> data.substring.expr); + free_ephemeral_outer_tree (expr -> data.substring.offset); + free_ephemeral_outer_tree (expr -> data.substring.len); + break; + + case expr_suffix: + free_ephemeral_outer_tree (expr -> data.suffix.expr); + free_ephemeral_outer_tree (expr -> data.suffix.len); + break; + + case expr_not: + free_ephemeral_outer_tree (expr -> data.not); + break; + + case expr_packet: + free_ephemeral_outer_tree (expr -> data.packet.offset); + free_ephemeral_outer_tree (expr -> data.packet.len); + break; + + case expr_extract_int8: + case expr_extract_int16: + case expr_extract_int32: + free_ephemeral_outer_tree (expr -> data.extract_int.expr); + free_ephemeral_outer_tree (expr -> data.extract_int.width); + break; + + /* No subexpressions. */ + case expr_const_int: + case expr_check: + case expr_host_lookup: + case expr_option: + case expr_const_data: + case expr_hardware: + break; + + default: + break; + } + + free_expression (expr, "free_expr_outer_tree"); + return 1; +} + +/* Free all of the state in an option state buffer. The buffer itself is + not freed, since these buffers are always contained in other structures. */ + +void free_option_state (state) + struct option_state *state; +{ + int i; + struct agent_option *ao;