// $Id: RuleCondition.cc,v 1.8 2003/06/15 20:18:34 vern Exp $

#include "config.h"

#include "RuleCondition.h"
#include "TCP.h"
#include "Scope.h"

static inline bool is_established(const TCP_Endpoint* e)
	{
	// We more or less follow Snort here: an established session
	// is one for which the initial handshake has succeded (but we
	// add partial connections).  The connection tear-down is part
	// of the connection.
	return e->state != TCP_INACTIVE &&
	       e->state != TCP_SYN_SENT &&
	       e->state != TCP_SYN_ACK_SENT;
	}

bool RuleConditionTCPState::DoMatch(Rule* rule, RuleEndpointState* state,
					const u_char* data, int len)
	{
	// FIXME: Snort and Bro have different interpretations of established
	// sessions.  For comparision, we ignore tcp-state completely for now.
	return true;	// ###

	Connection* c = state->Conn();

	if ( c->ConnType() != CONN_TCP )
		return false;

	if ( tcpstates & STATE_STATELESS )
		return true;

	if ( (tcpstates & STATE_ORIG) && ! state->IsOrig() )
		return false;

	if ( (tcpstates & STATE_RESP) && state->IsOrig() )
		return false;

	if ( (tcpstates & STATE_ESTABLISHED ) &&
		! (is_established(((TCP_Connection*) state->Conn())->Orig()) &&
		   is_established(((TCP_Connection*) state->Conn())->Resp())))
		return false;

	return true;
	}

void RuleConditionTCPState::PrintDebug()
	{
	fprintf(stderr, "	RuleConditionTCPState: 0x%x\n", tcpstates);
	}

void RuleConditionIPOptions::PrintDebug()
	{
	fprintf(stderr, "	RuleConditionIPOptions: 0x%x\n", options);
	}

bool RuleConditionIPOptions::DoMatch(Rule* rule, RuleEndpointState* state,
					const u_char* data, int len)
	{
	// FIXME: Not implemented yet
	return false;
	}

void RuleConditionSameIP::PrintDebug()
	{
	fprintf(stderr, "	RuleConditionSameIP\n");
	}

bool RuleConditionSameIP::DoMatch(Rule* rule, RuleEndpointState* state,
					const u_char* data, int len)
	{
	return state->Conn()->OrigAddr() == state->Conn()->RespAddr();
	}

void RuleConditionPayloadSize::PrintDebug()
	{
	fprintf(stderr, "	RuleConditionPayloadSize %d\n", val);
	}

bool RuleConditionPayloadSize::DoMatch(Rule* rule, RuleEndpointState* state,
					const u_char* data, int len)
	{
#ifdef MATCHER_PRINT_DEBUG
	fprintf(stderr, "%.8f PayloadSize check: val = %d, payload_size = %d\n",
		network_time, val, state->PayloadSize());
#endif

	if ( state->PayloadSize() < 0 )
		// The size has not been set yet, i.e. we're matching
		// on the pure rules now.
		return false;

	uint32 payload_size = uint32(state->PayloadSize());

	switch ( comp ) {
	case RULE_EQ:
		return payload_size == val;

	case RULE_NE:
		return payload_size != val;

	case RULE_LT:
		return payload_size < val;

	case RULE_GT:
		return payload_size > val;

	case RULE_LE:
		return payload_size <= val;

	case RULE_GE:
		return payload_size >= val;

	default:
		internal_error("unknown comparision type");
	}

	// Should not be reached
	return false;
	}

RuleConditionEval::RuleConditionEval(const char* func)
	{
	id = global_scope()->Lookup(func);
	if ( ! id )
		{
		rules_error("unknown identifier");
		return;
		}
	}

bool RuleConditionEval::DoMatch(Rule* rule, RuleEndpointState* state,
					const u_char* data, int len)
	{
	if ( ! id->HasVal() )
		{
		run_time("undefined value");
		return false;
		}

	if ( id->Type()->Tag() != TYPE_FUNC )
		return id->ID_Val()->AsBool();

	// Call function with a signature_state value as argument.
	val_list* args = new val_list;
	args->append(rule_matcher->BuildRuleStateValue(rule, state));

	if ( data )
		args->append(new StringVal(len, (const char*) data));
	else
		args->append(new StringVal(""));

	return id->ID_Val()->AsFunc()->Call(args)->AsBool();
	}

void RuleConditionEval::PrintDebug()
	{
	fprintf(stderr, "	RuleConditionEval: %s\n", id->Name());
	}

