diff -Naur snort-1.8.7.orig/Makefile.am snort-1.8.7p1/Makefile.am
--- snort-1.8.7.orig/Makefile.am	Wed May 15 06:30:59 2002
+++ snort-1.8.7p1/Makefile.am	Fri Sep  6 06:12:42 2002
@@ -34,8 +34,10 @@
 spo_unified.c spo_unified.h generators.h spp_stream4.h spp_stream4.c  \
 ubi_SplayTree.h sys_include.h spp_frag2.c spp_frag2.h spp_arpspoof.c  \
 spp_arpspoof.h spo_idmef.h spo_idmef.c spo_SnmpTrap.c spo_SnmpTrap.h  \
-event.h spo_log_null.c spo_log_null.h cdefs.h  
-
+event.h spo_log_null.c spo_log_null.h cdefs.h \
+engine2.h e2_help.c e2_cluster_tree.c e2_parse.c e2_extract.c         \
+e2_feature_int.c e2_feature_ipv4.c e2_feature_string.c                \
+e2_feature_flags.c
 
 EXTRA_DIST = BUGS RULES.SAMPLE CREDITS snort.conf USAGE backdoor.rules \
 info.rules smtp.rules ddos.rules local.rules telnet.rules dns.rules   \
diff -Naur snort-1.8.7.orig/e2_cluster_tree.c snort-1.8.7p1/e2_cluster_tree.c
--- snort-1.8.7.orig/e2_cluster_tree.c	Wed Dec 31 16:00:00 1969
+++ snort-1.8.7p1/e2_cluster_tree.c	Fri Sep  6 07:48:08 2002
@@ -0,0 +1,811 @@
+/*
+** Copyright (C) 2002 Distributed Systems Group, Technical University Vienna
+**
+**                    Thomas Toth (ttoth@infosys.tuwien.ac.at)
+**                    Christopher Kruegel (chris@infosys.tuwien.ac.t)
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation; either version 2 of the License, or
+** (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+** GNU General Public License for more details.
+**
+** You should have received a copy of the GNU General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include "engine2.h"
+#include <math.h>
+
+/******************************************************************************/
+/*                     Variable Definitions                                   */             
+/******************************************************************************/
+
+u_int8_t **rule_map;
+
+/*
+float threshold[NUM_TREE] = {
+    0.7,
+    0.9
+};
+*/
+
+float threshold[NUM_TREE] = {
+    0.3,
+    0.5
+};
+
+
+E2TreeNode *trees[NUM_PROTO][NUM_TREE];
+
+OptTreeNode **snort_rules;
+u_int32_t snort_rule_count;
+
+/******************************************************************************/
+/*                     Internal Functions                                     */             
+/******************************************************************************/
+
+/* 
+ * rules holds the original rule set, while succs all the successor nodes after splitting
+ *
+ *
+ * we want to include the following properties into the gain calculation:
+ * 
+ * 1. total number of rules in successor nodes (the fewer, the better)
+ * 2. decrease in entropy (i.e. how 'good' does this split divide the rules)
+ *    and the weights of individual rules 
+ * 3. cost of navigation function for this tree node (e.g. strings are more expensive than ints)
+ */
+ 
+
+
+float calc_gain(E2Rule *rules, u_int32_t rule_count, E2SuccessorNode *succs, u_int8_t feature)
+{
+    E2SuccessorNode *succ;
+    float children, local, child_count, total_count;
+    u_int32_t total;
+    
+    for (total = 0, succ = succs; succ != NULL; succ = succ->next) {
+	total += succ->rule_count;
+    }
+
+    local = log(rule_count);
+
+    children = 0.0;
+    total_count = (float) total;
+    for (succ = succs; succ != NULL; succ = succ->next) {
+	
+	if (succ->rule_count > 0) {
+	    child_count = (float) succ->rule_count;
+	    children += ((child_count / total_count) * log(child_count));
+	}
+    }
+    return (local - children);
+}
+
+u_int8_t* determine_max_features(u_int8_t *active, u_int8_t *hidden, u_int8_t proto, u_int32_t rule_count)
+{
+    u_int32_t sum, *feature_count;
+    u_int8_t *feature;
+    int feat, rule, index, final_index;
+#ifdef E2DEBUG
+    int	cnt;
+#endif
+
+    /* data structures to determine the best features for the decision tree */ 
+    feature = (u_int8_t *) calloc(e2_map_size[proto], sizeof(u_int8_t));
+    feature_count = (u_int32_t *) calloc(e2_map_size[proto], sizeof(u_int32_t));
+
+
+    /* determine total number of defined values for each feature */
+    /* and sort it in descending order */
+    for (feat = 0; feat < e2_map_size[proto]; ++feat) {
+
+	sum = 0;
+	for (rule = 0; rule < rule_count; ++rule) 
+	    if (active[rule] && !hidden[rule] && rule_map[feat][rule])
+		++sum;
+
+	/* find correct index */
+	for (index = 0; index < feat; ++index)
+	    if (feature_count[index] < sum)
+		break;
+	final_index = index;
+	
+	/* shift rest one pos to right */
+	for (index = feat; index > final_index; --index) {
+	    feature[index] = feature[index - 1];
+	    feature_count[index] = feature_count[index - 1];
+	}
+	    
+	/* insert value */
+	feature[index] = feat;
+	feature_count[index] = sum;
+    }
+
+#ifdef E2DEBUG
+    printf("   Max. features:\n");
+    for (cnt = 0; cnt < e2_map_size[proto]; ++cnt) 
+	printf("     %s - %d\n", e2_map[proto][feature[cnt]].f_name, feature_count[cnt]);
+#endif    
+
+    free(feature_count);
+    return feature;
+}
+
+u_int8_t* determine_rules(u_int32_t rule_count, u_int8_t *active, u_int8_t *hidden, u_int8_t proto, u_int8_t *feature, int fcount, int mode)
+{
+    u_int8_t *result;
+    int feat, rule, set, needed;
+
+    result = calloc(rule_count, sizeof(u_int32_t));
+
+    if (fcount > e2_map_size[proto])
+	fcount = e2_map_size[proto];
+   
+    if (fcount >= (mode+1))
+	needed = (fcount - (mode+1));
+    else
+	needed = 0;
+
+ 
+    for (rule = 0; rule < rule_count; ++rule) {
+
+	if (!active[rule])
+	    continue;
+	
+	if (!hidden[rule])
+	    set = 0;
+	else
+	    continue;
+	    // set = (needed - fcount);
+
+	for (feat = 0; feat < fcount; ++feat)
+	    if (rule_map[feature[feat]][rule])
+		++set;
+
+	if (set >= needed) 
+	    result[rule] = 1;
+    }
+
+    return result;
+}
+
+E2TreeNode* perform_split(E2Rule *rules, u_int8_t *active, u_int8_t proto, u_int8_t *features, int fcount)
+{
+    E2Rule *clone, *tr, *split_rules = NULL;
+    E2TreeNode *result;
+    int *useable_features;
+    int cnt, index;
+
+    for (tr = rules; tr != NULL; tr = tr->next) {
+	if (active[tr->id]) {
+	    clone = rule_clone(tr);
+	    clone->next = split_rules;
+	    split_rules = clone;
+	}
+    }
+
+    useable_features = calloc(e2_map_size[proto], sizeof(int));
+    for (cnt = 0; cnt < fcount; ++cnt) {
+	index = features[cnt];
+	useable_features[index] = 1;
+    }
+    for (cnt = 0; cnt < e2_map_size[proto]; ++cnt)
+	useable_features[cnt] = !useable_features[cnt];
+
+    result = split_snort_rules(split_rules, proto, useable_features);
+    
+    while (split_rules != NULL) {
+	tr = split_rules->next;
+	rule_destroy(split_rules);
+	split_rules = tr;
+    }
+
+    free(useable_features);
+
+
+    return result;
+}
+
+OptFpList* opt_list_dup(OptFpList *list)
+{
+    OptFpList *head, *tail, *element;
+
+    head = tail = NULL;
+
+    for (; list != NULL; list = list->next) {
+
+	element = calloc(1, sizeof(OptFpList));
+	memcpy(element, list, sizeof(OptFpList));
+
+	if (head == NULL) 
+	    head = tail = element;
+	else {
+	    tail->next = element;
+	    tail = element;
+	}
+	tail->next = NULL;
+    }
+
+    return head;
+}
+
+OptFpList* rtn_list_append(OptFpList *list, RuleFpList *rlist)
+{
+    OptFpList *wrapper;
+
+    for (; rlist->next != NULL; rlist = rlist->next) {
+	    
+	wrapper = calloc(1, sizeof(OptFpList));
+	
+	wrapper->feature_id = rlist->feature_id;
+	wrapper->referring = rlist;
+	wrapper->OptTestFunc = Engine2RuleFpListWrapper;
+	
+	wrapper->next = list;
+	list = wrapper;
+    }
+
+    return list;
+}
+
+/******************************************************************************/
+/*                            API Functions                                   */             
+/******************************************************************************/
+
+void build_e2_engine(E2Rule *rules, u_int32_t rule_count)
+{
+    u_int32_t active_count[NUM_TREE+1], unused, total_rules;
+    u_int8_t tree, proto, *active, *feature, *used[NUM_TREE+1], found;
+    int feat, cnt, sum, len, i, j; 
+    float ratio;
+
+    E2Rule *rule, *tr;
+    E2Trigger *trigger;
+
+    RuleTreeNode *parent;
+    OptTreeNode *otn;
+    OptFpList *fplist, *todelete;
+    RuleFpList *rlist;
+#ifdef E2DEBUG
+    int32_t uri_index;
+#endif
+
+    /* sanity checks */
+    if (rule_count == 0) {
+	printf("Fatal: build_e2_engine: attempt to build engine without any rules\n");
+	exit(1);
+    }
+
+
+    /* initialize data structures  */ 
+    active = (u_int8_t *) calloc(rule_count, sizeof(u_int8_t));
+    used[0] = (u_int8_t *) calloc(rule_count, sizeof(u_int8_t));
+
+    for (i = 0; i < NUM_PROTO; ++i)
+	for (j = 0; j < NUM_TREE; ++j)
+	    trees[i][j] = NULL;
+
+
+
+    /* build final snort rules array */
+    snort_rules = calloc(rule_count, sizeof(OptTreeNode));
+    for (rule = rules, total_rules = 0; rule != NULL; rule = rule->next) {
+#ifdef E2DEBUG
+	if (rule->id >= rule_count) {
+	    printf("Fatal: build_e2_engine: rule_id is out of bounds\n");
+	    exit(1);
+	}
+
+	
+	uri_index = e2_index_mapping_table[rule->proto][CONST_FEATURE_ID_CONTENT_URI];
+	if (uri_index < 0) {
+	    printf("Fatal: e2_build_engine: no URI index found\n");
+	    exit(1);
+	}
+	else if (rule->features[uri_index] != NULL) {
+	    printf("Fatal: e2_build_engine: assertion failed - URI feature != NULL\n");
+	    exit(1);
+	}
+	
+#endif
+
+	snort_rules[rule->id] = rule->otn;
+	rule->otn->rule_id = rule->id;
+
+	++total_rules;
+    }
+    snort_rule_count = rule_count;
+
+#ifdef E2DEBUG
+    for (cnt = 0; cnt < rule_count; ++cnt) {
+	if (snort_rules[cnt] == NULL) {
+	    printf("Fatal: build_e2_engine: rule id %d is missing in final array\n", cnt);
+	    exit(1);
+	}
+    }
+#endif
+
+
+#ifdef ENGINE2
+
+
+    /* copy the OptTreeNode checks to the list of interest - needed to handle ip rules */
+    for (rule = rules; rule != NULL; rule = rule->next) {
+
+	otn = snort_rules[rule->id];
+
+	switch (rule->proto) {
+
+	    case E2_PROTO_ICMP: 
+		if (otn->opt_func_proto[E2_PROTO_ICMP] == NULL)
+		    otn->opt_func_proto[E2_PROTO_ICMP] = opt_list_dup(otn->opt_func);
+		break;
+	    case E2_PROTO_UDP:
+		if (otn->opt_func_proto[E2_PROTO_UDP] == NULL)
+		    otn->opt_func_proto[E2_PROTO_UDP]  = opt_list_dup(otn->opt_func);
+		break;
+	    case E2_PROTO_TCP:
+		if (otn->opt_func_proto[E2_PROTO_TCP] == NULL)
+		    otn->opt_func_proto[E2_PROTO_TCP]  = opt_list_dup(otn->opt_func);
+		break;
+	    default:
+		printf("Fatal: build_e2_engine: rule has unknown proto (otn list)\n");
+		exit(1);
+	}
+    }
+
+    /* append RuleTreeNode checks (i.e. IP addresses and ports) to each individual rule */
+    for (rule = rules; rule != NULL; rule = rule->next) {
+
+	otn = snort_rules[rule->id];
+	parent = otn->rtn;
+	
+	rlist = parent->rule_func;
+
+#ifdef E2DEBUG
+	if (rlist == NULL) {
+	    printf("Fatal: build_e2_engine: RuleList is empty but should at least contain 'End of List' entry\n");
+	    exit(1);
+	}
+#endif
+
+	switch (rule->proto) {
+
+	    case E2_PROTO_ICMP: 
+		otn->opt_func_proto[E2_PROTO_ICMP] = rtn_list_append(otn->opt_func_proto[E2_PROTO_ICMP], rlist);
+		break;
+	    case E2_PROTO_UDP:
+		otn->opt_func_proto[E2_PROTO_UDP] = rtn_list_append(otn->opt_func_proto[E2_PROTO_UDP], rlist);
+		break;
+	    case E2_PROTO_TCP:
+		otn->opt_func_proto[E2_PROTO_TCP] = rtn_list_append(otn->opt_func_proto[E2_PROTO_TCP], rlist);
+		break;
+	    default:
+		printf("Fatal: build_e2_engine: rule has unknown proto (rtn list)\n");
+		exit(1);
+	}
+    }
+
+
+    printf("Snort -*> next generation detection engine <*-\n");
+    printf("By Christopher Kruegel and Thomas Toth (chris@infosys.tuwien.ac.at, ttoth@infosys.tuwien.ac.at)\n\n");
+    printf("Building decision trees for %d rules - this may take a while, please be patient\n", total_rules);
+
+    for (proto = 0; proto < NUM_PROTO; ++proto) {
+
+	/* only process 'good' protocols (e.g. not raw IP) */
+	switch (proto) {
+	    case E2_PROTO_ICMP: 
+#ifdef E2DEBUG
+		printf("Protocol: ICMP\n");
+#endif
+		break;
+	    case E2_PROTO_UDP:
+#ifdef E2DEBUG
+		printf("Protocol: UDP\n"); 
+#endif
+		break;
+	    case E2_PROTO_TCP:
+#ifdef E2DEBUG
+		printf("Protocol: TCP\n"); 
+#endif
+		break;
+	    default:
+		continue;
+	}
+
+	/* 'activate' all rules with correct protocol */
+	memset(active, 0, rule_count * sizeof(u_int8_t));
+	for (active_count[0] = 0, rule = rules; rule != NULL; rule = rule->next) {
+	    
+	    if ((active[rule->id]) && (rule->proto != proto)) {
+		printf("Fatal: rule %d has been activated previously\n", rule->id);
+		exit(1);
+	    }
+	    
+	    if (rule->proto == proto) {
+		active[rule->id] = 1;
+		++active_count[0];
+	    }
+	}
+
+	/* allocate rule map (rule x feature - matrix) */
+	rule_map = (u_int8_t **) calloc(e2_map_size[proto], sizeof(u_int8_t *));
+	for (feat = 0; feat < e2_map_size[proto]; ++feat)
+	    rule_map[feat] = calloc(rule_count, sizeof(u_int8_t));
+
+
+	/* insert defined features of each rule into rule_map */
+	for (rule = rules; rule != NULL; rule = rule->next) {
+	    for (feat = 0; feat < e2_map_size[proto]; ++feat) {
+
+		if (rule->id >= rule_count) {
+		    printf("Fatal: build_e2_engine: rule_id is out of bounds\n");
+		    exit(1);
+		}
+		if (rule->features == NULL) {
+		    printf("Fatal: build_e2_engine: feature array is null\n");
+		    exit(1);
+		}
+
+		if (active[rule->id]) {
+
+		    if (rule->proto != proto)  {
+			printf("Fatal: build_e2_engine: protos don't match\n");
+			exit(1);
+		    }
+
+		    if (rule->features[feat] != NULL)
+			rule_map[feat][rule->id] = 1;	    
+		}
+	    }
+	}
+	
+	/* init data structures */
+	for (cnt = 1; cnt < (NUM_TREE + 1); ++cnt)
+	    used[cnt] = NULL;
+
+	for (tree = 0; tree < NUM_TREE; ++tree) {
+
+	    if (active_count[tree] == 0)
+	    	break;
+
+	    feature = determine_max_features(active, used[tree], proto, rule_count);		
+
+	    len = 4;
+	    ratio = 1.0;
+	    while ((ratio > threshold[tree]) && (len <= e2_map_size[proto])) {
+
+		used[tree+1] = determine_rules(rule_count, active, used[tree], proto, feature, len, tree);
+
+		for (cnt = 0, sum = 0; cnt < rule_count; ++cnt)
+		    if (used[tree+1][cnt])
+			++sum;
+		ratio = (float) sum / (float) active_count[tree];
+#ifdef E2DEBUG
+		printf("   %d - %f\n", len, ratio);
+#endif
+		++len;    
+
+		free(used[tree+1]);
+	    }
+ 
+	    if (ratio < threshold[tree])
+		len -= 2;
+	    else
+		len -= 1;
+
+	    used[tree+1] = determine_rules(rule_count, active, used[tree], proto, feature, len, tree);
+	    for (cnt = 0, sum = 0; cnt < rule_count; ++cnt)
+		if (used[tree+1][cnt])
+		    ++sum;
+	    ratio = (float) sum / (float) active_count[tree];
+#ifdef E2DEBUG	    
+	    printf("Tree %d (Depth = %d): %d of %d rules used - ratio = %f", tree+1, len, sum, active_count[tree], ratio);
+#endif
+	    trees[proto][tree] = perform_split(rules, used[tree+1], proto, feature, len);
+
+#ifdef E2DEBUG
+	    printf(" ... done\n");
+#endif
+	    active_count[tree+1] = (active_count[tree] - sum);
+
+
+	    for (unused = 0, cnt = 0; cnt < rule_count; ++cnt) {
+		if (!active[cnt])
+		    continue;
+		found = 0;
+		for (i = 0; i < tree+1; ++i) {
+		    if (used[i+1][cnt]) {
+			found = 1;
+			break;
+		    }
+		}
+		if (!found)
+		    ++unused;
+	    }
+#ifdef E2DEBUG
+ 	    printf("-- %d (%.2f%%) rules are always true\n\n", unused, (100*(float) unused / (float) active_count[0]));
+#endif
+
+	    /* remove reduntant checks from otn function lists */
+	    for (cnt = 0; cnt < rule_count; ++cnt) {
+		
+		/* rule has been used in tree --> remove used feature checks */
+		if (active[cnt] && used[tree+1][cnt]) {
+		   
+
+		    /* search OptTreeNode function list for relevant entry and delete it */
+		    fplist = snort_rules[cnt]->opt_func_proto[proto];
+		    if (fplist == NULL)
+			continue;
+
+		    while (fplist == snort_rules[cnt]->opt_func_proto[proto]) {
+			
+			found = 0;
+			if (fplist->feature_id < CONST_NUMBER_OF_FEATURES) {
+			    for (feat = 0; feat < len; ++ feat) {
+				if (feature[feat] == e2_index_mapping_table[proto][fplist->feature_id]) {
+				    found = 1; break;
+				}
+			    }
+			}
+			if (found) {
+			    todelete = fplist;
+			    snort_rules[cnt]->opt_func_proto[proto] = fplist->next;
+			    free(todelete);
+			    fplist = snort_rules[cnt]->opt_func_proto[proto];
+			}
+			else
+			    fplist = NULL;
+		    }
+
+		    fplist = snort_rules[cnt]->opt_func_proto[proto];
+		    while (fplist->next != NULL) {
+
+			found = 0;
+			if (fplist->next->feature_id < CONST_NUMBER_OF_FEATURES) {
+			    for (feat = 0; feat < len; ++ feat) {
+				if (feature[feat] == e2_index_mapping_table[proto][fplist->next->feature_id]) {
+				    found = 1; break;
+				}
+			    }
+			}
+			if (found) {
+			    todelete = fplist->next;
+			    fplist->next = fplist->next->next;
+			    free(todelete);
+			}
+			else
+			    fplist = fplist->next;
+		    }
+		}
+	    }	    
+	    free(feature);			
+	}
+
+	
+	/* change rule lists to trigger arrays */
+	for (tree = 0; tree < NUM_TREE; ++tree) {
+	    
+	    if (trees[proto][tree] != NULL) {
+		
+#ifdef E2_DEBUG_CLUSTER_TREE
+		trees[proto][tree]->debug(trees[proto][tree]);
+#endif
+
+		trigger = trigger_create(rule_count);
+		
+		for (cnt = 0; cnt < rule_count; ++cnt)
+		    if (active[cnt] && !used[tree+1][cnt])
+			trigger_set(trigger, (u_int16_t) cnt);
+		
+		trees[proto][tree]->propagate(trees[proto][tree], trigger, trees[proto][tree]);
+		
+		if (trees[proto][tree]->trigger != NULL)
+		    trigger_destroy(trigger);
+		else {
+		    trigger_compact(trigger);
+		    trees[proto][tree]->trigger = trigger;
+		}
+	    }
+	}
+	
+	/* deallocate data structures */
+	for (feat = 0; feat < e2_map_size[proto]; ++feat)
+	    free(rule_map[feat]);
+	free(rule_map);
+	for (tree = 0; tree < NUM_TREE; ++tree) {
+	    if (used[tree + 1] != NULL)
+		free(used[tree + 1]);
+	}
+    }
+
+#endif
+    
+    /* remove initial rules */
+    for (rule = rules; rule != NULL; ) {
+	tr = rule->next;
+	rule_destroy(rule);
+	rule = tr;
+    }
+
+    /* free remaining data structures */
+    free(used[0]);
+    free(active);
+
+    printf("+++++++++++++++++++++++++++++++++++++++++++++++++++\n");
+
+#ifdef E2DEBUG
+    _mem_print();
+#endif
+}
+     
+
+/* 
+ *  "rules" list is removed by caller and split_snort_rules is NOT allowed to
+ *  use any rules directly (i.e. w/o calling rule_clone first)
+ */
+
+E2TreeNode* split_snort_rules(E2Rule *rules, u_int8_t proto, int *feature_map)
+{
+    
+    E2Rule *rule;
+    E2TreeNode *tree_node;
+    E2SuccessorNode *succs;
+    int feature, best_feature, type, best_type, string_feature;
+    float gain, best_gain;
+    u_int32_t rule_count;
+
+    if (rules == NULL) {
+	printf("[*] Fatal: split_snort_rules: tree rule list empty\n");
+	exit(1);
+    }
+    tree_node = calloc(1, sizeof(E2TreeNode));
+
+    tree_node->debug_count = 0;
+    tree_node->debug_rules = NULL;
+
+    for (rule = rules, rule_count = 0; rule != NULL; rule = rule->next) {
+ 	++rule_count;
+
+	tree_node->debug_rules = list_add(tree_node->debug_rules, rule_clone(rule)); 
+	++tree_node->debug_count;
+    }
+
+    if (rule_count == 0) {
+	printf("No rules to be split\n");
+	exit(1);
+    }
+
+
+    // choose each feature and calculate its gain 
+    best_gain = 0.0;
+    string_feature = best_feature = best_type = -1;
+    
+    for (feature = 0; feature < e2_map_size[proto]; ++feature) {
+
+	// only check features that have not been used previously 
+	if (feature_map[feature])
+	    continue;
+	
+	// determine feature type 
+	type = e2_map[proto][feature].index_into_type_map;
+	
+#ifdef E2DEBUG
+	if ((type < 0 ) || (type >= e2_t_map_size)) {
+	    printf("Fatal: split_snort_rules: unknown feature type (%d)\n", type);
+	    exit(1);
+	}
+#endif
+
+	// pre_split node 
+	succs = e2_t_map[type].pre_split_node(rules, feature);
+
+	// determine gain int rval;
+	gain = calc_gain(rule, rule_count, succs, feature);
+
+	if (type == CONST_STRING && gain > 0.0) {
+	    string_feature = feature;
+	}
+	else if (gain > best_gain) {
+	    best_gain = gain;
+	    best_feature = feature;
+	    best_type = type;
+	}
+	else if ((best_gain == 0.0) && (best_feature < 0)) {
+
+	    for (rule = rules; rule != NULL; rule = rule->next) 
+		if (rule->features[feature] != NULL) {
+		    best_feature = feature;
+		    best_type = type;
+		    break;
+		}
+	}
+
+	// undo previous split 
+	e2_t_map[type].undo_split_node(succs);
+    }
+
+
+    // split along best feature (with highest gain) --> non-string first 
+    if ((best_feature >= 0) || (string_feature >= 0)) {  
+
+	if (best_feature < 0) {
+	    best_feature = string_feature;
+	    best_type = CONST_STRING;
+	}
+
+	// build up tree structure 
+	succs = e2_t_map[best_type].pre_split_node(rules, best_feature);
+	tree_node->private = e2_t_map[best_type].final_split_node(succs, best_feature, feature_map, proto);
+	e2_t_map[best_type].undo_split_node(succs);
+	
+	// set functions 
+	tree_node->navigate = e2_t_map[best_type].navigate;
+	tree_node->extract = e2_map[proto][best_feature].extract;
+	tree_node->debug = e2_t_map[best_type].debug;
+	tree_node->propagate = e2_t_map[best_type].propagate;	
+	tree_node->feature = best_feature;
+    }
+    // no distinguishing feature found - choose one with rules that have at least one != any
+    else  { 
+
+	for (feature = 0; feature < e2_map_size[proto]; ++feature) {
+
+	    if (feature_map[feature])
+		continue;
+
+	    for (rule = rules; rule != NULL; rule = rule->next) 
+		if (rule->features[feature] != NULL) {
+		    best_feature = feature;
+		    best_type = e2_map[proto][feature].index_into_type_map;
+		    break;
+		}
+	    
+	    if (rule != NULL) {
+
+		printf("This may never happen now\n");
+		exit(1);
+
+		// build up tree structure 
+		succs = e2_t_map[best_type].pre_split_node(rules, best_feature);
+		tree_node->private = e2_t_map[best_type].final_split_node(succs, best_feature, feature_map, proto);
+		e2_t_map[best_type].undo_split_node(succs);
+		
+		// set functions 
+		tree_node->navigate = e2_t_map[best_type].navigate;
+		tree_node->extract = e2_map[proto][best_feature].extract;
+		tree_node->debug = e2_t_map[best_type].debug;
+		tree_node->propagate = e2_t_map[best_type].propagate;	
+		tree_node->feature = best_feature;
+		break;
+	    }
+	}
+
+	/* all remaining features have only NULL entries --> immediately finish tree branch */
+	if (feature == e2_map_size[proto]) {
+	    tree_node->navigate = e2_t_map[CONST_UNDEF].navigate;
+	    tree_node->propagate = e2_t_map[CONST_UNDEF].propagate;
+	    tree_node->debug = e2_t_map[CONST_UNDEF].debug;
+	    tree_node->feature = -1;
+
+	    for (rule = rules; rule != NULL; rule = rule->next) 
+		tree_node->fire = list_add(tree_node->fire, rule_clone(rule));
+	}
+    }
+    
+    // return root of current sub_tree 
+    return tree_node;
+}
+
+
+
+
diff -Naur snort-1.8.7.orig/e2_extract.c snort-1.8.7p1/e2_extract.c
--- snort-1.8.7.orig/e2_extract.c	Wed Dec 31 16:00:00 1969
+++ snort-1.8.7p1/e2_extract.c	Fri Sep  6 07:48:17 2002
@@ -0,0 +1,246 @@
+/*
+** Copyright (C) 2002 Distributed Systems Group, Technical University Vienna
+**
+**                    Thomas Toth (ttoth@infosys.tuwien.ac.at)
+**                    Christopher Kruegel (chris@infosys.tuwien.ac.t)
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation; either version 2 of the License, or
+** (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+** GNU General Public License for more details.
+**
+** You should have received a copy of the GNU General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include "decode.h"
+#include "engine2.h"
+
+void * extract_dsize (Packet * p) {
+  u_int32_t * ptr;
+  ptr = salloc(sizeof(u_int32_t));
+
+  *ptr = (u_int32_t) p->dsize;
+
+  return ptr;
+}
+
+void * extract_ipid (Packet * p) {
+  u_int32_t * value;
+
+  value = salloc(sizeof(u_int32_t));
+
+  *value = (u_int32_t) p->iph->ip_id;
+
+  return value;
+}
+
+void * extract_ipoptions (Packet * p) {
+  u_int32_t * value;
+  int i=0;
+
+  value = salloc(sizeof(u_int32_t));
+
+  for (i=0;i<p->ip_option_count; i++) {
+    switch (p->ip_options[i].code) {
+      case IPOPT_RR:
+        *value |= 1 << CONST_INDEX_OF_RR;
+      break;
+
+      case IPOPT_EOL:
+        *value |= 1 << CONST_INDEX_OF_EOL;
+      break;
+
+      case IPOPT_NOP:
+        *value |= 1 <<CONST_INDEX_OF_NOP;
+      break;
+
+      case IPOPT_TS:
+        *value |= 1 <<CONST_INDEX_OF_TS;
+      break;
+
+      case IPOPT_SECURITY:
+        *value |= 1 <<CONST_INDEX_OF_SEC;
+      break;
+
+      case IPOPT_LSRR:
+      case IPOPT_LSRR_E:
+        *value |= 1 <<CONST_INDEX_OF_LSRR;
+      break;
+
+      case IPOPT_SATID:
+        *value |= 1 <<CONST_INDEX_OF_SATID;
+      break;
+
+      case IPOPT_SSRR:
+        *value |= 1 <<CONST_INDEX_OF_SSRR;
+      break;
+
+      case IPOPT_RTRALT:
+        FatalError("ERROR => extract_ipoptions: option IPOPT_RTRALT found, but don't know how to handle this!");
+      break;
+    }
+  }
+
+  return value;
+}
+
+void * extract_ipfragbits (Packet * p) {
+  u_int32_t * value;
+
+  value = salloc(sizeof(u_int32_t));
+
+  *value =0;
+
+  *value |= p->df << CONST_INDEX_OF_DF;
+  *value |= p->rf << CONST_INDEX_OF_R;
+  *value |= p->mf << CONST_INDEX_OF_MF;
+
+  return value;
+}
+
+void * extract_tcpflags (Packet * p) {
+  u_int32_t * value;
+
+  value = salloc(sizeof(u_int32_t));
+
+  *value =0;
+  *value |= p->tcph->th_flags;
+
+  return value;
+}
+
+void * extract_icmp_id (Packet * p) {
+  u_int32_t * ptr;
+  ptr = salloc(sizeof(u_int32_t));
+
+  if (p->icmph->type == ICMP_ECHO || p->icmph->type == ICMP_ECHOREPLY) {
+    *ptr = htonl((u_int32_t) p->icmph->s_icmp_id);
+  }
+  else {
+    return NULL;
+  }
+  return ptr;
+}
+
+void * extract_icmp_seq (Packet * p) {
+  u_int32_t * ptr;
+  ptr = salloc(sizeof(u_int32_t));
+
+  if (p->icmph->type == ICMP_ECHO || p->icmph->type == ICMP_ECHOREPLY) {
+    *ptr = htonl((u_int32_t) p->icmph->s_icmp_id);
+  }
+  else {
+    return NULL;
+  }
+  return ptr;
+}
+
+void * extract_icode (Packet * p) {
+  u_int32_t * ptr;
+  ptr = salloc(sizeof(u_int32_t));
+
+  if (!p->icmph)
+    return NULL;
+
+  *ptr = (u_int32_t) p->icmph->code;
+
+  return ptr;
+}
+
+void * extract_itype (Packet * p) {
+  u_int32_t * ptr;
+  ptr = salloc(sizeof(u_int32_t));
+
+  if (!p->icmph)
+    return NULL;
+
+  *ptr = (u_int32_t) p->icmph->type;
+
+  return ptr;
+}
+
+void * extract_srcip (Packet * p) {
+  u_int32_t * ptr;
+
+  ptr = salloc(sizeof(u_int32_t));
+  *ptr = htonl(p->iph->ip_src.s_addr);
+
+  return ptr;
+}
+
+void * extract_dstip (Packet * p) {
+  u_int32_t * ptr;
+
+  ptr = salloc(sizeof(u_int32_t));
+  *ptr = htonl(p->iph->ip_dst.s_addr);
+
+  return ptr;
+}
+
+void * extract_udp_srcport (Packet * p) {
+  u_int32_t * ptr;
+
+  ptr = salloc(sizeof(u_int32_t));
+  *ptr = htons(p->udph->uh_sport);
+
+  return ptr;
+}
+
+void * extract_tcp_srcport (Packet * p) {
+  u_int32_t * ptr;
+
+  ptr = salloc(sizeof(u_int32_t));
+  *ptr = htons(p->tcph->th_sport);
+
+  return ptr;
+}
+
+void * extract_udp_dstport (Packet * p) {
+  u_int32_t * ptr;
+  ptr = salloc(sizeof(u_int32_t));
+
+  *ptr = htons(p->udph->uh_dport);
+  return ptr;
+}
+
+void * extract_tcp_dstport (Packet * p) {
+  u_int32_t * ptr;
+  ptr = salloc(sizeof(u_int32_t));
+
+  *ptr = htons(p->tcph->th_dport);
+  return ptr;
+}
+
+void * extract_content ( Packet * p) {
+  E2PAYLOAD * ret;
+
+  ret = (E2PAYLOAD *) salloc(sizeof(E2PAYLOAD));
+
+  ret->content = p->data;
+  ret->content_length = p->dsize;
+
+  return ret;
+}
+
+void * extract_uricontent ( Packet * p) {
+  E2PAYLOAD * ret;
+
+  if (p->URI.uri != NULL) {
+      ret = (E2PAYLOAD *) salloc(sizeof(E2PAYLOAD));
+
+      ret->content = p->URI.uri;
+      ret->content_length = p->URI.length;
+      
+      return ret;  
+  }
+  else {
+      return extract_content(p);
+  }
+}
diff -Naur snort-1.8.7.orig/e2_feature_flags.c snort-1.8.7p1/e2_feature_flags.c
--- snort-1.8.7.orig/e2_feature_flags.c	Wed Dec 31 16:00:00 1969
+++ snort-1.8.7p1/e2_feature_flags.c	Fri Sep  6 07:48:35 2002
@@ -0,0 +1,1282 @@
+/*
+** Copyright (C) 2002 Distributed Systems Group, Technical University Vienna
+**
+**                    Thomas Toth (ttoth@infosys.tuwien.ac.at)
+**                    Christopher Kruegel (chris@infosys.tuwien.ac.t)
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation; either version 2 of the License, or
+** (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+** GNU General Public License for more details.
+**
+** You should have received a copy of the GNU General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include "engine2.h"
+
+
+char *ipoptions_names [CONST_NUMBER_OF_SUPPORTED_IPOPTIONS] =
+  { "<record route>","<end of list>","<no operation>","<time stamp>","<ip security option",
+    "<loose source routing>","<strict source routing>","<stream identifier>"};
+
+
+
+char *frag_flag_names [CONST_NUMBER_OF_SUPPORTED_FRAGFLAGS] =
+  { "<reserved>","<don't fragment>","<more fragments>"};
+
+
+
+char *tcp_flag_names [CONST_NUMBER_OF_SUPPORTED_TCPFLAGS] =
+      {"FIN", "SYN", "RST", "PSH", "ACK", "URG", "reserved2", "reserved1","reserved0"};
+
+
+/******************************************************************************/
+/*                     Internal Variables                                     */             
+/******************************************************************************/
+
+typedef struct e2_feature_flags {
+    u_int32_t value;
+    u_int32_t mask;
+
+    u_int8_t length;
+    u_int8_t op;
+} E2FFlags;
+
+struct _flag_list;
+
+typedef struct _link {
+	struct _flag_list *to;
+	int weight;
+
+	struct _link *next;
+} Link;
+
+struct _jump_tree;
+
+typedef struct _flag_list {
+#ifdef E2DEBUG
+    int uid;
+#endif
+    u_int8_t marked;
+    
+    E2FFlags *flags;
+    
+    E2Rule *rules;
+    E2Rule *final_rules;
+    u_int32_t rule_count;
+    
+    struct _flag_list *next;
+    
+    Link *links;
+    u_int32_t in_edges;
+
+    struct _jump_tree *tree_node;
+} FlagList;
+
+struct _jump_tree;
+
+typedef struct _jump_tree_link {
+    struct _jump_tree *to;
+    struct _jump_tree_link *next;
+} JumpTreeLink;
+
+typedef struct _jump_tree {
+    u_int32_t value;
+    u_int32_t mask;
+    E2TreeNode *follow;
+    JumpTreeLink *links;
+    u_int8_t marked;
+} JumpTree;
+
+
+#ifdef E2DEBUG
+int flags_uid;
+#endif
+
+/******************** Supported operators *************************************/
+
+#define NOT_EQUAL 1
+#define SUB_FLAGS 2
+#define SUPER_FLAGS 3
+#define PARENT 4
+
+#define DEFAULT_FLAG_LENGTH 8
+
+/******************************************************************************/
+/*                     Internal Functions                                     */             
+/******************************************************************************/
+
+FlagList *add_flags_to_list(FlagList *list, E2Rule *rule, E2FFlags *flags)
+{
+	FlagList *tfl;
+
+	for (tfl = list; tfl != NULL; tfl = tfl->next) {	
+		if ((tfl->flags->mask == flags->mask) && (tfl->flags->value == flags->value)) {
+			tfl->rules = list_add(tfl->rules, rule);
+			return list;
+		}
+	}
+
+	tfl = calloc(1, sizeof(FlagList));
+#ifdef E2DEBUG
+	tfl->uid = ++flags_uid;	
+#endif
+	tfl->flags = calloc(1, sizeof(E2FFlags));
+	memcpy(tfl->flags, flags, sizeof(E2FFlags));
+
+	tfl->rules = rule;
+	tfl->next = list;
+	return tfl;
+}		
+
+FlagList *insert_new_flags(FlagList *list, FlagList *append, E2FFlags *flags)
+{
+	FlagList *tfl;
+
+	for (tfl = list; tfl != NULL; tfl = tfl->next) {
+		
+	    if ((tfl->flags->mask == flags->mask) && (tfl->flags->value == flags->value)) {
+		free(flags);
+		return tfl;
+	    }
+	}
+
+	tfl = calloc(1, sizeof(FlagList));
+#ifdef E2DEBUG
+	tfl->uid = ++flags_uid;
+#endif
+	tfl->flags = flags;
+
+	tfl->next = append->next;
+	append->next = tfl;
+
+	return tfl;
+}		
+
+FlagList *get_last(FlagList *list)
+{
+	FlagList *last;
+	
+	if (list == NULL)
+		return NULL;
+	else {
+		for (last = list; last->next != NULL; last = last->next);	
+		return last;
+	}
+}
+
+void make_flags_link(FlagList *src, FlagList *dst)
+{
+	Link *tl, *edge;
+	int cnt, src_mask_len = 0, dst_mask_len = 0;
+
+	for (cnt = 0; cnt < src->flags->length; ++cnt)
+		if ((src->flags->mask >> cnt) & 0x01)
+			++src_mask_len;
+
+	for (cnt = 0; cnt < dst->flags->length; ++cnt)
+		if ((dst->flags->mask >> cnt) & 0x01)
+			++dst_mask_len;
+
+	++dst->in_edges;
+
+	tl = calloc(1, sizeof(Link));
+	tl->to = dst;
+	tl->weight = (dst_mask_len - src_mask_len);
+
+	if (tl->weight <= 0) {
+#ifdef E2DEBUG
+		printf("Fatal: make_flags_link: weight of link (%d -> %d) <= 0\n", src->uid, dst->uid);
+#else
+		printf("Fatal: make_flags_link: weight of link <= 0\n");
+#endif
+		exit(1);
+	}
+
+	if ((src->links == NULL) || (tl->weight <= src->links->weight)) {
+		tl->next = src->links;
+		src->links = tl;
+	}
+	else {
+		edge = src->links; 
+		while ((edge->next != NULL) && (tl->weight > edge->next->weight))
+			edge = edge->next;
+		tl->next = edge->next;
+		edge->next = tl;
+	}
+}
+
+int cover_mask(E2FFlags *f1, E2FFlags *f2)
+{
+	return !(~f1->mask & f2->mask);
+}
+
+E2FFlags *unify(E2FFlags *f1, E2FFlags *f2, int *result)
+{
+	E2FFlags *parent;
+	u_int32_t common_mask;
+	u_int32_t common_value;
+
+	if (f1->mask == f2->mask) {
+		
+		if (f1->value == f2->value) {
+	
+			printf("e2_feature_flags: unify: flags are equal when they should not!");
+			exit(1);
+		}
+		else {
+			*result = NOT_EQUAL;
+			return NULL;
+		}
+	}
+	else if (cover_mask(f1, f2) && ((f1->value & f2->mask) == f2->value)) {
+		*result = SUPER_FLAGS;
+		return NULL;
+	}
+	else if (cover_mask(f2, f1) && ((f2->value & f1->mask) == f1->value)) {
+		*result = SUB_FLAGS;
+		return NULL;
+	}
+	else {
+
+		common_mask = (f1->mask | f2->mask);
+		common_value = (f1->value | f2->value);
+
+		if (((common_value & f1->mask) != (f1->value)) ||
+			((common_value & f2->mask) != (f2->value))) {
+
+			*result = NOT_EQUAL;
+			return NULL;
+		}
+
+		parent = calloc(1, sizeof(E2FFlags));
+
+		parent->value = common_value;
+		parent->mask = common_mask;
+		parent->length = f1->length;
+		parent->op = CONST_NOP;
+
+		*result = PARENT;
+		return parent;
+	}
+}
+
+void build_flags_graph(FlagList *list)
+{
+    FlagList *lptr, *last, *tfl, *obj;
+    E2FFlags *parent;
+    int result;
+	
+    if (list == NULL)
+	return;
+    
+    last = get_last(list);
+    
+    for (lptr = list; lptr != NULL; lptr = lptr->next) {
+	
+	for (tfl = lptr->next; tfl != last->next; tfl = tfl->next) {
+	    
+	    parent = unify(lptr->flags, tfl->flags, &result);
+	    
+	    switch (result) {
+		case NOT_EQUAL:
+		    break;
+
+		case SUB_FLAGS:
+		    make_flags_link(lptr, tfl);
+		    break;
+		    
+		case SUPER_FLAGS:
+		    make_flags_link(tfl, lptr);
+		    break;
+		    
+		case PARENT:
+		    obj = insert_new_flags(list, last, parent);
+		    make_flags_link(lptr, obj);
+		    make_flags_link(tfl, obj);
+		    break;
+	    }
+	}
+	
+	last = get_last(list);
+    }
+}
+
+void mark_flags_reachable(FlagList *node)
+{
+    Link *link;
+
+    if ((node != NULL) && (!node->marked)) {
+
+	node->marked = 1;
+	for (link = node->links; link != NULL; link = link->next)
+	    mark_flags_reachable(link->to);
+    }
+}
+
+
+void clear_flags_reachable(FlagList *node)
+{
+    for (; node != NULL; node = node->next)
+	node->marked = 0;
+}
+
+void compact_edges(FlagList *list)
+{
+    FlagList *tfl;
+    Link *link, *tl, *to_free;
+    
+    for (tfl = list; tfl != NULL; tfl = tfl->next) {
+	for (link = tfl->links; link != NULL; link = link->next) {
+
+	    mark_flags_reachable(link->to);
+	    for (tl = link; tl->next != NULL; ) {
+		if (tl->next->to->marked) {
+		    to_free = tl->next;
+		    tl->next = tl->next->next;
+		    free(to_free);
+		}
+		else
+		    tl = tl->next;
+	    }
+	    clear_flags_reachable(list);
+	}
+    }
+}
+
+E2SuccessorNode *build_flags_list(FlagList *flag_list, E2Rule *any_list)
+{
+    E2SuccessorNode *first, *last, *tmp;
+    FlagList *tl;
+
+    first = last = NULL;
+    
+    for (tl = flag_list; tl != NULL; tl = tl->next) {
+
+	tmp = calloc(1, sizeof(E2SuccessorNode));
+				
+	tmp->rules = tl->final_rules;
+	tmp->rule_count = tl->rule_count;
+	
+	if (first == NULL) {			
+	    first = last = tmp;
+	}
+	else {			
+	    last->next = tmp;
+	    last = tmp;
+	}
+    }
+    return first;
+}
+
+void flood_flags_rules(FlagList *flag_list, E2Rule *rules)
+{
+    E2Rule *tr;
+    Link *link;
+
+    if (flag_list->marked)
+	return;
+
+    flag_list->marked = 1;
+
+    for (tr = rules; tr != NULL; tr = tr->next) {
+	flag_list->final_rules = list_add(flag_list->final_rules, rule_clone(tr));
+	++flag_list->rule_count;
+    }
+
+    for (link = flag_list->links; link != NULL; link = link->next)
+	flood_flags_rules(link->to, rules);
+}
+
+void move_flags_rules(FlagList *flag_list, E2Rule *any_list)
+{
+    FlagList *tfl;
+    E2Rule *rule, *tr1, *tr2;
+
+    for (tfl = flag_list; tfl != NULL; tfl = tfl->next) {
+
+	for (rule = any_list; rule != NULL; rule = rule->next) {
+	    tfl->final_rules = list_add(tfl->final_rules, rule_clone(rule));
+	    ++tfl->rule_count;
+	}
+
+	if (tfl->rules != NULL) {
+	    flood_flags_rules(tfl, tfl->rules);
+	    clear_flags_reachable(flag_list);
+
+	    for (tr1 = tfl->rules; tr1 != NULL; ) {
+		tr2 = tr1->next;
+		rule_destroy(tr1);
+		tr1 = tr2;
+	    }
+	    tfl->rules = NULL;
+	}
+    }
+}
+ 
+
+JumpTree* build_jmp_graph(FlagList *flag_list, int *feature_map, u_int8_t proto)
+{
+    JumpTree *node;
+    Link *link;
+    JumpTreeLink *tree_link;
+
+    node = calloc(1, sizeof(JumpTree));
+    
+    flag_list->marked = 1;
+    flag_list->tree_node = node;
+
+    node->value = flag_list->flags->value;
+    node->mask = flag_list->flags->mask;
+
+    if (flag_list->final_rules != NULL)
+	node->follow = split_snort_rules(flag_list->final_rules, proto, feature_map);
+    else
+	node->follow = NULL;
+
+    for (link = flag_list->links; link != NULL; link = link->next) {
+
+	tree_link = calloc(1, sizeof(JumpTreeLink));
+
+	if (link->to->marked) {
+	    tree_link->to = link->to->tree_node;
+	    if (tree_link->to == NULL) {
+		printf("Fatal: build_jmp_graph: element not found that must be there\n");
+		exit(1);
+	    }
+	}
+	else
+	    tree_link->to = build_jmp_graph(link->to, feature_map, proto);
+
+	tree_link->next = node->links;
+	node->links = tree_link;
+    }
+
+    return node;
+}
+
+
+void debug_flags_tree(JumpTree *node, int mode)
+{
+    int cnt;
+    JumpTreeLink *link;
+
+    if ((node == NULL) || (node->marked > mode))
+	return;
+    
+    ++node->marked;
+
+    if (mode == 0) {
+	printf("      %p ( -> %p): ", node, node->follow);
+	for (cnt = 1; cnt <= DEFAULT_FLAG_LENGTH; ++cnt) {
+	    if (node->mask & (1 << (DEFAULT_FLAG_LENGTH - cnt))) 
+		printf("%d", (node->value & (1 << (DEFAULT_FLAG_LENGTH - cnt))) ? 1 : 0);
+	    else
+		printf("x");
+	}
+	printf(": ");
+	for (link = node->links; link != NULL; link = link->next)
+	    printf("%p ", link->to);
+	printf("\n");
+    }  
+    else 
+	node->follow->debug(node->follow);
+    
+    for (link = node->links; link != NULL; link = link->next)
+	debug_flags_tree(link->to, mode);
+}
+
+void flags_propagate_tree(JumpTree *node, E2Trigger *trigger, int mode, void *caller)
+{
+    JumpTreeLink *link;
+
+    if ((node == NULL) || (node->marked > mode))
+	return;
+    
+    ++node->marked;
+    
+    if (node->follow != NULL) 
+	node->follow->propagate(node->follow, trigger, caller);
+    
+    for (link = node->links; link != NULL; link = link->next)
+	flags_propagate_tree(link->to, trigger, mode, caller);
+}
+
+E2TreeNode* find_next(JumpTree *node, u_int32_t flags)
+{
+    JumpTreeLink *link; 
+    
+    for (link = node->links; link != NULL; link = link->next)
+	if ((flags & link->to->mask) == link->to->value)
+	    return find_next(link->to, flags);
+    
+    return node->follow;
+}
+
+#ifdef E2DEBUG
+
+void debug_flag_list(FlagList *flag_list)
+{
+    FlagList *tfl;
+    E2FFlags *flags;
+    E2Rule *tr;
+    int cnt;
+    Link *l;
+    
+    for (tfl = flag_list; tfl != NULL; tfl = tfl->next) {
+	
+	flags = tfl->flags;
+	
+	printf("%d: ", tfl->uid);
+	
+	for (cnt = 1; cnt <= flags->length; ++cnt) {
+	    
+	    if (flags->mask & (1 << (flags->length - cnt))) 
+		printf("%d", (flags->value & (1 << (flags->length - cnt))) ? 1 : 0);
+	    else
+		printf("x");
+	}
+
+	printf(" - ");
+	for (l = tfl->links; l != NULL; l = l->next)
+	    printf("%d ", l->to->uid);
+	
+	printf(" --->  ");
+	for (tr = tfl->final_rules; tr != NULL; tr = tr->next)
+	    printf("%d ", tr->id);
+	printf("= %d ", tfl->rule_count);
+	printf("\n");
+    }
+}
+
+#endif
+
+/******************************************************************************/
+/*                            API Functions                                   */             
+/******************************************************************************/
+
+
+/**************************** Decision Tree Building **************************/
+
+E2SuccessorNode* flags_pre_split_node(E2Rule *rules, u_int8_t feat)
+{
+    FlagList *flag_list;
+    E2Rule *any_list, *current_rule;
+    E2Feature *feature;
+    E2SuccessorNode *result;
+    u_int32_t any_length;
+
+#ifdef E2DEBUG
+    flags_uid = 0;
+#endif
+
+    flag_list = NULL;
+    any_list = NULL;
+    any_length = 0;
+    
+    current_rule = rules;
+    while (current_rule != NULL) {
+	
+	feature = current_rule->features[feat];
+	
+	if (feature == NULL) {
+	    any_list = list_add(any_list, rule_clone(current_rule));
+	    ++any_length;
+	}
+	else 
+	    flag_list = add_flags_to_list(flag_list, rule_clone(current_rule), (E2FFlags *) feature->f_val);
+	
+	current_rule = current_rule->next;	
+    }
+
+    build_flags_graph(flag_list);
+    compact_edges(flag_list);
+
+    /* push rules to leafs */
+    move_flags_rules(flag_list, any_list);
+    
+#ifdef E2DEBUG
+    // debug_flag_list(flag_list);
+#endif
+
+    /* set up the node holding the any_list rules */
+    result = calloc(1, sizeof(E2SuccessorNode));
+    result->rules = any_list;
+    result->rule_count = any_length;
+    
+    /* determine successor classes and add any_list to each firing node */
+    result->next = build_flags_list(flag_list, any_list);
+    if (result->next != NULL)
+	result->next->private = (void *) flag_list;
+    
+    return result;
+}
+
+
+void flags_undo_split_node(E2SuccessorNode *succs)
+{
+    E2Rule *tr1, *tr2; 
+    E2SuccessorNode *tsn1, *tsn2;
+    FlagList *tfl1, *tfl2;
+    Link *tl1, *tl2;
+
+    /* kill list elements */
+    if (succs->next) {
+	
+	for (tfl1 = (FlagList *) succs->next->private; tfl1 != NULL; ) {
+	    tfl2 = tfl1->next;
+	    
+	    for (tl1 = tfl1->links; tl1 != NULL; ) {
+		tl2 = tl1->next;
+		free(tl1);
+		tl1 = tl2;
+	    }
+
+	    free(tfl1->flags);
+	    free(tfl1);
+	    tfl1 = tfl2;
+	}
+    }
+   
+    /* remove successor list with rules */
+    for (tsn1 = succs; tsn1 != NULL; ) {
+
+	/* first the rules */
+	for (tr1 = tsn1->rules; tr1 != NULL; ) {
+	    tr2 = tr1->next;
+	    rule_destroy(tr1);
+	    tr1 = tr2;
+	}
+
+	/* then the successor node */
+	tsn2 = tsn1->next;
+	free(tsn1);
+	tsn1 = tsn2;
+    }
+}
+
+void* flags_final_split_node(E2SuccessorNode *succs, u_int8_t feat, int *feature_map, u_int8_t proto)
+{
+    JumpTree *result;
+    FlagList *root, *flag_list;
+    Link *link, *tl1, *tl2;
+
+    /* remove feature from further decision process */
+    feature_map[feat] = 1;
+
+    /* create decision data - root node of tree for any_list */
+    root = calloc(1, sizeof(FlagList));
+    root->final_rules = succs->rules;
+
+    /* keep flag value and mask zero to have all packets match */
+    root->flags = calloc(1, sizeof(E2FFlags));
+    root->flags->value = root->flags->mask = 0;
+
+    /* find all nodes with no incoming edges and add them to the links of root node */
+    if ((succs->next != NULL) && (succs->next->private)) {
+
+	flag_list = (FlagList *) succs->next->private;
+
+	for (; flag_list != NULL; flag_list = flag_list->next) {
+	    if (flag_list->in_edges == 0) {
+		link = calloc(1, sizeof(Link));
+		link->to = flag_list;
+		link->next = root->links;
+		root->links = link;
+	    }
+	}
+    }
+
+    result = build_jmp_graph(root, feature_map, proto);
+
+    /* put feature back into decision process */
+    feature_map[feat] = 0;
+
+    /* delete artifical root node */
+    free(root->flags);
+    for (tl1 = root->links; tl1 != NULL; ) {
+	tl2 = tl1->next;
+	free(tl1);
+	tl1 = tl2;
+    }
+    free(root);
+
+    return (void *) result;
+}
+
+void flags_propagate(E2TreeNode *node, E2Trigger *t, void *caller)
+{
+    E2Trigger *trigger;
+    E2Rule *rule, *tr;
+    JumpTree *root;
+
+#ifdef E2DEBUG
+    if (node->visited) {
+	printf("Fatal: flags_propagate: %p called twice - this time by %p\n", node, caller);
+	exit(1);
+    }
+    else 
+	node->visited = 1;
+#endif
+
+    root = (JumpTree *) node->private;
+    trigger = trigger_clone(t);
+    for (rule = node->fire; rule != NULL; rule = rule->next) {
+	printf("Error - flags\n");
+	exit(1);
+	trigger_set(trigger, rule->id);
+    }
+
+    flags_propagate_tree(root, trigger, 0, (void *) node);
+  
+    // trigger_destroy(trigger);
+    trigger_compact(trigger);
+    node->trigger = trigger;
+
+    for (rule = node->fire; rule != NULL; ) {
+      tr = rule->next;
+      rule_destroy(rule);
+      rule = tr;
+    }
+}
+
+/**************************** Decision Tree Navigation ************************/
+
+E2Trigger * flags_navigate(E2TreeNode *node, Packet *p)
+{
+    JumpTree *root;
+    E2TreeNode *next;
+    u_int32_t *fptr, fvalue;
+
+    fptr = (u_int32_t *) node->extract(p);
+
+    if (fptr != NULL) {
+	fvalue = *fptr;
+	free(fptr);
+    }
+    else
+	fvalue = 0;
+    
+    root = (JumpTree *) node->private;
+
+#ifdef E2DEBUG
+    if (root == NULL) {
+	printf("Fatal: flags_navigate: root is null\n");
+	exit(1);
+    }
+#endif
+
+    if ((fptr == NULL) && (root->follow != NULL))
+	return root->follow->navigate(root->follow, p);
+    else if ((next = find_next(root, fvalue)) != NULL)
+	return next->navigate(next, p);
+    else
+	return NULL;
+}
+
+
+E2FFlags * create_flags(u_int32_t value, u_int32_t mask, u_int8_t length, u_int8_t op) {
+
+  E2FFlags * flags = NULL;
+
+  flags = (E2FFlags *) salloc(sizeof(E2FFlags));
+
+  flags->value   = value;
+  flags->mask    = mask;
+  flags->length  = length;
+  flags->op      = op;
+
+  return flags;
+}
+
+/**************************** Parse Functions *********************************/
+
+
+#define CONST_INDEX_OF_R 0
+#define CONST_INDEX_OF_DF 1
+#define CONST_INDEX_OF_MF 2
+
+E2Alternative * create_frag_alternative (E2Alternative * list, int feature_type, u_int32_t value, u_int32_t mask, u_int8_t op, u_int8_t length, int id) {
+  E2FFlags      * flags = NULL;
+  E2Feature    * feat  = NULL;
+  E2Alternative * alt   = NULL;
+
+  flags  = create_flags( value, mask, length , op );
+  feat   = new_feature( feature_type, id , flags );
+  alt    = new_alternative(feat);
+
+  list = insert_alternative(list, alt, 0);
+
+  return list;
+}
+
+E2Alternative * ip_fragbits_parse(unsigned char * data,int id, u_int8_t proto) {
+
+    E2Alternative * list  = NULL;
+    u_int32_t value       = 0;
+    u_int32_t mask        = 0;
+    u_int8_t length       = CONST_NUMBER_OF_SUPPORTED_FRAGFLAGS;
+    u_int8_t end_reached  = 0;
+    u_int8_t operator     = CONST_EXACT;
+    int i;
+
+    // Parse away trailing spaces
+    while (*data==' ')
+      data++;
+
+    // Now the content starts
+    while (*data != 0) {
+      switch (*data) {
+        case 'r':
+        case 'R':
+          if (end_reached)
+            FatalError("ERROR => ip_fragbits_parse: encountered option %s, but bits after + or * are not allowed.", data);
+          value = value | (1<< CONST_INDEX_OF_R) ;
+        break;
+
+        case 'd':
+        case 'D':
+          if (end_reached)
+            FatalError("ERROR => ip_fragbits_parse: encountered option %s, but bits after + or * are not allowed.", data);
+          value = value | (1<< CONST_INDEX_OF_DF) ;
+        break;
+
+        case 'm':
+        case 'M':
+          if (end_reached)
+            FatalError("ERROR => ip_fragbits_parse: encountered option %s, but bits after + or * are not allowed.", data);
+          value = value | (1<< CONST_INDEX_OF_MF) ;
+        break;
+
+        case '*':
+          // Split here
+          if (end_reached)
+            FatalError("ERROR => ip_fragbits_parse: encountered option %s, but bits after + or * are not allowed.", data);
+          operator = CONST_ANY;
+          end_reached=1;
+        break;
+
+        case '+':
+          if (end_reached)
+            FatalError("ERROR => ip_fragbits_parse: encountered option %s, but bits after + or * are not allowed.", data);
+          mask = value;
+          operator = CONST_ALL;
+          end_reached=1;
+        break;
+
+        case '!':
+          if (end_reached)
+            FatalError("ERROR => ip_fragbits_parse: encountered option %s, but bits after + or * are not allowed.", data);
+          mask = value;
+          operator = CONST_NOT;
+          end_reached=1;
+        break;
+
+        case ' ':
+        break;
+      }
+      data++;
+    }
+
+    switch(operator) {
+      case CONST_NOT:
+        mask = value;
+        value =0;
+        list = create_frag_alternative ( list, CONST_IPFRAG, value, mask, operator, length, id);
+      break;
+
+      case CONST_ALL:
+        mask = value;
+        list = create_frag_alternative ( list, CONST_IPFRAG, value, mask, operator, length, id);
+      break;
+
+      case CONST_ANY:
+        // Iterate through all set bits
+        for (i=0; i < CONST_NUMBER_OF_SUPPORTED_FRAGFLAGS; i++) {
+
+          // if bit is set 1 => set exactly one bit in the mask and the value
+          if (value & (1<<i)) {
+            mask = 1<<i;
+            list = create_frag_alternative ( list, CONST_IPFRAG, mask, mask, operator, length, id);
+          }
+        }
+
+      break;
+
+      case CONST_EXACT:
+        mask=0;
+        for (i=0; i < CONST_NUMBER_OF_SUPPORTED_FRAGFLAGS; i++) {
+          mask |= 1<<i;
+        }
+        // Create alternatives
+        list = create_frag_alternative ( list, CONST_IPFRAG, value, mask, operator, length, id);
+      break;
+
+      default:
+        FatalError("ERROR => ip_fragbits_parse: unknown operator while determining mask.");
+    }
+    return list;
+}
+
+E2Alternative * tcp_flags_parse(unsigned char * data,int id, u_int8_t proto) {
+
+    E2Alternative * list  = NULL;
+    u_int32_t value       = 0;
+    u_int32_t mask        = 0;
+    u_int8_t length       = CONST_NUMBER_OF_SUPPORTED_TCPFLAGS;
+    u_int8_t end_reached  = 0;
+    u_int8_t operator     = CONST_EXACT;
+    int i;
+
+    // Parse away trailing spaces
+    while (*data==' ')
+      data++;
+
+    // Now the content starts
+    while (*data != 0) {
+      switch (*data) {
+        case 'f':
+        case 'F':
+          if (end_reached)
+            FatalError("ERROR => tcp_fragbits_parse: encountered option %s, but bits after + or * are not allowed.", data);
+          value = value | (1<< CONST_INDEX_OF_FIN) ;
+        break;
+
+        case 's':
+        case 'S':
+          if (end_reached)
+            FatalError("ERROR => tcp_fragbits_parse: encountered option %s, but bits after + or * are not allowed.", data);
+          value = value | (1<< CONST_INDEX_OF_SYN) ;
+        break;
+
+        case 'r':
+        case 'R':
+          if (end_reached)
+            FatalError("ERROR => tcp_fragbits_parse: encountered option %s, but bits after + or * are not allowed.", data);
+          value = value | (1<< CONST_INDEX_OF_RST) ;
+        break;
+
+        case 'p':
+        case 'P':
+          if (end_reached)
+            FatalError("ERROR => tcp_fragbits_parse: encountered option %s, but bits after + or * are not allowed.", data);
+          value = value | (1<< CONST_INDEX_OF_PSH) ;
+        break;
+
+        case 'a':
+        case 'A':
+          if (end_reached)
+            FatalError("ERROR => tcp_fragbits_parse: encountered option %s, but bits after + or * are not allowed.", data);
+          value = value | (1<< CONST_INDEX_OF_ACK) ;
+        break;
+
+        case 'u':
+        case 'U':
+          if (end_reached)
+            FatalError("ERROR => tcp_fragbits_parse: encountered option %s, but bits after + or * are not allowed.", data);
+          value = value | (1<< CONST_INDEX_OF_URG) ;
+        break;
+
+        case '2':
+          if (end_reached)
+            FatalError("ERROR => tcp_fragbits_parse: encountered option %s, but bits after + or * are not allowed.", data);
+          value = value | (1<< CONST_INDEX_OF_2) ;
+        break;
+
+        case '1':
+          if (end_reached)
+            FatalError("ERROR => tcp_fragbits_parse: encountered option %s, but bits after + or * are not allowed.", data);
+          value = value | (1<< CONST_INDEX_OF_1) ;
+        break;
+
+        case '0':
+          if (end_reached)
+            FatalError("ERROR => tcp_fragbits_parse: encountered option %s, but bits after + or * are not allowed.", data);
+          value = 0 ;
+        break;
+
+
+        case '*':
+          // Perform a split in this case
+          if (end_reached)
+            FatalError("ERROR => tcp_fragbits_parse: encountered option %s, but bits after + or * are not allowed.", data);
+          operator = CONST_ANY;
+          end_reached=1;
+        break;
+
+        case '+':
+          if (end_reached)
+            FatalError("ERROR => tcp_fragbits_parse: encountered option %s, but bits after + or * are not allowed.", data);
+          mask = value;
+          operator = CONST_ALL;
+          end_reached=1;
+        break;
+
+        case '!':
+          if (end_reached)
+            FatalError("ERROR => tcp_fragbits_parse: encountered option %s, but bits after + or * are not allowed.", data);
+          operator = CONST_NOT;
+          end_reached=1;
+        break;
+
+        case ' ':
+        break;
+      }
+      data++;
+    }
+
+    switch(operator) {
+      case CONST_NOT:
+        mask = value;
+        value =0;
+        list = create_frag_alternative ( list, CONST_TCPFLAGS, value, mask, operator, length, id);
+      break;
+
+      case CONST_ALL:
+        mask = value;
+        list = create_frag_alternative ( list, CONST_TCPFLAGS, value, mask, operator, length, id);
+      break;
+
+      case CONST_ANY:
+        // Iterate through all set bits
+        for (i=0; i < length; i++) {
+
+          // if bit is set 1 => set exactly one bit in the mask and the value
+          if (value & (1<<i)) {
+            mask = 1<<i;
+            list = create_frag_alternative ( list, CONST_TCPFLAGS, mask, mask, operator, length, id);
+          }
+        }
+
+      break;
+
+      case CONST_EXACT:
+        mask=0;
+        for (i=0; i < length; i++) {
+          mask |= 1<<i;
+        }
+        // Create alternatives
+        list = create_frag_alternative ( list, CONST_TCPFLAGS, value, mask, operator, length, id);
+      break;
+
+      default:
+        FatalError("ERROR => tcp_fragbits_parse: unknown operator while determining mask.");
+    }
+
+    return list;
+}
+
+E2Alternative * ip_option_parse(unsigned char * data,int id, u_int8_t proto)
+{
+    E2Alternative * alt   = NULL;
+    u_int32_t value       = 0;
+    u_int32_t mask        = 0;
+    u_int8_t length       = CONST_NUMBER_OF_SUPPORTED_IPOPTIONS;
+    u_int8_t op           = CONST_EXACT;;
+
+    // Parse away trailing spaces
+   while (*data==' ')
+      data++;
+
+    // Now the content starts
+    if (!strncasecmp(data,"rr",3)) {
+      // set the record route flag
+      mask = value =  1<<CONST_INDEX_OF_RR;
+    }
+    else
+    if (!strncasecmp(data,"eol",3)) {
+      // set the end of list flag
+      mask = value =  1<<CONST_INDEX_OF_EOL;
+    }
+    else
+    if (!strncasecmp(data,"nop",3)) {
+      // set the nop flag
+      mask = value =  1<<CONST_INDEX_OF_NOP;
+    }
+    else
+    if (!strncasecmp(data,"ts",3)) {
+      // set the time stamp flag
+      mask = value =  1<<CONST_INDEX_OF_TS;
+    }
+    else
+    if (!strncasecmp(data,"sec",3)) {
+      // set the ip security option
+      mask = value =  1<<CONST_INDEX_OF_SEC;
+    }
+    else
+    if (!strncasecmp(data,"lsrr",3)) {
+      // set the loose source routing flag
+      mask = value =  1<<CONST_INDEX_OF_LSRR;
+    }
+    else
+    if (!strncasecmp(data,"ssrr",3)) {
+      // set the strict source routing flag
+      mask = value =  1<<CONST_INDEX_OF_SSRR;
+    }
+    else
+    if (!strncasecmp(data,"satid",3)) {
+      // set the stream identifier flag
+      mask = value =  1<<CONST_INDEX_OF_SATID;
+    }
+    else
+      FatalError("ERROR => ip_flags_parse: flags %s are unknown / not supported.",data);
+
+    alt = create_frag_alternative ( NULL, CONST_IPOPTION, value, mask, op, length, id);
+    return alt;
+}
+
+void print_op(u_int8_t operator) {
+  switch (operator) {
+    case CONST_EXACT:
+    break;
+
+    case CONST_ALL:
+      printf("+");
+    break;
+
+    case CONST_ANY:
+      printf("*");
+    break;
+
+    case CONST_NOT:
+      printf("!");
+    break;
+  }
+}
+
+void print_flags(E2Feature * feature, char * title,int number_of_flags, char **flag_table) {
+  E2FFlags *  flags;
+  u_int32_t   value;
+  u_int32_t   mask;
+  int i;
+
+  flags = (E2FFlags *) feature->f_val;
+  value = flags->value;
+  mask  = flags->mask;
+
+  printf("%s",title);
+
+  if (flags->op!= CONST_NOT) {
+    for (i=0;i<number_of_flags;i++) {
+      if (value& (1<<i))
+        printf("%s ",flag_table[i]);
+    }
+    print_op(flags->op);
+  }
+  else {
+    // Let's have a look on the mask
+    for (i=0;i<number_of_flags;i++) {
+      if (mask& (1<<i))
+        printf("%s ",flag_table[i]);
+    }
+    print_op(flags->op);
+  }
+}
+
+void ip_options_print(E2Feature *feature) {
+  print_flags(feature, "IP Option: ",CONST_NUMBER_OF_SUPPORTED_IPOPTIONS,ipoptions_names);
+}
+
+void frag_flags_print (E2Feature * feature) {
+  print_flags(feature, "Frag-flag: ",CONST_NUMBER_OF_SUPPORTED_FRAGFLAGS,frag_flag_names);
+}
+
+void tcp_flags_print  (E2Feature * feature) {
+  print_flags(feature, "TCP-flags: ",CONST_NUMBER_OF_SUPPORTED_TCPFLAGS,tcp_flag_names);
+}
+
+/**************************** Maintenance Functions ***************************/
+void* flags_clone(void *fval)
+{
+    E2FFlags *src, *dst;
+
+    src = (E2FFlags *) fval;
+    dst = calloc(1, sizeof(E2FFlags));
+    
+    dst->value = src->value;
+    dst->mask = src->mask;
+    dst->length = src->length;
+    dst->op = src->op;
+
+    return (void *) dst;
+}
+
+
+void flags_destroy(E2Feature *feature)
+{
+#ifdef E2DEBUG
+    if (feature->f_type != CONST_IPOPTION && feature->f_type != CONST_IPFRAG && feature->f_type != CONST_TCPFLAGS ) {
+	printf("Fatal: flags_free: illegal feature type");
+	exit(1);
+    }
+#endif
+
+    if (--feature->ref_count > 0)
+	return;
+    else {
+	free(feature->f_val);
+	free(feature);
+    }
+}
+ 
+void flags_print(E2Feature *feature)
+{
+    int cnt;
+    E2FFlags *flags;
+
+#ifdef E2DEBUG
+    if (feature->f_type != CONST_IPOPTION) {
+	printf("Fatal: flags_print: illegal feature type");
+	exit(1);
+    }
+#endif
+
+    flags = (E2FFlags *) feature->f_val;
+
+    for (cnt = 1; cnt <= flags->length; ++cnt)
+		printf("%d", (flags->value & (1 << (flags->length - cnt))) ? 1 : 0);
+    printf("/");
+    for (cnt = 1; cnt <= flags->length; ++cnt)
+		printf("%d", (flags->mask & (1 << (flags->length - cnt))) ? 1 : 0);
+
+
+    switch (flags->op) {
+	case CONST_ALL:
+	    printf("+");
+	    break;
+	case CONST_ANY:
+	    printf("*");
+	    break;
+	case CONST_NOT:
+	    printf("!");
+	    break;
+	case CONST_NOP:
+	    printf("$");
+	    break;
+	case CONST_EXACT: 
+	    break;
+	default:
+	    printf("Fatal: flags_print: illegal operator value");
+	    exit(1);
+    }
+}
+
+void flags_debug(E2TreeNode *node)
+{
+    E2Rule *rule;
+
+    printf("Node %p:\n", node);
+    
+    for (rule = node->fire; rule != NULL; rule = rule->next)
+	rule_dump(rule, 6);
+
+    debug_flags_tree((JumpTree *) node->private, 0);    
+    debug_flags_tree((JumpTree *) node->private, 1);
+}
diff -Naur snort-1.8.7.orig/e2_feature_int.c snort-1.8.7p1/e2_feature_int.c
--- snort-1.8.7.orig/e2_feature_int.c	Wed Dec 31 16:00:00 1969
+++ snort-1.8.7p1/e2_feature_int.c	Fri Sep  6 07:48:52 2002
@@ -0,0 +1,757 @@
+/*
+** Copyright (C) 2002 Distributed Systems Group, Technical University Vienna
+**
+**                    Thomas Toth (ttoth@infosys.tuwien.ac.at)
+**                    Christopher Kruegel (chris@infosys.tuwien.ac.t)
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation; either version 2 of the License, or
+** (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+** GNU General Public License for more details.
+**
+** You should have received a copy of the GNU General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include "engine2.h"
+
+/******************************************************************************/
+/*                     Internal Variables                                     */             
+/******************************************************************************/
+
+typedef struct e2_feature_int {
+    u_int32_t lower;
+    u_int32_t upper;
+} E2FInt;
+
+typedef struct _interval {
+    u_int32_t lower;
+    u_int32_t upper;
+} Interval;
+
+typedef struct _jump_table_entry {
+    u_int32_t left;
+    E2TreeNode *next;
+} JumpTableEntry;
+
+typedef struct _jump_table {
+    JumpTableEntry *entries;
+    u_int32_t length;
+
+    E2TreeNode *any;
+} JumpTable;
+
+
+
+/******************** Supported operators *************************************/
+
+#define CONST_SMALLER 1
+#define CONST_LARGER 2
+#define CONST_EQUAL 3
+#define CONST_NEQUAL 4
+
+
+/******************************************************************************/
+/*                     Internal Functions                                     */             
+/******************************************************************************/
+
+E2SuccessorNode* add_to_successors(E2SuccessorNode *succs, E2Rule *rule, E2Feature *feat)
+{
+    E2SuccessorNode *succ, *tsn1, *tsn2;
+    E2Rule *clone, *tr;
+    Interval *inter;
+    u_int32_t r_lower, r_upper;
+
+    r_lower = ((E2FInt *) feat->f_val)->lower;
+    r_upper = ((E2FInt *) feat->f_val)->upper;
+
+    if (succs == NULL) {
+
+	succ = calloc(1, sizeof(E2SuccessorNode));
+	inter = calloc(1, sizeof(Interval));
+
+	inter->lower = r_lower;
+	inter->upper = r_upper;
+
+	succ->rules = rule_clone(rule);
+	succ->rule_count = 1;
+	succ->private = (void *) inter;
+
+	return succ;
+    }
+    else {
+
+	tsn1 = tsn2 = succs;
+
+	while (r_lower < r_upper) {
+
+	    while (tsn1 != NULL && r_lower >= ((Interval *) tsn1->private)->upper)
+		tsn1 = tsn1->next;
+
+
+	    if ((tsn1 == NULL) || (r_upper <= ((Interval *) tsn1->private)->lower)) {
+
+		succ = calloc(1, sizeof(E2SuccessorNode));
+		inter = calloc(1, sizeof(Interval));
+
+		inter->lower = r_lower;
+		inter->upper = r_upper;
+
+		succ->rules = rule_clone(rule);
+		succ->rule_count = 1;
+		succ->private = (void *) inter;
+
+		if (tsn1 != succs) {
+		    while (tsn2 != NULL && tsn2->next != tsn1) 
+			tsn2 = tsn2->next;
+		    succ->next = tsn1;
+		    tsn2->next = succ;
+		    return succs;
+		}
+		else {
+		    succ->next = succs;
+		    return succ;
+		}
+	    }
+	    else if (r_lower < ((Interval *) tsn1->private)->lower) {
+
+		succ = calloc(1, sizeof(E2SuccessorNode));
+		inter = calloc(1, sizeof(Interval));
+
+		inter->lower = r_lower;
+		if (((Interval *) tsn1->private)->lower >= r_upper) {
+		    inter->upper = r_upper;
+		    r_lower = r_upper;
+		}
+		else {
+		    inter->upper = ((Interval *) tsn1->private)->lower;
+		    r_lower = ((Interval *) tsn1->private)->lower;
+		}
+
+		succ->rules = rule_clone(rule);
+		succ->rule_count = 1;
+		succ->private = (void *) inter;
+
+		if (tsn1 == succs) {
+		    succ->next = succs;
+		    succs = succ;
+		}
+		else {
+
+		    while (tsn2 != NULL && tsn2->next != tsn1) 
+			tsn2 = tsn2->next;
+		    tsn2->next = succ;
+		    succ->next = tsn1;
+		}
+	    }
+	    else if (r_lower == ((Interval *) tsn1->private)->lower) {
+		
+		if (r_upper >= ((Interval *) tsn1->private)->upper) {
+		    tsn1->rules = list_add(tsn1->rules, rule_clone(rule));
+		    ++tsn1->rule_count;
+		    r_lower = ((Interval *) tsn1->private)->upper;
+		}
+		else {
+
+		    succ = calloc(1, sizeof(E2SuccessorNode));
+		    inter = calloc(1, sizeof(Interval));
+
+		    inter->lower = r_upper;
+		    inter->upper = ((Interval *) tsn1->private)->upper;
+		    
+		    ((Interval *) tsn1->private)->upper = r_upper;
+
+		    succ->private = (void *) inter;
+		    succ->next = tsn1->next;
+		    tsn1->next = succ;
+
+		    for (tr = tsn1->rules; tr != NULL; tr = tr->next) {
+			clone = rule_clone(tr);
+			succ->rules = list_add(succ->rules, clone);
+			++succ->rule_count;
+		    }
+
+		    tsn1->rules = list_add(tsn1->rules, rule_clone(rule));
+		    ++tsn1->rule_count;
+
+		    r_lower = r_upper;
+		}
+	    }
+
+	    else  { /* r_lower > ((Interval *) tsn1->private)->lower */
+		
+		succ = calloc(1, sizeof(E2SuccessorNode));
+		inter = calloc(1, sizeof(Interval));
+
+		inter->lower = r_lower;
+		inter->upper = ((Interval *) tsn1->private)->upper;
+		    
+		((Interval *) tsn1->private)->upper = r_lower;
+
+		succ->private = (void *) inter;
+		succ->next = tsn1->next;
+		tsn1->next = succ;
+
+		for (tr = tsn1->rules; tr != NULL; tr = tr->next) {
+		    clone = rule_clone(tr);
+		    succ->rules = list_add(succ->rules, clone);
+		    ++succ->rule_count;
+		}
+	    }
+	}
+    }
+    return succs;
+}
+
+E2FInt * newInt(u_int32_t lower, u_int32_t upper) {
+
+  E2FInt * integer;
+  integer = salloc(sizeof(E2FInt));
+
+  integer->lower=lower;
+  integer->upper=upper;
+  return integer;
+}
+
+E2FInt * setInt(E2FInt *integer, u_int32_t lower, u_int32_t upper) {
+  if (integer==NULL) {
+    FatalError ("ERROR => setInt called with NULL!\n");  }
+
+  integer->lower=lower;
+  integer->upper=upper;
+  return integer;
+}
+
+
+// Delivers back a E2Element
+E2Alternative * adapt_integer_x(E2FInt * target,u_int32_t value1,u_int32_t value2, int operator,u_int8_t f_type,u_int8_t f_id, u_int32_t maxvalue) {
+  E2Feature *f=NULL ;
+  E2Alternative * elements=NULL;
+
+  switch (operator) {
+
+    case CONST_SMALLER:
+      setInt( target, 0, value1 );
+      f=new_feature( f_type, f_id, target );
+      elements=insert_feature_into_alternatives( elements, f, 1, CONST_NO_SAME_ID );
+    break;
+
+    case CONST_LARGER:
+
+      switch(maxvalue) {
+        case CONST_MAX_INT_32:
+           if ( value2 == CONST_MAX_INT_32 )
+              FatalError("ERROR => adapt_integer_x: CONST_MAX_INT_32 is not supported");
+
+           setInt( target, value2+1, CONST_MAX_INT_32 );
+        break;
+
+        default:
+          setInt( target, value2+1, maxvalue+1 );
+          break;
+      }
+
+      f = new_feature( f_type, f_id, target);
+      elements = insert_feature_into_alternatives( elements, f, 1, CONST_NO_SAME_ID );
+    break;
+
+    case CONST_EQUAL:
+      setInt( target, value1, value2+1 );
+      f = new_feature( f_type, f_id, target );
+      elements = insert_feature_into_alternatives( elements, f, 1, CONST_NO_SAME_ID );
+      break;
+
+    case CONST_NEQUAL:
+      if (value1==0 && value2==maxvalue)
+        FatalError("ERROR => adapt_integer_x : Not equal whole range makes no sense.");
+
+      if (value1==0) {
+
+        switch(maxvalue) {
+          case CONST_MAX_INT_32:
+            setInt( target, value2+1, CONST_MAX_INT_32 );
+          break;
+
+         default:
+            setInt( target, value2+1, maxvalue+1 );
+            break;
+        }
+        f = new_feature( f_type, f_id, target );
+        elements = insert_feature_into_alternatives( elements, f, 1, CONST_NO_SAME_ID );
+
+      }
+      else if (value2 == maxvalue) {
+        setInt( target, 0, value1 );
+        f = new_feature( f_type, f_id, target );
+        elements = insert_feature_into_alternatives( elements, f, 1, CONST_NO_SAME_ID );
+      }
+      else {
+        target->lower = 0;
+        target->upper = value1;
+        f=new_feature( f_type, f_id, target);
+        elements = insert_feature_into_alternatives( elements, f, 1, CONST_NO_SAME_ID );
+        target = (E2FInt *) salloc(sizeof(E2FInt));
+
+        switch(maxvalue) {
+          case CONST_MAX_INT_32:
+            setInt( target, value2+1, maxvalue );
+          break;
+
+          default:
+            setInt( target, value2+1, maxvalue+1 );
+          break;
+        }
+
+        f = new_feature( f_type, f_id, target );
+        elements = insert_feature_into_alternatives( elements, f, 0, CONST_NO_SAME_ID );
+      }
+    break;
+
+    default:
+      FatalError("ERROR => adapt_integer_x: operator = %d\n",operator);
+  }
+  return elements;
+}
+
+
+/******************************************************************************/
+/*                            API Functions                                   */             
+/******************************************************************************/
+
+
+/**************************** Decision Tree Building **************************/
+
+E2SuccessorNode* int_pre_split_node(E2Rule *rules, u_int8_t feat)
+{
+    E2Feature *feature;
+    E2Rule *current_rule, *any_list;
+    E2SuccessorNode *result, *tsn;
+
+    any_list = NULL;
+    result = NULL;
+
+    current_rule = rules;
+    while (current_rule != NULL) {
+
+	feature = current_rule->features[feat];
+
+	if (feature == NULL)
+	    any_list = list_add(any_list, rule_clone(current_rule));
+	else  
+	    result = add_to_successors(result, current_rule, feature);
+
+	current_rule = current_rule->next;	
+    }
+
+
+    /* set up the node holding the any_list rules */
+    tsn = calloc(1, sizeof(E2SuccessorNode));
+    tsn->rules = any_list;
+    tsn->next = result;
+    result = tsn;
+
+    current_rule = result->rules;
+    while (current_rule != NULL) {
+
+	++result->rule_count;
+
+	/* add any_rule to all other successors as well */
+	for (tsn = result->next; tsn != NULL; tsn = tsn->next) {
+	    tsn->rules = list_add(tsn->rules, rule_clone(current_rule));
+	    ++tsn->rule_count;
+	}
+    
+	current_rule = current_rule->next;
+    }
+	   
+    return result;
+}
+
+void int_undo_split_node(E2SuccessorNode *succs)
+{
+    E2SuccessorNode *tsn1, *tsn2;
+    E2Rule *tr1, *tr2;
+
+    for (tsn1 = succs; tsn1 != NULL; ) {
+	tsn2 = tsn1->next;
+
+	for (tr1 = tsn1->rules; tr1 != NULL; ) {
+	    tr2 = tr1->next;
+	    rule_destroy(tr1);
+	    tr1 = tr2;
+	}
+
+	if (tsn1->private)
+	    free(tsn1->private);
+	free(tsn1);
+
+	tsn1 = tsn2;
+    }
+}
+
+void* int_final_split_node(E2SuccessorNode *succs, u_int8_t feat, int *feature_map, u_int8_t proto)
+{
+    E2SuccessorNode *succ, *start;
+    JumpTable *table;
+    int index, table_len = 0;
+    
+    /* remove feature from further decision process */
+    feature_map[feat] = 1;
+
+    /* create decision data */
+    table = calloc(1, sizeof(JumpTable));
+
+    /* handle any_rules (no feature was there, so no need to remove it ;) ) */
+    if (succs->rules != NULL)
+	table->any = split_snort_rules(succs->rules, proto, feature_map);
+    
+    start = succs->next;
+    if ((start == NULL) || (((Interval *) start->private)->lower != 0))
+	table_len = 1;
+    for (succ = start; succ != NULL; succ = succ->next) {
+	if ((succ->next == NULL) && ((Interval *) succ->private)->upper < CONST_MAX_INT_32)
+	    table_len += 2;
+	else if ((succ->next != NULL) && 
+		 (((Interval *) succ->private)->upper != ((Interval *) succ->next->private)->lower))
+	    table_len += 2;
+	else
+	    table_len += 1;
+    }
+
+    table->entries = calloc(table_len, sizeof(JumpTableEntry));
+    table->length = table_len;
+
+    index = 0;
+    if (((Interval *) start->private)->lower != 0) {
+	table->entries[0].left = 0;
+	table->entries[0].next = NULL;
+	index = 1;
+    }
+    for (succ = start; succ != NULL; succ = succ->next) {
+
+	table->entries[index].left = ((Interval *) succ->private)->lower;
+	table->entries[index].next = split_snort_rules(succ->rules, proto, feature_map);
+
+	++index;
+
+	if (succ->next == NULL) {
+
+	    if (((Interval *) succ->private)->upper < CONST_MAX_INT_32) {
+		table->entries[index].left = ((Interval *) succ->private)->upper;
+		table->entries[index].next = NULL;
+		++index;
+	    }
+	}
+	else {
+	    
+    	    if (((Interval *) succ->private)->upper != ((Interval *) succ->next->private)->lower) {
+		table->entries[index].left = ((Interval *) succ->private)->upper;
+		table->entries[index].next = NULL;
+		++index;
+	    }
+	}
+    }
+
+    /* put feature back into decision process */
+    feature_map[feat] = 0;
+
+    return (void *) table;
+}
+
+
+void int_propagate(E2TreeNode *node, E2Trigger *t, void *caller)
+{
+    E2Trigger *trigger;
+    E2Rule *rule, *tr;
+    JumpTable *table;
+    JumpTableEntry entry;
+    int cnt;
+
+#ifdef E2DEBUG
+    if (node->visited) {
+	printf("Fatal: int_propagate: %p called twice - this time by %p\n", node, caller);
+	exit(1);
+    }
+    else 
+	node->visited = 1;
+#endif
+
+    table = (JumpTable *) node->private;
+
+    trigger = trigger_clone(t);
+    for (rule = node->fire; rule != NULL; rule = rule->next) {
+	printf("Error - int\n");
+	exit(1);
+	trigger_set(trigger, rule->id);
+    }
+
+    if (table->any != NULL) 
+	table->any->propagate(table->any, trigger, (void *) node);
+    
+    for (cnt = 0; cnt < table->length; ++cnt) {
+	entry = table->entries[cnt];
+	if (entry.next != NULL) {
+	    entry.next->propagate(entry.next, trigger, (void *) node);
+	}
+    }
+    
+    // trigger_destroy(trigger);
+    trigger_compact(trigger);
+    node->trigger = trigger;
+
+    for (rule = node->fire; rule != NULL; ) {
+	tr = rule->next;
+	rule_destroy(rule);
+	rule = tr;
+    }
+}
+
+/**************************** Decision Tree Navigation ************************/
+
+E2Trigger * int_navigate(E2TreeNode *node, Packet *p)
+{
+    int index, low, high, length;
+    u_int32_t value = 0, *vptr;
+    JumpTable *table;
+
+    table = (JumpTable *) node->private;
+    vptr = (u_int32_t *) node->extract(p);
+
+    if (vptr == NULL) {
+	if (table->any) 
+	    return table->any->navigate(table->any, p);
+    }
+    else {
+	value = *vptr;
+	free(vptr);
+    }
+
+    /* binary search in jump table intervals ((value >= table[index]) && (value < table[index+1])) */
+    length = table->length;
+    
+    for (low = -1, high = length; (high - low > 1); ) {
+
+	index = (high + low) / 2;
+	
+	if (value < table->entries[index].left)
+	    high = index;
+	else if ((index == (length - 1)) || (value < table->entries[index+1].left)) {
+	    if (table->entries[index].next != NULL)
+		return table->entries[index].next->navigate(table->entries[index].next, p);
+	    else if (table->any) 
+		return table->any->navigate(table->any, p);
+	    else
+		return NULL;
+	}
+	else
+	    low = index;
+
+    }
+
+    return NULL;
+}
+
+/**************************** Parse Functions *********************************/
+
+E2Alternative * int_parse(unsigned char * data, int id, u_int8_t proto) {
+  unsigned char  intrel;
+  int            factor         = 1;
+  int            operator       = 0;
+  int            intValue1      = 0;
+  int            intValue2      = 0;
+  E2FInt         * target;
+  E2Alternative  * thealternative;
+  unsigned int   maxint=0;
+
+  int type = e2_map[proto][e2_index_mapping_table[proto][id]].f_type;
+
+  switch(type) {
+    case CONST_INT8:
+      maxint=255;
+      break;
+    case CONST_INT16:
+      maxint=65535;
+      break;
+    case CONST_INT32:
+      maxint=CONST_MAX_INT_32;
+      break;
+    default:
+      FatalError ("ERROR => encountered %s type while parsing an integer\n",e2_t_map[type].ft_name);
+  }
+
+  while (isspace ((int) *data))
+    data++;
+
+  intrel = *data;
+
+  if (!strncasecmp (data, "any", 3)) {
+    return NULL;
+  }
+  else {
+    switch (intrel)
+      {
+      case '-':
+        factor = -1;	/* leading dash flag */
+        data++;
+      case '>':
+        operator = CONST_LARGER;
+        data++;
+        break;
+      case '<':
+        operator = CONST_SMALLER;
+        data++;
+        break;
+      case '=':
+        operator = CONST_EQUAL;
+        data++;
+        break;
+      case '!':
+        operator = CONST_NEQUAL;
+        data++;
+        break;
+      default:
+        operator = CONST_EQUAL;
+        break;
+      }
+    while (isspace ((int) *data))
+      data++;
+
+    if (isdigit((int) *data)) {
+      intValue1 = atoi (data);
+
+      /* skip digits */
+      while (isdigit ((int) *data))
+        data++;
+      intValue2=intValue1;
+    }
+    else
+      if (*data=='~' || *data==':') {
+        intValue1=0;
+        intValue2=maxint;
+      }
+    else {
+      FatalError("ERROR=> int_parse : no integer value found in string.\n");
+    }
+
+
+
+    // Is there a : followed by a number?
+    switch ((int) *data) {
+      case 0:
+      break;
+      case '~':
+      case ':':
+        // Parse the integer starting a the next position
+        data++;
+
+        while (isspace((int) *data))
+          data++;
+
+        if(isdigit((int) *data)) {
+          intValue2=atoi(data);
+        }
+        else
+          if (*data==0) {
+            intValue2=maxint;
+          }
+        else
+          if (*data!=';') {
+            FatalError("ERROR =>int_parse: expected an integer at end of an int-range, found %s\n",data);
+          }
+        break;
+      default:
+        FatalError("ERROR =>int_parse: default: expected an integer at end of an int-range, found %s\n",data);
+
+    }
+
+    intValue1 = intValue1 * factor;
+  }
+
+  target = (E2FInt *) salloc(sizeof(E2FInt));
+
+  // Adapt the ranges according to their type
+  thealternative = adapt_integer_x(target, intValue1,intValue2, operator,type, id,maxint);
+  return thealternative;
+}
+
+/**************************** Maintenance Functions ***************************/
+void* int_clone(void *fval)
+{
+    E2FInt *src, *dst;
+
+    src = (E2FInt *) fval;
+    dst = calloc(1, sizeof(E2FInt));
+    
+    dst->lower = src->lower;
+    dst->upper = src->upper;
+
+    return (void *) dst;
+}
+
+
+void int_destroy(E2Feature *feature)
+{
+#ifdef E2DEBUG
+    if ((feature->f_type != CONST_INT8) &&
+	(feature->f_type != CONST_INT16) &&
+	(feature->f_type != CONST_INT32))
+    FatalError("Fatal: int_free: illegal feature type <%d>\n",feature->f_type);
+#endif
+
+    if (--feature->ref_count > 0)
+	return;
+    else {
+	free(feature->f_val);
+	free(feature);
+    }
+}
+ 
+void int_print(E2Feature *feature)
+{
+#ifdef E2DEBUG
+    if ((feature->f_type != CONST_INT8) &&
+	(feature->f_type != CONST_INT16) &&
+	(feature->f_type != CONST_INT32)) {
+	FatalError("Fatal: int_print: illegal feature type  %d", feature->f_type);
+    }
+#endif
+
+    printf("%u - %u", ((E2FInt *) feature->f_val)->lower, ((E2FInt *) feature->f_val)->upper);
+}
+
+
+void int_debug(E2TreeNode *node)
+{
+    JumpTable *table;
+    JumpTableEntry entry;
+    int cnt;
+
+    printf("\nNode: %p\n", node);
+    
+    table = (JumpTable *) node->private;
+    
+    printf("   Any Rules\n            * --> %p\n", table->any);
+
+    printf("   Jump Table\n");
+    for (cnt = 0; cnt < table->length; ++cnt) {
+	entry = table->entries[cnt];
+	printf("   %10d --> %p\n", entry.left, entry.next);
+    }
+
+    printf("\n");
+
+    if (table->any)
+	table->any->debug(table->any);
+    for (cnt = 0; cnt < table->length; ++cnt) {
+	entry = table->entries[cnt];
+	if (entry.next != NULL)
+	    entry.next->debug(entry.next);
+    }
+
+}
diff -Naur snort-1.8.7.orig/e2_feature_ipv4.c snort-1.8.7p1/e2_feature_ipv4.c
--- snort-1.8.7.orig/e2_feature_ipv4.c	Wed Dec 31 16:00:00 1969
+++ snort-1.8.7p1/e2_feature_ipv4.c	Fri Sep  6 07:49:11 2002
@@ -0,0 +1,1040 @@
+/*
+** Copyright (C) 2002 Distributed Systems Group, Technical University Vienna
+**
+**                    Thomas Toth (ttoth@infosys.tuwien.ac.at)
+**                    Christopher Kruegel (chris@infosys.tuwien.ac.t)
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation; either version 2 of the License, or
+** (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+** GNU General Public License for more details.
+**
+** You should have received a copy of the GNU General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include "engine2.h"
+#include <math.h>
+
+/******************** Supported operators *************************************/
+
+#define CONST_EQUAL 0
+#define CONST_NEQUAL 1
+
+/******************************************************************************/
+/*                     Internal Variables                                     */             
+/******************************************************************************/
+
+typedef struct e2_feature_ipv4 {
+    u_int32_t addr;
+    u_int32_t mask;
+    u_int8_t  not_flag;
+    u_int8_t  not_flag_external_set;
+} E2FIpv4;
+
+typedef struct _address_tree {
+    E2Rule *pos_rules, *neg_rules;
+    E2Rule *pos_fire, *neg_fire; 
+    u_int32_t pos_fire_count, neg_fire_count;
+
+    E2FIpv4 *ipv4;
+    struct _address_tree *zero, *one;
+
+} AddressTree;
+
+typedef struct _jump_tree {
+    u_int32_t addr;
+    u_int32_t mask;
+    u_int32_t filter;
+
+    E2TreeNode *pos_next, *neg_next;
+
+    struct _jump_tree *zero, *one, *not;
+} JumpTree;
+
+typedef struct _jump_table_ipv4 {
+    E2TreeNode *any;
+    struct _jump_tree *root;
+} JumpTableIpv4;
+    
+
+
+float log2;
+AddressTree *tree_root;
+
+/******************************************************************************/
+/*                     Internal Functions                                     */             
+/******************************************************************************/
+char* addr_to_str(u_int32_t addr, char *buffer)
+{
+    u_int8_t a1, a2, a3, a4;
+    
+    a1 = (addr >> 24) & 0xff;
+    a2 = (addr >> 16) & 0xff;
+    a3 = (addr >> 8) & 0xff;
+    a4 = addr & 0xff;
+
+    sprintf(buffer, "%d.%d.%d.%d", a1, a2, a3, a4);
+    return buffer;
+}
+
+AddressTree * node_create(E2Rule *rule, E2FIpv4 *ipv4)
+{
+    AddressTree *node;
+
+    node = calloc(1, sizeof(AddressTree));
+
+    if (ipv4->not_flag) 
+	node->neg_rules = rule_clone(rule);
+    else
+	node->pos_rules = rule_clone(rule);  
+
+    node->ipv4 = calloc(1, sizeof(E2FIpv4));
+    memcpy(node->ipv4, ipv4, sizeof(E2FIpv4));
+
+    return node;
+}
+
+u_int8_t get_mask_point(u_int32_t mask)
+{
+    mask = ~mask;
+
+    if (mask == 0)
+	return 0;
+    else 
+	return (u_int8_t) floor((log(mask) * log2) + 1);
+}
+
+int is_subnet(E2FIpv4 *subnet, E2FIpv4 *net)
+{
+    if ((subnet->mask >= net->mask) &&
+	((subnet->addr & net->mask) == net->addr))
+	return 1;
+    else
+	return 0;
+}
+
+int one_continuation(E2FIpv4 *shorter, E2FIpv4 *longer)
+{
+    u_int8_t point;
+
+    point = get_mask_point(shorter->mask);
+
+    if (point == 0) {
+	printf("Fatal: one_continuation: no continuation for complete 32-bit address\n");
+	exit(1);
+    }
+
+    return (longer->addr & (1 << (point - 1)));
+}
+    
+AddressTree* make_common_parent(AddressTree *child1, AddressTree *child2)
+{
+    u_int32_t filter;
+    int bit;
+    AddressTree *parent;
+
+    for (bit = 31; bit >= 0; --bit) {
+	
+	filter = (1 << bit);
+
+	if ((child1->ipv4->addr & filter) != (child2->ipv4->addr & filter))
+	    break;
+    }
+
+    parent = calloc(1, sizeof(AddressTree));
+    parent->ipv4 = calloc(1, sizeof(E2FIpv4));
+
+    if (bit == 31)
+	parent->ipv4->mask = 0;
+    else
+	parent->ipv4->mask = ~((1 << (bit+1)) - 1);
+
+    parent->ipv4->addr = (child1->ipv4->addr & parent->ipv4->mask);
+    
+#ifdef E2DEBUG
+    if (parent->ipv4->addr != (child2->ipv4->addr & parent->ipv4->mask)) {
+	printf("Fatal: make_common_parent: illegal mask\n");
+	exit(1);
+    }
+#endif
+
+    if (child1->ipv4->addr & filter) {
+	parent->one = child1;
+	parent->zero = child2;
+    }
+    else {
+	parent->one = child2;
+	parent->zero = child1;
+    }
+
+    return parent;
+}
+
+AddressTree * add_address_to_tree(AddressTree *tree, E2Rule *rule, E2FIpv4 *ipv4)
+{
+    AddressTree *node, *parent;
+
+    if (tree == NULL) {
+	node = node_create(rule, ipv4);
+	return node;
+    }
+    else if ((ipv4->mask == tree->ipv4->mask) && (ipv4->addr == tree->ipv4->addr)) {
+
+	if (ipv4->not_flag) 
+	    tree->neg_rules = list_add(tree->neg_rules, rule_clone(rule));
+	else
+	    tree->pos_rules = list_add(tree->pos_rules, rule_clone(rule));
+	return tree;
+    }
+    else if (is_subnet(tree->ipv4, ipv4)) {
+
+	node = node_create(rule, ipv4);
+
+	if (one_continuation(ipv4, tree->ipv4))
+	    node->one = tree;
+	else
+	    node->zero = tree;
+
+	return node;
+    }
+    else if (is_subnet(ipv4, tree->ipv4)) {
+
+	if (one_continuation(tree->ipv4, ipv4))
+	    tree->one = add_address_to_tree(tree->one, rule, ipv4);
+	else
+	    tree->zero = add_address_to_tree(tree->zero, rule, ipv4);
+	return tree;
+    }
+    else {
+	
+	node = node_create(rule, ipv4);
+
+	parent = make_common_parent(node, tree);
+	return parent;
+    }
+}
+
+void insert_rule(AddressTree *node, E2Rule *rule, int positive, AddressTree *start)
+{
+    E2Rule *neg_rule;
+
+    if (node == NULL)
+	return;
+
+    /* 
+     * if negative rules are inserted:
+     * stop them at nodes where the same rule id has already been inserted previously or 
+     * is waiting to be inserted --> needed to cope with ![ip1, ip2, ...] rules
+     *
+     * if positive rules are inserted:
+     * add rule to ONLY pos_rules when start equals current node and node is a leaf
+     */
+    if (!positive) {
+	
+	neg_rule = node->neg_fire;
+	while (neg_rule != NULL) {
+	    if (neg_rule->id == rule->id) 
+		return;
+	    else
+		neg_rule = neg_rule->next;
+	}
+
+	neg_rule = node->neg_rules;
+	while (neg_rule != NULL) {
+	    if (neg_rule->id == rule->id)
+		return;
+	    else
+		neg_rule = neg_rule->next;
+	}
+
+	node->neg_fire = list_add(node->neg_fire, rule_clone(rule));
+	++node->neg_fire_count;
+
+	/* this is a leaf node */
+	if (node->zero == NULL) {
+	    node->pos_fire = list_add(node->pos_fire, rule_clone(rule));
+	    ++node->pos_fire_count;
+	}
+    }
+    else {
+
+	if (node != start) {
+	    node->neg_fire = list_add(node->neg_fire, rule_clone(rule));
+	    ++node->neg_fire_count;
+	}
+
+
+	/* this is a leaf node */
+	if (node->zero == NULL) {
+	    node->pos_fire = list_add(node->pos_fire, rule_clone(rule));
+	    ++node->pos_fire_count;
+	}
+    }
+
+    insert_rule(node->zero, rule, positive, start);
+    insert_rule(node->one, rule, positive, start);
+}
+
+void move_neg_rules(AddressTree *node)
+{
+    E2Rule *rule, *tr;
+
+    if (node == NULL)
+	return;
+
+    /* insert NEGATIVE rules immediately at current node and flood rest of tree */ 
+    rule = node->neg_rules;
+    while (rule != NULL) {
+	tr = rule->next;
+
+	node->neg_fire = list_add(node->neg_fire, rule_clone(rule));
+	++node->neg_fire_count;
+
+	insert_rule(tree_root, rule, 0, NULL);
+	rule_destroy(rule);
+	rule = tr;
+    }
+    node->neg_rules = NULL;
+   
+    move_neg_rules(node->zero);
+    move_neg_rules(node->one);
+}
+
+void move_pos_rules(AddressTree *node)
+{
+    E2Rule *rule, *tr;
+
+    if (node == NULL)
+	return;
+
+    /* move POSITIVE rules from current node to all its leafs */
+    rule = node->pos_rules;
+    while (rule != NULL) {
+	tr = rule->next;
+	insert_rule(node, rule, 1, node);
+	rule_destroy(rule);
+	rule = tr;
+    }
+    node->pos_rules = NULL;
+
+    move_pos_rules(node->zero);
+    move_pos_rules(node->one);
+}
+
+
+void well_form_tree(AddressTree *node)
+{
+    if (node == NULL)
+	return;
+
+    /* make tree well-formed --> each node has two children */
+    if ((node->zero != NULL) || (node->one != NULL)) {
+	if (node->zero == NULL)
+	    node->zero = calloc(1, sizeof(AddressTree));
+	if (node->one == NULL)
+	    node->one = calloc(1, sizeof(AddressTree));
+    }
+
+    well_form_tree(node->zero);
+    well_form_tree(node->one);
+}
+
+
+void move_ipv4_rules(AddressTree *root)
+{
+    if (root == NULL)
+	return;
+
+    well_form_tree(root);
+    move_pos_rules(root);
+    move_neg_rules(root);
+}
+ 
+E2SuccessorNode* build_list(AddressTree *node, E2Rule *any_list)
+{
+    E2Rule *rule;
+    E2SuccessorNode *succ, *left, *right, *tsn;
+
+    if (node == NULL)
+	return NULL;
+
+    left = build_list(node->zero, any_list);
+    right = build_list(node->one, any_list);
+
+    if (left != NULL) {
+	for (tsn = left; tsn->next != NULL; tsn = tsn->next); 
+	tsn->next = right;
+    }
+    else
+	left = right;
+
+    
+    if (node->pos_fire != NULL) {
+	
+	for (rule = any_list; rule != NULL; rule = rule->next) {
+	    node->pos_fire = list_add(node->pos_fire, rule_clone(rule));
+	    ++node->pos_fire_count;
+	}
+
+	succ = calloc(1, sizeof(E2SuccessorNode));
+	
+	succ->rules = node->pos_fire;
+	succ->rule_count = node->pos_fire_count;
+
+	succ->next = left;
+	left = succ;
+    }
+
+    if (node->neg_fire != NULL) {
+	
+	for (rule = any_list; rule != NULL; rule = rule->next) {
+	    node->neg_fire = list_add(node->neg_fire, rule_clone(rule));
+	    ++node->neg_fire_count;
+	}
+
+	succ = calloc(1, sizeof(E2SuccessorNode));
+	
+	succ->rules = node->neg_fire;
+	succ->rule_count = node->neg_fire_count;
+
+	succ->next = left;
+	left = succ;
+    }
+
+    return left;
+}
+
+void kill_address_tree(AddressTree *node)
+{
+    E2Rule *tr1, *tr2;
+
+    if (node == NULL)
+	return;
+
+    kill_address_tree(node->zero);
+    kill_address_tree(node->one);
+    
+    if (node->ipv4)
+	free(node->ipv4);
+    
+    for (tr1 = node->pos_fire; tr1 != NULL; ) {
+	tr2 = tr1->next;
+	rule_destroy(tr1);
+	tr1 = tr2;
+    }
+    for (tr1 = node->neg_fire; tr1 != NULL; ) {
+	tr2 = tr1->next;
+	rule_destroy(tr1);
+	tr1 = tr2;
+    }
+
+    free(node);
+}
+
+E2TreeNode * follow_detection(JumpTree *node, u_int32_t addr)
+{
+    JumpTree *next_jump;
+
+    if (node == NULL)
+	return NULL;
+
+    /* in leaf nodes where addr and mask equals 0, the packet address always matches */ 
+    if ((addr & node->mask) == node->addr) {
+
+	if ((node->pos_next) || ((node->one == NULL) && (node->zero == NULL)))
+	    return node->pos_next;
+	else {
+	    
+	    if (addr & node->filter)
+		next_jump = node->one;
+	    else
+		next_jump = node->zero;
+	    
+	    if (next_jump == NULL)
+		return node->neg_next;
+	    else
+		return follow_detection(next_jump, addr);
+	}
+    }
+    else
+	return node->neg_next;
+}
+
+
+JumpTree * build_jmp_tree(AddressTree *addr_node, int *feature_map, u_int8_t proto)
+{
+    JumpTree *node;
+    u_int8_t point;
+
+    if (addr_node == NULL)
+	return NULL;
+
+    node = calloc(1, sizeof(JumpTree));
+
+    if (addr_node->pos_fire != NULL) 
+	node->pos_next = split_snort_rules(addr_node->pos_fire, proto, feature_map);
+    else
+	node->pos_next = NULL;
+
+    if (addr_node->neg_fire != NULL) 
+	node->neg_next = split_snort_rules(addr_node->neg_fire, proto, feature_map);
+    else
+	node->neg_next = NULL;
+
+
+    if (addr_node->ipv4 != NULL) {
+	
+	node->addr = addr_node->ipv4->addr;
+	node->mask = addr_node->ipv4->mask;
+	
+	point = get_mask_point(node->mask);
+
+	if (point > 0)
+	    node->filter = (1 << (point - 1));
+	else
+	    node->filter = 0;
+    }
+	
+    node->zero = build_jmp_tree(addr_node->zero, feature_map, proto);
+    node->one = build_jmp_tree(addr_node->one, feature_map, proto);
+
+    return node;
+}
+
+void debug_ipv4_tree(JumpTree *node, int mode, E2TreeNode *master)
+{
+    if (node == NULL)
+	return;
+    
+    if (mode == 0) {
+	printf("      [*] Node: %p, Addr: %x, Mask: %08x, Filter: %08x\n", node, node->addr, node->mask, node->filter);
+	printf("          Zero: %p, One: %p, PosNext: %p, NegNext: %p\n", node->zero, node->one, node->pos_next, node->neg_next);
+    }
+    else {
+	if (node->pos_next != NULL) 
+	    node->pos_next->debug(node->pos_next);
+	if (node->neg_next != NULL) 
+	    node->neg_next->debug(node->neg_next);
+    }
+
+    debug_ipv4_tree(node->zero, mode, master);
+    debug_ipv4_tree(node->one, mode, master);
+}
+
+
+E2FIpv4 * newIPv4() {
+  E2FIpv4 * tmp;
+
+  tmp =(E2FIpv4 *) salloc(sizeof(E2FIpv4));
+  tmp->not_flag=0;
+  tmp->addr=0;
+  tmp->mask=0;
+  return tmp;
+}
+
+void ipv4_propagate_tree(JumpTree *node, E2Trigger *trigger, void *caller)
+{
+    if (node == NULL)
+	return;
+
+    if (node->pos_next) 
+	node->pos_next->propagate(node->pos_next, trigger, caller);
+    if (node->neg_next) 
+	node->neg_next->propagate(node->neg_next, trigger, caller);
+
+    ipv4_propagate_tree(node->zero, trigger, caller);
+    ipv4_propagate_tree(node->one, trigger, caller);
+}
+
+
+/******************************************************************************/
+/*                            API Functions                                   */             
+/******************************************************************************/
+
+
+/**************************** Decision Tree Building **************************/
+
+E2SuccessorNode* ipv4_pre_split_node(E2Rule *rules, u_int8_t feat)
+{
+    E2Feature *feature;
+    E2Rule *current_rule, *any_list;
+    E2SuccessorNode *result;
+    u_int32_t any_length = 0;
+
+    /* set the global variable tree_root to NULL */
+    tree_root = NULL;
+    any_list = NULL;
+
+    current_rule = rules;
+    while (current_rule != NULL) {
+
+	feature = current_rule->features[feat];
+	
+
+	if (feature == NULL) {
+	    any_list = list_add(any_list, rule_clone(current_rule));
+	    ++any_length;
+	}
+	else 
+	    tree_root = add_address_to_tree(tree_root, current_rule, (E2FIpv4 *) feature->f_val);
+
+	current_rule = current_rule->next;	
+    }
+
+    /* move the rules to the correct leaf nodes */
+    move_ipv4_rules(tree_root);
+
+
+    /* set up the node holding the any_list rules */    
+    result = calloc(1, sizeof(E2SuccessorNode));
+    result->rules = any_list;
+    result->rule_count = any_length;
+    
+    /* determine successor classes and add any_list to each firing node */
+    result->next = build_list(tree_root, any_list);
+    if (result->next != NULL)
+	result->next->private = (void *) tree_root;
+  
+    return result;
+}
+
+void ipv4_undo_split_node(E2SuccessorNode *succs)
+{
+    E2Rule *tr1, *tr2; 
+    E2SuccessorNode *tsn1, *tsn2;
+
+    /* remove any_list */
+    for (tr1 = succs->rules; tr1 != NULL; ) {
+	tr2 = tr1->next;
+	rule_destroy(tr1);
+	tr1 = tr2;
+    }
+
+    /* kill tree */
+    if (succs->next)
+	kill_address_tree((AddressTree *) succs->next->private);
+
+    /* remove successor list */
+    for (tsn1 = succs; tsn1 != NULL; ) {
+	tsn2 = tsn1->next;
+	free(tsn1);
+	tsn1 = tsn2;
+    }
+}
+
+void* ipv4_final_split_node(E2SuccessorNode *succs, u_int8_t feat, int *feature_map, u_int8_t proto)
+{
+    JumpTableIpv4 *table;
+    JumpTree *tree;
+    AddressTree *root;
+
+    /* remove feature from further decision process */
+    feature_map[feat] = 1;
+
+    /* create decision data */
+    table = calloc(1, sizeof(JumpTableIpv4));
+    
+    /* handle any_rules (no feature was there, so no need to remove it ;) ) */
+    if (succs->rules != NULL)
+	table->any = split_snort_rules(succs->rules, proto, feature_map);
+
+    /* build the jump tree from the address tree */
+    if ((succs->next != NULL) && (succs->next->private)) {
+
+	root = (AddressTree *) succs->next->private;
+	tree = build_jmp_tree(root, feature_map, proto);
+	table->root = tree;
+    }
+
+    /* put feature back into decision process */
+    feature_map[feat] = 0;
+
+    return (void *) table;
+}
+
+
+void ipv4_propagate(E2TreeNode *node, E2Trigger *t, void *caller)
+{
+    E2Trigger *trigger;
+    E2Rule *rule, *tr;
+    JumpTableIpv4 *table;
+
+#ifdef E2DEBUG
+    if (node->visited) {
+	printf("Fatal: ipv4:propagate: %p called twice - this time by %p\n", node, caller);
+	exit(1);
+    }
+    else 
+	node->visited = 1;
+#endif
+
+    table = (JumpTableIpv4 *) node->private;
+    trigger = trigger_clone(t);
+    for (rule = node->fire; rule != NULL; rule = rule->next) {
+	printf("Error - ipv4\n");
+	exit(1);
+    	trigger_set(trigger, rule->id);
+    }
+
+    if (table->any != NULL) 
+	table->any->propagate(table->any, trigger, (void *) node);
+
+    ipv4_propagate_tree(table->root, trigger, (void *) node);
+    
+    // trigger_destroy(trigger);
+    trigger_compact(trigger);
+    node->trigger = trigger;
+
+    for (rule = node->fire; rule != NULL; ) {
+	tr = rule->next;
+	rule_destroy(rule);
+	rule = tr;
+    }
+}
+
+
+/**************************** Decision Tree Navigation ************************/
+
+E2Trigger * ipv4_navigate(E2TreeNode *node, Packet *p)
+{
+    u_int32_t value, *vptr;
+    JumpTableIpv4 *table;
+    E2TreeNode *next;
+
+    table = (JumpTableIpv4 *) node->private;
+    vptr = (u_int32_t *) node->extract(p);
+
+    if (vptr == NULL) {
+	if (table->any) 
+	    return table->any->navigate(table->any, p);
+	else
+	    return NULL;
+    }
+    else {
+	value = *vptr;
+	free(vptr);
+
+	if ((next = follow_detection(table->root, value)) != NULL)
+	    return next->navigate(next, p);
+	else if (table->any)
+	    return table->any->navigate(table->any, p);
+	else
+	    return NULL;
+    }
+}
+
+
+/**************************** Maintenance Functions ***************************/
+void* ipv4_clone(void *fval)
+{
+    E2FIpv4 *src, *dst;
+
+    src = (E2FIpv4 *) fval;
+    dst = calloc(1, sizeof(E2FIpv4));
+    
+    dst->addr = src->addr;
+    dst->mask = src->mask;
+    dst->not_flag = src->not_flag;
+    dst->not_flag_external_set = src->not_flag_external_set;
+
+    return (void *) dst;
+}
+
+
+void ipv4_destroy(E2Feature *feature)
+{
+#ifdef E2DEBUG
+    if (feature->f_type != CONST_IPv4) {
+	printf("Fatal: ipv4_free: illegal feature type");
+	exit(1);
+    }
+#endif
+
+    if (--feature->ref_count > 0) 
+	return;
+    else {
+	free(feature->f_val);
+	free(feature);
+    }
+}
+
+void ipv4_print(E2Feature *feature)
+{
+    int cnt;
+    u_int8_t octet, mask;
+    E2FIpv4 *f_ipv4;
+
+#ifdef E2DEBUG
+    if (feature->f_type != CONST_IPv4) {
+	printf("Fatal: ipv4_print: illegal feature type");
+	exit(1);
+    }
+#endif
+
+    f_ipv4 = (E2FIpv4 *) feature->f_val;
+    if (f_ipv4->not_flag)
+      printf("!");
+
+    /* print 4 octets */
+    for (cnt = 0; cnt < 4; ++cnt) {
+	octet = (f_ipv4->addr >> ((3 - cnt) * 8)) & 0xFF;
+	if (cnt == 3)
+	    printf("%u/", octet);
+	else
+	    printf("%u.", octet);
+    }
+
+    mask = get_mask_point(f_ipv4->mask);
+    printf("%u", (32-mask));
+}
+
+void ipv4_debug(E2TreeNode *node)
+{
+    JumpTableIpv4 *table;
+    E2Rule *rule;
+
+    printf("\nNode: %p\n", node);
+    
+    for (rule = node->fire; rule != NULL; rule = rule->next) 
+    	rule_dump(rule, 6);
+    
+
+    table = (JumpTableIpv4 *) node->private;
+    
+    printf("   Any Rules\n      --> %p\n", table->any);
+    if (table->any != NULL) 
+	table->any->debug(table->any);
+    
+
+    printf("   Jump Tree\n");
+    debug_ipv4_tree(table->root, 0, node);
+    debug_ipv4_tree(table->root, 1, node);
+}
+
+
+/**************************** Parse Functions *********************************/
+
+E2Alternative* ipv4_parse_single(unsigned char * data, int id, int not_set_outside)     {
+
+  E2Alternative * alt=NULL;
+  E2Feature *f;
+  E2FIpv4 * ip_address;
+
+  int not_flag=CONST_EQUAL;
+  char *addr;
+  int num_toks;
+  char **toks;
+  int cidr = 1;
+  int i;
+  int nmask;
+  struct hostent *host_info;	
+  struct sockaddr_in sin;	
+
+
+  addr = data;
+
+  not_flag=not_set_outside;
+  if (*addr == '!') {
+    if (not_set_outside==CONST_NEQUAL)
+      FatalError("ERROR => ipv4_parse: ! used outside an IP List as well as inside.\n");
+    else
+      not_flag = CONST_NEQUAL;
+    addr++;
+  }
+
+  if (!strncasecmp (addr, "any", 3)) {
+    return NULL;
+  }
+
+  ip_address = newIPv4();
+
+  toks = mSplit (addr, "/", 2, &num_toks, 0);
+
+  if (num_toks == 1) {
+      free(toks[0]);
+      toks = mSplit (addr, ":", 2, &num_toks, 0);
+  }
+  
+  if ((num_toks > 1) && strlen (toks[1]) > 2) {
+    cidr = 0;
+  }
+
+  switch (num_toks)  {
+    case 1:
+      ip_address->mask = netmasks[32];
+      break;
+
+    case 2:
+      if (cidr) {
+        nmask = atoi (toks[1]);
+
+        if (!isdigit ((int) toks[1][0]))
+          nmask = -1;
+
+        if ((nmask > -1) && (nmask < 33)) {
+          ip_address->mask = netmasks[nmask];
+        }
+        else  {
+          FatalError ("ERROR => Invalid CIDR block for IP "
+            "addr %s\n", addr);
+        }
+      }
+      else  {
+        if (!strncmp (toks[1], "255.255.255.255", 15))  {
+          ip_address->mask = INADDR_BROADCAST;
+        }
+        else if ((ip_address->mask = inet_addr (toks[1])) == -1) {
+          FatalError ("ERROR => Rule netmask (%s) didn't "
+            "x-late, WTF?\n", toks[1]);
+        }
+      }
+      break;
+
+    default:
+      FatalError ("ERROR => Unrecognized IP address/netmask %s\n",
+        addr);
+      break;
+  }
+
+#ifndef WORDS_BIGENDIAN
+  if (cidr) {
+//    ip_address->mask = htonl (ip_address->mask);
+  }
+#endif
+
+  if (isalpha ((int) toks[0][0])) {
+    if ((host_info = gethostbyname (toks[0]))) {
+      bcopy (host_info->h_addr, (char *) &sin.sin_addr,
+        host_info->h_length);
+    }
+    else
+      if ((sin.sin_addr.s_addr = inet_addr (toks[0])) == INADDR_NONE) {
+        FatalError ("ERROR => Couldn't resolve hostname %s\n",
+          toks[0]);
+      }
+
+    ip_address->addr = htonl(((u_long) (sin.sin_addr.s_addr) &
+      (ip_address->mask)));
+
+ }
+
+
+  if (!strncmp (toks[0], "255.255.255.255", 15))  {
+    ip_address->addr = INADDR_BROADCAST;
+  }
+  else
+    if ((ip_address->addr = htonl(inet_addr (toks[0]))) == -1)  {
+      FatalError ("ERROR => Rule IP address (%s) didn't translate,"
+        "please make sure you don't have an invalid IP address in"
+        "the rule\n",toks[0]);
+    }
+    else  {
+      ip_address->addr = ((u_long) (ip_address->addr) &
+      (ip_address->mask));
+    }
+
+  for (i = 0; i < num_toks; i++)  {
+    free (toks[i]);
+  }
+
+  ip_address->not_flag=not_flag;
+  f=new_feature(CONST_IPv4,id, ip_address);
+  alt=insert_feature_into_alternatives(NULL, f,1,not_set_outside);
+
+  return alt;
+}
+
+
+E2Alternative* ipv4_parse(unsigned char * addr, int id, u_int8_t proto) {
+  char **toks = NULL;
+  int num_toks;
+  int i;
+  char *tmp;
+  char *enbracket;
+  int not=0;
+
+  E2Alternative * alt=NULL;
+
+#ifdef DEBUG
+  printf ("Got address string: %s\n", addr);
+#endif
+
+  if (*addr == '!') {
+    not=CONST_NEQUAL;
+    addr++;
+  }
+
+  if (*addr == '$')   {
+    if ((tmp = VarGet (addr + 1)) == NULL)  {
+      FatalError ("ERROR %s (%d) => Undefined variable %s\n",
+        addr);
+    }
+  }
+  else  {
+    tmp = addr;
+  }
+
+  if (*tmp == '[')  {
+#ifdef DEBUG
+    printf ("Found IP list!\n");
+#endif
+
+    enbracket = strrchr (tmp, (int) ']');	
+    if (enbracket)
+      *enbracket = '\x0';
+
+    toks = mSplit (tmp + 1, ",", 128, &num_toks, 0);
+
+#ifdef DEBUG
+    printf ("mSplit got %d tokens...\n", num_toks);
+#endif
+
+    for (i = 0; i < num_toks; i++)  {
+#ifdef DEBUG
+      printf ("adding %s to IP address list\n", toks[i]);
+#endif
+      tmp = toks[i];
+      while (isspace ((int) *tmp) || *tmp == '[')
+        tmp++;
+
+      enbracket = strrchr (tmp, (int) ']');
+
+      if (enbracket)
+        *enbracket = '\x0';
+
+      if (strlen (tmp) == 0)
+        continue;
+
+
+      // Insert the alternative
+      alt =   insert_alternative(alt, ipv4_parse_single(tmp,id,not),0);
+    }
+
+#ifdef DEBUG
+    printf ("Freeing %d tokens...\n", num_toks);
+#endif
+
+    for (i = 0; i < num_toks; i++) {
+
+      free (toks[i]);
+    }
+  }
+
+  else  {
+#ifdef DEBUG
+    printf ("regular IP address, processing...\n");
+#endif
+    
+    alt =   insert_alternative(alt, ipv4_parse_single(tmp,id,not),0);
+  }
+
+  return alt;
+
+}
diff -Naur snort-1.8.7.orig/e2_feature_string.c snort-1.8.7p1/e2_feature_string.c
--- snort-1.8.7.orig/e2_feature_string.c	Wed Dec 31 16:00:00 1969
+++ snort-1.8.7p1/e2_feature_string.c	Fri Sep  6 07:49:31 2002
@@ -0,0 +1,2093 @@
+/*
+** Copyright (C) 2002 Distributed Systems Group, Technical University Vienna
+**
+**                    Thomas Toth (ttoth@infosys.tuwien.ac.at)
+**                    Christopher Kruegel (chris@infosys.tuwien.ac.t)
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation; either version 2 of the License, or
+** (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+** GNU General Public License for more details.
+**
+** You should have received a copy of the GNU General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include "snort.h"
+#include "engine2.h"
+
+/******************************************************************************/
+/*                     Internal Variables                                     */             
+/******************************************************************************/
+
+#define E2_STRING_BUFFERSIZE 2048
+#define E2_STRING_CRC_HASHTABLE 128
+// #define CONST_MAX_MIN_LEN 5
+#define CONST_MAX_MIN_LEN 12
+
+#define CONST_TYPE_CONTENT      0
+#define CONST_TYPE_URICONTENT   1
+
+
+#define CONST_CASE_SENSITIVE    0
+#define CONST_CASE_INSENSITIVE  1
+#define CONST_OFFSET_NOT_SET    0
+#define CONST_OFFSET_SET        1
+#define CONST_DEPTH_NOT_SET     0
+#define CONST_DEPTH_SET         1
+
+#define STANDARD_DEPTH 512
+#define AGGRESSIVENESS 0.4687
+
+
+typedef struct e2_feature_string {
+  u_int8_t * content;
+  u_int32_t content_length; // We need this, because \x00 can be in the string
+  u_int8_t type;            // CONST_TYPE_CONTENT or CONST_TYPE_URICONTENT
+  int not_set;
+  int depth;
+  int offset;
+  unsigned char nocase;
+
+  struct e2_feature_string * next;
+} E2FString;
+
+typedef struct e2_cyclic_stringlist {
+  int ruleID;
+  unsigned char * string;
+  u_int32_t length;
+  int nocase;
+  int depth;
+  int offset;
+  int not_flag_set;
+  int is_already_found;
+  //int string_number;
+  int fulfill_list_contains_not;
+  struct e2_cyclic_stringlist * next_to_fulfill;
+} E2CYCLICSTRINGLIST;
+
+typedef struct e2_hashtable_entry {
+  E2CYCLICSTRINGLIST * cyclic_list;
+  struct e2_hashtable_entry * next;
+} E2HashtableEntry;
+
+typedef struct e2_crc_hashtable {
+  E2HashtableEntry * entries[E2_STRING_CRC_HASHTABLE];
+} E2Hashtable;
+
+typedef struct _skiptable {
+  unsigned char skip[256];
+  unsigned char min_length;
+} E2StringSkipTable;
+
+typedef struct search_struct {
+  E2StringSkipTable * skip_table;
+  E2Hashtable * hash_table;
+  int nr_of_different_rules;
+
+  u_int32_t range_start;
+  u_int32_t range_end;
+
+  struct search_struct * next;
+} E2CONTENT;
+
+typedef struct content_compound {
+  E2CONTENT * content_list;
+  E2CONTENT * uri_list;
+  E2CYCLICSTRINGLIST ** cyclic_lists;
+  int cyclic_list_counter;
+
+  E2CYCLICSTRINGLIST ** rules_containing_not;
+  int rules_containing_not_counter;
+
+  E2CYCLICSTRINGLIST ** found_elements; // This is an array containing all pointers to found strings
+  int nr_of_found_elements;
+  int max_found_elements;
+
+} E2CONTENTCOMPOUND;
+
+typedef struct _offset_interval {
+    u_int32_t *active_rules;
+    u_int32_t length;
+    
+    u_int32_t rule_count;
+
+    u_int32_t start;
+    u_int32_t end;
+    struct _offset_interval *next;
+} OffsetInterval;
+
+typedef struct _string_container {
+    E2Rule *any_rules;
+    struct content_compound *compound;
+    E2Trigger *result_trigger;
+} StringContainer;
+
+u_int32_t min_length=1000;
+
+u_int8_t quick_resolve[] = {
+    0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 
+    2, 3, 2, 3, 3, 4, 1, 2, 2, 3, 
+    2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 
+    4, 5, 1, 2, 2, 3, 2, 3, 3, 4, 
+    2, 3, 3, 4, 3, 4, 4, 5, 2, 3, 
+    3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 
+    4, 5, 5, 6, 1, 2, 2, 3, 2, 3, 
+    3, 4, 2, 3, 3, 4, 3, 4, 4, 5, 
+    2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 
+    4, 5, 4, 5, 5, 6, 2, 3, 3, 4, 
+    3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 
+    5, 6, 3, 4, 4, 5, 4, 5, 5, 6, 
+    4, 5, 5, 6, 5, 6, 6, 7, 1, 2, 
+    2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 
+    3, 4, 4, 5, 2, 3, 3, 4, 3, 4, 
+    4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 
+    2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 
+    4, 5, 4, 5, 5, 6, 3, 4, 4, 5, 
+    4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 
+    6, 7, 2, 3, 3, 4, 3, 4, 4, 5, 
+    3, 4, 4, 5, 4, 5, 5, 6, 3, 4, 
+    4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 
+    5, 6, 6, 7, 3, 4, 4, 5, 4, 5, 
+    5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 
+    4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 
+    6, 7, 6, 7, 7, 8
+};
+
+void dump_content_compound(E2CONTENTCOMPOUND * src);
+
+/******************** Supported operators *************************************/
+//******************************************************************************
+//                      Internal Functions                                     *
+//******************************************************************************
+
+//*****************************************************************************
+//*                           newString                                       *
+//*                                                                           *
+//*  creates a new string E2FString object from the supplied parameters       *
+//*    performs a deep copy of the content, can therefore be used for         *
+//*    duplicating E2FStrings                                                 *
+//*****************************************************************************
+E2FString * new_string(char * string, int id, int content_length, unsigned char nocase, int offset, int depth, int not_set) {
+  E2FString * str;
+
+  str = (E2FString *) salloc(sizeof(E2FString));
+  str->content_length = content_length;
+  str->not_set        = not_set;
+  str->depth          = depth;
+  str->offset         = offset;
+  str->nocase         = nocase;
+  str->content        = (char*) salloc(content_length+1);
+
+  switch (id) {
+    case   CONST_FEATURE_ID_CONTENT:
+      str->type = CONST_TYPE_CONTENT;
+    break;
+    case   CONST_FEATURE_ID_CONTENT_URI:
+      str->type = CONST_TYPE_URICONTENT;
+    break;
+  }
+
+  memcpy(str->content,string, content_length);
+  return str;
+}
+
+E2FString * copyE2FString(E2FString * str1) {
+  return new_string(str1->content, str1->type, str1->content_length,str1->nocase, str1->offset, str1->depth, str1->not_set);
+}
+
+E2CONTENTCOMPOUND * new_content_compound () {
+  E2CONTENTCOMPOUND * comp;
+
+  comp = (E2CONTENTCOMPOUND *) salloc(sizeof(E2CONTENTCOMPOUND)); 
+  comp->content_list                  = NULL;
+  comp->uri_list                      = NULL;
+  comp->cyclic_lists                  = NULL;
+  comp->cyclic_list_counter           = 0;
+  comp->rules_containing_not          = NULL;
+  comp->rules_containing_not_counter  = 0;
+
+  return comp;
+}
+
+E2Hashtable * new_hashtable() {
+
+  E2Hashtable * hash;
+  int i;
+
+  hash = (E2Hashtable *) salloc(sizeof(E2Hashtable));
+
+  for (i=0;i<E2_STRING_CRC_HASHTABLE;i++) {
+    hash->entries[i]=NULL;
+  }
+
+  return hash;
+}
+
+E2CYCLICSTRINGLIST * new_cyclic(int ruleID, unsigned char * content, int length, int nocase, int offset, int depth, int not_flag_set) {
+  E2CYCLICSTRINGLIST * cyclic_list = NULL;
+
+  cyclic_list = salloc(sizeof(E2CYCLICSTRINGLIST));
+
+  cyclic_list->ruleID                     = ruleID;
+  cyclic_list->string                     = salloc(sizeof(unsigned char*) * length);
+  memcpy(cyclic_list->string, content, length);
+  cyclic_list->length                     = length;
+  cyclic_list->nocase                     = nocase;
+  cyclic_list->depth                      = depth;
+  cyclic_list->offset                     = offset;
+  cyclic_list->not_flag_set               = not_flag_set;
+  cyclic_list->next_to_fulfill            = NULL;
+  cyclic_list->fulfill_list_contains_not  = 0;
+  cyclic_list->next_to_fulfill            = NULL;
+  return cyclic_list;
+}
+
+E2HashtableEntry * new_hashtable_entry(  E2CYCLICSTRINGLIST * cyclic_list) {
+
+  E2HashtableEntry * entry;
+
+  entry = (E2HashtableEntry *) salloc (sizeof(E2HashtableEntry));
+  entry->cyclic_list = cyclic_list;
+  entry->next        = NULL;
+  return entry;
+}
+
+void insert_value_into_hashtable(E2Hashtable * hash, int hashvalue , E2HashtableEntry * entry) {
+
+  entry->next = hash->entries[hashvalue];
+  hash->entries[hashvalue] = entry;
+}
+
+//**************************************************************************
+//               clear_is_already_found_flag                               *
+//                                                                         *
+//  initializes the found flags of the elements in the hashtables          *
+//**************************************************************************
+void clear_is_already_found_flag(E2CONTENTCOMPOUND * search) {
+
+  int i;
+  int nr_of_different_strings=0;
+  E2CYCLICSTRINGLIST * list;
+  E2CYCLICSTRINGLIST * start;
+
+
+//  printf("%d \n",search->cyclic_list_counter);
+  for (i = 0; i < search->cyclic_list_counter; i++) {
+    list = search->cyclic_lists[i];
+
+    start = list;
+
+    do {
+      list->is_already_found = 0;
+      list = list->next_to_fulfill;
+      nr_of_different_strings++;
+    } while (list != start);
+  }
+
+//  printf("Nr_of_different_strings: %d\n", nr_of_different_strings);
+
+  search->found_elements = salloc(sizeof(E2CYCLICSTRINGLIST *) * nr_of_different_strings);
+  search->max_found_elements = nr_of_different_strings;
+
+  for (i=0; i < nr_of_different_strings; i++)
+    search->found_elements[i]=NULL;
+
+  search->nr_of_found_elements=0;
+}
+
+
+//**************************************************************************
+//               clear_is_already_found_flag                               *
+//                                                                         *
+//  initializes the found flags of the elements in the hashtables          *
+//**************************************************************************
+void clear_is_already_found_flag_optimized (E2CONTENTCOMPOUND * search) {
+
+  int i;
+  int nr_of_found_elements=search->nr_of_found_elements;
+  E2CYCLICSTRINGLIST ** array;
+  E2CYCLICSTRINGLIST * element;
+
+  array = search->found_elements;
+
+  for (i = 0; i < nr_of_found_elements; i++) {
+    element = array[i];
+    element->is_already_found=0;
+  }
+
+  search->nr_of_found_elements=0;
+}
+
+
+int check_this_element(E2CYCLICSTRINGLIST * c) {
+
+  if (c->not_flag_set == c->is_already_found) {
+    return 0;
+  }
+  return 1;
+}
+
+//**************************************************************************
+//               determine_trigger                                         *
+//                                                                         *
+//  evaluates the (uri)content elements of a rule                          *
+//                                                                         *
+// returns 0, if one or more elements don't match                          *
+// returns 1, if all elements match                                        *
+//**************************************************************************
+int determine_trigger(E2CYCLICSTRINGLIST * original) {
+  E2CYCLICSTRINGLIST * list;
+
+  if (!check_this_element(original))
+    return 0;
+
+  list = original->next_to_fulfill;
+
+  while (list != original) {
+    if (!check_this_element(original))
+      return 0;
+
+    list = list->next_to_fulfill;
+  }
+
+  return 1;
+}
+
+E2Trigger* determine_triggering_rules(E2CONTENTCOMPOUND * search, E2Trigger *trigger) {
+  E2CYCLICSTRINGLIST * c;
+  int i;
+
+  for (i = 0; i< search->rules_containing_not_counter; i++) {
+
+    c = search->rules_containing_not[i];
+
+    if (determine_trigger(c)) {
+      int ruleID = c->ruleID;
+      trigger_set_compact(trigger, ruleID);
+    }
+  }
+
+  return trigger;
+}
+
+int hash_n_characters_lowercase(unsigned char * content, int content_length, int min_size) {
+
+  int i;
+  int hash_value=0;
+
+  for (i=content_length-min_size;i<content_length;i++) {
+    unsigned char c;
+
+    c = (unsigned int) content[i];
+
+    if (isalpha(c))
+      c=tolower(c);
+
+    hash_value += c;
+  }
+
+  hash_value = hash_value % E2_STRING_CRC_HASHTABLE;
+
+  return hash_value;
+}
+
+E2CYCLICSTRINGLIST * search_cyclic(E2CONTENTCOMPOUND * content_compound, int ruleID, unsigned char * string, int length ) {
+
+  int i;
+  E2CYCLICSTRINGLIST * cyclic_list;
+
+  for (i=0; i<content_compound->cyclic_list_counter; i++) {
+    if (content_compound->cyclic_lists[i] == NULL) {
+      FatalError("ERROR => search_cyclic: content_compound->cyclic_lists[i]==NULL!");
+    }
+
+    cyclic_list = content_compound->cyclic_lists[i];
+
+    if (cyclic_list->ruleID != ruleID)
+      continue;
+
+    // So we have to search within the cyclic list (which is not yet cyclic)
+    while (cyclic_list != NULL) {
+      // Does this entry match?
+      int match_length;
+
+      match_length = cyclic_list->length;
+      if (length < match_length)
+        match_length = length;
+
+      if (!strncmp(cyclic_list->string, string, match_length)) {
+        // we found it => we can return this pointer
+        return cyclic_list;
+      }
+
+      cyclic_list = cyclic_list->next_to_fulfill;
+    }
+    return NULL;
+  }
+  return NULL;
+}
+
+void insert_cyclic_into_compound(E2CONTENTCOMPOUND * content_compound, E2CYCLICSTRINGLIST * cyclic) {
+  int i;
+  E2CYCLICSTRINGLIST * cyclic_list;
+  E2CYCLICSTRINGLIST ** new_cyclic_list;
+  int not_flag_exists;
+
+  for (i=0; i<content_compound->cyclic_list_counter; i++) {
+    if (content_compound->cyclic_lists[i] == NULL) {
+      FatalError("ERROR => insert_cyclic_into_compound: content_compound->cyclic_lists[i]==NULL!");
+    }
+
+    cyclic_list = content_compound->cyclic_lists[i];
+
+    if (cyclic_list->ruleID != cyclic->ruleID)
+      continue;
+
+    // Simply insert it into this list;
+    cyclic->next_to_fulfill = cyclic_list;
+    content_compound->cyclic_lists[i] = cyclic;
+
+    // Determine whether some element has the not_flag_set set
+    not_flag_exists = 0;
+    while (cyclic != NULL) {
+      if (cyclic->not_flag_set) {
+        not_flag_exists=1;
+        break;
+      }
+      cyclic = cyclic->next_to_fulfill;
+    }
+
+    if (not_flag_exists) {
+      cyclic = content_compound->cyclic_lists[i];
+
+      while (cyclic != NULL) {
+        cyclic->fulfill_list_contains_not = 1;
+        cyclic = cyclic->next_to_fulfill;
+      }
+    }
+
+    return;
+  }
+
+  // so we have to extend the array
+  content_compound->cyclic_list_counter ++;
+  new_cyclic_list = salloc(sizeof(E2CYCLICSTRINGLIST *) * content_compound->cyclic_list_counter);
+
+  if (content_compound->cyclic_list_counter!=1) {
+    // Copy old elements
+    memcpy(new_cyclic_list, content_compound->cyclic_lists,sizeof(E2CYCLICSTRINGLIST *) * (content_compound->cyclic_list_counter-1));
+    free(content_compound->cyclic_lists);
+  }
+
+  content_compound->cyclic_lists = new_cyclic_list;
+  content_compound->cyclic_lists[content_compound->cyclic_list_counter-1] = cyclic;
+  if (cyclic->not_flag_set)
+    cyclic->fulfill_list_contains_not = 1;
+
+  return;
+}
+
+// generate the hashtable-entries;
+E2Hashtable * generate_hashtable(E2CONTENTCOMPOUND * content_compound, E2FString ** str_array, E2Rule ** rule_array , int number_of_elements, int min_length) {
+  E2Hashtable * hash;
+  E2HashtableEntry * entry;
+  E2CYCLICSTRINGLIST * cyclic;
+  int i;
+  int hashvalue;
+
+  hash = new_hashtable();
+
+  for (i=0;i<number_of_elements;i++) {
+    E2FString * str;
+
+    str = (E2FString * ) str_array[i];
+
+    hashvalue = hash_n_characters_lowercase(str->content, str->content_length, min_length);
+
+    if ((cyclic = search_cyclic(content_compound, rule_array[i]->id, str->content, str->content_length ))== NULL) {
+      cyclic = new_cyclic(rule_array[i]->id, str->content, str->content_length, str->nocase, str->offset, str->depth, str->not_set);
+      insert_cyclic_into_compound(content_compound, cyclic);
+    }
+
+    entry = new_hashtable_entry(cyclic);
+
+    // Insert the Rule at the appropriate hashtable-pointer
+    insert_value_into_hashtable(hash, hashvalue,entry);
+  }
+
+  return hash;
+}
+
+//***********************************************************************************
+//*                                                                                 *
+//*                          perform_exact_match                                    *
+//*                                                                                 *
+//*  Searches the content for the exact word, taking nocase into account            *
+//***********************************************************************************
+void perform_exact_match(u_int8_t * content, int current_index, E2CONTENTCOMPOUND * search, E2CYCLICSTRINGLIST * entry, E2Trigger *trigger) {
+  int found;
+  int i;
+  int length;
+  unsigned char * help_ptr;
+  int offset;
+  int depth;
+
+  found=1;
+
+  if (entry == NULL) {
+    FatalError("ENTRY is NULL in perform_exact_match!");
+  }
+
+  if (entry->is_already_found)
+    return;
+
+  length = entry->length;
+  help_ptr = content+current_index;
+  offset = entry->offset;
+  depth  = entry->depth;
+
+  // Start the exact matching
+  for ( i=length-1; i>=0; i--) {
+
+    switch(entry->nocase) {
+      case 0:
+        if (entry->string[i]!=*help_ptr--)
+          return;
+      break;
+
+      case 1:
+        if (tolower(entry->string[i])!=tolower(*help_ptr--))
+          return;
+      break;
+    }
+  }
+
+  // check offset and depth
+  if (found) {
+
+    if ((current_index-entry->length+1) < offset || (current_index >= offset+depth))
+      found = 0;
+ 
+    if (found) {
+      entry->is_already_found = found;
+
+      // Track the found elements to speed up clearing
+      search->found_elements[search->nr_of_found_elements++] = entry;
+      if (search->nr_of_found_elements > search->max_found_elements)
+        FatalError("search->nr_of_found_elements > search->max_found_elements");
+
+
+      // check whether a !content entry exists in the rule
+      if (!entry->fulfill_list_contains_not) {
+        int fulfilled=1;
+        E2CYCLICSTRINGLIST * e;
+
+        e = entry->next_to_fulfill;
+        // Iterate through all the elements
+        while (e != entry) {
+          if (e->is_already_found == e->not_flag_set) {
+            fulfilled = 0;
+          }
+          e = e->next_to_fulfill;
+        }
+
+        if (fulfilled) {
+#ifdef E2_DEBUG_STRING_SEARCH
+          printf("Exact Match at rule %d\n",e->ruleID);
+#endif
+        trigger_set_compact(trigger, e->ruleID);
+        }
+      }
+
+#ifdef E2_DEBUG_STRING_SEARCH
+      printf("***Found '%s'\n",entry->string);
+#endif
+    }
+  }
+}
+
+//***********************************************************************************
+//*                                                                                 *
+//*             multiple_pattern_search                                             *
+//*                                                                                 *
+//*  Searches the whole packet for all possibly occurring keywords                  *
+//*    takes into account different search tables for the individual intervals      *
+//***********************************************************************************
+void multiple_pattern_search(unsigned char * content, int length, E2CONTENTCOMPOUND * search, E2CONTENT * tables, E2Trigger *trigger) {
+  int current_index=0;
+  int min_length;
+  E2StringSkipTable * skip_table = NULL;
+  E2Hashtable * hash_table = NULL;
+
+  unsigned char *s_ptr, *e_ptr;
+
+  skip_table = tables->skip_table;
+  hash_table = tables->hash_table;
+
+  min_length = skip_table->min_length;
+  current_index = tables->range_start + min_length-1;
+
+  while (current_index < length) {
+    int skip_value;
+    int hash_value;
+    E2HashtableEntry * entry;
+
+    if (current_index > tables->range_end) {
+      // Switch to next table
+#ifdef E2_DEBUG_STRING_SEARCH_DETAILED
+      printf("Switching to new element in the list\n");
+#endif
+
+      tables = tables->next;
+      if (tables == NULL) {
+        // We don't have any tables left now...
+        return;
+      }
+      skip_table = tables->skip_table;
+      hash_table = tables->hash_table;
+
+      min_length = skip_table->min_length;
+      current_index = tables->range_start;
+    }
+
+    // Determine the skip value
+    skip_value  = skip_table->skip[tolower(content[current_index])];
+    if (skip_value != 0) {
+      current_index += skip_value;
+      continue;
+    }
+
+    // Determine the hash
+    hash_value = 0;
+
+    e_ptr = content + current_index;
+    s_ptr = e_ptr - min_length;
+    
+    do {
+      ++s_ptr;
+      hash_value += tolower(*s_ptr);
+    } while (s_ptr != e_ptr);
+
+    hash_value = hash_value % E2_STRING_CRC_HASHTABLE;    
+
+    // hash_value = hash_n_characters_lowercase( content+current_index-min_length+1, min_length, min_length);
+
+
+#ifdef E2_DEBUG_STRING_SEARCH_DETAILED
+  printf("hash_n_characters (%d): mapping %s to %d\n",min_length,content+current_index-min_length+1, hash_value);
+#endif
+
+    // Iterate through the individual entries in the hashtable
+    entry = hash_table->entries[hash_value];
+
+    while (entry !=NULL) {
+      // Perform an exact match.
+
+#ifdef E2_DEBUG_STRING_SEARCH_DETAILED
+      printf("Performing an exact reverse match with %s at position: %d\n",entry->string,current_index);
+#endif
+
+      perform_exact_match(content, current_index, search, entry->cyclic_list, trigger);
+      entry=entry->next;
+    }
+    current_index++;
+  }
+}
+
+/******************** Optimized String Matching Functions**********************/
+
+unsigned int get_min_strlen(E2FString ** strings, int number_of_strings) {
+
+  unsigned int min_length=0xffffffff;
+  int i;
+
+  for (i=0;i<number_of_strings;i++) {
+    unsigned int tmp_value;
+
+    tmp_value = strings[i]->content_length;
+
+    if (tmp_value < min_length)
+      min_length=tmp_value;
+  }
+
+  return min_length;
+}
+
+E2StringSkipTable * new_skip_table(int min_length) {
+
+  E2StringSkipTable * skip;
+  int i;
+
+  // Allocate skip table
+  skip = (E2StringSkipTable *) salloc(sizeof(E2StringSkipTable));
+
+  skip->min_length = min_length;
+  for (i=0;i<256;i++)
+    skip->skip[i]=min_length;
+
+  return skip;
+}
+
+void skip_table_destroy(E2StringSkipTable * tmp) {
+  free (tmp);
+}
+
+E2StringSkipTable * generate_keyword_skip(E2FString * stringstr, int min_length) {
+  E2StringSkipTable * tmp;
+  int i;
+  int length;
+  char * string;
+
+  length = stringstr->content_length;
+  string = stringstr->content;
+
+  tmp = (E2StringSkipTable *) salloc(sizeof(E2StringSkipTable));
+
+  tmp->min_length=min_length;
+
+  for ( i=0; i < 256; i++) {
+    tmp->skip[i]=min_length;
+  }
+
+  for (i=length-min_length;i<length; i++) {
+    int possible_skip;
+    unsigned char c;
+
+    possible_skip = length - i-1;
+    c = (unsigned char) string[i];
+
+    if (isalpha(c))
+      c = tolower(c);
+
+    if (possible_skip < tmp->skip[(unsigned int) c ])
+      tmp->skip[(unsigned int) c] = possible_skip;
+  }
+
+  return tmp;
+}
+
+void skip_tables_merge( E2StringSkipTable * global, E2StringSkipTable *skip_for_keyword ) {
+
+  int i;
+
+  for ( i = 0; i< 256; i++) {
+    if (global->skip[i] > skip_for_keyword->skip[i])
+      global->skip[i] = skip_for_keyword->skip[i];
+  }
+}
+
+void skip_table_dump(E2StringSkipTable * skip_table, char * banner) {
+  int i;
+  int min_len;
+
+  min_len = skip_table->min_length;
+  printf("%s\n",banner);
+
+  for (i=0;i<256;i++) {
+    if (skip_table->skip[i]<min_len) {
+
+      printf("%02x (%c):%d\n",i,i,skip_table->skip[i]);
+    }
+  }
+
+  printf("\nEnd of skip_table_dump =======================\n");
+}
+
+E2StringSkipTable * generate_skip_table(E2FString ** strings, int number_of_strings, E2Rule ** rules) {
+  unsigned int min_length;
+  E2StringSkipTable * global_skip;
+  int i;
+
+
+#ifdef E2_DEBUG_STRING_TABLES_DETAILED
+  printf("****Generating skip tables\n");
+#endif
+
+  // Determine the minimal length of all strings
+  min_length= get_min_strlen(strings, number_of_strings);
+
+  if ( min_length > CONST_MAX_MIN_LEN)
+    min_length = CONST_MAX_MIN_LEN;
+
+  // Allocate and initialize the skip table
+  global_skip = new_skip_table(min_length);
+
+#ifdef E2_DEBUG_STRING_TABLES_DETAILED
+  printf("Min_length = %d\n",min_length);
+  skip_table_dump(global_skip, "Global Skip-Table");
+#endif
+
+  for (i=0; i < number_of_strings; i++) {
+    E2StringSkipTable * skip_for_keyword=NULL;
+
+    // Calculate the skip table for every keyword
+    skip_for_keyword = generate_keyword_skip(strings[i], min_length);
+
+#ifdef E2_DEBUG_STRING_TABLES_DETAILED
+    printf("%s\n",strings[i]->content);
+    skip_table_dump(skip_for_keyword, "Keyword table");
+#endif
+
+    // merge this skip table with the global one
+    skip_tables_merge( global_skip, skip_for_keyword);
+
+#ifdef E2_DEBUG_STRING_TABLES_DETAILED
+    skip_table_dump(global_skip, "Global Skip-Table");
+#endif
+
+    // Free the memory for skip_for_keyword
+    skip_table_destroy(skip_for_keyword);
+  }
+
+  return global_skip;
+}
+
+
+
+void print_contents_of_rule(E2CYCLICSTRINGLIST * entry) {
+  E2CYCLICSTRINGLIST * tmp;
+
+  tmp = entry;
+
+  printf("RuleID: %d\t",tmp->ruleID);
+  while (1) {
+    printf("%s ",tmp->string);
+
+    if (tmp->next_to_fulfill==entry)
+      break;
+
+    tmp = tmp->next_to_fulfill;
+  }
+}
+
+
+
+
+void insert_entry_into_not_list(E2CONTENTCOMPOUND * search, E2CYCLICSTRINGLIST * list) {
+  int i;
+  E2CYCLICSTRINGLIST ** array;
+
+  // Perform consistency checks
+  for (i=0; i<search->rules_containing_not_counter; i++) {
+    if (search->rules_containing_not[i]->ruleID == list->ruleID) {
+      FatalError("ERROR =>   insert_entry_into_not_list : consistency check failed, an item is inserted twice into the not list!");
+    }
+  }
+
+  search->rules_containing_not_counter++;
+
+  array = salloc(sizeof(E2CYCLICSTRINGLIST * ) * search->rules_containing_not_counter);
+  if (search->rules_containing_not_counter!=1) {
+    memcpy  ( array,
+            search->rules_containing_not,
+            sizeof(E2CYCLICSTRINGLIST * ) * (search->rules_containing_not_counter-1)
+          );
+
+    free( search->rules_containing_not);
+  }
+
+  search->rules_containing_not = array;
+  search->rules_containing_not[search->rules_containing_not_counter-1] = list;
+}
+
+void finishcycle(E2CONTENTCOMPOUND * search) {
+  int i;
+  E2CYCLICSTRINGLIST * list;
+
+  if (search == NULL) {
+    FatalError("ERROR => finishcycle: search == NULL!");
+  }
+
+  for (i = 0; i < search->cyclic_list_counter; i++) {
+    list = search->cyclic_lists[i];
+
+    if (list == NULL) {
+      FatalError("ERROR => finishcycle: list[%d] == NULL!", i);
+    }
+
+    while (list->next_to_fulfill != NULL) {
+      list = list->next_to_fulfill;
+    }
+
+    list->next_to_fulfill = search->cyclic_lists[i];
+
+
+    if (list->fulfill_list_contains_not) {
+      insert_entry_into_not_list(search, list);
+    }
+  }
+}
+
+void print_not_rules(E2CONTENTCOMPOUND *search) {
+  E2CYCLICSTRINGLIST * not_rule;
+  int i;
+
+  for (i=0 ; i < search->rules_containing_not_counter ; i++) {
+    not_rule = search->rules_containing_not[i];
+    printf("NOT-RULE:");
+    print_contents_of_rule(not_rule);
+    printf("\n");
+  }
+}
+
+void print_cyclic_lists(E2CONTENTCOMPOUND * search) {
+  int i;
+  E2CYCLICSTRINGLIST * rule;
+
+  printf("\nPrinting the cyclic lists:\n");
+
+  for (i=0 ; i < search->cyclic_list_counter ; i++) {
+    rule = search->cyclic_lists[i];
+    printf("RULE:");
+    print_contents_of_rule(rule);
+    printf("\n");
+  }
+}
+
+void link_hashtable_entries_cyclic( void * compound) {
+
+  E2CONTENTCOMPOUND * search;
+
+  search = (E2CONTENTCOMPOUND *) compound;
+  finishcycle(search);
+
+  // Initialize the 'is_already_found' flag
+  clear_is_already_found_flag(search);
+
+  // now print the things that can only be evaluated
+  // AFTER everything has been searched for
+#ifdef E2_DEBUG_STRING_SEARCH_DETAILED
+  print_cyclic_lists(search);
+  print_not_rules(search);
+#endif
+}
+
+//******************************************************************************
+//*                                                                            *
+//*                                  build_table                               *
+//*                                                                            *
+//* builds all the tables to match the content of several rules in an interval *
+//*                                                                            *
+//*******************************************************************************
+void * build_table(E2CONTENTCOMPOUND *content_compound, E2Rule * rules_of_node, int id, int start, int end) {
+  E2CONTENT * cont;
+  E2Rule * r;
+  E2FString * e2_string;
+  int content_counter=0;
+  int index_of_content;
+  int i;
+  int nr_of_different_rules=0;
+  int offset;
+  E2FString ** e2string_array;
+  E2Rule ** e2rule_array;
+
+
+  if (rules_of_node == NULL) {
+    FatalError("ERROR => build_table: called with rules_of_node == NULL!");
+    return NULL;
+  }
+
+  if (id!= CONST_FEATURE_ID_CONTENT && id != CONST_FEATURE_ID_CONTENT_URI)
+    FatalError("Called build_table with type %d\n", id);
+
+  cont = (E2CONTENT *) salloc(sizeof(E2CONTENT));
+  r = rules_of_node;
+
+  while (r!=NULL) {
+    index_of_content = e2_index_mapping_table[r->proto][CONST_FEATURE_ID_CONTENT];
+
+    if (r->features[index_of_content]!=NULL) {
+      e2_string =(E2FString * )  ((E2Feature *) r->features[index_of_content])->f_val;
+      nr_of_different_rules++;
+
+      while (e2_string!=NULL) {
+        offset = e2_string->offset;
+
+        if ((id == CONST_FEATURE_ID_CONTENT && e2_string->type == CONST_TYPE_CONTENT) ||
+            (id == CONST_FEATURE_ID_CONTENT_URI && e2_string->type == CONST_TYPE_URICONTENT))
+          content_counter++;
+
+        e2_string = e2_string->next;
+      }
+    }
+    r=r->next;
+  }
+
+  if (content_counter==0) {
+    free(cont);
+    return NULL;
+  }
+
+  r = rules_of_node;
+  e2string_array = salloc(sizeof(E2FString *) * content_counter);
+  e2rule_array   = salloc(sizeof(E2Rule *)    * content_counter);
+
+  i=0;
+
+  while (r!=NULL) {
+    index_of_content = e2_index_mapping_table[r->proto][CONST_FEATURE_ID_CONTENT];
+
+    if (r->features[index_of_content]!=NULL) {
+
+      e2_string =(E2FString *)  ((E2Feature *) r->features[index_of_content])->f_val;
+
+      while (e2_string!=NULL) {
+        offset = e2_string->offset;
+
+        if ((id == CONST_FEATURE_ID_CONTENT && e2_string->type == CONST_TYPE_CONTENT) ||
+            (id == CONST_FEATURE_ID_CONTENT_URI && e2_string->type == CONST_TYPE_URICONTENT)) {
+          e2string_array [i] = e2_string;
+          e2rule_array   [i] = r;
+          i++;
+        }
+
+        e2_string = e2_string->next;
+      }
+    }
+    r=r->next;
+  }
+
+  cont->skip_table            = generate_skip_table(e2string_array, content_counter, e2rule_array);
+  cont->hash_table            = generate_hashtable(content_compound, e2string_array, e2rule_array, content_counter, cont->skip_table->min_length);
+  cont->nr_of_different_rules = nr_of_different_rules;
+  cont->range_start           = start;
+  cont->range_end             = end;
+  cont->next                  = NULL;
+
+  free (e2string_array);
+  free (e2rule_array);
+
+  return cont;
+}
+
+E2Trigger * search_all_content(  unsigned char *   payload,
+                                      unsigned int      payload_length,
+                                      unsigned char *   uriload,
+                                      unsigned int      uri_length,
+                                      void          *   e2searchtables,
+                                      E2Trigger * trigger) {
+
+  E2CONTENTCOMPOUND * compound;
+
+  compound = (E2CONTENTCOMPOUND *) e2searchtables;
+
+  // Initialize the found flag in the cyclic lists
+  clear_is_already_found_flag_optimized ( compound );
+  //clear_is_already_found_flag ( compound);
+
+#ifdef E2_DEBUG_STRING_SEARCH
+  printf("Searching normal content\n");
+#endif
+  // Search the packet for the contents
+  if (compound->content_list != NULL)
+    multiple_pattern_search( payload,payload_length, compound, compound->content_list, trigger);
+
+#ifdef E2_DEBUG_STRING_SEARCH
+  printf("Searching uri content\n");
+#endif
+
+  clear_is_already_found_flag_optimized ( compound );
+
+
+  if (compound->uri_list != NULL) {
+    if (uriload != NULL)
+        multiple_pattern_search( uriload,uri_length, compound, compound->uri_list, trigger);
+    else
+        multiple_pattern_search( payload, payload_length, compound,  compound->uri_list, trigger);
+  }
+
+  // Find out which rules trigger and set the bits accordingly in the packed array
+  return determine_triggering_rules (compound, trigger);
+}
+
+void print_list(OffsetInterval *list)
+{
+    /*
+    printf("\nInterval List: ");
+    for ( ; list != NULL; list = list->next) 
+	printf("<[%d, %d[; %d> ", list->start, list->end, list->rule_count);
+    printf("\n");
+    */
+}
+
+OffsetInterval* offset_clone(OffsetInterval *inter)
+{
+    OffsetInterval *result;
+
+    result = calloc(1, sizeof(OffsetInterval));
+
+    result->active_rules = calloc(inter->length, sizeof(u_int32_t));
+    memcpy(result->active_rules, inter->active_rules, (inter->length * sizeof(u_int32_t)));
+    
+    result->length = inter->length;
+    result->rule_count = inter->rule_count;
+
+    return result;
+}
+
+void active_create(OffsetInterval *inter, u_int32_t length)
+{
+    inter->active_rules = calloc(((length >> 5) + 1), sizeof(u_int32_t));
+
+    inter->length = ((length >> 5) + 1);
+    inter->rule_count = 0;
+}
+
+void active_set(OffsetInterval *inter, u_int16_t id)
+{
+#ifdef E2DEBUG
+    if ((id >> 5) >= inter->length) {
+	printf("Fatal: active_set: index (%d) out of bounds (%d)\n", id, inter->length);
+	exit(1);
+    }
+#endif
+    
+    if (!(inter->active_rules[(id >> 5)] &  (1 << (id & 0x1f)))) {
+	inter->active_rules[(id >> 5)] |= (1 << (id & 0x1f));
+	++inter->rule_count;
+    }
+}
+
+u_int32_t active_and_count(OffsetInterval *oi1, OffsetInterval *oi2)
+{
+    u_int32_t result, word;
+    u_int8_t byte;
+    int i, j;
+
+    result = 0;
+
+    for (i = 0; i < oi1->length; ++i) {
+
+	word = oi1->active_rules[i] & oi2->active_rules[i];
+	
+	if (word != 0) {
+	    for (j = 0; j < 4; ++j) {
+		byte = ((word >> 8*j) & 0xff);
+		result += quick_resolve[byte];
+	    }
+	}
+    }
+
+    return result;
+}
+
+u_int32_t* active_or_count(OffsetInterval *oi1, OffsetInterval *oi2, u_int32_t *result)
+{
+    u_int8_t byte;
+    int i, j;
+        
+    *result = 0;
+    
+    for (i = 0; i < oi1->length; ++i) {
+	
+	oi1->active_rules[i] |= oi2->active_rules[i];
+	
+	if (oi1->active_rules[i] != 0) {
+	    for (j = 0; j < 4; ++j) {
+		byte = ((oi1->active_rules[i] >> (8*j)) & 0xff);
+		*result += quick_resolve[byte];
+	    }
+	}
+    }
+    
+    free(oi2->active_rules);
+    return oi1->active_rules;
+}
+
+
+OffsetInterval* add_to_offsetlist(OffsetInterval *list, E2Rule *rule, u_int32_t r_lower, u_int32_t r_upper, u_int32_t length)
+{
+    OffsetInterval *element, *toi1, *toi2;
+
+    if (list == NULL) {
+	
+    	element = calloc(1, sizeof(OffsetInterval));
+	element->start = r_lower;
+	element->end = r_upper;
+
+	active_create(element, length);
+	active_set(element, rule->id);
+
+	return element;
+    }
+    else {
+
+	toi1 = toi2 = list;
+
+	while (r_lower < r_upper) {
+
+	    while (toi1 != NULL && r_lower >= toi1->end)
+		  	toi1 = toi1->next;
+
+	    if ((toi1 == NULL) || (r_upper <= toi1->start)) {
+		
+		element = calloc(1, sizeof(OffsetInterval));
+		
+		element->start = r_lower;
+		element->end = r_upper;
+
+		active_create(element, length);
+		active_set(element, rule->id);
+
+		if (toi1 != list) {
+		    while (toi2 != NULL && toi2->next != toi1) 
+			toi2 = toi2->next;
+		    element->next = toi1;
+		    toi2->next = element;
+		    return list;
+		}
+		else {
+		    element->next = list;
+		    return element;
+		}
+	    }
+	    else if (r_lower < toi1->start) {
+
+		element = calloc(1, sizeof(OffsetInterval));
+
+		element->start = r_lower;
+		if (toi1->start >= r_upper) {
+		    element->end = r_upper;
+		    r_lower = r_upper;
+		}
+		else {
+		    element->end = toi1->start;
+		    r_lower = toi1->start;
+		}
+
+		active_create(element, length);
+		active_set(element, rule->id);
+		
+		if (toi1 == list) {
+		    element->next = list;
+		    list = element;
+		}
+		else {
+
+		    while (toi2 != NULL && toi2->next != toi1) 
+			toi2 = toi2->next;
+		    toi2->next = element;
+		    element->next = toi1;
+		}
+	    }
+	    else if (r_lower == toi1->start) {
+		
+		if (r_upper >= toi1->end) {
+		    active_set(toi1, rule->id);
+		    r_lower = toi1->end;
+		}
+		else {
+
+		    element = offset_clone(toi1);
+
+		    element->start = r_upper;
+		    element->end = toi1->end;
+		    
+		    toi1->end = r_upper;
+
+		    element->next = toi1->next;
+		    toi1->next = element;
+		    
+		    active_set(toi1, rule->id);
+		    r_lower = r_upper;
+		}
+	    }
+	    else  { /* r_lower > ti1->start */
+		
+		element = offset_clone(toi1);
+
+		element->start = r_lower;
+		element->end = toi1->end;
+		    
+		toi1->end = r_lower;
+		
+		element->next = toi1->next;
+		toi1->next = element;
+	    }
+	}
+    }
+    return list;
+}
+
+
+OffsetInterval* find_cheapest_merge(OffsetInterval *list, u_int32_t *cost)
+{
+    u_int32_t increase, depth, unique_left, unique_right, union_left_right, cut_left_right;
+    OffsetInterval *cheapest_ptr, *toi, *next;
+
+#ifdef E2DEBUG 
+    if (list == NULL) {
+	printf("Fatal: find_cheapest_merge: attempt to optimize null list\n");
+	exit(1);
+    }
+#endif
+
+    *cost = CONST_MAX_INT_32;
+    cheapest_ptr = NULL;
+    depth = 0;
+    
+    for (toi = list; toi->next != NULL; toi = toi->next) {
+
+	next = toi->next;
+
+	if ((next->next == NULL) && (next->end > (CONST_MAX_INT_16 + 1 - STANDARD_DEPTH))) {
+	    depth = next->end;
+	    next->end = (next->start + STANDARD_DEPTH);
+	}
+
+	cut_left_right = active_and_count(toi, next);
+
+	unique_left = toi->rule_count - cut_left_right;
+	unique_right = next->rule_count - cut_left_right;
+	union_left_right = toi->rule_count + next->rule_count - cut_left_right;
+
+	increase = (((toi->end - toi->start) * unique_right) + 
+		    ((next->end - next->start) * unique_left) +
+		    ((next->start - toi->end) * union_left_right));
+	
+	if (depth != 0) {
+	    next->end = depth;
+	    depth = 0;
+	}
+
+	if (increase < *cost) {
+	    *cost = increase;
+	    cheapest_ptr = toi;
+	}
+    }
+
+    return cheapest_ptr;
+}
+
+void do_merge(OffsetInterval *element)
+{
+    OffsetInterval *next;
+    u_int32_t count;
+
+#ifdef E2DEBUG 
+    if ((element == NULL) || (element->next == NULL)) {
+	printf("Fatal: do merge: attempt to merge element with null successor\n");
+	exit(1);
+    }
+#endif
+    next = element->next;
+
+    /* active_or frees both active_rule arrays */
+    count = 0;
+    element->active_rules = active_or_count(element, next, &count);
+    element->rule_count = count;
+    
+    element->end = next->end;
+
+    element->next = next->next;
+
+    free(next);
+}
+
+OffsetInterval* merge_interval_lists(OffsetInterval *list)
+{
+    OffsetInterval *toi, *best;
+    u_int32_t table_count, search_cost, add_cost, depth; 
+    u_int8_t optimize;
+    
+    table_count = search_cost = depth = 0;     
+    for (toi = list; toi != NULL; toi = toi->next) {
+	++table_count;
+
+	if ((toi->next == NULL) && (toi->end > (CONST_MAX_INT_16 + 1 - STANDARD_DEPTH))) {
+	    depth = toi->end;
+	    toi->end = (toi->start + STANDARD_DEPTH);
+	}
+	
+	search_cost += (toi->end - toi->start) * toi->rule_count;
+
+	if (depth != 0) {
+	    toi->end = depth;
+	    depth = 0;
+	}
+    }
+
+    for (optimize = 1; optimize != 0; ) {
+	
+	best = find_cheapest_merge(list, &add_cost);
+
+	if ((best != NULL) && 
+	    ((((double) search_cost + (double) add_cost) / (double) search_cost) < 
+	     (AGGRESSIVENESS * table_count))) {
+	    
+	    do_merge(best);
+
+	    print_list(list);
+	    --table_count;
+	    search_cost += add_cost;
+	}
+	else
+	    optimize = 0;
+    }
+
+    return list;
+}
+
+
+
+
+/******************************************************************************/
+/*                            API Functions                                   */             
+/******************************************************************************/
+
+//**************************************************************************
+//               search_all_content_in_packet                              *
+//                                                                         *
+//  searches a packet for all the strings specified in e2searchtables      *
+//                                                                         *
+//**************************************************************************
+E2Trigger * search_all_content_in_packet(Packet * p, void * e2searchtables, E2Trigger *trigger) {
+
+  E2PAYLOAD * ret;
+
+  unsigned char * payload;
+  unsigned int payload_length;
+  unsigned char * uriload;
+  unsigned int uri_length;
+
+  ret = extract_content (p);
+  payload = ret->content;
+  payload_length = ret->content_length;
+  free(ret);
+
+  ret = extract_uricontent (p);
+  uriload = ret->content;
+  uri_length = ret->content_length;
+  free(ret);
+
+  //  return the packed array
+  return search_all_content(payload, payload_length, uriload, uri_length, e2searchtables, trigger);
+}
+
+E2CONTENT * insert_content_into_content_list( E2CONTENT * old, E2CONTENT * element)
+{
+
+    E2CONTENT *last;
+    
+    if (element == NULL)
+      return old;
+
+    if (old == NULL)
+      return element;
+    else {
+      for (last = old; last->next != NULL; last = last->next);
+        last->next = element;
+    }
+
+    return old;
+}
+
+
+
+void * build_tables(void * old_ptr, E2Rule * rules_of_node, int start, int end) {
+  E2CONTENTCOMPOUND * old;
+  E2CONTENT * content;
+
+  old = (E2CONTENTCOMPOUND *) old_ptr;
+
+  if (old == NULL) {
+    old = new_content_compound();
+  }
+
+  if (rules_of_node == NULL)
+    FatalError("ERROR => build_tables: rules_of_node is NULL, and no table should be generated for this!");
+
+  // Insert the tables
+  content = build_table(old, rules_of_node, CONST_FEATURE_ID_CONTENT, start, end);
+  if (content != NULL)
+    old->content_list = insert_content_into_content_list(old->content_list, content);
+
+  content = build_table(old, rules_of_node, CONST_FEATURE_ID_CONTENT_URI, start, end);
+  if (content != NULL)
+    old->uri_list = insert_content_into_content_list(old->uri_list, content);
+
+//  dump_content_compound(old);
+
+  return (void *) old;
+}
+
+
+/**************************** Decision Tree Building **************************/
+
+E2SuccessorNode* string_pre_split_node(E2Rule *rules, u_int8_t feat)
+{
+    E2Feature *feature;
+    E2Rule *current_rule;
+    E2SuccessorNode *any, *defined;
+
+    any = calloc(1, sizeof(E2SuccessorNode));
+    
+    for (current_rule = rules; current_rule != NULL; current_rule = current_rule->next) {
+
+      feature = current_rule->features[feat];
+
+      if (feature == NULL) {
+        any->rules = list_add(any->rules, rule_clone(current_rule));
+        ++any->rule_count;
+      }
+      else {
+	  defined = calloc(1, sizeof(E2SuccessorNode));
+	  defined->rules = list_add(defined->rules, rule_clone(current_rule));
+	  ++defined->rule_count;
+	  
+	  defined->next = any->next;
+	  any->next = defined;
+      }
+    }
+
+    return any;
+}
+
+void string_undo_split_node(E2SuccessorNode *succs)
+{
+    E2SuccessorNode *tsn1, *tsn2;
+    E2Rule *tr1, *tr2; 
+
+    for (tsn1 = succs; tsn1 != NULL; ) {
+	
+	/* remove rules */
+	for (tr1 = tsn1->rules; tr1 != NULL; ) {
+	    tr2 = tr1->next;
+	    rule_destroy(tr1);
+	    tr1 = tr2;
+	}
+
+	tsn2 = tsn1->next;
+	free(tsn1);
+	tsn1 = tsn2;
+    }
+}
+
+void* string_final_split_node(E2SuccessorNode *succs, u_int8_t feat, int *feature_map, u_int8_t proto)
+{
+    E2Rule *rule, *rules, *tr;
+    OffsetInterval *list, *lptr;
+    E2FString *string;
+    u_int32_t start, end, rule_length;
+    u_int16_t rule_id;
+    void *content;
+    E2SuccessorNode *tsn;
+    StringContainer *container;
+    
+    /* init variables */
+    rule_length = snort_rule_count;
+
+    container = calloc(1, sizeof(StringContainer));
+    container->result_trigger = trigger_create(rule_length);
+    
+    /* remove feature from further decision process */
+    feature_map[feat] = 1;
+
+    for (rule = succs->rules; rule != NULL; rule = rule->next) 
+	container->any_rules = list_add(container->any_rules, rule_clone(rule));
+
+    if (succs->next != NULL) {
+	/* split rules into intervals */
+	list = NULL;
+	for (tsn = succs->next; tsn != NULL; tsn = tsn->next) {
+
+	    rule = tsn->rules;
+
+#ifdef E2DEBUG
+	    if (rule->features[feat] == NULL) {
+		printf("Fatal: string_final_split_node: string feature null in non any-list rule\n");
+		exit(1);
+	    }
+#endif
+	    string = (E2FString *) rule->features[feat]->f_val;
+
+#ifdef E2DEBUG
+	    if (string == NULL) {
+		printf("Fatal: string_final_split_node: string feature null in non any-list rule\n");
+		exit(1);
+	    }
+#endif
+	
+	    start = string->offset;
+	    end = (string->depth == CONST_MAX_INT_16 + 1) ? (CONST_MAX_INT_16 + 1) : (start + string->depth);
+	    
+	    list = add_to_offsetlist(list, rule, start, end, rule_length);
+	}
+
+#ifdef E2DEBUG
+	print_list(list);
+#endif
+
+	/* optimize tables */ 
+	//list = merge_interval_lists(list);
+
+#ifdef E2DEBUG
+	print_list(list);
+#endif
+
+	content = NULL;
+	for (lptr = list; lptr != NULL; lptr = lptr->next) {
+
+	    rules = NULL;
+	    for (tsn = succs->next; tsn != NULL; tsn = tsn->next) {
+
+#ifdef E2DEBUG
+		if (tsn->rules == NULL) {
+		    printf("Fatal: string_final_split_node: assetion failed - rules are null\n");
+		    exit(1);
+		}
+#endif
+		rule_id = tsn->rules->id;
+		if (lptr->active_rules[(rule_id >> 5)] & (1 << (rule_id & 0x1f)))
+		    rules = list_add(rules, rule_clone(tsn->rules));
+	    }
+		    
+	    content =  build_tables(content, rules, lptr->start, lptr->end);
+	    
+	    for (tr = rules; tr != NULL; ) {
+		rules = tr->next;
+		rule_destroy(tr);
+		tr = rules;
+	    }
+	}
+
+	link_hashtable_entries_cyclic(content);
+
+	/* delete helper list */
+	for (lptr = list; lptr != NULL; ) {
+	    
+	    list = lptr->next;
+	    free(lptr->active_rules);
+	    free(lptr);
+	    lptr = list;
+	}
+
+	/* put feature back into decision process */
+	feature_map[feat] = 0;
+
+	container->compound = (E2CONTENTCOMPOUND *) content;
+	return container;
+    }
+    else {
+
+	/* put feature back into decision process */
+	feature_map[feat] = 0;
+
+	return container;
+    }
+}
+
+
+/**************************** Decision Tree Navigation ************************/
+
+E2Trigger * string_navigate(E2TreeNode *node, Packet *p)
+{
+    StringContainer *container;
+
+    container = (StringContainer *) node->private; 
+    memcpy(container->result_trigger->rules, node->trigger->rules, (node->trigger->length * sizeof(u_int32_t)));
+
+    return search_all_content_in_packet(p, container->compound, container->result_trigger);
+}
+
+
+/**************************** Parse Functions *********************************/
+
+int hex_to_dec(unsigned char * data) {
+
+  switch(*data) {
+    case '0':
+      return 0;
+    case '1':
+      return 1;
+    case '2':
+      return 2;
+    case '3':
+      return 3;
+    case '4':
+      return 4;
+    case '5':
+      return 5;
+    case '6':
+      return 6;
+    case '7':
+      return 7;
+    case '8':
+      return 8;
+    case '9':
+      return 9;
+    case 'a':
+      return 10;
+    case 'b':
+      return 11;
+    case 'c':
+      return 12;
+    case 'd':
+      return 13;
+    case 'e':
+      return 14;
+    case 'f':
+      return 15;
+    case 'A':
+      return 10;
+    case 'B':
+      return 11;
+    case 'C':
+      return 12;
+    case 'D':
+      return 13;
+    case 'E':
+      return 14;
+    case 'F':
+      return 15;
+    default:
+//      FatalError("ERROR => hex_to_dec: could not convert character: %c\n",*data);
+      return -1;
+  }
+
+  return -1;
+}
+
+int get_hex_number(unsigned char * data) {
+  unsigned int first, second;
+
+  first = hex_to_dec(data);
+  if (first==-1)
+    return -1;
+
+  second = hex_to_dec(data+1);
+  if (second==-1)
+    return -1;
+
+  return first*16+second;
+}
+
+unsigned char * hex_parse(unsigned char *data, unsigned char * tmp_buf,int * index_into_tmp_buf) {
+  int start_block_index;
+
+  start_block_index = * index_into_tmp_buf;
+
+  while ((*data)!=0 && (*data)!='|') {
+    int number;
+
+    if (*data==' ') {
+      data++;
+      start_block_index = * index_into_tmp_buf;
+
+      continue;
+    }
+
+    number = get_hex_number(data);
+    if (number==-1) {
+      if ((hex_to_dec(data) != -1) && (*(data+1)==' ' || *(data+1)=='|')) {
+        u_int8_t tmp_value;
+        u_int8_t saved_value;
+        int tmp_index;
+
+        tmp_index = start_block_index;
+        tmp_value = 0;
+
+
+        do {
+          saved_value = tmp_buf[tmp_index];
+          tmp_buf[tmp_index] = (saved_value >> 4) | tmp_value;
+          tmp_value = ((saved_value &  15) << 4);
+          tmp_index ++;
+        } while (tmp_index < *index_into_tmp_buf);
+
+         tmp_buf[*index_into_tmp_buf] = tmp_value | hex_to_dec(data);
+      }
+      else {
+        FatalError("ERROR: hex_parse: %s is not a valid hex-string!",data);
+      }
+    }
+    else {
+      tmp_buf[*index_into_tmp_buf]=number;
+    }
+
+    (*index_into_tmp_buf)++;
+    if (*index_into_tmp_buf>E2_STRING_BUFFERSIZE)
+      FatalError("ERROR => hex_parse: while parsing string hexcode %s the buffer has been overflowed. Either recompile snort with an increased E2_STRING_BUFFERSIZE (which is now %d) or make your rule smaller.",*data,E2_STRING_BUFFERSIZE);
+
+    data=data+2;;
+  }
+
+  if (*data=='|')
+    data++;
+
+  return data;
+}
+
+E2Alternative * find_last_content_alternative(E2Alternative * list, char * routine, int id) {
+  E2Alternative * tmp;
+  E2Alternative * last_string=NULL;
+
+  // Search through the alternatives, find the last content section and set the nocase flag.
+  tmp = list;
+  while (tmp!=NULL) {
+
+    if (id == -1) {
+      if ( (tmp->f->f_id == CONST_FEATURE_ID_CONTENT) ||
+           (tmp->f->f_id == CONST_FEATURE_ID_CONTENT_URI) ) {
+        last_string = tmp;
+      }
+    }
+    else {
+      if ( tmp->f->f_id == id)
+        last_string = tmp;
+    }
+
+    tmp=tmp->right;
+  }
+
+  if (routine !=NULL &&last_string==NULL)
+    FatalError("ERROR => nocase_parse: found '%s', but content was not stated.", routine);
+
+
+  return last_string;
+}
+
+E2Alternative * nocase_parse(unsigned char * data, int id, u_int8_t proto) {
+  E2Alternative * last_string=NULL;
+  E2FString  * str;
+
+  // Search through the alternatives, find the last content section and set the nocase flag.
+  last_string = find_last_content_alternative(e2_alternatives_list, "nocase", -1);
+
+  str = (E2FString * ) last_string->f->f_val;
+  str->nocase = 1;
+
+  return NULL;
+}
+
+E2Alternative * offset_parse(unsigned char * data, int id, u_int8_t proto) {
+  E2Alternative * last_string=NULL;
+  E2FString  * str;
+  int value;
+
+  // Search through the alternatives, find the last content section and set the nocase flag.
+  last_string = find_last_content_alternative(e2_alternatives_list, "offset", -1);
+
+  // Parse the offset value
+  sscanf(data, "%d",&value);
+
+  str = (E2FString * ) last_string->f->f_val;
+  str->offset = value;
+
+  return NULL;
+}
+
+E2Alternative * depth_parse(unsigned char * data, int id, u_int8_t proto) {
+  E2Alternative * last_string=NULL;
+  E2FString  * str;
+  int value;
+
+  // Search through the alternatives, find the last content section and set the nocase flag.
+  last_string = find_last_content_alternative(e2_alternatives_list, "depth", -1);
+
+  // Parse the offset value
+  sscanf(data, "%d",&value);
+
+  str = (E2FString * ) last_string->f->f_val;
+  str->depth = value; //  + str->content_length;
+
+  return NULL;
+}
+
+E2Alternative * string_parse(unsigned char * data, int id, u_int8_t proto) {
+  E2Alternative * alt=NULL;
+
+  int index_into_tmp_buf=0;
+  int not_flag_set=0;
+
+  unsigned char tmp_buf[E2_STRING_BUFFERSIZE];
+  E2Alternative * last_string=NULL;
+
+  bzero(tmp_buf,E2_STRING_BUFFERSIZE);
+
+  while (*data==' ')
+    data++;
+
+  if (*data=='!') {
+    not_flag_set=1;
+    data++;
+  }
+
+  while (*data==' ')
+    data++;
+
+  if (*data !='"')
+    FatalError("ERROR => string_parse: string has to start with \", encountered %s\n",data);
+
+  data++;
+
+  // end_ptr = strrchr (data,'"');
+
+  while (*data != '\0') { // end_ptr) {
+
+    switch (*data) {
+      case '|':
+        data=data+1;
+        data = hex_parse(data, tmp_buf, &index_into_tmp_buf);
+        break;
+
+      case '\\':
+        tmp_buf[index_into_tmp_buf] = *(data+1);
+        if (*(data+1)=='\0') // end_ptr)
+          FatalError("Encountered \\ at the end of the string!");
+
+        index_into_tmp_buf+=1;
+        if (index_into_tmp_buf>E2_STRING_BUFFERSIZE)
+          FatalError("ERROR => hex_parse: while parsing escaped content %s the buffer has been overflowed. Either recompile snort with an increased E2_STRING_BUFFERSIZE (which is now %d) or make your rule smaller.",data,E2_STRING_BUFFERSIZE);
+        data=data+2;
+        break;
+
+      default:
+        tmp_buf[index_into_tmp_buf++] = *(data++);
+        if (index_into_tmp_buf>E2_STRING_BUFFERSIZE)
+          FatalError("ERROR => hex_parse: while parsing string hexcode %s the buffer has been overflowed. Either recompile snort with an increased E2_STRING_BUFFERSIZE (which is now %d) or make your rule smaller.",*data,E2_STRING_BUFFERSIZE);
+    }
+  }
+
+  // Is there already a E2Alternative that contains a content description?
+
+  // Search through the alternatives, find the last content section
+  last_string = find_last_content_alternative(e2_alternatives_list, NULL, CONST_FEATURE_ID_CONTENT);
+
+  if (last_string==NULL) {
+    alt = salloc(sizeof(E2Alternative));
+    alt->alternatives_counter=1;
+    alt->f=new_feature(CONST_STRING, CONST_FEATURE_ID_CONTENT, new_string(tmp_buf, id, index_into_tmp_buf,CONST_CASE_SENSITIVE, 0, 65536, not_flag_set));
+    alt->down=NULL;
+    alt->right=NULL;
+    return alt;
+  }
+  else {
+    E2FString *t;
+    t = (E2FString *) last_string->f->f_val;
+    while (t->next != NULL)
+      t=t->next;
+
+    t->next = new_string(tmp_buf, id, index_into_tmp_buf,CONST_CASE_SENSITIVE, 0, 65536, not_flag_set);
+    return NULL;
+  }
+}
+
+/**************************** Maintenance Functions ***************************/
+void* string_clone(void *fval)
+{
+    printf("Fatal: string_clone: not implemented\n");
+    exit(0);
+}
+
+void string_destroy(E2Feature *feature)
+{
+  E2FString *str;
+
+#ifdef E2DEBUG
+  if (feature->f_type != CONST_STRING) 
+      FatalError("Fatal: string_free: illegal feature type <%d>\n",feature->f_type);
+#endif
+
+  if (--feature->ref_count > 0)
+      return;
+  else {
+      str = (E2FString * ) feature->f_val;
+      
+      if (str) {
+	  if (str->content)
+	      free(str->content);
+	  free(str);
+      }
+      free(feature);
+  }
+}
+ 
+void string_print(E2Feature *feature)
+{
+  E2FString *t;
+  int i;
+
+  t= (E2FString *) feature->f_val;
+
+  while (t!=NULL) {
+    if (t->not_set)
+      printf("!");
+
+    printf("|");
+
+    for (i=0;i<t->content_length;i++)
+      printf("%02x ",t->content[i]);
+
+    printf("|");
+
+    if (t->nocase)
+      printf(" nocase");
+
+    if (t->offset!=-1)
+      printf(" offset: %d",t->offset);
+
+    if (t->depth !=-1)
+      printf(" depth: %d",t->depth);
+
+    if (t->next !=NULL)
+      printf(" -> ");
+
+    t=t->next;
+  }
+}
+
+void string_debug(E2TreeNode *node)
+{
+    /* bug(?) - here a routine that displays the relevant node infos */
+}
+
+void string_propagate(E2TreeNode *node, E2Trigger *t, void *caller)
+{
+    E2Trigger *trigger;
+    E2Rule *rule, *tr;
+    StringContainer *container;
+
+#ifdef E2DEBUG
+    if (node->visited) {
+	printf("Fatal: string_propagate: %p called twice - this time by %p\n", node, caller);
+	exit(1);
+    }
+    else 
+	node->visited = 1;
+#endif
+
+    trigger = trigger_clone(t);
+
+    for (rule = node->fire; rule != NULL; rule = rule->next) {
+	printf("Error - string\n");
+	exit(1);
+	trigger_set(trigger, rule->id);
+    }
+
+    container = (StringContainer *) node->private;
+    for (rule = container->any_rules; rule != NULL; rule = rule->next) 
+	trigger_set(trigger, rule->id);
+    
+
+    trigger_compact(trigger);
+    node->trigger = trigger;
+    
+    for (rule = node->fire; rule != NULL; ) {
+	tr = rule->next;
+	rule_destroy(rule);
+	rule = tr;
+    }
+    for (rule = container->any_rules; rule != NULL; ) {
+	tr = rule->next;
+	rule_destroy(rule);
+	rule = tr;
+    } 
+}
diff -Naur snort-1.8.7.orig/e2_help.c snort-1.8.7p1/e2_help.c
--- snort-1.8.7.orig/e2_help.c	Wed Dec 31 16:00:00 1969
+++ snort-1.8.7p1/e2_help.c	Fri Sep  6 06:54:22 2002
@@ -0,0 +1,709 @@
+#include "snort.h"
+#include "engine2.h"
+   
+/******************************************************************************/
+/*                    Global Variables                                        */
+/******************************************************************************/
+
+struct e2_feature_map e2_map_icmp[] = {
+    {CONST_STRING_DSIZE,          CONST_FEATURE_ID_DSIZE,         CONST_INT16, extract_dsize},
+    {CONST_STRING_IP_SRCIP,       CONST_FEATURE_ID_IP_SRCIP,      CONST_IPv4, extract_srcip},
+    {CONST_STRING_IP_DSTIP,       CONST_FEATURE_ID_IP_DSTIP,      CONST_IPv4, extract_dstip},
+    {CONST_STRING_IP_OPTIONS,     CONST_FEATURE_ID_IP_OPTIONS,    CONST_IPOPTION, extract_ipoptions},
+    {CONST_STRING_IP_FRAG_FLAGS,  CONST_FEATURE_ID_IP_FRAG_FLAGS, CONST_IPFRAG, extract_ipfragbits},
+    {CONST_STRING_IP_ID,          CONST_FEATURE_ID_IP_ID,           CONST_INT16, extract_ipid},
+    {CONST_STRING_CONTENT,        CONST_FEATURE_ID_CONTENT,       CONST_STRING, extract_content},
+    {CONST_STRING_CONTENT_URI,    CONST_FEATURE_ID_CONTENT_URI,   CONST_STRING, extract_uricontent},
+    {CONST_STRING_NOCASE,         CONST_FEATURE_ID_NOCASE,        CONST_NOCASE, NULL},
+    {CONST_STRING_OFFSET,         CONST_FEATURE_ID_OFFSET,        CONST_OFFSET, NULL},
+    {CONST_STRING_DEPTH,          CONST_FEATURE_ID_DEPTH,         CONST_DEPTH, NULL},
+    {CONST_STRING_ICMP_ID,        CONST_FEATURE_ID_ICMP_ID,       CONST_INT32, extract_icmp_id},
+    {CONST_STRING_ICMP_SEQ,       CONST_FEATURE_ID_ICMP_SEQ,      CONST_INT32, extract_icmp_seq},
+    {CONST_STRING_ICMP_ICODE,     CONST_FEATURE_ID_ICMP_ICODE,    CONST_INT8, extract_icode},
+    {CONST_STRING_ICMP_ITYPE,     CONST_FEATURE_ID_ICMP_ITYPE,    CONST_INT8, extract_itype}
+};
+
+struct e2_feature_map e2_map_udp[] = {
+    {CONST_STRING_DSIZE,          CONST_FEATURE_ID_DSIZE,           CONST_INT16, extract_dsize},
+    {CONST_STRING_IP_SRCIP,       CONST_FEATURE_ID_IP_SRCIP,        CONST_IPv4, extract_srcip},
+    {CONST_STRING_IP_DSTIP,       CONST_FEATURE_ID_IP_DSTIP,        CONST_IPv4, extract_dstip},
+    {CONST_STRING_IP_OPTIONS,     CONST_FEATURE_ID_IP_OPTIONS,      CONST_IPOPTION, extract_ipoptions},
+    {CONST_STRING_IP_FRAG_FLAGS,  CONST_FEATURE_ID_IP_FRAG_FLAGS,   CONST_IPFRAG, extract_ipfragbits},
+    {CONST_STRING_IP_ID,          CONST_FEATURE_ID_IP_ID,           CONST_INT16, extract_ipid},
+    {CONST_STRING_CONTENT,        CONST_FEATURE_ID_CONTENT,         CONST_STRING, extract_content},
+    {CONST_STRING_CONTENT_URI,    CONST_FEATURE_ID_CONTENT_URI,     CONST_STRING, extract_uricontent},
+    {CONST_STRING_NOCASE,         CONST_FEATURE_ID_NOCASE,          CONST_NOCASE, NULL},
+    {CONST_STRING_OFFSET,         CONST_FEATURE_ID_OFFSET,          CONST_OFFSET, NULL},
+    {CONST_STRING_DEPTH,          CONST_FEATURE_ID_DEPTH,           CONST_DEPTH, NULL},
+    {CONST_STRING_TCPUDP_SRCPORT, CONST_FEATURE_ID_TCPUDP_SRCPORT,  CONST_INT16, extract_udp_srcport},
+    {CONST_STRING_TCPUDP_DSTPORT, CONST_FEATURE_ID_TCPUDP_DSTPORT,  CONST_INT16, extract_udp_dstport}
+};
+
+struct e2_feature_map e2_map_tcp[] = {
+    {CONST_STRING_DSIZE,          CONST_FEATURE_ID_DSIZE,           CONST_INT16, extract_dsize},
+    {CONST_STRING_IP_SRCIP,       CONST_FEATURE_ID_IP_SRCIP,        CONST_IPv4, extract_srcip},
+    {CONST_STRING_IP_DSTIP,       CONST_FEATURE_ID_IP_DSTIP,        CONST_IPv4, extract_dstip},
+    {CONST_STRING_IP_OPTIONS,     CONST_FEATURE_ID_IP_OPTIONS,      CONST_IPOPTION, extract_ipoptions},
+    {CONST_STRING_IP_FRAG_FLAGS,  CONST_FEATURE_ID_IP_FRAG_FLAGS,   CONST_IPFRAG, extract_ipfragbits},
+    {CONST_STRING_IP_ID,          CONST_FEATURE_ID_IP_ID,           CONST_INT16, extract_ipid},
+    {CONST_STRING_TCP_FLAGS,      CONST_FEATURE_ID_TCP_FLAGS,       CONST_TCPFLAGS, extract_tcpflags},
+    {CONST_STRING_CONTENT,        CONST_FEATURE_ID_CONTENT,         CONST_STRING, extract_content},
+    {CONST_STRING_CONTENT_URI,    CONST_FEATURE_ID_CONTENT_URI,     CONST_STRING, extract_uricontent},
+    {CONST_STRING_NOCASE,         CONST_FEATURE_ID_NOCASE,          CONST_NOCASE, NULL},
+    {CONST_STRING_OFFSET,         CONST_FEATURE_ID_OFFSET,          CONST_OFFSET, NULL},
+    {CONST_STRING_DEPTH,          CONST_FEATURE_ID_DEPTH,           CONST_DEPTH, NULL},
+    {CONST_STRING_TCPUDP_SRCPORT, CONST_FEATURE_ID_TCPUDP_SRCPORT,  CONST_INT16, extract_tcp_srcport},
+    {CONST_STRING_TCPUDP_DSTPORT, CONST_FEATURE_ID_TCPUDP_DSTPORT,  CONST_INT16, extract_tcp_dstport}
+};
+
+struct e2_feature_map e2_map_ip[] = {
+    {CONST_STRING_DSIZE,          CONST_FEATURE_ID_DSIZE,           CONST_INT16, extract_dsize},
+    {CONST_STRING_IP_SRCIP,       CONST_FEATURE_ID_IP_SRCIP,        CONST_IPv4, extract_srcip},
+    {CONST_STRING_IP_DSTIP,       CONST_FEATURE_ID_IP_DSTIP,        CONST_IPv4, extract_dstip},
+    {CONST_STRING_IP_ID,          CONST_FEATURE_ID_IP_ID,           CONST_INT16, extract_ipid},
+    {CONST_STRING_IP_OPTIONS,     CONST_FEATURE_ID_IP_OPTIONS,      CONST_IPOPTION, extract_ipoptions},
+    {CONST_STRING_IP_FRAG_FLAGS,  CONST_FEATURE_ID_IP_FRAG_FLAGS,   CONST_IPFRAG, extract_ipfragbits},
+    {CONST_STRING_CONTENT,        CONST_FEATURE_ID_CONTENT,         CONST_STRING, extract_content},
+    {CONST_STRING_CONTENT_URI,    CONST_FEATURE_ID_CONTENT_URI,     CONST_STRING, extract_uricontent},
+    {CONST_STRING_NOCASE,         CONST_FEATURE_ID_NOCASE,          CONST_NOCASE, NULL},
+    {CONST_STRING_OFFSET,         CONST_FEATURE_ID_OFFSET,          CONST_OFFSET, NULL},
+    {CONST_STRING_DEPTH,          CONST_FEATURE_ID_DEPTH,           CONST_DEPTH, NULL}
+};
+
+int32_t *e2_index_mapping_table[NUM_PROTO] ;
+
+// In the following two structs (e2_map and e2_map_size) the items have to have the same order
+struct e2_feature_map* e2_map[NUM_PROTO] = {
+    e2_map_icmp,
+    e2_map_udp,    
+    e2_map_tcp,
+    e2_map_ip
+};
+
+int e2_map_size[NUM_PROTO] = {
+    sizeof(e2_map_icmp)/sizeof(struct e2_feature_map),
+    sizeof(e2_map_udp)/sizeof(struct e2_feature_map),
+    sizeof(e2_map_tcp)/sizeof(struct e2_feature_map),
+    sizeof(e2_map_ip)/sizeof(struct e2_feature_map)
+};
+
+struct e2_feature_type_map e2_t_map[] = {
+    {"undefined", CONST_UNDEF, NULL, NULL, NULL, NULL, undef_propagate, undef_navigate, NULL, NULL, NULL, undef_debug},
+    {"int8", CONST_INT8, int_parse,int_pre_split_node, int_undo_split_node, int_final_split_node, int_propagate, int_navigate, int_clone, int_destroy, int_print, int_debug },
+    {"int16", CONST_INT16, int_parse,int_pre_split_node, int_undo_split_node, int_final_split_node, int_propagate, int_navigate, int_clone, int_destroy, int_print, int_debug },
+    {"int32", CONST_INT32, int_parse,int_pre_split_node, int_undo_split_node, int_final_split_node, int_propagate, int_navigate, int_clone, int_destroy, int_print, int_debug },
+    {"string", CONST_STRING, string_parse, string_pre_split_node, string_undo_split_node, string_final_split_node, string_propagate, string_navigate, string_clone, string_destroy, string_print, string_debug},
+    {"ipv4 address", CONST_IPv4, ipv4_parse, ipv4_pre_split_node, ipv4_undo_split_node, ipv4_final_split_node, ipv4_propagate, ipv4_navigate, ipv4_clone, ipv4_destroy, ipv4_print, ipv4_debug},
+    {"ip options", CONST_IPOPTION, ip_option_parse ,flags_pre_split_node, flags_undo_split_node, flags_final_split_node, flags_propagate, flags_navigate, flags_clone, flags_destroy, ip_options_print, flags_debug},
+    {"ip frag", CONST_IPFRAG, ip_fragbits_parse ,flags_pre_split_node, flags_undo_split_node, flags_final_split_node, flags_propagate, flags_navigate, flags_clone, flags_destroy, frag_flags_print, flags_debug},
+    {"tcp flag", CONST_TCPFLAGS, tcp_flags_parse ,flags_pre_split_node, flags_undo_split_node, flags_final_split_node, flags_propagate, flags_navigate, flags_clone, flags_destroy, tcp_flags_print, flags_debug},
+    {"nocase", CONST_NOCASE, nocase_parse, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL},
+    {"offset", CONST_OFFSET, offset_parse, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL},
+    {"depth", CONST_DEPTH, depth_parse, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL}
+};
+
+int e2_t_map_size = sizeof(e2_t_map)/sizeof(struct e2_feature_type_map);
+
+/******************************************************************************/
+/*                    Function Implementations                                */
+/******************************************************************************/
+
+/******************** Initialization functions ********************************/
+
+// Initialize the indices of the individual features into the type map
+void e2_initialize_indices_in_e2_map() {
+  int i=0, proto;
+
+#ifdef E2DEBUG
+  fprintf(stderr,"Features:\n");
+#endif
+
+  for (proto = 0; proto < NUM_PROTO; ++proto) {
+
+    for (i=0;i<e2_map_size[proto];i++) {
+      int j;
+
+      for (j=0;j<e2_t_map_size;j++) {
+        if ((e2_map[proto][i]).f_type == e2_t_map[j].ft_type) {
+          e2_map[proto][i].index_into_type_map=j;
+#ifdef E2DEBUG
+          printf("     %s => %d\n",e2_map[proto][i].f_name,j);
+#endif
+          break;
+        }
+      }
+    }
+  }
+}
+
+// Initialize the indices of the individual features of all supported protocols
+void e2_initialize_feature_indices() {
+
+  int i,proto;
+
+  for (proto = 0; proto < NUM_PROTO; ++proto) {
+
+    e2_index_mapping_table[proto] = salloc(sizeof(int32_t)*CONST_NUMBER_OF_FEATURES);
+    for (i=0;i<CONST_NUMBER_OF_FEATURES;i++)
+      e2_index_mapping_table[proto][i]=-1;
+
+    for (i=0;i<e2_map_size[proto];i++) {
+      int id;
+
+      // Get the id
+      id = e2_map[proto][i].f_id;
+
+      // Set the index
+      e2_index_mapping_table[proto][id]=i;
+    }
+  }
+
+#ifdef DEBUG_INDICES_MAPPING_TABLES
+  for (proto=0; proto < NUM_PROTO; ++proto) {
+    printf("\nMAPPING FOR PROTOCOL %d\n",proto);
+    for (i=0;i<CONST_NUMBER_OF_FEATURES;i++)
+      printf("ID: %d, Index: %d\n",i,e2_index_mapping_table[proto][i]);
+  }
+#endif
+}
+
+void e2_initialize_debug()
+{
+
+}
+    
+
+void e2_initialize() {
+
+    e2_initialize_indices_in_e2_map();
+    e2_initialize_feature_indices();
+    e2_initialize_debug();
+    
+    e2_fast_debug = e2_jump = 0;
+    
+    log2 = (1 / log(2));
+}
+
+int _mem_rcount = 0;
+int _mem_fcount = 0;
+int _mem_tcount = 0;
+
+
+/******************** Trigger manipulation ************************************/
+
+E2Trigger* trigger_create(u_int32_t rule_count)
+{
+    E2Trigger *obj;
+    u_int32_t len;
+
+#ifdef E2DEBUG
+    if (rule_count == 0) {
+	printf("Warning: zero length trigger created\n");
+	exit(1);
+    }
+#endif
+
+    obj = calloc(1, sizeof(E2Trigger));
+
+    if ((rule_count & 0x0F) != 0)
+	len = (rule_count >> 4) + 1;
+    else
+    	len = (rule_count >> 4);
+
+    obj->length = len;
+    obj->rules = calloc(len, sizeof(u_int32_t));
+
+    ++_mem_tcount;
+
+    return obj;
+}
+
+void trigger_destroy(E2Trigger *trigger)
+{
+    if (trigger->length > 0)
+	free(trigger->rules);
+
+    --_mem_tcount;
+    free(trigger);
+}
+
+E2Trigger* trigger_clone(E2Trigger *src)
+{
+    E2Trigger *dst;
+
+#ifdef E2DEBUG
+    if (src->length == 0) {
+	printf("Warning: attempted to clone zero length trigger\n");
+	exit(1);
+    }
+#endif
+
+    dst = calloc(1, sizeof(E2Trigger));
+
+    dst->length = src->length;
+    
+    dst->rules = calloc(src->length, sizeof(u_int32_t));
+    memcpy(dst->rules, src->rules, (src->length * sizeof(u_int32_t)));
+
+    ++_mem_tcount;
+
+    return dst;
+}
+
+void trigger_set(E2Trigger *trigger, u_int16_t id)
+{
+    int index;
+
+    index = id >> 4;
+
+#ifdef E2DEBUG
+    if (index >= trigger->length) {
+	printf("Fatal: trigger_set: rule id (%d) is out of bounds [0, %d]\n", id, (trigger->length-1));
+	exit(1);
+    }
+#endif
+
+    trigger->rules[index] |= (1 << (id & 0x0F));
+}
+
+void trigger_compact(E2Trigger *trigger)
+{
+    int oldindex, index;
+
+    for (oldindex = 0, index = 1; index < trigger->length; ++index) {
+
+	if (trigger->rules[index] & 0xFFFF) {
+	    for (; oldindex < index; ++oldindex) 
+		trigger->rules[oldindex] |= ((index-oldindex) << 16);
+	}
+    }
+
+    oldindex &= 0xFFFF;
+}
+
+void trigger_set_compact(E2Trigger *trigger, u_int16_t id)
+{
+    int index, cnt;
+
+    index = id >> 4;
+
+#ifdef E2DEBUG
+    if (index >= trigger->length) {
+	printf("Fatal: trigger_set: rule id is out of bounds\n");
+	exit(1);
+    }
+#endif
+
+    if (trigger->rules[index] & 0xFFFF) 
+	trigger->rules[index] |= (1 << (id & 0x0F));
+    else {
+	trigger->rules[index] |= (1 << (id & 0x0F));
+	
+	for (cnt = 1, --index; index >= 0; --index) {
+	    
+	    if ((trigger->rules[index] >> 16) == 1)
+		break;
+	    else {
+		trigger->rules[index] &= 0xFFFF;
+		trigger->rules[index] |= (cnt << 16);
+	    }
+	}
+    }
+}
+
+void trigger_print(E2Trigger *trigger)
+{
+    int cnt, index;
+
+    printf("Trigger: ");
+
+    for (cnt = 0; cnt < trigger->length; ++cnt)
+	for (index = 0; index < 16; ++index)
+	    printf("%d", (trigger->rules[cnt] >> (15-index)) & 1);
+
+    printf("\n");
+}
+
+/******************** Rule manipulation ***************************************/
+
+
+void _mem_print()
+{
+    printf("rules still here: %d\n", _mem_rcount);
+    printf("features still here: %d\n", _mem_fcount);
+    printf("triggers still here: %d\n", _mem_tcount);
+}
+
+E2Rule* rule_create()
+{
+    E2Rule *tmp = calloc(1, sizeof(E2Rule));
+
+    ++_mem_rcount;
+
+    return tmp;
+}
+
+void rule_destroy(E2Rule *dst)
+{
+    E2Feature *feature;
+    int cnt;
+    fct_destroy func_destroy;
+
+    for (cnt = 0; cnt < e2_map_size[dst->proto]; ++cnt) {
+	
+	feature = dst->features[cnt];
+	
+	if (feature != NULL) {
+	    
+#ifdef E2DEBUG
+	    if (e2_index_mapping_table[dst->proto][feature->f_id] != cnt) {
+		printf("RuleID: %d\n",dst->id);
+		printf("Fatal: rule_destroy: assertion violated -> talk to tom ;-)\n");
+		exit(1);
+	    }
+#endif
+	    func_destroy = e2_t_map[e2_map[dst->proto][cnt].index_into_type_map].destroy;
+	    
+	    if (func_destroy != NULL) {
+		if (feature->ref_count == 1)
+		    --_mem_fcount;
+		else if (feature->ref_count <= 0) {
+		    printf("Fatal: rule_destroy: attempting to remove zero-count feature\n");
+		    exit(1);
+		}
+		func_destroy(feature);
+	    }
+	    else {
+		    printf("Fatal: rule_destroy: function pointer is null\n");
+		    exit(1);
+	    }
+	}
+    } 
+
+    free(dst->features);
+    free(dst);
+
+    --_mem_rcount;
+}
+
+E2Rule* rule_clone(E2Rule *src)
+{
+    E2Feature *feat;
+    E2Rule *tmp = calloc(1, sizeof(E2Rule));
+    int cnt;
+
+    tmp->features = calloc(e2_map_size[src->proto], sizeof(E2Feature *));
+    memcpy(tmp->features, src->features, (e2_map_size[src->proto] * sizeof(E2Feature *)));
+
+    for (cnt = 0; cnt < e2_map_size[src->proto]; ++cnt) {
+    	feat = src->features[cnt];
+    	if (feat != NULL) 
+	      ++feat->ref_count;
+    }
+
+    tmp->id = src->id;
+    tmp->weight = src->weight;
+    tmp->proto = src->proto;
+
+    tmp->next = tmp->prev = NULL;
+
+    ++_mem_rcount;
+
+    return tmp;
+}
+
+void make_spaces(int d)
+{
+    int cnt;
+    for (cnt = 0; cnt < d; ++cnt)
+	printf(" ");
+}
+
+
+
+void rule_dump(E2Rule *todump, int depth)
+{
+    E2Feature *feat;
+    int cnt, index;
+
+    if (todump == NULL) {
+      printf("Rule is NULL!\n");
+      return;
+    }
+
+    make_spaces(depth);
+    printf("****** Rule (%u) ******\n", todump->id);
+
+    make_spaces(depth);
+    printf("protocol:");
+
+    switch (todump->proto) {
+      case E2_PROTO_TCP:
+        printf("tcp\n");
+      break;
+
+      case E2_PROTO_UDP:
+        printf("udp\n");
+      break;
+
+      case E2_PROTO_ICMP:
+        printf("icmp\n");
+      break;
+
+      case E2_PROTO_IP:
+        FatalError("Error => rule_dump : ip is not a valid protocol anymore\n");
+      break;
+
+//      case ETHERNET_TYPE_ARP:
+//        printf("arp\n");
+//      break;
+
+      default:
+        FatalError("ERROR => rule_dump: Encountered unknown protocol.");
+    }
+
+    // if (todump->otn==NULL)
+    //  FatalError("OTN IS NULL!");
+
+    for (cnt = 0; cnt < e2_map_size[todump->proto]; ++cnt) {
+    	feat = todump->features[cnt];
+
+      	if (feat != NULL) {
+	    
+#ifdef E2DEBUG
+	    if (e2_index_mapping_table[todump->proto][feat->f_id] != cnt) {
+		printf("Fatal: rule_dump: index-feature-map assertion violated -> talk to tom ;-)\n");
+		exit(1);
+	    }
+
+	    for (index = 0; index < e2_t_map_size; ++index) 
+    		if (e2_map[todump->proto][cnt].f_type == e2_t_map[index].ft_type)
+		    break;
+	    
+	    if (index != e2_map[todump->proto][cnt].index_into_type_map) {
+		printf("Fatal: rule_dump: index-type-map assertion violated -> talk to tom ;-)\n");
+		exit(1);
+	    }
+#endif
+	    make_spaces(depth);
+	    index = e2_map[todump->proto][cnt].index_into_type_map;
+	    printf("* %s (type: %s - refs: %d): ", e2_map[todump->proto][cnt].f_name, e2_t_map[index].ft_name, feat->ref_count);
+	    e2_t_map[index].print(feat);
+	    printf("\n");
+  	}
+  }
+}
+
+void rules_dump(E2Rule *todump, int depth)
+{
+    for (; todump != NULL; todump = todump->next)
+      rule_dump(todump, depth);
+}
+
+/******************** Rule list manipulation **********************************/
+
+
+E2Rule* list_remove(E2Rule *list_head, E2Rule *element)
+{
+    E2Rule *tmp;
+
+    if (element == list_head) {
+	if (list_head->next != NULL) {
+	    list_head->next->prev = NULL;
+	    return list_head->next;
+	}
+	else
+	    return NULL;
+    }
+    else {
+	tmp = list_head->next;
+	while (tmp != NULL) {
+	    if (tmp == element) {
+		tmp->prev->next = tmp->next;
+		if (tmp->next != NULL)
+		    tmp->next->prev = tmp->prev;
+		return list_head;
+	    }
+	    else
+		tmp = tmp->next;
+	}
+	return list_head;
+    }
+}	
+
+E2Rule* lists_merge(E2Rule *first, E2Rule *second) {
+  E2Rule * help;
+
+  if (first==NULL)
+    return second;
+
+  if (second==NULL)
+    return first;
+
+  help=first;
+  while (help->next!=NULL) {
+    help=help->next;
+  }
+
+  help->next=second;
+  return first;
+}
+
+E2Rule* list_add(E2Rule *list_head, E2Rule *element)
+{
+    element->prev = NULL;
+
+    if (list_head == NULL)
+	element->next = NULL;
+    else {
+	element->next = list_head;
+	list_head->prev = element;
+    }
+    return element;
+}
+    
+
+/********************** feature helper functions ********************/
+
+//********************************************************************
+//*                     new_feature                                  *
+//*                                                                  *
+//* emulates a constructor for E2Feature                             *
+//********************************************************************
+E2Feature * new_feature(u_int8_t f_type, u_int8_t f_id, void *f_val) {
+
+  E2Feature *f;
+  f=salloc(sizeof(E2Feature));
+
+  f->f_type   = f_type;
+  f->f_id     = f_id;
+  f->f_val    = f_val;
+  f->ref_count= 0;
+  f->next     = NULL;
+
+  ++_mem_fcount;
+
+  return f;
+}
+
+//********************************************************************
+//*                         feature_clone                            *
+//*                                                                  *
+//* creates a new E2Feature which has the same elements as the       *
+//*   argument.                                                      *
+//********************************************************************
+E2Feature * feature_clone(E2Feature * src) {
+
+  E2Feature *dst;
+
+  if (src == NULL)
+    return NULL;
+  
+  dst = (E2Feature *) salloc(sizeof(E2Feature));
+  dst->f_id = src->f_id;
+  dst->f_type = src->f_type;
+  dst->f_val = e2_t_map[src->f_type].clone(src->f_val);
+  dst->ref_count = 0;
+
+  ++_mem_fcount;
+
+  return dst;
+}
+
+
+
+/********************* memory handling, struct operations *********************/
+
+void * salloc(size_t size) {
+  void * tmp;
+
+  tmp = calloc(size, 1);
+  if (tmp==NULL) {
+   FatalError ("ERROR => salloc failed, out of memory\n");
+  }
+
+  return tmp;
+}
+
+
+/********************* generic debug function *********************************/
+
+void undef_debug(E2TreeNode *node)
+{
+    E2Rule *rule;
+
+    printf("\nNode: %p\n", node);
+    
+    for (rule = node->fire; rule != NULL; rule = rule->next) 
+    	rule_dump(rule, 6);
+}
+
+
+void undef_propagate(E2TreeNode *node, E2Trigger *t, void *caller)
+{
+    E2Trigger *trigger;
+    E2Rule *rule, *tr;
+
+#ifdef E2DEBUG
+    if (node->visited) {
+	printf("Fatal: undef_propagate: %p called twice - this time by %p\n", node, caller);
+	exit(1);
+    }
+    else 
+	node->visited = 1;
+#endif
+
+    trigger = trigger_clone(t);
+    for (rule = node->fire; rule != NULL; rule = rule->next)
+	trigger_set(trigger, rule->id);
+
+    for (rule = node->fire; rule != NULL; ) {
+	tr = rule->next;
+	rule_destroy(rule);
+	rule = tr;
+    }
+
+    trigger_compact(trigger);
+    node->trigger = trigger;
+}
+
+E2Trigger * undef_navigate(E2TreeNode *node, Packet *p)
+{
+    return node->trigger;
+}
+
+
+
+/******************************************************************************/
+/*                    Debug Implementations                                   */
+/******************************************************************************/
+
+/*
+
+struct e2_feature_map e2_map[] = {
+    {CONST_STRING_IP_DSTIP, CONST_FLAGS, extract_dstip}
+};
+
+struct e2_feature_type_map e2_t_map[] = {
+    {"undefined", CONST_FLAGS, NULL, NULL, NULL, NULL, NULL, flags_destroy, flags_print, flags_debug}
+};
+
+void e2_initialize_parse()
+{
+}
+
+void* extract_dstip (Packet *p)
+{
+	return NULL;
+}
+
+void FatalError(const char *str, ...)
+{
+}
+*/
+
+
+
+
+
+
diff -Naur snort-1.8.7.orig/e2_parse.c snort-1.8.7p1/e2_parse.c
--- snort-1.8.7.orig/e2_parse.c	Wed Dec 31 16:00:00 1969
+++ snort-1.8.7p1/e2_parse.c	Mon Sep 30 20:29:09 2002
@@ -0,0 +1,652 @@
+#include "engine2.h"
+
+#define CONST_OLDFEATURE 0
+#define CONST_NEWFEATURE 1
+
+// e2_parse global variables
+int rule_ID_counter =0;
+
+int index_of_srcip_in_elementlist;
+int index_of_dstip_in_elementlist;
+int index_of_srcport_in_elementlist;
+int index_of_dstport_in_elementlist;
+
+
+E2IDHelper * ID_helper;
+E2Alternative * src_ip_old;
+E2Alternative * dst_ip_old;
+E2Rule * collected_bidirectional_rules;
+
+/************************** building alternative rules ******************/
+
+//********************************************************************
+//*                     new_alternative                              *
+//*                                                                  *
+//* emulates a constructor for E2Alternative                         *
+//********************************************************************
+E2Alternative * new_alternative(E2Feature *f) {
+  E2Alternative * tmp;
+
+  tmp = (E2Alternative *) salloc(sizeof(E2Alternative));
+  tmp->alternatives_counter=1;
+  tmp->f=f;
+  tmp->same_id=0;
+
+  tmp->right=NULL;
+  tmp->down=NULL;
+
+  return tmp;
+}
+
+//********************************************************************
+//*                     insertNewAlternative                         *
+//*                                                                  *
+//* inserts a new alternative into the 2-dimensional alternative     *
+//*   list                                                           *
+//********************************************************************
+E2Alternative * insert_alternative(E2Alternative * list,E2Alternative * e, int newfeature) {
+  E2Alternative * tmp;
+  if (list==NULL) {
+    return e;
+  }
+
+  tmp=list;
+  while (tmp->right!=NULL) {
+    tmp=tmp->right;
+  }
+
+  if (newfeature== CONST_NEWFEATURE) {
+    tmp->right = e;
+  }
+  else {
+    tmp->alternatives_counter++;
+    // Now go down and insert
+    while (tmp->down!=NULL) {
+      tmp=tmp->down;
+    }
+    tmp->down=e;
+  }
+  return list;
+}
+
+
+//********************************************************************
+//*                     insert_feature_into_alternatives             *
+//*                                                                  *
+//* inserts a new alternative into the 2-dimensional alternative     *
+//*   list                                                           *
+//********************************************************************
+E2Alternative * insert_feature_into_alternatives(E2Alternative * list, E2Feature *f, int newfeature, int same_id ) {
+  E2Alternative * help;
+
+  help=new_alternative(f);
+  help->same_id=same_id;
+  return insert_alternative (list,help, newfeature);
+}
+
+
+//********************************************************************
+//*                      freeAlternatives                            *
+//*                                                                  *
+//* frees the memory of allocated alternatives which are not         *
+//*   requierd anymore (every time a new rule is processed)          *
+//********************************************************************
+E2Alternative * freeAlternatives(E2Alternative * list) {
+  while (list!=NULL) {
+    E2Alternative * tmp;
+    tmp = list;
+    list=list->right;
+
+    while (tmp!=NULL) {
+      E2Alternative  * tmp_next;
+      tmp_next = tmp->down;
+      free (tmp);
+      tmp=tmp_next;
+    }
+  }
+  return NULL;
+}
+
+//********************************************************************
+//*              getNumberOfFeaturesInAlternativesChain              *
+//*                                                                  *
+//* determines the number of features residing an an E2Alternative   *
+//*   chain                                                          *
+//********************************************************************
+int getNumberOfFeaturesInAlternativesChain(E2Alternative * chain) {
+  int i=0;
+
+  while (chain!=NULL) {
+    ++i;
+    chain=chain->right;
+  }
+  return i;
+}
+
+//********************************************************************
+//*                   fill_array_with_max_values                     *
+//*                                                                  *
+//* sets the values of the array to the maximum number of            *
+//*   alternatives for each feature                                  *
+//********************************************************************
+void fill_array_with_max_values(E2Alternative * chain, int * array) {
+  int i=0;
+
+  while (chain!=NULL) {
+    array[i++]=chain->alternatives_counter;
+    chain=chain->right;
+  }
+}
+
+//********************************************************************
+//*                         new_rule_ID                              *
+//*                                                                  *
+//* generates a unique rule ID                                       *
+//********************************************************************
+int new_rule_ID() {
+  return rule_ID_counter++;
+}
+
+void restore_old_ID() {
+  -- rule_ID_counter;
+}
+
+//********************************************************************
+//*                         clear_ID_helper                          *
+//*                                                                  *
+//* clears the elements of the list containing the already           *
+//*   assigned IDs with the features                                 *
+//********************************************************************
+E2IDHelper * clear_ID_helper(E2IDHelper * helper) {
+  E2IDHelper * tmp;
+
+  while (helper!=NULL) {
+    tmp=helper->next;
+    free(helper);
+    helper=tmp;
+  }
+
+  return NULL;
+}
+
+
+//********************************************************************
+//*                        e2_insert_ID_helper                       *
+//*                                                                  *
+//* simply inserts a given feature array in an E2IDHelper list       *
+//********************************************************************
+E2IDHelper * e2_insert_ID_helper(E2IDHelper * old,u_int8_t proto, E2Feature * features[], int nr_of_features, int id)  {
+
+  E2IDHelper * tmp;
+
+  tmp=salloc(sizeof(E2IDHelper));
+
+  tmp->proto = proto;
+  tmp->features=features;
+  tmp->nr_of_features=nr_of_features;
+  tmp->set_id=id;
+  tmp->next=old;
+  return tmp;
+}
+
+//********************************************************************
+//*                   is_ID_helper_equal_with_features               *
+//*                                                                  *
+//* checks whether a given E2IDHelper struct has the same features   *
+//*   like a given feature array                                     *
+//********************************************************************
+int is_ID_helper_equal_with_features(E2IDHelper * helper, E2Feature * features[],
+      int srcip_index, int dstip_index, int external_not_flag_set_at_srcip,E2Alternative * src_ip_alt,
+      int external_not_flag_set_at_dstip, E2Alternative * dst_ip_alt) {
+
+  int i;
+
+  if (features==NULL)
+    FatalError("ERROR => is_ID_helper_equal_with_features: features == NULL!");
+
+  for (i=0;i<helper->nr_of_features;i++) {
+
+    if (i==srcip_index && external_not_flag_set_at_srcip) {
+      E2Alternative * tmp;
+      int found_equal=0;
+
+      tmp = src_ip_alt;
+      // Compare all the addresses in the alternatives list with this one,
+      // at least one has to match exactly.
+      while (tmp!=NULL) {
+        if (features[i]!=NULL) {
+          if (tmp->f->f_val==features[i]->f_val)
+            found_equal=1;
+        }
+        tmp=tmp->down;
+      }
+
+      if (!found_equal)
+        return 1;
+      else
+        continue;
+    }
+
+    if (i==dstip_index && external_not_flag_set_at_dstip) {
+      E2Alternative * tmp;
+      int found_equal=0;
+
+      tmp = dst_ip_alt;
+      // Compare all the addresses in the alternatives list with this one,
+      // at least one has to match exactly.
+      while (tmp!=NULL) {
+        if (features[i]!=NULL) {
+          if (tmp->f->f_val==features[i]->f_val)
+            found_equal=1;
+        }
+        tmp=tmp->down;
+      }
+
+      if (!found_equal)
+        return 1;
+      else
+        continue;
+    }
+
+    if (helper->features[i]!=NULL && features[i]!=NULL)
+      if (helper->features[i]->f_val!=features[i]->f_val) {
+        return 1; // Because it is different
+      }
+  }
+  return 0;
+}
+
+//********************************************************************
+//*                         e2_ID_exists                             *
+//*                                                                  *
+//* iterates the list of E2IDHelper and checks for feature           *
+//*   similarities                                                   *
+//********************************************************************
+int e2_ID_exists(E2IDHelper *helper, u_int8_t proto, E2Feature * features[], int srcip_index,
+      int dstip_index, int ignore_srcip, E2Alternative * src_ip_alt,
+      int ignore_dstip, E2Alternative * dst_ip_alt) {
+
+  while (helper!=NULL) {
+
+    if (proto==helper->proto) {
+      if (!is_ID_helper_equal_with_features(helper,features,srcip_index, dstip_index, ignore_srcip, src_ip_alt, ignore_dstip, dst_ip_alt))
+        return helper->set_id;
+    }
+    helper=helper->next;
+  }
+
+  return -1;
+}
+
+//********************************************************************
+//*                         determine_ID                             *
+//*                                                                  *
+//* determines the ID of a rule according to the features            *
+//*   in case that a rule has exactly the same features as an        *
+//*   existing one, the ID of the old rule is assigned to the new    *
+//*   rule.                                                          *
+//*                                                                  *
+//* uses the 'ID_helper' structure to track the existance of         *
+//*   similar rules                                                  *
+//********************************************************************
+int determine_ID(E2Feature * features[], u_int8_t proto, int ignore_srcip, int ignore_dstip,
+      E2Alternative * src_ip_alt, E2Alternative * dst_ip_alt) {
+  int ret_val;
+  int id=-1;
+  int srcip_index;
+  int dstip_index;
+  int nr_of_features;
+
+  srcip_index = e2_index_mapping_table[proto][CONST_FEATURE_ID_IP_SRCIP];
+  dstip_index = e2_index_mapping_table[proto][CONST_FEATURE_ID_IP_DSTIP];
+
+  nr_of_features = e2_map_size[proto];
+
+  ret_val=e2_ID_exists(ID_helper, proto, features, srcip_index, dstip_index, ignore_srcip,
+    src_ip_alt, ignore_dstip, dst_ip_alt);
+
+  if (ret_val!=-1)
+    return ret_val;
+  else {
+    id = new_rule_ID();
+
+    ID_helper = e2_insert_ID_helper(ID_helper, proto, features, nr_of_features, id);
+  }
+  return id;
+}
+
+void dumpfeature(E2Feature ** f,int nr_of_features) {
+  int i;
+
+  printf("Features:\n");
+  for (i=0;i<nr_of_features;i++) {
+    printf("%x\n",(int) f[i]);
+  }
+  printf("END:");
+}
+
+//********************************************************************
+//*                     adapt_feature                                *
+//*                                                                  *
+//* adapts the necessary features to create a bidirectional rule     *
+//*                                                                  *
+//********************************************************************
+void adapt_feature( E2Rule * r,  u_int8_t proto, int id1, int id2) {
+  E2Feature * f;
+
+  int index_1;
+  int index_2;
+
+  index_1 = e2_index_mapping_table[proto][id1];
+  index_2 = e2_index_mapping_table[proto][id2];
+
+  if (index_1 == -1 || index_2 == -1)
+    return;
+
+  f = r->features[index_1];
+  r->features[index_1] = feature_clone(r->features[index_2]);
+  r->features[index_2] = feature_clone(f);
+
+  // Adapt the ids
+
+  if (r->features[index_1]!=NULL)
+    r->features[index_1]->f_id = id1;
+
+  if (r->features[index_2]!=NULL)
+    r->features[index_2]->f_id = id2;
+}
+
+E2Rule * create_and_insert_bidirectional_rule(E2Rule * old, E2Rule * element, int new_rulenr, E2Feature ** existing_features, u_int8_t proto, OptTreeNode * otn) {
+
+  E2Rule * new_element=NULL;
+  int i;
+
+  new_element = rule_create();
+  new_element->proto = element->proto;
+  new_element->id = new_rulenr;
+
+  new_element->weight = element->weight;
+  new_element->otn = otn;
+
+  if (existing_features==NULL) {
+    if (index_of_srcip_in_elementlist!=-1 || index_of_dstip_in_elementlist !=-1 ||
+        index_of_srcport_in_elementlist!=-1 || index_of_dstport_in_elementlist!=-1 ) {
+      // Copy and modify features
+      new_element->features = (E2Feature **) salloc(sizeof(E2Feature *) * e2_map_size[proto]);
+      memcpy(new_element->features,element->features,sizeof(E2Feature*) * e2_map_size[proto]);
+
+      adapt_feature(new_element, proto, CONST_FEATURE_ID_IP_SRCIP,       CONST_FEATURE_ID_IP_DSTIP);
+      adapt_feature(new_element, proto, CONST_FEATURE_ID_TCPUDP_SRCPORT, CONST_FEATURE_ID_TCPUDP_DSTPORT);
+      // Adapt the refcount;
+
+    }
+    else {
+      restore_old_ID();
+      return old;
+    }
+  }
+  else
+    new_element->features = existing_features;
+
+  // Update the reference_counters in all the features
+  for (i=0;i<e2_map_size[proto];i++) {
+    if (new_element->features[i]!=NULL)
+      new_element->features[i]->ref_count++;
+  }
+
+  // Insert element in front of the list
+  new_element->next=old;
+
+  return new_element;
+}
+
+//********************************************************************
+//*               add_second_way_of_bidirectional_rule               *
+//*                                                                  *
+//* helper function that adds for each created rule in               *
+//*   generated_rules another rule with exchanged src/ip addresses   *
+//*   and ports                                                      *
+//********************************************************************
+E2Rule * get_second_way_of_bidirectional_rule(E2Rule *generated_rules, u_int8_t proto) {
+
+  E2Rule * bidirectional_rules=NULL;
+  E2Rule * last_element;
+  E2Rule * tmp;
+  int old_rulenr=-1;
+  int new_rulenr=-1;
+  OptTreeNode * otn;
+
+  tmp=generated_rules;
+
+  while (tmp!=NULL) {
+    E2Rule * tmp_src;
+
+    old_rulenr = tmp->id;
+    otn = tmp->otn;
+    proto = tmp->proto;
+
+    // Now start at the beginning of the list and search for the first
+    // occurence of this ID, follow the bidirectional link and read out the ID
+    tmp_src = generated_rules;
+
+    while (tmp_src!=tmp) {
+      if (tmp_src->id==old_rulenr)
+        break;
+
+      tmp_src=tmp_src->next;
+    }
+
+    if (tmp_src!=tmp) {
+      // So we are at the first occurance
+      new_rulenr= tmp_src->bidirectional_rule->id;
+      bidirectional_rules = create_and_insert_bidirectional_rule
+        (bidirectional_rules, tmp, new_rulenr,NULL, proto, otn);
+    }
+    else {
+      // There is no bidirectional rule yet.
+      new_rulenr =new_rule_ID();
+
+      bidirectional_rules = create_and_insert_bidirectional_rule(bidirectional_rules, tmp, new_rulenr,NULL, proto, otn);
+      // Set the bidirectional rule
+      tmp->bidirectional_rule = bidirectional_rules;
+    }
+
+    last_element=tmp;
+
+    tmp=tmp->next;
+  }
+
+  return bidirectional_rules;
+}
+
+E2Rule * generate_rule(u_int8_t proto,  int nr_of_features,
+      E2Alternative ** elementpointer,   int ignore_srcip, int ignore_dstip, OptTreeNode * e2_opt_tree_node) {
+    E2Rule * r=NULL;
+    int j;
+
+    // generate new rule
+    r = rule_create();
+    r->proto = proto;
+    r->otn = e2_opt_tree_node;
+
+    r->features=(E2Feature **) salloc(sizeof(E2Feature *)*e2_map_size[proto]);
+
+    // Set the features of the rule
+    for (j=0;j<nr_of_features;j++) {
+      int id;
+      int index;
+
+      elementpointer[j]->f->ref_count++;
+
+      // Find out the feature id
+      id = (elementpointer[j]->f)->f_id;
+
+      // Search the index of the feature id
+      index = e2_index_mapping_table[proto][id];
+
+      r->features[index] = elementpointer[j]->f;
+    }
+
+    // Determine whether to ignore srcip and/or dstip
+    if (index_of_srcip_in_elementlist!=-1)
+      ignore_srcip = elementpointer[index_of_srcip_in_elementlist]->same_id;
+
+    if (index_of_dstip_in_elementlist!=-1)
+      ignore_dstip = elementpointer[index_of_dstip_in_elementlist]->same_id;
+
+    // Determine the ID according to already existing rules
+    r->id = determine_ID(r->features, r->proto, ignore_srcip, ignore_dstip, src_ip_old, dst_ip_old);
+
+    return r;
+}
+
+
+//********************************************************************
+//*                     generateRulesRecursive                       *
+//*                                                                  *
+//* recursive helper function for generateIndividualRules            *
+//*   creates all rules from the E2Alternative chain                 *
+//*                                                                  *
+//*   takes into account the bidirectional                           *
+//*   takes into account that several Rules have to have the same ID *
+//*     if they include the '!' operator in front of IP lists        *
+//********************************************************************
+E2Rule * generateRulesRecursive(E2Rule * old_rules,E2Alternative *chain,
+    int index, int nr_of_features, E2Alternative ** elementpointer, u_int8_t proto, OptTreeNode *e2_opt_tree_node) {
+
+  E2Alternative * tmp;
+  char * id_string=NULL;
+  int ignore_srcip=0;
+  int ignore_dstip=0;
+
+  if (index==0) {
+
+    // Clear the variables that hold the state information about src/dst Port and IP
+    index_of_srcip_in_elementlist=-1;
+    index_of_dstip_in_elementlist=-1;
+    index_of_srcport_in_elementlist=-1;
+    index_of_dstport_in_elementlist=-1;
+  }
+
+  if (chain==NULL) {
+
+    E2Rule * r=NULL;
+
+    switch(proto) {
+      case E2_PROTO_IP:
+        r = generate_rule(E2_PROTO_ICMP, nr_of_features, elementpointer, ignore_srcip, ignore_dstip, e2_opt_tree_node);
+        old_rules = list_add(old_rules, r);
+        r = generate_rule(E2_PROTO_UDP, nr_of_features, elementpointer, ignore_srcip, ignore_dstip, e2_opt_tree_node);
+        old_rules = list_add(old_rules, r);
+        r = generate_rule(E2_PROTO_TCP, nr_of_features, elementpointer, ignore_srcip, ignore_dstip, e2_opt_tree_node);
+        old_rules = list_add(old_rules, r);
+      break;
+
+      default:
+        r = generate_rule(proto, nr_of_features, elementpointer, ignore_srcip, ignore_dstip, e2_opt_tree_node);
+        old_rules = list_add(old_rules, r);
+        break;
+    }
+
+    return old_rules;
+  }
+
+  tmp = chain;
+
+  // Determine the indices of valuable fields
+  id_string = e2_map[proto][e2_index_mapping_table[proto][tmp->f->f_id]].f_name;
+
+  if (strcmp(id_string,CONST_STRING_IP_SRCIP)==0) {
+      src_ip_old = chain;
+      index_of_srcip_in_elementlist=index;
+  }
+
+  if (strcmp(id_string,CONST_STRING_TCPUDP_SRCPORT)==0)
+    index_of_srcport_in_elementlist=index;
+
+  if (strcmp(id_string,CONST_STRING_IP_DSTIP)==0) {
+    dst_ip_old = chain;
+    index_of_dstip_in_elementlist=index;
+  }
+
+  if (strcmp(id_string,CONST_STRING_TCPUDP_DSTPORT)==0)
+      index_of_dstport_in_elementlist=index;
+  
+  while (tmp!=NULL) {
+      elementpointer[index]=tmp;
+      old_rules=generateRulesRecursive(old_rules,chain->right, index+1,nr_of_features, elementpointer, proto, e2_opt_tree_node);
+      tmp=tmp->down;
+  }
+  
+  return old_rules;
+}
+
+//********************************************************************
+//*                     generateIndividualRules                      *
+//*                                                                  *
+//* generate the individual rules from the E2Alternative data        *
+//*   structure. Take into account not_flag_external flags           *
+//********************************************************************
+E2Rule * generateIndividualRules(E2Alternative *chain, int bidirectional, u_int8_t proto, OptTreeNode * e2_opt_tree_node) {
+  int nr_of_features=0;
+  E2Alternative ** elementpointer=NULL; // Pointer to the individual alternatives within a single feature
+  E2Rule * generated_rules;
+
+  nr_of_features = getNumberOfFeaturesInAlternativesChain(chain);
+  elementpointer = salloc(sizeof(E2Alternative *) * nr_of_features);
+
+  src_ip_old=NULL;
+  dst_ip_old=NULL;
+
+  // Clear the list containing the existing ID_helpers
+  ID_helper=clear_ID_helper(ID_helper);
+
+  if (nr_of_features==0) {
+      free(elementpointer);
+      return NULL;
+  }
+
+  generated_rules = generateRulesRecursive(NULL,chain,0,nr_of_features, elementpointer, proto, e2_opt_tree_node);
+
+  if (bidirectional) {
+    collected_bidirectional_rules = lists_merge(collected_bidirectional_rules, get_second_way_of_bidirectional_rule(generated_rules, proto));
+  }
+
+  free(elementpointer);
+
+  return generated_rules;
+}
+
+
+
+//********************************************************************
+//*                         e2_parse                                 *
+//*                                                                  *
+//* routine called by rules.c, called for parsing all available      *
+//*   data types                                                     *
+//********************************************************************
+E2Alternative * e2_parse(char ** opts, E2Alternative * f, int options, u_int8_t proto) {
+  int i;
+  E2Alternative * alt;
+
+  // i is the index into the map
+  for (i=0;i<e2_map_size[proto];i++) {
+
+    if (!strcasecmp (opts[0], e2_map[proto][i].f_name)) {
+      int type;
+      int id;
+
+      type = e2_map[proto][i].f_type;
+      id = e2_map[proto][i].f_id;
+
+      alt = e2_t_map[ e2_map[proto][i].f_type ].parse(opts[1] , id, proto );
+
+      if (alt !=NULL) {
+        f=insert_alternative (f,alt,1);
+      }
+    }
+  }
+
+  return f;
+}
+
diff -Naur snort-1.8.7.orig/engine2.h snort-1.8.7p1/engine2.h
--- snort-1.8.7.orig/engine2.h	Wed Dec 31 16:00:00 1969
+++ snort-1.8.7p1/engine2.h	Fri Sep  6 07:47:56 2002
@@ -0,0 +1,516 @@
+/*
+** Copyright (C) 2002 Distributed Systems Group, Technical University Vienna
+**
+**                    Thomas Toth (ttoth@infosys.tuwien.ac.at)
+**                    Christopher Kruegel (chris@infosys.tuwien.ac.t)
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation; either version 2 of the License, or
+** (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+** GNU General Public License for more details.
+**
+** You should have received a copy of the GNU General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#ifndef __ENGINE2_H__
+#define __ENGINE2_H__
+
+#include "snort.h"
+#include "decode.h"
+#include "rules.h"
+
+#define ENGINE2
+
+/******************** Debugging ***********************************************/
+
+
+// #define E2DEBUG
+
+#ifdef E2DEBUG
+#define E2_DEBUG_PARSER
+//#define E2_DEBUG_STRING_SEARCH
+//#define E2_DEBUG_STRING_SEARCH_DETAILED
+//#define DEBUG_INDICES_MAPPING_TABLES
+#define E2_CLUSTER_TREE
+//#define E2_DEBUG_CLUSTER_TREE
+#endif
+
+/******************** Forward declarations ************************************/
+
+struct e2_rule;
+struct e2_successor_nodes;
+struct e2_tree_node;
+struct e2_feature;
+struct e2_alternative;
+struct e2_trigger;
+
+/******************** Function typedefs ***************************************/
+
+typedef void* (*fct_extract)(Packet *p);
+typedef struct e2_alternative * (*fct_parse) (unsigned char * data, int id, u_int8_t proto);
+typedef struct e2_successor_node * (*fct_pre_split_node)(struct e2_rule *rules, u_int8_t feat);
+typedef void (*fct_undo_split_node)(struct e2_successor_node *succs);
+typedef void* (*fct_final_split_node)(struct e2_successor_node *succs, u_int8_t feat, int *feature_map, u_int8_t proto);
+typedef struct e2_trigger* (*fct_navigate)(struct e2_tree_node *node, Packet *p);
+
+typedef void (*fct_destroy)(struct e2_feature *feature);
+typedef void (*fct_print)(struct e2_feature *feature);
+typedef void (*fct_debug)(struct e2_tree_node *node);
+typedef void (*fct_propagate)(struct e2_tree_node *node, struct e2_trigger *trigger, void *caller);
+typedef void* (*fct_clone)(void *fval);
+
+/******************** Supported features **************************************/
+
+#define CONST_STRING_PROTOCOL           "protocol"
+#define CONST_FEATURE_ID_PROTOCOL       0
+#define CONST_STRING_DSIZE              "dsize"
+#define CONST_FEATURE_ID_DSIZE          1
+#define CONST_STRING_ICMP_ID            "icmp_id"
+#define CONST_FEATURE_ID_ICMP_ID        2
+#define CONST_STRING_ICMP_SEQ           "icmp_seq"
+#define CONST_FEATURE_ID_ICMP_SEQ       3
+#define CONST_STRING_ICMP_ICODE         "icode"
+#define CONST_FEATURE_ID_ICMP_ICODE     4
+#define CONST_STRING_ICMP_ITYPE         "itype"
+#define CONST_FEATURE_ID_ICMP_ITYPE     5
+#define CONST_STRING_TCPUDP_SRCPORT     "srcport"
+#define CONST_FEATURE_ID_TCPUDP_SRCPORT 6
+#define CONST_STRING_TCPUDP_DSTPORT     "dstport"
+#define CONST_FEATURE_ID_TCPUDP_DSTPORT 7
+#define CONST_STRING_IP_SRCIP           "srcip"
+#define CONST_FEATURE_ID_IP_SRCIP       8
+#define CONST_STRING_IP_DSTIP           "dstip"
+#define CONST_FEATURE_ID_IP_DSTIP       9
+#define CONST_STRING_CONTENT            "content"
+#define CONST_FEATURE_ID_CONTENT        10
+#define CONST_STRING_IP_OPTIONS         "ipopts"
+#define CONST_FEATURE_ID_IP_OPTIONS     11
+#define CONST_STRING_IP_FRAG_FLAGS      "fragbits"
+#define CONST_FEATURE_ID_IP_FRAG_FLAGS  12
+#define CONST_STRING_TCP_FLAGS          "flags"
+#define CONST_FEATURE_ID_TCP_FLAGS      13
+#define CONST_STRING_CONTENT_URI        "uricontent"
+#define CONST_FEATURE_ID_CONTENT_URI    14
+#define CONST_STRING_IP_TTL             "icode"
+#define CONST_FEATURE_ID_IP_TTL         15
+#define CONST_STRING_IP_ID              "id"
+#define CONST_FEATURE_ID_IP_ID          16
+#define CONST_STRING_TCP_ACK            "ack"
+#define CONST_FEATURE_ID_TCP_ACK        17
+#define CONST_STRING_TCP_SEQ            "seq"
+#define CONST_FEATURE_ID_TCP_SEQ        18
+#define CONST_STRING_RPC                "rpc"
+#define CONST_FEATURE_ID_RPC            19
+#define CONST_STRING_TCP_SESSION        "session"
+#define CONST_FEATURE_ID_TCP_SESSION    20
+#define CONST_STRING_IP_TOS             "tos"
+#define CONST_FEATURE_ID_IP_TOS         21
+#define CONST_STRING_IP_SAMEIP          "sameip"
+#define CONST_FEATURE_ID_IP_SAMEIP      22
+#define CONST_STRING_NOCASE             "nocase"
+#define CONST_FEATURE_ID_NOCASE         23
+#define CONST_STRING_OFFSET             "offset"
+#define CONST_FEATURE_ID_OFFSET         24
+#define CONST_STRING_DEPTH              "depth"
+#define CONST_FEATURE_ID_DEPTH          25
+
+
+#define CONST_NUMBER_OF_FEATURES        26
+
+
+/******************** Some constants ******************************************/
+#define CONST_NO_SAME_ID 0
+#define CONST_SAME_ID 1
+
+#define CONST_NOP 0
+#define CONST_EXACT 1
+#define CONST_ALL 2
+#define CONST_ANY 3
+#define CONST_NOT 4
+
+// #define NUM_PROTO 4 - now defined in rules.h
+
+#define E2_PROTO_ICMP 0
+#define E2_PROTO_UDP 1
+#define E2_PROTO_TCP 2
+#define E2_PROTO_IP 3
+
+#define NUM_TREE 2
+
+#define CONST_MAX_INT_16 0xffff
+#define CONST_MAX_INT_32 0xffffffff
+
+
+/******************** Supported feature types *********************************/
+#define CONST_UNDEF    0
+#define CONST_INT8     1
+#define CONST_INT16    2
+#define CONST_INT32    3
+#define CONST_STRING   4
+#define CONST_IPv4     5
+#define CONST_IPOPTION 6
+#define CONST_IPFRAG   7
+#define CONST_TCPFLAGS 8
+#define CONST_NOCASE 9
+#define CONST_OFFSET 10
+#define CONST_DEPTH  11
+
+// ******************** Constants for IPOPTIONS *******************************
+#define CONST_NUMBER_OF_SUPPORTED_IPOPTIONS 8
+#define CONST_INDEX_OF_RR 0
+#define CONST_INDEX_OF_EOL 1
+#define CONST_INDEX_OF_NOP 2
+#define CONST_INDEX_OF_TS 3
+#define CONST_INDEX_OF_SEC 4
+#define CONST_INDEX_OF_LSRR 5
+#define CONST_INDEX_OF_SSRR 6
+#define CONST_INDEX_OF_SATID 7
+
+// ******************** Constants for FRAGBITS ********************************
+#define CONST_NUMBER_OF_SUPPORTED_FRAGFLAGS 3
+#define CONST_INDEX_OF_R 0
+#define CONST_INDEX_OF_DF 1
+#define CONST_INDEX_OF_MF 2
+
+// ******************** Constants for TCP Flags ********************************
+#define CONST_NUMBER_OF_SUPPORTED_TCPFLAGS 9
+#define CONST_INDEX_OF_FIN 0
+#define CONST_INDEX_OF_SYN 1
+#define CONST_INDEX_OF_RST 2
+#define CONST_INDEX_OF_PSH 3
+#define CONST_INDEX_OF_ACK 4
+#define CONST_INDEX_OF_URG 5
+#define CONST_INDEX_OF_2   6
+#define CONST_INDEX_OF_1   7
+#define CONST_INDEX_OF_0   8
+
+
+/******************** Global variables ****************************************/
+
+extern int e2_jump; // cmd line flag for old snort behaviour
+extern int e2_fast_debug; // cmd line flag for our testing system (don't use it)
+
+extern int rule_ID_counter;
+
+/******************** Feature structs *****************************************/
+
+struct e2_feature_type_map {
+    char * ft_name;
+    u_int8_t ft_type;
+
+    /* functions to parse content */
+    fct_parse parse;
+
+    /* functions to build decision tree */
+    fct_pre_split_node pre_split_node;
+    fct_undo_split_node undo_split_node;
+    fct_final_split_node final_split_node;
+    fct_propagate propagate;
+
+    /* function to navigate packet through decision tree */
+    fct_navigate navigate;
+
+    /* maintenance functions */
+    fct_clone   clone;
+    fct_destroy destroy;
+    fct_print   print;
+
+    /* debug function */
+    fct_debug debug;
+};
+
+extern struct e2_feature_type_map e2_t_map[];
+extern int e2_t_map_size;
+
+
+struct e2_feature_map {
+    char *f_name;
+    u_int16_t f_id;
+    u_int8_t f_type;
+
+    /* function to extract relevant packet data */
+    fct_extract extract;
+    int index_into_type_map;
+};
+
+extern struct e2_feature_type_map e2_t_map[];
+extern int e2_type_map_size;
+
+extern int32_t *e2_index_mapping_table[NUM_PROTO];
+
+/* add features as desired into e2_map in e2_help.c */
+extern struct e2_feature_map *e2_map[];
+extern int e2_map_size[];
+
+typedef struct e2_feature {
+    u_int16_t f_id;
+    u_int16_t f_index;
+    u_int8_t f_type;
+    void *f_val;
+
+    struct e2_feature *next;
+
+    int ref_count;
+} E2Feature;
+
+
+typedef struct e2_ID_helper {
+  u_int8_t proto;
+  E2Feature  **features;
+  int nr_of_features;
+  int set_id;
+
+  struct e2_ID_helper * next;
+} E2IDHelper;
+
+
+
+/******************** Snort rule **********************************************/
+
+
+typedef struct e2_rule {
+    u_int16_t id;
+    u_int8_t proto;
+    
+    OptTreeNode *otn;
+
+    struct e2_feature **features;
+    float weight;
+ 
+    struct e2_rule *prev, *next, * bidirectional_rule;
+} E2Rule;
+
+extern E2Rule *e2rule_list;
+extern E2Rule *e2rule_tmp;
+extern E2Rule *collected_bidirectional_rules;
+
+/******************** Cluster tree structs ************************************/
+
+typedef struct e2_tree_node {
+    struct e2_rule *fire, *debug_rules;
+    struct e2_trigger *trigger;
+    u_int32_t debug_count;
+
+    int visited;
+
+    int feature;
+    u_int8_t proto;
+
+    void *private;
+
+    fct_extract extract;
+    fct_navigate navigate;
+    fct_debug debug;
+    fct_propagate propagate;
+} E2TreeNode;
+
+typedef struct e2_successor_node {
+    E2Rule *rules;
+    u_int32_t rule_count;
+
+    void *private;
+
+   struct e2_successor_node *next;
+} E2SuccessorNode;
+
+extern E2TreeNode *trees[NUM_PROTO][NUM_TREE];
+extern OptTreeNode **snort_rules;
+extern u_int32_t snort_rule_count;
+
+typedef struct e2_trigger {
+    u_int32_t *rules;
+    u_int32_t length;
+} E2Trigger;
+
+
+/******************** Struct for Variant creation *****************************/
+typedef struct e2_alternative {
+  E2Feature *f;
+  int alternatives_counter;
+  int same_id;
+
+  struct e2_alternative * right;
+  struct e2_alternative * down;
+} E2Alternative ;
+
+typedef struct e2_payload {
+
+  u_int8_t  * content;
+  u_int16_t content_length;
+} E2PAYLOAD;
+
+/******************** Structures for content search **************************/
+
+
+/******************** Cluster tree functions **********************************/
+
+extern E2TreeNode* split_snort_rules(E2Rule *rules, u_int8_t proto, int *feature_map);
+extern void build_e2_engine(E2Rule *rules, u_int32_t rules_count);
+int Engine2RuleFpListWrapper(Packet * p, struct _OptTreeNode *otn_idx, OptFpList * ofp_list);
+
+/******************** Parsing functions ***************************************/
+extern E2Alternative * e2_parse(char ** opts, E2Alternative * f, int options, u_int8_t proto);
+extern E2Feature * new_feature(u_int8_t f_type, u_int8_t f_id, void *f_val);
+extern E2Alternative * insert_feature_into_alternatives(E2Alternative * list, E2Feature *f, int newfeature, int same_id );
+extern E2Alternative * insert_alternative(E2Alternative * list, E2Alternative *f, int newfeature );
+extern E2Rule * generateIndividualRules(E2Alternative *chain, int bidirectional, u_int8_t proto, OptTreeNode * e2_opt_tree_node);
+extern E2Alternative * freeAlternatives(E2Alternative * list);
+
+extern int rule_ID_counter; 
+
+
+/******************** Helper functions ****************************************/
+
+extern void e2_initialize();
+extern void e2_initialize_parse();
+
+extern E2Rule* rule_create();
+extern void rule_destroy(E2Rule *dst);
+extern E2Rule* rule_clone(E2Rule *src);
+extern void rule_dump(E2Rule * todump, int depth);
+extern void rules_dump(E2Rule * r, int depth);
+extern E2Feature* feature_clone(E2Feature * f);
+
+extern E2Rule* list_add(E2Rule *list_head, E2Rule *element);
+extern E2Rule* list_remove(E2Rule *list_head, E2Rule *element);
+extern E2Rule* lists_merge(E2Rule *first, E2Rule *second);
+
+extern E2Trigger* trigger_create();
+extern void trigger_destroy(E2Trigger *dst);
+extern E2Trigger* trigger_clone(E2Trigger *src);
+extern void trigger_set(E2Trigger *trigger, u_int16_t id);
+extern void trigger_compact(E2Trigger *trigger);
+extern void trigger_set_compact(E2Trigger *trigger, u_int16_t id);
+extern void trigger_print(E2Trigger *trigger);
+
+extern void * salloc(size_t );
+
+extern void undef_debug(E2TreeNode *node);
+extern void undef_propagate(E2TreeNode *node, E2Trigger *trigger, void *caller);
+extern E2Trigger * undef_navigate(E2TreeNode *node, Packet *p); 
+
+extern void _mem_print();
+
+/******************************************************************************/
+/*                    feature TYPE functions start here                       */
+/******************************************************************************/
+
+/********************** Feature type indices are stored here ******************/
+extern int index_of_srcip[NUM_PROTO];
+extern int index_of_dstip[NUM_PROTO];
+extern int index_of_srcport[NUM_PROTO];
+extern int index_of_dstport[NUM_PROTO];
+extern int index_of_content[NUM_PROTO];
+
+/******************** Alternative functions ***********************************/
+
+extern E2Alternative * new_alternative(E2Feature *f);
+extern E2Alternative *e2_alternatives_list;
+
+/******************** Integer functions ***************************************/ 
+
+extern E2SuccessorNode* int_pre_split_node(E2Rule *rules, u_int8_t feat);
+extern void int_undo_split_node(E2SuccessorNode *succs);
+extern void* int_final_split_node(E2SuccessorNode *succs, u_int8_t feat, int *feature_map, u_int8_t proto);
+extern void int_propagate(E2TreeNode *node, E2Trigger *trigger, void *caller);
+extern E2Trigger * int_navigate(E2TreeNode *node, Packet *p); 
+extern E2Alternative * int_parse(unsigned char * data,int id, u_int8_t proto);
+extern void* int_clone(void* fval);
+extern void int_destroy(E2Feature *feature);
+extern void int_print(E2Feature *feature);
+extern void int_debug(E2TreeNode *node);
+
+
+/******************** Flags functions *****************************************/
+
+extern E2SuccessorNode* flags_pre_split_node(E2Rule *rules, u_int8_t feat);
+extern void flags_undo_split_node(E2SuccessorNode *succs);
+extern void* flags_final_split_node(E2SuccessorNode *succs, u_int8_t feat, int *feature_map, u_int8_t proto);
+extern void flags_propagate(E2TreeNode *node, E2Trigger *trigger, void *caller);
+extern E2Trigger * flags_navigate(E2TreeNode *node, Packet *p); 
+extern E2Alternative * flags_parse(unsigned char * data,int id, u_int8_t proto);
+extern void* flags_clone(void* fval);
+extern void flags_destroy(E2Feature *feature);
+extern void flags_print(E2Feature *feature);
+extern void flags_debug(E2TreeNode *node);
+
+
+/******************** IP Address v4 functions *********************************/ 
+
+extern E2SuccessorNode* ipv4_pre_split_node(E2Rule *rules, u_int8_t feat);
+extern void ipv4_undo_split_node(E2SuccessorNode *succs);
+extern void* ipv4_final_split_node(E2SuccessorNode *succs, u_int8_t feat, int *feature_map, u_int8_t proto);
+extern void ipv4_propagate(E2TreeNode *node, E2Trigger *trigger, void *caller);
+extern E2Trigger * ipv4_navigate(E2TreeNode *node, Packet *p);
+extern E2Alternative * ipv4_parse(unsigned char * data, int id, u_int8_t proto);
+extern void* ipv4_clone(void* fval);
+extern void ipv4_destroy(E2Feature *feature);
+extern void ipv4_print(E2Feature *feature);
+extern void ipv4_debug(E2TreeNode *node);
+
+extern float log2;
+
+/******************** String functions ****************************************/
+
+extern E2SuccessorNode* string_pre_split_node(E2Rule *rules, u_int8_t feat);
+extern void string_undo_split_node(E2SuccessorNode *succs);
+extern void* string_final_split_node(E2SuccessorNode *succs, u_int8_t feat, int *feature_map, u_int8_t proto);
+extern void string_propagate(E2TreeNode *node, E2Trigger *trigger, void *caller);
+extern E2Trigger * string_navigate(E2TreeNode *node, Packet *p); 
+extern E2Alternative * string_parse(unsigned char * data,int id, u_int8_t proto);
+extern void* string_clone(void* fval);
+extern void string_destroy(E2Feature *feature);
+extern void string_print(E2Feature *feature);
+extern void string_debug(E2TreeNode *node);
+
+extern E2Alternative * nocase_parse(unsigned char * data, int id, u_int8_t proto);
+extern E2Alternative * offset_parse(unsigned char * data, int id, u_int8_t proto);
+extern E2Alternative * depth_parse(unsigned char * data, int id, u_int8_t proto);
+extern E2Trigger * search_all_content(unsigned char * payload, unsigned int payload_length, unsigned char * uriload, unsigned int uri_length, void * e2searchtables, E2Trigger *trigger);
+
+extern void * build_tables(void * old_ptr, E2Rule * rules_of_node, int start, int end);
+extern void * create_search_struct(void * content, void * uricontent);
+
+
+
+/******************** ip flags functions **************************************/
+
+extern E2Alternative * ip_option_parse(unsigned char * data,int id, u_int8_t proto);
+extern E2Alternative * ip_fragbits_parse(unsigned char * data,int id, u_int8_t proto);
+extern E2Alternative * tcp_flags_parse(unsigned char * data,int id, u_int8_t proto);
+
+extern void frag_flags_print (E2Feature * feature);
+extern void ip_options_print(E2Feature *feature);
+extern void tcp_flags_print  (E2Feature * feature);
+
+
+/******************************************************************************/
+/*                    FEATURE functions (i.e. extract) start here             */
+/******************************************************************************/
+
+extern void* extract_dsize (Packet *p);
+extern void* extract_icmp_id (Packet *p);
+extern void* extract_icmp_seq (Packet *p);
+extern void* extract_icode (Packet *p);
+extern void* extract_itype (Packet *p);
+extern void* extract_srcip (Packet *p);
+extern void* extract_dstip (Packet *p);
+extern void* extract_udp_srcport (Packet *p);
+extern void* extract_udp_dstport (Packet *p);
+extern void* extract_tcp_srcport (Packet *p);
+extern void* extract_tcp_dstport (Packet *p);
+extern void * extract_ipoptions (Packet *p);
+extern void * extract_ipfragbits (Packet *p);
+extern void * extract_ipid (Packet *p);
+extern void * extract_tcpflags (Packet * p);
+extern void * extract_content ( Packet * p);
+extern void * extract_uricontent ( Packet * p);
+#endif
+
diff -Naur snort-1.8.7.orig/parser.c snort-1.8.7p1/parser.c
--- snort-1.8.7.orig/parser.c	Wed May 15 06:30:59 2002
+++ snort-1.8.7p1/parser.c	Fri Sep  6 06:12:42 2002
@@ -898,7 +898,7 @@
 
     ProcessHeadNode(&proto_node, node->RuleList, protocol);
     rule_count++;
-    ParseRuleOptions(rule, node->mode, protocol);
+    ParseRuleOptions(rule, node->mode, protocol, 0);
     for(i=0;i<num_toks;i++)
     {
         free(toks[i]);
diff -Naur snort-1.8.7.orig/rules.c snort-1.8.7p1/rules.c
--- snort-1.8.7.orig/rules.c	Tue Jun 11 19:58:49 2002
+++ snort-1.8.7p1/rules.c	Mon Sep  9 02:12:34 2002
@@ -18,6 +18,19 @@
 */
 
 #include "rules.h"
+#include "engine2.h"
+
+/* Start: Engine 2 */
+
+E2Alternative *e2_alternatives_list=NULL;
+
+E2Rule *e2rule_list;
+E2Rule *e2rule_tmp;
+OptTreeNode * e2_opt_tree_node;
+
+int e2_jump, e2_fast_debug;
+
+/* End: Engine 2 */
 
 ListHead Alert;         /* Alert Block Header */
 ListHead Log;           /* Log Block Header */
@@ -67,6 +80,13 @@
 int cmpcount;           /* compare counter */
 #endif
 
+/* Start: Engine 2 */
+
+int RuleListEnd(Packet * p, struct _RuleTreeNode *rtn_idx, RuleFpList * fp_list);
+RuleFpList RuleListTerminator = { 0, RuleListEnd, NULL };
+
+/* End: Engine 2 */
+
 
 /****************************************************************************
  *
@@ -100,6 +120,9 @@
             printf("\n+++++++++++++++++++++++++++++++++++++++++++++++++++\n");
             printf("Initializing rule chains...\n");
         }
+
+	/* init engine 2 rule_list */
+	e2rule_list = NULL;
     }
 
     stored_file_line = file_line;
@@ -249,6 +272,33 @@
 
         IntegrityCheckRules();
         /*FindMaxSegSize();*/
+
+	/* buld engine2 data structures */
+	e2rule_list=lists_merge(e2rule_list, collected_bidirectional_rules);
+
+#ifdef E2_DEBUG_PARSER
+	fprintf(stderr,"Dumping normal rules\n");
+	rules_dump(e2rule_list,4);
+#endif
+
+	/* engine 2: perform necessary consistency checks - check id of features */
+	{
+	    E2Rule * r;
+	    r = e2rule_list;
+	    
+	    while (r!=NULL) {
+		int i;
+	      
+		for (i=0;i< e2_map_size[r->proto]; i++) {
+		    if (r->features[i]!=NULL)
+			if (r->features[i]->f_id>CONST_NUMBER_OF_FEATURES)
+			    FatalError("Engine2: a feature with ID > %d has been detected", CONST_NUMBER_OF_FEATURES);
+		}
+		r=r->next;
+	    }
+	}
+
+	build_e2_engine(e2rule_list, rule_ID_counter);
     }
 
     return;
@@ -416,6 +466,16 @@
     char *tmp;
     RuleTreeNode proto_node;
     int i;
+
+    /* Start: Engine 2 */
+    E2Rule * generated_rules;
+    int bidirectional=0;
+    int found =0;
+    char * opts[2];
+  
+    u_int8_t proto;
+    /* End: Engine 2 */
+
 #ifdef PARSERULE_BIFURCATE
     int bid = 0;
 #endif
@@ -430,6 +490,9 @@
 
     strncpy(rule, ExpandVars(prule), PARSERULE_SIZE-1);
 
+    /* initialize  (contains alternatives) */
+    e2_alternatives_list=freeAlternatives(e2_alternatives_list);
+
     /* break out the tokens from the rule string */
     toks = mSplit(rule, " ", 10, &num_toks, 0);
 
@@ -605,6 +668,30 @@
     /* set the rule protocol */
     protocol = WhichProto(toks[1]);
 
+    switch (protocol) {
+
+	case IPPROTO_ICMP:
+  	    proto = E2_PROTO_ICMP;
+	    break;
+	    
+	case IPPROTO_UDP:
+	    proto = E2_PROTO_UDP;
+	    break;
+	    
+	case IPPROTO_TCP:
+  	    proto = E2_PROTO_TCP;
+	    break;
+
+	case ETHERNET_TYPE_IP:
+	    proto = E2_PROTO_IP;
+	    break;
+	    
+	default:
+	    FatalError("Protocol '%s' is not supported!",toks[1]);
+	    return;
+	    
+    }
+
     /* Process the IP address and CIDR netmask */
     /* changed version 1.2.1 */
     /*
@@ -624,6 +711,10 @@
        else
        {*/
     ProcessIP(toks[2], &proto_node, SRC);
+    opts[0]="srcip";
+    opts[1]=toks[2];
+    found=0;
+    e2_alternatives_list=e2_parse(opts, e2_alternatives_list,0, proto);
     /*}*/
 
     /* check to make sure that the user entered port numbers */
@@ -646,6 +737,11 @@
     if(proto_node.not_sp_flag)
         proto_node.flags |= EXCEPT_SRC_PORT;
 
+    opts[0]="srcport";
+    opts[1]=toks[3];
+    found=0;
+    e2_alternatives_list=e2_parse(opts, e2_alternatives_list,0, proto);
+
     /* New in version 1.3: support for bidirectional rules */
     /*
      * this checks the rule "direction" token and sets the bidirectional flag
@@ -657,6 +753,7 @@
         printf("Bidirectional rule!\n");
 #endif
         proto_node.flags |= BIDIRECTIONAL;
+	bidirectional=1;
     }
 
     /* changed version 1.8.4
@@ -678,6 +775,10 @@
      * properly deal with it when we are processing packets
      * we found a negated address */
     ProcessIP(toks[5], &proto_node, DST);
+    opts[0]="dstip";
+    opts[1]=toks[5];
+    found=0;
+    e2_alternatives_list=e2_parse(opts, e2_alternatives_list,0, proto);
 
     if(ParsePort(toks[6], (u_short *) & proto_node.hdp,
                 (u_short *) & proto_node.ldp, toks[1],
@@ -688,6 +789,10 @@
 
     if(proto_node.not_dp_flag)
         proto_node.flags |= EXCEPT_DST_PORT;
+    opts[0]="dstport";
+    opts[1]=toks[6];
+    found=0;
+    e2_alternatives_list=e2_parse(opts, e2_alternatives_list,0, proto);
 
 #ifdef DEBUG
     printf("proto_node.flags = 0x%X\n", proto_node.flags);
@@ -726,7 +831,7 @@
     printf("Parsing Rule Options...\n");
 #endif
 
-    ParseRuleOptions(rule, rule_type, protocol);
+    ParseRuleOptions(rule, rule_type, protocol, proto);
 
 #ifdef PARSERULE_BIFURCATE
     if(bid)
@@ -742,6 +847,9 @@
     }
 #endif
 
+    generated_rules=generateIndividualRules(e2_alternatives_list, bidirectional, proto, e2_opt_tree_node);
+    e2rule_list=lists_merge(e2rule_list,generated_rules);
+
     for(i=0;i<num_toks;i++)
     {
         free(toks[i]);
@@ -927,7 +1035,7 @@
  * Returns: void function
  *
  ***************************************************************************/
-void AddRuleFuncToList(int (*func) (Packet *, struct _RuleTreeNode *, struct _RuleFpList *), RuleTreeNode * rtn)
+void AddRuleFuncToList(int (*func) (Packet *, struct _RuleTreeNode *, struct _RuleFpList *), RuleTreeNode * rtn, int feature_id)
 {
     RuleFpList *idx;
 
@@ -942,6 +1050,7 @@
         rtn->rule_func = (RuleFpList *) calloc(sizeof(RuleFpList), sizeof(char));
 
         rtn->rule_func->RuleHeadFunc = func;
+	rtn->rule_func->feature_id = feature_id;
     }
     else
     {
@@ -952,6 +1061,7 @@
 
         idx = idx->next;
         idx->RuleHeadFunc = func;
+	idx->feature_id = feature_id;
     }
 }
 
@@ -980,7 +1090,7 @@
 #ifdef DEBUG
         printf("CheckBidirectional->\n");
 #endif
-        AddRuleFuncToList(CheckBidirectional, rtn);
+        AddRuleFuncToList(CheckBidirectional, rtn, -1);
     }
     else
     {
@@ -1016,7 +1126,7 @@
 #endif
 
     /* tack the end (success) function to the list */
-    AddRuleFuncToList(RuleListEnd, rtn);
+    AddRuleFuncToList(RuleListEnd, rtn, -1);
 }
 
 
@@ -1053,7 +1163,7 @@
 #ifdef DEBUG
                 printf("CheckSrcIP -> ");
 #endif
-                AddRuleFuncToList(CheckSrcIP, rtn);
+                AddRuleFuncToList(CheckSrcIP, rtn, CONST_FEATURE_ID_IP_SRCIP);
             }
 
             break;
@@ -1064,7 +1174,7 @@
 #ifdef DEBUG
                 printf("CheckDstIP -> ");
 #endif
-                AddRuleFuncToList(CheckDstIP, rtn);
+                AddRuleFuncToList(CheckDstIP, rtn, CONST_FEATURE_ID_IP_DSTIP);
             }
 
             break;
@@ -1106,14 +1216,14 @@
 #ifdef DEBUG
                 printf("CheckSrcPortNotEq -> ");
 #endif
-                AddRuleFuncToList(CheckSrcPortNotEq, rtn);
+                AddRuleFuncToList(CheckSrcPortNotEq, rtn, CONST_FEATURE_ID_TCPUDP_SRCPORT);
                 break;
 
             case DST:
 #ifdef DEBUG
                 printf("CheckDstPortNotEq -> ");
 #endif
-                AddRuleFuncToList(CheckDstPortNotEq, rtn);
+                AddRuleFuncToList(CheckDstPortNotEq, rtn, CONST_FEATURE_ID_TCPUDP_DSTPORT);
                 break;
         }
 
@@ -1126,14 +1236,14 @@
 #ifdef DEBUG
             printf("CheckSrcPortEqual -> ");
 #endif
-            AddRuleFuncToList(CheckSrcPortEqual, rtn);
+            AddRuleFuncToList(CheckSrcPortEqual, rtn, CONST_FEATURE_ID_TCPUDP_SRCPORT);
             break;
 
         case DST:
 #ifdef DEBUG
             printf("CheckDstPortEqual -> ");
 #endif
-            AddRuleFuncToList(CheckDstPortEqual, rtn);
+            AddRuleFuncToList(CheckDstPortEqual, rtn, CONST_FEATURE_ID_TCPUDP_DSTPORT);
             break;
     }
 
@@ -1155,7 +1265,7 @@
  * Returns: void function
  *
  ***************************************************************************/
-void AddOptFuncToList(int (*func) (Packet *, struct _OptTreeNode *, struct _OptFpList *), OptTreeNode * otn)
+void AddOptFuncToList(int (*func) (Packet *, struct _OptTreeNode *, struct _OptFpList *), OptTreeNode * otn, int feature_id)
 {
     OptFpList *idx;     /* index pointer */
 
@@ -1179,6 +1289,7 @@
 
         /* set the head function */
         otn->opt_func->OptTestFunc = func;
+	otn->opt_func->feature_id = feature_id;
     }
     else
     {
@@ -1201,6 +1312,7 @@
 
         /* link the function to the new node */
         idx->OptTestFunc = func;
+	idx->feature_id = feature_id;
 
 #ifdef DEBUG
         printf("Set OptTestFunc to %p\n", func);
@@ -1592,7 +1704,7 @@
  * Returns: void function
  *
  ***************************************************************************/
-void ParseRuleOptions(char *rule, int rule_type, int protocol)
+void ParseRuleOptions(char *rule, int rule_type, int protocol, u_int8_t proto)
 {
     char **toks = NULL;
     char **opts;
@@ -1600,7 +1712,7 @@
     char *aux;
     int num_toks;
     int i;
-    int num_opts;
+    int num_opts = 0;
     int found = 0;
     OptTreeNode *otn_idx;
     KeywordXlateList *kw_idx;
@@ -1663,9 +1775,12 @@
     otn_tmp->proto_node = rtn_tmp;
     otn_tmp->event_data.sig_generator = GENERATOR_SNORT_ENGINE;
 
+    otn_tmp->rule_proto = proto;
+
     /* add link to parent RuleTreeNode */
     otn_tmp->rtn = rtn_tmp;
-
+    e2_opt_tree_node = otn_tmp;
+  
     /* find the start of the options block */
     idx = index(rule, '(');
     i = 0;
@@ -1853,6 +1968,9 @@
                     }
                 }
 
+		/* feed the engine 2 parser */
+		e2_alternatives_list = e2_parse(opts, e2_alternatives_list, 1, proto);
+
                 if(!found)
                 {
                     if(!strcasecmp(opts[0], "minfrag"))
@@ -1964,7 +2082,7 @@
         printf("OptListEnd\n");
 #endif
 
-        AddOptFuncToList(OptListEnd, otn_tmp);
+        AddOptFuncToList(OptListEnd, otn_tmp, -1);
     }
     else
     {
@@ -1972,7 +2090,7 @@
         printf("OptListEnd\n");
 #endif
 
-        AddOptFuncToList(OptListEnd, otn_tmp);
+        AddOptFuncToList(OptListEnd, otn_tmp, -1);
     }
 
 
@@ -2061,10 +2179,10 @@
     if(!strncasecmp(proto_str, "icmp", 4))
         return IPPROTO_ICMP;
 
-    if(!strncasecmp(proto_str, "ip", 4))
+    if(!strncasecmp(proto_str, "ip", 2))
         return ETHERNET_TYPE_IP;
 
-    if(!strncasecmp(proto_str, "arp", 4))
+    if(!strncasecmp(proto_str, "arp", 3))
         return ETHERNET_TYPE_ARP;
 
     /*
@@ -2659,6 +2777,9 @@
     char *read;
     char *write;
 
+    if (msg == NULL)
+	return;
+
     /* figure out where the message starts */
     ptr = index(msg, '"');
 
@@ -3696,6 +3817,261 @@
     return;
 }
 
+/*******************************************************************************
+ *                                                                             *
+ *                                                                             *
+ *    Engine 2 functions -> This is the new modified, detection engine         *
+ *                                                                             *
+ *                                                                             *
+ *******************************************************************************/
+
+void DebugAction()
+{
+    printf("§%d ", otn_tmp->rule_id);
+}
+
+int Engine2RuleFpListWrapper(Packet * p, struct _OptTreeNode *otn_idx, OptFpList * ofp_list)
+{
+    struct _RuleFpList *rfp_list, *trfp;   
+    int retval;
+
+    rfp_list = ofp_list->referring;
+
+    if (rfp_list != NULL) {
+    
+	trfp = rfp_list->next;
+	rfp_list->next = &RuleListTerminator;
+
+	retval = rfp_list->RuleHeadFunc(p, otn_idx->rtn, rfp_list);
+
+	rfp_list->next = trfp;
+    }
+    else {
+	printf("Fatal: RuleFpListWrapper: refering RuleTreeNode is null\n");
+	exit(1);
+    }
+
+    if (retval)
+	return ofp_list->next->OptTestFunc (p, otn_idx, ofp_list->next);
+    else
+	return 0;
+}
+
+
+/* imitate the EvalHeader functionality when a rule matches */
+int Engine2HandleMatch(OptTreeNode *otn, Packet *p, int * rval, u_int8_t proto)
+{
+    int rule_match;
+    RuleTreeNode *rtn_idx;
+
+    if (otn->type == RULE_DYNAMIC && !otn->active_flag)
+	return 0;
+
+    if (otn->opt_func_proto[proto] == NULL) {
+	printf("Fatal: Engine2HandleMatch: OptFuncPtr list is null\n");
+	exit(1);
+    }
+
+    rule_match = otn->opt_func_proto[proto]->OptTestFunc(p, otn, otn->opt_func_proto[proto]);
+
+    if (rule_match) {
+
+	rtn_idx = otn->rtn;
+	otn_tmp = otn;
+
+	if (otn->type != RULE_PASS)
+	    *rval = 1;
+
+	if (p->packet_flags & PKT_STREAM_UNEST_UNI &&
+	    pv.assurance_mode == ASSURE_EST &&
+	    (!(p->packet_flags & PKT_REBUILT_STREAM)) &&
+	    otn_tmp->stateless == 0)
+	{
+	    return 1;
+	}
+
+	TriggerResponses (rtn_idx->rsp_func, p);
+	
+	otn_tmp->event_data.event_id = event_id;
+
+	if (e2_fast_debug)
+	    DebugAction();
+
+	switch (rtn_idx->type)
+	{
+	    case RULE_PASS:
+	      PassAction ();
+	      break;
+
+	    case RULE_ACTIVATE:
+	      ActivateAction (p, otn_tmp, &otn_tmp->event_data);
+	      break;
+
+	    case RULE_ALERT:
+	      AlertAction (p, otn_tmp, &otn_tmp->event_data);
+	      break;
+
+	    case RULE_DYNAMIC:
+	      DynamicAction (p, otn_tmp, &otn_tmp->event_data);
+	      break;
+
+	    case RULE_LOG:
+	      LogAction (p, otn_tmp, &otn_tmp->event_data);
+	      break;
+	}
+
+	SetTags (p, otn_tmp, event_id);
+
+	if (rtn_idx->type != RULE_PASS)
+	{
+	    check_tags_flag = 0;
+	}
+	
+	return 1;
+    }
+    else 
+	return 0;
+}
+
+int Engine2EvalTrees(Packet * p, u_int8_t proto, int *rval)
+{
+    E2Trigger *first, *second;
+    u_int16_t index, f_inc, s_inc;
+    u_int32_t match;
+    int cnt, retval = 0;
+
+    if (NUM_TREE != 2) {
+	printf("Fatal: Engine2EvalTrees: Support for more than two trees not implemented yet\n");
+	exit(1);
+    }
+
+    if (trees[proto][0] != NULL) {
+	if ((first = trees[proto][0]->navigate(trees[proto][0], p)) == NULL)
+	    first = trees[proto][0]->trigger;
+    }
+    else
+	first = NULL;
+
+    if (trees[proto][1] != NULL) {
+	if ((second = trees[proto][1]->navigate(trees[proto][1], p)) == NULL)
+	    second = trees[proto][1]->trigger;
+    }
+    else
+	second = NULL;
+
+    if (first == NULL) {
+	if (second == NULL)
+	    return 0;
+	else {
+	    first = second;
+	    second = NULL;
+	}
+    }
+    
+    /* find all rules that still match and execute their corresponding OptFpLists */
+    if (second == NULL) {
+	
+	index = 0;
+	
+	match = first->rules[index] & 0xFFFF;
+	if (match != 0) {
+	    for (cnt = 0; cnt < 16; ++cnt) 
+		if (match & (1 << cnt)) {
+		    retval |= Engine2HandleMatch(snort_rules[(index << 4) + cnt], p, rval, proto);
+		    if (e2_jump && retval)
+			goto finish;
+		}
+	}
+	
+	while ((f_inc = (first->rules[index] >> 16)) != 0) {
+	    index += f_inc;
+	    
+	    match = first->rules[index] & 0xFFFF;
+	    if (match != 0) {
+		for (cnt = 0; cnt < 16; ++cnt) 
+		    if (match & (1 << cnt)) {
+			retval |= Engine2HandleMatch(snort_rules[(index << 4) + cnt], p, rval, proto);
+			if (e2_jump && retval)
+			    goto finish;
+		    }
+	    }
+	}	    
+    }
+    else {
+
+	index = 0;
+
+	match = (first->rules[index] & second->rules[index] & 0xFFFF);
+	if (match != 0) {
+	    for (cnt = 0; cnt < 16; ++cnt) 
+		if (match & (1 << cnt)) {
+		    retval |= Engine2HandleMatch(snort_rules[(index << 4) + cnt], p, rval, proto);
+		    if (e2_jump && retval)
+			goto finish;
+		}
+	}
+
+	for (;;) {
+
+	    f_inc = (first->rules[index] >> 16);
+	    s_inc = (second->rules[index] >> 16);
+
+	    if (!f_inc || !s_inc)
+		break;
+	    else if (f_inc > s_inc) 
+		index += f_inc;
+	    else 
+		index += s_inc;
+	
+	    match = (first->rules[index] & second->rules[index] & 0xFFFF);
+	    if (match != 0) {
+		for (cnt = 0; cnt < 16; ++cnt) 
+		    if (match & (1 << cnt)) {
+			retval |= Engine2HandleMatch(snort_rules[(index << 4) + cnt], p, rval, proto);
+			if (e2_jump && retval)
+			    goto finish;
+		    }
+	    }
+	}
+    }
+
+
+ finish:
+    return retval;
+}
+
+int Engine2EvalPacket(Packet * p, int * rval)
+{
+    int retval = 0;
+
+    *rval = 0;
+
+    if (p->iph == NULL)
+	return 0;
+    
+    /* figure out which list to look at */
+    switch (p->iph->ip_proto) {
+
+	case IPPROTO_TCP:
+	    DebugMessage (DEBUG_RULES, "Detecting on TcpList\n");
+	    retval = Engine2EvalTrees(p, E2_PROTO_TCP, rval);	    
+	    break;
+
+	case IPPROTO_UDP:
+	    DebugMessage (DEBUG_RULES, "Detecting on UdpList\n");
+	    retval = Engine2EvalTrees(p, E2_PROTO_UDP, rval);	    
+	    break;
+
+	case IPPROTO_ICMP:
+	    DebugMessage (DEBUG_RULES, "Detecting on IcmpList\n");
+	    retval = Engine2EvalTrees(p, E2_PROTO_ICMP, rval);	    
+	    break;
+ 
+	default:
+	    break;
+    }
+    return retval;
+}
 
 
 /****************************************************************************
@@ -3713,6 +4089,7 @@
 int Detect(Packet * p)
 {
     Event event;
+    int rval;
 
 #ifdef BENCHMARK
     cmpcount = 0;
@@ -3723,6 +4100,7 @@
 
     check_tags_flag = 1;
 
+#ifndef ENGINE2
     while(rule != NULL)
     {
 #ifdef DEBUG
@@ -3752,6 +4130,32 @@
 
         rule = rule->next;
     }
+#else
+    if (Engine2EvalPacket(p, &rval)) {
+
+	/* if a pass rule matches the current rule,
+	 * we still want to check the tags list
+	 */
+	if (check_tags_flag == 1) {
+	    
+	    if (CheckTagList (p, &event)) {
+		
+		DebugMessage (DEBUG_FLOW, "Matching tag node found, calling log functions\n");
+		
+		/* if we find a match, we want to send the packet to the logging mechanism */
+		CallLogFuncs (p, NULL, NULL, &event);
+	    }
+	}
+	
+	if (e2_fast_debug)
+	    printf("\n");
+	
+	return rval;
+    }
+#endif
+
+    if (e2_fast_debug)
+	printf("*\n");
 
     DebugMessage(DEBUG_FLOW, "Checking tags list (if check_tags_flag = 1)\n");
 
@@ -3926,6 +4330,9 @@
                 return 1;
             }
             
+	    if (e2_fast_debug)
+		DebugAction();
+
             TriggerResponses(rtn_idx->rsp_func, p);
 
             switch(rtn_idx->type)
diff -Naur snort-1.8.7.orig/rules.h snort-1.8.7p1/rules.h
--- snort-1.8.7.orig/rules.h	Wed May 15 06:30:59 2002
+++ snort-1.8.7p1/rules.h	Fri Sep  6 06:12:42 2002
@@ -20,6 +20,7 @@
 
 /*  I N C L U D E S  **********************************************************/
 #include "snort.h"
+#include "engine2.h"
 
 /*  D E F I N E S  ************************************************************/
 #ifndef __RULES_H__
@@ -98,6 +99,9 @@
 #endif
 /*#define PARSERULE_BIFURCATE   translate a <> b into a -> b, b -> a */
 
+/* needed for Engine 2 */
+#define NUM_PROTO 4
+
 /*  D A T A  S T R U C T U R E S  *********************************************/
 /* I'm forward declaring the rules structures so that the function
    pointer lists can reference them internally */
@@ -108,6 +112,8 @@
 /* function pointer list for rule head nodes */
 typedef struct _RuleFpList
 {
+    u_int16_t feature_id;
+
     /* rule check function pointer */
     int (*RuleHeadFunc)(Packet *, struct _RuleTreeNode *, struct _RuleFpList *);
 
@@ -119,6 +125,10 @@
 /* same as the rule header FP list */
 typedef struct _OptFpList
 {
+    u_int16_t feature_id;
+    
+    struct _RuleFpList *referring;
+
     int (*OptTestFunc)(Packet *, struct _OptTreeNode *, struct _OptFpList *);
 
     struct _OptFpList *next;
@@ -147,9 +157,15 @@
 
 typedef struct _OptTreeNode
 {
+    u_int16_t rule_id;
+    u_int8_t  rule_proto;
+
     /* plugin/detection functions go here */
     OptFpList *opt_func;
 
+    /* fp lists for each protocol -> needed for ip rules */
+    OptFpList *opt_func_proto[NUM_PROTO];  
+
     /* the ds_list is absolutely essential for the plugin system to work,
        it allows the plugin authors to associate "dynamic" data structures
        with the rule system, letting them link anything they can come up 
@@ -292,7 +308,7 @@
 void ProcessHeadNode(RuleTreeNode *, ListHead *, int);
 void ParsePreprocessor(char *);
 void ParseOutputPlugin(char *);
-void ParseRuleOptions(char *, int, int);
+void ParseRuleOptions(char *, int, int, u_int8_t);
 void ParseMessage(char *);
 void ParseLogto(char *);
 void ParseSID(char *, OptTreeNode *);
@@ -357,7 +373,7 @@
 void AddrToFunc(RuleTreeNode *, int);
 void PortToFunc(RuleTreeNode *, int, int, int);
 void SetupRTNFuncList(RuleTreeNode *);
-void AddOptFuncToList(int (*func)(Packet *,struct _OptTreeNode*,struct _OptFpList*), OptTreeNode *);
+void AddOptFuncToList(int (*func)(Packet *,struct _OptTreeNode*,struct _OptFpList*), OptTreeNode *, int feature_id);
 void AddRspFuncToList(int (*func) (Packet *, struct _RspFpList *), RuleTreeNode *, void *);
 void AddFuncToPreprocList(void (*func)(Packet *));
 void SetOutputList(void (*func)(Packet *, char *, void *, Event *), char, void *);
diff -Naur snort-1.8.7.orig/snort.c snort-1.8.7p1/snort.c
--- snort-1.8.7.orig/snort.c	Fri Jun 28 07:22:59 2002
+++ snort-1.8.7p1/snort.c	Fri Sep  6 06:19:16 2002
@@ -109,6 +109,9 @@
     InitNetmasks();
     InitProtoNames();
 
+    /* initialize the new detection engine */
+    e2_initialize();
+
     /* initialize the packet counter to loop forever */
     pv.pkt_cnt = -1;
 
@@ -593,6 +596,8 @@
     fputs("        -h <hn>    Home network = <hn>\n", stderr);
     fputs("        -i <if>    Listen on interface <if>\n", stderr);
     fputs("        -I         Add Interface name to alert output\n", stderr);
+    fputs("        -j         Jump immediately to next packet when first rule has matched (original snort behaviour)\n", stderr);
+    fputs("        -J         Enable fast debug mode for new engine (don't use it)\n", stderr);
     fputs("        -l <ld>    Log to directory <ld>\n", stderr);
     fputs("        -m <umask> Set umask = <umask>\n", stderr);
 #ifdef ENABLE_SMB_ALERTS
@@ -686,10 +691,10 @@
 
 #ifndef WIN32
     valid_options = "B:fk:TXL:IOCqS:pNA:m:F:DM:br:xeh:l:dc:n:P:"
-        "i:G:vV?aso6u:g:t:Uyz";
+        "i:G:vV?aso6u:g:t:UyzjJ";
 #else
     valid_options = "B:fk:TXL:IOCWqS:pNA:m:F:DM:br:xeh:l:dc:n:P:"
-        "i:G:vV?aEo6u:g:s:t:Uyzw:";
+        "i:G:vV?aEo6u:g:s:t:Uyzw:jJ";
 #endif
 
     /* loop through each command line var and process it */
@@ -924,6 +929,15 @@
                 pv.alert_interface_flag = 1;
                 break;
 
+	    case 'j':               /* Engine2 - jump to next packet after first rule has matched 
+				       imitate original Snort behavior */
+		e2_jump = 1;
+		break;
+		
+	    case 'J':               /* Engine2 - fast debug mode (don't use it) */
+		e2_fast_debug = 1;
+		break;
+
             case 'k':  /* set checksum mode */
 		if(!strcasecmp(optarg, "all"))
                 {
diff -Naur snort-1.8.7.orig/sp_dsize_check.c snort-1.8.7p1/sp_dsize_check.c
--- snort-1.8.7.orig/sp_dsize_check.c	Wed May 15 06:31:00 2002
+++ snort-1.8.7p1/sp_dsize_check.c	Fri Sep  6 06:12:42 2002
@@ -103,16 +103,16 @@
     if(*data == '>')
     {
         data++;
-        AddOptFuncToList(CheckDsizeGT, otn);
+        AddOptFuncToList(CheckDsizeGT, otn, CONST_FEATURE_ID_DSIZE);
     }
     else if(*data == '<')
     {
         data++;
-        AddOptFuncToList(CheckDsizeLT, otn);
+        AddOptFuncToList(CheckDsizeLT, otn, CONST_FEATURE_ID_DSIZE);
     }
     else
     {
-        AddOptFuncToList(CheckDsizeEq, otn);
+        AddOptFuncToList(CheckDsizeEq, otn, CONST_FEATURE_ID_DSIZE);
     }
 
     while(isspace((int)*data)) data++;
diff -Naur snort-1.8.7.orig/sp_icmp_code_check.c snort-1.8.7p1/sp_icmp_code_check.c
--- snort-1.8.7.orig/sp_icmp_code_check.c	Wed May 15 06:31:00 2002
+++ snort-1.8.7p1/sp_icmp_code_check.c	Fri Sep  6 06:12:42 2002
@@ -78,7 +78,7 @@
 
     /* finally, attach the option's detection function to the rule's 
        detect function pointer list */
-    AddOptFuncToList(IcmpCodeCheck, otn);
+    AddOptFuncToList(IcmpCodeCheck, otn, CONST_FEATURE_ID_ICMP_ICODE);
 }
 
 
diff -Naur snort-1.8.7.orig/sp_icmp_id_check.c snort-1.8.7p1/sp_icmp_id_check.c
--- snort-1.8.7.orig/sp_icmp_id_check.c	Wed May 15 06:31:00 2002
+++ snort-1.8.7p1/sp_icmp_id_check.c	Fri Sep  6 06:12:42 2002
@@ -104,7 +104,7 @@
 
     /* finally, attach the option's detection function to the rule's 
        detect function pointer list */
-    AddOptFuncToList(IcmpIdCheck, otn);
+    AddOptFuncToList(IcmpIdCheck, otn, CONST_FEATURE_ID_ICMP_ID);
 }
 
 
diff -Naur snort-1.8.7.orig/sp_icmp_seq_check.c snort-1.8.7p1/sp_icmp_seq_check.c
--- snort-1.8.7.orig/sp_icmp_seq_check.c	Fri Jun 28 07:22:59 2002
+++ snort-1.8.7p1/sp_icmp_seq_check.c	Fri Sep  6 06:12:42 2002
@@ -104,7 +104,7 @@
 
     /* finally, attach the option's detection function to the rule's 
        detect function pointer list */
-    AddOptFuncToList(IcmpSeqCheck, otn);
+    AddOptFuncToList(IcmpSeqCheck, otn, CONST_FEATURE_ID_ICMP_SEQ);
 }
 
 
diff -Naur snort-1.8.7.orig/sp_icmp_type_check.c snort-1.8.7p1/sp_icmp_type_check.c
--- snort-1.8.7.orig/sp_icmp_type_check.c	Wed May 15 06:31:00 2002
+++ snort-1.8.7p1/sp_icmp_type_check.c	Fri Sep  6 06:12:42 2002
@@ -78,7 +78,7 @@
 
     /* finally, attach the option's detection function to the rule's 
        detect function pointer list */
-    AddOptFuncToList(IcmpTypeCheck, otn);
+    AddOptFuncToList(IcmpTypeCheck, otn, CONST_FEATURE_ID_ICMP_ITYPE);
 }
 
 
diff -Naur snort-1.8.7.orig/sp_ip_fragbits.c snort-1.8.7p1/sp_ip_fragbits.c
--- snort-1.8.7.orig/sp_ip_fragbits.c	Wed May 15 06:31:00 2002
+++ snort-1.8.7p1/sp_ip_fragbits.c	Fri Sep  6 06:40:41 2002
@@ -104,7 +104,7 @@
 
     /* finally, attach the option's detection function to the rule's 
        detect function pointer list */
-    AddOptFuncToList(CheckFragBits, otn);
+    AddOptFuncToList(CheckFragBits, otn, CONST_FEATURE_ID_IP_FRAG_FLAGS);
 }
 
 
@@ -341,7 +341,7 @@
 
     /* finally, attach the option's detection function to the rule's 
        detect function pointer list */
-    AddOptFuncToList(CheckFragOffset, otn);
+    AddOptFuncToList(CheckFragOffset, otn, -1);
 }
 
 
diff -Naur snort-1.8.7.orig/sp_ip_id_check.c snort-1.8.7p1/sp_ip_id_check.c
--- snort-1.8.7.orig/sp_ip_id_check.c	Wed May 15 06:31:00 2002
+++ snort-1.8.7p1/sp_ip_id_check.c	Fri Sep  6 06:12:43 2002
@@ -73,7 +73,7 @@
 
     /* finally, attach the option's detection function to the rule's 
        detect function pointer list */
-    AddOptFuncToList(IpIdCheckEq, otn);
+    AddOptFuncToList(IpIdCheckEq, otn, CONST_FEATURE_ID_IP_ID);
 }
 
 
diff -Naur snort-1.8.7.orig/sp_ip_proto.c snort-1.8.7p1/sp_ip_proto.c
--- snort-1.8.7.orig/sp_ip_proto.c	Wed May 15 06:31:00 2002
+++ snort-1.8.7p1/sp_ip_proto.c	Fri Sep  6 06:12:43 2002
@@ -82,7 +82,7 @@
 
     /* finally, attach the option's detection function to the rule's 
        detect function pointer list */
-    AddOptFuncToList(IpProtoDetectorFunction, otn);
+    AddOptFuncToList(IpProtoDetectorFunction, otn, CONST_FEATURE_ID_PROTOCOL);
 }
 
 
diff -Naur snort-1.8.7.orig/sp_ip_same_check.c snort-1.8.7p1/sp_ip_same_check.c
--- snort-1.8.7.orig/sp_ip_same_check.c	Wed May 15 06:31:00 2002
+++ snort-1.8.7p1/sp_ip_same_check.c	Fri Sep  6 06:12:43 2002
@@ -74,7 +74,7 @@
 
     /* finally, attach the option's detection function to the rule's 
        detect function pointer list */
-    AddOptFuncToList(IpSameCheck, otn);
+    AddOptFuncToList(IpSameCheck, otn, CONST_FEATURE_ID_IP_SAMEIP);
 }
 
 
diff -Naur snort-1.8.7.orig/sp_ip_tos_check.c snort-1.8.7p1/sp_ip_tos_check.c
--- snort-1.8.7.orig/sp_ip_tos_check.c	Wed May 15 06:31:00 2002
+++ snort-1.8.7p1/sp_ip_tos_check.c	Fri Sep  6 06:12:43 2002
@@ -73,7 +73,7 @@
 
     /* finally, attach the option's detection function to the rule's 
        detect function pointer list */
-    AddOptFuncToList(IpTosCheckEq, otn);
+    AddOptFuncToList(IpTosCheckEq, otn, CONST_FEATURE_ID_IP_TOS);
 }
 
 
diff -Naur snort-1.8.7.orig/sp_ipoption_check.c snort-1.8.7p1/sp_ipoption_check.c
--- snort-1.8.7.orig/sp_ipoption_check.c	Wed May 15 06:31:00 2002
+++ snort-1.8.7p1/sp_ipoption_check.c	Fri Sep  6 06:12:43 2002
@@ -75,7 +75,7 @@
 
     /* finally, attach the option's detection function to the rule's 
        detect function pointer list */
-    AddOptFuncToList(CheckIpOptions, otn);
+    AddOptFuncToList(CheckIpOptions, otn, CONST_FEATURE_ID_IP_OPTIONS);
 }
 
 
diff -Naur snort-1.8.7.orig/sp_pattern_match.c snort-1.8.7p1/sp_pattern_match.c
--- snort-1.8.7.orig/sp_pattern_match.c	Wed May 15 06:31:00 2002
+++ snort-1.8.7p1/sp_pattern_match.c	Mon Sep 30 20:49:20 2002
@@ -75,7 +75,7 @@
 
 
     /* link the plugin function in to the current OTN */
-    AddOptFuncToList(CheckORPatternMatch, otn);
+    AddOptFuncToList(CheckORPatternMatch, otn, CONST_FEATURE_ID_CONTENT);
 
     return;
 }
@@ -94,7 +94,7 @@
     ParsePattern(data, otn);
 
     /* link the plugin function in to the current OTN */
-    AddOptFuncToList(CheckANDPatternMatch, otn);
+    AddOptFuncToList(CheckANDPatternMatch, otn, CONST_FEATURE_ID_CONTENT);
 
 #ifdef DEBUG
     DebugMessage(DEBUG_PATTERN_MATCH, "OTN function PatternMatch Added to rule!\n");
@@ -116,7 +116,7 @@
     ParsePattern(data, otn);
 
     /* link the plugin function in to the current OTN */
-    AddOptFuncToList(CheckUriPatternMatch, otn);
+    AddOptFuncToList(CheckUriPatternMatch, otn, CONST_FEATURE_ID_CONTENT_URI);
 
 #ifdef DEBUG
     DebugMessage(DEBUG_PATTERN_MATCH, "OTN function PatternMatch Added to rule!\n");
@@ -185,6 +185,7 @@
     while(isspace((int) *data))
         data++;
 
+    errno = 0;
     idx->depth = strtol(data, NULL, 10);
 
     if(errno == ERANGE)
diff -Naur snort-1.8.7.orig/sp_rpc_check.c snort-1.8.7p1/sp_rpc_check.c
--- snort-1.8.7.orig/sp_rpc_check.c	Wed May 15 06:31:00 2002
+++ snort-1.8.7p1/sp_rpc_check.c	Fri Sep  6 06:12:43 2002
@@ -79,7 +79,7 @@
 
     /* finally, attach the option's detection function to the rule's 
        detect function pointer list */
-    AddOptFuncToList(CheckRpc, otn);
+    AddOptFuncToList(CheckRpc, otn, CONST_FEATURE_ID_RPC);
 }
 
 
diff -Naur snort-1.8.7.orig/sp_session.c snort-1.8.7p1/sp_session.c
--- snort-1.8.7.orig/sp_session.c	Tue Jun 11 19:58:50 2002
+++ snort-1.8.7p1/sp_session.c	Fri Sep  6 06:12:43 2002
@@ -113,7 +113,7 @@
 
     /* finally, attach the option's detection function to the rule's 
        detect function pointer list */
-    AddOptFuncToList(LogSessionData, otn);
+    AddOptFuncToList(LogSessionData, otn, CONST_FEATURE_ID_TCP_SESSION);
 }
 
 
diff -Naur snort-1.8.7.orig/sp_tcp_ack_check.c snort-1.8.7p1/sp_tcp_ack_check.c
--- snort-1.8.7.orig/sp_tcp_ack_check.c	Wed May 15 06:31:00 2002
+++ snort-1.8.7p1/sp_tcp_ack_check.c	Fri Sep  6 06:12:43 2002
@@ -79,7 +79,7 @@
 
     /* finally, attach the option's detection function to the rule's 
        detect function pointer list */
-    AddOptFuncToList(CheckTcpAckEq, otn);
+    AddOptFuncToList(CheckTcpAckEq, otn, CONST_FEATURE_ID_TCP_ACK);
 }
 
 
diff -Naur snort-1.8.7.orig/sp_tcp_flag_check.c snort-1.8.7p1/sp_tcp_flag_check.c
--- snort-1.8.7.orig/sp_tcp_flag_check.c	Wed May 15 06:31:00 2002
+++ snort-1.8.7p1/sp_tcp_flag_check.c	Fri Sep  6 06:12:43 2002
@@ -50,7 +50,7 @@
 #endif
 
     /* link the plugin function in to the current OTN */
-    AddOptFuncToList(CheckTcpFlags, otn);
+    AddOptFuncToList(CheckTcpFlags, otn, CONST_FEATURE_ID_TCP_FLAGS);
 
 #ifdef DEBUG
     printf("OTN function CheckTcpFlags added to rule!\n");
diff -Naur snort-1.8.7.orig/sp_tcp_seq_check.c snort-1.8.7p1/sp_tcp_seq_check.c
--- snort-1.8.7.orig/sp_tcp_seq_check.c	Wed May 15 06:31:00 2002
+++ snort-1.8.7p1/sp_tcp_seq_check.c	Fri Sep  6 06:12:43 2002
@@ -79,7 +79,7 @@
 
     /* finally, attach the option's detection function to the rule's 
        detect function pointer list */
-    AddOptFuncToList(CheckTcpSeqEq, otn);
+    AddOptFuncToList(CheckTcpSeqEq, otn, CONST_FEATURE_ID_TCP_SEQ);
 }
 
 
diff -Naur snort-1.8.7.orig/sp_tcp_win_check.c snort-1.8.7p1/sp_tcp_win_check.c
--- snort-1.8.7.orig/sp_tcp_win_check.c	Wed May 15 06:31:00 2002
+++ snort-1.8.7p1/sp_tcp_win_check.c	Fri Sep  6 07:01:47 2002
@@ -79,7 +79,7 @@
 
     /* finally, attach the option's detection function to the rule's 
        detect function pointer list */
-    AddOptFuncToList(TcpWinCheckEq, otn);
+    AddOptFuncToList(TcpWinCheckEq, otn, -1);
 }
 
 
diff -Naur snort-1.8.7.orig/sp_ttl_check.c snort-1.8.7p1/sp_ttl_check.c
--- snort-1.8.7.orig/sp_ttl_check.c	Wed May 15 06:31:00 2002
+++ snort-1.8.7p1/sp_ttl_check.c	Fri Sep  6 06:12:43 2002
@@ -128,13 +128,13 @@
     }
     switch (ttlrel) {
         case '>':
-            AddOptFuncToList(CheckTtlGT, otn);
+            AddOptFuncToList(CheckTtlGT, otn, CONST_FEATURE_ID_IP_TTL);
             break;
         case '<':     
-            AddOptFuncToList(CheckTtlLT, otn);
+            AddOptFuncToList(CheckTtlLT, otn, CONST_FEATURE_ID_IP_TTL);
             break;
         case '=':
-            AddOptFuncToList(CheckTtlEq, otn);
+            AddOptFuncToList(CheckTtlEq, otn, CONST_FEATURE_ID_IP_TTL);
             break;
         case '-':
             while(isspace((int)*data)) data++;
@@ -152,7 +152,7 @@
                 ds_ptr->h_ttl = ds_ptr->ttl;
                 ds_ptr->ttl   = atoi(data);
             }
-            AddOptFuncToList(CheckTtlRG, otn);
+            AddOptFuncToList(CheckTtlRG, otn, CONST_FEATURE_ID_IP_TTL);
             break;
         default:
             /* wtf? */

