/* 
 * Copyright (C) 2002 scott campbell <axonpotential@yahoo.com>
 *
 * 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.
 *
 * --------------------------------------------------------------------------------
 * sp_stream_depth.c version 1.0 07/30/02
 * 
 * Purpose:
 * To allow a rule to access data in a tcp/udp stream based on the offset 
 * from the origin of that stream.  The offset can be measured in bytes or 
 * packets.
 * 
 * Arguments:
 * See the documentation web page for a complete listing of the plugin 
 * options.  it can be found at:
 * 	 http://www.geocities.com/axonpotential/snort/spp_stream_depth/index.html
 *
 * Comments:
 * The whole streams aspect of snort is a moving target.  Be prepared to see major changes
 * in this functionality, esp with regard to the conversation preprocessors evolution.
 *
 * 
 */

#include "sp_stream_depth.h"

extern char *file_name;  /* this is the file name from rules.c, generally used
                            for error messages */

extern int file_line;    /* this is the file line number from rules.c that is
                            used to indicate file lines for error messages */

/****************************************************************************
 * 
 * Function: SetupStreamAccess()
 *
 * Purpose: Generic detection engine plugin template.  Registers the
 *          configuration function and links it to a rule keyword.  This is
 *          the function that gets called from InitPlugins in plugbase.c.
 *
 * Arguments: None.
 *
 * Returns: void function
 *
 ****************************************************************************/
void SetupStreamAccess()
{
    /* map the keyword to an initialization/processing function */
    RegisterPlugin("stream_depth", StreamAccessInit);

    DEBUG_WRAP(DebugMessage(DEBUG_PLUGIN,"Plugin: stream_depth plugin Setup\n"););
}


/****************************************************************************
 * 
 * Function: StreamAccessInit(char *, OptTreeNode *)
 *
 * Purpose: Generic rule configuration function.  Handles parsing the rule 
 *          information and attaching the associated detection function to
 *          the OTN.
 *
 * Arguments: data => rule arguments/data
 *            otn => pointer to the current rule option list node
 *
 * Returns: void function
 *
 ****************************************************************************/
void StreamAccessInit(char *data, OptTreeNode *otn, int protocol)
{

	if(protocol != IPPROTO_TCP && protocol != IPPROTO_UDP)
	{
		FatalError("ERROR %s(%d) => Bad protocol in RPC Check rule...\n",
			file_name, file_line);
	}

    /* allocate the data structure and attach it to the
       rule's data struct list */
    otn->ds_list[PLUGIN_STREAM] = (StreamData *) calloc(sizeof(StreamData), sizeof(char));

    /* be sure to check that the protocol that is passed in matches the
       transport layer protocol that you're using for this rule! */

    /* this is where the keyword arguments are processed and placed into the 
       rule option's data structure */
    StreamParse(data, otn);

    /* finally, attach the option's detection function to the rule's 
       detect function pointer list */
    AddOptFuncToList(StreamFilter, otn);
}



/****************************************************************************
 * 
 * Function: TemplateRuleParseFunction(char *, OptTreeNode *)
 *
 * Purpose: This is the function that is used to process the option keyword's
 *          arguments and attach them to the rule's data structures.
 *
 * Arguments: data => argument data
 *            otn => pointer to the current rule's OTN
 *
 * Returns: void function
 *
 ****************************************************************************/
void StreamParse(char *data, OptTreeNode *otn)
{
    StreamData *ds_ptr;  /* data struct pointer */
    int num_toks = 4;
    int i = 0;
    char *token,*str, *p;

    /* set the ds pointer to make it easier to reference the option's
       particular data struct */
    ds_ptr = otn->ds_list[PLUGIN_STREAM];

    str = strdup(data);
    
    if(str == NULL)
    {
	    FatalError("ERROR: StreamArguments: can't strdup data\n");
    }

    p = str;

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

	if((token = strtok(p, ",")) == NULL)
	{
		FatalError("Parse error - bad command!\n");
	}
		
	
    	while (token)
	{
		//printf("PARSING TOKEN %s\n", token);

		if(!strcasecmp(token, "strm_pkt_eq"))
		{
			ds_ptr->command = STRM_PKT_EQ;
		}
		else if(!strcasecmp(token, "strm_pkt_lt"))
		{
			ds_ptr->command = STRM_PKT_LT;
		}
		else if(!strcasecmp(token, "strm_pkt_gt"))
		{
			ds_ptr->command = STRM_PKT_GT;
		}
		else if(!strcasecmp(token, "strm_pkt_lg"))
		{
			ds_ptr->command = STRM_PKT_LG;
		}
		else if(!strcasecmp(token, "strm_byte_lt"))
		{
			ds_ptr->command = STRM_BYTE_LT;
		}
		else if(!strcasecmp(token, "strm_byte_gt"))
		{
			ds_ptr->command = STRM_BYTE_GT;
		}
		else if(!strcasecmp(token, "strm_byte_lg"))
		{
			ds_ptr->command = STRM_BYTE_LG;
		}
		else
		{
			FatalError("Parse error - bad command!\n");
		}

		token = strtok(NULL, ",");
		//printf("PARSING TOKEN %s\n", token);
		ds_ptr->argument1 = atoi(token);
		token = strtok(NULL, ",");
		//printf("PARSING TOKEN %s\n", token);
		ds_ptr->argument2 = atoi(token);
		token = strtok(NULL, ",");
	}

}


/****************************************************************************
 * 
 * Function: TemplateDetectorFunction(char *, OptTreeNode *)
 *
 * Purpose: Use this function to perform the particular detection routine
 *          that this rule keyword is supposed to encompass.
 *
 * Arguments: data => argument data
 *            otn => pointer to the current rule's OTN
 *
 * Returns: If the detection test fails, this function *must* return a zero!
 *          On success, it calls the next function in the detection list 
 *
 ****************************************************************************/
int StreamFilter(Packet *p, struct _OptTreeNode *otn, OptFpList *fp_list)
{
	StreamData	*ds_ptr;
	Stream		*strm;
	Session		*sesn;
	int rtrn = 0; /* have single return value ... 1 = success*/

	ds_ptr=otn->ds_list[PLUGIN_STREAM];

	/* this breaks everything.  stay tuned for future implementations .. */
	/*if(!(p->packet_flags & PKT_REBUILT_STREAM))
	{
		printf("SKIPPING STREAM\n");
		return 0;
	}*/

	sesn=(Session*)(p->ssnptr);

	if(p->packet_flags & PKT_FROM_SERVER)
	{
		strm=(Stream*)(&sesn->server);
	}
	else if(p->packet_flags & PKT_FROM_CLIENT)
	{
		strm=(Stream*)(&sesn->client);
	}
	else 
	{
		printf("unknown packet state\n");
		return 0;
		/* we really ought to never get here ... */
	}

	
	/* Now that we have the stream counters at our disposal, we can
	 * reference them against the rule set.  Extending the rule set 
	 * should not be a problem */
	
	if(ds_ptr->command == STRM_PKT_EQ)
	{
		if(strm->pkts_sent == ds_ptr->argument1)
		{
			rtrn = 1;
		}
	}
	else if(ds_ptr->command == STRM_PKT_LT)
	{
		if(strm->pkts_sent < ds_ptr->argument1)
		{
			/*printf("LT: passed %d with a1=%d and a2=%d\n", strm->pkts_sent, 
					ds_ptr->argument1, ds_ptr->argument2); */
			rtrn = 1;
		}
	}
	else if(ds_ptr->command == STRM_PKT_GT)
	{
		if(strm->pkts_sent > ds_ptr->argument2)
		{
			/* printf("GT: passed %d with a1=%d and a2=%d\n", strm->pkts_sent, 
					ds_ptr->argument1, ds_ptr->argument2); */
			rtrn = 1;
		}
	}
	else if(ds_ptr->command == STRM_PKT_LG)
	{
		if((strm->pkts_sent > ds_ptr->argument1) && (strm->pkts_sent < ds_ptr->argument2))
		{
			/* printf("LG: passed %d with a1=%d and a2=%d\n", strm->pkts_sent, 
					ds_ptr->argument1, ds_ptr->argument2); */
			rtrn = 1;
		}
	}

	else if(ds_ptr->command == STRM_BYTE_LT)
	{
		if(strm->bytes_sent < ds_ptr->argument1)
		{
			/* printf("LTB: passed %d with a1=%d and a2=%d\n", strm->bytes_sent, 
					ds_ptr->argument1, ds_ptr->argument2); */
			rtrn = 1;
		}
	}
	else if(ds_ptr->command == STRM_BYTE_GT)
	{
		if(strm->bytes_sent > ds_ptr->argument1)
		{
			/* printf("GTB: passed %d with a1=%d and a2=%d\n", strm->bytes_sent, 
					ds_ptr->argument1, ds_ptr->argument2);*/
			rtrn = 1;
		}
	}
	else if(ds_ptr->command == STRM_BYTE_LG)
	{
		if((strm->bytes_sent > ds_ptr->argument1) && (strm->bytes_sent < ds_ptr->argument2))
		{
			/* printf("LGB: passed %d with a1=%d and a2=%d\n", strm->bytes_sent, 
					ds_ptr->argument1, ds_ptr->argument2); */
			rtrn = 1;
		}
	}

	
    if (rtrn == 1)
    {
        /* call the next function in the function list recursively */
        /* THIS CALL *MUST* BE IN THE PLUGIN, OTHERWISE YOU WILL BREAK
           SNORT'S DETECTION ENGINE!!! */
        return fp_list->next->OptTestFunc(p, otn, fp_list->next);
    }
#ifdef DEBUG
    else
    {
        /* you can put debug comments here or not */
        DebugMessage(DEBUG_PLUGIN,"No match\n");
    }
#endif

    /* if the test isn't successful, this function *must* return 0 */
    return 0;
}
