/* 
 *  Ths code in this file is part of tcptrack. For more information see
 *    http://www.rhythm.cx/~steve/devel/tcptrack
 *
 *     Copyright (C) Steve Benson - 2003
 *
 *  tcptrack 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, or (at your
 *  option) any later version.
 *   
 *  tcptrack 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 GNU Make; see the file COPYING.  If not, write to
 *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. 
 *  
 */
#define _BSD_SOURCE 1
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#ifdef HAVE_PCAP_PCAP_H
#include <pcap/pcap.h>
#elif HAVE_PCAP_H
#include <pcap.h>
#endif
#include <time.h>
#include "headers.h"
#include "List.h"
#include "ListIterator.h"
#include "TCPConnection.h"
#include "util.h"

bool TCPConnection::isFinished()
{
	if( state == TCP_STATE_CLOSED || state == TCP_STATE_RESET )
		return true;
	return false;
}

in_addr TCPConnection::getSrcAddr()
{
	return addr_src;
}

int TCPConnection::getSrcPort()
{
	return port_src;
}

in_addr TCPConnection::getDstAddr()
{
	return addr_dst;
}

int TCPConnection::getDstPort()
{
	return port_dst;
}

int TCPConnection::getPacketCount()
{
	return packet_count;
}

int TCPConnection::getState()
{
	return state;
}

TCPConnection::TCPConnection( const u_char *packet )
{
	const struct sniff_ip *ip; /* The IP header */
	const struct sniff_tcp *tcp; /* The TCP header */

	// parse the headers off the packet 
	ip = (struct sniff_ip*)(packet);
	tcp = (struct sniff_tcp*)(packet + IP_HEADER_LEN);

	// only a SYN packet can initiate a connection
	assert(tcp->th_flags & TH_SYN);

	addr_src.s_addr = ip->ip_src.s_addr;
	addr_dst.s_addr = ip->ip_dst.s_addr;
	port_src = tcp->th_sport;
	port_dst = tcp->th_dport;

	packet_count=1;
	state = TCP_STATE_SYN_SYNACK;
	
	// init per-second stats counters
	this_second = time(0);
	packets_this_second = 1;

	payload_bytes_this_second = ntohs(ip->ip_len)-(IP_HEADER_LEN+TCP_HEADER_LEN);
	all_bytes_this_second = ntohs(ip->ip_len);
	payload_bytes_last_second = 0;
	all_bytes_last_second = 0;



	last_pkt_ts = time(NULL);
	activity_toggle=false;

	//free((void *)packet); // TODO: debug;

}

// recalculate packets/bytes per second counters
// should be called once per second
void TCPConnection::recalcAvg()
{
	if( this_second != time(0) )
	{
		packets_last_second = packets_this_second;
		payload_bytes_last_second = payload_bytes_this_second;
		all_bytes_last_second = all_bytes_this_second;
			
		this_second = time(0);
		packets_this_second = 0;
		payload_bytes_this_second = 0;
		all_bytes_this_second = 0;
	}
}

time_t TCPConnection::getLastPktTimestamp()
{
	return last_pkt_ts;
}

bool TCPConnection::match(in_addr sa, in_addr da, int sp, int dp)
{
	if( addr_src.s_addr != sa.s_addr ) 
		return false;
	if( addr_dst.s_addr != da.s_addr )
		return false;
	if( dp != port_dst  ||  sp != port_src )
		return false;
	return true;
}

time_t TCPConnection::getIdleSeconds()
{
	return time(NULL) - getLastPktTimestamp();
}

bool TCPConnection::acceptPacket( const u_char *packet )
{
	const struct sniff_ip *ip; /* The IP header */
	const struct sniff_tcp *tcp; /* The TCP header */

	// parse the headers off the packet 
	ip = (struct sniff_ip*)(packet);
	tcp = (struct sniff_tcp*)(packet + IP_HEADER_LEN);

	if( state == TCP_STATE_CLOSED ) 
		return false;

	if(  match(ip->ip_src, ip->ip_dst, tcp->th_sport, tcp->th_dport) 
	  || match(ip->ip_dst, ip->ip_src, tcp->th_dport, tcp->th_sport) )
	{
		++packet_count;
		activity_toggle=true;

		// recalculate packets/bytes per second counters
		if( this_second != time(0) )
		{
			packets_last_second = packets_this_second;
			payload_bytes_last_second = payload_bytes_this_second;
			all_bytes_last_second = all_bytes_this_second;
			
			this_second = time(0);
			packets_this_second = 1;
			payload_bytes_this_second = ntohs(ip->ip_len)-(IP_HEADER_LEN+TCP_HEADER_LEN);
			all_bytes_this_second = ntohs(ip->ip_len);
		}
		else
		{
			packets_this_second++;
			payload_bytes_this_second += ntohs(ip->ip_len)-(IP_HEADER_LEN+TCP_HEADER_LEN);
			all_bytes_this_second += ntohs(ip->ip_len);
		}

		if( state == TCP_STATE_SYNACK_ACK )
			if( tcp->th_flags&TH_ACK ) 
				state = TCP_STATE_UP; // connection up

		if( state == TCP_STATE_SYN_SYNACK )
			if( tcp->th_flags&TH_SYN && tcp->th_flags&TH_ACK )
				state = TCP_STATE_SYNACK_ACK; // SYN|ACK sent, awaiting ACK

		if( state == TCP_STATE_UP )
			if( tcp->th_flags&TH_FIN )
				state = TCP_STATE_FIN_FINACK; // FIN sent, awaiting FIN|ACK

		if( state == TCP_STATE_FIN_FINACK )
			if( tcp->th_flags&TH_FIN  &&  tcp->th_flags&TH_ACK )
				state = TCP_STATE_CLOSED; // FIN|ACK sent, connection closed.
		
		if( tcp->th_flags&TH_RST )
			state = TCP_STATE_RESET;

		last_pkt_ts = time(NULL);

		//free((void *)packet); // TODO: debug THIS ONE
		return true;
	}
	return false;
}

// used only for debugging
void TCPConnection::print2( FILE *fp )
{
	char *saddr = in_addr2ascii(addr_src);
	char *daddr = in_addr2ascii(addr_dst);
	fprintf(fp,"%s:%d -> ",saddr,ntohs(port_src));
	fprintf(fp,"%s:%d  #",  daddr,ntohs(port_dst));
	fprintf(fp,"%d, ",packet_count);
	if( state == TCP_STATE_SYN_SYNACK )
		fprintf(fp,"awaiting SYN&ACK");
	if( state == TCP_STATE_SYNACK_ACK )
		fprintf(fp,"awaiting ACK");
	if( state == TCP_STATE_UP )
		fprintf(fp,"up");
	fprintf(fp,"\n");
	free(saddr); 
	free(daddr); 
}

int TCPConnection::getPacketsPerSecond()
{
	return packets_last_second;
}

bool TCPConnection::operator<( TCPConnection &other )
{
	return false;
	if( this->getPayloadBytesPerSecond() < other.getPayloadBytesPerSecond() )
		return true;
	else
		return false;
}

bool TCPConnection::operator>( TCPConnection &other )
{
	return false;
	if( this->getPayloadBytesPerSecond() > other.getPayloadBytesPerSecond() )
		return true;
	else
		return false;
}

int TCPConnection::getPayloadBytesPerSecond()
{
	return payload_bytes_last_second;
}

int TCPConnection::getAllBytesPerSecond()
{
	return all_bytes_last_second;
}

bool TCPConnection::activityToggle()
{
	bool r = activity_toggle;
	activity_toggle=false;
	return r;
}

