// $Id: TCP.h,v 1.72 2003/06/13 00:25:16 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.

#ifndef tcp_h
#define tcp_h

#include "Conn.h"
#include "Reassem.h"
#include "NetVar.h"

typedef enum {
	TCP_INACTIVE,	// no SYN (or any other packets) seen for this side
	TCP_SYN_SENT,	// SYN seen, but no ack
	TCP_SYN_ACK_SENT,	// SYN ack seen, no initial SYN
	TCP_PARTIAL,	// data seen, but no SYN
	TCP_ESTABLISHED,	// SYN ack seen (implicit for SYN sent by
				// responder)
	TCP_CLOSED,	// FIN seen
	TCP_RESET	// RST seen
} EndpointState;

class TCP_Connection;
class TCP_EndpointLine;
class TCP_EndpointContents;
class TCP_NVT;

class TCP_EndpointAnalyzer;
class TCP_Analyzer;

// One endpoint of a TCP connection.
class TCP_Endpoint {
public:
	TCP_Endpoint(TCP_Connection* conn, int is_orig);
	virtual ~TCP_Endpoint();

	void SetPeer(TCP_Endpoint* p)	{ peer = p; }

	void SetState(EndpointState new_state);
	int Size() const;
	int IsActive() const	{ return state != TCP_INACTIVE && ! did_close; }

	double StartTime() const	{ return start_time; }
	double LastTime() const		{ return last_time; }

	uint32 StartSeq() const		{ return start_seq; }
	uint32 LastSeq() const		{ return last_seq; }
	uint32 AckSeq() const		{ return ack_seq; }

	TCP_Connection* Conn() const	{ return conn; }

	int IsTCP_EndpointContents() const
					{ return is_TCP_endpoint_contents; }
	int IsTCP_NVT() const		{ return is_TCP_NVT; }

	inline int IsOrig() const;

	int HasDoneSomething() const	{ return last_time != 0.0; }

	TCP_EndpointContents* AsEndpointContents()
		{ return is_TCP_endpoint_contents ? (TCP_EndpointContents*) this : 0; }
	TCP_EndpointLine* AsEndpointLine()
		{ return is_TCP_endpoint_line ? (TCP_EndpointLine*) this : 0; }

	TCP_NVT* AsNVT()
		{ return is_TCP_NVT ? (TCP_NVT*) this : 0; }

	TCP_EndpointAnalyzer* Analyzer()		{ return analyzer; }
	void AddAnalyzer(TCP_EndpointAnalyzer* a);

	int ValidChecksum(const struct tcphdr* tp, int len) const;

	// Returns true if the data was used (and hence should be recorded
	// in the save file), false otherwise.
	virtual int DataSent(double t, int seq, int len, const u_char* data,
				const IP_Hdr* ip, const struct tcphdr* tp);

	virtual void AckReceived(double t, int seq);

	virtual void SetContentsFile(BroFile* f);

	//### combine into a set of flags:
	int is_TCP_endpoint_contents, is_TCP_endpoint_line, is_TCP_NVT;
	EndpointState state, prev_state;
	TCP_Endpoint* peer;
	TCP_EndpointAnalyzer* analyzer;
	TCP_Connection* conn;
	BroFile* contents_file;
	uint32 checksum_base;

	double start_time, last_time;
	uint32 start_seq, last_seq, ack_seq;	// in host order
	const uint32* src_addr; // the other endpoint
	const uint32* dst_addr; // this endpoint
	uint32 window; // current congestion window (*scaled*, not pre-scaling)
	int window_scale;  // from the TCP option
	uint32 window_ack_seq; // at which ack_seq number did we record 'window'
	uint32 window_seq; // at which sending sequence number did we record 'window'
	int contents_start_seq;	// relative seq # where contents file starts
	int FIN_seq;		// relative seq # to start_seq
	int SYN_cnt, FIN_cnt, RST_cnt;
	int did_close;		// whether we've reported it closing
};

class TCP_Reassembler : public Reassembler {
public:
	TCP_Reassembler(TCP_EndpointContents* arg_endp) :
	    Reassembler(1, ((TCP_Endpoint*) arg_endp)->src_addr, REASSEM_TCP)
		{ endp = arg_endp; record_contents_file = 0; }
	virtual ~TCP_Reassembler();

	void SetContentsFile(BroFile* f);

	void MatchUndelivered(int up_to_seq = -1);

protected:
	void Undelivered(int up_to_seq);

	void RecordToSeq(int start_seq, int stop_seq, BroFile* f);
	void RecordBlock(DataBlock* b, BroFile* f);
	void RecordGap(int start_seq, int upper_seq, BroFile* f);

	void BlockInserted(double t, DataBlock* b);
	void Overlap(const u_char* b1, const u_char* b2, int n);

	TCP_EndpointContents* endp;
	BroFile* record_contents_file; // file on which to reassemble contents
};


class BroFile;

class TCP_EndpointContents : public TCP_Endpoint {
public:
	TCP_EndpointContents(TCP_Connection* conn, int is_orig, int stop_on_gap = 0);
	~TCP_EndpointContents();

	int DataSent(double t, int seq, int len, const u_char* data,
			const IP_Hdr* ip, const struct tcphdr* tp);
	void AckReceived(double t, int seq);

	// CheckEOF checks if we have delivered all contents that we
	// can possibly deliver for this endpoint.  It calls
	// TCP_Connection::EndpointEOF() when so.
	void CheckEOF();

	int HasUndeliveredData() const	{ return t_reassem->HasBlocks(); }
	void MatchUndeliveredData()	{ t_reassem->MatchUndelivered(); }
	int DataSeq() const		{ return t_reassem->LastReassemSeq(); }

	virtual void Deliver(double t, int seq, int len, u_char* data);
	virtual void Undelivered(int seq, int len);

	int SkippingDeliveries() const	{ return skip_deliveries; }
	void SetSkipDeliveries(int should_skip)
		{ skip_deliveries = should_skip; }

	void SetContentsFile(BroFile* f);

protected:
	TCP_Reassembler* t_reassem;
	int skip_deliveries;	// if true, skip future data delivery
	int stop_on_gap;
	int did_EOF;
};

#define CR_as_EOL 1
#define LF_as_EOL 2

class TCP_EndpointLine : public TCP_EndpointContents {
public:
	TCP_EndpointLine(TCP_Connection* conn, int is_orig,
			int is_NUL_sensitive, int skip_partial,
			int CRLF_as_EOL = (CR_as_EOL | LF_as_EOL));
	~TCP_EndpointLine();

	int HasPartialLine() const	{ return buf && offset > 0; }

protected:
	void Init(int size);
	void Deliver(double t, int seq, int len, u_char* data);
	virtual void DoDeliver(double t, int seq, int len, u_char* data);
	virtual int DoDeliverOnce(int len, u_char* data);
	virtual void ExcessiveLine(const u_char* data, int len);
	u_char* NextNewLine(int len, u_char* data, int& offset);
	void CheckNUL();

	char* buf;	// where we build up the body of the request
	int offset;	// where we are in buf
	int buf_len;	// how big buf is, total
	unsigned int last_char;	// last (non-option) character scanned

	int flag_NULs;	// if true, flag (first) line with embedded NUL
	int punt_on_partial;	// if true, don't process TCP_PARTIAL conn's
	int line_limit;	// if non-zero, flag lines longer than this
	int CR_LF_as_EOL;  // whether single CR/LF are considered as EOL
};

class TCP_EndpointAnalyzer: public BroObj {
public:
	TCP_EndpointAnalyzer(TCP_Endpoint* e);
	virtual ~TCP_EndpointAnalyzer();

	void AddAnalyzer(TCP_EndpointAnalyzer* a)
		{
		if ( a )
			a->next = next;
		next = a;
		}

	virtual int DataSent(double t, int seq, int len, const u_char* data,
				const IP_Hdr* ip, const struct tcphdr* tp);

	TCP_EndpointAnalyzer* NextAnalyzer()	{ return next; }

	TCP_Endpoint* Endpoint() const	{ return endp; }
	TCP_Connection* Conn() const	{ return endp->Conn(); }

	void Describe(ODesc* d) const;

protected:
	TCP_Endpoint* endp;
	TCP_EndpointAnalyzer* next;
};


#define ENDIAN_UNKNOWN 0
#define ENDIAN_LITTLE 1
#define ENDIAN_BIG 2
#define ENDIAN_CONFUSED 3

class TCP_EndpointStats : public TCP_EndpointAnalyzer {
public:
	TCP_EndpointStats(TCP_Endpoint* endp);

	int DataSent(double t, int seq, int len, const u_char* data,
			const IP_Hdr* ip, const struct tcphdr* tp);

	RecordVal* BuildStats();

protected:
	int num_pkts;
	int num_rxmit;
	int num_rxmit_bytes;
	int num_in_order;
	int num_OO;
	int num_repl;
	int max_top_seq;
	int last_id;
	int endian_type;
};

class TCP_Rewriter;
class TCP_SourcePacketWriter;
class TCP_Connection : public Connection {
public:
	TCP_Connection(NetSessions* s, HashKey* k, double t, const ConnID* id,
			const struct tcphdr* tp);
	virtual void Init();

	~TCP_Connection();
	void Done();

	void AddAnalyzer(TCP_Analyzer* a);
	void AnalyzerDone();

	int HasAnalyzers() const
		{ return orig->Analyzer() != 0 || resp->Analyzer() != 0; }

	void 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);

	virtual void Deliver(TCP_Endpoint* sender, double t, int seq, int len,
				u_char* data);
	virtual void Undelivered(TCP_Endpoint* sender, int seq, int len);

	// If this connection has a TCP_EndpointLine, the following is called
	// for each new line of data.  Note that the data is delivered as
	// const char*, and \0-terminated.  The caller should not modify
	// the data.  However, the default version of this method then calls
	// the next flavor, which is passed a char* instead.  The caller is
	// free to modify that data, but must not retain a pointer to it,
	// as it is volatile.
	//
	// const_len gives the number of const char's pointed to by line,
	// not including the newline.  If it's 0, that means that the
	// callee can safely cast line to char* and modify it (but not
	// keep a copy of a pointer to it, per the above).
	virtual void NewLine(TCP_Endpoint* sender, int length,
				const char* line, int is_const);

	// "name" and "val" both now belong to this object, which needs to
	// delete them when done with them.
	virtual void SetEnv(TCP_Endpoint* sender, char* name, char* val);

	void SetContentsFile(unsigned int direction, BroFile* f);

	TCP_Rewriter* TraceRewriter() const	{ return trace_rewriter; }
	void SetTraceRewriter(TCP_Rewriter* rewriter);

	TCP_SourcePacketWriter* SourcePacketWriter() const 	{ return src_pkt_writer; }

	int IsReuse(double t, const u_char* pkt);

	// The given endpoint's data delivery is complete.
	virtual void EndpointEOF(TCP_EndpointContents* endp);

	// TraceRewriterEOF is virtual so that individual protocols
	// can change the decision on whether to call EndOfData() or not.
	virtual void TraceRewriterEOF(TCP_Endpoint* endp);

	int IsPartial() const		{ return is_partial; }

	TCP_Endpoint* Orig() const	{ return orig; }
	TCP_Endpoint* Resp() const	{ return resp; }

	// True if the connection has closed in some sense, false otherwise.
	int IsClosed() const	{ return orig->did_close || resp->did_close; }
	int BothClosed() const	{ return orig->did_close && resp->did_close; }

	uint32 OrigSeq() const	{ return orig->last_seq; }
	uint32 RespSeq() const	{ return resp->last_seq; }

	int IsOrig(TCP_Endpoint* endp)	{ return endp == orig; }

	ConnectionType ConnType() const	{ return CONN_TCP; }

	void Describe(ODesc* d) const;

protected:
	virtual void BuildEndpoints();

	// True if either endpoint still has pending data.  closing_endp
	// is an endpoint that has indicated it is closing (i.e., for
	// which we have seen a FIN) - for it, data is pending unless
	// everything's been delivered up to the FIN.  For its peer,
	// the test is whether it has any outstanding, un-acked data.
	int DataPending(TCP_Endpoint* closing_endp);

	void UpdateEndpointVal(RecordVal* endp, int is_orig);

	// Called whenever an end enters TCP_CLOSED or TCP_RESET.  If
	// gen_event is true and the connection is now fully closed,
	// a connection_finished event will be generated; otherwise not.
	virtual void ConnectionClosed(TCP_Endpoint* endpoint,
					TCP_Endpoint* peer, int gen_event);
	virtual void ConnectionFinished(int half_finished);
	virtual void ConnectionReset();

	// Called whenever a RST packet is seen -- sometimes invocation
	// of ConnectionReset is delayed.
	virtual void PacketWithRST() { }

	void AttemptTimer(double t);
	void PartialCloseTimer(double t);
	void ExpireTimer(double t);
	void ResetTimer(double t);
	void DeleteTimer(double t);

	Val* BuildSYNPacketVal(int is_orig,
				const IP_Hdr* ip, const struct tcphdr* tcp);

	int is_partial;

	TCP_Endpoint* orig;
	TCP_Endpoint* resp;

	TCP_Analyzer* analyzer;

	// Whether we're waiting on final data delivery before closing
	// this connection out.
	int close_deferred;

	// Whether to generate an event when we finally do.
	int deferred_gen_event;

	TCP_Rewriter* trace_rewriter;
	TCP_SourcePacketWriter* src_pkt_writer;
};

typedef enum {
	// The numbers here are important, they're used for bit-masking.
	TCP_CONTENTS_NONE = 0,
	TCP_CONTENTS_ORIG = 1,	// reassemble originator stream
	TCP_CONTENTS_RESP = 2,	// reassemble responder stream
	TCP_CONTENTS_BOTH = 3,	// reassemble both sides
} TCPContentsType;

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

protected:
	void BuildEndpoints();

	TCPContentsType contents_type;
};

class TCP_Analyzer : public BroObj {
public:
	TCP_Analyzer(TCP_Connection* c)
		{
		conn = c;
		done = 0;
		next = 0;
		}

	virtual ~TCP_Analyzer();
	virtual void Done();

	void AddAnalyzer(TCP_Analyzer* a)
		{
		if ( a )
			a->next = next;
		next = a;
		}

	TCP_Analyzer* NextAnalyzer()		{ return next; }

	TCP_Connection* Conn() const		{ return conn; }

	void Describe(ODesc* d) const;

protected:
	TCP_Analyzer* next;
	TCP_Connection* conn;
	int done;	// if true, corresponding connection has finished
};

class TCP_Stats : public TCP_Analyzer {
public:
	TCP_Stats(TCP_Connection* c);
	~TCP_Stats();
	void Done();

protected:
	TCP_EndpointStats* orig_stats;
	TCP_EndpointStats* resp_stats;
};


int TCP_Endpoint::IsOrig() const	{ return conn->Orig() == this; }

#endif
