入侵检测

入侵检测系统首推开源项目snort,完善的匹配规则定义,灵活的插件扩充能力使其有强大的生命力,创建规则匹配的核心代码节选如下:

可以看出,在分解规则的时候,就已经用多个链表,将不同的规则进行分类了,可以根据分析的场景,有选择的去使用合适的规则,实现高效。

void ParseRule(char *prule, int inclevel)
{
    char **toks;		/* dbl ptr for mSplit call, holds rule tokens */
    int num_toks;		/* holds number of tokens found by mSplit */
    int rule_type;		/* rule type enumeration variable */
    char rule[1024];
    int protocol;
    RuleTreeNode proto_node;

    /* clean house */
    bzero((char *) &proto_node, sizeof(RuleTreeNode));

    /* chop off the <CR/LF> from the string */
    strip(prule);

    /* expand all variables */
    strcpy(rule, ExpandVars(prule));

    /* break out the tokens from the rule string */
    toks = mSplit(rule, " ", 10, &num_toks, 0);

#ifdef DEBUG
    printf("[*] Rule start\n");
#endif

    /* figure out what we're looking at */
    rule_type = RuleType(toks[0]);

#ifdef DEBUG
    printf("Rule type: ");
#endif

    /* handle non-rule entries */
    switch (rule_type)
    {
    case RULE_PASS:
#ifdef DEBUG
	printf("Pass\n");
#endif
	break;

    case RULE_LOG:
#ifdef DEBUG
	printf("Log\n");
#endif
	break;

    case RULE_ALERT:
#ifdef DEBUG
	printf("Alert\n");
#endif
	break;

    case RULE_INCLUDE:
#ifdef DEBUG
	printf("Include\n");
#endif
	ParseRulesFile(toks[1], inclevel + 1);
	return;

    case RULE_VAR:
#ifdef DEBUG
	printf("Variable\n");
#endif
	VarDefine(toks[1], toks[2]);
	return;

    case RULE_PREPROCESS:
#ifdef DEBUG
	printf("Preprocessor\n");
#endif
	ParsePreprocessor(rule);
	return;

    case RULE_OUTPUT:
#ifdef DEBUG
	printf("Output Plugin\n");
#endif
	ParseOutputPlugin(rule);
	return;

    case RULE_ACTIVATE:
#ifdef DEBUG
	printf("Activation rule\n");
#endif
	break;

    case RULE_DYNAMIC:
#ifdef DEBUG
	printf("Dynamic rule\n");
#endif
	break;

    case RULE_CONFIG:
#ifdef DEBUG
	printf("Rule file config\n");
#endif
	ParseConfig(rule);
	return;

    case RULE_DECLARE:
#ifdef DEBUG
	printf("Rule type declaration\n");
#endif
	ParseRuleTypeDeclaration(rule);
	return;

    case RULE_UNKNOWN:
#ifdef DEBUG
	printf("Unknown rule type, might be declared\n");
#endif
	ParseDeclaredRuleType(rule);
	return;

    default:
	printf("Invalid input: %s\n", prule);
	return;
    }

    proto_node.type = rule_type;

    /* set the rule protocol */
    protocol = WhichProto(toks[1]);

    /* Process the IP address and CIDR netmask */
    /* changed version 1.2.1 */
    /*
     * "any" IP's are now set to addr 0, netmask 0, and the normal rules are
     * applied instead of checking the flag
     */
    /*
     * if we see a "!<ip number>" we need to set a flag so that we can
     * properly deal with it when we are processing packets
     */
    if (*toks[2] == '!')	/* we found a negated address */
    {
	proto_node.flags |= EXCEPT_SRC_IP;
	ParseIP(&toks[2][1], (u_long *) & proto_node.sip,
		(u_long *) & proto_node.smask);
    } else
    {
	ParseIP(toks[2], (u_long *) & proto_node.sip,
		(u_long *) & proto_node.smask);
    }

    /* do the same for the port */
    if (ParsePort(toks[3], (u_short *) & proto_node.hsp,
		  (u_short *) & proto_node.lsp, toks[1],
		  (int *) &proto_node.not_sp_flag))
    {
	proto_node.flags |= ANY_SRC_PORT;
    }
    if (proto_node.not_sp_flag)
	proto_node.flags |= EXCEPT_SRC_PORT;

    /* New in version 1.3: support for bidirectional rules */
    /*
     * this checks the rule "direction" token and sets the bidirectional flag
     * if the token = '<>'
     */
    if (!strncmp("<>", toks[4], 2))
    {
#ifdef DEBUG
	printf("Bidirectional rule!\n");
#endif
	proto_node.flags |= BIDIRECTIONAL;
    }
    /* changed version 1.2.1 */
    /*
     * "any" IP's are now set to addr 0, netmask 0, and the normal rules are
     * applied instead of checking the flag
     */
    /*
     * if we see a "!<ip number>" we need to set a flag so that we can
     * properly deal with it when we are processing packets
     */
    if (*toks[5] == '!')	/* we found a negated address */
    {
#ifdef DEBUG
	printf("setting exception flag for dest IP\n");
#endif
	proto_node.flags |= EXCEPT_DST_IP;
	ParseIP(&toks[5][1], (u_long *) & proto_node.dip,
		(u_long *) & proto_node.dmask);
    } else
	ParseIP(toks[5], (u_long *) & proto_node.dip,
		(u_long *) & proto_node.dmask);

    if (ParsePort(toks[6], (u_short *) & proto_node.hdp,
		  (u_short *) & proto_node.ldp, toks[1],
		  (int *) &proto_node.not_dp_flag))
    {
	proto_node.flags |= ANY_DST_PORT;
    }
    if (proto_node.not_dp_flag)
	proto_node.flags |= EXCEPT_DST_PORT;

#ifdef DEBUG
    printf("proto_node.flags = 0x%X\n", proto_node.flags);
    printf("Processing Head Node....\n");
#endif

    switch (rule_type)
    {
    case RULE_ALERT:
	ProcessHeadNode(&proto_node, &Alert, protocol);
	break;

    case RULE_LOG:
	ProcessHeadNode(&proto_node, &Log, protocol);
	break;

    case RULE_PASS:
	ProcessHeadNode(&proto_node, &Pass, protocol);
	break;

    case RULE_ACTIVATE:
	ProcessHeadNode(&proto_node, &Activation, protocol);
	break;

    case RULE_DYNAMIC:
	ProcessHeadNode(&proto_node, &Dynamic, protocol);
	break;

    default:
	FatalError("Unable to determine rule type (%s) for processing, exiting!\n", toks[0]);
    }

    rule_count++;

#ifdef DEBUG
    printf("Parsing Rule Options...\n");
#endif

    ParseRuleOptions(rule, rule_type, protocol);

    free(toks);

    return;
}


/****************************************************************************
 *
 * Function: ProcessHeadNode(RuleTreeNode *, ListHead *, int)
 *
 * Purpose:  Process the header block info and add to the block list if
 *           necessary
 *
 * Arguments: test_node => data generated by the rules parsers
 *            list => List Block Header refernece
 *            protocol => ip protocol
 *
 * Returns: void function
 *
 ***************************************************************************/
void ProcessHeadNode(RuleTreeNode * test_node, ListHead * list, int protocol)
{
    int match = 0;
    RuleTreeNode *rtn_idx;
    int count = 0;

    /* select the proper protocol list to attach the current rule to */
    switch (protocol)
    {
    case IPPROTO_TCP:
	rtn_idx = list->TcpList;
	break;

    case IPPROTO_UDP:
	rtn_idx = list->UdpList;
	break;

    case IPPROTO_ICMP:
	rtn_idx = list->IcmpList;
	break;

    default:
	rtn_idx = NULL;
	break;
    }

    /*
     * if the list head is NULL (empty), make a new one and attach the
     * ListHead to it
     */
    if (rtn_idx == NULL)
    {
	head_count++;

	switch (protocol)
	{
	case IPPROTO_TCP:
	    list->TcpList = (RuleTreeNode *) calloc(sizeof(RuleTreeNode), sizeof(char));
	    rtn_tmp = list->TcpList;
	    break;

	case IPPROTO_UDP:
	    list->UdpList = (RuleTreeNode *) calloc(sizeof(RuleTreeNode), sizeof(char));
	    rtn_tmp = list->UdpList;
	    break;

	case IPPROTO_ICMP:
	    list->IcmpList = (RuleTreeNode *) calloc(sizeof(RuleTreeNode), sizeof(char));
	    rtn_tmp = list->IcmpList;
	    break;
	}

	/* copy the prototype header data into the new node */
	XferHeader(test_node, rtn_tmp);

	rtn_tmp->head_node_number = head_count;

	/* null out the down (options) pointer */
	rtn_tmp->down = NULL;

	/* add the function list to the new rule */
	SetupRTNFuncList(rtn_tmp);

	/* add link to parent listhead */
	rtn_tmp->listhead = list;

	return;
    }
    /* see if this prototype node matches any of the existing header nodes */
    match = TestHeader(rtn_idx, test_node);

    while ((rtn_idx->right != NULL) && !match)
    {
	count++;
	match = TestHeader(rtn_idx, test_node);

	if (!match)
	    rtn_idx = rtn_idx->right;
	else
	    break;
    }

    /*
     * have to check this twice since my loop above exits early, which sucks
     * but it's not performance critical
     */
    match = TestHeader(rtn_idx, test_node);

    /*
     * if it doesn't match any of the existing nodes, make a new node and
     * stick it at the end of the list
     */
    if (!match)
    {
#ifdef DEBUG
	printf("Building New Chain head node\n");
#endif

	head_count++;

	/* build a new node */
	rtn_idx->right = (RuleTreeNode *) calloc(sizeof(RuleTreeNode), sizeof(char));

	/* set the global ptr so we can play with this from anywhere */
	rtn_tmp = rtn_idx->right;

	/* uh oh */
	if (rtn_tmp == NULL)
	{
	    FatalError("ERROR: Unable to allocate Rule Head Node!!\n");
	}
	/* copy the prototype header info into the new header block */
	XferHeader(test_node, rtn_tmp);

	rtn_tmp->head_node_number = head_count;
	rtn_tmp->down = NULL;

	/* initialize the function list for the new RTN */
	SetupRTNFuncList(rtn_tmp);

	/* add link to parent listhead */
	rtn_tmp->listhead = list;
#ifdef DEBUG
	printf("New Chain head flags = 0x%X\n", rtn_tmp->flags);
#endif
    } else
    {
	rtn_tmp = rtn_idx;
#ifdef DEBUG
	printf("Chain head %d  flags = 0x%X\n", count, rtn_tmp->flags);
#endif

#ifdef DEBUG
	printf("Adding options to chain head %d\n", count);
#endif
    }
}


/****************************************************************************
 *
 * Function: AddRuleFuncToList(int (*func)(), RuleTreeNode *)
 *
 * Purpose:  Adds RuleTreeNode associated detection functions to the
 *          current rule's function list
 *
 * Arguments: *func => function pointer to the detection function
 *            rtn   => pointer to the current rule
 *
 * Returns: void function
 *
 ***************************************************************************/
void AddRuleFuncToList(int (*func) (Packet *, struct _RuleTreeNode *, struct _RuleFpList *), RuleTreeNode * rtn)
{
    RuleFpList *idx;

#ifdef DEBUG
    printf("Adding new rule to list\n");
#endif

    idx = rtn->rule_func;

    if (idx == NULL)
    {
	rtn->rule_func = (RuleFpList *) calloc(sizeof(RuleFpList), sizeof(char));

	rtn->rule_func->RuleHeadFunc = func;
    } else
    {
	while (idx->next != NULL)
	    idx = idx->next;

	idx->next = (RuleFpList *) calloc(sizeof(RuleFpList), sizeof(char));

	idx = idx->next;
	idx->RuleHeadFunc = func;
    }
}


/****************************************************************************
 *
 * Function: SetupRTNFuncList(RuleTreeNode *)
 *
 * Purpose: Configures the function list for the rule header detection
 *          functions (addrs and ports)
 *
 * Arguments: rtn => the pointer to the current rules list entry to attach to
 *
 * Returns: void function
 *
 ***************************************************************************/
void SetupRTNFuncList(RuleTreeNode * rtn)
{
#ifdef DEBUG
    printf("Initializing RTN function list!\n");
    printf("Functions: ");
#endif

    if (rtn->flags & BIDIRECTIONAL)
    {
#ifdef DEBUG
	printf("CheckBidirectional->\n");
#endif
	AddRuleFuncToList(CheckBidirectional, rtn);
    } else
    {
	/* link in the proper IP address detection function */
	/*
	 * the in-line "if" determines whether or not the negation operator
	 * has been set for this rule and tells the AddrToFunc call which
	 * function it should be linking in
	 */
	AddrToFunc(rtn, rtn->sip, rtn->smask, (rtn->flags & EXCEPT_SRC_IP ? 1 : 0), SRC);

	/* last verse, same as the first (but for dest IP) ;) */
	AddrToFunc(rtn, rtn->dip, rtn->dmask, (rtn->flags & EXCEPT_DST_IP ? 1 : 0), DST);

	/* Attach the proper port checking function to the function list */
	/*
	 * the in-line "if's" check to see if the "any" or "not" flags have
	 * been set so the PortToFunc call can determine which port testing
	 * function to attach to the list
	 */
	PortToFunc(rtn, (rtn->flags & ANY_SRC_PORT ? 1 : 0),
		   (rtn->flags & EXCEPT_SRC_PORT ? 1 : 0), SRC);

	/* as above */
	PortToFunc(rtn, (rtn->flags & ANY_DST_PORT ? 1 : 0),
		   (rtn->flags & EXCEPT_DST_PORT ? 1 : 0), DST);
    }

#ifdef DEBUG
    printf("RuleListEnd\n");
#endif

    /* tack the end (success) function to the list */
    AddRuleFuncToList(RuleListEnd, rtn);
}



/****************************************************************************
 *
 * Function: AddrToFunc(RuleTreeNode *, u_long, u_long, int, int)
 *
 * Purpose: Links the proper IP address testing function to the current RTN
 *          based on the address, netmask, and addr flags
 *
 * Arguments: rtn => the pointer to the current rules list entry to attach to
 *            ip =>  IP address of the current rule
 *            mask => netmask of the current rule
 *            exception_flag => indicates that a "!" has been set for this
 *                              address
 *            mode => indicates whether this is a rule for the source
 *                    or destination IP for the rule
 *
 * Returns: void function
 *
 ***************************************************************************/
void AddrToFunc(RuleTreeNode * rtn, u_long ip, u_long mask, int exception_flag, int mode)
{
    /*
     * if IP and mask are both 0, this is a "any" IP and we don't need to
     * check it
     */
    if ((ip == 0) && (mask == 0))
	return;

    /* if the exception flag is up, test with the exception function */
    if (exception_flag)
    {
	switch (mode)
	{
	case SRC:
#ifdef DEBUG
	    printf("CheckSrcIPNotEq -> ");
#endif
	    AddRuleFuncToList(CheckSrcIPNotEq, rtn);
	    break;

	case DST:
#ifdef DEBUG
	    printf("CheckDstIPNotEq -> ");
#endif
	    AddRuleFuncToList(CheckDstIPNotEq, rtn);
	    break;
	}

	return;
    }
    switch (mode)
    {
    case SRC:
#ifdef DEBUG
	printf("CheckSrcIPEqual -> ");
#endif
	AddRuleFuncToList(CheckSrcIPEqual, rtn);
	break;

    case DST:
#ifdef DEBUG
	printf("CheckDstIPEqual -> ");
#endif
	AddRuleFuncToList(CheckDstIPEqual, rtn);
	break;
    }
}



/****************************************************************************
 *
 * Function: PortToFunc(RuleTreeNode *, int, int, int)
 *
 * Purpose: Links in the port analysis function for the current rule
 *
 * Arguments: rtn => the pointer to the current rules list entry to attach to
 *            any_flag =>  accept any port if set
 *            except_flag => indicates negation (logical NOT) of the test
 *            mode => indicates whether this is a rule for the source
 *                    or destination port for the rule
 *
 * Returns: void function
 *
 ***************************************************************************/
void PortToFunc(RuleTreeNode * rtn, int any_flag, int except_flag, int mode)
{
    /*
     * if the any flag is set we don't need to perform any test to match on
     * this port
     */
    if (any_flag)
	return;

    /* if the except_flag is up, test with the "NotEq" funcs */
    if (except_flag)
    {
	switch (mode)
	{
	case SRC:
#ifdef DEBUG
	    printf("CheckSrcPortNotEq -> ");
#endif
	    AddRuleFuncToList(CheckSrcPortNotEq, rtn);
	    break;

	case DST:
#ifdef DEBUG
	    printf("CheckDstPortNotEq -> ");
#endif
	    AddRuleFuncToList(CheckDstPortNotEq, rtn);
	    break;
	}

	return;
    }
    /* default to setting the straight test function */
    switch (mode)
    {
    case SRC:
#ifdef DEBUG
	printf("CheckSrcPortEqual -> ");
#endif
	AddRuleFuncToList(CheckSrcPortEqual, rtn);
	break;

    case DST:
#ifdef DEBUG
	printf("CheckDstPortEqual -> ");
#endif
	AddRuleFuncToList(CheckDstPortEqual, rtn);
	break;
    }

    return;
}




/****************************************************************************
 *
 * Function: AddOptFuncToList(int (*func)(), OptTreeNode *)
 *
 * Purpose: Links the option detection module to the OTN
 *
 * Arguments: (*func)() => function pointer to the detection module
 *            otn =>  pointer to the current OptTreeNode
 *
 * Returns: void function
 *
 ***************************************************************************/
void AddOptFuncToList(int (*func) (Packet *, struct _OptTreeNode *, struct _OptFpList *), OptTreeNode * otn)
{
    OptFpList *idx;		/* index pointer */

#ifdef DEBUG
    printf("Adding new rule to list\n");
#endif

    /* set the index pointer to the start of this OTN's function list */
    idx = otn->opt_func;

    /* if there are no nodes on the function list... */
    if (idx == NULL)
    {
	/* calloc the list head */
	otn->opt_func = (OptFpList *) calloc(sizeof(OptFpList), sizeof(char));

	if (otn->opt_func == NULL)
	{
	    FatalError("ERROR => AddOptFuncToList new node calloc failed: %s\n", strerror(errno));
	}
	/* set the head function */
	otn->opt_func->OptTestFunc = func;
    } else
    {
	/* walk to the end of the list */
	while (idx->next != NULL)
	{
	    idx = idx->next;
	}

	/* allocate a new node on the end of the list */
	idx->next = (OptFpList *) calloc(sizeof(OptFpList), sizeof(char));

	if (idx->next == NULL)
	{
	    FatalError("ERROR => AddOptFuncToList new node calloc failed: %s\n", strerror(errno));
	}
	/* move up to the new node */
	idx = idx->next;

	/* link the function to the new node */
	idx->OptTestFunc = func;

#ifdef DEBUG
	printf("Set OptTestFunc to %p\n", func);
#endif
    }
}



/****************************************************************************
 *
 * Function: ParsePreprocessor(char *)
 *
 * Purpose: Walks the preprocessor function list looking for the user provided
 *          keyword.  Once found, call the preprocessor's initialization
 *          function.
 *
 * Arguments: rule => the preprocessor initialization string from the rules file
 *
 * Returns: void function
 *
 ***************************************************************************/
void ParsePreprocessor(char *rule)
{
    char **toks;		/* pointer to the tokenized array parsed from
				 * the rules list */
    char **pp_head;		/* parsed keyword list, with preprocessor
				 * keyword being the 2nd element */
    char *funcname;		/* the ptr to the actual preprocessor keyword */
    char *pp_args = NULL;	/* parsed list of arguments to the
				 * preprocessor */
    int num_toks;		/* number of tokens returned by the mSplit
				 * function */
    int found = 0;		/* flag var */
    PreprocessKeywordList *pl_idx;	/* index into the preprocessor
					 * keyword/func list */

    /* break out the arguments from the keywords */
    toks = mSplit(rule, ":", 2, &num_toks, '\\');

    if (num_toks >= 1)
    {
#ifdef DEBUG
	printf("toks[1] = %s\n", toks[1]);
#endif
	/* the args are everything after the ":" */
	pp_args = toks[1];
    }
    /* split the head section for the preprocessor keyword */
    pp_head = mSplit(toks[0], " ", 2, &num_toks, '\\');

    /* set a pointer to the actual keyword */
    funcname = pp_head[1];

    /* set the index to the head of the keyword list */
    pl_idx = PreprocessKeywords;

    /* walk the keyword list */
    while (pl_idx != NULL)
    {
#ifdef DEBUG
	printf("comparing: \"%s\" => \"%s\"\n", funcname, pl_idx->entry.keyword);
#endif
	/* compare the keyword against the current list element's keyword */
	if (!strcasecmp(funcname, pl_idx->entry.keyword))
	{
	    pl_idx->entry.func(pp_args);
	    found = 1;
	}
	if (!found)
	{
	    pl_idx = pl_idx->next;
	} else
	    break;
    }
    if (!found)
	printf("\n*WARNING*: unknown preprocessor \"%s\", ignoring!\n\n",
	       funcname);
}



void AddFuncToPreprocList(void (*func) (Packet *))
{
    PreprocessFuncNode *idx;

    idx = PreprocessList;

    if (idx == NULL)
    {
	PreprocessList = (PreprocessFuncNode *) calloc(sizeof(PreprocessFuncNode), sizeof(char));

	PreprocessList->func = func;
    } else
    {
	while (idx->next != NULL)
	    idx = idx->next;

	idx->next = (PreprocessFuncNode *) calloc(sizeof(PreprocessFuncNode), sizeof(char));

	idx = idx->next;
	idx->func = func;
    }

    return;
}


void ParseOutputPlugin(char *rule)
{
    char **toks;
    char **pp_head;
    char *funcname;
    char *pp_args = NULL;
    int num_toks;
    int found = 0;
    OutputKeywordList *pl_idx;

    toks = mSplit(rule, ":", 2, &num_toks, '\\');

    if (num_toks >= 1)
    {
	pp_args = toks[1];
    }
    pp_head = mSplit(toks[0], " ", 2, &num_toks, '\\');

    funcname = pp_head[1];

    pl_idx = OutputKeywords;

    while (pl_idx != NULL)
    {
#ifdef DEBUG
	printf("comparing: \"%s\" => \"%s\"\n", funcname, pl_idx->entry.keyword);
#endif
	if (!strcasecmp(funcname, pl_idx->entry.keyword))
	{
	    switch (pl_idx->entry.node_type)
	    {
	    case NT_OUTPUT_ALERT:
		if (!pv.alert_cmd_override)
		{
		    if (AlertFunc == NULL)
		    {
			AlertFunc = CallAlertPlugins;
		    }
		    /* call the configuration function for the plugin */
		    pl_idx->entry.func(pp_args);
		} else
		{
		    ErrorMessage("WARNING: command line overrides rules file alert plugin!\n");
		}

		break;

	    case NT_OUTPUT_LOG:
		if (!pv.log_cmd_override)
		{
		    if (LogFunc == NULL)
		    {
			LogFunc = CallLogPlugins;
		    }
		    /* call the configuration function for the plugin */
		    pl_idx->entry.func(pp_args);
		} else
		{
		    ErrorMessage("WARNING: command line overrides rules file logging plugin!\n");
		}

		break;
	    }

	    found = 1;
	}
	if (!found)
	{
	    pl_idx = pl_idx->next;
	} else
	    break;
    }

    if (!found)
    {
	printf("\n*WARNING*: unknown output plugin \"%s\", ignoring!\n\n", funcname);
    }
}

/*
 * frees the existing OutputList ands sets it a single node for the
 * function argument
 */
void SetOutputList(void (*func) (Packet *, char *, void *), char node_type,
		        void *arg)
{
    OutputFuncNode *idx;
    OutputFuncNode *prev;

    switch (node_type)
    {
    case NT_OUTPUT_ALERT:
	prev = AlertList;
	break;
    case NT_OUTPUT_LOG:
	prev = LogList;
	break;
    default:
	return;
    }
    while (prev != NULL)
    {
	idx = prev->next;
	free(prev);
	prev = idx;
    }
    return AddFuncToOutputList(func, node_type, arg);
}

void AddFuncToOutputList(void (*func) (Packet *, char *, void *), char node_type,
			      void *arg)
{

    switch (node_type)
    {
	case NT_OUTPUT_ALERT:
	if (head_tmp != NULL)
	    head_tmp->AlertList = AppendOutputFuncList(func, arg,
						       head_tmp->AlertList);
	else
	    AlertList = AppendOutputFuncList(func, arg, AlertList);
	break;

    case NT_OUTPUT_LOG:
	if (head_tmp != NULL)
	    head_tmp->LogList = AppendOutputFuncList(func, arg,
						     head_tmp->LogList);
	else
	    LogList = AppendOutputFuncList(func, arg, LogList);
	break;

    default:
	/* just to be error-prone */
	FatalError("Unknown nodetype: %i. Possible bug. please report\n",
		   node_type);
    }
}

OutputFuncNode *AppendOutputFuncList(void (*func) (Packet *, char *, void *),
			                   void *arg, OutputFuncNode * list)
{
    OutputFuncNode *idx = list;

    if (idx == NULL)
    {
	idx = (OutputFuncNode *) calloc(sizeof(OutputFuncNode), sizeof(char));
	idx->func = func;
	idx->arg = arg;
	list = idx;
    } else
    {
	while (idx->next != NULL)
	    idx = idx->next;

	idx->next = (OutputFuncNode *) calloc(sizeof(OutputFuncNode), sizeof(char));
	idx = idx->next;
	idx->func = func;
	idx->arg = arg;
    }
    idx->next = NULL;

    return list;
}




/****************************************************************************
 *
 * Function: ParseRuleOptions(char *, int)
 *
 * Purpose:  Process an individual rule's options and add it to the
 *           appropriate rule chain
 *
 * Arguments: rule => rule string
 *            rule_type => enumerated rule type (alert, pass, log)
 *
 * Returns: void function
 *
 ***************************************************************************/
void ParseRuleOptions(char *rule, int rule_type, int protocol)
{
    char **toks = NULL;
    char **opts;
    char *idx;
    char *aux;
    int num_toks;
    int i;
    int num_opts;
    int found = 0;
    OptTreeNode *otn_idx;
    KeywordXlateList *kw_idx;

    /* set the OTN to the beginning of the list */
    otn_idx = rtn_tmp->down;

    /*
     * make a new one and stick it either at the end of the list or hang it
     * off the RTN pointer
     */
    if (otn_idx != NULL)
    {
	/* loop to the end of the list */
	while (otn_idx->next != NULL)
	{
	    otn_idx = otn_idx->next;
	}

	/* setup the new node */
	otn_idx->next = (OptTreeNode *) calloc(sizeof(OptTreeNode), sizeof(char));

	/* set the global temp ptr */
	otn_tmp = otn_idx->next;

	if (otn_tmp == NULL)
	{
	    FatalError("ERROR: Unable to alloc OTN: %s", strerror(errno));
	}
	otn_tmp->next = NULL;
	opt_count++;

    } else
    {
	/* first entry on the chain, make a new node and attach it */
	otn_idx = (OptTreeNode *) calloc(sizeof(OptTreeNode), sizeof(char));

	bzero((char *) otn_idx, sizeof(OptTreeNode));

	otn_tmp = otn_idx;

	if (otn_tmp == NULL)
	{
	    FatalError("ERROR: Unable to alloc OTN!\n");
	}
	otn_tmp->next = NULL;
	rtn_tmp->down = otn_tmp;
	opt_count++;
    }

    otn_tmp->chain_node_number = opt_count;
    otn_tmp->type = rule_type;

    /* add link to parent RuleTreeNode */
    otn_tmp->rtn = rtn_tmp;

    /* find the start of the options block */
    idx = index(rule, '(');
    i = 0;

    if (idx != NULL)
    {
	idx++;

	/* find the end of the options block */
	aux = strrchr(idx, ')');

	/* get rid of the trailing ")" */
	if (aux == NULL)
	{
	    FatalError("ERROR: problems parsing rule file.\n");
	}
	*aux = 0;


	/* seperate all the options out, the seperation token is a semicolon */
	/*
	 * NOTE: if you want to include a semicolon in the content of your
	 * rule, it must be preceeded with a '\'
	 */
	toks = mSplit(idx, ";", 10, &num_toks, '\\');

#ifdef DEBUG
	printf("   Got %d tokens\n", num_toks);
#endif
	/* decrement the number of toks */
	num_toks--;

#ifdef DEBUG
	printf("Parsing options list: ");
#endif

	while (num_toks)
	{
#ifdef DEBUG
	    printf("   option: %s\n", toks[i]);
#endif

	    /* break out the option name from its data */
	    opts = mSplit(toks[i], ":", 4, &num_opts, '\\');

#ifdef DEBUG
	    printf("   option name: %s\n", opts[0]);
	    printf("   option args: %s\n", opts[1]);
#endif

	    /* advance to the beginning of the data (past the whitespace) */
	    while (isspace((int) *opts[0]))
		opts[0]++;

	    /* figure out which option tag we're looking at */
	    if (!strncasecmp(opts[0], "msg", 3))
	    {
		ParseMessage(opts[1]);
	    } else if (!strncasecmp(opts[0], "logto", 5))
	    {
		ParseLogto(opts[1]);
	    } else if (!strncasecmp(opts[0], "activates", 9))
	    {
		ParseActivates(opts[1]);
		dynamic_rules_present++;
	    } else if (!strncasecmp(opts[0], "activated_by", 12))
	    {
		ParseActivatedBy(opts[1]);
		dynamic_rules_present++;
	    } else if (!strncasecmp(opts[0], "count", 5))
	    {
		if (otn_tmp->type != RULE_DYNAMIC)
		    FatalError("The \"count\" option may only be used with the dynamic rule type!\n");

		ParseCount(opts[1]);
	    }
#ifdef ENABLE_RESPONSE
	    else if (!strncasecmp(opts[0], "resp", 4))
	    {
		otn_tmp->response_flag = RESP_RST_SND;

		ParseResponse(opts[1]);
	    }
#endif
	    else
	    {
		kw_idx = KeywordList;
		found = 0;

		while (kw_idx != NULL)
		{
#ifdef DEBUG
		    printf("comparing: \"%s\" => \"%s\"\n", opts[0], kw_idx->entry.keyword);
#endif
		    if (!strcasecmp(opts[0], kw_idx->entry.keyword))
		    {
			kw_idx->entry.func(opts[1], otn_tmp, protocol);
			found = 1;
#ifdef DEBUG
			printf("%s->", kw_idx->entry.keyword);
#endif
		    }
		    if (!found)
		    {
			kw_idx = kw_idx->next;
		    } else
			break;
		}

		if (!found)
		{
		    if (!strcasecmp(opts[0], "minfrag"))
		    {
			FatalError("\nERROR: %s (%d) => Minfrag is no longer a rule option, it is a\npreprocessor (please remove it from your rules file).  See RULES.SAMPLE or\nsnort-lib for examples of using the new preprocessors!\n", file_name, file_line);
		    } else
		    {
			FatalError("\nERROR: %s (%d) => Unknown keyword \"%s\" in rule!\n", file_name, file_line, opts[0]);
		    }
		}
	    }

	    free(opts);
	    --num_toks;
	    i++;
	}
#ifdef DEBUG
	printf("OptListEnd\n");
#endif

	AddOptFuncToList(OptListEnd, otn_tmp);
    } else
    {
#ifdef DEBUG
	printf("OptListEnd\n");
#endif

	AddOptFuncToList(OptListEnd, otn_tmp);
    }


    free(toks);
}


/****************************************************************************
 *
 * Function: RuleType(char *)
 *
 * Purpose:  Determine what type of rule is being processed and return its
 *           equivalent value
 *
 * Arguments: func => string containing the rule type
 *
 * Returns: The rule type designation
 *
 ***************************************************************************/
int RuleType(char *func)
{
    if (func == NULL)
    {
	FatalError("ERROR line %s (%d) => Unknown rule type (%s)\n", file_name, file_line, func);
    }
    if (!strncasecmp(func, "log", 3))
	return RULE_LOG;

    if (!strncasecmp(func, "alert", 5))
	return RULE_ALERT;

    if (!strncasecmp(func, "pass", 4))
	return RULE_PASS;

    if (!strncasecmp(func, "var", 3))
	return RULE_VAR;

    if (!strncasecmp(func, "include", 7))
	return RULE_INCLUDE;

    if (!strncasecmp(func, "preprocessor", 12))
	return RULE_PREPROCESS;

    if (!strncasecmp(func, "output", 6))
	return RULE_OUTPUT;

    if (!strncasecmp(func, "activate", 8))
	return RULE_ACTIVATE;

    if (!strncasecmp(func, "dynamic", 7))
	return RULE_DYNAMIC;

    if (!strncasecmp(func, "config", 6))
	return RULE_CONFIG;

    if (!strncasecmp(func, "ruletype", 8))
	return RULE_DECLARE;

    return RULE_UNKNOWN;
}



/****************************************************************************
 *
 * Function: WhichProto(char *)
 *
 * Purpose: Figure out which protocol the current rule is talking about
 *
 * Arguments: proto_str => the protocol string
 *
 * Returns: The integer value of the protocol
 *
 ***************************************************************************/
int WhichProto(char *proto_str)
{
    if (!strncasecmp(proto_str, "tcp", 3))
	return IPPROTO_TCP;

    if (!strncasecmp(proto_str, "udp", 3))
	return IPPROTO_UDP;

    if (!strncasecmp(proto_str, "icmp", 4))
	return IPPROTO_ICMP;

    /*
     * if we've gotten here, we have a protocol string we din't recognize and
     * should exit
     */
    FatalError("ERROR %s (%d) => Bad protocol: %s\n", file_name, file_line, proto_str);

    return 0;
}


/****************************************************************************
 *
 * Function: ParseIP(char *, u_long *, u_long *)
 *
 * Purpose: Convert a supplied IP address to it's network order 32-bit long
           value.  Also convert the CIDR block notation into a real
 *          netmask.
 *
 * Arguments: addr => address string to convert
 *            ip_addr => storage point for the converted ip address
 *            netmask => storage point for the converted netmask
 *
 * Returns: 0 for normal addresses, 1 for an "any" address
 *
 ***************************************************************************/
int ParseIP(char *paddr, u_long * ip_addr, u_long * netmask)
{
    char **toks;		/* token dbl buffer */
    int num_toks;		/* number of tokens found by mSplit() */
    int cidr = 1;		/* is network expressed in CIDR format */
    int nmask;			/* netmask temporary storage */
    char *addr;			/* string to parse, eventually a
				 * variable-contents */
    struct hostent *host_info;	/* various struct pointers for stuff */
    struct sockaddr_in sin;	/* addr struct */

    /* check for variable */
    if (!strncmp(paddr, "{1}quot;, 1))
    {
	if ((addr = VarGet(paddr + 1)) == NULL)
	{
	    FatalError("ERROR %s (%d) => Undefined variable %s\n", file_name, file_line, paddr);
	}
    } else
	addr = paddr;

    /* check for wildcards */
    if (!strncasecmp(addr, "any", 3))
    {
	*ip_addr = 0;
	*netmask = 0;
	return 1;
    }
    /* break out the CIDR notation from the IP address */
    toks = mSplit(addr, "/", 2, &num_toks, 0);
    /* "/" was not used as a delimeter, try ":" */
    if (num_toks == 1)
	toks = mSplit(addr, ":", 2, &num_toks, 0);

    /*
     * if we have a mask spec and it is more than two characters long, assume
     * it is netmask format
     */
    if ((num_toks > 1) && strlen(toks[1]) > 2)
	cidr = 0;

    switch (num_toks)
    {
    case 1:
	*netmask = netmasks[32];
	break;

    case 2:
	if (cidr)
	{
	    /* convert the CIDR notation into a real live netmask */
	    nmask = atoi(toks[1]);

	    /* it's pain to differ whether toks[1] is correct if netmask */
	    /* is /0, so we deploy some sort of evil hack with isdigit */

	    if (!isdigit((int) toks[1][0]))
		nmask = -1;

	    if ((nmask > -1) && (nmask < 33))
	    {
		*netmask = netmasks[nmask];
	    } else
	    {
		FatalError("ERROR %s (%d) => Invalid CIDR block for IP addr %s\n", file_name, file_line, addr);
	    }
	} else
	{
	    /* convert the netmask into its 32-bit value */

	    /* broadcast address fix from Steve Beaty <beaty@emess.mscd.edu> */

	    /*
	     * * if the address is the (v4) broadcast address, inet_addr *
	     * returns -1 which usually signifies an error, but in the *
	     * broadcast address case, is correct.  we'd use inet_aton() *
	     * here, but it's less portable.
	     */
	    if (!strncmp(toks[1], "255.255.255.255", 15))
	    {
		*netmask = INADDR_BROADCAST;
	    } else if ((*netmask = inet_addr(toks[1])) == -1)
	    {
		FatalError("ERROR %s (%d) => Rule netmask (%s) didn't x-late, WTF?\n", file_name, file_line, toks[1]);
	    }
	}
	break;

    default:
	FatalError("ERROR %s (%d) => Unrecognized IP address/netmask %s\n", file_name, file_line, addr);
	break;
    }

#ifndef WORDS_BIGENDIAN
    /*
     * since PC's store things the "wrong" way, shuffle the bytes into the
     * right order.  Non-CIDR netmasks are already correct.
     */
    if (cidr)
    {
	*netmask = htonl(*netmask);
    }
#endif

    /* convert names to IP addrs */
    if (isalpha((int) toks[0][0]))
    {
	/* get the hostname and fill in the host_info struct */
	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 %s (%d) => Couldn't resolve hostname %s\n",
		       file_name, file_line, toks[0]);
	}
	*ip_addr = ((u_long) (sin.sin_addr.s_addr) & (*netmask));
	return 1;
    }
    /* convert the IP addr into its 32-bit value */

    /* broadcast address fix from Steve Beaty <beaty@emess.mscd.edu> */

    /*
     * * if the address is the (v4) broadcast address, inet_addr returns -1 *
     * which usually signifies an error, but in the broadcast address case, *
     * is correct.  we'd use inet_aton() here, but it's less portable.
     */
    if (!strncmp(toks[0], "255.255.255.255", 15))
    {
	*ip_addr = INADDR_BROADCAST;
    } else if ((*ip_addr = inet_addr(toks[0])) == -1)
    {
	FatalError("ERROR %s (%d) => Rule IP addr (%s) didn't x-late, WTF?\n", file_name, file_line, toks[0]);
    } else
    {
	/* set the final homenet address up */
	*ip_addr = ((u_long) (*ip_addr) & (*netmask));
    }

    free(toks);

    return 0;
}



/****************************************************************************
 *
 * Function: ParsePort(char *, u_short *)
 *
 * Purpose:  Convert the port string over to an integer value
 *
 * Arguments: prule_port => port rule string
 *            port => converted integer value of the port
 *
 * Returns: 0 for a normal port number, 1 for an "any" port
 *
 ***************************************************************************/
int ParsePort(char *prule_port, u_short * hi_port, u_short * lo_port, char *proto, int *not_flag)
{
    char **toks;		/* token dbl buffer */
    int num_toks;		/* number of tokens found by mSplit() */
    char *rule_port;		/* port string */

    *not_flag = 0;

    /* check for variable */
    if (!strncmp(prule_port, "{1}quot;, 1))
    {
	if ((rule_port = VarGet(prule_port + 1)) == NULL)
	{
	    FatalError("ERROR %s (%d) => Undefined variable %s\n", file_name, file_line, prule_port);
	}
    } else
	rule_port = prule_port;

    /* check for wildcards */
    if (!strncasecmp(rule_port, "any", 3))
    {
	*hi_port = 0;
	*lo_port = 0;
	return 1;
    }
    if (rule_port[0] == '!')
    {
	*not_flag = 1;
	rule_port++;
    }
    if (rule_port[0] == ':')
    {
	*lo_port = 0;
    }
    toks = mSplit(rule_port, ":", 2, &num_toks, 0);

    switch (num_toks)
    {
    case 1:
	*hi_port = ConvPort(toks[0], proto);

	if (rule_port[0] == ':')
	{
	    *lo_port = 0;
	} else
	{
	    *lo_port = *hi_port;

	    if (index(rule_port, ':') != NULL)
	    {
		*hi_port = 65535;
	    }
	}

	return 0;

    case 2:
	*lo_port = ConvPort(toks[0], proto);

	if (toks[1][0] == 0)
	    *hi_port = 65535;
	else
	    *hi_port = ConvPort(toks[1], proto);

	return 0;

    default:
	FatalError("ERROR %s (%d) => port conversion failed on \"%s\"\n",
		   file_name, file_line, rule_port);
    }

    return 0;
}


/****************************************************************************
 *
 * Function: ConvPort(char *, char *)
 *
 * Purpose:  Convert the port string over to an integer value
 *
 * Arguments: port => port string
 *            proto => converted integer value of the port
 *
 * Returns:  the port number
 *
 ***************************************************************************/
int ConvPort(char *port, char *proto)
{
    int conv;			/* storage for the converted number */
    struct servent *service_info;

    /*
     * convert a "word port" (http, ftp, imap, whatever) to its corresponding
     * numeric port value
     */
    if (isalpha((int) port[0]) != 0)
    {
	service_info = getservbyname(port, proto);

	if (service_info != NULL)
	{
	    conv = ntohs(service_info->s_port);
	    return conv;
	} else
	{
	    FatalError("ERROR %s (%d) => getservbyname() failed on \"%s\"\n",
		       file_name, file_line, port);
	}
    }
    if (!isdigit((int) port[0]))
    {
	FatalError("ERROR %s (%d) => Invalid port: %s\n", file_name,
		   file_line, port);
    }
    /* convert the value */
    conv = atoi(port);

    /* make sure it's in bounds */
    if ((conv >= 0) && (conv < 65536))
    {
	return conv;
    } else
    {
	FatalError("ERROR %s (%d) => bad port number: %s", file_name,
		   file_line, port);
    }

    return 0;
}





/****************************************************************************
 *
 * Function: ParseMessage(char *)
 *
 * Purpose: Stuff the alert message onto the rule
 *
 * Arguments: msg => the msg string
 *
 * Returns: void function
 *
 ***************************************************************************/
void ParseMessage(char *msg)
{
    char *ptr;
    char *end;
    int size;

    /* figure out where the message starts */
    ptr = index(msg, '"');

    if (ptr == NULL)
    {
	ptr = msg;
    } else
	ptr++;

    end = index(ptr, '"');

    if (end != NULL)
	*end = 0;

    while (isspace((int) *ptr))
	ptr++;

    /* find the end of the alert string */
    size = strlen(msg) + 1;

    /* alloc space for the string and put it in the rule */
    if (size > 0)
    {
	otn_tmp->message = (char *) calloc((sizeof(char) * size), sizeof(char));
	strncpy(otn_tmp->message, ptr, size);
	otn_tmp->message[size - 1] = 0;

#ifdef DEBUG
	printf("Rule message set to: %s\n", otn_tmp->message);
#endif

    } else
    {
	ErrorMessage("ERROR %s (%d): bad alert message size %d\n", file_name, file_line, size);
    }
}



/****************************************************************************
 *
 * Function: ParseLogto(char *)
 *
 * Purpose: stuff the special log filename onto the proper rule option
 *
 * Arguments: filename => the file name
 *
 * Returns: void function
 *
 ***************************************************************************/
void ParseLogto(char *filename)
{
    char *sptr;
    char *eptr;

    /* grab everything between the starting " and the end one */
    sptr = index(filename, '"');
    eptr = strrchr(filename, '"');

    if (sptr != NULL && eptr != NULL)
    {
	/* increment past the first quote */
	sptr++;

	/* zero out the second one */
	*eptr = 0;
    } else
    {
	sptr = filename;
    }

    /* malloc up a nice shiny clean buffer */
    otn_tmp->logto = (char *) calloc(strlen(sptr) + 1, sizeof(char));

    bzero((char *) otn_tmp->logto, strlen(sptr) + 1);

    strncpy(otn_tmp->logto, sptr, strlen(sptr));
}



#ifdef ENABLE_RESPONSE
/****************************************************************************
 *
 * Function: ParseResponse(char *)
 *
 * Purpose: Figure out how to handle hostile connection attempts
 *
 * Arguments: type => string of comma-sepatared modifiers
 *
 * Returns: void function
 *
 ***************************************************************************/
void ParseResponse(char *type)
{
    char *p;

    while (isspace((int) *type))
	type++;

    if (!type || !(*type))
	return;

    otn_tmp->response_flag = 0;

    p = strtok(type, ",");
    while (p)
    {
	if (!strncasecmp(p, "rst_snd", 7))
	    otn_tmp->response_flag |= RESP_RST_SND;
	else if (!strncasecmp(p, "rst_rcv", 7))
	    otn_tmp->response_flag |= RESP_RST_RCV;
	else if (!strncasecmp(p, "rst_all", 7))
	    otn_tmp->response_flag |= (RESP_RST_SND | RESP_RST_RCV);
	else if (!strncasecmp(p, "icmp_net", 8))
	    otn_tmp->response_flag |= RESP_BAD_NET;
	else if (!strncasecmp(p, "icmp_host", 9))
	    otn_tmp->response_flag |= RESP_BAD_HOST;
	else if (!strncasecmp(p, "icmp_port", 9))
	    otn_tmp->response_flag |= RESP_BAD_PORT;
	else if (!strncasecmp(p, "icmp_all", 9))
	    otn_tmp->response_flag |= (RESP_BAD_NET | RESP_BAD_HOST | RESP_BAD_PORT);
	else
	{
	    FatalError("ERROR %s (%d): invalid response modifier: %s\n", file_name, file_line, p);
	}

	p = strtok(NULL, ",");
    }
}

#endif


/****************************************************************************
 *
 * Function: ParseActivates(char *)
 *
 * Purpose: Set an activation link record
 *
 * Arguments: act_num => rule number to be activated
 *
 * Returns: void function
 *
 ****************************************************************************/
void ParseActivates(char *act_num)
{
    /*
     * allocate a new node on the RTN get rid of whitespace at the front of
     * the list
     */
    while (!isdigit((int) *act_num))
	act_num++;

    otn_tmp->activates = atoi(act_num);
}




/****************************************************************************
 *
 * Function: ParseActivatedBy(char *)
 *
 * Purpose: Set an activation link record
 *
 * Arguments: act_by => rule number to be activated
 *
 * Returns: void function
 *
 ****************************************************************************/
void ParseActivatedBy(char *act_by)
{
    ActivateList *al_ptr;

    al_ptr = rtn_tmp->activate_list;

    if (al_ptr == NULL)
    {
	rtn_tmp->activate_list = (ActivateList *) calloc(sizeof(ActivateList), sizeof(char));

	if (rtn_tmp->activate_list == NULL)
	{
	    FatalError("ERROR: ParseActivatedBy() calloc failed: %s\n", strerror(errno));
	}
	al_ptr = rtn_tmp->activate_list;
    } else
    {
	while (al_ptr->next != NULL)
	{
	    al_ptr = al_ptr->next;
	}

	al_ptr->next = (ActivateList *) calloc(sizeof(ActivateList), sizeof(char));

	al_ptr = al_ptr->next;

	if (al_ptr == NULL)
	{
	    FatalError("ERROR: ParseActivatedBy() calloc failed: %s\n", strerror(errno));
	}
    }

    /* get rid of whitespace at the front of the list */
    while (!isdigit((int) *act_by))
	act_by++;

    /* set the RTN list node number */
    al_ptr->activated_by = atoi(act_by);

    /* set the OTN list node number */
    otn_tmp->activated_by = atoi(act_by);

    return;
}



void ParseCount(char *num)
{
    while (!isdigit((int) *num))
	num++;

    otn_tmp->activation_counter = atoi(num);

#ifdef DEBUG
    printf("Set activation counter to %d\n", otn_tmp->activation_counter);
#endif

    return;
}



/****************************************************************************
 *
 * Function: XferHeader(RuleTreeNode *, RuleTreeNode *)
 *
 * Purpose: Transfer the rule block header data from point A to point B
 *
 * Arguments: rule => the place to xfer from
 *            rtn => the place to xfer to
 *
 * Returns: void function
 *
 ***************************************************************************/
void XferHeader(RuleTreeNode * rule, RuleTreeNode * rtn)
{
    rtn->type = rule->type;
    rtn->sip = rule->sip;
    rtn->dip = rule->dip;
    rtn->smask = rule->smask;
    rtn->dmask = rule->dmask;
    rtn->hsp = rule->hsp;
    rtn->lsp = rule->lsp;
    rtn->hdp = rule->hdp;
    rtn->ldp = rule->ldp;
    rtn->flags = rule->flags;
}



/****************************************************************************
 *
 * Function: TestHeader(RuleTreeNode *, RuleTreeNode *)
 *
 * Purpose: Check to see if the two header blocks are identical
 *
 * Arguments: rule => uh
 *            rtn  => uuuuhhhhh....
 *
 * Returns: 1 if they match, 0 if they don't
 *
 ***************************************************************************/
int TestHeader(RuleTreeNode * rule, RuleTreeNode * rtn)
{
    if (rtn->sip == rule->sip)
    {
	if (rtn->dip == rule->dip)
	{
	    if (rtn->dmask == rule->dmask)
	    {
		if (rtn->smask == rule->smask)
		{
		    if (rtn->hsp == rule->hsp)
		    {
			if (rtn->lsp == rule->lsp)
			{
			    if (rtn->hdp == rule->hdp)
			    {
				if (rtn->ldp == rule->ldp)
				{
				    if (rtn->flags == rule->flags)
				    {
					return 1;
				    }
				}
			    }
			}
		    }
		}
	    }
	}
    }
    return 0;
}


/****************************************************************************
 *
 * Function: VarAlloc()
 *
 * Purpose: allocates memory for a variable
 *
 * Arguments: none
 *
 * Returns: pointer to new VarEntry
 *
 ***************************************************************************/
struct VarEntry *VarAlloc()
{
    struct VarEntry *new;

    if ((new = (struct VarEntry *) calloc(sizeof(struct VarEntry), sizeof(char))) == NULL)
    {
	FatalError("ERROR: cannot allocate memory for VarEntry.");
    }
    new->name = NULL;
    new->value = NULL;
    new->prev = NULL;
    new->next = NULL;

    return (new);
}

/****************************************************************************
 *
 * Function: VarDefine(char *, char *)
 *
 * Purpose: define the contents of a variable
 *
 * Arguments: name => the name of the variable
 *            value => the contents of the variable
 *
 * Returns: void function
 *
 ***************************************************************************/
void VarDefine(char *name, char *value)
{
    struct VarEntry *p;
    int found = 0;


    if (!VarHead)
    {
	p = VarAlloc();
	p->name = strdup(name);
	p->value = strdup(value);
	p->prev = p;
	p->next = p;

	VarHead = p;

	return;
    }
    p = VarHead;

    do
    {
	if (strcasecmp(p->name, name) == 0)
	{
	    found = 1;
	    break;
	}
	p = p->next;
    } while (p != VarHead);

    if (found)
    {
	if (p->value)
	    free(p->value);

	p->value = strdup(value);
    } else
    {
	p = VarAlloc();
	p->name = strdup(name);
	p->value = strdup(value);
	p->prev = VarHead;
	p->next = VarHead->next;
	p->next->prev = p;
	VarHead->next = p;
    }
}


/****************************************************************************
 *
 * Function: VarDelete(char *)
 *
 * Purpose: deletes a defined variable
 *
 * Arguments: name => the name of the variable
 *
 * Returns: void function
 *
 ***************************************************************************/
void VarDelete(char *name)
{
    struct VarEntry *p;


    if (!VarHead)
	return;

    p = VarHead;

    do
    {
	if (strcasecmp(p->name, name) == 0)
	{
	    p->prev->next = p->next;
	    p->next->prev = p->prev;

	    if (VarHead == p)
		if ((VarHead = p->next) == p)
		    VarHead = NULL;

	    if (p->name)
		free(p->name);

	    if (p->value)
		free(p->value);

	    free(p);

	    return;
	}
	p = p->next;

    } while (p != VarHead);
}


/****************************************************************************
 *
 * Function: VarGet(char *)
 *
 * Purpose: get the contents of a variable
 *
 * Arguments: name => the name of the variable
 *
 * Returns: char * to contents of variable or NULL
 *
 ***************************************************************************/
char *VarGet(char *name)
{
    struct VarEntry *p;


    if (!VarHead)
	return (NULL);

    p = VarHead;

    do
    {
	if (strcasecmp(p->name, name) == 0)
	    return (p->value);

	p = p->next;

    } while (p != VarHead);

    return (NULL);
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值