// $Id: UDP.cc,v 1.19 2003/08/31 00:38:11 vern Exp $
//
// Copyright (c) 1996, 1997, 1998, 1999, 2001, 2002
//      The Regents of the University of California.  All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that: (1) source code distributions
// retain the above copyright notice and this paragraph in its entirety, (2)
// distributions including binary code include the above copyright notice and
// this paragraph in its entirety in the documentation or other materials
// provided with the distribution, and (3) all advertising materials mentioning
// features or use of this software display the following acknowledgement:
// ``This product includes software developed by the University of California,
// Lawrence Berkeley Laboratory and its contributors.'' Neither the name of
// the University nor the names of its contributors may be used to endorse
// or promote products derived from this software without specific prior
// written permission.
// THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
// WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.

#include "config.h"

#include "Net.h"
#include "NetVar.h"
#include "UDP.h"

UDP_Connection::UDP_Connection(NetSessions* s, HashKey* k, double t,
		const ConnID* id, const struct udphdr* /* up */)
: Connection(s, k, t, id)
	{
	EnableStatusUpdateTimer();
	request_len = reply_len = -1;	// -1 means "haven't seen any activity"
	}

void UDP_Connection::Done()
	{
	FinishEndpointMatcher();
	finished = 1;
	}

void UDP_Connection::NextPacket(double t, int is_orig,
			const IP_Hdr* ip, int len, int caplen,
			const u_char*& data,
			int& /* record_packet */, int& record_content,
			const struct pcap_pkthdr* /* hdr */,
			const u_char* const /* pkt */,
			int /* hdr_size */)
	{
	if ( Skipping() )
		return;

	const struct udphdr* up = (const struct udphdr*) data;
	int chksum = up->uh_sum;

	if ( chksum && ! ignore_checksums && caplen >= len &&
	     udp_checksum(ip->IP4_Hdr(), up, len) != 0xffff )
		{
		Weird("bad_UDP_checksum");
		return;
		}

	if ( ntohs(up->uh_ulen) != len )
		Weird("UDP_datagram_length_mismatch");

	data += sizeof(struct udphdr);
	len -= sizeof(struct udphdr);

	last_time = t;

	if ( is_orig )
		{
		if ( request_len < 0 )
			request_len = len;
		else
			request_len += len;

		record_content = Request(t, data, len);
		}
	else
		{
		if ( reply_len < 0 )
			reply_len = len;
		else
			reply_len += len;

		reply_len = len;
		record_content = Reply(t, data, len);
		}

	if ( rule_matcher )
		{
		if ( is_orig )
			{
			if ( ! orig_match_state )
				InitEndpointMatcher(ip, caplen, 1);
			else
				ClearMatchState(1); // Don't match stream-wise
			}
		else
			{
			if ( ! resp_match_state )
				InitEndpointMatcher(ip, caplen, 0);
			else
				ClearMatchState(0);  // Don't match stream-wise
			}

		Match(Rule::PAYLOAD, data, len, false, false, is_orig);
		}
	}

int UDP_Connection::IsReuse(double /* t */, const u_char* /* pkt */)
	{
	return 0;
	}

void UDP_Connection::Describe(ODesc* d) const
	{
	d->Add(start_time);
	d->Add("(");
	d->Add(last_time);
	d->AddSP(")");

	d->Add(dotted_addr(orig_addr));
	d->Add(".");
	d->Add(ntohs(orig_port));

	d->SP();
	d->AddSP("->");

	d->Add(dotted_addr(resp_addr));
	d->Add(".");
	d->Add(ntohs(resp_port));
	}

int UDP_Connection::Request(double /* t */,
				const u_char* /* data */, int /* len */)
	{
	Event(udp_request);
	return 1;
	}

int UDP_Connection::Reply(double /* t */,
				const u_char* /* data */, int /* len */)
	{
	Event(udp_reply);
	return 1;
	}

void UDP_Connection::UpdateEndpointVal(RecordVal* endp, int is_orig)
	{
	int size = is_orig ? request_len : reply_len;
	if ( size < 0 )
		{
		endp->Assign(0, new Val(0, TYPE_COUNT));
		endp->Assign(1, new Val(int(UDP_INACTIVE), TYPE_COUNT));
		}

	else
		{
		endp->Assign(0, new Val(size, TYPE_COUNT));
		endp->Assign(1, new Val(int(UDP_ACTIVE), TYPE_COUNT));
		}
	}
