// $Id: TCP.cc,v 1.129 2003/09/04 06:04:55 vern Exp $
//
// Copyright (c) 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003
//      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 <arpa/inet.h>

#include <stdlib.h>

#include "Active.h"
#include "Net.h"
#include "NetVar.h"
#include "TCP.h"
#include "TCP_Rewriter.h"
#include "Sessions.h"
#include "Event.h"
#include "File.h"
#include "Val.h"

#define TOO_LARGE_SEQ_DELTA 1048576

TCP_Endpoint::TCP_Endpoint(TCP_Connection* arg_conn, int is_orig)
	{
	is_TCP_endpoint_contents = is_TCP_endpoint_line = is_TCP_NVT = 0;
	prev_state = state = TCP_INACTIVE;
	peer = 0;
	start_time = last_time = 0.0;
	start_seq = last_seq = ack_seq = 0;
	window = 0;
	window_scale = 0;
	window_seq = window_ack_seq = 0;
	contents_start_seq = 0;
	SYN_cnt = FIN_cnt = RST_cnt = 0;
	did_close = 0;
	analyzer = 0;
	contents_file = 0;
	conn = arg_conn;

	src_addr = is_orig ? conn->RespAddr() : conn->OrigAddr();
	dst_addr = is_orig ? conn->OrigAddr() : conn->RespAddr();

#ifdef BROv6
	checksum_base = ones_complement_checksum((void*) src_addr, 16, 0);
	checksum_base = ones_complement_checksum((void*) dst_addr, 16, checksum_base);
#else
	checksum_base = ones_complement_checksum((void*) src_addr, 4, 0);
	checksum_base = ones_complement_checksum((void*) dst_addr, 4, checksum_base);
#endif
	// Note, for IPv6, strictly speaking this field is 32 bits
	// rather than 16 bits.  But because the upper bits are all zero,
	// we get the same checksum either way.  The same applies to
	// later when we add in the data length in ValidChecksum().
	checksum_base += htons(IPPROTO_TCP);
	}

TCP_Endpoint::~TCP_Endpoint()
	{
	// No need to delete analyzers, that's done when our TCP_Connection
	// is deleted (really, Done()).
	}

void TCP_Endpoint::AddAnalyzer(TCP_EndpointAnalyzer* a)
	{
	a->AddAnalyzer(analyzer);
	analyzer = a;
	}

int TCP_Endpoint::ValidChecksum(const struct tcphdr* tp, int len) const
	{
	uint32 sum = checksum_base;
	int tcp_len = tp->th_off * 4 + len;

	if ( len % 2 == 1 )
		// Add in pad byte.
		sum += htons(((const u_char*) tp)[tcp_len - 1] << 8);

	sum += htons((unsigned short) tcp_len);	// fill out pseudo header
	sum = ones_complement_checksum((void*) tp, tcp_len, sum);

	return sum == 0xffff;
	}

void TCP_Endpoint::SetState(EndpointState new_state)
	{
	if ( new_state != state )
		{
		prev_state = state;
		state = new_state;
		}
	}

int TCP_Endpoint::Size() const
	{
	int size;

	if ( seq_delta(last_seq, ack_seq) > 0 || ack_seq == start_seq + 1 )
		// Either last_seq corresponds to more data sent than we've
		// seen ack'd, or we haven't seen any data ack'd (in which
		// case we should trust last_seq anyway).  This last test
		// matters for the case in which the connection has
		// transferred > 2 GB of data, in which case we will find
		// seq_delta(last_seq, ack_seq) < 0 even if ack_seq
		// corresponds to no data transferred.
		size = last_seq - start_seq;

	else
		// It could be that ack_seq > last_seq, if we've seen an
		// ack for the connection (say in a FIN) without seeing
		// the corresponding data.
		size = ack_seq - start_seq;

	// Don't include SYN octet in sequence space.  For partial connections
	// (no SYN seen), we're still careful to adjust start_seq as though
	// there was an initial SYN octet, because if we don't then the
	// packet reassembly code gets confused.
	if ( size != 0 )
		--size;

	if ( FIN_cnt > 0 && size != 0 )
		--size;	// don't include FIN octet.

	return size;
	}

int TCP_Endpoint::DataSent(double t, int seq, int len, const u_char* data,
				const IP_Hdr* ip, const struct tcphdr* tp)
	{
	int status = 0;

	for ( TCP_EndpointAnalyzer* a = analyzer; a; a = a->NextAnalyzer() )
		status = a->DataSent(t, seq, len, data, ip, tp) || status;

	if ( contents_file && seq + len > contents_start_seq )
		{
		int under_seq = contents_start_seq - seq;
		if ( under_seq > 0 )
			{
			seq += under_seq;
			data += under_seq;
			len -= under_seq;
			}

		FILE* f = contents_file->Seek(seq - contents_start_seq);

		if ( fwrite(data, 1, len, f) < unsigned(len) )
			// ### this should really generate an event
			internal_error("contents write failed");
		}

	if ( rule_matcher && ! is_TCP_endpoint_contents )
		conn->Match(Rule::PAYLOAD, data, len, false, false, IsOrig());

	return status;
	}

void TCP_Endpoint::AckReceived(double /* t */, int /* seq */)
	{
	}

void TCP_Endpoint::SetContentsFile(BroFile* f)
	{
	contents_file = f;
	contents_start_seq = last_seq - start_seq;
	}

TCP_Reassembler::~TCP_Reassembler()
	{
	if ( record_contents_file )
		{ // Record any undelivered data.
		if ( blocks && last_reassem_seq < last_block->upper )
			RecordToSeq(last_reassem_seq, last_block->upper,
					record_contents_file);
		Unref(record_contents_file);
		}
	}

void TCP_Reassembler::SetContentsFile(BroFile* f)
	{
	if ( ! f->File() )
		{
		run_time("no such file \"%s\"", f->Name());
		return;
		}

	if ( record_contents_file )
		// We were already recording, no need to catch up.
		Unref(record_contents_file);
	else
		{
		if ( blocks )
			RecordToSeq(blocks->seq, last_reassem_seq, f);
		}

	record_contents_file = f;
	}

void TCP_Reassembler::Undelivered(int up_to_seq)
	{
	if ( up_to_seq <= 2 && endp->conn->IsPartial() )
		// Since it was a partial connection, we faked up its
		// initial sequence numbers as though we'd seen a SYN.
		// We've now received the first ack and are getting a
		// complaint that either that data is missing (if
		// up_to_seq is 1), or one octet beyond it is missing
		// (if up_to_seq is 2).  The latter can occur when the
		// first packet we saw instantiating the partial connection
		// was a keep-alive.  So, in either case, just ignore it.
		return;

#if 0
	if ( endp->FIN_cnt > 0 )
		{
		// Make sure we're not worrying about undelivered
		// FIN control octets!
		int FIN_seq = endp->FIN_seq - endp->start_seq;
		if ( up_to_seq >= FIN_seq )
			up_to_seq = FIN_seq - 1;
		}
#endif
	if ( endp->FIN_cnt > 0 || endp->RST_cnt > 0 )
		// We could be running on a SYN/FIN/RST-filtered trace - don't
		// complain about data missing at the end of the connection.
		return;

	if ( up_to_seq <= last_reassem_seq )
		return;

	if ( record_contents_file )
		RecordToSeq(last_reassem_seq, up_to_seq, record_contents_file);

	// Inform the endpoint about the gap.
	endp->Undelivered(last_reassem_seq, up_to_seq - last_reassem_seq);

	if ( tcp_match_undelivered )
		MatchUndelivered(up_to_seq);

	last_reassem_seq = up_to_seq;	// we've done our best ...
	}

void TCP_Reassembler::MatchUndelivered(int up_to_seq)
	{
	if ( ! blocks || ! rule_matcher )
		return;

	if ( up_to_seq == -1 )
		up_to_seq = last_block->upper;

	if ( last_reassem_seq < up_to_seq )
		endp->Conn()->Weird("matching_undelivered_data");

	for ( DataBlock* b = blocks; b && b->upper <= up_to_seq; b = b->next )
		endp->Conn()->Match(Rule::PAYLOAD, b->block,
			b->upper - b->seq, false, false, endp->IsOrig());
	}

void TCP_Reassembler::RecordToSeq(int start_seq, int stop_seq, BroFile* f)
	{
	DataBlock* b = blocks;
	// Skip over blocks up to the start seq.
	while ( b && b->upper <= start_seq )
		b = b->next;

	if ( ! b )
		return;

	int last_seq = start_seq;
	while ( b && b->upper <= stop_seq )
		{
		if ( b->seq > last_seq )
			RecordGap(last_seq, b->seq, f);

		RecordBlock(b, f);
		last_seq = b->upper;
		b = b->next;
		}

	if ( b )
		// Check for final gap.
		if ( last_seq < stop_seq )
			RecordGap(last_seq, stop_seq, f);
	}

void TCP_Reassembler::RecordBlock(DataBlock* b, BroFile* f)
	{
	unsigned int len = b->upper - b->seq;
	if ( fwrite(b->block, 1, len, f->File()) < len )
		// ### this should really generate an event
		internal_error("contents write failed");
	}

void TCP_Reassembler::RecordGap(int start_seq, int upper_seq, BroFile* f)
	{
	if ( fprintf(f->File(), "\n<<gap %d>>\n", upper_seq - start_seq) <= 0 )
		// ### this should really generate an event
		internal_error("contents gap write failed");
	}

void TCP_Reassembler::BlockInserted(double t, DataBlock* start_block)
	{
	if ( start_block->seq > last_reassem_seq ||
	     start_block->upper <= last_reassem_seq )
		return;

	// We've filled a leading hole.  Deliver as much as possible.
	// Note that the new block may include both some old stuff
	// and some new stuff.  AddAndCheck() will have split the
	// new stuff off into its own block(s), but in the following
	// loop we have to take care not to deliver already-delivered
	// data.
	for ( DataBlock* b = start_block; b && b->seq <= last_reassem_seq;
	      b = b->next )
		{
		if ( b->seq == last_reassem_seq )
			{ // New stuff.
			int len = b->upper - b->seq;

			if ( record_contents_file )
				RecordBlock(b, record_contents_file);

			endp->Deliver(t, last_reassem_seq, len, b->block);
			if ( rule_matcher )
				endp->Conn()->Match(Rule::PAYLOAD, b->block,
							len, false, false, endp->IsOrig());
			last_reassem_seq += len;
			}
		}

	if ( ! endp->peer->IsTCP_EndpointContents() )
		// Our endpoint's peer doesn't do reassembly and so
		// (presumably) isn't processing acks.  So don't hold
		// the now-delivered data.
		TrimToSeq(last_reassem_seq);

	// Note: don't make an EOF check here, because then we'd miss it
	// for FIN packets that don't carry any payload (and thus
	// endpoint->DataSent is not called).  Instead, do the check in
	// TCP_Connection::NextPacket.
	}

void TCP_Reassembler::Overlap(const u_char* b1, const u_char* b2, int n)
	{
	if ( rexmit_inconsistency &&
	     memcmp((const void*) b1, (const void*) b2, n) &&
	     // The following weeds out keep-alives for which that's all
	     // we've ever seen for the connection.
	     (n > 1 || endp->peer->HasDoneSomething()) )
		{
		BroString* b1_s = new BroString((const u_char*) b1, n, 0);
		BroString* b2_s = new BroString((const u_char*) b2, n, 0);
		endp->conn->Event(rexmit_inconsistency,
					new StringVal(b1_s), new StringVal(b2_s));
		}
	}

TCP_EndpointContents::TCP_EndpointContents(TCP_Connection* arg_conn, int is_orig, int arg_stop_on_gap)
: TCP_Endpoint(arg_conn, is_orig)
	{
	t_reassem = new TCP_Reassembler(this);
	skip_deliveries = 0;
	is_TCP_endpoint_contents = 1;
	stop_on_gap = arg_stop_on_gap;
	did_EOF = 0;
	}

TCP_EndpointContents::~TCP_EndpointContents()
	{
	Unref(t_reassem);
	}

int TCP_EndpointContents::DataSent(double t, int seq, int len,
				const u_char* data, const IP_Hdr* ip,
				const struct tcphdr* tp)
	{
	int status = TCP_Endpoint::DataSent(t, seq, len, data, ip, tp);

	if ( skip_deliveries )
		return 0;

	int ack = ack_seq - start_seq;
	int upper_seq = seq + len;

	if ( seq < ack )
		{
		if ( upper_seq <= ack )
			// We've already delivered this and it's been acked.
			return status;

		// We've seen an ack for part of this packet, but not the
		// whole thing.  This can happen when, for example, a previous
		// packet held [a, a+b) and this packet holds [a, a+c) for c>b
		// (which some TCP's will do when retransmitting).  Trim the
		// packet to just the unacked data.
		int amount_acked = ack - seq;
		seq += amount_acked;
		data += amount_acked;
		len -= amount_acked;
		}

	t_reassem->NewBlock(t, seq, len, data);

	return 1;
	}

void TCP_EndpointContents::AckReceived(double /* t */, int seq)
	{
	if ( FIN_cnt > 0 && seq >= FIN_seq )
		// TrimToSeq: FIN_seq - 1
		seq = FIN_seq - 1;

	if ( ! SkippingDeliveries() && ! conn->Skipping() &&
	     ! t_reassem->TrimToSeq(seq) &&
	     reading_live && state != TCP_PARTIAL && peer->state != TCP_PARTIAL )
		// Only generate these events when reading live; from
		// a trace file, the hole might exist because the
		// file was trimmed while writing it.
		conn->Event(ack_above_hole);

	// Check EOF here because t_reassem->LastReassemSeq() may have
	// changed after calling TrimToSeq().
	CheckEOF();
	}

void TCP_EndpointContents::CheckEOF()
	{
	if ( ! did_EOF &&
	     (FIN_cnt > 0 || state == TCP_CLOSED || state == TCP_RESET) &&
	     (conn->Skipping() || skip_deliveries ||
	      start_seq + t_reassem->LastReassemSeq() >= last_seq - 1) )
		{
		// We've now delivered all of the data.
		did_EOF = 1;
		conn->EndpointEOF(this);
		}
	}

void TCP_EndpointContents::Deliver(double t, int seq, int len, u_char* data)
	{
	conn->Deliver(this, t, seq, len, data);
	}

void TCP_EndpointContents::Undelivered(int seq, int len)
	{
	if ( stop_on_gap )
		// No more data will be sent to the reassembler via
		// DataSent, and thus no data will be delivered. Note
		// that it is safe to stop right here because all the data
		// before seq that exists in the trace must have already
		// appeared.
		SetSkipDeliveries(1);

	conn->Undelivered(this, seq, len);
	}

void TCP_EndpointContents::SetContentsFile(BroFile* f)
	{
	t_reassem->SetContentsFile(f);
	}


TCP_EndpointLine::TCP_EndpointLine(TCP_Connection* arg_conn,
			int is_orig, int is_NUL_sensitive, int skip_partial,
		        int arg_CRLF_as_EOL)
: TCP_EndpointContents(arg_conn, is_orig)
	{
	flag_NULs = is_NUL_sensitive;
	punt_on_partial = skip_partial;
	CR_LF_as_EOL = arg_CRLF_as_EOL;
	line_limit = 1024;
	is_TCP_endpoint_line = 1;
	buf = 0;
	Init(0);
	}

void TCP_EndpointLine::Init(int size)
	{
	if ( size < 128 )
		size = 128;

	char* b = (char*) safe_malloc(size);

	if ( buf )
		{
		if ( offset > 0 )
			memcpy(b, buf, offset);
		free(buf);
		}
	else
		{
		offset = 0;
		last_char = 0;
		}

	buf = b;
	buf_len = size;
	}

TCP_EndpointLine::~TCP_EndpointLine()
	{
	free(buf);
	}

void TCP_EndpointLine::Deliver(double t, int seq, int len, u_char* data)
	{
	if ( len <= 0 || skip_deliveries )
		return;

	if ( punt_on_partial &&
	     (state == TCP_PARTIAL || peer->state == TCP_PARTIAL) )
		{
		conn->SetSkip(1);
		conn->SetRecordPackets(0);	// ###
		return;
		}

	if ( buf && len + offset >= buf_len )
		{ // Make sure we have enough room to accommodate the new stuff.
		buf_len = ((offset + len) * 3) / 2 + 1;
		buf = (char*) safe_realloc((void*) buf, buf_len);

		if ( ! buf )
			internal_error("out of memory delivering endpoint line");
		}

	DoDeliver(t, seq, len, data);
	}

u_char* TCP_EndpointLine::NextNewLine(int len, u_char* data, int& next_pos)
	{
	u_char* end_of_data = data + len;
	for ( u_char* p = data; p < end_of_data; ++p )
		if ( *p == '\n' || *p == '\r' )
			{
			next_pos = p + 1 - data;		// where the next line begins
			last_char = *p;
			return p;
			}
	next_pos = 0;
	return 0;
	}


void TCP_EndpointLine::DoDeliver(double /* t */, int /* seq */, int len, u_char* data)
	{
	while ( len > 0 )
		{
		if ( (CR_LF_as_EOL & CR_as_EOL) &&
		     last_char == '\r' && *data == '\n' )
			{
			// CR is already considered as EOL.
			// Compress CRLF to just one line termination.
			last_char = *data;
			--len; ++data;
			}

		int n = DoDeliverOnce(len, data);
		len -= n;
		data += n;
		}
	}

int TCP_EndpointLine::DoDeliverOnce(int len, u_char* data)
	{
	const u_char* data_start = data;

	// ### Note, excessive_line checking no longer done.

	// ### This code should be correspondingly updated in TCP_NVT
	// ### and Rlogin.
	//        fprintf(stderr, "Data delivered (len): '%.*s' (%d)\n", len, data, len);

	if ( len <= 0 )
		return 0;

	for ( ; len > 0; --len, ++data )
		{
		if ( offset >= buf_len )
			Init(buf_len * 2);

		int c = data[0];

#define EMIT_LINE \
	{ \
	buf[offset] = '\0'; \
	conn->NewLine(this, offset, buf, 0); \
	offset = 0; \
	last_char = c; \
	return data + 1 - data_start; \
	}

		switch ( c ) {
		case '\r':
			if ( CR_LF_as_EOL & CR_as_EOL )
				EMIT_LINE
			else
				buf[offset++] = c;
			break;

		case '\n':
			if ( last_char == '\r' )
				{
				--offset; // remove '\r'
				EMIT_LINE
				}
			else if ( CR_LF_as_EOL & LF_as_EOL )
				EMIT_LINE
			else
				{
				if ( conn->FlagEvent(SINGULAR_LF) )
					conn->Weird("line_terminated_with_single_LF");
				buf[offset++] = c;
				}
			break;

		case '\0':
			if ( flag_NULs )
				CheckNUL();
			else
				buf[offset++] = c;
			break;

		default:
			buf[offset++] = c;
			break;
		}

		if ( last_char == '\r' )
			if ( conn->FlagEvent(SINGULAR_CR) )
				conn->Weird("line_terminated_with_single_CR");

		last_char = c;
		}

	return data - data_start;
	}

void TCP_EndpointLine::CheckNUL()
	{
	// If this is the first byte seen on this connection,
	// and if the connection's state is PARTIAL, then we've
	// intercepted a keep-alive, and shouldn't complain
	// about it.  Note that for PARTIAL connections, the
	// starting sequence number is adjusted as though there
	// had been an initial SYN, so we check for whether
	// the connection has at most two bytes so far.
	if ( state == TCP_PARTIAL &&
	     last_seq - start_seq <= 2 )
		; // Ignore it.
	else
		{
		if ( conn->FlagEvent(NUL_IN_LINE) )
			conn->Weird("NUL_in_line");
		flag_NULs = 0;
		}
	}

void TCP_EndpointLine::ExcessiveLine(const u_char* /* data */, int /* len */)
	{
	conn->Event(excessive_line);
	line_limit = 0;
	}

TCP_EndpointAnalyzer::TCP_EndpointAnalyzer(TCP_Endpoint* e)
	{
	endp = e;
	next = 0;
	e->AddAnalyzer(this);
	Ref(Conn());
	}

TCP_EndpointAnalyzer::~TCP_EndpointAnalyzer()
	{
	Unref(Conn());
	}

void TCP_EndpointAnalyzer::Describe(ODesc* d) const
	{
	d->Add(Endpoint()->IsOrig() ? "analyzer(orig)" : "analyzer(resp)");
	Conn()->Describe(d);
	}

int TCP_EndpointAnalyzer::DataSent(double /* t */, int /* seq */,
				int /* len */, const u_char* /* data */,
				const IP_Hdr* /* ip */,
				const struct tcphdr* /* tp */)
	{
	return 0;
	}


TCP_EndpointStats::TCP_EndpointStats(TCP_Endpoint* e)
: TCP_EndpointAnalyzer(e)
	{
	num_pkts = 0;
	num_rxmit = 0;
	num_rxmit_bytes = 0;
	num_in_order = 0;
	num_OO = 0;
	num_repl = 0;
	max_top_seq = 0;
	last_id = 0;
	endian_type = ENDIAN_UNKNOWN;
	}

int endian_flip(int n)
	{
	return ((n & 0xff) << 8) | ((n & 0xff00) >> 8);
	}

int TCP_EndpointStats::DataSent(double /* t */, int seq, int len,
			const u_char* /* data */,
			const IP_Hdr* ip, const struct tcphdr* /* tp */)
	{
	if ( ++num_pkts == 1 )
		{ // First packet.
		last_id = ntohs(ip->ID4());
		return 0;
		}

	int id = ntohs(ip->ID4());

	if ( id == last_id )
		{
		++num_repl;
		return 0;
		}

	short id_delta = id - last_id;
	short id_endian_delta = endian_flip(id) - endian_flip(last_id);

	int abs_id_delta = id_delta > 0 ? id_delta : -id_delta;
	int abs_id_endian_delta = id_endian_delta > 0 ? id_endian_delta : -id_endian_delta;

	int final_id_delta;

	if ( abs_id_delta < abs_id_endian_delta )
		{ // Consistent with big-endian.
		if ( endian_type == ENDIAN_UNKNOWN )
			endian_type = ENDIAN_BIG;
		else if ( endian_type == ENDIAN_BIG )
			;
		else
			endian_type = ENDIAN_CONFUSED;

		final_id_delta = id_delta;
		}
	else
		{ // Consistent with little-endian.
		if ( endian_type == ENDIAN_UNKNOWN )
			endian_type = ENDIAN_LITTLE;
		else if ( endian_type == ENDIAN_LITTLE )
			;
		else
			endian_type = ENDIAN_CONFUSED;

		final_id_delta = id_endian_delta;
		}

	if ( final_id_delta < 0 && final_id_delta > -256 )
		{
		++num_OO;
		return 0;
		}

	last_id = id;

	++num_in_order;

	int top_seq = seq + len;
	if ( top_seq <= max_top_seq )
		{
		++num_rxmit;
		num_rxmit_bytes += len;
		}
	else
		max_top_seq = top_seq;

	return 0;
	}

RecordVal* TCP_EndpointStats::BuildStats()
	{
	RecordVal* stats = new RecordVal(endpoint_stats);

	stats->Assign(0, new Val(num_pkts,TYPE_COUNT));
	stats->Assign(1, new Val(num_rxmit,TYPE_COUNT));
	stats->Assign(2, new Val(num_rxmit_bytes,TYPE_COUNT));
	stats->Assign(3, new Val(num_in_order,TYPE_COUNT));
	stats->Assign(4, new Val(num_OO,TYPE_COUNT));
	stats->Assign(5, new Val(num_repl,TYPE_COUNT));
	stats->Assign(6, new Val(endian_type,TYPE_COUNT));

	return stats;
	};


TCP_Connection::TCP_Connection(NetSessions* s, HashKey* k, double t,
		const ConnID* id, const struct tcphdr* tp)
: Connection(s, k, t, id)
	{
	// We record the contents of all packets except for HTTP - eliminating
	// the latter cuts the save file size by about a factor of two, since
	// often HTTP FIN packets have most of the server reply in them.
	if ( ntohs(resp_port) == 80 )
		record_contents = 0;

	// Set a timer to eventually time out this connection.
	ADD_TIMER(&TCP_Connection::ExpireTimer, t + tcp_SYN_timeout, 0);

	deferred_gen_event = close_deferred = 0;
	analyzer = 0;

	if ( ! (tp->th_flags & TH_SYN) || (tp->th_flags & TH_ACK) )
		is_partial = 1;
	else
		is_partial = 0;

	trace_rewriter = 0;
	src_pkt_writer = 0;
	}

void TCP_Connection::Init()
	{
	BuildEndpoints();

	orig->SetPeer(resp);
	resp->SetPeer(orig);

	// Can't put this in construction because RewritingTrace() is virtual.
	if ( transformed_pkt_dump && RewritingTrace() )
		{ // rewrite TCP trace
		trace_rewriter =
			new TCP_Rewriter(this, transformed_pkt_dump,
						transformed_pkt_dump_MTU,
						requires_trace_commitment);
		}

	if ( pkt_dump && dump_source_packets && transformed_pkt_dump )
		{
		src_pkt_writer =
			new TCP_SourcePacketWriter(this, transformed_pkt_dump);
		}
	}

TCP_Connection::~TCP_Connection()
	{
	delete orig;
	delete resp;
	delete trace_rewriter;
	delete src_pkt_writer;
	}

void TCP_Connection::Done()
	{
	if ( tcp_match_undelivered && orig && orig->is_TCP_endpoint_contents )
		orig->AsEndpointContents()->MatchUndeliveredData();

	if ( tcp_match_undelivered && resp && resp->is_TCP_endpoint_contents )
		resp->AsEndpointContents()->MatchUndeliveredData();

	FinishEndpointMatcher();

	if ( connection_pending && is_active && ! BothClosed() )
		Event(connection_pending);

	while ( analyzer )
		{
		TCP_Analyzer* a = analyzer;
		analyzer = analyzer->NextAnalyzer();
		a->Done();
		Unref(a);
		}

	finished = 1;
	}

void TCP_Connection::AddAnalyzer(TCP_Analyzer* a)
	{
	a->AddAnalyzer(analyzer);
	analyzer = a;

	// Don't Ref(a) - when this connection goes away, we want
	// the analyzer to go away, too (unless somewhere else it was
	// Ref()'d).
	}

void TCP_Connection::AnalyzerDone()
	{
	TCP_Analyzer* a = analyzer;

	while ( a )
		{
		TCP_Analyzer* b = a;
		a = a->NextAnalyzer();
		b->Done();
		}

	// Don't Unref(b) - it'll be done when this connection goes away.
	}

void TCP_Connection::BuildEndpoints()
	{
	orig = new TCP_Endpoint(this, 1);
	resp = new TCP_Endpoint(this, 0);
	}

Val* TCP_Connection::BuildSYNPacketVal(int is_orig,
				const IP_Hdr* ip, const struct tcphdr* tcp)
	{
	int winscale = -1;
	int MSS = 0;
	int SACK = 0;

	// Passive OS fingerprinting wants to know whether there are NOPs
	// in the options!
	int NOP = 0;

	// Parse TCP options.
	u_char* options = (u_char*) tcp + sizeof(struct tcphdr);
	u_char* opt_end = (u_char*) tcp + tcp->th_off * 4;

	while ( options < opt_end )
		{
		unsigned int opt = options[0];

		if ( opt == TCPOPT_EOL )
			// All done - could flag if more junk left over ....
			break;

		if ( opt == TCPOPT_NOP )
			{
			++options;
			++NOP;
			continue;
			}

		if ( options + 1 >= opt_end )
			// We've run off the end, no room for the length.
			break;

		unsigned int opt_len = options[1];

		if ( options + opt_len > opt_end )
			// No room for rest of option.
			break;

		if ( opt_len == 0 )
			// Trashed length field.
			break;

		switch ( opt ) {
		case TCPOPT_SACK_PERMITTED:
			SACK = 1;
			break;

		case TCPOPT_MAXSEG:
			if ( opt_len < 4 )
				break;	// bad length

			MSS = (options[2] << 8) | options[3];
			break;

		case 3: // TCPOPT_WSCALE
			if ( opt_len < 3 )
				break;	// bad length

			winscale = options[2];
			break;

		default:	// just skip over
			break;
		}

		options += opt_len;
		}

	RecordVal* v = new RecordVal(SYN_packet);

	v->Assign(0, new Val(is_orig, TYPE_BOOL));
	v->Assign(1, new Val(int(ip->DF()), TYPE_BOOL));
	v->Assign(2, new Val(int(ip->TTL()), TYPE_INT));
	v->Assign(3, new Val((ip->TotalLen()), TYPE_INT));
	v->Assign(4, new Val(ntohs(tcp->th_win), TYPE_INT));
	v->Assign(5, new Val(winscale, TYPE_INT));
	v->Assign(6, new Val(MSS, TYPE_INT));
	v->Assign(7, new Val(SACK, TYPE_BOOL));
	v->Assign(8, new Val(NOP, TYPE_COUNT));

	return v;
	}

void TCP_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* pcap_hdr,
			const u_char* const pcap_pkt,
			int hdr_size)
	{
	const struct tcphdr* tp = (const struct tcphdr*) data;
	uint32 tcp_hdr_len = tp->th_off * 4;

	if ( tcp_hdr_len < sizeof(struct tcphdr) )
		{
		Weird("bad_TCP_header_len");
		return;
		}

	if ( tcp_hdr_len > uint32(len) )
		{
		// This can happen even with the above test, due to TCP
		// options.
		Weird("truncated_header");
		return;
		}

	len -= tcp_hdr_len;	// remove TCP header
	caplen -= tcp_hdr_len;
	data += tcp_hdr_len;

	TCP_Endpoint* endpoint = is_orig ? orig : resp;
	TCP_Endpoint* peer = endpoint->peer;

	if ( ! ignore_checksums && caplen >= len &&
	     ! endpoint->ValidChecksum(tp, len) )
		{
		Weird("bad_TCP_checksum");
		return;
		}

	if ( rule_matcher )
		{
		if ( is_orig && ! orig_match_state )
			InitEndpointMatcher(ip, caplen, 1);

		if ( ! is_orig && ! resp_match_state )
			InitEndpointMatcher(ip, caplen, 0);
		}

	u_char flags = tp->th_flags;
	uint32 base_seq = ntohl(tp->th_seq);
	uint32 ack_seq = ntohl(tp->th_ack);
	int SYN = flags & TH_SYN;
	int FIN = flags & TH_FIN;
	int RST = flags & TH_RST;
	int ACK = flags & TH_ACK;
	int seq_len = len;	// length in terms of sequence space

	// Note, the offered window on an initial SYN is unscaled, even
	// if the SYN includes scaling, so we need to do the following
	// test *before* updating the scaling information below.  (Hmmm,
	// how does this work for windows on SYN/ACKs? ###)
	int scale = endpoint->window_scale;
	unsigned int window = (ntohs(tp->th_win)) << scale;

	if ( ! SYN )
		{
		// Don't analyze window values off of SYNs, they're sometimes
		// immediately rescinded.

		// ### Decide whether to accept new window based on AM policy.
		if ( int(base_seq - endpoint->window_seq) >= 0 &&
		     int(ack_seq - endpoint->window_ack_seq) >= 0 )
			{
			uint32 new_edge = ack_seq + window;
			uint32 old_edge = endpoint->window_ack_seq + endpoint->window;
			int advance = new_edge - old_edge;

			if ( advance < 0 )
				{
				// A window recision.  We don't report these
				// for FINs or RSTs, or if the connection
				// has already been partially closed, since
				// such recisions occur frequently in practice,
				// probably as the receiver loses buffer memory
				// due to its process going away.
				//
				// We also, for window scaling, allow a bit
				// of slop ###.  This is because sometimes
				// there will be an apparent recision due
				// to the granularity of the scaling.
				if ( ! FIN && ! RST &&
				     endpoint->state != TCP_CLOSED &&
				     endpoint->state != TCP_RESET &&
				     (-advance) >= (1 << scale) )
					Weird("window_recision");
				}

			endpoint->window = window;
			endpoint->window_ack_seq = ack_seq;
			endpoint->window_seq = base_seq;
			}
		}

	if ( ! orig->did_close || ! resp->did_close )
		last_time = t;

	if ( SYN )
		{
		++seq_len;	// SYN consumes a byte of sequence space
		if ( RST )
			Weird("TCP_christmas");

		if ( (flags & TH_URG) )
			Weird("baroque_SYN");

		if ( len > 0 )
			// T/TCP definitely complicates this.
			Weird("SYN_with_data");

		RecordVal* SYN_vals =
			BuildSYNPacketVal(is_orig, ip, tp)->AsRecordVal();

		// ### In the following, we could be fooled by an
		// inconsistent SYN retransmission.  Where's a normalizer
		// when you need one?

		// ### We know that field 5 is the window scaling ....
		int scale = SYN_vals->Lookup(5)->CoerceToInt();

		if ( scale < 0 )
			{ // no window scaling option
			if ( ACK )
				{ // window scaling not negotiated
				endpoint->window_scale = 0;
				peer->window_scale = 0;
				}
			else
				// We're not offering window scaling.
				// Ideally, we'd remember this fact so that
				// if the SYN/ACK *does* include window
				// scaling, we know it won't be negotiated.
				// But it's a pain to track that, and hard
				// to see how an adversarial responder could
				// use it to evade.  Also, if we *do* want
				// to track it, we could do so using
				// connection_SYN_packet.
				endpoint->window_scale = 0;
			}
		else
			{
			endpoint->window_scale = scale;
			endpoint->window_seq = base_seq;
			endpoint->window_ack_seq = ack_seq;

			peer->window_seq = ack_seq;
			peer->window_ack_seq = base_seq;
			}

		if ( connection_SYN_packet )
			{
			val_list* vl = new val_list;
			vl->append(BuildConnVal());
			vl->append(SYN_vals);
			ConnectionEvent(connection_SYN_packet, vl);
			}
		else
			Unref(SYN_vals);
		}

	if ( FIN )
		{
		++seq_len;	// FIN consumes a byte of sequence space
		// Remember the relative seq in FIN_seq.
		endpoint->FIN_seq = base_seq - endpoint->start_seq + seq_len;

		if ( t < endpoint->last_time + tcp_storm_interarrival_thresh &&
		     ++endpoint->FIN_cnt == tcp_storm_thresh )
			Weird("FIN_storm");

		else if ( endpoint->FIN_cnt == 0 )
			// Remember that we've seen a FIN.
			++endpoint->FIN_cnt;
		}

	if ( RST )
		{
		if ( t < endpoint->last_time + tcp_storm_interarrival_thresh &&
		     ++endpoint->RST_cnt == tcp_storm_thresh )
			Weird("RST_storm");

		else if ( endpoint->RST_cnt == 0 )
			++endpoint->RST_cnt;	// Remember we've seen a RST

#ifdef ACTIVE_MAPPING
//		if ( peer->state == TCP_INACTIVE )
//		    debug_msg("rst while inactive\n"); // ### Cold-start: what to do?
//		else

		const NumericData* AM_policy;
		get_map_result(ip->DstAddr4(), AM_policy);

		if ( base_seq == endpoint->ack_seq )
			;	 // everyone should accept a RST in sequence

		else if ( endpoint->window == 0 )
			; // ### Cold Start: we don't know the window,
			  // so just go along for now

		else
			{
			uint32 right_edge = endpoint->ack_seq +
				(endpoint->window << endpoint->window_scale);

			if ( base_seq < right_edge  )
				{
				if ( ! AM_policy->accepts_rst_in_window )
					{
#if 0
					debug_msg("Machine does not accept RST merely in window; ignoring. t=%.6f,base=%u, ackseq=%u, window=%hd \n",
						network_time, base_seq, endpoint->ack_seq, window);
#endif
					return;
					}
				}

			else if ( ! AM_policy->accepts_rst_outside_window )
				{
#if 0
				debug_msg("Machine does not accept RST outside window; ignoring. t=%.6f,base=%u, ackseq=%u, window=%hd \n",
					network_time, base_seq, endpoint->ack_seq, window);
#endif
				return;
				}
			}
#endif

		if ( len > 0 )
			{
			Weird("RST_with_data");

			// Don't include the data in the computation of
			// the sequence space for this connection, as
			// it's not in fact part of the TCP stream.
			seq_len = 0;
			}

		PacketWithRST();
		}

	uint32 last_seq = base_seq + seq_len;

	if ( endpoint->state == TCP_INACTIVE )
		{
		if ( ! SYN )
			// This is a partial connection - set up the
			// initial sequence numbers as though we saw
			// a SYN, to keep the relative byte numbering
			// consistent.
			endpoint->ack_seq = endpoint->start_seq = base_seq - 1;
		else
			endpoint->ack_seq = endpoint->start_seq = base_seq;

		endpoint->last_seq = last_seq;
		endpoint->start_time = t;
		}

	int delta_last = seq_delta(last_seq, endpoint->last_seq);

	if ( (SYN || RST) && (delta_last > TOO_LARGE_SEQ_DELTA || delta_last < -TOO_LARGE_SEQ_DELTA) )
		// ### perhaps trust RST seq #'s if initial and not too
		// outlandish, but not if they're coming after the other
		// side has sent a FIN - trust the FIN ack instead
		;

	else if ( FIN && endpoint->last_seq == endpoint->start_seq + 1 )
		// Update last_seq based on the FIN even if delta_last < 0.
		// This is to accommodate > 2 GB connections for which
		// we've only seen the SYN and the FIN (hence the check
		// for last_seq == start_seq + 1).
		endpoint->last_seq = last_seq;

	else if ( endpoint->state == TCP_RESET )
		// don't trust any subsequent sequence numbers
		;

	else if ( delta_last > 0 )
		// ### check for large jumps here.
		endpoint->last_seq = last_seq;

	else if ( delta_last <= 0 )
		// ### ++retransmit, unless this is a pure ack
		;

	if ( ACK )
		{
		if ( peer->state == TCP_INACTIVE )
			{
			if ( ! SYN && ! FIN && ! RST )
				{
				if ( endpoint->state == TCP_SYN_SENT ||
				     endpoint->state == TCP_SYN_ACK_SENT ||
				     endpoint->state == TCP_ESTABLISHED )
					{
					// We've already sent a SYN, but
					// that hasn't roused the other
					// end, yet we're ack'ing their
					// data.
					if ( weird == 0 )
						Weird("possible_split_routing");
					}
				}

			// Start the sequence numbering as if there
			// was an initial SYN, so the relative
			// numbering of subsequent data packets stays
			// consistent.
			peer->start_seq = peer->ack_seq = peer->last_seq =
				ack_seq - 1;
			}

		else if ( ! RST )
			{ // don't trust ack's in RST packets
			int delta_ack = seq_delta(ack_seq, peer->ack_seq);
			if ( ack_seq == 0 && delta_ack > TOO_LARGE_SEQ_DELTA )
				// More likely that this is a broken ack
				// than a large connection that happens
				// to land on 0 in the sequence space.
				;

			else if ( delta_ack > 0 )
				peer->ack_seq = ack_seq;
			}

		peer->AckReceived(t, ack_seq - peer->start_seq);
		}

	endpoint->last_time = t;

	int do_close = 0;	// whether to report the connection as closed
	int gen_event = 0;	// if so, whether to generate an event

	switch ( endpoint->state ) {

	case TCP_INACTIVE:
		{
		if ( SYN )
			{
			if ( is_orig )
				{
				if ( ACK )
					{
					Weird("connection_originator_SYN_ack");
					endpoint->SetState(TCP_SYN_ACK_SENT);
					}
				else
					endpoint->SetState(TCP_SYN_SENT);

				if ( connection_attempt )
					ADD_TIMER(&TCP_Connection::AttemptTimer,
						t + tcp_attempt_delay, 1);
				}
			else
				{
				if ( ACK )
					{
					if ( peer->state != TCP_INACTIVE &&
					     peer->state != TCP_PARTIAL &&
					     ! seq_between(ack_seq, peer->start_seq, peer->last_seq) )
						Weird("bad_SYN_ack");
					}

				else if ( peer->state == TCP_SYN_ACK_SENT &&
					  base_seq == endpoint->start_seq )
					{
					// This is a SYN/SYN-ACK reversal,
					// per the discussion in IsReuse.
					// Flip the endpoints and establish
					// the connection.
					TCP_Endpoint* tmp_ep = resp;
					resp = orig;
					orig = tmp_ep;

					uint32 tmp_addr[NUM_ADDR_WORDS];
					copy_addr(resp_addr, tmp_addr);
					copy_addr(orig_addr, resp_addr);
					copy_addr(tmp_addr, orig_addr);

					uint16 tmp_port = resp_port;
					resp_port = orig_port;
					orig_port = tmp_port;

					Unref(conn_val);
					conn_val = 0;

					peer->SetState(TCP_ESTABLISHED);
					}

				else
					Weird("simultaneous_open");

				if ( peer->state == TCP_SYN_SENT )
					peer->SetState(TCP_ESTABLISHED);
				else if ( peer->state == TCP_INACTIVE )
					{
					// If we were to ignore SYNs and
					// only instantiate state on SYN
					// acks, then we'd do:
					//    peer->SetState(TCP_ESTABLISHED);
					// here.
					Weird("unsolicited_SYN_response");
					}

				endpoint->SetState(TCP_ESTABLISHED);

				if ( peer->state != TCP_PARTIAL )
					{
					Event(connection_established);
					EnableStatusUpdateTimer();
					}
				}
			}

		if ( FIN )
			{
			endpoint->SetState(TCP_CLOSED);
			do_close = gen_event = 1;
			if ( peer->state != TCP_PARTIAL && ! SYN )
				Weird("spontaneous_FIN");
			}

		if ( RST )
			{
			endpoint->SetState(TCP_RESET);

			int is_reject = 0;

			if ( is_orig )
				{
				// If our peer is established then we saw
				// a SYN-ack but not SYN - so a reverse
				// scan, and we should treat this as a
				// reject.
				if ( peer->state == TCP_ESTABLISHED )
					is_reject = 1;
				}

			else if ( peer->state == TCP_SYN_SENT ||
				  peer->state == TCP_SYN_ACK_SENT )
				// We're rejecting an initial SYN.
				is_reject = 1;

			do_close = 1;
			gen_event = ! is_reject;

			if ( is_reject )
				Event(connection_rejected);

			else if ( peer->state == TCP_INACTIVE )
				Weird("spontaneous_RST");
			}

		if ( endpoint->state == TCP_INACTIVE )
			{ // No control flags to change the state.
			if ( ! is_orig && len == 0 &&
			     orig->state == TCP_SYN_SENT )
				// Some eccentric TCP's will ack an initial
				// SYN prior to sending a SYN reply (hello,
				// ftp.microsoft.com).  For those, don't
				// consider the ack as forming a partial
				// connection.
				;
			else
				{
				endpoint->SetState(TCP_PARTIAL);
				if ( peer->state == TCP_PARTIAL )
					// We've seen both sides of a partial
					// connection, report it.
					Event(partial_connection);
				}
			}
		}
		break;

	case TCP_SYN_SENT:
	case TCP_SYN_ACK_SENT:
		{
		if ( SYN )
			{
			if ( is_orig )
				{
				if ( ACK && ! FIN && ! RST &&
				     endpoint->state != TCP_SYN_ACK_SENT )
					Weird("repeated_SYN_with_ack");
				else if ( peer->state == TCP_INACTIVE )
					// ###
					start_time = endpoint->start_time = t;
				}
			else
				{
				if ( ! ACK &&
				     endpoint->state != TCP_SYN_SENT )
					Weird("repeated_SYN_reply_wo_ack");
				}

			if ( base_seq != endpoint->start_seq )
				{
				Weird("SYN_seq_jump");
				endpoint->ack_seq = endpoint->start_seq = base_seq;
				endpoint->last_seq = last_seq;
				}
			}

		if ( FIN )
			{
			if ( peer->state == TCP_INACTIVE ||
			     peer->state == TCP_SYN_SENT )
				Weird("inappropriate_FIN");
			endpoint->SetState(TCP_CLOSED);
			do_close = gen_event = 1;
			}

		if ( RST )
			{
			endpoint->SetState(TCP_RESET);
			ConnectionReset();
			do_close = 1;
			}
		else if ( len > 0 )
			Weird("data_before_established");
		}
		break;

	case TCP_ESTABLISHED:
	case TCP_PARTIAL:
		{
		if ( SYN )
			{
			if ( endpoint->state == TCP_PARTIAL &&
			     peer->state == TCP_INACTIVE && ! ACK )
				{
				Weird("SYN_after_partial");
				endpoint->SetState(TCP_SYN_SENT);
				}

			if ( endpoint->Size() > 0 )
				Weird("SYN_inside_connection");

			if ( is_orig && peer->state == TCP_ESTABLISHED ) // ###
				start_time = endpoint->start_time = t;

			if ( base_seq != endpoint->start_seq )
				Weird("SYN_seq_jump");

			// Make a guess that somehow the connection didn't
			// get established, and this SYN will be the
			// one that actually sets it up.
			endpoint->ack_seq = endpoint->start_seq = base_seq;
			endpoint->last_seq = last_seq;
			}

		if ( FIN && ! RST )	// ###
			{ // should check sequence/ack numbers here ###
			endpoint->SetState(TCP_CLOSED);

			if ( peer->state == TCP_RESET &&
			     peer->prev_state == TCP_CLOSED )
				// The peer sent a FIN followed by a RST.
				// Turn it back into CLOSED state, because
				// this was actually normal termination.
				peer->SetState(TCP_CLOSED);

			do_close = gen_event = 1;
			}

		if ( RST )
			{
			endpoint->SetState(TCP_RESET);
			do_close = 1;

			if ( peer->state != TCP_RESET ||
			     peer->prev_state != TCP_ESTABLISHED )
				ConnectionReset();
			}
		}
		break;

	case TCP_CLOSED:
		{
		if ( SYN )
			Weird("SYN_after_close");

		if ( FIN && delta_last > 0 )
			// Probably should also complain on FIN recision.
			// That requires an extra state variable to avoid
			// generating slews of weird's when a TCP gets
			// seriously confused (this from experience).
			Weird("FIN_advanced_last_seq");

		if ( RST )
			{
			endpoint->SetState(TCP_RESET);

			if ( ! endpoint->did_close )
				// RST after FIN.
				do_close = 1;

			if ( connection_reset )
				ADD_TIMER(&TCP_Connection::ResetTimer,
						t + tcp_reset_delay, 1);
			}
		}
		break;

	case TCP_RESET:
		{
		if ( SYN )
			Weird("SYN_after_reset");
		if ( FIN )
			Weird("FIN_after_reset");

		if ( len > 0 && ! RST )
			Weird("data_after_reset");
		}
		break;
	}

	if ( trace_rewriter )
		trace_rewriter->NextPacket(is_orig, t, pcap_hdr,
					pcap_pkt, hdr_size, ip->IP4_Hdr(), tp);

	if ( src_pkt_writer )
		src_pkt_writer->NextPacket(pcap_hdr, pcap_pkt);

	int need_contents = 0;
	if ( rule_matcher ||
	     (len > 0 &&
	      (caplen >= len || endpoint->Analyzer()) &&
	      ! RST && ! skip) )
		{
		int data_seq = base_seq - endpoint->start_seq;
		if ( SYN )
			++data_seq;	// skip over SYN octet

		need_contents = endpoint->DataSent(t, data_seq, len, data, ip, tp);
		}

	TCP_EndpointContents* e = endpoint->AsEndpointContents();
	if ( e )
		e->CheckEOF();

	if ( do_close )
		{
		// We need to postpone doing this until after we process
		// DataSent, so we don't generate a connection_finished event
		// until after data perhaps included with the FIN is processed.
		ConnectionClosed(endpoint, peer, gen_event);
		}

	record_content = need_contents || RecordContents();
//###	record_content = RecordContents();	// a hack to avoid dumping EVERY packet
	record_packet = record_packets || SYN || FIN || RST;
	}

void TCP_Connection::Deliver(TCP_Endpoint* /* sender */, double /* t */,
			int /* seq */, int /* len */, u_char* /* data */)
	{
	}

void TCP_Connection::Undelivered(TCP_Endpoint* sender, int seq, int len)
	{
	// This can happen because we're processing a trace that's been
	// filtered.  For example, if it's just SYN/FIN data, then there
	// can be data in the FIN packet, but it's undelievered because
	// it's out of sequence.
	if ( content_gap )
		{
		val_list* vl = new val_list;
		vl->append(BuildConnVal());
		vl->append(new Val(IsOrig(sender), TYPE_BOOL));
		vl->append(new Val(seq, TYPE_COUNT));
		vl->append(new Val(len, TYPE_COUNT));

		ConnectionEvent(content_gap, vl);
		}

	if ( trace_rewriter )
		trace_rewriter->ContentGap(IsOrig(sender), len);
	}

void TCP_Connection::NewLine(TCP_Endpoint* /* sender */, int /* length */, const char* /* line */, int /* is_const */)
	{
	}

void TCP_Connection::SetEnv(TCP_Endpoint* /* sender */, char* name, char* val)
	{
	delete name;
	delete val;
	}

void TCP_Connection::AttemptTimer(double /* t */)
	{
	if ( ! is_active )
		return;

	if ( (orig->state == TCP_SYN_SENT || orig->state == TCP_SYN_ACK_SENT) &&
	     resp->state == TCP_INACTIVE )
		{
		Event(connection_attempt);
		is_active = 0;

		// All done with this connection.
		sessions->Remove(this, 1);
		}
	}

void TCP_Connection::PartialCloseTimer(double /* t */)
	{
	if ( ! is_active )
		return;

	if ( orig->state != TCP_INACTIVE && resp->state != TCP_INACTIVE &&
	     (! orig->did_close || ! resp->did_close) )
		{
		if ( orig->state == TCP_RESET || resp->state == TCP_RESET )
			// Presumably the RST is what caused the partial
			// close.  Don't report it.
			return;

		Event(connection_partial_close);
		sessions->Remove(this, 1);
		}
	}

void TCP_Connection::ExpireTimer(double t)
	{
	if ( ! is_active )
		return;

	if ( last_time + tcp_connection_linger < t )
		{
		if ( orig->did_close || resp->did_close )
			{
			// No activity for tcp_connection_linger seconds, and
			// at least one side has closed.  See whether
			// connection has likely terminated.
			if ( (orig->did_close && resp->did_close) ||
			     (orig->state == TCP_RESET ||
			      resp->state == TCP_RESET) ||
			     (orig->state == TCP_INACTIVE ||
			      resp->state == TCP_INACTIVE) )
				{
				// Either both closed, or one RST,
				// or half-closed.

				// The Timer has Ref()'d us and won't Unref()
				// us until we return, so it's safe to have
				// the session remove and Unref() us here.
				Event(connection_timeout);
				is_active = 0;
				sessions->Remove(this, 1);
				return;
				}
			}

		if ( resp->state == TCP_INACTIVE )
			{
			if ( (orig->state == TCP_SYN_SENT ||
			      orig->state == TCP_SYN_ACK_SENT) )
				{
				if ( ! connection_attempt )
					{
					// Time out the connection attempt,
					// since the AttemptTimer isn't going
					// to do it for us, and we don't want
					// to clog the data structures with
					// old, failed attempts.
					Event(connection_timeout);
					is_active = 0;
					sessions->Remove(this, 1);
					return;
					}
				}

			else if ( orig->state == TCP_INACTIVE )
				{
				// Nothing ever happened on this connection.
				// This can occur when we see a trashed
				// packet - it's discarded by NextPacket
				// before setting up an attempt timer,
				// so we need to clean it up here.
				Event(connection_timeout);
				sessions->Remove(this, 1);
				return;
				}
			}
		}

	// Connection still active, so reschedule timer if inactivity
	// timeout hasn't been reached yet.
	// ### if PQ_Element's were BroObj's, could just Ref the timer
	// and adjust its value here, instead of creating a new timer.
	if ( default_inactivity_timeout == 0 ||
	     last_time + default_inactivity_timeout >= network_time )
		ADD_TIMER(&TCP_Connection::ExpireTimer, t + tcp_session_timer, 0);
	}

void TCP_Connection::ResetTimer(double /* t */)
	{
	if ( ! is_active )
		return;

	if ( ! BothClosed() )
		ConnectionReset();

	sessions->Remove(this, 1);
	}

void TCP_Connection::DeleteTimer(double /* t */)
	{
	sessions->Remove(this, 1);
	}


// The following need to be consistent with bro.init.
#define CONTENTS_NONE 0
#define CONTENTS_ORIG 1
#define CONTENTS_RESP 2
#define CONTENTS_BOTH 3

void TCP_Connection::SetContentsFile(unsigned int direction, BroFile* f)
	{
	if ( direction == CONTENTS_NONE )
		{
		orig->SetContentsFile(0);
		resp->SetContentsFile(0);
		}

	else
		{
		if ( direction == CONTENTS_ORIG || direction == CONTENTS_BOTH )
			orig->SetContentsFile(f);
		if ( direction == CONTENTS_RESP || direction == CONTENTS_BOTH )
			resp->SetContentsFile(f);
		}
	}

void TCP_Connection::SetTraceRewriter(TCP_Rewriter* rewriter)
	{
	trace_rewriter = rewriter;
	}

int TCP_Connection::IsReuse(double t, const u_char* pkt)
	{
	const struct tcphdr* tp = (const struct tcphdr*) pkt;

	if ( unsigned(tp->th_off) < sizeof(struct tcphdr) / 4 )
		// Bogus header, don't interpret further.
		return 0;

	TCP_Endpoint* conn_orig = orig;

	// Reuse only occurs on initial SYN's, except for half connections
	// it can occur on SYN-acks.
	if ( ! (tp->th_flags & TH_SYN) )
		return 0;

	if ( (tp->th_flags & TH_ACK) )
		{
		if ( orig->state != TCP_INACTIVE )
			// Not a half connection.
			return 0;

		conn_orig = resp;
		}

	if ( ! IsClosed() )
		{
		uint32 base_seq = ntohl(tp->th_seq);
		if ( base_seq == conn_orig->start_seq )
			return 0;

		if ( (tp->th_flags & TH_ACK) == 0 &&
		     conn_orig->state == TCP_SYN_ACK_SENT &&
		     resp->state == TCP_INACTIVE &&
		     base_seq == resp->start_seq )
			{
			// This is an initial SYN with the right sequence
			// number, and the state is consistent with the
			// SYN & the SYN-ACK being flipped (e.g., due to
			// reading from two interfaces w/ interrupt
			// coalescence).  Don't treat this as a reuse.
			// NextPacket() will flip set the connection
			// state correctly
			return 0;
			}

		if ( conn_orig->state == TCP_SYN_SENT )
			Weird("SYN_seq_jump");
		else
			Weird("active_connection_reuse");
		}

	else if ( (orig->IsActive() || resp->IsActive()) &&
	     orig->state != TCP_RESET && resp->state != TCP_RESET )
		Weird("active_connection_reuse");

	else if ( t - last_time < tcp_connection_linger &&
	     orig->state != TCP_RESET && resp->state != TCP_RESET )
		Weird("premature_connection_reuse");

	return 1;
	}

void TCP_Connection::EndpointEOF(TCP_EndpointContents* endp)
	{
	TraceRewriterEOF(endp);

	if ( close_deferred )
		{
		if ( DataPending(endp) )
			{
			if ( BothClosed() )
				Weird("pending_data_when_closed");

			// Defer further, until the other endpoint
			// EOF's, too.
			}

		ConnectionClosed(endp, endp->peer, deferred_gen_event);
		close_deferred = 0;
		}
	}

void TCP_Connection::TraceRewriterEOF(TCP_Endpoint* endp)
	{
	// Add a FIN packet if there is one in the original trace.
	if ( trace_rewriter && endp->FIN_cnt > 0 )
		trace_rewriter->ScheduleFIN(IsOrig(endp));
	}

void TCP_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));
	if ( ! orig->did_close )
		d->Add("*");

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

	d->Add(dotted_addr(resp_addr));
	d->Add(".");
	d->Add(ntohs(resp_port));
	if ( ! resp->did_close )
		d->Add("*");

	d->SP();

	d->Add("(");
	d->Add(orig->Size());
	d->AddSP(",");
	d->Add(resp->Size());
	d->Add(")");
	d->NL();
	}

void TCP_Connection::ConnectionClosed(TCP_Endpoint* endpoint, TCP_Endpoint* peer,
					int gen_event)
	{
	if ( DataPending(endpoint) )
		{
		// Don't close out the connection yet, there's still data to
		// deliver.
		close_deferred = 1;
		if ( ! deferred_gen_event )
			deferred_gen_event = gen_event;
		return;
		}

	close_deferred = 0;

	if ( endpoint->did_close )
		return;	// nothing new to report

	endpoint->did_close = 1;

	int close_complete = endpoint->state == TCP_RESET ||
			     peer->did_close || peer->state == TCP_INACTIVE;

	if ( close_complete )
		{
		if ( endpoint->prev_state != TCP_INACTIVE ||
		     peer->state != TCP_INACTIVE )
			{
			if ( deferred_gen_event )
				{
				gen_event = 1;
				deferred_gen_event = 0;	// clear flag
				}

			// We have something interesting to report.
			if ( gen_event )
				{
				if ( peer->state == TCP_INACTIVE )
					ConnectionFinished(1);
				else
					ConnectionFinished(0);
				}
			}

		CancelTimers(0);

		// Note, even if tcp_close_delay is zero, we can't
		// simply do:
		//
		//	sessions->Remove(this);
		//
		// here, because that would cause the object to be
		// deleted out from under us.
		if ( tcp_close_delay != 0.0 )
			ADD_TIMER(&Connection::DeleteTimer,
				last_time + tcp_close_delay, 0);
		else
			ADD_TIMER(&TCP_Connection::DeleteTimer, last_time, 0);
		}

	else
		{ // We haven't yet seen a full close.
		if ( endpoint->prev_state == TCP_INACTIVE )
			{ // First time we've seen anything from this side.
			if ( connection_partial_close )
				ADD_TIMER(&TCP_Connection::PartialCloseTimer,
					last_time + tcp_partial_close_delay, 0);
			}

		else
			{
			// Create a timer to look for the other side closing,
			// too.
			ADD_TIMER(&TCP_Connection::ExpireTimer,
					last_time + tcp_session_timer, 0);
			}
		}
	}

void TCP_Connection::ConnectionFinished(int half_finished)
	{
	if ( half_finished )
		Event(connection_half_finished);
	else
		Event(connection_finished);

	is_active = 0;
	}

void TCP_Connection::ConnectionReset()
	{
	Event(connection_reset);
	is_active = 0;
	}

int TCP_Connection::DataPending(TCP_Endpoint* closing_endp)
	{
	if ( Skipping() )
		return 0;

	TCP_EndpointContents* end_c = closing_endp->AsEndpointContents();
	TCP_EndpointContents* peer_c =
		closing_endp->peer->AsEndpointContents();

	if ( end_c )
		{
		uint32 delivered_seq = end_c->start_seq + end_c->DataSeq();

		// We've delivered everything if we're up to the penultimate
		// sequence number (since a FIN consumes an octet in the
		// sequence space), or right at it (because a RST does not).
		if ( delivered_seq != end_c->last_seq - 1 &&
		     delivered_seq != end_c->last_seq )
			return 1;
		}

	// If end_c sends RST, then end_c may not send ACKs any more.
	if ( ! (end_c && end_c->state == TCP_RESET) &&
	     peer_c && peer_c->HasUndeliveredData() )
		return 1;

	return 0;
	}

void TCP_Connection::UpdateEndpointVal(RecordVal* endp, int is_orig)
	{
	TCP_Endpoint* s = is_orig ? orig : resp;
	endp->Assign(0, new Val(s->Size(), TYPE_COUNT));
	endp->Assign(1, new Val(int(s->state), TYPE_COUNT));
	}


TCP_ConnectionContents::TCP_ConnectionContents(NetSessions* s,
		HashKey* k, double t, const ConnID* id,
		const struct tcphdr* tp, TCPContentsType arg_contents_type)
: TCP_Connection(s, k, t, id, tp)
	{
	contents_type = arg_contents_type;
	}

void TCP_ConnectionContents::BuildEndpoints()
	{
	orig = contents_type & TCP_CONTENTS_ORIG ?
		new TCP_EndpointContents(this, 1) : new TCP_Endpoint(this, 1);
	resp = contents_type & TCP_CONTENTS_RESP ?
		new TCP_EndpointContents(this, 0) : new TCP_Endpoint(this, 0);
	}


TCP_Analyzer::~TCP_Analyzer()
	{
	}

void TCP_Analyzer::Done()
	{
	done = 1;
	}

void TCP_Analyzer::Describe(ODesc* d) const
	{
	d->AddSP("analyzer for");
	conn->Describe(d);
	}

TCP_Stats::TCP_Stats(TCP_Connection* c)
: TCP_Analyzer(c)
	{
	orig_stats = new TCP_EndpointStats(conn->Orig());
	resp_stats = new TCP_EndpointStats(conn->Resp());
	}

TCP_Stats::~TCP_Stats()
	{
	delete orig_stats;
	delete resp_stats;
	}

void TCP_Stats::Done()
	{
	val_list* vl = new val_list;
	vl->append(conn->BuildConnVal());
	vl->append(orig_stats->BuildStats());
	vl->append(resp_stats->BuildStats());

	conn->ConnectionEvent(conn_stats, vl);

	TCP_Analyzer::Done();
	}
